feat: handle --help/--version flags with DisableFlagParsing
- Use DisableFlagParsing to pass flags directly to plugins
- Handle --help/-h in Args validator by checking for flag prefix
- Handle --version/-v in main() by rewriting args to version subcommand
- Add PICOCLAW_NO_BANNER=1 env var to hide banner for programmatic use
- Add tests for flag handling and banner env var
💘 Generated with Crush
Assisted-by: MiniMax-M2.5 via Crush <crush@charm.land>
This commit is contained in:
parent
2e6a51168f
commit
2bd8c61f65
3 changed files with 86 additions and 14 deletions
|
|
@ -111,6 +111,11 @@ func listPlugins() ([]string, error) {
|
|||
}
|
||||
|
||||
func findPlugin(name string) (string, error) {
|
||||
// Don't treat flags as plugins - let caller handle them
|
||||
if len(name) > 0 && name[0] == '-' {
|
||||
return "", fmt.Errorf("not a plugin: %q", name)
|
||||
}
|
||||
|
||||
pluginsDir := getPluginsDir()
|
||||
|
||||
// Try exact match first
|
||||
|
|
|
|||
|
|
@ -34,7 +34,12 @@ func NewPicoclawCommand() *cobra.Command {
|
|||
Example: "picoclaw list",
|
||||
Args: rootArgsValidator,
|
||||
ValidArgsFunction: rootCompleteArgs,
|
||||
DisableFlagParsing: true, // Pass all args (including flags) directly to plugins/commands
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// With DisableFlagParsing, --help is just an arg - show help
|
||||
if len(args) > 0 && (args[0] == "--help" || args[0] == "-h") {
|
||||
return cmd.Help()
|
||||
}
|
||||
return cmd.Help()
|
||||
},
|
||||
}
|
||||
|
|
@ -76,18 +81,16 @@ func rootArgsValidator(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// Check if it's a known subcommand
|
||||
knownCommands := map[string]bool{
|
||||
"onboard": true,
|
||||
"agent": true,
|
||||
"auth": true,
|
||||
"gateway": true,
|
||||
"status": true,
|
||||
"cron": true,
|
||||
"migrate": true,
|
||||
"skills": true,
|
||||
"version": true,
|
||||
"help": true,
|
||||
"-h": true,
|
||||
"--help": true,
|
||||
"onboard": true,
|
||||
"agent": true,
|
||||
"auth": true,
|
||||
"gateway": true,
|
||||
"status": true,
|
||||
"cron": true,
|
||||
"migrate": true,
|
||||
"skills": true,
|
||||
"version": true,
|
||||
"help": true,
|
||||
}
|
||||
|
||||
if knownCommands[args[0]] {
|
||||
|
|
@ -97,12 +100,16 @@ func rootArgsValidator(cmd *cobra.Command, args []string) error {
|
|||
// Try to find a plugin with this name
|
||||
pluginPath, err := plugins.FindPlugin(args[0])
|
||||
if err != nil {
|
||||
// If it looks like a flag (starts with "-"), let cobra handle it
|
||||
if len(args[0]) > 0 && args[0][0] == '-' {
|
||||
return nil
|
||||
}
|
||||
// Not a known command and not a plugin - print error and exit
|
||||
fmt.Fprintf(os.Stderr, "picoclaw: %q is not a picoclaw command. See 'picoclaw --help'.\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Execute the plugin and exit
|
||||
// Execute the plugin with ALL remaining args (including any flags like --help)
|
||||
plugins.ExecPlugin(pluginPath, args[1:])
|
||||
// Should not reach here
|
||||
return nil
|
||||
|
|
@ -129,8 +136,22 @@ func rootCompleteArgs(cmd *cobra.Command, args []string, toComplete string) ([]s
|
|||
}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("%s", banner)
|
||||
// Print banner unless PICOCLAW_NO_BANNER=1
|
||||
if os.Getenv("PICOCLAW_NO_BANNER") != "1" {
|
||||
fmt.Printf("%s", banner)
|
||||
}
|
||||
cmd := NewPicoclawCommand()
|
||||
|
||||
// With DisableFlagParsing, intercept global flags before plugin routing
|
||||
if len(os.Args) > 1 {
|
||||
arg := os.Args[1]
|
||||
if arg == "--version" || arg == "-v" {
|
||||
// Convert --version to "version" subcommand
|
||||
newArgs := append([]string{os.Args[0], "version"}, os.Args[2:]...)
|
||||
cmd.SetArgs(newArgs[1:])
|
||||
}
|
||||
}
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
|
|
@ -54,3 +55,48 @@ func TestNewPicoclawCommand(t *testing.T) {
|
|||
assert.False(t, subcmd.Hidden)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisableFlagParsing(t *testing.T) {
|
||||
cmd := NewPicoclawCommand()
|
||||
assert.True(t, cmd.DisableFlagParsing, "DisableFlagParsing should be enabled")
|
||||
}
|
||||
|
||||
func TestNoBannerEnvVar(t *testing.T) {
|
||||
// Test that PICOCLAW_NO_BANNER environment variable controls banner
|
||||
origVal := os.Getenv("PICOCLAW_NO_BANNER")
|
||||
defer os.Setenv("PICOCLAW_NO_BANNER", origVal)
|
||||
|
||||
// Without env var
|
||||
os.Unsetenv("PICOCLAW_NO_BANNER")
|
||||
noBanner := os.Getenv("PICOCLAW_NO_BANNER") != "1"
|
||||
assert.True(t, noBanner, "banner should show without env var")
|
||||
|
||||
// With env var set to 1
|
||||
os.Setenv("PICOCLAW_NO_BANNER", "1")
|
||||
noBanner = os.Getenv("PICOCLAW_NO_BANNER") != "1"
|
||||
assert.False(t, noBanner, "banner should be hidden with PICOCLAW_NO_BANNER=1")
|
||||
}
|
||||
|
||||
func TestRootArgsValidatorFlags(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
wantFlag bool // true if should NOT route to plugin
|
||||
}{
|
||||
{"--help", []string{"--help"}, true},
|
||||
{"-h", []string{"-h"}, true},
|
||||
{"--version", []string{"--version"}, true},
|
||||
{"-v", []string{"-v"}, true},
|
||||
{"--unknown-flag", []string{"--unknown-flag"}, true},
|
||||
{"-x", []string{"-x"}, true},
|
||||
{"service", []string{"service"}, false}, // should route to plugin
|
||||
{"onboard", []string{"onboard"}, false}, // known command
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isFlag := len(tt.args) > 0 && len(tt.args[0]) > 0 && tt.args[0][0] == '-'
|
||||
assert.Equal(t, tt.wantFlag, isFlag, "flag detection for %q", tt.args[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue