jules-orchestrator-kit 0.20.0 → 0.22.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.
@@ -0,0 +1,11 @@
1
+ # Janitor Protocol: Technical Debt & Dead Code Elimination
2
+
3
+ You are **Janitor**, a specialist autonomous agent optimized for technical debt elimination, dead code pruning, and strict zero-dependency refactoring.
4
+
5
+ ## Strict Operational Invariants
6
+
7
+ 1. **Zero External Runtime Dependencies**: You are STRICTLY FORBIDDEN from adding third-party npm packages. Use ONLY native Node.js ESM built-in modules (`node:fs`, `node:path`, `node:crypto`, `node:child_process`, `node:os`).
8
+ 2. **Dead Code Elimination**: Prune unused variables, unreachable branches, and redundant helper functions.
9
+ 3. **Atomic Payload Limit**: Keep total patch payload under 75 KB (`git diff | wc -c`).
10
+ 4. **Verification Requirement**: Execute `npm test` and `npm run lint` to ensure 100% of tests pass with 0 lint errors before completing work.
11
+ 5. **No Assert Weakening**: Never weak or remove test assertions to make a test pass.
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  strategy:
13
13
  matrix:
14
- node-version: ['22.x', '24.x']
14
+ node-version: ['20.x', '22.x', '24.x']
15
15
  steps:
16
16
  - name: Checkout repository
17
17
  uses: actions/checkout@v4
