jules-orchestrator-kit 0.26.1 β†’ 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,11 +19,11 @@
19
19
  <p align="center">
20
20
  <a href="#-2-sentence-mental-model">πŸ’‘ What is Kit?</a> β€’
21
21
  <a href="#-universal-30-second-quickstart-zero-to-verified-pr">⚑ 30s Quickstart</a> β€’
22
+ <a href="#-triage-guidelines-when-to-use-vs-when-not-to-use">🎯 Triage Guidelines</a> β€’
22
23
  <a href="#-feature-comparison-matrix">πŸ“Š Comparison Matrix</a> β€’
23
24
  <a href="#-system-architecture--visual-diagrams">πŸ›οΈ Architecture</a> β€’
24
25
  <a href="#-cli-command-reference-agentctl">πŸ› οΈ CLI Reference</a> β€’
25
- <a href="#-v027-next-gen-feature-roadmap">πŸ—ΊοΈ Roadmap</a> β€’
26
- <a href="./docs/UNIVERSAL_POLYGLOT_ARCHITECTURE.md">πŸ“– Polyglot Spec</a>
26
+ <a href="#-v027-next-gen-feature-roadmap">πŸ—ΊοΈ Roadmap</a>
27
27
  </p>
28
28
 
29
29
  </div>
@@ -52,6 +52,25 @@ Autonomous coding agents can write software at 100Γ— human speedβ€”but unconstra
52
52
 
53
53
  ---
54
54
 
55
+ ## 🎯 Triage Guidelines: When to Use vs. When NOT to Use
56
+
57
+ To ensure maximum merge success, dispatch tasks according to our deterministic triage boundaries:
58
+
59
+ ### 🟒 Ideal Tasks for Autonomous Swarms
60
+ - βœ… **Scoped Code Changes & Bug Fixes:** Well-defined objectives mechanically verifiable via unit tests (`npm test`, `pytest`, `cargo test`, `dotnet test`).
61
+ - βœ… **Type & Linter Migrations:** TypeScript strict mode fixes, PHP 8.3 type hint additions, or Python MyPy type annotation passes.
62
+ - βœ… **Dependency Bumps & Security Audits:** Remediating CVEs in lockfiles (`package.json`, `Cargo.toml`, `composer.json`) with hermetic test verification.
63
+ - βœ… **Refactoring Legacy Codebases:** Modularizing backend routes, API controllers, or database query layers.
64
+ - βœ… **Visual & E2E Testing (via Playwright):** UI changes paired with automated headless Playwright snapshot tests (`npx playwright test`).
65
+
66
+ ### πŸ”΄ When NOT to Use (Out of Scope)
67
+ - ❌ **Unverifiable Visual UI Tweaks:** Pixel-perfect CSS/Tailwind adjustments lacking automated visual regression tests (agents cannot "see" raw browser output without Playwright).
68
+ - ❌ **Closed Proprietary Platforms Without CLI:** Systems lacking local CLI tools or git repositories (e.g., Salesforce, Webflow, closed SAP backends).
69
+ - ❌ **Unmocked Live Cloud Systems:** Code requiring live connections to 10+ external cloud APIs without local emulators or mocks.
70
+ - ❌ **Protected Infrastructure Paths:** Direct edits to `.github/workflows/`, production deployment keys, or agent security gate rules (enforced fail-closed by `Agent Scope Guard`).
71
+
72
+ ---
73
+
55
74
  ## πŸ“Š Feature Comparison Matrix
56
75
 
57
76
  | Dimension | Raw Agent Execution (No Orchestrator) | Standard CI/CD Pipelines | `jules-orchestrator-kit` (v0.26+) |
@@ -100,7 +119,14 @@ npx jules-orchestrator-kit dispatch --title "Implement OrderService caching" \
100
119
  --prompt "Add IMemoryCache caching to OrderService.cs with xUnit coverage."
