feat(command): improve slash command UX (#361)

Make slash commands easier to navigate in chat by splitting help into levels, compacting list output, and surfacing current selections for model, search, memory, and browser settings. Also route /status to the active conversation session and add an access inspector so users can understand their current command and ACL context.
This commit is contained in:
Acbox
2026-04-13 12:37:12 +08:00
committed by GitHub
parent c9c221e35d
commit d46269de89
22 changed files with 1080 additions and 138 deletions
+78 -12
View File
@@ -8,10 +8,18 @@ import (
// CommandContext carries execution context for a sub-command.
type CommandContext struct {
Ctx context.Context
BotID string
Role string // "owner", "admin", "member", or "" (guest)
Args []string
Ctx context.Context
BotID string
Role string // "owner", "admin", "member", or "" (guest)
Args []string
ChannelIdentityID string
UserID string
ChannelType string
ConversationType string
ConversationID string
ThreadID string
RouteID string
SessionID string
}
// SubCommand describes a single sub-command within a resource group.
@@ -50,15 +58,38 @@ func (g *CommandGroup) Usage() string {
fmt.Fprintf(&b, "/%s - %s\n", g.Name, g.Description)
for _, name := range g.order {
sub := g.commands[name]
perm := ""
desc := subSummary(sub)
if sub.IsWrite {
perm = " [owner]"
desc += " [owner]"
}
fmt.Fprintf(&b, "- %s%s\n", sub.Usage, perm)
fmt.Fprintf(&b, "- %s\n", desc)
}
fmt.Fprintf(&b, "\nUse /help %s <action> for details.", g.Name)
return b.String()
}
func (g *CommandGroup) ActionHelp(action string) string {
sub, ok := g.commands[action]
if !ok {
return fmt.Sprintf("Unknown action %q for /%s.\n\n%s", action, g.Name, g.Usage())
}
usage, summary := splitUsage(sub.Usage)
var b strings.Builder
fmt.Fprintf(&b, "/%s %s\n", g.Name, sub.Name)
if summary != "" {
fmt.Fprintf(&b, "- Summary: %s\n", summary)
}
if usage == "" {
usage = sub.Name
}
fmt.Fprintf(&b, "- Usage: /%s %s\n", g.Name, usage)
if sub.IsWrite {
b.WriteString("- Access: owner only\n")
}
fmt.Fprintf(&b, "- Tip: use /help %s to view sibling actions.", g.Name)
return strings.TrimRight(b.String(), "\n")
}
// Registry holds all registered command groups.
type Registry struct {
groups map[string]*CommandGroup
@@ -83,12 +114,47 @@ func (r *Registry) GlobalHelp() string {
b.WriteString("/help - Show this help message\n")
b.WriteString("/new - Start a new conversation (resets session context)\n")
b.WriteString("/stop - Stop the current generation\n\n")
for i, name := range r.order {
if i > 0 {
b.WriteByte('\n')
}
for _, name := range r.order {
group := r.groups[name]
b.WriteString(group.Usage())
fmt.Fprintf(&b, "- /%s - %s\n", group.Name, group.Description)
}
b.WriteString("\nUse /help <group> to view actions, e.g. /help model")
return strings.TrimRight(b.String(), "\n")
}
func (r *Registry) GroupHelp(name string) string {
group, ok := r.groups[name]
if !ok {
return fmt.Sprintf("Unknown command group: /%s\n\n%s", name, r.GlobalHelp())
}
return group.Usage()
}
func (r *Registry) ActionHelp(groupName, action string) string {
group, ok := r.groups[groupName]
if !ok {
return fmt.Sprintf("Unknown command group: /%s\n\n%s", groupName, r.GlobalHelp())
}
return group.ActionHelp(action)
}
func splitUsage(usage string) (commandUsage string, summary string) {
usage = strings.TrimSpace(usage)
if usage == "" {
return "", ""
}
parts := strings.SplitN(usage, " - ", 2)
commandUsage = strings.TrimSpace(parts[0])
if len(parts) > 1 {
summary = strings.TrimSpace(parts[1])
}
return commandUsage, summary
}
func subSummary(sub SubCommand) string {
usage, summary := splitUsage(sub.Usage)
if summary == "" {
return usage
}
return fmt.Sprintf("%s - %s", sub.Name, summary)
}