jules-orchestrator-kit 0.72.1 → 0.72.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 +8 -0
- package/README.md +1 -0
- package/ROADMAP_V1.md +8 -3
- package/bin/agentctl.mjs +7 -4
- package/package.json +1 -1
- package/src/git.mjs +32 -23
- package/src/stack-detector.mjs +18 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ All notable changes to this project 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
|
+
## [0.72.2] - 2026-09-08
|
|
9
|
+
*A test suite that writes to stderr did not write silence.*
|
|
10
|
+
|
|
11
|
+
Fixes stream preservation in child process execution, stack-detection build defaults, and CLI flag parity uncovered during Bun + TypeScript trial execution:
|
|
12
|
+
- **Child Process Stream Fidelity & Stderr Preservation (`src/git.mjs`)**: `runCmd()` used `execSync`/`execFileSync` which hardcoded `stderr: ""` on zero exit status. Test runners that print progress and summary statistics to `stderr` (notably `bun test`) had their results silenced, causing `parseCollectedTests` to report unverified test counts. `runCmd()` now invokes `spawnSync` natively, capturing both `stdout` and `stderr` streams regardless of exit status.
|
|
13
|
+
- **Conditional Build Command Resolution (`src/stack-detector.mjs`)**: Bun and Deno detectors previously assigned `buildCmd: "bun run build"` and `buildCmd: "deno task build"` unconditionally upon discovering `bunfig.toml` or `deno.json`. In pure zero-dependency projects lacking a `build` script/task, `agentctl gate` failed in the build stage. `detectPolyglotStack()` now checks `package.json` (or `deno.json`) for explicit build scripts before populating `buildCmd`.
|
|
14
|
+
- **CLI Ergonomics & Alias Parity (`bin/agentctl.mjs`)**: Added `--verify` as a first-class alias for `--verify-cmd` across `agentctl task create`, `task template`, and `task optimize`.
|
|
15
|
+
|
|
8
16
|
## [0.72.1] - 2026-09-08
|
|
9
17
|
*A diff cannot judge what it cannot see.*
|
|
10
18
|
|
package/README.md
CHANGED
|
@@ -448,6 +448,7 @@ const result = await fast.dispatch({ prompt: "Fix a typo." }, { root: process.cw
|
|
|
448
448
|
|
|
449
449
|
| Feature | Module / Command | Architectural Description | Status |
|
|
450
450
|
| :--- | :--- | :--- | :---: |
|
|
451
|
+
| **Child Streams & Polyglot Build Detection** | `src/git.mjs`, `src/stack-detector.mjs`, `bin/agentctl.mjs` | Native `spawnSync` execution in `runCmd()` preserving stderr stream on status 0 (supporting Bun test output), conditional `buildCmd` resolution for Bun/Deno scripts, and `--verify` alias parity. | **v0.72.2** *(Shipped)* |
|
|
451
452
|
| **Staged Diff Fidelity & Indentation Dead Guards** | `src/git.mjs`, `src/security.mjs` | Query cached index in staged mode (`git diff --cached <base>`), detect literal falsity dead guards (`if False:`, `if (false)`, `if 0:`), and support indentation-aware block traversal for Python test suites. | **v0.72.1** *(Shipped)* |
|
|
452
453
|
| **Cold-Start Hardened Kernel & Tamper Defense** | `src/config.mjs`, `src/engine.mjs`, `src/git.mjs`, `src/security.mjs` | Full remediation of 22 cold-start audit findings (F01–F22): authoritative base policy resolution, ephemeral snapshot worktree isolation, canonical root test tamper guard, conditional assertion defense, multi-target Cargo test aggregation, Python src-layout injection, and complete repository uninstall documentation. | **v0.72.0** *(Shipped)* |
|
|
453
454
|
| **Silence Is Not A Suite & Scaffolding Linter Fixes** | `src/ops/test-collection.mjs`, `src/wizard-init.mjs`, `src/config.mjs` | Reject zero-output test suite commands, quote-aware YAML parser with scalar emission, test de-registration detection (`TEST_DEREGISTERED`), and active waiver telemetry banner. | **v0.71.0** *(Shipped)* |
|
package/ROADMAP_V1.md
CHANGED
|
@@ -11,13 +11,18 @@ The **jules-orchestrator-kit** is the zero-dependency safety gatekeeper and self
|
|
|
11
11
|
## 📌 Release Milestones Overview
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
-
v0.72.
|
|
15
|
-
(
|
|
14
|
+
v0.72.2 (Current Stable) ──► v0.73.0 (Distributed Swarms & Leases) ──► v1.0.0 (Production Hardened Kernel)
|
|
15
|
+
(Child Streams & Bun/Deno) (Multi-Agent DAG & Resource Locks) (Enterprise Telemetry & SLA)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
## ✅ Shipped Milestones (v0.20.0 – v0.72.
|
|
20
|
+
## ✅ Shipped Milestones (v0.20.0 – v0.72.2)
|
|
21
|
+
|
|
22
|
+
### v0.72.2: Child Process Stream Fidelity & Polyglot Build Detection
|
|
23
|
+
- [x] **Child Process Stream Fidelity (`src/git.mjs`)** — `runCmd()` invokes native `spawnSync`, preserving both `stdout` and `stderr` streams on exit 0 so test runners emitting summaries to stderr (`bun test`) are fully recognized by `parseCollectedTests`.
|
|
24
|
+
- [x] **Conditional Polyglot Build Resolution (`src/stack-detector.mjs`)** — Bun and Deno projects without declared build scripts default to `buildCmd: ""`, preventing `agentctl gate` false reds during pure script verification.
|
|
25
|
+
- [x] **CLI Flag Parity (`bin/agentctl.mjs`)** — `--verify` supported alongside `--verify-cmd` across `agentctl task create`, `task template`, and `task optimize`.
|
|
21
26
|
|
|
22
27
|
### v0.72.1: Staged Mode Diff Fidelity & Indentation-Aware Tamper Defense
|
|
23
28
|
- [x] **Staged Mode Diff Fidelity (`src/git.mjs`)** — `diffText` in staged mode queries `git diff --cached <base>`, ensuring staged additions on feature branches are visible to secret and tamper scanners.
|
package/bin/agentctl.mjs
CHANGED
|
@@ -1854,6 +1854,7 @@ async function main() {
|
|
|
1854
1854
|
template: { type: "string" },
|
|
1855
1855
|
depends: { type: "string" },
|
|
1856
1856
|
"depends-on": { type: "string" },
|
|
1857
|
+
verify: { type: "string" },
|
|
1857
1858
|
"verify-cmd": { type: "string", short: "v" },
|
|
1858
1859
|
"auto-pr": { type: "boolean" },
|
|
1859
1860
|
"require-plan-approval": { type: "boolean" },
|
|
@@ -1886,7 +1887,7 @@ async function main() {
|
|
|
1886
1887
|
tier: values.tier,
|
|
1887
1888
|
template: values.template,
|
|
1888
1889
|
dependsOn: values["depends-on"] || values.depends,
|
|
1889
|
-
verifyCmd: values["verify-cmd"],
|
|
1890
|
+
verifyCmd: values["verify-cmd"] || values.verify,
|
|
1890
1891
|
autoPr: values["auto-pr"],
|
|
1891
1892
|
requirePlanApproval: values["require-plan-approval"],
|
|
1892
1893
|
repoless: values.repoless,
|
|
@@ -1916,6 +1917,7 @@ async function main() {
|
|
|
1916
1917
|
options: {
|
|
1917
1918
|
list: { type: "boolean", short: "l" },
|
|
1918
1919
|
json: { type: "boolean", short: "j" },
|
|
1920
|
+
verify: { type: "string" },
|
|
1919
1921
|
"verify-cmd": { type: "string", short: "v" },
|
|
1920
1922
|
"dry-run": { type: "boolean", short: "d" },
|
|
1921
1923
|
},
|
|
@@ -1949,7 +1951,7 @@ async function main() {
|
|
|
1949
1951
|
process.exit(1);
|
|
1950
1952
|
}
|
|
1951
1953
|
|
|
1952
|
-
const envelope = synthesizeWebEnvelope(templateName, {}, { verifyCmd: values["verify-cmd"] });
|
|
1954
|
+
const envelope = synthesizeWebEnvelope(templateName, {}, { verifyCmd: values["verify-cmd"] || values.verify });
|
|
1953
1955
|
if (values.json) {
|
|
1954
1956
|
console.log(JSON.stringify({ ok: true, ...envelope }, null, 2));
|
|
1955
1957
|
} else {
|
|
@@ -1971,6 +1973,7 @@ async function main() {
|
|
|
1971
1973
|
dir: { type: "string", short: "d" },
|
|
1972
1974
|
web: { type: "boolean", short: "w" },
|
|
1973
1975
|
json: { type: "boolean", short: "j" },
|
|
1976
|
+
verify: { type: "string" },
|
|
1974
1977
|
"verify-cmd": { type: "string", short: "v" },
|
|
1975
1978
|
"dry-run": { type: "boolean" },
|
|
1976
1979
|
},
|
|
@@ -1982,7 +1985,7 @@ async function main() {
|
|
|
1982
1985
|
const promptText = resolvePromptInput(values, positionals);
|
|
1983
1986
|
|
|
1984
1987
|
if (values.fix) {
|
|
1985
|
-
const opt = optimizeTaskPrompt(promptText, { rootDir: targetDir, verifyCmd: values["verify-cmd"], web: values.web });
|
|
1988
|
+
const opt = optimizeTaskPrompt(promptText, { rootDir: targetDir, verifyCmd: values["verify-cmd"] || values.verify, web: values.web });
|
|
1986
1989
|
if (values.json) {
|
|
1987
1990
|
console.log(JSON.stringify(opt, null, 2));
|
|
1988
1991
|
} else {
|
|
@@ -1991,7 +1994,7 @@ async function main() {
|
|
|
1991
1994
|
process.exit(0);
|
|
1992
1995
|
}
|
|
1993
1996
|
|
|
1994
|
-
const analysis = scorePromptFalsifiability(promptText, { rootDir: targetDir, verifyCmd: values["verify-cmd"] });
|
|
1997
|
+
const analysis = scorePromptFalsifiability(promptText, { rootDir: targetDir, verifyCmd: values["verify-cmd"] || values.verify });
|
|
1995
1998
|
if (values.json) {
|
|
1996
1999
|
console.log(JSON.stringify(analysis, null, 2));
|
|
1997
2000
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jules-orchestrator-kit",
|
|
3
|
-
"version": "0.72.
|
|
3
|
+
"version": "0.72.2",
|
|
4
4
|
"description": "Zero-dependency safety gatekeeper, test oracle generator, and multi-agent coordination protocol for autonomous coding agents — Google Jules, Claude Code, Codex and Gemini CLI.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
package/src/git.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execFileSync,
|
|
1
|
+
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
2
|
import { readFileSync, existsSync, statSync, lstatSync, readlinkSync, mkdtempSync, rmSync, symlinkSync } from "node:fs";
|
|
3
3
|
import { join, delimiter } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
@@ -216,16 +216,17 @@ export function runCmd(command, opts = {}) {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
try {
|
|
219
|
-
const
|
|
220
|
-
?
|
|
219
|
+
const ret = useShell
|
|
220
|
+
? spawnSync(shellCmd, {
|
|
221
221
|
cwd,
|
|
222
222
|
encoding: "utf-8",
|
|
223
|
+
shell: true,
|
|
223
224
|
stdio: ["ignore", "pipe", "pipe"],
|
|
224
225
|
env: childEnv,
|
|
225
226
|
timeout,
|
|
226
227
|
maxBuffer,
|
|
227
228
|
})
|
|
228
|
-
:
|
|
229
|
+
: spawnSync(winSpawn ? winSpawn.file : binary, winSpawn ? winSpawn.args : args, {
|
|
229
230
|
cwd,
|
|
230
231
|
encoding: "utf-8",
|
|
231
232
|
shell: winShim,
|
|
@@ -236,20 +237,14 @@ export function runCmd(command, opts = {}) {
|
|
|
236
237
|
maxBuffer,
|
|
237
238
|
});
|
|
238
239
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const isTimeout = err.code === "ETIMEDOUT" || (err.signal === "SIGTERM" && err.killed);
|
|
242
|
-
const isNobufs = err.code === "ENOBUFS" || (err.message && err.message.includes("maxBuffer"));
|
|
240
|
+
const isTimeout = Boolean(ret.error && (ret.error.code === "ETIMEDOUT" || (ret.signal === "SIGTERM" && ret.error.killed)));
|
|
241
|
+
const isNobufs = Boolean(ret.error && (ret.error.code === "ENOBUFS" || (ret.error.message && ret.error.message.includes("maxBuffer"))));
|
|
243
242
|
|
|
244
|
-
const status =
|
|
245
|
-
let stdout = (
|
|
246
|
-
let stderr = (
|
|
243
|
+
const status = typeof ret.status === "number" ? ret.status : (isTimeout ? 124 : 1);
|
|
244
|
+
let stdout = (ret.stdout || "").toString().trim();
|
|
245
|
+
let stderr = (ret.stderr || (ret.error && ret.error.message) || "").toString().trim();
|
|
247
246
|
|
|
248
247
|
if (isTimeout) {
|
|
249
|
-
// Node's own message for this is `spawnSync sh ETIMEDOUT`, which already
|
|
250
|
-
// contains the token the old guard tested for — so the explanation was
|
|
251
|
-
// skipped exactly when it was needed, and the user was left with five
|
|
252
|
-
// words that name neither the limit nor the way to raise it.
|
|
253
248
|
stderr =
|
|
254
249
|
`Command execution timed out after ${timeout}ms (ETIMEDOUT). ` +
|
|
255
250
|
`The command was killed, not failed: raise verify.timeout_ms in ` +
|
|
@@ -260,17 +255,31 @@ export function runCmd(command, opts = {}) {
|
|
|
260
255
|
stderr = `Command output buffer exceeded limit of ${maxBuffer} bytes (ENOBUFS)${stderr ? "\n" + stderr : ""}`;
|
|
261
256
|
}
|
|
262
257
|
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
258
|
+
if (ret.error || status !== 0) {
|
|
259
|
+
if (opts.ignoreError) {
|
|
260
|
+
return { status, stdout, stderr };
|
|
261
|
+
}
|
|
266
262
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
263
|
+
if (isTimeout) {
|
|
264
|
+
throw new GateError(`Command execution timed out (ETIMEDOUT): ${useShell ? shellCmd : binary}`, { code: status });
|
|
265
|
+
}
|
|
266
|
+
if (isNobufs) {
|
|
267
|
+
throw new GateError(`Command output buffer exceeded limit (ENOBUFS): ${useShell ? shellCmd : binary}`, { code: status });
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const err = ret.error || new Error(`Command failed with status ${status}: ${useShell ? shellCmd : binary}`);
|
|
271
|
+
err.status = status;
|
|
272
|
+
err.code = status;
|
|
273
|
+
err.stdout = stdout;
|
|
274
|
+
err.stderr = stderr;
|
|
275
|
+
throw err;
|
|
272
276
|
}
|
|
273
277
|
|
|
278
|
+
return { status: 0, stdout, stderr };
|
|
279
|
+
} catch (err) {
|
|
280
|
+
if (opts.ignoreError && !(err instanceof GateError)) {
|
|
281
|
+
return { status: err.status || 1, stdout: (err.stdout || "").toString().trim(), stderr: (err.stderr || err.message || "").toString().trim() };
|
|
282
|
+
}
|
|
274
283
|
throw err;
|
|
275
284
|
}
|
|
276
285
|
}
|
package/src/stack-detector.mjs
CHANGED
|
@@ -475,11 +475,27 @@ export function detectPolyglotStack(projectRoot = process.cwd()) {
|
|
|
475
475
|
: existsSync(join(projectRoot, "bun.lockb"))
|
|
476
476
|
? "bun.lockb"
|
|
477
477
|
: "bun.lock";
|
|
478
|
-
|
|
478
|
+
let buildCmd = "";
|
|
479
|
+
if (existsSync(join(projectRoot, "package.json"))) {
|
|
480
|
+
try {
|
|
481
|
+
const pkg = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf-8"));
|
|
482
|
+
if (pkg && typeof pkg === "object" && pkg.scripts && pkg.scripts.build) {
|
|
483
|
+
buildCmd = "bun run build";
|
|
484
|
+
}
|
|
485
|
+
} catch (_) {}
|
|
486
|
+
}
|
|
487
|
+
return { ...container, stack: "bun", setupCmd, testCmd: "bun test", buildCmd, triggerFile };
|
|
479
488
|
}
|
|
480
489
|
if (existsSync(join(projectRoot, "deno.json")) || existsSync(join(projectRoot, "deno.jsonc"))) {
|
|
481
490
|
const triggerFile = existsSync(join(projectRoot, "deno.json")) ? "deno.json" : "deno.jsonc";
|
|
482
|
-
|
|
491
|
+
let buildCmd = "";
|
|
492
|
+
try {
|
|
493
|
+
const denoConfig = JSON.parse(readFileSync(join(projectRoot, triggerFile), "utf-8"));
|
|
494
|
+
if (denoConfig && typeof denoConfig === "object" && denoConfig.tasks && denoConfig.tasks.build) {
|
|
495
|
+
buildCmd = "deno task build";
|
|
496
|
+
}
|
|
497
|
+
} catch (_) {}
|
|
498
|
+
return { ...container, stack: "deno", setupCmd, testCmd: "deno test", buildCmd, triggerFile };
|
|
483
499
|
}
|
|
484
500
|
if (existsSync(join(projectRoot, "package.json"))) {
|
|
485
501
|
const hasPnpm = existsSync(join(projectRoot, "pnpm-lock.yaml"));
|