@r1z4x/tezgah 1.4.0 → 1.5.1
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/CHANGELOG.md +120 -1
- package/MANIFEST +0 -1
- package/README.md +6 -4
- package/bin/tezgah-docs +3 -3
- package/bin/tezgah-route +12 -6
- package/bin/tezgah-setup +67 -24
- package/bin/tezgah-status +3 -1
- package/bin/tezgah-task +132 -39
- package/bin/tezgah-taste +49 -31
- package/bin/tezgah-triage +12 -7
- package/docs/architecture.md +1 -1
- package/docs/clarity-baseline.json +7 -7
- package/docs/contract.md +21 -11
- package/docs/environment.md +3 -1
- package/docs/evidence.md +4 -3
- package/docs/glossary.md +1 -1
- package/docs/hosts.md +9 -0
- package/docs/judge.md +95 -27
- package/docs/layers.md +1 -1
- package/docs/models.md +17 -7
- package/docs/operations.md +23 -13
- package/docs/skills.md +1 -1
- package/docs/testing.md +1 -1
- package/docs/vision.md +10 -5
- package/hooks/tezgah_agents.py +149 -105
- package/hooks/tezgah_context.py +42 -7
- package/hooks/tezgah_judge.py +252 -79
- package/hooks/tezgah_models.py +50 -3
- package/hooks/tezgah_paths.py +180 -27
- package/hooks/tezgah_policy.py +41 -10
- package/hooks/tezgah_replay.py +26 -15
- package/hooks/tezgah_task.py +160 -46
- package/hooks/tezgah_taste.py +2 -2
- package/hooks/tezgah_taste_ledger.py +13 -4
- package/hosts/codex/hook.py +5 -0
- package/package.json +1 -2
- package/skills/harness/SKILL.md +4 -0
- package/skills/plan-add/SKILL.md +23 -7
- package/skills/plan-add/render_table.py +3 -2
- package/skills/plan-status/SKILL.md +12 -4
- package/skills/plan-sync/SKILL.md +19 -9
- package/skills/tezgah-contract/SKILL.md +29 -14
- package/agents/tezgah-reviewer.md +0 -70
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,123 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.5.1] - 2026-10-09
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Subagents load in the first session.** Claude and Codex missed every role a
|
|
14
|
+
session wrote into a new repo until the next session. Codex also ignored
|
|
15
|
+
`.codex/agents` in an untrusted repo. The install now writes the roles to
|
|
16
|
+
`~/.claude/agents` and `$CODEX_HOME/agents`, as it already did for omp. A
|
|
17
|
+
session removes the copies an older release left in a repo's `.claude/agents`
|
|
18
|
+
and `.codex/agents`. Cursor read 9 of 10 role descriptions as ">" and got a
|
|
19
|
+
model id it does not list. It now gets its own `.cursor/agents` with a
|
|
20
|
+
one-line description and `model: inherit`.
|
|
21
|
+
|
|
22
|
+
- **`tezgah-route` names the specialists.** It could only name a tier. The
|
|
23
|
+
five specialists ran 0 times in 121 delegations. The generic task agent ran 86.
|
|
24
|
+
The judge now also answers whether a brief is a specialist's whole
|
|
25
|
+
job, and the route names that agent. `--phase review` and `--phase research`
|
|
26
|
+
name the reviewer and the researcher. A security brief that only reviews
|
|
27
|
+
goes to `tezgah-security`. The routing and fan-out text say to spawn the
|
|
28
|
+
named agent, never the generic one.
|
|
29
|
+
- **Jev through OpenRouter asks a model OpenRouter has.** The carrier sent
|
|
30
|
+
`typesafe/jev-latest`, which OpenRouter answers with 400 (no such model). It
|
|
31
|
+
now sends the bare `jev-latest`, which OpenRouter maps itself.
|
|
32
|
+
- **The Workers AI model id picks Clef.** `tezgah-setup` refused
|
|
33
|
+
`--jev @cf/cloudflare/clef-flash`, and the same value in
|
|
34
|
+
`TEZGAH_JEV_PROVIDER` chose no carrier. Both now name `clef-flash`, and
|
|
35
|
+
`@cf/cloudflare/clef` names `clef`.
|
|
36
|
+
- **opencode's judge run denies tools at agent level too.** A user's
|
|
37
|
+
`agent.build.permission` beat the inline top-level deny.
|
|
38
|
+
- **The refusal note needs an asked session CLI.** The seam writes it only
|
|
39
|
+
when it asked the session CLI first. A caller that named the CLI gets it.
|
|
40
|
+
- **A chosen carrier reads only its own key.** A `clef` choice no longer opens
|
|
41
|
+
omp's login database on every status-line redraw.
|
|
42
|
+
- **`pnpm test` names a command.** An Acceptance item counted as a command
|
|
43
|
+
only when a word after the first was an option or a path. So `pnpm test`,
|
|
44
|
+
`cargo test` and `make test` read as missing, and the phase move refused the
|
|
45
|
+
plan. A known runner followed by `test` or `run` now counts. Prose such as
|
|
46
|
+
`run the tests` still does not.
|
|
47
|
+
- **Each carrier's price is now pinned by a test.** The one cost check priced a
|
|
48
|
+
TypeSafe answer at Jev's rate. It passed with or without per-carrier pricing.
|
|
49
|
+
New tests in `tezgah-taste`, `tezgah-triage` and the replay labeller price Clef
|
|
50
|
+
and Clef-flash answers at their own rates. A run with an unpriced answer prints
|
|
51
|
+
`cost unknown`.
|
|
52
|
+
- **Claude lists the reviewer once.** The plugin shipped its own
|
|
53
|
+
`agents/tezgah-reviewer.md`, which Claude shows as `tezgah:tezgah-reviewer`
|
|
54
|
+
beside the generated reviewer and never shadows. The plugin now ships no
|
|
55
|
+
agent, and its release-time renderer is gone with it.
|
|
56
|
+
|
|
57
|
+
## [1.5.0] - 2026-10-09
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- **A plan gets one review, at most two rounds.** The review reads the plan's
|
|
62
|
+
whole diff once, in its verification phase. Round one spawns its reviewers in
|
|
63
|
+
one message, one per dimension, or one reviewer for a diff under 300 lines.
|
|
64
|
+
Only a confirmed critical or major finding opens round two. Round two reads
|
|
65
|
+
the fix's delta. A minor finding goes on the fix-later list. `tezgah-task review`
|
|
66
|
+
refuses a third round. What is left after round two goes to the user. The
|
|
67
|
+
reviewer agent, the contract and plan-sync now state this one rule. Before,
|
|
68
|
+
the reviewer looped until a round confirmed nothing.
|
|
69
|
+
- **One Acceptance rule.** A plan enters a writing phase only when each
|
|
70
|
+
Acceptance item names its command or says `unverifiable` and why. Before, one
|
|
71
|
+
command-bearing item let the move through. `render_table --acceptance
|
|
72
|
+
--strict` then refused the same plan. Both now read one predicate,
|
|
73
|
+
`tezgah_task.unproven`, and the refusal names each item's line.
|
|
74
|
+
- **Typed decisions take any Jev carrier.** Taste's `learn`, `label`,
|
|
75
|
+
`calibrate`, `gate` and `measure --typed` require the Jev model, not
|
|
76
|
+
TypeSafe's address. So do taste's background start and replay's
|
|
77
|
+
`--label-model`. OpenRouter System One, Cloudflare Workers AI or a
|
|
78
|
+
compatible endpoint counts the same. Old `typesafe` rows keep counting.
|
|
79
|
+
`learn`'s report key `typesafe` is now `jev`. Replay's `--provider typesafe`
|
|
80
|
+
is now `--provider jev`, and the old name still works. The triage's no-judge
|
|
81
|
+
message names every carrier, the selection knob and the session CLIs.
|
|
82
|
+
- **The fan-out rule arms on the lists the owner writes.** Items written
|
|
83
|
+
`1 - ...`, `1- `, `1:`, `a)` or `+` now arm it. An inline `1. ... 2. ...`
|
|
84
|
+
arms it, and so does "subagentlerle". Three files named as one list arm it
|
|
85
|
+
too, and so do "First, ... Second, ..." and three imperative clauses. A later
|
|
86
|
+
list in the same session gets a line that names the action: count the items,
|
|
87
|
+
one subagent per item in one message. The rule covers every phase: a plan's
|
|
88
|
+
tasks, its acceptance commands and a survey's sources fan out too. The clause
|
|
89
|
+
that let a list of small items run one by one is gone.
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
|
|
93
|
+
- **Plan order and progress in `tezgah-task status`.** A plan can name the
|
|
94
|
+
plans it waits on with `after:`. `status` prints each open plan's ticked
|
|
95
|
+
Acceptance boxes and what it still waits on. A `parallel:` line names the
|
|
96
|
+
ready plans whose `allowed_paths:` share no prefix. Those can run at once.
|
|
97
|
+
An open plan whose `status:` is not open or blocked is a `FAIL` line, and so
|
|
98
|
+
is an `after:` id no plan carries. `--json` adds `open` and `parallel`.
|
|
99
|
+
- **The judge asks opencode, Cursor and Codex sessions too.** The judgement
|
|
100
|
+
seam's session CLI was omp or claude only. It now also runs `opencode run`,
|
|
101
|
+
`cursor-agent -p` and `codex exec` headless when their host's marker is set
|
|
102
|
+
(`OPENCODE`, `CURSOR_AGENT`/`CURSOR_VERSION`, `CODEX_THREAD_ID`).
|
|
103
|
+
`TEZGAH_JUDGE_CLI`, else `judge_cli` in `config.json`, picks one of the five
|
|
104
|
+
by name. A picked CLI that is not installed means no session CLI.
|
|
105
|
+
- **Jev through any carrier.** The judge reaches Jev through TypeSafe,
|
|
106
|
+
OpenRouter, Cloudflare Workers AI or a System One-compatible URL
|
|
107
|
+
(`JEV_API_BASE_URL`, `JEV_API_KEY`).
|
|
108
|
+
`TEZGAH_JEV_PROVIDER`, `JEV_PROVIDER` or config.json `jev` picks the carrier,
|
|
109
|
+
and `tezgah-setup --jev <value>` writes the config key. `auto` asks every
|
|
110
|
+
carrier that resolves. A named carrier without its credential gets no
|
|
111
|
+
request, and no other carrier stands in. `tezgah-status --judge` prints the
|
|
112
|
+
choice and its carriers.
|
|
113
|
+
- **The omp login is a TypeSafe key.** With no `TYPESAFE_API_KEY` and no key
|
|
114
|
+
file, the seam reads the active omp login, read-only.
|
|
115
|
+
- **Clef and Clef-flash as typed judges.** Cloudflare's own decision models
|
|
116
|
+
answer the System One API on Workers AI with the Cloudflare token and account
|
|
117
|
+
id. `--jev clef` or `--jev clef-flash` picks one, and `auto` asks them after
|
|
118
|
+
the Jev carriers. A typed decision counts them like Jev. Cost rows now price
|
|
119
|
+
the carrier that answered: $0.24 per 1M input for Clef, $0.09 for Clef-flash.
|
|
120
|
+
|
|
121
|
+
### Fixed
|
|
122
|
+
|
|
123
|
+
- **A spent key stops costing a request.** OpenRouter answers 403 to a key
|
|
124
|
+
past its limit. A 403 now marks a provider down for five minutes, like a 401.
|
|
125
|
+
|
|
9
126
|
## [1.4.0] - 2026-10-08
|
|
10
127
|
|
|
11
128
|
### Changed
|
|
@@ -864,7 +981,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
864
981
|
the copy could not find `hooks/` and died on import. It now links into the
|
|
865
982
|
installed tree, and also installs the command as `tezgah`.
|
|
866
983
|
|
|
867
|
-
[Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.
|
|
984
|
+
[Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.5.1...HEAD
|
|
985
|
+
[1.5.1]: https://github.com/r1z4x/tezgah/releases/tag/v1.5.1
|
|
986
|
+
[1.5.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.5.0
|
|
868
987
|
[1.4.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.4.0
|
|
869
988
|
[1.3.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.3.0
|
|
870
989
|
[1.2.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.2.0
|
package/MANIFEST
CHANGED
package/README.md
CHANGED
|
@@ -109,10 +109,12 @@ it set out to arm is not armed.
|
|
|
109
109
|
`~/.codex/AGENTS.md`, `~/.omp/agent/RULES.md`). Claude Code's hooks ship in a
|
|
110
110
|
plugin registered through `claude plugin`; with no `claude` CLI, Claude stays
|
|
111
111
|
unarmed and the report says so.
|
|
112
|
-
- **Agent files
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
`.
|
|
112
|
+
- **Agent files.** The install writes tezgah's subagent roles to
|
|
113
|
+
`~/.claude/agents/`, `$CODEX_HOME/agents/` (default `~/.codex/agents/`) and
|
|
114
|
+
`~/.omp/agent/agents/`. Inside the configured roots, a session writes them to
|
|
115
|
+
the repository's `.cursor/agents/` and `.opencode/agents/`, and lists those
|
|
116
|
+
directories in the clone's own `.git/info/exclude` (`TEZGAH_NO_EXCLUDE=1`
|
|
117
|
+
stops that).
|
|
116
118
|
- **A daily update check.** The status line asks for a newer release at most
|
|
117
119
|
once a day; `~/.config/tezgah/update-check-off` or `TEZGAH_UPDATE_CHECK=0`
|
|
118
120
|
turns it off.
|
package/bin/tezgah-docs
CHANGED
|
@@ -24,7 +24,7 @@ every word is part of the query.
|
|
|
24
24
|
|
|
25
25
|
Matching is word-based and case-insensitive against an entry's title, its
|
|
26
26
|
questions, its sources and its path; every word must match, so a second word
|
|
27
|
-
narrows. A query that matches nothing is the last resort of one
|
|
27
|
+
narrows. A query that matches nothing is the last resort of one judge-seam Choice
|
|
28
28
|
question over the pages themselves; it answers `none` for a query no page
|
|
29
29
|
covers. When that question gives no judgement - unavailable (`docs-judge-off`,
|
|
30
30
|
the seam's `judge-off`, no credential) or a call that failed or came back
|
|
@@ -106,7 +106,7 @@ RUN = re.compile(r"((?:`[A-Za-z_][A-Za-z0-9_]*(?:\(\))?`\s*[/,]?\s*){1,5})$")
|
|
|
106
106
|
CITE = re.compile(r"`([\w.-]+(?:/[\w.-]+)+|[\w-]+\.\w+):(\d+)(?:-(\d+))?`"
|
|
107
107
|
r"|`(:(\d+)(?:-(\d+))?)`")
|
|
108
108
|
# The symbol written after the citation instead of before it, the way a table
|
|
109
|
-
# cell reads: `hooks/tezgah_policy.py:
|
|
109
|
+
# cell reads: `hooks/tezgah_policy.py:680-948` (`CORE`).
|
|
110
110
|
AFTER = re.compile(r"\s*\(`([A-Za-z_][A-Za-z0-9_]*)(?:\(\))?`\)")
|
|
111
111
|
# Any `path:line`, backticked or not: what CITE does not match of these (a
|
|
112
112
|
# bare token, a comma list `x.py:12,40`) is counted as unjudged, so a citation
|
|
@@ -1036,7 +1036,7 @@ def record(result):
|
|
|
1036
1036
|
|
|
1037
1037
|
|
|
1038
1038
|
def judge_pick(query):
|
|
1039
|
-
"""(judged, page): whether a
|
|
1039
|
+
"""(judged, page): whether a judge-seam Choice question came back with one of
|
|
1040
1040
|
its options, and the page it named (None for `none`).
|
|
1041
1041
|
|
|
1042
1042
|
The last resort for a query the index could not place: a word the index does
|
package/bin/tezgah-route
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""tezgah-route - which
|
|
2
|
+
"""tezgah-route - which worker a delegated task goes to, and the model table.
|
|
3
3
|
|
|
4
4
|
tezgah-route "<brief>" [--phase P] [--json] the agent to spawn, and why
|
|
5
5
|
tezgah-route --check the table's age and refresh flags
|
|
@@ -9,11 +9,17 @@
|
|
|
9
9
|
tezgah-route --report each route's first check, by tier
|
|
10
10
|
|
|
11
11
|
A brief naming stored data, a migration, credentials, the gate or security goes to
|
|
12
|
-
tezgah-frontier by rule
|
|
13
|
-
tokens, ~0.4 s)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
tezgah-frontier by rule - or to tezgah-security when it only reviews; otherwise
|
|
13
|
+
Jev answers two Choices in one call (~700 input tokens, ~0.4 s): the tier, and
|
|
14
|
+
whether the brief is the whole job of a specialist (tezgah-tester, -docs,
|
|
15
|
+
-security, -ui, -researcher, -reviewer), which is then the agent named. With no
|
|
16
|
+
key or `judge-off`, `--phase review|research` names the reviewer or the
|
|
17
|
+
researcher, another `--phase` (explore|code|mechanical|plan) picks the tier from
|
|
18
|
+
the static table, and with neither the middle tier is used. The brief is sent to the judge seam's
|
|
19
|
+
provider - the session's own CLI (omp, claude, opencode, cursor, codex) or the Jev
|
|
20
|
+
model through TypeSafe, OpenRouter System One, Cloudflare Workers AI or a
|
|
21
|
+
compatible endpoint (TEZGAH_JEV_PROVIDER picks one), else the OpenRouter chat
|
|
22
|
+
fallback - so write it in English, the language the judge reads best.
|
|
17
23
|
|
|
18
24
|
Every routed call writes one `route` row to the session's ledger (TEZGAH_SESSION):
|
|
19
25
|
tier, agent, what set the tier (`via`: rule|jev|phase|default), the static
|
package/bin/tezgah-setup
CHANGED
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
tezgah-setup --adopt move the predecessor wiring aside
|
|
14
14
|
tezgah-setup --sync dev: copy this checkout over the Claude plugin copy
|
|
15
15
|
tezgah-setup --write-manifest release: regenerate the tracked MANIFEST from git
|
|
16
|
-
tezgah-setup --write-plugin-agents release: re-render the plugin's agents/ from the role source
|
|
17
16
|
tezgah-setup --status [PATH] print the armed/used checklist
|
|
18
17
|
tezgah-setup --agents [PATH] regenerate this repo's subagent set
|
|
19
18
|
tezgah-setup --deps [--dry-run] install missing optional tools only
|
|
@@ -884,6 +883,22 @@ def save_features(ids):
|
|
|
884
883
|
return True
|
|
885
884
|
|
|
886
885
|
|
|
886
|
+
def set_jev(value):
|
|
887
|
+
"""Persist `value` as config.json["jev"] - this key's only writer - and print
|
|
888
|
+
the carriers it resolves to now. An unchanged value writes nothing, like
|
|
889
|
+
`save_features`; a config that does not parse is refused, not replaced."""
|
|
890
|
+
import tezgah_judge
|
|
891
|
+
cfg = read_config(tp.CONFIG, {})
|
|
892
|
+
if cfg is None:
|
|
893
|
+
sys.stderr.write("tezgah-setup: %s is not valid JSON; jev not set\n" % tp.CONFIG)
|
|
894
|
+
return 1
|
|
895
|
+
if cfg.get("jev") != value:
|
|
896
|
+
cfg["jev"] = value
|
|
897
|
+
write_json(tp.CONFIG, cfg)
|
|
898
|
+
print("%s %s %s" % (OK, tp.CONFIG, tezgah_judge.jev_summary()))
|
|
899
|
+
return 0
|
|
900
|
+
|
|
901
|
+
|
|
887
902
|
def app_servers(selected=None):
|
|
888
903
|
"""The app-analysis MCP servers to wire this run (shared spec)."""
|
|
889
904
|
return tezgah_apps.servers(selection() if selected is None else selected)
|
|
@@ -1147,9 +1162,33 @@ def install_claude():
|
|
|
1147
1162
|
# (`TEZGAH_CORE_IN_FILE`, hooks/projects-auto-init.py), so a session pays for
|
|
1148
1163
|
# the contract once.
|
|
1149
1164
|
print("%s %s (always-on contract)" % (OK, write_rules("claude")))
|
|
1165
|
+
write_user_agents("claude")
|
|
1150
1166
|
register_claude_plugin()
|
|
1151
1167
|
|
|
1152
1168
|
|
|
1169
|
+
def write_user_agents(host):
|
|
1170
|
+
"""The host's tezgah roles in its user agent dir, where it loads them from
|
|
1171
|
+
the first session (a repo dir written at session start is missed there),
|
|
1172
|
+
then tezgah's MARKER files no longer wanted - `agents-off`, a lost
|
|
1173
|
+
capability, a retired role - are removed. The user's own files stay."""
|
|
1174
|
+
agents_dir = tezgah_agents.user_agent_dir(host)
|
|
1175
|
+
wanted = tezgah_agents.user_agents(host)
|
|
1176
|
+
for name, text in sorted(wanted.items()):
|
|
1177
|
+
path = os.path.join(agents_dir, name)
|
|
1178
|
+
if write_if_changed(path, text):
|
|
1179
|
+
print("%s %s" % (OK, path))
|
|
1180
|
+
for path in tezgah_agents.remove_stale(agents_dir, wanted):
|
|
1181
|
+
print("%s removed %s" % (OK, path))
|
|
1182
|
+
|
|
1183
|
+
|
|
1184
|
+
def user_agent_files(host):
|
|
1185
|
+
"""{filename: text} of tezgah's MARKER files in the host's user agent dir."""
|
|
1186
|
+
d = tezgah_agents.user_agent_dir(host)
|
|
1187
|
+
return {n: t for n in (os.listdir(d) if os.path.isdir(d) else [])
|
|
1188
|
+
if n.startswith("tezgah-")
|
|
1189
|
+
and tezgah_agents.MARKER in (t := tezgah_agents._read(os.path.join(d, n)) or "")}
|
|
1190
|
+
|
|
1191
|
+
|
|
1153
1192
|
# The marketplace a manifest rendered by this installer carries, so Claude
|
|
1154
1193
|
# installs the plugin as `tezgah@tezgah-local`. A manifest the tree already has
|
|
1155
1194
|
# under another name (the maintainer's) is kept, name included.
|
|
@@ -1342,6 +1381,7 @@ def install_codex():
|
|
|
1342
1381
|
# instructions Codex keeps for the whole session. Global, like omp's
|
|
1343
1382
|
# RULES.md, because a per-project file would be a tracked project file.
|
|
1344
1383
|
print("%s %s (always-on contract)" % (OK, write_rules("codex")))
|
|
1384
|
+
write_user_agents("codex")
|
|
1345
1385
|
|
|
1346
1386
|
|
|
1347
1387
|
def _toml_str(value):
|
|
@@ -3277,6 +3317,9 @@ def verify_uninstall(hosts, full):
|
|
|
3277
3317
|
# the user's own CLAUDE.md may name tezgah; only the block is ours
|
|
3278
3318
|
clean("claude global CLAUDE.md block gone", [CLAUDE_RULES],
|
|
3279
3319
|
needle="<!-- tezgah:start")
|
|
3320
|
+
claim("claude subagents gone",
|
|
3321
|
+
[os.path.join(tezgah_agents.user_agent_dir("claude"), n)
|
|
3322
|
+
for n in user_agent_files("claude")])
|
|
3280
3323
|
if "codex" in hosts:
|
|
3281
3324
|
clean("codex hooks.json unwired", [os.path.join(CODEX, "hooks.json")])
|
|
3282
3325
|
claim("codex skills unlinked",
|
|
@@ -3288,6 +3331,9 @@ def verify_uninstall(hosts, full):
|
|
|
3288
3331
|
clean("codex global AGENTS.md block gone",
|
|
3289
3332
|
[os.path.join(CODEX, n) for n in CODEX_RULES],
|
|
3290
3333
|
needle="<!-- tezgah:start")
|
|
3334
|
+
claim("codex subagents gone",
|
|
3335
|
+
[os.path.join(tezgah_agents.user_agent_dir("codex"), n)
|
|
3336
|
+
for n in user_agent_files("codex")])
|
|
3291
3337
|
if "opencode" in hosts:
|
|
3292
3338
|
claim("opencode plugin unlinked",
|
|
3293
3339
|
[os.path.join(OPENCODE, d, "tezgah.js") for d in ("plugins", "plugin")]
|
|
@@ -3438,6 +3484,13 @@ def uninstall_claude():
|
|
|
3438
3484
|
unwire_claude_settings()
|
|
3439
3485
|
drop_claude_mcp_row()
|
|
3440
3486
|
strip_managed_md(CLAUDE_RULES)
|
|
3487
|
+
remove_user_agents("claude")
|
|
3488
|
+
|
|
3489
|
+
|
|
3490
|
+
def remove_user_agents(host):
|
|
3491
|
+
"""Every tezgah MARKER file in the host's user agent dir; the user's stay."""
|
|
3492
|
+
for path in tezgah_agents.remove_stale(tezgah_agents.user_agent_dir(host), ()):
|
|
3493
|
+
print("%s removed %s" % (OK, path))
|
|
3441
3494
|
|
|
3442
3495
|
|
|
3443
3496
|
def drop_claude_mcp_row():
|
|
@@ -3472,6 +3525,7 @@ def uninstall_codex():
|
|
|
3472
3525
|
# both names: the override may have appeared, or gone, since the install
|
|
3473
3526
|
for name in CODEX_RULES:
|
|
3474
3527
|
strip_managed_md(os.path.join(CODEX, name))
|
|
3528
|
+
remove_user_agents("codex")
|
|
3475
3529
|
|
|
3476
3530
|
|
|
3477
3531
|
def uninstall_opencode():
|
|
@@ -4413,6 +4467,8 @@ def host_checks_claude():
|
|
|
4413
4467
|
bool(copies) and all(plugin_copy_current(t) for t in copies)),
|
|
4414
4468
|
("CLAUDE.md carries the contract (matches a fresh render)",
|
|
4415
4469
|
rules_current("claude")),
|
|
4470
|
+
("subagents in %s (match a fresh render)" % tezgah_agents.user_agent_dir("claude"),
|
|
4471
|
+
user_agent_files("claude") == tezgah_agents.user_agents("claude")),
|
|
4416
4472
|
] + claude_mcp_rows(copies)
|
|
4417
4473
|
|
|
4418
4474
|
|
|
@@ -4601,6 +4657,8 @@ def host_checks_codex():
|
|
|
4601
4657
|
("MCP in config.toml", "mcp_servers.codegraph" in toml),
|
|
4602
4658
|
("%s carries the contract (matches a fresh render)"
|
|
4603
4659
|
% os.path.basename(codex_rules()), rules_current("codex")),
|
|
4660
|
+
("subagents in %s (match a fresh render)" % tezgah_agents.user_agent_dir("codex"),
|
|
4661
|
+
user_agent_files("codex") == tezgah_agents.user_agents("codex")),
|
|
4604
4662
|
] + mcp_rows(toml_servers(toml_path), "config.toml") + [
|
|
4605
4663
|
mcp_row(TEZGAH_MCP_NAME, toml_mcp_entry(toml_path, TEZGAH_MCP_NAME),
|
|
4606
4664
|
"config.toml", tezgah_mcp_command()),
|
|
@@ -4767,7 +4825,7 @@ def host_checks_omp():
|
|
|
4767
4825
|
tp.have_typesafe_key()),
|
|
4768
4826
|
# One row per channel: the two disagree in both directions - a key file
|
|
4769
4827
|
# alone serves the seam, an omp login-store record alone serves omp.
|
|
4770
|
-
("judgement seam key resolvable (
|
|
4828
|
+
("judgement seam key resolvable (session CLI, Jev carrier or OpenRouter)",
|
|
4771
4829
|
tp.have_judge_key()),
|
|
4772
4830
|
("status line answers", omp_hook_answers()), ("tier models in task.agentModelOverrides and modelRoles.plan/slow", tier_models_current()),
|
|
4773
4831
|
]
|
|
@@ -4913,24 +4971,6 @@ def write_manifest():
|
|
|
4913
4971
|
return 0
|
|
4914
4972
|
|
|
4915
4973
|
|
|
4916
|
-
def write_plugin_agents():
|
|
4917
|
-
"""Re-render the plugin's tracked `agents/*.md` from tezgah_agents' role
|
|
4918
|
-
bodies, the source the per-repo agents are generated from. A plain write,
|
|
4919
|
-
not write_if_changed: a tracked file needs no `.tezgah-bak` beside it."""
|
|
4920
|
-
for name, text in tezgah_agents.plugin_agents().items():
|
|
4921
|
-
path = os.path.join(HERE, "agents", name)
|
|
4922
|
-
try:
|
|
4923
|
-
with open(path, encoding="utf-8") as fh:
|
|
4924
|
-
if fh.read() == text:
|
|
4925
|
-
continue
|
|
4926
|
-
except OSError:
|
|
4927
|
-
pass
|
|
4928
|
-
with open(path, "w", encoding="utf-8") as fh:
|
|
4929
|
-
fh.write(text)
|
|
4930
|
-
print("%s %s" % (OK, path))
|
|
4931
|
-
return 0
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
4974
|
def tree_sha(root, files):
|
|
4935
4975
|
"""sha256 over each relpath and its bytes, or None when one is unreadable."""
|
|
4936
4976
|
h = hashlib.sha256()
|
|
@@ -5631,8 +5671,6 @@ def main(argv):
|
|
|
5631
5671
|
help="regenerate this repo's subagent set (default: cwd)")
|
|
5632
5672
|
ap.add_argument("--write-manifest", action="store_true",
|
|
5633
5673
|
help="release: regenerate the tracked MANIFEST from git")
|
|
5634
|
-
ap.add_argument("--write-plugin-agents", action="store_true",
|
|
5635
|
-
help="release: re-render the plugin's agents/ from the role source")
|
|
5636
5674
|
ap.add_argument("--deps", action="store_true")
|
|
5637
5675
|
ap.add_argument("--mcp-schemas", action="store_true",
|
|
5638
5676
|
help="measure the MCP tool-schema band by asking each server")
|
|
@@ -5679,6 +5717,11 @@ def main(argv):
|
|
|
5679
5717
|
help="with --install: the language replies are asked for and "
|
|
5680
5718
|
"the Stop rule checks (config.json `reply_lang`; `tr` "
|
|
5681
5719
|
"when unset, `any` checks none)")
|
|
5720
|
+
ap.add_argument("--jev", type=tp.jev_name, choices=tp.JEV_CHOICES,
|
|
5721
|
+
help="the carrier the judgement seam reaches the Jev model "
|
|
5722
|
+
"through (config.json `jev`; `auto` tries every one "
|
|
5723
|
+
"that resolves); TEZGAH_JEV_PROVIDER and JEV_PROVIDER "
|
|
5724
|
+
"still win over it")
|
|
5682
5725
|
# `--version` is argparse's print-and-exit action, so `--upgrade --version V`
|
|
5683
5726
|
# would print the current version, exit 0 and move nothing at all - the
|
|
5684
5727
|
# "reads as done, did nothing" shape this repo hunts. Refused by name here,
|
|
@@ -5731,6 +5774,8 @@ def main(argv):
|
|
|
5731
5774
|
if args.features:
|
|
5732
5775
|
features_report()
|
|
5733
5776
|
return 0
|
|
5777
|
+
if args.jev:
|
|
5778
|
+
return set_jev(args.jev)
|
|
5734
5779
|
if args.refresh:
|
|
5735
5780
|
paths = refresh_contract()
|
|
5736
5781
|
for path in paths:
|
|
@@ -5740,8 +5785,6 @@ def main(argv):
|
|
|
5740
5785
|
return 0
|
|
5741
5786
|
if args.write_manifest:
|
|
5742
5787
|
return write_manifest()
|
|
5743
|
-
if args.write_plugin_agents:
|
|
5744
|
-
return write_plugin_agents()
|
|
5745
5788
|
if args.status:
|
|
5746
5789
|
print(tezgah_context.health_lines(os.path.realpath(expand(args.status))))
|
|
5747
5790
|
# The install-health half of this report, from the same function the
|
package/bin/tezgah-status
CHANGED
|
@@ -298,7 +298,9 @@ def main():
|
|
|
298
298
|
last = tj.last_use()
|
|
299
299
|
if "--json" in flags:
|
|
300
300
|
print(json.dumps(last, sort_keys=True))
|
|
301
|
-
|
|
301
|
+
return 0
|
|
302
|
+
print(tj.jev_summary())
|
|
303
|
+
if not last:
|
|
302
304
|
print("judge: no judgement recorded")
|
|
303
305
|
elif last.get("provider"):
|
|
304
306
|
print("judge: %s answered with %s/%s (fallback=%s)%s" % (
|