@os-eco/overstory-cli 0.6.4 → 0.6.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.
Files changed (117) hide show
  1. package/README.md +61 -61
  2. package/agents/builder.md +16 -16
  3. package/agents/coordinator.md +57 -57
  4. package/agents/issue-reviews.md +71 -0
  5. package/agents/lead.md +43 -42
  6. package/agents/merger.md +15 -15
  7. package/agents/monitor.md +37 -37
  8. package/agents/pr-reviews.md +60 -0
  9. package/agents/prioritize.md +110 -0
  10. package/agents/release.md +56 -0
  11. package/agents/reviewer.md +15 -15
  12. package/agents/scout.md +18 -18
  13. package/agents/supervisor.md +78 -78
  14. package/package.json +1 -1
  15. package/src/agents/checkpoint.test.ts +2 -2
  16. package/src/agents/hooks-deployer.test.ts +59 -25
  17. package/src/agents/hooks-deployer.ts +24 -6
  18. package/src/agents/identity.test.ts +27 -27
  19. package/src/agents/identity.ts +10 -10
  20. package/src/agents/lifecycle.test.ts +6 -6
  21. package/src/agents/lifecycle.ts +2 -2
  22. package/src/agents/overlay.test.ts +14 -14
  23. package/src/agents/overlay.ts +14 -14
  24. package/src/commands/agents.test.ts +5 -5
  25. package/src/commands/agents.ts +10 -9
  26. package/src/commands/clean.test.ts +5 -5
  27. package/src/commands/clean.ts +5 -5
  28. package/src/commands/completions.test.ts +10 -10
  29. package/src/commands/completions.ts +26 -28
  30. package/src/commands/coordinator.test.ts +4 -4
  31. package/src/commands/coordinator.ts +13 -13
  32. package/src/commands/costs.test.ts +45 -45
  33. package/src/commands/costs.ts +1 -1
  34. package/src/commands/dashboard.ts +11 -11
  35. package/src/commands/doctor.ts +4 -4
  36. package/src/commands/errors.ts +1 -1
  37. package/src/commands/feed.ts +1 -1
  38. package/src/commands/group.ts +3 -3
  39. package/src/commands/hooks.test.ts +7 -7
  40. package/src/commands/hooks.ts +7 -7
  41. package/src/commands/init.test.ts +6 -2
  42. package/src/commands/init.ts +19 -19
  43. package/src/commands/inspect.test.ts +16 -16
  44. package/src/commands/inspect.ts +19 -19
  45. package/src/commands/log.test.ts +21 -21
  46. package/src/commands/log.ts +10 -10
  47. package/src/commands/logs.ts +1 -1
  48. package/src/commands/mail.test.ts +7 -7
  49. package/src/commands/mail.ts +28 -11
  50. package/src/commands/merge.test.ts +8 -8
  51. package/src/commands/merge.ts +15 -15
  52. package/src/commands/metrics.test.ts +7 -7
  53. package/src/commands/metrics.ts +3 -3
  54. package/src/commands/monitor.test.ts +5 -5
  55. package/src/commands/monitor.ts +5 -5
  56. package/src/commands/nudge.test.ts +1 -1
  57. package/src/commands/nudge.ts +1 -1
  58. package/src/commands/prime.test.ts +5 -5
  59. package/src/commands/prime.ts +8 -8
  60. package/src/commands/replay.ts +1 -1
  61. package/src/commands/run.test.ts +1 -1
  62. package/src/commands/run.ts +2 -2
  63. package/src/commands/sling.test.ts +89 -7
  64. package/src/commands/sling.ts +109 -18
  65. package/src/commands/spec.test.ts +2 -2
  66. package/src/commands/spec.ts +13 -14
  67. package/src/commands/status.test.ts +99 -3
  68. package/src/commands/status.ts +19 -20
  69. package/src/commands/stop.test.ts +1 -1
  70. package/src/commands/stop.ts +2 -2
  71. package/src/commands/supervisor.test.ts +10 -10
  72. package/src/commands/supervisor.ts +14 -14
  73. package/src/commands/trace.test.ts +7 -7
  74. package/src/commands/trace.ts +10 -10
  75. package/src/commands/watch.ts +5 -5
  76. package/src/commands/worktree.test.ts +208 -32
  77. package/src/commands/worktree.ts +56 -18
  78. package/src/doctor/consistency.test.ts +14 -14
  79. package/src/doctor/dependencies.test.ts +5 -5
  80. package/src/doctor/dependencies.ts +2 -2
  81. package/src/doctor/logs.ts +1 -1
  82. package/src/doctor/merge-queue.test.ts +4 -4
  83. package/src/doctor/structure.test.ts +1 -1
  84. package/src/doctor/structure.ts +1 -1
  85. package/src/doctor/version.test.ts +3 -3
  86. package/src/doctor/version.ts +1 -1
  87. package/src/e2e/init-sling-lifecycle.test.ts +8 -4
  88. package/src/errors.ts +1 -1
  89. package/src/index.ts +13 -11
  90. package/src/mail/broadcast.test.ts +1 -1
  91. package/src/mail/client.test.ts +7 -7
  92. package/src/mail/client.ts +2 -2
  93. package/src/mail/store.test.ts +3 -3
  94. package/src/merge/queue.test.ts +12 -12
  95. package/src/merge/queue.ts +2 -2
  96. package/src/merge/resolver.test.ts +159 -7
  97. package/src/merge/resolver.ts +46 -2
  98. package/src/metrics/store.test.ts +44 -44
  99. package/src/metrics/store.ts +2 -2
  100. package/src/metrics/summary.test.ts +35 -35
  101. package/src/mulch/client.test.ts +1 -1
  102. package/src/mulch/client.ts +1 -1
  103. package/src/sessions/compat.test.ts +3 -3
  104. package/src/sessions/compat.ts +1 -1
  105. package/src/sessions/store.test.ts +4 -4
  106. package/src/sessions/store.ts +2 -2
  107. package/src/types.ts +14 -14
  108. package/src/watchdog/daemon.test.ts +10 -10
  109. package/src/watchdog/daemon.ts +1 -1
  110. package/src/watchdog/health.test.ts +1 -1
  111. package/src/worktree/manager.test.ts +20 -20
  112. package/src/worktree/manager.ts +120 -4
  113. package/src/worktree/tmux.test.ts +8 -3
  114. package/src/worktree/tmux.ts +19 -18
  115. package/templates/CLAUDE.md.tmpl +27 -27
  116. package/templates/hooks.json.tmpl +15 -11
  117. package/templates/overlay.md.tmpl +7 -7
