@r1z4x/tezgah 1.3.0 → 1.5.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 +103 -1
- package/MANIFEST +3 -0
- package/agents/tezgah-reviewer.md +19 -10
- package/bin/tezgah-docs +3 -3
- package/bin/tezgah-doctor +75 -18
- package/bin/tezgah-route +5 -2
- package/bin/tezgah-setup +30 -18
- package/bin/tezgah-status +3 -1
- package/bin/tezgah-task +130 -37
- package/bin/tezgah-taste +74 -55
- package/bin/tezgah-triage +12 -7
- package/docs/architecture.md +4 -4
- package/docs/clarity-baseline.json +9 -9
- package/docs/contract.md +22 -12
- package/docs/environment.md +4 -1
- package/docs/evidence.md +71 -34
- package/docs/gate.md +21 -21
- package/docs/glossary.md +5 -5
- package/docs/hosts.md +8 -8
- package/docs/judge.md +93 -27
- package/docs/operations.md +69 -30
- package/docs/skills.md +2 -2
- package/docs/status-line.md +7 -7
- package/docs/vision.md +10 -5
- package/hooks/tezgah_agents.py +22 -11
- package/hooks/tezgah_attest.py +15 -22
- package/hooks/tezgah_context.py +84 -124
- package/hooks/tezgah_gate.py +26 -23
- package/hooks/tezgah_guard.py +9 -11
- package/hooks/tezgah_integrity.py +77 -178
- package/hooks/tezgah_judge.py +270 -122
- package/hooks/tezgah_lessons.py +18 -63
- package/hooks/tezgah_paths.py +183 -55
- package/hooks/tezgah_policy.py +35 -8
- package/hooks/tezgah_replay.py +57 -33
- package/hooks/tezgah_shapes.py +4 -4
- package/hooks/tezgah_skill_pick.py +6 -35
- package/hooks/tezgah_snapshot.py +25 -46
- package/hooks/tezgah_store.py +1268 -0
- package/hooks/tezgah_task.py +146 -40
- package/hooks/tezgah_taste.py +21 -18
- package/hooks/tezgah_taste_ledger.py +63 -88
- package/hooks/tezgah_update.py +6 -26
- package/hosts/codex/hook.py +5 -0
- package/hosts/cursor/hook.py +19 -31
- package/hosts/opencode/plugins/tezgah.js +309 -108
- package/package.json +1 -1
- package/skills/harness/SKILL.md +4 -0
- package/skills/plan-add/SKILL.md +23 -7
- package/skills/plan-add/render_table.py +3 -2
- package/skills/plan-status/SKILL.md +12 -4
- package/skills/plan-sync/SKILL.md +19 -9
- package/skills/tezgah-contract/SKILL.md +25 -12
package/hooks/tezgah_update.py
CHANGED
|
@@ -22,6 +22,7 @@ import sys
|
|
|
22
22
|
import time
|
|
23
23
|
|
|
24
24
|
import tezgah_paths as tp
|
|
25
|
+
import tezgah_store
|
|
25
26
|
|
|
26
27
|
REPO = os.environ.get("TEZGAH_REPO") or "r1z4x/tezgah"
|
|
27
28
|
CHECK_EVERY = 24 * 3600
|
|
@@ -30,10 +31,6 @@ SEMVER = re.compile(r"^v?(\d+)\.(\d+)\.(\d+)$")
|
|
|
30
31
|
ARROW = "\u2191" # ↑: East Asian width Ambiguous, one cell, never an emoji
|
|
31
32
|
|
|
32
33
|
|
|
33
|
-
def cache_path():
|
|
34
|
-
return os.path.join(tp.cache_dir(), "update.json")
|
|
35
|
-
|
|
36
|
-
|
|
37
34
|
def endpoint():
|
|
38
35
|
"""Where the newest release is read, repointed by TEZGAH_UPDATE_URL (tests).
|
|
39
36
|
|
|
@@ -73,32 +70,15 @@ def disabled():
|
|
|
73
70
|
|
|
74
71
|
|
|
75
72
|
def read_cache():
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return data if isinstance(data, dict) else {}
|
|
80
|
-
except Exception:
|
|
81
|
-
return {}
|
|
73
|
+
"""The `update_check` store's one document, `{checked, latest}`, or {}."""
|
|
74
|
+
data = tezgah_store.doc("update_check", "")
|
|
75
|
+
return data if isinstance(data, dict) else {}
|
|
82
76
|
|
|
83
77
|
|
|
84
78
|
def write_cache(data):
|
|
85
|
-
"""Write the cache whole,
|
|
79
|
+
"""Write the cache whole, in one statement, so a redraw reading it
|
|
86
80
|
mid-write sees the old answer or the new one and never half of either."""
|
|
87
|
-
|
|
88
|
-
tmp = "%s.%d.tmp" % (path, os.getpid())
|
|
89
|
-
try:
|
|
90
|
-
os.makedirs(os.path.dirname(path), mode=0o700, exist_ok=True)
|
|
91
|
-
fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
|
|
92
|
-
with os.fdopen(fd, "w", encoding="utf-8") as fh:
|
|
93
|
-
json.dump(data, fh)
|
|
94
|
-
os.replace(tmp, path)
|
|
95
|
-
return True
|
|
96
|
-
except OSError:
|
|
97
|
-
try:
|
|
98
|
-
os.remove(tmp)
|
|
99
|
-
except OSError:
|
|
100
|
-
pass
|
|
101
|
-
return False
|
|
81
|
+
return tezgah_store.put_doc("update_check", "", data)
|
|
102
82
|
|
|
103
83
|
|
|
104
84
|
# Every 0.x ever published, retired by 1.0.0: npm served 0.17.0 through 0.32.0
|
package/hosts/codex/hook.py
CHANGED
|
@@ -158,6 +158,11 @@ def main():
|
|
|
158
158
|
event = payload.get("hook_event_name") or "SessionStart"
|
|
159
159
|
cwd = payload.get("cwd") or os.getcwd()
|
|
160
160
|
session_id = payload.get("session_id")
|
|
161
|
+
# codex hands its hooks its own env, which carries no codex marker (only the
|
|
162
|
+
# shell tool gets CODEX_THREAD_ID), so the judge seam's session_cli() could
|
|
163
|
+
# not tell this is a codex session without it.
|
|
164
|
+
if session_id:
|
|
165
|
+
os.environ.setdefault("CODEX_THREAD_ID", str(session_id))
|
|
161
166
|
safe(session_id, bind_session, session_id) # the switch latch (tezgah_paths.off)
|
|
162
167
|
if event == "SessionStart":
|
|
163
168
|
safe(session_id, attest_session, "codex", session_id, cwd)
|
package/hosts/cursor/hook.py
CHANGED
|
@@ -52,7 +52,8 @@ try:
|
|
|
52
52
|
from tezgah_integrity import ( # noqa: E402
|
|
53
53
|
SUBAGENT_CHANNEL, bind_session, note, note_tool, ran_nothing, report_bytes,
|
|
54
54
|
stop_reason)
|
|
55
|
-
from tezgah_paths import
|
|
55
|
+
from tezgah_paths import off # noqa: E402
|
|
56
|
+
import tezgah_store # noqa: E402
|
|
56
57
|
from tezgah_taste import write_note # noqa: E402
|
|
57
58
|
from tezgah_untrusted import marks # noqa: E402
|
|
58
59
|
except Exception as exc:
|
|
@@ -215,25 +216,21 @@ def first_time(session_id, tag):
|
|
|
215
216
|
"""True the first time a tag is seen for a session; fail-open when unwritable."""
|
|
216
217
|
if not session_id:
|
|
217
218
|
return True
|
|
218
|
-
mark = os.path.join(cache_dir(), "reinforced", slug(str(session_id)), tag)
|
|
219
|
-
if os.path.exists(mark):
|
|
220
|
-
return False
|
|
221
219
|
try:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
pass
|
|
226
|
-
return True
|
|
220
|
+
return tezgah_store.mark("cursor_reinforced", "%s/%s" % (slug(str(session_id)), tag))
|
|
221
|
+
except tezgah_store.ERRORS:
|
|
222
|
+
return True
|
|
227
223
|
|
|
228
224
|
|
|
229
|
-
def
|
|
230
|
-
"""
|
|
225
|
+
def answer_key(session_id):
|
|
226
|
+
"""The key of the last assistant text of a conversation in the
|
|
227
|
+
`cursor_answer` store.
|
|
231
228
|
|
|
232
229
|
Cursor's `stop` payload carries only `status`/`loop_count`, so the reply the
|
|
233
230
|
Stop rule has to read arrives on the earlier `afterAgentResponse`, which
|
|
234
|
-
documents `{"text": "<assistant final text>"}`. One
|
|
235
|
-
overwritten."""
|
|
236
|
-
return
|
|
231
|
+
documents `{"text": "<assistant final text>"}`. One document per
|
|
232
|
+
conversation, overwritten."""
|
|
233
|
+
return slug(str(session_id or "nosession"))
|
|
237
234
|
|
|
238
235
|
|
|
239
236
|
# A long reply is kept by its two ends rather than by one slice: the Stop rule's
|
|
@@ -255,29 +252,20 @@ def remember_answer(session_id, text):
|
|
|
255
252
|
count of what was dropped, then `ANSWER_TAIL` characters of its tail - so
|
|
256
253
|
both the opener the Stop rule reads and the closing marker it must not lose
|
|
257
254
|
survive the round trip. A reply with no `conversation_id` is not stored; a
|
|
258
|
-
|
|
255
|
+
store that cannot be written is not fatal."""
|
|
259
256
|
if not session_id or not text:
|
|
260
257
|
return
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
text =
|
|
265
|
-
|
|
266
|
-
if dropped > 0:
|
|
267
|
-
text = text[:ANSWER_HEAD] + ANSWER_CUT % dropped + text[-ANSWER_TAIL:]
|
|
268
|
-
with open(path, "w", encoding="utf-8") as fh:
|
|
269
|
-
fh.write(text)
|
|
270
|
-
except OSError:
|
|
271
|
-
pass
|
|
258
|
+
text = str(text)
|
|
259
|
+
dropped = len(text) - ANSWER_HEAD - ANSWER_TAIL
|
|
260
|
+
if dropped > 0:
|
|
261
|
+
text = text[:ANSWER_HEAD] + ANSWER_CUT % dropped + text[-ANSWER_TAIL:]
|
|
262
|
+
tezgah_store.put_doc("cursor_answer", answer_key(session_id), text)
|
|
272
263
|
|
|
273
264
|
|
|
274
265
|
def last_answer(session_id):
|
|
275
266
|
"""The reply remembered for this conversation, or ""."""
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return fh.read()
|
|
279
|
-
except OSError:
|
|
280
|
-
return ""
|
|
267
|
+
text = tezgah_store.doc("cursor_answer", answer_key(session_id))
|
|
268
|
+
return text if isinstance(text, str) else ""
|
|
281
269
|
|
|
282
270
|
|
|
283
271
|
def dispatch(payload):
|