@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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Orca: is this session inside it, which CLI drives it, and what it tracks.
|
|
3
|
+
|
|
4
|
+
Orca (an IDE for agent sessions) owns worktrees, terminals, its embedded browser
|
|
5
|
+
and artifacts, and it exports its own markers into every terminal it starts.
|
|
6
|
+
tezgah reads those markers - never writes them - so a session in Orca is told to
|
|
7
|
+
create a checkout with `orca worktree create` rather than a raw `git worktree
|
|
8
|
+
add` Orca does not list, and `tezgah-status --orca` shows which of a repo's
|
|
9
|
+
checkouts Orca tracks. Stdlib only; detection forks nothing, so the session-start
|
|
10
|
+
line costs no process. docs/orca.md is the page.
|
|
11
|
+
"""
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import shlex
|
|
15
|
+
import shutil
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
# The markers an Orca terminal exports (observed in Orca 1.4.221's env). Any one
|
|
20
|
+
# is enough; TERM_PROGRAM=Orca covers a shell that dropped the ORCA_* set.
|
|
21
|
+
MARKERS = ("ORCA_WORKTREE_ID", "ORCA_TERMINAL_HANDLE", "ORCA_PANE_KEY")
|
|
22
|
+
MAC_APP_CLI = "/Applications/Orca.app/Contents/Resources/bin/orca"
|
|
23
|
+
# The file Orca leaves in each per-account codex home it hands out as CODEX_HOME.
|
|
24
|
+
MANAGED_HOME = ".orca-managed-home"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def session(env=None):
|
|
28
|
+
"""{worktree, terminal, version} when this process runs in an Orca terminal,
|
|
29
|
+
else None. `worktree` is the path half of ORCA_WORKTREE_ID
|
|
30
|
+
(`<repoId>::<path>`)."""
|
|
31
|
+
env = os.environ if env is None else env
|
|
32
|
+
if not any(env.get(k) for k in MARKERS) and env.get("TERM_PROGRAM") != "Orca":
|
|
33
|
+
return None
|
|
34
|
+
return {"worktree": env.get("ORCA_WORKTREE_ID", "").partition("::")[2],
|
|
35
|
+
"terminal": env.get("ORCA_TERMINAL_HANDLE", ""),
|
|
36
|
+
"version": env.get("ORCA_APP_VERSION", "")}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def cli(env=None, platform=None):
|
|
40
|
+
"""The argv prefix that runs Orca's CLI, or None. The order is the orca-cli
|
|
41
|
+
skill's: Orca's own ORCA_CLI_COMMAND (WSL), then its exported bin dir, then
|
|
42
|
+
PATH. On Linux outside an Orca terminal a bare `orca` is the GNOME screen
|
|
43
|
+
reader, so only `orca-ide` counts there. TEZGAH_ORCA_BIN pins one binary
|
|
44
|
+
(tests): a missing one is None, never a fallback to the real app."""
|
|
45
|
+
env = os.environ if env is None else env
|
|
46
|
+
platform = platform or sys.platform
|
|
47
|
+
pinned = env.get("TEZGAH_ORCA_BIN")
|
|
48
|
+
if pinned:
|
|
49
|
+
return [pinned] if os.access(pinned, os.X_OK) else None
|
|
50
|
+
if env.get("ORCA_CLI_COMMAND"):
|
|
51
|
+
return shlex.split(env["ORCA_CLI_COMMAND"])
|
|
52
|
+
bindir = env.get("ORCA_CLI_BIN_DIR")
|
|
53
|
+
if bindir and os.access(os.path.join(bindir, "orca"), os.X_OK):
|
|
54
|
+
return [os.path.join(bindir, "orca")]
|
|
55
|
+
path = env.get("PATH", os.defpath)
|
|
56
|
+
name = ("orca-ide" if platform.startswith("linux") and not session(env)
|
|
57
|
+
else "orca")
|
|
58
|
+
found = shutil.which(name, path=path)
|
|
59
|
+
if found:
|
|
60
|
+
return [found]
|
|
61
|
+
if platform == "darwin" and os.access(MAC_APP_CLI, os.X_OK):
|
|
62
|
+
return [MAC_APP_CLI]
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def run(args, timeout=10):
|
|
67
|
+
"""`orca <args> --json`'s `result`, or None when there is no CLI, the
|
|
68
|
+
runtime is down or the answer is not `ok`."""
|
|
69
|
+
argv = cli()
|
|
70
|
+
if not argv:
|
|
71
|
+
return None
|
|
72
|
+
try:
|
|
73
|
+
proc = subprocess.run(argv + list(args) + ["--json"], capture_output=True,
|
|
74
|
+
text=True, timeout=timeout)
|
|
75
|
+
data = json.loads(proc.stdout)
|
|
76
|
+
except (OSError, ValueError, subprocess.SubprocessError):
|
|
77
|
+
return None
|
|
78
|
+
return data.get("result") if isinstance(data, dict) and data.get("ok") else None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def checkouts(paths):
|
|
82
|
+
"""Orca's view of a repository's git checkouts (`paths`, real paths):
|
|
83
|
+
{"tracked": [row per checkout Orca lists], "untracked": [path Orca does not
|
|
84
|
+
list]}, or None when the runtime cannot answer. Read from `orca worktree ps`,
|
|
85
|
+
the one call that carries terminals and agent states together."""
|
|
86
|
+
ps = run(["worktree", "ps", "--limit", "1000"])
|
|
87
|
+
if ps is None:
|
|
88
|
+
return None
|
|
89
|
+
wanted = set(paths)
|
|
90
|
+
tracked = {}
|
|
91
|
+
for row in ps.get("worktrees") or []:
|
|
92
|
+
path = os.path.realpath(row.get("path") or "")
|
|
93
|
+
if path in wanted:
|
|
94
|
+
tracked[path] = {
|
|
95
|
+
"path": path, "name": row.get("displayName") or "",
|
|
96
|
+
"card": row.get("workspaceStatus") or "",
|
|
97
|
+
"status": row.get("status") or "",
|
|
98
|
+
"terminals": row.get("liveTerminalCount") or 0,
|
|
99
|
+
"agents": ["%s:%s" % (a.get("agentType"), a.get("state"))
|
|
100
|
+
for a in row.get("agents") or []]}
|
|
101
|
+
return {"tracked": [tracked[p] for p in paths if p in tracked],
|
|
102
|
+
"untracked": [p for p in paths if p not in tracked]}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def context_line(env=None):
|
|
106
|
+
"""The session-start line for a session inside Orca, or "" outside it."""
|
|
107
|
+
here = session(env)
|
|
108
|
+
if not here:
|
|
109
|
+
return ""
|
|
110
|
+
where = here["worktree"] or "unknown"
|
|
111
|
+
return ("Orca: this session runs in an Orca terminal (worktree `%s`); Orca "
|
|
112
|
+
"owns worktrees, terminals, its embedded browser and artifacts here. "
|
|
113
|
+
"A parallel slice that needs its own checkout gets one from `orca "
|
|
114
|
+
"worktree create --name <slug> --parent-worktree active --json` (add "
|
|
115
|
+
"`--agent <cli> --prompt <brief>` to start its worker there), not "
|
|
116
|
+
"`git worktree add`, which Orca does not list. A long job runs in "
|
|
117
|
+
"`orca terminal create --worktree <selector> --command <cmd> --json`, "
|
|
118
|
+
"not a background shell. Track with `orca worktree ps --json` and "
|
|
119
|
+
"`tezgah-status --orca`; mark checkpoints with `orca worktree set "
|
|
120
|
+
"--worktree active --comment <text>`. Full guide: `orca skills get "
|
|
121
|
+
"orca-cli`." % where)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def summary(codex_home):
|
|
125
|
+
"""One `orca:` line for `tezgah-setup --status`: app/runtime, whether this
|
|
126
|
+
shell is in an Orca terminal, and whose codex home CODEX_HOME names; ""
|
|
127
|
+
when Orca is neither installed nor around this shell."""
|
|
128
|
+
argv, here = cli(), session()
|
|
129
|
+
if not argv and not here:
|
|
130
|
+
return ""
|
|
131
|
+
runtime = ((run(["status"]) or {}).get("runtime") or {}) if argv else {}
|
|
132
|
+
parts = ["orca: %s" % (
|
|
133
|
+
"runtime %s reachable" % runtime.get("appVersion", "")
|
|
134
|
+
if runtime.get("reachable") else
|
|
135
|
+
"CLI %s, runtime not reachable (`orca open`)" % argv[-1] if argv else
|
|
136
|
+
"no orca CLI found")]
|
|
137
|
+
parts.append("this shell runs in an Orca terminal (worktree %s)"
|
|
138
|
+
% (here["worktree"] or "unknown") if here
|
|
139
|
+
else "this shell is not an Orca terminal")
|
|
140
|
+
if os.path.isfile(os.path.join(codex_home, MANAGED_HOME)):
|
|
141
|
+
parts.append("CODEX_HOME %s is an Orca-managed codex home: tezgah adds "
|
|
142
|
+
"its hook groups beside Orca's and leaves Orca's entries "
|
|
143
|
+
"and env alone" % codex_home)
|
|
144
|
+
return "; ".join(parts)
|
package/hooks/tezgah_paths.py
CHANGED
|
@@ -497,18 +497,23 @@ def have_typesafe_key():
|
|
|
497
497
|
|
|
498
498
|
|
|
499
499
|
def have_judge_key():
|
|
500
|
-
"""True when tezgah's own judgement seam
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
500
|
+
"""True when tezgah's own judgement seam can ask anyone: the session's own
|
|
501
|
+
CLI (`session_cli()`), else - when the `fallback` setting lets a third party
|
|
502
|
+
answer - the TypeSafe env var or key file, then the OpenRouter fallback's own
|
|
503
|
+
two channels (`OPENROUTER_API_KEY`, `~/.config/openrouter/key`), which are
|
|
504
|
+
the channels `hooks/tezgah_judge.py`'s `providers()` picks between. This is
|
|
505
|
+
what `bin/tezgah-triage`, `bin/tezgah-docs` and the skill picker spend. Never
|
|
506
|
+
omp's login store: the seam does not open it.
|
|
506
507
|
|
|
507
508
|
Kept next to `have_typesafe_key()` because the two are asked together and a
|
|
508
509
|
report that showed one answer under both questions was wrong in both
|
|
509
510
|
directions - a key file alone (seam works, omp does not) and a login-store
|
|
510
511
|
record alone (omp works, seam does not). A blank file is not a credential
|
|
511
512
|
here either, so this stays in step with the seam's own `strip() or None`."""
|
|
513
|
+
if session_cli():
|
|
514
|
+
return True
|
|
515
|
+
if fallback_policy() == "none":
|
|
516
|
+
return False
|
|
512
517
|
for env, home in (("TYPESAFE_API_KEY", ".config/typesafe/key"),
|
|
513
518
|
("OPENROUTER_API_KEY", ".config/openrouter/key")):
|
|
514
519
|
if os.environ.get(env, "").strip():
|
|
@@ -522,6 +527,35 @@ def have_judge_key():
|
|
|
522
527
|
return False
|
|
523
528
|
|
|
524
529
|
|
|
530
|
+
# How far a model call may leave the session's own vendor (config.json
|
|
531
|
+
# `fallback`). `vendor`, the default: the session's own model first, and a
|
|
532
|
+
# fallback only to the same vendor's model (a subagent chain may reach Opus
|
|
533
|
+
# through OpenRouter; the judge has no such route, so it falls back to nobody
|
|
534
|
+
# while the session CLI exists, and to a third party only when there is no
|
|
535
|
+
# session CLI at all). `any`: a cross-vendor fallback is allowed after the
|
|
536
|
+
# session's own model fails. `none`: no provider but the session's, ever.
|
|
537
|
+
FALLBACKS = ("vendor", "any", "none")
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def fallback_policy():
|
|
541
|
+
"""The configured `fallback` value, `vendor` when unset or unknown."""
|
|
542
|
+
cfg = config()
|
|
543
|
+
value = cfg.get("fallback") if isinstance(cfg, dict) else None
|
|
544
|
+
return value if value in FALLBACKS else "vendor"
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def session_cli():
|
|
548
|
+
"""`omp` or `claude` - the CLI of the host this process runs under, when it
|
|
549
|
+
is installed - else None. omp sets CLAUDECODE too, so OMPCODE is read first,
|
|
550
|
+
and an omp session whose binary is missing is None rather than Claude: the
|
|
551
|
+
session's credential is omp's, not whatever `claude` is logged in as."""
|
|
552
|
+
if os.environ.get("OMPCODE"):
|
|
553
|
+
return "omp" if omp_bin() else None
|
|
554
|
+
if os.environ.get("CLAUDECODE"):
|
|
555
|
+
return "claude" if claude_bin() else None
|
|
556
|
+
return None
|
|
557
|
+
|
|
558
|
+
|
|
525
559
|
def orx_bin():
|
|
526
560
|
"""The OpenResearch `orx` executable, or None when it is not installed.
|
|
527
561
|
|
package/hooks/tezgah_policy.py
CHANGED
|
@@ -273,13 +273,25 @@ discovery, review, impact analysis (research goes to OpenResearch - see the
|
|
|
273
273
|
Research section - with a host subagent only as the fallback when `orx` is
|
|
274
274
|
absent). Subtasks with no data dependency
|
|
275
275
|
between them MUST be spawned in ONE message so they run in parallel; dependent
|
|
276
|
-
ones run sequentially, each briefed with the previous result.
|
|
276
|
+
ones run sequentially, each briefed with the previous result. Fanning out is the
|
|
277
|
+
default, not a favour the user has to ask for: two or more independent items -
|
|
278
|
+
a list, several files or areas, todo entries that share nothing - go to one
|
|
279
|
+
subagent each, and every writing slice gets its own branch and checkout (see
|
|
280
|
+
below), never the main checkout. The
|
|
281
|
+
orchestrator is the main thread itself: it does not wait idle on a slice while
|
|
282
|
+
independent work remains. Fan out only when
|
|
277
283
|
the subtasks share no mutable file and no interface: if two of them would edit
|
|
278
284
|
the same file, or one's answer decides the other's, keep them in one context or
|
|
279
285
|
sequence them. The shared artifact is the coordination channel, not chatter -
|
|
280
286
|
naming a lead coordinates nothing by itself. If the work
|
|
281
287
|
cannot be split - one file, one bounded change, a strictly serial chain - do it
|
|
282
288
|
directly. Never spawn a subagent whose briefing is bigger than the work.
|
|
289
|
+
A slice that needs its own checkout: inside Orca (the session-start `Orca:`
|
|
290
|
+
line) create it with `orca worktree create --name <slug> --parent-worktree
|
|
291
|
+
active --json` and track it with `orca worktree ps --json` or `tezgah-status
|
|
292
|
+
--orca` - a raw `git worktree add` gets no card, terminal tracking or agent
|
|
293
|
+
status in Orca; outside Orca, `git worktree add`. Either way the brief names
|
|
294
|
+
absolute paths under that checkout.
|
|
283
295
|
Routing: `{ROUTE_BIN} "<brief>"` names the worker - tezgah-cheap, -standard or
|
|
284
296
|
-frontier (overrides for stored data, credentials, security first; then a Jev
|
|
285
297
|
judgement; the static phase table with no key). A worker that answers
|
|
@@ -400,12 +412,30 @@ experiments/variants, or producing a research artifact (report, figure, dataset)
|
|
|
400
412
|
It is NOT "where is X defined" or "who calls Y" - that is code discovery and
|
|
401
413
|
stays on the codegraph index.
|
|
402
414
|
|
|
415
|
+
**Lock the line to the question asked.** Restate the user's question verbatim as
|
|
416
|
+
the line's scope (`--ask`) and name what is in it and what is out (`--in`,
|
|
417
|
+
`--out`: state.json `scope`). Every finding answers an in-scope item - a claim's
|
|
418
|
+
`trace` names its `S<n>`, and `{RESEARCH_BIN} check` refuses one that names none -
|
|
419
|
+
and a finding that answers nothing asked is dropped, not reported as a bonus. Do
|
|
420
|
+
not widen the question, add sub-questions or chase an adjacent topic; a tangent
|
|
421
|
+
worth having is one line under Open questions for the user to pick. The question
|
|
422
|
+
picks the method, not the tooling: a qualitative, conceptual or reference question
|
|
423
|
+
is a source review (`--method qualitative`: no metric, no experiment), and a
|
|
424
|
+
metric, a locked evaluation and the experiment tree belong only to a question
|
|
425
|
+
whose answer is a measurement (`--method measured`). A plain factual or reference
|
|
426
|
+
question gets no line at all: answer it in the reply from primary sources. Depth
|
|
427
|
+
is the primary source read, not more breadth - the spec, the paper, the code, the
|
|
428
|
+
vendor's own documentation - each finding citing the source it rests on and
|
|
429
|
+
stating its confidence (high, medium or low, and why); a summary of summaries is
|
|
430
|
+
not an answer.
|
|
431
|
+
|
|
403
432
|
When the task is research and the OpenResearch CLI (`{ORX_BIN}`) is installed,
|
|
404
433
|
drive it through that CLI instead of ad-hoc local scripting. Load the operating
|
|
405
434
|
manual first: the `orx` skill if the host has it, otherwise run
|
|
406
|
-
`{ORX_BIN} skill` from the shell; then
|
|
407
|
-
|
|
408
|
-
`{ORX_BIN} skill evidence
|
|
435
|
+
`{ORX_BIN} skill` from the shell; then the module the method needs -
|
|
436
|
+
`{ORX_BIN} skill lit-review` for sources, `{ORX_BIN} skill experiment-tree` and
|
|
437
|
+
`{ORX_BIN} skill evidence` for a measured line. On a measured line its cardinal
|
|
438
|
+
rules are not style preferences - they
|
|
409
439
|
are what keeps results comparable, and breaking one silently invalidates the run:
|
|
410
440
|
never edit a node once a run has answered it (branch a child instead); the run
|
|
411
441
|
command and environment are a fixed contract identical on every node; vary the
|
|
@@ -420,7 +450,8 @@ afterwards - and `{RESEARCH_BIN} check` refuses a claim that claims a wider scop
|
|
|
420
450
|
than the rows it rests on.
|
|
421
451
|
|
|
422
452
|
If the CLI is not installed, say the research tooling is unavailable and do not
|
|
423
|
-
improvise its protocol; fall back to a host subagent and say so
|
|
453
|
+
improvise its protocol; fall back to a host subagent and say so, briefed with the
|
|
454
|
+
question verbatim, its in- and out-of-scope items and its method. Kill switch:
|
|
424
455
|
`research-off`.
|
|
425
456
|
|
|
426
457
|
Keep the line auditable in the repository, not in your head:
|
|
@@ -437,11 +468,13 @@ into a refusal. A report owes an independent review (`reviewer` is not the
|
|
|
437
468
|
construct and conclusion validity threats, whatever `phase` says. `state.json`
|
|
438
469
|
`deliverable` names what the line delivers and lists the ask's items, each
|
|
439
470
|
answered in the report, a decision or a claim `trace`, or written `A<n> not
|
|
440
|
-
delivered: <reason>`. A design
|
|
471
|
+
delivered: <reason>`. A design or plan the user will choose between is produced
|
|
472
|
+
as >= 3 variants in
|
|
441
473
|
`decisions/<id>/` - `criteria.json` committed before any cell, `variants.jsonl`
|
|
442
474
|
(V0 the status quo), cells appended by `{RESEARCH_BIN} compare`, `decision.md`
|
|
443
475
|
naming the chosen id, each rejected id with the criterion that ruled it out, and a
|
|
444
|
-
`flip:` condition; a new version of a
|
|
476
|
+
`flip:` condition (an analysis answers its question once); a new version of a
|
|
477
|
+
line is opened `init --supersedes <old>` and
|
|
445
478
|
carries the old one as a variant. Code or config variants are sibling orx nodes
|
|
446
479
|
under one head node. Literature is found with `{ORX_BIN} discover` and read with
|
|
447
480
|
`{ORX_BIN} paper`; an agent's summary is indexed as `agent-report` and never alone
|
|
@@ -602,7 +635,7 @@ prefers shell tools; that preference does NOT cover definitions, callers, or
|
|
|
602
635
|
blast radius - those go to the graph. The graph answers from a parsed call
|
|
603
636
|
graph, so it beats text search on renames, dynamic dispatch and cross-file
|
|
604
637
|
callers. On Claude a PreToolUse hook denies the Explore subagent here and nudges
|
|
605
|
-
the first
|
|
638
|
+
the first code-symbol search per session toward the graph. The MCP tool's
|
|
606
639
|
schema is deferred: on the FIRST code-discovery step of the session load it -
|
|
607
640
|
ToolSearch("select:mcp__codegraph__codegraph_explore,mcp__plugin_tezgah_codegraph__codegraph_explore")
|
|
608
641
|
(the plain name on a checkout's own server, the `plugin_tezgah_` one on a plugin
|
|
@@ -645,27 +678,40 @@ it - no apology theater, no self-justifying phrasing.
|
|
|
645
678
|
**Ponytail (minimal code).** Laziest solution that works: YAGNI -> reuse an
|
|
646
679
|
existing helper -> stdlib -> native platform feature -> installed dependency ->
|
|
647
680
|
one line -> minimum code. No unrequested abstractions, no scaffolding "for
|
|
648
|
-
later", shortest working diff. Trace the
|
|
649
|
-
simplify away validation, error handling,
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
681
|
+
later", deletion over addition, fewest files, shortest working diff. Trace the
|
|
682
|
+
problem fully before climbing; never simplify away validation, error handling,
|
|
683
|
+
security, accessibility or anything requested, and build the full version
|
|
684
|
+
without re-arguing when the user insists. Bug fix = root cause where all
|
|
685
|
+
callers route through. A deliberate corner cut gets a `ponytail:` comment
|
|
686
|
+
naming the ceiling. Non-trivial logic leaves ONE runnable check (an assert
|
|
687
|
+
self-check or one small test); a one-liner needs none. A complex ask ships the
|
|
688
|
+
lazy version and names the fuller one in the same reply instead of stalling.
|
|
689
|
+
Reply: code first, then at most three lines - what was skipped, when to add it.
|
|
690
|
+
In force from the first turn; levels lite|full|ultra are set with `tezgah-pony`
|
|
691
|
+
and ride the per-turn reminder when not `full`; read the full `ponytail` skill
|
|
692
|
+
from the router for the level table and examples. Off: "stop ponytail".
|
|
655
693
|
|
|
656
694
|
**Output shape: ADHD-friendly.** The answer or the next action is on the first
|
|
657
|
-
line
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
695
|
+
line - a command, path, snippet or the decision itself, never context or the
|
|
696
|
+
question restated - prose after it. Multi-step work is a numbered list, one
|
|
697
|
+
bounded action per step, and while it is in flight its position is restated in
|
|
698
|
+
one line - the todo list is that source, never re-narrate the plan. End with one
|
|
699
|
+
concrete next step. Finish the issue in hand before raising a second one; an
|
|
700
|
+
error states location, cause and fix with no drama, naming a cause only when the
|
|
701
|
+
evidence identifies it (else what is known and what would confirm it); after a
|
|
702
|
+
change say what now works. A list shows at most five items, ranked, the rest
|
|
703
|
+
kept in reserve rather than dropped - presentation only, never the analysis. An
|
|
663
704
|
estimate is in concrete units and marked as an estimate, never presented as a
|
|
664
705
|
measurement. No preamble, no recap, no closer, and a question the reader raises
|
|
665
|
-
mid-work is answered rather than deferred as the second issue.
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
the
|
|
706
|
+
mid-work is answered rather than deferred as the second issue. Break the shape
|
|
707
|
+
only where it would delete the answer: an explanation runs as long as it needs,
|
|
708
|
+
with headers; an options question gets two to four ranked options,
|
|
709
|
+
recommendation first; after three "still broken" turns, name the suspect
|
|
710
|
+
assumption and ask one diagnostic question. Before sending, delete an opening
|
|
711
|
+
sentence that announces, a closing one that recaps or asks "anything else?", any
|
|
712
|
+
by-the-way sidebar and any hedge that carries no real uncertainty. In force from
|
|
713
|
+
the first answer; read the full `i-have-adhd` skill from the router for the
|
|
714
|
+
examples. Off: `tezgah-adhd off`, or the repo's `.no-adhd`.
|
|
669
715
|
|
|
670
716
|
**Deliver the whole ask; never the shortcut.** The request defines the
|
|
671
717
|
deliverable: every named item is in scope until the user says otherwise, and the
|
|
@@ -756,19 +802,30 @@ Off: `consult-off`.
|
|
|
756
802
|
|
|
757
803
|
**Research: route it to OpenResearch.** When a task is research - a literature
|
|
758
804
|
or reference review, forming and testing hypotheses, running or comparing
|
|
759
|
-
experiments, producing a research artifact -
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
805
|
+
experiments, producing a research artifact - lock it to the question asked:
|
|
806
|
+
restate the user's question verbatim as the scope, name what is in and out of it
|
|
807
|
+
(`init --ask --in --out`), and drop every finding that does not answer an
|
|
808
|
+
in-scope item; never widen it or chase an adjacent topic. The question picks the
|
|
809
|
+
method: a qualitative or reference question is a source review (`--method
|
|
810
|
+
qualitative`, no metric, no experiment); metrics and the experiment tree only
|
|
811
|
+
when the answer is a measurement (`--method measured`). A plain factual or
|
|
812
|
+
reference question gets no line and no orx experiment: answer it in the reply.
|
|
813
|
+
Depth means primary sources read and cited, each finding with its confidence,
|
|
814
|
+
not a thin summary. Drive the work through the OpenResearch CLI (`{ORX_BIN}`)
|
|
815
|
+
and load its manual first (`{ORX_BIN} skill`; `lit-review` for sources,
|
|
816
|
+
`experiment-tree` for a measured line) instead of improvising the protocol.
|
|
817
|
+
Plain code discovery stays on the code graph, not OpenResearch.
|
|
818
|
+
At most one line is open at a time:
|
|
763
819
|
while one is under way - not `concluded`, or carrying a live proposal, an unrun
|
|
764
820
|
experiment, a missing report or review, an unlabelled review finding - no new line
|
|
765
821
|
opens until it is closed or `{RESEARCH_BIN} close <slug> --limit "<reason>"` records
|
|
766
822
|
what is left. `{RESEARCH_BIN} init` refuses on that rule and names each open line;
|
|
767
823
|
`--allow-open "<reason>"` passes it (never beside a line `check` fails) and logs why.
|
|
768
|
-
A
|
|
769
|
-
first (`decisions/`);
|
|
824
|
+
A design or plan the user will choose between is >= 3 variants compared under
|
|
825
|
+
criteria committed first (`decisions/`); an analysis answers its question once.{OPEN_LINES}
|
|
770
826
|
If the CLI is not installed, say the research tooling is unavailable and fall
|
|
771
|
-
back to a host subagent
|
|
827
|
+
back to a host subagent briefed with the question verbatim, its in/out scope and
|
|
828
|
+
its method. Off: `research-off`.
|
|
772
829
|
|
|
773
830
|
**Product analysis: five axes, one evidence class per finding.** A product
|
|
774
831
|
question - "analyse this product", "why is retention falling", "which feature
|
|
@@ -826,7 +883,9 @@ Never install, upgrade, restart or kill anything for tezgah, and never open an
|
|
|
826
883
|
issue for one of its tools mid-session. Name a missing capability in one line,
|
|
827
884
|
use the documented fallback (grep/find, or the second opinion skipped), and carry
|
|
828
885
|
on with the task in hand. Tezgah maintenance is in scope when the user asks for
|
|
829
|
-
it, or when the repo IS the tezgah checkout.
|
|
886
|
+
it, or when the repo IS the tezgah checkout. Before working a topic an
|
|
887
|
+
installed skill covers, run `tezgah-skill <words>` and read only the range it
|
|
888
|
+
returns.
|
|
830
889
|
|
|
831
890
|
**Workspace: `.tezgah/` only.** Every per-project tezgah artifact - plans,
|
|
832
891
|
research lines, analysis, lessons - lives under `<repo>/.tezgah/`, which the
|
|
@@ -835,6 +894,21 @@ project's `.gitignore` excludes and which keeps its own private git repository
|
|
|
835
894
|
project root and never `git add -f` anything under `.tezgah/`. Off:
|
|
836
895
|
`workspace-off`.
|
|
837
896
|
|
|
897
|
+
**Parallel by default: fan out independent items.** A request with two or
|
|
898
|
+
more independent items - a numbered or bulleted list, several files or areas, a
|
|
899
|
+
todo list whose entries share no file and no interface - is delegated without
|
|
900
|
+
being asked: one subagent per item, all spawned in ONE message, while the main
|
|
901
|
+
thread decomposes, integrates, verifies and reports. Every writing slice gets
|
|
902
|
+
its own branch and checkout - inside Orca `orca worktree create --name <slug>
|
|
903
|
+
--parent-worktree active --json`, else `git worktree add -b <branch> <absolute
|
|
904
|
+
path>`; its brief names every read, edit and shell `cwd` as an absolute path
|
|
905
|
+
under that checkout, never the main checkout, whose `git status --short` must
|
|
906
|
+
not grow. Read-only slices need no worktree. Pick each agent by task class from
|
|
907
|
+
the specialists line; `{ROUTE_BIN}` names the tier for code work. Serialize
|
|
908
|
+
only a real dependency - one slice's output is another's input, or two slices
|
|
909
|
+
edit one file - and say which. Never wait idle on a spawned slice while
|
|
910
|
+
independent work remains. Off: `orchestrate-off`.
|
|
911
|
+
|
|
838
912
|
**Kill switches:** each one removes its own rule from this text, not just the
|
|
839
913
|
status mark. `~/.config/tezgah/`: `exec-mode.off`, `orchestrate-off`,
|
|
840
914
|
`consult-off`, `research-off`, `ponytail-auto.off`, `adhd-off`, `spec-off`,
|
|
@@ -852,13 +926,13 @@ The ponytail intensity level is not a switch: `tezgah-pony lite|full|ultra`.
|
|
|
852
926
|
# time (hooks/tezgah_context.classify_prompt), because a session that never asks
|
|
853
927
|
# a structural or research question should not carry their text. Keys match the
|
|
854
928
|
# CORE_RULES labels in hooks/tezgah_context.py.
|
|
855
|
-
CONDITIONAL_KEYS = ("spec", "consult", "research", "product", "graph")
|
|
929
|
+
CONDITIONAL_KEYS = ("spec", "consult", "research", "product", "graph", "fanout")
|
|
856
930
|
|
|
857
931
|
# The always-on replacement for the conditional paragraphs: one line each so a
|
|
858
932
|
# host without a per-turn hook still knows the rule exists and where the full
|
|
859
933
|
# text lives.
|
|
860
934
|
POINTERS = """
|
|
861
|
-
**On-demand rules (armed when the task class matches; full text in the `tezgah-contract` skill).** Spec-first for an underspecified or quality-only ask. A second opinion before a call that is hard to reverse or that one model would answer with unearned confidence. OpenResearch routing for research. Product analysis is a five-axis evidence task - value, usability (the running app, not the source), feasibility (a cited `path:line`), competition, and keep/fix/cut/bet triage - with one named evidence class per finding. The code graph for "who calls X" and "what breaks if Z changes". The design floor a UI turn is judged against is `.tezgah/design-contract.md`: `tezgah-design derive` writes it from the repository's own tokens, `tezgah-design check` measures a change against it, and the `design-contract` skill owns its shape.
|
|
935
|
+
**On-demand rules (armed when the task class matches; full text in the `tezgah-contract` skill).** Spec-first for an underspecified or quality-only ask. A second opinion before a call that is hard to reverse or that one model would answer with unearned confidence. OpenResearch routing for research. Product analysis is a five-axis evidence task - value, usability (the running app, not the source), feasibility (a cited `path:line`), competition, and keep/fix/cut/bet triage - with one named evidence class per finding. The code graph for "who calls X" and "what breaks if Z changes". Two or more independent items fan out to parallel subagents in one message, one git worktree per writing slice. The design floor a UI turn is judged against is `.tezgah/design-contract.md`: `tezgah-design derive` writes it from the repository's own tokens, `tezgah-design check` measures a change against it, and the `design-contract` skill owns its shape.
|
|
862
936
|
"""
|
|
863
937
|
|
|
864
938
|
# The compact per-turn form, inside the <harness-reminder> envelope the hosts
|