jonah-fleet 1.4.0 → 1.4.2
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 +26 -0
- package/dist/commands/daemon.d.ts +2 -0
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/index.js +137 -61
- package/dist/lib/daemon.d.ts +11 -3
- package/dist/lib/daemon.d.ts.map +1 -1
- package/dist/lib/presets.d.ts +1 -1
- package/dist/lib/presets.d.ts.map +1 -1
- package/dist/lib/runner.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/docs/AGENTS.template.md +6 -0
- package/templates/prompts/ORCHESTRATION.md +3 -0
- package/templates/prompts/autowork.md +10 -1
- package/templates/skills/grill-me/SKILL.md +64 -0
- package/templates/skills/grill-me/agents/openai.yaml +5 -0
- package/templates/skills/triage/SKILL.md +1 -1
- package/templates/workflows/trigger-review-routine.yml +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,9 +5,35 @@ 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.4.2] - 2026-09-03
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Strict Allowlist for Cloud Review Triggers:
|
|
12
|
+
- Updated `trigger-review-routine.yml` to strictly require `priority/P0` or `priority/P1` labels to fire immediate cloud reviews on `pull_request` events.
|
|
13
|
+
- 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).
|
|
14
|
+
|
|
15
|
+
## [1.4.1] - 2026-09-03
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Decoupled Multi-Cadence Daemon & Zero-Cost PR Preflight:
|
|
19
|
+
- Added independent scheduling in `jonah-fleet daemon` via `--review-interval` (default: 3m) and `--autowork-interval` (default: 30m).
|
|
20
|
+
- Added ultra-fast (~100ms) local PR preflight check (`countOpenReadyPRs`) in `src/lib/daemon.ts` that queries `gh pr list` and skips agent invocations with 0 token spend when 0 ready PRs are open.
|
|
21
|
+
- User-Friendly Terminal Cards & Daemon Ticker:
|
|
22
|
+
- Replaced noisy raw markdown streaming in `jonah-fleet daemon --foreground` and `jonah-fleet run` with a dynamic single-line status spinner showing target and active phase heuristics.
|
|
23
|
+
- Added real-time token stream redirection to `.jonah-fleet/daemon.log` with `-v, --verbose` escape hatch for debugging.
|
|
24
|
+
- Added formatted Unicode summary and error cards with ephemeral worktree path sanitization and disk log fallbacks (`src/lib/terminal-card.ts`).
|
|
25
|
+
- Added in-place countdown ticker (`\r`) during daemon watchdog idle states to avoid cluttering terminal scrollback.
|
|
26
|
+
|
|
8
27
|
## [1.4.0] - 2026-09-03
|
|
9
28
|
|
|
10
29
|
### Added
|
|
30
|
+
- Inquisitive Agent Fleet & Grilling Protocol:
|
|
31
|
+
- Added new `grill-me` engineering skill (`templates/skills/grill-me/SKILL.md` and `.agents/skills/grill-me/SKILL.md`) to stress-test proposals, requirements, and designs through a targeted, sequential interview to eliminate assumptions before implementation (#54, #55).
|
|
32
|
+
- Included `grill-me` in `standard` and `full` presets in `src/lib/presets.ts` and `agents-manifest.json` (#54, #55).
|
|
33
|
+
- Added Requirements Discovery & Inquisitive Stance rules to `templates/docs/AGENTS.template.md` and `AGENTS.md`, establishing default skepticism on necessity, zero-guesswork ambiguity gates, and high-leverage question structuring (#54, #55).
|
|
34
|
+
- Added Ambiguity & Missing Acceptance Criteria Gate to Step 12 in `autowork.md` (`templates/prompts/autowork.md` & `.github/prompts/autowork.md`) to post clarifying questions, label `needs-info`, and release the claim instead of making silent assumptions (#54, #55).
|
|
35
|
+
- Added Selective Server-Side Issue Querying in Step 8 of `autowork.md` using `gh --search` with exclusion filters (`no:assignee -label:measurement -label:needs-human -label:needs-design -label:wontfix -label:needs-info`) to avoid tool buffer truncation and token waste (#54, #55).
|
|
36
|
+
- Updated `triage.md` and `ORCHESTRATION.md` to integrate `/grill-me` into issue evaluation and planning (#54, #55).
|
|
11
37
|
- Priority-Driven Dual Agent Execution (Local + GitHub Actions):
|
|
12
38
|
- Added CLI `jonah-fleet run <routine>` command to execute prompt routines locally with automatic Git worktree isolation (`.jonah-fleet/worktrees/`), protecting active working copies and uncommitted editor files.
|
|
13
39
|
- Added CLI `jonah-fleet daemon [start|stop|status]` background worker to continuously poll open issues and PRs for local execution.
|
|
@@ -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,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,
|
|
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"}
|
package/dist/index.js
CHANGED
|
@@ -49,7 +49,8 @@ var PRESET_CONFIGS = {
|
|
|
49
49
|
"diagnosing-bugs",
|
|
50
50
|
"resolving-merge-conflicts",
|
|
51
51
|
"writing-for-agents",
|
|
52
|
-
"triage"
|
|
52
|
+
"triage",
|
|
53
|
+
"grill-me"
|
|
53
54
|
]
|
|
54
55
|
},
|
|
55
56
|
full: {
|
|
@@ -71,6 +72,7 @@ var PRESET_CONFIGS = {
|
|
|
71
72
|
"resolving-merge-conflicts",
|
|
72
73
|
"writing-for-agents",
|
|
73
74
|
"triage",
|
|
75
|
+
"grill-me",
|
|
74
76
|
"to-spec",
|
|
75
77
|
"to-tickets"
|
|
76
78
|
]
|
|
@@ -90,7 +92,7 @@ var ROUTINE_TO_WORKFLOW_MAP = {
|
|
|
90
92
|
"product-planning": [],
|
|
91
93
|
"analytics-review": []
|
|
92
94
|
};
|
|
93
|
-
var FLEET_VERSION = "1.4.
|
|
95
|
+
var FLEET_VERSION = "1.4.2";
|
|
94
96
|
var SCHEMA_URL = "https://raw.githubusercontent.com/juliendurandeu/jonah-fleet/main/schema.json";
|
|
95
97
|
|
|
96
98
|
// src/lib/manifest.ts
|
|
@@ -2161,7 +2163,7 @@ import pc9 from "picocolors";
|
|
|
2161
2163
|
import fs11 from "fs";
|
|
2162
2164
|
import path11 from "path";
|
|
2163
2165
|
import os2 from "os";
|
|
2164
|
-
import { spawn } from "child_process";
|
|
2166
|
+
import { spawn, execSync as execSync2 } from "child_process";
|
|
2165
2167
|
|
|
2166
2168
|
// src/lib/worktree.ts
|
|
2167
2169
|
import fs10 from "fs";
|
|
@@ -2509,8 +2511,10 @@ import pc11 from "picocolors";
|
|
|
2509
2511
|
// src/lib/daemon.ts
|
|
2510
2512
|
import fs12 from "fs";
|
|
2511
2513
|
import path12 from "path";
|
|
2512
|
-
import { spawn as spawn2 } from "child_process";
|
|
2514
|
+
import { spawn as spawn2, execFile as execFile3 } from "child_process";
|
|
2515
|
+
import { promisify as promisify3 } from "util";
|
|
2513
2516
|
import pc10 from "picocolors";
|
|
2517
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
2514
2518
|
function getDaemonStatePath(repoRoot) {
|
|
2515
2519
|
return path12.join(repoRoot, ".jonah-fleet", "daemon.json");
|
|
2516
2520
|
}
|
|
@@ -2548,18 +2552,40 @@ function isDaemonRunning(repoRoot) {
|
|
|
2548
2552
|
return false;
|
|
2549
2553
|
}
|
|
2550
2554
|
}
|
|
2555
|
+
async function countOpenReadyPRs(repoRoot) {
|
|
2556
|
+
try {
|
|
2557
|
+
const { stdout } = await execFileAsync3(
|
|
2558
|
+
"gh",
|
|
2559
|
+
["pr", "list", "--state", "open", "--draft=false", "--json", "number", "--jq", "length"],
|
|
2560
|
+
{ cwd: repoRoot }
|
|
2561
|
+
);
|
|
2562
|
+
return parseInt(stdout.trim(), 10) || 0;
|
|
2563
|
+
} catch {
|
|
2564
|
+
return 0;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2551
2567
|
async function startBackgroundDaemon(repoRoot, options = {}) {
|
|
2552
2568
|
if (isDaemonRunning(repoRoot)) {
|
|
2553
2569
|
const existing = readDaemonState(repoRoot);
|
|
2554
2570
|
throw new Error(`Daemon is already running with PID ${existing?.pid}`);
|
|
2555
2571
|
}
|
|
2556
|
-
const
|
|
2557
|
-
const
|
|
2572
|
+
const reviewInterval = options.reviewInterval || 3;
|
|
2573
|
+
const autoworkInterval = options.autoworkInterval || options.interval || 30;
|
|
2574
|
+
const routines = options.routines || ["peer-review", "autowork"];
|
|
2558
2575
|
const logFilePath = path12.join(repoRoot, ".jonah-fleet", "daemon.log");
|
|
2559
2576
|
fs12.mkdirSync(path12.dirname(logFilePath), { recursive: true });
|
|
2560
2577
|
const logFd = fs12.openSync(logFilePath, "a");
|
|
2561
2578
|
const cliPath = process.argv[1];
|
|
2562
|
-
const args = [
|
|
2579
|
+
const args = [
|
|
2580
|
+
"daemon",
|
|
2581
|
+
"--foreground",
|
|
2582
|
+
"--review-interval",
|
|
2583
|
+
String(reviewInterval),
|
|
2584
|
+
"--autowork-interval",
|
|
2585
|
+
String(autoworkInterval),
|
|
2586
|
+
"--routines",
|
|
2587
|
+
routines.join(",")
|
|
2588
|
+
];
|
|
2563
2589
|
if (options.model) {
|
|
2564
2590
|
args.push("--model", options.model);
|
|
2565
2591
|
}
|
|
@@ -2573,7 +2599,8 @@ async function startBackgroundDaemon(repoRoot, options = {}) {
|
|
|
2573
2599
|
const state = {
|
|
2574
2600
|
pid: child.pid,
|
|
2575
2601
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2576
|
-
|
|
2602
|
+
reviewIntervalMinutes: reviewInterval,
|
|
2603
|
+
autoworkIntervalMinutes: autoworkInterval,
|
|
2577
2604
|
routines,
|
|
2578
2605
|
status: "idle"
|
|
2579
2606
|
};
|
|
@@ -2594,25 +2621,27 @@ async function stopDaemon(repoRoot) {
|
|
|
2594
2621
|
}
|
|
2595
2622
|
}
|
|
2596
2623
|
async function runDaemonLoop(repoRoot, options = {}) {
|
|
2597
|
-
const
|
|
2598
|
-
const
|
|
2599
|
-
const routines = options.routines || ["
|
|
2624
|
+
const reviewInterval = options.reviewInterval || 3;
|
|
2625
|
+
const autoworkInterval = options.autoworkInterval || options.interval || 30;
|
|
2626
|
+
const routines = options.routines || ["peer-review", "autowork"];
|
|
2600
2627
|
const state = {
|
|
2601
2628
|
pid: process.pid,
|
|
2602
2629
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2603
|
-
|
|
2630
|
+
reviewIntervalMinutes: reviewInterval,
|
|
2631
|
+
autoworkIntervalMinutes: autoworkInterval,
|
|
2604
2632
|
routines,
|
|
2605
2633
|
status: "idle"
|
|
2606
2634
|
};
|
|
2607
2635
|
writeDaemonState(repoRoot, state);
|
|
2608
2636
|
console.log(pc10.cyan(`
|
|
2609
|
-
\u{1F916} Jonah Fleet Local Agent Daemon Started`));
|
|
2637
|
+
\u{1F916} Jonah Fleet Multi-Cadence Local Agent Daemon Started`));
|
|
2610
2638
|
console.log(pc10.dim(` PID: ${process.pid}`));
|
|
2611
|
-
console.log(pc10.dim(`
|
|
2612
|
-
console.log(pc10.dim(`
|
|
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`));
|
|
2613
2641
|
console.log(pc10.dim(` Working Directory: ${repoRoot}
|
|
2614
2642
|
`));
|
|
2615
2643
|
let isStopping = false;
|
|
2644
|
+
let isWorking = false;
|
|
2616
2645
|
const handleStop = async () => {
|
|
2617
2646
|
if (isStopping) return;
|
|
2618
2647
|
isStopping = true;
|
|
@@ -2624,45 +2653,85 @@ Stopping local agent daemon...`));
|
|
|
2624
2653
|
};
|
|
2625
2654
|
process.once("SIGINT", handleStop);
|
|
2626
2655
|
process.once("SIGTERM", handleStop);
|
|
2627
|
-
const
|
|
2628
|
-
if (isStopping) return;
|
|
2629
|
-
|
|
2630
|
-
state.lastCheckAt = now;
|
|
2656
|
+
const runReviewCheck = async () => {
|
|
2657
|
+
if (isStopping || isWorking) return;
|
|
2658
|
+
state.lastReviewCheckAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2631
2659
|
writeDaemonState(repoRoot, state);
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
}
|
|
2654
|
-
console.
|
|
2655
|
-
} finally {
|
|
2656
|
-
state.status = "idle";
|
|
2657
|
-
state.activeRoutine = void 0;
|
|
2658
|
-
state.activeWorktree = void 0;
|
|
2659
|
-
writeDaemonState(repoRoot, state);
|
|
2660
|
+
const openPRCount = await countOpenReadyPRs(repoRoot);
|
|
2661
|
+
if (openPRCount === 0) {
|
|
2662
|
+
console.log(pc10.dim(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Peer Review Watchdog: 0 ready PRs found (0 tokens used).`));
|
|
2663
|
+
return;
|
|
2664
|
+
}
|
|
2665
|
+
try {
|
|
2666
|
+
isWorking = true;
|
|
2667
|
+
state.status = "working";
|
|
2668
|
+
state.activeRoutine = "peer-review";
|
|
2669
|
+
writeDaemonState(repoRoot, state);
|
|
2670
|
+
console.log(pc10.cyan(`
|
|
2671
|
+
[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] \u{1F50D} Peer Review Watchdog: Found ${openPRCount} ready PR(s). Starting review session...`));
|
|
2672
|
+
await cleanupStaleWorktrees(repoRoot);
|
|
2673
|
+
const result = await runLocalRoutine({
|
|
2674
|
+
targetDir: repoRoot,
|
|
2675
|
+
routine: "peer-review",
|
|
2676
|
+
model: options.model,
|
|
2677
|
+
noWorktree: false
|
|
2678
|
+
});
|
|
2679
|
+
if (result.success) {
|
|
2680
|
+
console.log(pc10.green(`\u2713 Local peer-review completed successfully.`));
|
|
2681
|
+
} else {
|
|
2682
|
+
console.warn(pc10.yellow(`\u26A0\uFE0F Local peer-review completed with code ${result.exitCode}.`));
|
|
2660
2683
|
}
|
|
2684
|
+
} catch (err) {
|
|
2685
|
+
console.error(pc10.red(`\u2717 Error in peer-review: ${err.message}`));
|
|
2686
|
+
} finally {
|
|
2687
|
+
isWorking = false;
|
|
2688
|
+
state.status = "idle";
|
|
2689
|
+
state.activeRoutine = void 0;
|
|
2690
|
+
writeDaemonState(repoRoot, state);
|
|
2661
2691
|
}
|
|
2662
|
-
console.log(pc10.dim(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Sweep completed. Next run in ${intervalMinutes}m.`));
|
|
2663
2692
|
};
|
|
2664
|
-
|
|
2665
|
-
|
|
2693
|
+
const runAutoworkCheck = async () => {
|
|
2694
|
+
if (isStopping || isWorking || !routines.includes("autowork")) return;
|
|
2695
|
+
state.lastAutoworkCheckAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2696
|
+
writeDaemonState(repoRoot, state);
|
|
2697
|
+
try {
|
|
2698
|
+
isWorking = true;
|
|
2699
|
+
state.status = "working";
|
|
2700
|
+
state.activeRoutine = "autowork";
|
|
2701
|
+
writeDaemonState(repoRoot, state);
|
|
2702
|
+
console.log(pc10.cyan(`
|
|
2703
|
+
[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] \u{1F680} Autowork Backlog Scan: Starting session...`));
|
|
2704
|
+
await cleanupStaleWorktrees(repoRoot);
|
|
2705
|
+
const result = await runLocalRoutine({
|
|
2706
|
+
targetDir: repoRoot,
|
|
2707
|
+
routine: "autowork",
|
|
2708
|
+
model: options.model,
|
|
2709
|
+
noWorktree: false
|
|
2710
|
+
});
|
|
2711
|
+
if (result.success) {
|
|
2712
|
+
console.log(pc10.green(`\u2713 Local autowork completed successfully.`));
|
|
2713
|
+
} else {
|
|
2714
|
+
console.warn(pc10.yellow(`\u26A0\uFE0F Local autowork completed with code ${result.exitCode}.`));
|
|
2715
|
+
}
|
|
2716
|
+
} catch (err) {
|
|
2717
|
+
console.error(pc10.red(`\u2717 Error in autowork: ${err.message}`));
|
|
2718
|
+
} finally {
|
|
2719
|
+
isWorking = false;
|
|
2720
|
+
state.status = "idle";
|
|
2721
|
+
state.activeRoutine = void 0;
|
|
2722
|
+
writeDaemonState(repoRoot, state);
|
|
2723
|
+
}
|
|
2724
|
+
};
|
|
2725
|
+
if (routines.includes("peer-review")) {
|
|
2726
|
+
await runReviewCheck();
|
|
2727
|
+
}
|
|
2728
|
+
if (routines.includes("autowork")) {
|
|
2729
|
+
await runAutoworkCheck();
|
|
2730
|
+
}
|
|
2731
|
+
const reviewIntervalMs = reviewInterval * 60 * 1e3;
|
|
2732
|
+
const autoworkIntervalMs = autoworkInterval * 60 * 1e3;
|
|
2733
|
+
const reviewTimer = setInterval(runReviewCheck, reviewIntervalMs);
|
|
2734
|
+
const autoworkTimer = setInterval(runAutoworkCheck, autoworkIntervalMs);
|
|
2666
2735
|
await new Promise(() => {
|
|
2667
2736
|
});
|
|
2668
2737
|
}
|
|
@@ -2673,6 +2742,8 @@ async function runDaemonCommand(action, options = {}) {
|
|
|
2673
2742
|
const act = action?.toLowerCase() || (options.foreground ? "foreground" : "status");
|
|
2674
2743
|
const daemonOpts = {
|
|
2675
2744
|
interval: options.interval ? parseInt(options.interval, 10) : void 0,
|
|
2745
|
+
reviewInterval: options.reviewInterval ? parseInt(options.reviewInterval, 10) : void 0,
|
|
2746
|
+
autoworkInterval: options.autoworkInterval ? parseInt(options.autoworkInterval, 10) : options.interval ? parseInt(options.interval, 10) : void 0,
|
|
2676
2747
|
routines: options.routines ? options.routines.split(",").map((r) => r.trim()) : void 0,
|
|
2677
2748
|
model: options.model,
|
|
2678
2749
|
foreground: options.foreground
|
|
@@ -2687,7 +2758,8 @@ async function runDaemonCommand(action, options = {}) {
|
|
|
2687
2758
|
console.log(pc11.green(`
|
|
2688
2759
|
\u2713 Background agent daemon started successfully.`));
|
|
2689
2760
|
console.log(pc11.dim(` PID: ${state2.pid}`));
|
|
2690
|
-
console.log(pc11.dim(`
|
|
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`));
|
|
2691
2763
|
console.log(pc11.dim(` Routines: ${state2.routines.join(", ")}`));
|
|
2692
2764
|
console.log(pc11.dim(` Log file: .jonah-fleet/daemon.log`));
|
|
2693
2765
|
console.log(pc11.dim(` Run 'jonah-fleet daemon status' or 'jonah-fleet daemon stop' to manage.`));
|
|
@@ -2727,17 +2799,21 @@ Stopping background agent daemon (PID ${state2?.pid})...`));
|
|
|
2727
2799
|
\u{1F916} Jonah Fleet Local Daemon Status
|
|
2728
2800
|
`));
|
|
2729
2801
|
if (running && state) {
|
|
2730
|
-
console.log(` Status:
|
|
2731
|
-
console.log(` PID:
|
|
2732
|
-
console.log(` Started:
|
|
2733
|
-
console.log(`
|
|
2734
|
-
console.log(`
|
|
2735
|
-
console.log(`
|
|
2736
|
-
|
|
2737
|
-
|
|
2802
|
+
console.log(` Status: ${pc11.green(pc11.bold("RUNNING"))}`);
|
|
2803
|
+
console.log(` PID: ${state.pid}`);
|
|
2804
|
+
console.log(` Started: ${new Date(state.startedAt).toLocaleString()}`);
|
|
2805
|
+
console.log(` Peer Review Cadence: Every ${state.reviewIntervalMinutes} minutes (0-token fast preflight)`);
|
|
2806
|
+
console.log(` Autowork Cadence: Every ${state.autoworkIntervalMinutes} minutes`);
|
|
2807
|
+
console.log(` Routines: ${state.routines.join(", ")}`);
|
|
2808
|
+
console.log(` Current State: ${state.status === "working" ? pc11.yellow("WORKING on " + state.activeRoutine) : pc11.green("IDLE")}`);
|
|
2809
|
+
if (state.lastReviewCheckAt) {
|
|
2810
|
+
console.log(` Last Review Check: ${new Date(state.lastReviewCheckAt).toLocaleTimeString()}`);
|
|
2811
|
+
}
|
|
2812
|
+
if (state.lastAutoworkCheckAt) {
|
|
2813
|
+
console.log(` Last Autowork Check: ${new Date(state.lastAutoworkCheckAt).toLocaleTimeString()}`);
|
|
2738
2814
|
}
|
|
2739
2815
|
} else {
|
|
2740
|
-
console.log(` Status:
|
|
2816
|
+
console.log(` Status: ${pc11.gray("STOPPED")}`);
|
|
2741
2817
|
console.log(pc11.dim(` Run 'jonah-fleet daemon start' to start the local worker daemon.`));
|
|
2742
2818
|
}
|
|
2743
2819
|
console.log(`
|
|
@@ -2754,7 +2830,7 @@ program.name("jonah-fleet").description("Manage autonomous agent fleet, prompt r
|
|
|
2754
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) => {
|
|
2755
2831
|
await runRoutineCommand(routine, options);
|
|
2756
2832
|
});
|
|
2757
|
-
program.command("daemon [action]").description("Manage background local worker daemon polling for unclaimed issues and pull requests").option("-i, --interval <minutes>", "
|
|
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) => {
|
|
2758
2834
|
await runDaemonCommand(action, options);
|
|
2759
2835
|
});
|
|
2760
2836
|
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) => {
|
package/dist/lib/daemon.d.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
export interface DaemonState {
|
|
2
2
|
pid: number;
|
|
3
3
|
startedAt: string;
|
|
4
|
-
|
|
4
|
+
reviewIntervalMinutes: number;
|
|
5
|
+
autoworkIntervalMinutes: number;
|
|
5
6
|
routines: string[];
|
|
6
|
-
|
|
7
|
+
lastReviewCheckAt?: string;
|
|
8
|
+
lastAutoworkCheckAt?: string;
|
|
7
9
|
status: 'idle' | 'working' | 'stopped';
|
|
8
10
|
activeRoutine?: string;
|
|
9
11
|
activeWorktree?: string;
|
|
10
12
|
}
|
|
11
13
|
export interface DaemonOptions {
|
|
12
14
|
interval?: number;
|
|
15
|
+
reviewInterval?: number;
|
|
16
|
+
autoworkInterval?: number;
|
|
13
17
|
routines?: string[];
|
|
14
18
|
model?: string;
|
|
15
19
|
foreground?: boolean;
|
|
@@ -19,6 +23,10 @@ export declare function readDaemonState(repoRoot: string): DaemonState | null;
|
|
|
19
23
|
export declare function writeDaemonState(repoRoot: string, state: DaemonState): void;
|
|
20
24
|
export declare function clearDaemonState(repoRoot: string): void;
|
|
21
25
|
export declare function isDaemonRunning(repoRoot: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Fast pre-flight check to query number of open ready PRs in ~100ms with 0 token cost.
|
|
28
|
+
*/
|
|
29
|
+
export declare function countOpenReadyPRs(repoRoot: string): Promise<number>;
|
|
22
30
|
/**
|
|
23
31
|
* Starts the daemon in the background by detaching a child process.
|
|
24
32
|
*/
|
|
@@ -28,7 +36,7 @@ export declare function startBackgroundDaemon(repoRoot: string, options?: Daemon
|
|
|
28
36
|
*/
|
|
29
37
|
export declare function stopDaemon(repoRoot: string): Promise<boolean>;
|
|
30
38
|
/**
|
|
31
|
-
* Runs the polling daemon loop in the current process.
|
|
39
|
+
* Runs the multi-cadence polling daemon loop in the current process.
|
|
32
40
|
*/
|
|
33
41
|
export declare function runDaemonLoop(repoRoot: string, options?: DaemonOptions): Promise<void>;
|
|
34
42
|
//# sourceMappingURL=daemon.d.ts.map
|
package/dist/lib/daemon.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/lib/daemon.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/lib/presets.d.ts
CHANGED
|
@@ -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.
|
|
50
|
+
export declare const FLEET_VERSION = "1.4.2";
|
|
51
51
|
export declare const SCHEMA_URL = "https://raw.githubusercontent.com/juliendurandeu/jonah-fleet/main/schema.json";
|
|
52
52
|
//# sourceMappingURL=presets.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/lib/presets.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEpE,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE;QACR,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,OAAO,CAAC;QACvB,SAAS,EAAE,OAAO,CAAC;QACnB,qBAAqB,EAAE,OAAO,CAAC;QAC/B,kCAAkC,EAAE,OAAO,CAAC;QAC5C,kBAAkB,EAAE,OAAO,CAAC;QAC5B,kBAAkB,EAAE,OAAO,CAAC;KAC7B,CAAC;IACF,SAAS,CAAC,EAAE;QACV,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,kCAAkC,CAAC,EAAE,MAAM,CAAC;QAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KACnC,CAAC;IACF,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,QAAQ,GAAG,QAAQ,CAAC;KAC9B,CAAC;IACF,SAAS,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,6BAA6B,EAAE,mBAI3C,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE;IAAE,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/lib/presets.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEpE,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE;QACR,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,OAAO,CAAC;QACvB,SAAS,EAAE,OAAO,CAAC;QACnB,qBAAqB,EAAE,OAAO,CAAC;QAC/B,kCAAkC,EAAE,OAAO,CAAC;QAC5C,kBAAkB,EAAE,OAAO,CAAC;QAC5B,kBAAkB,EAAE,OAAO,CAAC;KAC7B,CAAC;IACF,SAAS,CAAC,EAAE;QACV,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAC/B,kCAAkC,CAAC,EAAE,MAAM,CAAC;QAC5C,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KACnC,CAAC;IACF,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,QAAQ,GAAG,QAAQ,CAAC;KAC9B,CAAC;IACF,SAAS,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,6BAA6B,EAAE,mBAI3C,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE;IAAE,QAAQ,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAiE3H,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,CAarF,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,aAAa,CAAC,UAAU,CAAC,GAAG,YAAY,CAOlG,CAAC;AAEF,eAAO,MAAM,aAAa,UAAU,CAAC;AACrC,eAAO,MAAM,UAAU,kFAAkF,CAAC"}
|
package/dist/lib/runner.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/package.json
CHANGED
|
@@ -41,6 +41,12 @@ tests/ # Test suites
|
|
|
41
41
|
- **Error Handling**: Validate inputs at server and module boundaries. Return explicit structured errors.
|
|
42
42
|
- **Testing Philosophy**: Write regression unit tests for every bug fix and integration tests for key workflows.
|
|
43
43
|
|
|
44
|
+
## Requirements Discovery & Inquisitive Stance
|
|
45
|
+
|
|
46
|
+
- **Challenge Premise First**: Do not be a passive order-taker. Before writing code or drafting large plans, verify whether the requested change is necessary, whether it solves a root cause rather than a symptom, or if existing primitives in the codebase already handle it.
|
|
47
|
+
- **Zero-Guesswork Ambiguity Gate**: Never make silent assumptions about underspecified requirements, schemas, edge cases, or UX flows. Stop and ask clarifying questions before implementation.
|
|
48
|
+
- **Structured Clarifications & Grilling**: When asking questions, propose 1–2 high-leverage questions at a time with concrete options or trade-offs. For complex features or fuzzy requirements, invoke `/grill-me` to stress-test the design before writing code.
|
|
49
|
+
|
|
44
50
|
## Conventions
|
|
45
51
|
|
|
46
52
|
- **Language**: All code, comments, commit messages, and PR titles must be written in **English**.
|
|
@@ -83,8 +83,11 @@ The routines invoke specialized engineering skills at key workflow checkpoints:
|
|
|
83
83
|
- `/resolving-merge-conflicts`: Resolves merge conflicts mechanically before squash-merging.
|
|
84
84
|
- **Issues Housekeeping (`issues-housekeeping.md`)**:
|
|
85
85
|
- `/triage`: Evaluates incoming issues into canonical roles.
|
|
86
|
+
- **Triage & Interactive Planning**:
|
|
87
|
+
- `/grill-me`: Interrogates proposals and requirements to uncover edge cases and force explicit trade-offs before implementation.
|
|
86
88
|
- **Product Planning (`product-planning.md`)**:
|
|
87
89
|
- `/domain-modeling`: Pressure-tests proposals and records domain terms / ADRs.
|
|
90
|
+
- `/grill-me`: Stress-tests proposed roadmaps and user stories.
|
|
88
91
|
- `/to-spec`: Authors formal PRDs for larger proposals.
|
|
89
92
|
- `/to-tickets`: Decomposes approved epics/proposals into dependency-linked issues.
|
|
90
93
|
- **Prompt Optimizer (`optimizer.md`)**:
|
|
@@ -43,6 +43,7 @@ If any criterion cannot be met, stop immediately and log FAILURE with the reason
|
|
|
43
43
|
- Do not start implementing an issue before claiming it (both assignment AND claim comment).
|
|
44
44
|
- Do not mark a PR ready while its `mergeable_state` is `dirty` — resolve merge conflicts first.
|
|
45
45
|
- Do not fall into the **Telemetry Rabbit Hole**: do not spend cycles instrumenting elaborate fallback telemetry or defensive error handling for features that suffer from lack of user intent rather than software bugs.
|
|
46
|
+
- Do not guess or invent arbitrary specifications for ambiguous issues — post clarifying questions, label `needs-info`, and release the claim instead of blindly writing code.
|
|
46
47
|
|
|
47
48
|
## Instructions
|
|
48
49
|
|
|
@@ -85,10 +86,13 @@ b. **Eligible → claim, then implement.** Call `get_me` once to learn your own
|
|
|
85
86
|
6. Count open PRs authored by automated sessions representing live reviewable work (excluding log-only PRs). If 3 or more non-log PRs are open, STOP — run is SUCCESS with "Too many open PRs, converging first".
|
|
86
87
|
7. Call `get_me` once to learn your own GitHub account (`login`).
|
|
87
88
|
8. List open issues sorted by priority labels (P1 > P2 > P3). Within the same priority tier, order by type (`bug`/`security` before others), then oldest first.
|
|
89
|
+
- **Selective Server-Side Querying**: To prevent tool buffer truncation and avoid ingesting un-actionable issues, query GitHub with server-side exclusion filters rather than dumping all issues. E.g.:
|
|
90
|
+
`gh issue list --state open --search "no:assignee -label:measurement -label:needs-human -label:needs-design -label:wontfix -label:needs-info" --limit 30`
|
|
91
|
+
(or filter by priority tier: `--search "label:priority/P0,priority/P1 no:assignee -label:measurement -label:needs-human -label:needs-design -label:wontfix -label:needs-info"`).
|
|
88
92
|
- **Dual Execution Priority Routing**:
|
|
89
93
|
- If running in **Cloud Actions** (`$GITHUB_ACTIONS` / `$CI`): Scan mode selects `priority/P0` or `priority/P1` issues first. Lower-priority `priority/P2` or `priority/P3` issues are eligible in cloud Scan mode ONLY if the issue has remained unclaimed for more than 48 hours (`created_at` older than 48h, acting as a cloud catchup sweep).
|
|
90
94
|
- If running in **Local Agent** (`$LOCAL_AGENT`): Scan mode selects across all priorities (P0 → P1 → P2 → P3) without time gating, prioritizing active local backlog consumption.
|
|
91
|
-
- Skip assigned issues (unless stale claim per `ORCHESTRATION.md`), issues with open PRs, issues with unclosed blocking dependencies (`Blocked by #N` / `Depends on #N`), issues labeled `needs-human` or `needs-
|
|
95
|
+
- Skip assigned issues (unless stale claim per `ORCHESTRATION.md`), issues with open PRs, issues with unclosed blocking dependencies (`Blocked by #N` / `Depends on #N`), issues labeled `needs-human`, `needs-design`, or `needs-info`, and issues under cross-run cooldown.
|
|
92
96
|
9. If no eligible candidate exists, STOP — run is SUCCESS with "No unclaimed work available".
|
|
93
97
|
10. If the candidate should be closed already (work done, PRs merged), close it and return to step 8.
|
|
94
98
|
10a. **Stale-claim reclamation:** If candidate is a stale claim per `ORCHESTRATION.md`, re-read immediately before writing, unassign the dead owner, post reclamation comment, and proceed to claim.
|
|
@@ -103,6 +107,11 @@ b. **Eligible → claim, then implement.** Call `get_me` once to learn your own
|
|
|
103
107
|
- Read the issue description, linked code, and comment thread.
|
|
104
108
|
- If bug: use `/diagnosing-bugs` to establish reproduction test before fixing.
|
|
105
109
|
- If large/complex: use `/domain-modeling` and `/codebase-design`.
|
|
110
|
+
- **Ambiguity & Missing Acceptance Criteria Gate**: Challenge underspecified or incomplete requests before writing any code. If the issue lacks observable acceptance criteria, relies on unverified assumptions, or leaves critical technical/UX decisions ambiguous:
|
|
111
|
+
- Do NOT guess or invent arbitrary requirements to force completion.
|
|
112
|
+
- Post a comment on the issue posing 1–3 focused clarifying questions that identify the exact decisions or trade-offs needed.
|
|
113
|
+
- Apply the `needs-info` label and release the claim (unassign).
|
|
114
|
+
- Select the next candidate (evaluating ambiguous issues counts toward step 12's infeasible-continuation cap).
|
|
106
115
|
- **Intent vs. Defect Guardrail**: When investigating issues related to low conversion, zero-click events, or underperforming features: verify whether the issue is a software defect or a lack of user intent. If data indicates the root cause is **lack of user intent** (e.g. button is rendered above fold and functions correctly when clicked, but user interaction rate is <2%) rather than a software defect, do NOT fall into the **telemetry rabbit hole** (adding elaborate fallback telemetry, downstream error handling, or defensive rendering). Categorize the issue as a **product/UX question** (`needs-design` / `roadmap/*`), comment explaining the lack of user intent, release the claim (unassign), and select the next candidate.
|
|
107
116
|
- If infeasible: comment explaining blocker, release claim (unassign), and select next candidate (up to 3 infeasible evaluations per run). If permanent blocker on 2nd strike, apply `needs-human` label and tag repo owner.
|
|
108
117
|
12a. **Umbrella-issue handoff + batching:** If candidate is an umbrella epic:
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: grill-me
|
|
3
|
+
description: Stress-test proposals, requirements, and designs through a targeted, sequential interview to eliminate assumptions before implementation.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Grill Me
|
|
7
|
+
|
|
8
|
+
Stress-test plans, requirements, and designs through an active, sequential interview. The agent acts as an inquisitor—a cross between a principal engineer and a rigorous technical product manager.
|
|
9
|
+
|
|
10
|
+
Your goal is **not** to validate or agree with the user. Your goal is to **uncover gaps, challenge necessity, expose hidden edge cases, and force clear decisions** before any code is written or formal spec drafted.
|
|
11
|
+
|
|
12
|
+
## The Grilling Mindset
|
|
13
|
+
|
|
14
|
+
- **Default to skepticism**: Ask why this needs to be built at all. Can this be solved with existing codebase primitives, configuration, or by deleting code?
|
|
15
|
+
- **Zero-guesswork discipline**: Do not guess what the user meant when requirements are ambiguous. Force explicit choices.
|
|
16
|
+
- **One to two questions per turn**: Never dump a questionnaire or a wall of questions. Ask at most 1–2 high-leverage questions at a time, provide concrete trade-offs or recommended options, and wait for the response before proceeding.
|
|
17
|
+
|
|
18
|
+
## Process
|
|
19
|
+
|
|
20
|
+
Execute these phases in sequence:
|
|
21
|
+
|
|
22
|
+
### Phase 1: Challenge the Premise & Scope
|
|
23
|
+
|
|
24
|
+
1. **Problem vs. Symptom**: Is the user solving the real root problem or patching a surface symptom?
|
|
25
|
+
2. **Alternative & Simplicity**: Could this be handled without new abstractions? What is the simplest thing that could possibly work?
|
|
26
|
+
3. **Necessity & ROI**: What happens if we do nothing? Who specifically benefits from this change?
|
|
27
|
+
|
|
28
|
+
Probe the premise:
|
|
29
|
+
- "What exact problem does this solve, and why can't we solve it with [existing module/primitive]?"
|
|
30
|
+
- "What is explicitly *out of scope* for this change?"
|
|
31
|
+
|
|
32
|
+
### Phase 2: Stress-Test Technical Seams & Failure Modes
|
|
33
|
+
|
|
34
|
+
Once the premise is solid, probe failure modes and system boundaries:
|
|
35
|
+
|
|
36
|
+
1. **Failure & Degraded States**: What happens if network calls fail, services time out, or dependencies crash?
|
|
37
|
+
2. **Boundary & Empty States**: What happens with empty data, maximum limits, concurrent writes, or conflicting inputs?
|
|
38
|
+
3. **Security & Auth**: Are there permission gates, auth boundaries, or data leakage risks?
|
|
39
|
+
4. **Data Model & Schema**: Does this change existing contracts? Is it backward-compatible?
|
|
40
|
+
|
|
41
|
+
### Phase 3: Establish Concrete Acceptance Criteria
|
|
42
|
+
|
|
43
|
+
Force observable, testable definitions of done:
|
|
44
|
+
|
|
45
|
+
1. **Test Seams**: At which seam (unit, integration, end-to-end) will this be verified?
|
|
46
|
+
2. **Observable Outcomes**: What is the exact input and expected output? How will a test or user prove this works?
|
|
47
|
+
3. **Negative Paths**: What negative test cases must fail safely?
|
|
48
|
+
|
|
49
|
+
### Phase 4: Hand-off & Synthesis
|
|
50
|
+
|
|
51
|
+
When all critical ambiguities are resolved:
|
|
52
|
+
1. Summarize the agreed scope, explicit non-goals, architectural decisions, and acceptance criteria in a concise brief.
|
|
53
|
+
2. Direct the next action:
|
|
54
|
+
- For formal requirements: run `/to-spec` or author an agent brief.
|
|
55
|
+
- For domain terminology: run `/domain-modeling`.
|
|
56
|
+
- For immediate implementation: proceed test-first via `/tdd`.
|
|
57
|
+
|
|
58
|
+
## Completion Criteria
|
|
59
|
+
|
|
60
|
+
The grilling session is complete only when:
|
|
61
|
+
- [ ] Core motivation and explicit non-goals are established
|
|
62
|
+
- [ ] Edge cases and failure modes have explicit decisions recorded
|
|
63
|
+
- [ ] Acceptance criteria and verification seams are concrete and testable
|
|
64
|
+
- [ ] The user confirms alignment with the summarized decisions
|
|
@@ -73,7 +73,7 @@ Show counts and a one-line summary per item. Let the maintainer pick.
|
|
|
73
73
|
|
|
74
74
|
3. **Verify the claim.** Before any grilling, check that the claim holds up. For a bug, reproduce it from the reporter's steps. For a PR, confirm the diff does what it claims: check it out, run the relevant tests or commands. Report what happened: confirmed (with code path), failed, or insufficient detail (a strong `needs-info` signal). A confirmed verification makes a much stronger agent brief.
|
|
75
75
|
|
|
76
|
-
4. **Grill (if needed).** If the request needs fleshing out, call the Skill tool twice, for "
|
|
76
|
+
4. **Grill (if needed).** If the request needs fleshing out, call the Skill tool twice, for "grill-me" and "domain-modeling", and grill it into shape a round of questions at a time, sharpening domain terms and updating `CONTEXT.md`/ADRs inline as decisions land.
|
|
77
77
|
|
|
78
78
|
5. **Apply the outcome:**
|
|
79
79
|
- `ready-for-agent`: post an agent brief comment ([AGENT-BRIEF.md](AGENT-BRIEF.md)).
|
|
@@ -107,12 +107,13 @@ jobs:
|
|
|
107
107
|
fi
|
|
108
108
|
|
|
109
109
|
# Priority-driven dual execution filter on direct pull_request triggers:
|
|
110
|
-
#
|
|
110
|
+
# Strict allowlist: only trigger immediate cloud review if explicitly labeled priority/P0 or priority/P1.
|
|
111
|
+
# Untagged PRs and lower-priority PRs (P2/P3) skip immediate cloud review and are handled by local peer-review daemons
|
|
111
112
|
# (cloud scheduled watchdog sweep will catch up if unreviewed > 48h).
|
|
112
113
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
113
114
|
labels=$(echo "$pr" | jq -r '.labels[].name' 2>/dev/null || echo "")
|
|
114
|
-
if echo "$labels" | grep -qE '^priority/P[
|
|
115
|
-
echo "PR is labeled
|
|
115
|
+
if ! echo "$labels" | grep -qE '^priority/P[01]$'; then
|
|
116
|
+
echo "PR is not explicitly labeled priority/P0 or priority/P1 — skipping immediate cloud review for local peer-review daemon."
|
|
116
117
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
117
118
|
exit 0
|
|
118
119
|
fi
|