101
120
  ```
102
121
 
103
- ### 5️⃣ Polyglot Monorepo (FastAPI + React + Rust CLI)
122
+ ### 5️⃣ UI / Frontend E2E (Playwright)
123
+ ```bash
124
+ # Dispatch frontend task verified via headless Playwright E2E tests
125
+ npx jules-orchestrator-kit dispatch --title "Add Dark Mode Toggle Component" \
126
+ --prompt "Create ThemeToggle component in src/components/ThemeToggle.tsx and verify via npx playwright test."
127
+ ```
128
+
129
+ ### 6️⃣ Polyglot Monorepo (FastAPI + React + Rust CLI)
104
130
  ```bash
105
131
  # Run a parallel worktree swarm; changed files automatically route to scoped subproject tests
106
132
  npx jules-orchestrator-kit swarm
@@ -189,7 +215,7 @@ In monorepos containing multiple languages, `resolveWorkspaceBoundary(changedFil
189
215
  | `clean` | `agentctl clean` | Prunes stale git worktrees, lockfiles, and temporary ledgers. | `0` (Clean) |
190
216
  | `init` | `agentctl init` | Scaffolds `.agent/` directory structure and default `.agent/config.yml`. | `0` (Created) |
191
217
  | `mcp` | `agentctl mcp` | Starts stdio Model Context Protocol (MCP) server for tool integration. | `0` / Stdio stream |
192
- | `version` | `agentctl version` | Outputs orchestrator kit semantic version (`v0.26.0`). | `0` |
218
+ | `version` | `agentctl version` | Outputs orchestrator kit semantic version (`v0.26.1`). | `0` |
193
219
 
194
220
  ---
195
221
 
package/bin/agentctl.mjs CHANGED
@@ -14,7 +14,7 @@ const command = args[0];
14
14
 
15
15
  function printHelp() {
16
16
  console.log(`
17
- πŸš€ agentctl v0.26.1 β€” Universal Agent Orchestrator & Safety Gatekeeper
17
+ πŸš€ agentctl v0.27.0 β€” Universal Agent Orchestrator & Safety Gatekeeper
18
18
 
19
19
  Usage: agentctl <command> [options]
20
20
 
@@ -28,6 +28,8 @@ Commands:
28
28
  lock <action> Manage mutex locks (acquire | release | status | cleanup)
29
29
  doctor Run system diagnostics and stack resolution checks
30
30
  bootstrap Bootstrap zero-test repository with verification oracle
31
+ review-repair Parse PR review comments and synthesize OODA repair tasks
32
+ dashboard Start local HTTP telemetry and audit dashboard
31
33
  init Scaffold .agent/ directory and config.yml
32
34
  version Output agentctl version
33
35
 
@@ -45,7 +47,7 @@ async function main() {
45
47
  }
46
48
 
47
49
  if (command === "version" || command === "--version" || command === "-v") {
48
- console.log("agentctl v0.26.1");
50
+ console.log("agentctl v0.27.0");
49
51
  process.exit(0);
50
52
  }
51
53
 
@@ -263,6 +265,31 @@ async function main() {
263
265
  break;
264
266
  }
265
267
 
