Consolidate CLI commands to reduce top-level surface area
- migrate-* commands → subcommands of `bd migrate` - relate/unrelate → subcommands of `bd dep` - daemons subcommands → available under `bd daemon` - comment alias → hidden with deprecation warning All old commands still work with deprecation warnings for backwards compatibility. Reduces visible top-level commands from ~73 to 66. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -203,21 +203,15 @@ Examples:
|
||||
},
|
||||
}
|
||||
|
||||
// commentCmd is a top-level alias for commentsAddCmd
|
||||
// commentCmd is a hidden top-level alias for commentsAddCmd (backwards compat)
|
||||
var commentCmd = &cobra.Command{
|
||||
Use: "comment [issue-id] [text]",
|
||||
GroupID: "issues",
|
||||
Short: "Add a comment to an issue (alias for 'comments add')",
|
||||
Long: `Add a comment to an issue. This is a convenient alias for 'bd comments add'.
|
||||
|
||||
Examples:
|
||||
# Add a comment
|
||||
bd comment bd-123 "Working on this now"
|
||||
|
||||
# Add a comment from a file
|
||||
bd comment bd-123 -f notes.txt`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: commentsAddCmd.Run,
|
||||
Use: "comment [issue-id] [text]",
|
||||
Short: "Add a comment to an issue (alias for 'comments add')",
|
||||
Long: `Add a comment to an issue. This is an alias for 'bd comments add'.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: commentsAddCmd.Run,
|
||||
Hidden: true,
|
||||
Deprecated: "use 'bd comments add' instead",
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -604,14 +604,26 @@ stale sockets, version mismatches, and unresponsive daemons.`,
|
||||
},
|
||||
}
|
||||
func init() {
|
||||
rootCmd.AddCommand(daemonsCmd)
|
||||
// Add subcommands
|
||||
// Add multi-daemon subcommands to daemonCmd (primary location)
|
||||
daemonCmd.AddCommand(daemonsListCmd)
|
||||
daemonCmd.AddCommand(daemonsHealthCmd)
|
||||
daemonCmd.AddCommand(daemonsStopCmd)
|
||||
daemonCmd.AddCommand(daemonsLogsCmd)
|
||||
daemonCmd.AddCommand(daemonsKillallCmd)
|
||||
daemonCmd.AddCommand(daemonsRestartCmd)
|
||||
|
||||
// Also add to daemonsCmd for backwards compatibility
|
||||
// Make daemonsCmd a hidden alias that shows deprecation
|
||||
daemonsCmd.Hidden = true
|
||||
daemonsCmd.Deprecated = "use 'bd daemon <subcommand>' instead (e.g., 'bd daemon list')"
|
||||
daemonsCmd.AddCommand(daemonsListCmd)
|
||||
daemonsCmd.AddCommand(daemonsHealthCmd)
|
||||
daemonsCmd.AddCommand(daemonsStopCmd)
|
||||
daemonsCmd.AddCommand(daemonsLogsCmd)
|
||||
daemonsCmd.AddCommand(daemonsKillallCmd)
|
||||
daemonsCmd.AddCommand(daemonsRestartCmd)
|
||||
rootCmd.AddCommand(daemonsCmd)
|
||||
|
||||
// Flags for list command
|
||||
daemonsListCmd.Flags().StringSlice("search", nil, "Directories to search for daemons (default: home, /tmp, cwd)")
|
||||
daemonsListCmd.Flags().Bool("no-cleanup", false, "Skip auto-cleanup of stale sockets")
|
||||
|
||||
@@ -23,25 +23,27 @@ import (
|
||||
var migrateCmd = &cobra.Command{
|
||||
Use: "migrate",
|
||||
GroupID: "maint",
|
||||
Short: "Migrate database to current version",
|
||||
Long: `Detect and migrate database files to the current version.
|
||||
Short: "Database migration commands",
|
||||
Long: `Database migration and data transformation commands.
|
||||
|
||||
This command:
|
||||
Without subcommand, detects and migrates database schema to current version:
|
||||
- Finds all .db files in .beads/
|
||||
- Checks schema versions
|
||||
- Migrates old databases to beads.db
|
||||
- Updates schema version metadata
|
||||
- Migrates sequential IDs to hash-based IDs (with --to-hash-ids)
|
||||
- Enables separate branch workflow (with --to-separate-branch)
|
||||
- Removes stale databases (with confirmation)`,
|
||||
- Removes stale databases (with confirmation)
|
||||
|
||||
Subcommands:
|
||||
hash-ids Migrate sequential IDs to hash-based IDs (legacy)
|
||||
issues Move issues between repositories
|
||||
sync Set up sync.branch workflow for multi-clone setups
|
||||
tombstones Convert deletions.jsonl to inline tombstones`,
|
||||
Run: func(cmd *cobra.Command, _ []string) {
|
||||
autoYes, _ := cmd.Flags().GetBool("yes")
|
||||
cleanup, _ := cmd.Flags().GetBool("cleanup")
|
||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
||||
updateRepoID, _ := cmd.Flags().GetBool("update-repo-id")
|
||||
toHashIDs, _ := cmd.Flags().GetBool("to-hash-ids")
|
||||
inspect, _ := cmd.Flags().GetBool("inspect")
|
||||
toSeparateBranch, _ := cmd.Flags().GetString("to-separate-branch")
|
||||
|
||||
// Block writes in readonly mode (migration modifies data, --inspect is read-only)
|
||||
if !dryRun && !inspect {
|
||||
@@ -60,12 +62,6 @@ This command:
|
||||
return
|
||||
}
|
||||
|
||||
// Handle --to-separate-branch
|
||||
if toSeparateBranch != "" {
|
||||
handleToSeparateBranch(toSeparateBranch, dryRun)
|
||||
return
|
||||
}
|
||||
|
||||
// Find .beads directory
|
||||
beadsDir := beads.FindBeadsDir()
|
||||
if beadsDir == "" {
|
||||
@@ -376,92 +372,6 @@ This command:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Migrate to hash IDs if requested
|
||||
if toHashIDs {
|
||||
if !jsonOutput {
|
||||
fmt.Println("\n→ Migrating to hash-based IDs...")
|
||||
}
|
||||
|
||||
store, err := sqlite.New(rootCtx, targetPath)
|
||||
if err != nil {
|
||||
if jsonOutput {
|
||||
outputJSON(map[string]interface{}{
|
||||
"error": "hash_migration_failed",
|
||||
"message": err.Error(),
|
||||
})
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error: failed to open database: %v\n", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx := rootCtx
|
||||
issues, err := store.SearchIssues(ctx, "", types.IssueFilter{})
|
||||
if err != nil {
|
||||
_ = store.Close()
|
||||
if jsonOutput {
|
||||
outputJSON(map[string]interface{}{
|
||||
"error": "hash_migration_failed",
|
||||
"message": err.Error(),
|
||||
})
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error: failed to list issues: %v\n", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(issues) > 0 && !isHashID(issues[0].ID) {
|
||||
// Create backup
|
||||
if !dryRun {
|
||||
backupPath := strings.TrimSuffix(targetPath, ".db") + ".backup-pre-hash-" + time.Now().Format("20060102-150405") + ".db"
|
||||
if err := copyFile(targetPath, backupPath); err != nil {
|
||||
_ = store.Close()
|
||||
if jsonOutput {
|
||||
outputJSON(map[string]interface{}{
|
||||
"error": "backup_failed",
|
||||
"message": err.Error(),
|
||||
})
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error: failed to create backup: %v\n", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
if !jsonOutput {
|
||||
fmt.Printf("%s\n", ui.RenderPass(fmt.Sprintf("✓ Created backup: %s", filepath.Base(backupPath))))
|
||||
}
|
||||
}
|
||||
|
||||
mapping, err := migrateToHashIDs(ctx, store, issues, dryRun)
|
||||
_ = store.Close()
|
||||
|
||||
if err != nil {
|
||||
if jsonOutput {
|
||||
outputJSON(map[string]interface{}{
|
||||
"error": "hash_migration_failed",
|
||||
"message": err.Error(),
|
||||
})
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Error: hash ID migration failed: %v\n", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !jsonOutput {
|
||||
if dryRun {
|
||||
fmt.Printf("\nWould migrate %d issues to hash-based IDs\n", len(mapping))
|
||||
} else {
|
||||
fmt.Printf("%s\n", ui.RenderPass(fmt.Sprintf("✓ Migrated %d issues to hash-based IDs", len(mapping))))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_ = store.Close()
|
||||
if !jsonOutput {
|
||||
fmt.Println("Database already uses hash-based IDs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save updated config
|
||||
if !dryRun {
|
||||
if err := cfg.Save(beadsDir); err != nil {
|
||||
@@ -1063,9 +973,7 @@ func init() {
|
||||
migrateCmd.Flags().Bool("cleanup", false, "Remove old database files after migration")
|
||||
migrateCmd.Flags().Bool("dry-run", false, "Show what would be done without making changes")
|
||||
migrateCmd.Flags().Bool("update-repo-id", false, "Update repository ID (use after changing git remote)")
|
||||
migrateCmd.Flags().Bool("to-hash-ids", false, "Migrate sequential IDs to hash-based IDs")
|
||||
migrateCmd.Flags().Bool("inspect", false, "Show migration plan and database state for AI agent analysis")
|
||||
migrateCmd.Flags().String("to-separate-branch", "", "Enable separate branch workflow (e.g., 'beads-metadata')")
|
||||
migrateCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output migration statistics in JSON format")
|
||||
rootCmd.AddCommand(migrateCmd)
|
||||
}
|
||||
|
||||
@@ -23,8 +23,7 @@ import (
|
||||
|
||||
// TODO: Consider integrating into 'bd doctor' migration detection
|
||||
var migrateHashIDsCmd = &cobra.Command{
|
||||
Use: "migrate-hash-ids",
|
||||
GroupID: "maint",
|
||||
Use: "hash-ids",
|
||||
Short: "Migrate sequential IDs to hash-based IDs (legacy)",
|
||||
Long: `Migrate database from sequential IDs (bd-1, bd-2) to hash-based IDs (bd-a3f8e9a2).
|
||||
|
||||
@@ -425,5 +424,12 @@ func copyFile(src, dst string) error {
|
||||
|
||||
func init() {
|
||||
migrateHashIDsCmd.Flags().Bool("dry-run", false, "Show what would be done without making changes")
|
||||
rootCmd.AddCommand(migrateHashIDsCmd)
|
||||
migrateCmd.AddCommand(migrateHashIDsCmd)
|
||||
|
||||
// Backwards compatibility alias at root level (hidden)
|
||||
migrateHashIDsAliasCmd := *migrateHashIDsCmd
|
||||
migrateHashIDsAliasCmd.Use = "migrate-hash-ids"
|
||||
migrateHashIDsAliasCmd.Hidden = true
|
||||
migrateHashIDsAliasCmd.Deprecated = "use 'bd migrate hash-ids' instead"
|
||||
rootCmd.AddCommand(&migrateHashIDsAliasCmd)
|
||||
}
|
||||
|
||||
@@ -14,8 +14,7 @@ import (
|
||||
|
||||
// TODO: Consider integrating into 'bd doctor' migration detection
|
||||
var migrateIssuesCmd = &cobra.Command{
|
||||
Use: "migrate-issues",
|
||||
GroupID: "maint",
|
||||
Use: "issues",
|
||||
Short: "Move issues between repositories",
|
||||
Long: `Move issues from one source repository to another with filtering and dependency preservation.
|
||||
|
||||
@@ -692,7 +691,7 @@ func loadIDsFromFile(path string) ([]string, error) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(migrateIssuesCmd)
|
||||
migrateCmd.AddCommand(migrateIssuesCmd)
|
||||
|
||||
migrateIssuesCmd.Flags().String("from", "", "Source repository (required)")
|
||||
migrateIssuesCmd.Flags().String("to", "", "Destination repository (required)")
|
||||
@@ -710,4 +709,11 @@ func init() {
|
||||
|
||||
_ = migrateIssuesCmd.MarkFlagRequired("from") // Only fails if flag missing (caught in tests)
|
||||
_ = migrateIssuesCmd.MarkFlagRequired("to") // Only fails if flag missing (caught in tests)
|
||||
|
||||
// Backwards compatibility alias at root level (hidden)
|
||||
migrateIssuesAliasCmd := *migrateIssuesCmd
|
||||
migrateIssuesAliasCmd.Use = "migrate-issues"
|
||||
migrateIssuesAliasCmd.Hidden = true
|
||||
migrateIssuesAliasCmd.Deprecated = "use 'bd migrate issues' instead"
|
||||
rootCmd.AddCommand(&migrateIssuesAliasCmd)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,7 @@ import (
|
||||
|
||||
// TODO: Consider integrating into 'bd doctor' migration detection
|
||||
var migrateSyncCmd = &cobra.Command{
|
||||
Use: "migrate-sync <branch-name>",
|
||||
GroupID: "maint",
|
||||
Use: "sync <branch-name>",
|
||||
Short: "Migrate to sync.branch workflow for multi-clone setups",
|
||||
Long: `Migrate to using a dedicated sync branch for beads data.
|
||||
|
||||
@@ -60,7 +59,14 @@ Examples:
|
||||
func init() {
|
||||
migrateSyncCmd.Flags().Bool("dry-run", false, "Preview migration without making changes")
|
||||
migrateSyncCmd.Flags().Bool("force", false, "Force migration even if already configured")
|
||||
rootCmd.AddCommand(migrateSyncCmd)
|
||||
migrateCmd.AddCommand(migrateSyncCmd)
|
||||
|
||||
// Backwards compatibility alias at root level (hidden)
|
||||
migrateSyncAliasCmd := *migrateSyncCmd
|
||||
migrateSyncAliasCmd.Use = "migrate-sync"
|
||||
migrateSyncAliasCmd.Hidden = true
|
||||
migrateSyncAliasCmd.Deprecated = "use 'bd migrate sync' instead"
|
||||
rootCmd.AddCommand(&migrateSyncAliasCmd)
|
||||
}
|
||||
|
||||
func runMigrateSync(ctx context.Context, branchName string, dryRun, force bool) error {
|
||||
|
||||
@@ -71,8 +71,7 @@ func loadLegacyDeletionsCmd(path string) (map[string]legacyDeletionRecordCmd, []
|
||||
|
||||
// TODO: Consider integrating into 'bd doctor' migration detection
|
||||
var migrateTombstonesCmd = &cobra.Command{
|
||||
Use: "migrate-tombstones",
|
||||
GroupID: "maint",
|
||||
Use: "tombstones",
|
||||
Short: "Convert deletions.jsonl entries to inline tombstones",
|
||||
Long: `Migrate legacy deletions.jsonl entries to inline tombstones in issues.jsonl.
|
||||
|
||||
@@ -344,5 +343,12 @@ func init() {
|
||||
migrateTombstonesCmd.Flags().Bool("dry-run", false, "Preview changes without modifying files")
|
||||
migrateTombstonesCmd.Flags().Bool("verbose", false, "Show detailed progress")
|
||||
migrateTombstonesCmd.Flags().BoolVar(&jsonOutput, "json", false, "Output in JSON format")
|
||||
rootCmd.AddCommand(migrateTombstonesCmd)
|
||||
migrateCmd.AddCommand(migrateTombstonesCmd)
|
||||
|
||||
// Backwards compatibility alias at root level (hidden)
|
||||
migrateTombstonesAliasCmd := *migrateTombstonesCmd
|
||||
migrateTombstonesAliasCmd.Use = "migrate-tombstones"
|
||||
migrateTombstonesAliasCmd.Hidden = true
|
||||
migrateTombstonesAliasCmd.Deprecated = "use 'bd migrate tombstones' instead"
|
||||
rootCmd.AddCommand(&migrateTombstonesAliasCmd)
|
||||
}
|
||||
|
||||
@@ -13,9 +13,8 @@ import (
|
||||
)
|
||||
|
||||
var relateCmd = &cobra.Command{
|
||||
Use: "relate <id1> <id2>",
|
||||
GroupID: "deps",
|
||||
Short: "Create a bidirectional relates_to link between issues",
|
||||
Use: "relate <id1> <id2>",
|
||||
Short: "Create a bidirectional relates_to link between issues",
|
||||
Long: `Create a loose 'see also' relationship between two issues.
|
||||
|
||||
The relates_to link is bidirectional - both issues will reference each other.
|
||||
@@ -29,9 +28,8 @@ Examples:
|
||||
}
|
||||
|
||||
var unrelateCmd = &cobra.Command{
|
||||
Use: "unrelate <id1> <id2>",
|
||||
GroupID: "deps",
|
||||
Short: "Remove a relates_to link between issues",
|
||||
Use: "unrelate <id1> <id2>",
|
||||
Short: "Remove a relates_to link between issues",
|
||||
Long: `Remove a relates_to relationship between two issues.
|
||||
|
||||
Removes the link in both directions.
|
||||
@@ -43,8 +41,20 @@ Example:
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(relateCmd)
|
||||
rootCmd.AddCommand(unrelateCmd)
|
||||
// Add as subcommands of dep
|
||||
depCmd.AddCommand(relateCmd)
|
||||
depCmd.AddCommand(unrelateCmd)
|
||||
|
||||
// Backwards compatibility aliases at root level (hidden)
|
||||
relateAliasCmd := *relateCmd
|
||||
relateAliasCmd.Hidden = true
|
||||
relateAliasCmd.Deprecated = "use 'bd dep relate' instead"
|
||||
rootCmd.AddCommand(&relateAliasCmd)
|
||||
|
||||
unrelateAliasCmd := *unrelateCmd
|
||||
unrelateAliasCmd.Hidden = true
|
||||
unrelateAliasCmd.Deprecated = "use 'bd dep unrelate' instead"
|
||||
rootCmd.AddCommand(&unrelateAliasCmd)
|
||||
}
|
||||
|
||||
func runRelate(cmd *cobra.Command, args []string) error {
|
||||
|
||||
Reference in New Issue
Block a user