@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,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: overstory run [subcommand] [--json]
|
|
3
|
+
*
|
|
4
|
+
* Manage runs (coordinator session groupings).
|
|
5
|
+
* A "run" groups all agents spawned from one coordinator session.
|
|
6
|
+
*
|
|
7
|
+
* Subcommands:
|
|
8
|
+
* (default) Show current run status
|
|
9
|
+
* list List recent runs
|
|
10
|
+
* complete Mark current run as completed
|
|
11
|
+
* show <id> Show run details with agents
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { loadConfig } from "../config.ts";
|
|
16
|
+
import { createRunStore, createSessionStore } from "../sessions/store.ts";
|
|
17
|
+
import type { AgentSession, Run } from "../types.ts";
|
|
18
|
+
|
|
19
|
+
const RUN_HELP = `overstory run -- Manage runs (coordinator session groupings)
|
|
20
|
+
|
|
21
|
+
Usage: overstory run [subcommand] [options]
|
|
22
|
+
|
|
23
|
+
Subcommands:
|
|
24
|
+
(default) Show current run status
|
|
25
|
+
list [--last <n>] List recent runs (default last 10)
|
|
26
|
+
complete Mark current run as completed
|
|
27
|
+
show <id> Show run details (agents, duration)
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
--json Output as JSON
|
|
31
|
+
--help, -h Show this help`;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Parse a named flag value from args.
|
|
35
|
+
*/
|
|
36
|
+
function getFlag(args: string[], flag: string): string | undefined {
|
|
37
|
+
const idx = args.indexOf(flag);
|
|
38
|
+
if (idx === -1 || idx + 1 >= args.length) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return args[idx + 1];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hasFlag(args: string[], flag: string): boolean {
|
|
45
|
+
return args.includes(flag);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Format milliseconds as human-readable duration.
|
|
50
|
+
*/
|
|
51
|
+
function formatDuration(ms: number): string {
|
|
52
|
+
if (ms === 0) return "0s";
|
|
53
|
+
const seconds = Math.floor(ms / 1000);
|
|
54
|
+
if (seconds < 60) return `${seconds}s`;
|
|
55
|
+
const minutes = Math.floor(seconds / 60);
|
|
56
|
+
const remainSec = seconds % 60;
|
|
57
|
+
if (minutes < 60) return `${minutes}m ${remainSec}s`;
|
|
58
|
+
const hours = Math.floor(minutes / 60);
|
|
59
|
+
const remainMin = minutes % 60;
|
|
60
|
+
return `${hours}h ${remainMin}m`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get the path to the current-run.txt file.
|
|
65
|
+
*/
|
|
66
|
+
function currentRunPath(overstoryDir: string): string {
|
|
67
|
+
return join(overstoryDir, "current-run.txt");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Read the current run ID from current-run.txt, or null if no active run.
|
|
72
|
+
*/
|
|
73
|
+
async function readCurrentRunId(overstoryDir: string): Promise<string | null> {
|
|
74
|
+
const path = currentRunPath(overstoryDir);
|
|
75
|
+
const file = Bun.file(path);
|
|
76
|
+
if (!(await file.exists())) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const text = await file.text();
|
|
80
|
+
const trimmed = text.trim();
|
|
81
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Compute duration string for a run.
|
|
86
|
+
*/
|
|
87
|
+
function runDuration(run: Run): string {
|
|
88
|
+
const start = new Date(run.startedAt).getTime();
|
|
89
|
+
const end = run.completedAt ? new Date(run.completedAt).getTime() : Date.now();
|
|
90
|
+
return formatDuration(end - start);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Show current run status (default subcommand).
|
|
95
|
+
*/
|
|
96
|
+
async function showCurrentRun(overstoryDir: string, json: boolean): Promise<void> {
|
|
97
|
+
const runId = await readCurrentRunId(overstoryDir);
|
|
98
|
+
if (!runId) {
|
|
99
|
+
if (json) {
|
|
100
|
+
process.stdout.write('{"run":null,"message":"No active run"}\n');
|
|
101
|
+
} else {
|
|
102
|
+
process.stdout.write("No active run\n");
|
|
103
|
+
}
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const dbPath = join(overstoryDir, "sessions.db");
|
|
108
|
+
const runStore = createRunStore(dbPath);
|
|
109
|
+
try {
|
|
110
|
+
const run = runStore.getRun(runId);
|
|
111
|
+
if (!run) {
|
|
112
|
+
if (json) {
|
|
113
|
+
process.stdout.write(
|
|
114
|
+
`${JSON.stringify({ run: null, message: `Run ${runId} not found in store` })}\n`,
|
|
115
|
+
);
|
|
116
|
+
} else {
|
|
117
|
+
process.stdout.write(`Run ${runId} not found in store\n`);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (json) {
|
|
123
|
+
process.stdout.write(`${JSON.stringify({ run, duration: runDuration(run) })}\n`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
process.stdout.write("Current Run\n");
|
|
128
|
+
process.stdout.write(`${"=".repeat(50)}\n`);
|
|
129
|
+
process.stdout.write(` ID: ${run.id}\n`);
|
|
130
|
+
process.stdout.write(` Status: ${run.status}\n`);
|
|
131
|
+
process.stdout.write(` Started: ${run.startedAt}\n`);
|
|
132
|
+
process.stdout.write(` Agents: ${run.agentCount}\n`);
|
|
133
|
+
process.stdout.write(` Duration: ${runDuration(run)}\n`);
|
|
134
|
+
} finally {
|
|
135
|
+
runStore.close();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* List recent runs.
|
|
141
|
+
*/
|
|
142
|
+
async function listRuns(overstoryDir: string, limit: number, json: boolean): Promise<void> {
|
|
143
|
+
const dbPath = join(overstoryDir, "sessions.db");
|
|
144
|
+
const dbFile = Bun.file(dbPath);
|
|
145
|
+
if (!(await dbFile.exists())) {
|
|
146
|
+
if (json) {
|
|
147
|
+
process.stdout.write('{"runs":[]}\n');
|
|
148
|
+
} else {
|
|
149
|
+
process.stdout.write("No runs recorded yet.\n");
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const runStore = createRunStore(dbPath);
|
|
155
|
+
try {
|
|
156
|
+
const runs = runStore.listRuns({ limit });
|
|
157
|
+
|
|
158
|
+
if (json) {
|
|
159
|
+
const runsWithDuration = runs.map((r) => ({ ...r, duration: runDuration(r) }));
|
|
160
|
+
process.stdout.write(`${JSON.stringify({ runs: runsWithDuration })}\n`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (runs.length === 0) {
|
|
165
|
+
process.stdout.write("No runs recorded yet.\n");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
process.stdout.write("Recent Runs\n");
|
|
170
|
+
process.stdout.write(`${"=".repeat(70)}\n`);
|
|
171
|
+
process.stdout.write(
|
|
172
|
+
`${"ID".padEnd(36)} ${"Status".padEnd(10)} ${"Agents".padEnd(7)} Duration\n`,
|
|
173
|
+
);
|
|
174
|
+
process.stdout.write(`${"-".repeat(70)}\n`);
|
|
175
|
+
|
|
176
|
+
for (const run of runs) {
|
|
177
|
+
const id = run.id.length > 35 ? `${run.id.slice(0, 32)}...` : run.id.padEnd(36);
|
|
178
|
+
const status = run.status.padEnd(10);
|
|
179
|
+
const agents = String(run.agentCount).padEnd(7);
|
|
180
|
+
const duration = runDuration(run);
|
|
181
|
+
process.stdout.write(`${id} ${status} ${agents} ${duration}\n`);
|
|
182
|
+
}
|
|
183
|
+
} finally {
|
|
184
|
+
runStore.close();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Mark the current run as completed.
|
|
190
|
+
*/
|
|
191
|
+
async function completeCurrentRun(overstoryDir: string, json: boolean): Promise<void> {
|
|
192
|
+
const runId = await readCurrentRunId(overstoryDir);
|
|
193
|
+
if (!runId) {
|
|
194
|
+
if (json) {
|
|
195
|
+
process.stdout.write('{"success":false,"message":"No active run to complete"}\n');
|
|
196
|
+
} else {
|
|
197
|
+
process.stderr.write("No active run to complete\n");
|
|
198
|
+
}
|
|
199
|
+
process.exitCode = 1;
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const dbPath = join(overstoryDir, "sessions.db");
|
|
204
|
+
const runStore = createRunStore(dbPath);
|
|
205
|
+
try {
|
|
206
|
+
runStore.completeRun(runId, "completed");
|
|
207
|
+
} finally {
|
|
208
|
+
runStore.close();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Delete current-run.txt
|
|
212
|
+
const { unlink } = await import("node:fs/promises");
|
|
213
|
+
try {
|
|
214
|
+
await unlink(currentRunPath(overstoryDir));
|
|
215
|
+
} catch {
|
|
216
|
+
// File may already be gone, that's fine
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (json) {
|
|
220
|
+
process.stdout.write(`${JSON.stringify({ success: true, runId, status: "completed" })}\n`);
|
|
221
|
+
} else {
|
|
222
|
+
process.stdout.write(`Run ${runId} marked as completed\n`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Show detailed information for a specific run.
|
|
228
|
+
*/
|
|
229
|
+
async function showRun(overstoryDir: string, runId: string, json: boolean): Promise<void> {
|
|
230
|
+
const dbPath = join(overstoryDir, "sessions.db");
|
|
231
|
+
const dbFile = Bun.file(dbPath);
|
|
232
|
+
if (!(await dbFile.exists())) {
|
|
233
|
+
if (json) {
|
|
234
|
+
process.stdout.write(`${JSON.stringify({ run: null, message: `Run ${runId} not found` })}\n`);
|
|
235
|
+
} else {
|
|
236
|
+
process.stderr.write(`Run ${runId} not found\n`);
|
|
237
|
+
}
|
|
238
|
+
process.exitCode = 1;
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const runStore = createRunStore(dbPath);
|
|
243
|
+
const sessionStore = createSessionStore(dbPath);
|
|
244
|
+
try {
|
|
245
|
+
const run = runStore.getRun(runId);
|
|
246
|
+
if (!run) {
|
|
247
|
+
if (json) {
|
|
248
|
+
process.stdout.write(
|
|
249
|
+
`${JSON.stringify({ run: null, message: `Run ${runId} not found` })}\n`,
|
|
250
|
+
);
|
|
251
|
+
} else {
|
|
252
|
+
process.stderr.write(`Run ${runId} not found\n`);
|
|
253
|
+
}
|
|
254
|
+
process.exitCode = 1;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const agents = sessionStore.getByRun(runId);
|
|
259
|
+
|
|
260
|
+
if (json) {
|
|
261
|
+
process.stdout.write(`${JSON.stringify({ run, duration: runDuration(run), agents })}\n`);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
process.stdout.write("Run Details\n");
|
|
266
|
+
process.stdout.write(`${"=".repeat(60)}\n`);
|
|
267
|
+
process.stdout.write(` ID: ${run.id}\n`);
|
|
268
|
+
process.stdout.write(` Status: ${run.status}\n`);
|
|
269
|
+
process.stdout.write(` Started: ${run.startedAt}\n`);
|
|
270
|
+
if (run.completedAt) {
|
|
271
|
+
process.stdout.write(` Ended: ${run.completedAt}\n`);
|
|
272
|
+
}
|
|
273
|
+
process.stdout.write(` Agents: ${run.agentCount}\n`);
|
|
274
|
+
process.stdout.write(` Duration: ${runDuration(run)}\n`);
|
|
275
|
+
|
|
276
|
+
if (agents.length > 0) {
|
|
277
|
+
process.stdout.write(`\nAgents (${agents.length}):\n`);
|
|
278
|
+
process.stdout.write(`${"-".repeat(60)}\n`);
|
|
279
|
+
for (const agent of agents) {
|
|
280
|
+
const agentDuration = formatAgentDuration(agent);
|
|
281
|
+
process.stdout.write(
|
|
282
|
+
` ${agent.agentName} [${agent.capability}] ${agent.state} | ${agentDuration}\n`,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
} else {
|
|
286
|
+
process.stdout.write("\nNo agents recorded for this run.\n");
|
|
287
|
+
}
|
|
288
|
+
} finally {
|
|
289
|
+
runStore.close();
|
|
290
|
+
sessionStore.close();
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Format an agent's duration from startedAt to now (or completion).
|
|
296
|
+
*/
|
|
297
|
+
function formatAgentDuration(agent: AgentSession): string {
|
|
298
|
+
const start = new Date(agent.startedAt).getTime();
|
|
299
|
+
const end =
|
|
300
|
+
agent.state === "completed" || agent.state === "zombie"
|
|
301
|
+
? new Date(agent.lastActivity).getTime()
|
|
302
|
+
: Date.now();
|
|
303
|
+
return formatDuration(end - start);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Entry point for `overstory run [subcommand] [options]`.
|
|
308
|
+
*/
|
|
309
|
+
export async function runCommand(args: string[]): Promise<void> {
|
|
310
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
311
|
+
process.stdout.write(`${RUN_HELP}\n`);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const json = hasFlag(args, "--json");
|
|
316
|
+
const cwd = process.cwd();
|
|
317
|
+
const config = await loadConfig(cwd);
|
|
318
|
+
const overstoryDir = join(config.project.root, ".overstory");
|
|
319
|
+
|
|
320
|
+
const subcommand = args[0];
|
|
321
|
+
|
|
322
|
+
switch (subcommand) {
|
|
323
|
+
case "list": {
|
|
324
|
+
const lastStr = getFlag(args, "--last");
|
|
325
|
+
const limit = lastStr ? Number.parseInt(lastStr, 10) : 10;
|
|
326
|
+
await listRuns(overstoryDir, limit, json);
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
case "complete":
|
|
330
|
+
await completeCurrentRun(overstoryDir, json);
|
|
331
|
+
break;
|
|
332
|
+
case "show": {
|
|
333
|
+
const runId = args[1];
|
|
334
|
+
if (!runId || runId.startsWith("--")) {
|
|
335
|
+
if (json) {
|
|
336
|
+
process.stdout.write('{"error":"Missing run ID. Usage: overstory run show <id>"}\n');
|
|
337
|
+
} else {
|
|
338
|
+
process.stderr.write("Missing run ID. Usage: overstory run show <id>\n");
|
|
339
|
+
}
|
|
340
|
+
process.exitCode = 1;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
await showRun(overstoryDir, runId, json);
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
default:
|
|
347
|
+
// Default: show current run status
|
|
348
|
+
await showCurrentRun(overstoryDir, json);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
}
|