@os-eco/overstory-cli 0.6.1 → 0.6.5
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/README.md +8 -7
- package/package.json +12 -4
- package/src/agents/checkpoint.test.ts +2 -2
- package/src/agents/hooks-deployer.test.ts +131 -16
- package/src/agents/hooks-deployer.ts +33 -1
- package/src/agents/identity.test.ts +27 -27
- package/src/agents/identity.ts +10 -10
- package/src/agents/lifecycle.test.ts +6 -6
- package/src/agents/lifecycle.ts +2 -2
- package/src/agents/manifest.test.ts +86 -0
- package/src/agents/overlay.test.ts +9 -9
- package/src/agents/overlay.ts +4 -4
- package/src/commands/agents.test.ts +8 -8
- package/src/commands/agents.ts +62 -91
- package/src/commands/clean.test.ts +36 -51
- package/src/commands/clean.ts +28 -49
- package/src/commands/completions.ts +14 -0
- package/src/commands/coordinator.test.ts +133 -26
- package/src/commands/coordinator.ts +101 -64
- package/src/commands/costs.test.ts +47 -47
- package/src/commands/costs.ts +96 -75
- package/src/commands/dashboard.test.ts +2 -2
- package/src/commands/dashboard.ts +75 -95
- package/src/commands/doctor.test.ts +2 -2
- package/src/commands/doctor.ts +92 -79
- package/src/commands/errors.test.ts +2 -2
- package/src/commands/errors.ts +56 -50
- package/src/commands/feed.test.ts +2 -2
- package/src/commands/feed.ts +86 -83
- package/src/commands/group.ts +167 -177
- package/src/commands/hooks.test.ts +2 -2
- package/src/commands/hooks.ts +52 -42
- package/src/commands/init.test.ts +19 -19
- package/src/commands/init.ts +7 -16
- package/src/commands/inspect.test.ts +18 -18
- package/src/commands/inspect.ts +55 -58
- package/src/commands/log.test.ts +26 -31
- package/src/commands/log.ts +97 -91
- package/src/commands/logs.test.ts +1 -1
- package/src/commands/logs.ts +101 -104
- package/src/commands/mail.test.ts +5 -5
- package/src/commands/mail.ts +157 -169
- package/src/commands/merge.test.ts +28 -66
- package/src/commands/merge.ts +21 -51
- package/src/commands/metrics.test.ts +8 -8
- package/src/commands/metrics.ts +34 -35
- package/src/commands/monitor.test.ts +3 -3
- package/src/commands/monitor.ts +57 -62
- package/src/commands/nudge.test.ts +1 -1
- package/src/commands/nudge.ts +41 -89
- package/src/commands/prime.test.ts +19 -51
- package/src/commands/prime.ts +13 -50
- package/src/commands/replay.test.ts +2 -2
- package/src/commands/replay.ts +79 -86
- package/src/commands/run.test.ts +1 -1
- package/src/commands/run.ts +97 -77
- package/src/commands/sling.test.ts +201 -5
- package/src/commands/sling.ts +37 -64
- package/src/commands/spec.test.ts +14 -40
- package/src/commands/spec.ts +32 -101
- package/src/commands/status.test.ts +97 -1
- package/src/commands/status.ts +63 -58
- package/src/commands/stop.test.ts +22 -40
- package/src/commands/stop.ts +18 -33
- package/src/commands/supervisor.test.ts +12 -14
- package/src/commands/supervisor.ts +144 -165
- package/src/commands/trace.test.ts +15 -15
- package/src/commands/trace.ts +59 -82
- package/src/commands/watch.test.ts +2 -2
- package/src/commands/watch.ts +38 -45
- package/src/commands/worktree.test.ts +213 -37
- package/src/commands/worktree.ts +110 -55
- package/src/config.test.ts +96 -0
- package/src/doctor/consistency.test.ts +14 -14
- package/src/doctor/databases.test.ts +22 -2
- package/src/doctor/databases.ts +16 -0
- package/src/doctor/dependencies.test.ts +55 -1
- package/src/doctor/dependencies.ts +113 -18
- package/src/doctor/merge-queue.test.ts +4 -4
- package/src/e2e/init-sling-lifecycle.test.ts +8 -8
- package/src/errors.ts +1 -1
- package/src/index.ts +223 -213
- package/src/logging/color.test.ts +74 -91
- package/src/logging/color.ts +52 -46
- package/src/logging/reporter.test.ts +10 -10
- package/src/logging/reporter.ts +6 -5
- package/src/mail/broadcast.test.ts +1 -1
- package/src/mail/client.test.ts +6 -6
- package/src/mail/store.test.ts +3 -3
- package/src/merge/queue.test.ts +73 -7
- package/src/merge/queue.ts +17 -2
- package/src/merge/resolver.test.ts +159 -7
- package/src/merge/resolver.ts +46 -2
- package/src/metrics/store.test.ts +44 -44
- package/src/metrics/store.ts +2 -2
- package/src/metrics/summary.test.ts +35 -35
- package/src/mulch/client.test.ts +1 -1
- package/src/schema-consistency.test.ts +239 -0
- package/src/sessions/compat.test.ts +3 -3
- package/src/sessions/compat.ts +2 -2
- package/src/sessions/store.test.ts +41 -4
- package/src/sessions/store.ts +13 -2
- package/src/types.ts +14 -14
- package/src/watchdog/daemon.test.ts +10 -10
- package/src/watchdog/daemon.ts +1 -1
- package/src/watchdog/health.test.ts +1 -1
- package/src/worktree/manager.test.ts +20 -20
- package/src/worktree/manager.ts +120 -4
- package/src/worktree/tmux.test.ts +98 -9
- package/src/worktree/tmux.ts +18 -0
|
@@ -47,7 +47,7 @@ describe("initCommand: agent-defs deployment", () => {
|
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
test("creates .overstory/agent-defs/ with all 8 agent definition files", async () => {
|
|
50
|
-
await initCommand(
|
|
50
|
+
await initCommand({});
|
|
51
51
|
|
|
52
52
|
const agentDefsDir = join(tempDir, ".overstory", "agent-defs");
|
|
53
53
|
const files = await readdir(agentDefsDir);
|
|
@@ -57,7 +57,7 @@ describe("initCommand: agent-defs deployment", () => {
|
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
test("copied files match source content", async () => {
|
|
60
|
-
await initCommand(
|
|
60
|
+
await initCommand({});
|
|
61
61
|
|
|
62
62
|
for (const fileName of AGENT_DEF_FILES) {
|
|
63
63
|
const sourcePath = join(SOURCE_AGENTS_DIR, fileName);
|
|
@@ -72,7 +72,7 @@ describe("initCommand: agent-defs deployment", () => {
|
|
|
72
72
|
|
|
73
73
|
test("--force reinit overwrites existing agent def files", async () => {
|
|
74
74
|
// First init
|
|
75
|
-
await initCommand(
|
|
75
|
+
await initCommand({});
|
|
76
76
|
|
|
77
77
|
// Tamper with one of the deployed files
|
|
78
78
|
const tamperPath = join(tempDir, ".overstory", "agent-defs", "scout.md");
|
|
@@ -83,7 +83,7 @@ describe("initCommand: agent-defs deployment", () => {
|
|
|
83
83
|
expect(tampered).toBe("# tampered content\n");
|
|
84
84
|
|
|
85
85
|
// Reinit with --force
|
|
86
|
-
await initCommand(
|
|
86
|
+
await initCommand({ force: true });
|
|
87
87
|
|
|
88
88
|
// Verify the file was overwritten with the original source
|
|
89
89
|
const sourceContent = await Bun.file(join(SOURCE_AGENTS_DIR, "scout.md")).text();
|
|
@@ -92,7 +92,7 @@ describe("initCommand: agent-defs deployment", () => {
|
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
test("Stop hook includes mulch learn command", async () => {
|
|
95
|
-
await initCommand(
|
|
95
|
+
await initCommand({});
|
|
96
96
|
|
|
97
97
|
const hooksPath = join(tempDir, ".overstory", "hooks.json");
|
|
98
98
|
const content = await Bun.file(hooksPath).text();
|
|
@@ -105,7 +105,7 @@ describe("initCommand: agent-defs deployment", () => {
|
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
test("PostToolUse hooks include Bash-matched mulch diff hook", async () => {
|
|
108
|
-
await initCommand(
|
|
108
|
+
await initCommand({});
|
|
109
109
|
|
|
110
110
|
const hooksPath = join(tempDir, ".overstory", "hooks.json");
|
|
111
111
|
const content = await Bun.file(hooksPath).text();
|
|
@@ -147,7 +147,7 @@ describe("initCommand: .overstory/.gitignore", () => {
|
|
|
147
147
|
});
|
|
148
148
|
|
|
149
149
|
test("creates .overstory/.gitignore with wildcard+whitelist model", async () => {
|
|
150
|
-
await initCommand(
|
|
150
|
+
await initCommand({});
|
|
151
151
|
|
|
152
152
|
const gitignorePath = join(tempDir, ".overstory", ".gitignore");
|
|
153
153
|
const content = await Bun.file(gitignorePath).text();
|
|
@@ -167,7 +167,7 @@ describe("initCommand: .overstory/.gitignore", () => {
|
|
|
167
167
|
|
|
168
168
|
test("gitignore is always written when init completes", async () => {
|
|
169
169
|
// Init should write gitignore
|
|
170
|
-
await initCommand(
|
|
170
|
+
await initCommand({});
|
|
171
171
|
|
|
172
172
|
const gitignorePath = join(tempDir, ".overstory", ".gitignore");
|
|
173
173
|
const content = await Bun.file(gitignorePath).text();
|
|
@@ -182,7 +182,7 @@ describe("initCommand: .overstory/.gitignore", () => {
|
|
|
182
182
|
|
|
183
183
|
test("--force reinit overwrites stale .overstory/.gitignore", async () => {
|
|
184
184
|
// First init
|
|
185
|
-
await initCommand(
|
|
185
|
+
await initCommand({});
|
|
186
186
|
|
|
187
187
|
const gitignorePath = join(tempDir, ".overstory", ".gitignore");
|
|
188
188
|
|
|
@@ -195,7 +195,7 @@ describe("initCommand: .overstory/.gitignore", () => {
|
|
|
195
195
|
expect(tampered).not.toContain("!.gitignore\n");
|
|
196
196
|
|
|
197
197
|
// Reinit with --force
|
|
198
|
-
await initCommand(
|
|
198
|
+
await initCommand({ force: true });
|
|
199
199
|
|
|
200
200
|
// Verify the file was overwritten with the new wildcard+whitelist format
|
|
201
201
|
const restored = await Bun.file(gitignorePath).text();
|
|
@@ -206,7 +206,7 @@ describe("initCommand: .overstory/.gitignore", () => {
|
|
|
206
206
|
|
|
207
207
|
test("subsequent init without --force does not overwrite gitignore", async () => {
|
|
208
208
|
// First init
|
|
209
|
-
await initCommand(
|
|
209
|
+
await initCommand({});
|
|
210
210
|
|
|
211
211
|
const gitignorePath = join(tempDir, ".overstory", ".gitignore");
|
|
212
212
|
|
|
@@ -218,7 +218,7 @@ describe("initCommand: .overstory/.gitignore", () => {
|
|
|
218
218
|
expect(tampered).toBe("# custom content\n");
|
|
219
219
|
|
|
220
220
|
// Second init without --force should return early (not overwrite)
|
|
221
|
-
await initCommand(
|
|
221
|
+
await initCommand({});
|
|
222
222
|
|
|
223
223
|
// Verify the file was NOT overwritten (early return prevented it)
|
|
224
224
|
const afterSecondInit = await Bun.file(gitignorePath).text();
|
|
@@ -248,7 +248,7 @@ describe("initCommand: .overstory/README.md", () => {
|
|
|
248
248
|
});
|
|
249
249
|
|
|
250
250
|
test("creates .overstory/README.md with expected content", async () => {
|
|
251
|
-
await initCommand(
|
|
251
|
+
await initCommand({});
|
|
252
252
|
|
|
253
253
|
const readmePath = join(tempDir, ".overstory", "README.md");
|
|
254
254
|
const exists = await Bun.file(readmePath).exists();
|
|
@@ -264,7 +264,7 @@ describe("initCommand: .overstory/README.md", () => {
|
|
|
264
264
|
|
|
265
265
|
test("--force reinit overwrites README.md", async () => {
|
|
266
266
|
// First init
|
|
267
|
-
await initCommand(
|
|
267
|
+
await initCommand({});
|
|
268
268
|
|
|
269
269
|
const readmePath = join(tempDir, ".overstory", "README.md");
|
|
270
270
|
|
|
@@ -274,7 +274,7 @@ describe("initCommand: .overstory/README.md", () => {
|
|
|
274
274
|
expect(tampered).toBe("# tampered\n");
|
|
275
275
|
|
|
276
276
|
// Reinit with --force
|
|
277
|
-
await initCommand(
|
|
277
|
+
await initCommand({ force: true });
|
|
278
278
|
|
|
279
279
|
// Verify restored to canonical content
|
|
280
280
|
const restored = await Bun.file(readmePath).text();
|
|
@@ -283,7 +283,7 @@ describe("initCommand: .overstory/README.md", () => {
|
|
|
283
283
|
|
|
284
284
|
test("subsequent init without --force does not overwrite README.md", async () => {
|
|
285
285
|
// First init
|
|
286
|
-
await initCommand(
|
|
286
|
+
await initCommand({});
|
|
287
287
|
|
|
288
288
|
const readmePath = join(tempDir, ".overstory", "README.md");
|
|
289
289
|
|
|
@@ -293,7 +293,7 @@ describe("initCommand: .overstory/README.md", () => {
|
|
|
293
293
|
expect(tampered).toBe("# custom content\n");
|
|
294
294
|
|
|
295
295
|
// Second init without --force returns early
|
|
296
|
-
await initCommand(
|
|
296
|
+
await initCommand({});
|
|
297
297
|
|
|
298
298
|
// Verify tampered content preserved (early return)
|
|
299
299
|
const afterSecondInit = await Bun.file(readmePath).text();
|
|
@@ -329,7 +329,7 @@ describe("initCommand: canonical branch detection", () => {
|
|
|
329
329
|
// Switch to a non-standard branch name
|
|
330
330
|
await runGitInDir(tempDir, ["switch", "-c", "trunk"]);
|
|
331
331
|
|
|
332
|
-
await initCommand(
|
|
332
|
+
await initCommand({});
|
|
333
333
|
|
|
334
334
|
const configPath = join(tempDir, ".overstory", "config.yaml");
|
|
335
335
|
const content = await Bun.file(configPath).text();
|
|
@@ -338,7 +338,7 @@ describe("initCommand: canonical branch detection", () => {
|
|
|
338
338
|
|
|
339
339
|
test("standard branch names (main) still work as canonicalBranch", async () => {
|
|
340
340
|
// createTempGitRepo defaults to main branch
|
|
341
|
-
await initCommand(
|
|
341
|
+
await initCommand({});
|
|
342
342
|
|
|
343
343
|
const configPath = join(tempDir, ".overstory", "config.yaml");
|
|
344
344
|
const content = await Bun.file(configPath).text();
|
package/src/commands/init.ts
CHANGED
|
@@ -514,6 +514,10 @@ export async function writeOverstoryReadme(overstoryPath: string): Promise<void>
|
|
|
514
514
|
await Bun.write(readmePath, OVERSTORY_README);
|
|
515
515
|
}
|
|
516
516
|
|
|
517
|
+
export interface InitOptions {
|
|
518
|
+
force?: boolean;
|
|
519
|
+
}
|
|
520
|
+
|
|
517
521
|
/**
|
|
518
522
|
* Print a success status line.
|
|
519
523
|
*/
|
|
@@ -526,23 +530,10 @@ function printCreated(relativePath: string): void {
|
|
|
526
530
|
*
|
|
527
531
|
* Scaffolds the .overstory/ directory structure in the current working directory.
|
|
528
532
|
*
|
|
529
|
-
* @param
|
|
533
|
+
* @param opts - Command options
|
|
530
534
|
*/
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
Usage: overstory init [--force]
|
|
534
|
-
|
|
535
|
-
Options:
|
|
536
|
-
--force Reinitialize even if .overstory/ already exists
|
|
537
|
-
--help, -h Show this help`;
|
|
538
|
-
|
|
539
|
-
export async function initCommand(args: string[]): Promise<void> {
|
|
540
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
541
|
-
process.stdout.write(`${INIT_HELP}\n`);
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
const force = args.includes("--force");
|
|
535
|
+
export async function initCommand(opts: InitOptions): Promise<void> {
|
|
536
|
+
const force = opts.force ?? false;
|
|
546
537
|
const projectRoot = process.cwd();
|
|
547
538
|
const overstoryPath = join(projectRoot, OVERSTORY_DIR);
|
|
548
539
|
|
|
@@ -39,7 +39,7 @@ function makeEvent(overrides: Partial<InsertEvent> = {}): InsertEvent {
|
|
|
39
39
|
function makeMetrics(overrides: Partial<SessionMetrics> = {}): SessionMetrics {
|
|
40
40
|
return {
|
|
41
41
|
agentName: "builder-1",
|
|
42
|
-
|
|
42
|
+
taskId: "overstory-001",
|
|
43
43
|
capability: "builder",
|
|
44
44
|
startedAt: new Date().toISOString(),
|
|
45
45
|
completedAt: null,
|
|
@@ -102,7 +102,7 @@ describe("inspectCommand", () => {
|
|
|
102
102
|
test("--help shows help text", async () => {
|
|
103
103
|
await inspectCommand(["--help"]);
|
|
104
104
|
const out = output();
|
|
105
|
-
expect(out).toContain("
|
|
105
|
+
expect(out).toContain("inspect");
|
|
106
106
|
expect(out).toContain("--json");
|
|
107
107
|
expect(out).toContain("--follow");
|
|
108
108
|
expect(out).toContain("--limit");
|
|
@@ -112,7 +112,7 @@ describe("inspectCommand", () => {
|
|
|
112
112
|
test("-h shows help text", async () => {
|
|
113
113
|
await inspectCommand(["-h"]);
|
|
114
114
|
const out = output();
|
|
115
|
-
expect(out).toContain("
|
|
115
|
+
expect(out).toContain("inspect");
|
|
116
116
|
});
|
|
117
117
|
});
|
|
118
118
|
|
|
@@ -142,7 +142,7 @@ describe("inspectCommand", () => {
|
|
|
142
142
|
capability: "builder",
|
|
143
143
|
worktreePath: "/tmp/wt",
|
|
144
144
|
branchName: "overstory/builder-1/test",
|
|
145
|
-
|
|
145
|
+
taskId: "overstory-001",
|
|
146
146
|
tmuxSession: "overstory-test-builder-1",
|
|
147
147
|
state: "working",
|
|
148
148
|
pid: 12345,
|
|
@@ -174,7 +174,7 @@ describe("inspectCommand", () => {
|
|
|
174
174
|
capability: "builder",
|
|
175
175
|
worktreePath: "/tmp/wt",
|
|
176
176
|
branchName: "overstory/builder-1/test",
|
|
177
|
-
|
|
177
|
+
taskId: "overstory-001",
|
|
178
178
|
tmuxSession: "overstory-test-builder-1",
|
|
179
179
|
state: "working",
|
|
180
180
|
pid: 12345,
|
|
@@ -210,7 +210,7 @@ describe("inspectCommand", () => {
|
|
|
210
210
|
capability: "builder",
|
|
211
211
|
worktreePath: "/tmp/wt",
|
|
212
212
|
branchName: "overstory/builder-1/test",
|
|
213
|
-
|
|
213
|
+
taskId: "overstory-001",
|
|
214
214
|
tmuxSession: "overstory-test-builder-1",
|
|
215
215
|
state: "working",
|
|
216
216
|
pid: 12345,
|
|
@@ -229,7 +229,7 @@ describe("inspectCommand", () => {
|
|
|
229
229
|
expect(data.session.agentName).toBe("builder-1");
|
|
230
230
|
expect(data.session.capability).toBe("builder");
|
|
231
231
|
expect(data.session.state).toBe("working");
|
|
232
|
-
expect(data.session.
|
|
232
|
+
expect(data.session.taskId).toBe("overstory-001");
|
|
233
233
|
expect(data.timeSinceLastActivity).toBeGreaterThan(4000);
|
|
234
234
|
expect(data.timeSinceLastActivity).toBeLessThan(10000);
|
|
235
235
|
});
|
|
@@ -246,7 +246,7 @@ describe("inspectCommand", () => {
|
|
|
246
246
|
capability: "builder",
|
|
247
247
|
worktreePath: "/tmp/wt",
|
|
248
248
|
branchName: "overstory/builder-1/test",
|
|
249
|
-
|
|
249
|
+
taskId: "overstory-001",
|
|
250
250
|
tmuxSession: "overstory-test-builder-1",
|
|
251
251
|
state: "working",
|
|
252
252
|
pid: 12345,
|
|
@@ -285,7 +285,7 @@ describe("inspectCommand", () => {
|
|
|
285
285
|
capability: "builder",
|
|
286
286
|
worktreePath: "/tmp/wt",
|
|
287
287
|
branchName: "overstory/builder-1/test",
|
|
288
|
-
|
|
288
|
+
taskId: "overstory-001",
|
|
289
289
|
tmuxSession: "overstory-test-builder-1",
|
|
290
290
|
state: "working",
|
|
291
291
|
pid: 12345,
|
|
@@ -320,7 +320,7 @@ describe("inspectCommand", () => {
|
|
|
320
320
|
capability: "builder",
|
|
321
321
|
worktreePath: "/tmp/wt",
|
|
322
322
|
branchName: "overstory/builder-1/test",
|
|
323
|
-
|
|
323
|
+
taskId: "overstory-001",
|
|
324
324
|
tmuxSession: "overstory-test-builder-1",
|
|
325
325
|
state: "working",
|
|
326
326
|
pid: 12345,
|
|
@@ -355,7 +355,7 @@ describe("inspectCommand", () => {
|
|
|
355
355
|
capability: "builder",
|
|
356
356
|
worktreePath: "/tmp/wt",
|
|
357
357
|
branchName: "overstory/builder-1/test",
|
|
358
|
-
|
|
358
|
+
taskId: "overstory-001",
|
|
359
359
|
tmuxSession: "overstory-test-builder-1",
|
|
360
360
|
state: "working",
|
|
361
361
|
pid: 12345,
|
|
@@ -399,7 +399,7 @@ describe("inspectCommand", () => {
|
|
|
399
399
|
capability: "builder",
|
|
400
400
|
worktreePath: "/tmp/wt",
|
|
401
401
|
branchName: "overstory/builder-1/test",
|
|
402
|
-
|
|
402
|
+
taskId: "overstory-001",
|
|
403
403
|
tmuxSession: "overstory-test-builder-1",
|
|
404
404
|
state: "working",
|
|
405
405
|
pid: 12345,
|
|
@@ -442,7 +442,7 @@ describe("inspectCommand", () => {
|
|
|
442
442
|
capability: "builder",
|
|
443
443
|
worktreePath: "/tmp/wt",
|
|
444
444
|
branchName: "overstory/builder-1/test",
|
|
445
|
-
|
|
445
|
+
taskId: "overstory-001",
|
|
446
446
|
tmuxSession: "overstory-test-builder-1",
|
|
447
447
|
state: "working",
|
|
448
448
|
pid: 12345,
|
|
@@ -491,7 +491,7 @@ describe("inspectCommand", () => {
|
|
|
491
491
|
capability: "builder",
|
|
492
492
|
worktreePath: "/tmp/wt",
|
|
493
493
|
branchName: "overstory/builder-1/test",
|
|
494
|
-
|
|
494
|
+
taskId: "overstory-001",
|
|
495
495
|
tmuxSession: "overstory-test-builder-1",
|
|
496
496
|
state: "working",
|
|
497
497
|
pid: 12345,
|
|
@@ -528,7 +528,7 @@ describe("inspectCommand", () => {
|
|
|
528
528
|
capability: "builder",
|
|
529
529
|
worktreePath: "/tmp/wt",
|
|
530
530
|
branchName: "overstory/builder-1/test",
|
|
531
|
-
|
|
531
|
+
taskId: "overstory-001",
|
|
532
532
|
tmuxSession: "overstory-test-builder-1",
|
|
533
533
|
state: "working",
|
|
534
534
|
pid: 12345,
|
|
@@ -564,7 +564,7 @@ describe("inspectCommand", () => {
|
|
|
564
564
|
capability: "builder",
|
|
565
565
|
worktreePath: "/tmp/wt",
|
|
566
566
|
branchName: "overstory/builder-1/test",
|
|
567
|
-
|
|
567
|
+
taskId: "overstory-001",
|
|
568
568
|
tmuxSession: "overstory-test-builder-1",
|
|
569
569
|
state: "working",
|
|
570
570
|
pid: 12345,
|
|
@@ -601,7 +601,7 @@ describe("inspectCommand", () => {
|
|
|
601
601
|
capability: "builder",
|
|
602
602
|
worktreePath: "/tmp/wt",
|
|
603
603
|
branchName: "overstory/builder-1/test",
|
|
604
|
-
|
|
604
|
+
taskId: "overstory-001",
|
|
605
605
|
tmuxSession: "overstory-test-builder-1",
|
|
606
606
|
state: "working",
|
|
607
607
|
pid: 12345,
|
|
@@ -639,7 +639,7 @@ describe("inspectCommand", () => {
|
|
|
639
639
|
capability: "builder",
|
|
640
640
|
worktreePath: "/tmp/wt",
|
|
641
641
|
branchName: "overstory/builder-1/test",
|
|
642
|
-
|
|
642
|
+
taskId: "overstory-001",
|
|
643
643
|
tmuxSession: "overstory-test-builder-1",
|
|
644
644
|
state: "working",
|
|
645
645
|
pid: 12345,
|
package/src/commands/inspect.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
+
import { Command } from "commander";
|
|
9
10
|
import { loadConfig } from "../config.ts";
|
|
10
11
|
import { ValidationError } from "../errors.ts";
|
|
11
12
|
import { createEventStore } from "../events/store.ts";
|
|
@@ -14,21 +15,6 @@ import { createMetricsStore } from "../metrics/store.ts";
|
|
|
14
15
|
import { openSessionStore } from "../sessions/compat.ts";
|
|
15
16
|
import type { AgentSession, StoredEvent, ToolStats } from "../types.ts";
|
|
16
17
|
|
|
17
|
-
/**
|
|
18
|
-
* Parse a named flag value from args.
|
|
19
|
-
*/
|
|
20
|
-
function getFlag(args: string[], flag: string): string | undefined {
|
|
21
|
-
const idx = args.indexOf(flag);
|
|
22
|
-
if (idx === -1 || idx + 1 >= args.length) {
|
|
23
|
-
return undefined;
|
|
24
|
-
}
|
|
25
|
-
return args[idx + 1];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function hasFlag(args: string[], flag: string): boolean {
|
|
29
|
-
return args.includes(flag);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
18
|
/**
|
|
33
19
|
* Format a duration in ms to a human-readable string.
|
|
34
20
|
*/
|
|
@@ -49,15 +35,15 @@ function formatDuration(ms: number): string {
|
|
|
49
35
|
function getStateIcon(state: AgentSession["state"]): string {
|
|
50
36
|
switch (state) {
|
|
51
37
|
case "booting":
|
|
52
|
-
return `${color.yellow}
|
|
38
|
+
return `${color.yellow("⏳")}`; // Yellow hourglass
|
|
53
39
|
case "working":
|
|
54
|
-
return `${color.green}
|
|
40
|
+
return `${color.green("●")}`; // Green circle
|
|
55
41
|
case "stalled":
|
|
56
|
-
return `${color.yellow}
|
|
42
|
+
return `${color.yellow("⚠")}`; // Yellow warning
|
|
57
43
|
case "completed":
|
|
58
|
-
return `${color.blue}
|
|
44
|
+
return `${color.blue("✓")}`; // Blue checkmark
|
|
59
45
|
case "zombie":
|
|
60
|
-
return `${color.red}
|
|
46
|
+
return `${color.red("☠")}`; // Red skull
|
|
61
47
|
default:
|
|
62
48
|
return "?";
|
|
63
49
|
}
|
|
@@ -273,7 +259,7 @@ export function printInspectData(data: InspectData): void {
|
|
|
273
259
|
const stateIcon = getStateIcon(session.state);
|
|
274
260
|
w(`${stateIcon} State: ${session.state}\n`);
|
|
275
261
|
w(`⏱ Last activity: ${formatDuration(data.timeSinceLastActivity)} ago\n`);
|
|
276
|
-
w(`🎯 Task: ${session.
|
|
262
|
+
w(`🎯 Task: ${session.taskId}\n`);
|
|
277
263
|
w(`🔧 Capability: ${session.capability}\n`);
|
|
278
264
|
w(`🌿 Branch: ${session.branchName}\n`);
|
|
279
265
|
if (session.parentAgent) {
|
|
@@ -343,44 +329,21 @@ export function printInspectData(data: InspectData): void {
|
|
|
343
329
|
}
|
|
344
330
|
}
|
|
345
331
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
--interval <ms> Polling interval for --follow in milliseconds (default: 3000, min: 500)
|
|
354
|
-
--limit <n> Number of recent tool calls to show (default: 20)
|
|
355
|
-
--no-tmux Skip tmux capture-pane
|
|
356
|
-
--help, -h Show this help
|
|
357
|
-
|
|
358
|
-
Examples:
|
|
359
|
-
overstory inspect builder-1
|
|
360
|
-
overstory inspect scout-alpha --json
|
|
361
|
-
overstory inspect builder-1 --follow --interval 2000`;
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Entry point for `overstory inspect <agent-name>`.
|
|
365
|
-
*/
|
|
366
|
-
export async function inspectCommand(args: string[]): Promise<void> {
|
|
367
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
368
|
-
process.stdout.write(`${INSPECT_HELP}\n`);
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
const agentName = args[0];
|
|
373
|
-
if (!agentName) {
|
|
374
|
-
throw new ValidationError("Agent name is required", {
|
|
375
|
-
field: "agent-name",
|
|
376
|
-
});
|
|
377
|
-
}
|
|
332
|
+
interface InspectOpts {
|
|
333
|
+
json?: boolean;
|
|
334
|
+
follow?: boolean;
|
|
335
|
+
interval?: string;
|
|
336
|
+
limit?: string;
|
|
337
|
+
tmux?: boolean; // Commander: --no-tmux sets tmux=false
|
|
338
|
+
}
|
|
378
339
|
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
const
|
|
340
|
+
async function executeInspect(agentName: string, opts: InspectOpts): Promise<void> {
|
|
341
|
+
const json = opts.json ?? false;
|
|
342
|
+
const follow = opts.follow ?? false;
|
|
343
|
+
// Commander --no-tmux sets opts.tmux = false
|
|
344
|
+
const noTmux = opts.tmux === false;
|
|
382
345
|
|
|
383
|
-
const intervalStr =
|
|
346
|
+
const intervalStr = opts.interval;
|
|
384
347
|
const interval = intervalStr ? Number.parseInt(intervalStr, 10) : 3000;
|
|
385
348
|
if (Number.isNaN(interval) || interval < 500) {
|
|
386
349
|
throw new ValidationError("--interval must be a number >= 500 (milliseconds)", {
|
|
@@ -389,7 +352,7 @@ export async function inspectCommand(args: string[]): Promise<void> {
|
|
|
389
352
|
});
|
|
390
353
|
}
|
|
391
354
|
|
|
392
|
-
const limitStr =
|
|
355
|
+
const limitStr = opts.limit;
|
|
393
356
|
const limit = limitStr ? Number.parseInt(limitStr, 10) : 20;
|
|
394
357
|
if (Number.isNaN(limit) || limit < 1) {
|
|
395
358
|
throw new ValidationError("--limit must be a number >= 1", {
|
|
@@ -429,3 +392,37 @@ export async function inspectCommand(args: string[]): Promise<void> {
|
|
|
429
392
|
}
|
|
430
393
|
}
|
|
431
394
|
}
|
|
395
|
+
|
|
396
|
+
export function createInspectCommand(): Command {
|
|
397
|
+
return new Command("inspect")
|
|
398
|
+
.description("Deep inspection of a single agent")
|
|
399
|
+
.argument("<agent-name>", "Agent name to inspect")
|
|
400
|
+
.option("--json", "Output as JSON")
|
|
401
|
+
.option("--follow", "Poll and refresh continuously")
|
|
402
|
+
.option("--interval <ms>", "Polling interval for --follow in milliseconds (default: 3000)")
|
|
403
|
+
.option("--limit <n>", "Number of recent tool calls to show (default: 20)")
|
|
404
|
+
.option("--no-tmux", "Skip tmux capture-pane")
|
|
405
|
+
.action(async (agentName: string, opts: InspectOpts) => {
|
|
406
|
+
await executeInspect(agentName, opts);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export async function inspectCommand(args: string[]): Promise<void> {
|
|
411
|
+
const cmd = createInspectCommand();
|
|
412
|
+
cmd.exitOverride();
|
|
413
|
+
try {
|
|
414
|
+
await cmd.parseAsync(args, { from: "user" });
|
|
415
|
+
} catch (err: unknown) {
|
|
416
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
417
|
+
const code = (err as { code: string }).code;
|
|
418
|
+
if (code === "commander.helpDisplayed" || code === "commander.version") {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (code.startsWith("commander.")) {
|
|
422
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
423
|
+
throw new ValidationError(message, { field: "args" });
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
throw err;
|
|
427
|
+
}
|
|
428
|
+
}
|