leos-agent 10.7.2 → 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.2**, 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.2 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.
@@ -189,45 +215,82 @@ python3 scripts/usage_scan.py --since 7d
189
215
  ## How it works
190
216
 
191
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.
192
221
 
193
- Cursor reads that file natively as an always-apply rule. Every other harness
194
- gets it through its global instruction file, written by
195
- [`scripts/leo-install.py`](scripts/leo-install.py) into a marker block:
196
-
197
- ```
198
- <leos-agent version="10.7.2">
199
- ...the payload...
200
- </leos-agent>
201
- ```
202
-
203
- Updating replaces that block and nothing else, so anything you wrote in those
204
- files by hand survives an upgrade untouched. The script writes only when the
205
- bytes actually differ, so running it twice is a no-op, and it writes through a
206
- temporary file and an atomic rename, so an interrupted run cannot leave a
207
- half-written instruction file behind.
208
-
209
- If it ever finds markers it cannot pair — an opener with no closer, a stray
210
- closer, two blocks — it refuses to touch that file and tells you what to fix.
211
- Guessing there would mean deleting whatever sits between the markers, which is
212
- exactly the content it exists to protect.
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:
213
227
 
214
- | Harness | Global file the installer writes |
215
- |---|---|
216
- | Claude Code | `~/.claude/CLAUDE.md` (the `leo-runner` / `leo-executor` agents need no installer step — the plugin's `agents/` directory delivers them) |
217
- | Codex | `~/.codex/AGENTS.md` (plus `~/.codex/agents/leo-runner.toml` and `leo-executor.toml`) |
218
- | Cursor | none — the plugin's always-apply rule delivers it |
219
- | Hermes | `~/.hermes/SOUL.md` (edited only if it already exists) |
220
- | Pi | `~/.pi/agent/AGENTS.md` |
221
- | OpenCode | `~/.config/opencode/AGENTS.md` (plus copied skills and commands) |
222
-
223
- **The installer is per-harness and manual.** Running it inside Codex installs Codex and
224
- nothing else; it never writes to another harness's files behind your back, and
225
- it never runs on its own at session start. Install the plugin, then run the
226
- 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.
227
253
 
228
254
  Requires Python 3.9+ and macOS, Linux, or WSL. No symlinks are used anywhere —
229
255
  installs are real clones and copies.
230
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
+
231
294
  ---
232
295
 
233
296
  ## Claude Code
@@ -242,11 +305,19 @@ claude plugin marketplace add foxhatleo/leos-agent
242
305
  claude plugin install leos-agent@leos-agent --scope user
243
306
  ```
244
307
 
245
- Then, in a Claude Code session, run `/install` (or ask it to use the `install`
246
- 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.
247
313
 
248
314
  **Upgrade**
249
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
+
250
321
  ```bash
251
322
  claude plugin marketplace update leos-agent
252
323
  ```
@@ -255,16 +326,24 @@ claude plugin marketplace update leos-agent
255
326
  claude plugin install leos-agent@leos-agent --scope user
256
327
  ```
257
328
 
258
- Re-run `/install` afterwards to refresh the block, then start a new session.
259
- Both commands are safe to repeat; installing an already-current version reports
260
- 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
+ ```
261
339
 
262
340
  **Uninstall**
263
341
 
264
- 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:
265
344
 
266
345
  ```bash
267
- python3 ~/.claude/plugins/cache/leos-agent/leos-agent/10.7.2/scripts/leo-install.py claude --uninstall
346
+ python3 ~/.claude/plugins/cache/leos-agent/leos-agent/11.202609070.0/scripts/leo-install.py claude --uninstall
268
347
  ```
269
348
 
270
349
  ```bash
@@ -291,18 +370,22 @@ codex plugin marketplace add foxhatleo/leos-agent
291
370
  codex plugin add leos-agent@leos-agent
292
371
  ```
293
372
 
294
- Then run the `install` skill in a Codex session (`$leos-agent`, then `install`), or
295
- 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:
296
378
 
297
379
  ```bash
298
- python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.7.2/scripts/leo-install.py codex
380
+ python3 ~/.codex/plugins/cache/leos-agent/leos-agent/11.202609070.0/scripts/leo-install.py codex
299
381
  ```
300
382
 
301
- This writes `~/.codex/AGENTS.md` and installs two economical agents:
302
- `leo-runner` (`gpt-5.6-luna`, low effort) for narrow repeatable work and
303
- `leo-executor` (`gpt-5.6-terra`, medium effort) for well-specified
304
- implementation. Codex plugins cannot ship agent definitions themselves, which
305
- 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`.
306
389
 
307
390
  **Upgrade**
308
391
 
@@ -314,13 +397,15 @@ codex plugin marketplace upgrade leos-agent
314
397
  codex plugin add leos-agent@leos-agent
315
398
  ```
316
399
 
317
- Re-run the installer, then start a new thread — Codex picks up plugin changes on new
318
- 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.
319
404
 
320
405
  **Uninstall**
321
406
 
322
407
  ```bash
323
- python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.7.2/scripts/leo-install.py codex --uninstall
408
+ python3 ~/.codex/plugins/cache/leos-agent/leos-agent/11.202609070.0/scripts/leo-install.py codex --uninstall
324
409
  ```
325
410
 
326
411
  ```bash
@@ -336,9 +421,12 @@ codex plugin marketplace remove leos-agent
336
421
  ## Cursor
337
422
 
338
423
  Cursor has no on-disk global rules file — its User Rules live in your synced
339
- Cursor account — so there is nothing for the installer to write. The plugin ships the
340
- payload as an always-apply rule instead, which takes effect as soon as the
341
- 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.
342
430
 
343
431
  **Install** — either through the UI, or as a local clone.
344
432
 
@@ -399,14 +487,17 @@ plugins:
399
487
  - leos-agent
400
488
  ```
401
489
 
402
- **Run Hermes once before installing.** The payload goes into `~/.hermes/SOUL.md`,
403
- the agent's identity prompt, and Hermes writes its own starter version of that
404
- file on first run. The installer deliberately never creates it — if `SOUL.md` is
405
- 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.
406
497
 
407
- ```bash
408
- /leo-install
409
- ```
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`.
410
501
 
411
502
  **Upgrade**
412
503
 
@@ -420,7 +511,9 @@ or, for a clone:
420
511
  git -C ~/.hermes/plugins/leos-agent pull
421
512
  ```
422
513
 
423
- 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.
424
517
 
425
518
  **Uninstall**
426
519
 
@@ -433,7 +526,8 @@ hermes plugins remove leos-agent
433
526
  ```
434
527
 
435
528
  Remove the `leos-agent` entry from `plugins.enabled`, and delete the clone if
436
- 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.
437
531
 
438
532
  **Note on model routing:** Hermes applies a single `delegation.model` to every
439
533
  child of a `delegate_task` call, so it cannot vary the model per spawn. A
@@ -450,14 +544,22 @@ say so where a per-spawn model is not available.
450
544
  pi install git:github.com/foxhatleo/leos-agent
451
545
  ```
452
546
 
453
- Then run the install skill in a pi session:
454
-
455
- ```
456
- /skill:install
457
- ```
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.
458
554
 
459
555
  Pi pins the git ref it installed and records the package in
460
- `~/.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
+ ```
461
563
 
462
564
  **Upgrade**
463
565
 
@@ -469,10 +571,10 @@ Pinned refs are reconciled, never silently advanced — to move to a new tag,
469
571
  install it explicitly:
470
572
 
471
573
  ```bash
472
- pi install git:github.com/foxhatleo/leos-agent@v10.7.2
574
+ pi install git:github.com/foxhatleo/leos-agent@v11.202609070.0
473
575
  ```
474
576
 
475
- Re-run `/skill:install` afterwards.
577
+ Start a new session afterwards; there is nothing else to re-run.
476
578
 
477
579
  **Uninstall**
478
580
 
@@ -499,19 +601,32 @@ opencode plugin leos-agent -g
499
601
 
500
602
  That adds the package to the `plugin` array in `~/.config/opencode/opencode.json`
501
603
  (or `.jsonc`) and caches it. Bootstrap the installer once by running the script from
502
- the cache — OpenCode's plugin API cannot register skills or commands, so the
503
- 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:
504
606
 
505
607
  ```bash
506
608
  python3 ~/.cache/opencode/packages/leos-agent@latest/node_modules/leos-agent/scripts/leo-install.py opencode
507
609
  ```
508
610
 
509
- That writes `~/.config/opencode/AGENTS.md` and copies the skills and commands into
510
- `~/.config/opencode/skills/` and `~/.config/opencode/commands/`. From then on
511
- `/leo-install` works inside OpenCode. The copies are installed with the plugin
512
- root already resolved to an absolute path — OpenCode sets no resolution env var,
513
- and the copies live apart from the scripts they invoke — so re-run the installer
514
- 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.
515
630
 
516
631
  **Upgrade**
517
632
 
@@ -525,7 +640,10 @@ If the cache holds a stale copy, clear it and let OpenCode refetch:
525
640
  rm -rf ~/.cache/opencode/packages/leos-agent@*
526
641
  ```
527
642
 
528
- 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.
529
647
 
530
648
  **Uninstall**
531
649
 
@@ -533,10 +651,11 @@ Re-run the bootstrap install command above to refresh the copied files.
533
651
  python3 ~/.cache/opencode/packages/leos-agent@latest/node_modules/leos-agent/scripts/leo-install.py opencode --uninstall
534
652
  ```
535
653
 
536
- OpenCode has no plugin-remove command, so delete the `"leos-agent"` entry from
537
- the `plugin` array in `~/.config/opencode/opencode.json` **by hand**. The installer
538
- never edits that file: it is JSONC, with your comments in it, and rewriting it
539
- 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:
540
659
 
541
660
  ```bash
542
661
  rm -rf ~/.cache/opencode/packages/leos-agent@*
@@ -643,6 +762,14 @@ JSON.
643
762
 
644
763
  ## Development
645
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
+
646
773
  Run the checks:
647
774
 
648
775
  ```bash
@@ -689,16 +816,25 @@ claude plugin uninstall leos-agent@leos-agent && claude plugin install leos-agen
689
816
  ```
690
817
 
691
818
  or replace the cachebuster suffix in the Codex manifest with one in the form
692
- `10.7.2+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
693
820
  reach a **new** session or thread.
694
821
 
695
822
  `--check` exits non-zero when a file is out of date, and `--force` replaces a
696
823
  copied file that something else has since overwritten.
697
824
 
698
- To release: bump the version in `package.json`, the three `plugin.json` files,
699
- `.claude-plugin/marketplace.json`, `plugin.yaml`, and every mention in this
700
- README (the uninstall commands embed it in cache paths — `check.py` fails on any
701
- 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.
702
838
 
703
839
  Pushing that tag is the whole release. `.github/workflows/release.yml` runs the
704
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.2",
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/",