268
+ case "review-repair": {
269
+ const fileArg = args[1];
270
+ if (!fileArg || !existsSync(fileArg)) {
271
+ console.error("Error: Please provide a valid JSON file containing PR review comments.");
272
+ process.exit(1);
273
+ }
274
+ const { parseReviewComments, createReviewRepairTask } = await import("../src/review-repair.mjs");
275
+ const raw = readFileSync(fileArg, "utf-8");
276
+ const comments = parseReviewComments(raw);
277
+ console.log(`βœ… Parsed ${comments.length} actionable review comment(s).`);
278
+ for (const c of comments) {
279
+ const task = createReviewRepairTask(c);
280
+ console.log(` Task: ${task.title} (Author: @${c.author}, Line: ${c.line || "N/A"})`);
281
+ }
282
+ process.exit(0);
283
+ break;
284
+ }
285
+
286
+ case "dashboard": {
287
+ const port = Number(args[1] || 4100);
288
+ const { startDashboardServer } = await import("../src/dashboard.mjs");
289
+ startDashboardServer(port, root);
290
+ break;
291
+ }
292
+
266
293
  case "init": {
267
294
  const agentDir = join(root, ".agent");
268
295
  ensureDir(agentDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jules-orchestrator-kit",
3
- "version": "0.26.1",
3
+ "version": "0.27.0",
4
4
  "description": "Orchestration kit for running Google Jules autonomous agents.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Zero-Dependency Local Telemetry & Audit HTTP Dashboard for jules-orchestrator-kit (v0.27.0).
3
+ * Uses node:http to serve real-time HTML/CSS dark mode visualizer and REST JSON endpoints.
4
+ */
5
+
6
+ import http from "node:http";
7
+ import { readTelemetry, verifyTelemetryIntegrity } from "./telemetry.mjs";
8
+ import { readVerifyRuns, flakyVerdict } from "./flaky-ledger.mjs";
9
+ import { lockStatus } from "./state.mjs";
10
+
11
+ export function getDashboardHtml(root = process.cwd()) {
12
+ return `<!DOCTYPE html>
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="UTF-8">
16
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
17
+ <title>jules-orchestrator-kit Dashboard</title>
18
+ <style>
19
+ body { background-color: #0d1117; color: #c9d1d9; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin: 0; padding: 24px; }
20
+ h1 { color: #58a6ff; font-size: 24px; margin-bottom: 8px; }
21
+ .subtitle { color: #8b949e; font-size: 14px; margin-bottom: 24px; }
22
+ .grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; margin-bottom: 24px; }
23
+ .card { background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 16px; }
24
+ .card h2 { font-size: 16px; color: #f0f6fc; margin-top: 0; border-bottom: 1px solid #21262d; padding-bottom: 8px; }
25
+ table { width: 100%; border-collapse: collapse; font-size: 13px; }
26
+ th, td { text-align: left; padding: 8px; border-bottom: 1px solid #21262d; }
27
+ th { color: #8b949e; }
28
+ .badge { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 12px; font-weight: 600; }
29
+ .badge-success { background: rgba(46, 160, 67, 0.15); color: #3fb950; border: 1px solid rgba(46, 160, 67, 0.4); }
30
+ .badge-warn { background: rgba(210, 153, 34, 0.15); color: #d29922; border: 1px solid rgba(210, 153, 34, 0.4); }
31
+ code { font-family: monospace; background: #21262d; padding: 2px 4px; border-radius: 4px; }
32
+ </style>
33
+ </head>
34
+ <body>
35
+ <h1>πŸš€ jules-orchestrator-kit Dashboard (v0.27.0)</h1>
36
+ <div class="subtitle">Repository: <code>${root}</code></div>
37
+
38
+ <div class="grid">
39
+ <div class="card">
40
+ <h2>πŸ›‘οΈ SHA-256 Telemetry Integrity</h2>
41
+ <div id="telemetry-status">Loading...</div>
42
+ </div>
43
+ <div class="card">
44
+ <h2>βš–οΈ Wilson-Score Flaky Ledger</h2>
45
+ <div id="flaky-status">Loading...</div>
46
+ </div>
47
+ <div class="card">
48
+ <h2>πŸ”’ Active VFS Mutex Locks</h2>
49
+ <div id="locks-status">Loading...</div>
50
+ </div>
51
+ </div>
52
+
53
+ <script>
54
+ async function updateDashboard() {
55
+ try {
56
+ const [telRes, flakyRes, locksRes] = await Promise.all([
57
+ fetch('/api/telemetry?limit=10').then(r => r.json()),
58
+ fetch('/api/flaky').then(r => r.json()),
59
+ fetch('/api/locks').then(r => r.json())
60
+ ]);
61
+
62
+ document.getElementById('telemetry-status').innerHTML = telRes.integrity?.valid
63
+ ? '<span class="badge badge-success">βœ“ SHA-256 Hash Chain Valid</span> <p>Events logged: ' + telRes.events.length + '</p>'
64
+ : '<span class="badge badge-warn">⚠ Check Telemetry Logs</span>';
65
+
66
+ document.getElementById('flaky-status').innerHTML = '<p>Verdict: <code>' + (flakyRes.verdict?.action || 'CLEAN') + '</code></p><p>Tracked test runs: ' + flakyRes.count + '</p>';
67
+ document.getElementById('locks-status').innerHTML = '<p>Active VFS Locks: <code>' + locksRes.count + '</code></p>';
68
+ } catch (err) {
69
+ console.error("Dashboard update failed:", err);
70
+ }
71
+ }
72
+ updateDashboard();
73
+ setInterval(updateDashboard, 5000);
74
+ </script>
75
+ </body>
76
+ </html>`;
77
+ }
78
+
79
+ export function createDashboardServer({ root = process.cwd(), port: _port = 4100 } = {}) {
80
+ return http.createServer((req, res) => {
81
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
82
+ res.setHeader("Access-Control-Allow-Origin", "*");
83
+ res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
84
+
85
+ if (req.method === "OPTIONS") {
86
+ res.writeHead(204);
87
+ return res.end();
88
+ }
89
+
90
+ if (url.pathname === "/" || url.pathname === "/index.html") {
91
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
92
+ return res.end(getDashboardHtml(root));
93
+ }
94
+
95
+ if (url.pathname === "/api/status") {
96
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
97
+ return res.end(JSON.stringify({ ok: true, version: "0.27.0", root, ts: new Date().toISOString() }));
98
+ }
99
+
100
+ if (url.pathname === "/api/telemetry") {
101
+ const limit = Number(url.searchParams.get("limit") || "50");
102
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
103
+ return res.end(
104
+ JSON.stringify({
105
+ ok: true,
106
+ count: limit,
107
+ integrity: verifyTelemetryIntegrity(root),
108
+ events: readTelemetry(root, limit),
109
+ })
110
+ );
111
+ }
112
+
113
+ if (url.pathname === "/api/flaky") {
114
+ const runs = readVerifyRuns(root);
115
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
116
+ return res.end(JSON.stringify({ ok: true, count: runs.length, verdict: flakyVerdict(runs), runs }));
117
+ }
118
+
119
+ if (url.pathname === "/api/locks") {
120
+ const locks = lockStatus(root);
121
+ res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
122
+ return res.end(JSON.stringify({ ok: true, count: locks.length, locks }));
123
+ }
124
+
125
+ res.writeHead(404, { "Content-Type": "application/json; charset=utf-8" });
126
+ res.end(JSON.stringify({ ok: false, error: "Not Found", path: url.pathname }));
127
+ });
128
+ }
129
+
130
+ export function startDashboardServer(port = 4100, root = process.cwd()) {
131
+ const server = createDashboardServer({ root, port });
132
+ server.listen(port, "0.0.0.0", () => {
133
+ console.log(`πŸš€ jules-orchestrator-kit Dashboard running at http://localhost:${port}`);
134
+ });
135
+ return server;
136
+ }
package/src/provider.mjs CHANGED
@@ -218,7 +218,6 @@ export function createProvider(spec = "jules", config = {}) {
218
218
  const rawArgs = providerSpec.args || (providerSpec.command ? providerSpec.command.split(" ").slice(1) : []);
219
219
  const command = providerSpec.command ? providerSpec.command.split(" ")[0] : "claude";
220
220
 
221
- // CRITICAL H-a FIX: Filter out '{prompt}' argument if prompt is passed via stdin
222
221
  const filteredArgs = providerSpec.promptViaStdin
223
222
  ? rawArgs.filter((arg) => !arg.includes("{prompt}"))
224
223
  : rawArgs;
@@ -228,7 +227,7 @@ export function createProvider(spec = "jules", config = {}) {
228
227
  const res = spawnSync(command, processedArgs, {
229
228
  cwd: config._root || process.cwd(),
230
229
  encoding: "utf-8",
231
- shell: false, // CRITICAL: Ban shell execution to eliminate CWE-77
230
+ shell: false,
232
231
  input: providerSpec.promptViaStdin ? data.prompt : undefined,
233
232
  timeout: providerSpec.timeoutMs || 900000,
234
233
  maxBuffer: 32 * 1024 * 1024,
@@ -259,3 +258,44 @@ export function createProvider(spec = "jules", config = {}) {
259
258
  },
260
259
  };
261
260
  }
261
+
262
+ /**
263
+ * Creates a failover provider router that attempts dispatches sequentially across an ordered array of providers.
264
+ * Intercepts rate limits (429) and 5xx unavailability, logging telemetry events before falling over.
265
+ */
266
+ export function createFailoverProvider(providers = ["jules"], config = {}) {
267
+ const providerList = (Array.isArray(providers) && providers.length > 0 ? providers : ["jules"]).map((spec) =>
268
+ spec && typeof spec === "object" && typeof spec.dispatch === "function" ? spec : createProvider(spec, config)
269
+ );
270
+
271
+ return {
272
+ name: `failover:${providerList.map((p) => p.name).join("->")}`,
273
+
274
+ async dispatch(task, ctx = {}) {
275
+ const errors = [];
276
+ for (let i = 0; i < providerList.length; i++) {
277
+ const provider = providerList[i];
278
+ try {
279
+ const res = await provider.dispatch(task, ctx);
280
+ return { ...res, _routedProvider: provider.name, _failoverAttempts: i };
281
+ } catch (err) {
282
+ errors.push({ provider: provider.name, error: err });
283
+ const isRecoverable =
284
+ err instanceof ProviderRateLimitError ||
285
+ err instanceof ProviderUnavailableError ||
286
+ (err.status && err.status >= 500 && err.status < 600) ||
287
+ err.status === 429;
288
+
289
+ if (i === providerList.length - 1 || !isRecoverable) {
290
+ err._failoverErrors = errors;
291
+ throw err;
292
+ }
293
+ }
294
+ }
295
+ },
296
+
297
+ validate() {
298
+ return providerList.every((p) => p.validate());
299
+ },
300
+ };
301
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * PR Review Auto-Remediation Engine for jules-orchestrator-kit (v0.27.0).
3
+ * Parses GitHub PR review comments, filters conversational praise/noise,
4
+ * and synthesizes actionable OODA repair task envelopes.
5
+ */
6
+
7
+ export function parseReviewComments(input) {
8
+ let comments = typeof input === "string" ? (() => { try { return JSON.parse(input); } catch { return []; } })() : input;
9
+ comments = Array.isArray(comments) ? comments : comments?.comments || comments?.reviews || [comments];
10
+
11
+ const actionable = [];
12
+ let idx = 0;
13
+ for (const c of comments) {
14
+ if (!c || typeof c !== "object") continue;
15
+
16
+ const state = String(c.state || c.status || "").toUpperCase();
17
+ if (state === "APPROVED" || state === "DISMISSED" || c.isResolved === true || c.resolved === true) continue;
18
+
19
+ const body = String(c.body || c.comment || "").trim();
20
+ if (body.length < 5) continue;
21
+
22
+ const praiseRegex = /^(?:lgtm|looks good(?: to me)?|nice|awesome|thanks|thank you|great work|approved|ship it)[\s.!]*$/i;
23
+ if (praiseRegex.test(body)) continue;
24
+
25
+ idx++;
26
+ const path = c.path || c.file || null;
27
+ const line = c.line || c.original_line || null;
28
+ const author = c.user?.login || c.author || "reviewer";
29
+
30
+ actionable.push({
31
+ id: String(c.id || `review-${idx}`),
32
+ path,
33
+ line: line ? Number(line) : null,
34
+ author,
35
+ body,
36
+ actionable: true,
37
+ prompt: `Fix PR review comment by @${author} on ${path || "code"}${line ? ` line ${line}` : ""}:\n\n"${body}"\n\nEnsure all unit tests and safety gates pass cleanly after applying fix.`,
38
+ });
39
+ }
40
+
41
+ return actionable;
42
+ }
43
+
44
+ export function createReviewRepairTask(comment, baseBranch = "main") {
45
+ return {
46
+ id: `repair-${comment.id}`,
47
+ title: `PR Review Repair: ${comment.path || "code"} (${comment.id})`,
48
+ prompt: comment.prompt || `Fix review feedback: ${comment.body}`,
49
+ baseBranch,
50
+ targetFiles: comment.path ? [comment.path] : [],
51
+ metadata: { source: "pr-review", commentId: comment.id, author: comment.author, line: comment.line },
52
+ };
53
+ }