leos-agent 10.7.1 → 11.202609070.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # leos-agent
2
2
 
3
- Leo's portable agent operating policy, version **10.7.1**, installable on Claude
3
+ Leo's portable agent operating policy, version **11.202609070.0**, installable on Claude
4
4
  Code, Codex, Cursor, Hermes, Pi, and OpenCode through each harness's own plugin
5
5
  system.
6
6
 
@@ -82,21 +82,42 @@ accepts goes in verbatim. A misspelled *key*, though, is a hard error, because a
82
82
  typo that silently left a harness on the expensive model is the one failure this
83
83
  is here to prevent.
84
84
 
85
- **Nothing reads it at run time.** `leo-install.py` renders the result into the
86
- `<leos-agent>` block it already writes, so a session pays nothing to know its own
87
- routing — no config read, no extra turn. It costs *less* than before: each
88
- machine now carries only its own harness's dispatch line instead of all of them,
89
- which took the installed payload from 4497 bytes to 4315–4344 depending on the
90
- harness. `scripts/measure_context.py` prints the per-harness figure and fails if
91
- an unconfigured harness ever grows past the old one.
85
+ **Read once per session now, not never.** Most harnesses render the routing
86
+ stanza live: `emit_payload.py` calls `routing.load()` every time it runs — on
87
+ a `SessionStart` hook (Claude Code, Codex), inside Hermes's frozen
88
+ session-scoped prompt section, and on every *turn* for Pi, since
89
+ `before_agent_start` has no cheaper hook to sit on (see `pi-extension.js`).
90
+ Codex's and Cursor's per-machine halves are still baked at install time —
91
+ into the profile TOMLs and the `.mdc` rule respectively — because neither is
92
+ otherwise re-rendered. Either way this costs one read of a small local JSON
93
+ file, never a network call or a re-render of the whole payload. The bytes it
94
+ adds are unchanged from before this delivery mechanism moved: each machine
95
+ still carries only its own harness's dispatch line rather than all of them,
96
+ which is what keeps the rendered payload at 4222–4251 bytes depending on the
97
+ harness (measured with no routing configured; a configured harness costs a
98
+ little more, bounded by the model names chosen) against the pre-split figure
99
+ of 4497. `scripts/measure_context.py --check` enforces the ceiling and prints
100
+ the per-harness figure.
92
101
 