package/README.md CHANGED
@@ -5,47 +5,68 @@
5
5
  [![Jules PR Audit](https://github.com/FullThrottle83/jules-orchestrator-kit/actions/workflows/jules-audit.yml/badge.svg)](https://github.com/FullThrottle83/jules-orchestrator-kit/actions/workflows/jules-audit.yml)
6
6
  [![npm version](https://img.shields.io/npm/v/jules-orchestrator-kit.svg)](https://www.npmjs.com/package/jules-orchestrator-kit)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![Node.js Version](https://img.shields.io/badge/node-%3E%3D20.0.0-brightgreen.svg)](https://nodejs.org)
8
9
 
9
- > **v0.20.0 Early Community Release Candidate**
10
- > High-volume autonomous orchestration engine for Google Jules. Built specifically to handle 300+ daily sessions and parallel agent swarms with zero external runtime dependencies.
10
+ > **High-Volume Autonomous Orchestration Engine for Google Jules**
11
+ > Built specifically to execute 300+ daily agent sessions and parallel swarms safely. Zero external runtime dependencies. Built strictly on native Node.js 20+ ESM.
12
+
13
+ ---
14
+
15
+ ![Autonomous Orchestration Pipeline](docs/assets/hero-flow.svg?v=3)
11
16
 
12
17
  ---
13
18
 
14
19
  ## ⚡ 2-Minute Quickstart
15
20
 
16
- Get up and running in under 2 minutes:
21
+ Get up and running in under 2 minutes with zero complex configuration:
17
22
 
18
23
  ```bash
19
- # 1. Install or initialize in your target codebase
24
+ # 1. Initialize orchestrator structure in your target codebase
20
25
  npx jules-orchestrator-kit init
21
26
 
22
- # 2. Dispatch a task (Dry-Run mode for testing)
27
+ # 2. Dispatch an autonomous task (Dry-Run mode for local simulation)
23
28
  JULES_DRY_RUN=1 npx agentctl dispatch \
24
29
  --title "Add JWT Validator" \
25
30
  --prompt "Implement JWT validation middleware with unit tests"
26
31
 
27
- # 3. Run security & verification gatekeeper
32
+ # 3. Run the 4-phase security & verification gatekeeper
28
33
  npx agentctl gate
29
34
 
30
- # 4. Start stdio MCP Server (for Cursor, Claude Code, or AGY integration)
35
+ # 4. Connect as a native stdio MCP server (for Claude Code, Cursor, or Antigravity)
31
36
  npx agentctl mcp
32
37
  ```
33
38
 
39
+ > 📖 **Looking for production deployment patterns?**
40
+ > Check out [**EXAMPLES.md**](./EXAMPLES.md) for 6 real-world recipes (Nightly TODO Scanner, Composite CI Action, Multi-Worktree Swarms, OODA Auto-Fix, MCP IDE setup, and Specialist Rosters).
41
+
34
42
  ---
35
43
 
36
- ## 🚀 Built for High-Volume Jules Swarms
44
+ ## 🎯 Why jules-orchestrator-kit?
37
45
 
38
- `jules-orchestrator-kit` provides the production-hardened control plane needed to execute parallel Google Jules agent swarms safely:
46
+ Whether you are dispatching your first automated coding task or managing high-throughput CI/CD swarms across large engineering teams, `jules-orchestrator-kit` provides total operational safety:
39
47
 
40
- - **Parallel Agent Swarms**: Execute multi-agent task batches concurrently with deterministic lock management and automatic collision prevention.
41
- - **Self-Healing OODA Loop**: Automatic test/build verification and repair loop with sliding-window thrash detection to halt non-convergent agent loops ($A \rightarrow B \rightarrow A \rightarrow B$) and preserve API token budgets.
42
- - **4-Phase Security Gatekeeper**: Fails closed on untrusted PRs by verifying Scope (`forbidden_paths`), Diff Payload Size, Secret Entropy (> 3.6 bits), and Trusted Build/Test Execution.
48
+ | Feature | 🐣 For Rookies & Beginners | 🛠️ For Senior Developers & Infrastructure Engineers |
49
+ | :--- | :--- | :--- |
50
+ | **Safety First** | Never breaks `main` branch or pushes failing code. | 4-Phase Safety Gatekeeper fails closed on scope drift, high entropy secrets, or test regressions. |
51
+ | **Token Budget Protection** | Prevents runaway loops from burning API quotas. | Sliding-window OODA thrash detector ($A \rightarrow B \rightarrow A \rightarrow B$) halts non-convergent repair cycles automatically. |
52
+ | **Zero Setup Hassle** | Works out of the box with standard `npm test`. | **Zero External Runtime Dependencies** (`node:fs`, `node:path`, `node:crypto`, `node:child_process`). |
53
+ | **Multi-Agent Swarms** | Run multiple tasks simultaneously without conflict. | Deterministic VFS mutex and 3-way structural merge engine resolve parallel worktree changes cleanly. |
54
+ | **IDE & Tooling** | Seamlessly connects to your favorite editor. | Native Model Context Protocol (MCP) server over memory-bounded stdio streams. |
43
55
 
44
56
  ---
45
57
 
46
- ## ⚙️ Core Technical Architecture
58
+ <details open>
59
+ <summary><b>⚙️ Control Plane Architecture & Deep Dive</b></summary>
60
+
61
+ <br/>
62
+
63
+ ![Control Plane Architecture Layers](docs/assets/architecture-layers.svg?v=3)
47
64
 
48
- Built strictly on native Node.js 18+ ESM with **Zero External Runtime Dependencies** (`"node": ">=18.0.0"`):
65
+ <br/>
66
+
67
+ ![Self-Healing OODA Loop](docs/assets/ooda-loop-cycle.svg?v=3)
68
+
69
+ ### Engine System Highlights
49
70
 
50
71
  - **Linearizable VFS Mutex (`src/state.mjs`)**: Kernel-level directory mutex (`withVfsMutex`) guaranteeing serial linearizability for SHA-256 hash-chained session ledgers under high concurrency.
51
72
  - **PID Recycling & Stale Lock Protection (`src/state.mjs`)**: Linux `/proc/<pid>/stat` launch-time validation prevents false-positive lock reaps from recycled OS process IDs.
@@ -54,46 +75,232 @@ Built strictly on native Node.js 18+ ESM with **Zero External Runtime Dependenci
54
75
  - **TOCTOU & Symlink Defense (`src/security.mjs`)**: `safeAtomicWrite()` uses `O_CREAT | O_EXCL | O_WRONLY` temp files with `fsyncSync` + `renameSync` and `lstatSync`/`realpathSync` symlink checks.
55
76
  - **3-Way Structural AST/JSON Merge (`scripts/jules-merge-swarm.mjs`)**: Pure Node `deepMerge3Way()` algorithm for recursive object and array merges executed in isolated temporary directories (`os.tmpdir()`).
56
77
 
57
- ---
78
+ </details>
79
+
80
+ <details>
81
+ <summary><b>🛡️ Zero-Trust Security Gatekeeper</b></summary>
82
+
83
+ <br/>
84
+
85
+ ![Zero-Trust Security Guarantees](docs/assets/security-shield.svg?v=3)
86
+
87
+ ### The 4-Phase Safety Audit (`agentctl gate`)
88
+
89
+ 1. **Scope Fencing (`forbidden_paths`)**: Ensures agents cannot modify protected files (`package.json`, `.github/`, deployment keys) without explicit overrides.
90
+ 2. **Diff Payload Governor**: Rejects oversized diffs (> 75 KB) to prevent truncation and hidden payload injections.
91
+ 3. **Secret Entropy Scanner**: Scans diffs for high-confidence secrets (AWS keys, Stripe keys, GitHub tokens, SSH private keys) using Shannon Entropy analysis (> 3.6 bits).
92
+ 4. **Trusted Verification Suite**: Executes auto-detected unit tests and linters (`npm test`) to guarantee zero regressions before merging.
93
+
94
+ > [!NOTE]
95
+ > All security rules are fetched strictly from `origin/main` (never untrusted PR branches) to prevent prompt-injection attacks from altering security rules.
96
+
97
+ </details>
98
+
99
+ <details>
100
+ <summary><b>🔌 Model Context Protocol (MCP) & IDE Integration</b></summary>
101
+
102
+ <br/>
103
+
104
+ ![Dual-Way MCP Integration](docs/assets/mcp-integration.svg?v=3)
105
+
106
+ ### Connecting to Claude Desktop, Cursor, or Antigravity
107
+
108
+ Start the native stdio MCP server:
109
+
110
+ ```bash
111
+ npx agentctl mcp
112
+ ```
58
113
 
59
- ## 🛠️ CLI Command Reference (`agentctl`)
114
+ #### MCP Tool Registry Exposed:
115
+ - `dispatch_jules_task`: Dispatch autonomous coding tasks directly from your LLM prompt.
116
+ - `audit_jules_gate`: Execute the 4-phase safety gate against the workspace.
117
+ - `check_risk_tier`: Classify workspace changes into Risk Tiers (R0 Cosmetic to R3 Restricted).
118
+ - `get_jules_status`: Fetch real-time status of active, pending, and completed tasks.
60
119
 
61
- | Command | Usage | Description |
120
+ </details>
121
+
122
+ <details>
123
+ <summary><b>🤖 Specialist Agent Prompt Presets (.agent/prompts/)</b></summary>
124
+
125
+ <br/>
126
+
127
+ Specialized prompt presets enforcement payload limits (< 75 KB) and domain guardrails out of the box:
128
+
129
+ | Preset | Role & Domain | Primary Focus |
62
130
  | :--- | :--- | :--- |
63
- | `agentctl dispatch` | `agentctl dispatch --title "..." --prompt "..."` | Dispatches an autonomous task to Jules |
64
- | `agentctl gate` | `agentctl gate [--fix] [--base main]` | Runs 4-Phase Safety Gatekeeper against workspace |
65
- | `agentctl queue` | `agentctl queue` | Processes pending task queue from `.agent/jules-queue/` |
66
- | `agentctl swarm` | `agentctl swarm` | Executes parallel swarm task queue |
67
- | `agentctl merge-swarm` | `agentctl merge-swarm` | Performs 3-way structural merge on completed swarm PRs |
68
- | `agentctl mcp` | `agentctl mcp` | Starts stdio Model Context Protocol (MCP) server |
69
- | `agentctl doctor` | `agentctl doctor` | Verifies stack configuration, environment, and budget |
131
+ | **`Overseer.md`** | **Architect & Supervisor** | System-wide refactoring, linearizable state, and structural integrity. |
132
+ | **`Bolt.md`** | **Performance Engineer** | Bottleneck elimination, streaming optimization, and low-latency execution. |
133
+ | **`Sentinel.md`** | **Security Auditor** | Vulnerability patching, secret sanitization, and TOCTOU defense. |
134
+ | **`Janitor.md`** | **Technical Debt & Cleanup** | Dead code elimination, unused import pruning, and zero-dependency compliance. |
70
135
 
71
- ---
136
+ ```bash
137
+ # Example: Dispatch a cleanup task using the Janitor preset
138
+ npx agentctl dispatch \
139
+ --prompt "$(cat .agent/prompts/Janitor.md) Prune unused helper methods in src/utils.mjs"
140
+ ```
141
+
142
+ </details>
143
+
144
+ <details>
145
+ <summary><b>⚡ GitHub Actions Composite Action (.github/actions/setup-jules)</b></summary>
146
+
147
+ <br/>
148
+
149
+ Integrate `jules-orchestrator-kit` into any GitHub Actions workflow with 3 lines of YAML:
150
+
151
+ ```yaml
152
+ steps:
153
+ - uses: actions/checkout@v4
154
+ - uses: FullThrottle83/jules-orchestrator-kit/.github/actions/setup-jules@main
155
+ with:
156
+ action: 'gate'
157
+ base_branch: 'main'
158
+ tier: 'ultra'
159
+ env:
160
+ JULES_API_KEY: ${{ secrets.JULES_API_KEY }}
161
+ ```
72
162
 
73
- ## 📋 Exit Code Protocol
163
+ </details>
164
+
165
+ <details>
166
+ <summary><b>🛠️ CLI Command Reference (`agentctl`)</b></summary>
167
+
168
+ <br/>
169
+
170
+ | Command | Usage Example | Description |
171
+ | :--- | :--- | :--- |
172
+ | **`init`** | `npx agentctl init` | Initializes `.agent/` configuration, workflows, and task queue directory |
173
+ | **`dispatch`** | `agentctl dispatch --title "Fix Bug" --prompt "..."` | Dispatches an autonomous task to Google Jules |
174
+ | **`gate`** | `agentctl gate [--fix] [--base main]` | Runs 4-phase safety gatekeeper audit against workspace |
175
+ | **`queue`** | `agentctl queue` | Processes pending task queue sequentially from `.agent/jules-queue/` |
176
+ | **`swarm`** | `agentctl swarm` | Launches parallel multi-agent swarm in isolated git worktrees |
177
+ | **`merge-swarm`** | `agentctl merge-swarm` | Performs 3-way structural merge on completed swarm PRs |
178
+ | **`mcp`** | `agentctl mcp` | Starts stdio Model Context Protocol (MCP) JSON-RPC 2.0 server |
179
+ | **`doctor`** | `agentctl doctor` | Verifies stack configuration, environment keys, and daily token budget |
180
+ | **`scan`** | `agentctl scan` | Scans codebase for `TODO` and `FIXME` comments and generates task queue |
181
+ | **`cleanup`** | `agentctl cleanup` | Audits and cleans up stale git worktrees and temporary state files |
182
+
183
+ </details>
184
+
185
+ <details>
186
+ <summary><b>💳 Subscription Tier Presets (Free / Pro / Ultra)</b></summary>
187
+
188
+ <br/>
189
+
190
+ ![Subscription Tier Presets Matrix](docs/assets/tier-presets.svg?v=3)
191
+
192
+ <br/>
193
+
194
+ Tailor session limits and rate-limiting behavior to your Google Jules API subscription tier:
195
+
196
+ | Tier | `dailyTasks` | `repairAttempts` | `concurrency` | `staggerMs` | Target Usage |
197
+ | :--- | :---: | :---: | :---: | :---: | :--- |
198
+ | **`free`** | `15` | `1` | `1` | `3000 ms` | **Hobby / Free Tier:** Conserves quota, prevents HTTP 429 rate limits. |
199
+ | **`pro`** | `100` | `2` | `2` | `1500 ms` | **Developer Pro:** Balanced throughput for everyday work. |
200
+ | **`ultra`** *(default)* | `300` | `3` | `3` | `1000 ms` | **Swarm / Enterprise:** Maximum parallel throughput & CI/CD. |
201
+
202
+ **How to activate:**
203
+ - **Environment Variable:** `export JULES_TIER=free` (or set in `.env`)
204
+ - **Config File (`.agent/jules.yml`):** Set `tier: free`
205
+
206
+ </details>
207
+
208
+ <details>
209
+ <summary><b>📝 Configuration Reference (`.agent/jules.yml`)</b></summary>
210
+
211
+ <br/>
212
+
213
+ Auto-generated by `agentctl init` at the root of your project:
214
+
215
+ ```yaml
216
+ version: 2
217
+ tier: "pro" # Options: free, pro, ultra (default: ultra)
218
+ test_cmd: "npm test"
219
+ build_cmd: "npm run build"
220
+ forbidden_paths:
221
+ - ".github/"
222
+ - "package.json"
223
+ - ".agent/jules.yml"
224
+ allow_paths: []
225
+ limits:
226
+ dailyTasks: 300
227
+ repairAttempts: 3
228
+ diffKb: 75
229
+ ```
230
+
231
+ </details>
232
+
233
+ <details>
234
+ <summary><b>🚦 Exit Code Registry & Troubleshooting</b></summary>
235
+
236
+ <br/>
74
237
 
75
238
  Standardized exit codes enforced across all CLI utilities:
76
239
 
77
- | Code | Status | Description |
240
+ | Exit Code | Status | Description & Immediate Action |
78
241
  | :---: | :--- | :--- |
79
- | `0` | **Success** | Task completed cleanly; verification passed 100%. |
80
- | `1` | **Arg / Pre-Dispatch Failure** | Invalid arguments, prompt > 50 KB, or pre-dispatch error. |
81
- | `2` | **API / Network Failure** | Jules API rate-limit (429), `FAILED_PRECONDITION`, or timeout. |
82
- | `3` | **Scope Violation** | Attempted modification of protected/forbidden files. |
83
- | `4` | **OODA Exhausted / Regression** | Verification failed after max repair attempts or thrash loop. |
84
- | `5` | **Diff Payload Exceeded** | Git diff exceeds payload budget (`limits.diffKb`, default 75 KB). |
85
- | `6` | **Secret Detected** | High-confidence secret or token detected in patch diff. |
242
+ | `0` | **Success** | Task completed cleanly; PR opened or verification passed. |
243
+ | `1` | **Pre-Dispatch / Arg Error** | Invalid arguments, prompt > 50 KB, or pre-dispatch validation error. |
244
+ | `2` | **API / Network Failure** | Jules API rate-limit (HTTP 429), `FAILED_PRECONDITION` quota, or timeout. |
245
+ | `3` | **Scope Violation** | Attempted modification of restricted files (`.github/`, command files, agent rules). |
246
+ | `4` | **OODA Exhausted / Thrash** | Verification suite failed after 3 repair attempts or hit deterministic regression. |
247
+ | `5` | **Diff Payload Limit** | Post-change git diff exceeds payload budget (`limits.diffKb`, default 75 KB). |
248
+ | `6` | **Secret Detected** | High-confidence secret or private key detected in patch diff. |
86
249
  | `7` | **Budget Exhausted** | Daily task session quota limit reached (`limits.dailyTasks`, default 300). |
87
250
 
251
+ </details>
252
+
253
+ <details>
254
+ <summary><b>🔐 Environment Variables Reference</b></summary>
255
+
256
+ <br/>
257
+
258
+ | Variable | Description | Default |
259
+ | :--- | :--- | :--- |
260
+ | `JULES_API_KEY` | Google Jules REST API key | *(none)* |
261
+ | `JULES_REPO` | Target GitHub Repository (`owner/repo`) | Auto-detected from `git remote` |
262
+ | `JULES_TIER` | Subscription tier preset (`free`, `pro`, `ultra`) | `ultra` |
263
+ | `JULES_DRY_RUN` | Set to `1` or `true` for dry-run simulation mode | `false` |
264
+ | `JULES_DAILY_BUDGET` | Custom daily session budget limit | `300` |
265
+ | `JULES_MAX_DIFF_KB` | Custom git diff payload limit in KB | `75` |
266
+ | `JULES_ALLOW_COMMAND_FILE_CHANGES` | Allow PR changes to command files (`package.json`, etc.) | `false` |
267
+ | `JULES_ALLOW_AGENT_RULE_CHANGES` | Allow PR changes to agent rule files (`AGENTS.md`, etc.) | `false` |
268
+ | `BASE_BRANCH` | Base branch for PR Audits & Merge-Base checks | `main` |
269
+ | `NO_COLOR` | Set to `true` to disable ANSI color output | `false` |
270
+
271
+ </details>
272
+
273
+ <details>
274
+ <summary><b>🌐 Supported Tech Stacks & Auto-Detection</b></summary>
275
+
276
+ <br/>
277
+
278
+ The orchestrator automatically infers verification and build commands across ecosystems:
279
+
280
+ | Stack / Ecosystem | Manifest File | Inferred Test Command | Inferred Build Command |
281
+ | :--- | :--- | :--- | :--- |
282
+ | **Turborepo** | `turbo.json` | `npx turbo run test` | `npx turbo run build` |
283
+ | **pnpm Workspace** | `pnpm-workspace.yaml` | `pnpm test` | `pnpm build` |
284
+ | **Nx Workspace** | `nx.json` | `npx nx run-many -t test` | `npx nx run-many -t build` |
285
+ | **JavaScript / TypeScript** | `package.json` | `npm test` | `npm run build` |
286
+ | **Rust** | `Cargo.toml` | `cargo test --workspace` | `cargo build` |
287
+ | **Go** | `go.mod` | `go test ./...` | `go build ./...` |
288
+ | **Python** | `pyproject.toml` | `pytest` | *(none)* |
289
+ | **Bun / Deno** | `bunfig.toml` / `deno.json` | `bun test` / `deno test` | `bun run build` |
290
+ | **Elixir / Ruby** | `mix.exs` / `Gemfile` | `mix test` / `rake test` | *(standard build)* |
291
+ | **Java / C / C++** | `pom.xml` / `Makefile` | `mvn test` / `make test` | `mvn compile` / `make` |
292
+
293
+ </details>
294
+
88
295
  ---
89
296
 
90
- ## 🤝 Join the Community & Field-Testing
297
+ ## 🤝 Contributing & Standards
91
298
 
92
- We are actively field-testing `v0.20.0` across 300+ daily autonomous sessions and opening the kit to the Google Jules developer community for feedback and contributions!
299
+ We welcome community contributions! Please adhere to our core engineering invariants:
93
300
 
94
- - **Test & Benchmark**: Clone the repository, test your edge cases, and run parallel swarms against your codebases.
95
- - **Report Issues**: Found a bug, state race condition, or edge-case failure? Open an issue on GitHub.
96
- - **Submit PRs**: We welcome contributions! Ensure all additions preserve our Zero Runtime Dependency invariant and pass `npm test` & `npm run lint`.
301
+ 1. **Zero External Runtime Dependencies**: Use ONLY native Node.js ESM built-in modules (`node:fs`, `node:path`, `node:crypto`, `node:child_process`, `node:os`).
302
+ 2. **100% Verification Suite**: All test suites must pass cleanly with 0 errors.
303
+ 3. **Cross-Platform Compatibility**: Always normalize Windows backslashes (`\`) to POSIX slashes (`/`).
97
304
 
98
305
  ### Running Tests Locally
99
306
 
package/bin/agentctl.mjs CHANGED
@@ -13,7 +13,7 @@ const command = args[0];
13
13
 
14
14
  function printHelp() {
15
15
  console.log(`
16
- 🚀 agentctl v0.20.0 — Universal Agent Orchestrator & Safety Gatekeeper
16
+ 🚀 agentctl v0.22.0 — Universal Agent Orchestrator & Safety Gatekeeper
17
17
 
18
18
  Usage: agentctl <command> [options]
19
19
 
@@ -43,7 +43,7 @@ async function main() {
43
43
  }
44
44
 
45
45
  if (command === "version" || command === "--version" || command === "-v") {
46
- console.log("agentctl v0.9.4");
46
+ console.log("agentctl v0.22.0");
47
47
  process.exit(0);
48
48
  }
49
49
 
@@ -223,7 +223,7 @@ async function main() {
223
223
  }
224
224
 
225
225
  case "doctor": {
226
- console.log(`\n🔍 agentctl System Diagnostics (v0.9.0)`);
226
+ console.log(`\n🔍 agentctl System Diagnostics (v0.22.0)`);
227
227
  console.log(`--------------------------------------------------`);
228
228
  console.log(` Project Root : ${root}`);
229
229
  console.log(` Config File : ${config._file || "None (Using defaults)"}`);
package/bin/init.js CHANGED
@@ -145,11 +145,26 @@ const rulesDir = path.join(agentDir, "rules");
145
145
  const queueDir = path.join(agentDir, "jules-queue");
146
146
  const completedQueueDir = path.join(queueDir, "completed");
147
147
  const workflowsDir = path.join(agentDir, "workflows");
148
+ const promptsDir = path.join(agentDir, "prompts");
148
149
 
149
- [agentDir, rulesDir, queueDir, completedQueueDir, workflowsDir].forEach((d) => {
150
+ [agentDir, rulesDir, queueDir, completedQueueDir, workflowsDir, promptsDir].forEach((d) => {
150
151
  if (!fs.existsSync(d)) fs.mkdirSync(d, { recursive: true });
151
152
  });
152
153
 
154
+ // Scaffold .agent/prompts files
155
+ const sourcePromptsDir = path.join(kitRoot, ".agent/prompts");
156
+ if (fs.existsSync(sourcePromptsDir)) {
157
+ const promptFiles = fs.readdirSync(sourcePromptsDir);
158
+ promptFiles.forEach((file) => {
159
+ const srcPrompt = path.join(sourcePromptsDir, file);
160
+ const destPrompt = path.join(promptsDir, file);
161
+ if (!fs.existsSync(destPrompt) || isForce) {
162
+ fs.copyFileSync(srcPrompt, destPrompt);
163
+ }
164
+ });
165
+ console.log("✅ Created: .agent/prompts presets (Overseer, Bolt, Sentinel, Janitor, Task_Template)");
166
+ }
167
+
153
168
  // Scaffold .agent/jules.yml
154
169
  const yamlConfigPath = path.join(agentDir, "jules.yml");
155
170
  if (!fs.existsSync(yamlConfigPath) || isForce) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jules-orchestrator-kit",
3
- "version": "0.20.0",
3
+ "version": "0.22.0",
4
4
  "description": "Orchestration kit for running Google Jules autonomous agents.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -58,10 +58,11 @@
58
58
  "jules:check-asset-integrity": "node scripts/asset-integrity-check.mjs",
59
59
  "jules:risk-tier": "node scripts/risk-tier.mjs",
60
60
  "jules:rules-lint": "node scripts/rules-lint.mjs",
61
+ "release": "node scripts/release.mjs",
61
62
  "lint": "eslint ."
62
63
  },
63
64
  "engines": {
64
- "node": ">=18.0.0"
65
+ "node": ">=20.0.0"
65
66
  },
66
67
  "keywords": [
67
68
  "jules",
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Automated Release Orchestrator script for jules-orchestrator-kit.
5
+ * Verifies test suite, updates git tags, and creates GitHub Release via gh CLI.
6
+ */
7
+
8
+ import { readFileSync, existsSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { execSync } from "node:child_process";
11
+ import { resolveRoot } from "../src/config.mjs";
12
+
13
+ const root = resolveRoot();
14
+ const pkgPath = join(root, "package.json");
15
+ const changelogPath = join(root, "CHANGELOG.md");
16
+
17
+ if (!existsSync(pkgPath)) {
18
+ console.error("❌ Error: package.json not found.");
19
+ process.exit(1);
20
+ }
21
+
22
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
23
+ const version = pkg.version;
24
+ const tagName = `v${version}`;
25
+
26
+ console.log(`🚀 Automated Release Pipeline for ${pkg.name} (${tagName})`);
27
+ console.log("-------------------------------------------------------");
28
+
29
+ // 1. Verify test suite
30
+ console.log("1. Running unit test verification suite...");
31
+ try {
32
+ execSync("npm test", { cwd: root, stdio: "inherit" });
33
+ console.log(" ✅ Test suite passed cleanly.\n");
34
+ } catch (err) {
35
+ console.error("❌ Release Aborted: Test suite failed.");
36
+ process.exit(1);
37
+ }
38
+
39
+ // 2. Extract release notes from CHANGELOG.md
40
+ console.log(`2. Extracting release notes for ${tagName} from CHANGELOG.md...`);
41
+ let notes = "";
42
+ if (existsSync(changelogPath)) {
43
+ const changelog = readFileSync(changelogPath, "utf-8");
44
+ const regex = new RegExp(`## \\[${version}\\][^\\n]*\\n([\\s\\S]*?)(?=\\n## \\[|$)`, "i");
45
+ const match = changelog.match(regex);
46
+ if (match && match[1]) {
47
+ notes = match[1].trim();
48
+ }
49
+ }
50
+
51
+ if (!notes) {
52
+ notes = `Release ${tagName}`;
53
+ console.log(" ⚠️ Warning: Release entry not found in CHANGELOG.md, using default title.");
54
+ } else {
55
+ console.log(" ✅ Extracted release notes from CHANGELOG.md.\n");
56
+ }
57
+
58
+ // 3. Create Git Tag if not exists
59
+ console.log(`3. Checking Git tag ${tagName}...`);
60
+ try {
61
+ const tags = execSync("git tag -l", { cwd: root, encoding: "utf-8" });
62
+ if (tags.split("\n").includes(tagName)) {
63
+ console.log(` ℹ️ Git tag ${tagName} already exists.`);
64
+ } else {
65
+ execSync(`git tag -a ${tagName} -m "${pkg.name} ${tagName}"`, { cwd: root, stdio: "inherit" });
66
+ console.log(` ✅ Created Git tag ${tagName}.`);
67
+ }
68
+ } catch (err) {
69
+ console.error(`❌ Tagging failed: ${err.message}`);
70
+ process.exit(1);
71
+ }
72
+
73
+ // 4. Push git commits and tag to origin
74
+ console.log(`4. Pushing main and tag ${tagName} to origin...`);
75
+ try {
76
+ execSync("git push origin main", { cwd: root, stdio: "inherit" });
77
+ execSync(`git push origin ${tagName}`, { cwd: root, stdio: "inherit" });
78
+ console.log(" ✅ Pushed commits and tag to origin.\n");
79
+ } catch (err) {
80
+ console.error(`❌ Push failed: ${err.message}`);
81
+ process.exit(1);
82
+ }
83
+
84
+ // 5. Create GitHub Release via gh CLI
85
+ console.log(`5. Creating GitHub Release via gh CLI...`);
86
+ try {
87
+ const notesFile = join(root, ".agent", "temp_release_notes.txt");
88
+ const fs = await import("node:fs");
89
+ fs.writeFileSync(notesFile, notes, "utf-8");
90
+
91
+ execSync(`gh release create ${tagName} --title "${tagName}" --notes-file "${notesFile}"`, {
92
+ cwd: root,
93
+ stdio: "inherit",
94
+ });
95
+
96
+ if (fs.existsSync(notesFile)) fs.unlinkSync(notesFile);
97
+ console.log(` 🎉 GitHub Release ${tagName} created successfully!`);
98
+ } catch (err) {
99
+ console.log(` ℹ️ Note: gh release creation finished or already exists.`);
100
+ }
101
+
102
+ console.log("-------------------------------------------------------");
103
+ console.log(`✅ Release ${tagName} Completed Successfully!\n`);
package/src/config.mjs CHANGED
@@ -306,6 +306,9 @@ export const TIER_PRESETS = {
306
306
  * Loads and validates configuration from .agent/config.yml or .agent/jules.yml.
307
307
  */
308
308
  export function loadConfig(root = resolveRoot(), explicitPath = null) {
309
+ if (root === null || root === undefined) {
310
+ root = resolveRoot();
311
+ }
309
312
  const candidates = explicitPath
310
313
  ? [explicitPath]
311
314
  : [join(root, ".agent/config.yml"), join(root, ".agent/jules.yml")];
@@ -328,8 +331,8 @@ export function loadConfig(root = resolveRoot(), explicitPath = null) {
328
331
  const activeTier = String(process.env.JULES_TIER || parsed.tier || "ultra").toLowerCase();
329
332
  const tierLimits = TIER_PRESETS[activeTier] || TIER_PRESETS.ultra;
330
333
 
331
- const envDailyTasks = process.env.JULES_DAILY_BUDGET ? Number(process.env.JULES_DAILY_BUDGET) : null;
332
- const envDiffKb = process.env.JULES_MAX_DIFF_KB ? Number(process.env.JULES_MAX_DIFF_KB) : null;
334
+ const envDailyTasks = process.env.JULES_DAILY_BUDGET !== undefined ? Number(process.env.JULES_DAILY_BUDGET) : null;
335
+ const envDiffKb = process.env.JULES_MAX_DIFF_KB !== undefined ? Number(process.env.JULES_MAX_DIFF_KB) : null;
333
336
 
334
337
  const config = {
335
338
  version: parsed.version || DEFAULTS.version,
@@ -344,8 +347,8 @@ export function loadConfig(root = resolveRoot(), explicitPath = null) {
344
347
  ...DEFAULTS.limits,
345
348
  ...tierLimits,
346
349
  ...(parsed.limits || {}),
347
- ...(envDailyTasks && !isNaN(envDailyTasks) ? { dailyTasks: envDailyTasks } : {}),
348
- ...(envDiffKb && !isNaN(envDiffKb) ? { diffKb: envDiffKb } : {}),
350
+ ...(envDailyTasks !== null && !isNaN(envDailyTasks) ? { dailyTasks: envDailyTasks } : {}),
351
+ ...(envDiffKb !== null && !isNaN(envDiffKb) ? { diffKb: envDiffKb } : {}),
349
352
  },
350
353
  isolation: parsed.isolation || DEFAULTS.isolation,
351
354
  runner: parsed.runner || DEFAULTS.runner,
package/src/engine.mjs CHANGED
@@ -13,7 +13,12 @@ import { createHash } from "node:crypto";
13
13
  export function fingerprintFailureState(failure = {}, root = process.cwd()) {
14
14
  const rawStderr = failure.stderr || failure.stdout || failure.message || "Unknown Error";
15
15
  const normalizedStderr = String(rawStderr)
16
+ .replace(/[\u001b\x1b]\[[0-9;]*[a-zA-Z]/g, "")
17
+ .replace(/\?[^\s"'\)\\]+/g, "<?>")
16
18
  .replace(/:\d+:\d+/g, ":?:?")
19
+ .replace(/:\d+/g, ":?")
20
+ .replace(/\bline \d+/gi, "line ?")
21
+ .replace(/\bcol(umn)? \d+/gi, "col ?")
17
22
  .replace(/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z?\b/g, "<timestamp>")
18
23
  .replace(/\b[0-9a-f]{7,40}\b/g, "<sha>")
19
24
  .replace(/\/[\w\/-]+\//g, "/?/")
package/src/mcp.mjs CHANGED
@@ -207,7 +207,28 @@ export async function handleMcpRequest(request, opts = {}) {
207
207
  }
208
208
 
209
209
  if (toolName === "check_risk_tier") {
210
- const tierResult = classifyRiskTier(args.files || [], { diffLines: args.diffLines || 0 });
210
+ if (!args || !args.files || !Array.isArray(args.files)) {
211
+ return {
212
+ jsonrpc: "2.0",
213
+ id,
214
+ error: { code: -32602, message: "Invalid parameters: 'files' must be an array" },
215
+ };
216
+ }
217
+ if (args.files.some(f => typeof f !== "string")) {
218
+ return {
219
+ jsonrpc: "2.0",
220
+ id,
221
+ error: { code: -32602, message: "Invalid parameters: 'files' must be an array of strings" },
222
+ };
223
+ }
224
+ if (args.diffLines !== undefined && typeof args.diffLines !== "number") {
225
+ return {
226
+ jsonrpc: "2.0",
227
+ id,
228
+ error: { code: -32602, message: "Invalid parameters: 'diffLines' must be a number" },
229
+ };
230
+ }
231
+ const tierResult = classifyRiskTier(args.files, { diffLines: args.diffLines || 0 });
211
232
  return {
212
233
  jsonrpc: "2.0",
213
234
  id,
package/src/risk.mjs CHANGED
@@ -18,6 +18,7 @@ const RESTRICTED_PATH_PATTERNS = [
18
18
  "**/ledger/**",
19
19
  ".github/**",
20
20
  ".githooks/**",
21
+ ".agent/rules/**",
21
22
  "wrangler.jsonc",
22
23
  "pnpm-lock.yaml",
23
24
  "package-lock.json",
package/src/security.mjs CHANGED
@@ -15,7 +15,7 @@ export const HIGH_CONFIDENCE_PATTERNS = [
15
15
  /\bASIA[0-9A-Z]{16}\b/g,
16
16
  /\baws_secret_access_key\s*=\s*['"]?[A-Za-z0-9\/+=]{40}['"]?/g,
17
17
 
18
- /-----BEGIN (?:RSA|DSA|EC|OPENSSH|PRIVATE) KEY-----[\s\S]*?-----END (?:RSA|DSA|EC|OPENSSH|PRIVATE) KEY-----/g,
18
+ /-----BEGIN (?:RSA|DSA|EC|OPENSSH|PRIVATE)(?:\s+PRIVATE)? KEY-----[\s\S]*?-----END (?:RSA|DSA|EC|OPENSSH|PRIVATE)(?:\s+PRIVATE)? KEY-----/g,
19
19
  /PuTTY-User-Key-File-[0-9]:[^\n]+/g,
20
20
 
21
21
  /\bsk_live_[0-9a-zA-Z]{24,99}\b/g,
package/src/webhook.mjs CHANGED
@@ -10,7 +10,12 @@ import { createServer } from "node:http";
10
10
  */
11
11
  export function verifySignature(payload, signatureHeader, secret) {
12
12
  if (!secret) return false; // Fail closed if secret is not configured
13
- if (!signatureHeader || !signatureHeader.startsWith("sha256=")) return false;
13
+ if (typeof signatureHeader !== "string") return false;
14
+ if (!signatureHeader.startsWith("sha256=")) return false;
15
+
16
+ const hexSignature = signatureHeader.slice(7);
17
+ // Verify hexSignature consists strictly of 64 hex characters (0-9, a-f, A-F)
18
+ if (!/^[0-9a-fA-F]{64}$/.test(hexSignature)) return false;
14
19
 
15
20
  const hmac = createHmac("sha256", secret);
16
21
  const bodyBuf = Buffer.isBuffer(payload) ? payload : Buffer.from(payload, "utf-8");
@@ -30,13 +35,16 @@ export function verifySignature(payload, signatureHeader, secret) {
30
35
  * @returns {object} Parsed JSON payload
31
36
  */
32
37
  export function parseWebhookPayload(bodyBuf, contentType = "application/json") {
33
- const str = bodyBuf.toString("utf-8");
38
+ if (!bodyBuf || bodyBuf.length === 0) return {};
39
+ const str = bodyBuf.toString("utf-8").trim();
40
+ if (!str) return {};
41
+
34
42
  if (contentType.includes("application/x-www-form-urlencoded")) {
35
43
  const params = new URLSearchParams(str);
36
44
  const payloadStr = params.get("payload");
37
45
  return payloadStr ? JSON.parse(payloadStr) : {};
38
46
  }
39
- return str ? JSON.parse(str) : {};
47
+ return JSON.parse(str);
40
48
  }
41
49
 
42
50
  /**
@@ -47,37 +55,64 @@ export function parseWebhookPayload(bodyBuf, contentType = "application/json") {
47
55
  * @returns {object} Response summary { handled: boolean, action: string, details: object }
48
56
  */
49
57
  export function routeWebhookEvent(eventType, payload, handlers = {}) {
58
+ const safePayload = payload || {};
59
+
50
60
  if (eventType === "ping") {
51
- return { handled: true, action: "ping", details: { zen: payload.zen, hookId: payload.hook_id } };
61
+ return { handled: true, action: "ping", details: { zen: safePayload.zen, hookId: safePayload.hook_id } };
52
62
  }
53
63
 
54
64
  if (eventType === "pull_request") {
55
- const action = payload.action;
56
- const prNumber = payload.number || payload.pull_request?.number;
57
- const repo = payload.repository?.full_name;
58
- const branch = payload.pull_request?.head?.ref;
59
- const merged = payload.pull_request?.merged || false;
65
+ const action = safePayload.action;
66
+ const prNumber = safePayload.number || safePayload.pull_request?.number;
67
+ const repo = safePayload.repository?.full_name;
68
+ const branch = safePayload.pull_request?.head?.ref;
69
+ const merged = safePayload.pull_request?.merged || false;
60
70
 
61
71
  const details = { action, prNumber, repo, branch, merged };
62
72
  if (typeof handlers.onPullRequest === "function") {
63
- handlers.onPullRequest(details, payload);
73
+ try {
74
+ handlers.onPullRequest(details, safePayload);
75
+ } catch (err) {
76
+ // Safely ignore errors from handlers
77
+ }
64
78
  }
65
79
  return { handled: true, action: `pull_request:${action}`, details };
66
80
  }
67
81
 
68
82
  if (eventType === "workflow_run") {
69
- const action = payload.action;
70
- const name = payload.workflow?.name || payload.workflow_run?.name;
71
- const conclusion = payload.workflow_run?.conclusion;
72
- const repo = payload.repository?.full_name;
83
+ const action = safePayload.action;
84
+ const name = safePayload.workflow?.name || safePayload.workflow_run?.name;
85
+ const conclusion = safePayload.workflow_run?.conclusion;
86
+ const repo = safePayload.repository?.full_name;
73
87
 
74
88
  const details = { action, name, conclusion, repo };
75
89
  if (typeof handlers.onWorkflowRun === "function") {
76
- handlers.onWorkflowRun(details, payload);
90
+ try {
91
+ handlers.onWorkflowRun(details, safePayload);
92
+ } catch (err) {
93
+ // Safely ignore errors from handlers
94
+ }
77
95
  }
78
96
  return { handled: true, action: `workflow_run:${action}`, details };
79
97
  }
80
98
 
99
+ // Fallback status handler for unhandled GitHub event types
100
+ if (typeof handlers.onUnhandled === "function") {
101
+ try {
102
+ handlers.onUnhandled(eventType, safePayload);
103
+ } catch (err) {
104
+ // Safely ignore or log error from fallback handler to prevent crashing
105
+ }
106
+ }
107
+
108
+ if (typeof handlers.onFallback === "function") {
109
+ try {
110
+ handlers.onFallback(eventType, safePayload);
111
+ } catch (err) {
112
+ // Safely ignore or log error from fallback handler to prevent crashing
113
+ }
114
+ }
115
+
81
116
  return { handled: false, action: eventType, details: {} };
82
117
  }
83
118