mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
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:
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user