From ff37d2213a2d2073875af37f318792712985f5ba Mon Sep 17 00:00:00 2001 From: furiosa Date: Fri, 2 Jan 2026 13:41:53 -0800 Subject: [PATCH] feat(done): Add --exit flag for session self-termination (gt-lynar) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add --exit flag to `gt done` that terminates the Claude session immediately after submitting the MR to the merge queue. This prevents polecats from sitting idle (and wasting money) while waiting for the Witness to kill them. Changes: - Rename existing --exit flag to --status (for exit type) - Add new --exit boolean flag for session self-termination - Update docs and help text to reflect new flag names Usage: gt done --exit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- docs/escalation.md | 2 +- internal/cmd/done.go | 33 +++++++++++++++++++++++---------- internal/cmd/handoff.go | 2 +- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/escalation.md b/docs/escalation.md index 2944ca4e..54a17ad4 100644 --- a/docs/escalation.md +++ b/docs/escalation.md @@ -225,7 +225,7 @@ EOF gt escalate --type decision --issue $ISSUE "Caching approach needs decision" # 3. Exit cleanly -gt done --exit ESCALATED +gt done --status ESCALATED ``` ## Mayor Startup Check diff --git a/internal/cmd/done.go b/internal/cmd/done.go index 5bfd7c9d..a44724e7 100644 --- a/internal/cmd/done.go +++ b/internal/cmd/done.go @@ -24,24 +24,27 @@ This is a convenience command for polecats that: 1. Submits the current branch to the merge queue 2. Auto-detects issue ID from branch name 3. Notifies the Witness with the exit outcome +4. Optionally exits the Claude session (--exit flag) -Exit types: +Exit statuses: COMPLETED - Work done, MR submitted (default) ESCALATED - Hit blocker, needs human intervention DEFERRED - Work paused, issue still open Examples: - gt done # Submit branch, notify COMPLETED - gt done --issue gt-abc # Explicit issue ID - gt done --exit ESCALATED # Signal blocker, skip MR - gt done --exit DEFERRED # Pause work, skip MR`, + gt done # Submit branch, notify COMPLETED + gt done --exit # Submit and exit Claude session + gt done --issue gt-abc # Explicit issue ID + gt done --status ESCALATED # Signal blocker, skip MR + gt done --status DEFERRED # Pause work, skip MR`, RunE: runDone, } var ( doneIssue string donePriority int - doneExit string + doneStatus string + doneExit bool ) // Valid exit types for gt done @@ -54,16 +57,17 @@ const ( func init() { doneCmd.Flags().StringVar(&doneIssue, "issue", "", "Source issue ID (default: parse from branch name)") doneCmd.Flags().IntVarP(&donePriority, "priority", "p", -1, "Override priority (0-4, default: inherit from issue)") - doneCmd.Flags().StringVar(&doneExit, "exit", ExitCompleted, "Exit type: COMPLETED, ESCALATED, or DEFERRED") + doneCmd.Flags().StringVar(&doneStatus, "status", ExitCompleted, "Exit status: COMPLETED, ESCALATED, or DEFERRED") + doneCmd.Flags().BoolVar(&doneExit, "exit", false, "Exit Claude session after MR submission (self-terminate)") rootCmd.AddCommand(doneCmd) } func runDone(cmd *cobra.Command, args []string) error { - // Validate exit type - exitType := strings.ToUpper(doneExit) + // Validate exit status + exitType := strings.ToUpper(doneStatus) if exitType != ExitCompleted && exitType != ExitEscalated && exitType != ExitDeferred { - return fmt.Errorf("invalid exit type '%s': must be COMPLETED, ESCALATED, or DEFERRED", doneExit) + return fmt.Errorf("invalid exit status '%s': must be COMPLETED, ESCALATED, or DEFERRED", doneStatus) } // Find workspace @@ -277,6 +281,15 @@ func runDone(cmd *cobra.Command, args []string) error { // Update agent bead state (ZFC: self-report completion) updateAgentStateOnDone(cwd, townRoot, exitType, issueID) + // Handle session self-termination if requested + if doneExit { + fmt.Println() + fmt.Printf("%s Session self-terminating (--exit flag)\n", style.Bold.Render("→")) + fmt.Printf(" Witness will handle worktree cleanup.\n") + fmt.Printf(" Goodbye!\n") + os.Exit(0) + } + return nil } diff --git a/internal/cmd/handoff.go b/internal/cmd/handoff.go index b1597dbf..bc67e507 100644 --- a/internal/cmd/handoff.go +++ b/internal/cmd/handoff.go @@ -25,7 +25,7 @@ var handoffCmd = &cobra.Command{ This is the canonical way to end any agent session. It handles all roles: - Mayor, Crew, Witness, Refinery, Deacon: Respawns with fresh Claude instance - - Polecats: Calls 'gt done --exit DEFERRED' (Witness handles lifecycle) + - Polecats: Calls 'gt done --status DEFERRED' (Witness handles lifecycle) When run without arguments, hands off the current session. When given a bead ID (gt-xxx, hq-xxx), hooks that work first, then restarts.