@os-eco/overstory-cli 0.6.1 → 0.6.5

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.
Files changed (110) hide show
  1. package/README.md +8 -7
  2. package/package.json +12 -4
  3. package/src/agents/checkpoint.test.ts +2 -2
  4. package/src/agents/hooks-deployer.test.ts +131 -16
  5. package/src/agents/hooks-deployer.ts +33 -1
  6. package/src/agents/identity.test.ts +27 -27
  7. package/src/agents/identity.ts +10 -10
  8. package/src/agents/lifecycle.test.ts +6 -6
  9. package/src/agents/lifecycle.ts +2 -2
  10. package/src/agents/manifest.test.ts +86 -0
  11. package/src/agents/overlay.test.ts +9 -9
  12. package/src/agents/overlay.ts +4 -4
  13. package/src/commands/agents.test.ts +8 -8
  14. package/src/commands/agents.ts +62 -91
  15. package/src/commands/clean.test.ts +36 -51
  16. package/src/commands/clean.ts +28 -49
  17. package/src/commands/completions.ts +14 -0
  18. package/src/commands/coordinator.test.ts +133 -26
  19. package/src/commands/coordinator.ts +101 -64
  20. package/src/commands/costs.test.ts +47 -47
  21. package/src/commands/costs.ts +96 -75
  22. package/src/commands/dashboard.test.ts +2 -2
  23. package/src/commands/dashboard.ts +75 -95
  24. package/src/commands/doctor.test.ts +2 -2
  25. package/src/commands/doctor.ts +92 -79
  26. package/src/commands/errors.test.ts +2 -2
  27. package/src/commands/errors.ts +56 -50
  28. package/src/commands/feed.test.ts +2 -2
  29. package/src/commands/feed.ts +86 -83
  30. package/src/commands/group.ts +167 -177
  31. package/src/commands/hooks.test.ts +2 -2
  32. package/src/commands/hooks.ts +52 -42
  33. package/src/commands/init.test.ts +19 -19
  34. package/src/commands/init.ts +7 -16
  35. package/src/commands/inspect.test.ts +18 -18
  36. package/src/commands/inspect.ts +55 -58
  37. package/src/commands/log.test.ts +26 -31
  38. package/src/commands/log.ts +97 -91
  39. package/src/commands/logs.test.ts +1 -1
  40. package/src/commands/logs.ts +101 -104
  41. package/src/commands/mail.test.ts +5 -5
  42. package/src/commands/mail.ts +157 -169
  43. package/src/commands/merge.test.ts +28 -66
  44. package/src/commands/merge.ts +21 -51
  45. package/src/commands/metrics.test.ts +8 -8
  46. package/src/commands/metrics.ts +34 -35
  47. package/src/commands/monitor.test.ts +3 -3
  48. package/src/commands/monitor.ts +57 -62
  49. package/src/commands/nudge.test.ts +1 -1
  50. package/src/commands/nudge.ts +41 -89
  51. package/src/commands/prime.test.ts +19 -51
  52. package/src/commands/prime.ts +13 -50
  53. package/src/commands/replay.test.ts +2 -2
  54. package/src/commands/replay.ts +79 -86
  55. package/src/commands/run.test.ts +1 -1
  56. package/src/commands/run.ts +97 -77
  57. package/src/commands/sling.test.ts +201 -5
  58. package/src/commands/sling.ts +37 -64
  59. package/src/commands/spec.test.ts +14 -40
  60. package/src/commands/spec.ts +32 -101
  61. package/src/commands/status.test.ts +97 -1
  62. package/src/commands/status.ts +63 -58
  63. package/src/commands/stop.test.ts +22 -40
  64. package/src/commands/stop.ts +18 -33
  65. package/src/commands/supervisor.test.ts +12 -14
  66. package/src/commands/supervisor.ts +144 -165
  67. package/src/commands/trace.test.ts +15 -15
  68. package/src/commands/trace.ts +59 -82
  69. package/src/commands/watch.test.ts +2 -2
  70. package/src/commands/watch.ts +38 -45
  71. package/src/commands/worktree.test.ts +213 -37
  72. package/src/commands/worktree.ts +110 -55
  73. package/src/config.test.ts +96 -0
  74. package/src/doctor/consistency.test.ts +14 -14
  75. package/src/doctor/databases.test.ts +22 -2
  76. package/src/doctor/databases.ts +16 -0
  77. package/src/doctor/dependencies.test.ts +55 -1
  78. package/src/doctor/dependencies.ts +113 -18
  79. package/src/doctor/merge-queue.test.ts +4 -4
  80. package/src/e2e/init-sling-lifecycle.test.ts +8 -8
  81. package/src/errors.ts +1 -1
  82. package/src/index.ts +223 -213
  83. package/src/logging/color.test.ts +74 -91
  84. package/src/logging/color.ts +52 -46
  85. package/src/logging/reporter.test.ts +10 -10
  86. package/src/logging/reporter.ts +6 -5
  87. package/src/mail/broadcast.test.ts +1 -1
  88. package/src/mail/client.test.ts +6 -6
  89. package/src/mail/store.test.ts +3 -3
  90. package/src/merge/queue.test.ts +73 -7
  91. package/src/merge/queue.ts +17 -2
  92. package/src/merge/resolver.test.ts +159 -7
  93. package/src/merge/resolver.ts +46 -2
  94. package/src/metrics/store.test.ts +44 -44
  95. package/src/metrics/store.ts +2 -2
  96. package/src/metrics/summary.test.ts +35 -35
  97. package/src/mulch/client.test.ts +1 -1
  98. package/src/schema-consistency.test.ts +239 -0
  99. package/src/sessions/compat.test.ts +3 -3
  100. package/src/sessions/compat.ts +2 -2
  101. package/src/sessions/store.test.ts +41 -4
  102. package/src/sessions/store.ts +13 -2
  103. package/src/types.ts +14 -14
  104. package/src/watchdog/daemon.test.ts +10 -10
  105. package/src/watchdog/daemon.ts +1 -1
  106. package/src/watchdog/health.test.ts +1 -1
  107. package/src/worktree/manager.test.ts +20 -20
  108. package/src/worktree/manager.ts +120 -4
  109. package/src/worktree/tmux.test.ts +98 -9
  110. package/src/worktree/tmux.ts +18 -0
