@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
package/src/commands/logs.ts
CHANGED
|
@@ -10,26 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
import { readdir, stat } from "node:fs/promises";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
+
import { Command } from "commander";
|
|
13
14
|
import { loadConfig } from "../config.ts";
|
|
14
15
|
import { ValidationError } from "../errors.ts";
|
|
16
|
+
import type { ColorFn } from "../logging/color.ts";
|
|
15
17
|
import { color } from "../logging/color.ts";
|
|
16
18
|
import type { LogEvent } from "../types.ts";
|
|
17
19
|
|
|
18
|
-
/**
|
|
19
|
-
* Parse a named flag value from args.
|
|
20
|
-
*/
|
|
21
|
-
function getFlag(args: string[], flag: string): string | undefined {
|
|
22
|
-
const idx = args.indexOf(flag);
|
|
23
|
-
if (idx === -1 || idx + 1 >= args.length) {
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
return args[idx + 1];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function hasFlag(args: string[], flag: string): boolean {
|
|
30
|
-
return args.includes(flag);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
20
|
/**
|
|
34
21
|
* Parse relative time formats like "1h", "30m", "2d", "10s" into a Date object.
|
|
35
22
|
* Falls back to parsing as ISO 8601 if not in relative format.
|
|
@@ -247,21 +234,53 @@ function filterEvents(
|
|
|
247
234
|
});
|
|
248
235
|
}
|
|
249
236
|
|
|
237
|
+
/** Resolve a log level string to a color function. */
|
|
238
|
+
function getLevelColor(level: string): ColorFn {
|
|
239
|
+
switch (level) {
|
|
240
|
+
case "debug":
|
|
241
|
+
return color.gray;
|
|
242
|
+
case "info":
|
|
243
|
+
return color.blue;
|
|
244
|
+
case "warn":
|
|
245
|
+
return color.yellow;
|
|
246
|
+
case "error":
|
|
247
|
+
return color.red;
|
|
248
|
+
default:
|
|
249
|
+
return color.gray;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Resolve a log level to its label string. */
|
|
254
|
+
function getLevelLabel(level: string): string {
|
|
255
|
+
switch (level) {
|
|
256
|
+
case "debug":
|
|
257
|
+
return "DBG";
|
|
258
|
+
case "info":
|
|
259
|
+
return "INF";
|
|
260
|
+
case "warn":
|
|
261
|
+
return "WRN";
|
|
262
|
+
case "error":
|
|
263
|
+
return "ERR";
|
|
264
|
+
default:
|
|
265
|
+
return String(level).slice(0, 3).toUpperCase();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
250
269
|
/**
|
|
251
270
|
* Print log events with ANSI colors and date separators.
|
|
252
271
|
*/
|
|
253
272
|
function printLogs(events: LogEvent[]): void {
|
|
254
273
|
const w = process.stdout.write.bind(process.stdout);
|
|
255
274
|
|
|
256
|
-
w(`${color.bold
|
|
275
|
+
w(`${color.bold("Logs")}\n`);
|
|
257
276
|
w(`${"=".repeat(70)}\n`);
|
|
258
277
|
|
|
259
278
|
if (events.length === 0) {
|
|
260
|
-
w(`${color.dim
|
|
279
|
+
w(`${color.dim("No log files found.")}\n`);
|
|
261
280
|
return;
|
|
262
281
|
}
|
|
263
282
|
|
|
264
|
-
w(`${color.dim
|
|
283
|
+
w(`${color.dim(`${events.length} ${events.length === 1 ? "entry" : "entries"}`)}\n\n`);
|
|
265
284
|
|
|
266
285
|
let lastDate = "";
|
|
267
286
|
|
|
@@ -272,44 +291,21 @@ function printLogs(events: LogEvent[]): void {
|
|
|
272
291
|
if (lastDate !== "") {
|
|
273
292
|
w("\n");
|
|
274
293
|
}
|
|
275
|
-
w(`${color.dim
|
|
294
|
+
w(`${color.dim(`--- ${date} ---`)}\n`);
|
|
276
295
|
lastDate = date;
|
|
277
296
|
}
|
|
278
297
|
|
|
279
298
|
const time = formatAbsoluteTime(event.timestamp);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
let levelStr: string;
|
|
283
|
-
let levelColorCode: string;
|
|
284
|
-
switch (event.level) {
|
|
285
|
-
case "debug":
|
|
286
|
-
levelStr = "DBG";
|
|
287
|
-
levelColorCode = color.gray;
|
|
288
|
-
break;
|
|
289
|
-
case "info":
|
|
290
|
-
levelStr = "INF";
|
|
291
|
-
levelColorCode = color.blue;
|
|
292
|
-
break;
|
|
293
|
-
case "warn":
|
|
294
|
-
levelStr = "WRN";
|
|
295
|
-
levelColorCode = color.yellow;
|
|
296
|
-
break;
|
|
297
|
-
case "error":
|
|
298
|
-
levelStr = "ERR";
|
|
299
|
-
levelColorCode = color.red;
|
|
300
|
-
break;
|
|
301
|
-
default:
|
|
302
|
-
levelStr = String(event.level).slice(0, 3).toUpperCase();
|
|
303
|
-
levelColorCode = color.gray;
|
|
304
|
-
}
|
|
299
|
+
const levelColorFn = getLevelColor(event.level);
|
|
300
|
+
const levelStr = getLevelLabel(event.level);
|
|
305
301
|
|
|
306
302
|
const agentLabel = event.agentName ? `[${event.agentName}]` : "[unknown]";
|
|
307
303
|
const detail = buildLogDetail(event);
|
|
308
|
-
const detailSuffix = detail ? ` ${color.dim
|
|
304
|
+
const detailSuffix = detail ? ` ${color.dim(detail)}` : "";
|
|
309
305
|
|
|
310
306
|
w(
|
|
311
|
-
`${time} ${
|
|
312
|
-
`${event.event} ${color.dim
|
|
307
|
+
`${time} ${levelColorFn(levelStr)} ` +
|
|
308
|
+
`${event.event} ${color.dim(agentLabel)}${detailSuffix}\n`,
|
|
313
309
|
);
|
|
314
310
|
}
|
|
315
311
|
}
|
|
@@ -326,7 +322,7 @@ async function followLogs(
|
|
|
326
322
|
): Promise<void> {
|
|
327
323
|
const w = process.stdout.write.bind(process.stdout);
|
|
328
324
|
|
|
329
|
-
w(`${color.bold
|
|
325
|
+
w(`${color.bold("Following logs (Ctrl+C to stop)")}\n\n`);
|
|
330
326
|
|
|
331
327
|
// Track file positions for tailing
|
|
332
328
|
const filePositions = new Map<string, number>();
|
|
@@ -376,38 +372,16 @@ async function followLogs(
|
|
|
376
372
|
|
|
377
373
|
// Print immediately
|
|
378
374
|
const time = formatAbsoluteTime(event.timestamp);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
let levelColorCode: string;
|
|
382
|
-
switch (event.level) {
|
|
383
|
-
case "debug":
|
|
384
|
-
levelStr = "DBG";
|
|
385
|
-
levelColorCode = color.gray;
|
|
386
|
-
break;
|
|
387
|
-
case "info":
|
|
388
|
-
levelStr = "INF";
|
|
389
|
-
levelColorCode = color.blue;
|
|
390
|
-
break;
|
|
391
|
-
case "warn":
|
|
392
|
-
levelStr = "WRN";
|
|
393
|
-
levelColorCode = color.yellow;
|
|
394
|
-
break;
|
|
395
|
-
case "error":
|
|
396
|
-
levelStr = "ERR";
|
|
397
|
-
levelColorCode = color.red;
|
|
398
|
-
break;
|
|
399
|
-
default:
|
|
400
|
-
levelStr = String(event.level).slice(0, 3).toUpperCase();
|
|
401
|
-
levelColorCode = color.gray;
|
|
402
|
-
}
|
|
375
|
+
const levelColorFn = getLevelColor(event.level);
|
|
376
|
+
const levelStr = getLevelLabel(event.level);
|
|
403
377
|
|
|
404
378
|
const agentLabel = event.agentName ? `[${event.agentName}]` : "[unknown]";
|
|
405
379
|
const detail = buildLogDetail(event);
|
|
406
|
-
const detailSuffix = detail ? ` ${color.dim
|
|
380
|
+
const detailSuffix = detail ? ` ${color.dim(detail)}` : "";
|
|
407
381
|
|
|
408
382
|
w(
|
|
409
|
-
`${time} ${
|
|
410
|
-
`${event.event} ${color.dim
|
|
383
|
+
`${time} ${levelColorFn(levelStr)} ` +
|
|
384
|
+
`${event.event} ${color.dim(agentLabel)}${detailSuffix}\n`,
|
|
411
385
|
);
|
|
412
386
|
}
|
|
413
387
|
} catch {
|
|
@@ -427,36 +401,24 @@ async function followLogs(
|
|
|
427
401
|
}
|
|
428
402
|
}
|
|
429
403
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
--limit <n> Max entries to show (default: 100, returns most recent)
|
|
440
|
-
--follow Tail logs in real time (poll every 1s, Ctrl+C to stop)
|
|
441
|
-
--json Output as JSON array of LogEvent objects
|
|
442
|
-
--help, -h Show this help`;
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Entry point for `overstory logs` command.
|
|
446
|
-
*/
|
|
447
|
-
export async function logsCommand(args: string[]): Promise<void> {
|
|
448
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
449
|
-
process.stdout.write(`${LOGS_HELP}\n`);
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
404
|
+
interface LogsOpts {
|
|
405
|
+
agent?: string;
|
|
406
|
+
level?: string;
|
|
407
|
+
since?: string;
|
|
408
|
+
until?: string;
|
|
409
|
+
limit?: string;
|
|
410
|
+
follow?: boolean;
|
|
411
|
+
json?: boolean;
|
|
412
|
+
}
|
|
452
413
|
|
|
453
|
-
|
|
454
|
-
const
|
|
455
|
-
const
|
|
456
|
-
const
|
|
457
|
-
const
|
|
458
|
-
const
|
|
459
|
-
const
|
|
414
|
+
async function executeLogs(opts: LogsOpts): Promise<void> {
|
|
415
|
+
const json = opts.json ?? false;
|
|
416
|
+
const follow = opts.follow ?? false;
|
|
417
|
+
const agentName = opts.agent;
|
|
418
|
+
const level = opts.level;
|
|
419
|
+
const sinceStr = opts.since;
|
|
420
|
+
const untilStr = opts.until;
|
|
421
|
+
const limitStr = opts.limit;
|
|
460
422
|
const limit = limitStr ? Number.parseInt(limitStr, 10) : 100;
|
|
461
423
|
|
|
462
424
|
if (Number.isNaN(limit) || limit < 1) {
|
|
@@ -544,3 +506,38 @@ export async function logsCommand(args: string[]): Promise<void> {
|
|
|
544
506
|
|
|
545
507
|
printLogs(limited);
|
|
546
508
|
}
|
|
509
|
+
|
|
510
|
+
export function createLogsCommand(): Command {
|
|
511
|
+
return new Command("logs")
|
|
512
|
+
.description("Query NDJSON logs across agents")
|
|
513
|
+
.option("--agent <name>", "Filter logs by agent name")
|
|
514
|
+
.option("--level <level>", "Filter by log level: debug, info, warn, error")
|
|
515
|
+
.option("--since <time>", "Start time filter (ISO 8601 or relative: 1h, 30m, 2d, 10s)")
|
|
516
|
+
.option("--until <time>", "End time filter (ISO 8601)")
|
|
517
|
+
.option("--limit <n>", "Max entries to show (default: 100, returns most recent)")
|
|
518
|
+
.option("--follow", "Tail logs in real time (poll every 1s, Ctrl+C to stop)")
|
|
519
|
+
.option("--json", "Output as JSON array of LogEvent objects")
|
|
520
|
+
.action(async (opts: LogsOpts) => {
|
|
521
|
+
await executeLogs(opts);
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export async function logsCommand(args: string[]): Promise<void> {
|
|
526
|
+
const cmd = createLogsCommand();
|
|
527
|
+
cmd.exitOverride();
|
|
528
|
+
try {
|
|
529
|
+
await cmd.parseAsync(args, { from: "user" });
|
|
530
|
+
} catch (err: unknown) {
|
|
531
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
532
|
+
const code = (err as { code: string }).code;
|
|
533
|
+
if (code === "commander.helpDisplayed" || code === "commander.version") {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
if (code.startsWith("commander.")) {
|
|
537
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
538
|
+
throw new ValidationError(message, { field: "args" });
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
throw err;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
@@ -761,7 +761,7 @@ describe("mailCommand", () => {
|
|
|
761
761
|
capability: "coordinator",
|
|
762
762
|
worktreePath: "/worktrees/orchestrator",
|
|
763
763
|
branchName: "main",
|
|
764
|
-
|
|
764
|
+
taskId: "bead-001",
|
|
765
765
|
tmuxSession: "overstory-test-orchestrator",
|
|
766
766
|
state: "working" as const,
|
|
767
767
|
pid: 12345,
|
|
@@ -779,7 +779,7 @@ describe("mailCommand", () => {
|
|
|
779
779
|
capability: "builder",
|
|
780
780
|
worktreePath: "/worktrees/builder-1",
|
|
781
781
|
branchName: "builder-1",
|
|
782
|
-
|
|
782
|
+
taskId: "bead-002",
|
|
783
783
|
tmuxSession: "overstory-test-builder-1",
|
|
784
784
|
state: "working" as const,
|
|
785
785
|
pid: 12346,
|
|
@@ -797,7 +797,7 @@ describe("mailCommand", () => {
|
|
|
797
797
|
capability: "builder",
|
|
798
798
|
worktreePath: "/worktrees/builder-2",
|
|
799
799
|
branchName: "builder-2",
|
|
800
|
-
|
|
800
|
+
taskId: "bead-003",
|
|
801
801
|
tmuxSession: "overstory-test-builder-2",
|
|
802
802
|
state: "working" as const,
|
|
803
803
|
pid: 12347,
|
|
@@ -815,7 +815,7 @@ describe("mailCommand", () => {
|
|
|
815
815
|
capability: "scout",
|
|
816
816
|
worktreePath: "/worktrees/scout-1",
|
|
817
817
|
branchName: "scout-1",
|
|
818
|
-
|
|
818
|
+
taskId: "bead-004",
|
|
819
819
|
tmuxSession: "overstory-test-scout-1",
|
|
820
820
|
state: "working" as const,
|
|
821
821
|
pid: 12348,
|
|
@@ -1135,7 +1135,7 @@ describe("mailCommand", () => {
|
|
|
1135
1135
|
| "monitor",
|
|
1136
1136
|
worktreePath: `/worktrees/${session.agentName}`,
|
|
1137
1137
|
branchName: session.agentName,
|
|
1138
|
-
|
|
1138
|
+
taskId: `bead-${idx}`,
|
|
1139
1139
|
tmuxSession: `overstory-test-${session.agentName}`,
|
|
1140
1140
|
state: "working" as const,
|
|
1141
1141
|
pid: 10000 + idx,
|