package orch import ( "fmt" "ai-workflow-skill/packages/coord-core/protocol" "ai-workflow-skill/packages/coord-core/store" "github.com/spf13/cobra" ) type cleanupOptions struct { runID string taskID string attemptNo int allCompleted bool force bool } func newCleanupCmd(root *rootOptions) *cobra.Command { opts := &cleanupOptions{} cmd := &cobra.Command{ Use: "cleanup", Short: "Remove completed or abandoned attempt worktrees", Long: helpLong( "Use cleanup to remove worktrees that belong to completed, abandoned, or explicitly forced attempt cleanup.", "cleanup only affects attempts that actually have worktree-backed execution state; analysis-mode attempts have no worktree to remove.", "Pass --task when you want to clean one task's latest cleanup candidate.", "Pass --task plus --attempt when you want one exact attempt.", "Pass --all-completed when you want a run-wide cleanup sweep.", ), Example: ` orch --db .agents/coord.db cleanup --run blog_mvp_001 --task T1 orch --db .agents/coord.db cleanup --run blog_mvp_001 --task T1 --attempt 2 orch --db .agents/coord.db cleanup --run blog_mvp_001 --all-completed`, RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() sqlDB, err := openOrchDB(ctx, root.dbPath) if err != nil { return err } defer sqlDB.Close() s := store.NewOrchStore(sqlDB) candidates, err := s.ListCleanupCandidates(ctx, store.CleanupInput{ RunID: opts.runID, TaskID: opts.taskID, AttemptNo: opts.attemptNo, AllCompleted: opts.allCompleted, Force: opts.force, }) if err != nil { return err } records := make([]store.CleanupRecord, 0, len(candidates)) for _, candidate := range candidates { if err := cleanupAttemptWorktree(ctx, candidate.Attempt, opts.force); err != nil { return err } records = append(records, store.CleanupRecord{Attempt: candidate.Attempt}) } cleaned, err := s.MarkAttemptsCleaned(ctx, records) if err != nil { return err } resp := protocol.Success{ OK: true, Command: "cleanup", Data: map[string]any{ "cleaned": cleaned, }, } if root.json { return protocol.WriteJSON(cmd.OutOrStdout(), resp) } _, err = fmt.Fprintf(cmd.OutOrStdout(), "cleaned %d worktrees\n", len(cleaned)) return err }, } cmd.Flags().StringVar(&opts.runID, "run", "", "Run ID") cmd.Flags().StringVar(&opts.taskID, "task", "", "Limit cleanup candidates to one task") cmd.Flags().IntVar(&opts.attemptNo, "attempt", 0, "Limit cleanup to one specific attempt number") cmd.Flags().BoolVar(&opts.allCompleted, "all-completed", false, "Clean every completed or abandoned worktree in the run") cmd.Flags().BoolVar(&opts.force, "force", false, "Force cleanup even for non-terminal worktrees") _ = cmd.MarkFlagRequired("run") return cmd }