93
102
  Edit the file — by hand, or with `routing.py set --harness <h> --runner
94
- <model>`, which [`/tune-routing`](#what-it-ships) drives end to end — then
95
- re-run the installer to re-render; `leo-install.py <harness> --check` reports
96
- "out of date" until you do, and `/doctor` surfaces it. Installing is
103
+ <model>`, which [`/tune-routing`](#what-it-ships) drives end to end — and it
104
+ takes effect at the next session with nothing else to run on Claude Code,
105
+ Hermes, and Pi, all of which read it live. Codex, Cursor and OpenCode still
106
+ need the installer re-run afterwards, because their halves are baked into
107
+ files at install time; `leo-install.py <harness> --check` reports "out of
108
+ date" for those three until you do, and `/doctor` surfaces it. Installing is
97
109
  idempotent: same config, same version, same bytes, so a second run reports
98
110
  `unchanged`.
99
111
 
112
+ OpenCode needs a second file for a reason worth knowing: its `instructions`
113
+ list reads `rules/preferences.md` straight off disk, **un-rendered**, so the
114
+ routing region in that file keeps its shipped default no matter what is
115
+ configured. The per-machine half therefore ships as its own
116
+ `~/.config/opencode/leos-agent-routing.md`, written only when routing is
117
+ actually configured for OpenCode, and added to `instructions` alongside the
118
+ payload — exactly the shape Cursor has had all along, and for exactly the same
119
+ reason.
120
+
100
121
  **The config is yours, never the installer's.** `leo-install.py` only ever reads
101
122
  it, and never creates, migrates, rewrites, or removes it, including under
102
123
  `--uninstall`; it lives outside the plugin so an upgrade cannot take it. The one
@@ -109,17 +130,22 @@ default and behaviour is exactly what it was before this existed.
109
130
 
110
131
  Delivery differs by harness only in the last mile: Claude Code gets a `model:`
111
132
  override alongside `subagent_type:` (the plugin-owned `agents/*.md` are never
112
- rewritten), Codex gets the models substituted into its installed profile TOMLs,
113
- Cursor gets its own `~/.cursor/rules/leos-agent-routing.mdc` because its rules
114
- come straight out of the plugin directory, and the rest get the rendered line in
115
- their global instruction file.
133
+ rewritten), rendered live at session start. Codex gets the models substituted
134
+ into its installed profile TOMLs by the installer, because Codex plugins
135
+ cannot ship agent definitions and nothing re-renders them between installer
136
+ runs. Cursor gets its own `~/.cursor/rules/leos-agent-routing.mdc`, also
137
+ written by the installer, because its rules come straight out of the plugin
138
+ directory and are not otherwise re-rendered per session. Hermes and Pi get
139
+ the rendered dispatch line inside the payload itself, live, at whatever
140
+ cadence that harness re-renders it (see the delivery table under
141
+ [How it works](#how-it-works)). OpenCode gets nothing — see above.
116
142
 
117
143
  ## The dispatch guard
118
144
 
119
145
  The payload has always said that a subagent dispatch must name a model. Prose
120
146
  alone did not hold: a forgotten dispatch inherits the parent's expensive model
121
147
  and pays a cold cache write per child, which is the single most expensive shape
122
- this policy has. From 10.7.1 that half of the rule is enforced by a hook instead,
148
+ this policy has. From 11.202609070.0 that half of the rule is enforced by a hook instead,
123
149
  and the prose it replaced came out of the always-loaded payload — enforcement in
124
150
  code costs **zero** context per turn, so the guard paid for itself in bytes
125
151
  before saving a cent.
@@ -143,11 +169,12 @@ is also deliberately narrow: a false block costs one re-dispatch, while a caught
143
169
  inherited fan-out saves the cold prefix of every child, so the margin only holds
144
170
  while the rule refuses to make judgment calls.
145
171
 
146
- Detection is by **argument shape**, not tool name — only Claude Code's dispatch
147
- tool is verified, so an unanticipated one degrades to a no-op rather than a
148
- broken harness. MCP tools are never guarded. Anything that goes wrong inside the
149
- guard allows the call and records `decision: "error"`, kept distinct from a
150
- decision to allow, because a guard that dies quietly is worse than no guard.
172
+ Detection is by **argument shape**, not tool name: a dispatch is a call that
173
+ selects an agent and carries a brief. A shape the guard does not recognise is
174
+ left alone, so a harness it has never met keeps working. MCP tools are never
175
+ guarded. Anything that goes wrong inside the guard allows the call and records
176
+ `decision: "error"`, kept distinct from a decision to allow, because a guard
177
+ that dies quietly is worse than no guard.
151
178
 
152
179
  ```
153
180
  LEOS_AGENT_DISPATCH_GUARD=on block (default)
@@ -157,13 +184,17 @@ LEOS_AGENT_DISPATCH_GUARD=on block (default)
157
184
  LEOS_AGENT_DISPATCH_LOG_PROMPTS=1 debug only: keep 200 chars of brief text in the log
158
185
  ```
159
186
 
160
- **Coverage is honest, not uniform.** Claude Code is verified. Codex, Cursor,
161
- Hermes and OpenCode are wired with the same policy but their dispatch argument
162
- shapes are unconfirmed; there the guard is best-effort and no-ops rather than
163
- misfires. Pi has no hook surface and gets nothing. **Codex hash-pins hooks**, so
164
- upgrading to 10.7.1 — and every later edit to the guard — needs re-approval
165
- through `/hooks` there. Until you do, Codex silently enforces nothing; zero Codex
166
- rows in the report is the symptom.
187
+ **Per harness.** Claude Code dispatches `Agent` with `subagent_type`, and a
188
+ plugin install namespaces it — both forms are tiers. Codex dispatches
189
+ `spawn_agent`, which selects behaviour by `model` and `reasoning_effort` rather
190
+ than by naming an agent, so there the guard requires `model`; its `message` is
191
+ encrypted, so the over-delegation heuristic and block-conversion tracking do not
192
+ apply on Codex. Hermes and OpenCode run the same policy in-process, blocking by
193
+ directive and by throw respectively. Pi has no hook surface.
194
+
195
+ **Codex hash-pins hooks.** A new version of the guard enforces nothing there
196
+ until it is re-approved through `/hooks`, and the symptom is silence — zero
197
+ Codex rows in `dispatch_log.py report`.
167
198
 
168
199
  ### What it records
169
200
 
@@ -184,45 +215,82 @@ python3 scripts/usage_scan.py --since 7d
184
215
  ## How it works
185
216
 
186
217
  The payload lives in exactly one file: [`rules/preferences.md`](rules/preferences.md).
218
+ Every harness now reads it live, out of the plugin directory, at or near session
219
+ start — upgrading the plugin *is* upgrading the policy, with no render-to-disk
220
+ step in between.
187
221
 
188
- Cursor reads that file natively as an always-apply rule. Every other harness
189
- gets it through its global instruction file, written by
190
- [`scripts/leo-install.py`](scripts/leo-install.py) into a marker block:
191
-
192
- ```
193
- <leos-agent version="10.7.1">
194
- ...the payload...
195
- </leos-agent>
196
- ```
197
-
198
- Updating replaces that block and nothing else, so anything you wrote in those
199
- files by hand survives an upgrade untouched. The script writes only when the
200
- bytes actually differ, so running it twice is a no-op, and it writes through a
201
- temporary file and an atomic rename, so an interrupted run cannot leave a
202
- half-written instruction file behind.
203
-
204
- If it ever finds markers it cannot pair — an opener with no closer, a stray
205
- closer, two blocks — it refuses to touch that file and tells you what to fix.
206
- Guessing there would mean deleting whatever sits between the markers, which is
207
- exactly the content it exists to protect.
208
-
209
- | Harness | Global file the installer writes |
210
- |---|---|
211
- | Claude Code | `~/.claude/CLAUDE.md` (the `leo-runner` / `leo-executor` agents need no installer step — the plugin's `agents/` directory delivers them) |
212
- | Codex | `~/.codex/AGENTS.md` (plus `~/.codex/agents/leo-runner.toml` and `leo-executor.toml`) |
213
- | Cursor | none — the plugin's always-apply rule delivers it |
214
- | Hermes | `~/.hermes/SOUL.md` (edited only if it already exists) |
215
- | Pi | `~/.pi/agent/AGENTS.md` |
216
- | OpenCode | `~/.config/opencode/AGENTS.md` (plus copied skills and commands) |
222
+ Cursor reads that file directly as an always-apply rule; it was the model for
223
+ everything that follows. Every other harness runs
224
+ [`scripts/emit_payload.py`](scripts/emit_payload.py), which strips the
225
+ frontmatter, renders this machine's [routing](#per-machine-model-routing)
226
+ stanza, and prints the result — the trigger and the plumbing differ by harness:
217
227
 
218
- **The installer is per-harness and manual.** Running it inside Codex installs Codex and
219
- nothing else; it never writes to another harness's files behind your back, and
220
- it never runs on its own at session start. Install the plugin, then run the
221
- installer once in that harness.
228
+ | Harness | How the payload arrives | What the installer still does |
229
+ |---|---|---|
230
+ | Claude Code | a `SessionStart` hook (`hooks/hooks.json`, auto-discovered) runs `emit_payload.py`; its stdout becomes session context | nothing for the payload — `agents/` ships `leo-runner`/`leo-executor` directly; only cleans up a `<leos-agent>` block a pre-11.0 install left in `~/.claude/CLAUDE.md` |
231
+ | Codex | the same `hooks/hooks.json` entry — Codex auto-discovers it too, aliasing `${CLAUDE_PLUGIN_ROOT}` to its own plugin root | writes `~/.codex/agents/leo-runner.toml` and `leo-executor.toml`, since Codex plugins cannot ship agent definitions; cleans up a legacy `~/.codex/AGENTS.md` block |
232
+ | Cursor | native — `rules/preferences.md` read straight off the plugin directory as an always-apply rule | writes `~/.cursor/rules/leos-agent-routing.mdc`, only when routing is configured for Cursor |
233
+ | Hermes | `register(ctx)` in `__init__.py` calls `ctx.register_system_prompt_section("leos-agent", ..., position="after_memory")` — rendered once per session and frozen, so it never invalidates the cache mid-session | cleans up a legacy `~/.hermes/SOUL.md` block; `SOUL.md` itself is never written any more |
234
+ | Pi | `pi-extension.js` appends `emit_payload.py`'s output to the system prompt on `before_agent_start`, which fires every *turn*, not once per session — the one harness that pays a spawn per turn rather than per session — and contributes `skills/` via `resources_discover` | cleans up a legacy `~/.pi/agent/AGENTS.md` block |
235
+ | OpenCode | a one-time `"instructions": ["<plugin-root>/rules/preferences.md"]` line in `~/.config/opencode/opencode.json`, read at startup | copies skills and commands into `~/.config/opencode/skills/` and `commands/` (OpenCode's JS-only plugin API cannot register those); prints the `instructions` line and reports it outstanding until it's present — the file is JSONC with your comments in it, so the installer never edits it itself; cleans up a legacy `~/.config/opencode/AGENTS.md` block |
236
+
237
+ **The `<leos-agent version="...">` marker block still exists**, but only for
238
+ migration and uninstall now — nothing writes it on a fresh install. Malformed
239
+ markers are still refused rather than guessed at: an opener with no closer, a
240
+ stray closer, two blocks all stop the run and tell you what to fix, because
241
+ guessing would mean deleting whatever sits between them. Where a pre-11.0
242
+ install left a block behind, a normal run strips it and reports `migrated`,
243
+ preserving whatever surrounding text you wrote by hand; `--uninstall` does the
244
+ same cleanup, so either command clears the leftover.
245
+
246
+ Determinism is part of the contract, not an implementation detail: two runs of
247
+ `emit_payload.py` must produce byte-identical output, or the session's cached
248
+ prompt prefix stops being cacheable and every session pays a full cold write —
249
+ so nothing in the render path may read the clock, an absolute path, or git
250
+ state. It also fails open, silently, on stdout: any error prints nothing, exits
251
+ 0, and leaves a breadcrumb in `$LEOS_AGENT_LOCAL_PATH/emit-payload.log` instead
252
+ of injecting a traceback into the session as context.
222
253
 
223
254
  Requires Python 3.9+ and macOS, Linux, or WSL. No symlinks are used anywhere —
224
255
  installs are real clones and copies.
225
256
 
257
+ ## Multi-surface and cloud reach
258
+
259
+ "One install per harness" undersells how many surfaces a single install
260
+ reaches — and where it doesn't:
261
+
262
+ - **Claude Code's CLI, VS Code extension, JetBrains plugin, and desktop local
263
+ sessions share `~/.claude`.** The VS Code docs describe plugin management as
264
+ using "the same CLI commands under the hood," and the JetBrains plugin runs
265
+ the `claude` CLI rather than bundling its own — one `claude plugin install`
266
+ reaches all four.
267
+ - **Claude Code cloud/web sessions do not** — `~/.claude/CLAUDE.md` is
268
+ documented as not carried into them, so the old block-injection design never
269
+ reached the web at all. The plugin route does: declare `leos-agent` under
270
+ `enabledPlugins` in the repo's `.claude/settings.json` and it installs at
271
+ session start. That is a real gain of this design — the policy reaches cloud
272
+ for the first time.
273
+ - **Desktop WSL sessions have no plugin support**; SSH sessions read the
274
+ *remote* host's `~/.claude`, so the plugin needs installing on each SSH
275
+ target separately — a local install does not follow you there.
276
+ - **The desktop Cowork tab is a separate, account-synced config surface** and
277
+ will not see a CLI install.
278
+ - **Codex's IDE extension does not support plugins at all**, though it does
279
+ read `~/.codex/AGENTS.md` and the installed agent TOMLs — so a migrated
280
+ instruction file and the agent profiles still reach it; the live hook
281
+ delivery does not.
282
+ - **Cursor Cloud Agents do not see plugin-shipped rules.** User Rules are
283
+ account-synced and do reach them; a plugin's rules live on local disk and
284
+ stop there.
285
+ - **Hermes profiles and OpenCode's `OPENCODE_CONFIG_DIR` each create a second,
286
+ invisible config scope** — a plugin installed under one is invisible under
287
+ the other.
288
+
289
+ Upgrades need a new session almost everywhere: Claude Code's
290
+ `/reload-plugins` or a VS Code restart banner, Codex "start a new session,"
291
+ Cursor's Reload Window, Hermes a restart or `/restart`, OpenCode only rereads
292
+ its config at startup.
293
+
226
294
  ---
227
295
 
228
296
  ## Claude Code
@@ -237,11 +305,19 @@ claude plugin marketplace add foxhatleo/leos-agent
237
305
  claude plugin install leos-agent@leos-agent --scope user
238
306
  ```
239
307
 
240
- Then, in a Claude Code session, run `/install` (or ask it to use the `install`
241
- skill). That writes the block into `~/.claude/CLAUDE.md`.
308
+ Nothing else to install. `hooks/hooks.json`'s `SessionStart` hook is
309
+ auto-discovered, and the payload starts arriving at the next session — no
310
+ `/install` run needed. If this checkout has a `<leos-agent>` block in
311
+ `~/.claude/CLAUDE.md` left by a version before 11.0, see Upgrade below to clear
312
+ it.
242
313
 
243
314
  **Upgrade**
244
315
 
316
+ Claude Code auto-updates an installed plugin in the background roughly once
317
+ per session, so most of the time this is zero commands — start a new session
318
+ (`/reload-plugins`, or restart in VS Code) and the new payload is already
319
+ live. To force it immediately:
320
+
245
321
  ```bash
246
322
  claude plugin marketplace update leos-agent
247
323
  ```
@@ -250,16 +326,24 @@ claude plugin marketplace update leos-agent
250
326
  claude plugin install leos-agent@leos-agent --scope user
251
327
  ```
252
328
 
253
- Re-run `/install` afterwards to refresh the block, then start a new session.
254
- Both commands are safe to repeat; installing an already-current version reports
255
- that it is already installed and changes nothing.
329
+ Both commands are safe to repeat; installing an already-current version
330
+ reports that it is already installed and changes nothing. If you're
331
+ upgrading a checkout that still carries a `<leos-agent>` block from before
332
+ this delivery mechanism, run the installer once to strip it — it reports
333
+ `migrated` and leaves the rest of `~/.claude/CLAUDE.md` exactly as you wrote
334
+ it:
335
+
336
+ ```bash
337
+ python3 ~/.claude/plugins/cache/leos-agent/leos-agent/11.202609070.0/scripts/leo-install.py claude
338
+ ```
256
339
 
257
340
  **Uninstall**
258
341
 
259
- Run the installer's uninstall first, while the script is still on disk:
342
+ Run the installer's uninstall first, while the script is still on disk — it
343
+ only has a legacy block to clean up, but do it before the plugin cache is gone:
260
344
 
261
345
  ```bash
262
- python3 ~/.claude/plugins/cache/leos-agent/leos-agent/10.7.1/scripts/leo-install.py claude --uninstall
346
+ python3 ~/.claude/plugins/cache/leos-agent/leos-agent/11.202609070.0/scripts/leo-install.py claude --uninstall
263
347
  ```
264
348
 
265
349
  ```bash
@@ -286,18 +370,22 @@ codex plugin marketplace add foxhatleo/leos-agent
286
370
  codex plugin add leos-agent@leos-agent
287
371
  ```
288
372
 
289
- Then run the `install` skill in a Codex session (`$leos-agent`, then `install`), or
290
- run the script directly:
373
+ The payload arrives the same way it does on Claude Code: `hooks/hooks.json`'s
374
+ `SessionStart` hook is auto-discovered, and Codex aliases
375
+ `${CLAUDE_PLUGIN_ROOT}` to its own plugin root, so no Codex-specific hook file
376
+ is needed. The installer is still required for what Codex plugins cannot ship
377
+ on their own — the two economical agent profiles:
291
378
 
292
379
  ```bash
293
- python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.7.1/scripts/leo-install.py codex
380
+ python3 ~/.codex/plugins/cache/leos-agent/leos-agent/11.202609070.0/scripts/leo-install.py codex
294
381
  ```
295
382
 
296
- This writes `~/.codex/AGENTS.md` and installs two economical agents:
297
- `leo-runner` (`gpt-5.6-luna`, low effort) for narrow repeatable work and
298
- `leo-executor` (`gpt-5.6-terra`, medium effort) for well-specified
299
- implementation. Codex plugins cannot ship agent definitions themselves, which
300
- is why the installer writes them.
383
+ This writes `~/.codex/agents/leo-runner.toml` (`gpt-5.6-luna`, low effort) and
384
+ `leo-executor.toml` (`gpt-5.6-terra`, medium effort), and cleans up a
385
+ `<leos-agent>` block a pre-11.0 install left in `~/.codex/AGENTS.md`. Codex
386
+ trusts a hook by the hash of its command, so the command string in
387
+ `hooks/hooks.json` is deliberately constant — a payload edit alone never
388
+ requires re-approving the hook through `/hooks`.
301
389
 
302
390
  **Upgrade**
303
391
 
@@ -309,13 +397,15 @@ codex plugin marketplace upgrade leos-agent
309
397
  codex plugin add leos-agent@leos-agent
310
398
  ```
311
399
 
312
- Re-run the installer, then start a new thread — Codex picks up plugin changes on new
313
- threads only. Re-adding an already-installed plugin is idempotent.
400
+ Re-run the installer to refresh the agent TOMLs (a no-op unless your routing
401
+ config changed), then start a new thread — Codex picks up plugin changes on
402
+ new threads only. Re-adding an already-installed plugin is idempotent, and the
403
+ hook needs no re-approval since its command string never changes.
314
404
 
315
405
  **Uninstall**
316
406
 
317
407
  ```bash
318
- python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.7.1/scripts/leo-install.py codex --uninstall
408
+ python3 ~/.codex/plugins/cache/leos-agent/leos-agent/11.202609070.0/scripts/leo-install.py codex --uninstall
319
409
  ```
320
410
 
321
411
  ```bash
@@ -331,9 +421,12 @@ codex plugin marketplace remove leos-agent
331
421
  ## Cursor
332
422
 
333
423
  Cursor has no on-disk global rules file — its User Rules live in your synced
334
- Cursor account — so there is nothing for the installer to write. The plugin ships the
335
- payload as an always-apply rule instead, which takes effect as soon as the
336
- plugin is installed.
424
+ Cursor account — so there was never anything for the installer to write here;
425
+ this is the harness the rest of leos-agent's live delivery was modeled on. The
426
+ plugin ships the payload as an always-apply rule instead, which takes effect
427
+ as soon as the plugin is installed. The installer still has one job for
428
+ Cursor: writing `~/.cursor/rules/leos-agent-routing.mdc`, and only when
429
+ [routing](#per-machine-model-routing) is actually configured for it.
337
430
 
338
431
  **Install** — either through the UI, or as a local clone.
339
432
 
@@ -394,14 +487,17 @@ plugins:
394
487
  - leos-agent
395
488
  ```
396
489
 
397
- **Run Hermes once before installing.** The payload goes into `~/.hermes/SOUL.md`,
398
- the agent's identity prompt, and Hermes writes its own starter version of that
399
- file on first run. The installer deliberately never creates it — if `SOUL.md` is
400
- missing it reports `skipped` and leaves Hermes' bootstrap alone. Once it exists:
490
+ Nothing else to install. `register(ctx)` in `__init__.py` calls
491
+ `ctx.register_system_prompt_section("leos-agent", ..., position="after_memory")`
492
+ — the cache-safe path: it renders once per session and freezes, rather than
493
+ re-rendering on every turn. `~/.hermes/SOUL.md` is not written by this plugin
494
+ at all, so there is no "run Hermes once first" step any more. On a Hermes build
495
+ old enough to lack `register_system_prompt_section`, the plugin degrades
496
+ silently and delivers nothing — upgrade Hermes.
401
497
 
402
- ```bash
403
- /leo-install
404
- ```
498
+ `/leo-install` still exists, mainly for `--dry-run` and `--uninstall`; a plain
499
+ run is a no-op unless a pre-11.0 install left a `<leos-agent>` block in
500
+ `SOUL.md`, in which case it strips it and reports `migrated`.
405
501
 
406
502
  **Upgrade**
407
503
 
@@ -415,7 +511,9 @@ or, for a clone:
415
511
  git -C ~/.hermes/plugins/leos-agent pull
416
512
  ```
417
513
 
418
- Then re-run `/leo-install`.
514
+ Restart Hermes (or `/restart`) for a fresh session to pick up the new payload.
515
+ If this checkout still carries a legacy `<leos-agent>` block in `SOUL.md`, run
516
+ `/leo-install` once to strip it.
419
517
 
420
518
  **Uninstall**
421
519
 
@@ -428,7 +526,8 @@ hermes plugins remove leos-agent
428
526
  ```
429
527
 
430
528
  Remove the `leos-agent` entry from `plugins.enabled`, and delete the clone if
431
- you made one. Your own `SOUL.md` content is left intact — only the block goes.
529
+ you made one. Your own `SOUL.md` content is left intact — only a legacy block,
530
+ if one is present, goes.
432
531
 
433
532
  **Note on model routing:** Hermes applies a single `delegation.model` to every
434
533
  child of a `delegate_task` call, so it cannot vary the model per spawn. A
@@ -445,14 +544,22 @@ say so where a per-spawn model is not available.
445
544
  pi install git:github.com/foxhatleo/leos-agent
446
545
  ```
447
546
 
448
- Then run the install skill in a pi session:
449
-
450
- ```
451
- /skill:install
452
- ```
547
+ Nothing else to install. `pi-extension.js` registers `before_agent_start`,
548
+ which runs `scripts/emit_payload.py` and appends its output to the system
549
+ prompt, and `resources_discover`, which contributes `skills/` directly — no
550
+ `/skill:install`, no `~/.pi/agent/AGENTS.md` write. Unlike Claude Code's and
551
+ Codex's session-scoped hook, `before_agent_start` fires on every turn, not
552
+ once per session, so Pi pays one `python3` spawn per turn rather than per
553
+ session.
453
554
 
454
555
  Pi pins the git ref it installed and records the package in
455
- `~/.pi/agent/settings.json`; re-running install is idempotent.
556
+ `~/.pi/agent/settings.json`; re-running install is idempotent. If a legacy
557
+ `<leos-agent>` block exists in `~/.pi/agent/AGENTS.md` from a pre-11.0 install,
558
+ strip it with the installer:
559
+
560
+ ```bash
561
+ python3 ~/.pi/agent/git/github.com/foxhatleo/leos-agent/scripts/leo-install.py pi
562
+ ```
456
563
 
457
564
  **Upgrade**
458
565
 
@@ -464,10 +571,10 @@ Pinned refs are reconciled, never silently advanced — to move to a new tag,
464
571
  install it explicitly:
465
572
 
466
573
  ```bash
467
- pi install git:github.com/foxhatleo/leos-agent@v10.7.1
574
+ pi install git:github.com/foxhatleo/leos-agent@v11.202609070.0
468
575
  ```
469
576
 
470
- Re-run `/skill:install` afterwards.
577
+ Start a new session afterwards; there is nothing else to re-run.
471
578
 
472
579
  **Uninstall**
473
580
 
@@ -494,19 +601,32 @@ opencode plugin leos-agent -g
494
601
 
495
602
  That adds the package to the `plugin` array in `~/.config/opencode/opencode.json`
496
603
  (or `.jsonc`) and caches it. Bootstrap the installer once by running the script from
497
- the cache — OpenCode's plugin API cannot register skills or commands, so the
498
- first run has to come from the package itself:
604
+ the cache — OpenCode's JS-only plugin API cannot register skills, commands, or a
605
+ payload source, so the first run has to come from the package itself:
499
606
 
500
607
  ```bash
501
608
  python3 ~/.cache/opencode/packages/leos-agent@latest/node_modules/leos-agent/scripts/leo-install.py opencode
502
609
  ```
503
610
 
504
- That writes `~/.config/opencode/AGENTS.md` and copies the skills and commands into
505
- `~/.config/opencode/skills/` and `~/.config/opencode/commands/`. From then on
506
- `/leo-install` works inside OpenCode. The copies are installed with the plugin
507
- root already resolved to an absolute path — OpenCode sets no resolution env var,
508
- and the copies live apart from the scripts they invoke — so re-run the installer
509
- after clearing or moving the package cache to point them at the new location.
611
+ That copies the skills and commands into `~/.config/opencode/skills/` and
612
+ `~/.config/opencode/commands/`, cleans up a `<leos-agent>` block a pre-11.0
613
+ install left in `~/.config/opencode/AGENTS.md`, and — since the payload itself
614
+ now arrives through a one-time line in `opencode.json` rather than a written
615
+ file — prints the exact line to add and reports it outstanding until it's
616
+ there:
617
+
618
+ ```jsonc
619
+ "instructions": ["<plugin-root>/rules/preferences.md"]
620
+ ```
621
+
622
+ The installer never adds that line for you: `opencode.json` is JSONC, with
623
+ your comments in it, and rewriting it would destroy them. Add it by hand,
624
+ once — OpenCode reads it at startup from then on. From then on `/leo-install`
625
+ works inside OpenCode for re-copying the skills and commands. Those copies are
626
+ installed with the plugin root already resolved to an absolute path —
627
+ OpenCode sets no resolution env var, and the copies live apart from the
628
+ scripts they invoke — so re-run the installer after clearing or moving the
629
+ package cache to point them at the new location.
510
630
 
511
631
  **Upgrade**
512
632
 
@@ -520,7 +640,10 @@ If the cache holds a stale copy, clear it and let OpenCode refetch:
520
640
  rm -rf ~/.cache/opencode/packages/leos-agent@*
521
641
  ```
522
642
 
523
- Re-run the bootstrap install command above to refresh the copied files.
643
+ Re-run the bootstrap install command above to refresh the copied skills and
644
+ commands — the `instructions` line does not need touching, since it just
645
+ points at the plugin root and the payload behind it updates live. OpenCode
646
+ only rereads its config at startup, so restart it to pick up either change.
524
647
 
525
648
  **Uninstall**
526
649
 
@@ -528,10 +651,11 @@ Re-run the bootstrap install command above to refresh the copied files.
528
651
  python3 ~/.cache/opencode/packages/leos-agent@latest/node_modules/leos-agent/scripts/leo-install.py opencode --uninstall
529
652
  ```
530
653
 
531
- OpenCode has no plugin-remove command, so delete the `"leos-agent"` entry from
532
- the `plugin` array in `~/.config/opencode/opencode.json` **by hand**. The installer
533
- never edits that file: it is JSONC, with your comments in it, and rewriting it
534
- would destroy them. Then clear the cache:
654
+ OpenCode has no plugin-remove command, so **by hand**: delete the
655
+ `"leos-agent"` entry from the `plugin` array, and remove the `"instructions"`
656
+ line pointing at this plugin's `rules/preferences.md`, in
657
+ `~/.config/opencode/opencode.json`. The installer edits neither — same JSONC
658
+ reason as above. Then clear the cache:
535
659
 
536
660
  ```bash
537
661
  rm -rf ~/.cache/opencode/packages/leos-agent@*
@@ -638,6 +762,14 @@ JSON.
638
762
 
639
763
  ## Development
640
764
 
765
+ **One-time setup:** `git config core.hooksPath .githooks` activates
766
+ `.githooks/pre-commit`, which stamps today's version with `scripts/bump.py`
767
+ and runs `scripts/check.py` on every commit — so a normal commit already
768
+ carries a canonical version and a green structural check, and you should not
769
+ need to run either by hand for a routine change. The scheme is
770
+ `11.YYYYMMDDX.0`: major pinned at 11, minor the UTC calendar date with a
771
+ same-day serial digit appended, patch always 0.
772
+
641
773
  Run the checks:
642
774
 
643
775
  ```bash
@@ -684,16 +816,25 @@ claude plugin uninstall leos-agent@leos-agent && claude plugin install leos-agen
684
816
  ```
685
817
 
686
818
  or replace the cachebuster suffix in the Codex manifest with one in the form
687
- `10.7.1+codex.local-YYYYMMDD-HHMMSS` and re-add. Either way, plugin changes only
819
+ `11.202609070.0+codex.local-YYYYMMDD-HHMMSS` and re-add. Either way, plugin changes only
688
820
  reach a **new** session or thread.
689
821
 
690
822
  `--check` exits non-zero when a file is out of date, and `--force` replaces a
691
823
  copied file that something else has since overwritten.
692
824
 
693
- To release: bump the version in `package.json`, the three `plugin.json` files,
694
- `.claude-plugin/marketplace.json`, `plugin.yaml`, and every mention in this
695
- README (the uninstall commands embed it in cache paths — `check.py` fails on any
696
- stale one); run `scripts/check.py`; then push a `v`-prefixed tag.
825
+ To release: the pre-commit hook has already stamped the version via
826
+ `scripts/bump.py` on your latest commit if `core.hooksPath` is set up as
827
+ above. Otherwise run it by hand:
828
+
829
+ ```bash
830
+ python3 scripts/bump.py
831
+ ```
832
+
833
+ That rewrites every `major.minor.patch` string it owns — `package.json`, the
834
+ three `plugin.json` files, `.claude-plugin/marketplace.json`, `plugin.yaml`,
835
+ and every mention in this README, including the uninstall commands' cache
836
+ paths — in one pass; `scripts/check.py` still fails the build on any stale
837
+ one it finds. Then push a `v`-prefixed tag matching that version.
697
838
 
698
839
  Pushing that tag is the whole release. `.github/workflows/release.yml` runs the
699
840
  tests and both checks, refuses a tag that disagrees with `package.json`,
package/hooks/hooks.json CHANGED
@@ -1,5 +1,17 @@
1
1
  {
2
2
  "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume|clear|compact",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "python3 \"${CLAUDE_PLUGIN_ROOT}/scripts/emit_payload.py\"",
10
+ "timeout": 10
11
+ }
12
+ ]
13
+ }
14
+ ],
3
15
  "PreToolUse": [
4
16
  {
5
17
  "matcher": "[Tt]ask|[Aa]gent|[Ss]ubagent|[Dd]ispatch|[Dd]elegate|[Ss]pawn",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leos-agent",
3
- "version": "10.7.1",
3
+ "version": "11.202609070.0",
4
4
  "description": "Leo's portable agent operating policy: orchestrator main thread, subagent-first execution, cost-tiered model routing.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -23,6 +23,9 @@
23
23
  "pi": {
24
24
  "skills": [
25
25
  "./skills"
26
+ ],
27
+ "extensions": [
28
+ "./pi-extension.js"
26
29
  ]
27
30
  },
28
31
  "files": [
@@ -31,6 +34,7 @@
31
34
  "hooks/",
32
35
  "index.js",
33
36
  "payload/",
37
+ "pi-extension.js",
34
38
  "rules/",
35
39
  "scripts/",
36
40
  "skills-claude/",