@@ -137,7 +137,7 @@ function makeAgentSession(overrides: Partial<AgentSession> = {}): AgentSession {
137
137
  capability: "builder",
138
138
  worktreePath: join(tempDir, ".overstory", "worktrees", "my-builder"),
139
139
  branchName: "overstory/my-builder/bead-123",
140
- beadId: "bead-123",
140
+ taskId: "bead-123",
141
141
  tmuxSession: "overstory-test-project-my-builder",
142
142
  state: "working",
143
143
  pid: 99999,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * CLI command: overstory stop <agent-name>
2
+ * CLI command: ov stop <agent-name>
3
3
  *
4
4
  * Explicitly terminates a running agent by:
5
5
  * 1. Looking up the agent session by name
@@ -37,7 +37,7 @@ export interface StopDeps {
37
37
  }
38
38
 
39
39
  /**
40
- * Entry point for `overstory stop <agent-name>`.
40
+ * Entry point for `ov stop <agent-name>`.
41
41
  *
42
42
  * @param agentName - Name of the agent to stop
43
43
  * @param opts - Command options
@@ -11,10 +11,10 @@ import { buildSupervisorBeacon, supervisorCommand } from "./supervisor.ts";
11
11
  */
12
12
 
13
13
  describe("buildSupervisorBeacon", () => {
14
- test("contains agent name and beadId from opts", () => {
14
+ test("contains agent name and taskId from opts", () => {
15
15
  const beacon = buildSupervisorBeacon({
16
16
  name: "supervisor-1",
17
- beadId: "task-abc123",
17
+ taskId: "task-abc123",
18
18
  depth: 1,
19
19
  parent: "coordinator",
20
20
  });
@@ -26,7 +26,7 @@ describe("buildSupervisorBeacon", () => {
26
26
  test("contains [OVERSTORY] prefix", () => {
27
27
  const beacon = buildSupervisorBeacon({
28
28
  name: "supervisor-1",
29
- beadId: "task-1",
29
+ taskId: "task-1",
30
30
  depth: 1,
31
31
  parent: "coordinator",
32
32
  });
@@ -37,7 +37,7 @@ describe("buildSupervisorBeacon", () => {
37
37
  test("contains (supervisor) designation", () => {
38
38
  const beacon = buildSupervisorBeacon({
39
39
  name: "supervisor-1",
40
- beadId: "task-1",
40
+ taskId: "task-1",
41
41
  depth: 1,
42
42
  parent: "coordinator",
43
43
  });
@@ -48,7 +48,7 @@ describe("buildSupervisorBeacon", () => {
48
48
  test("contains depth and parent info from opts", () => {
49
49
  const beacon = buildSupervisorBeacon({
50
50
  name: "supervisor-1",
51
- beadId: "task-1",
51
+ taskId: "task-1",
52
52
  depth: 2,
53
53
  parent: "lead-cli",
54
54
  });
@@ -60,7 +60,7 @@ describe("buildSupervisorBeacon", () => {
60
60
  test("contains startup instructions", () => {
61
61
  const beacon = buildSupervisorBeacon({
62
62
  name: "supervisor-1",
63
- beadId: "task-1",
63
+ taskId: "task-1",
64
64
  depth: 1,
65
65
  parent: "coordinator",
66
66
  });
@@ -69,9 +69,9 @@ describe("buildSupervisorBeacon", () => {
69
69
  expect(beacon).toContain("mulch prime");
70
70
 
71
71
  // Should include mail check with agent name
72
- expect(beacon).toContain("overstory mail check --agent supervisor-1");
72
+ expect(beacon).toContain("ov mail check --agent supervisor-1");
73
73
 
74
- // Should include bd show with beadId
74
+ // Should include bd show with taskId
75
75
  expect(beacon).toContain("bd show task-1");
76
76
  });
77
77
 
@@ -79,13 +79,13 @@ describe("buildSupervisorBeacon", () => {
79
79
  const before = new Date();
80
80
  const beacon = buildSupervisorBeacon({
81
81
  name: "supervisor-1",
82
- beadId: "task-1",
82
+ taskId: "task-1",
83
83
  depth: 1,
84
84
  parent: "coordinator",
85
85
  });
86
86
  const after = new Date();
87
87
 
88
- // Extract timestamp from beacon (format: [OVERSTORY] {name} (supervisor) {timestamp} task:{beadId})
88
+ // Extract timestamp from beacon (format: [OVERSTORY] {name} (supervisor) {timestamp} task:{taskId})
89
89
  const timestampMatch = beacon.match(/\(supervisor\)\s+(\S+)\s+task:/);
90
90
  expect(timestampMatch).toBeTruthy();
91
91
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * CLI command: overstory supervisor start|stop|status
2
+ * CLI command: ov supervisor start|stop|status
3
3
  *
4
4
  * Manages per-project supervisor agent lifecycle. The supervisor is a persistent
5
5
  * agent that runs at the project root (NOT in a worktree), assigned to a specific
@@ -36,13 +36,13 @@ import { isRunningAsRoot } from "./sling.ts";
36
36
  * Build the supervisor startup beacon.
37
37
  *
38
38
  * @param opts.name - Supervisor agent name
39
- * @param opts.beadId - Bead task ID
39
+ * @param opts.taskId - Bead task ID
40
40
  * @param opts.depth - Hierarchy depth (default 1)
41
41
  * @param opts.parent - Parent agent name (default "coordinator")
42
42
  */
43
43
  export function buildSupervisorBeacon(opts: {
44
44
  name: string;
45
- beadId: string;
45
+ taskId: string;
46
46
  depth: number;
47
47
  parent: string;
48
48
  trackerCli?: string;
@@ -50,9 +50,9 @@ export function buildSupervisorBeacon(opts: {
50
50
  const cli = opts.trackerCli ?? "bd";
51
51
  const timestamp = new Date().toISOString();
52
52
  const parts = [
53
- `[OVERSTORY] ${opts.name} (supervisor) ${timestamp} task:${opts.beadId}`,
53
+ `[OVERSTORY] ${opts.name} (supervisor) ${timestamp} task:${opts.taskId}`,
54
54
  `Depth: ${opts.depth} | Parent: ${opts.parent} | Role: per-project supervisor`,
55
- `Startup: run mulch prime, check mail (overstory mail check --agent ${opts.name}), read task (${cli} show ${opts.beadId}), then begin supervising`,
55
+ `Startup: run mulch prime, check mail (ov mail check --agent ${opts.name}), read task (${cli} show ${opts.taskId}), then begin supervising`,
56
56
  ];
57
57
  return parts.join(" — ");
58
58
  }
@@ -182,7 +182,7 @@ async function startSupervisor(opts: {
182
182
 
183
183
  const beacon = buildSupervisorBeacon({
184
184
  name: opts.name,
185
- beadId: opts.task,
185
+ taskId: opts.task,
186
186
  depth: opts.depth,
187
187
  parent: opts.parent,
188
188
  trackerCli: trackerCliName(resolvedBackend),
@@ -202,7 +202,7 @@ async function startSupervisor(opts: {
202
202
  capability: "supervisor",
203
203
  worktreePath: projectRoot, // Supervisor uses project root, not a worktree
204
204
  branchName: config.project.canonicalBranch, // Operates on canonical branch
205
- beadId: opts.task,
205
+ taskId: opts.task,
206
206
  tmuxSession,
207
207
  state: "booting",
208
208
  pid,
@@ -222,7 +222,7 @@ async function startSupervisor(opts: {
222
222
  capability: "supervisor",
223
223
  tmuxSession,
224
224
  projectRoot,
225
- beadId: opts.task,
225
+ taskId: opts.task,
226
226
  parent: opts.parent,
227
227
  depth: opts.depth,
228
228
  pid,
@@ -347,7 +347,7 @@ async function statusSupervisor(opts: { name?: string; json: boolean }): Promise
347
347
  agentName: session.agentName,
348
348
  state: session.state,
349
349
  tmuxSession: session.tmuxSession,
350
- beadId: session.beadId,
350
+ taskId: session.taskId,
351
351
  parentAgent: session.parentAgent,
352
352
  depth: session.depth,
353
353
  pid: session.pid,
@@ -362,7 +362,7 @@ async function statusSupervisor(opts: { name?: string; json: boolean }): Promise
362
362
  process.stdout.write(`Supervisor '${opts.name}': ${stateLabel}\n`);
363
363
  process.stdout.write(` Session: ${session.id}\n`);
364
364
  process.stdout.write(` Tmux: ${session.tmuxSession}\n`);
365
- process.stdout.write(` Task: ${session.beadId}\n`);
365
+ process.stdout.write(` Task: ${session.taskId}\n`);
366
366
  process.stdout.write(` Parent: ${session.parentAgent}\n`);
367
367
  process.stdout.write(` Depth: ${session.depth}\n`);
368
368
  process.stdout.write(` PID: ${session.pid}\n`);
@@ -401,7 +401,7 @@ async function statusSupervisor(opts: { name?: string; json: boolean }): Promise
401
401
  ? ("zombie" as const)
402
402
  : session.state,
403
403
  tmuxSession: session.tmuxSession,
404
- beadId: session.beadId,
404
+ taskId: session.taskId,
405
405
  parentAgent: session.parentAgent,
406
406
  depth: session.depth,
407
407
  startedAt: session.startedAt,
@@ -416,7 +416,7 @@ async function statusSupervisor(opts: { name?: string; json: boolean }): Promise
416
416
  for (const status of statuses) {
417
417
  const stateLabel = status.running ? "running" : status.state;
418
418
  process.stdout.write(
419
- ` ${status.agentName}: ${stateLabel} (task: ${status.beadId}, parent: ${status.parentAgent})\n`,
419
+ ` ${status.agentName}: ${stateLabel} (task: ${status.taskId}, parent: ${status.parentAgent})\n`,
420
420
  );
421
421
  }
422
422
  }
@@ -427,7 +427,7 @@ async function statusSupervisor(opts: { name?: string; json: boolean }): Promise
427
427
  }
428
428
 
429
429
  /**
430
- * Create the Commander command for `overstory supervisor`.
430
+ * Create the Commander command for `ov supervisor`.
431
431
  */
432
432
  export function createSupervisorCommand(): Command {
433
433
  const cmd = new Command("supervisor").description("Manage per-project supervisor agents");
@@ -480,7 +480,7 @@ export function createSupervisorCommand(): Command {
480
480
  }
481
481
 
482
482
  /**
483
- * Entry point for `overstory supervisor <subcommand>`.
483
+ * Entry point for `ov supervisor <subcommand>`.
484
484
  */
485
485
  export async function supervisorCommand(args: string[]): Promise<void> {
486
486
  const cmd = createSupervisorCommand();
@@ -509,7 +509,7 @@ describe("traceCommand", () => {
509
509
  // === Target resolution ===
510
510
 
511
511
  describe("target resolution", () => {
512
- test("agent name is used as-is when not a bead ID pattern", async () => {
512
+ test("agent name is used as-is when not a task ID pattern", async () => {
513
513
  const dbPath = join(tempDir, ".overstory", "events.db");
514
514
  const store = createEventStore(dbPath);
515
515
  store.insert(makeEvent({ agentName: "my-custom-agent" }));
@@ -523,8 +523,8 @@ describe("traceCommand", () => {
523
523
  expect(parsed[0]?.agentName).toBe("my-custom-agent");
524
524
  });
525
525
 
526
- test("bead ID pattern is detected and resolved to agent name via SessionStore", async () => {
527
- // Create a session that maps bead ID to agent name
526
+ test("task ID pattern is detected and resolved to agent name via SessionStore", async () => {
527
+ // Create a session that maps task ID to agent name
528
528
  const sessDbPath = join(tempDir, ".overstory", "sessions.db");
529
529
  const sessionStore = createSessionStore(sessDbPath);
530
530
  sessionStore.upsert({
@@ -533,7 +533,7 @@ describe("traceCommand", () => {
533
533
  capability: "builder",
534
534
  worktreePath: "/tmp/wt",
535
535
  branchName: "feat/task",
536
- beadId: "overstory-rj1k",
536
+ taskId: "overstory-rj1k",
537
537
  tmuxSession: "tmux-001",
538
538
  state: "completed",
539
539
  pid: null,
@@ -561,7 +561,7 @@ describe("traceCommand", () => {
561
561
  expect(parsed[0]?.agentName).toBe("builder-for-task");
562
562
  });
563
563
 
564
- test("unresolved bead ID falls back to using bead ID as agent name", async () => {
564
+ test("unresolved task ID falls back to using task ID as agent name", async () => {
565
565
  // Create sessions.db but with no matching bead
566
566
  const sessDbPath = join(tempDir, ".overstory", "sessions.db");
567
567
  const sessionStore = createSessionStore(sessDbPath);
@@ -579,13 +579,13 @@ describe("traceCommand", () => {
579
579
  expect(parsed).toEqual([]);
580
580
  });
581
581
 
582
- test("short agent names without bead pattern are not resolved as bead IDs", async () => {
582
+ test("short agent names without task pattern are not resolved as task IDs", async () => {
583
583
  const dbPath = join(tempDir, ".overstory", "events.db");
584
584
  const store = createEventStore(dbPath);
585
585
  store.insert(makeEvent({ agentName: "scout" }));
586
586
  store.close();
587
587
 
588
- // "scout" does not match bead pattern word-alphanumeric
588
+ // "scout" does not match task pattern word-alphanumeric
589
589
  await traceCommand(["scout", "--json"]);
590
590
  const out = output();
591
591
 
@@ -1,8 +1,8 @@
1
1
  /**
2
- * CLI command: overstory trace <target> [--json] [--since <ts>] [--until <ts>] [--limit <n>]
2
+ * CLI command: ov trace <target> [--json] [--since <ts>] [--until <ts>] [--limit <n>]
3
3
  *
4
4
  * Shows a chronological timeline of events for an agent or bead task.
5
- * Target can be an agent name or a bead ID (resolved to agent name via SessionStore).
5
+ * Target can be an agent name or a task ID (resolved to agent name via SessionStore).
6
6
  */
7
7
 
8
8
  import { join } from "node:path";
@@ -29,10 +29,10 @@ const EVENT_LABELS: Record<EventType, { label: string; color: ColorFn }> = {
29
29
  };
30
30
 
31
31
  /**
32
- * Detect whether a target string looks like a bead ID.
33
- * Bead IDs follow the pattern: word-alphanumeric (e.g., "overstory-rj1k", "myproject-abc1").
32
+ * Detect whether a target string looks like a task ID.
33
+ * Task IDs follow the pattern: word-alphanumeric (e.g., "overstory-rj1k", "myproject-abc1").
34
34
  */
35
- function looksLikeBeadId(target: string): boolean {
35
+ function looksLikeTaskId(target: string): boolean {
36
36
  return /^[a-z][a-z0-9]*-[a-z0-9]{3,}$/i.test(target);
37
37
  }
38
38
 
@@ -220,16 +220,16 @@ async function executeTrace(target: string, opts: TraceOpts): Promise<void> {
220
220
  // Resolve target to agent name
221
221
  let agentName = target;
222
222
 
223
- if (looksLikeBeadId(target)) {
224
- // Try to resolve bead ID to agent name via SessionStore
223
+ if (looksLikeTaskId(target)) {
224
+ // Try to resolve task ID to agent name via SessionStore
225
225
  const { store: sessionStore } = openSessionStore(overstoryDir);
226
226
  try {
227
227
  const allSessions = sessionStore.getAll();
228
- const matchingSession = allSessions.find((s) => s.beadId === target);
228
+ const matchingSession = allSessions.find((s) => s.taskId === target);
229
229
  if (matchingSession) {
230
230
  agentName = matchingSession.agentName;
231
231
  } else {
232
- // No session found for this bead ID; treat it as an agent name anyway
232
+ // No session found for this task ID; treat it as an agent name anyway
233
233
  // (the event query will return empty results if no events match)
234
234
  agentName = target;
235
235
  }
@@ -275,7 +275,7 @@ async function executeTrace(target: string, opts: TraceOpts): Promise<void> {
275
275
  export function createTraceCommand(): Command {
276
276
  return new Command("trace")
277
277
  .description("Chronological event timeline for agent/bead")
278
- .argument("<target>", "Agent name or bead ID")
278
+ .argument("<target>", "Agent name or task ID")
279
279
  .option("--json", "Output as JSON array of StoredEvent objects")
280
280
  .option("--since <timestamp>", "Start time filter (ISO 8601)")
281
281
  .option("--until <timestamp>", "End time filter (ISO 8601)")
@@ -20,12 +20,12 @@ import { isProcessRunning } from "../watchdog/health.ts";
20
20
  function formatCheck(check: HealthCheck): string {
21
21
  const actionIcon =
22
22
  check.action === "terminate"
23
- ? "💀"
23
+ ? "x"
24
24
  : check.action === "escalate"
25
- ? "⚠️"
25
+ ? "!"
26
26
  : check.action === "investigate"
27
- ? "🔍"
28
- : "";
27
+ ? ">"
28
+ : "x";
29
29
  const pidLabel = check.pidAlive === null ? "n/a" : check.pidAlive ? "up" : "down";
30
30
  let line = `${actionIcon} ${check.agentName}: ${check.state} (tmux=${check.tmuxAlive ? "up" : "down"}, pid=${pidLabel})`;
31
31
  if (check.reconciliationNote) {
@@ -84,7 +84,7 @@ async function removePidFile(pidFilePath: string): Promise<void> {
84
84
  */
85
85
  async function resolveOverstoryBin(): Promise<string> {
86
86
  try {
87
- const proc = Bun.spawn(["which", "overstory"], {
87
+ const proc = Bun.spawn(["which", "ov"], {
88
88
  stdout: "pipe",
89
89
  stderr: "pipe",
90
90
  });