@os-eco/overstory-cli 0.6.1

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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +381 -0
  3. package/agents/builder.md +137 -0
  4. package/agents/coordinator.md +263 -0
  5. package/agents/lead.md +301 -0
  6. package/agents/merger.md +160 -0
  7. package/agents/monitor.md +214 -0
  8. package/agents/reviewer.md +140 -0
  9. package/agents/scout.md +119 -0
  10. package/agents/supervisor.md +423 -0
  11. package/package.json +47 -0
  12. package/src/agents/checkpoint.test.ts +88 -0
  13. package/src/agents/checkpoint.ts +101 -0
  14. package/src/agents/hooks-deployer.test.ts +2040 -0
  15. package/src/agents/hooks-deployer.ts +607 -0
  16. package/src/agents/identity.test.ts +603 -0
  17. package/src/agents/identity.ts +384 -0
  18. package/src/agents/lifecycle.test.ts +196 -0
  19. package/src/agents/lifecycle.ts +183 -0
  20. package/src/agents/manifest.test.ts +746 -0
  21. package/src/agents/manifest.ts +354 -0
  22. package/src/agents/overlay.test.ts +676 -0
  23. package/src/agents/overlay.ts +308 -0
  24. package/src/beads/client.test.ts +217 -0
  25. package/src/beads/client.ts +202 -0
  26. package/src/beads/molecules.test.ts +338 -0
  27. package/src/beads/molecules.ts +198 -0
  28. package/src/commands/agents.test.ts +322 -0
  29. package/src/commands/agents.ts +287 -0
  30. package/src/commands/clean.test.ts +670 -0
  31. package/src/commands/clean.ts +618 -0
  32. package/src/commands/completions.test.ts +342 -0
  33. package/src/commands/completions.ts +887 -0
  34. package/src/commands/coordinator.test.ts +1530 -0
  35. package/src/commands/coordinator.ts +733 -0
  36. package/src/commands/costs.test.ts +1119 -0
  37. package/src/commands/costs.ts +564 -0
  38. package/src/commands/dashboard.test.ts +308 -0
  39. package/src/commands/dashboard.ts +838 -0
  40. package/src/commands/doctor.test.ts +294 -0
  41. package/src/commands/doctor.ts +213 -0
  42. package/src/commands/errors.test.ts +647 -0
  43. package/src/commands/errors.ts +248 -0
  44. package/src/commands/feed.test.ts +578 -0
  45. package/src/commands/feed.ts +361 -0
  46. package/src/commands/group.test.ts +262 -0
  47. package/src/commands/group.ts +511 -0
  48. package/src/commands/hooks.test.ts +458 -0
  49. package/src/commands/hooks.ts +253 -0
  50. package/src/commands/init.test.ts +347 -0
  51. package/src/commands/init.ts +650 -0
  52. package/src/commands/inspect.test.ts +670 -0
  53. package/src/commands/inspect.ts +431 -0
  54. package/src/commands/log.test.ts +1454 -0
  55. package/src/commands/log.ts +724 -0
  56. package/src/commands/logs.test.ts +379 -0
  57. package/src/commands/logs.ts +546 -0
  58. package/src/commands/mail.test.ts +1270 -0
  59. package/src/commands/mail.ts +771 -0
  60. package/src/commands/merge.test.ts +670 -0
  61. package/src/commands/merge.ts +355 -0
  62. package/src/commands/metrics.test.ts +444 -0
  63. package/src/commands/metrics.ts +143 -0
  64. package/src/commands/monitor.test.ts +191 -0
  65. package/src/commands/monitor.ts +390 -0
  66. package/src/commands/nudge.test.ts +230 -0
  67. package/src/commands/nudge.ts +372 -0
  68. package/src/commands/prime.test.ts +470 -0
  69. package/src/commands/prime.ts +381 -0
  70. package/src/commands/replay.test.ts +741 -0
  71. package/src/commands/replay.ts +360 -0
  72. package/src/commands/run.test.ts +431 -0
  73. package/src/commands/run.ts +351 -0
  74. package/src/commands/sling.test.ts +657 -0
  75. package/src/commands/sling.ts +661 -0
  76. package/src/commands/spec.test.ts +203 -0
  77. package/src/commands/spec.ts +168 -0
  78. package/src/commands/status.test.ts +430 -0
  79. package/src/commands/status.ts +398 -0
  80. package/src/commands/stop.test.ts +420 -0
  81. package/src/commands/stop.ts +151 -0
  82. package/src/commands/supervisor.test.ts +187 -0
  83. package/src/commands/supervisor.ts +535 -0
  84. package/src/commands/trace.test.ts +745 -0
  85. package/src/commands/trace.ts +325 -0
  86. package/src/commands/watch.test.ts +145 -0
  87. package/src/commands/watch.ts +247 -0
  88. package/src/commands/worktree.test.ts +786 -0
  89. package/src/commands/worktree.ts +311 -0
  90. package/src/config.test.ts +822 -0
  91. package/src/config.ts +829 -0
  92. package/src/doctor/agents.test.ts +454 -0
  93. package/src/doctor/agents.ts +396 -0
  94. package/src/doctor/config-check.test.ts +190 -0
  95. package/src/doctor/config-check.ts +183 -0
  96. package/src/doctor/consistency.test.ts +651 -0
  97. package/src/doctor/consistency.ts +294 -0
  98. package/src/doctor/databases.test.ts +290 -0
  99. package/src/doctor/databases.ts +218 -0
  100. package/src/doctor/dependencies.test.ts +184 -0
  101. package/src/doctor/dependencies.ts +175 -0
  102. package/src/doctor/logs.test.ts +251 -0
  103. package/src/doctor/logs.ts +295 -0
  104. package/src/doctor/merge-queue.test.ts +216 -0
  105. package/src/doctor/merge-queue.ts +144 -0
  106. package/src/doctor/structure.test.ts +291 -0
  107. package/src/doctor/structure.ts +198 -0
  108. package/src/doctor/types.ts +37 -0
  109. package/src/doctor/version.test.ts +136 -0
  110. package/src/doctor/version.ts +129 -0
  111. package/src/e2e/init-sling-lifecycle.test.ts +277 -0
  112. package/src/errors.ts +217 -0
  113. package/src/events/store.test.ts +660 -0
  114. package/src/events/store.ts +369 -0
  115. package/src/events/tool-filter.test.ts +330 -0
  116. package/src/events/tool-filter.ts +126 -0
  117. package/src/index.ts +316 -0
  118. package/src/insights/analyzer.test.ts +466 -0
  119. package/src/insights/analyzer.ts +203 -0
  120. package/src/logging/color.test.ts +142 -0
  121. package/src/logging/color.ts +71 -0
  122. package/src/logging/logger.test.ts +813 -0
  123. package/src/logging/logger.ts +266 -0
  124. package/src/logging/reporter.test.ts +259 -0
  125. package/src/logging/reporter.ts +109 -0
  126. package/src/logging/sanitizer.test.ts +190 -0
  127. package/src/logging/sanitizer.ts +57 -0
  128. package/src/mail/broadcast.test.ts +203 -0
  129. package/src/mail/broadcast.ts +92 -0
  130. package/src/mail/client.test.ts +773 -0
  131. package/src/mail/client.ts +223 -0
  132. package/src/mail/store.test.ts +705 -0
  133. package/src/mail/store.ts +387 -0
  134. package/src/merge/queue.test.ts +359 -0
  135. package/src/merge/queue.ts +231 -0
  136. package/src/merge/resolver.test.ts +1345 -0
  137. package/src/merge/resolver.ts +645 -0
  138. package/src/metrics/store.test.ts +667 -0
  139. package/src/metrics/store.ts +445 -0
  140. package/src/metrics/summary.test.ts +398 -0
  141. package/src/metrics/summary.ts +178 -0
  142. package/src/metrics/transcript.test.ts +356 -0
  143. package/src/metrics/transcript.ts +175 -0
  144. package/src/mulch/client.test.ts +671 -0
  145. package/src/mulch/client.ts +332 -0
  146. package/src/sessions/compat.test.ts +280 -0
  147. package/src/sessions/compat.ts +104 -0
  148. package/src/sessions/store.test.ts +873 -0
  149. package/src/sessions/store.ts +494 -0
  150. package/src/test-helpers.test.ts +124 -0
  151. package/src/test-helpers.ts +126 -0
  152. package/src/tracker/beads.ts +56 -0
  153. package/src/tracker/factory.test.ts +80 -0
  154. package/src/tracker/factory.ts +64 -0
  155. package/src/tracker/seeds.ts +182 -0
  156. package/src/tracker/types.ts +52 -0
  157. package/src/types.ts +724 -0
  158. package/src/watchdog/daemon.test.ts +1975 -0
  159. package/src/watchdog/daemon.ts +671 -0
  160. package/src/watchdog/health.test.ts +431 -0
  161. package/src/watchdog/health.ts +264 -0
  162. package/src/watchdog/triage.test.ts +164 -0
  163. package/src/watchdog/triage.ts +179 -0
  164. package/src/worktree/manager.test.ts +439 -0
  165. package/src/worktree/manager.ts +198 -0
  166. package/src/worktree/tmux.test.ts +1009 -0
  167. package/src/worktree/tmux.ts +509 -0
  168. package/templates/CLAUDE.md.tmpl +89 -0
  169. package/templates/hooks.json.tmpl +105 -0
  170. package/templates/overlay.md.tmpl +81 -0
