feat(bootstrap): add command alias expansion
This commit is contained in:
parent
1b603f552c
commit
8c4f88ea93
2 changed files with 139 additions and 1 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue