@hecer/yoke 1.15.1 → 1.16.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.
@@ -2,12 +2,12 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "yoke",
4
4
  "displayName": "Yoke",
5
- "version": "1.15.1",
6
- "description": "Cross-agent coding harness for Claude, Codex, Gemini, Qwen, OpenCode, Kilo and Pi: one curated skill canon plus mechanical safety gates and an autonomous loop via the yoke CLI.",
5
+ "version": "1.16.0",
6
+ "description": "Cross-agent coding harness for Claude, Codex, Gemini, Qwen, OpenCode, Kilo, Pi and Hermes: one curated skill canon plus mechanical safety gates and an autonomous loop via the yoke CLI.",
7
7
  "author": { "name": "HECer", "url": "https://github.com/HECer" },
8
8
  "homepage": "https://github.com/HECer/yoke#readme",
9
9
  "repository": "https://github.com/HECer/yoke",
10
10
  "license": "MIT",
11
- "keywords": ["harness", "cross-agent", "tdd", "code-review", "autonomous-loop", "codex", "gemini-cli", "qwen-code", "opencode", "kilo", "pi"],
11
+ "keywords": ["harness", "cross-agent", "tdd", "code-review", "autonomous-loop", "codex", "gemini-cli", "qwen-code", "opencode", "kilo", "pi", "hermes"],
12
12
  "skills": "./canon/skills/"
13
13
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "yoke",
3
- "version": "1.15.1",
4
- "description": "Cross-agent coding discipline, mechanical gates, and release workflows for seven supported harnesses",
3
+ "version": "1.16.0",
4
+ "description": "Cross-agent coding discipline, mechanical gates, and release workflows for eight supported harnesses",
5
5
  "skills": "./canon/skills/",
6
6
  "hooks": "./hooks/hooks.json"
7
7
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.16.0 — 2026-09-17
4
+
5
+ ### Added
6
+ - Add first-class adapter support for Nous Research's Hermes Agent (`hermes` CLI) as the eighth supported harness.
7
+ - Add non-interactive execution for Hermes via `hermes chat --format stream-json --query-file -`.
8
+ - Map permission profiles to Hermes toolset flags: `safe` to `--toolsets file,terminal`, `read-only` to `--toolsets file`, and `unsafe` to `--yolo`.
9
+ - Add telemetry extraction for Hermes `stream-json` streams: assistant text deltas, token usage (`input`, `output`, `cache_read`, `cache_write`), total cost USD, and active model identities.
10
+ - Recognize successful Hermes `tool_result` events as watchdog progress activity.
11
+ - Add Hermes project marker detection (`.hermes`, `HERMES.md`, `hermes.yaml`, `hermes.json`) and host environment marker detection (`HERMES_SESSION_ID`, `HERMES_CONFIG`, `HERMES_HOME`).
12
+ - Add Hermes retrofit planner generating complete skill packages under `.hermes/skills/`, shared `AGENTS.md` instructions, and a read-only reviewer agent (`.hermes/agents/yoke-reviewer.md`).
13
+ - Add `hermes` to setup CLI options, routing worker presets (`hermes-standard`), fallback review providers, and runner affinity validation.
14
+ - Add Hermes integration documentation in `docs/HARNESSES.md`.
15
+
16
+ ### Changed
17
+ - Update README, package metadata, and manifests to reflect eight supported harnesses across invocation, routing, and retrofit architecture.
18
+ - Synchronize Claude plugin, Codex plugin, Gemini extension, Canon, and npm package versions to 1.16.0.
19
+
20
+ ### Migration and validation limits
21
+ - Run `yoke retrofit . --agent=hermes` or `yoke retrofit . --agent=all` to install native Hermes artifacts. External `hermes` CLI must be installed and configured separately; Yoke does not bundle runtimes or credentials.
22
+ - Hermes runner does not support bare startup mode or native multi-agent delegation; Yoke enforces managed worker and single-flight execution boundaries.
23
+ - Local verification: 1,285 tests passed, two platform-specific tests skipped, 141 test files passed; TypeScript lint/build, Canon validation, documentation metadata check, package dry run, and dependency audit passed.
24
+
3
25
  ## 1.15.1 — 2026-09-16
4
26
 
5
27
  ### Fixed
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  <div align="center">
2
2
 
3
- <h1><img src="https://raw.githubusercontent.com/HECer/yoke/v1.15.1/docs/assets/yoke-logo.png" alt="Yoke" width="100" height="63"></h1>
3
+ <h1><img src="https://raw.githubusercontent.com/HECer/yoke/v1.16.0/docs/assets/yoke-logo.png" alt="Yoke" width="100" height="63"></h1>
4
4
 
5
- <!-- yoke:version:start -->1.15.1<!-- yoke:version:end -->
6
- <!-- yoke:tests:start -->1283<!-- yoke:tests:end -->
5
+ <!-- yoke:version:start -->1.16.0<!-- yoke:version:end -->
6
+ <!-- yoke:tests:start -->1287<!-- yoke:tests:end -->
7
7
  <!-- yoke:skills:start -->34<!-- yoke:skills:end -->
8
- <!-- yoke:agents:start -->Claude | Codex | Gemini | Qwen | OpenCode | Kilo | Pi<!-- yoke:agents:end -->
8
+ <!-- yoke:agents:start -->Claude | Codex | Gemini | Qwen | OpenCode | Kilo | Pi | Hermes<!-- yoke:agents:end -->
9
9
 
10
- ### One harness, seven agents — and zero trust in "done."
10
+ ### One harness, eight agents — and zero trust in "done."
11
11
 
12
- **Yoke** installs one curated canon of skills, **mechanical safety gates**, and tool wiring into any project — natively for **Claude Code, OpenAI Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo, and Pi coding agent**. Its opt-in loop implements and verifies stories before committing. Independent review and browser proofs run when configured; screenshots and videos require the browser smoke gate.
12
+ **Yoke** installs one curated canon of skills, **mechanical safety gates**, and tool wiring into any project — natively for **Claude Code, OpenAI Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo, Pi coding agent, and Hermes Agent**. Its opt-in loop implements and verifies stories before committing. Independent review and browser proofs run when configured; screenshots and videos require the browser smoke gate.
13
13
 
