@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.
- package/LICENSE +21 -0
- package/README.md +381 -0
- package/agents/builder.md +137 -0
- package/agents/coordinator.md +263 -0
- package/agents/lead.md +301 -0
- package/agents/merger.md +160 -0
- package/agents/monitor.md +214 -0
- package/agents/reviewer.md +140 -0
- package/agents/scout.md +119 -0
- package/agents/supervisor.md +423 -0
- package/package.json +47 -0
- package/src/agents/checkpoint.test.ts +88 -0
- package/src/agents/checkpoint.ts +101 -0
- package/src/agents/hooks-deployer.test.ts +2040 -0
- package/src/agents/hooks-deployer.ts +607 -0
- package/src/agents/identity.test.ts +603 -0
- package/src/agents/identity.ts +384 -0
- package/src/agents/lifecycle.test.ts +196 -0
- package/src/agents/lifecycle.ts +183 -0
- package/src/agents/manifest.test.ts +746 -0
- package/src/agents/manifest.ts +354 -0
- package/src/agents/overlay.test.ts +676 -0
- package/src/agents/overlay.ts +308 -0
- package/src/beads/client.test.ts +217 -0
- package/src/beads/client.ts +202 -0
- package/src/beads/molecules.test.ts +338 -0
- package/src/beads/molecules.ts +198 -0
- package/src/commands/agents.test.ts +322 -0
- package/src/commands/agents.ts +287 -0
- package/src/commands/clean.test.ts +670 -0
- package/src/commands/clean.ts +618 -0
- package/src/commands/completions.test.ts +342 -0
- package/src/commands/completions.ts +887 -0
- package/src/commands/coordinator.test.ts +1530 -0
- package/src/commands/coordinator.ts +733 -0
- package/src/commands/costs.test.ts +1119 -0
- package/src/commands/costs.ts +564 -0
- package/src/commands/dashboard.test.ts +308 -0
- package/src/commands/dashboard.ts +838 -0
- package/src/commands/doctor.test.ts +294 -0
- package/src/commands/doctor.ts +213 -0
- package/src/commands/errors.test.ts +647 -0
- package/src/commands/errors.ts +248 -0
- package/src/commands/feed.test.ts +578 -0
- package/src/commands/feed.ts +361 -0
- package/src/commands/group.test.ts +262 -0
- package/src/commands/group.ts +511 -0
- package/src/commands/hooks.test.ts +458 -0
- package/src/commands/hooks.ts +253 -0
- package/src/commands/init.test.ts +347 -0
- package/src/commands/init.ts +650 -0
- package/src/commands/inspect.test.ts +670 -0
- package/src/commands/inspect.ts +431 -0
- package/src/commands/log.test.ts +1454 -0
- package/src/commands/log.ts +724 -0
- package/src/commands/logs.test.ts +379 -0
- package/src/commands/logs.ts +546 -0
- package/src/commands/mail.test.ts +1270 -0
- package/src/commands/mail.ts +771 -0
- package/src/commands/merge.test.ts +670 -0
- package/src/commands/merge.ts +355 -0
- package/src/commands/metrics.test.ts +444 -0
- package/src/commands/metrics.ts +143 -0
- package/src/commands/monitor.test.ts +191 -0
- package/src/commands/monitor.ts +390 -0
- package/src/commands/nudge.test.ts +230 -0
- package/src/commands/nudge.ts +372 -0
- package/src/commands/prime.test.ts +470 -0
- package/src/commands/prime.ts +381 -0
- package/src/commands/replay.test.ts +741 -0
- package/src/commands/replay.ts +360 -0
- package/src/commands/run.test.ts +431 -0
- package/src/commands/run.ts +351 -0
- package/src/commands/sling.test.ts +657 -0
- package/src/commands/sling.ts +661 -0
- package/src/commands/spec.test.ts +203 -0
- package/src/commands/spec.ts +168 -0
- package/src/commands/status.test.ts +430 -0
- package/src/commands/status.ts +398 -0
- package/src/commands/stop.test.ts +420 -0
- package/src/commands/stop.ts +151 -0
- package/src/commands/supervisor.test.ts +187 -0
- package/src/commands/supervisor.ts +535 -0
- package/src/commands/trace.test.ts +745 -0
- package/src/commands/trace.ts +325 -0
- package/src/commands/watch.test.ts +145 -0
- package/src/commands/watch.ts +247 -0
- package/src/commands/worktree.test.ts +786 -0
- package/src/commands/worktree.ts +311 -0
- package/src/config.test.ts +822 -0
- package/src/config.ts +829 -0
- package/src/doctor/agents.test.ts +454 -0
- package/src/doctor/agents.ts +396 -0
- package/src/doctor/config-check.test.ts +190 -0
- package/src/doctor/config-check.ts +183 -0
- package/src/doctor/consistency.test.ts +651 -0
- package/src/doctor/consistency.ts +294 -0
- package/src/doctor/databases.test.ts +290 -0
- package/src/doctor/databases.ts +218 -0
- package/src/doctor/dependencies.test.ts +184 -0
- package/src/doctor/dependencies.ts +175 -0
- package/src/doctor/logs.test.ts +251 -0
- package/src/doctor/logs.ts +295 -0
- package/src/doctor/merge-queue.test.ts +216 -0
- package/src/doctor/merge-queue.ts +144 -0
- package/src/doctor/structure.test.ts +291 -0
- package/src/doctor/structure.ts +198 -0
- package/src/doctor/types.ts +37 -0
- package/src/doctor/version.test.ts +136 -0
- package/src/doctor/version.ts +129 -0
- package/src/e2e/init-sling-lifecycle.test.ts +277 -0
- package/src/errors.ts +217 -0
- package/src/events/store.test.ts +660 -0
- package/src/events/store.ts +369 -0
- package/src/events/tool-filter.test.ts +330 -0
- package/src/events/tool-filter.ts +126 -0
- package/src/index.ts +316 -0
- package/src/insights/analyzer.test.ts +466 -0
- package/src/insights/analyzer.ts +203 -0
- package/src/logging/color.test.ts +142 -0
- package/src/logging/color.ts +71 -0
- package/src/logging/logger.test.ts +813 -0
- package/src/logging/logger.ts +266 -0
- package/src/logging/reporter.test.ts +259 -0
- package/src/logging/reporter.ts +109 -0
- package/src/logging/sanitizer.test.ts +190 -0
- package/src/logging/sanitizer.ts +57 -0
- package/src/mail/broadcast.test.ts +203 -0
- package/src/mail/broadcast.ts +92 -0
- package/src/mail/client.test.ts +773 -0
- package/src/mail/client.ts +223 -0
- package/src/mail/store.test.ts +705 -0
- package/src/mail/store.ts +387 -0
- package/src/merge/queue.test.ts +359 -0
- package/src/merge/queue.ts +231 -0
- package/src/merge/resolver.test.ts +1345 -0
- package/src/merge/resolver.ts +645 -0
- package/src/metrics/store.test.ts +667 -0
- package/src/metrics/store.ts +445 -0
- package/src/metrics/summary.test.ts +398 -0
- package/src/metrics/summary.ts +178 -0
- package/src/metrics/transcript.test.ts +356 -0
- package/src/metrics/transcript.ts +175 -0
- package/src/mulch/client.test.ts +671 -0
- package/src/mulch/client.ts +332 -0
- package/src/sessions/compat.test.ts +280 -0
- package/src/sessions/compat.ts +104 -0
- package/src/sessions/store.test.ts +873 -0
- package/src/sessions/store.ts +494 -0
- package/src/test-helpers.test.ts +124 -0
- package/src/test-helpers.ts +126 -0
- package/src/tracker/beads.ts +56 -0
- package/src/tracker/factory.test.ts +80 -0
- package/src/tracker/factory.ts +64 -0
- package/src/tracker/seeds.ts +182 -0
- package/src/tracker/types.ts +52 -0
- package/src/types.ts +724 -0
- package/src/watchdog/daemon.test.ts +1975 -0
- package/src/watchdog/daemon.ts +671 -0
- package/src/watchdog/health.test.ts +431 -0
- package/src/watchdog/health.ts +264 -0
- package/src/watchdog/triage.test.ts +164 -0
- package/src/watchdog/triage.ts +179 -0
- package/src/worktree/manager.test.ts +439 -0
- package/src/worktree/manager.ts +198 -0
- package/src/worktree/tmux.test.ts +1009 -0
- package/src/worktree/tmux.ts +509 -0
- package/templates/CLAUDE.md.tmpl +89 -0
- package/templates/hooks.json.tmpl +105 -0
- 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
|
+
}
|