diff --git a/shortcuts/common/runner.go b/shortcuts/common/runner.go index 7623c8d4..051e0b3a 100644 --- a/shortcuts/common/runner.go +++ b/shortcuts/common/runner.go @@ -540,6 +540,7 @@ func (s Shortcut) mountDeclarative(parent *cobra.Command, f *cmdutil.Factory) { cmd := &cobra.Command{ Use: shortcut.Command, Short: shortcut.Description, + Args: rejectPositionalArgs(), RunE: func(cmd *cobra.Command, _ []string) error { return runShortcut(cmd, f, &shortcut, botOnly) }, @@ -681,6 +682,19 @@ func handleShortcutDryRun(f *cmdutil.Factory, rctx *RuntimeContext, s *Shortcut) return nil } +// rejectPositionalArgs returns a cobra.PositionalArgs that rejects any +// positional arguments. The error is intentionally a plain error (not +// ExitError) so that cobra prints usage and the root handler prints a +// simple "Error:" line instead of a JSON envelope. +func rejectPositionalArgs() cobra.PositionalArgs { + return func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return nil + } + return fmt.Errorf("positional arguments are not supported (got %q); pass values via flags", args) + } +} + func registerShortcutFlags(cmd *cobra.Command, s *Shortcut) { for _, fl := range s.Flags { desc := fl.Desc diff --git a/shortcuts/common/runner_args_test.go b/shortcuts/common/runner_args_test.go new file mode 100644 index 00000000..f04999da --- /dev/null +++ b/shortcuts/common/runner_args_test.go @@ -0,0 +1,58 @@ +// Copyright (c) 2026 Lark Technologies Pte. Ltd. +// SPDX-License-Identifier: MIT + +package common + +import ( + "strings" + "testing" + + "github.com/spf13/cobra" +) + +func TestRejectPositionalArgs_WithArgs(t *testing.T) { + t.Parallel() + + validator := rejectPositionalArgs() + + err := validator(&cobra.Command{}, []string{"hello"}) + if err == nil { + t.Fatal("expected error for positional arg, got nil") + } + if !strings.Contains(err.Error(), "positional arguments are not supported") { + t.Errorf("expected positional args rejection message, got: %v", err) + } + if !strings.Contains(err.Error(), `"hello"`) { + t.Errorf("expected the positional arg value in error, got: %v", err) + } +} + +func TestRejectPositionalArgs_MultipleArgs(t *testing.T) { + t.Parallel() + + validator := rejectPositionalArgs() + + err := validator(&cobra.Command{}, []string{"hello", "world"}) + if err == nil { + t.Fatal("expected error for multiple positional args, got nil") + } + if !strings.Contains(err.Error(), "positional arguments are not supported") { + t.Errorf("unexpected error message: %v", err) + } + if !strings.Contains(err.Error(), "hello") || !strings.Contains(err.Error(), "world") { + t.Errorf("expected all positional args in error, got: %v", err) + } +} + +func TestRejectPositionalArgs_NoArgs(t *testing.T) { + t.Parallel() + + validator := rejectPositionalArgs() + + if err := validator(&cobra.Command{}, nil); err != nil { + t.Fatalf("expected no error for nil args, got: %v", err) + } + if err := validator(&cobra.Command{}, []string{}); err != nil { + t.Fatalf("expected no error for empty args, got: %v", err) + } +}