Skip to content

Commit 0be06c1

Browse files
that-github-userunknownclaude
authored
Add graceful Ctrl+C shutdown: clean up worktrees on interrupt (#83)
Register SIGINT handler before worktree creation that cleans up all worktrees and branches on Ctrl+C, then exits with code 130. Handler is deregistered after normal run completion. Generated by thinktank (5 agents, Agent #5 recommended — only agent passing tests, +14/-1, focused single-file change). Closes #60 Co-authored-by: unknown <that-github-user@github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4601c0c commit 0be06c1

File tree

1 file changed

+14
-1
lines changed

1 file changed

+14
-1
lines changed

src/commands/run.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { analyzeConvergence, recommend } from "../scoring/convergence.js";
55
import { runTests } from "../scoring/test-runner.js";
66
import type { AgentResult, EnsembleResult, RunOptions } from "../types.js";
77
import { displayApplyInstructions, displayHeader, displayResults } from "../utils/display.js";
8-
import { cleanupBranches, createWorktree } from "../utils/git.js";
8+
import { cleanupBranches, createWorktree, removeWorktree } from "../utils/git.js";
99

1010
export async function run(opts: RunOptions): Promise<void> {
1111
displayHeader(opts.prompt, opts.attempts, opts.model);
@@ -33,6 +33,16 @@ export async function run(opts: RunOptions): Promise<void> {
3333
console.log(" Creating worktrees...");
3434
const worktrees: Array<{ id: number; path: string }> = [];
3535

36+
// Graceful Ctrl+C: clean up all worktrees created so far, then exit.
37+
// Registered before worktree creation so any interrupt is handled.
38+
const handleSigint = () => {
39+
console.log("\n\n Interrupted — cleaning up worktrees...");
40+
Promise.all(worktrees.map(({ path }) => removeWorktree(path).catch(() => {})))
41+
.then(() => cleanupBranches().catch(() => {}))
42+
.then(() => process.exit(130));
43+
};
44+
process.on("SIGINT", handleSigint);
45+
3646
for (let i = 1; i <= opts.attempts; i++) {
3747
const path = await createWorktree(i);
3848
worktrees.push({ id: i, path });
@@ -106,6 +116,9 @@ export async function run(opts: RunOptions): Promise<void> {
106116
// Save result to .thinktank/
107117
await saveResult(result);
108118

119+
// Deregister SIGINT handler — run completed normally, no cleanup needed.
120+
process.removeListener("SIGINT", handleSigint);
121+
109122
// Note: we intentionally do NOT clean up worktrees here so the user
110123
// can inspect them. They get cleaned up on next run.
111124
}

0 commit comments

Comments
 (0)