Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions tui/component_command_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (m *model) activateSkillCommand(input, name string, args map[string]string)
if err != nil {
return err
}
response := fmt.Sprintf("Activated skill `%s` (%s).\nTool policy: %s\nEntry: %s", skill.Name, skill.Scope, skill.ToolPolicy.Policy, skill.Entry.Slash)
response := fmt.Sprintf("Activated skill `%s` (%s).\nTool policy: %s", skill.Name, skill.Scope, skill.ToolPolicy.Policy)
if len(args) > 0 {
argParts := make([]string, 0, len(args))
keys := make([]string, 0, len(args))
Expand Down Expand Up @@ -145,5 +145,5 @@ func (m *model) activateSelectedSkill() error {
}
index := clamp(m.commandCursor, 0, len(items)-1)
selected := items[index]
return m.activateSkillCommand(selected.Usage, selected.Usage, nil)
return m.activateSkillCommand("Skill select: "+selected.Name, selected.Name, nil)
}
2 changes: 1 addition & 1 deletion tui/component_command_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ func (m model) helpText() string {
"- `/agents [name]`: list available subagents or show one definition.",
"- `/review`: run the builtin review subagent with a bounded task.",
"- `/explorer`: run the builtin explorer subagent with a bounded task.",
"- `/skills-select`: open the loaded skills picker.",
"- `/skills`: list discovered skills and diagnostics.",
"- `/mcp list`: list configured MCP servers and runtime status.",
"- `/mcp help`: show MCP command help.",
"- `/mcp show <id>`: show one MCP server config and runtime status.",
"- Add MCP servers by writing `.bytemind/mcp.json` in the workspace.",
"- `/model`: open the configured provider/model picker and switch with Up/Down + Enter.",
"- `/<skill-name> [k=v...]`: activate a skill for this session.",
"- `/skill clear`: clear the active skill in this session.",
"- `/skill delete <name>`: delete the specified project skill.",
"- `/new`: start a fresh session.",
Expand Down
2 changes: 1 addition & 1 deletion tui/component_palette_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ func (m model) skillPickerItems() []commandItem {
}
items = append(items, commandItem{
Name: name,
Usage: "/" + name,
Usage: name,
Description: description,
Kind: "skill",
})
Expand Down
56 changes: 39 additions & 17 deletions tui/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3742,6 +3742,7 @@ func TestHelpTextOnlyMentionsSupportedEntryPoints(t *testing.T) {
"/plan",
"/skill use",
"/skill show",
"/<skill-name>",
} {
if strings.Contains(text, unwanted) {
t.Fatalf("help text should not mention %q", unwanted)
Expand All @@ -3752,6 +3753,7 @@ func TestHelpTextOnlyMentionsSupportedEntryPoints(t *testing.T) {
"go run ./cmd/bytemind chat",
"go run ./cmd/bytemind run -prompt",
"/session",
"/skills-select",
"TUI does not expose `/resume`",
"CLI keeps `/resume <id>`",
"/skill clear",
Expand Down Expand Up @@ -4496,26 +4498,27 @@ func TestFilteredCommandsExcludeSkillSlashCommands(t *testing.T) {

items := m.filteredCommands()
for _, item := range items {
if item.Name == "review" && item.Kind == "skill" {
t.Fatalf("skill commands should NOT appear in filtered commands, found %q", item.Name)
if item.Kind == "skill" {
t.Fatalf("command palette should not expose skill slash items, got %+v in %+v", item, items)
}
}

m.syncCommandPalette()
skills := m.skillPickerItems()
found := false
for _, s := range skills {
if s.Name == "review" && s.Usage == "/review" && s.Kind == "skill" {
for _, item := range skills {
if item.Name == "review" && item.Usage == "review" && item.Kind == "skill" {
found = true
break
}
if strings.HasPrefix(item.Usage, "/") {
t.Fatalf("skill picker should show skill names without slash, got %+v", item)
}
}
if !found {
t.Fatal("expected /review skill in skillPickerItems")
t.Fatalf("expected review skill in picker items, got %+v", skills)
}
}

func TestFilteredCommandsExcludeProjectSkillSlashCommands(t *testing.T) {
func TestSkillPickerActivatesProjectSkillWithoutShowingSlash(t *testing.T) {
workspace := t.TempDir()
skillDir := filepath.Join(workspace, ".bytemind", "skills", "review-plus")
if err := os.MkdirAll(skillDir, 0o755); err != nil {
Expand Down Expand Up @@ -4558,21 +4561,40 @@ func TestFilteredCommandsExcludeProjectSkillSlashCommands(t *testing.T) {

items := m.filteredCommands()
for _, item := range items {
if item.Name == "review-plus" && item.Kind == "skill" {
t.Fatalf("skill commands should NOT appear in filtered commands, found %q", item.Name)
if item.Kind == "skill" {
t.Fatalf("command palette should not expose project skill slash items, got %+v in %+v", item, items)
}
}

skills := m.skillPickerItems()
found := false
for _, s := range skills {
if s.Name == "review-plus" && s.Usage == "/review-plus" && s.Kind == "skill" {
found = true
pickerItems := m.skillPickerItems()
pickerIndex := -1
for i, item := range pickerItems {
if item.Name == "review-plus" {
pickerIndex = i
if item.Usage != "review-plus" {
t.Fatalf("expected skill picker usage without slash, got %+v", item)
}
break
}
}
if !found {
t.Fatal("expected /review-plus project skill in skillPickerItems")
if pickerIndex < 0 {
t.Fatalf("expected review-plus in skill picker items, got %+v", pickerItems)
}

m.commandCursor = pickerIndex
if err := m.activateSelectedSkill(); err != nil {
t.Fatalf("expected skill picker activation to succeed, got %v", err)
}
if m.sess.ActiveSkill == nil || m.sess.ActiveSkill.Name != "review-plus" {
t.Fatalf("expected review-plus active after picker activation, got %#v", m.sess.ActiveSkill)
}
if len(m.chatItems) < 2 {
t.Fatalf("expected picker activation command exchange, got %#v", m.chatItems)
}
userBody := m.chatItems[len(m.chatItems)-2].Body
responseBody := m.chatItems[len(m.chatItems)-1].Body
if strings.Contains(userBody, "/review-plus") || strings.Contains(responseBody, "/review-plus") || strings.Contains(responseBody, "Entry:") {
t.Fatalf("skill picker activation should not render slash entry, user=%q response=%q", userBody, responseBody)
}
}

Expand Down
Loading