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 +241 -105
- package/hooks/hooks.json +12 -0
- package/package.json +5 -1
- package/pi-extension.js +90 -0
- package/scripts/bump.py +186 -0
- package/scripts/check.py +74 -4
- package/scripts/emit_payload.py +99 -0
- package/scripts/leo-install.py +142 -107
- package/scripts/measure_context.py +12 -10
- package/scripts/payload.py +95 -0
- package/skills/doctor/SKILL.md +64 -41
- package/skills/install/SKILL.md +59 -33
- package/skills/tune-routing/SKILL.md +21 -12
- package/skills/tune-routing/reference/harnesses.md +14 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# leos-agent
|
|
2
2
|
|
|
3
|
-
Leo's portable agent operating policy, version **
|
|
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
|
-
**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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 —
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
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
|
|
194
|
-
|
|
195
|
-
[`scripts/
|
|
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 |
|
|
215
|
-
|
|
216
|
-
| Claude Code |
|
|
217
|
-
| Codex |
|
|
218
|
-
| Cursor |
|
|
219
|
-
| Hermes | `~/.hermes/SOUL.md`
|
|
220
|
-
| Pi | `~/.pi/agent/AGENTS.md` |
|
|
221
|
-
| OpenCode | `~/.config/opencode/
|
|
222
|
-
|
|
223
|
-
**The
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
246
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
that
|
|
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/
|
|
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
|
-
|
|
295
|
-
|
|
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/
|
|
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/
|
|
302
|
-
`leo-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
is
|
|
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
|
|
318
|
-
|
|
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/
|
|
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
|
|
340
|
-
|
|
341
|
-
plugin
|
|
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
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
-
|
|
408
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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@
|
|
574
|
+
pi install git:github.com/foxhatleo/leos-agent@v11.202609070.0
|
|
473
575
|
```
|
|
474
576
|
|
|
475
|
-
|
|
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
|
|
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
|
|
510
|
-
`~/.config/opencode/
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
|
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
|
|
537
|
-
the `plugin` array
|
|
538
|
-
|
|
539
|
-
|
|
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
|
-
`
|
|
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:
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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": "
|
|
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/",
|