jules-orchestrator-kit 0.52.8 → 0.53.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 +66 -7
- package/bin/agentctl.mjs +172 -1
- package/bin/init.js +87 -74
- package/index.mjs +10 -0
- package/package.json +5 -2
- package/scripts/jules-create.mjs +5 -16
- package/scripts/jules-patch.mjs +18 -10
- package/scripts/stale-base-check.mjs +1 -1
- package/src/ci-templates.mjs +199 -0
- package/src/config.mjs +39 -11
- package/src/engine.mjs +2 -21
- package/src/env-aliases.mjs +80 -0
- package/src/mcp.mjs +19 -1
- package/src/ops/doctor-registry.mjs +53 -32
- package/src/ops/next-step.mjs +38 -7
- package/src/profiles-io.mjs +77 -0
- package/src/profiles.mjs +188 -0
- package/src/provider-readiness.mjs +215 -0
- package/src/security.mjs +42 -1
- package/src/tui.mjs +14 -3
- package/src/wizard-init.mjs +32 -4
- package/src/ops/doctor-planner.mjs +0 -220
- package/src/ops/receipts.mjs +0 -171
- package/src/ops/swarm-actions.mjs +0 -156
- package/src/ops/task-actions.mjs +0 -214
- package/src/ops/transaction.mjs +0 -278
- package/src/ux/capabilities.mjs +0 -180
- package/src/ux/diff-viewer.mjs +0 -289
- package/src/ux/layout.mjs +0 -195
- package/src/ux/log-viewer.mjs +0 -110
- package/src/ux/palette.mjs +0 -179
- package/src/ux/queue-model.mjs +0 -206
- package/src/ux/renderer.mjs +0 -188
- package/src/ux/swarm-model.mjs +0 -146
- package/src/ux/terminal-session.mjs +0 -215
- package/src/ux/widgets.mjs +0 -323
- /package/src/{ux/key-decoder.mjs → key-decoder.mjs} +0 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# jules-orchestrator-kit
|
|
4
4
|
|
|
5
|
-
### Task orchestration and automated verification harness for
|
|
5
|
+
### Task orchestration and automated verification harness for coding agents
|
|
6
6
|
|
|
7
7
|
<br/>
|
|
8
8
|
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
<p align="center">
|
|
26
26
|
<a href="#quickstart">Quickstart</a> •
|
|
27
|
+
<a href="#any-repository">Any Repository</a> •
|
|
27
28
|
<a href="#overview">Overview</a> •
|
|
28
29
|
<a href="#target-workflows">Target Workflows</a> •
|
|
29
30
|
<a href="#triage-guidelines">Triage</a> •
|
|
@@ -57,8 +58,8 @@ npx jules-orchestrator-kit init
|
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
```bash
|
|
60
|
-
# 2. Commit what init wrote — .agent/config.yml is
|
|
61
|
-
#
|
|
61
|
+
# 2. Commit what init wrote — .agent/config.yml is protected by BUILTIN_PROTECT,
|
|
62
|
+
# so leaving it uncommitted makes the first gate reject your tree
|
|
62
63
|
git add .agent AGENTS.md SPEC.md CONSTRAINTS.md .gitignore && git commit -m "chore: add agent config"
|
|
63
64
|
```
|
|
64
65
|
|
|
@@ -67,6 +68,20 @@ git add .agent AGENTS.md SPEC.md CONSTRAINTS.md .gitignore && git commit -m "cho
|
|
|
67
68
|
npx jules-orchestrator-kit task create
|
|
68
69
|
```
|
|
69
70
|
|
|
71
|
+
`init` reads the repository, not a template: it detects the stack, picks a
|
|
72
|
+
provider this machine can actually reach, and generates a CI workflow for the
|
|
73
|
+
toolchain the project uses. Nothing about your setup is assumed.
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Which agents can this machine dispatch to, and what is missing for the rest?
|
|
77
|
+
npx jules-orchestrator-kit providers
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# How hard should the gate verify agent work? (minimal | standard | max)
|
|
82
|
+
npx jules-orchestrator-kit profile --set max
|
|
83
|
+
```
|
|
84
|
+
|
|
70
85
|
> [!TIP]
|
|
71
86
|
> **Not sure what to run next?**
|
|
72
87
|
> `agentctl` with no arguments reads the repository state and prints the single
|
|
@@ -87,6 +102,44 @@ npx jules-orchestrator-kit task create
|
|
|
87
102
|
|
|
88
103
|
<br/>
|
|
89
104
|
|
|
105
|
+
<a id="any-repository"></a>
|
|
106
|
+
## Using It In Any Repository
|
|
107
|
+
|
|
108
|
+
Four things differ between projects, and the kit resolves each one from the
|
|
109
|
+
repository rather than from a template.
|
|
110
|
+
|
|
111
|
+
| What differs | How it is resolved | Inspect / override |
|
|
112
|
+
| :--- | :--- | :--- |
|
|
113
|
+
| **The stack** | `detectStack()` recognises 24+ ecosystems (Cargo, Go, Python/Django, Maven/Gradle, .NET, PHP/Laravel, Ruby, Elixir, Swift, Flutter/Dart, CMake, Bun, Deno, Node + Turbo/pnpm/Nx workspaces) and derives the setup, lint, test and build commands from the manifest it finds. | `agentctl doctor` · `verify:` in `.agent/config.yml` |
|
|
114
|
+
| **The agent** | `provider:` selects Google Jules (hosted REST), the Claude Code CLI, the Codex CLI or the Gemini CLI. Readiness means a credential for the hosted one and a binary on `PATH` for the local ones — never both. | `agentctl providers` · `agentctl init --provider <name>` |
|
|
115
|
+
| **How hard to verify** | `verify.profile` expands at load time into a stage pipeline that skips gates the runtime cannot support, and says which and why. | `agentctl profile` · `agentctl profile --set max` |
|
|
116
|
+
| **Where CI runs** | A workflow is *generated* for the detected stack — the project's toolchain plus Node for the CLI — not copied from this repository. | `agentctl ci init [--target github\|gitlab]` |
|
|
117
|
+
|
|
118
|
+
### Verification profiles
|
|
119
|
+
|
|
120
|
+
| Profile | Runs | Use when |
|
|
121
|
+
| :--- | :--- | :--- |
|
|
122
|
+
| `minimal` | setup → tests | The suite is slow, the stack is unfamiliar, or it is day one. |
|
|
123
|
+
| `standard` | setup → lint → tests → build → anti-tamper on the diff | The everyday gate. Scaffolded by default. |
|
|
124
|
+
| `max` | everything above → mutation scoring → V8 diff coverage *(Node runtimes only)* → 3-pass flakiness probe | The change is consequential, or an agent has been getting green too easily. |
|
|
125
|
+
|
|
126
|
+
Nothing in a profile is Node-specific by assumption. Gates a runtime cannot
|
|
127
|
+
support are skipped with a stated reason rather than failing the diff — a Cargo
|
|
128
|
+
repository on `max` runs mutation and stability probing and is never asked for
|
|
129
|
+
`NODE_V8_COVERAGE`.
|
|
130
|
+
|
|
131
|
+
### No provider? Still useful
|
|
132
|
+
|
|
133
|
+
Every gate below runs locally with no API key, no CLI and no network:
|
|
134
|
+
`agentctl check`, `mutate`, `coverage`, `probe`, `assert`, `evidence`, `rules`,
|
|
135
|
+
`doctor`. The provider is only needed to *dispatch* work, not to verify it.
|
|
136
|
+
|
|
137
|
+
<br/>
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
<br/>
|
|
142
|
+
|
|
90
143
|
<a id="overview"></a>
|
|
91
144
|
## Overview
|
|
92
145
|
|
|
@@ -137,13 +190,17 @@ To maximize PR merge rates, dispatch tasks according to deterministic boundaries
|
|
|
137
190
|
|
|
138
191
|
## Core Capabilities
|
|
139
192
|
|
|
193
|
+
* **Provider-Agnostic:** Dispatches to Google Jules (hosted REST), the Claude Code CLI, the OpenAI Codex CLI or the Gemini CLI. `agentctl providers` probes each one — a credential for the hosted provider, a binary on `PATH` for the local ones — and every verification gate works with no provider configured at all.
|
|
194
|
+
* **Vendor-Neutral Configuration:** Every `JULES_*` environment variable also answers to an `AGENT_*` spelling (`AGENT_API_KEY`, `AGENT_REPO`, `AGENT_SWARM_CONCURRENCY`). The legacy name always wins where both are set, so adding an alias cannot change a working setup.
|
|
195
|
+
* **One-Word Verification Depth:** `verify.profile: minimal | standard | max` expands at load time into a stack-aware pipeline — `max` adds mutation scoring, flakiness probing and, where the runtime emits it, V8 diff coverage. A Cargo repository is never asked for `NODE_V8_COVERAGE`.
|
|
196
|
+
* **Generated, Not Copied, CI:** `agentctl ci init` writes a GitHub Actions or GitLab job carrying the toolchain the detected stack needs (`setup-python`, `setup-go`, `setup-java`, …) plus Node for the CLI itself.
|
|
140
197
|
* **Zero Runtime Dependencies:** Built exclusively on Node.js 20+ built-in modules (`node:fs`, `node:child_process`, `node:crypto`, `node:path`, `node:http`, `node:tty`, `node:test`).
|
|
141
198
|
* **Cross-Platform Parity:** Verified 100% green across Linux, macOS (Darwin), and Windows on Node 20, 22, and 24.
|
|
142
199
|
* **Autonomous Self-Healing Loop:** Captures test stderr/stdout, fingerprints error traces, and feeds structured context back into automated repair turns (up to 3 attempts) before human escalation.
|
|
143
200
|
* **Fail-Closed Security & Secret Redaction:** Evaluates explicit Deny rules before Allow rules against canonicalized, case-folded paths. Redacts high-entropy keys and base64-encoded credentials (such as Kubernetes `Secret` manifests).
|
|
144
201
|
* **Complexity & Cost Router:** Zero-dependency heuristic classifier (`src/router.mjs`) routing mechanical tasks to lightweight models while reserving primary models for complex refactors, with a `node --check` syntax-verification gate that transparently escalates a FAST-tier result to the primary provider if it left broken JS on disk.
|
|
145
202
|
* **Terminal UI & Diagnostic Matrix (`agentctl doctor`):** Interactive terminal dashboard, task sidecar manager, and automated transactional self-repair.
|
|
146
|
-
* **Verified Test Suite:** Tested with **
|
|
203
|
+
* **Verified Test Suite:** Tested with **797 unit tests across 98 suites passing in < 12.0s**.
|
|
147
204
|
|
|
148
205
|
<br/>
|
|
149
206
|
|
|
@@ -158,7 +215,7 @@ To maximize PR merge rates, dispatch tasks according to deterministic boundaries
|
|
|
158
215
|
|
|
159
216
|
| Command | Usage | Description | Exit Codes |
|
|
160
217
|
| :--- | :--- | :--- | :--- |
|
|
161
|
-
| `init` | `agentctl init [--interactive] [--tier pro] [--force]` | Interactive onboarding wizard & stack detector. Generates `.agent/config.yml` and scaffolds `AGENTS.md`, the role prompts, the guardrails and the runtime `.gitignore` entries. Existing files are preserved unless `--force`. | `0` (Created) |
|
|
218
|
+
| `init` | `agentctl init [--interactive] [--tier pro] [--provider <name>] [--profile <name>] [--force]` | Interactive onboarding wizard & stack detector. Generates `.agent/config.yml` and scaffolds `AGENTS.md`, the role prompts, the guardrails and the runtime `.gitignore` entries. Existing files are preserved unless `--force`. | `0` (Created) |
|
|
162
219
|
| `budget` | `agentctl budget [--by-user] [--json] [reset]` | Reports rolling 24h task budget, quota headroom, and per-developer task attribution without external auth servers. | `0` (Status), `2` (Arg Error) |
|
|
163
220
|
| `task create` | `agentctl task create [<prompt>] [--title <t>] [-p <prompt>] [-f <file>] [--template <id>] [--role <name>] [--tier fast\|complex]` | Interactively authors & scopes falsifiable task envelopes with secret scrubbing, preflight gate checks, and DAG dependency wiring. | `0` (Queued), `1` (Secret/Unfalsifiable) |
|
|
164
221
|
| `task template` | `agentctl task template [<id>] [--list] [--json]` | Lists and synthesizes pre-calibrated task envelopes (Web, Deep Think, Universal & Agent Hardening: `web-cwv`, `web-wcag`, `web-seo`, `web-playwright`, `agent-dead-code-audit`, `web-flaky-heal`, `web-i18n`, `web-ai-access`, `agent-qa-mutation`, `agent-ci-falsify`, `agent-service-isolate`, `agent-error-paths`, `agent-security-audit`, `agent-dep-audit`, `agent-doc-drift`, `agent-config-audit`, `agent-api-contract`, `deep-debug`, `deep-feature`, `deep-optimize`, `deep-harden`). | `0` (Listed/Synthesized) |
|
|
@@ -169,6 +226,9 @@ To maximize PR merge rates, dispatch tasks according to deterministic boundaries
|
|
|
169
226
|
| `retry` | `agentctl retry <sessionId> [--role <role>] [--with-failure] [--json]` | Fetches error traces and activity logs from a failed session and synthesizes a targeted OODA retry dispatch. | `0` (Dispatched), `1` (Error) |
|
|
170
227
|
| `prune` | `agentctl prune [--age 7d] [--state <state>] [--delete] [--yes] [--json]` | Queries and batch-archives or deletes stale/completed sessions via Jules v1alpha API to keep workspaces clean. | `0` (Cleaned) |
|
|
171
228
|
| `pr harvest` | `agentctl pr harvest [--tier r0,r1] [--limit <n>] [--auto] [--allow-no-checks] [--dry-run]` | Discovers open agent PRs, evaluates CI checks & risk tiers, and auto-squashes green low-risk changes autonomously. A PR reporting **no** CI checks is skipped unless `--allow-no-checks` is passed, and an unavailable changed-file list blocks rather than classifying as low risk. | `0` (Triaged/Merged), `1` (Error) |
|
|
229
|
+
| `providers` | `agentctl providers [--json]` | Probes every built-in provider and reports which ones this machine can dispatch to, what each one is missing, and which is active. | `0` (Active provider ready), `1` (Not ready) |
|
|
230
|
+
| `profile` | `agentctl profile [--list] [--set minimal\|standard\|max] [--json]` | Shows the verification stages the configured profile expands to on this stack, or writes a new profile into `.agent/config.yml` without disturbing comments. | `0` (Shown/Set), `2` (Unknown profile) |
|
|
231
|
+
| `ci init` | `agentctl ci init [--target github\|gitlab] [--force] [--dry-run] [--json]` | Generates a stack-aware CI gate workflow (`.github/workflows/agent-gate.yml` or `.gitlab-ci.agent-gate.yml`) that runs `agentctl check --mode committed`. Refuses to overwrite without `--force`. | `0` (Written/Skipped), `1` (Write error), `2` (Unknown target) |
|
|
172
232
|
| `doctor` | `agentctl doctor [--json]` | Diagnostic DAG check runner & automated transactional self-repair engine. | `0` (Healthy), `1` (Failures) |
|
|
173
233
|
| `queue` | `agentctl queue [--dag] [--concurrency <n>] [--dry-run] [--json]` | Consumes and executes task envelopes in `.agent/jules-queue/` with Kahn's DAG dependency resolution. Non-task files (manifests, `README.md`) are skipped, and `--dry-run` previews without moving anything. | `0` (Complete) |
|
|
174
234
|
| `swarm` | `agentctl swarm [--json]` | Runs parallel multi-agent swarm across worker slots with PID liveness detection. | `0` (Complete) |
|
|
@@ -222,7 +282,6 @@ verify:
|
|
|
222
282
|
scope:
|
|
223
283
|
deny:
|
|
224
284
|
- ".github/**"
|
|
225
|
-
- ".agent/config.yml"
|
|
226
285
|
- "keys/**"
|
|
227
286
|
|
|
228
287
|
# Plan tier. Defaults to `free` when unset — the kit will not assume you are
|
|
@@ -391,7 +450,7 @@ const result = await fast.dispatch({ prompt: "Fix a typo." }, { root: process.cw
|
|
|
391
450
|
| **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)* |
|
|
392
451
|
| **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)* |
|
|
393
452
|
| **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)* |
|
|
394
|
-
| **
|
|
453
|
+
| **Terminal UI Engine** | `src/tui.mjs`, `src/key-decoder.mjs` | Zero-dependency terminal capabilities detector, sequence key decoder, and interactive prompt widgets. | **v0.30.0** *(Shipped)* |
|
|
395
454
|
| **PR Review Auto-Remediation Loop** | `agentctl review-repair` (`src/review-repair.mjs`) | Ingests GitHub PR review comments (`CHANGES_REQUESTED`), extracts line/file context, and dispatches automated repair turns. | **v0.27.0** *(Shipped)* |
|
|
396
455
|
|
|
397
456
|
</details>
|
package/bin/agentctl.mjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { readFileSync, existsSync, readdirSync } from "node:fs";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
|
+
import { applyEnvAliases } from "../src/env-aliases.mjs";
|
|
6
7
|
import { loadConfig, resolveRoot, detectStack, bootstrapZeroTestRepo } from "../src/config.mjs";
|
|
7
8
|
import { gate, dispatch, run, isTaskFile } from "../src/engine.mjs";
|
|
8
9
|
import { acquireLock, releaseLock, lockStatus, getQueueDir } from "../src/state.mjs";
|
|
@@ -11,6 +12,12 @@ import { reapOrphanedIntents, reapStaleMutexDirs } from "../src/journal.mjs";
|
|
|
11
12
|
import { KIT_VERSION } from "../src/version.mjs";
|
|
12
13
|
import { budgetStatus, listOpenReservations, releaseOpenReservations, resolveConcurrency } from "../src/budget.mjs";
|
|
13
14
|
|
|
15
|
+
// Vendor-neutral env spellings are filled in before any module reads
|
|
16
|
+
// process.env, so `AGENT_API_KEY` works everywhere `JULES_API_KEY` does. A
|
|
17
|
+
// value already present under the legacy name always wins — see
|
|
18
|
+
// src/env-aliases.mjs.
|
|
19
|
+
applyEnvAliases(process.env);
|
|
20
|
+
|
|
14
21
|
const args = process.argv.slice(2);
|
|
15
22
|
const command = args[0];
|
|
16
23
|
|
|
@@ -35,7 +42,7 @@ export function formatBudgetLine(b) {
|
|
|
35
42
|
|
|
36
43
|
export function printHelp() {
|
|
37
44
|
console.log(`
|
|
38
|
-
|
|
45
|
+
agentctl v${VERSION} — Universal Agent Orchestrator & Safety Gatekeeper
|
|
39
46
|
|
|
40
47
|
Usage: agentctl <command> [options]
|
|
41
48
|
|
|
@@ -55,6 +62,9 @@ Commands:
|
|
|
55
62
|
clean Clean stale branches, worktrees, locks, and ledgers
|
|
56
63
|
lock <action> Manage mutex locks (acquire | release | status)
|
|
57
64
|
doctor Run system diagnostics and stack resolution checks
|
|
65
|
+
providers List agent providers and whether this machine can reach them (--json)
|
|
66
|
+
profile Show or set the verification profile (--list, --set <name>, --json)
|
|
67
|
+
ci init Generate a stack-aware CI gate workflow (--target github|gitlab, --force)
|
|
58
68
|
bootstrap Bootstrap zero-test repository with verification oracle
|
|
59
69
|
review-repair Parse PR review comments and synthesize OODA repair tasks
|
|
60
70
|
dashboard Start local HTTP telemetry and audit dashboard
|
|
@@ -1193,6 +1203,150 @@ async function main() {
|
|
|
1193
1203
|
break;
|
|
1194
1204
|
}
|
|
1195
1205
|
|
|
1206
|
+
case "providers": {
|
|
1207
|
+
const { values } = parseArgs({
|
|
1208
|
+
args: args.slice(1),
|
|
1209
|
+
options: { json: { type: "boolean", short: "j" } },
|
|
1210
|
+
allowPositionals: true,
|
|
1211
|
+
});
|
|
1212
|
+
const { detectAvailableProviders, probeProvider } = await import("../src/provider-readiness.mjs");
|
|
1213
|
+
const probes = detectAvailableProviders();
|
|
1214
|
+
const active = config.provider || "jules";
|
|
1215
|
+
const activeProbe = probeProvider(active);
|
|
1216
|
+
|
|
1217
|
+
if (values.json) {
|
|
1218
|
+
console.log(JSON.stringify({ active, activeReady: activeProbe.ready, providers: probes }, null, 2));
|
|
1219
|
+
process.exit(0);
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
console.log(`\n🔌 Agent providers\n`);
|
|
1223
|
+
for (const p of probes) {
|
|
1224
|
+
const marker = p.name === active ? "▸" : " ";
|
|
1225
|
+
console.log(` ${marker} ${p.ready ? "✅" : "⬜"} ${p.name.padEnd(14)} ${p.label}`);
|
|
1226
|
+
console.log(` ${p.reason}`);
|
|
1227
|
+
if (!p.ready) console.log(` Fix: ${p.remedy}`);
|
|
1228
|
+
}
|
|
1229
|
+
if (!probes.some((p) => p.name === active)) {
|
|
1230
|
+
console.log(` ▸ ⬜ ${active.padEnd(14)} ${activeProbe.label}`);
|
|
1231
|
+
console.log(` ${activeProbe.reason}`);
|
|
1232
|
+
}
|
|
1233
|
+
console.log(`\n Active: ${active} (provider: in ${config._file || ".agent/config.yml"})`);
|
|
1234
|
+
console.log(` Switch: agentctl init --provider <name> · every gate below works with no provider at all\n`);
|
|
1235
|
+
process.exit(activeProbe.ready ? 0 : 1);
|
|
1236
|
+
break;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
case "profile": {
|
|
1240
|
+
const { values } = parseArgs({
|
|
1241
|
+
args: args.slice(1),
|
|
1242
|
+
options: {
|
|
1243
|
+
list: { type: "boolean", short: "l" },
|
|
1244
|
+
set: { type: "string" },
|
|
1245
|
+
json: { type: "boolean", short: "j" },
|
|
1246
|
+
},
|
|
1247
|
+
allowPositionals: true,
|
|
1248
|
+
});
|
|
1249
|
+
const { PROFILE_NAMES, PROFILE_DESCRIPTIONS, buildProfileStages, buildDefaultStages } = await import("../src/profiles.mjs");
|
|
1250
|
+
|
|
1251
|
+
if (values.set) {
|
|
1252
|
+
const name = String(values.set).toLowerCase();
|
|
1253
|
+
if (!PROFILE_NAMES.includes(name)) {
|
|
1254
|
+
console.error(`❌ Unknown profile '${values.set}'. Choose one of: ${PROFILE_NAMES.join(", ")}`);
|
|
1255
|
+
process.exit(2);
|
|
1256
|
+
}
|
|
1257
|
+
const { setVerificationProfile } = await import("../src/profiles-io.mjs");
|
|
1258
|
+
const res = setVerificationProfile(root, name);
|
|
1259
|
+
if (!res.ok) {
|
|
1260
|
+
console.error(`❌ ${res.error}`);
|
|
1261
|
+
process.exit(1);
|
|
1262
|
+
}
|
|
1263
|
+
console.log(`✅ Verification profile set to '${name}' in ${res.file}`);
|
|
1264
|
+
process.exit(0);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
if (values.list) {
|
|
1268
|
+
if (values.json) {
|
|
1269
|
+
console.log(JSON.stringify(PROFILE_NAMES.map((n) => ({ name: n, description: PROFILE_DESCRIPTIONS[n] })), null, 2));
|
|
1270
|
+
} else {
|
|
1271
|
+
console.log(`\n🎚️ Verification profiles\n`);
|
|
1272
|
+
for (const n of PROFILE_NAMES) console.log(` ${n.padEnd(10)} ${PROFILE_DESCRIPTIONS[n]}`);
|
|
1273
|
+
console.log(`\n Set with: agentctl profile --set max\n`);
|
|
1274
|
+
}
|
|
1275
|
+
process.exit(0);
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
const stack = detectStack(root).stack;
|
|
1279
|
+
const activeProfile = config.verify.profile || "(none — running the built-in pipeline)";
|
|
1280
|
+
const plan = config.verify.profile
|
|
1281
|
+
? buildProfileStages(config.verify.profile, { stack, verify: config.verify })
|
|
1282
|
+
: { profile: null, stages: config.verify.stages || buildDefaultStages(config.verify), skipped: [] };
|
|
1283
|
+
|
|
1284
|
+
if (values.json) {
|
|
1285
|
+
console.log(JSON.stringify({ profile: config.verify.profile, stack, stages: plan.stages, skipped: plan.skipped }, null, 2));
|
|
1286
|
+
process.exit(0);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
console.log(`\n🎚️ Verification profile: ${activeProfile} (stack: ${stack})\n`);
|
|
1290
|
+
if (plan.stages.length === 0) {
|
|
1291
|
+
console.log(` No stages resolved — no verification command is configured or detectable.`);
|
|
1292
|
+
console.log(` Set verify.test in .agent/config.yml, or run: agentctl bootstrap`);
|
|
1293
|
+
}
|
|
1294
|
+
for (const st of plan.stages) {
|
|
1295
|
+
const what = st.assert ? `assert:${st.assert}` : st.cmd;
|
|
1296
|
+
console.log(` ${String(st.id).padEnd(14)} ${what}`);
|
|
1297
|
+
}
|
|
1298
|
+
for (const sk of plan.skipped) {
|
|
1299
|
+
console.log(` ${String(sk.id).padEnd(14)} — skipped: ${sk.reason}`);
|
|
1300
|
+
}
|
|
1301
|
+
console.log(`\n Change with: agentctl profile --set ${config.verify.profile === "max" ? "standard" : "max"}\n`);
|
|
1302
|
+
process.exit(0);
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
case "ci": {
|
|
1307
|
+
const sub = args[1];
|
|
1308
|
+
if (sub !== "init") {
|
|
1309
|
+
console.error(`❌ Unknown 'ci' action '${sub || ""}'. Usage: agentctl ci init [--target github|gitlab] [--force]`);
|
|
1310
|
+
process.exit(2);
|
|
1311
|
+
}
|
|
1312
|
+
const { values } = parseArgs({
|
|
1313
|
+
args: args.slice(2),
|
|
1314
|
+
options: {
|
|
1315
|
+
target: { type: "string", default: "github" },
|
|
1316
|
+
force: { type: "boolean", short: "f" },
|
|
1317
|
+
"dry-run": { type: "boolean", short: "d" },
|
|
1318
|
+
json: { type: "boolean", short: "j" },
|
|
1319
|
+
},
|
|
1320
|
+
allowPositionals: true,
|
|
1321
|
+
});
|
|
1322
|
+
const { writeCiWorkflow } = await import("../src/ci-templates.mjs");
|
|
1323
|
+
const res = writeCiWorkflow(root, {
|
|
1324
|
+
target: values.target,
|
|
1325
|
+
force: values.force,
|
|
1326
|
+
dryRun: values["dry-run"],
|
|
1327
|
+
config,
|
|
1328
|
+
version: VERSION,
|
|
1329
|
+
stack: detectStack(root),
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
if (values.json) {
|
|
1333
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1334
|
+
process.exit(res.ok ? 0 : 1);
|
|
1335
|
+
}
|
|
1336
|
+
if (!res.ok) {
|
|
1337
|
+
console.error(`❌ ${res.error}`);
|
|
1338
|
+
process.exit(1);
|
|
1339
|
+
}
|
|
1340
|
+
console.log(`✅ ${res.written ? "Wrote" : "Would write"} ${res.file}`);
|
|
1341
|
+
console.log(` Stack: ${res.stack} · runtime setup: ${res.setupSummary}`);
|
|
1342
|
+
console.log(` The workflow runs: agentctl check --mode committed`);
|
|
1343
|
+
if (!res.written && !values["dry-run"]) {
|
|
1344
|
+
console.log(` (unchanged — pass --force to overwrite)`);
|
|
1345
|
+
}
|
|
1346
|
+
process.exit(0);
|
|
1347
|
+
break;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1196
1350
|
case "bootstrap": {
|
|
1197
1351
|
const { values } = parseArgs({
|
|
1198
1352
|
args: args.slice(1),
|
|
@@ -1250,6 +1404,8 @@ async function main() {
|
|
|
1250
1404
|
"no-interactive": { type: "boolean" },
|
|
1251
1405
|
yes: { type: "boolean", short: "y" },
|
|
1252
1406
|
tier: { type: "string", short: "t" },
|
|
1407
|
+
provider: { type: "string" },
|
|
1408
|
+
profile: { type: "string" },
|
|
1253
1409
|
json: { type: "boolean", short: "j" },
|
|
1254
1410
|
"dry-run": { type: "boolean", short: "d" },
|
|
1255
1411
|
force: { type: "boolean", short: "f" },
|
|
@@ -1267,6 +1423,10 @@ async function main() {
|
|
|
1267
1423
|
// re-run. Undefined lets the wizard seed the menu from the existing
|
|
1268
1424
|
// config and fall back to FALLBACK_TIER when there is nothing to seed.
|
|
1269
1425
|
tier: values.tier,
|
|
1426
|
+
// Both undefined by default so the wizard can detect a provider the
|
|
1427
|
+
// machine can actually reach and keep the profile already in config.
|
|
1428
|
+
provider: values.provider,
|
|
1429
|
+
profile: values.profile,
|
|
1270
1430
|
allowDefaults: true,
|
|
1271
1431
|
});
|
|
1272
1432
|
|
|
@@ -1282,6 +1442,17 @@ async function main() {
|
|
|
1282
1442
|
} else {
|
|
1283
1443
|
console.log(`✅ Onboarding complete! Manifest generated at ${res.configPath}`);
|
|
1284
1444
|
console.log(` Tier: ${res.plan.tier.toUpperCase()} (${res.plan.limits.concurrency} worker(s), ${res.plan.limits.daily_tasks} daily tasks)`);
|
|
1445
|
+
{
|
|
1446
|
+
const { probeProvider } = await import("../src/provider-readiness.mjs");
|
|
1447
|
+
const probe = probeProvider(res.plan.provider);
|
|
1448
|
+
console.log(` Provider : ${probe.name} ${probe.ready ? "(ready)" : `(not ready — ${probe.remedy})`}`);
|
|
1449
|
+
const { buildProfileStages, describeProfilePlan } = await import("../src/profiles.mjs");
|
|
1450
|
+
const planned = buildProfileStages(res.plan.profile, {
|
|
1451
|
+
stack: res.plan.stack,
|
|
1452
|
+
verify: { ...res.plan.verify, unit: res.plan.verify.test },
|
|
1453
|
+
});
|
|
1454
|
+
console.log(` Verification Profile : ${res.plan.profile} → ${describeProfilePlan(planned)}`);
|
|
1455
|
+
}
|
|
1285
1456
|
if (res.plan.verify.test) {
|
|
1286
1457
|
console.log(` Verification Test Command : "${res.plan.verify.test}"`);
|
|
1287
1458
|
} else {
|
package/bin/init.js
CHANGED
|
@@ -8,6 +8,8 @@ import readline from "node:readline/promises";
|
|
|
8
8
|
import zlib from "node:zlib";
|
|
9
9
|
import crypto from "node:crypto";
|
|
10
10
|
import { resolveProjectCommands } from "../scripts/command-resolver.mjs";
|
|
11
|
+
import { KIT_VERSION } from "../src/version.mjs";
|
|
12
|
+
import { detectStack } from "../src/config.mjs";
|
|
11
13
|
|
|
12
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
15
|
const __dirname = path.dirname(__filename);
|
|
@@ -52,7 +54,7 @@ Options:
|
|
|
52
54
|
process.exit(0);
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
console.log("\n🚀 Initializing
|
|
57
|
+
console.log("\n🚀 Initializing agent orchestrator kit...\n");
|
|
56
58
|
console.log(`📁 Target Directory: ${targetDir}`);
|
|
57
59
|
if (isForce) console.log("⚠️ Force mode enabled (existing files will be overwritten).");
|
|
58
60
|
|
|
@@ -129,65 +131,62 @@ for (const item of scaffolded.created) {
|
|
|
129
131
|
|
|
130
132
|
const agentDir = path.join(targetDir, ".agent");
|
|
131
133
|
|
|
132
|
-
// Scaffold .
|
|
134
|
+
// Scaffold the manifest pair.
|
|
135
|
+
//
|
|
136
|
+
// This entry point used to hand-roll a thinner `.agent/jules.yml` while
|
|
137
|
+
// `agentctl init` wrote a `.agent/config.yml` the runtime actually reads, so
|
|
138
|
+
// which of the two scaffolders you happened to run decided whether the
|
|
139
|
+
// repository had a provider, a tier and a verification profile at all. Both
|
|
140
|
+
// now go through `planInit`.
|
|
141
|
+
const { planInit } = await import("../src/wizard-init.mjs");
|
|
142
|
+
const initPlan = planInit(targetDir, {
|
|
143
|
+
testCmd: detected.testCmd,
|
|
144
|
+
buildCmd: detected.buildCmd,
|
|
145
|
+
lintCmd: detected.lintCmd,
|
|
146
|
+
baseBranch: answerBranchVal || process.env.BASE_BRANCH || undefined,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const configPath = path.join(agentDir, "config.yml");
|
|
150
|
+
if (!fs.existsSync(configPath) || isForce) {
|
|
151
|
+
fs.writeFileSync(configPath, initPlan.configYaml, "utf-8");
|
|
152
|
+
console.log(`✅ Created: .agent/config.yml (provider: ${initPlan.provider}, profile: ${initPlan.profile})`);
|
|
153
|
+
}
|
|
154
|
+
|
|
133
155
|
const yamlConfigPath = path.join(agentDir, "jules.yml");
|
|
134
156
|
if (!fs.existsSync(yamlConfigPath) || isForce) {
|
|
135
|
-
|
|
136
|
-
version: 2
|
|
137
|
-
test_cmd: "${detected.testCmd || "npm test"}"
|
|
138
|
-
build_cmd: "${detected.buildCmd || "npm run build"}"
|
|
139
|
-
forbidden_paths: [".github/**", "**/.env*", "**/*.pem", "**/lock-manager*"]
|
|
140
|
-
`;
|
|
141
|
-
fs.writeFileSync(yamlConfigPath, yamlContent, "utf-8");
|
|
157
|
+
fs.writeFileSync(yamlConfigPath, initPlan.julesYaml, "utf-8");
|
|
142
158
|
console.log("✅ Created: .agent/jules.yml");
|
|
143
159
|
}
|
|
144
160
|
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
// 3b. Generate a CI gate workflow for *this* repository's stack.
|
|
162
|
+
//
|
|
163
|
+
// This used to copy the kit's own audit workflow verbatim: a nine-way Node
|
|
164
|
+
// matrix running `npm install`, `npm test` and a script path that exists only
|
|
165
|
+
// inside the kit. In a Rust, Python or Go repository it was red on the first
|
|
166
|
+
// push for reasons that had nothing to do with that repository's code.
|
|
167
|
+
const { writeCiWorkflow } = await import("../src/ci-templates.mjs");
|
|
168
|
+
const ciRes = writeCiWorkflow(targetDir, {
|
|
169
|
+
target: "github",
|
|
170
|
+
force: isForce,
|
|
171
|
+
// `resolveProjectCommands` reports a human-readable source string; the CI
|
|
172
|
+
// generator needs the machine-readable stack id.
|
|
173
|
+
stack: detectStack(targetDir),
|
|
174
|
+
config: { baseBranch: answerBranchVal || process.env.BASE_BRANCH || "main" },
|
|
175
|
+
version: KIT_VERSION,
|
|
176
|
+
});
|
|
177
|
+
if (ciRes.ok && ciRes.written) {
|
|
178
|
+
console.log(`✅ Generated CI gate for '${ciRes.stack}': ${ciRes.file}`);
|
|
179
|
+
} else if (ciRes.ok) {
|
|
180
|
+
console.log(`↩️ Kept existing ${ciRes.file} (use --force to regenerate)`);
|
|
158
181
|
}
|
|
159
182
|
|
|
160
|
-
// 4.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
let copiedCount = 0;
|
|
168
|
-
let skippedCount = 0;
|
|
169
|
-
|
|
170
|
-
if (fs.existsSync(sourceScriptsDir)) {
|
|
171
|
-
const scriptFiles = fs.readdirSync(sourceScriptsDir);
|
|
172
|
-
const excludeFiles = new Set(["release.mjs"]);
|
|
173
|
-
scriptFiles.forEach((file) => {
|
|
174
|
-
if (excludeFiles.has(file)) return;
|
|
175
|
-
const srcFile = path.join(sourceScriptsDir, file);
|
|
176
|
-
const destFile = path.join(targetScriptsDir, file);
|
|
177
|
-
if (!fs.existsSync(destFile) || isForce) {
|
|
178
|
-
fs.copyFileSync(srcFile, destFile);
|
|
179
|
-
try {
|
|
180
|
-
fs.chmodSync(destFile, 0o755);
|
|
181
|
-
} catch (err) {
|
|
182
|
-
console.warn(`⚠️ Could not set executable permissions on ${file}:`, err.message);
|
|
183
|
-
}
|
|
184
|
-
copiedCount++;
|
|
185
|
-
} else {
|
|
186
|
-
skippedCount++;
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
console.log(`✅ Copied ${copiedCount} orchestration scripts to ./scripts/ (${skippedCount} skipped, use --force to overwrite)`);
|
|
190
|
-
}
|
|
183
|
+
// 4. The kit's own scripts/ directory is NOT copied here.
|
|
184
|
+
//
|
|
185
|
+
// It used to be — twenty orchestration scripts dropped into the target repo's
|
|
186
|
+
// scripts/ folder, where they collided with the project's own files, aged
|
|
187
|
+
// independently of the installed kit, and duplicated commands that `agentctl`
|
|
188
|
+
// already exposes. Everything they did is reachable through the CLI, which is
|
|
189
|
+
// what the injected package.json entries below point at.
|
|
191
190
|
|
|
192
191
|
// 5. Optionally inject scripts into target package.json if present
|
|
193
192
|
const targetPkgPath = path.join(targetDir, "package.json");
|
|
@@ -196,18 +195,22 @@ if (fs.existsSync(targetPkgPath) && targetDir !== kitRoot) {
|
|
|
196
195
|
const pkg = JSON.parse(fs.readFileSync(targetPkgPath, "utf-8"));
|
|
197
196
|
pkg.scripts = pkg.scripts || {};
|
|
198
197
|
let updated = false;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
"
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
198
|
+
// Vendor-neutral names: these run `agentctl`, which dispatches to whichever
|
|
199
|
+
// provider the repository selected. `jules:*` entries from an older init are
|
|
200
|
+
// left in place — removing scripts a project may reference in CI is not
|
|
201
|
+
// this command's business.
|
|
202
|
+
const agentScripts = {
|
|
203
|
+
"agent:gate": "agentctl check",
|
|
204
|
+
"agent:dispatch": "agentctl dispatch",
|
|
205
|
+
"agent:queue": "agentctl queue",
|
|
206
|
+
"agent:create": "agentctl task create",
|
|
207
|
+
"agent:status": "agentctl status",
|
|
208
|
+
"agent:doctor": "agentctl doctor",
|
|
209
|
+
"agent:swarm": "agentctl swarm",
|
|
210
|
+
"agent:clean": "agentctl clean"
|
|
208
211
|
};
|
|
209
212
|
|
|
210
|
-
for (const [key, val] of Object.entries(
|
|
213
|
+
for (const [key, val] of Object.entries(agentScripts)) {
|
|
211
214
|
if (!pkg.scripts[key] || isForce) {
|
|
212
215
|
pkg.scripts[key] = val;
|
|
213
216
|
updated = true;
|
|
@@ -216,7 +219,7 @@ if (fs.existsSync(targetPkgPath) && targetDir !== kitRoot) {
|
|
|
216
219
|
|
|
217
220
|
if (updated) {
|
|
218
221
|
fs.writeFileSync(targetPkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
219
|
-
console.log("✅ Added
|
|
222
|
+
console.log("✅ Added agent:* commands to package.json");
|
|
220
223
|
}
|
|
221
224
|
} catch (err) {
|
|
222
225
|
console.warn("⚠️ Failed to inject helper scripts into target package.json:", err.message);
|
|
@@ -229,7 +232,7 @@ if (scaffolded.gitignore.length > 0) {
|
|
|
229
232
|
console.log(`✅ Added ${scaffolded.gitignore.length} runtime state entries to .gitignore`);
|
|
230
233
|
}
|
|
231
234
|
|
|
232
|
-
console.log("\n🎉
|
|
235
|
+
console.log("\n🎉 Agent orchestrator kit initialized.");
|
|
233
236
|
|
|
234
237
|
// 6. Generate Encoded Workspace Manifest (JULES_WEB_SETUP.md)
|
|
235
238
|
const agentState = {
|
|
@@ -272,13 +275,23 @@ ${payloadToken}
|
|
|
272
275
|
|
|
273
276
|
fs.writeFileSync(path.join(targetDir, ".agent", "JULES_WEB_SETUP.md"), setupMdContent, "utf-8");
|
|
274
277
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
278
|
+
// The encoded manifest is a Google Jules convenience — it pastes the detected
|
|
279
|
+
// workspace into the Jules web UI. It is written unconditionally because it
|
|
280
|
+
// costs nothing and a repository can change provider later, but it is only
|
|
281
|
+
// *announced* when Jules is the provider this repo actually selected;
|
|
282
|
+
// otherwise it is one more Google-specific string in the way of someone who
|
|
283
|
+
// chose a different agent.
|
|
284
|
+
if (initPlan.provider === "jules") {
|
|
285
|
+
console.log("\n🔗 Google Jules workspace manifest");
|
|
286
|
+
console.log(` 👉 Paste into the Jules web UI: \x1b[36m${payloadToken}\x1b[0m`);
|
|
287
|
+
console.log(" (A copy was written to .agent/JULES_WEB_SETUP.md)");
|
|
288
|
+
} else {
|
|
289
|
+
console.log(`\n Provider: ${initPlan.provider}. A Google Jules workspace manifest was also written to .agent/JULES_WEB_SETUP.md if you ever switch.`);
|
|
290
|
+
}
|
|
279
291
|
console.log("\nNext Steps:");
|
|
280
|
-
console.log(" 1.
|
|
281
|
-
console.log(" 2.
|
|
282
|
-
console.log(" 3. Scaffold a
|
|
283
|
-
console.log(" 4. Dispatch the queue:
|
|
284
|
-
console.log(" 5. Run pre-merge
|
|
292
|
+
console.log(" 1. Pick a provider: agentctl providers (jules | claude-code | codex | gemini)");
|
|
293
|
+
console.log(" 2. Pick a gate depth: agentctl profile --list (minimal | standard | max)");
|
|
294
|
+
console.log(" 3. Scaffold a task: agentctl task create");
|
|
295
|
+
console.log(" 4. Dispatch the queue: agentctl queue");
|
|
296
|
+
console.log(" 5. Run the pre-merge gate: agentctl check\n");
|
|
297
|
+
console.log(" Not sure? Run `agentctl` with no arguments — it reads the repo and names one step.\n");
|
package/index.mjs
CHANGED
|
@@ -36,6 +36,16 @@ export {
|
|
|
36
36
|
parseRetryAfter,
|
|
37
37
|
} from "./src/provider.mjs";
|
|
38
38
|
export { resolveRoutedProvider } from "./src/router.mjs";
|
|
39
|
+
export {
|
|
40
|
+
PROVIDER_DESCRIPTORS,
|
|
41
|
+
PROVIDER_PREFERENCE,
|
|
42
|
+
whichBinary,
|
|
43
|
+
probeProvider,
|
|
44
|
+
detectAvailableProviders,
|
|
45
|
+
suggestProvider,
|
|
46
|
+
} from "./src/provider-readiness.mjs";
|
|
47
|
+
export { ENV_ALIASES, applyEnvAliases, describeEnvVar } from "./src/env-aliases.mjs";
|
|
48
|
+
export { PROFILE_NAMES, PROFILE_DESCRIPTIONS, buildProfileStages, describeProfilePlan } from "./src/profiles.mjs";
|
|
39
49
|
export {
|
|
40
50
|
detectPolyglotStack,
|
|
41
51
|
resolveWorkspaceBoundary,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jules-orchestrator-kit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Zero-dependency safety gatekeeper, test oracle generator, and multi-agent coordination protocol for Google Jules
|
|
3
|
+
"version": "0.53.0",
|
|
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",
|
|
7
7
|
"url": "git+https://github.com/FullThrottle83/jules-orchestrator-kit.git"
|
|
@@ -63,6 +63,9 @@
|
|
|
63
63
|
"keywords": [
|
|
64
64
|
"jules",
|
|
65
65
|
"google-jules",
|
|
66
|
+
"claude-code",
|
|
67
|
+
"codex",
|
|
68
|
+
"gemini-cli",
|
|
66
69
|
"ai-agent",
|
|
67
70
|
"orchestrator",
|
|
68
71
|
"mcp",
|