@r1z4x/tezgah 1.0.0 → 1.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/CHANGELOG.md +92 -1
- package/MANIFEST +10 -0
- package/bin/tezgah-agents +10 -1
- package/bin/tezgah-docs +1 -1
- package/bin/tezgah-doctor +11 -0
- package/bin/tezgah-research +31 -14
- package/bin/tezgah-setup +154 -29
- package/bin/tezgah-skill +59 -0
- package/bin/tezgah-skill.py +59 -0
- package/bin/tezgah-status +62 -1
- package/bin/tezgah-taste +504 -5
- package/bin/tezgah-triage +5 -3
- package/docs/README.md +1 -0
- package/docs/architecture.md +5 -2
- package/docs/citations-baseline.json +1 -1
- package/docs/clarity-baseline.json +9 -9
- package/docs/contract.md +68 -26
- package/docs/environment.md +6 -3
- package/docs/evidence.md +7 -0
- package/docs/gate.md +13 -7
- package/docs/glossary.md +1 -1
- package/docs/hosts.md +48 -1
- package/docs/index.json +34 -3
- package/docs/judge.md +75 -60
- package/docs/models.md +14 -8
- package/docs/operations.md +80 -25
- package/docs/orca.md +126 -0
- package/docs/research.md +38 -3
- package/docs/skills.md +74 -0
- package/docs/status-line.md +3 -1
- package/docs/testing.md +33 -21
- package/docs/vision.md +12 -8
- package/hooks/projects-posttooluse.py +7 -0
- package/hooks/projects-pretooluse.py +17 -5
- package/hooks/tezgah_agents.py +135 -13
- package/hooks/tezgah_components.py +3 -0
- package/hooks/tezgah_context.py +84 -22
- package/hooks/tezgah_gate.py +133 -17
- package/hooks/tezgah_integrity.py +60 -13
- package/hooks/tezgah_judge.py +288 -66
- package/hooks/tezgah_models.py +41 -5
- package/hooks/tezgah_orca.py +144 -0
- package/hooks/tezgah_paths.py +40 -6
- package/hooks/tezgah_policy.py +109 -35
- package/hooks/tezgah_research.py +104 -8
- package/hooks/tezgah_skill_pick.py +459 -8
- package/hooks/tezgah_stack.py +382 -0
- package/hooks/tezgah_stopspec.py +2 -2
- package/hooks/tezgah_taste.py +35 -9
- package/hooks/tezgah_taste_ledger.py +628 -0
- package/hosts/codex/hook.py +12 -1
- package/hosts/cursor/hook.py +12 -4
- package/hosts/omp/hook.py +18 -6
- package/hosts/omp/tezgah-hook.ts.in +20 -7
- package/hosts/opencode/plugins/tezgah.js +28 -15
- package/output-styles/tezgah.md +34 -19
- package/package.json +1 -1
- package/skills/i-have-adhd/SKILL.md +2 -2
- package/skills/research/SKILL.md +60 -17
- package/skills/tezgah-contract/SKILL.md +55 -13
package/bin/tezgah-skill
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""tezgah-skill [-k N] [--json] [--root DIR] QUERY... - find the skill section
|
|
3
|
+
that answers a topic.
|
|
4
|
+
|
|
5
|
+
The roster a session meets is hundreds of one-line names, so the skill that
|
|
6
|
+
holds the answer is a guess unless something searches the bodies. This is that
|
|
7
|
+
search: every installed skill root on the machine (the plugin's own, omp's user
|
|
8
|
+
and custom-directories corpora, Claude/Codex/Agents/opencode), indexed by
|
|
9
|
+
heading, ranked by the same BM25 the per-turn lessons block uses. Each hit is a
|
|
10
|
+
section, not a file:
|
|
11
|
+
|
|
12
|
+
skill://attack-jwt:26-122 /abs/SKILL.md JWT Token Attack
|
|
13
|
+
|
|
14
|
+
The `skill://<name>:<start>-<end>` address is what omp's read tool resolves
|
|
15
|
+
(line selectors included); the absolute path is for the hosts that have no
|
|
16
|
+
skill:// transport, and is also the tiebreaker where two roots hold different
|
|
17
|
+
skills under one name (the search reports the first root's copy, omp's own
|
|
18
|
+
precedence order - it does not resolve the collision for you).
|
|
19
|
+
|
|
20
|
+
--root DIR adds a directory to the discovered roots (a project's vendored
|
|
21
|
+
skills); --json prints the machine shape. Exit 0 hits, 1 none, 2 usage.
|
|
22
|
+
"""
|
|
23
|
+
import argparse
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
HERE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
29
|
+
sys.path.insert(0, os.path.join(HERE, "hooks"))
|
|
30
|
+
import tezgah_skill_pick as sp # noqa: E402
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main(argv=None):
|
|
34
|
+
parser = argparse.ArgumentParser(
|
|
35
|
+
prog="tezgah-skill", description=__doc__.splitlines()[0])
|
|
36
|
+
parser.add_argument("query", nargs="+", help="the topic to search for")
|
|
37
|
+
parser.add_argument("-k", type=int, default=5, metavar="N",
|
|
38
|
+
help="sections to print (default 5)")
|
|
39
|
+
parser.add_argument("--root", action="append", default=[], metavar="DIR",
|
|
40
|
+
help="an extra skills directory to index (repeatable)")
|
|
41
|
+
parser.add_argument("--json", action="store_true",
|
|
42
|
+
help="print [{skill, uri, path, title, start, end}]")
|
|
43
|
+
args = parser.parse_args(argv)
|
|
44
|
+
query = " ".join(args.query)
|
|
45
|
+
hits = sp.search(query, k=max(1, args.k),
|
|
46
|
+
roots=(sp.skill_roots(args.root) if args.root else None))
|
|
47
|
+
if args.json:
|
|
48
|
+
print(json.dumps([{"skill": h[0],
|
|
49
|
+
"uri": "skill://%s:%d-%d" % (h[0], h[3], h[4]),
|
|
50
|
+
"path": h[1], "title": h[2],
|
|
51
|
+
"start": h[3], "end": h[4]} for h in hits], indent=1))
|
|
52
|
+
else:
|
|
53
|
+
for skill, path, title, start, end in hits:
|
|
54
|
+
print("skill://%s:%d-%d\t%s\t%s" % (skill, start, end, path, title))
|
|
55
|
+
return 0 if hits else 1
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
sys.exit(main())
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""tezgah-skill [-k N] [--json] [--root DIR] QUERY... - find the skill section
|
|
3
|
+
that answers a topic.
|
|
4
|
+
|
|
5
|
+
The roster a session meets is hundreds of one-line names, so the skill that
|
|
6
|
+
holds the answer is a guess unless something searches the bodies. This is that
|
|
7
|
+
search: every installed skill root on the machine (the plugin's own, omp's user
|
|
8
|
+
and custom-directories corpora, Claude/Codex/Agents/opencode), indexed by
|
|
9
|
+
heading, ranked by the same BM25 the per-turn lessons block uses. Each hit is a
|
|
10
|
+
section, not a file:
|
|
11
|
+
|
|
12
|
+
skill://attack-jwt:26-122 /abs/SKILL.md JWT Token Attack
|
|
13
|
+
|
|
14
|
+
The `skill://<name>:<start>-<end>` address is what omp's read tool resolves
|
|
15
|
+
(line selectors included); the absolute path is for the hosts that have no
|
|
16
|
+
skill:// transport, and is also the tiebreaker where two roots hold different
|
|
17
|
+
skills under one name (the search reports the first root's copy, omp's own
|
|
18
|
+
precedence order - it does not resolve the collision for you).
|
|
19
|
+
|
|
20
|
+
--root DIR adds a directory to the discovered roots (a project's vendored
|
|
21
|
+
skills); --json prints the machine shape. Exit 0 hits, 1 none, 2 usage.
|
|
22
|
+
"""
|
|
23
|
+
import argparse
|
|
24
|
+
import json
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
|
|
28
|
+
HERE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
29
|
+
sys.path.insert(0, os.path.join(HERE, "hooks"))
|
|
30
|
+
import tezgah_skill_pick as sp # noqa: E402
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def main(argv=None):
|
|
34
|
+
parser = argparse.ArgumentParser(
|
|
35
|
+
prog="tezgah-skill", description=__doc__.splitlines()[0])
|
|
36
|
+
parser.add_argument("query", nargs="+", help="the topic to search for")
|
|
37
|
+
parser.add_argument("-k", type=int, default=5, metavar="N",
|
|
38
|
+
help="sections to print (default 5)")
|
|
39
|
+
parser.add_argument("--root", action="append", default=[], metavar="DIR",
|
|
40
|
+
help="an extra skills directory to index (repeatable)")
|
|
41
|
+
parser.add_argument("--json", action="store_true",
|
|
42
|
+
help="print [{skill, uri, path, title, start, end}]")
|
|
43
|
+
args = parser.parse_args(argv)
|
|
44
|
+
query = " ".join(args.query)
|
|
45
|
+
hits = sp.search(query, k=max(1, args.k),
|
|
46
|
+
roots=(sp.skill_roots(args.root) if args.root else None))
|
|
47
|
+
if args.json:
|
|
48
|
+
print(json.dumps([{"skill": h[0],
|
|
49
|
+
"uri": "skill://%s:%d-%d" % (h[0], h[3], h[4]),
|
|
50
|
+
"path": h[1], "title": h[2],
|
|
51
|
+
"start": h[3], "end": h[4]} for h in hits], indent=1))
|
|
52
|
+
else:
|
|
53
|
+
for skill, path, title, start, end in hits:
|
|
54
|
+
print("skill://%s:%d-%d\t%s\t%s" % (skill, start, end, path, title))
|
|
55
|
+
return 0 if hits else 1
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
if __name__ == "__main__":
|
|
59
|
+
sys.exit(main())
|
package/bin/tezgah-status
CHANGED
|
@@ -62,7 +62,17 @@ their status line; this CLI is how every host (and a human) gets it:
|
|
|
62
62
|
records, prose-only) and the record from
|
|
63
63
|
docs/contract.md. A report, not a gate: it always
|
|
64
64
|
exits 0. `--json` prints the rows.
|
|
65
|
+
tezgah-status --judge what the judgement seam used last: the provider and
|
|
66
|
+
model that answered, and why it was not the session's
|
|
67
|
+
own CLI when it was not, or the refusal and the
|
|
68
|
+
failures that left no judgement. `--json` prints it.
|
|
65
69
|
tezgah-status --legend explain the marks
|
|
70
|
+
tezgah-status --orca [PATH]
|
|
71
|
+
this repository's checkouts as Orca sees them: each
|
|
72
|
+
one Orca tracks with its card status, live terminal
|
|
73
|
+
count and agent states (`orca worktree ps`), and each
|
|
74
|
+
git worktree Orca does not list. Exits 0 when Orca is
|
|
75
|
+
absent or down and says so. `--json` prints the fold.
|
|
66
76
|
tezgah-status --observable=consult,research,graph,orch
|
|
67
77
|
the measures this surface can see; a measure outside
|
|
68
78
|
the set renders as dim "no state" instead of claiming
|
|
@@ -200,6 +210,37 @@ def print_enforcers(as_json):
|
|
|
200
210
|
print("unrecorded: %s" % failure)
|
|
201
211
|
|
|
202
212
|
|
|
213
|
+
def print_orca(cwd, as_json):
|
|
214
|
+
"""`--orca`: this repository's git checkouts against Orca's listing. A
|
|
215
|
+
checkout Orca does not list has no card, terminal tracking or agent status
|
|
216
|
+
in Orca: create the next one with `orca worktree create`, or show the
|
|
217
|
+
existing ones with `orca repo set --external-worktree-visibility show`."""
|
|
218
|
+
import tezgah_orca
|
|
219
|
+
from tezgah_paths import worktrees
|
|
220
|
+
if not tezgah_orca.cli():
|
|
221
|
+
print("orca CLI not found: nothing to track")
|
|
222
|
+
return 0
|
|
223
|
+
found = tezgah_orca.checkouts(worktrees(cwd) or [cwd])
|
|
224
|
+
if found is None:
|
|
225
|
+
print("orca runtime did not answer `orca worktree ps --json` "
|
|
226
|
+
"(start it with `orca open`)")
|
|
227
|
+
return 0
|
|
228
|
+
if as_json:
|
|
229
|
+
print(json.dumps(found))
|
|
230
|
+
return 0
|
|
231
|
+
print("Orca tracks %d checkout(s) of this repository:" % len(found["tracked"]))
|
|
232
|
+
for w in found["tracked"]:
|
|
233
|
+
print(" %s [%s/%s] %d terminal(s) agents: %s" % (
|
|
234
|
+
w["path"], w["card"] or "-", w["status"] or "-", w["terminals"],
|
|
235
|
+
", ".join(w["agents"]) or "-"))
|
|
236
|
+
if found["untracked"]:
|
|
237
|
+
print("not listed by Orca (raw `git worktree`; no card, terminals or "
|
|
238
|
+
"agent status there): %d" % len(found["untracked"]))
|
|
239
|
+
for path in found["untracked"]:
|
|
240
|
+
print(" %s" % path)
|
|
241
|
+
return 0
|
|
242
|
+
|
|
243
|
+
|
|
203
244
|
def main():
|
|
204
245
|
# `--` ends the options: what follows is PATH and SESSION_ID even when it
|
|
205
246
|
# starts with a dash. The dsh route and the MCP tool hand caller-chosen
|
|
@@ -219,7 +260,7 @@ def main():
|
|
|
219
260
|
pos = [a for a in args if not a.startswith("--")] + rest
|
|
220
261
|
known = {"--legend", "--json", "--color", "--no-color", "--counters",
|
|
221
262
|
"--all", "--trend", "--skill-fitness", "--failure-shapes",
|
|
222
|
-
"--rule-yield", "--enforcers", "--help"}
|
|
263
|
+
"--rule-yield", "--enforcers", "--orca", "--judge", "--help"}
|
|
223
264
|
unknown = sorted(f for f in flags
|
|
224
265
|
if f not in known and not f.startswith("--observable="))
|
|
225
266
|
if unknown:
|
|
@@ -252,6 +293,23 @@ def main():
|
|
|
252
293
|
sys.stderr.write("tezgah-status: %s only means something with "
|
|
253
294
|
"--counters\n" % modifier)
|
|
254
295
|
return 2
|
|
296
|
+
if "--judge" in flags:
|
|
297
|
+
import tezgah_judge as tj
|
|
298
|
+
last = tj.last_use()
|
|
299
|
+
if "--json" in flags:
|
|
300
|
+
print(json.dumps(last, sort_keys=True))
|
|
301
|
+
elif not last:
|
|
302
|
+
print("judge: no judgement recorded")
|
|
303
|
+
elif last.get("provider"):
|
|
304
|
+
print("judge: %s answered with %s/%s (fallback=%s)%s" % (
|
|
305
|
+
last.get("at"), last["provider"], last.get("model"),
|
|
306
|
+
last.get("policy"), "; not the session: " + last["fallback"]
|
|
307
|
+
if last.get("fallback") else ""))
|
|
308
|
+
else:
|
|
309
|
+
print("judge: %s no judgement (fallback=%s): %s; failed: %s" % (
|
|
310
|
+
last.get("at"), last.get("policy"),
|
|
311
|
+
last.get("refused") or "every provider failed", last.get("failed")))
|
|
312
|
+
return 0
|
|
255
313
|
if "--skill-fitness" in flags:
|
|
256
314
|
fit = skill_fitness()
|
|
257
315
|
if "--json" in flags:
|
|
@@ -362,6 +420,9 @@ def main():
|
|
|
362
420
|
if "--enforcers" in flags:
|
|
363
421
|
print_enforcers("--json" in flags)
|
|
364
422
|
return 0
|
|
423
|
+
if "--orca" in flags:
|
|
424
|
+
return print_orca(os.path.realpath(pos[0] if pos else os.getcwd()),
|
|
425
|
+
"--json" in flags)
|
|
365
426
|
cwd = os.path.realpath(pos[0] if pos else os.getcwd())
|
|
366
427
|
session = pos[1] if len(pos) > 1 else os.environ.get("TEZGAH_SESSION")
|
|
367
428
|
spec = next((f.split("=", 1)[1] for f in flags
|