From 118d7541dd5d16fc0231d78ea754d29d66ebcf02 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Fri, 31 Oct 2025 14:36:20 -0700 Subject: [PATCH] Add --json flag support to more bd commands - stats: Added --json flag for programmatic output - show, update: Added --json flag for issue details - close, reopen: Added --json flag for bulk operations - dep (add, remove, tree, cycles): Added --json flags - label (add, remove, list, list-all): Added --json flags - duplicates, merge: Added --json flags Closes bd-4dcd2d09 --- cmd/bd/dep.go | 13 +++++++++++++ cmd/bd/duplicates.go | 2 ++ cmd/bd/label.go | 19 ++++++++++++++----- cmd/bd/merge.go | 2 ++ cmd/bd/ready.go | 4 ++++ cmd/bd/reopen.go | 2 ++ cmd/bd/show.go | 6 ++++++ 7 files changed, 43 insertions(+), 5 deletions(-) diff --git a/cmd/bd/dep.go b/cmd/bd/dep.go index ab2dd63a..a9e44ee9 100644 --- a/cmd/bd/dep.go +++ b/cmd/bd/dep.go @@ -25,6 +25,7 @@ var depAddCmd = &cobra.Command{ Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { depType, _ := cmd.Flags().GetString("type") + jsonOutput, _ := cmd.Flags().GetBool("json") ctx := context.Background() @@ -147,6 +148,7 @@ var depRemoveCmd = &cobra.Command{ Short: "Remove a dependency", Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") ctx := context.Background() // Resolve partial IDs first @@ -238,6 +240,7 @@ var depTreeCmd = &cobra.Command{ Short: "Show dependency tree", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") ctx := context.Background() // Resolve partial ID first @@ -338,6 +341,8 @@ var depCyclesCmd = &cobra.Command{ Use: "cycles", Short: "Detect dependency cycles", Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") + // If daemon is running but doesn't support this command, use direct storage if daemonClient != nil && store == nil { var err error @@ -385,9 +390,17 @@ var depCyclesCmd = &cobra.Command{ func init() { depAddCmd.Flags().StringP("type", "t", "blocks", "Dependency type (blocks|related|parent-child|discovered-from)") + depAddCmd.Flags().Bool("json", false, "Output JSON format") + + depRemoveCmd.Flags().Bool("json", false, "Output JSON format") + depTreeCmd.Flags().Bool("show-all-paths", false, "Show all paths to nodes (no deduplication for diamond dependencies)") depTreeCmd.Flags().IntP("max-depth", "d", 50, "Maximum tree depth to display (safety limit)") depTreeCmd.Flags().Bool("reverse", false, "Show dependent tree (what was discovered from this) instead of dependency tree (what blocks this)") + depTreeCmd.Flags().Bool("json", false, "Output JSON format") + + depCyclesCmd.Flags().Bool("json", false, "Output JSON format") + depCmd.AddCommand(depAddCmd) depCmd.AddCommand(depRemoveCmd) depCmd.AddCommand(depTreeCmd) diff --git a/cmd/bd/duplicates.go b/cmd/bd/duplicates.go index db0665b9..40624523 100644 --- a/cmd/bd/duplicates.go +++ b/cmd/bd/duplicates.go @@ -38,6 +38,7 @@ Example: autoMerge, _ := cmd.Flags().GetBool("auto-merge") dryRun, _ := cmd.Flags().GetBool("dry-run") + jsonOutput, _ := cmd.Flags().GetBool("json") ctx := context.Background() @@ -174,6 +175,7 @@ Example: func init() { duplicatesCmd.Flags().Bool("auto-merge", false, "Automatically merge all duplicates") duplicatesCmd.Flags().Bool("dry-run", false, "Show what would be merged without making changes") + duplicatesCmd.Flags().Bool("json", false, "Output JSON format") rootCmd.AddCommand(duplicatesCmd) } diff --git a/cmd/bd/label.go b/cmd/bd/label.go index e0590e5c..e4aacec3 100644 --- a/cmd/bd/label.go +++ b/cmd/bd/label.go @@ -22,7 +22,7 @@ var labelCmd = &cobra.Command{ } // Helper function to process label operations for multiple issues -func processBatchLabelOperation(issueIDs []string, label string, operation string, +func processBatchLabelOperation(issueIDs []string, label string, operation string, jsonOut bool, daemonFunc func(string, string) error, storeFunc func(context.Context, string, string, string) error) { ctx := context.Background() results := []map[string]interface{}{} @@ -40,7 +40,7 @@ func processBatchLabelOperation(issueIDs []string, label string, operation strin continue } - if jsonOutput { + if jsonOut { results = append(results, map[string]interface{}{ "status": operation, "issue_id": issueID, @@ -62,7 +62,7 @@ func processBatchLabelOperation(issueIDs []string, label string, operation strin markDirtyAndScheduleFlush() } - if jsonOutput && len(results) > 0 { + if jsonOut && len(results) > 0 { outputJSON(results) } } @@ -79,6 +79,7 @@ var labelAddCmd = &cobra.Command{ Short: "Add a label to one or more issues", Args: cobra.MinimumNArgs(2), Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") issueIDs, label := parseLabelArgs(args) // Resolve partial IDs @@ -107,7 +108,7 @@ var labelAddCmd = &cobra.Command{ } issueIDs = resolvedIDs - processBatchLabelOperation(issueIDs, label, "added", + processBatchLabelOperation(issueIDs, label, "added", jsonOutput, func(issueID, lbl string) error { _, err := daemonClient.AddLabel(&rpc.LabelAddArgs{ID: issueID, Label: lbl}) return err @@ -124,6 +125,7 @@ var labelRemoveCmd = &cobra.Command{ Short: "Remove a label from one or more issues", Args: cobra.MinimumNArgs(2), Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") issueIDs, label := parseLabelArgs(args) // Resolve partial IDs @@ -152,7 +154,7 @@ var labelRemoveCmd = &cobra.Command{ } issueIDs = resolvedIDs - processBatchLabelOperation(issueIDs, label, "removed", + processBatchLabelOperation(issueIDs, label, "removed", jsonOutput, func(issueID, lbl string) error { _, err := daemonClient.RemoveLabel(&rpc.LabelRemoveArgs{ID: issueID, Label: lbl}) return err @@ -168,6 +170,7 @@ var labelListCmd = &cobra.Command{ Short: "List labels for an issue", Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") ctx := context.Background() // Resolve partial ID first @@ -242,6 +245,7 @@ var labelListAllCmd = &cobra.Command{ Use: "list-all", Short: "List all unique labels in the database", Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") ctx := context.Background() var issues []*types.Issue @@ -342,6 +346,11 @@ var labelListAllCmd = &cobra.Command{ } func init() { + labelAddCmd.Flags().Bool("json", false, "Output JSON format") + labelRemoveCmd.Flags().Bool("json", false, "Output JSON format") + labelListCmd.Flags().Bool("json", false, "Output JSON format") + labelListAllCmd.Flags().Bool("json", false, "Output JSON format") + labelCmd.AddCommand(labelAddCmd) labelCmd.AddCommand(labelRemoveCmd) labelCmd.AddCommand(labelListCmd) diff --git a/cmd/bd/merge.go b/cmd/bd/merge.go index cecbcd13..8da1a274 100644 --- a/cmd/bd/merge.go +++ b/cmd/bd/merge.go @@ -43,6 +43,7 @@ Example: sourceIDs := args dryRun, _ := cmd.Flags().GetBool("dry-run") + jsonOutput, _ := cmd.Flags().GetBool("json") // Validate merge operation if err := validateMerge(targetID, sourceIDs); err != nil { @@ -96,6 +97,7 @@ Example: func init() { mergeCmd.Flags().String("into", "", "Target issue ID to merge into (required)") mergeCmd.Flags().Bool("dry-run", false, "Validate without making changes") + mergeCmd.Flags().Bool("json", false, "Output JSON format") rootCmd.AddCommand(mergeCmd) } diff --git a/cmd/bd/ready.go b/cmd/bd/ready.go index 2a419053..eabefb5c 100644 --- a/cmd/bd/ready.go +++ b/cmd/bd/ready.go @@ -206,6 +206,8 @@ var statsCmd = &cobra.Command{ Use: "stats", Short: "Show statistics", Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") + // If daemon is running, use RPC if daemonClient != nil { resp, err := daemonClient.Stats() @@ -296,6 +298,8 @@ func init() { readyCmd.Flags().StringP("sort", "s", "hybrid", "Sort policy: hybrid (default), priority, oldest") readyCmd.Flags().Bool("json", false, "Output JSON format") + statsCmd.Flags().Bool("json", false, "Output JSON format") + rootCmd.AddCommand(readyCmd) rootCmd.AddCommand(blockedCmd) rootCmd.AddCommand(statsCmd) diff --git a/cmd/bd/reopen.go b/cmd/bd/reopen.go index f8863171..b9468277 100644 --- a/cmd/bd/reopen.go +++ b/cmd/bd/reopen.go @@ -22,6 +22,7 @@ This is more explicit than 'bd update --status open' and emits a Reopened event. Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { reason, _ := cmd.Flags().GetString("reason") + jsonOutput, _ := cmd.Flags().GetBool("json") ctx := context.Background() @@ -146,5 +147,6 @@ This is more explicit than 'bd update --status open' and emits a Reopened event. func init() { reopenCmd.Flags().StringP("reason", "r", "", "Reason for reopening") + reopenCmd.Flags().Bool("json", false, "Output JSON format") rootCmd.AddCommand(reopenCmd) } diff --git a/cmd/bd/show.go b/cmd/bd/show.go index 0ed25f36..28e96997 100644 --- a/cmd/bd/show.go +++ b/cmd/bd/show.go @@ -20,6 +20,7 @@ var showCmd = &cobra.Command{ Short: "Show issue details", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") ctx := context.Background() // Resolve partial IDs first @@ -329,6 +330,7 @@ var updateCmd = &cobra.Command{ Short: "Update one or more issues", Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { + jsonOutput, _ := cmd.Flags().GetBool("json") updates := make(map[string]interface{}) if cmd.Flags().Changed("status") { @@ -691,6 +693,7 @@ var closeCmd = &cobra.Command{ if reason == "" { reason = "Closed" } + jsonOutput, _ := cmd.Flags().GetBool("json") ctx := context.Background() @@ -776,6 +779,7 @@ var closeCmd = &cobra.Command{ } func init() { + showCmd.Flags().Bool("json", false, "Output JSON format") rootCmd.AddCommand(showCmd) updateCmd.Flags().StringP("status", "s", "", "New status") @@ -789,6 +793,7 @@ func init() { updateCmd.Flags().String("acceptance-criteria", "", "DEPRECATED: use --acceptance") _ = updateCmd.Flags().MarkHidden("acceptance-criteria") updateCmd.Flags().String("external-ref", "", "External reference (e.g., 'gh-9', 'jira-ABC')") + updateCmd.Flags().Bool("json", false, "Output JSON format") rootCmd.AddCommand(updateCmd) editCmd.Flags().Bool("title", false, "Edit the title") @@ -799,5 +804,6 @@ func init() { rootCmd.AddCommand(editCmd) closeCmd.Flags().StringP("reason", "r", "", "Reason for closing") + closeCmd.Flags().Bool("json", false, "Output JSON format") rootCmd.AddCommand(closeCmd) }