14
14
  [![npm](https://img.shields.io/npm/v/%40hecer%2Fyoke?logo=npm&color=CB3837)](https://www.npmjs.com/package/@hecer/yoke)
15
15
  [![npm downloads](https://img.shields.io/npm/dm/%40hecer%2Fyoke?logo=npm)](https://www.npmjs.com/package/@hecer/yoke)
@@ -17,8 +17,8 @@
17
17
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](#-license)
18
18
  ![Node](https://img.shields.io/badge/node-%E2%89%A520-339933?logo=node.js&logoColor=white)
19
19
  ![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)
20
- ![Tests](https://img.shields.io/badge/tests-1283%20defined-blue.svg)
21
- ![Agents](https://img.shields.io/badge/agents-Claude%20%7C%20Codex%20%7C%20Gemini%20%7C%20Qwen%20%7C%20OpenCode%20%7C%20Kilo%20%7C%20Pi-8A2BE2)
20
+ ![Tests](https://img.shields.io/badge/tests-1287%20defined-blue.svg)
21
+ ![Agents](https://img.shields.io/badge/agents-Claude%20%7C%20Codex%20%7C%20Gemini%20%7C%20Qwen%20%7C%20OpenCode%20%7C%20Kilo%20%7C%20Pi%20%7C%20Hermes-8A2BE2)
22
22
  ![Built with TDD](https://img.shields.io/badge/built%20with-TDD%20%2B%20review-ff69b4.svg)
23
23
 
24
24
  **Install:** [`npm i -g @hecer/yoke`](https://www.npmjs.com/package/@hecer/yoke)
@@ -104,7 +104,7 @@ Agentic coding in 2026 fails in four well-documented ways. Yoke answers each one
104
104
  | 🌀 **Overnight loops going off the rails** | Raw Ralph-loop users "wake up to broken codebases that don't compile" | Yoke is **"Ralph, but with gates"**: clean-worktree gate, acceptance-criteria gate, green-tests gate, review gate, per-story worktree isolation, idle-timeout watchdog, single-flight lock, commit integrity. |
105
105
  | 😵 **Review fatigue** | AI adoption nearly doubles PR volume and review time; humans start skimming | **`yoke review`**: a second model writes a schema-validated pass/fail verdict — chainable into verify, pre-push, or CI. Cross-model review catches what self-review misses. |
106
106
 
107
- **Who it's for:** anyone driving Claude Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo or Pi on real projects — especially if you use more than one, want autonomous runs you can trust, or are tired of "done" meaning "probably". Greenfield (`yoke new`) and brownfield (`yoke retrofit`) both work.
107
+ **Who it's for:** anyone driving Claude Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo, Pi or Hermes on real projects — especially if you use more than one, want autonomous runs you can trust, or are tired of "done" meaning "probably". Greenfield (`yoke new`) and brownfield (`yoke retrofit`) both work.
108
108
 
109
109
  **Who it's not for:** if you want a chat pair-programmer with no process, you don't need a harness. Yoke is for shipping with discipline.
110
110
 
@@ -159,7 +159,7 @@ The canon is also packaged as a Claude Code plugin — the repo is its own marke
159
159
  /plugin install yoke@yoke
160
160
  ```
161
161
 
162
- That gives you all canon skills under the `yoke:` namespace (e.g. `yoke:tdd`, `yoke:review`) inside Claude Code — no retrofit needed. The `yoke` CLI (loop, gates, retrofit for Codex/Gemini/Qwen/OpenCode/Kilo/Pi) still comes from `npm i -g @hecer/yoke`. Gemini CLI users can likewise `gemini extensions install https://github.com/HECer/yoke`.
162
+ That gives you all canon skills under the `yoke:` namespace (e.g. `yoke:tdd`, `yoke:review`) inside Claude Code — no retrofit needed. The `yoke` CLI (loop, gates, retrofit for Codex/Gemini/Qwen/OpenCode/Kilo/Pi/Hermes) still comes from `npm i -g @hecer/yoke`. Gemini CLI users can likewise `gemini extensions install https://github.com/HECer/yoke`.
163
163
 
164
164
  For Codex, no preinstalled skill is required: run `npx @hecer/yoke setup .` in a terminal, or
165
165
  ask Codex to run the six-question Yoke setup flow. The retrofit writes native skills to
@@ -186,7 +186,7 @@ Auto-upgrade is deliberately **not** the default: a gate harness shouldn't chang
186
186
 
187
187
  ## 🤖 Driving it through an agent
188
188
 
189
- Yoke is meant to be operated *by* your coding agent — after a retrofit, the agent has the skills, the safety policy, and the routing, so it knows the methodology. Copy-paste prompts (identical wording works for Claude Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo, and Pi):
189
+ Yoke is meant to be operated *by* your coding agent — after a retrofit, the agent has the skills, the safety policy, and the routing, so it knows the methodology. Copy-paste prompts (identical wording works for Claude Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo, Pi, and Hermes):
190
190
 
191
191
  > **Set it up** — *"Set up Yoke in this project. Ask me the Yoke setup questions one at a time with your recommendation, then run `yoke setup . --yes` with the selected host, agents, code graph, loop, runner, and decision policy. Commit in my configured identity."*
192
192
 
@@ -210,11 +210,11 @@ Yoke's CLI is deterministic and chainable by design: an agent (or a shell `&&`)
210
210
  | `yoke projects add\|list\|remove` | Register a project, list registrations or remove a reference by ID | `0` · `2` invalid/unavailable |
211
211
  | `yoke check [dir] [--json] [--requirement=] [--protect [--refresh]]` | Execute acceptance checks or explicitly pin their infrastructure | `0` passed/pinned · `1` failed · `2` unverified/unavailable |
212
212
  | `yoke goal set\|run\|resume\|pause\|status\|handoff\|budget [dir]` | Durable objectives, provider handoff, protected checks and checkpoint budgets | run/resume: `0` complete · `1` unfinished · `2` unavailable |
213
- | `yoke setup [dir] [--yes] [--host=] [--agent=] [--runner=] [--code-graph=] [--code-intelligence=off\|shadow\|active] [--decision-policy=] [--loop\|--no-loop] [--routing\|--no-routing] [--model-provider=deepseek,kimi]` | Shared setup for all seven harnesses; optional federated code intelligence and DeepSeek/Kimi API profiles | `0` · `1` invalid setup |
213
+ | `yoke setup [dir] [--yes] [--host=] [--agent=] [--runner=] [--code-graph=] [--code-intelligence=off\|shadow\|active] [--decision-policy=] [--loop\|--no-loop] [--routing\|--no-routing] [--model-provider=deepseek,kimi]` | Shared setup for all eight harnesses; optional federated code intelligence and DeepSeek/Kimi API profiles | `0` · `1` invalid setup |
214
214
  | `yoke code-intelligence-server [--workspace=] [--mode=off\|shadow\|active]` | Serve the single Yoke-controlled MCP facade for federated code intelligence | `0` · `1` invalid/unavailable |
215
215
  | `yoke validate [canonDir]` | Validate the canon (schema, frontmatter, templates) | `0` valid · `1` errors |
216
216
  | `yoke new <dir> [--idea=] [--agent=] [--runner=] [--loop]` | Greenfield bootstrap: git init → scaffold → retrofit → context → PRD (drafted from `--idea`) → committed | `0` · `1` usage / non-empty dir / draft failed (scaffold survives) · `2` draft agent unavailable |
217
- | `yoke retrofit [dir] [--agent=claude,codex,gemini,qwen,opencode,kilo,pi\|all] [--code-graph=graphify\|serena] [--code-intelligence=off\|shadow\|active] [--loop]` | Install/update the harness for the selected agents, non-destructively | `0` |
217
+ | `yoke retrofit [dir] [--agent=claude,codex,gemini,qwen,opencode,kilo,pi,hermes\|all] [--code-graph=graphify\|serena] [--code-intelligence=off\|shadow\|active] [--loop]` | Install/update the harness for the selected agents, non-destructively | `0` |
218
218
  | `yoke prd draft [dir] --idea= [--runner=] [--force]` | Idea → 5–12 stories with testable acceptance criteria | `0` · `1` invalid/guarded · `2` agent unavailable |
219
219
  | `yoke prd check [dir]` | PRD lint gate (schema, dependencies, cycles, duplicate ids, acceptance) | `0` valid · `1` violations |
220
220
  | `yoke change add\|status [dir] [--idea=]` | Queue a change at any time; the loop turns it into append-only stories at the next safe boundary | `0` · `1` invalid inbox/request |
@@ -234,7 +234,7 @@ Three excellent projects, three different jobs. Honest version:
234
234
  | | [superpowers](https://github.com/obra/superpowers) (obra) | [gstack](https://github.com/garrytan/gstack) (Garry Tan) | **Yoke** |
235
235
  |---|---|---|---|
236
236
  | **What it is** | The canonical *skills methodology*: brainstorm → plan → TDD → review as composable skills | A *software factory* for Claude Code: ~40 role skills (QA, CSO, ship…) + a real Chromium browser layer | A *cross-agent harness*: one canon → native installs, plus a gated autonomous loop |
237
- | **Agents** | Claude Code first | Claude Code + hosts like Codex/Cursor/Kiro — **no Gemini CLI** | **Claude Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo, Pi** from one source of truth |
237
+ | **Agents** | Claude Code first | Claude Code + hosts like Codex/Cursor/Kiro — **no Gemini CLI** | **Claude Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo, Pi, Hermes** from one source of truth |
238
238
  | **Enforcement** | Advisory — skills *describe* the discipline; following them is up to the agent | Skill-driven; browser QA is genuinely real | **Mechanical** — gates live in code: clean tree, acceptance criteria, green tests, review verdict, commit integrity |
239
239
  | **Autonomy** | Interactive sessions | Interactive slash-commands (`/qa`, `/ship`, …) | Opt-in **Ralph loop** with watchdog, worktree isolation, single-flight lock, per-story proofs |
240
240
  | **Visual QA** | — | **Best-in-class**: live browser daemon (Chromium/CDP) with deep interactive QA | Built-in `flow-smoke` gate: screenshots always, video on failure, labelled per story — lighter, but *enforced* and cross-agent |
@@ -242,7 +242,7 @@ Three excellent projects, three different jobs. Honest version:
242
242
  | **Footprint** | Markdown skills (plugin) | ~230 MB with browser runtime; hourly auto-update | Node CLI + markdown canon; Playwright only if you use flow-smoke, resolved **from your project** |
243
243
  | **License** | MIT | MIT | MIT |
244
244
 
245
- **They compose — use all three where they're strongest.** Yoke's canon *ships* the superpowers methodology natively for all seven agents (13 skills, [attributed](canon/skills/ATTRIBUTION.md)). And if gstack is installed, `yoke retrofit` detects it and adds a routing note to `CLAUDE.md` telling Claude to prefer gstack's live-browser `/qa`, `/cso`, and ship pipeline for what Yoke deliberately doesn't bundle — no dependency, no conflict, and non-Claude artifacts stay uniform.
245
+ **They compose — use all three where they're strongest.** Yoke's canon *ships* the superpowers methodology natively for all eight agents (13 skills, [attributed](canon/skills/ATTRIBUTION.md)). And if gstack is installed, `yoke retrofit` detects it and adds a routing note to `CLAUDE.md` telling Claude to prefer gstack's live-browser `/qa`, `/cso`, and ship pipeline for what Yoke deliberately doesn't bundle — no dependency, no conflict, and non-Claude artifacts stay uniform.
246
246
 
247
247
  **Choose Yoke when** you run more than one agent, want autonomy you can audit (gates + proofs + logs), or want one place to maintain your team's methodology. **Choose gstack when** you live 100% in Claude Code and want the deepest interactive browser QA. **Choose superpowers when** you want the methodology alone, interactively, in Claude Code — or just use it *through* Yoke.
248
248
 
@@ -262,6 +262,7 @@ flowchart TD
262
262
  Skill --> OpenCode["OpenCode<br/>AGENTS.md · opencode.json · .opencode/skills"]
263
263
  Skill --> Kilo["Kilo<br/>AGENTS.md · kilo.jsonc · .kilo/skills"]
264
264
  Skill --> Pi["Pi<br/>AGENTS.md · .pi/settings.json · .pi/skills"]
265
+ Skill --> Hermes["Hermes<br/>AGENTS.md · .hermes/skills · yoke-reviewer.md"]
265
266
  Loop["🤖 yoke loop — autonomous Ralph loop<br/>gates · verify · review · isolation · proofs"]
266
267
  Claude -. drives .-> Loop
267
268
  Codex -. drives .-> Loop
@@ -270,6 +271,7 @@ flowchart TD
270
271
  OpenCode -. drives .-> Loop
271
272
  Kilo -. drives .-> Loop
272
273
  Pi -. drives .-> Loop
274
+ Hermes -. drives .-> Loop
273
275
  ```
274
276
 
275
277
  Three layers — **Canon** (`yoke validate`) → **Retrofit** (`yoke retrofit`) → **Loop** (`yoke loop`) — on top of a durable **Context layer** (`yoke context`).
@@ -285,6 +287,7 @@ Three layers — **Canon** (`yoke validate`) → **Retrofit** (`yoke retrofit`)
285
287
  | **OpenCode** | Complete skill packages under `.opencode/skills/`, shared `AGENTS.md`, merged `opencode.json` (instructions + local MCP), and `.opencode/agents/yoke-reviewer.md` |
286
288
  | **Kilo** | Complete skill packages under `.kilo/skills/`, shared `AGENTS.md`, merged `kilo.jsonc` (instructions + local MCP), and `.kilo/agents/yoke-reviewer.md` |
287
289
  | **Pi** | Complete skill packages under `.pi/skills/`, shared `AGENTS.md`, and merged `.pi/settings.json`; Pi's tool allowlists are applied at invocation time |
290
+ | **Hermes** | Complete skill packages under `.hermes/skills/`, shared `AGENTS.md`, and `.hermes/agents/yoke-reviewer.md` |
288
291
 
289
292
  > **rtk integration:** Claude receives its PreToolUse hook; Codex receives a native hook adapter around `rtk hook check`; Gemini retains instruction-mode fallback where its CLI has no equivalent command-rewrite lifecycle.
290
293
 
@@ -301,11 +304,11 @@ Three layers — **Canon** (`yoke validate`) → **Retrofit** (`yoke retrofit`)
301
304
 
302
305
  `yoke retrofit` installs all of these into each selected agent natively. Provenance is credited in [`canon/skills/ATTRIBUTION.md`](canon/skills/ATTRIBUTION.md).
303
306
 
304
- To stop overlapping skills from auto-invoking against each other, `canon/AGENTS.md` carries a **skill routing & precedence** block (methodology before role; one canonical entrypoint per concern — e.g. pre-merge code review is always `review`), emitted into all seven agents.
307
+ To stop overlapping skills from auto-invoking against each other, `canon/AGENTS.md` carries a **skill routing & precedence** block (methodology before role; one canonical entrypoint per concern — e.g. pre-merge code review is always `review`), emitted into all eight agents.
305
308
 
306
309
  Each manifest entry also declares `invocation: auto|manual`. Retrofit translates that intent into
307
310
  the provider's native controls: Claude and Qwen disable model invocation for manual skills, Codex writes
308
- `agents/openai.yaml`, Gemini lists only automatic skills in its generated index, and OpenCode/Kilo/Pi
311
+ `agents/openai.yaml`, Gemini lists only automatic skills in its generated index, and OpenCode/Kilo/Pi/Hermes
309
312
  receive complete project-local skill packages. Validation
310
313
  rejects conflicting package metadata and broken local Markdown links before anything is installed.
311
314
 
@@ -608,8 +611,8 @@ Loop runners disable native delegation in Codex, Claude, Gemini, Qwen, OpenCode
608
611
  the Yoke worker budget. Integration retains its execution slot until the candidate lands.
609
612
 
610
613
  **Provider support:** adaptive routing uses Yoke's shared provider adapter and works with Claude
611
- Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo and Pi, including mixed-provider worker lists.
612
- Internal contract tests cover invocation and routing behavior for all seven providers. The measured
614
+ Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo, Pi, and Hermes, including mixed-provider worker lists.
615
+ Internal contract tests cover invocation and routing behavior for all eight providers. The measured
613
616
  performance evidence below is intentionally **Codex-only**; it does not claim equivalent savings
614
617
  until authenticated, repeated in-the-wild runs exist for each provider.
615
618
 
@@ -910,7 +913,7 @@ src/
910
913
  canon/ # manifest schema + validator (yoke validate)
911
914
  code-intelligence/ # federated MCP facade, adapters, snapshots and guarded edits
912
915
  change/ # append-only change inbox · planning · independent coverage review
913
- retrofit/ # detect · plan · apply · planners (all seven harnesses) · tools
916
+ retrofit/ # detect · plan · apply · planners (all eight harnesses) · tools
914
917
  loop/ # prd · gates · runner · verify · git/worktree · loop · run-command · lock · cleanup
915
918
  quality/ # reference collection · blind critic · bounded repair · candidate comparison
916
919
  new/ # yoke new — greenfield bootstrap
@@ -931,7 +934,7 @@ release provenance.
931
934
  ## 🧪 Development
932
935
 
933
936
  ```bash
934
- npm test # vitest (1283 tests)
937
+ npm test # vitest (1287 tests)
935
938
  npm run build # tsc, no emit errors
936
939
  npm run yoke -- validate canon
937
940
  ```
@@ -1,6 +1,6 @@
1
1
  name: yoke-canon
2
- version: 1.15.1
3
- agents: [claude, codex, gemini, qwen, opencode, kilo, pi]
2
+ version: 1.16.0
3
+ agents: [claude, codex, gemini, qwen, opencode, kilo, pi, hermes]
4
4
  skills:
5
5
  - { id: tdd, path: skills/tdd, kind: methodology, invocation: auto }
6
6
  - { id: yoke-retrofit, path: skills/yoke-retrofit, kind: methodology, invocation: auto }
@@ -7,7 +7,7 @@ description: Use when asked to "retrofit", "yoke this project", or set up the Yo
7
7
 
8
8
  Set up or update Yoke through the shared `yoke setup` contract.
9
9
 
10
- 1. Inspect the project and identify the current host (`claude`, `codex`, `gemini`, `qwen`, `opencode`, `kilo`, or `pi`).
10
+ 1. Inspect the project and identify the current host (`claude`, `codex`, `gemini`, `qwen`, `opencode`, `kilo`, `pi`, or `hermes`).
11
11
  2. Ask these setup questions one at a time and give a direct recommendation:
12
12
  - target agents (recommend the current host; use `all` for deliberately cross-agent projects),
13
13
  - code-graph tool,
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- export const AgentSchema = z.enum(['claude', 'codex', 'gemini', 'qwen', 'opencode', 'kilo', 'pi']);
2
+ export const AgentSchema = z.enum(['claude', 'codex', 'gemini', 'qwen', 'opencode', 'kilo', 'pi', 'hermes']);
3
3
  export const PermissionProfileSchema = z.enum(['safe', 'unsafe', 'read-only']);
4
4
  export const ModelSelectionSchema = z.object({
5
5
  provider: z.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/).optional(),
@@ -13,6 +13,8 @@ export function detectHostAgent(env = process.env) {
13
13
  return 'opencode';
14
14
  if (env.KILO_CLIENT || env.KILO_CONFIG || env.KILO_CONFIG_DIR)
15
15
  return 'kilo';
16
+ if (env.HERMES_SESSION_ID || env.HERMES_CONFIG)
17
+ return 'hermes';
16
18
  if (env.CODEX_HOME)
17
19
  return 'codex';
18
20
  if (env.CLAUDE_CONFIG_DIR)
@@ -21,6 +23,8 @@ export function detectHostAgent(env = process.env) {
21
23
  return 'gemini';
22
24
  if (env.QWEN_CLI_HOME)
23
25
  return 'qwen';
26
+ if (env.HERMES_HOME)
27
+ return 'hermes';
24
28
  return undefined;
25
29
  }
26
30
  export function resolveRunnerAgent(config, explicit, host) {
@@ -38,6 +38,13 @@ const argsFor = (agent, permissions) => {
38
38
  args.push('--tools', permissions === 'read-only' ? 'read,grep,find,ls' : 'read,bash,edit,write');
39
39
  return args;
40
40
  }
41
+ if (agent === 'hermes') {
42
+ const args = ['chat', '--format', 'stream-json', '--query-file', '-'];
43
+ if (permissions === 'unsafe')
44
+ return [...args, '--yolo'];
45
+ const toolsets = permissions === 'read-only' ? 'file' : 'file,terminal';
46
+ return [...args, '--toolsets', toolsets];
47
+ }
41
48
  if (permissions === 'unsafe')
42
49
  return ['--yolo', '--output-format', 'stream-json'];
43
50
  const approval = permissions === 'read-only' ? 'plan' : 'auto_edit';
@@ -45,7 +52,7 @@ const argsFor = (agent, permissions) => {
45
52
  };
46
53
  export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe', selection = {}, output = {}) {
47
54
  const parsedSelection = ModelSelectionSchema.parse(selection);
48
- if (['opencode', 'kilo', 'pi'].includes(agent) && parsedSelection.reasoningEffort && parsedSelection.variant && parsedSelection.reasoningEffort !== parsedSelection.variant) {
55
+ if (['opencode', 'kilo', 'pi', 'hermes'].includes(agent) && parsedSelection.reasoningEffort && parsedSelection.variant && parsedSelection.reasoningEffort !== parsedSelection.variant) {
49
56
  throw new Error(`${agent} reasoningEffort and variant selections must match`);
50
57
  }
51
58
  if (agent === 'gemini' && parsedSelection.bare)
@@ -60,9 +67,13 @@ export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe'
60
67
  throw new Error('Qwen does not support the reasoningEffort selection');
61
68
  if (agent === 'qwen' && parsedSelection.nativeMultiAgent === true)
62
69
  throw new Error('Qwen does not support enabling the nativeMultiAgent selection');
63
- if (parsedSelection.provider && !['opencode', 'kilo', 'pi'].includes(agent))
70
+ if (agent === 'hermes' && parsedSelection.bare)
71
+ throw new Error('Hermes does not support the bare startup selection');
72
+ if (agent === 'hermes' && parsedSelection.nativeMultiAgent === true)
73
+ throw new Error('Hermes does not support enabling the nativeMultiAgent selection');
74
+ if (parsedSelection.provider && !['opencode', 'kilo', 'pi', 'hermes'].includes(agent))
64
75
  throw new Error(`${agent} does not support an explicit provider selection`);
65
- if (parsedSelection.variant && !['opencode', 'kilo', 'pi'].includes(agent))
76
+ if (parsedSelection.variant && !['opencode', 'kilo', 'pi', 'hermes'].includes(agent))
66
77
  throw new Error(`${agent} does not support a model variant selection`);
67
78
  const args = argsFor(agent, permissions);
68
79
  if (output.schemaFile !== undefined || output.jsonSchema !== undefined) {
@@ -77,10 +88,10 @@ export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe'
77
88
  throw new Error('Inline structured output is unsupported by the Windows provider shell shim');
78
89
  args.push('--json-schema', schema);
79
90
  }
80
- else if (!['opencode', 'kilo', 'pi'].includes(agent))
91
+ else if (!['opencode', 'kilo', 'pi', 'hermes'].includes(agent))
81
92
  throw new Error(`${agent} structured output schema requires ${agent === 'codex' ? 'schemaFile' : agent === 'claude' ? 'jsonSchema' : 'a supported native schema option (unavailable)'}`);
82
93
  }
83
- if (parsedSelection.provider && agent === 'pi')
94
+ if (parsedSelection.provider && (agent === 'pi' || agent === 'hermes'))
84
95
  args.push('--provider', parsedSelection.provider);
85
96
  if (parsedSelection.model) {
86
97
  const qualified = agent === 'qwen' && parsedSelection.model.includes('::')
@@ -107,6 +118,8 @@ export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe'
107
118
  args.push('--variant', parsedSelection.reasoningEffort);
108
119
  else if (agent === 'pi')
109
120
  args.push('--thinking', parsedSelection.reasoningEffort);
121
+ else if (agent === 'hermes')
122
+ args.push('--reasoning-effort', parsedSelection.reasoningEffort);
110
123
  }
111
124
  if (parsedSelection.variant) {
112
125
  if ((agent === 'opencode' || agent === 'kilo') && !parsedSelection.reasoningEffort)
@@ -117,6 +130,12 @@ export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe'
117
130
  if (!parsedSelection.reasoningEffort)
118
131
  args.push('--thinking', parsedSelection.variant);
119
132
  }
133
+ else if (agent === 'hermes') {
134
+ if (parsedSelection.reasoningEffort && parsedSelection.reasoningEffort !== parsedSelection.variant)
135
+ throw new Error('Hermes reasoningEffort and variant selections must match');
136
+ if (!parsedSelection.reasoningEffort)
137
+ args.push('--reasoning-effort', parsedSelection.variant);
138
+ }
120
139
  }
121
140
  if (agent === 'codex' && parsedSelection.nativeMultiAgent === false)
122
141
  args.push('--disable', 'multi_agent');
@@ -134,6 +153,8 @@ export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe'
134
153
  args.push('--pure');
135
154
  else if (agent === 'pi')
136
155
  throw new Error('Pi does not support the bare startup selection');
156
+ else if (agent === 'hermes')
157
+ throw new Error('Hermes does not support the bare startup selection');
137
158
  }
138
159
  if (agent === 'gemini' && parsedSelection.nativeMultiAgent === false) {
139
160
  return { command: process.execPath, args: [fileURLToPath(new URL('../../hooks/bounded-gemini.mjs', import.meta.url)), ...args], input: prompt, cwd };
@@ -38,7 +38,7 @@ export function inspectProviderEvent(line) {
38
38
  return { progress: (event.type === 'item.completed' && ((item?.type === 'command_execution' && item.exit_code === 0) || (item?.type === 'file_change' && item.status === 'completed')))
39
39
  || (event.type === 'tool_execution_end' && event.isError === false)
40
40
  || (event.type === 'tool_use' && event.part?.type === 'tool' && event.part.state?.status === 'completed')
41
- || (event.type === 'tool_result' && event.status === 'success')
41
+ || (event.type === 'tool_result' && (event.status === 'success' || event.is_error === false))
42
42
  || (event.type === 'user' && Array.isArray(event.message?.content) && event.message.content.some((part) => part.type === 'tool_result' && part.is_error === false)) };
43
43
  }
44
44
  export function inspectProviderDiagnostic(line) {
@@ -42,7 +42,7 @@ export function parseProviderResult(agent, output) {
42
42
  if (agent === 'claude' && event.parent_tool_use_id != null)
43
43
  continue;
44
44
  if (event.type === 'error' || event.type === 'turn.failed' ||
45
- (event.type === 'result' && (event.is_error === true || event.status === 'error')))
45
+ (event.type === 'result' && (event.is_error === true || event.status === 'error' || event.error != null)))
46
46
  return null;
47
47
  switch (agent) {
48
48
  case 'claude':
@@ -77,6 +77,17 @@ export function parseProviderResult(agent, output) {
77
77
  fragments.push(text);
78
78
  }
79
79
  break;
80
+ case 'hermes':
81
+ if (event.type === 'result' && directMachineResult(event.structured_output ?? event.structured_result) !== undefined) {
82
+ structuredResult = event.structured_output ?? event.structured_result;
83
+ }
84
+ if (event.type === 'text' && typeof event.text === 'string')
85
+ fragments.push(event.text);
86
+ if (event.type === 'result' && typeof event.text === 'string')
87
+ fragments.push(event.text);
88
+ if (event.type === 'result' && typeof event.result === 'string')
89
+ fragments.push(event.result);
90
+ break;
80
91
  }
81
92
  }
82
93
  if (structuredResult !== undefined)
@@ -202,7 +213,9 @@ export function parseProviderTelemetry(agent, lines) {
202
213
  ? message.usage
203
214
  : stats?.usage && typeof stats.usage === 'object'
204
215
  ? stats.usage
205
- : stats);
216
+ : event.tokens && typeof event.tokens === 'object'
217
+ ? event.tokens
218
+ : stats);
206
219
  const models = isRecord(stats?.models) ? stats.models : undefined;
207
220
  const modelEntries = models ? Object.entries(models) : [];
208
221
  const firstModel = modelEntries.length === 1 ? modelEntries[0] : undefined;
@@ -242,8 +255,8 @@ export function parseProviderTelemetry(agent, lines) {
242
255
  source.cached_input_tokens = aggregateCached;
243
256
  }
244
257
  const inValue = finite(source?.input_tokens ?? source?.inputTokens ?? source?.prompt_tokens ?? source?.promptTokenCount ?? source?.input);
245
- const cachedValue = finite(source?.cached_input_tokens ?? source?.cache_read_input_tokens ?? source?.cachedInputTokens ?? source?.cacheRead ?? source?.cachedContentTokenCount ?? source?.cached);
246
- const cacheWriteValue = finite(source?.cache_write_input_tokens ?? source?.cache_creation_input_tokens ?? source?.cacheWriteInputTokens ?? source?.cacheWrite);
258
+ const cachedValue = finite(source?.cached_input_tokens ?? source?.cache_read_input_tokens ?? source?.cachedInputTokens ?? source?.cacheRead ?? source?.cachedContentTokenCount ?? source?.cached ?? source?.cache_read);
259
+ const cacheWriteValue = finite(source?.cache_write_input_tokens ?? source?.cache_creation_input_tokens ?? source?.cacheWriteInputTokens ?? source?.cacheWrite ?? source?.cache_write);
247
260
  const outValue = finite(source?.output_tokens ?? source?.outputTokens ?? source?.completion_tokens ?? source?.candidatesTokenCount ?? source?.output);
248
261
  const reasoningValue = finite(source?.reasoning_output_tokens ?? source?.reasoningOutputTokens ?? source?.thoughtsTokenCount ?? source?.thoughts);
249
262
  if (inValue !== undefined)
@@ -104,7 +104,7 @@ export function buildChangePrompt(request, proposalPath, stories, brief = '') {
104
104
  'Every proposed story must have passes: false and 2-5 structured acceptance criteria.',
105
105
  'Every criterion must have a stable id, behavioral text, and one or more executable verify commands.',
106
106
  'Each criterion id must appear in every verify command; each entry must be one approved test command without shell control operators.',
107
- 'Use only a configured Yoke harness if an optional agent affinity is useful (claude, codex, gemini, qwen, opencode, kilo, or pi).',
107
+ 'Use only a configured Yoke harness if an optional agent affinity is useful (claude, codex, gemini, qwen, opencode, kilo, pi, or hermes).',
108
108
  '',
109
109
  `Write ONLY a YAML array of the NEW stories to this exact file: ${proposalPath}`,
110
110
  'Do not edit .yoke/prd.yaml or any source file. Do not commit.',
@@ -39,7 +39,7 @@ export function buildPrdDraftPrompt(idea, planningBrief) {
39
39
  if (planningBrief?.trim()) {
40
40
  lines.push('', '## Approved planning brief (treat these decisions as settled)', planningBrief.trim(), '', 'Do not reopen settled choices or invent alternatives that contradict this brief.');
41
41
  }
42
- lines.push('', 'Break the idea into 5-12 small, independently shippable stories; each must fit one loop iteration.', 'Each story needs:', '- id: STORY-1, STORY-2, ... (unique)', '- title: one imperative sentence', '- priority: dense integers from 1 (lower = built first)', '- needs: optional list of story IDs that must pass first; the graph must be acyclic', '- area: optional collision domain for safe parallel scheduling', '- agent: optional harness affinity (claude, codex, gemini, qwen, opencode, kilo, or pi)', '- acceptance: 2-5 testable, behavioral criteria (observable outcomes, never implementation steps)', ' Each criterion is an object with a stable id, behavioral text, and verify: [one or more approved test commands].', ' Every criterion id must appear in every verify command; use one test command without shell control operators.', '- passes: false', '- writes: explicit relative write scopes for safe scheduling', assessmentInstructions, 'Include a complete assessment on every story in this same planning pass. Do not choose worker model names; the scheduler does that.', '', 'If the project has no source code yet, STORY-1 must scaffold the project skeleton with a runnable', 'test suite, and its acceptance must include that the verify command (verify.command in', '.yoke/config.yaml) exits 0.', '', 'Write ONLY the file .yoke/prd.yaml as a YAML array of stories in exactly that shape.', 'Do not modify any other file. Do not commit.');
42
+ lines.push('', 'Break the idea into 5-12 small, independently shippable stories; each must fit one loop iteration.', 'Each story needs:', '- id: STORY-1, STORY-2, ... (unique)', '- title: one imperative sentence', '- priority: dense integers from 1 (lower = built first)', '- needs: optional list of story IDs that must pass first; the graph must be acyclic', '- area: optional collision domain for safe parallel scheduling', '- agent: optional harness affinity (claude, codex, gemini, qwen, opencode, kilo, pi, or hermes)', '- acceptance: 2-5 testable, behavioral criteria (observable outcomes, never implementation steps)', ' Each criterion is an object with a stable id, behavioral text, and verify: [one or more approved test commands].', ' Every criterion id must appear in every verify command; use one test command without shell control operators.', '- passes: false', '- writes: explicit relative write scopes for safe scheduling', assessmentInstructions, 'Include a complete assessment on every story in this same planning pass. Do not choose worker model names; the scheduler does that.', '', 'If the project has no source code yet, STORY-1 must scaffold the project skeleton with a runnable', 'test suite, and its acceptance must include that the verify command (verify.command in', '.yoke/config.yaml) exits 0.', '', 'Write ONLY the file .yoke/prd.yaml as a YAML array of stories in exactly that shape.', 'Do not modify any other file. Do not commit.');
43
43
  return lines.join('\n');
44
44
  }
45
45
  export function prdFile(targetDir) {
@@ -71,7 +71,7 @@ export function runPrdDraft(targetDir, opts) {
71
71
  const planner = resolvePlanner(config, resolveRunnerAgent(config, undefined, detectHostAgent()), config?.runner, opts.runner);
72
72
  const agent = planner.agent;
73
73
  if (!available(agent)) {
74
- console.error(`Agent CLI "${agent}" was not found on PATH. Install it, or pick another with --runner=<claude|codex|gemini|qwen|opencode|kilo|pi>.`);
74
+ console.error(`Agent CLI "${agent}" was not found on PATH. Install it, or pick another with --runner=<claude|codex|gemini|qwen|opencode|kilo|pi|hermes>.`);
75
75
  return 2;
76
76
  }
77
77
  const idleMs = resolveIdleMs(opts.timeoutMinutes, undefined);
@@ -18,6 +18,8 @@ export function detectProject(targetDir) {
18
18
  agents.push('kilo');
19
19
  if (has('.pi') || has('PI.md'))
20
20
  agents.push('pi');
21
+ if (has('.hermes') || has('HERMES.md') || has('hermes.yaml') || has('hermes.json'))
22
+ agents.push('hermes');
21
23
  return {
22
24
  agents,
23
25
  hasAgentsMd: has('AGENTS.md'),
@@ -5,6 +5,7 @@ import { planQwen } from './planners/qwen.js';
5
5
  import { planOpenCode } from './planners/opencode.js';
6
6
  import { planKilo } from './planners/kilo.js';
7
7
  import { planPi } from './planners/pi.js';
8
+ import { planHermes } from './planners/hermes.js';
8
9
  import { baseContextActions } from './context-actions.js';
9
10
  export function planClaudeRetrofit(canonDir, targetDir) {
10
11
  return planClaude(canonDir, targetDir);
@@ -17,6 +18,7 @@ export const PLANNERS = {
17
18
  opencode: planOpenCode,
18
19
  kilo: planKilo,
19
20
  pi: planPi,
21
+ hermes: planHermes,
20
22
  };
21
23
  export function planRetrofit(canonDir, targetDir, agents, codeGraph = 'graphify', codeIntelligence = 'off') {
22
24
  const seen = new Set();
@@ -0,0 +1,32 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { loadManifest } from '../../canon/manifest.js';
4
+ import { rtkInstruction } from '../tools.js';
5
+ import { PRESERVE_SCAFFOLD } from '../preserve.js';
6
+ import { skillPackageActions } from '../skill-actions.js';
7
+ const reviewerAgent = `---
8
+ description: Read-only Yoke reviewer for correctness and acceptance criteria
9
+ mode: primary
10
+ toolsets:
11
+ - file
12
+ ---
13
+
14
+ Review the observed diff and test evidence. Do not modify files. Return only actionable findings grounded in evidence.
15
+ `;
16
+ export function planHermes(canonDir, _targetDir, _codeGraph = 'graphify', _codeIntelligence = 'off') {
17
+ const manifest = loadManifest(join(canonDir, 'manifest.yaml'));
18
+ const baseline = readFileSync(join(canonDir, 'AGENTS.md'), 'utf8');
19
+ const actions = manifest.skills.flatMap(skill => skillPackageActions(canonDir, skill, 'hermes'));
20
+ actions.push({
21
+ kind: 'write',
22
+ target: 'AGENTS.md',
23
+ content: `${baseline.trimEnd()}\n\n${rtkInstruction()}\n\n## Hermes integration\n\nHermes loads project skills from .hermes/skills and project context from AGENTS.md.\n\n${PRESERVE_SCAFFOLD}\n`,
24
+ reason: 'baseline instructions (Hermes reads AGENTS.md natively)',
25
+ }, {
26
+ kind: 'write',
27
+ target: '.hermes/agents/yoke-reviewer.md',
28
+ content: reviewerAgent,
29
+ reason: 'Hermes read-only reviewer agent',
30
+ });
31
+ return actions;
32
+ }
@@ -8,6 +8,7 @@ const roots = {
8
8
  opencode: '.opencode/skills',
9
9
  kilo: '.kilo/skills',
10
10
  pi: '.pi/skills',
11
+ hermes: '.hermes/skills',
11
12
  };
12
13
  function manualClaudeSkill(content, skill) {
13
14
  const source = content.toString('utf8');
@@ -52,7 +53,7 @@ export function skillPackageActions(canonDir, skill, provider) {
52
53
  .map(file => ({
53
54
  kind: 'write',
54
55
  target: `${roots[provider]}/${skill.id}/${file.relativePath}`,
55
- content: (provider === 'claude' || provider === 'qwen' || provider === 'pi') && skill.invocation === 'manual' && file.relativePath === 'SKILL.md'
56
+ content: (provider === 'claude' || provider === 'qwen' || provider === 'pi' || provider === 'hermes') && skill.invocation === 'manual' && file.relativePath === 'SKILL.md'
56
57
  ? manualClaudeSkill(file.content, skill)
57
58
  : portableContent(file),
58
59
  executable: file.executable,
@@ -6,7 +6,7 @@ import { loadConfig } from '../retrofit/config.js';
6
6
  import { parseReviewVerdict } from './verdict.js';
7
7
  // Resolve to the first available agent, preferring a *second* model so the review
8
8
  // is genuinely cross-model. claude last => a Claude-only box degrades to self-review.
9
- const RESOLUTION_ORDER = ['codex', 'gemini', 'qwen', 'claude', 'opencode', 'kilo', 'pi'];
9
+ const RESOLUTION_ORDER = ['codex', 'gemini', 'qwen', 'claude', 'opencode', 'kilo', 'pi', 'hermes'];
10
10
  export function runReview(targetDir, opts = {}) {
11
11
  const available = opts.isAvailable ?? isAgentAvailable;
12
12
  const implementer = opts.implementer ?? loadConfig(targetDir)?.agents[0] ?? 'claude';
@@ -70,7 +70,7 @@ export function formatReviewContract(path, provider) {
70
70
  return [
71
71
  `Write your final verdict to this absolute path: ${path}`,
72
72
  'The file must contain exactly one JSON object with this contract:',
73
- `{"schemaVersion":1,"approved":boolean,"summary":"non-empty string","findings":[{"id":"optional id","severity":"blocking|warning|info","message":"non-empty string","file":"optional path","line":1,"actionable":true,"suggestedFix":"optional repair","evidence":["optional evidence reference"]}],"provenance":{"provider":"${provider ?? 'claude|codex|gemini|qwen|opencode|kilo|pi'}","model":"provider-reported model","role":"review","promptVersion":1,"permissions":"safe"}}`,
73
+ `{"schemaVersion":1,"approved":boolean,"summary":"non-empty string","findings":[{"id":"optional id","severity":"blocking|warning|info","message":"non-empty string","file":"optional path","line":1,"actionable":true,"suggestedFix":"optional repair","evidence":["optional evidence reference"]}],"provenance":{"provider":"${provider ?? 'claude|codex|gemini|qwen|opencode|kilo|pi|hermes'}","model":"provider-reported model","role":"review","promptVersion":1,"permissions":"safe"}}`,
74
74
  'Set approved=false when any blocking finding exists. Create the file even when the process also exits non-zero.',
75
75
  ].join('\n');
76
76
  }
@@ -68,7 +68,7 @@ export function chooseCapability(input) {
68
68
  const worker = reliable[0];
69
69
  const blocked = !worker && (input.fallback === 'block' || input.maxTier !== undefined);
70
70
  const provider = worker?.agent ?? input.story.agent ?? input.parent;
71
- const selection = worker ? { provider: worker.provider, model: worker.model, reasoningEffort: worker.reasoningEffort, variant: worker.variant, nativeMultiAgent: false, ...(provider !== 'gemini' && provider !== 'qwen' && provider !== 'pi' && input.parentSelection?.bare !== undefined ? { bare: input.parentSelection.bare } : {}) }
71
+ const selection = worker ? { provider: worker.provider, model: worker.model, reasoningEffort: worker.reasoningEffort, variant: worker.variant, nativeMultiAgent: false, ...(provider !== 'gemini' && provider !== 'qwen' && provider !== 'pi' && provider !== 'hermes' && input.parentSelection?.bare !== undefined ? { bare: input.parentSelection.bare } : {}) }
72
72
  : { ...(provider === input.parent ? input.parentSelection : {}), nativeMultiAgent: false };
73
73
  const reason = `${role}: ${tiers[level]}; ${input.assessment.reason}${failures.length ? `; ${failures.length} verified failure(s), ${failures.length === 1 ? 'one targeted repair' : 'escalated'}` : ''}${worker ? '' : '; no eligible profile, parent/provider fallback'}`;
74
74
  return { worker, provider, selection, reason: blocked ? `${role}: no eligible profile within routing limits; execution blocked` : reason, blocked, requiredTier: baseTier, selectedTier: tiers[level], failures: failures.length, exhausted, next: input.maxTier && level >= tiers.indexOf(input.maxTier) ? 'stop at configured tier limit' : level < 3 ? tiers[level + 1] : 'stop after bounded attempts' };
@@ -249,7 +249,7 @@ function routingSteps(options) {
249
249
  return blocked('Selected routing profile exceeds configured limits; execution blocked');
250
250
  const provider = worker?.agent ?? options.parent;
251
251
  const selection = worker
252
- ? { provider: worker.provider, model: worker.model, reasoningEffort: worker.reasoningEffort, variant: worker.variant, nativeMultiAgent: false, ...(provider !== 'gemini' && provider !== 'qwen' && provider !== 'pi' ? { bare: options.parentSelection?.bare } : {}) }
252
+ ? { provider: worker.provider, model: worker.model, reasoningEffort: worker.reasoningEffort, variant: worker.variant, nativeMultiAgent: false, ...(provider !== 'gemini' && provider !== 'qwen' && provider !== 'pi' && provider !== 'hermes' ? { bare: options.parentSelection?.bare } : {}) }
253
253
  : { ...(options.parentSelection ?? {}), nativeMultiAgent: false };
254
254
  const workerStarted = now();
255
255
  const result = yield () => makeWorker(provider, selection)(ctx);
@@ -42,6 +42,9 @@ export function defaultRoutingWorkers(agents) {
42
42
  pi: [
43
43
  { id: 'pi-standard', agent: 'pi', tier: 'standard', costTier: 'medium', capabilities: ['implementation'] },
44
44
  ],
45
+ hermes: [
46
+ { id: 'hermes-standard', agent: 'hermes', tier: 'standard', costTier: 'medium', capabilities: ['implementation'] },
47
+ ],
45
48
  };
46
49
  return agents.flatMap(agent => workers[agent]);
47
50
  }
package/docs/HARNESSES.md CHANGED
@@ -1,6 +1,6 @@
1
- # OpenCode, Kilo and Pi
1
+ # OpenCode, Kilo, Pi and Hermes
2
2
 
3
- Yoke 1.13.0 adds first-class adapters for OpenCode, Kilo and Pi coding agent. The adapters share Yoke's invocation, routing, review, quality, telemetry and retrofit contracts, while preserving the controls each harness actually provides.
3
+ Yoke adds first-class adapters for OpenCode, Kilo, Pi coding agent, and Hermes Agent. The adapters share Yoke's invocation, routing, review, quality, telemetry and retrofit contracts, while preserving the controls each harness actually provides.
4
4
 
5
5
  This is a CLI integration, not an authentication bundle. Install the selected harness, log in or configure its API provider, and verify it independently before starting a Yoke loop.
6
6
 
@@ -10,12 +10,13 @@ Pi project trust is separate from Yoke's permission profile. Current Pi ignores
10
10
 
11
11
  ## Setup and retrofit
12
12
 
13
- Select one of the new harnesses explicitly:
13
+ Select one of the harnesses explicitly:
14
14
 
15
15
  ```sh
16
16
  yoke setup . --yes --agent=opencode --runner=opencode
17
17
  yoke setup . --yes --agent=kilo --runner=kilo
18
18
  yoke setup . --yes --agent=pi --runner=pi
19
+ yoke setup . --yes --agent=hermes --runner=hermes
19
20
  ```
20
21
 
21
22
  To add one to an existing project without changing the other generated artifacts:
@@ -24,9 +25,10 @@ To add one to an existing project without changing the other generated artifacts
24
25
  yoke retrofit . --agent=opencode
25
26
  yoke retrofit . --agent=kilo
26
27
  yoke retrofit . --agent=pi
28
+ yoke retrofit . --agent=hermes
27
29
  ```
28
30
 
29
- `--agent=all` now includes all seven supported harnesses. Retrofit is merge-aware for the native config files and backs up Yoke-managed overwrites under `.yoke/backup/`.
31
+ `--agent=all` includes all eight supported harnesses. Retrofit is merge-aware for the native config files and backs up Yoke-managed overwrites under `.yoke/backup/`.
30
32
 
31
33
  ## Native artifacts
32
34
 
@@ -35,8 +37,9 @@ yoke retrofit . --agent=pi
35
37
  | OpenCode | `AGENTS.md`, `.opencode/skills/`, `opencode.json`, `.opencode/agents/yoke-reviewer.md` | `run --format json`, provider/model selection, variants, plan agent, local MCP servers |
36
38
  | Kilo | `AGENTS.md`, `.kilo/skills/`, `kilo.jsonc`, `.kilo/agents/yoke-reviewer.md` | OpenCode-compatible `run --format json`, provider/model selection, variants, plan agent, local MCP servers |
37
39
  | Pi | `AGENTS.md`, `.pi/skills/`, `.pi/settings.json` | JSONL mode, provider/model selection, thinking level, explicit tool allowlists |
40
+ | Hermes | `AGENTS.md`, `.hermes/skills/`, `.hermes/agents/yoke-reviewer.md` | `chat --format stream-json`, `--query-file -`, provider/model selection, reasoning effort, toolsets |
38
41
 
39
- All three consume the shared `AGENTS.md` and `.yoke/context/*.md` context. OpenCode and Kilo also receive the configured local MCP servers from the Yoke code-graph choice. Pi has no native MCP or sub-agent layer, so its integration intentionally uses the portable skills and Yoke's own loop rather than pretending those features exist.
42
+ All consume the shared `AGENTS.md` and `.yoke/context/*.md` context. OpenCode and Kilo also receive the configured local MCP servers from the Yoke code-graph choice. Pi and Hermes integrations use portable skills and Yoke's loop boundaries.
40
43
 
41
44
  ## Provider, model and variant selection
42
45
 
@@ -64,24 +67,36 @@ This becomes `--provider openai --model gpt-5.6 --thinking high`. Pi calls `vari
64
67
 
65
68
  OpenCode and Kilo likewise map these two fields to a single `--variant`: matching values are emitted once and conflicting values are rejected before spawning a process.
66
69
 
70
+ For Hermes the equivalent is:
71
+
72
+ ```yaml
73
+ runner:
74
+ agent: hermes
75
+ provider: openrouter
76
+ model: anthropic/claude-sonnet-4
77
+ reasoningEffort: high
78
+ ```
79
+
80
+ This becomes `--provider openrouter --model anthropic/claude-sonnet-4 --reasoning-effort high`. Hermes accepts `--reasoning-effort`; configuring conflicting `variant` and `reasoningEffort` values is rejected.
81
+
67
82
  The same fields are available on routing workers and quality critic/repair roles. Routing evidence is keyed by harness, provider, model, reasoning effort and variant, so a model profile does not inherit another profile's success history.
68
83
 
69
84
  ## Permission profiles and honest limits
70
85
 
71
- | Yoke profile | OpenCode / Kilo | Pi |
72
- |---|---|---|
73
- | `safe` | Headless `--auto` run; Yoke still verifies the resulting tree and gates the commit | `read,bash,edit,write` tool allowlist so the implementer can test and modify the project |
74
- | `read-only` | `--agent plan` plus JSON output | `read,grep,find,ls` only |
75
- | `unsafe` | Explicit `--dangerously-skip-permissions` | Harness default tool set; no Yoke-added allowlist |
86
+ | Yoke profile | OpenCode / Kilo | Pi | Hermes |
87
+ |---|---|---|---|
88
+ | `safe` | Headless `--auto` run; Yoke still verifies the resulting tree and gates the commit | `read,bash,edit,write` tool allowlist so the implementer can test and modify the project | `--toolsets file,terminal` restricts tools to local filesystem and shell execution |
89
+ | `read-only` | `--agent plan` plus JSON output | `read,grep,find,ls` only | `--toolsets file` allows inspection without terminal or write execution |
90
+ | `unsafe` | Explicit `--dangerously-skip-permissions` | Harness default tool set; no Yoke-added allowlist | `--yolo` skips dangerous command approvals |
76
91
 
77
- OpenCode, Kilo and Pi do not provide the same OS-level sandbox boundary as Codex or Gemini. The Yoke `safe` label therefore describes the selected harness controls and Yoke's mechanical gates, not a universal filesystem sandbox. Use `read-only` for review and do not use `unsafe` unless the project owner accepts the boundary.
92
+ OpenCode, Kilo, Pi and Hermes do not provide the same OS-level sandbox boundary as Codex or Gemini. The Yoke `safe` label therefore describes the selected harness controls and Yoke's mechanical gates, not a universal filesystem sandbox. Use `read-only` for review and do not use `unsafe` unless the project owner accepts the boundary.
78
93
 
79
- The Yoke loop disables native delegation where the harness exposes it, so Yoke's worker budget remains the authority. OpenCode and Kilo native agents are installed as a read-only reviewer artifact; Yoke's review runner still validates the structured verdict itself. Pi has no native sub-agent or plan mode by design.
94
+ The Yoke loop disables native delegation where the harness exposes it, so Yoke's worker budget remains the authority. OpenCode and Kilo native agents are installed as a read-only reviewer artifact; Hermes uses a read-only agent configuration with `toolsets: [file]`; Yoke's review runner still validates the structured verdict itself. Pi has no native sub-agent or plan mode by design.
80
95
 
81
96
  ## Telemetry and validation limits
82
97
 
83
- Yoke parses OpenCode/Kilo JSON text events and `step_finish` token/cost events, and Pi `message_end` plus `message_update.usage` events. Missing provider events produce partial or unknown measurements; they are never reported as zero-cost success. Provider-reported model and usage remain provider claims.
98
+ Yoke parses OpenCode/Kilo JSON text events and `step_finish` token/cost events, Pi `message_end` plus `message_update.usage` events, and Hermes `result` (tokens, cost, model) and `text` streaming events. Missing provider events produce partial or unknown measurements; they are never reported as zero-cost success. Provider-reported model and usage remain provider claims.
84
99
 
85
100
  The release test suite covers argument construction, provider/variant propagation, routing and quality configuration, retrofit plans, host detection, JSON result parsing, and representative telemetry fixtures. It does not authenticate against every provider or claim equal model quality. Run a small project-specific smoke task after installing a harness and configuring credentials.
86
101
 
87
- Official CLI references: [OpenCode CLI](https://dev.opencode.ai/docs/cli), [OpenCode configuration](https://opencode.ai/docs/config), [OpenCode skills](https://opencode.ai/docs/skills), [Kilo CLI reference](https://github.com/Kilo-Org/kilocode/blob/main/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md), and [Pi coding agent](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/README.md).
102
+ Official CLI references: [OpenCode CLI](https://dev.opencode.ai/docs/cli), [OpenCode configuration](https://opencode.ai/docs/config), [OpenCode skills](https://opencode.ai/docs/skills), [Kilo CLI reference](https://github.com/Kilo-Org/kilocode/blob/main/packages/kilo-docs/pages/code-with-ai/platforms/cli-reference.md), [Pi coding agent](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/README.md), and [Hermes Agent](https://github.com/NousResearch/hermes-agent).
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yoke",
3
- "version": "1.15.1",
4
- "description": "Cross-agent coding harness for seven supported CLIs: curated skill canon, mechanical safety gates, autonomous loop with proof artifacts. CLI: npm i -g @hecer/yoke",
3
+ "version": "1.16.0",
4
+ "description": "Cross-agent coding harness for eight supported CLIs: curated skill canon, mechanical safety gates, autonomous loop with proof artifacts. CLI: npm i -g @hecer/yoke",
5
5
  "contextFileName": "GEMINI-EXTENSION.md"
6
6
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hecer/yoke",
3
- "version": "1.15.1",
4
- "description": "One harness, seven agents, zero trust in \"done\" — cross-agent coding harness for Claude Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo and Pi: one skill canon, mechanical safety gates, an autonomous loop with screenshot/video proofs.",
3
+ "version": "1.16.0",
4
+ "description": "One harness, eight agents, zero trust in \"done\" — cross-agent coding harness for Claude Code, Codex CLI, Gemini CLI, Qwen Code, OpenCode, Kilo, Pi and Hermes: one skill canon, mechanical safety gates, an autonomous loop with screenshot/video proofs.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "yoke": "dist/cli.js"
@@ -49,6 +49,7 @@
49
49
  "opencode",
50
50
  "kilo",
51
51
  "pi-coding-agent",
52
+ "hermes-agent",
52
53
  "agents",
53
54
  "agentic-coding",
54
55
  "harness",