@r1z4x/tezgah 1.5.0 → 1.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/CHANGELOG.md +96 -1
- package/MANIFEST +6 -1
- package/README.md +6 -4
- package/bin/consult +13 -10
- package/bin/tezgah-docs +1 -1
- package/bin/tezgah-research +53 -3
- package/bin/tezgah-route +8 -5
- package/bin/tezgah-setup +45 -25
- package/bin/tezgah-task +194 -41
- package/docs/architecture.md +1 -1
- package/docs/citations-baseline.json +1 -1
- package/docs/clarity-baseline.json +4 -4
- package/docs/contract.md +4 -1
- package/docs/feature-audit.md +24 -3
- package/docs/gate.md +10 -11
- package/docs/hosts.md +9 -0
- package/docs/index.json +21 -7
- package/docs/judge.md +3 -1
- package/docs/layers.md +1 -1
- package/docs/models.md +17 -7
- package/docs/operations.md +0 -1
- package/docs/research.md +46 -0
- package/docs/skills.md +23 -5
- package/docs/testing.md +1 -1
- package/hooks/tezgah_agents.py +130 -96
- package/hooks/tezgah_components.py +2 -1
- package/hooks/tezgah_context.py +67 -15
- package/hooks/tezgah_gate.py +62 -76
- package/hooks/tezgah_judge.py +15 -8
- package/hooks/tezgah_models.py +50 -3
- package/hooks/tezgah_paths.py +26 -11
- package/hooks/tezgah_policy.py +47 -19
- package/hooks/tezgah_product.py +107 -0
- package/hooks/tezgah_research.py +3 -3
- package/hooks/tezgah_skill_pick.py +58 -17
- package/hooks/tezgah_task.py +16 -8
- package/hosts/opencode/plugins/tezgah.js +17 -37
- package/package.json +1 -2
- package/skills/analyze-app/SKILL.md +57 -0
- package/skills/design-contract/EVALS.md +24 -1
- package/skills/design-contract/SKILL.md +17 -1
- package/skills/design-contract/data-views.md +86 -0
- package/skills/feature-audit/SKILL.md +32 -3
- package/skills/plan-add/EVALS.md +8 -8
- package/skills/plan-add/SKILL.md +20 -21
- package/skills/plan-sync/EVALS.md +2 -2
- package/skills/plan-sync/SKILL.md +11 -7
- package/skills/pm-frameworks/SKILL.md +6 -0
- package/skills/product-analysis/EVALS.md +23 -1
- package/skills/product-analysis/SKILL.md +50 -11
- package/skills/product-flow/EVALS.md +81 -0
- package/skills/product-flow/SKILL.md +91 -0
- package/skills/research/SKILL.md +10 -1
- package/skills/tezgah-contract/SKILL.md +67 -41
- package/agents/tezgah-reviewer.md +0 -79
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,99 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.6.0] - 2026-10-10
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **A data, metric or dashboard ask reaches the product rule.** "Bu veriye uygun bir metric dashboard tasarla" armed no rule before. A dashboard, chart or metric-choice ask now arms `product`, judged per clause. A code marker in the same clause, such as a metrics endpoint, keeps it out.
|
|
14
|
+
- **product-analysis chooses metrics.** It names a North Star and its input metrics. Each metric gets one record: goal, signal, definition, window, baseline and more.
|
|
15
|
+
- **Dashboard rules in design-contract.** The new `data-views.md` picks the chart for the question. It also sets the context, the window and the widget states.
|
|
16
|
+
- **feature-audit forward mode.** A feature not built yet gets its five matrices in `spec.md`. The audit after the build compares against it.
|
|
17
|
+
- **analyze-app writes the design measurement.** Nothing produced `measurement.json` before. The skill now gives its shape and the snippet that reads it.
|
|
18
|
+
- **The local skill search reaches design-library's entries.** The search now reads the entries a skill's `INDEX.md` lists, one row each.
|
|
19
|
+
- **The product-flow skill and `tezgah-research next`.** A new product capability now runs in a fixed order, from research to re-measure. Each step leaves a file under `.tezgah/analysis/<topic>/`: a `## <step>` note in `flow.md`, or `spec.md` for the forward spec. `tezgah-research next --topic <t>` prints each step's status from those files, and a re-measure is due once the decision's `observe after:` date has passed. It then names the first missing or due step's skill and command. The skill lists the steps, and `FLOWS` in `hooks/tezgah_product.py` holds their done rules.
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
|
|
23
|
+
- **The product ledger is cut before release (ADR 025).** Plan 075's measured run did not show the ledger beating the playbook alone. Gone: writing and reading `.tezgah/product/ledger.jsonl`, `tezgah-research record`, `due` and `commit --product`, the ledger part of `check`, `product:` citations and the session-start note on due decisions. An existing `ledger.jsonl` is left in place, and nothing reads it.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- **The session runs the plan lifecycle itself.** The gate refused a session's
|
|
28
|
+
own `tezgah-task start|phase|allow|stop`. It told the session to ask the user,
|
|
29
|
+
so every plan ended with a command handed back. That refusal is gone. A phase
|
|
30
|
+
or scope refusal now names the move (`tezgah-task phase implementation`,
|
|
31
|
+
`tezgah-task allow`). The CLI still checks Acceptance, the spike, the
|
|
32
|
+
checkpoint and the review on each move. The gate still refuses a write to the
|
|
33
|
+
plan file itself. `tezgah-task allow` now needs `--why`, and `status` prints
|
|
34
|
+
the reason next to the widened scope.
|
|
35
|
+
- **`close` cleans up.** `tezgah-task close` on the active task clears its
|
|
36
|
+
phase instead of refusing. It then removes each linked worktree whose branch
|
|
37
|
+
has landed and which holds nothing else, through Orca when Orca tracks it. A
|
|
38
|
+
squash merge counts as landed. A branch whose commits cancel out does not. It
|
|
39
|
+
prints every worktree it keeps, with the reason. Uncommitted files, unknown
|
|
40
|
+
ignored files and `.tezgah` data keep a worktree. So do an unmerged branch, a
|
|
41
|
+
branch nobody committed on, an agent at work and an Orca that does not answer.
|
|
42
|
+
- **No process questions.** The contract says the orchestrator picks a process
|
|
43
|
+
parameter the user did not set, such as a spend ceiling, and states it.
|
|
44
|
+
`consult` on exit 4/5 records the first member of another model family
|
|
45
|
+
itself instead of asking the user to choose. After two review rounds the
|
|
46
|
+
session fixes what is left and closes with `close NNN done --resolved`. A
|
|
47
|
+
missing credential sends it to a route that needs none first. A silent gate
|
|
48
|
+
has it run `tezgah-setup --report` itself.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **product-analysis no longer says `check` demands three compared solutions.** An analysis answers once. A choice between solutions goes under `decisions/`.
|
|
53
|
+
|
|
54
|
+
## [1.5.1] - 2026-10-09
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **Subagents load in the first session.** Claude and Codex missed every role a
|
|
59
|
+
session wrote into a new repo until the next session. Codex also ignored
|
|
60
|
+
`.codex/agents` in an untrusted repo. The install now writes the roles to
|
|
61
|
+
`~/.claude/agents` and `$CODEX_HOME/agents`, as it already did for omp. A
|
|
62
|
+
session removes the copies an older release left in a repo's `.claude/agents`
|
|
63
|
+
and `.codex/agents`. Cursor read 9 of 10 role descriptions as ">" and got a
|
|
64
|
+
model id it does not list. It now gets its own `.cursor/agents` with a
|
|
65
|
+
one-line description and `model: inherit`.
|
|
66
|
+
|
|
67
|
+
- **`tezgah-route` names the specialists.** It could only name a tier. The
|
|
68
|
+
five specialists ran 0 times in 121 delegations. The generic task agent ran 86.
|
|
69
|
+
The judge now also answers whether a brief is a specialist's whole
|
|
70
|
+
job, and the route names that agent. `--phase review` and `--phase research`
|
|
71
|
+
name the reviewer and the researcher. A security brief that only reviews
|
|
72
|
+
goes to `tezgah-security`. The routing and fan-out text say to spawn the
|
|
73
|
+
named agent, never the generic one.
|
|
74
|
+
- **Jev through OpenRouter asks a model OpenRouter has.** The carrier sent
|
|
75
|
+
`typesafe/jev-latest`, which OpenRouter answers with 400 (no such model). It
|
|
76
|
+
now sends the bare `jev-latest`, which OpenRouter maps itself.
|
|
77
|
+
- **The Workers AI model id picks Clef.** `tezgah-setup` refused
|
|
78
|
+
`--jev @cf/cloudflare/clef-flash`, and the same value in
|
|
79
|
+
`TEZGAH_JEV_PROVIDER` chose no carrier. Both now name `clef-flash`, and
|
|
80
|
+
`@cf/cloudflare/clef` names `clef`.
|
|
81
|
+
- **opencode's judge run denies tools at agent level too.** A user's
|
|
82
|
+
`agent.build.permission` beat the inline top-level deny.
|
|
83
|
+
- **The refusal note needs an asked session CLI.** The seam writes it only
|
|
84
|
+
when it asked the session CLI first. A caller that named the CLI gets it.
|
|
85
|
+
- **A chosen carrier reads only its own key.** A `clef` choice no longer opens
|
|
86
|
+
omp's login database on every status-line redraw.
|
|
87
|
+
- **`pnpm test` names a command.** An Acceptance item counted as a command
|
|
88
|
+
only when a word after the first was an option or a path. So `pnpm test`,
|
|
89
|
+
`cargo test` and `make test` read as missing, and the phase move refused the
|
|
90
|
+
plan. A known runner followed by `test` or `run` now counts. Prose such as
|
|
91
|
+
`run the tests` still does not.
|
|
92
|
+
- **Each carrier's price is now pinned by a test.** The one cost check priced a
|
|
93
|
+
TypeSafe answer at Jev's rate. It passed with or without per-carrier pricing.
|
|
94
|
+
New tests in `tezgah-taste`, `tezgah-triage` and the replay labeller price Clef
|
|
95
|
+
and Clef-flash answers at their own rates. A run with an unpriced answer prints
|
|
96
|
+
`cost unknown`.
|
|
97
|
+
- **Claude lists the reviewer once.** The plugin shipped its own
|
|
98
|
+
`agents/tezgah-reviewer.md`, which Claude shows as `tezgah:tezgah-reviewer`
|
|
99
|
+
beside the generated reviewer and never shadows. The plugin now ships no
|
|
100
|
+
agent, and its release-time renderer is gone with it.
|
|
101
|
+
|
|
9
102
|
## [1.5.0] - 2026-10-09
|
|
10
103
|
|
|
11
104
|
### Changed
|
|
@@ -933,7 +1026,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
933
1026
|
the copy could not find `hooks/` and died on import. It now links into the
|
|
934
1027
|
installed tree, and also installs the command as `tezgah`.
|
|
935
1028
|
|
|
936
|
-
[Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.
|
|
1029
|
+
[Unreleased]: https://github.com/r1z4x/tezgah/compare/v1.6.0...HEAD
|
|
1030
|
+
[1.6.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.6.0
|
|
1031
|
+
[1.5.1]: https://github.com/r1z4x/tezgah/releases/tag/v1.5.1
|
|
937
1032
|
[1.5.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.5.0
|
|
938
1033
|
[1.4.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.4.0
|
|
939
1034
|
[1.3.0]: https://github.com/r1z4x/tezgah/releases/tag/v1.3.0
|
package/MANIFEST
CHANGED
|
@@ -19,7 +19,6 @@ README.md
|
|
|
19
19
|
README.tr.md
|
|
20
20
|
RELEASING.md
|
|
21
21
|
SECURITY.md
|
|
22
|
-
agents/tezgah-reviewer.md
|
|
23
22
|
assets/logo/README.md
|
|
24
23
|
assets/logo/tezgah-logo-16.png
|
|
25
24
|
assets/logo/tezgah-logo-256.png
|
|
@@ -134,6 +133,7 @@ hooks/tezgah_models.py
|
|
|
134
133
|
hooks/tezgah_orca.py
|
|
135
134
|
hooks/tezgah_paths.py
|
|
136
135
|
hooks/tezgah_policy.py
|
|
136
|
+
hooks/tezgah_product.py
|
|
137
137
|
hooks/tezgah_rank.py
|
|
138
138
|
hooks/tezgah_replay.py
|
|
139
139
|
hooks/tezgah_research.py
|
|
@@ -546,6 +546,7 @@ skills/analyze-app/EVALS.md
|
|
|
546
546
|
skills/analyze-app/SKILL.md
|
|
547
547
|
skills/design-contract/EVALS.md
|
|
548
548
|
skills/design-contract/SKILL.md
|
|
549
|
+
skills/design-contract/data-views.md
|
|
549
550
|
skills/design-library/EVALS.md
|
|
550
551
|
skills/design-library/INDEX.md
|
|
551
552
|
skills/design-library/SKILL.md
|
|
@@ -613,6 +614,8 @@ skills/ponytail/EVALS.md
|
|
|
613
614
|
skills/ponytail/SKILL.md
|
|
614
615
|
skills/product-analysis/EVALS.md
|
|
615
616
|
skills/product-analysis/SKILL.md
|
|
617
|
+
skills/product-flow/EVALS.md
|
|
618
|
+
skills/product-flow/SKILL.md
|
|
616
619
|
skills/research/EVALS.md
|
|
617
620
|
skills/research/SKILL.md
|
|
618
621
|
skills/rl-env/SKILL.md
|
|
@@ -734,6 +737,8 @@ tests/test_packaging_scripts.py
|
|
|
734
737
|
tests/test_paths.py
|
|
735
738
|
tests/test_paths_worktree.py
|
|
736
739
|
tests/test_platform.py
|
|
740
|
+
tests/test_product_flow_e2e.py
|
|
741
|
+
tests/test_product_next.py
|
|
737
742
|
tests/test_providers.py
|
|
738
743
|
tests/test_rank.py
|
|
739
744
|
tests/test_replay.py
|
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/consult
CHANGED
|
@@ -56,7 +56,8 @@ suffix); 2 no key for the chosen provider, or no member available at all; 3 the
|
|
|
56
56
|
--provider/--models run got no answer; 4 no recorded choice - one "offer:" line
|
|
57
57
|
per available member, nothing was asked; 5 every recorded member (and the
|
|
58
58
|
fallback) failed - one "reoffer:" line per member still available that did not
|
|
59
|
-
fail. On 4 or 5,
|
|
59
|
+
fail. On 4 or 5, the agent records the first listed member of another model
|
|
60
|
+
family with --use itself and asks again; the user is not asked to choose.
|
|
60
61
|
"""
|
|
61
62
|
import concurrent.futures
|
|
62
63
|
import json
|
|
@@ -504,11 +505,11 @@ def retry_hint(classes):
|
|
|
504
505
|
return "the referee answered outside its five headings; re-run, or judge the panel yourself"
|
|
505
506
|
if c == "credit":
|
|
506
507
|
return ("the provider account has no credit (HTTP 402) - the key is fine; "
|
|
507
|
-
"top it up, or
|
|
508
|
+
"top it up, or record another member with --use")
|
|
508
509
|
if c == "key":
|
|
509
|
-
return "set the provider's key, or
|
|
510
|
+
return "set the provider's key, or record another member with --use"
|
|
510
511
|
if c == "missing":
|
|
511
|
-
return "the agent CLI is not installed;
|
|
512
|
+
return "the agent CLI is not installed; record another member with --use"
|
|
512
513
|
if c.startswith("cli-exit"):
|
|
513
514
|
return ("the agent CLI failed (not logged in, or a model name it does not "
|
|
514
515
|
"know); run it once by hand, or record another member with --use")
|
|
@@ -645,8 +646,9 @@ def main():
|
|
|
645
646
|
print("consult: no recorded choice, so nothing was asked.")
|
|
646
647
|
for opt in found:
|
|
647
648
|
print(offer_line("offer", opt))
|
|
648
|
-
print("next:
|
|
649
|
-
"panel)
|
|
649
|
+
print("next: record the first of these from a model family other than "
|
|
650
|
+
"yours (two make a panel) with: consult --use <member>[,<member>], "
|
|
651
|
+
"then ask again - do not ask the user to choose")
|
|
650
652
|
sys.stdout.flush()
|
|
651
653
|
os._exit(4)
|
|
652
654
|
|
|
@@ -775,14 +777,15 @@ def main():
|
|
|
775
777
|
print("retry: %s" % hint)
|
|
776
778
|
code = 0 if answers else 3
|
|
777
779
|
if not answers and not explicit:
|
|
778
|
-
#
|
|
779
|
-
#
|
|
780
|
+
# consult never swaps the recorded choice on its own: it names what is
|
|
781
|
+
# left, and the agent records the next member and asks again.
|
|
780
782
|
lost = {c[1] for c, _cls in failures} | skip_opts
|
|
781
783
|
left = [o for o in options() if o not in lost]
|
|
782
784
|
for opt in left:
|
|
783
785
|
print(offer_line("reoffer", opt))
|
|
784
|
-
print("next:
|
|
785
|
-
"consult --use <member>[,<member>]
|
|
786
|
+
print("next: record the first of these from another model family with: "
|
|
787
|
+
"consult --use <member>[,<member>], then ask again - do not ask the "
|
|
788
|
+
"user to choose" if left else
|
|
786
789
|
"next: nothing else can answer here; tell the user the second "
|
|
787
790
|
"opinion was skipped (install an agent CLI or add a provider key)")
|
|
788
791
|
code = 5
|
package/bin/tezgah-docs
CHANGED
|
@@ -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:697-972` (`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
|
package/bin/tezgah-research
CHANGED
|
@@ -16,11 +16,17 @@
|
|
|
16
16
|
tezgah-research source <slug> <hypothesis> --run <orxRunId> [--command "<cmd>"]
|
|
17
17
|
[--scope real|fixture|derived] [--fixture "<what was generated>"]
|
|
18
18
|
tezgah-research compare <slug> <decision> record one variants x criteria cell from stdin
|
|
19
|
+
tezgah-research verdict <slug> <id> met|not-met|unanswerable --evidence "<ref>"
|
|
20
|
+
record one success criterion's verdict
|
|
21
|
+
tezgah-research conclude <slug> conclude a line whose criteria all carry a verdict
|
|
22
|
+
tezgah-research migrate-layout move flat research/<slug> into open/ or done/
|
|
19
23
|
tezgah-research close <slug> --limit "<reason>" conclude a line as a deliberate limit
|
|
20
24
|
tezgah-research seal <slug> [--history-lost] --ack "<owner decision>"
|
|
21
25
|
seal a line concluded before seals existed
|
|
22
26
|
tezgah-research import <checkout> [<slug>] [--allow-open "<why>"]
|
|
23
27
|
bring lines from another checkout, with history
|
|
28
|
+
tezgah-research next --topic <t> [--flow metric-dashboard|feature] [--json]
|
|
29
|
+
the product-flow steps of a topic and the next one
|
|
24
30
|
|
|
25
31
|
A research line lives at <repo>/.tezgah/research/<slug>/ and holds the state an
|
|
26
32
|
auditable investigation needs: the question, the decision log, the findings, the
|
|
@@ -103,6 +109,7 @@ import time
|
|
|
103
109
|
HERE = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
104
110
|
sys.path.insert(0, os.path.join(HERE, "hooks"))
|
|
105
111
|
import tezgah_paths as tp # noqa: E402
|
|
112
|
+
import tezgah_product as tp_product # noqa: E402
|
|
106
113
|
import tezgah_research as tr # noqa: E402
|
|
107
114
|
|
|
108
115
|
|
|
@@ -245,12 +252,17 @@ def cmd_commit(args):
|
|
|
245
252
|
known = tr.slugs(repo)
|
|
246
253
|
if slug not in known:
|
|
247
254
|
return misuse("no such research line (have: %s)" % (", ".join(known) or "none"))
|
|
255
|
+
return commit_path(repo, tr.line_dir(repo, slug), message)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def commit_path(repo, path, message):
|
|
259
|
+
"""Stage and commit `path` alone in the private repository of `repo`."""
|
|
248
260
|
ws = tp.ensure_workspace(repo)
|
|
249
261
|
if ws is None:
|
|
250
262
|
print("commit refused: %s is not a git work tree (or .tezgah could not be "
|
|
251
263
|
"set up), so there is no private repository to commit into" % repo)
|
|
252
264
|
return 1
|
|
253
|
-
rel = os.path.relpath(
|
|
265
|
+
rel = os.path.relpath(path, ws)
|
|
254
266
|
try:
|
|
255
267
|
done = tp.ws_git(repo, "add", "-A", "--", rel)
|
|
256
268
|
if not done.returncode:
|
|
@@ -298,6 +310,7 @@ def cmd_check(args):
|
|
|
298
310
|
print("warn %s: %s" % (slug, warn))
|
|
299
311
|
else:
|
|
300
312
|
print("no research line under %s" % tr.root(repo))
|
|
313
|
+
lines = len(report)
|
|
301
314
|
if ask_orx:
|
|
302
315
|
# The registry is the repository's, not a line's, so this runs whatever the
|
|
303
316
|
# lines look like - which is also why it is only asked for when --orx says so.
|
|
@@ -313,7 +326,7 @@ def cmd_check(args):
|
|
|
313
326
|
print(json.dumps(report, indent=2))
|
|
314
327
|
return 1 if problems else 0
|
|
315
328
|
if not problems:
|
|
316
|
-
print("research: %d line(s) ok" %
|
|
329
|
+
print("research: %d line(s) ok" % lines)
|
|
317
330
|
if not rest and not all_lines:
|
|
318
331
|
done = [slug for slug in known if tr.sealed(repo, slug)]
|
|
319
332
|
if done:
|
|
@@ -576,6 +589,40 @@ def cmd_compare(args):
|
|
|
576
589
|
return 0
|
|
577
590
|
|
|
578
591
|
|
|
592
|
+
def cmd_next(args):
|
|
593
|
+
"""The product-flow steps of one topic with their status, then the first
|
|
594
|
+
missing or due step's skill and command; a report, so exit 0."""
|
|
595
|
+
usage = "next --topic <t> [--flow %s] [--json]" % "|".join(tp_product.FLOWS)
|
|
596
|
+
opts, as_json, rest = {}, False, list(args)
|
|
597
|
+
while rest:
|
|
598
|
+
arg = rest.pop(0)
|
|
599
|
+
if arg == "--json":
|
|
600
|
+
as_json = True
|
|
601
|
+
elif arg in ("--topic", "--flow") and rest and arg not in opts:
|
|
602
|
+
opts[arg] = rest.pop(0)
|
|
603
|
+
else:
|
|
604
|
+
return misuse(usage)
|
|
605
|
+
flow = opts.get("--flow", "metric-dashboard")
|
|
606
|
+
if "--topic" not in opts:
|
|
607
|
+
return misuse(usage)
|
|
608
|
+
if flow not in tp_product.FLOWS:
|
|
609
|
+
return misuse("unknown flow %r (%s)" % (flow, ", ".join(tp_product.FLOWS)))
|
|
610
|
+
topic = opts["--topic"]
|
|
611
|
+
steps = tp_product.next_steps(repo_root(os.getcwd()), topic, flow)
|
|
612
|
+
if as_json:
|
|
613
|
+
# the step list next_steps returns; the next step is its first
|
|
614
|
+
# missing or due entry
|
|
615
|
+
print(json.dumps(steps, indent=2))
|
|
616
|
+
return 0
|
|
617
|
+
first = next((s for s in steps if s["status"] in ("missing", "due")), None)
|
|
618
|
+
for n, step in enumerate(steps, 1):
|
|
619
|
+
print("%d. %s [%s] %s - %s" % (n, step["id"], step["status"], step["skill"],
|
|
620
|
+
step["detail"]))
|
|
621
|
+
print("next: %s - %s" % (first["skill"], first["command"]) if first
|
|
622
|
+
else "next: nothing left")
|
|
623
|
+
return 0
|
|
624
|
+
|
|
625
|
+
|
|
579
626
|
def cmd_verdict(args):
|
|
580
627
|
"""Record one success criterion's verdict, with the evidence it rests on."""
|
|
581
628
|
if len(args) < 3:
|
|
@@ -769,6 +816,8 @@ def main(argv):
|
|
|
769
816
|
return cmd_source(rest)
|
|
770
817
|
if cmd == "compare":
|
|
771
818
|
return cmd_compare(rest)
|
|
819
|
+
if cmd == "next":
|
|
820
|
+
return cmd_next(rest)
|
|
772
821
|
if cmd == "verdict":
|
|
773
822
|
return cmd_verdict(rest)
|
|
774
823
|
if cmd == "conclude":
|
|
@@ -782,7 +831,8 @@ def main(argv):
|
|
|
782
831
|
if cmd == "import":
|
|
783
832
|
return cmd_import(rest)
|
|
784
833
|
return misuse("unknown command %r (init, commit, check, status, --all, claim, predict, "
|
|
785
|
-
"components, migrate, source, compare,
|
|
834
|
+
"components, migrate, source, compare, verdict, conclude, migrate-layout, "
|
|
835
|
+
"close, seal, import, next)" % cmd)
|
|
786
836
|
|
|
787
837
|
|
|
788
838
|
if __name__ == "__main__":
|
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,10 +9,13 @@
|
|
|
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
|
-
|
|
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
|
|
16
19
|
provider - the session's own CLI (omp, claude, opencode, cursor, codex) or the Jev
|
|
17
20
|
model through TypeSafe, OpenRouter System One, Cloudflare Workers AI or a
|
|
18
21
|
compatible endpoint (TEZGAH_JEV_PROVIDER picks one), else the OpenRouter chat
|
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
|
|
@@ -188,7 +187,7 @@ OMP_LESSONS = ("Read `.tezgah/lessons.md` in the repository if it exists and tre
|
|
|
188
187
|
LOCAL_BIN = os.path.join(HOME, ".local", "bin")
|
|
189
188
|
SKILLS = ["harness", "tezgah-contract", "ponytail", "i-have-adhd", "no-ai-slop",
|
|
190
189
|
"analyze-app", "plan-add", "plan-status", "plan-sync", "research",
|
|
191
|
-
"product-analysis", "feature-audit", "design-contract",
|
|
190
|
+
"product-analysis", "product-flow", "feature-audit", "design-contract",
|
|
192
191
|
"design-library", "pm-frameworks", "ai-research", "rl-env"]
|
|
193
192
|
|
|
194
193
|
|
|
@@ -1163,9 +1162,33 @@ def install_claude():
|
|
|
1163
1162
|
# (`TEZGAH_CORE_IN_FILE`, hooks/projects-auto-init.py), so a session pays for
|
|
1164
1163
|
# the contract once.
|
|
1165
1164
|
print("%s %s (always-on contract)" % (OK, write_rules("claude")))
|
|
1165
|
+
write_user_agents("claude")
|
|
1166
1166
|
register_claude_plugin()
|
|
1167
1167
|
|
|
1168
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
|
+
|
|
1169
1192
|
# The marketplace a manifest rendered by this installer carries, so Claude
|
|
1170
1193
|
# installs the plugin as `tezgah@tezgah-local`. A manifest the tree already has
|
|
1171
1194
|
# under another name (the maintainer's) is kept, name included.
|
|
@@ -1358,6 +1381,7 @@ def install_codex():
|
|
|
1358
1381
|
# instructions Codex keeps for the whole session. Global, like omp's
|
|
1359
1382
|
# RULES.md, because a per-project file would be a tracked project file.
|
|
1360
1383
|
print("%s %s (always-on contract)" % (OK, write_rules("codex")))
|
|
1384
|
+
write_user_agents("codex")
|
|
1361
1385
|
|
|
1362
1386
|
|
|
1363
1387
|
def _toml_str(value):
|
|
@@ -3293,6 +3317,9 @@ def verify_uninstall(hosts, full):
|
|
|
3293
3317
|
# the user's own CLAUDE.md may name tezgah; only the block is ours
|
|
3294
3318
|
clean("claude global CLAUDE.md block gone", [CLAUDE_RULES],
|
|
3295
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")])
|
|
3296
3323
|
if "codex" in hosts:
|
|
3297
3324
|
clean("codex hooks.json unwired", [os.path.join(CODEX, "hooks.json")])
|
|
3298
3325
|
claim("codex skills unlinked",
|
|
@@ -3304,6 +3331,9 @@ def verify_uninstall(hosts, full):
|
|
|
3304
3331
|
clean("codex global AGENTS.md block gone",
|
|
3305
3332
|
[os.path.join(CODEX, n) for n in CODEX_RULES],
|
|
3306
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")])
|
|
3307
3337
|
if "opencode" in hosts:
|
|
3308
3338
|
claim("opencode plugin unlinked",
|
|
3309
3339
|
[os.path.join(OPENCODE, d, "tezgah.js") for d in ("plugins", "plugin")]
|
|
@@ -3454,6 +3484,13 @@ def uninstall_claude():
|
|
|
3454
3484
|
unwire_claude_settings()
|
|
3455
3485
|
drop_claude_mcp_row()
|
|
3456
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))
|
|
3457
3494
|
|
|
3458
3495
|
|
|
3459
3496
|
def drop_claude_mcp_row():
|
|
@@ -3488,6 +3525,7 @@ def uninstall_codex():
|
|
|
3488
3525
|
# both names: the override may have appeared, or gone, since the install
|
|
3489
3526
|
for name in CODEX_RULES:
|
|
3490
3527
|
strip_managed_md(os.path.join(CODEX, name))
|
|
3528
|
+
remove_user_agents("codex")
|
|
3491
3529
|
|
|
3492
3530
|
|
|
3493
3531
|
def uninstall_opencode():
|
|
@@ -4429,6 +4467,8 @@ def host_checks_claude():
|
|
|
4429
4467
|
bool(copies) and all(plugin_copy_current(t) for t in copies)),
|
|
4430
4468
|
("CLAUDE.md carries the contract (matches a fresh render)",
|
|
4431
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")),
|
|
4432
4472
|
] + claude_mcp_rows(copies)
|
|
4433
4473
|
|
|
4434
4474
|
|
|
@@ -4617,6 +4657,8 @@ def host_checks_codex():
|
|
|
4617
4657
|
("MCP in config.toml", "mcp_servers.codegraph" in toml),
|
|
4618
4658
|
("%s carries the contract (matches a fresh render)"
|
|
4619
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")),
|
|
4620
4662
|
] + mcp_rows(toml_servers(toml_path), "config.toml") + [
|
|
4621
4663
|
mcp_row(TEZGAH_MCP_NAME, toml_mcp_entry(toml_path, TEZGAH_MCP_NAME),
|
|
4622
4664
|
"config.toml", tezgah_mcp_command()),
|
|
@@ -4929,24 +4971,6 @@ def write_manifest():
|
|
|
4929
4971
|
return 0
|
|
4930
4972
|
|
|
4931
4973
|
|
|
4932
|
-
def write_plugin_agents():
|
|
4933
|
-
"""Re-render the plugin's tracked `agents/*.md` from tezgah_agents' role
|
|
4934
|
-
bodies, the source the per-repo agents are generated from. A plain write,
|
|
4935
|
-
not write_if_changed: a tracked file needs no `.tezgah-bak` beside it."""
|
|
4936
|
-
for name, text in tezgah_agents.plugin_agents().items():
|
|
4937
|
-
path = os.path.join(HERE, "agents", name)
|
|
4938
|
-
try:
|
|
4939
|
-
with open(path, encoding="utf-8") as fh:
|
|
4940
|
-
if fh.read() == text:
|
|
4941
|
-
continue
|
|
4942
|
-
except OSError:
|
|
4943
|
-
pass
|
|
4944
|
-
with open(path, "w", encoding="utf-8") as fh:
|
|
4945
|
-
fh.write(text)
|
|
4946
|
-
print("%s %s" % (OK, path))
|
|
4947
|
-
return 0
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
4974
|
def tree_sha(root, files):
|
|
4951
4975
|
"""sha256 over each relpath and its bytes, or None when one is unreadable."""
|
|
4952
4976
|
h = hashlib.sha256()
|
|
@@ -5647,8 +5671,6 @@ def main(argv):
|
|
|
5647
5671
|
help="regenerate this repo's subagent set (default: cwd)")
|
|
5648
5672
|
ap.add_argument("--write-manifest", action="store_true",
|
|
5649
5673
|
help="release: regenerate the tracked MANIFEST from git")
|
|
5650
|
-
ap.add_argument("--write-plugin-agents", action="store_true",
|
|
5651
|
-
help="release: re-render the plugin's agents/ from the role source")
|
|
5652
5674
|
ap.add_argument("--deps", action="store_true")
|
|
5653
5675
|
ap.add_argument("--mcp-schemas", action="store_true",
|
|
5654
5676
|
help="measure the MCP tool-schema band by asking each server")
|
|
@@ -5695,7 +5717,7 @@ def main(argv):
|
|
|
5695
5717
|
help="with --install: the language replies are asked for and "
|
|
5696
5718
|
"the Stop rule checks (config.json `reply_lang`; `tr` "
|
|
5697
5719
|
"when unset, `any` checks none)")
|
|
5698
|
-
ap.add_argument("--jev", choices=tp.JEV_CHOICES,
|
|
5720
|
+
ap.add_argument("--jev", type=tp.jev_name, choices=tp.JEV_CHOICES,
|
|
5699
5721
|
help="the carrier the judgement seam reaches the Jev model "
|
|
5700
5722
|
"through (config.json `jev`; `auto` tries every one "
|
|
5701
5723
|
"that resolves); TEZGAH_JEV_PROVIDER and JEV_PROVIDER "
|
|
@@ -5763,8 +5785,6 @@ def main(argv):
|
|
|
5763
5785
|
return 0
|
|
5764
5786
|
if args.write_manifest:
|
|
5765
5787
|
return write_manifest()
|
|
5766
|
-
if args.write_plugin_agents:
|
|
5767
|
-
return write_plugin_agents()
|
|
5768
5788
|
if args.status:
|
|
5769
5789
|
print(tezgah_context.health_lines(os.path.realpath(expand(args.status))))
|
|
5770
5790
|
# The install-health half of this report, from the same function the
|