work-kit-cli 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { readState, findWorktreeRoot, stateDir } from "../state/store.js";
5
+
6
+ export interface CancelResult {
7
+ action: "cancelled" | "error";
8
+ slug?: string;
9
+ branch?: string;
10
+ worktreeRemoved: boolean;
11
+ branchDeleted: boolean;
12
+ message: string;
13
+ }
14
+
15
+ function resolveMainRepoRoot(worktreeRoot: string): string {
16
+ try {
17
+ const output = execFileSync("git", ["worktree", "list", "--porcelain"], {
18
+ cwd: worktreeRoot,
19
+ encoding: "utf-8",
20
+ timeout: 5000,
21
+ });
22
+ const firstLine = output.split("\n").find(l => l.startsWith("worktree "));
23
+ if (firstLine) return firstLine.slice("worktree ".length).trim();
24
+ } catch {
25
+ // fallback
26
+ }
27
+ return worktreeRoot;
28
+ }
29
+
30
+ export function cancelCommand(worktreeRoot?: string): CancelResult {
31
+ const root = worktreeRoot || findWorktreeRoot();
32
+ if (!root) {
33
+ return {
34
+ action: "error",
35
+ worktreeRemoved: false,
36
+ branchDeleted: false,
37
+ message: "No work-kit state found. Nothing to cancel.",
38
+ };
39
+ }
40
+
41
+ const state = readState(root);
42
+
43
+ if (state.status === "completed") {
44
+ return {
45
+ action: "error",
46
+ slug: state.slug,
47
+ worktreeRemoved: false,
48
+ branchDeleted: false,
49
+ message: `${state.slug} is already completed. Nothing to cancel.`,
50
+ };
51
+ }
52
+
53
+ const slug = state.slug;
54
+ const branch = state.branch;
55
+ const mainRoot = resolveMainRepoRoot(root);
56
+ const isWorktree = path.resolve(root) !== path.resolve(mainRoot);
57
+
58
+ let worktreeRemoved = false;
59
+ let branchDeleted = false;
60
+
61
+ // Remove .work-kit/ state directory
62
+ const stDir = stateDir(root);
63
+ if (fs.existsSync(stDir)) {
64
+ fs.rmSync(stDir, { recursive: true, force: true });
65
+ }
66
+
67
+ // Remove the worktree (if we're in one)
68
+ if (isWorktree) {
69
+ try {
70
+ execFileSync("git", ["worktree", "remove", root, "--force"], {
71
+ cwd: mainRoot,
72
+ encoding: "utf-8",
73
+ timeout: 10000,
74
+ });
75
+ worktreeRemoved = true;
76
+ } catch {
77
+ // Worktree removal failed — may need manual cleanup
78
+ }
79
+ }
80
+
81
+ // Delete the feature branch
82
+ if (branch) {
83
+ try {
84
+ execFileSync("git", ["branch", "-D", branch], {
85
+ cwd: mainRoot,
86
+ encoding: "utf-8",
87
+ timeout: 5000,
88
+ });
89
+ branchDeleted = true;
90
+ } catch {
91
+ // Branch may not exist or may be checked out elsewhere
92
+ }
93
+ }
94
+
95
+ return {
96
+ action: "cancelled",
97
+ slug,
98
+ branch,
99
+ worktreeRemoved,
100
+ branchDeleted,
101
+ message: `Cancelled ${slug}.${worktreeRemoved ? " Worktree removed." : ""}${branchDeleted ? " Branch deleted." : ""}`,
102
+ };
103
+ }
package/cli/src/index.ts CHANGED
@@ -16,6 +16,7 @@ import { completionsCommand } from "./commands/completions.js";
16
16
  import { observeCommand } from "./commands/observe.js";
17
17
  import { uninstallCommand } from "./commands/uninstall.js";
18
18
  import { bootstrapCommand } from "./commands/bootstrap.js";
19
+ import { cancelCommand } from "./commands/cancel.js";
19
20
  import { bold, green, yellow, red } from "./utils/colors.js";
20
21
  import type { Classification, PhaseName } from "./state/schema.js";
21
22
 
@@ -269,4 +270,21 @@ program
269
270
  }
270
271
  });
271
272
 
273
+ // ── cancel ──────────────────────────────────────────────────────────
274
+
275
+ program
276
+ .command("cancel")
277
+ .description("Cancel the active work-kit, remove worktree and branch")
278
+ .option("--worktree-root <path>", "Override worktree root")
279
+ .action((opts) => {
280
+ try {
281
+ const result = cancelCommand(opts.worktreeRoot);
282
+ console.log(JSON.stringify(result, null, 2));
283
+ process.exit(result.action === "error" ? 1 : 0);
284
+ } catch (e: any) {
285
+ console.error(JSON.stringify({ action: "error", message: e.message }));
286
+ process.exit(1);
287
+ }
288
+ });
289
+
272
290
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "work-kit-cli",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Structured development workflow for Claude Code. Two modes, 6 phases, 27 sub-stages.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: cancel-kit
3
+ description: "Cancel the active work-kit session. Removes state, worktree, and feature branch."
4
+ user-invocable: true
5
+ allowed-tools: Bash, Read
6
+ ---
7
+
8
+ # Cancel Kit
9
+
10
+ Cancels the active work-kit session and cleans up all artifacts.
11
+
12
+ ## What it does
13
+
14
+ 1. Finds the active work-kit session (via `bootstrap`)
15
+ 2. Confirms with the user before proceeding
16
+ 3. Runs `npx work-kit-cli cancel` to:
17
+ - Remove `.work-kit/` state directory
18
+ - Remove the git worktree
19
+ - Delete the feature branch
20
+ 4. Reports what was cleaned up
21
+
22
+ ## Instructions
23
+
24
+ 1. Run `npx work-kit-cli bootstrap` to detect the active session
25
+ 2. If no active session: tell the user there's nothing to cancel
26
+ 3. If active: show the user what will be cancelled:
27
+ - Slug and branch name
28
+ - Current phase and sub-stage
29
+ - Any uncommitted work in the worktree will be lost
30
+ 4. **Ask the user to confirm** — do not proceed without explicit confirmation
31
+ 5. `cd` into the worktree directory
32
+ 6. Run `npx work-kit-cli cancel`
33
+ 7. `cd` back to the main repo root
34
+ 8. Report the result to the user