@os-eco/overstory-cli 0.6.10 → 0.7.0
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 +156 -274
- package/agents/lead.md +29 -19
- package/package.json +5 -3
- package/src/agents/hooks-deployer.test.ts +53 -0
- package/src/agents/hooks-deployer.ts +4 -4
- package/src/agents/manifest.test.ts +1 -0
- package/src/agents/overlay.test.ts +102 -0
- package/src/agents/overlay.ts +45 -6
- package/src/commands/completions.ts +3 -3
- package/src/commands/coordinator.ts +25 -13
- package/src/commands/costs.test.ts +1 -1
- package/src/commands/costs.ts +13 -20
- package/src/commands/dashboard.ts +38 -138
- package/src/commands/doctor.test.ts +1 -1
- package/src/commands/doctor.ts +2 -2
- package/src/commands/ecosystem.ts +2 -1
- package/src/commands/errors.test.ts +4 -5
- package/src/commands/errors.ts +4 -62
- package/src/commands/feed.test.ts +2 -2
- package/src/commands/feed.ts +12 -106
- package/src/commands/group.ts +4 -4
- package/src/commands/inspect.ts +10 -44
- package/src/commands/logs.ts +7 -63
- package/src/commands/mail.test.ts +63 -1
- package/src/commands/mail.ts +18 -1
- package/src/commands/merge.ts +2 -2
- package/src/commands/metrics.test.ts +2 -2
- package/src/commands/metrics.ts +3 -17
- package/src/commands/monitor.ts +19 -9
- package/src/commands/replay.test.ts +2 -2
- package/src/commands/replay.ts +12 -135
- package/src/commands/run.ts +7 -23
- package/src/commands/sling.test.ts +227 -27
- package/src/commands/sling.ts +120 -21
- package/src/commands/status.ts +5 -18
- package/src/commands/supervisor.ts +22 -12
- package/src/commands/trace.test.ts +5 -6
- package/src/commands/trace.ts +13 -111
- package/src/config.test.ts +22 -0
- package/src/config.ts +22 -0
- package/src/doctor/agents.test.ts +1 -0
- package/src/doctor/config-check.test.ts +1 -0
- package/src/doctor/consistency.test.ts +1 -0
- package/src/doctor/databases.test.ts +1 -0
- package/src/doctor/dependencies.test.ts +1 -0
- package/src/doctor/ecosystem.test.ts +1 -0
- package/src/doctor/logs.test.ts +1 -0
- package/src/doctor/merge-queue.test.ts +1 -0
- package/src/doctor/structure.test.ts +1 -0
- package/src/doctor/version.test.ts +1 -0
- package/src/index.ts +8 -4
- package/src/logging/format.ts +214 -0
- package/src/logging/theme.ts +132 -0
- package/src/metrics/store.test.ts +46 -0
- package/src/metrics/store.ts +11 -0
- package/src/mulch/client.test.ts +20 -0
- package/src/mulch/client.ts +312 -45
- package/src/runtimes/claude.test.ts +616 -0
- package/src/runtimes/claude.ts +218 -0
- package/src/runtimes/registry.test.ts +53 -0
- package/src/runtimes/registry.ts +33 -0
- package/src/runtimes/types.ts +125 -0
- package/src/types.ts +15 -0
- package/src/worktree/tmux.test.ts +28 -13
- package/src/worktree/tmux.ts +14 -28
- package/templates/overlay.md.tmpl +3 -1
package/src/mulch/client.ts
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
* Mulch CLI client.
|
|
3
3
|
*
|
|
4
4
|
* Wraps the `mulch` command-line tool for structured expertise operations.
|
|
5
|
-
*
|
|
5
|
+
* record(), search(), and query() use the @os-eco/mulch-cli programmatic API
|
|
6
|
+
* via a variable-based dynamic import so tsc cannot statically resolve the
|
|
7
|
+
* module (avoiding type errors in mulch's raw .ts source files).
|
|
8
|
+
* Remaining methods (prime, status, diff, learn, prune, doctor, ready, compact)
|
|
9
|
+
* remain as Bun.spawn CLI wrappers.
|
|
6
10
|
*/
|
|
7
11
|
|
|
8
12
|
import { AgentError } from "../errors.ts";
|
|
@@ -87,6 +91,149 @@ export interface MulchClient {
|
|
|
87
91
|
): Promise<MulchCompactResult>;
|
|
88
92
|
}
|
|
89
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Local type matching @os-eco/mulch-cli ExpertiseRecord.
|
|
96
|
+
* Defined locally to avoid tsc following into mulch's raw .ts source
|
|
97
|
+
* (which conflicts with our noUncheckedIndexedAccess setting).
|
|
98
|
+
*/
|
|
99
|
+
type MulchClassification = "foundational" | "tactical" | "observational";
|
|
100
|
+
|
|
101
|
+
interface MulchEvidence {
|
|
102
|
+
commit?: string;
|
|
103
|
+
date?: string;
|
|
104
|
+
issue?: string;
|
|
105
|
+
file?: string;
|
|
106
|
+
bead?: string;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface MulchOutcome {
|
|
110
|
+
status: "success" | "failure" | "partial";
|
|
111
|
+
duration?: number;
|
|
112
|
+
test_results?: string;
|
|
113
|
+
agent?: string;
|
|
114
|
+
notes?: string;
|
|
115
|
+
recorded_at?: string;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
type MulchExpertiseRecord =
|
|
119
|
+
| {
|
|
120
|
+
type: "convention";
|
|
121
|
+
content: string;
|
|
122
|
+
classification: MulchClassification;
|
|
123
|
+
recorded_at: string;
|
|
124
|
+
id?: string;
|
|
125
|
+
tags?: string[];
|
|
126
|
+
evidence?: MulchEvidence;
|
|
127
|
+
outcomes?: MulchOutcome[];
|
|
128
|
+
relates_to?: string[];
|
|
129
|
+
supersedes?: string[];
|
|
130
|
+
}
|
|
131
|
+
| {
|
|
132
|
+
type: "pattern";
|
|
133
|
+
name: string;
|
|
134
|
+
description: string;
|
|
135
|
+
files?: string[];
|
|
136
|
+
classification: MulchClassification;
|
|
137
|
+
recorded_at: string;
|
|
138
|
+
id?: string;
|
|
139
|
+
tags?: string[];
|
|
140
|
+
evidence?: MulchEvidence;
|
|
141
|
+
outcomes?: MulchOutcome[];
|
|
142
|
+
relates_to?: string[];
|
|
143
|
+
supersedes?: string[];
|
|
144
|
+
}
|
|
145
|
+
| {
|
|
146
|
+
type: "failure";
|
|
147
|
+
description: string;
|
|
148
|
+
resolution: string;
|
|
149
|
+
classification: MulchClassification;
|
|
150
|
+
recorded_at: string;
|
|
151
|
+
id?: string;
|
|
152
|
+
tags?: string[];
|
|
153
|
+
evidence?: MulchEvidence;
|
|
154
|
+
outcomes?: MulchOutcome[];
|
|
155
|
+
relates_to?: string[];
|
|
156
|
+
supersedes?: string[];
|
|
157
|
+
}
|
|
158
|
+
| {
|
|
159
|
+
type: "decision";
|
|
160
|
+
title: string;
|
|
161
|
+
rationale: string;
|
|
162
|
+
classification: MulchClassification;
|
|
163
|
+
recorded_at: string;
|
|
164
|
+
id?: string;
|
|
165
|
+
tags?: string[];
|
|
166
|
+
evidence?: MulchEvidence;
|
|
167
|
+
outcomes?: MulchOutcome[];
|
|
168
|
+
relates_to?: string[];
|
|
169
|
+
supersedes?: string[];
|
|
170
|
+
}
|
|
171
|
+
| {
|
|
172
|
+
type: "reference";
|
|
173
|
+
name: string;
|
|
174
|
+
description: string;
|
|
175
|
+
files?: string[];
|
|
176
|
+
classification: MulchClassification;
|
|
177
|
+
recorded_at: string;
|
|
178
|
+
id?: string;
|
|
179
|
+
tags?: string[];
|
|
180
|
+
evidence?: MulchEvidence;
|
|
181
|
+
outcomes?: MulchOutcome[];
|
|
182
|
+
relates_to?: string[];
|
|
183
|
+
supersedes?: string[];
|
|
184
|
+
}
|
|
185
|
+
| {
|
|
186
|
+
type: "guide";
|
|
187
|
+
name: string;
|
|
188
|
+
description: string;
|
|
189
|
+
classification: MulchClassification;
|
|
190
|
+
recorded_at: string;
|
|
191
|
+
id?: string;
|
|
192
|
+
tags?: string[];
|
|
193
|
+
evidence?: MulchEvidence;
|
|
194
|
+
outcomes?: MulchOutcome[];
|
|
195
|
+
relates_to?: string[];
|
|
196
|
+
supersedes?: string[];
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Interface for mulch programmatic API functions.
|
|
201
|
+
* Uses a dynamic import with a variable specifier so tsc cannot statically
|
|
202
|
+
* resolve the module (avoiding type errors in mulch's raw .ts source files).
|
|
203
|
+
*/
|
|
204
|
+
interface MulchProgrammaticApi {
|
|
205
|
+
recordExpertise(
|
|
206
|
+
domain: string,
|
|
207
|
+
record: MulchExpertiseRecord,
|
|
208
|
+
options?: { force?: boolean; cwd?: string },
|
|
209
|
+
): Promise<{ action: "created" | "updated" | "skipped"; record: MulchExpertiseRecord }>;
|
|
210
|
+
searchExpertise(
|
|
211
|
+
query: string,
|
|
212
|
+
options?: {
|
|
213
|
+
domain?: string;
|
|
214
|
+
type?: string;
|
|
215
|
+
tag?: string;
|
|
216
|
+
classification?: string;
|
|
217
|
+
file?: string;
|
|
218
|
+
cwd?: string;
|
|
219
|
+
},
|
|
220
|
+
): Promise<Array<{ domain: string; records: MulchExpertiseRecord[] }>>;
|
|
221
|
+
queryDomain(
|
|
222
|
+
domain: string,
|
|
223
|
+
options?: { type?: string; classification?: string; file?: string; cwd?: string },
|
|
224
|
+
): Promise<MulchExpertiseRecord[]>;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const MULCH_PKG = "@os-eco/mulch-cli";
|
|
228
|
+
let _mulchApi: MulchProgrammaticApi | undefined;
|
|
229
|
+
|
|
230
|
+
async function loadMulchApi(): Promise<MulchProgrammaticApi> {
|
|
231
|
+
if (!_mulchApi) {
|
|
232
|
+
_mulchApi = (await import(MULCH_PKG)) as MulchProgrammaticApi;
|
|
233
|
+
}
|
|
234
|
+
return _mulchApi;
|
|
235
|
+
}
|
|
236
|
+
|
|
90
237
|
/**
|
|
91
238
|
* Run a shell command and capture its output.
|
|
92
239
|
*/
|
|
@@ -105,6 +252,127 @@ async function runCommand(
|
|
|
105
252
|
return { stdout, stderr, exitCode };
|
|
106
253
|
}
|
|
107
254
|
|
|
255
|
+
/**
|
|
256
|
+
* Build an ExpertiseRecord from record() options.
|
|
257
|
+
*
|
|
258
|
+
* CRITICAL MAPPING: --description maps to record.content for convention records,
|
|
259
|
+
* but to record.description for all other types.
|
|
260
|
+
*/
|
|
261
|
+
function buildExpertiseRecord(options: {
|
|
262
|
+
type: string;
|
|
263
|
+
name?: string;
|
|
264
|
+
description?: string;
|
|
265
|
+
title?: string;
|
|
266
|
+
rationale?: string;
|
|
267
|
+
tags?: string[];
|
|
268
|
+
classification?: string;
|
|
269
|
+
evidenceBead?: string;
|
|
270
|
+
outcomeStatus?: "success" | "failure";
|
|
271
|
+
outcomeDuration?: number;
|
|
272
|
+
outcomeTestResults?: string;
|
|
273
|
+
outcomeAgent?: string;
|
|
274
|
+
}): MulchExpertiseRecord {
|
|
275
|
+
const base = {
|
|
276
|
+
classification: (options.classification ?? "tactical") as
|
|
277
|
+
| "foundational"
|
|
278
|
+
| "tactical"
|
|
279
|
+
| "observational",
|
|
280
|
+
recorded_at: new Date().toISOString(),
|
|
281
|
+
tags: options.tags,
|
|
282
|
+
evidence: options.evidenceBead ? { bead: options.evidenceBead } : undefined,
|
|
283
|
+
outcomes: options.outcomeStatus
|
|
284
|
+
? [
|
|
285
|
+
{
|
|
286
|
+
status: options.outcomeStatus as "success" | "failure" | "partial",
|
|
287
|
+
duration: options.outcomeDuration,
|
|
288
|
+
test_results: options.outcomeTestResults,
|
|
289
|
+
agent: options.outcomeAgent,
|
|
290
|
+
recorded_at: new Date().toISOString(),
|
|
291
|
+
},
|
|
292
|
+
]
|
|
293
|
+
: undefined,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
switch (options.type) {
|
|
297
|
+
case "convention":
|
|
298
|
+
return { ...base, type: "convention", content: options.description ?? "" };
|
|
299
|
+
case "pattern":
|
|
300
|
+
return {
|
|
301
|
+
...base,
|
|
302
|
+
type: "pattern",
|
|
303
|
+
name: options.name ?? "",
|
|
304
|
+
description: options.description ?? "",
|
|
305
|
+
};
|
|
306
|
+
case "failure":
|
|
307
|
+
return {
|
|
308
|
+
...base,
|
|
309
|
+
type: "failure",
|
|
310
|
+
description: options.description ?? "",
|
|
311
|
+
resolution: "",
|
|
312
|
+
};
|
|
313
|
+
case "decision":
|
|
314
|
+
return {
|
|
315
|
+
...base,
|
|
316
|
+
type: "decision",
|
|
317
|
+
title: options.title ?? "",
|
|
318
|
+
rationale: options.rationale ?? "",
|
|
319
|
+
};
|
|
320
|
+
case "reference":
|
|
321
|
+
return {
|
|
322
|
+
...base,
|
|
323
|
+
type: "reference",
|
|
324
|
+
name: options.name ?? "",
|
|
325
|
+
description: options.description ?? "",
|
|
326
|
+
};
|
|
327
|
+
case "guide":
|
|
328
|
+
return {
|
|
329
|
+
...base,
|
|
330
|
+
type: "guide",
|
|
331
|
+
name: options.name ?? "",
|
|
332
|
+
description: options.description ?? "",
|
|
333
|
+
};
|
|
334
|
+
default:
|
|
335
|
+
return {
|
|
336
|
+
...base,
|
|
337
|
+
type: "convention",
|
|
338
|
+
content: options.description ?? "",
|
|
339
|
+
} as MulchExpertiseRecord;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Format search/query results as a plain string for callers that expect string output.
|
|
345
|
+
* Preserves behavior for parseConflictPatterns regex in resolver.ts.
|
|
346
|
+
*/
|
|
347
|
+
function formatSearchResults(
|
|
348
|
+
results: Array<{ domain: string; records: MulchExpertiseRecord[] }>,
|
|
349
|
+
): string {
|
|
350
|
+
const lines: string[] = [];
|
|
351
|
+
for (const result of results) {
|
|
352
|
+
for (const record of result.records) {
|
|
353
|
+
lines.push(formatRecordText(record));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return lines.join("\n");
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function formatRecordText(record: MulchExpertiseRecord): string {
|
|
360
|
+
switch (record.type) {
|
|
361
|
+
case "convention":
|
|
362
|
+
return record.content;
|
|
363
|
+
case "pattern":
|
|
364
|
+
return record.description;
|
|
365
|
+
case "failure":
|
|
366
|
+
return record.description;
|
|
367
|
+
case "decision":
|
|
368
|
+
return `${record.title}: ${record.rationale}`;
|
|
369
|
+
case "reference":
|
|
370
|
+
return record.description;
|
|
371
|
+
case "guide":
|
|
372
|
+
return record.description;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
108
376
|
/**
|
|
109
377
|
* Create a MulchClient bound to the given working directory.
|
|
110
378
|
*
|
|
@@ -158,61 +426,60 @@ export function createMulchClient(cwd: string): MulchClient {
|
|
|
158
426
|
},
|
|
159
427
|
|
|
160
428
|
async record(domain, options) {
|
|
161
|
-
|
|
162
|
-
if (options.name) {
|
|
163
|
-
args.push("--name", options.name);
|
|
164
|
-
}
|
|
165
|
-
if (options.description) {
|
|
166
|
-
args.push("--description", options.description);
|
|
167
|
-
}
|
|
168
|
-
if (options.title) {
|
|
169
|
-
args.push("--title", options.title);
|
|
170
|
-
}
|
|
171
|
-
if (options.rationale) {
|
|
172
|
-
args.push("--rationale", options.rationale);
|
|
173
|
-
}
|
|
174
|
-
if (options.tags && options.tags.length > 0) {
|
|
175
|
-
args.push("--tags", options.tags.join(","));
|
|
176
|
-
}
|
|
177
|
-
if (options.classification) {
|
|
178
|
-
args.push("--classification", options.classification);
|
|
179
|
-
}
|
|
429
|
+
// stdin mode: no programmatic API equivalent, fall back to CLI
|
|
180
430
|
if (options.stdin) {
|
|
431
|
+
const args = ["record", domain, "--type", options.type];
|
|
432
|
+
if (options.description) args.push("--description", options.description);
|
|
181
433
|
args.push("--stdin");
|
|
434
|
+
await runMulch(args, `record ${domain}`);
|
|
435
|
+
return;
|
|
182
436
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
437
|
+
|
|
438
|
+
const expertiseRecord = buildExpertiseRecord(options);
|
|
439
|
+
const api = await loadMulchApi();
|
|
440
|
+
try {
|
|
441
|
+
await api.recordExpertise(domain, expertiseRecord, { cwd });
|
|
442
|
+
} catch (error) {
|
|
443
|
+
if (error instanceof Error && error.message.includes("not found in config")) {
|
|
444
|
+
// Auto-create domain (matching mulch CLI 0.6.1+ behavior)
|
|
445
|
+
await runMulch(["add", domain], `add ${domain}`);
|
|
446
|
+
await api.recordExpertise(domain, expertiseRecord, { cwd });
|
|
447
|
+
} else {
|
|
448
|
+
throw new AgentError(
|
|
449
|
+
`mulch record ${domain} failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
450
|
+
);
|
|
451
|
+
}
|
|
197
452
|
}
|
|
198
|
-
await runMulch(args, `record ${domain}`);
|
|
199
453
|
},
|
|
200
454
|
|
|
201
455
|
async query(domain) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
456
|
+
if (!domain) {
|
|
457
|
+
throw new AgentError("mulch query failed (exit 1): domain argument required");
|
|
458
|
+
}
|
|
459
|
+
try {
|
|
460
|
+
const api = await loadMulchApi();
|
|
461
|
+
const records = await api.queryDomain(domain, { cwd });
|
|
462
|
+
return formatSearchResults([{ domain, records }]);
|
|
463
|
+
} catch (error) {
|
|
464
|
+
throw new AgentError(
|
|
465
|
+
`mulch query failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
466
|
+
);
|
|
205
467
|
}
|
|
206
|
-
const { stdout } = await runMulch(args, "query");
|
|
207
|
-
return stdout;
|
|
208
468
|
},
|
|
209
469
|
|
|
210
470
|
async search(query, options) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
471
|
+
try {
|
|
472
|
+
const api = await loadMulchApi();
|
|
473
|
+
const results = await api.searchExpertise(query, {
|
|
474
|
+
file: options?.file,
|
|
475
|
+
cwd,
|
|
476
|
+
});
|
|
477
|
+
return formatSearchResults(results);
|
|
478
|
+
} catch (error) {
|
|
479
|
+
throw new AgentError(
|
|
480
|
+
`mulch search failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
481
|
+
);
|
|
482
|
+
}
|
|
216
483
|
},
|
|
217
484
|
|
|
218
485
|
async diff(options) {
|