jonah-fleet 1.4.2 → 1.5.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/CHANGELOG.md CHANGED
@@ -5,12 +5,56 @@ All notable changes to `jonah-fleet` will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.5.0](https://github.com/juliendurandeu/jonah-fleet/compare/jonah-fleet-v1.4.2...jonah-fleet-v1.5.0) (2026-09-03)
9
+
10
+
11
+ ### Features
12
+
13
+ * **autowork:** add selective server-side issue querying to prevent tool buffer truncation ([#54](https://github.com/juliendurandeu/jonah-fleet/issues/54)) ([e85958b](https://github.com/juliendurandeu/jonah-fleet/commit/e85958bc9129b16af170961a0ae85ff91211ec85))
14
+ * **cli:** add multi-repo fleet monitoring command (jonah-fleet monitor) ([#6](https://github.com/juliendurandeu/jonah-fleet/issues/6)) ([0093c0f](https://github.com/juliendurandeu/jonah-fleet/commit/0093c0f2bdeb0d48d952119b96e541be73c59a03))
15
+ * **cli:** add per-agent token breakdown to jonah-fleet status and monitor commands ([#16](https://github.com/juliendurandeu/jonah-fleet/issues/16)) ([7e51d0c](https://github.com/juliendurandeu/jonah-fleet/commit/7e51d0cb6bdf8fef127cac7f13f8320821cb4391))
16
+ * **cli:** enhance jonah-fleet init with smart tech-stack auto-detection ([#37](https://github.com/juliendurandeu/jonah-fleet/issues/37)) ([b1b028a](https://github.com/juliendurandeu/jonah-fleet/commit/b1b028a5cc145aeb6a0a9b0df2e4ab010c1fa104))
17
+ * **daemon:** dynamically track and display active issue or PR in terminal spinner and status ([#57](https://github.com/juliendurandeu/jonah-fleet/issues/57)) ([f9c3baa](https://github.com/juliendurandeu/jonah-fleet/commit/f9c3baaab1562e21a0db170f311873cdc9a3e287))
18
+ * **daemon:** multi-cadence daemon, terminal cards, and strict allowlist review filters ([5889a0c](https://github.com/juliendurandeu/jonah-fleet/commit/5889a0c2b11b74faece74f7a64db43cb98fd47a1))
19
+ * **dual-execution:** priority-driven dual execution (local agents + actions) ([#53](https://github.com/juliendurandeu/jonah-fleet/issues/53)) ([5711bf3](https://github.com/juliendurandeu/jonah-fleet/commit/5711bf3798bba799ba4638c8cb2584878ad25d25))
20
+ * **evals:** add automated test suite for bi-directional optimization bridge and downstream sync ([#23](https://github.com/juliendurandeu/jonah-fleet/issues/23)) ([2e55227](https://github.com/juliendurandeu/jonah-fleet/commit/2e552273600c22879d81b6ad607ad912809d3dbb))
21
+ * **fleet-query:** extend fleet query engine with per-routine token and iteration aggregation ([#15](https://github.com/juliendurandeu/jonah-fleet/issues/15)) ([cabbbbc](https://github.com/juliendurandeu/jonah-fleet/commit/cabbbbcc62d8fe39ea135f56acdba0343aa11e02))
22
+ * **fleet:** initial release of jonah-fleet standalone agent suite and CLI ([1b08b3e](https://github.com/juliendurandeu/jonah-fleet/commit/1b08b3e14626cda25e76464d92782f16cfaae936))
23
+ * **optimizer:** implement token anomaly heuristics and automated PR triggers ([#10](https://github.com/juliendurandeu/jonah-fleet/issues/10)) ([#17](https://github.com/juliendurandeu/jonah-fleet/issues/17)) ([3090785](https://github.com/juliendurandeu/jonah-fleet/commit/30907857f7e24edaee1d2c2dbca61568d95f7767))
24
+ * **optimizer:** update optimizer routine with per-agent token aggregation protocol and scorecard schema ([#14](https://github.com/juliendurandeu/jonah-fleet/issues/14)) ([2e435ef](https://github.com/juliendurandeu/jonah-fleet/commit/2e435ef7492bf75dbf3c081d64375f49ea542667))
25
+ * **orchestration:** add failure notification and periodic scan watchdog to prevent stuck PRs ([#32](https://github.com/juliendurandeu/jonah-fleet/issues/32)) ([a7c555f](https://github.com/juliendurandeu/jonah-fleet/commit/a7c555f2f38a868674931bd9b61511b4530f2784))
26
+ * **orchestration:** add symphony radar, attribution notice, and inward blocker dependency gating ([#34](https://github.com/juliendurandeu/jonah-fleet/issues/34)) ([f8995ef](https://github.com/juliendurandeu/jonah-fleet/commit/f8995efd63a1509414768b2f0bf726d1de15a214))
27
+ * **peer-review:** add autonomous issue synthesis for unlinked PRs ([54e9a54](https://github.com/juliendurandeu/jonah-fleet/commit/54e9a54685ee89bf124923fb6086760c3753d097))
28
+ * **peer-review:** allow self-contained PR descriptions without blocking on linked issues ([97b7034](https://github.com/juliendurandeu/jonah-fleet/commit/97b703403040fc9dbe4f6b6622000822d9925aae))
29
+ * **prompts:** add Design System & Telemetry Friction Guardrails to core routines (closes [#49](https://github.com/juliendurandeu/jonah-fleet/issues/49)) ([#50](https://github.com/juliendurandeu/jonah-fleet/issues/50)) ([16d1011](https://github.com/juliendurandeu/jonah-fleet/commit/16d1011ac5255b84f93a8ece7e3f516d7355dc09))
30
+ * **prompts:** add feedback loop stagnation heuristic and closed-loop verification audit ([#24](https://github.com/juliendurandeu/jonah-fleet/issues/24)) ([c3ea012](https://github.com/juliendurandeu/jonah-fleet/commit/c3ea01297017347f6b3c43e4bdd99ea2704fd60e))
31
+ * **routines:** bridge analytics measurement outcomes into product planning for feature pivots and deprecations ([#43](https://github.com/juliendurandeu/jonah-fleet/issues/43)) ([446641d](https://github.com/juliendurandeu/jonah-fleet/commit/446641da7a9f9fc0c00dcf1fa98c26c067145ddc)), closes [#28](https://github.com/juliendurandeu/jonah-fleet/issues/28)
32
+ * **telemetry:** design and implement centralized cross-repo telemetry and token tracking hub (closes [#5](https://github.com/juliendurandeu/jonah-fleet/issues/5)) ([#22](https://github.com/juliendurandeu/jonah-fleet/issues/22)) ([e910277](https://github.com/juliendurandeu/jonah-fleet/commit/e9102779293601e94a23401a10e669bb174cb972))
33
+ * **workflows:** support custom cron schedules in manifest and preserve across syncs ([#47](https://github.com/juliendurandeu/jonah-fleet/issues/47)) ([#48](https://github.com/juliendurandeu/jonah-fleet/issues/48)) ([029fadb](https://github.com/juliendurandeu/jonah-fleet/commit/029fadbcabe805fb46268fa369e8f0f7272c33f1))
34
+ * **workflows:** support manual and comment-based (re)triggering for peer review routine ([#30](https://github.com/juliendurandeu/jonah-fleet/issues/30)) ([e8be955](https://github.com/juliendurandeu/jonah-fleet/commit/e8be955f429450fdce2d79f52a27c3ee4bb41acd)), closes [#29](https://github.com/juliendurandeu/jonah-fleet/issues/29)
35
+ * **workflows:** support triggering autowork via workflow_dispatch badge link (closes [#18](https://github.com/juliendurandeu/jonah-fleet/issues/18)) ([#20](https://github.com/juliendurandeu/jonah-fleet/issues/20)) ([a44e54e](https://github.com/juliendurandeu/jonah-fleet/commit/a44e54e647db11587ab58eb0ec01201cb7e9376f))
36
+
37
+
38
+ ### Bug Fixes
39
+
40
+ * **ci:** grant issues:write to release-please and use googleapis action ([37a6782](https://github.com/juliendurandeu/jonah-fleet/commit/37a67824e4cbbced5e08dc48d79b70a4145646ff))
41
+ * **detector:** use literal replacers and pmPrefix for fastapi dev ([0b96e2a](https://github.com/juliendurandeu/jonah-fleet/commit/0b96e2a06230df86a1c5a9097aa98b380565bcc8))
42
+ * **security:** resolve Critical and High vulnerabilities in vitest and transitive dependencies ([#42](https://github.com/juliendurandeu/jonah-fleet/issues/42)) ([9c88fa9](https://github.com/juliendurandeu/jonah-fleet/commit/9c88fa97b1529ba20fc9223ddb6e7c08e1b345f6))
43
+
44
+
45
+ ### Performance Improvements
46
+
47
+ * **workflows:** optimize github actions quota consumption ([#51](https://github.com/juliendurandeu/jonah-fleet/issues/51)) ([af8a8fb](https://github.com/juliendurandeu/jonah-fleet/commit/af8a8fb08cc9b884192e1204548a53b571f1a4a6))
48
+
8
49
  ## [1.4.2] - 2026-09-03
9
50
 
10
51
  ### Changed
11
52
  - Strict Allowlist for Cloud Review Triggers:
12
53
  - Updated `trigger-review-routine.yml` to strictly require `priority/P0` or `priority/P1` labels to fire immediate cloud reviews on `pull_request` events.
13
54
  - Untagged PRs and lower-priority PRs (P2/P3) skip immediate cloud review, routing review execution exclusively to local peer-review daemons (with 48h scheduled cloud catchup fallback).
55
+ - Dynamic Issue & PR Target Tracking:
56
+ - Added real-time target detection (`detectClaimedIssue` and `detectClaimedPR`) in `src/lib/terminal-card.ts` that dynamically switches the live spinner label from `autowork` / `peer-review` to `[Issue #<N>]` or `[PR #<N>]` the moment a candidate is claimed.
57
+ - Added `activeTarget` tracking in `DaemonState` (`.jonah-fleet/daemon.json`), displayed in `jonah-fleet daemon status` (e.g. `WORKING on autowork (Issue #42)`).
14
58
 
15
59
  ## [1.4.1] - 2026-09-03
16
60
 
@@ -5,6 +5,7 @@ export interface DaemonCommandOptions {
5
5
  routines?: string;
6
6
  model?: string;
7
7
  foreground?: boolean;
8
+ verbose?: boolean;
8
9
  }
9
10
  export declare function runDaemonCommand(action?: string, options?: DaemonCommandOptions): Promise<void>;
10
11
  //# sourceMappingURL=daemon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/commands/daemon.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAsB,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2FzG"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/commands/daemon.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6FzG"}
@@ -6,6 +6,7 @@ export interface RunCommandOptions {
6
6
  worktree?: boolean;
7
7
  keepWorktree?: boolean;
8
8
  dryRun?: boolean;
9
+ verbose?: boolean;
9
10
  }
10
11
  export declare function runRoutineCommand(routine: string, options?: RunCommandOptions): Promise<void>;
11
12
  //# sourceMappingURL=run.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DvG"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CA+DvG"}
package/dist/index.js CHANGED
@@ -92,7 +92,7 @@ var ROUTINE_TO_WORKFLOW_MAP = {
92
92
  "product-planning": [],
93
93
  "analytics-review": []
94
94
  };
95
- var FLEET_VERSION = "1.4.2";
95
+ var FLEET_VERSION = "1.5.0";
96
96
  var SCHEMA_URL = "https://raw.githubusercontent.com/juliendurandeu/jonah-fleet/main/schema.json";
97
97
 
98
98
  // src/lib/manifest.ts
@@ -2157,11 +2157,11 @@ async function runTelemetry(options = {}) {
2157
2157
  }
2158
2158
 
2159
2159
  // src/commands/run.ts
2160
- import pc9 from "picocolors";
2160
+ import pc10 from "picocolors";
2161
2161
 
2162
2162
  // src/lib/runner.ts
2163
- import fs11 from "fs";
2164
- import path11 from "path";
2163
+ import fs12 from "fs";
2164
+ import path12 from "path";
2165
2165
  import os2 from "os";
2166
2166
  import { spawn, execSync as execSync2 } from "child_process";
2167
2167
 
@@ -2286,18 +2286,369 @@ async function cleanupStaleWorktrees(repoRoot) {
2286
2286
  return cleaned;
2287
2287
  }
2288
2288
 
2289
+ // src/lib/terminal-card.ts
2290
+ import fs11 from "fs";
2291
+ import path11 from "path";
2292
+ import pc9 from "picocolors";
2293
+ function sanitizeWorktreePaths(text) {
2294
+ let cleaned = text.replace(/file:\/\/\/[^\s"'()]+?\/\.jonah-fleet\/worktrees\/[^/\s"'()]+\//g, "");
2295
+ cleaned = cleaned.replace(/(?:^|[\s"'(`[])(?:\/[^\s"'()]+?)?\.jonah-fleet\/worktrees\/[^/\s"'()]+\//g, (match) => {
2296
+ const prefix = match.charAt(0);
2297
+ return prefix === "/" ? "" : prefix;
2298
+ });
2299
+ cleaned = cleaned.replace(/\[`?([^`\]]+?)`?\]\(file:\/\/\/[^\s)]+\)/g, "`$1`");
2300
+ return cleaned;
2301
+ }
2302
+ function extractExecutionSummary(output) {
2303
+ const summaryHeaderRegex = /#\s+([A-Za-z0-9\s_-]+?Execution\s+Summary[\s\S]*)/i;
2304
+ const match = output.match(summaryHeaderRegex);
2305
+ if (!match) return null;
2306
+ let summary = match[1].trim();
2307
+ const trailingSeparators = [
2308
+ "\u2713 Local peer-review completed",
2309
+ "\u2713 Local autowork completed",
2310
+ "\u2713 Local agent session",
2311
+ "[6:",
2312
+ "Peer Review Watchdog:"
2313
+ ];
2314
+ for (const sep of trailingSeparators) {
2315
+ const idx = summary.indexOf(sep);
2316
+ if (idx !== -1) {
2317
+ summary = summary.slice(0, idx).trim();
2318
+ }
2319
+ }
2320
+ return sanitizeWorktreePaths(summary);
2321
+ }
2322
+ function findLatestRunLog(repoRoot, routine) {
2323
+ const logsDir = path11.join(repoRoot, ".github", "prompts", "logs", routine);
2324
+ if (!fs11.existsSync(logsDir)) return null;
2325
+ try {
2326
+ const files = fs11.readdirSync(logsDir).filter((f) => f.endsWith(".md") && !f.startsWith("_"));
2327
+ if (files.length === 0) return null;
2328
+ files.sort().reverse();
2329
+ return path11.join(logsDir, files[0]);
2330
+ } catch {
2331
+ return null;
2332
+ }
2333
+ }
2334
+ function parseRunLog(logContent) {
2335
+ const summary = {
2336
+ passes: [],
2337
+ actions: []
2338
+ };
2339
+ const lines = logContent.split("\n");
2340
+ for (const line of lines) {
2341
+ const trimmed = line.trim();
2342
+ if (trimmed.startsWith("|") && trimmed.includes("|")) {
2343
+ const parts = trimmed.split("|").map((p) => p.trim()).filter(Boolean);
2344
+ if (parts.length >= 2) {
2345
+ const key = parts[0].toLowerCase();
2346
+ const value = parts[1].replace(/`/g, "");
2347
+ if (key.includes("routine")) summary.routine = value;
2348
+ if (key.includes("target pr") || key.includes("target issue")) summary.target = value;
2349
+ if (key.includes("decision")) summary.decision = value;
2350
+ if (key.includes("result")) summary.result = value;
2351
+ if (key.includes("duration")) summary.duration = value;
2352
+ }
2353
+ }
2354
+ }
2355
+ let inDoD = false;
2356
+ let inFindings = false;
2357
+ let inActions = false;
2358
+ for (const line of lines) {
2359
+ const trimmed = line.trim();
2360
+ if (trimmed.startsWith("## Definition of Done")) {
2361
+ inDoD = true;
2362
+ inFindings = false;
2363
+ inActions = false;
2364
+ continue;
2365
+ } else if (trimmed.startsWith("## Code Review Findings") || trimmed.startsWith("## Findings")) {
2366
+ inDoD = false;
2367
+ inFindings = true;
2368
+ inActions = false;
2369
+ continue;
2370
+ } else if (trimmed.startsWith("## Execution Trace") || trimmed.startsWith("### Actions Taken")) {
2371
+ inDoD = false;
2372
+ inFindings = false;
2373
+ inActions = true;
2374
+ continue;
2375
+ } else if (trimmed.startsWith("## ")) {
2376
+ inDoD = false;
2377
+ inFindings = false;
2378
+ inActions = false;
2379
+ }
2380
+ if (inDoD && trimmed.startsWith("|") && !trimmed.includes("Criterion") && !trimmed.includes("---")) {
2381
+ const parts = trimmed.split("|").map((p) => p.trim()).filter(Boolean);
2382
+ if (parts.length >= 2) {
2383
+ const criterion = parts[0];
2384
+ const met = parts[1].toUpperCase() === "YES" || parts[1].toUpperCase() === "PASS";
2385
+ const evidence = parts[2] ? ` (${parts[2].slice(0, 60)}...)` : "";
2386
+ summary.passes?.push({
2387
+ name: criterion,
2388
+ status: met ? "pass" : "fail",
2389
+ detail: evidence
2390
+ });
2391
+ }
2392
+ }
2393
+ if (inActions && (trimmed.startsWith("- ") || trimmed.startsWith("* "))) {
2394
+ summary.actions?.push(sanitizeWorktreePaths(trimmed.slice(2)));
2395
+ }
2396
+ }
2397
+ return summary;
2398
+ }
2399
+ function detectActivePhase(chunk, currentPhase = "Executing routine") {
2400
+ const lower = chunk.toLowerCase();
2401
+ if (lower.includes("\u{1F512} claimed") || lower.includes("claimed by local autowork") || lower.includes("claimed by autowork")) {
2402
+ return "Claimed target issue, starting implementation";
2403
+ }
2404
+ if (lower.includes("starting review (round")) {
2405
+ return "Claimed review window, starting review passes";
2406
+ }
2407
+ if (lower.includes("check-client-boundary")) return "Verifying React Server Component boundaries";
2408
+ if (lower.includes("type-check") || lower.includes("tsc --noemit")) return "Running TypeScript type checks";
2409
+ if (lower.includes("lint") || lower.includes("eslint")) return "Running codebase linter";
2410
+ if (lower.includes("test") || lower.includes("vitest") || lower.includes("jest")) return "Running automated test suite";
2411
+ if (lower.includes("build") || lower.includes("next build") || lower.includes("tsup")) return "Running production build verification";
2412
+ if (lower.includes("code-review") || lower.includes("subagent")) return "Running multi-angle code review passes";
2413
+ if (lower.includes("squash-merge") || lower.includes("pr merge")) return "Squash-merging target PR to main";
2414
+ if (lower.includes("gh issue create") || lower.includes("autonomous issue synthesis")) return "Synthesizing tracking issue";
2415
+ if (lower.includes("--undo") || lower.includes("draft")) return "Bouncing PR back to draft for author fixes";
2416
+ if (lower.includes("issue edit") || lower.includes("pr edit")) return "Linking PR & tracking issues";
2417
+ if (lower.includes("pr comment") || lower.includes("review summary")) return "Submitting review comment";
2418
+ if (lower.includes("worktree")) return "Preparing workspace worktree";
2419
+ return currentPhase;
2420
+ }
2421
+ function detectClaimedIssue(chunk) {
2422
+ const claimMatch = chunk.match(/🔒\s*Claimed[^\n#]*?#(\d+)/i);
2423
+ if (claimMatch) return `Issue #${claimMatch[1]}`;
2424
+ const ghMatch = chunk.match(/gh\s+issue\s+(?:view|edit|comment|develop)\s+(\d+)/i);
2425
+ if (ghMatch) return `Issue #${ghMatch[1]}`;
2426
+ const textMatch = chunk.match(/(?:selected|claimed|claiming|target(?:ing)?|working|candidate)\s+(?:candidate\s+)?issue\s+#?(\d+)/i);
2427
+ if (textMatch) return `Issue #${textMatch[1]}`;
2428
+ const passiveMatch = chunk.match(/issue\s+#(\d+)\s+(?:claimed|selected)/i);
2429
+ if (passiveMatch) return `Issue #${passiveMatch[1]}`;
2430
+ return null;
2431
+ }
2432
+ function detectClaimedPR(chunk) {
2433
+ const reviewMatch = chunk.match(/Starting\s+review[^\n#]*?#(\d+)/i);
2434
+ if (reviewMatch) return `PR #${reviewMatch[1]}`;
2435
+ const prMatch = chunk.match(/(?:selected|target|reviewing)\s+(?:target\s+)?PR:?\s*\[?PR\s*#?(\d+)/i);
2436
+ if (prMatch) return `PR #${prMatch[1]}`;
2437
+ const ghPrMatch = chunk.match(/gh\s+pr\s+(?:view|diff|checkout|review)\s+(\d+)/i);
2438
+ if (ghPrMatch) return `PR #${ghPrMatch[1]}`;
2439
+ return null;
2440
+ }
2441
+ function renderSummaryCard(options) {
2442
+ const width = Math.min(Math.max((process.stdout.columns || 80) - 4, 60), 86);
2443
+ const horizontal = "\u2500".repeat(width - 2);
2444
+ const rawSummary = options.output ? extractExecutionSummary(options.output) : null;
2445
+ let parsedFromLog = null;
2446
+ if (options.repoRoot) {
2447
+ const latestLog = findLatestRunLog(options.repoRoot, options.routine);
2448
+ if (latestLog) {
2449
+ try {
2450
+ const content = fs11.readFileSync(latestLog, "utf8");
2451
+ parsedFromLog = parseRunLog(content);
2452
+ parsedFromLog.logPath = path11.relative(options.repoRoot, latestLog);
2453
+ } catch {
2454
+ }
2455
+ }
2456
+ }
2457
+ let target = options.pr ? `PR #${options.pr}` : options.issue ? `Issue #${options.issue}` : "";
2458
+ if (!target && parsedFromLog?.target) target = parsedFromLog.target;
2459
+ let decision = parsedFromLog?.decision || "";
2460
+ if (!decision && rawSummary) {
2461
+ const decisionMatch = rawSummary.match(/\*\*Final Action\*\*:\s*([^\n]+)/i);
2462
+ if (decisionMatch) decision = decisionMatch[1].replace(/[`*]/g, "").trim();
2463
+ }
2464
+ const durationStr = options.durationMs ? `${Math.round(options.durationMs / 1e3)}s` : parsedFromLog?.duration || "";
2465
+ const lines = [];
2466
+ lines.push(pc9.cyan(`\u250C${horizontal}\u2510`));
2467
+ const title = ` Jonah Fleet Routine: ${pc9.bold(options.routine.toUpperCase())} `;
2468
+ lines.push(
2469
+ pc9.cyan("\u2502") + ` ${pc9.bold(pc9.white(options.routine.toUpperCase()))}` + (target ? ` \xB7 ${pc9.yellow(target)}` : "") + (durationStr ? pc9.dim(` (${durationStr})`) : "") + " ".repeat(
2470
+ Math.max(
2471
+ 1,
2472
+ width - 4 - options.routine.length - target.length - (durationStr ? durationStr.length + 3 : 0)
2473
+ )
2474
+ ) + pc9.cyan("\u2502")
2475
+ );
2476
+ if (decision) {
2477
+ let decisionBadge = pc9.green(`\u2714 ${decision}`);
2478
+ if (/bounce|draft|reject|fail/i.test(decision)) {
2479
+ decisionBadge = pc9.yellow(`\u26A0\uFE0F ${decision}`);
2480
+ } else if (/escalat/i.test(decision)) {
2481
+ decisionBadge = pc9.red(`\u{1F6A8} ${decision}`);
2482
+ }
2483
+ lines.push(
2484
+ pc9.cyan("\u2502") + ` Action: ${decisionBadge}` + " ".repeat(Math.max(1, width - 11 - decision.length)) + pc9.cyan("\u2502")
2485
+ );
2486
+ }
2487
+ lines.push(pc9.cyan(`\u251C${horizontal}\u2524`));
2488
+ if (rawSummary) {
2489
+ const summaryLines = rawSummary.split("\n");
2490
+ for (const rawLine of summaryLines) {
2491
+ const line = rawLine.trim();
2492
+ if (!line) continue;
2493
+ if (line.startsWith("# ")) continue;
2494
+ if (line.startsWith("---")) continue;
2495
+ if (line.startsWith("**Selected Target") || line.startsWith("**Final Action") || line.startsWith("**Mode**:")) {
2496
+ continue;
2497
+ }
2498
+ if (line.startsWith("### ")) {
2499
+ const heading = line.replace("### ", "").trim();
2500
+ lines.push(
2501
+ pc9.cyan("\u2502") + ` ${pc9.bold(pc9.cyan(heading))}` + " ".repeat(Math.max(1, width - 3 - heading.length)) + pc9.cyan("\u2502")
2502
+ );
2503
+ } else if (line.startsWith("- ") || line.startsWith("* ")) {
2504
+ const item = sanitizeWorktreePaths(line.slice(2)).trim();
2505
+ const formatted = item.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, (_, text) => pc9.bold(text)).replace(/`([^`]+)`/g, (_, code) => pc9.yellow(code));
2506
+ const plainLen = item.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").length;
2507
+ if (plainLen <= width - 6) {
2508
+ lines.push(pc9.cyan("\u2502") + ` \u2022 ${formatted}` + " ".repeat(Math.max(1, width - 5 - plainLen)) + pc9.cyan("\u2502"));
2509
+ } else {
2510
+ const truncated = formatted.slice(0, width - 10) + "...";
2511
+ lines.push(pc9.cyan("\u2502") + ` \u2022 ${truncated}` + " ".repeat(Math.max(1, width - 5 - (width - 7))) + pc9.cyan("\u2502"));
2512
+ }
2513
+ } else if (/^[0-9]+\.\s+/.test(line)) {
2514
+ const item = sanitizeWorktreePaths(line.replace(/^[0-9]+\.\s+/, "")).trim();
2515
+ const formatted = item.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, (_, text) => pc9.bold(text)).replace(/`([^`]+)`/g, (_, code) => pc9.yellow(code));
2516
+ const plainLen = item.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/\*\*([^*]+)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1").length;
2517
+ if (plainLen <= width - 6) {
2518
+ lines.push(pc9.cyan("\u2502") + ` \u2714 ${formatted}` + " ".repeat(Math.max(1, width - 5 - plainLen)) + pc9.cyan("\u2502"));
2519
+ } else {
2520
+ const truncated = formatted.slice(0, width - 10) + "...";
2521
+ lines.push(pc9.cyan("\u2502") + ` \u2714 ${truncated}` + " ".repeat(Math.max(1, width - 5 - (width - 7))) + pc9.cyan("\u2502"));
2522
+ }
2523
+ }
2524
+ }
2525
+ } else if (parsedFromLog && parsedFromLog.passes && parsedFromLog.passes.length > 0) {
2526
+ lines.push(pc9.cyan("\u2502") + ` ${pc9.bold("Verification Passes:")}` + " ".repeat(Math.max(1, width - 23)) + pc9.cyan("\u2502"));
2527
+ for (const pass of parsedFromLog.passes.slice(0, 6)) {
2528
+ const icon = pass.status === "pass" ? pc9.green("\u2714") : pc9.red("\u2716");
2529
+ const text = `${pass.name}${pass.detail || ""}`;
2530
+ const plainLen = text.length + 4;
2531
+ const truncated = plainLen > width - 6 ? text.slice(0, width - 10) + "..." : text;
2532
+ lines.push(pc9.cyan("\u2502") + ` ${icon} ${truncated}` + " ".repeat(Math.max(1, width - 5 - truncated.length)) + pc9.cyan("\u2502"));
2533
+ }
2534
+ }
2535
+ if (parsedFromLog?.logPath) {
2536
+ lines.push(pc9.cyan(`\u251C${horizontal}\u2524`));
2537
+ const logInfo = ` Run log: ${pc9.dim(parsedFromLog.logPath)}`;
2538
+ const logPlain = ` Run log: ${parsedFromLog.logPath}`;
2539
+ lines.push(pc9.cyan("\u2502") + logInfo + " ".repeat(Math.max(1, width - 2 - logPlain.length)) + pc9.cyan("\u2502"));
2540
+ }
2541
+ lines.push(pc9.cyan(`\u2514${horizontal}\u2518`));
2542
+ return lines.join("\n");
2543
+ }
2544
+ function renderErrorCard(options) {
2545
+ const width = Math.min(Math.max((process.stdout.columns || 80) - 4, 60), 86);
2546
+ const horizontal = "\u2500".repeat(width - 2);
2547
+ const lines = [];
2548
+ lines.push(pc9.red(`\u250C${horizontal}\u2510`));
2549
+ const target = options.pr ? ` \xB7 PR #${options.pr}` : options.issue ? ` \xB7 Issue #${options.issue}` : "";
2550
+ const durationStr = options.durationMs ? ` (${Math.round(options.durationMs / 1e3)}s)` : "";
2551
+ const routineUpper = options.routine.toUpperCase();
2552
+ const header = ` \u2717 Routine '${routineUpper}' Failed (Exit Code ${options.exitCode})${target}${durationStr}`;
2553
+ const headerPlain = ` \u2717 Routine '${routineUpper}' Failed (Exit Code ${options.exitCode})${target}${durationStr}`;
2554
+ lines.push(
2555
+ pc9.red("\u2502") + pc9.bold(pc9.red(header.slice(0, width - 3))) + " ".repeat(Math.max(1, width - 2 - headerPlain.length)) + pc9.red("\u2502")
2556
+ );
2557
+ lines.push(pc9.red(`\u251C${horizontal}\u2524`));
2558
+ const logPath = path11.join(options.repoRoot, ".jonah-fleet", "daemon.log");
2559
+ lines.push(pc9.red("\u2502") + pc9.yellow(" Recent Log Output:") + " ".repeat(Math.max(1, width - 21)) + pc9.red("\u2502"));
2560
+ if (fs11.existsSync(logPath)) {
2561
+ try {
2562
+ const logContent = fs11.readFileSync(logPath, "utf8");
2563
+ const allLines = logContent.split("\n").filter((l) => l.trim().length > 0);
2564
+ const tailLines = allLines.slice(-10);
2565
+ for (const line of tailLines) {
2566
+ const cleaned = sanitizeWorktreePaths(line).trim();
2567
+ const truncated = cleaned.length > width - 6 ? cleaned.slice(0, width - 9) + "..." : cleaned;
2568
+ lines.push(pc9.red("\u2502") + pc9.dim(` ${truncated}`) + " ".repeat(Math.max(1, width - 4 - truncated.length)) + pc9.red("\u2502"));
2569
+ }
2570
+ } catch {
2571
+ lines.push(
2572
+ pc9.red("\u2502") + pc9.dim(" (Could not read .jonah-fleet/daemon.log)") + " ".repeat(Math.max(1, width - 45)) + pc9.red("\u2502")
2573
+ );
2574
+ }
2575
+ } else {
2576
+ lines.push(pc9.red("\u2502") + pc9.dim(" (No daemon.log found)") + " ".repeat(Math.max(1, width - 26)) + pc9.red("\u2502"));
2577
+ }
2578
+ lines.push(pc9.red(`\u251C${horizontal}\u2524`));
2579
+ const relLogPath = path11.relative(options.repoRoot, logPath) || ".jonah-fleet/daemon.log";
2580
+ const footer = ` Full trace: ${relLogPath}`;
2581
+ const truncatedFooter = footer.slice(0, width - 4);
2582
+ lines.push(pc9.red("\u2502") + pc9.dim(truncatedFooter) + " ".repeat(Math.max(1, width - 2 - truncatedFooter.length)) + pc9.red("\u2502"));
2583
+ lines.push(pc9.red(`\u2514${horizontal}\u2518`));
2584
+ return lines.join("\n");
2585
+ }
2586
+ var TerminalSpinner = class {
2587
+ frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
2588
+ currentFrame = 0;
2589
+ intervalId = null;
2590
+ startTime = 0;
2591
+ message = "";
2592
+ isRunning = false;
2593
+ isTTY;
2594
+ constructor() {
2595
+ this.isTTY = Boolean(process.stderr.isTTY);
2596
+ }
2597
+ start(initialMessage) {
2598
+ this.message = initialMessage;
2599
+ this.startTime = Date.now();
2600
+ this.isRunning = true;
2601
+ if (!this.isTTY) {
2602
+ process.stderr.write(`[jonah-fleet] ${initialMessage}
2603
+ `);
2604
+ return;
2605
+ }
2606
+ this.intervalId = setInterval(() => {
2607
+ this.render();
2608
+ }, 80);
2609
+ }
2610
+ update(newMessage) {
2611
+ this.message = newMessage;
2612
+ if (!this.isTTY) {
2613
+ process.stderr.write(`[jonah-fleet] ${newMessage}
2614
+ `);
2615
+ }
2616
+ }
2617
+ render() {
2618
+ if (!this.isRunning || !this.isTTY) return;
2619
+ const frame = pc9.cyan(this.frames[this.currentFrame]);
2620
+ this.currentFrame = (this.currentFrame + 1) % this.frames.length;
2621
+ const elapsedSeconds = Math.floor((Date.now() - this.startTime) / 1e3);
2622
+ const mins = Math.floor(elapsedSeconds / 60);
2623
+ const secs = elapsedSeconds % 60;
2624
+ const timeStr = pc9.dim(`[${mins}m ${secs < 10 ? "0" : ""}${secs}s]`);
2625
+ process.stderr.write(`\r\x1B[K ${frame} ${this.message} ${timeStr}`);
2626
+ }
2627
+ stop() {
2628
+ if (!this.isRunning) return;
2629
+ this.isRunning = false;
2630
+ if (this.intervalId) {
2631
+ clearInterval(this.intervalId);
2632
+ this.intervalId = null;
2633
+ }
2634
+ if (this.isTTY) {
2635
+ process.stderr.write("\r\x1B[K");
2636
+ }
2637
+ }
2638
+ };
2639
+
2289
2640
  // src/lib/runner.ts
2290
2641
  function discoverSkillsPrompt(targetDir) {
2291
- const skillsDir = path11.join(targetDir, ".agents", "skills");
2292
- if (!fs11.existsSync(skillsDir)) return "";
2642
+ const skillsDir = path12.join(targetDir, ".agents", "skills");
2643
+ if (!fs12.existsSync(skillsDir)) return "";
2293
2644
  let skillsPrompt = "";
2294
2645
  try {
2295
- const entries = fs11.readdirSync(skillsDir, { withFileTypes: true });
2646
+ const entries = fs12.readdirSync(skillsDir, { withFileTypes: true });
2296
2647
  for (const entry of entries) {
2297
2648
  if (entry.isDirectory()) {
2298
- const skillPath = path11.join(".agents", "skills", entry.name, "SKILL.md");
2299
- const fullPath = path11.join(targetDir, skillPath);
2300
- if (fs11.existsSync(fullPath)) {
2649
+ const skillPath = path12.join(".agents", "skills", entry.name, "SKILL.md");
2650
+ const fullPath = path12.join(targetDir, skillPath);
2651
+ if (fs12.existsSync(fullPath)) {
2301
2652
  skillsPrompt += `Read and follow ${skillPath}. `;
2302
2653
  }
2303
2654
  }
@@ -2324,14 +2675,14 @@ function buildRoutinePrompt(targetDir, routine, options = {}) {
2324
2675
  return `You are the ${routine} routine for this repository. Read and follow the instructions in ${promptFile} exactly. ${skillsPrompt}`;
2325
2676
  }
2326
2677
  async function runLocalRoutine(options) {
2327
- const targetDir = path11.resolve(options.targetDir);
2678
+ const targetDir = path12.resolve(options.targetDir);
2328
2679
  const routine = options.routine;
2329
2680
  const model = options.model || "gemini-3.7-flash-high";
2330
2681
  const printTimeout = options.printTimeout || "30m";
2331
2682
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2332
2683
  const hostname = os2.hostname();
2333
- const promptFile = path11.join(targetDir, ".github", "prompts", `${routine}.md`);
2334
- if (!fs11.existsSync(promptFile)) {
2684
+ const promptFile = path12.join(targetDir, ".github", "prompts", `${routine}.md`);
2685
+ if (!fs12.existsSync(promptFile)) {
2335
2686
  throw new Error(`Routine prompt file not found: ${promptFile}`);
2336
2687
  }
2337
2688
  let branchName = `agent/${routine}-${timestamp}`;
@@ -2384,7 +2735,19 @@ Timeout: ${printTimeout}`,
2384
2735
  ];
2385
2736
  let output = "";
2386
2737
  let exitCode = 0;
2738
+ const startTime = Date.now();
2739
+ const logDir = path12.join(targetDir, ".jonah-fleet");
2740
+ fs12.mkdirSync(logDir, { recursive: true });
2741
+ const logFilePath = path12.join(logDir, "daemon.log");
2742
+ let targetLabel = options.pr ? `PR #${options.pr}` : options.issue ? `Issue #${options.issue}` : routine;
2743
+ let dynamicTargetDetected = Boolean(options.pr || options.issue);
2744
+ let activePhase = "Starting session...";
2745
+ const spinner = !options.verbose ? new TerminalSpinner() : null;
2746
+ if (spinner) {
2747
+ spinner.start(`${targetLabel}: ${activePhase}`);
2748
+ }
2387
2749
  const cleanup = async () => {
2750
+ spinner?.stop();
2388
2751
  if (worktreePath && !options.keepWorktree) {
2389
2752
  await removeWorktree(targetDir, worktreePath, { deleteBranch: false }).catch(() => {
2390
2753
  });
@@ -2396,6 +2759,40 @@ Timeout: ${printTimeout}`,
2396
2759
  };
2397
2760
  process.once("SIGINT", sigintHandler);
2398
2761
  process.once("SIGTERM", sigintHandler);
2762
+ const processChunk = (chunk, isStderr = false) => {
2763
+ output += chunk;
2764
+ try {
2765
+ fs12.appendFileSync(logFilePath, chunk, "utf8");
2766
+ } catch {
2767
+ }
2768
+ if (!dynamicTargetDetected) {
2769
+ const detected = routine === "peer-review" ? detectClaimedPR(chunk) : detectClaimedIssue(chunk);
2770
+ if (detected) {
2771
+ dynamicTargetDetected = true;
2772
+ targetLabel = detected;
2773
+ options.onTargetDetected?.(detected);
2774
+ if (spinner) {
2775
+ spinner.update(`${targetLabel}: ${activePhase}`);
2776
+ }
2777
+ }
2778
+ }
2779
+ if (options.onLog) {
2780
+ options.onLog(chunk);
2781
+ }
2782
+ if (options.verbose) {
2783
+ if (isStderr) {
2784
+ process.stderr.write(chunk);
2785
+ } else {
2786
+ process.stdout.write(chunk);
2787
+ }
2788
+ } else if (spinner) {
2789
+ const newPhase = detectActivePhase(chunk, activePhase);
2790
+ if (newPhase !== activePhase) {
2791
+ activePhase = newPhase;
2792
+ spinner.update(`${targetLabel}: ${activePhase}`);
2793
+ }
2794
+ }
2795
+ };
2399
2796
  try {
2400
2797
  exitCode = await new Promise((resolve, reject) => {
2401
2798
  const child = spawn("agy", args, {
@@ -2404,37 +2801,56 @@ Timeout: ${printTimeout}`,
2404
2801
  stdio: ["inherit", "pipe", "pipe"]
2405
2802
  });
2406
2803
  child.stdout?.on("data", (data) => {
2407
- const chunk = data.toString();
2408
- output += chunk;
2409
- if (options.onLog) {
2410
- options.onLog(chunk);
2411
- } else {
2412
- process.stdout.write(chunk);
2413
- }
2804
+ processChunk(data.toString(), false);
2414
2805
  });
2415
2806
  child.stderr?.on("data", (data) => {
2416
- const chunk = data.toString();
2417
- output += chunk;
2418
- if (options.onLog) {
2419
- options.onLog(chunk);
2420
- } else {
2421
- process.stderr.write(chunk);
2422
- }
2807
+ processChunk(data.toString(), true);
2423
2808
  });
2424
2809
  child.on("error", (err) => {
2810
+ spinner?.stop();
2425
2811
  reject(err);
2426
2812
  });
2427
2813
  child.on("close", (code) => {
2814
+ spinner?.stop();
2428
2815
  resolve(code ?? 0);
2429
2816
  });
2430
2817
  });
2431
2818
  } finally {
2819
+ spinner?.stop();
2432
2820
  process.removeListener("SIGINT", sigintHandler);
2433
2821
  process.removeListener("SIGTERM", sigintHandler);
2434
2822
  if (!options.keepWorktree) {
2435
2823
  await cleanup();
2436
2824
  }
2437
2825
  }
2826
+ if (options.showCard !== false && !options.verbose) {
2827
+ const durationMs = Date.now() - startTime;
2828
+ const effectiveIssue = options.issue || (targetLabel.startsWith("Issue #") ? targetLabel.replace("Issue #", "") : void 0);
2829
+ const effectivePR = options.pr || (targetLabel.startsWith("PR #") ? targetLabel.replace("PR #", "") : void 0);
2830
+ if (exitCode === 0) {
2831
+ console.log(
2832
+ "\n" + renderSummaryCard({
2833
+ routine,
2834
+ output,
2835
+ repoRoot: targetDir,
2836
+ issue: effectiveIssue,
2837
+ pr: effectivePR,
2838
+ durationMs
2839
+ }) + "\n"
2840
+ );
2841
+ } else {
2842
+ console.log(
2843
+ "\n" + renderErrorCard({
2844
+ routine,
2845
+ exitCode,
2846
+ repoRoot: targetDir,
2847
+ issue: effectiveIssue,
2848
+ pr: effectivePR,
2849
+ durationMs
2850
+ }) + "\n"
2851
+ );
2852
+ }
2853
+ }
2438
2854
  return {
2439
2855
  success: exitCode === 0,
2440
2856
  exitCode,
@@ -2450,28 +2866,31 @@ async function runRoutineCommand(routine, options = {}) {
2450
2866
  const manifest = loadManifest(cwd);
2451
2867
  if (!manifest) {
2452
2868
  console.warn(
2453
- pc9.yellow(`\u26A0\uFE0F No agents-manifest.json found in ${cwd}. Running in unmanaged repository mode.`)
2869
+ pc10.yellow(`\u26A0\uFE0F No agents-manifest.json found in ${cwd}. Running in unmanaged repository mode.`)
2454
2870
  );
2455
2871
  } else if (manifest.routines && manifest.routines[routine] === false) {
2456
2872
  console.warn(
2457
- pc9.yellow(`\u26A0\uFE0F Routine '${routine}' is disabled in agents-manifest.json. Running anyway via explicit command.`)
2873
+ pc10.yellow(`\u26A0\uFE0F Routine '${routine}' is disabled in agents-manifest.json. Running anyway via explicit command.`)
2458
2874
  );
2459
2875
  }
2460
- console.log(pc9.cyan(`
2461
- \u{1F680} Launching local agent session for routine: ${pc9.bold(routine)}`));
2876
+ console.log(pc10.cyan(`
2877
+ \u{1F680} Launching local agent session for routine: ${pc10.bold(routine)}`));
2462
2878
  if (options.issue) {
2463
- console.log(pc9.dim(` Target issue: #${options.issue}`));
2879
+ console.log(pc10.dim(` Target issue: #${options.issue}`));
2464
2880
  }
2465
2881
  if (options.pr) {
2466
- console.log(pc9.dim(` Target pull request: #${options.pr}`));
2882
+ console.log(pc10.dim(` Target pull request: #${options.pr}`));
2467
2883
  }
2468
2884
  if (options.model) {
2469
- console.log(pc9.dim(` Model override: ${options.model}`));
2885
+ console.log(pc10.dim(` Model override: ${options.model}`));
2886
+ }
2887
+ if (options.verbose) {
2888
+ console.log(pc10.dim(` Verbose output: Enabled (streaming raw tokens)`));
2470
2889
  }
2471
2890
  if (options.worktree !== false) {
2472
- console.log(pc9.dim(` Workspace isolation: Git Worktree (.jonah-fleet/worktrees/)`));
2891
+ console.log(pc10.dim(` Workspace isolation: Git Worktree (.jonah-fleet/worktrees/)`));
2473
2892
  } else {
2474
- console.log(pc9.yellow(` Workspace isolation: Disabled (running in current directory)`));
2893
+ console.log(pc10.yellow(` Workspace isolation: Disabled (running in current directory)`));
2475
2894
  }
2476
2895
  console.log("");
2477
2896
  try {
@@ -2484,59 +2903,60 @@ async function runRoutineCommand(routine, options = {}) {
2484
2903
  printTimeout: options.timeout,
2485
2904
  noWorktree: options.worktree === false,
2486
2905
  keepWorktree: options.keepWorktree,
2487
- dryRun: options.dryRun
2906
+ dryRun: options.dryRun,
2907
+ verbose: options.verbose
2488
2908
  });
2489
2909
  if (options.dryRun) {
2490
- console.log(pc9.green(result.output));
2910
+ console.log(pc10.green(result.output));
2491
2911
  return;
2492
2912
  }
2493
2913
  if (result.success) {
2494
- console.log(pc9.green(`
2914
+ console.log(pc10.green(`
2495
2915
  \u2713 Local agent session for '${routine}' completed successfully.`));
2496
2916
  } else {
2497
- console.error(pc9.red(`
2917
+ console.error(pc10.red(`
2498
2918
  \u2717 Local agent session for '${routine}' failed with exit code ${result.exitCode}.`));
2499
2919
  process.exit(result.exitCode);
2500
2920
  }
2501
2921
  } catch (error) {
2502
- console.error(pc9.red(`
2922
+ console.error(pc10.red(`
2503
2923
  \u2717 Failed to execute routine '${routine}': ${error.message}`));
2504
2924
  process.exit(1);
2505
2925
  }
2506
2926
  }
2507
2927
 
2508
2928
  // src/commands/daemon.ts
2509
- import pc11 from "picocolors";
2929
+ import pc12 from "picocolors";
2510
2930
 
2511
2931
  // src/lib/daemon.ts
2512
- import fs12 from "fs";
2513
- import path12 from "path";
2932
+ import fs13 from "fs";
2933
+ import path13 from "path";
2514
2934
  import { spawn as spawn2, execFile as execFile3 } from "child_process";
2515
2935
  import { promisify as promisify3 } from "util";
2516
- import pc10 from "picocolors";
2936
+ import pc11 from "picocolors";
2517
2937
  var execFileAsync3 = promisify3(execFile3);
2518
2938
  function getDaemonStatePath(repoRoot) {
2519
- return path12.join(repoRoot, ".jonah-fleet", "daemon.json");
2939
+ return path13.join(repoRoot, ".jonah-fleet", "daemon.json");
2520
2940
  }
2521
2941
  function readDaemonState(repoRoot) {
2522
2942
  const statePath = getDaemonStatePath(repoRoot);
2523
- if (!fs12.existsSync(statePath)) return null;
2943
+ if (!fs13.existsSync(statePath)) return null;
2524
2944
  try {
2525
- return JSON.parse(fs12.readFileSync(statePath, "utf8"));
2945
+ return JSON.parse(fs13.readFileSync(statePath, "utf8"));
2526
2946
  } catch {
2527
2947
  return null;
2528
2948
  }
2529
2949
  }
2530
2950
  function writeDaemonState(repoRoot, state) {
2531
2951
  const statePath = getDaemonStatePath(repoRoot);
2532
- fs12.mkdirSync(path12.dirname(statePath), { recursive: true });
2533
- fs12.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf8");
2952
+ fs13.mkdirSync(path13.dirname(statePath), { recursive: true });
2953
+ fs13.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf8");
2534
2954
  }
2535
2955
  function clearDaemonState(repoRoot) {
2536
2956
  const statePath = getDaemonStatePath(repoRoot);
2537
- if (fs12.existsSync(statePath)) {
2957
+ if (fs13.existsSync(statePath)) {
2538
2958
  try {
2539
- fs12.unlinkSync(statePath);
2959
+ fs13.unlinkSync(statePath);
2540
2960
  } catch {
2541
2961
  }
2542
2962
  }
@@ -2572,9 +2992,9 @@ async function startBackgroundDaemon(repoRoot, options = {}) {
2572
2992
  const reviewInterval = options.reviewInterval || 3;
2573
2993
  const autoworkInterval = options.autoworkInterval || options.interval || 30;
2574
2994
  const routines = options.routines || ["peer-review", "autowork"];
2575
- const logFilePath = path12.join(repoRoot, ".jonah-fleet", "daemon.log");
2576
- fs12.mkdirSync(path12.dirname(logFilePath), { recursive: true });
2577
- const logFd = fs12.openSync(logFilePath, "a");
2995
+ const logFilePath = path13.join(repoRoot, ".jonah-fleet", "daemon.log");
2996
+ fs13.mkdirSync(path13.dirname(logFilePath), { recursive: true });
2997
+ const logFd = fs13.openSync(logFilePath, "a");
2578
2998
  const cliPath = process.argv[1];
2579
2999
  const args = [
2580
3000
  "daemon",
@@ -2589,6 +3009,9 @@ async function startBackgroundDaemon(repoRoot, options = {}) {
2589
3009
  if (options.model) {
2590
3010
  args.push("--model", options.model);
2591
3011
  }
3012
+ if (options.verbose) {
3013
+ args.push("--verbose");
3014
+ }
2592
3015
  const child = spawn2(process.execPath, [cliPath, ...args], {
2593
3016
  cwd: repoRoot,
2594
3017
  detached: true,
@@ -2633,19 +3056,48 @@ async function runDaemonLoop(repoRoot, options = {}) {
2633
3056
  status: "idle"
2634
3057
  };
2635
3058
  writeDaemonState(repoRoot, state);
2636
- console.log(pc10.cyan(`
3059
+ console.log(pc11.cyan(`
2637
3060
  \u{1F916} Jonah Fleet Multi-Cadence Local Agent Daemon Started`));
2638
- console.log(pc10.dim(` PID: ${process.pid}`));
2639
- console.log(pc10.dim(` Peer Review Watchdog: Every ${reviewInterval} minutes (with zero-cost PR preflight)`));
2640
- console.log(pc10.dim(` Autowork Backlog Scan: Every ${autoworkInterval} minutes`));
2641
- console.log(pc10.dim(` Working Directory: ${repoRoot}
3061
+ console.log(pc11.dim(` PID: ${process.pid}`));
3062
+ console.log(pc11.dim(` Peer Review Watchdog: Every ${reviewInterval} minutes (with zero-cost PR preflight)`));
3063
+ console.log(pc11.dim(` Autowork Backlog Scan: Every ${autoworkInterval} minutes`));
3064
+ console.log(pc11.dim(` Working Directory: ${repoRoot}
2642
3065
  `));
2643
3066
  let isStopping = false;
2644
3067
  let isWorking = false;
3068
+ const reviewIntervalMs = reviewInterval * 60 * 1e3;
3069
+ const autoworkIntervalMs = autoworkInterval * 60 * 1e3;
3070
+ let nextReviewCheckTime = Date.now() + (routines.includes("peer-review") ? reviewIntervalMs : Infinity);
3071
+ let nextAutoworkCheckTime = Date.now() + (routines.includes("autowork") ? autoworkIntervalMs : Infinity);
3072
+ let lastOpenPRCount = void 0;
3073
+ const clearTicker = () => {
3074
+ if (process.stderr.isTTY && !options.verbose) {
3075
+ process.stderr.write("\r\x1B[K");
3076
+ }
3077
+ };
3078
+ const updateTicker = () => {
3079
+ if (isStopping || isWorking || options.verbose || !process.stderr.isTTY) return;
3080
+ const now = Date.now();
3081
+ const nextCheck = Math.min(nextReviewCheckTime, nextAutoworkCheckTime);
3082
+ const diffMs = Math.max(0, nextCheck - now);
3083
+ const remainingSecs = Math.ceil(diffMs / 1e3);
3084
+ const mins = Math.floor(remainingSecs / 60);
3085
+ const secs = remainingSecs % 60;
3086
+ const timeStr = `${mins}m ${secs < 10 ? "0" : ""}${secs}s`;
3087
+ const prStr = lastOpenPRCount !== void 0 ? ` (${lastOpenPRCount} ready PRs)` : "";
3088
+ process.stderr.write(
3089
+ `\r\x1B[K${pc11.dim("[" + (/* @__PURE__ */ new Date()).toLocaleTimeString() + "]")} \u{1F4A4} ${pc11.dim("Watchdog Idle \xB7 Next check in " + timeStr + prStr)}`
3090
+ );
3091
+ };
3092
+ const tickerInterval = setInterval(updateTicker, 1e3);
2645
3093
  const handleStop = async () => {
2646
3094
  if (isStopping) return;
2647
3095
  isStopping = true;
2648
- console.log(pc10.yellow(`
3096
+ clearInterval(tickerInterval);
3097
+ clearInterval(reviewTimer);
3098
+ clearInterval(autoworkTimer);
3099
+ clearTicker();
3100
+ console.log(pc11.yellow(`
2649
3101
  Stopping local agent daemon...`));
2650
3102
  clearDaemonState(repoRoot);
2651
3103
  await cleanupStaleWorktrees(repoRoot);
@@ -2658,36 +3110,52 @@ Stopping local agent daemon...`));
2658
3110
  state.lastReviewCheckAt = (/* @__PURE__ */ new Date()).toISOString();
2659
3111
  writeDaemonState(repoRoot, state);
2660
3112
  const openPRCount = await countOpenReadyPRs(repoRoot);
3113
+ lastOpenPRCount = openPRCount;
2661
3114
  if (openPRCount === 0) {
2662
- console.log(pc10.dim(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Peer Review Watchdog: 0 ready PRs found (0 tokens used).`));
3115
+ if (options.verbose) {
3116
+ console.log(pc11.dim(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Peer Review Watchdog: 0 ready PRs found (0 tokens used).`));
3117
+ }
3118
+ nextReviewCheckTime = Date.now() + reviewIntervalMs;
3119
+ updateTicker();
2663
3120
  return;
2664
3121
  }
2665
3122
  try {
2666
3123
  isWorking = true;
3124
+ clearTicker();
2667
3125
  state.status = "working";
2668
3126
  state.activeRoutine = "peer-review";
2669
3127
  writeDaemonState(repoRoot, state);
2670
- console.log(pc10.cyan(`
3128
+ console.log(pc11.cyan(`
2671
3129
  [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] \u{1F50D} Peer Review Watchdog: Found ${openPRCount} ready PR(s). Starting review session...`));
2672
3130
  await cleanupStaleWorktrees(repoRoot);
2673
3131
  const result = await runLocalRoutine({
2674
3132
  targetDir: repoRoot,
2675
3133
  routine: "peer-review",
2676
3134
  model: options.model,
2677
- noWorktree: false
3135
+ verbose: options.verbose,
3136
+ noWorktree: false,
3137
+ onTargetDetected: (target) => {
3138
+ state.activeTarget = target;
3139
+ writeDaemonState(repoRoot, state);
3140
+ }
2678
3141
  });
2679
3142
  if (result.success) {
2680
- console.log(pc10.green(`\u2713 Local peer-review completed successfully.`));
3143
+ console.log(pc11.green(`\u2713 Local peer-review completed successfully.
3144
+ `));
2681
3145
  } else {
2682
- console.warn(pc10.yellow(`\u26A0\uFE0F Local peer-review completed with code ${result.exitCode}.`));
3146
+ console.warn(pc11.yellow(`\u26A0\uFE0F Local peer-review completed with code ${result.exitCode}.
3147
+ `));
2683
3148
  }
2684
3149
  } catch (err) {
2685
- console.error(pc10.red(`\u2717 Error in peer-review: ${err.message}`));
3150
+ console.error(pc11.red(`\u2717 Error in peer-review: ${err.message}`));
2686
3151
  } finally {
2687
3152
  isWorking = false;
2688
3153
  state.status = "idle";
2689
3154
  state.activeRoutine = void 0;
3155
+ state.activeTarget = void 0;
2690
3156
  writeDaemonState(repoRoot, state);
3157
+ nextReviewCheckTime = Date.now() + reviewIntervalMs;
3158
+ updateTicker();
2691
3159
  }
2692
3160
  };
2693
3161
  const runAutoworkCheck = async () => {
@@ -2696,30 +3164,41 @@ Stopping local agent daemon...`));
2696
3164
  writeDaemonState(repoRoot, state);
2697
3165
  try {
2698
3166
  isWorking = true;
3167
+ clearTicker();
2699
3168
  state.status = "working";
2700
3169
  state.activeRoutine = "autowork";
2701
3170
  writeDaemonState(repoRoot, state);
2702
- console.log(pc10.cyan(`
3171
+ console.log(pc11.cyan(`
2703
3172
  [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] \u{1F680} Autowork Backlog Scan: Starting session...`));
2704
3173
  await cleanupStaleWorktrees(repoRoot);
2705
3174
  const result = await runLocalRoutine({
2706
3175
  targetDir: repoRoot,
2707
3176
  routine: "autowork",
2708
3177
  model: options.model,
2709
- noWorktree: false
3178
+ verbose: options.verbose,
3179
+ noWorktree: false,
3180
+ onTargetDetected: (target) => {
3181
+ state.activeTarget = target;
3182
+ writeDaemonState(repoRoot, state);
3183
+ }
2710
3184
  });
2711
3185
  if (result.success) {
2712
- console.log(pc10.green(`\u2713 Local autowork completed successfully.`));
3186
+ console.log(pc11.green(`\u2713 Local autowork completed successfully.
3187
+ `));
2713
3188
  } else {
2714
- console.warn(pc10.yellow(`\u26A0\uFE0F Local autowork completed with code ${result.exitCode}.`));
3189
+ console.warn(pc11.yellow(`\u26A0\uFE0F Local autowork completed with code ${result.exitCode}.
3190
+ `));
2715
3191
  }
2716
3192
  } catch (err) {
2717
- console.error(pc10.red(`\u2717 Error in autowork: ${err.message}`));
3193
+ console.error(pc11.red(`\u2717 Error in autowork: ${err.message}`));
2718
3194
  } finally {
2719
3195
  isWorking = false;
2720
3196
  state.status = "idle";
2721
3197
  state.activeRoutine = void 0;
3198
+ state.activeTarget = void 0;
2722
3199
  writeDaemonState(repoRoot, state);
3200
+ nextAutoworkCheckTime = Date.now() + autoworkIntervalMs;
3201
+ updateTicker();
2723
3202
  }
2724
3203
  };
2725
3204
  if (routines.includes("peer-review")) {
@@ -2728,8 +3207,6 @@ Stopping local agent daemon...`));
2728
3207
  if (routines.includes("autowork")) {
2729
3208
  await runAutoworkCheck();
2730
3209
  }
2731
- const reviewIntervalMs = reviewInterval * 60 * 1e3;
2732
- const autoworkIntervalMs = autoworkInterval * 60 * 1e3;
2733
3210
  const reviewTimer = setInterval(runReviewCheck, reviewIntervalMs);
2734
3211
  const autoworkTimer = setInterval(runAutoworkCheck, autoworkIntervalMs);
2735
3212
  await new Promise(() => {
@@ -2746,7 +3223,8 @@ async function runDaemonCommand(action, options = {}) {
2746
3223
  autoworkInterval: options.autoworkInterval ? parseInt(options.autoworkInterval, 10) : options.interval ? parseInt(options.interval, 10) : void 0,
2747
3224
  routines: options.routines ? options.routines.split(",").map((r) => r.trim()) : void 0,
2748
3225
  model: options.model,
2749
- foreground: options.foreground
3226
+ foreground: options.foreground,
3227
+ verbose: options.verbose
2750
3228
  };
2751
3229
  if (act === "start") {
2752
3230
  if (options.foreground) {
@@ -2755,16 +3233,16 @@ async function runDaemonCommand(action, options = {}) {
2755
3233
  }
2756
3234
  try {
2757
3235
  const state2 = await startBackgroundDaemon(cwd, daemonOpts);
2758
- console.log(pc11.green(`
3236
+ console.log(pc12.green(`
2759
3237
  \u2713 Background agent daemon started successfully.`));
2760
- console.log(pc11.dim(` PID: ${state2.pid}`));
2761
- console.log(pc11.dim(` Peer Review Watchdog: Every ${state2.reviewIntervalMinutes} minutes (zero-cost PR preflight)`));
2762
- console.log(pc11.dim(` Autowork Backlog Scan: Every ${state2.autoworkIntervalMinutes} minutes`));
2763
- console.log(pc11.dim(` Routines: ${state2.routines.join(", ")}`));
2764
- console.log(pc11.dim(` Log file: .jonah-fleet/daemon.log`));
2765
- console.log(pc11.dim(` Run 'jonah-fleet daemon status' or 'jonah-fleet daemon stop' to manage.`));
3238
+ console.log(pc12.dim(` PID: ${state2.pid}`));
3239
+ console.log(pc12.dim(` Peer Review Watchdog: Every ${state2.reviewIntervalMinutes} minutes (zero-cost PR preflight)`));
3240
+ console.log(pc12.dim(` Autowork Backlog Scan: Every ${state2.autoworkIntervalMinutes} minutes`));
3241
+ console.log(pc12.dim(` Routines: ${state2.routines.join(", ")}`));
3242
+ console.log(pc12.dim(` Log file: .jonah-fleet/daemon.log`));
3243
+ console.log(pc12.dim(` Run 'jonah-fleet daemon status' or 'jonah-fleet daemon stop' to manage.`));
2766
3244
  } catch (err) {
2767
- console.error(pc11.red(`
3245
+ console.error(pc12.red(`
2768
3246
  \u2717 Failed to start daemon: ${err.message}`));
2769
3247
  process.exit(1);
2770
3248
  }
@@ -2772,18 +3250,18 @@ async function runDaemonCommand(action, options = {}) {
2772
3250
  }
2773
3251
  if (act === "stop") {
2774
3252
  if (!isDaemonRunning(cwd)) {
2775
- console.log(pc11.yellow(`
3253
+ console.log(pc12.yellow(`
2776
3254
  \u26A0\uFE0F No local agent daemon is currently running in this repository.`));
2777
3255
  return;
2778
3256
  }
2779
3257
  const state2 = readDaemonState(cwd);
2780
- console.log(pc11.cyan(`
3258
+ console.log(pc12.cyan(`
2781
3259
  Stopping background agent daemon (PID ${state2?.pid})...`));
2782
3260
  const stopped = await stopDaemon(cwd);
2783
3261
  if (stopped) {
2784
- console.log(pc11.green(`\u2713 Local agent daemon stopped successfully.`));
3262
+ console.log(pc12.green(`\u2713 Local agent daemon stopped successfully.`));
2785
3263
  } else {
2786
- console.error(pc11.red(`\u2717 Could not terminate daemon process.`));
3264
+ console.error(pc12.red(`\u2717 Could not terminate daemon process.`));
2787
3265
  process.exit(1);
2788
3266
  }
2789
3267
  return;
@@ -2795,17 +3273,18 @@ Stopping background agent daemon (PID ${state2?.pid})...`));
2795
3273
  const running = isDaemonRunning(cwd);
2796
3274
  const state = readDaemonState(cwd);
2797
3275
  const activeWorktrees = await listActiveWorktrees(cwd);
2798
- console.log(pc11.cyan(`
3276
+ console.log(pc12.cyan(`
2799
3277
  \u{1F916} Jonah Fleet Local Daemon Status
2800
3278
  `));
2801
3279
  if (running && state) {
2802
- console.log(` Status: ${pc11.green(pc11.bold("RUNNING"))}`);
3280
+ console.log(` Status: ${pc12.green(pc12.bold("RUNNING"))}`);
2803
3281
  console.log(` PID: ${state.pid}`);
2804
3282
  console.log(` Started: ${new Date(state.startedAt).toLocaleString()}`);
2805
3283
  console.log(` Peer Review Cadence: Every ${state.reviewIntervalMinutes} minutes (0-token fast preflight)`);
2806
3284
  console.log(` Autowork Cadence: Every ${state.autoworkIntervalMinutes} minutes`);
2807
3285
  console.log(` Routines: ${state.routines.join(", ")}`);
2808
- console.log(` Current State: ${state.status === "working" ? pc11.yellow("WORKING on " + state.activeRoutine) : pc11.green("IDLE")}`);
3286
+ const workingDesc = state.activeRoutine + (state.activeTarget ? ` (${pc12.bold(state.activeTarget)})` : "");
3287
+ console.log(` Current State: ${state.status === "working" ? pc12.yellow("WORKING on " + workingDesc) : pc12.green("IDLE")}`);
2809
3288
  if (state.lastReviewCheckAt) {
2810
3289
  console.log(` Last Review Check: ${new Date(state.lastReviewCheckAt).toLocaleTimeString()}`);
2811
3290
  }
@@ -2813,13 +3292,13 @@ Stopping background agent daemon (PID ${state2?.pid})...`));
2813
3292
  console.log(` Last Autowork Check: ${new Date(state.lastAutoworkCheckAt).toLocaleTimeString()}`);
2814
3293
  }
2815
3294
  } else {
2816
- console.log(` Status: ${pc11.gray("STOPPED")}`);
2817
- console.log(pc11.dim(` Run 'jonah-fleet daemon start' to start the local worker daemon.`));
3295
+ console.log(` Status: ${pc12.gray("STOPPED")}`);
3296
+ console.log(pc12.dim(` Run 'jonah-fleet daemon start' to start the local worker daemon.`));
2818
3297
  }
2819
3298
  console.log(`
2820
3299
  Active Worktrees: ${activeWorktrees.length}`);
2821
3300
  for (const wt of activeWorktrees) {
2822
- console.log(pc11.dim(` - [${wt.branch}] ${wt.path}`));
3301
+ console.log(pc12.dim(` - [${wt.branch}] ${wt.path}`));
2823
3302
  }
2824
3303
  console.log("");
2825
3304
  }
@@ -2827,10 +3306,10 @@ Stopping background agent daemon (PID ${state2?.pid})...`));
2827
3306
  // src/index.ts
2828
3307
  var program = new Command();
2829
3308
  program.name("jonah-fleet").description("Manage autonomous agent fleet, prompt routines, workflows, and skills").version(FLEET_VERSION);
2830
- program.command("run <routine>").description("Run a specific prompt routine locally in an isolated git worktree").option("-i, --issue <number>", "Targeted issue number for autowork").option("-p, --pr <number>", "Targeted pull request number for peer-review").option("-m, --model <model>", "LLM model override (defaults to gemini-3.7-flash-high)").option("--timeout <duration>", "CLI execution print timeout (default: 30m)").option("--no-worktree", "Execute directly in current directory without creating a git worktree").option("--keep-worktree", "Preserve the git worktree after routine execution completes").option("-d, --dry-run", "Preview prompt and execution parameters without launching agent").action(async (routine, options) => {
3309
+ program.command("run <routine>").description("Run a specific prompt routine locally in an isolated git worktree").option("-i, --issue <number>", "Targeted issue number for autowork").option("-p, --pr <number>", "Targeted pull request number for peer-review").option("-m, --model <model>", "LLM model override (defaults to gemini-3.7-flash-high)").option("--timeout <duration>", "CLI execution print timeout (default: 30m)").option("--no-worktree", "Execute directly in current directory without creating a git worktree").option("--keep-worktree", "Preserve the git worktree after routine execution completes").option("-d, --dry-run", "Preview prompt and execution parameters without launching agent").option("-v, --verbose", "Stream raw agent tokens and logs directly to stdout").action(async (routine, options) => {
2831
3310
  await runRoutineCommand(routine, options);
2832
3311
  });
2833
- program.command("daemon [action]").description("Manage background local worker daemon polling for unclaimed issues and pull requests").option("-i, --interval <minutes>", "Legacy global polling interval in minutes (default: 30)").option("--review-interval <minutes>", "Peer Review watchdog cadence in minutes (default: 3)").option("--autowork-interval <minutes>", "Autowork backlog cadence in minutes (default: 30)").option("-r, --routines <list>", "Comma-separated routines to run (default: peer-review,autowork)").option("-m, --model <model>", "LLM model override").option("--foreground", "Run daemon in foreground with live console logs").action(async (action, options) => {
3312
+ program.command("daemon [action]").description("Manage background local worker daemon polling for unclaimed issues and pull requests").option("-i, --interval <minutes>", "Legacy global polling interval in minutes (default: 30)").option("--review-interval <minutes>", "Peer Review watchdog cadence in minutes (default: 3)").option("--autowork-interval <minutes>", "Autowork backlog cadence in minutes (default: 30)").option("-r, --routines <list>", "Comma-separated routines to run (default: peer-review,autowork)").option("-m, --model <model>", "LLM model override").option("--foreground", "Run daemon in foreground with live console logs").option("-v, --verbose", "Stream raw agent tokens and logs directly to stdout").action(async (action, options) => {
2834
3313
  await runDaemonCommand(action, options);
2835
3314
  });
2836
3315
  program.command("init").description("Initialize Jonah Fleet configuration, routines, workflows, and skills in the current repo").option("-p, --preset <preset>", "Preset profile to install (minimal | standard | full)", "standard").option("-f, --force", "Force overwrite existing files", false).option("--stack <stack>", "Override detected tech stack name").option("--package-manager <pm>", "Override package manager (npm, pnpm, yarn, bun, uv, poetry, cargo, go)").option("--test-cmd <cmd>", "Override test execution command").option("--build-cmd <cmd>", "Override build execution command").option("--interactive", "Force interactive prompts for stack configuration").option("--no-interactive", "Disable interactive prompts").action(async (options) => {
@@ -8,6 +8,7 @@ export interface DaemonState {
8
8
  lastAutoworkCheckAt?: string;
9
9
  status: 'idle' | 'working' | 'stopped';
10
10
  activeRoutine?: string;
11
+ activeTarget?: string;
11
12
  activeWorktree?: string;
12
13
  }
13
14
  export interface DaemonOptions {
@@ -17,6 +18,7 @@ export interface DaemonOptions {
17
18
  routines?: string[];
18
19
  model?: string;
19
20
  foreground?: boolean;
21
+ verbose?: boolean;
20
22
  }
21
23
  export declare function getDaemonStatePath(repoRoot: string): string;
22
24
  export declare function readDaemonState(repoRoot: string): DaemonState | null;
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/lib/daemon.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,uBAAuB,EAAE,MAAM,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAQpE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAI3E;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CASvD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAYzD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWzE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAmD/G;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAanE;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmIhG"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/lib/daemon.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,uBAAuB,EAAE,MAAM,CAAC;IAChC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAQpE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAI3E;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CASvD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAYzD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWzE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAsD/G;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAanE;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2LhG"}
@@ -47,6 +47,6 @@ export declare const PRESET_CONFIGS: Record<Exclude<PresetName, 'custom'>, {
47
47
  }>;
48
48
  export declare const ROUTINE_TO_WORKFLOW_MAP: Record<keyof FleetManifest['routines'], string[]>;
49
49
  export declare const WORKFLOW_TO_ROUTINE_MAP: Record<string, keyof FleetManifest['routines'] | 'sync-fleet'>;
50
- export declare const FLEET_VERSION = "1.4.2";
50
+ export declare const FLEET_VERSION = "1.5.0";
51
51
  export declare const SCHEMA_URL = "https://raw.githubusercontent.com/juliendurandeu/jonah-fleet/main/schema.json";
52
52
  //# sourceMappingURL=presets.d.ts.map
@@ -8,8 +8,11 @@ export interface RunLocalRoutineOptions {
8
8
  noWorktree?: boolean;
9
9
  keepWorktree?: boolean;
10
10
  dryRun?: boolean;
11
+ verbose?: boolean;
12
+ showCard?: boolean;
11
13
  env?: Record<string, string>;
12
14
  onLog?: (chunk: string) => void;
15
+ onTargetDetected?: (target: string) => void;
13
16
  }
14
17
  export interface RunLocalRoutineResult {
15
18
  success: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAoB9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAO,GAC9D,MAAM,CAmBR;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAOxC;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAwIrG"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAoB9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAO,GAC9D,MAAM,CAmBR;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAOxC;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAgOrG"}
@@ -0,0 +1,85 @@
1
+ export interface SummaryCardOptions {
2
+ routine: string;
3
+ output?: string;
4
+ repoRoot?: string;
5
+ issue?: string | number;
6
+ pr?: string | number;
7
+ durationMs?: number;
8
+ }
9
+ export interface ErrorCardOptions {
10
+ routine: string;
11
+ exitCode: number;
12
+ repoRoot: string;
13
+ issue?: string | number;
14
+ pr?: string | number;
15
+ durationMs?: number;
16
+ error?: Error;
17
+ }
18
+ export interface ParsedRunSummary {
19
+ routine?: string;
20
+ target?: string;
21
+ decision?: string;
22
+ result?: string;
23
+ duration?: string;
24
+ passes?: Array<{
25
+ name: string;
26
+ status: 'pass' | 'fail' | 'info';
27
+ detail?: string;
28
+ }>;
29
+ actions?: string[];
30
+ logPath?: string;
31
+ }
32
+ /**
33
+ * Strips temporary worktree roots and converts absolute worktree paths to clean relative paths.
34
+ */
35
+ export declare function sanitizeWorktreePaths(text: string): string;
36
+ /**
37
+ * Extracts the '# ... Execution Summary' section from routine output text.
38
+ */
39
+ export declare function extractExecutionSummary(output: string): string | null;
40
+ /**
41
+ * Finds the latest committed run log in .github/prompts/logs/<routine>/*.md.
42
+ */
43
+ export declare function findLatestRunLog(repoRoot: string, routine: string): string | null;
44
+ /**
45
+ * Parses markdown run log files into structured data.
46
+ */
47
+ export declare function parseRunLog(logContent: string): ParsedRunSummary;
48
+ /**
49
+ * Heuristics to detect active stage from streaming agent tokens.
50
+ */
51
+ export declare function detectActivePhase(chunk: string, currentPhase?: string): string;
52
+ /**
53
+ * Detects if the agent has selected or claimed a specific issue in Scan mode.
54
+ */
55
+ export declare function detectClaimedIssue(chunk: string): string | null;
56
+ /**
57
+ * Detects if the agent has selected a specific pull request in Scan mode.
58
+ */
59
+ export declare function detectClaimedPR(chunk: string): string | null;
60
+ /**
61
+ * Renders a styled Unicode summary card.
62
+ */
63
+ export declare function renderSummaryCard(options: SummaryCardOptions): string;
64
+ /**
65
+ * Renders an Error Card with tail of daemon.log.
66
+ */
67
+ export declare function renderErrorCard(options: ErrorCardOptions): string;
68
+ /**
69
+ * Minimalist zero-dependency terminal spinner.
70
+ */
71
+ export declare class TerminalSpinner {
72
+ private frames;
73
+ private currentFrame;
74
+ private intervalId;
75
+ private startTime;
76
+ private message;
77
+ private isRunning;
78
+ private isTTY;
79
+ constructor();
80
+ start(initialMessage: string): void;
81
+ update(newMessage: string): void;
82
+ private render;
83
+ stop(): void;
84
+ }
85
+ //# sourceMappingURL=terminal-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-card.d.ts","sourceRoot":"","sources":["../../src/lib/terminal-card.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAc1D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcjF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CA8EhE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,GAAE,MAA4B,GAAG,MAAM,CAuBnG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkB/D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAsJrE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAqDjE;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAsD;IACpE,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,KAAK,CAAU;IAEvB,cAEC;IAEM,KAAK,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAazC;IAEM,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAKtC;IAED,OAAO,CAAC,MAAM;IAeP,IAAI,IAAI,IAAI,CAYlB;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jonah-fleet",
3
- "version": "1.4.2",
3
+ "version": "1.5.0",
4
4
  "description": "Standalone autonomous agent fleet with Symphony orchestration, claim protocols, and continuous improvement loops",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",