diff --git a/cmd/api/api.go b/cmd/api/api.go index 057493d42..072117d75 100644 --- a/cmd/api/api.go +++ b/cmd/api/api.go @@ -90,6 +90,7 @@ func NewCmdApiWithContext(ctx context.Context, f *cmdutil.Factory, runF func(*AP cmd.Flags().IntVar(&opts.PageLimit, "page-limit", 10, "max pages to fetch with --page-all (0 = unlimited)") cmd.Flags().IntVar(&opts.PageDelay, "page-delay", 200, "delay in ms between pages") cmd.Flags().StringVar(&opts.Format, "format", "json", "output format: json|ndjson|table|csv") + cmd.Flags().Bool("json", false, "shorthand for --format json") cmd.Flags().StringVarP(&opts.JqExpr, "jq", "q", "", "jq expression to filter JSON output") cmd.Flags().BoolVar(&opts.DryRun, "dry-run", false, "print request without executing") cmd.Flags().StringVar(&opts.File, "file", "", "file to upload as multipart/form-data ([field=]path, supports - for stdin)") diff --git a/cmd/api/api_test.go b/cmd/api/api_test.go index a966f1430..c3530c323 100644 --- a/cmd/api/api_test.go +++ b/cmd/api/api_test.go @@ -718,3 +718,23 @@ func TestApiCmd_PermissionError_DerivesFirstClassFields(t *testing.T) { t.Errorf("LogID = %q, want %q", pe.LogID, "20260527-test-log") } } + +func TestApiCmd_JsonFlag_Accepted(t *testing.T) { + f, _, _, _ := cmdutil.TestFactory(t, &core.CliConfig{ + AppID: "test-app", AppSecret: "test-secret", Brand: core.BrandFeishu, + }) + + var gotOpts *APIOptions + cmd := NewCmdApi(f, func(opts *APIOptions) error { + gotOpts = opts + return nil + }) + cmd.SetArgs([]string{"GET", "/open-apis/test", "--json"}) + err := cmd.Execute() + if err != nil { + t.Fatalf("--json should be accepted without error, got: %v", err) + } + if gotOpts.Method != "GET" { + t.Errorf("expected method GET, got %s", gotOpts.Method) + } +} diff --git a/cmd/service/service.go b/cmd/service/service.go index 125cc584f..50a6e97b2 100644 --- a/cmd/service/service.go +++ b/cmd/service/service.go @@ -180,6 +180,7 @@ func NewCmdServiceMethodWithContext(ctx context.Context, f *cmdutil.Factory, spe cmd.Flags().IntVar(&opts.PageLimit, "page-limit", 10, "max pages to fetch with --page-all (0 = unlimited)") cmd.Flags().IntVar(&opts.PageDelay, "page-delay", 200, "delay in ms between pages") cmd.Flags().StringVar(&opts.Format, "format", "json", "output format: json|ndjson|table|csv") + cmd.Flags().Bool("json", false, "shorthand for --format json") cmd.Flags().StringVarP(&opts.JqExpr, "jq", "q", "", "jq expression to filter JSON output") cmd.Flags().BoolVar(&opts.DryRun, "dry-run", false, "print request without executing") if risk == "high-risk-write" { diff --git a/cmd/service/service_test.go b/cmd/service/service_test.go index 42377850e..172ed7f8a 100644 --- a/cmd/service/service_test.go +++ b/cmd/service/service_test.go @@ -765,3 +765,22 @@ func TestDetectFileFields(t *testing.T) { }) } } + +func TestServiceMethod_JsonFlag_Accepted(t *testing.T) { + f, _, _, _ := cmdutil.TestFactory(t, testConfig) + + var captured *ServiceMethodOptions + cmd := NewCmdServiceMethod(f, driveSpec(), + map[string]interface{}{"description": "desc", "httpMethod": "GET"}, "list", "files", + func(opts *ServiceMethodOptions) error { + captured = opts + return nil + }) + cmd.SetArgs([]string{"--json"}) + if err := cmd.Execute(); err != nil { + t.Fatalf("--json should be accepted without error, got: %v", err) + } + if captured == nil { + t.Fatal("expected runF to be called") + } +} diff --git a/shortcuts/common/runner.go b/shortcuts/common/runner.go index 7b023e6e9..e091754b8 100644 --- a/shortcuts/common/runner.go +++ b/shortcuts/common/runner.go @@ -1176,6 +1176,9 @@ func registerShortcutFlagsWithContext(ctx context.Context, cmd *cobra.Command, f cmdutil.RegisterFlagCompletion(cmd, "format", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { return []string{"json", "pretty", "table", "ndjson", "csv"}, cobra.ShellCompDirectiveNoFileComp }) + if cmd.Flags().Lookup("json") == nil { + cmd.Flags().Bool("json", false, "shorthand for --format json") + } } if s.Risk == "high-risk-write" { cmd.Flags().Bool("yes", false, "confirm high-risk operation") diff --git a/shortcuts/common/runner_flag_completion_test.go b/shortcuts/common/runner_flag_completion_test.go index 3c96dbd16..c0eaf2807 100644 --- a/shortcuts/common/runner_flag_completion_test.go +++ b/shortcuts/common/runner_flag_completion_test.go @@ -96,3 +96,76 @@ func TestShortcutMount_FlagCompletionsDisabled(t *testing.T) { t.Fatal("did not expect completion func for --format when disabled") } } + +func TestShortcutMount_JsonFlag_AcceptedWhenHasFormat(t *testing.T) { + f, _, _, _ := cmdutil.TestFactory(t, nil) + parent := &cobra.Command{Use: "root"} + shortcut := Shortcut{ + Service: "test", + Command: "+read", + Description: "test read", + HasFormat: true, + Execute: func(context.Context, *RuntimeContext) error { return nil }, + } + shortcut.Mount(parent, f) + + cmd, _, err := parent.Find([]string{"+read"}) + if err != nil { + t.Fatalf("Find() error = %v", err) + } + if flag := cmd.Flags().Lookup("json"); flag == nil { + t.Fatal("expected --json flag to be registered on HasFormat shortcut") + } +} + +func TestShortcutMount_JsonFlag_SkippedWhenConflict(t *testing.T) { + f, _, _, _ := cmdutil.TestFactory(t, nil) + parent := &cobra.Command{Use: "root"} + shortcut := Shortcut{ + Service: "test", + Command: "+update", + Description: "test update", + HasFormat: true, + Flags: []Flag{ + {Name: "json", Desc: "body JSON object", Required: true}, + }, + Execute: func(context.Context, *RuntimeContext) error { return nil }, + } + shortcut.Mount(parent, f) + + cmd, _, err := parent.Find([]string{"+update"}) + if err != nil { + t.Fatalf("Find() error = %v", err) + } + // --json flag exists (from custom Flags), but should be the string type, not bool. + flag := cmd.Flags().Lookup("json") + if flag == nil { + t.Fatal("expected --json flag from custom Flags") + } + if flag.DefValue != "" { + t.Errorf("expected empty default (string flag), got %q", flag.DefValue) + } +} + +func TestShortcutMount_JsonFlag_RegisteredWithoutHasFormat(t *testing.T) { + f, _, _, _ := cmdutil.TestFactory(t, nil) + parent := &cobra.Command{Use: "root"} + shortcut := Shortcut{ + Service: "test", + Command: "+write", + Description: "test write", + HasFormat: false, + Execute: func(context.Context, *RuntimeContext) error { return nil }, + } + shortcut.Mount(parent, f) + + cmd, _, err := parent.Find([]string{"+write"}) + if err != nil { + t.Fatalf("Find() error = %v", err) + } + // --format is now registered for all shortcuts (regardless of HasFormat), + // so --json should also be present. + if flag := cmd.Flags().Lookup("json"); flag == nil { + t.Fatal("expected --json flag to be registered even when HasFormat is false") + } +}