@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
@@ -58,7 +58,7 @@ describe("createWorktree", () => {
58
58
  baseDir: worktreesDir,
59
59
  agentName: "auth-login",
60
60
  baseBranch: defaultBranch,
61
- beadId: "bead-abc123",
61
+ taskId: "bead-abc123",
62
62
  });
63
63
 
64
64
  expect(result.path).toBe(join(worktreesDir, "auth-login"));
@@ -71,7 +71,7 @@ describe("createWorktree", () => {
71
71
  baseDir: worktreesDir,
72
72
  agentName: "auth-login",
73
73
  baseBranch: defaultBranch,
74
- beadId: "bead-abc123",
74
+ taskId: "bead-abc123",
75
75
  });
76
76
 
77
77
  expect(existsSync(result.path)).toBe(true);
@@ -85,7 +85,7 @@ describe("createWorktree", () => {
85
85
  baseDir: worktreesDir,
86
86
  agentName: "auth-login",
87
87
  baseBranch: defaultBranch,
88
- beadId: "bead-abc123",
88
+ taskId: "bead-abc123",
89
89
  });
90
90
 
91
91
  const branchList = await git(repoDir, ["branch", "--list"]);
@@ -98,7 +98,7 @@ describe("createWorktree", () => {
98
98
  baseDir: worktreesDir,
99
99
  agentName: "auth-login",
100
100
  baseBranch: defaultBranch,
101
- beadId: "bead-abc123",
101
+ taskId: "bead-abc123",
102
102
  });
103
103
 
104
104
  await expect(
@@ -107,7 +107,7 @@ describe("createWorktree", () => {
107
107
  baseDir: worktreesDir,
108
108
  agentName: "auth-login",
109
109
  baseBranch: defaultBranch,
110
- beadId: "bead-abc123",
110
+ taskId: "bead-abc123",
111
111
  }),
112
112
  ).rejects.toThrow(WorktreeError);
113
113
  });
@@ -119,7 +119,7 @@ describe("createWorktree", () => {
119
119
  baseDir: worktreesDir,
120
120
  agentName: "auth-login",
121
121
  baseBranch: defaultBranch,
122
- beadId: "bead-abc123",
122
+ taskId: "bead-abc123",
123
123
  });
124
124
 