@@ -0,0 +1,277 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { readdir, stat } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { createManifestLoader } from "../agents/manifest.ts";
5
+ import { writeOverlay } from "../agents/overlay.ts";
6
+ import { initCommand } from "../commands/init.ts";
7
+ import { loadConfig } from "../config.ts";
8
+ import { cleanupTempDir, createTempGitRepo } from "../test-helpers.ts";
9
+ import type { OverlayConfig } from "../types.ts";
10
+
11
+ /**
12
+ * E2E test: init→sling lifecycle on a throwaway external project.
13
+ *
14
+ * Validates the "project-agnostic" promise by running overstory init on a
15
+ * fresh temp git repo (NOT the overstory repo itself), then verifying all
16
+ * artifacts, loading config + manifest via real APIs, and generating an overlay.
17
+ *
18
+ * Uses real filesystem and real git repos. No mocks.
19
+ * Suppresses stdout because initCommand prints status lines.
20
+ */
21
+
22
+ const EXPECTED_AGENT_DEFS = [
23
+ "builder.md",
24
+ "coordinator.md",
25
+ "lead.md",
26
+ "merger.md",
27
+ "monitor.md",
28
+ "reviewer.md",
29
+ "scout.md",
30
+ "supervisor.md",
31
+ ];
32
+
33
+ describe("E2E: init→sling lifecycle on external project", () => {
34
+ let tempDir: string;
35
+ let originalCwd: string;
36
+ let originalWrite: typeof process.stdout.write;
37
+
38
+ beforeEach(async () => {
39
+ tempDir = await createTempGitRepo();
40
+ originalCwd = process.cwd();
41
+ process.chdir(tempDir);
42
+
43
+ // Suppress stdout noise from initCommand
44
+ originalWrite = process.stdout.write;
45
+ process.stdout.write = (() => true) as typeof process.stdout.write;
46
+ });
47
+
48
+ afterEach(async () => {
49
+ process.chdir(originalCwd);
50
+ process.stdout.write = originalWrite;
51
+ await cleanupTempDir(tempDir);
52
+ });
53
+
54
+ test("init creates all expected artifacts", async () => {
55
+ await initCommand([]);
56
+
57
+ const overstoryDir = join(tempDir, ".overstory");
58
+
59
+ // config.yaml exists
60
+ const configFile = Bun.file(join(overstoryDir, "config.yaml"));
61
+ expect(await configFile.exists()).toBe(true);
62
+
63
+ // agent-manifest.json exists and is valid JSON
64
+ const manifestFile = Bun.file(join(overstoryDir, "agent-manifest.json"));
65
+ expect(await manifestFile.exists()).toBe(true);
66
+ const manifestText = await manifestFile.text();
67
+ const manifestJson = JSON.parse(manifestText);
68
+ expect(manifestJson).toBeDefined();
69
+ expect(manifestJson.version).toBe("1.0");
70
+ expect(typeof manifestJson.agents).toBe("object");
71
+
72
+ // hooks.json exists
73
+ const hooksFile = Bun.file(join(overstoryDir, "hooks.json"));
74
+ expect(await hooksFile.exists()).toBe(true);
75
+
76
+ // .gitignore exists
77
+ const gitignoreFile = Bun.file(join(overstoryDir, ".gitignore"));
78
+ expect(await gitignoreFile.exists()).toBe(true);
79
+
80
+ // agent-defs/ contains all 8 agent definition files
81
+ const agentDefsDir = join(overstoryDir, "agent-defs");
82
+ const agentDefFiles = (await readdir(agentDefsDir)).filter((f) => f.endsWith(".md")).sort();
83
+ expect(agentDefFiles).toEqual(EXPECTED_AGENT_DEFS);
84
+
85
+ // Required subdirectories exist
86
+ const expectedDirs = ["agents", "worktrees", "specs", "logs"];
87
+ for (const dirName of expectedDirs) {
88
+ const dirPath = join(overstoryDir, dirName);
89
+ const dirStat = await stat(dirPath);
90
+ expect(dirStat.isDirectory()).toBe(true);
91
+ }
92
+ });
93
+
94
+ test("loadConfig returns valid config pointing to temp dir", async () => {
95
+ await initCommand([]);
96
+
97
+ const config = await loadConfig(tempDir);
98
+
99
+ // project.root should point to the temp directory
100
+ expect(config.project.root).toBe(tempDir);
101
+
102
+ // agents.baseDir should be the relative path to agent-defs
103
+ expect(config.agents.baseDir).toBe(".overstory/agent-defs");
104
+
105
+ // canonicalBranch should be detected (main for our test repos)
106
+ expect(config.project.canonicalBranch).toBeTruthy();
107
+
108
+ // name should be set (from dir basename or git remote)
109
+ expect(config.project.name).toBeTruthy();
110
+ });
111
+
112
+ test("manifest loads successfully with all 8 agents", async () => {
113
+ await initCommand([]);
114
+
115
+ const manifestPath = join(tempDir, ".overstory", "agent-manifest.json");
116
+ const agentDefsDir = join(tempDir, ".overstory", "agent-defs");
117
+ const loader = createManifestLoader(manifestPath, agentDefsDir);
118
+
119
+ const manifest = await loader.load();
120
+
121
+ // All 8 agents present
122
+ const agentNames = Object.keys(manifest.agents).sort();
123
+ expect(agentNames).toEqual([
124
+ "builder",
125
+ "coordinator",
126
+ "lead",
127
+ "merger",
128
+ "monitor",
129
+ "reviewer",
130
+ "scout",
131
+ "supervisor",
132
+ ]);
133
+
134
+ // Each agent has a valid file reference
135
+ for (const [_name, def] of Object.entries(manifest.agents)) {
136
+ expect(def.file).toEndWith(".md");
137
+ // Verify the referenced .md file actually exists
138
+ const mdFile = Bun.file(join(agentDefsDir, def.file));
139
+ expect(await mdFile.exists()).toBe(true);
140
+ }
141
+
142
+ // Validation returns no errors
143
+ const errors = loader.validate();
144
+ expect(errors).toEqual([]);
145
+ });
146
+
147
+ test("manifest capability index is consistent", async () => {
148
+ await initCommand([]);
149
+
150
+ const manifestPath = join(tempDir, ".overstory", "agent-manifest.json");
151
+ const agentDefsDir = join(tempDir, ".overstory", "agent-defs");
152
+ const loader = createManifestLoader(manifestPath, agentDefsDir);
153
+
154
+ const manifest = await loader.load();
155
+
156
+ // capabilityIndex should map capabilities to agent names
157
+ expect(Object.keys(manifest.capabilityIndex).length).toBeGreaterThan(0);
158
+
159
+ // Each capability in the index should reference agents that declare it
160
+ for (const [cap, names] of Object.entries(manifest.capabilityIndex)) {
161
+ for (const name of names) {
162
+ const agent = manifest.agents[name];
163
+ expect(agent).toBeDefined();
164
+ expect(agent?.capabilities).toContain(cap);
165
+ }
166
+ }
167
+ });
168
+
169
+ test("overlay generation works for external project", async () => {
170
+ await initCommand([]);
171
+
172
+ const agentDefsDir = join(tempDir, ".overstory", "agent-defs");
173
+ const baseDefinition = await Bun.file(join(agentDefsDir, "builder.md")).text();
174
+
175
+ const overlayConfig: OverlayConfig = {
176
+ agentName: "test-agent",
177
+ beadId: "test-bead-001",
178
+ specPath: null,
179
+ branchName: "overstory/test-agent/test-bead-001",
180
+ worktreePath: join(tempDir, ".overstory", "worktrees", "test-agent"),
181
+ fileScope: [],
182
+ mulchDomains: [],
183
+ parentAgent: null,
184
+ depth: 0,
185
+ canSpawn: false,
186
+ capability: "builder",
187
+ baseDefinition,
188
+ };
189
+
190
+ // Write the overlay into a subdirectory of the temp dir (simulating a worktree)
191
+ const worktreePath = join(tempDir, ".overstory", "worktrees", "test-agent");
192
+ const { mkdir } = await import("node:fs/promises");
193
+ await mkdir(worktreePath, { recursive: true });
194
+
195
+ await writeOverlay(worktreePath, overlayConfig, tempDir);
196
+
197
+ // Verify the overlay was written
198
+ const overlayPath = join(worktreePath, ".claude", "CLAUDE.md");
199
+ const overlayFile = Bun.file(overlayPath);
200
+ expect(await overlayFile.exists()).toBe(true);
201
+
202
+ const content = await overlayFile.text();
203
+
204
+ // Verify template placeholders were replaced
205
+ expect(content).toContain("test-agent");
206
+ expect(content).toContain("test-bead-001");
207
+ expect(content).toContain("overstory/test-agent/test-bead-001");
208
+ expect(content).not.toContain("{{AGENT_NAME}}");
209
+ expect(content).not.toContain("{{BEAD_ID}}");
210
+ expect(content).not.toContain("{{BRANCH_NAME}}");
211
+ });
212
+
213
+ test("full init→config→manifest→overlay pipeline succeeds", async () => {
214
+ // This test validates the entire lifecycle in sequence:
215
+ // init → load config → load manifest → generate overlay
216
+
217
+ // Step 1: Init
218
+ await initCommand([]);
219
+
220
+ // Step 2: Load config
221
+ const config = await loadConfig(tempDir);
222
+ expect(config.project.root).toBe(tempDir);
223
+
224
+ // Step 3: Load manifest using config paths
225
+ const manifestPath = join(config.project.root, config.agents.manifestPath);
226
+ const agentDefsDir = join(config.project.root, config.agents.baseDir);
227
+ const loader = createManifestLoader(manifestPath, agentDefsDir);
228
+ await loader.load();
229
+
230
+ // Verify builder agent exists (the one we'll use for overlay)
231
+ const builder = loader.getAgent("builder");
232
+ expect(builder).toBeDefined();
233
+ expect(builder?.canSpawn).toBe(false);
234
+
235
+ // Verify lead agent can spawn
236
+ const lead = loader.getAgent("lead");
237
+ expect(lead).toBeDefined();
238
+ expect(lead?.canSpawn).toBe(true);
239
+
240
+ // Step 4: Generate overlay using a realistic config
241
+ const builderDef = await Bun.file(join(agentDefsDir, "builder.md")).text();
242
+ const overlayConfig: OverlayConfig = {
243
+ agentName: "lifecycle-builder",
244
+ beadId: "lifecycle-001",
245
+ specPath: join(tempDir, ".overstory", "specs", "lifecycle-001.md"),
246
+ branchName: "overstory/lifecycle-builder/lifecycle-001",
247
+ worktreePath: join(tempDir, ".overstory", "worktrees", "lifecycle-builder"),
248
+ fileScope: ["src/main.ts", "src/utils.ts"],
249
+ mulchDomains: ["typescript"],
250
+ parentAgent: "orchestrator",
251
+ depth: 0,
252
+ canSpawn: false,
253
+ capability: "builder",
254
+ baseDefinition: builderDef,
255
+ };
256
+
257
+ const worktreePath = join(tempDir, ".overstory", "worktrees", "lifecycle-builder");
258
+ const { mkdir } = await import("node:fs/promises");
259
+ await mkdir(worktreePath, { recursive: true });
260
+
261
+ await writeOverlay(worktreePath, overlayConfig, tempDir);
262
+
263
+ const overlayContent = await Bun.file(join(worktreePath, ".claude", "CLAUDE.md")).text();
264
+
265
+ // Verify all overlay fields rendered correctly
266
+ expect(overlayContent).toContain("lifecycle-builder");
267
+ expect(overlayContent).toContain("lifecycle-001");
268
+ expect(overlayContent).toContain("overstory/lifecycle-builder/lifecycle-001");
269
+ expect(overlayContent).toContain("orchestrator");
270
+ expect(overlayContent).toContain("`src/main.ts`");
271
+ expect(overlayContent).toContain("`src/utils.ts`");
272
+ expect(overlayContent).toContain("mulch prime typescript");
273
+
274
+ // No unresolved placeholders
275
+ expect(overlayContent).not.toMatch(/\{\{[A-Z_]+\}\}/);
276
+ });
277
+ });
package/src/errors.ts ADDED
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Base error class for all Overstory errors.
3
+ * Includes a machine-readable `code` field for programmatic handling.
4
+ */
5
+ export class OverstoryError extends Error {
6
+ readonly code: string;
7
+
8
+ constructor(message: string, code: string, options?: ErrorOptions) {
9
+ super(message, options);
10
+ this.name = "OverstoryError";
11
+ this.code = code;
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Raised when config loading or validation fails.
17
+ * Examples: missing config file, invalid YAML, schema violations.
18
+ */
19
+ export class ConfigError extends OverstoryError {
20
+ readonly configPath: string | null;
21
+ readonly field: string | null;
22
+
23
+ constructor(
24
+ message: string,
25
+ context?: {
26
+ configPath?: string;
27
+ field?: string;
28
+ cause?: Error;
29
+ },
30
+ ) {
31
+ super(message, "CONFIG_ERROR", { cause: context?.cause });
32
+ this.name = "ConfigError";
33
+ this.configPath = context?.configPath ?? null;
34
+ this.field = context?.field ?? null;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Raised for agent lifecycle issues.
40
+ * Examples: spawn failure, agent not found, depth limit exceeded.
41
+ */
42
+ export class AgentError extends OverstoryError {
43
+ readonly agentName: string | null;
44
+ readonly capability: string | null;
45
+
46
+ constructor(
47
+ message: string,
48
+ context?: {
49
+ agentName?: string;
50
+ capability?: string;
51
+ cause?: Error;
52
+ },
53
+ ) {
54
+ super(message, "AGENT_ERROR", { cause: context?.cause });
55
+ this.name = "AgentError";
56
+ this.agentName = context?.agentName ?? null;
57
+ this.capability = context?.capability ?? null;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Raised when hierarchy constraints are violated.
63
+ * Examples: coordinator spawning a builder directly instead of through a lead.
64
+ */
65
+ export class HierarchyError extends OverstoryError {
66
+ readonly agentName: string | null;
67
+ readonly requestedCapability: string | null;
68
+
69
+ constructor(
70
+ message: string,
71
+ context?: {
72
+ agentName?: string;
73
+ requestedCapability?: string;
74
+ cause?: Error;
75
+ },
76
+ ) {
77
+ super(message, "HIERARCHY_VIOLATION", { cause: context?.cause });
78
+ this.name = "HierarchyError";
79
+ this.agentName = context?.agentName ?? null;
80
+ this.requestedCapability = context?.requestedCapability ?? null;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Raised when git worktree operations fail.
86
+ * Examples: worktree creation, branch conflicts, cleanup failures.
87
+ */
88
+ export class WorktreeError extends OverstoryError {
89
+ readonly worktreePath: string | null;
90
+ readonly branchName: string | null;
91
+
92
+ constructor(
93
+ message: string,
94
+ context?: {
95
+ worktreePath?: string;
96
+ branchName?: string;
97
+ cause?: Error;
98
+ },
99
+ ) {
100
+ super(message, "WORKTREE_ERROR", { cause: context?.cause });
101
+ this.name = "WorktreeError";
102
+ this.worktreePath = context?.worktreePath ?? null;
103
+ this.branchName = context?.branchName ?? null;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Raised when mail system operations fail.
109
+ * Examples: DB access errors, invalid message format, delivery failures.
110
+ */
111
+ export class MailError extends OverstoryError {
112
+ readonly agentName: string | null;
113
+ readonly messageId: string | null;
114
+
115
+ constructor(
116
+ message: string,
117
+ context?: {
118
+ agentName?: string;
119
+ messageId?: string;
120
+ cause?: Error;
121
+ },
122
+ ) {
123
+ super(message, "MAIL_ERROR", { cause: context?.cause });
124
+ this.name = "MailError";
125
+ this.agentName = context?.agentName ?? null;
126
+ this.messageId = context?.messageId ?? null;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Raised when merge or conflict resolution fails.
132
+ * Examples: unresolvable conflicts, merge queue errors, tier escalation failures.
133
+ */
134
+ export class MergeError extends OverstoryError {
135
+ readonly branchName: string | null;
136
+ readonly conflictFiles: string[];
137
+
138
+ constructor(
139
+ message: string,
140
+ context?: {
141
+ branchName?: string;
142
+ conflictFiles?: string[];
143
+ cause?: Error;
144
+ },
145
+ ) {
146
+ super(message, "MERGE_ERROR", { cause: context?.cause });
147
+ this.name = "MergeError";
148
+ this.branchName = context?.branchName ?? null;
149
+ this.conflictFiles = context?.conflictFiles ?? [];
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Raised when input validation fails.
155
+ * Examples: invalid agent names, malformed beadIds, bad CLI arguments.
156
+ */
157
+ export class ValidationError extends OverstoryError {
158
+ readonly field: string | null;
159
+ readonly value: unknown;
160
+
161
+ constructor(
162
+ message: string,
163
+ context?: {
164
+ field?: string;
165
+ value?: unknown;
166
+ cause?: Error;
167
+ },
168
+ ) {
169
+ super(message, "VALIDATION_ERROR", { cause: context?.cause });
170
+ this.name = "ValidationError";
171
+ this.field = context?.field ?? null;
172
+ this.value = context?.value ?? null;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Raised when task group operations fail.
178
+ * Examples: group not found, duplicate member, auto-close failures.
179
+ */
180
+ export class GroupError extends OverstoryError {
181
+ readonly groupId: string | null;
182
+
183
+ constructor(
184
+ message: string,
185
+ context?: {
186
+ groupId?: string;
187
+ cause?: Error;
188
+ },
189
+ ) {
190
+ super(message, "GROUP_ERROR", { cause: context?.cause });
191
+ this.name = "GroupError";
192
+ this.groupId = context?.groupId ?? null;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Raised when session lifecycle operations fail.
198
+ * Examples: checkpoint save/restore failures, handoff failures.
199
+ */
200
+ export class LifecycleError extends OverstoryError {
201
+ readonly agentName: string | null;
202
+ readonly sessionId: string | null;
203
+
204
+ constructor(
205
+ message: string,
206
+ context?: {
207
+ agentName?: string;
208
+ sessionId?: string;
209
+ cause?: Error;
210
+ },
211
+ ) {
212
+ super(message, "LIFECYCLE_ERROR", { cause: context?.cause });
213
+ this.name = "LifecycleError";
214
+ this.agentName = context?.agentName ?? null;
215
+ this.sessionId = context?.sessionId ?? null;
216
+ }
217
+ }