leos-agent 10.2.0 → 10.6.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 +110 -31
- package/package.json +1 -1
- package/rules/preferences.md +37 -32
- package/scripts/check.py +77 -1
- package/scripts/ghreview.py +12 -0
- package/scripts/handoff.py +44 -21
- package/scripts/leo-install.py +146 -25
- package/scripts/measure_context.py +43 -0
- package/scripts/routing.py +420 -0
- package/scripts/state.py +8 -3
- package/scripts/watch_review.py +143 -44
- package/skills/doctor/SKILL.md +24 -6
- package/skills/handoff/SKILL.md +16 -6
- package/skills/handon/SKILL.md +26 -9
- package/skills/install/SKILL.md +1 -1
- package/skills/review-pr/SKILL.md +4 -4
- package/skills/review-pr/reference/procedure.md +18 -15
- package/skills/tune-routing/SKILL.md +129 -0
- package/skills/tune-routing/agents/openai.yaml +5 -0
- package/skills/tune-routing/reference/harnesses.md +63 -0
- package/skills-claude/attach-pr/SKILL.md +14 -4
- package/skills-claude/watch-review/SKILL.md +74 -25
package/scripts/leo-install.py
CHANGED
|
@@ -23,7 +23,14 @@ import sys
|
|
|
23
23
|
import tempfile
|
|
24
24
|
from pathlib import Path
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
27
|
+
import routing # noqa: E402 owns the harness list and the machine-local model config
|
|
28
|
+
|
|
29
|
+
HARNESSES = routing.HARNESSES
|
|
30
|
+
|
|
31
|
+
# The routing region inside the payload, replaced per harness at install time.
|
|
32
|
+
ROUTING_OPEN = "<!-- leos-agent:routing -->"
|
|
33
|
+
ROUTING_CLOSE = "<!-- /leos-agent:routing -->"
|
|
27
34
|
|
|
28
35
|
OPEN_RE = re.compile(r"^<leos-agent\b[^>]*>[ \t]*$", re.MULTILINE)
|
|
29
36
|
CLOSE_RE = re.compile(r"^</leos-agent>[ \t]*$", re.MULTILINE)
|
|
@@ -36,10 +43,18 @@ CODEX_SOFT_CAP = 28 * 1024
|
|
|
36
43
|
# copies apart from a file the user happens to have put at the same path.
|
|
37
44
|
PROVENANCE = "leos-agent"
|
|
38
45
|
|
|
46
|
+
# Skill and command text refers to scripts as <plugin-root>/scripts/… and the
|
|
47
|
+
# model resolves the root once. OpenCode copies live apart from the scripts,
|
|
48
|
+
# and OpenCode sets no resolution env var, so their copies are installed with
|
|
49
|
+
# the token already replaced by this machine's absolute plugin root. Installing
|
|
50
|
+
# alternately from a checkout and a cache re-bakes the root each time — last
|
|
51
|
+
# install wins, and --check reports a stale root as out of date.
|
|
52
|
+
PLUGIN_ROOT_TOKEN = "<plugin-root>"
|
|
53
|
+
|
|
39
54
|
# OpenCode plugins cannot register skills or commands from JS, so these are
|
|
40
55
|
# copied to disk instead. check.py asserts every file they name carries
|
|
41
56
|
# PROVENANCE, without which the installer would refuse to upgrade its own copy.
|
|
42
|
-
OPENCODE_SKILLS = ("doctor", "review-pr", "handoff", "handon")
|
|
57
|
+
OPENCODE_SKILLS = ("doctor", "review-pr", "handoff", "handon", "tune-routing")
|
|
43
58
|
OPENCODE_COMMANDS = ("review-pr", "handoff", "handon")
|
|
44
59
|
|
|
45
60
|
# Codex plugins cannot package custom agent definitions directly, so these are
|
|
@@ -100,19 +115,71 @@ def read_version(root):
|
|
|
100
115
|
sys.exit(f"leo-install: {manifest} has no version field")
|
|
101
116
|
|
|
102
117
|
|
|
103
|
-
def
|
|
104
|
-
"""
|
|
118
|
+
def render_routing(body, harness, config):
|
|
119
|
+
"""Replace the routing region with the stanza for this machine's config.
|
|
120
|
+
|
|
121
|
+
The payload ships with a default inside the region, so an un-rendered read of
|
|
122
|
+
rules/preferences.md -- Cursor's plugin-delivered rule, a human opening the
|
|
123
|
+
file -- still says something true. Rendering only ever narrows it to the one
|
|
124
|
+
harness being installed, which is why the installed payload is smaller than
|
|
125
|
+
the file on disk rather than larger.
|
|
126
|
+
"""
|
|
127
|
+
start = body.find(ROUTING_OPEN)
|
|
128
|
+
end = body.find(ROUTING_CLOSE)
|
|
129
|
+
if start < 0 or end < start:
|
|
130
|
+
sys.exit(f"leo-install: rules/preferences.md is missing its {ROUTING_OPEN} region")
|
|
131
|
+
return body[:start] + routing.stanza(harness, config) + body[end + len(ROUTING_CLOSE):]
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def payload_body(root, harness=None, config=None):
|
|
135
|
+
"""The canonical payload: rules/preferences.md with its frontmatter stripped.
|
|
136
|
+
|
|
137
|
+
With a harness, the routing region is rendered for it; without one the region
|
|
138
|
+
keeps its shipped default, markers and all.
|
|
139
|
+
"""
|
|
105
140
|
text = (root / "rules" / "preferences.md").read_text(encoding="utf-8")
|
|
106
141
|
body = re.sub(r"(?s)\A---\n.*?\n---\n", "", text, count=1).strip()
|
|
107
142
|
if not body:
|
|
108
143
|
sys.exit("leo-install: rules/preferences.md has no body below its frontmatter")
|
|
109
144
|
if OPEN_RE.search(body) or CLOSE_RE.search(body):
|
|
110
145
|
sys.exit("leo-install: rules/preferences.md contains a <leos-agent> marker; it must not")
|
|
146
|
+
if harness:
|
|
147
|
+
body = render_routing(body, harness, config if config is not None else routing.load())
|
|
111
148
|
return body
|
|
112
149
|
|
|
113
150
|
|
|
114
|
-
def build_block(root):
|
|
115
|
-
return f'<leos-agent version="{read_version(root)}">\n{payload_body(root)}\n</leos-agent>\n'
|
|
151
|
+
def build_block(root, harness=None, config=None):
|
|
152
|
+
return f'<leos-agent version="{read_version(root)}">\n{payload_body(root, harness, config)}\n</leos-agent>\n'
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def render_codex_agent(text, agent_name, config):
|
|
156
|
+
"""Substitute a Codex profile's model, leaving the shipped default when unset."""
|
|
157
|
+
entry = routing.profile(config, "codex", agent_name.split("-", 1)[1])
|
|
158
|
+
if not entry:
|
|
159
|
+
return text
|
|
160
|
+
text = re.sub(r'(?m)^model = ".*"$', f'model = "{entry["model"]}"', text, count=1)
|
|
161
|
+
if entry["effort"]:
|
|
162
|
+
text = re.sub(
|
|
163
|
+
r'(?m)^model_reasoning_effort = ".*"$',
|
|
164
|
+
f'model_reasoning_effort = "{entry["effort"]}"',
|
|
165
|
+
text,
|
|
166
|
+
count=1,
|
|
167
|
+
)
|
|
168
|
+
return text
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def cursor_routing_rule(harness, config):
|
|
172
|
+
"""Cursor reads its rules straight out of the plugin, so the per-machine half
|
|
173
|
+
has to arrive as its own always-applied rule file."""
|
|
174
|
+
return (
|
|
175
|
+
"---\n"
|
|
176
|
+
"description: leos-agent model routing for this machine.\n"
|
|
177
|
+
"alwaysApply: true\n"
|
|
178
|
+
"---\n"
|
|
179
|
+
"This supersedes the model-routing dispatch line in Leo's agent operating\n"
|
|
180
|
+
"preferences:\n\n"
|
|
181
|
+
f"{routing.stanza(harness, config)}\n"
|
|
182
|
+
)
|
|
116
183
|
|
|
117
184
|
|
|
118
185
|
def scan_markers(text):
|
|
@@ -124,16 +191,19 @@ def scan_markers(text):
|
|
|
124
191
|
into a permanent "two blocks" error, so fenced regions are skipped.
|
|
125
192
|
"""
|
|
126
193
|
opens, closes = [], []
|
|
127
|
-
fence = None
|
|
194
|
+
fence = None # (char, run length) of the currently open fence
|
|
128
195
|
offset = 0
|
|
129
196
|
for line in text.splitlines(keepends=True):
|
|
130
197
|
stripped = line.lstrip()
|
|
131
|
-
|
|
132
|
-
if
|
|
133
|
-
token =
|
|
198
|
+
run = re.match(r"(`{3,}|~{3,})", stripped)
|
|
199
|
+
if run:
|
|
200
|
+
token = run.group(1)
|
|
134
201
|
if fence is None:
|
|
135
|
-
fence = token
|
|
136
|
-
|
|
202
|
+
fence = (token[0], len(token))
|
|
203
|
+
# CommonMark: only a run of the same character at least as long as
|
|
204
|
+
# the opener closes a fence; a shorter run is fence content, so a
|
|
205
|
+
# ``` line inside a ```` example must not end the example.
|
|
206
|
+
elif fence[0] == token[0] and len(token) >= fence[1]:
|
|
137
207
|
fence = None
|
|
138
208
|
elif fence is None:
|
|
139
209
|
if OPEN_RE.match(line.rstrip("\n")):
|
|
@@ -283,11 +353,16 @@ def install_markdown(path, block, args, label, create=True):
|
|
|
283
353
|
return write_if_changed(path, inject(current, block), current, existed, crlf, args, label)
|
|
284
354
|
|
|
285
355
|
|
|
286
|
-
def install_file_copy(src, dest, args, label, owned_parent=False):
|
|
287
|
-
"""Install a payload file the harness's plugin system cannot deliver itself.
|
|
356
|
+
def install_file_copy(src, dest, args, label, owned_parent=False, payload=None):
|
|
357
|
+
"""Install a payload file the harness's plugin system cannot deliver itself.
|
|
358
|
+
|
|
359
|
+
`payload` overrides the source text for files rendered from the machine's
|
|
360
|
+
routing config rather than copied verbatim.
|
|
361
|
+
"""
|
|
288
362
|
dest = dest.expanduser()
|
|
289
363
|
existed = dest.is_file()
|
|
290
|
-
payload
|
|
364
|
+
if payload is None:
|
|
365
|
+
payload = src.read_text(encoding="utf-8")
|
|
291
366
|
current = dest.read_text(encoding="utf-8") if existed else ""
|
|
292
367
|
|
|
293
368
|
# Never clobber or delete a same-named file this tool did not put there.
|
|
@@ -307,8 +382,27 @@ def install_file_copy(src, dest, args, label, owned_parent=False):
|
|
|
307
382
|
return write_if_changed(dest, payload, current, existed, False, args, label)
|
|
308
383
|
|
|
309
384
|
|
|
385
|
+
def opencode_payload(src, root, rename_install=False):
|
|
386
|
+
"""An OpenCode copy's content: the plugin root baked in, optionally renamed.
|
|
387
|
+
|
|
388
|
+
OpenCode reads the copies out of ~/.config/opencode, far from the scripts
|
|
389
|
+
they invoke, and sets none of the resolution env vars — so the placeholder
|
|
390
|
+
is resolved here, at install time, where the root is known for certain.
|
|
391
|
+
The install skill is additionally renamed to match the leo-install/
|
|
392
|
+
directory it is copied into, keeping directory and frontmatter in
|
|
393
|
+
agreement whichever one OpenCode keys on.
|
|
394
|
+
"""
|
|
395
|
+
text = src.read_text(encoding="utf-8").replace(PLUGIN_ROOT_TOKEN, str(root))
|
|
396
|
+
if rename_install:
|
|
397
|
+
text = re.sub(r"(?m)^name:\s*install\s*$", "name: leo-install", text, count=1)
|
|
398
|
+
return text
|
|
399
|
+
|
|
400
|
+
|
|
310
401
|
def run(harness, root, args):
|
|
311
|
-
|
|
402
|
+
# Read once per run: rendering has to be a pure function of (version, config)
|
|
403
|
+
# or a second install would not come back "unchanged".
|
|
404
|
+
config = routing.load()
|
|
405
|
+
block = build_block(root, harness, config)
|
|
312
406
|
home = Path.home()
|
|
313
407
|
targets = []
|
|
314
408
|
|
|
@@ -328,22 +422,43 @@ def run(harness, root, args):
|
|
|
328
422
|
home / ".codex" / "agents" / f"{n}.toml",
|
|
329
423
|
args,
|
|
330
424
|
l,
|
|
425
|
+
payload=render_codex_agent(
|
|
426
|
+
(root / "payload" / "codex-agents" / f"{n}.toml").read_text(encoding="utf-8"),
|
|
427
|
+
n,
|
|
428
|
+
config,
|
|
429
|
+
),
|
|
331
430
|
),
|
|
332
431
|
)
|
|
333
432
|
)
|
|
334
433
|
|
|
335
434
|
elif harness == "cursor":
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
435
|
+
# The payload itself arrives natively through the plugin's alwaysApply
|
|
436
|
+
# rule, which is the file in the plugin directory -- so there is nothing
|
|
437
|
+
# per-machine in it. Only the routing stanza needs installing, and only
|
|
438
|
+
# as its own rule -- and only when something is actually configured: an
|
|
439
|
+
# unconfigured rule would restate the payload's default, an always-loaded
|
|
440
|
+
# no-op that costs context on every turn.
|
|
441
|
+
label = "~/.cursor/rules/leos-agent-routing.mdc"
|
|
442
|
+
dest = home / ".cursor" / "rules" / "leos-agent-routing.mdc"
|
|
443
|
+
configured = bool(
|
|
444
|
+
routing.profile(config, "cursor", "runner") or routing.profile(config, "cursor", "executor")
|
|
345
445
|
)
|
|
346
446
|
|
|
447
|
+
def cursor_rule_target(l=label):
|
|
448
|
+
if args.uninstall or configured:
|
|
449
|
+
return install_file_copy(None, dest, args, l, payload=cursor_routing_rule(harness, config))
|
|
450
|
+
# Unconfigured install: write nothing, and take back a stale rule a
|
|
451
|
+
# previous config left behind -- but only one that is provably ours.
|
|
452
|
+
if not dest.is_file():
|
|
453
|
+
return Result(l, "skipped", "no routing configured for cursor")
|
|
454
|
+
if PROVENANCE not in dest.read_text(encoding="utf-8"):
|
|
455
|
+
return Result(l, "skipped", "no routing configured; leaving the unrelated file at this path")
|
|
456
|
+
if args.writes:
|
|
457
|
+
dest.unlink()
|
|
458
|
+
return Result(l, "removed", "no routing configured; stale rule removed")
|
|
459
|
+
|
|
460
|
+
targets.append((label, cursor_rule_target))
|
|
461
|
+
|
|
347
462
|
elif harness == "hermes":
|
|
348
463
|
# Never create SOUL.md: Hermes writes its own starter identity file on
|
|
349
464
|
# first run, and pre-empting that would fight the bootstrap.
|
|
@@ -374,6 +489,9 @@ def run(harness, root, args):
|
|
|
374
489
|
args,
|
|
375
490
|
skill_label,
|
|
376
491
|
owned_parent=True,
|
|
492
|
+
payload=opencode_payload(
|
|
493
|
+
root / "skills" / "install" / "SKILL.md", root, rename_install=True
|
|
494
|
+
),
|
|
377
495
|
),
|
|
378
496
|
)
|
|
379
497
|
)
|
|
@@ -394,6 +512,7 @@ def run(harness, root, args):
|
|
|
394
512
|
args,
|
|
395
513
|
l,
|
|
396
514
|
owned_parent=True,
|
|
515
|
+
payload=opencode_payload(s, root),
|
|
397
516
|
),
|
|
398
517
|
)
|
|
399
518
|
)
|
|
@@ -407,6 +526,7 @@ def run(harness, root, args):
|
|
|
407
526
|
args,
|
|
408
527
|
l,
|
|
409
528
|
owned_parent=True,
|
|
529
|
+
payload=opencode_payload(root / "skills" / n / "SKILL.md", root),
|
|
410
530
|
),
|
|
411
531
|
)
|
|
412
532
|
)
|
|
@@ -420,6 +540,7 @@ def run(harness, root, args):
|
|
|
420
540
|
cfg / "commands" / f"{n}.md",
|
|
421
541
|
args,
|
|
422
542
|
l,
|
|
543
|
+
payload=opencode_payload(root / "commands" / f"{n}.md", root),
|
|
423
544
|
),
|
|
424
545
|
)
|
|
425
546
|
)
|
|
@@ -8,6 +8,7 @@ regressions remain visible without a tokenizer or network access.
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import argparse
|
|
11
|
+
import importlib.util
|
|
11
12
|
import json
|
|
12
13
|
import re
|
|
13
14
|
import sys
|
|
@@ -19,9 +20,19 @@ ROOT = Path(__file__).resolve().parent.parent
|
|
|
19
20
|
# the before/after output in the change that raises it.
|
|
20
21
|
LIMITS = {
|
|
21
22
|
"global_policy_bytes": 4_500,
|
|
23
|
+
# What an unconfigured machine actually installs. Rendering the routing region
|
|
24
|
+
# per harness dropped this below the old whole-file figure of 4497, and it must
|
|
25
|
+
# stay there: the model config exists to save money, so it may not cost
|
|
26
|
+
# always-loaded bytes to have. A configured harness exceeds this only by the
|
|
27
|
+
# length of the model names chosen, which is bounded and deliberate.
|
|
28
|
+
"rendered_policy_bytes": 4_497,
|
|
22
29
|
"codex_implicit_skill_metadata_bytes": 600,
|
|
23
30
|
"claude_implicit_skill_metadata_bytes": 800,
|
|
24
31
|
"codex_agent_description_bytes": 550,
|
|
32
|
+
"claude_agent_description_bytes": 550,
|
|
33
|
+
# Command descriptions are listed alongside skills in Claude Code and Cursor,
|
|
34
|
+
# so they are always-loaded context on the same terms as skill metadata.
|
|
35
|
+
"command_description_bytes": 400,
|
|
25
36
|
"review_dispatch_bytes": 3_500,
|
|
26
37
|
}
|
|
27
38
|
|
|
@@ -69,6 +80,20 @@ def agent_description(path):
|
|
|
69
80
|
return match.group(1)
|
|
70
81
|
|
|
71
82
|
|
|
83
|
+
def rendered_policy():
|
|
84
|
+
"""The installed payload body per harness, with no routing config present.
|
|
85
|
+
|
|
86
|
+
This is what a session actually loads -- rules/preferences.md on disk keeps a
|
|
87
|
+
harness-neutral default in its routing region, and the installer narrows it to
|
|
88
|
+
one harness. Measured with the config forced empty so the number is a property
|
|
89
|
+
of the repository, not of whoever runs it.
|
|
90
|
+
"""
|
|
91
|
+
spec = importlib.util.spec_from_file_location("leo_install_measure", ROOT / "scripts" / "leo-install.py")
|
|
92
|
+
installer = importlib.util.module_from_spec(spec)
|
|
93
|
+
spec.loader.exec_module(installer)
|
|
94
|
+
return {h: byte_len(installer.payload_body(ROOT, h, {})) for h in installer.HARNESSES}
|
|
95
|
+
|
|
96
|
+
|
|
72
97
|
def measurements():
|
|
73
98
|
portable = sorted((ROOT / "skills").glob("*/SKILL.md"))
|
|
74
99
|
claude_only = sorted((ROOT / "skills-claude").glob("*/SKILL.md"))
|
|
@@ -77,11 +102,26 @@ def measurements():
|
|
|
77
102
|
review_fm, review_body = frontmatter(ROOT / "skills" / "review-pr" / "SKILL.md")
|
|
78
103
|
del review_fm
|
|
79
104
|
agent_paths = sorted((ROOT / "payload" / "codex-agents").glob("*.toml"))
|
|
105
|
+
# Claude Code lists every plugin agent's name and description in the parent's
|
|
106
|
+
# always-loaded agent roster, so they are part of the static footprint too.
|
|
107
|
+
claude_agent_paths = sorted((ROOT / "agents").glob("*.md"))
|
|
108
|
+
claude_agent_bytes = 0
|
|
109
|
+
for path in claude_agent_paths:
|
|
110
|
+
fm, _ = frontmatter(path)
|
|
111
|
+
claude_agent_bytes += byte_len(field(fm, "name")) + byte_len(field(fm, "description"))
|
|
112
|
+
command_paths = sorted((ROOT / "commands").glob("*.md")) + sorted((ROOT / "commands-claude").glob("*.md"))
|
|
113
|
+
command_bytes = 0
|
|
114
|
+
for path in command_paths:
|
|
115
|
+
fm, _ = frontmatter(path)
|
|
116
|
+
command_bytes += byte_len(field(fm, "description"))
|
|
80
117
|
return {
|
|
81
118
|
"global_policy_bytes": byte_len(policy_body.strip()),
|
|
119
|
+
"rendered_policy_bytes": max(rendered_policy().values()),
|
|
82
120
|
"codex_implicit_skill_metadata_bytes": skill_metadata_bytes(portable, codex_implicit),
|
|
83
121
|
"claude_implicit_skill_metadata_bytes": skill_metadata_bytes(portable + claude_only, claude_implicit),
|
|
84
122
|
"codex_agent_description_bytes": sum(byte_len(agent_description(path)) for path in agent_paths),
|
|
123
|
+
"claude_agent_description_bytes": claude_agent_bytes,
|
|
124
|
+
"command_description_bytes": command_bytes,
|
|
85
125
|
"review_dispatch_bytes": byte_len(review_body.strip()),
|
|
86
126
|
}
|
|
87
127
|
|
|
@@ -99,6 +139,9 @@ def main(argv=None):
|
|
|
99
139
|
print("Static prompt footprint (bytes; tokens are roughly bytes / 4 for this prose)")
|
|
100
140
|
for name, value in values.items():
|
|
101
141
|
print(f" {name:38} {value:5} limit {LIMITS[name]:5}")
|
|
142
|
+
print(" rendered_policy_bytes is the worst case across harnesses; each one installs:")
|
|
143
|
+
for harness, value in sorted(rendered_policy().items()):
|
|
144
|
+
print(f" {harness:38} {value:5}")
|
|
102
145
|
print("This excludes conversation history, tool output, cache effects, and subagent work.")
|
|
103
146
|
|
|
104
147
|
over = {name: (value, LIMITS[name]) for name, value in values.items() if value > LIMITS[name]}
|