leos-agent 7.0.0 → 10.2.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 +544 -28
- package/commands/handoff.md +11 -0
- package/commands/handon.md +10 -0
- package/commands/review-pr.md +9 -0
- package/commands-claude/watch-review.md +9 -0
- package/index.js +12 -0
- package/package.json +29 -14
- package/payload/codex-agents/leo-executor.toml +36 -0
- package/payload/codex-agents/leo-runner.toml +28 -0
- package/rules/preferences.md +97 -0
- package/scripts/check.py +244 -0
- package/scripts/ghreview.py +17 -3
- package/scripts/handoff.py +183 -0
- package/scripts/leo-install.py +509 -0
- package/scripts/measure_context.py +113 -0
- package/scripts/publish-npm.py +138 -0
- package/scripts/watch_review.py +169 -0
- package/skills/doctor/SKILL.md +73 -115
- package/skills/doctor/agents/openai.yaml +5 -0
- package/skills/handoff/SKILL.md +99 -0
- package/skills/handoff/agents/openai.yaml +5 -0
- package/skills/handon/SKILL.md +61 -0
- package/skills/install/SKILL.md +79 -0
- package/skills/install/agents/openai.yaml +5 -0
- package/skills/review-pr/SKILL.md +59 -318
- package/skills/review-pr/reference/lenses.md +67 -0
- package/skills/review-pr/reference/procedure.md +348 -0
- package/skills-claude/attach-pr/SKILL.md +178 -0
- package/skills-claude/watch-review/SKILL.md +91 -0
- package/adapters/cursor/agents/executor.md +0 -17
- package/adapters/cursor/agents/expert.md +0 -70
- package/adapters/cursor/agents/explore.md +0 -16
- package/adapters/cursor/agents/implementer.md +0 -18
- package/adapters/cursor/agents/investigator.md +0 -18
- package/adapters/cursor/agents/planner.md +0 -28
- package/adapters/cursor/agents/review-lens.md +0 -22
- package/adapters/cursor/agents/reviewer.md +0 -34
- package/adapters/opencode/agents.json +0 -105
- package/adapters/opencode/plugin.js +0 -576
- package/config/MCP_PINS.md +0 -17
- package/config/models.json +0 -676
- package/hooks/bash-guard.py +0 -583
- package/hooks/cursor-guard.py +0 -84
- package/hooks/hooks-cursor.json +0 -11
- package/hooks/hooks.json +0 -20
- package/hooks/session-start.py +0 -148
- package/roles/executor.md +0 -15
- package/roles/expert.md +0 -67
- package/roles/explore.md +0 -13
- package/roles/implementer.md +0 -16
- package/roles/investigator.md +0 -15
- package/roles/planner.md +0 -25
- package/roles/review-lens.md +0 -20
- package/roles/reviewer.md +0 -31
- package/scripts/doctor.py +0 -520
- package/scripts/jsonc_bridge.cjs +0 -23
- package/scripts/memory.py +0 -744
- package/scripts/render_adapters.py +0 -508
- package/scripts/setup.py +0 -1753
- package/settings.json +0 -7
- package/skills/.gitkeep +0 -0
- package/skills/brainstorming/SKILL.md +0 -111
- package/skills/debugging/SKILL.md +0 -100
- package/skills/delegation/SKILL.md +0 -143
- package/skills/executing-plans/SKILL.md +0 -117
- package/skills/finishing-a-branch/SKILL.md +0 -125
- package/skills/freshness/SKILL.md +0 -131
- package/skills/memory/SKILL.md +0 -154
- package/skills/resolve-ticket/SKILL.md +0 -275
- package/skills/setup/SKILL.md +0 -199
- package/skills/setup/agents/openai.yaml +0 -5
- package/skills/test-first/SKILL.md +0 -92
- package/skills/using-leo/SKILL.md +0 -101
- package/skills/using-leo/references/claude-mapping.md +0 -33
- package/skills/using-leo/references/codex-mapping.md +0 -33
- package/skills/using-leo/references/cursor-mapping.md +0 -35
- package/skills/using-leo/references/hermes-mapping.md +0 -37
- package/skills/using-leo/references/opencode-mapping.md +0 -39
- package/skills/verification/SKILL.md +0 -110
- package/skills/visual-verification/SKILL.md +0 -115
- package/skills/watch-review/SKILL.md +0 -128
- package/skills/watch-review/agents/openai.yaml +0 -5
- package/skills/worktrees/SKILL.md +0 -131
- package/skills/writing-plans/SKILL.md +0 -97
- package/skills/writing-skills/SKILL.md +0 -141
- package/vendor/jsonc-parser-3.3.1/LICENSE.md +0 -21
- package/vendor/jsonc-parser-3.3.1/README.md +0 -26
- package/vendor/jsonc-parser-3.3.1/lib/umd/impl/edit.js +0 -201
- package/vendor/jsonc-parser-3.3.1/lib/umd/impl/format.js +0 -275
- package/vendor/jsonc-parser-3.3.1/lib/umd/impl/parser.js +0 -682
- package/vendor/jsonc-parser-3.3.1/lib/umd/impl/scanner.js +0 -456
- package/vendor/jsonc-parser-3.3.1/lib/umd/impl/string-intern.js +0 -42
- package/vendor/jsonc-parser-3.3.1/lib/umd/main.d.ts +0 -351
- package/vendor/jsonc-parser-3.3.1/lib/umd/main.js +0 -194
- package/vendor/jsonc-parser-3.3.1/package.json +0 -37
- package/workflows/cost-tiered-fix.js +0 -287
package/README.md
CHANGED
|
@@ -1,50 +1,566 @@
|
|
|
1
|
-
|
|
1
|
+
# leos-agent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Leo's portable agent operating policy, version **10.2.0**, installable on Claude
|
|
4
|
+
Code, Codex, Cursor, Hermes, Pi, and OpenCode through each harness's own plugin
|
|
5
|
+
system.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
The policy it carries is short: **the main thread is an orchestrator.**
|
|
8
|
+
Investigation, brainstorming, debugging, execution, testing, and mechanical work
|
|
9
|
+
all run in briefed subagents, so the main thread never fills up with the files,
|
|
10
|
+
retries, and logs that produced an answer — only the answer. Work runs at one of two
|
|
11
|
+
named tiers: **standard**, the model you are already using, for thinking and
|
|
12
|
+
judging; **economical**, the cheapest sufficient profile where the harness can
|
|
13
|
+
select one. Narrow search, reading, testing, and mechanical work use the runner
|
|
14
|
+
profile; well-specified implementation uses the executor profile.
|
|
6
15
|
|
|
7
|
-
|
|
16
|
+
## What it ships
|
|
8
17
|
|
|
9
|
-
|
|
18
|
+
Beyond the preferences payload: a setup diagnostic, a session handoff pair, and
|
|
19
|
+
three GitHub skills. The GitHub ones need `gh`, authenticated.
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
|
|
21
|
+
| Skill | What it does | Where |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `review-pr` | Reviews a pull request and stages inline comments as a **pending** review — visible only to you until you submit or discard on GitHub. Never submits. Resolves the originating ticket (Linear, Jira, GitHub issue) from the PR's title, body, or branch when one is named, and adds a spec lens that checks the diff against it. | every skill-loading harness |
|
|
24
|
+
| `watch-review` | Arms a watcher that streams new direct review requests into the session, one notification per pull request, for `review-pr` to handle. Polling is a shell script (`scripts/watch_review.py`), not a model loop: an idle tick is one `gh` call and zero tokens. | **Claude Code only** — built on its Monitor tool |
|
|
25
|
+
| `doctor` | Diagnoses this harness's setup, read-only: whether the `<leos-agent>` block is injected and current, what else is loaded into every session (global instruction file, memories, settings, skills), and whether a local checkout passes `scripts/check.py`. Run it with `/doctor`. | every skill-loading harness |
|
|
26
|
+
| `handoff` | Writes this session's context — goal, what landed, what is next, key files, decisions, gotchas — to a markdown document under `~/.leos-agent-local/handoffs/`, so a later session can pick the work up. Pointers, not contents: it names files rather than pasting them. Run it with `/handoff`. | every skill-loading harness |
|
|
27
|
+
| `handon` | Loads a handoff written earlier — in this harness or a different one — and resumes from it, reporting any drift first when the directory, branch, or HEAD has moved since. Loading never consumes a handoff. Run it with `/handon <name>`. | every skill-loading harness |
|
|
28
|
+
| `attach-pr` | Attaches the current desktop session to an existing pull request so the app shows its PR card. Creates nothing and pushes nothing. | **Claude Code only** — it drives that app's card |
|
|
29
|
+
|
|
30
|
+
The Claude-only pair live in `skills-claude/` and `commands-claude/`, listed in
|
|
31
|
+
`.claude-plugin/plugin.json` and nowhere else. Hermes receives the preferences
|
|
32
|
+
payload but no skills — it has no skill loader.
|
|
33
|
+
|
|
34
|
+
The watcher records each reviewed pull request under `~/.leos-agent-local/`
|
|
35
|
+
(override with `$LEOS_AGENT_LOCAL_PATH`), so it never surfaces the same one
|
|
36
|
+
twice; `watch_review.py forget <n>` puts one back in play.
|
|
37
|
+
|
|
38
|
+
Handoffs live in the same place, under `handoffs/`. Nothing there is ever pruned
|
|
39
|
+
automatically: `handoff.py list [--all]` shows what exists and `handoff.py rm
|
|
40
|
+
<name>` is the only way one goes away. The directory is deliberately outside
|
|
41
|
+
the plugin, so upgrading or reinstalling can never take state with it.
|
|
42
|
+
|
|
43
|
+
## How it works
|
|
44
|
+
|
|
45
|
+
The payload lives in exactly one file: [`rules/preferences.md`](rules/preferences.md).
|
|
46
|
+
|
|
47
|
+
Cursor reads that file natively as an always-apply rule. Every other harness
|
|
48
|
+
gets it through its global instruction file, written by
|
|
49
|
+
[`scripts/leo-install.py`](scripts/leo-install.py) into a marker block:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
<leos-agent version="10.2.0">
|
|
53
|
+
...the payload...
|
|
54
|
+
</leos-agent>
|
|
13
55
|
```
|
|
14
56
|
|
|
15
|
-
|
|
57
|
+
Updating replaces that block and nothing else, so anything you wrote in those
|
|
58
|
+
files by hand survives an upgrade untouched. The script writes only when the
|
|
59
|
+
bytes actually differ, so running it twice is a no-op, and it writes through a
|
|
60
|
+
temporary file and an atomic rename, so an interrupted run cannot leave a
|
|
61
|
+
half-written instruction file behind.
|
|
62
|
+
|
|
63
|
+
If it ever finds markers it cannot pair — an opener with no closer, a stray
|
|
64
|
+
closer, two blocks — it refuses to touch that file and tells you what to fix.
|
|
65
|
+
Guessing there would mean deleting whatever sits between the markers, which is
|
|
66
|
+
exactly the content it exists to protect.
|
|
67
|
+
|
|
68
|
+
| Harness | Global file the installer writes |
|
|
69
|
+
|---|---|
|
|
70
|
+
| Claude Code | `~/.claude/CLAUDE.md` |
|
|
71
|
+
| Codex | `~/.codex/AGENTS.md` (plus `~/.codex/agents/leo-runner.toml` and `leo-executor.toml`) |
|
|
72
|
+
| Cursor | none — the plugin's always-apply rule delivers it |
|
|
73
|
+
| Hermes | `~/.hermes/SOUL.md` (edited only if it already exists) |
|
|
74
|
+
| Pi | `~/.pi/agent/AGENTS.md` |
|
|
75
|
+
| OpenCode | `~/.config/opencode/AGENTS.md` (plus copied skills and commands) |
|
|
76
|
+
|
|
77
|
+
**The installer is per-harness and manual.** Running it inside Codex installs Codex and
|
|
78
|
+
nothing else; it never writes to another harness's files behind your back, and
|
|
79
|
+
it never runs on its own at session start. Install the plugin, then run the
|
|
80
|
+
installer once in that harness.
|
|
16
81
|
|
|
17
|
-
|
|
18
|
-
|
|
82
|
+
Requires Python 3.9+ and macOS, Linux, or WSL. No symlinks are used anywhere —
|
|
83
|
+
installs are real clones and copies.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Claude Code
|
|
88
|
+
|
|
89
|
+
**Install**
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
claude plugin marketplace add foxhatleo/leos-agent
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
claude plugin install leos-agent@leos-agent --scope user
|
|
19
97
|
```
|
|
20
98
|
|
|
21
|
-
|
|
99
|
+
Then, in a Claude Code session, run `/install` (or ask it to use the `install`
|
|
100
|
+
skill). That writes the block into `~/.claude/CLAUDE.md`.
|
|
22
101
|
|
|
23
|
-
|
|
102
|
+
**Upgrade**
|
|
24
103
|
|
|
25
|
-
|
|
104
|
+
```bash
|
|
105
|
+
claude plugin marketplace update leos-agent
|
|
106
|
+
```
|
|
26
107
|
|
|
27
|
-
|
|
108
|
+
```bash
|
|
109
|
+
claude plugin install leos-agent@leos-agent --scope user
|
|
110
|
+
```
|
|
28
111
|
|
|
29
|
-
|
|
112
|
+
Re-run `/install` afterwards to refresh the block, then start a new session.
|
|
113
|
+
Both commands are safe to repeat; installing an already-current version reports
|
|
114
|
+
that it is already installed and changes nothing.
|
|
30
115
|
|
|
31
|
-
|
|
116
|
+
**Uninstall**
|
|
32
117
|
|
|
33
|
-
|
|
118
|
+
Run the installer's uninstall first, while the script is still on disk:
|
|
34
119
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
120
|
+
```bash
|
|
121
|
+
python3 ~/.claude/plugins/cache/leos-agent/leos-agent/10.2.0/scripts/leo-install.py claude --uninstall
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
claude plugin uninstall leos-agent@leos-agent
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Optionally drop the marketplace too:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
claude plugin marketplace remove leos-agent
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Codex
|
|
137
|
+
|
|
138
|
+
**Install**
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
codex plugin marketplace add foxhatleo/leos-agent
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
codex plugin add leos-agent@leos-agent
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Then run the `install` skill in a Codex session (`$leos-agent`, then `install`), or
|
|
149
|
+
run the script directly:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.2.0/scripts/leo-install.py codex
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
This writes `~/.codex/AGENTS.md` and installs two economical agents:
|
|
156
|
+
`leo-runner` (`gpt-5.6-luna`, low effort) for narrow repeatable work and
|
|
157
|
+
`leo-executor` (`gpt-5.6-terra`, medium effort) for well-specified
|
|
158
|
+
implementation. Codex plugins cannot ship agent definitions themselves, which
|
|
159
|
+
is why the installer writes them.
|
|
160
|
+
|
|
161
|
+
**Upgrade**
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
codex plugin marketplace upgrade leos-agent
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
codex plugin add leos-agent@leos-agent
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Re-run the installer, then start a new thread — Codex picks up plugin changes on new
|
|
172
|
+
threads only. Re-adding an already-installed plugin is idempotent.
|
|
173
|
+
|
|
174
|
+
**Uninstall**
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
python3 ~/.codex/plugins/cache/leos-agent/leos-agent/10.2.0/scripts/leo-install.py codex --uninstall
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
codex plugin remove leos-agent@leos-agent
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
codex plugin marketplace remove leos-agent
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Cursor
|
|
191
|
+
|
|
192
|
+
Cursor has no on-disk global rules file — its User Rules live in your synced
|
|
193
|
+
Cursor account — so there is nothing for the installer to write. The plugin ships the
|
|
194
|
+
payload as an always-apply rule instead, which takes effect as soon as the
|
|
195
|
+
plugin is installed.
|
|
196
|
+
|
|
197
|
+
**Install** — either through the UI, or as a local clone.
|
|
198
|
+
|
|
199
|
+
In the IDE: open the **Customize** sidebar, add the marketplace
|
|
200
|
+
`foxhatleo/leos-agent`, and install **Leo's Agent** at user scope.
|
|
201
|
+
|
|
202
|
+
Or clone it into Cursor's local plugin directory (a clone, not a symlink):
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
git clone https://github.com/foxhatleo/leos-agent ~/.cursor/plugins/local/leos-agent
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Cursor does not currently expose a reliable non-interactive per-plugin install
|
|
209
|
+
command, so those two paths are the supported ones.
|
|
210
|
+
|
|
211
|
+
**Upgrade**
|
|
212
|
+
|
|
213
|
+
Refresh the marketplace from the Customize panel, or for a local clone:
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
git -C ~/.cursor/plugins/local/leos-agent pull
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Uninstall**
|
|
220
|
+
|
|
221
|
+
Remove the plugin from the Customize panel, or delete the clone:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
rm -rf ~/.cursor/plugins/local/leos-agent
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
There is no block to remove — nothing was written outside the plugin directory.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Hermes
|
|
232
|
+
|
|
233
|
+
**Install**
|
|
234
|
+
|
|
235
|
+
If the plugin resolves through the community index:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
hermes plugins install leos-agent
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Otherwise clone it into the Hermes plugin directory:
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
git clone https://github.com/foxhatleo/leos-agent ~/.hermes/plugins/leos-agent
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Hermes plugins are opt-in, so enable it by adding `leos-agent` to
|
|
248
|
+
`plugins.enabled` in `~/.hermes/config.yaml`:
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
plugins:
|
|
252
|
+
enabled:
|
|
253
|
+
- leos-agent
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Run Hermes once before installing.** The payload goes into `~/.hermes/SOUL.md`,
|
|
257
|
+
the agent's identity prompt, and Hermes writes its own starter version of that
|
|
258
|
+
file on first run. The installer deliberately never creates it — if `SOUL.md` is
|
|
259
|
+
missing it reports `skipped` and leaves Hermes' bootstrap alone. Once it exists:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
/leo-install
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
**Upgrade**
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
hermes plugins update leos-agent
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
or, for a clone:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
git -C ~/.hermes/plugins/leos-agent pull
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Then re-run `/leo-install`.
|
|
278
|
+
|
|
279
|
+
**Uninstall**
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
python3 ~/.hermes/plugins/leos-agent/scripts/leo-install.py hermes --uninstall
|
|
283
|
+
```
|
|
41
284
|
|
|
42
|
-
|
|
285
|
+
```bash
|
|
286
|
+
hermes plugins remove leos-agent
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Remove the `leos-agent` entry from `plugins.enabled`, and delete the clone if
|
|
290
|
+
you made one. Your own `SOUL.md` content is left intact — only the block goes.
|
|
291
|
+
|
|
292
|
+
**Note on model routing:** Hermes applies a single `delegation.model` to every
|
|
293
|
+
child of a `delegate_task` call, so it cannot vary the model per spawn. The
|
|
294
|
+
policy therefore routes all Hermes work to the current model.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Pi
|
|
299
|
+
|
|
300
|
+
**Install**
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
pi install git:github.com/foxhatleo/leos-agent
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Then run the install skill in a pi session:
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
/skill:install
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Pi pins the git ref it installed and records the package in
|
|
313
|
+
`~/.pi/agent/settings.json`; re-running install is idempotent.
|
|
314
|
+
|
|
315
|
+
**Upgrade**
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
pi update git:github.com/foxhatleo/leos-agent
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Pinned refs are reconciled, never silently advanced — to move to a new tag,
|
|
322
|
+
install it explicitly:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
pi install git:github.com/foxhatleo/leos-agent@v10.2.0
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Re-run `/skill:install` afterwards.
|
|
329
|
+
|
|
330
|
+
**Uninstall**
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
python3 ~/.pi/agent/git/github.com/foxhatleo/leos-agent/scripts/leo-install.py pi --uninstall
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
pi remove git:github.com/foxhatleo/leos-agent
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## OpenCode
|
|
343
|
+
|
|
344
|
+
OpenCode loads plugins as npm packages, so this one is published to npm as
|
|
345
|
+
`leos-agent`.
|
|
346
|
+
|
|
347
|
+
**Install**
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
opencode plugin leos-agent -g
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
That adds the package to the `plugin` array in `~/.config/opencode/opencode.json`
|
|
354
|
+
(or `.jsonc`) and caches it. Bootstrap the installer once by running the script from
|
|
355
|
+
the cache — OpenCode's plugin API cannot register skills or commands, so the
|
|
356
|
+
first run has to come from the package itself:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
python3 ~/.cache/opencode/packages/leos-agent@latest/node_modules/leos-agent/scripts/leo-install.py opencode
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
That writes `~/.config/opencode/AGENTS.md` and copies the skills and commands into
|
|
363
|
+
`~/.config/opencode/skills/` and `~/.config/opencode/commands/`. From then on
|
|
364
|
+
`/leo-install` works inside OpenCode.
|
|
365
|
+
|
|
366
|
+
**Upgrade**
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
opencode plugin leos-agent -g -f
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
If the cache holds a stale copy, clear it and let OpenCode refetch:
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
rm -rf ~/.cache/opencode/packages/leos-agent@*
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Re-run the bootstrap install command above to refresh the copied files.
|
|
379
|
+
|
|
380
|
+
**Uninstall**
|
|
381
|
+
|
|
382
|
+
```bash
|
|
383
|
+
python3 ~/.cache/opencode/packages/leos-agent@latest/node_modules/leos-agent/scripts/leo-install.py opencode --uninstall
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
OpenCode has no plugin-remove command, so delete the `"leos-agent"` entry from
|
|
387
|
+
the `plugin` array in `~/.config/opencode/opencode.json` **by hand**. The installer
|
|
388
|
+
never edits that file: it is JSONC, with your comments in it, and rewriting it
|
|
389
|
+
would destroy them. Then clear the cache:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
rm -rf ~/.cache/opencode/packages/leos-agent@*
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Migrating from v8
|
|
398
|
+
|
|
399
|
+
Version 10 renames the plugin from `leo` to `leos-agent`, so the old install
|
|
400
|
+
does not upgrade in place — remove it first. Most skills that were invoked
|
|
401
|
+
as `leo:<name>` are gone; v10 ships a deliberately lean payload plus the three
|
|
402
|
+
GitHub skills above, now unprefixed (`review-pr`, not `leo:review-pr`). The
|
|
403
|
+
watcher no longer runs under `/loop`: it is a shell process streaming into
|
|
404
|
+
Claude Code's Monitor tool, so idle polling costs nothing.
|
|
405
|
+
|
|
406
|
+
**Claude Code.** The old plugin will show as `failed to load` once the
|
|
407
|
+
marketplace points at v10 (`Plugin leo not found in marketplace leos-agent`).
|
|
408
|
+
Remove it:
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
claude plugin uninstall leo@leos-agent
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
**Codex.** The v8 marketplace entry is pinned to an old commit, and Codex
|
|
415
|
+
refuses to re-add a marketplace from a different source. Remove and re-add:
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
codex plugin marketplace remove leos-agent && codex plugin marketplace add foxhatleo/leos-agent
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**OpenCode.** The existing `"leos-agent"` plugin entry stays valid; clear the
|
|
422
|
+
cache so it refetches v10.
|
|
423
|
+
|
|
424
|
+
**Leftover files.** v8 wrote `*.leo-backup` files next to the instruction files
|
|
425
|
+
it touched. v10 does not create backups — the block replacement is surgical, and
|
|
426
|
+
`--dry-run` shows you any change before it happens. These are safe to delete:
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
rm -f ~/.claude/CLAUDE.md.leo-backup ~/.codex/AGENTS.md.leo-backup ~/.config/opencode/AGENTS.md.leo-backup
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Extending it
|
|
435
|
+
|
|
436
|
+
The repo root is the plugin. Each harness reads its own manifest from the same
|
|
437
|
+
tree, and the three payload directories are shared between them.
|
|
438
|
+
|
|
439
|
+
**Skills** live in `skills/<name>/SKILL.md`, or `skills-claude/<name>/SKILL.md`
|
|
440
|
+
for one only Claude Code can use; commands mirror that with `commands/` and
|
|
441
|
+
`commands-claude/`. The two `-claude` directories are listed in
|
|
442
|
+
`.claude-plugin/plugin.json` and nowhere else. Keep the frontmatter of a
|
|
443
|
+
portable skill to `name` and `description`, plus
|
|
444
|
+
`disable-model-invocation: true` on a skill that must never fire on its own.
|
|
445
|
+
Codex uses the matching sibling `agents/openai.yaml` with
|
|
446
|
+
`policy.allow_implicit_invocation: false`; harnesses without a control for it
|
|
447
|
+
get the constraint stated in the description. That subset is what all five skill-loading harnesses accept, and
|
|
448
|
+
anything richer will parse on Claude Code and be ignored or rejected elsewhere.
|
|
449
|
+
Claude Code, Codex, Cursor, and Pi load `skills/` straight from their manifests;
|
|
450
|
+
OpenCode gets a copy from the installer.
|
|
451
|
+
|
|
452
|
+
**Commands** live in `commands/<name>.md`. Claude Code and Cursor read the
|
|
453
|
+
directory from their manifests; OpenCode gets a copy. Codex dropped custom
|
|
454
|
+
prompts in favour of skills, so add a skill there instead.
|
|
455
|
+
|
|
456
|
+
### Two conventions, both enforced by `scripts/check.py`
|
|
457
|
+
|
|
458
|
+
**Progressive disclosure.** `SKILL.md` is the *dispatch contract* — what the
|
|
459
|
+
main thread does. The procedure a subagent follows goes in
|
|
460
|
+
`skills/<name>/reference/*.md`, which the brief points at by path. `review-pr`
|
|
461
|
+
is the worked example: the main thread loads a 3.2 KB contract, the reviewer
|
|
462
|
+
subagent reads `reference/procedure.md`, and the lens sub-subagents read
|
|
463
|
+
`reference/lenses.md` that the reviewer itself never loads. Before the split the
|
|
464
|
+
main thread and the reviewer each loaded the same 21 KB file, and every turn
|
|
465
|
+
after that re-read it. Split a file out only when some run genuinely does not
|
|
466
|
+
read it; moving prose around costs the same tokens.
|
|
467
|
+
|
|
468
|
+
**Invocation split.** A skill is either *user-invoked* — reached by typing its
|
|
469
|
+
slash command, and carrying `disable-model-invocation: true` — or *deliberately
|
|
470
|
+
model-invocable*, reached when the model decides the task fits. On Claude Code
|
|
471
|
+
the flag also drops the skill's description from the always-loaded skill
|
|
472
|
+
listing, which is the larger saving: a description is context in every session,
|
|
473
|
+
invoked or not. Only `review-pr` and `handon` are model-invocable here, because
|
|
474
|
+
they are the two you would phrase in words ("review PR 41", "pick up where I
|
|
475
|
+
left off") rather than by name; `check.py` fails the build on any other skill
|
|
476
|
+
that omits the flag. A user-invoked skill may invoke a model-invoked one, but
|
|
477
|
+
never chains another user-invoked skill.
|
|
478
|
+
|
|
479
|
+
A description says **when to reach for this** and **what it is not** — never how
|
|
480
|
+
the skill works. The mechanism is what the body is for, and every word of it in
|
|
481
|
+
the description is paid for in sessions that never invoke the skill.
|
|
482
|
+
|
|
483
|
+
**Hooks** live in `hooks/`, wired but empty — v10 enforces its policy through
|
|
484
|
+
the payload rather than by intercepting tool calls. There are two files because
|
|
485
|
+
the formats genuinely differ: `hooks.json` (PascalCase events) serves Claude
|
|
486
|
+
Code and Codex, which both auto-load it and must never name it in their
|
|
487
|
+
manifests, and `hooks-cursor.json` (camelCase, `version: 1`) serves Cursor,
|
|
488
|
+
which does name it. See [`hooks/README.md`](hooks/README.md) for how to add one,
|
|
489
|
+
including the Hermes, OpenCode, and Pi equivalents, which are code rather than
|
|
490
|
+
JSON.
|
|
43
491
|
|
|
44
|
-
##
|
|
492
|
+
## Development
|
|
45
493
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
494
|
+
Run the checks:
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
python3 scripts/check.py
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
The structural check asserts that the version matches across every manifest,
|
|
501
|
+
the marketplace entry, and this README; that each manifest carries what its
|
|
502
|
+
harness requires and every declared path exists; that both hook files parse in
|
|
503
|
+
their own format; and that injection is idempotent, uninstall round-trips, and
|
|
504
|
+
malformed markers are refused.
|
|
505
|
+
|
|
506
|
+
Run the behavioral tests:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
python3 -m unittest discover -s tests -v
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Measure the repository-controlled static prompt footprint and enforce its
|
|
513
|
+
committed ceilings:
|
|
514
|
+
|
|
515
|
+
```bash
|
|
516
|
+
python3 scripts/measure_context.py --check
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
The measurement is a byte-based proxy for always-listed or dispatch-loaded
|
|
520
|
+
text. It deliberately does not claim to measure total task tokens or credits,
|
|
521
|
+
which also depend on conversation history, cache state, tool output, and the
|
|
522
|
+
number and model of spawned agents.
|
|
523
|
+
|
|
524
|
+
Preview any install without writing:
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
python3 scripts/leo-install.py <harness> --dry-run
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
**Both Claude Code and Codex cache a plugin by version**, so reinstalling while
|
|
531
|
+
the version is unchanged is a no-op and quietly leaves the old code in place —
|
|
532
|
+
you will be testing the previous build without being told. While iterating,
|
|
533
|
+
either uninstall and reinstall:
|
|
534
|
+
|
|
535
|
+
```bash
|
|
536
|
+
claude plugin uninstall leos-agent@leos-agent && claude plugin install leos-agent@leos-agent --scope user
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
or replace the cachebuster suffix in the Codex manifest with one in the form
|
|
540
|
+
`10.2.0+codex.local-YYYYMMDD-HHMMSS` and re-add. Either way, plugin changes only
|
|
541
|
+
reach a **new** session or thread.
|
|
542
|
+
|
|
543
|
+
`--check` exits non-zero when a file is out of date, and `--force` replaces a
|
|
544
|
+
copied file that something else has since overwritten.
|
|
545
|
+
|
|
546
|
+
To release: bump the version in `package.json`, the three `plugin.json` files,
|
|
547
|
+
`.claude-plugin/marketplace.json`, `plugin.yaml`, and every mention in this
|
|
548
|
+
README (the uninstall commands embed it in cache paths — `check.py` fails on any
|
|
549
|
+
stale one); run `scripts/check.py`; then push a `v`-prefixed tag.
|
|
550
|
+
|
|
551
|
+
Pushing that tag is the whole release. `.github/workflows/release.yml` runs the
|
|
552
|
+
tests and both checks, refuses a tag that disagrees with `package.json`,
|
|
553
|
+
inspects the tree npm would ship, and publishes to npm for OpenCode. It
|
|
554
|
+
authenticates by OIDC trusted publishing, so there is no token in the repository
|
|
555
|
+
— npm's configuration names this workflow by path, and renaming the file breaks
|
|
556
|
+
publishing until npm is updated to match. Publishing is idempotent: a version
|
|
557
|
+
already on the registry is a no-op, and a lookup that fails for any reason other
|
|
558
|
+
than a confirmed 404 aborts rather than assuming the version is absent.
|
|
559
|
+
|
|
560
|
+
Check what a publish would contain, without publishing:
|
|
561
|
+
|
|
562
|
+
```bash
|
|
563
|
+
python3 scripts/publish-npm.py --dry-run
|
|
564
|
+
```
|
|
49
565
|
|
|
50
566
|
MIT licensed.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Write this session's context to a leos-agent handoff document for a later session to resume.
|
|
3
|
+
argument-hint: "[what to emphasise]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use the `handoff` skill. `$ARGUMENTS` says what to emphasise while writing — it
|
|
7
|
+
steers this document and is not stored.
|
|
8
|
+
|
|
9
|
+
Pick a slug naming the work, claim it with `scripts/handoff.py new`, write the
|
|
10
|
+
six sections against this session, and report the name plus `/handon <name>`.
|
|
11
|
+
Pointers, not contents; under about 100 lines.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Resume work from a leos-agent handoff document written by an earlier session.
|
|
3
|
+
argument-hint: "[name]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use the `handon` skill on `$ARGUMENTS`.
|
|
7
|
+
|
|
8
|
+
With no argument or an ambiguous one, list the handoffs and ask which — never
|
|
9
|
+
pick one unprompted. Read it, compare its frontmatter against the current
|
|
10
|
+
directory, branch, and HEAD, and report any drift before anything else.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Stage a pending (unsubmitted) GitHub review on a pull request of this repository.
|
|
3
|
+
argument-hint: "[pr-number]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use the leos-agent `review-pr` skill on `$ARGUMENTS`.
|
|
7
|
+
|
|
8
|
+
With no argument, review the pull request for the current branch. Comments are
|
|
9
|
+
staged as a PENDING review — never submitted, never made public.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Arm the review-request watcher for this repository (Claude Code only).
|
|
3
|
+
argument-hint: "[repo-path]"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Use the `watch-review` skill. Argument: `$ARGUMENTS`.
|
|
7
|
+
|
|
8
|
+
Arm it with the Monitor tool and let the notifications come — do not hand-poll
|
|
9
|
+
it turn after turn.
|
package/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode plugin entry point for leos-agent.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately does nothing at runtime. OpenCode's plugin API registers tools
|
|
5
|
+
* and hooks, not skills or commands, and the payload is installed to disk on
|
|
6
|
+
* demand rather than written at session start. This export exists so that
|
|
7
|
+
* `opencode plugin leos-agent -g` resolves and loads cleanly; the real work
|
|
8
|
+
* happens in scripts/leo-install.py, which the installed `install` skill runs.
|
|
9
|
+
*/
|
|
10
|
+
export const LeosAgent = async () => ({});
|
|
11
|
+
|
|
12
|
+
export default LeosAgent;
|