jules-orchestrator-kit 0.32.3 → 0.32.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -108,13 +108,19 @@ Autonomous coding agents can write software at 100× human speed—but unconstra
108
108
 
109
109
  * **💻 Native Interactive UX & Command Palette (`v0.30.0`):** Features a zero-dependency full-screen Terminal Engine (`capabilities`, `key-decoder`, `renderer`, `layout`, `widgets`), interactive diagnostic matrix (`agentctl doctor`), task queue/swarm managers (`agentctl queue`, `agentctl swarm`), and a searchable Command Palette.
110
110
 
111
- * **🌐 Universal Polyglot Spine:** Natively auto-detects 24+ tech stacks (PHP/Laravel/WordPress, .NET/C#, Python, Go, Rust, C/C++, Flutter/Swift, Node/Deno/Bun) and transparently wraps verification suites in Docker Compose or Devcontainer sandboxes.
111
+ * **🌐 Universal Polyglot Spine:** Natively auto-detects 26+ tech stacks (PHP/Laravel/WordPress, .NET/C#, Python, Go, Rust, C/C++, Flutter/Swift, Node/Deno/Bun, Solidity/Foundry/Hardhat) and transparently wraps verification suites in Docker Compose or Devcontainer sandboxes.
112
+
113
+ * **🧩 DAG-Ordered Task Queue & Specialist Agent Roles:** `agentctl queue --dag` resolves inter-task dependencies via Kahn's algorithm with cycle detection instead of linear FIFO order, and `--role <overseer|bolt|sentinel|janitor>` binds a task to a pre-defined specialist prompt persona resolved from `.agent/prompts/`.
114
+
115
+ * **🔏 Cryptographic Evidence Ledger (`agentctl evidence`):** Generates a SHA-256 manifest of changed files, test-file hashes, and tamper-detection locks for every verified task — a portable, offline-verifiable audit trail toward the roadmap's SOC2 compliance exporter.
116
+
117
+ * **💸 Dynamic Complexity & Cost Router (`router:` in `.agent/config.yml`, opt-in):** A zero-dependency, rule-based heuristic classifier (`src/router.mjs`) routes trivial tasks (typos, lint fixes, single-file lockfile bumps) to a cheap/fast provider — e.g. the built-in `gemini-flash` (Gemini CLI, headless mode) preset — while reserving your primary provider for complex, multi-file, or safety-sensitive work. Tasks touching `scope.deny`, `auth/**`, `migrations/**`, secrets, or using the `sentinel` role always force the primary provider regardless of score. Fully provider-agnostic: swap in any exec/HTTP provider spec for `router.fast`/`router.complex`, and override per-task with `--tier fast|complex`.
112
118
 
113
119
  * **📂 Scoped Monorepo Boundary Resolver:** Statically maps changed files up directory ancestry to invoke isolated subshell test suites (`(cd backend && pytest) && (cd cli && cargo test)`), eliminating global test thrashing.
114
120
 
115
121
  * **🚀 Zero-Test Bootstrapping (`agentctl bootstrap`):** Synthesizes deterministic syntax-check and smoke-test verification oracles for untested legacy repositories so agents always operate against a falsifiable feedback loop.
116
122
 
117
- * **📈 Proven Scale & Reliability:** Empirically tested with **368 unit tests across 52 suites passing in < 3.0s**, supporting 300+ daily agent sessions per repository.
123
+ * **📈 Proven Scale & Reliability:** Empirically tested with **421 unit tests across 59 suites passing in < 8.0s**, supporting 300+ daily agent sessions per repository.
118
124
 
119
125
  <br/>
120
126
 
@@ -166,7 +172,7 @@ To ensure maximum merge success, dispatch tasks according to our deterministic t
166
172
  | **Self-Healing Loop** | ❌ None (Crashes on test error) | ❌ None (Fails build; notifies human) | ✅ **Autonomous OODA Loop** (Max 3 repair turns with error fingerprinting) |
167
173
  | **Interactive UX Engine**| ❌ Raw unformatted CLI dumps | ❌ Non-interactive log outputs | ✅ **Native TUI Engine & Command Palette** (Zero-dependency alternate-screen TUI) |
168
174
  | **Scope Isolation** | ❌ None (Can modify CI files or lockfiles) | 🟡 Post-commit branch rules only | ✅ **Fail-Closed Scope Guard** (Deny-first evaluation; blocks protected paths) |
169
- | **Polyglot Stack Detection**| ❌ Manual prompt instructions | 🟡 Hardcoded YAML workflow steps | ✅ **Universal 24+ Stack Detector** (`src/config.mjs`) |
175
+ | **Polyglot Stack Detection**| ❌ Manual prompt instructions | 🟡 Hardcoded YAML workflow steps | ✅ **Universal 26+ Stack Detector** (`src/config.mjs`) |
170
176
  | **Flaky Test Quarantine** | ❌ Fails session randomly | ❌ Breaks CI pipeline randomly | ✅ **Wilson-Score Statistical Quarantine** (Oscillation ≥ 0.40 quarantined automatically) |
171
177
  | **Monorepo Scoping** | ❌ Runs full global test suite | 🟡 Requires custom Nx/Turbo scripting | ✅ **Scoped Subshell Boundary Resolver** (`resolveWorkspaceBoundary`) |
172
178
  | **Zero-Test Bootstrapping**| ❌ Halts without verification oracle | ❌ Fails build if no tests exist | ✅ **Instant Oracle Synthesis** (`php -l`, `compileall`, `dotnet build`, `tsc`, `smoke`) |
@@ -227,7 +233,7 @@ npx jules-orchestrator-kit queue --interactive
227
233
  <br/>
228
234
 
229
235
  <details>
230
- <summary><b>🔍 View All 24+ Supported Ecosystems & Stack Triggers</b></summary>
236
+ <summary><b>🔍 View All 26+ Supported Ecosystems & Stack Triggers</b></summary>
231
237
 
232
238
  <br/>
233
239
 
@@ -242,6 +248,8 @@ Ecosystems Natively Supported by src/config.mjs:
242
248
  ├── Systems / Rust Cargo (Cargo.toml)
243
249
  ├── Systems / Go (go.mod)
244
250
  ├── Systems / Make (Makefile)
251
+ ├── Web3 / Solidity Foundry (foundry.toml, remappings.txt) — offline-enforced forge test/build/fmt
252
+ ├── Web3 / Solidity Hardhat (hardhat.config.js, hardhat.config.ts)
245
253
  ├── Python / FastAPI / Django (pyproject.toml, requirements.txt, setup.py)
246
254
  ├── Elixir / Phoenix (mix.exs)
247
255
  ├── Ruby / Rails (Gemfile)
@@ -269,7 +277,7 @@ Ecosystems Natively Supported by src/config.mjs:
269
277
  # .agent/config.yml — Universal Orchestrator Configuration
270
278
 
271
279
  version: 1
272
- provider: "jules" # Provider key ("jules" | "claude-code" | "local")
280
+ provider: "jules" # Provider key ("jules" | "claude-code" | "codex" | "gemini-flash")
273
281
  baseBranch: "main" # Default target base branch
274
282
  branchPrefix: "agent/" # Prefix for task branches
275
283
 
@@ -292,6 +300,15 @@ limits:
292
300
  dailyTasks: 300 # Daily task session quota limit
293
301
  repairAttempts: 3 # Maximum OODA repair iterations
294
302
  concurrency: 1 # Worker slot concurrency limit
303
+
304
+ # Dynamic Complexity & Cost Router — opt-in, disabled by default.
305
+ # Provider-agnostic: "fast"/"complex" accept any provider key ("jules" |
306
+ # "claude-code" | "codex" | "gemini-flash") or an inline custom provider spec.
307
+ router:
308
+ enabled: false
309
+ fast: "gemini-flash" # Trivial/mechanical tasks (score <= threshold)
310
+ complex: "jules" # Complex/multi-file/safety-sensitive tasks; defaults to `provider`
311
+ threshold: 0 # Heuristic score above which a task escalates to `complex`
295
312
  ```
296
313
 
297
314
  <br/>
@@ -369,15 +386,15 @@ Native stdio server exposing task dispatch, gate verification, and risk auditing
369
386
  | Command | Usage | Description | Exit Codes |
370
387
  | :--- | :--- | :--- | :--- |
371
388
  | `init` | `agentctl init [--interactive] [--tier pro]` | Interactive onboarding wizard & stack oracle inspector generating `.agent/config.yml`. | `0` (Created) |
372
- | `task create` | `agentctl task create [--title <t>] [--prompt <p>] [--template <id>]` | Interactively authors & scopes falsifiable task envelopes with secret scrubbing & preflight gate checks. | `0` (Queued), `1` (Unfalsifiable / Secret leak) |
389
+ | `task create` | `agentctl task create [--title <t>] [--prompt <p>] [--template <id>] [--role <name>] [--tier fast\|complex] [--depends-on <id,...>]` | Interactively authors & scopes falsifiable task envelopes with secret scrubbing, preflight gate checks, specialist role resolution, DAG dependency wiring, and an optional Cost Router tier override. | `0` (Queued), `1` (Unfalsifiable / Secret leak) |
373
390
  | `task template` | `agentctl task template [<id>] [--list] [--json]` | Lists and synthesizes specialized web task envelopes (`web-cwv`, `web-wcag`, `web-seo`, `web-playwright`, `web-flaky-heal`). | `0` (Synthesized/Listed) |
374
391
  | `task optimize` | `agentctl task optimize "<prompt>" [--fix] [--web] [--json]` | Linter & optimizer injecting Google Labs 3-phase exploration budgets, critic steering, and web oracles. | `0` (Scored/Fixed) |
375
392
  | `test-gen` | `agentctl test-gen --title <t> --spec <s> [--run]` | Scaffolds falsifiable unit tests, verifies **RED** failure state, and locks test in `scope.deny`. | `0` (Scaffolded/Red) |
376
393
  | `rollback` | `agentctl rollback [sessionId \| --latest]` | Restores exact commit, uncommitted files, and cleans orphan task worktrees from pre-flight checkpoints. | `0` (Restored), `1` (Error) |
377
394
  | `resume` | `agentctl resume <sessionId> --response "<reply>"` | Streams engineer response back into active Google Jules warm session context window. | `0` (Resumed), `1` (Error) |
378
- | `dispatch` | `agentctl dispatch --title <t> --prompt <p>` | Dispatches a single task to an AI agent in an isolated worktree. | `0` (Success), `1` (Arg error), `2` (429 Rate limit), `3` (Scope deny), `4` (OODA exhausted), `5` (Diff > 75KB), `6` (Secret leak) |
395
+ | `dispatch` | `agentctl dispatch --title <t> --prompt <p> [--role <name>] [--tier fast\|complex]` | Dispatches a single task to an AI agent in an isolated worktree, optionally binding a specialist role prompt (`overseer`\|`bolt`\|`sentinel`\|`janitor`) and/or overriding the Cost Router tier. | `0` (Success), `1` (Arg error), `2` (429 Rate limit), `3` (Scope deny), `4` (OODA exhausted), `5` (Diff > 75KB), `6` (Secret leak) |
379
396
  | `doctor` | `agentctl doctor [--interactive] [--fix safe]` | Diagnostic DAG check runner & automated transactional repair planner. | `0` (Healthy) |
380
- | `queue` | `agentctl queue [--interactive] [--json]` | Consumes, inspects, and executes task envelopes in `.agent/jules-queue/` (supports `--json`). | `0` (Complete) |
397
+ | `queue` | `agentctl queue [--interactive] [--dag] [--concurrency <n>] [--json]` | Consumes, inspects, and executes task envelopes in `.agent/jules-queue/`; `--dag` resolves inter-task dependencies via Kahn's algorithm with cycle detection instead of linear FIFO order (supports `--json`). | `0` (Complete) |
381
398
  | `swarm` | `agentctl swarm [--interactive] [--json]` | Runs parallel multi-agent swarm across worker slots with process PID liveness detection (supports `--json`). | `0` (Complete) |
382
399
  | `scan` | `agentctl scan` | Scans codebase for TODO/FIXME annotations to seed task authoring. | `0` (Scanned) |
383
400
  | `review-repair`| `agentctl review-repair <pr-comments.json>`| Parses GitHub PR review comments and synthesizes actionable OODA repair tasks. | `0` (Parsed), `1` (Missing file) |
@@ -386,9 +403,10 @@ Native stdio server exposing task dispatch, gate verification, and risk auditing
386
403
  | `bootstrap` | `agentctl bootstrap [--force] [--json]` | Inspects an untested repository and synthesizes `.agent/config.yml` with a zero-test verification oracle (`php -l`, `compileall`, `dotnet build`, `tsc`, `smoke`). | `0` (Bootstrapped / Existing) |
387
404
  | `lock` | `agentctl lock <acquire\|release\|status>`| Manages VFS mutex locks for multi-agent non-overlapping file ownership. | `0` (Locked/Released), `1` (Conflict) |
388
405
  | `clean` | `agentctl clean` | Prunes stale git worktrees, lockfiles, and temporary ledgers. | `0` (Clean) |
406
+ | `evidence` | `agentctl evidence <generate\|verify\|show> [--manifest <path>] [--json]` | Generates, verifies, or prints a SHA-256 cryptographic evidence manifest (changed-file hashes + test-file tamper lock) for audit trails. | `0` (Verified/Generated), `1` (Tamper detected / Verification failed) |
389
407
  | `mcp` | `agentctl mcp` | Starts stdio Model Context Protocol (MCP) server for tool integration. | `0` / Stdio stream |
390
408
  | `mcp init` | `agentctl mcp init [--target cursor\|vscode\|claude\|all]` | 1-click scaffolding for Cursor (`.cursor/mcp.json`), VS Code tasks (`tasks.json`), and Claude Desktop. | `0` (Scaffolded) |
391
- | `version` | `agentctl version` | Outputs orchestrator kit semantic version (`v0.32.3`). | `0` |
409
+ | `version` | `agentctl version` | Outputs orchestrator kit semantic version (`v0.32.5`). | `0` |
392
410
 
393
411
  <br/>
394
412
 
@@ -419,7 +437,23 @@ const result = await provider.dispatch(
419
437
  );
420
438
  ```
421
439
 
422
- ### 3. Model Context Protocol (MCP) Server
440
+ ### 3. Dynamic Complexity & Cost Router (`resolveRoutedProvider`)
441
+ Opt-in, config-driven routing between a cheap/fast provider and your primary provider — see [Configuration Reference](#configuration) for the `router:` block. Programmatic usage mirrors `createFailoverProvider`:
442
+
443
+ ```javascript
444
+ import { resolveRoutedProvider, loadConfig } from "jules-orchestrator-kit";
445
+
446
+ const config = loadConfig(process.cwd()); // router.enabled must be true in .agent/config.yml
447
+ const { provider, classification } = resolveRoutedProvider(
448
+ { title: "Fix typo", prompt: "Fix a typo in the README." },
449
+ config
450
+ );
451
+ console.log(classification.tier); // "fast" | "complex"
452
+ ```
453
+
454
+ Ships with a `gemini-flash` preset (Gemini CLI headless mode, `gemini-3.6-flash`) as a batteries-included fast tier, but any provider key or custom spec works for `router.fast`/`router.complex` — the router is provider-agnostic by design, not tied to any single vendor.
455
+
456
+ ### 4. Model Context Protocol (MCP) Server
423
457
  Expose orchestrator gates and queue controls over stdio to client tools (Antigravity, Claude, Cursor):
424
458
  ```bash
425
459
  npx jules-orchestrator-kit mcp
@@ -436,6 +470,8 @@ npx jules-orchestrator-kit mcp
436
470
 
437
471
  | Feature | Module / Command | Architectural Description | Target Release |
438
472
  | :--- | :--- | :--- | :---: |
473
+ | **Dynamic Complexity & Cost Router** | `src/router.mjs`, `router:` in `.agent/config.yml` | Provider-agnostic, zero-dependency heuristic classifier routing trivial tasks to a fast/cheap provider (`gemini-flash` preset included) and complex/safety-sensitive tasks to the primary provider; opt-in, `--tier` override. | **Unreleased** *(main)* |
474
+ | **DAG Task Queue, Specialist Roles & Evidence Ledger** | `src/dag-engine.mjs`, `src/evidence.mjs`, `agentctl evidence` | Kahn's-algorithm dependency-ordered queue execution (`queue --dag`), `--role` specialist prompt resolution, and SHA-256 cryptographic evidence manifests with test-tamper locking. | **Unreleased** *(main)* |
439
475
  | **Warm Session Resumption & PR Bundler** | `src/provider.mjs`, `src/engine.mjs` | Multi-turn warm session context streaming via `POST /v1alpha/sessions/{id}:sendMessage` & evidence PR descriptions. | **v0.31.0** *(Shipped)* |
440
476
  | **TDD Harness & Prompt Falsifiability Linter** | `agentctl test-gen`, `agentctl task optimize` | Automated RED-state test generator, `scope.deny` test locking, and prompt testability linter with fuzzy path resolution. | **v0.31.0** *(Shipped)* |
441
477
  | **Atomic Git Checkpoint & Rollback** | `agentctl rollback` (`src/ops/checkpoint.mjs`) | Pre-flight git HEAD/stash snapshotting, atomic rollback restoration, and 10-session pruning rotation. | **v0.31.0** *(Shipped)* |
package/bin/agentctl.mjs CHANGED
@@ -12,16 +12,18 @@ import { reapOrphanedIntents, reapStaleMutexDirs } from "../src/journal.mjs";
12
12
  const args = process.argv.slice(2);
13
13
  const command = args[0];
14
14
 
15
- function printHelp() {
15
+ export const VERSION = "0.32.5";
16
+
17
+ export function printHelp() {
16
18
  console.log(`
17
- 🚀 agentctl v0.32.3 — Universal Agent Orchestrator & Safety Gatekeeper
19
+ 🚀 agentctl v0.32.5 — Universal Agent Orchestrator & Safety Gatekeeper
18
20
 
19
21
  Usage: agentctl <command> [options]
20
22
 
21
23
  Commands:
22
- dispatch | create Dispatch a single task to an AI agent
24
+ dispatch | create Dispatch a single task to an AI agent (--role <name>, --tier fast|complex)
23
25
  gate | audit Run CI security and verification gate against current branch
24
- queue Run pending task queue
26
+ queue Run pending task queue (--dag, --concurrency <n>)
25
27
  swarm Run parallel task swarm
26
28
  mcp Start stdio Model Context Protocol (MCP) server
27
29
  clean Clean stale branches, worktrees, locks, and ledgers
@@ -31,7 +33,7 @@ Commands:
31
33
  review-repair Parse PR review comments and synthesize OODA repair tasks
32
34
  dashboard Start local HTTP telemetry and audit dashboard
33
35
  init Scaffold .agent/ config and run onboarding wizard
34
- task create Interactively author and scope a Jules task envelope (--template <name>)
36
+ task create Interactively author and scope a Jules task envelope (--template <name>, --role <name>, --tier fast|complex)
35
37
  task optimize Linter & optimizer for Jules task prompts (--fix, --json, --web)
36
38
  task template List and generate web development task templates (--list, --json)
37
39
  test-gen Scaffold & run automated TDD Red-to-Green test cycle (--run)
@@ -43,9 +45,13 @@ Commands:
43
45
  hydrate [prompt] Prepend active system learnings and baton-pass state to a prompt
44
46
  harvest Harvest failure traces and record/quarantine resolution rules
45
47
  learning add Record a system learning rule into .agent/knowledge/
48
+ evidence <action> Manage cryptographic audit evidence (generate | verify | show)
46
49
  version Output agentctl version
47
50
 
48
51
  Options:
52
+ --role, -r Specify specialist agent role (overseer | bolt | sentinel | janitor)
53
+ --tier Force routing tier when router.enabled (fast | complex) — see .agent/config.yml router:
54
+ --dag Execute queue tasks via DAG dependency resolution
49
55
  --dry-run, -d Simulate action without making API calls or modifying git
50
56
  --mode, -m Gate evaluation mode (working-tree | committed | staged)
51
57
  --repoless Dispatch task in repoless execution mode
@@ -63,7 +69,7 @@ async function main() {
63
69
  }
64
70
 
65
71
  if (command === "version" || command === "--version" || command === "-v") {
66
- console.log("agentctl v0.32.3");
72
+ console.log("agentctl v0.32.5");
67
73
  process.exit(0);
68
74
  }
69
75
 
@@ -81,6 +87,8 @@ async function main() {
81
87
  title: { type: "string", short: "t" },
82
88
  prompt: { type: "string", short: "p" },
83
89
  "prompt-file": { type: "string", short: "f" },
90
+ role: { type: "string", short: "r" },
91
+ tier: { type: "string" },
84
92
  source: { type: "string", short: "s" },
85
93
  branch: { type: "string", short: "b" },
86
94
  repoless: { type: "boolean" },
@@ -109,6 +117,8 @@ async function main() {
109
117
  const task = {
110
118
  title: values.title || "CLI Dispatch Task",
111
119
  prompt: promptContent,
120
+ role: values.role,
121
+ tier: values.tier === "fast" || values.tier === "complex" ? values.tier : undefined,
112
122
  source: values.source,
113
123
  branch: values.branch,
114
124
  repoless: values.repoless,
@@ -131,6 +141,9 @@ async function main() {
131
141
  console.log(`\n✅ Task Dispatched Successfully!`);
132
142
  console.log(` Session ID : ${session.id}`);
133
143
  console.log(` Session URL : ${session.url || "N/A"}`);
144
+ if (session._routeTier) {
145
+ console.log(` Router Tier : ${session._routeTier} (${session._routeReason || "n/a"})`);
146
+ }
134
147
  }
135
148
  process.exit(0);
136
149
  } catch (err) {
@@ -200,17 +213,34 @@ async function main() {
200
213
  }
201
214
 
202
215
  case "queue": {
216
+ const { values } = parseArgs({
217
+ args: args.slice(1),
218
+ options: {
219
+ dag: { type: "boolean" },
220
+ concurrency: { type: "string", short: "c" },
221
+ "dry-run": { type: "boolean", short: "d" },
222
+ json: { type: "boolean", short: "j" },
223
+ },
224
+ allowPositionals: true,
225
+ });
226
+
203
227
  const queueDir = getQueueDir(root);
204
228
  const files = readdirSync(queueDir).filter((f) => isTaskFile(f, queueDir));
205
229
  console.log(`Found ${files.length} queued task(s) in .agent/queue/`);
206
230
  if (files.length > 0) {
207
- const tasks = files.map((f) => ({
208
- id: f,
209
- title: f.replace(/\.md$/, ""),
210
- prompt: readFileSync(join(queueDir, f), "utf-8"),
211
- }));
212
- const results = await run(tasks, { root, config });
213
- console.log(`\nProcessed ${results.length} tasks.`);
231
+ const concurrency = values.concurrency ? Number(values.concurrency) : undefined;
232
+ const results = await run(null, {
233
+ root,
234
+ config,
235
+ dag: values.dag,
236
+ concurrency,
237
+ dryRun: values["dry-run"],
238
+ });
239
+ if (values.json) {
240
+ console.log(JSON.stringify(results, null, 2));
241
+ } else {
242
+ console.log(`\nProcessed ${results.processed || results.results?.length || 0} task(s).`);
243
+ }
214
244
  }
215
245
  process.exit(0);
216
246
  break;
@@ -372,7 +402,11 @@ async function main() {
372
402
  options: {
373
403
  title: { type: "string", short: "t" },
374
404
  prompt: { type: "string", short: "p" },
405
+ role: { type: "string", short: "r" },
406
+ tier: { type: "string" },
375
407
  template: { type: "string" },
408
+ depends: { type: "string" },
409
+ "depends-on": { type: "string" },
376
410
  "verify-cmd": { type: "string", short: "v" },
377
411
  "auto-pr": { type: "boolean" },
378
412
  "require-plan-approval": { type: "boolean" },
@@ -386,7 +420,10 @@ async function main() {
386
420
  const res = await runTaskCreateWizard(root, {
387
421
  title: values.title,
388
422
  prompt: values.prompt,
423
+ role: values.role,
424
+ tier: values.tier,
389
425
  template: values.template,
426
+ dependsOn: values["depends-on"] || values.depends,
390
427
  verifyCmd: values["verify-cmd"],
391
428
  autoPr: values["auto-pr"],
392
429
  requirePlanApproval: values["require-plan-approval"],
@@ -399,6 +436,9 @@ async function main() {
399
436
  console.log(`✅ Task synthesized & queued at ${res.taskFile}`);
400
437
  console.log(` Task ID : ${res.plan.taskId}`);
401
438
  console.log(` Title : ${res.plan.title}`);
439
+ if (res.plan.role) console.log(` Role : ${res.plan.role}`);
440
+ if (res.plan.tier) console.log(` Tier : ${res.plan.tier} (routing override)`);
441
+ if (res.plan.dependsOn && res.plan.dependsOn.length > 0) console.log(` DependsOn: ${res.plan.dependsOn.join(", ")}`);
402
442
  console.log(` Auto-PR : ${res.plan.flags.autoPr}`);
403
443
  }
404
444
  process.exit(0);
@@ -748,6 +788,77 @@ async function main() {
748
788
  process.exit(1);
749
789
  }
750
790
 
791
+ case "evidence": {
792
+ const subAction = args[1] || "show";
793
+ const { planEvidenceGenerate, planEvidenceVerify, planEvidenceShow } = await import("../src/ops/evidence-actions.mjs");
794
+ const { values } = parseArgs({
795
+ args: args.slice(2),
796
+ options: {
797
+ output: { type: "string", short: "o" },
798
+ manifest: { type: "string", short: "m" },
799
+ markdown: { type: "string" },
800
+ json: { type: "boolean", short: "j" },
801
+ },
802
+ allowPositionals: true,
803
+ });
804
+
805
+ if (subAction === "generate" || subAction === "create") {
806
+ const res = planEvidenceGenerate(root, {
807
+ output: values.output,
808
+ markdownOutput: values.markdown,
809
+ });
810
+ if (values.json) {
811
+ console.log(JSON.stringify(res, null, 2));
812
+ } else {
813
+ console.log(`\n🛡️ Cryptographic Evidence Manifest Generated!`);
814
+ console.log(` Manifest ID : ${res.manifest.manifestId}`);
815
+ console.log(` Signature : ${res.manifest.evidenceHash}`);
816
+ console.log(` Location : ${res.manifestPath}`);
817
+ console.log(` Test Files : ${res.manifest.testIntegrity.testFileCount}`);
818
+ console.log(` Tampered : ${res.manifest.testIntegrity.tamperDetected ? "YES (FAILED)" : "NO (VERIFIED)"}\n`);
819
+ }
820
+ process.exit(res.manifest.testIntegrity.tamperDetected ? 1 : 0);
821
+ } else if (subAction === "verify" || subAction === "check") {
822
+ const res = planEvidenceVerify(root, {
823
+ manifest: values.manifest,
824
+ });
825
+ if (values.json) {
826
+ console.log(JSON.stringify(res, null, 2));
827
+ } else {
828
+ if (res.ok) {
829
+ console.log(`\n✅ Evidence Verification PASSED`);
830
+ console.log(` Manifest ID : ${res.manifestId}`);
831
+ console.log(` Signature : ${res.evidenceHash}\n`);
832
+ } else {
833
+ console.error(`\n❌ Evidence Verification FAILED`);
834
+ console.error(` Reason : ${res.reason}`);
835
+ if (res.details) {
836
+ console.error(` Details : ${JSON.stringify(res.details)}\n`);
837
+ }
838
+ }
839
+ }
840
+ process.exit(res.ok ? 0 : 1);
841
+ } else if (subAction === "show" || subAction === "print") {
842
+ const res = planEvidenceShow(root, {
843
+ manifest: values.manifest,
844
+ });
845
+ if (values.json) {
846
+ console.log(JSON.stringify(res, null, 2));
847
+ } else {
848
+ if (res.ok) {
849
+ console.log(`\n${res.markdown}\n`);
850
+ } else {
851
+ console.error(`\n❌ Failed to show evidence: ${res.reason}\n`);
852
+ }
853
+ }
854
+ process.exit(res.ok ? 0 : 1);
855
+ } else {
856
+ console.error(`Unknown evidence subaction: ${subAction}. Use generate | verify | show.`);
857
+ process.exit(1);
858
+ }
859
+ break;
860
+ }
861
+
751
862
  default:
752
863
  console.error(`Unknown command: ${command}`);
753
864
  printHelp();
package/index.mjs CHANGED
@@ -13,6 +13,8 @@ export {
13
13
  matchesGlob,
14
14
  checkScope,
15
15
  scanDiff,
16
+ checkEdgeRuntimeImports,
17
+ checkCrossPackageImports,
16
18
  } from "./src/security.mjs";
17
19
  export { sanitizeUntrustedData, buildAgentEnvelope } from "./src/prompt-guard.mjs";
18
20
  export { isolateMcpStdout, writeMcpFrame } from "./src/mcp.mjs";
@@ -29,7 +31,14 @@ export {
29
31
  ProviderSchemaError,
30
32
  parseRetryAfter,
31
33
  } from "./src/provider.mjs";
32
- export { detectPolyglotStack, resolveWorkspaceBoundary, bootstrapZeroTestRepo } from "./src/stack-detector.mjs";
34
+ export {
35
+ detectPolyglotStack,
36
+ resolveWorkspaceBoundary,
37
+ bootstrapZeroTestRepo,
38
+ findSubprojectRoot,
39
+ detectCrossPackageBoundaryViolations,
40
+ detectCircularDependencies,
41
+ } from "./src/stack-detector.mjs";
33
42
  export {
34
43
  appendLedger,
35
44
  readLedger,
@@ -100,9 +109,24 @@ export { synthesizePrDescription, probeDevServer } from "./src/engine.mjs";
100
109
  // Automated TDD Red-to-Green Harness
101
110
  export { scaffoldTddTest, runTddCycle, TddError } from "./src/ops/tdd-generator.mjs";
102
111
 
103
- // IDE Native MCP Scaffolder
104
- export { scaffoldIdeConfig, IdeScaffoldError } from "./src/ops/ide-scaffold.mjs";
105
-
112
+ // SPORE Memory & System Learnings
113
+ export { recordLearning, loadLearnings, hydratePrompt, harvestFailure, getLearningsPath, getSystemLearningsMdPath } from "./src/memory.mjs";
106
114
 
115
+ // Specialist Role Resolution & DAG Queue Execution
116
+ export { resolveRolePrompt } from "./src/wizard-task.mjs";
117
+ export { executeQueueDag, resolveAffectedTests } from "./src/dag-engine.mjs";
107
118
 
119
+ // IDE Native MCP Scaffolder
120
+ export { scaffoldIdeConfig, IdeScaffoldError } from "./src/ops/ide-scaffold.mjs";
108
121
 
122
+ // Cryptographic Evidence & Test Integrity Manifests
123
+ export {
124
+ computeFileHash,
125
+ computeDirectoryHash,
126
+ generateEvidenceManifest,
127
+ writeEvidenceManifest,
128
+ loadEvidenceManifest,
129
+ verifyEvidenceManifest,
130
+ generateEvidenceMarkdown,
131
+ computeEvidenceHash,
132
+ } from "./src/evidence.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jules-orchestrator-kit",
3
- "version": "0.32.3",
3
+ "version": "0.32.5",
4
4
  "description": "Orchestration kit for running Google Jules autonomous agents.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -70,7 +70,7 @@
70
70
  "author": "FullThrottle83",
71
71
  "license": "MIT",
72
72
  "devDependencies": {
73
- "eslint": "^9.39.5",
74
- "globals": "^17.8.0"
73
+ "eslint": "^10.8.1",
74
+ "globals": "^17.11.0"
75
75
  }
76
76
  }
@@ -22,7 +22,13 @@ export function parseYamlConfig(root = process.cwd()) {
22
22
  if (parsed && (parsed.test_cmd || parsed.build_cmd || parsed.verify)) {
23
23
  return {
24
24
  testCmd: parsed.verify?.test || parsed.test_cmd || "",
25
+ lintCmd: parsed.verify?.lint || parsed.lint_cmd || "",
26
+ fuzzCmd: parsed.verify?.fuzz || parsed.fuzz_cmd || "",
27
+ invariantCmd: parsed.verify?.invariant || parsed.invariant_cmd || "",
28
+ e2eCmd: parsed.verify?.e2e || parsed.e2e_cmd || "",
25
29
  buildCmd: parsed.verify?.build || parsed.build_cmd || "",
30
+ policy: parsed.verify?.policy || { networkAccess: "allow", offline: false },
31
+ stages: parsed.verify?.stages || null,
26
32
  source: configPath.endsWith("jules.yml") ? ".agent/jules.yml" : ".agent/config.yml",
27
33
  };
28
34
  }
@@ -50,8 +56,16 @@ export function detectFrameworkCommands(root = process.cwd()) {
50
56
  const res = detectStack(root);
51
57
  return {
52
58
  testCmd: res.testCmd,
59
+ lintCmd: res.fmtCmd || "",
60
+ fuzzCmd: res.fuzzCmd || "",
61
+ invariantCmd: res.invariantCmd || "",
62
+ e2eCmd: res.e2eCmd || "",
53
63
  buildCmd: res.buildCmd,
54
- source: `package.json (${res.stack})`,
64
+ policy: {
65
+ networkAccess: res.stack === "foundry" ? "forbidden" : "allow",
66
+ offline: res.stack === "foundry",
67
+ },
68
+ source: `${res.triggerFile || "stack"} (${res.stack})`,
55
69
  };
56
70
  }
57
71
 
@@ -10,7 +10,7 @@ import { join } from "node:path";
10
10
  import { tmpdir } from "node:os";
11
11
  import { spawnSync } from "node:child_process";
12
12
  import { classifyRiskTier, RISK_TIERS } from "../src/risk.mjs";
13
- import { changedFiles, git, resolveBase } from "../src/git.mjs";
13
+ import { git, resolveBase } from "../src/git.mjs";
14
14
  import { normalizePath } from "../src/config.mjs";
15
15
 
16
16
  export const EXIT = Object.freeze({
package/src/config.mjs CHANGED
@@ -185,9 +185,23 @@ export function detectPackageManager(root = process.cwd(), pkg = {}) {
185
185
  return "npm";
186
186
  }
187
187
 
188
- import { detectPolyglotStack, resolveWorkspaceBoundary, bootstrapZeroTestRepo } from "./stack-detector.mjs";
189
-
190
- export { detectPolyglotStack, resolveWorkspaceBoundary, bootstrapZeroTestRepo };
188
+ import {
189
+ detectPolyglotStack,
190
+ resolveWorkspaceBoundary,
191
+ bootstrapZeroTestRepo,
192
+ findSubprojectRoot,
193
+ detectCrossPackageBoundaryViolations,
194
+ detectCircularDependencies,
195
+ } from "./stack-detector.mjs";
196
+
197
+ export {
198
+ detectPolyglotStack,
199
+ resolveWorkspaceBoundary,
200
+ bootstrapZeroTestRepo,
201
+ findSubprojectRoot,
202
+ detectCrossPackageBoundaryViolations,
203
+ detectCircularDependencies,
204
+ };
191
205
 
192
206
  /**
193
207
  * Autodetects verification test/build commands across 24+ polyglot tech stacks.
@@ -200,9 +214,19 @@ export function resolveVerify(root = process.cwd(), userVerify = {}) {
200
214
  const s = detectStack(root);
201
215
  return {
202
216
  setup: userVerify.setup ?? s.setupCmd ?? "",
217
+ lint: userVerify.lint ?? s.fmtCmd ?? "",
203
218
  test: userVerify.test ?? s.testCmd ?? "",
219
+ unit: userVerify.unit ?? userVerify.test ?? s.testCmd ?? "",
220
+ fuzz: userVerify.fuzz ?? s.fuzzCmd ?? "",
221
+ invariant: userVerify.invariant ?? s.invariantCmd ?? "",
222
+ e2e: userVerify.e2e ?? s.e2eCmd ?? "",
204
223
  teardown: userVerify.teardown ?? s.teardownCmd ?? "",
205
224
  build: userVerify.build ?? s.buildCmd ?? "",
225
+ policy: {
226
+ networkAccess: userVerify.policy?.networkAccess || (s.stack === "foundry" ? "forbidden" : "allow"),
227
+ offline: userVerify.policy?.offline ?? (s.stack === "foundry"),
228
+ },
229
+ stages: Array.isArray(userVerify.stages) ? userVerify.stages : null,
206
230
  server: userVerify.server ? {
207
231
  command: userVerify.server.command || "",
208
232
  url: userVerify.server.url || "http://localhost:3000",
@@ -259,6 +283,10 @@ export function loadConfig(root = resolveRoot(), explicitPath = null) {
259
283
 
260
284
  const setupCmd = parsed.setup_cmd || parsed.verify?.setup || "";
261
285
  const testCmd = parsed.test_cmd || parsed.verify?.test || "";
286
+ const lintCmd = parsed.lint_cmd || parsed.verify?.lint || "";
287
+ const fuzzCmd = parsed.fuzz_cmd || parsed.verify?.fuzz || "";
288
+ const invariantCmd = parsed.invariant_cmd || parsed.verify?.invariant || "";
289
+ const e2eCmd = parsed.e2e_cmd || parsed.verify?.e2e || "";
262
290
  const teardownCmd = parsed.teardown_cmd || parsed.verify?.teardown || "";
263
291
  const buildCmd = parsed.build_cmd || parsed.verify?.build || "";
264
292
  const verifyTimeoutMs = parsed.verify?.timeoutMs ?? parsed.verify?.timeout_ms ?? 60000;
@@ -289,11 +317,28 @@ export function loadConfig(root = resolveRoot(), explicitPath = null) {
289
317
  tier: activeTier,
290
318
  verify: {
291
319
  setup: setupCmd || autoVerify.setup || "",
320
+ lint: lintCmd || autoVerify.lint || "",
292
321
  test: testCmd || autoVerify.test,
322
+ unit: parsed.verify?.unit || testCmd || autoVerify.unit || autoVerify.test,
323
+ fuzz: fuzzCmd || autoVerify.fuzz || "",
324
+ invariant: invariantCmd || autoVerify.invariant || "",
325
+ e2e: e2eCmd || autoVerify.e2e || "",
293
326
  teardown: teardownCmd || autoVerify.teardown || "",
294
327
  build: buildCmd || autoVerify.build,
328
+ stages: parsed.verify?.stages || autoVerify.stages || null,
329
+ policy: parsed.verify?.policy || autoVerify.policy,
295
330
  timeoutMs: Number.isFinite(Number(verifyTimeoutMs)) ? Number(verifyTimeoutMs) : 60000,
296
331
  },
332
+ evidence: {
333
+ enabled: parsed.evidence?.enabled ?? true,
334
+ strictTestLock: parsed.evidence?.strict_test_lock ?? parsed.evidence?.strictTestLock ?? true,
335
+ },
336
+ router: {
337
+ enabled: parsed.router?.enabled ?? false,
338
+ fast: parsed.router?.fast || "gemini-flash",
339
+ complex: parsed.router?.complex || "",
340
+ threshold: Number.isFinite(Number(parsed.router?.threshold)) ? Number(parsed.router.threshold) : 0,
341
+ },
297
342
  scope: normalizeScope(parsed),
298
343
  limits: {
299
344
  ...DEFAULTS.limits,