@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,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for agents doctor checks.
|
|
3
|
+
*
|
|
4
|
+
* Uses temp directories with real filesystem operations.
|
|
5
|
+
* No mocks needed -- all operations are cheap and local.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
9
|
+
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import type { OverstoryConfig } from "../types.ts";
|
|
13
|
+
import { checkAgents } from "./agents.ts";
|
|
14
|
+
|
|
15
|
+
describe("checkAgents", () => {
|
|
16
|
+
let tempDir: string;
|
|
17
|
+
let overstoryDir: string;
|
|
18
|
+
let mockConfig: OverstoryConfig;
|
|
19
|
+
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
tempDir = await mkdtemp(join(tmpdir(), "agents-test-"));
|
|
22
|
+
overstoryDir = join(tempDir, ".overstory");
|
|
23
|
+
await mkdir(overstoryDir, { recursive: true });
|
|
24
|
+
|
|
25
|
+
mockConfig = {
|
|
26
|
+
project: {
|
|
27
|
+
name: "test-project",
|
|
28
|
+
root: tempDir,
|
|
29
|
+
canonicalBranch: "main",
|
|
30
|
+
},
|
|
31
|
+
agents: {
|
|
32
|
+
manifestPath: ".overstory/agent-manifest.json",
|
|
33
|
+
baseDir: ".overstory/agent-defs",
|
|
34
|
+
maxConcurrent: 5,
|
|
35
|
+
staggerDelayMs: 1000,
|
|
36
|
+
maxDepth: 2,
|
|
37
|
+
maxSessionsPerRun: 0,
|
|
38
|
+
},
|
|
39
|
+
worktrees: {
|
|
40
|
+
baseDir: ".overstory/worktrees",
|
|
41
|
+
},
|
|
42
|
+
taskTracker: {
|
|
43
|
+
backend: "auto",
|
|
44
|
+
enabled: true,
|
|
45
|
+
},
|
|
46
|
+
mulch: {
|
|
47
|
+
enabled: true,
|
|
48
|
+
domains: [],
|
|
49
|
+
primeFormat: "markdown",
|
|
50
|
+
},
|
|
51
|
+
merge: {
|
|
52
|
+
aiResolveEnabled: false,
|
|
53
|
+
reimagineEnabled: false,
|
|
54
|
+
},
|
|
55
|
+
providers: {
|
|
56
|
+
anthropic: { type: "native" },
|
|
57
|
+
},
|
|
58
|
+
watchdog: {
|
|
59
|
+
tier0Enabled: true,
|
|
60
|
+
tier0IntervalMs: 30000,
|
|
61
|
+
tier1Enabled: false,
|
|
62
|
+
tier2Enabled: false,
|
|
63
|
+
staleThresholdMs: 300000,
|
|
64
|
+
zombieThresholdMs: 600000,
|
|
65
|
+
nudgeIntervalMs: 60000,
|
|
66
|
+
},
|
|
67
|
+
models: {},
|
|
68
|
+
logging: {
|
|
69
|
+
verbose: false,
|
|
70
|
+
redactSecrets: true,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
afterEach(async () => {
|
|
76
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("fails when manifest is missing", async () => {
|
|
80
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
81
|
+
|
|
82
|
+
const parseCheck = checks.find((c) => c.name === "Manifest parsing");
|
|
83
|
+
expect(parseCheck).toBeDefined();
|
|
84
|
+
expect(parseCheck?.status).toBe("fail");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("fails when manifest has invalid JSON", async () => {
|
|
88
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), "invalid json{");
|
|
89
|
+
|
|
90
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
91
|
+
|
|
92
|
+
const parseCheck = checks.find((c) => c.name === "Manifest parsing");
|
|
93
|
+
expect(parseCheck?.status).toBe("fail");
|
|
94
|
+
expect(parseCheck?.details?.some((d) => d.includes("JSON"))).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("passes when manifest is valid", async () => {
|
|
98
|
+
const manifest = {
|
|
99
|
+
version: "1.0",
|
|
100
|
+
agents: {
|
|
101
|
+
scout: {
|
|
102
|
+
file: "scout.md",
|
|
103
|
+
model: "haiku",
|
|
104
|
+
tools: ["Read"],
|
|
105
|
+
capabilities: ["explore"],
|
|
106
|
+
canSpawn: false,
|
|
107
|
+
constraints: [],
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
capabilityIndex: {
|
|
111
|
+
explore: ["scout"],
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
await mkdir(join(overstoryDir, "agent-defs"), { recursive: true });
|
|
116
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
117
|
+
await Bun.write(join(overstoryDir, "agent-defs", "scout.md"), "# Scout");
|
|
118
|
+
|
|
119
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
120
|
+
|
|
121
|
+
const parseCheck = checks.find((c) => c.name === "Manifest parsing");
|
|
122
|
+
expect(parseCheck?.status).toBe("pass");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("fails when agent has invalid model", async () => {
|
|
126
|
+
const manifest = {
|
|
127
|
+
version: "1.0",
|
|
128
|
+
agents: {
|
|
129
|
+
scout: {
|
|
130
|
+
file: "scout.md",
|
|
131
|
+
model: "invalid-model",
|
|
132
|
+
tools: ["Read"],
|
|
133
|
+
capabilities: ["explore"],
|
|
134
|
+
canSpawn: false,
|
|
135
|
+
constraints: [],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
capabilityIndex: {
|
|
139
|
+
explore: ["scout"],
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
144
|
+
|
|
145
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
146
|
+
|
|
147
|
+
const parseCheck = checks.find((c) => c.name === "Manifest parsing");
|
|
148
|
+
expect(parseCheck?.status).toBe("fail");
|
|
149
|
+
expect(parseCheck?.details?.some((d) => d.includes("model"))).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("fails when agent has zero capabilities", async () => {
|
|
153
|
+
const manifest = {
|
|
154
|
+
version: "1.0",
|
|
155
|
+
agents: {
|
|
156
|
+
scout: {
|
|
157
|
+
file: "scout.md",
|
|
158
|
+
model: "haiku",
|
|
159
|
+
tools: ["Read"],
|
|
160
|
+
capabilities: [],
|
|
161
|
+
canSpawn: false,
|
|
162
|
+
constraints: [],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
capabilityIndex: {},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
169
|
+
|
|
170
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
171
|
+
|
|
172
|
+
const parseCheck = checks.find((c) => c.name === "Manifest parsing");
|
|
173
|
+
expect(parseCheck?.status).toBe("fail");
|
|
174
|
+
expect(parseCheck?.details?.some((d) => d.includes("capability"))).toBe(true);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("fails when referenced .md file is missing", async () => {
|
|
178
|
+
const manifest = {
|
|
179
|
+
version: "1.0",
|
|
180
|
+
agents: {
|
|
181
|
+
scout: {
|
|
182
|
+
file: "scout.md",
|
|
183
|
+
model: "haiku",
|
|
184
|
+
tools: ["Read"],
|
|
185
|
+
capabilities: ["explore"],
|
|
186
|
+
canSpawn: false,
|
|
187
|
+
constraints: [],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
capabilityIndex: {
|
|
191
|
+
explore: ["scout"],
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
await mkdir(join(overstoryDir, "agent-defs"), { recursive: true });
|
|
196
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
197
|
+
// Don't create scout.md
|
|
198
|
+
|
|
199
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
200
|
+
|
|
201
|
+
const filesCheck = checks.find((c) => c.name === "Agent definition files");
|
|
202
|
+
expect(filesCheck?.status).toBe("fail");
|
|
203
|
+
expect(filesCheck?.details?.some((d) => d.includes("scout.md"))).toBe(true);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test("warns when capability index is inconsistent", async () => {
|
|
207
|
+
const manifest = {
|
|
208
|
+
version: "1.0",
|
|
209
|
+
agents: {
|
|
210
|
+
scout: {
|
|
211
|
+
file: "scout.md",
|
|
212
|
+
model: "haiku",
|
|
213
|
+
tools: ["Read"],
|
|
214
|
+
capabilities: ["explore", "research"],
|
|
215
|
+
canSpawn: false,
|
|
216
|
+
constraints: [],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
capabilityIndex: {
|
|
220
|
+
explore: ["scout"],
|
|
221
|
+
// Missing "research" from index
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
await mkdir(join(overstoryDir, "agent-defs"), { recursive: true });
|
|
226
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
227
|
+
await Bun.write(join(overstoryDir, "agent-defs", "scout.md"), "# Scout");
|
|
228
|
+
|
|
229
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
230
|
+
|
|
231
|
+
const indexCheck = checks.find((c) => c.name === "Capability index");
|
|
232
|
+
expect(indexCheck?.status).toBe("warn");
|
|
233
|
+
expect(indexCheck?.details?.some((d) => d.includes("research"))).toBe(true);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("passes when no identity files exist", async () => {
|
|
237
|
+
const manifest = {
|
|
238
|
+
version: "1.0",
|
|
239
|
+
agents: {
|
|
240
|
+
scout: {
|
|
241
|
+
file: "scout.md",
|
|
242
|
+
model: "haiku",
|
|
243
|
+
tools: ["Read"],
|
|
244
|
+
capabilities: ["explore"],
|
|
245
|
+
canSpawn: false,
|
|
246
|
+
constraints: [],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
capabilityIndex: {
|
|
250
|
+
explore: ["scout"],
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
await mkdir(join(overstoryDir, "agent-defs"), { recursive: true });
|
|
255
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
256
|
+
await Bun.write(join(overstoryDir, "agent-defs", "scout.md"), "# Scout");
|
|
257
|
+
|
|
258
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
259
|
+
|
|
260
|
+
const identityCheck = checks.find((c) => c.name === "Agent identities");
|
|
261
|
+
expect(identityCheck?.status).toBe("pass");
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("validates identity files correctly", async () => {
|
|
265
|
+
const manifest = {
|
|
266
|
+
version: "1.0",
|
|
267
|
+
agents: {
|
|
268
|
+
scout: {
|
|
269
|
+
file: "scout.md",
|
|
270
|
+
model: "haiku",
|
|
271
|
+
tools: ["Read"],
|
|
272
|
+
capabilities: ["explore"],
|
|
273
|
+
canSpawn: false,
|
|
274
|
+
constraints: [],
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
capabilityIndex: {
|
|
278
|
+
explore: ["scout"],
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
await mkdir(join(overstoryDir, "agent-defs"), { recursive: true });
|
|
283
|
+
await mkdir(join(overstoryDir, "agents", "scout"), { recursive: true });
|
|
284
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
285
|
+
await Bun.write(join(overstoryDir, "agent-defs", "scout.md"), "# Scout");
|
|
286
|
+
|
|
287
|
+
const identity = `name: scout
|
|
288
|
+
capability: explore
|
|
289
|
+
created: "2024-01-01T00:00:00Z"
|
|
290
|
+
sessionsCompleted: 5
|
|
291
|
+
expertiseDomains: []
|
|
292
|
+
recentTasks: []
|
|
293
|
+
`;
|
|
294
|
+
|
|
295
|
+
await Bun.write(join(overstoryDir, "agents", "scout", "identity.yaml"), identity);
|
|
296
|
+
|
|
297
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
298
|
+
|
|
299
|
+
const identityCheck = checks.find((c) => c.name === "Identity validation");
|
|
300
|
+
expect(identityCheck?.status).toBe("pass");
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("warns when identity has invalid timestamp", async () => {
|
|
304
|
+
const manifest = {
|
|
305
|
+
version: "1.0",
|
|
306
|
+
agents: {
|
|
307
|
+
scout: {
|
|
308
|
+
file: "scout.md",
|
|
309
|
+
model: "haiku",
|
|
310
|
+
tools: ["Read"],
|
|
311
|
+
capabilities: ["explore"],
|
|
312
|
+
canSpawn: false,
|
|
313
|
+
constraints: [],
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
capabilityIndex: {
|
|
317
|
+
explore: ["scout"],
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
await mkdir(join(overstoryDir, "agent-defs"), { recursive: true });
|
|
322
|
+
await mkdir(join(overstoryDir, "agents", "scout"), { recursive: true });
|
|
323
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
324
|
+
await Bun.write(join(overstoryDir, "agent-defs", "scout.md"), "# Scout");
|
|
325
|
+
|
|
326
|
+
const identity = `name: scout
|
|
327
|
+
capability: explore
|
|
328
|
+
created: "invalid-timestamp"
|
|
329
|
+
sessionsCompleted: 5
|
|
330
|
+
`;
|
|
331
|
+
|
|
332
|
+
await Bun.write(join(overstoryDir, "agents", "scout", "identity.yaml"), identity);
|
|
333
|
+
|
|
334
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
335
|
+
|
|
336
|
+
const identityCheck = checks.find((c) => c.name === "Identity validation");
|
|
337
|
+
expect(identityCheck?.status).toBe("warn");
|
|
338
|
+
expect(identityCheck?.details?.some((d) => d.includes("timestamp"))).toBe(true);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("warns when identity has negative sessionsCompleted", async () => {
|
|
342
|
+
const manifest = {
|
|
343
|
+
version: "1.0",
|
|
344
|
+
agents: {
|
|
345
|
+
scout: {
|
|
346
|
+
file: "scout.md",
|
|
347
|
+
model: "haiku",
|
|
348
|
+
tools: ["Read"],
|
|
349
|
+
capabilities: ["explore"],
|
|
350
|
+
canSpawn: false,
|
|
351
|
+
constraints: [],
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
capabilityIndex: {
|
|
355
|
+
explore: ["scout"],
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
await mkdir(join(overstoryDir, "agent-defs"), { recursive: true });
|
|
360
|
+
await mkdir(join(overstoryDir, "agents", "scout"), { recursive: true });
|
|
361
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
362
|
+
await Bun.write(join(overstoryDir, "agent-defs", "scout.md"), "# Scout");
|
|
363
|
+
|
|
364
|
+
const identity = `name: scout
|
|
365
|
+
capability: explore
|
|
366
|
+
created: "2024-01-01T00:00:00Z"
|
|
367
|
+
sessionsCompleted: -5
|
|
368
|
+
`;
|
|
369
|
+
|
|
370
|
+
await Bun.write(join(overstoryDir, "agents", "scout", "identity.yaml"), identity);
|
|
371
|
+
|
|
372
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
373
|
+
|
|
374
|
+
const identityCheck = checks.find((c) => c.name === "Identity validation");
|
|
375
|
+
expect(identityCheck?.status).toBe("warn");
|
|
376
|
+
expect(identityCheck?.details?.some((d) => d.includes("sessionsCompleted"))).toBe(true);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("warns about stale identity files", async () => {
|
|
380
|
+
const manifest = {
|
|
381
|
+
version: "1.0",
|
|
382
|
+
agents: {
|
|
383
|
+
scout: {
|
|
384
|
+
file: "scout.md",
|
|
385
|
+
model: "haiku",
|
|
386
|
+
tools: ["Read"],
|
|
387
|
+
capabilities: ["explore"],
|
|
388
|
+
canSpawn: false,
|
|
389
|
+
constraints: [],
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
capabilityIndex: {
|
|
393
|
+
explore: ["scout"],
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
await mkdir(join(overstoryDir, "agent-defs"), { recursive: true });
|
|
398
|
+
await mkdir(join(overstoryDir, "agents", "old-agent"), { recursive: true });
|
|
399
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
400
|
+
await Bun.write(join(overstoryDir, "agent-defs", "scout.md"), "# Scout");
|
|
401
|
+
|
|
402
|
+
const staleIdentity = `name: old-agent
|
|
403
|
+
capability: obsolete
|
|
404
|
+
created: "2024-01-01T00:00:00Z"
|
|
405
|
+
sessionsCompleted: 5
|
|
406
|
+
`;
|
|
407
|
+
|
|
408
|
+
await Bun.write(join(overstoryDir, "agents", "old-agent", "identity.yaml"), staleIdentity);
|
|
409
|
+
|
|
410
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
411
|
+
|
|
412
|
+
const staleCheck = checks.find((c) => c.name === "Stale identities");
|
|
413
|
+
expect(staleCheck?.status).toBe("warn");
|
|
414
|
+
expect(staleCheck?.details?.some((d) => d.includes("old-agent"))).toBe(true);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
test("warns when identity name contains invalid characters", async () => {
|
|
418
|
+
const manifest = {
|
|
419
|
+
version: "1.0",
|
|
420
|
+
agents: {
|
|
421
|
+
scout: {
|
|
422
|
+
file: "scout.md",
|
|
423
|
+
model: "haiku",
|
|
424
|
+
tools: ["Read"],
|
|
425
|
+
capabilities: ["explore"],
|
|
426
|
+
canSpawn: false,
|
|
427
|
+
constraints: [],
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
capabilityIndex: {
|
|
431
|
+
explore: ["scout"],
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
await mkdir(join(overstoryDir, "agent-defs"), { recursive: true });
|
|
436
|
+
await mkdir(join(overstoryDir, "agents", "scout"), { recursive: true });
|
|
437
|
+
await Bun.write(join(overstoryDir, "agent-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
438
|
+
await Bun.write(join(overstoryDir, "agent-defs", "scout.md"), "# Scout");
|
|
439
|
+
|
|
440
|
+
const identity = `name: "scout@invalid!"
|
|
441
|
+
capability: explore
|
|
442
|
+
created: "2024-01-01T00:00:00Z"
|
|
443
|
+
sessionsCompleted: 5
|
|
444
|
+
`;
|
|
445
|
+
|
|
446
|
+
await Bun.write(join(overstoryDir, "agents", "scout", "identity.yaml"), identity);
|
|
447
|
+
|
|
448
|
+
const checks = await checkAgents(mockConfig, overstoryDir);
|
|
449
|
+
|
|
450
|
+
const identityCheck = checks.find((c) => c.name === "Identity validation");
|
|
451
|
+
expect(identityCheck?.status).toBe("warn");
|
|
452
|
+
expect(identityCheck?.details?.some((d) => d.includes("invalid characters"))).toBe(true);
|
|
453
|
+
});
|
|
454
|
+
});
|