@@ -71,26 +71,11 @@ afterEach(async () => {
71
71
  await cleanupTempDir(tempDir);
72
72
  });
73
73
 
74
- // === help ===
75
-
76
- describe("help", () => {
77
- test("--help shows usage", async () => {
78
- await cleanCommand(["--help"]);
79
- expect(stdoutOutput).toContain("overstory clean");
80
- expect(stdoutOutput).toContain("--all");
81
- });
82
-
83
- test("-h shows usage", async () => {
84
- await cleanCommand(["-h"]);
85
- expect(stdoutOutput).toContain("overstory clean");
86
- });
87
- });
88
-
89
74
  // === validation ===
90
75
 
91
76
  describe("validation", () => {
92
77
  test("no flags throws ValidationError", async () => {
93
- await expect(cleanCommand([])).rejects.toThrow("No cleanup targets specified");
78
+ await expect(cleanCommand({})).rejects.toThrow("No cleanup targets specified");
94
79
  });
95
80
  });
96
81
 
@@ -116,7 +101,7 @@ describe("--all", () => {
116
101
  // Verify DB exists
117
102
  expect(await Bun.file(mailDbPath).exists()).toBe(true);
118
103
 
119
- await cleanCommand(["--all"]);
104
+ await cleanCommand({ all: true });
120
105
 
121
106
  // DB should be gone
122
107
  expect(await Bun.file(mailDbPath).exists()).toBe(false);
@@ -128,7 +113,7 @@ describe("--all", () => {
128
113
  const store = createMetricsStore(metricsDbPath);
129
114
  store.recordSession({
130
115
  agentName: "test-agent",
131
- beadId: "task-1",
116
+ taskId: "task-1",
132
117
  capability: "builder",
133
118
  startedAt: new Date().toISOString(),
134
119
  completedAt: null,
@@ -148,7 +133,7 @@ describe("--all", () => {
148
133
 
149
134
  expect(await Bun.file(metricsDbPath).exists()).toBe(true);
150
135
 
151
- await cleanCommand(["--all"]);
136
+ await cleanCommand({ all: true });
152
137
 
153
138
  expect(await Bun.file(metricsDbPath).exists()).toBe(false);
154
139
  expect(stdoutOutput).toContain("Wiped metrics.db");
@@ -163,7 +148,7 @@ describe("--all", () => {
163
148
  capability: "builder",
164
149
  worktreePath: "/tmp/wt",
165
150
  branchName: "overstory/test/task",
166
- beadId: "task-1",
151
+ taskId: "task-1",
167
152
  tmuxSession: "overstory-test-agent",
168
153
  state: "completed",
169
154
  pid: 12345,
@@ -180,7 +165,7 @@ describe("--all", () => {
180
165
  const sessionsDbPath = join(overstoryDir, "sessions.db");
181
166
  expect(await Bun.file(sessionsDbPath).exists()).toBe(true);
182
167
 
183
- await cleanCommand(["--all"]);
168
+ await cleanCommand({ all: true });
184
169
 
185
170
  expect(await Bun.file(sessionsDbPath).exists()).toBe(false);
186
171
  expect(stdoutOutput).toContain("Wiped sessions.db");
@@ -192,13 +177,13 @@ describe("--all", () => {
192
177
  const queue = createMergeQueue(queuePath);
193
178
  queue.enqueue({
194
179
  branchName: "test-branch",
195
- beadId: "beads-test",
180
+ taskId: "beads-test",
196
181
  agentName: "test",
197
182
  filesModified: ["src/test.ts"],
198
183
  });
199
184
  queue.close();
200
185
 
201
- await cleanCommand(["--all"]);
186
+ await cleanCommand({ all: true });
202
187
 
203
188
  expect(await Bun.file(queuePath).exists()).toBe(false);
204
189
  expect(stdoutOutput).toContain("Wiped merge-queue.db");
@@ -209,7 +194,7 @@ describe("--all", () => {
209
194
  await mkdir(join(logsDir, "agent-a", "2026-01-01"), { recursive: true });
210
195
  await writeFile(join(logsDir, "agent-a", "2026-01-01", "session.log"), "log data");
211
196
 
212
- await cleanCommand(["--all"]);
197
+ await cleanCommand({ all: true });
213
198
 
214
199
  const entries = await readdir(logsDir);
215
200
  expect(entries).toHaveLength(0);
@@ -221,7 +206,7 @@ describe("--all", () => {
221
206
  await mkdir(join(agentsDir, "test-agent"), { recursive: true });
222
207
  await writeFile(join(agentsDir, "test-agent", "identity.yaml"), "name: test-agent");
223
208
 
224
- await cleanCommand(["--all"]);
209
+ await cleanCommand({ all: true });
225
210
 
226
211
  const entries = await readdir(agentsDir);
227
212
  expect(entries).toHaveLength(0);
@@ -232,7 +217,7 @@ describe("--all", () => {
232
217
  const specsDir = join(overstoryDir, "specs");
233
218
  await writeFile(join(specsDir, "task-123.md"), "# Spec");
234
219
 
235
- await cleanCommand(["--all"]);
220
+ await cleanCommand({ all: true });
236
221
 
237
222
  const entries = await readdir(specsDir);
238
223
  expect(entries).toHaveLength(0);
@@ -243,7 +228,7 @@ describe("--all", () => {
243
228
  const nudgePath = join(overstoryDir, "nudge-state.json");
244
229
  await Bun.write(nudgePath, "{}");
245
230
 
246
- await cleanCommand(["--all"]);
231
+ await cleanCommand({ all: true });
247
232
 
248
233
  expect(await Bun.file(nudgePath).exists()).toBe(false);
249
234
  expect(stdoutOutput).toContain("Cleared nudge-state.json");
@@ -253,7 +238,7 @@ describe("--all", () => {
253
238
  const currentRunPath = join(overstoryDir, "current-run.txt");
254
239
  await Bun.write(currentRunPath, "run-2026-02-13T10-00-00-000Z");
255
240
 
256
- await cleanCommand(["--all"]);
241
+ await cleanCommand({ all: true });
257
242
 
258
243
  expect(await Bun.file(currentRunPath).exists()).toBe(false);
259
244
  expect(stdoutOutput).toContain("Cleared current-run.txt");
@@ -261,7 +246,7 @@ describe("--all", () => {
261
246
 
262
247
  test("handles missing current-run.txt gracefully", async () => {
263
248
  // current-run.txt does not exist — should not error
264
- await cleanCommand(["--all"]);
249
+ await cleanCommand({ all: true });
265
250
  expect(stdoutOutput).not.toContain("Cleared current-run.txt");
266
251
  });
267
252
  });
@@ -288,7 +273,7 @@ describe("individual flags", () => {
288
273
  const sessionsPath = join(overstoryDir, "sessions.json");
289
274
  await Bun.write(sessionsPath, '[{"id":"s1"}]\n');
290
275
 
291
- await cleanCommand(["--mail"]);
276
+ await cleanCommand({ mail: true });
292
277
 
293
278
  // Mail gone
294
279
  expect(await Bun.file(mailDbPath).exists()).toBe(false);
@@ -307,7 +292,7 @@ describe("individual flags", () => {
307
292
  capability: "builder",
308
293
  worktreePath: "/tmp/wt",
309
294
  branchName: "overstory/test/task",
310
- beadId: "task-1",
295
+ taskId: "task-1",
311
296
  tmuxSession: "overstory-test-agent",
312
297
  state: "completed",
313
298
  pid: 12345,
@@ -324,7 +309,7 @@ describe("individual flags", () => {
324
309
  // Create a spec file that should survive
325
310
  await writeFile(join(overstoryDir, "specs", "task.md"), "spec");
326
311
 
327
- await cleanCommand(["--sessions"]);
312
+ await cleanCommand({ sessions: true });
328
313
 
329
314
  // sessions.db should be gone
330
315
  expect(await Bun.file(sessionsDbPath).exists()).toBe(false);
@@ -341,7 +326,7 @@ describe("individual flags", () => {
341
326
 
342
327
  await writeFile(join(overstoryDir, "specs", "task.md"), "spec");
343
328
 
344
- await cleanCommand(["--logs"]);
329
+ await cleanCommand({ logs: true });
345
330
 
346
331
  const logEntries = await readdir(logsDir);
347
332
  expect(logEntries).toHaveLength(0);
@@ -356,7 +341,7 @@ describe("individual flags", () => {
356
341
 
357
342
  describe("idempotent", () => {
358
343
  test("running --all when nothing exists does not error", async () => {
359
- await cleanCommand(["--all"]);
344
+ await cleanCommand({ all: true });
360
345
  expect(stdoutOutput).toContain("Nothing to clean");
361
346
  });
362
347
 
@@ -366,9 +351,9 @@ describe("idempotent", () => {
366
351
  const store = createMailStore(mailDbPath);
367
352
  store.close();
368
353
 
369
- await cleanCommand(["--all"]);
354
+ await cleanCommand({ all: true });
370
355
  stdoutOutput = "";
371
- await cleanCommand(["--all"]);
356
+ await cleanCommand({ all: true });
372
357
  expect(stdoutOutput).toContain("Nothing to clean");
373
358
  });
374
359
  });
@@ -391,7 +376,7 @@ describe("JSON output", () => {
391
376
  });
392
377
  store.close();
393
378
 
394
- await cleanCommand(["--all", "--json"]);
379
+ await cleanCommand({ all: true, json: true });
395
380
 
396
381
  const result = JSON.parse(stdoutOutput);
397
382
  expect(result).toHaveProperty("tmuxKilled");
@@ -402,7 +387,7 @@ describe("JSON output", () => {
402
387
  });
403
388
 
404
389
  test("--json includes sessionEndEventsLogged field", async () => {
405
- await cleanCommand(["--all", "--json"]);
390
+ await cleanCommand({ all: true, json: true });
406
391
  const result = JSON.parse(stdoutOutput);
407
392
  expect(result).toHaveProperty("sessionEndEventsLogged");
408
393
  });
@@ -411,7 +396,7 @@ describe("JSON output", () => {
411
396
  const currentRunPath = join(overstoryDir, "current-run.txt");
412
397
  await Bun.write(currentRunPath, "run-2026-02-13T10-00-00-000Z");
413
398
 
414
- await cleanCommand(["--all", "--json"]);
399
+ await cleanCommand({ all: true, json: true });
415
400
  const result = JSON.parse(stdoutOutput);
416
401
  expect(result).toHaveProperty("currentRunCleared");
417
402
  expect(result.currentRunCleared).toBe(true);
@@ -428,7 +413,7 @@ describe("synthetic session-end events", () => {
428
413
  capability: "builder",
429
414
  worktreePath: "/tmp/wt",
430
415
  branchName: "overstory/test-builder/task-1",
431
- beadId: "task-1",
416
+ taskId: "task-1",
432
417
  tmuxSession: "overstory-test-builder",
433
418
  state: "working",
434
419
  pid: 12345,
@@ -449,7 +434,7 @@ describe("synthetic session-end events", () => {
449
434
  const sessions = [makeSession({ agentName: "builder-a", state: "working" })];
450
435
  await Bun.write(sessionsPath, JSON.stringify(sessions));
451
436
 
452
- await cleanCommand(["--all"]);
437
+ await cleanCommand({ all: true });
453
438
 
454
439
  // Verify event was written to events.db
455
440
  const eventsDbPath = join(overstoryDir, "events.db");
@@ -478,7 +463,7 @@ describe("synthetic session-end events", () => {
478
463
  ];
479
464
  await Bun.write(sessionsPath, JSON.stringify(sessions));
480
465
 
481
- await cleanCommand(["--all"]);
466
+ await cleanCommand({ all: true });
482
467
 
483
468
  const eventsDbPath = join(overstoryDir, "events.db");
484
469
  const eventStore = createEventStore(eventsDbPath);
@@ -501,7 +486,7 @@ describe("synthetic session-end events", () => {
501
486
  ];
502
487
  await Bun.write(sessionsPath, JSON.stringify(sessions));
503
488
 
504
- await cleanCommand(["--all"]);
489
+ await cleanCommand({ all: true });
505
490
 
506
491
  // events.db may not even be created if there are no events to log
507
492
  const eventsDbPath = join(overstoryDir, "events.db");
@@ -520,7 +505,7 @@ describe("synthetic session-end events", () => {
520
505
  const sessions = [makeSession({ agentName: "wt-agent", state: "working" })];
521
506
  await Bun.write(sessionsPath, JSON.stringify(sessions));
522
507
 
523
- await cleanCommand(["--worktrees"]);
508
+ await cleanCommand({ worktrees: true });
524
509
 
525
510
  const eventsDbPath = join(overstoryDir, "events.db");
526
511
  const eventStore = createEventStore(eventsDbPath);
@@ -543,7 +528,7 @@ describe("synthetic session-end events", () => {
543
528
  ];
544
529
  await Bun.write(sessionsPath, JSON.stringify(sessions));
545
530
 
546
- await cleanCommand(["--all"]);
531
+ await cleanCommand({ all: true });
547
532
 
548
533
  const eventsDbPath = join(overstoryDir, "events.db");
549
534
  const eventStore = createEventStore(eventsDbPath);
@@ -558,7 +543,7 @@ describe("synthetic session-end events", () => {
558
543
 
559
544
  test("handles missing sessions.json gracefully", async () => {
560
545
  // No sessions.json file — should not error
561
- await cleanCommand(["--all"]);
546
+ await cleanCommand({ all: true });
562
547
  // Just verify it didn't crash
563
548
  expect(stdoutOutput).toBeDefined();
564
549
  });
@@ -580,7 +565,7 @@ describe("mulch health checks", () => {
580
565
  `{"id":"mx-1","type":"convention","description":"Test record 1","recorded_at":"2026-01-01T00:00:00Z"}\n`,
581
566
  );
582
567
 
583
- await cleanCommand(["--all"]);
568
+ await cleanCommand({ all: true });
584
569
 
585
570
  // Mulch health checks should have run (might show warnings or might be clean)
586
571
  // The output should not error, and if there are no issues, it's fine
@@ -589,7 +574,7 @@ describe("mulch health checks", () => {
589
574
 
590
575
  test("handles missing .mulch directory gracefully", async () => {
591
576
  // No .mulch directory — should not error
592
- await cleanCommand(["--all"]);
577
+ await cleanCommand({ all: true });
593
578
  expect(stdoutOutput).toBeDefined();
594
579
  });
595
580
 
@@ -606,7 +591,7 @@ describe("mulch health checks", () => {
606
591
  `{"id":"mx-1","type":"convention","description":"Test","recorded_at":"2026-01-01T00:00:00Z"}\n`,
607
592
  );
608
593
 
609
- await cleanCommand(["--all", "--json"]);
594
+ await cleanCommand({ all: true, json: true });
610
595
 
611
596
  const result = JSON.parse(stdoutOutput);
612
597
  expect(result).toHaveProperty("mulchHealth");
@@ -632,7 +617,7 @@ describe("mulch health checks", () => {
632
617
  const store = createMailStore(mailDbPath);
633
618
  store.close();
634
619
 
635
- await cleanCommand(["--mail", "--json"]);
620
+ await cleanCommand({ mail: true, json: true });
636
621
 
637
622
  const result = JSON.parse(stdoutOutput);
638
623
  // mulchHealth should be null because we didn't use --all
@@ -661,7 +646,7 @@ describe("mulch health checks", () => {
661
646
  return; // Skip this test if mulch setup failed
662
647
  }
663
648
 
664
- await cleanCommand(["--all"]);
649
+ await cleanCommand({ all: true });
665
650
 
666
651
  // Should show warning about domain near limit (if mulch status worked)
667
652
  // The exact output depends on whether mulch CLI is available in the test environment
@@ -31,8 +31,17 @@ import type { AgentSession, MulchDoctorResult, MulchPruneResult, MulchStatus } f
31
31
  import { listWorktrees, removeWorktree } from "../worktree/manager.ts";
32
32
  import { killSession, listSessions } from "../worktree/tmux.ts";
33
33
 
34
- function hasFlag(args: string[], flag: string): boolean {
35
- return args.includes(flag);
34
+ export interface CleanOptions {
35
+ all?: boolean;
36
+ mail?: boolean;
37
+ sessions?: boolean;
38
+ metrics?: boolean;
39
+ logs?: boolean;
40
+ worktrees?: boolean;
41
+ branches?: boolean;
42
+ agents?: boolean;
43
+ specs?: boolean;
44
+ json?: boolean;
36
45
  }
37
46
 
38
47
  /**
@@ -384,53 +393,23 @@ async function checkMulchHealth(repoRoot: string): Promise<{
384
393
  }
385
394
  }
386
395
 
387
- const CLEAN_HELP = `overstory clean — Wipe runtime state (nuclear cleanup)
388
-
389
- Usage: overstory clean [flags]
390
-
391
- Flags:
392
- --all Wipe everything (nuclear option)
393
- --mail Delete mail.db (all messages)
394
- --sessions Wipe sessions.db
395
- --metrics Delete metrics.db
396
- --logs Remove all agent logs
397
- --worktrees Remove all worktrees + kill tmux sessions
398
- --branches Delete all overstory/* branch refs
399
- --agents Remove agent identity files
400
- --specs Remove task spec files
401
-
402
- Options:
403
- --json Output as JSON
404
- --help, -h Show this help
405
-
406
- When --all is passed, ALL of the above are executed in safe order:
407
- 0. Run mulch health checks (informational, non-destructive):
408
- - Check domains approaching governance limits (warn threshold: 400 records)
409
- - Run mulch prune --dry-run (report stale record counts)
410
- - Run mulch doctor (report health issues)
411
- 1. Kill all overstory tmux sessions (processes first)
412
- 2. Remove all worktrees
413
- 3. Delete orphaned branch refs
414
- 4. Wipe mail.db, metrics.db, sessions.db, merge-queue.db
415
- 5. Clear logs, agents, specs, nudge state`;
416
-
417
- export async function cleanCommand(args: string[]): Promise<void> {
418
- if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
419
- process.stdout.write(`${CLEAN_HELP}\n`);
420
- return;
421
- }
422
-
423
- const json = hasFlag(args, "--json");
424
- const all = hasFlag(args, "--all");
425
-
426
- const doWorktrees = all || hasFlag(args, "--worktrees");
427
- const doBranches = all || hasFlag(args, "--branches");
428
- const doMail = all || hasFlag(args, "--mail");
429
- const doSessions = all || hasFlag(args, "--sessions");
430
- const doMetrics = all || hasFlag(args, "--metrics");
431
- const doLogs = all || hasFlag(args, "--logs");
432
- const doAgents = all || hasFlag(args, "--agents");
433
- const doSpecs = all || hasFlag(args, "--specs");
396
+ /**
397
+ * Entry point for `overstory clean [flags]`.
398
+ *
399
+ * @param opts - Command options
400
+ */
401
+ export async function cleanCommand(opts: CleanOptions): Promise<void> {
402
+ const json = opts.json ?? false;
403
+ const all = opts.all ?? false;
404
+
405
+ const doWorktrees = all || (opts.worktrees ?? false);
406
+ const doBranches = all || (opts.branches ?? false);
407
+ const doMail = all || (opts.mail ?? false);
408
+ const doSessions = all || (opts.sessions ?? false);
409
+ const doMetrics = all || (opts.metrics ?? false);
410
+ const doLogs = all || (opts.logs ?? false);
411
+ const doAgents = all || (opts.agents ?? false);
412
+ const doSpecs = all || (opts.specs ?? false);
434
413
 
435
414
  const anySelected =
436
415
  doWorktrees || doBranches || doMail || doSessions || doMetrics || doLogs || doAgents || doSpecs;
@@ -4,6 +4,8 @@
4
4
  * Generates completion scripts for bash, zsh, and fish shells.
5
5
  */
6
6
 
7
+ import { Command } from "commander";
8
+
7
9
  interface FlagDef {
8
10
  name: string;
9
11
  desc: string;
@@ -856,6 +858,18 @@ export function generateFish(): string {
856
858
  return lines.join("\n");
857
859
  }
858
860
 
861
+ /**
862
+ * Create the Commander command for `overstory completions`.
863
+ */
864
+ export function createCompletionsCommand(): Command {
865
+ return new Command("completions")
866
+ .description("Generate shell completions")
867
+ .argument("<shell>", "Shell to generate completions for (bash, zsh, fish)")
868
+ .action((shell: string) => {
869
+ completionsCommand([shell]);
870
+ });
871
+ }
872
+
859
873
  export function completionsCommand(args: string[]): void {
860
874
  const shell = args[0];
861
875