@r1z4x/tezgah 1.11.0 → 1.14.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 +507 -1
- package/MANIFEST +40 -0
- package/NOTICE +152 -0
- package/README.de.md +41 -20
- package/README.es.md +39 -19
- package/README.fr.md +41 -20
- package/README.md +18 -10
- package/README.tr.md +39 -21
- package/bin/codegen +9 -2
- package/bin/consult +6 -0
- package/bin/tezgah-adhd +3 -0
- package/bin/tezgah-agents +3 -0
- package/bin/tezgah-capture +3 -0
- package/bin/tezgah-design +329 -54
- package/bin/tezgah-docs +158 -42
- package/bin/tezgah-dsh +1 -1
- package/bin/tezgah-gate +7 -2
- package/bin/tezgah-import-ai-research +16 -4
- package/bin/tezgah-index +3 -0
- package/bin/tezgah-lessons +13 -4
- package/bin/tezgah-mcp +4 -0
- package/bin/tezgah-migrate +3 -0
- package/bin/tezgah-ocr-review +2 -1
- package/bin/tezgah-optimize +4 -1
- package/bin/tezgah-pony +3 -0
- package/bin/tezgah-research +51 -10
- package/bin/tezgah-rollback +3 -0
- package/bin/tezgah-route +1 -1
- package/bin/tezgah-setup +1741 -95
- package/bin/tezgah-status +11 -1
- package/bin/tezgah-task +58 -17
- package/bin/tezgah.js +10 -3
- package/commands/adhd.md +1 -1
- package/docs/README.md +19 -8
- package/docs/architecture.md +2 -2
- package/docs/citations-baseline.json +11 -17
- package/docs/clarity-baseline.json +12 -6
- package/docs/contract.md +83 -34
- package/docs/environment.md +59 -24
- package/docs/evidence.md +128 -25
- package/docs/gate.md +77 -23
- package/docs/glossary.md +1 -1
- package/docs/hosts.md +343 -48
- package/docs/layers.md +2 -2
- package/docs/models.md +6 -0
- package/docs/operations.md +83 -29
- package/docs/research.md +37 -11
- package/docs/skills.md +31 -8
- package/docs/status-line.md +39 -23
- package/docs/testing.md +50 -17
- package/docs/vision.md +6 -2
- package/hooks/hooks.json +1 -1
- package/hooks/lang/en.json +16 -2
- package/hooks/lang/tr.json +14 -2
- package/hooks/projects-posttooluse.py +11 -3
- package/hooks/projects-stop.py +3 -2
- package/hooks/tezgah_agents.py +27 -3
- package/hooks/tezgah_attest.py +79 -10
- package/hooks/tezgah_context.py +61 -31
- package/hooks/tezgah_gate.py +480 -126
- package/hooks/tezgah_integrity.py +577 -133
- package/hooks/tezgah_lessons.py +5 -2
- package/hooks/tezgah_paths.py +60 -8
- package/hooks/tezgah_policy.py +48 -18
- package/hooks/tezgah_replay.py +8 -2
- package/hooks/tezgah_research.py +167 -24
- package/hooks/tezgah_skill_pick.py +1 -1
- package/hooks/tezgah_snapshot.py +6 -3
- package/hooks/tezgah_stack.py +236 -1
- package/hooks/tezgah_stopspec.py +2 -1
- package/hooks/tezgah_task.py +2 -1
- package/hosts/amp/hook.py +217 -0
- package/hosts/amp/tezgah-plugin.ts.in +396 -0
- package/hosts/antigravity/hook.py +255 -0
- package/hosts/codex/hook.py +3 -2
- package/hosts/commandcode/hook.py +239 -0
- package/hosts/copilot/hook.py +284 -0
- package/hosts/cursor/hook.py +4 -2
- package/hosts/droid/hook.py +259 -0
- package/hosts/dsh/hooks.json +1 -1
- package/hosts/gemini/hook.py +213 -0
- package/hosts/goose/hook.py +145 -0
- package/hosts/omp/hook.py +5 -2
- package/hosts/omp/tezgah-hook.ts.in +157 -42
- package/hosts/opencode/plugins/tezgah.js +209 -63
- package/hosts/qwen/hook.py +206 -0
- package/output-styles/tezgah.md +11 -6
- package/package.json +6 -3
- package/packaging/build.sh +4 -4
- package/packaging/install.sh +11 -0
- package/packaging/upgrade.sh +3 -0
- package/skills/ai-research/LICENSE +21 -0
- package/skills/design-contract/SKILL.md +27 -6
- package/skills/design-library/LICENSE +21 -0
- package/skills/harness/SKILL.md +2 -0
- package/skills/i-have-adhd/SKILL.md +10 -1
- package/skills/plan-add/SKILL.md +5 -1
- package/skills/plan-add/render_table.py +7 -0
- package/skills/pm-frameworks/LICENSE +21 -0
- package/skills/research/SKILL.md +22 -9
- package/skills/rl-env/LICENSE +201 -0
- package/skills/tezgah-contract/SKILL.md +21 -8
- package/statusline.py +20 -9
- package/third_party/open-code-review/LICENSE +201 -0
- package/third_party/open-code-review/README.md +15 -0
- package/third_party/open-code-review/prompts/main_task_system.md +26 -0
- package/third_party/open-code-review/prompts/review_filter_task_system.md +13 -0
- package/workflows/graph-map.js +14 -5
- package/workflows/graph-review.js +14 -2
package/hooks/tezgah_attest.py
CHANGED
|
@@ -11,8 +11,9 @@ One store, not two: the install record is the per-artifact sha file the contract
|
|
|
11
11
|
artifacts already use (`contract.sha256`, `bin/tezgah-setup::record_contract`),
|
|
12
12
|
one line per entry under a `hook:<host>:<entry>` key. An entry is one hook event's
|
|
13
13
|
tezgah-owned groups (Claude, dsh, Codex, Cursor - so a removed PreToolUse is named
|
|
14
|
-
while PostToolUse stays), opencode's plugin links, and nothing for omp
|
|
15
|
-
is compared to a fresh render, the way `rules_current`
|
|
14
|
+
while PostToolUse stays), opencode's plugin links, and nothing for omp and Amp:
|
|
15
|
+
their bridge or plugin is compared to a fresh render, the way `rules_current`
|
|
16
|
+
compares a rules file. The
|
|
16
17
|
code an entry runs is hashed only on a packaged install (a tree with no `.git`):
|
|
17
18
|
a checkout install points into a tree its owner edits on purpose. A hook file
|
|
18
19
|
any user can write (0666) is drift too; group write is left alone, because a
|
|
@@ -43,10 +44,22 @@ CODE_TARGETS = {
|
|
|
43
44
|
"cursor": (os.path.join("hosts", "cursor", "hook.py"),),
|
|
44
45
|
"opencode": (os.path.join("hosts", "opencode", "plugins", "tezgah.js"),),
|
|
45
46
|
"omp": (os.path.join("hosts", "omp", "hook.py"),),
|
|
47
|
+
"qwen": (os.path.join("hosts", "qwen", "hook.py"),),
|
|
48
|
+
"droid": (os.path.join("hosts", "droid", "hook.py"),),
|
|
49
|
+
"commandcode": (os.path.join("hosts", "commandcode", "hook.py"),),
|
|
50
|
+
"copilot": (os.path.join("hosts", "copilot", "hook.py"),),
|
|
51
|
+
"gemini": (os.path.join("hosts", "gemini", "hook.py"),),
|
|
52
|
+
"antigravity": (os.path.join("hosts", "antigravity", "hook.py"),),
|
|
53
|
+
"goose": (os.path.join("hosts", "goose", "hook.py"),),
|
|
54
|
+
"amp": (os.path.join("hosts", "amp", "hook.py"),
|
|
55
|
+
os.path.join("hosts", "amp", "tezgah-plugin.ts.in")),
|
|
46
56
|
}
|
|
47
57
|
OPENCODE_DIRS = ("plugins", "plugin")
|
|
48
58
|
OMP_BRIDGE = os.path.join(tp.HOST_DIRS["omp"], "agent", "hooks", "pre",
|
|
49
59
|
"tezgah-hook.ts")
|
|
60
|
+
# The first line of every Amp plugin tezgah renders: an uninstall removes a
|
|
61
|
+
# file at AMP_PLUGIN only when it equals a fresh render or starts with this.
|
|
62
|
+
AMP_HEADER = "// tezgah: Amp plugin rendered by tezgah-setup"
|
|
50
63
|
|
|
51
64
|
|
|
52
65
|
def sha(text):
|
|
@@ -82,10 +95,15 @@ def own_groups(data, own=False):
|
|
|
82
95
|
return out
|
|
83
96
|
|
|
84
97
|
|
|
85
|
-
def _manifest(path, own):
|
|
98
|
+
def _manifest(path, own, bare=False, key=None):
|
|
99
|
+
"""`bare`: the file is keyed by event at the top, as Droid's hooks.json is;
|
|
100
|
+
`key`: the events sit under that top-level name, as Antigravity's do."""
|
|
86
101
|
try:
|
|
87
102
|
with open(path, encoding="utf-8") as fh:
|
|
88
|
-
|
|
103
|
+
data = json.load(fh)
|
|
104
|
+
if key:
|
|
105
|
+
data, bare = (data.get(key) if isinstance(data, dict) else None) or {}, True
|
|
106
|
+
return own_groups({"hooks": data} if bare else data, own)
|
|
89
107
|
except (OSError, ValueError):
|
|
90
108
|
return None
|
|
91
109
|
|
|
@@ -97,11 +115,31 @@ def omp_bridge(root=ROOT):
|
|
|
97
115
|
with open(os.path.join(root, "hosts", "omp", "tezgah-hook.ts.in"),
|
|
98
116
|
encoding="utf-8") as fh:
|
|
99
117
|
return fh.read().replace(
|
|
100
|
-
"@HOOK@", os.path.join(root, "hosts", "omp", "hook.py"))
|
|
118
|
+
"@HOOK@", js_string(os.path.join(root, "hosts", "omp", "hook.py")))
|
|
101
119
|
except OSError:
|
|
102
120
|
return ""
|
|
103
121
|
|
|
104
122
|
|
|
123
|
+
def amp_plugin(root=ROOT):
|
|
124
|
+
"""The Amp plugin as an install writes it: the template with this tree's
|
|
125
|
+
hook path, or "" when the template is unreadable. The install, the report
|
|
126
|
+
row, the live probe and the attestation all compare against this."""
|
|
127
|
+
try:
|
|
128
|
+
with open(os.path.join(root, "hosts", "amp", "tezgah-plugin.ts.in"),
|
|
129
|
+
encoding="utf-8") as fh:
|
|
130
|
+
return fh.read().replace(
|
|
131
|
+
"@HOOK@", js_string(os.path.join(root, "hosts", "amp", "hook.py")))
|
|
132
|
+
except OSError:
|
|
133
|
+
return ""
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def js_string(path):
|
|
137
|
+
"""`path` as the inside of a JS double-quoted string: a Windows checkout's
|
|
138
|
+
`\\t`, `\\u` or `\\x` would otherwise become a control character or a
|
|
139
|
+
SyntaxError, and the bridge would not load (review of plan 140)."""
|
|
140
|
+
return json.dumps(path)[1:-1]
|
|
141
|
+
|
|
142
|
+
|
|
105
143
|
def registration(host, root=ROOT):
|
|
106
144
|
"""({entry: text}, [files]) for the tezgah-owned hook entries this host has
|
|
107
145
|
on disk; the dict is None when the host's file cannot be read."""
|
|
@@ -109,9 +147,30 @@ def registration(host, root=ROOT):
|
|
|
109
147
|
path = os.path.join(root, *(("hooks",) if host == "claude"
|
|
110
148
|
else ("hosts", "dsh")), "hooks.json")
|
|
111
149
|
return _manifest(path, own=True), [path]
|
|
112
|
-
if host in ("codex", "cursor"):
|
|
113
|
-
|
|
114
|
-
|
|
150
|
+
if host in ("codex", "cursor", "qwen", "droid", "commandcode"):
|
|
151
|
+
# Qwen's and Command Code's hooks live in a shared settings.json
|
|
152
|
+
path = os.path.join(tp.HOST_DIRS[host], "settings.json"
|
|
153
|
+
if host in ("qwen", "commandcode") else "hooks.json")
|
|
154
|
+
return _manifest(path, own=False, bare=host == "droid"), [path]
|
|
155
|
+
if host == "copilot":
|
|
156
|
+
# tezgah's own file in Copilot's user hooks dir: every entry is ours
|
|
157
|
+
path = os.path.join(tp.HOST_DIRS["copilot"], "hooks", "tezgah.json")
|
|
158
|
+
return _manifest(path, own=True), [path]
|
|
159
|
+
if host == "gemini":
|
|
160
|
+
# tezgah's own extension: every entry in its hooks.json is ours
|
|
161
|
+
path = os.path.join(tp.HOST_DIRS["gemini"], "extensions", "tezgah", "hooks",
|
|
162
|
+
"hooks.json")
|
|
163
|
+
return _manifest(path, own=True), [path]
|
|
164
|
+
if host == "antigravity":
|
|
165
|
+
# the shared ~/.gemini/config/hooks.json: every entry under its
|
|
166
|
+
# top-level `tezgah` name is ours
|
|
167
|
+
path = os.path.join(os.path.dirname(tp.HOST_DIRS["antigravity"]), "config",
|
|
168
|
+
"hooks.json")
|
|
169
|
+
return _manifest(path, own=True, key="tezgah"), [path]
|
|
170
|
+
if host == "goose":
|
|
171
|
+
# tezgah's own plugin: every entry in its hooks.json is ours
|
|
172
|
+
path = os.path.join(tp.GOOSE_PLUGIN, "hooks", "hooks.json")
|
|
173
|
+
return _manifest(path, own=True), [path]
|
|
115
174
|
if host == "opencode":
|
|
116
175
|
links = (os.path.join(tp.HOST_DIRS["opencode"], d, "tezgah.js")
|
|
117
176
|
for d in OPENCODE_DIRS)
|
|
@@ -124,6 +183,12 @@ def registration(host, root=ROOT):
|
|
|
124
183
|
return {"bridge": fh.read()}, [OMP_BRIDGE]
|
|
125
184
|
except OSError:
|
|
126
185
|
return None, [OMP_BRIDGE]
|
|
186
|
+
if host == "amp":
|
|
187
|
+
try:
|
|
188
|
+
with open(tp.AMP_PLUGIN, encoding="utf-8") as fh:
|
|
189
|
+
return {"plugin": fh.read()}, [tp.AMP_PLUGIN]
|
|
190
|
+
except OSError:
|
|
191
|
+
return None, [tp.AMP_PLUGIN]
|
|
127
192
|
return None, []
|
|
128
193
|
|
|
129
194
|
|
|
@@ -148,7 +213,7 @@ def record_texts(host, root=ROOT):
|
|
|
148
213
|
"""{key: text} an install records for one host (`bin/tezgah-setup::record_hooks`)."""
|
|
149
214
|
entries, _files = registration(host, root)
|
|
150
215
|
out = {}
|
|
151
|
-
if host
|
|
216
|
+
if host not in ("omp", "amp"):
|
|
152
217
|
out.update((KEY % (host, name), text)
|
|
153
218
|
for name, text in (entries or {}).items())
|
|
154
219
|
code = code_text(host, root) if packaged(root) else None
|
|
@@ -173,10 +238,14 @@ def drift(host, root=ROOT, shas=None):
|
|
|
173
238
|
if mode & 0o002:
|
|
174
239
|
found.append("%s mode %04o" % (path, mode))
|
|
175
240
|
if entries is None:
|
|
176
|
-
|
|
241
|
+
# a host `registration` does not know names no file: say so, never IndexError
|
|
242
|
+
found.append("%s unreadable or missing" % (files[0] if files else host))
|
|
177
243
|
elif host == "omp":
|
|
178
244
|
if entries["bridge"] != omp_bridge(root):
|
|
179
245
|
found.append("omp bridge %s differs from a fresh render" % OMP_BRIDGE)
|
|
246
|
+
elif host == "amp":
|
|
247
|
+
if entries["plugin"] != amp_plugin(root):
|
|
248
|
+
found.append("amp plugin %s differs from a fresh render" % tp.AMP_PLUGIN)
|
|
180
249
|
else:
|
|
181
250
|
names = set(recorded) - {"code"}
|
|
182
251
|
if not names:
|
package/hooks/tezgah_context.py
CHANGED
|
@@ -335,9 +335,10 @@ def skill_read_kind(tool, inp):
|
|
|
335
335
|
|
|
336
336
|
Only the hosts that can see a read without paying a process per read call
|
|
337
337
|
this (Claude parses its transcript, opencode classifies in-process, omp's
|
|
338
|
-
embedded runner filters before it asks python). On codex,
|
|
339
|
-
|
|
340
|
-
state and the
|
|
338
|
+
embedded runner filters before it asks python). On codex, commandcode,
|
|
339
|
+
gemini, cursor, qwen, droid, copilot, antigravity and dsh a read is not
|
|
340
|
+
observable at that price, so those marks stay at their armed state and the
|
|
341
|
+
legend says so.
|
|
341
342
|
|
|
342
343
|
A read of one of the two marked skills earns its mark (`SKILL_MARKS`); a read
|
|
343
344
|
of any OTHER shipped skill earns `skill:<name>`. That second kind lights
|
|
@@ -1354,11 +1355,17 @@ def session_of(payload):
|
|
|
1354
1355
|
# again on 2026-10-22 (plan 126, second pass): a research ask arms the Research
|
|
1355
1356
|
# and Product paragraphs (6239 B on Claude, S4Remeasure p08) and the skill hint
|
|
1356
1357
|
# that names the research skill (about 330 B with its path) was the block given
|
|
1357
|
-
# up, so the prompt budget moves to 7000 B.
|
|
1358
|
+
# up, so the prompt budget moves to 7000 B. Moved again on 2026-10-28 (plan
|
|
1359
|
+
# 136): the subagent pointer says an owner-only question goes back as
|
|
1360
|
+
# `ESCALATE: owner` (+330 B), the fixture's subagent_start reached 5958 B and
|
|
1361
|
+
# gave up `consult` (205 B), so subagent_start moves to 6100 B. Moved again on
|
|
1362
|
+
# 2026-10-29 (plan 142): the output-shape paragraph says what the host's plan
|
|
1363
|
+
# mode changes (+168 B), and the worktree fixture's session_start gave up its
|
|
1364
|
+
# worktrees line again, so the session budgets move to 16200 B. A
|
|
1358
1365
|
# budget in bytes, not tokens: this file has no tokenizer and a wrong estimate
|
|
1359
1366
|
# would be worse than a bound.
|
|
1360
|
-
CONTEXT_BUDGET = {"session_start":
|
|
1361
|
-
"subagent_start":
|
|
1367
|
+
CONTEXT_BUDGET = {"session_start": 16200, "post_compact": 16200,
|
|
1368
|
+
"subagent_start": 6100, "user_prompt": 7000}
|
|
1362
1369
|
DEFAULT_BUDGET = 13000
|
|
1363
1370
|
# The blocks in the order they are given up when the budget is exceeded, lowest
|
|
1364
1371
|
# value first: text another surface already carries (the plan table lives in the
|
|
@@ -1520,12 +1527,13 @@ GATE_MIN_CALLS = 3
|
|
|
1520
1527
|
# The tool names whose every call leaves a PreToolUse row (`began`,
|
|
1521
1528
|
# tezgah_gate.decision's write and shell branch) and every refused one a `deny`:
|
|
1522
1529
|
# Claude's write and shell tools, omp's `bash`/`edit`/`write` and Codex's
|
|
1523
|
-
# `exec_command`/`apply_patch`. A
|
|
1530
|
+
# `exec_command`/`apply_patch`, Droid's `Execute`/`Create`/`ApplyPatch`. A
|
|
1524
1531
|
# tool the gate may pass without a row (Read, Grep, Task, WebFetch, an MCP
|
|
1525
1532
|
# call) is not counted, so its absence from the ledger is never read as a
|
|
1526
1533
|
# disarmed gate.
|
|
1527
1534
|
GATED_TOOLS = re.compile(r"(?i)^(?:bash|powershell|pwsh|edit|write|multiedit|"
|
|
1528
|
-
r"notebookedit|exec_command|shell|apply_patch
|
|
1535
|
+
r"notebookedit|exec_command|shell|apply_patch|"
|
|
1536
|
+
r"execute|create|applypatch)$")
|
|
1529
1537
|
GATE_INACTIVE = (
|
|
1530
1538
|
"tezgah gate inactive on this host: %d gated tool call(s) since this "
|
|
1531
1539
|
"session's first turn and not one row from the gate, so the shortcut "
|
|
@@ -1651,7 +1659,8 @@ def context_nudge(payload, session_id):
|
|
|
1651
1659
|
# The ledger kinds a hook other than the tool hooks writes: the prompt hook's
|
|
1652
1660
|
# turn marker, judge row, lesson rows and disarm row, the Stop hook's claim,
|
|
1653
1661
|
# refusal, after_block and shape rows and the stop_spec rows of plan 063's shadow
|
|
1654
|
-
# (old ledgers only), the subagent-end hook's subagent_end row,
|
|
1662
|
+
# (old ledgers only), the subagent-end hook's subagent_end row, Droid's Stop
|
|
1663
|
+
# `reply_unread` row (a turn end whose reply the transcript did not yield),
|
|
1655
1664
|
# compaction, the subagent mark and the `subagent_start` lineage row (both from
|
|
1656
1665
|
# SubagentStart), the omp fork's `forked` row, the guard's crash
|
|
1657
1666
|
# row (any hook), the damage row any reader writes and the session start's
|
|
@@ -1664,7 +1673,7 @@ NOT_TOOL_HOOK = frozenset((b"turn", b"judge", b"claim", b"refusal",
|
|
|
1664
1673
|
b"after_block", b"subagent_end", b"stop_spec",
|
|
1665
1674
|
b"shape", b"compact", b"orch", b"crash", b"route",
|
|
1666
1675
|
b"spawned", b"forked", b"lesson", b"disarm", b"ledger_damage",
|
|
1667
|
-
b"attest", b"subagent_start"))
|
|
1676
|
+
b"attest", b"subagent_start", b"reply_unread"))
|
|
1668
1677
|
# The rows PostToolUse writes (`note_tool`): they prove the host ran the call,
|
|
1669
1678
|
# not that the gate saw it - a PostToolUse hook keeps writing them while a
|
|
1670
1679
|
# broken PreToolUse hook lets every call through - so they are no proof either.
|
|
@@ -2087,7 +2096,8 @@ def context_for(event, cwd, payload=None, with_core=True):
|
|
|
2087
2096
|
# The switch latch: every off() below answers for this session, and a
|
|
2088
2097
|
# prompt's turn marker (with its `authorized` row) is written first, so a
|
|
2089
2098
|
# switch the user's prompt names is honored from this very turn.
|
|
2090
|
-
bind_session(session_of(payload)
|
|
2099
|
+
bind_session(session_of(payload),
|
|
2100
|
+
payload.get("parent") if isinstance(payload, dict) else None)
|
|
2091
2101
|
if event == "user_prompt":
|
|
2092
2102
|
# One marker per user turn, written before the reminder check: the loop
|
|
2093
2103
|
# guard counts an identical call's failures in the current turn only, so
|
|
@@ -2095,13 +2105,16 @@ def context_for(event, cwd, payload=None, with_core=True):
|
|
|
2095
2105
|
# guard state rather than part of the reminder. Only a hash of the prompt
|
|
2096
2106
|
# is stored - note_turn keys the row on it so one submission cannot write
|
|
2097
2107
|
# two markers and hide the failures the guard had just counted.
|
|
2098
|
-
note_turn(session_of(payload), prompt_text(payload), workspace=root_for(cwd)
|
|
2108
|
+
note_turn(session_of(payload), prompt_text(payload), workspace=root_for(cwd),
|
|
2109
|
+
subagent=isinstance(payload, dict) and bool(payload.get("subagent")))
|
|
2099
2110
|
core, disabled = core_for(cwd)
|
|
2100
2111
|
# A disabled rule is also removed from the on-demand skill's reach, because
|
|
2101
2112
|
# the skill is loaded separately and would otherwise re-enable it.
|
|
2113
|
+
# Claude's `tezgah` output style is a static copy of the core that no switch
|
|
2114
|
+
# re-renders, so the note names it beside the skill (OQ-12).
|
|
2102
2115
|
off_note = ("Kill switches active this session: %s. Those rules are OFF; "
|
|
2103
|
-
"ignore the matching section in the `tezgah-contract` skill
|
|
2104
|
-
% ", ".join(disabled)) if disabled else ""
|
|
2116
|
+
"ignore the matching section in the `tezgah-contract` skill and "
|
|
2117
|
+
"the `tezgah` output style." % ", ".join(disabled)) if disabled else ""
|
|
2105
2118
|
if event == "user_prompt":
|
|
2106
2119
|
prompt = prompt_text(payload)
|
|
2107
2120
|
session_id = session_of(payload)
|
|
@@ -2110,24 +2123,24 @@ def context_for(event, cwd, payload=None, with_core=True):
|
|
|
2110
2123
|
host = (payload or {}).get("host") if isinstance(payload, dict) else None
|
|
2111
2124
|
tezgah_taste.note_prompt(session_id, prompt, cwd,
|
|
2112
2125
|
host=host if isinstance(host, str) else None)
|
|
2113
|
-
#
|
|
2114
|
-
#
|
|
2115
|
-
#
|
|
2126
|
+
# The gate notice: never in DROP_ORDER, so no budget gives it up, and
|
|
2127
|
+
# `reminder-off` keeps it - it says the rules below are not being
|
|
2128
|
+
# enforced. Reading it also writes the status mark.
|
|
2116
2129
|
gate = gate_inactive(session_id, payload)
|
|
2117
|
-
# The user's grants and lifts (`user_grants`)
|
|
2118
|
-
#
|
|
2119
|
-
#
|
|
2120
|
-
# user's grant (omp's bridge sends `subagent`).
|
|
2130
|
+
# The user's grants and lifts (`user_grants`): the Stop rule reads them
|
|
2131
|
+
# whatever this turn's text shows. A subagent's prompt is its parent's
|
|
2132
|
+
# brief, never the user's grant (omp's bridge sends `subagent`).
|
|
2121
2133
|
for clause, target in ([] if isinstance(payload, dict) and payload.get("subagent")
|
|
2122
2134
|
else user_grants(prompt)):
|
|
2123
2135
|
note(session_id, GRANT_KIND, clause, target=target)
|
|
2124
2136
|
# per-turn nudge: openers decay over long sessions. Kept short because
|
|
2125
2137
|
# it is paid every turn, and on Claude the output style already carries
|
|
2126
2138
|
# the same rules on every response. The conditional rules ride along
|
|
2127
|
-
# only on the turn whose prompt matches their task class.
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
parts = [("reminder"
|
|
2139
|
+
# only on the turn whose prompt matches their task class. `reminder-off`
|
|
2140
|
+
# mutes this one line: every other part has its own switch or none
|
|
2141
|
+
# (OQ-10), and the gate notice must never be given up.
|
|
2142
|
+
parts = ([] if off("reminder-off") else
|
|
2143
|
+
[("reminder", render(prompt_reminder(dropped_switches(cwd))))])
|
|
2131
2144
|
if gate:
|
|
2132
2145
|
parts.append(("gate", gate))
|
|
2133
2146
|
# The previous turn's stamp, read before the armed paragraphs: they are
|
|
@@ -2250,8 +2263,8 @@ def context_for(event, cwd, payload=None, with_core=True):
|
|
|
2250
2263
|
if stale:
|
|
2251
2264
|
parts.append(("index", stale))
|
|
2252
2265
|
if disabled:
|
|
2253
|
-
parts.append(("offnote", "(off this session: %s
|
|
2254
|
-
% ", ".join(disabled)))
|
|
2266
|
+
parts.append(("offnote", "(off this session: %s; ignore their sections "
|
|
2267
|
+
"in the `tezgah` output style)" % ", ".join(disabled)))
|
|
2255
2268
|
text = budgeted(event, parts)
|
|
2256
2269
|
# latched only when the budget kept it, so a dropped line comes back
|
|
2257
2270
|
if heavy and heavy in text:
|
|
@@ -2450,7 +2463,12 @@ def context_for(event, cwd, payload=None, with_core=True):
|
|
|
2450
2463
|
"(each tagged DERIVED, you concluded it, or RETRIEVED, "
|
|
2451
2464
|
"you read it), Evidence (the path:line or command behind "
|
|
2452
2465
|
"each), Confidence, Unresolved, Disconfirming, Conflicts. "
|
|
2453
|
-
"
|
|
2466
|
+
"A question only the owner can answer - choosing a variant "
|
|
2467
|
+
"or option a research, analysis or review recommends; a way "
|
|
2468
|
+
"around a tezgah refusal; deciding about another session's "
|
|
2469
|
+
"work (its line, plan or branch) - is never yours: return "
|
|
2470
|
+
"`ESCALATE: owner` with the options and a recommended "
|
|
2471
|
+
"default. Full rules: the `tezgah-contract` skill."))
|
|
2454
2472
|
else:
|
|
2455
2473
|
parts.append(("pointer", POINTER_LINE))
|
|
2456
2474
|
text = budgeted(event, [(key, render(text.strip())) for key, text in parts])
|
|
@@ -2612,7 +2630,18 @@ def shell_kind(command):
|
|
|
2612
2630
|
could light from `orx` alone and the layer's own commands stayed invisible to
|
|
2613
2631
|
it. The absolute spelling needs no case of its own - this reader keeps
|
|
2614
2632
|
basenames, so `<bin>/tezgah-research` and the bare name arrive as the same
|
|
2615
|
-
word.
|
|
2633
|
+
word.
|
|
2634
|
+
|
|
2635
|
+
A python or node interpreter given a script file (`python3 bin/tezgah-docs
|
|
2636
|
+
q`) runs that script, so the interpreter word is dropped before the line is
|
|
2637
|
+
read; `-c`, `-m` and `-` (stdin) keep it, as their next word is code or a
|
|
2638
|
+
module, not a file. The match stays on one line (spaces and tabs only), and a
|
|
2639
|
+
shell interpreter needs none of this: the wrapper reader already looks past
|
|
2640
|
+
`bash`/`sh`. `shell_programs` itself stays as it is: the gate reads it and
|
|
2641
|
+
must keep the interpreter."""
|
|
2642
|
+
command = re.sub(r"(?<![\w/.$=-])(?:[\w./-]*/)?(?:python[\d.]*|node)"
|
|
2643
|
+
r"(?:[ \t]+-(?![cm]\b)\w[\w-]*)*[ \t]+(?=[^\s-])", "",
|
|
2644
|
+
str(command or ""))
|
|
2616
2645
|
ran = shell_programs(command)
|
|
2617
2646
|
if "consult" in ran:
|
|
2618
2647
|
return "consult"
|
|
@@ -2917,8 +2946,9 @@ colored, and the glyph carries the state on its own where color does not):
|
|
|
2917
2946
|
name\u25cb yellow armed, on demand - not used yet this session
|
|
2918
2947
|
name\u2717 red turned off by a kill switch or a per-repo .no-* mark
|
|
2919
2948
|
pony, adhd, dim this surface cannot report that measure (a skill read
|
|
2920
|
-
needs a process per read to observe on codex,
|
|
2921
|
-
|
|
2949
|
+
needs a process per read to observe on codex, commandcode,
|
|
2950
|
+
gemini, cursor, qwen, droid, copilot, antigravity and dsh,
|
|
2951
|
+
so those two marks state nothing there instead of
|
|
2922
2952
|
claiming the skill was never opened)
|
|
2923
2953
|
dim no state to report: idx n/a or uncomparable, or no blocked
|
|
2924
2954
|
plan
|