feat(bootstrap): add command alias expansion

This commit is contained in:
thibaud-lclr 2026-04-14 16:31:26 +02:00
parent 1b603f552c
commit 8c4f88ea93
2 changed files with 139 additions and 1 deletions

View file

@ -47,6 +47,7 @@ type Options struct {
BinaryName string
Description string
Version string
Aliases map[string][]string
Args []string
Stdin io.Reader
Stdout io.Writer
@ -113,7 +114,7 @@ func Run(ctx context.Context, opts Options) error {
return ErrBinaryNameRequired
}
command, commandArgs, showHelp := parseArgs(normalized.Args)
command, commandArgs, showHelp := parseArgs(expandAliases(normalized.Args, normalized.Aliases))
if showHelp {
return printHelp(normalized, command, commandArgs)
}
@ -168,6 +169,7 @@ func normalize(opts Options) Options {
}
func parseArgs(args []string) (command string, commandArgs []string, showHelp bool) {
args = trimArgs(args)
if len(args) == 0 {
return "", nil, false
}
@ -193,6 +195,63 @@ func parseArgs(args []string) (command string, commandArgs []string, showHelp bo
return command, commandArgs, false
}
func expandAliases(args []string, aliases map[string][]string) []string {
args = trimArgs(args)
if len(args) == 0 || len(aliases) == 0 {
return args
}
if args[0] == "help" && len(args) > 1 {
expanded, ok := resolveAlias(aliases, args[1])
if !ok {
return args
}
withHelp := make([]string, 0, 1+len(expanded)+len(args[2:]))
withHelp = append(withHelp, "help")
withHelp = append(withHelp, expanded...)
withHelp = append(withHelp, args[2:]...)
return withHelp
}
expanded, ok := resolveAlias(aliases, args[0])
if !ok {
return args
}
withCommand := make([]string, 0, len(expanded)+len(args[1:]))
withCommand = append(withCommand, expanded...)
withCommand = append(withCommand, args[1:]...)
if len(withCommand) == 0 {
return args
}
last := strings.TrimSpace(withCommand[len(withCommand)-1])
if last != "-h" && last != "--help" {
return withCommand
}
helpArgs := make([]string, 0, len(withCommand))
helpArgs = append(helpArgs, "help")
helpArgs = append(helpArgs, withCommand[:len(withCommand)-1]...)
return helpArgs
}
func resolveAlias(aliases map[string][]string, command string) ([]string, bool) {
target, ok := aliases[strings.TrimSpace(command)]
if !ok {
return nil, false
}
expanded := trimArgs(target)
if len(expanded) == 0 {
return nil, false
}
return expanded, true
}
func trimArgs(args []string) []string {
if len(args) == 0 {
return nil

View file

@ -319,3 +319,82 @@ func TestRunConfigReturnsUnknownSubcommand(t *testing.T) {
t.Fatalf("Run error = %v, want ErrUnknownSubcommand", err)
}
}
func TestRunRoutesAliasToConfigSubcommandHook(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
var got Invocation
err := Run(context.Background(), Options{
BinaryName: "my-mcp",
Aliases: map[string][]string{
"doctor": {CommandConfig, ConfigSubcommandTest},
},
Args: []string{"doctor", "--profile", "work"},
Stdout: &stdout,
Stderr: &stderr,
Hooks: Hooks{
ConfigTest: func(_ context.Context, inv Invocation) error {
got = inv
return nil
},
},
})
if err != nil {
t.Fatalf("Run error = %v", err)
}
if got.Command != "config test" {
t.Fatalf("invocation command = %q, want %q", got.Command, "config test")
}
wantArgs := []string{"--profile", "work"}
if !slices.Equal(got.Args, wantArgs) {
t.Fatalf("invocation args = %v, want %v", got.Args, wantArgs)
}
}
func TestRunPrintsAliasHelpFromHelpCommand(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
err := Run(context.Background(), Options{
BinaryName: "my-mcp",
Aliases: map[string][]string{
"doctor": {CommandConfig, ConfigSubcommandTest},
},
Args: []string{"help", "doctor"},
Stdout: &stdout,
Stderr: &stderr,
})
if err != nil {
t.Fatalf("Run error = %v", err)
}
text := stdout.String()
if !strings.Contains(text, "my-mcp config test [args]") {
t.Fatalf("command help output = %q", text)
}
}
func TestRunPrintsAliasHelpFromTrailingHelpFlag(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
err := Run(context.Background(), Options{
BinaryName: "my-mcp",
Aliases: map[string][]string{
"doctor": {CommandConfig, ConfigSubcommandTest},
},
Args: []string{"doctor", "--help"},
Stdout: &stdout,
Stderr: &stderr,
})
if err != nil {
t.Fatalf("Run error = %v", err)
}
text := stdout.String()
if !strings.Contains(text, "my-mcp config test [args]") {
t.Fatalf("command help output = %q", text)
}
}