feat(commands): add SubCommand type and EffectiveUsage method
Introduce SubCommand struct for declaring sub-commands structurally within a parent command Definition. The EffectiveUsage() method auto-generates usage strings from sub-command names and args, preventing drift between help text and actual handler behavior. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6a6d2c9d47
commit
9f61d50470
2 changed files with 75 additions and 2 deletions
|
|
@ -1,5 +1,18 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SubCommand defines a single sub-command within a parent command.
|
||||
type SubCommand struct {
|
||||
Name string
|
||||
Description string
|
||||
ArgsUsage string // optional, e.g. "<session-id>"
|
||||
Handler Handler
|
||||
}
|
||||
|
||||
// Definition is the single-source metadata and behavior contract for a slash command.
|
||||
//
|
||||
// Design notes (phase 1):
|
||||
|
|
@ -10,7 +23,26 @@ package commands
|
|||
type Definition struct {
|
||||
Name string
|
||||
Description string
|
||||
Usage string
|
||||
Usage string // for simple commands; ignored when SubCommands is set
|
||||
Aliases []string
|
||||
Handler Handler
|
||||
SubCommands []SubCommand // optional; when set, Executor routes to sub-command handlers
|
||||
Handler Handler // for simple commands without sub-commands
|
||||
}
|
||||
|
||||
// EffectiveUsage returns the usage string. When SubCommands are present,
|
||||
// it is auto-generated from sub-command names so metadata and behavior
|
||||
// cannot drift.
|
||||
func (d Definition) EffectiveUsage() string {
|
||||
if len(d.SubCommands) == 0 {
|
||||
return d.Usage
|
||||
}
|
||||
names := make([]string, 0, len(d.SubCommands))
|
||||
for _, sc := range d.SubCommands {
|
||||
name := sc.Name
|
||||
if sc.ArgsUsage != "" {
|
||||
name += " " + sc.ArgsUsage
|
||||
}
|
||||
names = append(names, name)
|
||||
}
|
||||
return fmt.Sprintf("/%s [%s]", d.Name, strings.Join(names, "|"))
|
||||
}
|
||||
|
|
|
|||
41
pkg/commands/definition_test.go
Normal file
41
pkg/commands/definition_test.go
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefinition_EffectiveUsage_NoSubCommands(t *testing.T) {
|
||||
d := Definition{Name: "start", Usage: "/start"}
|
||||
if got := d.EffectiveUsage(); got != "/start" {
|
||||
t.Fatalf("EffectiveUsage()=%q, want %q", got, "/start")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefinition_EffectiveUsage_WithSubCommands(t *testing.T) {
|
||||
d := Definition{
|
||||
Name: "show",
|
||||
SubCommands: []SubCommand{
|
||||
{Name: "model"},
|
||||
{Name: "channel"},
|
||||
{Name: "agents"},
|
||||
},
|
||||
}
|
||||
want := "/show [model|channel|agents]"
|
||||
if got := d.EffectiveUsage(); got != want {
|
||||
t.Fatalf("EffectiveUsage()=%q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefinition_EffectiveUsage_WithArgsUsage(t *testing.T) {
|
||||
d := Definition{
|
||||
Name: "session",
|
||||
SubCommands: []SubCommand{
|
||||
{Name: "list"},
|
||||
{Name: "resume", ArgsUsage: "<id>"},
|
||||
},
|
||||
}
|
||||
want := "/session [list|resume <id>]"
|
||||
if got := d.EffectiveUsage(); got != want {
|
||||
t.Fatalf("EffectiveUsage()=%q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue