返回 DeepSeek-Reasonix
help_view.go
根目录 / internal / cli / help_view.go
1 package cli
2
3 import (
4 "fmt"
5 "strings"
6
7 "reasonix/internal/command"
8 "reasonix/internal/control"
9 "reasonix/internal/plugin"
10 "reasonix/internal/skill"
11 )
12
13 const helpMaxDynamicItems = 8
14
15 func (m *chatTUI) showHelp() {
16 m.commitLine(renderHelp(m.width, m.commands, m.skills, m.prompts()))
17 }
18
19 func renderHelp(width int, commands []command.Command, skills []skill.Skill, prompts []plugin.Prompt) string {
20 var b strings.Builder
21 docsOwner := control.ResolveSlashCommandOwner(control.DocsSlashName, commands, skills)
22 docsBuiltin := "/" + control.ResolvedBuiltinSlashName(control.DocsSlashName, commands, skills)
23 builtins := renameSlashItem(builtinHelpItems(), "/docs", docsBuiltin)
24 fmt.Fprintf(&b, "%s\n", viewHeader("commands"))
25 writeHelpItems(&b, width, "built-in", builtins, 0)
26 if len(commands) > 0 {
27 writeHelpItems(&b, width, "custom", customHelpItems(commands), helpMaxDynamicItems)
28 }
29 if len(skills) > 0 {
30 items := skillHelpItems(skills)
31 if docsOwner == control.SlashOwnerCustom {
32 items = removeSlashItems(items, "/docs")
33 }
34 writeHelpItems(&b, width, "skills", items, helpMaxDynamicItems)
35 }
36 if len(prompts) > 0 {
37 writeHelpItems(&b, width, "MCP prompts", promptHelpItems(prompts), helpMaxDynamicItems)
38 }
39 b.WriteString(viewHint("type a command, or press Tab after / for completion"))
40 return strings.TrimRight(b.String(), "\n")
41 }
42
43 func writeHelpItems(b *strings.Builder, width int, title string, items []compItem, limit int) {
44 if len(items) == 0 {
45 return
46 }
47 b.WriteString(viewSubhead(title) + "\n")
48 n := len(items)
49 if limit > 0 && n > limit {
50 n = limit
51 }
52 for _, item := range items[:n] {
53 name := item.label
54 used := 2 + viewPadWidth(name, 18) + 1
55 hint := viewCompactText(item.hint, viewBudget(width, used))
56 fmt.Fprintf(b, " %-18s %s\n", name, viewMeta(hint))
57 }
58 if extra := len(items) - n; extra > 0 {
59 b.WriteString(viewMore(extra, "items") + "\n")
60 }
61 }
62
63 func builtinHelpItems() []compItem {
64 return builtinSlashHelpItems()
65 }
66
67 func customHelpItems(commands []command.Command) []compItem {
68 items := make([]compItem, 0, len(commands))
69 for _, c := range commands {
70 if c.Hidden {
71 continue
72 }
73 items = append(items, compItem{label: "/" + c.Name, hint: customCommandHint(c)})
74 }
75 return items
76 }
77
78 func customCommandHint(c command.Command) string {
79 if c.Plugin == "" {
80 return c.Description
81 }
82 source := "plugin " + c.Plugin
83 if c.Description == "" {
84 return source
85 }
86 return source + " · " + c.Description
87 }
88
89 func skillHelpItems(skills []skill.Skill) []compItem {
90 items := make([]compItem, 0, len(skills))
91 for _, s := range skills {
92 hint := s.Description
93 if s.RunAs == skill.RunSubagent {
94 hint = "subagent · " + hint
95 }
96 items = append(items, compItem{label: "/" + s.SlashName(), hint: skillCommandHint(s, hint)})
97 }
98 return items
99 }
100
101 func skillCommandHint(s skill.Skill, hint string) string {
102 if s.Plugin == "" {
103 return hint
104 }
105 source := "plugin " + s.Plugin
106 if hint == "" {
107 return source
108 }
109 return source + " · " + hint
110 }
111
112 func promptHelpItems(prompts []plugin.Prompt) []compItem {
113 items := make([]compItem, 0, len(prompts))
114 for _, p := range prompts {
115 items = append(items, compItem{label: "/" + p.Name, hint: p.Description})
116 }
117 return items
118 }
119
119 lines GO