@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,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: overstory agents <sub> [--json]
|
|
3
|
+
*
|
|
4
|
+
* Discover and query agents by capability.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { loadConfig } from "../config.ts";
|
|
9
|
+
import { ValidationError } from "../errors.ts";
|
|
10
|
+
import { openSessionStore } from "../sessions/compat.ts";
|
|
11
|
+
import { type AgentSession, SUPPORTED_CAPABILITIES } from "../types.ts";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse a named flag value from args.
|
|
15
|
+
*/
|
|
16
|
+
function getFlag(args: string[], flag: string): string | undefined {
|
|
17
|
+
const idx = args.indexOf(flag);
|
|
18
|
+
if (idx === -1 || idx + 1 >= args.length) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
return args[idx + 1];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hasFlag(args: string[], flag: string): boolean {
|
|
25
|
+
return args.includes(flag);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Discovered agent information including file scope.
|
|
30
|
+
*/
|
|
31
|
+
export interface DiscoveredAgent {
|
|
32
|
+
agentName: string;
|
|
33
|
+
capability: string;
|
|
34
|
+
state: string;
|
|
35
|
+
beadId: string;
|
|
36
|
+
branchName: string;
|
|
37
|
+
parentAgent: string | null;
|
|
38
|
+
depth: number;
|
|
39
|
+
fileScope: string[];
|
|
40
|
+
startedAt: string;
|
|
41
|
+
lastActivity: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Extract file scope from an agent's overlay CLAUDE.md.
|
|
46
|
+
* Returns empty array if overlay doesn't exist, has no file scope restrictions,
|
|
47
|
+
* or can't be read.
|
|
48
|
+
*
|
|
49
|
+
* @param worktreePath - Absolute path to the agent's worktree
|
|
50
|
+
* @returns Array of file paths (relative to worktree root)
|
|
51
|
+
*/
|
|
52
|
+
export async function extractFileScope(worktreePath: string): Promise<string[]> {
|
|
53
|
+
try {
|
|
54
|
+
const overlayPath = join(worktreePath, ".claude", "CLAUDE.md");
|
|
55
|
+
const overlayFile = Bun.file(overlayPath);
|
|
56
|
+
|
|
57
|
+
if (!(await overlayFile.exists())) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const content = await overlayFile.text();
|
|
62
|
+
|
|
63
|
+
// Find the section between "## File Scope (exclusive ownership)" and "## Expertise"
|
|
64
|
+
const startMarker = "## File Scope (exclusive ownership)";
|
|
65
|
+
const endMarker = "## Expertise";
|
|
66
|
+
|
|
67
|
+
const startIdx = content.indexOf(startMarker);
|
|
68
|
+
if (startIdx === -1) {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const endIdx = content.indexOf(endMarker, startIdx);
|
|
73
|
+
if (endIdx === -1) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const section = content.slice(startIdx, endIdx);
|
|
78
|
+
|
|
79
|
+
// Check for "No file scope restrictions"
|
|
80
|
+
if (section.includes("No file scope restrictions")) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Extract file paths from markdown list items: - `path`
|
|
85
|
+
const paths: string[] = [];
|
|
86
|
+
const regex = /^- `(.+)`$/gm;
|
|
87
|
+
let match = regex.exec(section);
|
|
88
|
+
|
|
89
|
+
while (match !== null) {
|
|
90
|
+
if (match[1]) {
|
|
91
|
+
paths.push(match[1]);
|
|
92
|
+
}
|
|
93
|
+
match = regex.exec(section);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return paths;
|
|
97
|
+
} catch {
|
|
98
|
+
// Best effort: return empty array if anything fails
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Discover agents in the project.
|
|
105
|
+
*
|
|
106
|
+
* @param root - Absolute path to project root
|
|
107
|
+
* @param opts - Filter options
|
|
108
|
+
* @returns Array of discovered agents with file scopes
|
|
109
|
+
*/
|
|
110
|
+
export async function discoverAgents(
|
|
111
|
+
root: string,
|
|
112
|
+
opts?: { capability?: string; includeAll?: boolean },
|
|
113
|
+
): Promise<DiscoveredAgent[]> {
|
|
114
|
+
const overstoryDir = join(root, ".overstory");
|
|
115
|
+
const { store } = openSessionStore(overstoryDir);
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const sessions: AgentSession[] = opts?.includeAll ? store.getAll() : store.getActive();
|
|
119
|
+
|
|
120
|
+
// Filter by capability if specified
|
|
121
|
+
let filteredSessions = sessions;
|
|
122
|
+
if (opts?.capability) {
|
|
123
|
+
filteredSessions = sessions.filter((s) => s.capability === opts.capability);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Extract file scopes for each agent
|
|
127
|
+
const agents: DiscoveredAgent[] = await Promise.all(
|
|
128
|
+
filteredSessions.map(async (session) => {
|
|
129
|
+
const fileScope = await extractFileScope(session.worktreePath);
|
|
130
|
+
return {
|
|
131
|
+
agentName: session.agentName,
|
|
132
|
+
capability: session.capability,
|
|
133
|
+
state: session.state,
|
|
134
|
+
beadId: session.beadId,
|
|
135
|
+
branchName: session.branchName,
|
|
136
|
+
parentAgent: session.parentAgent,
|
|
137
|
+
depth: session.depth,
|
|
138
|
+
fileScope,
|
|
139
|
+
startedAt: session.startedAt,
|
|
140
|
+
lastActivity: session.lastActivity,
|
|
141
|
+
};
|
|
142
|
+
}),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return agents;
|
|
146
|
+
} finally {
|
|
147
|
+
store.close();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Format the state icon for display.
|
|
153
|
+
*/
|
|
154
|
+
function getStateIcon(state: string): string {
|
|
155
|
+
switch (state) {
|
|
156
|
+
case "working":
|
|
157
|
+
return "●";
|
|
158
|
+
case "booting":
|
|
159
|
+
return "○";
|
|
160
|
+
case "stalled":
|
|
161
|
+
return "◌";
|
|
162
|
+
default:
|
|
163
|
+
return " ";
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Print discovered agents in human-readable format.
|
|
169
|
+
*/
|
|
170
|
+
function printAgents(agents: DiscoveredAgent[]): void {
|
|
171
|
+
const w = process.stdout.write.bind(process.stdout);
|
|
172
|
+
|
|
173
|
+
if (agents.length === 0) {
|
|
174
|
+
w("No agents found.\n");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
w(`Found ${agents.length} agent${agents.length === 1 ? "" : "s"}:\n\n`);
|
|
179
|
+
|
|
180
|
+
for (const agent of agents) {
|
|
181
|
+
const icon = getStateIcon(agent.state);
|
|
182
|
+
w(` ${icon} ${agent.agentName} [${agent.capability}]\n`);
|
|
183
|
+
w(` State: ${agent.state} | Task: ${agent.beadId}\n`);
|
|
184
|
+
w(` Branch: ${agent.branchName}\n`);
|
|
185
|
+
w(` Parent: ${agent.parentAgent ?? "none"} | Depth: ${agent.depth}\n`);
|
|
186
|
+
|
|
187
|
+
if (agent.fileScope.length === 0) {
|
|
188
|
+
w(" Files: (unrestricted)\n");
|
|
189
|
+
} else {
|
|
190
|
+
w(` Files: ${agent.fileScope.join(", ")}\n`);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
w("\n");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const DISCOVER_HELP = `overstory agents discover — Find active agents by capability
|
|
198
|
+
|
|
199
|
+
Usage: overstory agents discover [--capability <type>] [--all] [--json]
|
|
200
|
+
|
|
201
|
+
Options:
|
|
202
|
+
--capability <type> Filter by capability (builder, scout, reviewer, lead, merger, coordinator, supervisor)
|
|
203
|
+
--all Include completed and zombie agents (default: active only)
|
|
204
|
+
--json Output as JSON
|
|
205
|
+
--help, -h Show this help`;
|
|
206
|
+
|
|
207
|
+
const AGENTS_HELP = `overstory agents — Discover and query agents
|
|
208
|
+
|
|
209
|
+
Usage: overstory agents <subcommand> [options]
|
|
210
|
+
|
|
211
|
+
Subcommands:
|
|
212
|
+
discover Find active agents by capability
|
|
213
|
+
|
|
214
|
+
Options:
|
|
215
|
+
--json Output as JSON
|
|
216
|
+
--help, -h Show this help
|
|
217
|
+
|
|
218
|
+
Run 'overstory agents <subcommand> --help' for subcommand-specific help.`;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Handle the 'discover' subcommand.
|
|
222
|
+
*/
|
|
223
|
+
async function discoverCommand(args: string[]): Promise<void> {
|
|
224
|
+
if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
|
|
225
|
+
process.stdout.write(`${DISCOVER_HELP}\n`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const json = hasFlag(args, "--json");
|
|
230
|
+
const includeAll = hasFlag(args, "--all");
|
|
231
|
+
const capability = getFlag(args, "--capability");
|
|
232
|
+
|
|
233
|
+
// Validate capability if provided
|
|
234
|
+
if (capability && !SUPPORTED_CAPABILITIES.includes(capability as never)) {
|
|
235
|
+
throw new ValidationError(
|
|
236
|
+
`Invalid capability: ${capability}. Must be one of: ${SUPPORTED_CAPABILITIES.join(", ")}`,
|
|
237
|
+
{
|
|
238
|
+
field: "capability",
|
|
239
|
+
value: capability,
|
|
240
|
+
},
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const cwd = process.cwd();
|
|
245
|
+
const config = await loadConfig(cwd);
|
|
246
|
+
const root = config.project.root;
|
|
247
|
+
|
|
248
|
+
const agents = await discoverAgents(root, { capability, includeAll });
|
|
249
|
+
|
|
250
|
+
if (json) {
|
|
251
|
+
process.stdout.write(`${JSON.stringify(agents, null, "\t")}\n`);
|
|
252
|
+
} else {
|
|
253
|
+
printAgents(agents);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Entry point for `overstory agents <subcommand>`.
|
|
259
|
+
*/
|
|
260
|
+
export async function agentsCommand(args: string[]): Promise<void> {
|
|
261
|
+
if (hasFlag(args, "--help") || hasFlag(args, "-h")) {
|
|
262
|
+
process.stdout.write(`${AGENTS_HELP}\n`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Extract subcommand: first arg that is not a flag
|
|
267
|
+
const subcommand = args.find((arg) => !arg.startsWith("-"));
|
|
268
|
+
|
|
269
|
+
if (!subcommand) {
|
|
270
|
+
process.stdout.write(`${AGENTS_HELP}\n`);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Remove the subcommand from args before passing to handler
|
|
275
|
+
const subArgs = args.filter((arg) => arg !== subcommand);
|
|
276
|
+
|
|
277
|
+
switch (subcommand) {
|
|
278
|
+
case "discover":
|
|
279
|
+
await discoverCommand(subArgs);
|
|
280
|
+
break;
|
|
281
|
+
default:
|
|
282
|
+
throw new ValidationError(`Unknown subcommand: ${subcommand}`, {
|
|
283
|
+
field: "subcommand",
|
|
284
|
+
value: subcommand,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|