125
125
  try {
@@ -128,7 +128,7 @@ describe("createWorktree", () => {
128
128
  baseDir: worktreesDir,
129
129
  agentName: "auth-login",
130
130
  baseBranch: defaultBranch,
131
- beadId: "bead-abc123",
131
+ taskId: "bead-abc123",
132
132
  });
133
133
  // Should not reach here
134
134
  expect(true).toBe(false);
@@ -174,7 +174,7 @@ describe("listWorktrees", () => {
174
174
  baseDir: worktreesDir,
175
175
  agentName: "auth-login",
176
176
  baseBranch: defaultBranch,
177
- beadId: "bead-abc",
177
+ taskId: "bead-abc",
178
178
  });
179
179
 
180
180
  await createWorktree({
@@ -182,7 +182,7 @@ describe("listWorktrees", () => {
182
182
  baseDir: worktreesDir,
183
183
  agentName: "data-sync",
184
184
  baseBranch: defaultBranch,
185
- beadId: "bead-xyz",
185
+ taskId: "bead-xyz",
186
186
  });
187
187
 
188
188
  const entries = await listWorktrees(repoDir);
@@ -206,7 +206,7 @@ describe("listWorktrees", () => {
206
206
  baseDir: worktreesDir,
207
207
  agentName: "feature-worker",
208
208
  baseBranch: defaultBranch,
209
- beadId: "bead-123",
209
+ taskId: "bead-123",
210
210
  });
211
211
 
212
212
  const entries = await listWorktrees(repoDir);
@@ -223,7 +223,7 @@ describe("listWorktrees", () => {
223
223
  baseDir: worktreesDir,
224
224
  agentName: "auth-login",
225
225
  baseBranch: defaultBranch,
226
- beadId: "bead-abc",
226
+ taskId: "bead-abc",
227
227
  });
228
228
 
229
229
  const entries = await listWorktrees(repoDir);
@@ -266,7 +266,7 @@ describe("isBranchMerged", () => {
266
266
  baseDir: worktreesDir,
267
267
  agentName: "feature-agent",
268
268
  baseBranch: defaultBranch,
269
- beadId: "bead-merged",
269
+ taskId: "bead-merged",
270
270
  });
271
271
 
272
272
  // Add a commit to the feature branch
@@ -285,7 +285,7 @@ describe("isBranchMerged", () => {
285
285
  baseDir: worktreesDir,
286
286
  agentName: "feature-agent",
287
287
  baseBranch: defaultBranch,
288
- beadId: "bead-unmerged",
288
+ taskId: "bead-unmerged",
289
289
  });
290
290
 
291
291
  // Add a commit to the feature branch (not merged)
@@ -302,7 +302,7 @@ describe("isBranchMerged", () => {
302
302
  baseDir: worktreesDir,
303
303
  agentName: "feature-agent",
304
304
  baseBranch: defaultBranch,
305
- beadId: "bead-same",
305
+ taskId: "bead-same",
306
306
  });
307
307
 
308
308
  // The branch was created from defaultBranch with no additional commits,
@@ -334,7 +334,7 @@ describe("removeWorktree", () => {
334
334
  baseDir: worktreesDir,
335
335
  agentName: "auth-login",
336
336
  baseBranch: defaultBranch,
337
- beadId: "bead-abc",
337
+ taskId: "bead-abc",
338
338
  });
339
339
 
340
340
  expect(existsSync(wtPath)).toBe(true);
@@ -350,7 +350,7 @@ describe("removeWorktree", () => {
350
350
  baseDir: worktreesDir,
351
351
  agentName: "auth-login",
352
352
  baseBranch: defaultBranch,
353
- beadId: "bead-abc",
353
+ taskId: "bead-abc",
354
354
  });
355
355
 
356
356
  await removeWorktree(repoDir, wtPath);
@@ -365,7 +365,7 @@ describe("removeWorktree", () => {
365
365
  baseDir: worktreesDir,
366
366
  agentName: "auth-login",
367
367
  baseBranch: defaultBranch,
368
- beadId: "bead-abc",
368
+ taskId: "bead-abc",
369
369
  });
370
370
 
371
371
  await removeWorktree(repoDir, wtPath);
@@ -381,7 +381,7 @@ describe("removeWorktree", () => {
381
381
  baseDir: worktreesDir,
382
382
  agentName: "auth-login",
383
383
  baseBranch: defaultBranch,
384
- beadId: "bead-abc",
384
+ taskId: "bead-abc",
385
385
  });
386
386
 
387
387
  // Create an untracked file in the worktree
@@ -400,7 +400,7 @@ describe("removeWorktree", () => {
400
400
  baseDir: worktreesDir,
401
401
  agentName: "auth-login",
402
402
  baseBranch: defaultBranch,
403
- beadId: "bead-abc",
403
+ taskId: "bead-abc",
404
404
  });
405
405
 
406
406
  // Add a commit in the worktree so the branch diverges (making it "unmerged")
@@ -419,7 +419,7 @@ describe("removeWorktree", () => {
419
419
  baseDir: worktreesDir,
420
420
  agentName: "auth-login",
421
421
  baseBranch: defaultBranch,
422
- beadId: "bead-abc",
422
+ taskId: "bead-abc",
423
423
  });
424
424
 
425
425
  // Add a commit to make the branch unmerged
@@ -1,3 +1,4 @@
1
+ import { unlink } from "node:fs/promises";
1
2
  import { join } from "node:path";
2
3
  import { WorktreeError } from "../errors.ts";
3
4
 
@@ -38,7 +39,7 @@ async function runGit(
38
39
  * Create a new git worktree for an agent.
39
40
  *
40
41
  * Creates a worktree at `{baseDir}/{agentName}` with a new branch
41
- * named `overstory/{agentName}/{beadId}` based on `baseBranch`.
42
+ * named `overstory/{agentName}/{taskId}` based on `baseBranch`.
42
43
  *
43
44
  * @returns The absolute worktree path and branch name.
44
45
  */
@@ -47,12 +48,12 @@ export async function createWorktree(options: {
47
48
  baseDir: string;
48
49
  agentName: string;
49
50
  baseBranch: string;
50
- beadId: string;
51
+ taskId: string;
51
52
  }): Promise<{ path: string; branch: string }> {
52
- const { repoRoot, baseDir, agentName, baseBranch, beadId } = options;
53
+ const { repoRoot, baseDir, agentName, baseBranch, taskId } = options;
53
54
 
54
55
  const worktreePath = join(baseDir, agentName);
55
- const branchName = `overstory/${agentName}/${beadId}`;
56
+ const branchName = `overstory/${agentName}/${taskId}`;
56
57
 
57
58
  await runGit(repoRoot, ["worktree", "add", "-b", branchName, worktreePath, baseBranch], {
58
59
  worktreePath,
@@ -196,3 +197,118 @@ export async function removeWorktree(
196
197
  }
197
198
  }
198
199
  }
200
+
201
+ /**
202
+ * Preserve .seeds/ changes from a branch into the canonical branch.
203
+ *
204
+ * Lead agent branches are never merged via the normal merge pipeline, so
205
+ * any .seeds/ issue files they create would be lost when the worktree is
206
+ * cleaned. This function extracts only the .seeds/ diff from the branch
207
+ * and applies it to the canonical branch via a patch.
208
+ *
209
+ * @returns `{ preserved: true }` if changes were found and committed,
210
+ * `{ preserved: false }` if there were no .seeds/ changes,
211
+ * `{ preserved: false, error: "..." }` if something went wrong.
212
+ */
213
+ export async function preserveSeedsChanges(
214
+ repoRoot: string,
215
+ branch: string,
216
+ canonicalBranch: string,
217
+ agentName: string,
218
+ ): Promise<{ preserved: boolean; error?: string }> {
219
+ // Step 1: Get the .seeds/ diff between canonical and the branch (three-dot diff).
220
+ // Three-dot diff shows changes introduced on branch since it diverged from canonicalBranch.
221
+ let diff: string;
222
+ try {
223
+ diff = await runGit(repoRoot, ["diff", `${canonicalBranch}...${branch}`, "--", ".seeds/"]);
224
+ } catch (err) {
225
+ const msg = err instanceof Error ? err.message : String(err);
226
+ return { preserved: false, error: `Failed to compute .seeds/ diff: ${msg}` };
227
+ }
228
+
229
+ if (diff.trim() === "") {
230
+ // No .seeds/ changes on this branch
231
+ return { preserved: false };
232
+ }
233
+
234
+ // Step 2: Verify the repo root is currently on canonicalBranch.
235
+ let currentBranch: string;
236
+ try {
237
+ currentBranch = (await runGit(repoRoot, ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
238
+ } catch (err) {
239
+ const msg = err instanceof Error ? err.message : String(err);
240
+ return { preserved: false, error: `Failed to determine current branch: ${msg}` };
241
+ }
242
+
243
+ if (currentBranch !== canonicalBranch) {
244
+ return {
245
+ preserved: false,
246
+ error: `Repo root is on '${currentBranch}', expected '${canonicalBranch}'. Cannot apply patch.`,
247
+ };
248
+ }
249
+
250
+ // Step 3: Check that .seeds/ is clean in the canonical branch.
251
+ let statusOutput: string;
252
+ try {
253
+ statusOutput = await runGit(repoRoot, ["status", "--porcelain", "--", ".seeds/"]);
254
+ } catch (err) {
255
+ const msg = err instanceof Error ? err.message : String(err);
256
+ return { preserved: false, error: `Failed to check .seeds/ status: ${msg}` };
257
+ }
258
+
259
+ if (statusOutput.trim() !== "") {
260
+ return {
261
+ preserved: false,
262
+ error: `.seeds/ has uncommitted changes in canonical branch. Cannot apply patch safely.`,
263
+ };
264
+ }
265
+
266
+ // Step 4: Write diff to a temp file.
267
+ const tmpFile = join(repoRoot, ".overstory", `_seeds-patch-${Date.now()}.diff`);
268
+ try {
269
+ await Bun.write(tmpFile, diff);
270
+
271
+ // Step 5: Apply the patch with --index (stages changes).
272
+ try {
273
+ await runGit(repoRoot, ["apply", "--index", tmpFile]);
274
+ } catch (err) {
275
+ const msg = err instanceof Error ? err.message : String(err);
276
+ // Revert any partial changes
277
+ try {
278
+ await runGit(repoRoot, ["reset", "HEAD", "--", ".seeds/"]);
279
+ await runGit(repoRoot, ["checkout", "--", ".seeds/"]);
280
+ } catch {
281
+ // Best-effort revert
282
+ }
283
+ return { preserved: false, error: `Failed to apply .seeds/ patch: ${msg}` };
284
+ }
285
+
286
+ // Step 6: Commit the changes.
287
+ try {
288
+ await runGit(repoRoot, [
289
+ "commit",
290
+ "-m",
291
+ `chore: preserve .seeds/ changes from lead ${agentName}`,
292
+ ]);
293
+ } catch (err) {
294
+ const msg = err instanceof Error ? err.message : String(err);
295
+ // Revert any staged changes
296
+ try {
297
+ await runGit(repoRoot, ["reset", "HEAD", "--", ".seeds/"]);
298
+ await runGit(repoRoot, ["checkout", "--", ".seeds/"]);
299
+ } catch {
300
+ // Best-effort revert
301
+ }
302
+ return { preserved: false, error: `Failed to commit .seeds/ changes: ${msg}` };
303
+ }
304
+
305
+ return { preserved: true };
306
+ } finally {
307
+ // Step 8: Always clean up the temp file.
308
+ try {
309
+ await unlink(tmpFile);
310
+ } catch {
311
+ // Ignore cleanup errors
312
+ }
313
+ }
314
+ }
@@ -210,15 +210,19 @@ describe("createSession", () => {
210
210
  }
211
211
  });
212
212
 
213
- test("still creates session when which overstory fails (uses fallback)", async () => {
213
+ test("still creates session when which ov and which overstory both fail (uses fallback)", async () => {
214
214
  let callCount = 0;
215
215
  spawnSpy.mockImplementation(() => {
216
216
  callCount++;
217
217
  if (callCount === 1) {
218
+ // which ov fails
219
+ return mockSpawnResult("", "ov not found", 1);
220
+ }
221
+ if (callCount === 2) {
218
222
  // which overstory fails
219
223
  return mockSpawnResult("", "overstory not found", 1);
220
224
  }
221
- if (callCount === 2) {
225
+ if (callCount === 3) {
222
226
  // tmux new-session
223
227
  return mockSpawnResult("", "", 0);
224
228
  }
@@ -230,7 +234,8 @@ describe("createSession", () => {
230
234
  expect(pid).toBe(5555);
231
235
 
232
236
  // The tmux command should contain the original command
233
- const tmuxCallArgs = spawnSpy.mock.calls[1] as unknown[];
237
+ // Call 0: which ov, Call 1: which overstory, Call 2: tmux new-session
238
+ const tmuxCallArgs = spawnSpy.mock.calls[2] as unknown[];
234
239
  const cmd = tmuxCallArgs[0] as string[];
235
240
  const tmuxCmd = cmd[7] as string;
236
241
  expect(tmuxCmd).toContain("echo test");
@@ -13,30 +13,31 @@ import { AgentError } from "../errors.ts";
13
13
  /**
14
14
  * Detect the directory containing the overstory binary.
15
15
  *
16
- * Checks process.argv[0] first (the bun/node executable path won't help,
17
- * but process.argv[1] is the script path for `bun run`), then falls back
18
- * to `which overstory` to find it on the current PATH.
16
+ * Tries `which ov` first (the short alias), then falls back to
17
+ * `which overstory` (the original name). Both are registered in
18
+ * package.json bin, but depending on how the tool was installed
19
+ * (bun link, npm link, global install), only one may be on PATH.
19
20
  *
20
21
  * Returns null if detection fails.
21
22
  */
22
23
  async function detectOverstoryBinDir(): Promise<string | null> {
23
- // process.argv[1] is the script entry point (e.g., /path/to/overstory/src/index.ts)
24
- // The overstory binary (bun link) resolves to a bin dir
25
- // Try `which overstory` for the most reliable result
26
- try {
27
- const proc = Bun.spawn(["which", "overstory"], {
28
- stdout: "pipe",
29
- stderr: "pipe",
30
- });
31
- const exitCode = await proc.exited;
32
- if (exitCode === 0) {
33
- const binPath = (await new Response(proc.stdout).text()).trim();
34
- if (binPath.length > 0) {
35
- return dirname(resolve(binPath));
24
+ // Try both command names — the alias migration may leave only one resolvable
25
+ for (const cmdName of ["ov", "overstory"]) {
26
+ try {
27
+ const proc = Bun.spawn(["which", cmdName], {
28
+ stdout: "pipe",
29
+ stderr: "pipe",
30
+ });
31
+ const exitCode = await proc.exited;
32
+ if (exitCode === 0) {
33
+ const binPath = (await new Response(proc.stdout).text()).trim();
34
+ if (binPath.length > 0) {
35
+ return dirname(resolve(binPath));
36
+ }
36
37
  }
38
+ } catch {
39
+ // which not available or command not on PATH — try next
37
40
  }
38
- } catch {
39
- // which not available or overstory not on PATH
40
41
  }
41
42
 
42
43
  // Fallback: if process.argv[1] points to overstory's own entry point (src/index.ts),
@@ -1,6 +1,6 @@
1
1
  # {{PROJECT_NAME}} — Overstory Orchestration
2
2
 
3
- > Auto-generated by `overstory init`. You may edit this file.
3
+ > Auto-generated by `ov init`. You may edit this file.
4
4
 
5
5
  This project uses **overstory** for Claude Code agent orchestration. Your session
6
6
  acts as the orchestrator: you decide what work to delegate, spawn worker agents,
@@ -10,34 +10,34 @@ monitor progress, and merge results.
10
10
 
11
11
  ```bash
12
12
  # Spawn a worker agent
13
- overstory sling <bead-id> --capability <type> --name <agent-name> \
13
+ ov sling <bead-id> --capability <type> --name <agent-name> \
14
14
  [--spec <path>] [--files file1,file2] [--parent <agent>] [--depth <n>]
15
15
 
16
16
  # Check system status
17
- overstory status # Overview of all agents, worktrees, {{TRACKER_NAME}}
18
- overstory status --json # Machine-readable output
19
- overstory status --watch # Live updating
17
+ ov status # Overview of all agents, worktrees, {{TRACKER_NAME}}
18
+ ov status --json # Machine-readable output
19
+ ov status --watch # Live updating
20
20
 
21
21
  # Messaging (SQLite-backed, ~1-5ms per query)
22
- overstory mail send --to <agent> --subject "..." --body "..."
23
- overstory mail check # Your inbox
24
- overstory mail list --unread # All unread messages
25
- overstory mail reply <id> --body "..."
22
+ ov mail send --to <agent> --subject "..." --body "..."
23
+ ov mail check # Your inbox
24
+ ov mail list --unread # All unread messages
25
+ ov mail reply <id> --body "..."
26
26
 
27
27
  # Merge completed work
28
- overstory merge --branch <name> # Merge a specific branch
29
- overstory merge --all # Merge all completed branches
30
- overstory merge --dry-run --branch <name> # Preview conflicts
28
+ ov merge --branch <name> # Merge a specific branch
29
+ ov merge --all # Merge all completed branches
30
+ ov merge --dry-run --branch <name> # Preview conflicts
31
31
 
32
32
  # Worktree management
33
- overstory worktree list # Show all worktrees with status
34
- overstory worktree clean --completed # Remove finished worktrees
33
+ ov worktree list # Show all worktrees with status
34
+ ov worktree clean --completed # Remove finished worktrees
35
35
 
36
36
  # Context and monitoring
37
- overstory prime # Reload context (config, mulch, recent activity)
38
- overstory watch --background # Start watchdog daemon
39
- overstory metrics # Performance summary
40
- overstory log <event> --agent <name> # Hook-driven event logging
37
+ ov prime # Reload context (config, mulch, recent activity)
38
+ ov watch --background # Start watchdog daemon
39
+ ov metrics # Performance summary
40
+ ov log <event> --agent <name> # Hook-driven event logging
41
41
  ```
42
42
 
43
43
  ## How to Spawn Agents
@@ -46,10 +46,10 @@ overstory log <event> --agent <name> # Hook-driven event logging
46
46
  2. Choose a capability based on the task:
47
47
  {{AGENT_DEFINITIONS}}
48
48
  3. Assign exclusive file scope so agents do not conflict
49
- 4. Spawn: `overstory sling <bead-id> --capability <type> --name <unique-name> --files src/foo.ts,src/bar.ts`
49
+ 4. Spawn: `ov sling <bead-id> --capability <type> --name <unique-name> --files src/foo.ts,src/bar.ts`
50
50
 
51
51
  Each spawned agent gets its own git worktree, branch, CLAUDE.md overlay, and
52
- tmux session. Agents communicate via `overstory mail` and report completion
52
+ tmux session. Agents communicate via `ov mail` and report completion
53
53
  by closing their {{TRACKER_NAME}} issue (`{{TRACKER_CLI}} close <id> --reason "summary"`).
54
54
 
55
55
  ## Hierarchical Delegation
@@ -58,10 +58,10 @@ You can spawn **team leads** that themselves spawn sub-workers:
58
58
 
59
59
  ```
60
60
  Orchestrator (this session)
61
- └── overstory sling bd-xyz --capability lead --name build-lead
62
- ├── overstory sling bd-abc --capability builder --name auth-login
63
- ├── overstory sling bd-def --capability builder --name auth-signup
64
- └── overstory sling bd-ghi --capability builder --name auth-reset
61
+ └── ov sling bd-xyz --capability lead --name build-lead
62
+ ├── ov sling bd-abc --capability builder --name auth-login
63
+ ├── ov sling bd-def --capability builder --name auth-signup
64
+ └── ov sling bd-ghi --capability builder --name auth-reset
65
65
  ```
66
66
 
67
67
  Depth limit is configurable (default: 2). Leads use `--parent` and `--depth`
@@ -69,7 +69,7 @@ to track hierarchy.
69
69
 
70
70
  ## Checking Status
71
71
 
72
- Run `overstory status` to see:
72
+ Run `ov status` to see:
73
73
  - Active agents and their states (booting, working, stalled, zombie)
74
74
  - Worktree locations and branches
75
75
  - Beads issue progress
@@ -83,7 +83,7 @@ All merges target **{{CANONICAL_BRANCH}}**. Agents work on branches named
83
83
  ## Conventions
84
84
 
85
85
  - Agents own files exclusively — no two agents modify the same file
86
- - Use `overstory mail` for all inter-agent communication (not {{TRACKER_NAME}})
86
+ - Use `ov mail` for all inter-agent communication (not {{TRACKER_NAME}})
87
87
  - Use `{{TRACKER_CLI}} close` to report task completion (not mail)
88
- - Merge via `overstory merge`, not raw `git merge`
88
+ - Merge via `ov merge`, not raw `git merge`
89
89
  - Logs live in `.overstory/logs/` — never delete them manually
@@ -6,7 +6,11 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; overstory prime --agent {{AGENT_NAME}}"
9
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ov prime --agent {{AGENT_NAME}}"
10
+ },
11
+ {
12
+ "type": "command",
13
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ov mail check --inject --agent {{AGENT_NAME}}"
10
14
  }
11
15
  ]
12
16
  }
@@ -17,7 +21,7 @@
17
21
  "hooks": [
18
22
  {
19
23
  "type": "command",
20
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; overstory mail check --inject --agent {{AGENT_NAME}}"
24
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ov mail check --inject --agent {{AGENT_NAME}}"
21
25
  }
22
26
  ]
23
27
  }
@@ -28,7 +32,7 @@
28
32
  "hooks": [
29
33
  {
30
34
  "type": "command",
31
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; overstory log tool-start --agent {{AGENT_NAME}} --stdin"
35
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ov log tool-start --agent {{AGENT_NAME}} --stdin"
32
36
  }
33
37
  ]
34
38
  },
@@ -37,7 +41,7 @@
37
41
  "hooks": [
38
42
  {
39
43
  "type": "command",
40
- "command": "read -r INPUT; CMD=$(echo \"$INPUT\" | sed 's/.*\"command\": *\"\\([^\"]*\\)\".*/\\1/'); if echo \"$CMD\" | grep -qE '\\bgit\\s+push\\b'; then echo '{\"decision\":\"block\",\"reason\":\"git push is blocked — use overstory merge to integrate changes, push manually when ready\"}'; exit 0; fi;"
44
+ "command": "read -r INPUT; CMD=$(echo \"$INPUT\" | sed 's/.*\"command\": *\"\\([^\"]*\\)\".*/\\1/'); if echo \"$CMD\" | grep -qE '\\bgit\\s+push\\b'; then echo '{\"decision\":\"block\",\"reason\":\"git push is blocked — use ov merge to integrate changes, push manually when ready\"}'; exit 0; fi;"
41
45
  }
42
46
  ]
43
47
  }
@@ -48,11 +52,11 @@
48
52
  "hooks": [
49
53
  {
50
54
  "type": "command",
51
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; overstory log tool-end --agent {{AGENT_NAME}} --stdin"
55
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ov log tool-end --agent {{AGENT_NAME}} --stdin"
52
56
  },
53
57
  {
54
58
  "type": "command",
55
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; overstory mail check --inject --agent {{AGENT_NAME}} --debounce 500"
59
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ov mail check --inject --agent {{AGENT_NAME}} --debounce 500"
56
60
  }
57
61
  ]
58
62
  },
@@ -61,7 +65,7 @@
61
65
  "hooks": [
62
66
  {
63
67
  "type": "command",
64
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; overstory mail check --inject --agent {{AGENT_NAME}} --debounce 30000"
68
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ov mail check --inject --agent {{AGENT_NAME}} --debounce 30000"
65
69
  }
66
70
  ]
67
71
  },
@@ -70,7 +74,7 @@
70
74
  "hooks": [
71
75
  {
72
76
  "type": "command",
73
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; read -r INPUT; if echo \"$INPUT\" | grep -qE '\\bgit\\s+commit\\b'; then mulch diff HEAD~1 >/dev/null 2>&1 || true; fi; exit 0;"
77
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; read -r INPUT; if echo \"$INPUT\" | grep -qE '\\bgit\\s+commit\\b'; then ml diff HEAD~1 >/dev/null 2>&1 || true; fi; exit 0;"
74
78
  }
75
79
  ]
76
80
  }
@@ -81,11 +85,11 @@
81
85
  "hooks": [
82
86
  {
83
87
  "type": "command",
84
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; overstory log session-end --agent {{AGENT_NAME}} --stdin"
88
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ov log session-end --agent {{AGENT_NAME}} --stdin"
85
89
  },
86
90
  {
87
91
  "type": "command",
88
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; mulch learn"
92
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ml learn"
89
93
  }
90
94
  ]
91
95
  }
@@ -96,7 +100,7 @@
96
100
  "hooks": [
97
101
  {
98
102
  "type": "command",
99
- "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; overstory prime --agent {{AGENT_NAME}} --compact"
103
+ "command": "[ -z \"$OVERSTORY_AGENT_NAME\" ] && exit 0; ov prime --agent {{AGENT_NAME}} --compact"
100
104
  }
101
105
  ]
102
106
  }
@@ -1,5 +1,5 @@
1
1
  # DO NOT EDIT — Auto-generated by overstory
2
- # This overlay was written by `overstory sling`. Manual edits will be
2
+ # This overlay was written by `ov sling`. Manual edits will be
3
3
  # overwritten on the next spawn. Modify the template at
4
4
  # templates/overlay.md.tmpl in the overstory repo instead.
5
5
 
@@ -50,26 +50,26 @@ Prime relevant domain knowledge before starting work:
50
50
 
51
51
  ## Communication
52
52
 
53
- Use `overstory mail` for all communication. Your address is **{{AGENT_NAME}}**.
53
+ Use `ov mail` for all communication. Your address is **{{AGENT_NAME}}**.
54
54
 
55
55
  ```bash
56
56
  # Check your inbox (do this regularly)
57
- overstory mail check --agent {{AGENT_NAME}}
57
+ ov mail check --agent {{AGENT_NAME}}
58
58
 
59
59
  # Send a status update to your parent
60
- overstory mail send --to {{PARENT_AGENT}} --subject "status" \
60
+ ov mail send --to {{PARENT_AGENT}} --subject "status" \
61
61
  --body "Progress update here" --type status --agent {{AGENT_NAME}}
62
62
 
63
63
  # Ask a question
64
- overstory mail send --to {{PARENT_AGENT}} --subject "question" \
64
+ ov mail send --to {{PARENT_AGENT}} --subject "question" \
65
65
  --body "Your question here" --type question --priority high --agent {{AGENT_NAME}}
66
66
 
67
67
  # Report completion
68
- overstory mail send --to {{PARENT_AGENT}} --subject "done" \
68
+ ov mail send --to {{PARENT_AGENT}} --subject "done" \
69
69
  --body "Summary of what was done" --type result --agent {{AGENT_NAME}}
70
70
 
71
71
  # Reply to a message
72
- overstory mail reply <message-id> --body "Your reply" --agent {{AGENT_NAME}}
72
+ ov mail reply <message-id> --body "Your reply" --agent {{AGENT_NAME}}
73
73
  ```
74
74
 
75
75
  ## Spawning Sub-Workers