@r1z4x/tezgah 1.13.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 +175 -1
- package/MANIFEST +18 -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 +15 -8
- 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 +157 -41
- 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 +12 -6
- package/bin/tezgah-rollback +3 -0
- package/bin/tezgah-route +1 -1
- package/bin/tezgah-setup +596 -157
- package/bin/tezgah-status +11 -1
- package/bin/tezgah-task +3 -0
- 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 +3 -3
- package/docs/contract.md +77 -31
- package/docs/environment.md +58 -24
- package/docs/evidence.md +49 -19
- package/docs/gate.md +71 -21
- package/docs/hosts.md +65 -52
- package/docs/layers.md +1 -1
- package/docs/operations.md +67 -24
- package/docs/research.md +1 -1
- package/docs/skills.md +31 -8
- package/docs/status-line.md +33 -23
- package/docs/testing.md +48 -17
- package/docs/vision.md +6 -2
- package/hooks/lang/en.json +5 -2
- package/hooks/lang/tr.json +3 -2
- package/hooks/projects-posttooluse.py +11 -3
- package/hooks/projects-stop.py +1 -1
- package/hooks/tezgah_context.py +40 -23
- package/hooks/tezgah_gate.py +404 -106
- package/hooks/tezgah_integrity.py +367 -97
- package/hooks/tezgah_lessons.py +5 -2
- package/hooks/tezgah_paths.py +17 -6
- package/hooks/tezgah_policy.py +13 -5
- package/hooks/tezgah_skill_pick.py +1 -1
- package/hooks/tezgah_snapshot.py +6 -3
- package/hooks/tezgah_stopspec.py +2 -1
- package/hosts/amp/hook.py +1 -1
- package/hosts/amp/tezgah-plugin.ts.in +13 -3
- package/hosts/antigravity/hook.py +1 -1
- package/hosts/codex/hook.py +1 -1
- package/hosts/commandcode/hook.py +1 -1
- package/hosts/copilot/hook.py +1 -1
- package/hosts/cursor/hook.py +2 -1
- package/hosts/droid/hook.py +1 -1
- package/hosts/gemini/hook.py +1 -1
- package/hosts/goose/hook.py +1 -1
- package/hosts/omp/hook.py +1 -1
- package/hosts/omp/tezgah-hook.ts.in +127 -35
- package/hosts/opencode/plugins/tezgah.js +209 -63
- package/hosts/qwen/hook.py +1 -1
- package/output-styles/tezgah.md +1 -1
- 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/plan-add/render_table.py +7 -0
- package/skills/pm-frameworks/LICENSE +21 -0
- package/skills/rl-env/LICENSE +201 -0
- package/skills/tezgah-contract/SKILL.md +2 -2
- 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/docs/hosts.md
CHANGED
|
@@ -17,12 +17,12 @@ surface can honestly report this session; the two skill-read marks (`pony`,
|
|
|
17
17
|
|
|
18
18
|
| Host | Hook events it fires | Always-on contract | Per-turn reminder | Status surface, how drawn | Measures |
|
|
19
19
|
|---|---|---|---|---|---|
|
|
20
|
-
| claude | SessionStart, SubagentStart, PostCompact, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, Stop, SubagentStop (record-only, `subagent_end`) - `hooks/hooks.json:2-32` | the managed block in `~/.claude/CLAUDE.md` (`CLAUDE_RULES`), Claude's global memory file; the SessionStart/PostCompact hook rows declare `TEZGAH_CORE_IN_FILE` (`hooks/hooks.json:4,10`) and the hook drops the core when the block is there, so the contract is paid for once (`hooks/projects-auto-init.py::core_is_in_a_file`; the static `output-styles/tezgah.md` mirror (`output-styles/tezgah.md:1`) is the hookless build's copy) | UserPromptSubmit -> `additionalContext` (`hooks/hooks.json:12-14`, `hooks/projects-auto-init.py::main`) | `statusLine` command in `~/.claude/settings.json` (`bin/tezgah-setup::wire_claude_statusline`), ANSI, forwards Orca first (`statusline.py:
|
|
20
|
+
| claude | SessionStart, SubagentStart, PostCompact, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, Stop, SubagentStop (record-only, `subagent_end`) - `hooks/hooks.json:2-32` | the managed block in `~/.claude/CLAUDE.md` (`CLAUDE_RULES`), Claude's global memory file; the SessionStart/PostCompact hook rows declare `TEZGAH_CORE_IN_FILE` (`hooks/hooks.json:4,10`) and the hook drops the core when the block is there, so the contract is paid for once (`hooks/projects-auto-init.py::core_is_in_a_file`; the static `output-styles/tezgah.md` mirror (`output-styles/tezgah.md:1`) is the hookless build's copy) | UserPromptSubmit -> `additionalContext` (`hooks/hooks.json:12-14`, `hooks/projects-auto-init.py::main`) | `statusLine` command in `~/.claude/settings.json` (`bin/tezgah-setup::wire_claude_statusline`), ANSI, forwards Orca first (`orca_out`, `statusline.py:52-60`) | all seven: the transcript parse covers the two skill reads, `graph`, `orch` and the three shell kinds - `consult`, `research` and the judgement callers (`statusline.py::claude_used`, `observable=None` at `statusline.py::observable`) |
|
|
21
21
|
| codex | SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, SubagentStart, PostCompact, Stop (`bin/tezgah-setup::CODEX_HOOK_EVENTS`; named in `hosts/codex/hook.py:6-7`) | hook: the normalized events inject `context_for` (`hosts/codex/hook.py::main`) | UserPromptSubmit -> `additionalContext` (`hosts/codex/hook.py::main`) | no custom footer item (`tui.status_line` is a closed enum) - the line rides `systemMessage`, plain, at SessionStart and Stop (`hosts/codex/hook.py:10-12`, `hosts/codex/hook.py::main`) | five tool-use (`TOOL_USE_MEASURES` at `hosts/codex/hook.py::main`) |
|
|
22
|
-
| cursor | 13 events: sessionStart, preToolUse, postToolUse, postToolUseFailure, subagentStart, subagentStop, beforeSubmitPrompt, stop, beforeMCPExecution, afterShellExecution, afterMCPExecution, afterFileEdit, afterAgentResponse (`bin/tezgah-setup::CURSOR_HOOK_EVENTS`) | hook: sessionStart -> `additional_context` (`hosts/cursor/hook.py::dispatch`) | beforeSubmitPrompt -> `additional_context` plus `{"continue": true}` (`hosts/cursor/hook.py::dispatch`) | `statusLine` in `~/.cursor/cli-config.json` -> `tezgah-statusline --cursor` (`bin/tezgah-setup::install_cursor`), ANSI (`statusline.py
|
|
23
|
-
| opencode | no lifecycle events; plugin hooks: config, permission.ask, tool.execute.before, shell.env, experimental.session.compacting, chat.message, tool.execute.after (`hosts/opencode/plugins/tezgah.js
|
|
24
|
-
| dsh | SessionStart, SubagentStart, UserPromptSubmit, PreToolUse, PostToolUse (`hosts/dsh/hooks.json:2-19`); no Stop rule, because the bridge drops every call's outcome and no check there can show a pass | hook: the same Claude-family scripts, named by `configPath` (`bin/tezgah-setup::dsh_patch_block`) | UserPromptSubmit, through the bridge (`hosts/dsh/hooks.json:9-11`) | web-profile plugin: authenticated `GET /api/tezgah.status` -> `tezgah-status` (`hosts/dsh/statusline/lib/index.js
|
|
25
|
-
| omp | session_start, session_compact, session_switch, turn_end, before_agent_start, tool_call, tool_result, session_stop (`hosts/omp/tezgah-hook.ts.in
|
|
22
|
+
| cursor | 13 events: sessionStart, preToolUse, postToolUse, postToolUseFailure, subagentStart, subagentStop, beforeSubmitPrompt, stop, beforeMCPExecution, afterShellExecution, afterMCPExecution, afterFileEdit, afterAgentResponse (`bin/tezgah-setup::CURSOR_HOOK_EVENTS`) | hook: sessionStart -> `additional_context` (`hosts/cursor/hook.py::dispatch`) | beforeSubmitPrompt -> `additional_context` plus `{"continue": true}` (`hosts/cursor/hook.py::dispatch`) | `statusLine` in `~/.cursor/cli-config.json` -> `tezgah-statusline --cursor` (`bin/tezgah-setup::install_cursor`), ANSI (`statusline.py::color`) | five tool-use (`statusline.py::observable`) |
|
|
23
|
+
| opencode | no lifecycle events; plugin hooks: config, permission.ask, tool.execute.before, event, shell.env, experimental.session.compacting, chat.message, tool.execute.after (the hook object `Tezgah` in `hosts/opencode/plugins/tezgah.js` returns) - `tool.execute.before` carries the whole gate, `event` writes a `spawned` row naming the parent on a child's `session.created`, `tool.execute.after` the channel on the row, the provenance label on the result and the taint notice on the next effect (`untrustedSource` and `labelResult` in the same file) | static only: generated `instructions` files, because there is no session-start injection point (the header comment of `hosts/opencode/plugins/tezgah.js`, `bin/tezgah-setup::opencode_contract_text`) | chat.message pushes a synthetic part (the `chat.message` hook of `Tezgah`) | TUI plugin `tezgah-tui.tsx` declared in `tui.json`, runs `tezgah-status` on the event bus (`TezgahBar` in `hosts/opencode/tui/tezgah-tui.tsx`; wiring `bin/tezgah-setup::install_opencode`) | all seven: skill reads and kinds are classified in process (`classify` in `hosts/opencode/plugins/tezgah.js`) |
|
|
24
|
+
| dsh | SessionStart, SubagentStart, UserPromptSubmit, PreToolUse, PostToolUse (`hosts/dsh/hooks.json:2-19`); no Stop rule, because the bridge drops every call's outcome and no check there can show a pass | hook: the same Claude-family scripts, named by `configPath` (`bin/tezgah-setup::dsh_patch_block`) | UserPromptSubmit, through the bridge (`hosts/dsh/hooks.json:9-11`) | web-profile plugin: authenticated `GET /api/tezgah.status` -> `tezgah-status` (`STATUS_PATH` and `apply` in `hosts/dsh/statusline/lib/index.js`) | five tool-use: the route passes `--observable=consult,research,graph,orch,judge` (`OBSERVABLE` in `hosts/dsh/statusline/lib/index.js`) |
|
|
25
|
+
| omp | 13 events: agent_start, tool_execution_start, tool_execution_end, agent_end, session_shutdown, session_start, session_compact, session_switch, turn_end, before_agent_start, tool_call, tool_result, session_stop (the `pi.on` registrations in `hosts/omp/tezgah-hook.ts.in`); the first five drive the busy spinner, and `agent_end` also records a task subagent's end, since omp fires no `session_stop` for one | both: static `RULES.md` (`bin/tezgah-setup::STATIC_RULES`, `bin/tezgah-setup::rules_block`) and a session payload carrying only what a static file cannot know (`hosts/omp/hook.py::handle`) | before_agent_start returns a hidden message (its `pi.on` handler in `hosts/omp/tezgah-hook.ts.in`) | extension draws `ctx.ui.setWidget` below the editor, `setStatus` as fallback (`draw` in `hosts/omp/tezgah-hook.ts.in`); ANSI line from `hosts/omp/hook.py::status_line` | all seven: the embedded runner filters skill reads before asking python (`skillRead` in `hosts/omp/tezgah-hook.ts.in`) and no `observable` is passed (`hosts/omp/hook.py::status_line`) |
|
|
26
26
|
| qwen | SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, SubagentStart, SubagentStop (record-only, `subagent_end`), Stop (`bin/tezgah-setup::QWEN_HOOK_EVENTS`), all in `~/.qwen/settings.json` beside Orca's rows | the managed block in `~/.qwen/QWEN.md` (`bin/tezgah-setup::STATIC_RULES`); SessionStart drops the core while the block is there (`hosts/qwen/hook.py::rules_carry_core`) | UserPromptSubmit -> `additionalContext` (`hosts/qwen/hook.py::main`) | `ui.statusLine` command in `~/.qwen/settings.json` -> `tezgah-statusline --qwen`, ANSI kept by `respectUserColors` (`bin/tezgah-setup::install_qwen`) | five tool-use (`statusline.py::observable`) |
|
|
27
27
|
| droid | SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, SubagentStop (record-only, `subagent_end`), Stop (`bin/tezgah-setup::DROID_HOOK_EVENTS`), all in `~/.factory/hooks.json` | the managed block in `~/.factory/AGENTS.md` (`bin/tezgah-setup::STATIC_RULES`); SessionStart drops the core while the block is there (`hosts/droid/hook.py::rules_carry_core`) | UserPromptSubmit -> `additionalContext` (`hosts/droid/hook.py::main`) | `statusLine` command in `~/.factory/settings.json` -> `tezgah-statusline --droid`, set in place with the file's comments kept (`bin/tezgah-setup::install_droid`, `bin/tezgah-setup::jsonc_put`) | five tool-use (`statusline.py::observable`) |
|
|
28
28
|
| commandcode | SessionStart, PreToolUse (matcher `shell\|write\|edit`), PostToolUse, Stop (`bin/tezgah-setup::COMMANDCODE_HOOK_EVENTS`), all in the `hooks` key of `~/.commandcode/settings.json` | the managed block in `~/.commandcode/AGENTS.md` (`bin/tezgah-setup::STATIC_RULES`); SessionStart drops the core while the block is there (`hosts/commandcode/hook.py::rules_carry_core`) | unavailable: Command Code has no prompt event | no custom status line - the segment rides `systemMessage`, plain, TUI only, at SessionStart and Stop (`hosts/commandcode/hook.py::status`) | five tool-use (`TOOL_USE_MEASURES` at `hosts/commandcode/hook.py::status`) |
|
|
@@ -44,19 +44,21 @@ What each host can enforce, in the
|
|
|
44
44
|
[enforcement capability](glossary.md#enforcement-capability) words. `gate`: the
|
|
45
45
|
hook can refuse. `observe`: it records and never refuses. `partial`: only part
|
|
46
46
|
of the surface fires. `unavailable`: the host has no event for it. A cell gets
|
|
47
|
-
`unverified` when no
|
|
48
|
-
the code path there
|
|
47
|
+
`unverified` when no run has shown the host binary obeying it. Fixture payloads
|
|
48
|
+
test the code path there. `--report --live` goes one step further: it drives the
|
|
49
|
+
installed wiring, the hook command or the plugin loaded with node. No host
|
|
50
|
+
binary runs, so a live `ok` proves that the wiring answers, not that the host obeys.
|
|
49
51
|
|
|
50
52
|
| Host | Gate (PreToolUse) | Stop | Subagent |
|
|
51
53
|
|---|---|---|---|
|
|
52
|
-
| claude | gate, probed by `--report --live` (`bin/tezgah-setup::live_probe`) | gate, unverified (`hooks/projects-stop.py::main`) | observe, unverified: a brief at SubagentStart, a record-only `subagent_end` row at SubagentStop (ADR 011) |
|
|
53
|
-
| codex | gate, probed by `--report --live`; Codex runs only a trusted group | gate, unverified (`hosts/codex/hook.py::main`) | partial, unverified: a brief at SubagentStart, no subagent-end event |
|
|
54
|
-
| cursor | gate, probed by `--report --live` | gate, unverified (`hosts/cursor/hook.py::dispatch`) | observe, unverified: a brief at subagentStart, a record-only `subagent_end` row at subagentStop |
|
|
55
|
-
| opencode | gate, probed by `--report --live
|
|
56
|
-
| dsh | gate, probed by `--report --live` through the bridge | unavailable: ADR 003 removed the Stop entry, because the bridge carries no outcome | partial, unverified: a brief at SubagentStart, no SubagentStop entry |
|
|
57
|
-
| omp | gate, probed by `--report --live
|
|
58
|
-
| qwen | gate, probed by `--report --live` through the hook command in `~/.qwen/settings.json`; that the qwen binary obeys the deny
|
|
59
|
-
| droid | gate, probed by `--report --live` through the hook command in `~/.factory/hooks.json`; that the droid binary obeys the deny
|
|
54
|
+
| claude | gate, unverified: probed by `--report --live` (`bin/tezgah-setup::live_probe`), which runs the PreToolUse command of the plugin copy's `hooks/hooks.json` on a deny payload; no claude binary runs, so that Claude Code obeys the deny is not shown | gate, unverified (`hooks/projects-stop.py::main`) | observe, unverified: a brief at SubagentStart, a record-only `subagent_end` row at SubagentStop (ADR 011) |
|
|
55
|
+
| codex | gate, unverified: probed by `--report --live` through the PreToolUse command in `$CODEX_HOME/hooks.json`; no codex binary runs, and Codex runs only a trusted group | gate, unverified (`hosts/codex/hook.py::main`) | partial, unverified: a brief at SubagentStart, no subagent-end event |
|
|
56
|
+
| cursor | gate, unverified: probed by `--report --live` through the preToolUse command in `~/.cursor/hooks.json`; no cursor-agent binary runs | gate, unverified (`hosts/cursor/hook.py::dispatch`) | observe, unverified: a brief at subagentStart, a record-only `subagent_end` row at subagentStop |
|
|
57
|
+
| opencode | gate, unverified: probed by `--report --live`, which imports the installed plugin with node and fires its `tool.execute.before`; opencode's own loader is not what ran it | unavailable: the plugin API has no end-of-turn event | unavailable |
|
|
58
|
+
| dsh | gate, unverified: probed by `--report --live` through the bridge's PreToolUse command; no dsh binary runs | unavailable: ADR 003 removed the Stop entry, because the bridge carries no outcome | partial, unverified: a brief at SubagentStart, no SubagentStop entry |
|
|
59
|
+
| omp | gate, unverified: probed by `--report --live`, which loads the installed extension with node 22.6+; omp's own loader is not what ran it | gate, unverified (`hosts/omp/hook.py::handle`) | partial, unverified: a brief at a task session's start; `session_stop` does not fire for task sessions |
|
|
60
|
+
| qwen | gate, unverified: probed by `--report --live` through the hook command in `~/.qwen/settings.json`; that the qwen binary obeys the deny stays unverified until `TEZGAH_E2E_SPEND=1 python3 tests/e2e_qwen.py` runs (it spends a model call) | gate, unverified (`hosts/qwen/hook.py::main`, judged on `last_assistant_message`) | observe, unverified: a brief at SubagentStart, a record-only `subagent_end` row at SubagentStop |
|
|
61
|
+
| droid | gate, unverified: probed by `--report --live` through the hook command in `~/.factory/hooks.json`; that the droid binary obeys the deny stays unverified until `TEZGAH_E2E_SPEND=1 python3 tests/e2e_droid.py` runs (it spends a model call) | gate, unverified: Droid's Stop carries no reply, so the reply is read from `transcript_path` (`hosts/droid/hook.py::last_reply`), and a turn end whose reply is not there yet is a `reply_unread` row, never judged | partial, unverified: a record-only `subagent_end` row from SubagentStop's `task_result`; no SubagentStart, so no brief |
|
|
60
62
|
| commandcode | gate, unverified: probed by `--report --live` through the hook command in `~/.commandcode/settings.json`, but no run of the binary has shown it obeying the deny; only shell, write and edit reach the hook (read, MCP, subagent and web tools do not), and plan mode skips every hook | gate, unverified: Stop carries no reply, so the reply is read from `transcript_path` (`hosts/commandcode/hook.py::last_reply`) and a turn end with none there is a `reply_unread` row, never judged; the host caps a block at three retries | unavailable: the settings hooks have no subagent event |
|
|
61
63
|
| copilot | gate, unverified: probed by `--report --live` through the hook command in `$COPILOT_HOME/hooks/tezgah.json`, which answers with a top-level `permissionDecision: deny`, but no run of the binary has shown it obeying the deny. A PreToolUse hook that times out (10 s) lets the call through, by Copilot's own rule; one that crashes denies it. A repo's `.claude/settings.json` hooks run there too | gate, unverified: Stop carries no reply, so the reply is the last `assistant.message` in `transcript_path` (`hosts/copilot/hook.py::last_reply`), and a turn end with none there is a `reply_unread` row, never judged; the host ends the turn anyway after 8 blocks in a row | observe, unverified: a brief at subagentStart, a record-only `subagent_end` row at SubagentStop; the built-in `general-purpose` agent fires neither |
|
|
62
64
|
| gemini | gate, unverified: probed by `--report --live` through the hook command in `~/.gemini/extensions/tezgah/hooks/hooks.json`, which answers with a top-level `decision: deny`, but no run of the binary has shown it obeying the deny, and no run has shown Gemini loading an extension dir written without `gemini extensions install` (its loader reads every subdir; a non-empty `security.allowedExtensions` asks for install metadata tezgah does not write). A hook that crashes or prints bad JSON is a warning and the call goes on | gate, unverified: AfterAgent carries the reply as `prompt_response` and `stop_hook_active` (`hosts/gemini/hook.py::main`); a deny sends the reason back as a new prompt | unavailable: no subagent event; a subagent is a tool call (`codebase_investigator`, `generalist`, `invoke_agent`) the gate sees as `Task` |
|
|
@@ -120,7 +122,8 @@ shared config, contract hash and the CLI symlinks every host shell can call
|
|
|
120
122
|
(`bin/tezgah-setup::write_user_agents`).
|
|
121
123
|
- **cursor** - `install_cursor` (`bin/tezgah-setup::install_cursor`): `~/.cursor/hooks.json`
|
|
122
124
|
(tezgah entries replaced, a user's Orca entries merged), `skills/*` symlinks,
|
|
123
|
-
`mcp.json` servers, and `cli-config.json` `statusLine`.
|
|
125
|
+
`mcp.json` servers, and `cli-config.json` `statusLine`. The symlinks skip a
|
|
126
|
+
skill another dir Cursor reads already links (`bin/tezgah-setup::cursor_shared_copy`).
|
|
124
127
|
- **opencode** - `install_opencode` (`bin/tezgah-setup::install_opencode`): the plugin under
|
|
125
128
|
both `plugins/` and `plugin/` (version drift), `skills/*` symlinks, the
|
|
126
129
|
generated `~/.config/tezgah/opencode-contract.md` (`bin/tezgah-setup::opencode_contract`) and
|
|
@@ -160,7 +163,8 @@ shared config, contract hash and the CLI symlinks every host shell can call
|
|
|
160
163
|
`~/.qwen/settings.json` (or `$QWEN_HOME`). It adds one hook group per event of
|
|
161
164
|
`QWEN_HOOK_EVENTS`, each `timeout` in milliseconds. The PreToolUse group
|
|
162
165
|
carries `QWEN_PRETOOL_MATCHER` over Qwen's runtime tool ids. It also sets
|
|
163
|
-
`ui.statusLine`.
|
|
166
|
+
`ui.statusLine`. A status line the user had comes back at uninstall. Orca's
|
|
167
|
+
groups and the provider keys in that file stay, and
|
|
164
168
|
the backup is owner-only (`bin/tezgah-setup::backup`). The install links
|
|
165
169
|
`skills/*` into `~/.qwen/skills`. It writes the always-on block in
|
|
166
170
|
`~/.qwen/QWEN.md`. tezgah writes no subagent roles, MCP rows or orx shim for
|
|
@@ -175,7 +179,9 @@ shared config, contract hash and the CLI symlinks every host shell can call
|
|
|
175
179
|
comments, so the install edits it in place and keeps them
|
|
176
180
|
(`bin/tezgah-setup::jsonc_put`). It adds the `statusLine`. It sets
|
|
177
181
|
`includeCoAuthoredByDroid` to false, the switch for Droid's co-author trailer.
|
|
178
|
-
|
|
182
|
+
The uninstall puts that switch back to what the install found: the user's
|
|
183
|
+
value, or no key. A file it cannot edit safely is left alone and the run names
|
|
184
|
+
both keys. The install
|
|
179
185
|
links `skills/*` into `~/.factory/skills` beside the user's own links. It
|
|
180
186
|
writes the always-on block in `~/.factory/AGENTS.md`. The uninstall deletes a
|
|
181
187
|
hooks.json that held only tezgah's groups. An empty one would still hide
|
|
@@ -188,8 +194,8 @@ shared config, contract hash and the CLI symlinks every host shell can call
|
|
|
188
194
|
carries `COMMANDCODE_PRETOOL_MATCHER`, which Command Code tests against the
|
|
189
195
|
tool's display name. The same write sets `attribution.commit` to `""`. That
|
|
190
196
|
turns off the default `Co-authored-by: CommandCodeBot` commit trailer. A value
|
|
191
|
-
the user set stays. The uninstall
|
|
192
|
-
install
|
|
197
|
+
the user set stays. The uninstall restores the value the install found, from
|
|
198
|
+
the install record (`bin/tezgah-setup::restore_prior`). The install links `skills/*` into `~/.commandcode/skills`. It
|
|
193
199
|
writes the always-on block in `~/.commandcode/AGENTS.md`. tezgah writes no
|
|
194
200
|
subagent roles, MCP rows or orx shim for Command Code yet.
|
|
195
201
|
- **copilot** - `install_copilot` (`bin/tezgah-setup::install_copilot`) writes
|
|
@@ -200,12 +206,14 @@ shared config, contract hash and the CLI symlinks every host shell can call
|
|
|
200
206
|
there rejects every hook in it. The PreToolUse entry carries
|
|
201
207
|
`COPILOT_PRETOOL_MATCHER`. settings.json is JSON with comments. So the status
|
|
202
208
|
line and `includeCoAuthoredBy: false` go in only while it parses as plain
|
|
203
|
-
JSON.
|
|
209
|
+
JSON. A file holding comments gets a `holds comments - left untouched` line,
|
|
210
|
+
and the install still exits 0. The install refuses an unreadable, non-UTF-8
|
|
211
|
+
or non-object file and fails. That setting turns off the default `Co-authored-by` trailer on the
|
|
204
212
|
agent's commits. A value the user set stays. The install links `skills/*`
|
|
205
213
|
into `$COPILOT_HOME/skills` and writes the always-on block in
|
|
206
|
-
`$COPILOT_HOME/copilot-instructions.md`. The uninstall deletes tezgah.json
|
|
207
|
-
|
|
208
|
-
MCP rows or orx shim
|
|
214
|
+
`$COPILOT_HOME/copilot-instructions.md`. The uninstall deletes tezgah.json.
|
|
215
|
+
Each settings key goes back to what the install found, the status line
|
|
216
|
+
too. Copilot gets no subagent roles, MCP rows or orx shim from tezgah yet.
|
|
209
217
|
- **gemini** - `install_gemini` (`bin/tezgah-setup::install_gemini`) writes
|
|
210
218
|
tezgah's own extension, `~/.gemini/extensions/tezgah/`, which is wholly
|
|
211
219
|
tezgah's. `GEMINI_CLI_HOME` moves `~/.gemini` under it. The dir holds
|
|
@@ -227,12 +235,13 @@ shared config, contract hash and the CLI symlinks every host shell can call
|
|
|
227
235
|
never loads hooks from `antigravity-cli/settings.json`. The Antigravity App
|
|
228
236
|
and IDE read the same global hooks.json, so the install arms them too. The
|
|
229
237
|
status line goes in `~/.gemini/antigravity-cli/settings.json` only while it
|
|
230
|
-
is plain JSON. The
|
|
238
|
+
is plain JSON. The install names a file holding comments, leaves it alone and exits 0.
|
|
239
|
+
The skill links go in `antigravity-cli/skills`. Antigravity
|
|
231
240
|
adds no co-author trailer of its own, and no setting names one. An open
|
|
232
241
|
feature request asks for one (google-antigravity/antigravity-cli#230). The
|
|
233
242
|
always-on block is the `~/.gemini/GEMINI.md` one Gemini CLI shares
|
|
234
243
|
(`bin/tezgah-setup::gemini_md_kept`). The uninstall takes out the `tezgah`
|
|
235
|
-
name
|
|
244
|
+
name and the links, and puts the user's own statusLine back. It removes a file that held only
|
|
236
245
|
tezgah's keys. Antigravity gets no subagent roles, MCP rows or orx shim from
|
|
237
246
|
tezgah yet. Antigravity can never show a passing check. Its PostToolUse
|
|
238
247
|
carries no output. A shell call that went to the background also ends with an
|
|
@@ -264,13 +273,16 @@ shared config, contract hash and the CLI symlinks every host shell can call
|
|
|
264
273
|
always-on block into `~/.config/amp/AGENTS.md`. By default Amp adds a
|
|
265
274
|
`Co-authored-by: Amp` and an `Amp-Thread-ID` trailer to its commits. The
|
|
266
275
|
install sets `amp.git.commit.coauthor.enabled` and
|
|
267
|
-
`amp.git.commit.ampThread.enabled` to false in `~/.config/amp/settings.json
|
|
268
|
-
|
|
269
|
-
|
|
276
|
+
`amp.git.commit.ampThread.enabled` to false in `~/.config/amp/settings.json`,
|
|
277
|
+
or in `settings.jsonc` when that is the only one, unless the user set them.
|
|
278
|
+
It writes that file only while it is plain JSON. Install, uninstall and the
|
|
279
|
+
report resolve it the same way (`bin/tezgah-setup::amp_settings`). The
|
|
280
|
+
install names a file holding comments, leaves it alone and exits 0. It
|
|
281
|
+
refuses both files at once, or an unreadable one, and exits 1. Then the run
|
|
270
282
|
prints both keys to set by hand. The uninstall removes the plugin only when
|
|
271
283
|
it is a fresh render or starts with tezgah's header line
|
|
272
|
-
(`bin/tezgah-setup::amp_plugin_ours`).
|
|
273
|
-
|
|
284
|
+
(`bin/tezgah-setup::amp_plugin_ours`). A trailer key still false goes back to
|
|
285
|
+
what the install found. Amp gets no subagent roles, MCP rows or orx shim from
|
|
274
286
|
tezgah.
|
|
275
287
|
|
|
276
288
|
An adapter is responsible for its host's **envelope** and its **surface**: it
|
|
@@ -332,7 +344,7 @@ and drops the later ones.
|
|
|
332
344
|
| skills | omp | `skills.customDirectories`, then `.omp/skills` and `~/.omp/agent/skills`, Claude's project dir (user dir opt-in), `.agents/skills` and `~/.agents/skills`; first wins, identical copies collapse, a differing copy is renamed `<namespace>/<name>` | links in `~/.omp/agent/skills` |
|
|
333
345
|
| skills | claude | `.claude/skills`, `~/.claude/skills`; plugin skills are namespaced `tezgah:<name>` | the plugin copy |
|
|
334
346
|
| skills | codex | `.agents/skills` from the cwd up to the repo root, `~/.agents/skills`, `$CODEX_HOME/skills`; one name in two dirs is listed twice, not merged | links in `$CODEX_HOME/skills` |
|
|
335
|
-
| skills | cursor | `.agents/skills`, `.cursor/skills`, `~/.agents/skills`, `~/.cursor/skills`, then Claude's and Codex's project and user dirs; no documented winner | links in `~/.cursor/skills` |
|
|
347
|
+
| skills | cursor | `.agents/skills`, `.cursor/skills`, `~/.agents/skills`, `~/.cursor/skills`, then Claude's and Codex's project and user dirs; no documented winner | links in `~/.cursor/skills`, only for the skills `~/.codex`, `~/.claude` or `~/.agents/skills` do not already link |
|
|
336
348
|
| skills | opencode | its own skill tool is denied; the router lists `~/.config/opencode/skills`, `~/.claude/skills`, `~/.agents/skills`, first wins (`bin/tezgah-setup::skill_groups`) | links and the router |
|
|
337
349
|
| skills | qwen | `~/.qwen/skills`, `.qwen/skills`, extension and bundled skills; no documented winner | links in `~/.qwen/skills` |
|
|
338
350
|
| skills | droid | `.factory/skills` (project and folder), `~/.factory/skills`, `.agents/skills` and `~/.agents/skills`, plugin and built-in skills; no documented winner | links in `~/.factory/skills`, beside the user's own |
|
|
@@ -421,16 +433,16 @@ and say which document it came from.
|
|
|
421
433
|
|
|
422
434
|
A skill read is observable only where the host gives a channel that does not cost
|
|
423
435
|
a process per read: Claude parses its transcript (`statusline.py::claude_used`),
|
|
424
|
-
opencode classifies in process (`hosts/opencode/plugins/tezgah.js
|
|
436
|
+
opencode classifies in process (`classify` in `hosts/opencode/plugins/tezgah.js`),
|
|
425
437
|
omp filters in its embedded runner before it asks python
|
|
426
|
-
(`hosts/omp/tezgah-hook.ts.in
|
|
438
|
+
(`skillRead` in `hosts/omp/tezgah-hook.ts.in`), and Amp's plugin does the same for a
|
|
427
439
|
`Read` of a shipped skill (`hosts/amp/tezgah-plugin.ts.in`). On codex, cursor, qwen, droid, commandcode, copilot, gemini, antigravity, goose and dsh a read is not
|
|
428
440
|
observable at that price. Their surfaces pass the five tool-use measures as
|
|
429
441
|
`observable`, and the two skill marks render dim (`info`, no glyph) instead of
|
|
430
442
|
claiming the skill was never opened (`hooks/tezgah_context.py::TOOL_USE_MEASURES`, and the
|
|
431
443
|
`observable` branch at `hooks/tezgah_context.py::health_segments`). Callers that pass `TOOL_USE_MEASURES`:
|
|
432
444
|
`statusline.py::observable`, `hosts/codex/hook.py::main`, `hosts/commandcode/hook.py::status`,
|
|
433
|
-
`hosts/dsh/statusline/lib/index.js
|
|
445
|
+
`OBSERVABLE` in `hosts/dsh/statusline/lib/index.js`. A kill switch is observable everywhere
|
|
434
446
|
and still renders `off` (`hooks/tezgah_context.py::health_segments`).
|
|
435
447
|
|
|
436
448
|
## Adding a host
|
|
@@ -440,7 +452,7 @@ In order, each step verified by the one below it:
|
|
|
440
452
|
1. `HOST_DIRS` and, when the CLI can exist without a config dir, `HOST_BINS` in
|
|
441
453
|
`hooks/tezgah_paths.py::HOST_DIRS`, `hooks/tezgah_paths.py::HOST_BINS`. These are shared by the installer, the report
|
|
442
454
|
and agent generation, so "is this host installed?" has one answer
|
|
443
|
-
(`hooks/tezgah_paths.py
|
|
455
|
+
(`hooks/tezgah_paths.py::host_installed`). Add the name to `ALL_HOSTS` in the report
|
|
444
456
|
order (`bin/tezgah-setup::ALL_HOSTS`).
|
|
445
457
|
2. The hook adapter(s) under `hosts/<host>/` (an `install_*`-independent
|
|
446
458
|
directory like `hosts/codex/hook.py`, `hosts/cursor/hook.py`) plus, for a
|
|
@@ -471,8 +483,9 @@ In order, each step verified by the one below it:
|
|
|
471
483
|
the host cannot see a skill read, and `idx_override` on any redraw that must
|
|
472
484
|
not fork git for a cosmetic glyph (`hooks/tezgah_context.py::health_segments`).
|
|
473
485
|
6. Tests, in two tiers: a per-host class in `tests/test_setup.py` pinning the
|
|
474
|
-
report rows and the written files (e.g. `OmpHost
|
|
475
|
-
`CodexHome
|
|
486
|
+
report rows and the written files (e.g. `tests/test_setup.py::OmpHost`,
|
|
487
|
+
`tests/test_setup.py::CodexHome`, `tests/test_setup.py::CursorMatcher`,
|
|
488
|
+
`tests/test_setup.py::DshStatusline`), and an
|
|
476
489
|
opt-in script under `tests/e2e_*.py` for the thing only a real host can prove
|
|
477
490
|
- not collected by `unittest discover`, it exits 0 with `SKIP` when the host
|
|
478
491
|
is absent (`tests/e2e_omp_statusline.py:13-16`). A script that calls a model
|
|
@@ -536,7 +549,7 @@ In order, each step verified by the one below it:
|
|
|
536
549
|
(`hooks/projects-auto-init.py:28-38`, `hosts/codex/hook.py::main`).
|
|
537
550
|
- **opencode**: `experimental.session.compacting` carries the builder's
|
|
538
551
|
post-compact block, so the contract survives the summary
|
|
539
|
-
(`hosts/opencode/plugins/tezgah.js
|
|
552
|
+
(the `experimental.session.compacting` hook of `Tezgah` in `hosts/opencode/plugins/tezgah.js`). It hands the builder the
|
|
540
553
|
session id. The builder then forgets the lessons and armed paragraphs
|
|
541
554
|
that session saw, and restates its pinned user constraints. The call
|
|
542
555
|
used to send `{}`, which keyed nothing on the session.
|
|
@@ -546,7 +559,7 @@ In order, each step verified by the one below it:
|
|
|
546
559
|
`hosts/omp/hook.py` for `{"event": "post_compact"}`; the block is sent with
|
|
547
560
|
`pi.sendMessage(..., {deliverAs: "nextTurn"})`, and the summary rides as
|
|
548
561
|
`compact_summary` so `remember_compaction` writes the compaction row
|
|
549
|
-
(`hosts/omp/tezgah-hook.ts.in
|
|
562
|
+
(the `session_compact` handler in `hosts/omp/tezgah-hook.ts.in`, `hosts/omp/hook.py::handle`).
|
|
550
563
|
Subagent sessions are skipped, because the post-compact block starts the
|
|
551
564
|
indexer.
|
|
552
565
|
- **cursor**: not re-injected - a limitation. Cursor's only compaction hook,
|
|
@@ -570,7 +583,7 @@ In order, each step verified by the one below it:
|
|
|
570
583
|
SubagentStop and Stop, and silently drops anything else - which is why
|
|
571
584
|
`configPath` names `hosts/dsh/hooks.json` and not the Claude manifest
|
|
572
585
|
(`bin/tezgah-setup::dsh_patch_block`). Its status route goes through `tezgah-status`
|
|
573
|
-
with the five tool-use measures (`hosts/dsh/statusline/lib/index.js
|
|
586
|
+
with the five tool-use measures (`OBSERVABLE` in `hosts/dsh/statusline/lib/index.js`), it
|
|
574
587
|
declares `TEZGAH_CALL_OUTCOME=none` because the bridge drops the outcome field
|
|
575
588
|
(`hosts/dsh/hooks.json:16-18`, `hooks/projects-posttooluse.py::main`), and its
|
|
576
589
|
statusline row lives in the web profile because the plugin injects the
|
|
@@ -580,9 +593,9 @@ In order, each step verified by the one below it:
|
|
|
580
593
|
`hosts/dsh/hooks.json` until the bridge carries outcomes.
|
|
581
594
|
- **opencode carries the untrusted-content control in the plugin**, not by
|
|
582
595
|
importing `hooks/tezgah_untrusted.py`, which a plugin cannot do in process: the
|
|
583
|
-
channel is decided in JS (`untrustedSource` `hosts/opencode/plugins/tezgah.js
|
|
584
|
-
the tier's argv reader
|
|
585
|
-
result the hook is handed (`labelResult`
|
|
596
|
+
channel is decided in JS (`untrustedSource` in `hosts/opencode/plugins/tezgah.js`,
|
|
597
|
+
the tier's argv reader `tierRead`), the label and the taint notice ride the
|
|
598
|
+
result the hook is handed (`labelResult`). A shared corpus in
|
|
586
599
|
`tests/test_opencode_plugin.py::OpenCodePlugin.test_the_classifier_agrees_with_the_python_half_on_a_shared_corpus` drives both halves over the same calls and
|
|
587
600
|
fails if their answers differ, so neither can move without the other.
|
|
588
601
|
- **omp carries both untrusted-content marks through the shared core.** Its
|
|
@@ -614,20 +627,20 @@ In order, each step verified by the one below it:
|
|
|
614
627
|
- **opencode asks the core for the rules it cannot port, and counts the ask.** The
|
|
615
628
|
task rule's phase and allowlist and the language rule's word list are answered by
|
|
616
629
|
`bin/tezgah-gate decide` (the recording verb; `check` is the dry run) rather than copied into JavaScript (`IDENT_CMD`
|
|
617
|
-
`hosts/opencode/plugins/tezgah.js
|
|
630
|
+
in `hosts/opencode/plugins/tezgah.js`, tested in the `tool.execute.before` hook) - one spawn per write
|
|
618
631
|
and per identifier-creating command. The delegation fails open by design: a missing
|
|
619
|
-
CLI, a non-zero exit, a broken pipe or a 10 s timeout refuses nothing
|
|
620
|
-
`
|
|
621
|
-
answer (`noteDelegation`
|
|
632
|
+
CLI, a non-zero exit, a broken pipe or a 10 s timeout refuses nothing
|
|
633
|
+
(`collect`). One `delegation` row now records the class when the core could not
|
|
634
|
+
answer (`noteDelegation`), so a silent fail-open is countable instead
|
|
622
635
|
of invisible.
|
|
623
636
|
- **opencode has no session-start hook**, so its always-on file must carry the
|
|
624
637
|
pointer every hook host appends, and the per-session index, agent and contract
|
|
625
638
|
refreshes ride the first `chat.message` instead (`bin/tezgah-setup::refresh_contract`,
|
|
626
|
-
`hosts/opencode/plugins/tezgah.js
|
|
639
|
+
the `oncePerSession` checks in the `chat.message` hook of `hosts/opencode/plugins/tezgah.js`). It denies the native `skill`
|
|
627
640
|
tool - the generated router replaces the injected skill list
|
|
628
641
|
(`bin/tezgah-setup::install_opencode`) - and `permission.ask` may never be emitted by a
|
|
629
642
|
given build, which is why `tool.execute.before` stays the enforcing half
|
|
630
|
-
(`hosts/opencode/plugins/tezgah.js
|
|
643
|
+
(the header comment of `hosts/opencode/plugins/tezgah.js`).
|
|
631
644
|
- **Cursor only runs a PreToolUse hook for the tool types its matcher names**, so
|
|
632
645
|
the matcher must list the write spellings or the gate's edit branches are
|
|
633
646
|
unreachable there (`hosts/cursor/hook.py:33-42`,
|
|
@@ -648,7 +661,7 @@ In order, each step verified by the one below it:
|
|
|
648
661
|
`hosts/cursor/hooks.json:8` - neither named them until 2026-09-19, so a
|
|
649
662
|
PowerShell call on those two reached no hook at all while the shared rules
|
|
650
663
|
already knew the spelling), and omp's `GATED` list
|
|
651
|
-
(`hosts/omp/tezgah-hook.ts.in
|
|
664
|
+
(`GATED` in `hosts/omp/tezgah-hook.ts.in`). Qwen Code has two shell tool ids,
|
|
652
665
|
`run_shell_command` and `monitor`, which `bin/tezgah-setup::QWEN_PRETOOL_MATCHER`
|
|
653
666
|
names beside `notebook_edit` and `mcp__.*`. Claude's and dsh's `Monitor` runs a
|
|
654
667
|
shell command too, so their matcher names it. Droid's shell is `Execute`, which `bin/tezgah-setup::DROID_PRETOOL_MATCHER`
|
|
@@ -720,7 +733,7 @@ In order, each step verified by the one below it:
|
|
|
720
733
|
- **omp spawns the MCP `command` as one executable** and takes the rest in
|
|
721
734
|
`args`; the whole argv in `command` fails with ENOENT (`bin/tezgah-setup::install_omp`),
|
|
722
735
|
and `setStatus` strips ANSI, so only the widget path keeps the per-mark colors
|
|
723
|
-
(`hosts/omp/tezgah-hook.ts.in
|
|
736
|
+
(the comment above `draw` in `hosts/omp/tezgah-hook.ts.in`).
|
|
724
737
|
- **omp drops browser MCP servers while its native browser is on**: a server
|
|
725
738
|
named `playwright`/`puppeteer`/... or running `@playwright/mcp`/... never
|
|
726
739
|
connects there (omp 18.2.11 `/mcp` listed codegraph, mobile-mcp and tezgah
|
package/docs/layers.md
CHANGED
|
@@ -94,7 +94,7 @@ frameworks.
|
|
|
94
94
|
| agent framework | is it composed *from* elsewhere, or does it compose? A framework is composed from building blocks and stays a framework agent | tezgah plugs into its extension points, supplies none |
|
|
95
95
|
| SDK | does it leave the iteration to its caller? A client library gives a call and stops | tezgah ships no client; it reads a host's event JSON on stdin (`hosts/codex/hook.py:1-2`) |
|
|
96
96
|
| IDE plugin | is it a guest inside another program's surface? It draws and forwards and owns no task runtime | `hosts/opencode/plugins/tezgah.js:1-12` is one, and is a plugin |
|
|
97
|
-
| eval harness | does it produce a score over a task set? It measures and enforces nothing | an eval scaffold is vendored as the `lm-evaluation-harness` skill body (`skills/ai-research/11-evaluation/lm-evaluation-harness/ENTRY.md:11`), and this project's measurements live apart from the layer (`docs/README.md:
|
|
97
|
+
| eval harness | does it produce a score over a task set? It measures and enforces nothing | an eval scaffold is vendored as the `lm-evaluation-harness` skill body (`skills/ai-research/11-evaluation/lm-evaluation-harness/ENTRY.md:11`), and this project's measurements live apart from the layer (`docs/README.md:100-101`) |
|
|
98
98
|
| orchestrator | does it split one request across many runs and merge them? A harness runs one job to a report | the `harness` skill's route table (`skills/harness/SKILL.md:41-53`) and the graph workflows (`hooks/tezgah_policy.py::WORKFLOWS`) |
|
|
99
99
|
| MCP | does it only carry a call and its result? Then it is transport, not a layer with policy or evidence | the section below |
|
|
100
100
|
|
package/docs/operations.md
CHANGED
|
@@ -52,14 +52,14 @@ A green `--report` says the wiring is on disk; `--report --live` says it ran. Wh
|
|
|
52
52
|
What a user receives is a release tarball, not this checkout:
|
|
53
53
|
`dist/tezgah-<version>.tar.gz` beside `dist/tezgah-<version>.tar.gz.sha256`,
|
|
54
54
|
built by `packaging/build.sh` from the tracked `MANIFEST` plus a generated
|
|
55
|
-
`VERSION`, both written into the payload (`packaging/build.sh:
|
|
56
|
-
`
|
|
55
|
+
`VERSION`, both written into the payload (`GENERATED`, `packaging/build.sh:61`,
|
|
56
|
+
and `VERSION`, `packaging/build.sh:137`). A version unpacks at
|
|
57
57
|
`<prefix>/<version>` and `<prefix>/current` is a symlink to the active one, so a
|
|
58
58
|
team can stay on one version and a rollback is one tree away: the older version is
|
|
59
59
|
never removed, and going back is
|
|
60
|
-
`ln -sfn <prefix>/<previous> <prefix>/current` (
|
|
61
|
-
`packaging/upgrade.sh
|
|
62
|
-
`TEZGAH_PREFIX` or `$XDG_DATA_HOME` says otherwise (`packaging/upgrade.sh
|
|
60
|
+
`ln -sfn <prefix>/<previous> <prefix>/current` (the header comment of
|
|
61
|
+
`packaging/upgrade.sh`, and its `ln -sfn "$dest"` flip). The prefix is `~/.local/share/tezgah` unless
|
|
62
|
+
`TEZGAH_PREFIX` or `$XDG_DATA_HOME` says otherwise (`PREFIX=` in `packaging/upgrade.sh`),
|
|
63
63
|
and `bin/tezgah-setup --prefix DIR` wins over both (`bin/tezgah-setup::INSTALL_PREFIX`).
|
|
64
64
|
A farm link counts as tezgah's when it resolves into the checkout **or** into the
|
|
65
65
|
install root, which is what keeps an upgrade from orphaning the wiring
|
|
@@ -68,23 +68,23 @@ install root, which is what keeps an upgrade from orphaning the wiring
|
|
|
68
68
|
| Step | Command |
|
|
69
69
|
|---|---|
|
|
70
70
|
| first install, POSIX | `curl -fsSL https://raw.githubusercontent.com/r1z4x/tezgah/v<version>/packaging/install.sh \| sh -s -- --version <version>` |
|
|
71
|
-
| from a tarball you hold | unpack it, then `sh packaging/install.sh --version <version>` — it finds `upgrade.sh` beside itself (`packaging/install.sh
|
|
71
|
+
| from a tarball you hold | unpack it, then `sh packaging/install.sh --version <version>` — it finds `upgrade.sh` beside itself (`here=` in `packaging/install.sh`) |
|
|
72
72
|
| first install, Windows | `$env:TEZGAH_VERSION = '<version>'; irm https://raw.githubusercontent.com/r1z4x/tezgah/v<version>/packaging/install.ps1 \| iex` |
|
|
73
73
|
| upgrade | `tezgah update` (any channel), `bin/tezgah-setup --upgrade [VERSION]` from the installed tree, or `sh packaging/upgrade.sh --version X.Y.Z` |
|
|
74
74
|
|
|
75
75
|
`packaging/install.sh` is a bootstrap, never a second implementation: it runs
|
|
76
76
|
`packaging/upgrade.sh` — the one place a version is fetched, checksum-verified,
|
|
77
77
|
unpacked and made current — and then starts `bin/tezgah-setup --install` from the
|
|
78
|
-
tree that just became current (`packaging/install.sh
|
|
78
|
+
tree that just became current (the `sh "$up"` call in `packaging/install.sh`). `upgrade.sh`
|
|
79
79
|
compares the digest it fetched against the artifact before unpacking anything and
|
|
80
|
-
stops, printing both, on a mismatch (`packaging/upgrade.sh
|
|
80
|
+
stops, printing both, on a mismatch (`want=` and `got=` in `packaging/upgrade.sh`). It then
|
|
81
81
|
checks the tarball's build provenance with `gh attestation verify` when `gh` is
|
|
82
82
|
on PATH. Without `gh` it prints `provenance not checked`, and a `gh` that cannot
|
|
83
83
|
answer is a warning: the checksum stays the gate.
|
|
84
84
|
`TEZGAH_DIST` points `upgrade.sh` and `install.ps1` at a directory holding
|
|
85
85
|
`tezgah-<version>.tar.gz` + `.sha256` instead of the release URL — the seam that
|
|
86
86
|
installs a locally built artifact with no published release, which is what CI
|
|
87
|
-
uses (`
|
|
87
|
+
uses (the `TEZGAH_DIST` branch of `packaging/upgrade.sh`).
|
|
88
88
|
|
|
89
89
|
**A private `TEZGAH_REPO` needs a token.** GitHub answers `404` to
|
|
90
90
|
`releases/download/...` from a session without one. Measured 2026-10-17: the token
|
|
@@ -105,8 +105,8 @@ Windows goes through `packaging/install.ps1`, which assumes no `sh`, no `python3
|
|
|
105
105
|
and no symlink privilege: it unpacks with the `tar.exe` that Windows 10 1803+
|
|
106
106
|
ships, materialises `current` as a directory junction, copies the tree where a
|
|
107
107
|
junction is refused, and starts the installer with the first of `py -3`,
|
|
108
|
-
`python`, `python3` it finds, passing `--prefix` on (`
|
|
109
|
-
`packaging/install.ps1
|
|
108
|
+
`python`, `python3` it finds, passing `--prefix` on (the header and the `$NoInstall`
|
|
109
|
+
block of `packaging/install.ps1`). The installer knows both shapes of `current`
|
|
110
110
|
(`bin/tezgah-setup::running_prefix`). A junction answers to
|
|
111
111
|
`os.path.isjunction`, or to its reparse tag below Python 3.12. A copy is the
|
|
112
112
|
running tree at `<prefix>/current` with no `.git`, whose `VERSION` names a
|
|
@@ -140,11 +140,13 @@ cd ~/Projects/tezgah && bin/tezgah-setup --install
|
|
|
140
140
|
install the missing optional tools (host CLIs only with `--host-deps`) and whether to wire the DevTools MCP, prints
|
|
141
141
|
the plan, and writes only after a yes (`bin/tezgah-setup::wizard`). A pre-tezgah setup is
|
|
142
142
|
named under `predecessor wiring still present` and retired by `--adopt`
|
|
143
|
-
(`bin/tezgah-setup::adopt`); narrow
|
|
143
|
+
(`bin/tezgah-setup::adopt`); narrow an install with `--hosts omp` and `--roots ~/work:~/oss`.
|
|
144
|
+
On `--install`, `--dry-run` covers only the optional-tool installs (`bin/tezgah-setup::install_deps`).
|
|
145
|
+
`--install --dry-run` still writes every host file, the farm and `config.json`, so it is not a preview.
|
|
144
146
|
|
|
145
147
|
An install from a checkout is a live symlink into that working tree, so a half-finished edit or a branch switch there crashes the sessions running on it (the `crash` ledger rows of 09-20..09-21 were names removed mid-edit) - do tezgah maintenance in a `git worktree`, never in the installed checkout.
|
|
146
148
|
|
|
147
|
-
The `~/.config/tezgah/bin` farm carries `tezgah-mcp` and `tezgah-design` beside the other CLIs; two are left out on purpose: `tezgah-dsh` is wired as `~/.local/bin/dsh` (`dsh_launcher`), and `tezgah-import-ai-research` is dev-time only (its docstring). The report's `config.json hosts (...) match the hosts wired (...)` row (`hosts_row`) reads `MISS` when the recorded host list differs from the hosts whose wiring is on disk; `--upgrade`, `--enable`/`--disable` and a full uninstall act on the recorded list, so the row only reports it - `--install --hosts ...` records the list you choose.
|
|
149
|
+
The `~/.config/tezgah/bin` farm carries `tezgah-mcp` and `tezgah-design` beside the other CLIs; two are left out on purpose: `tezgah-dsh` is wired as `~/.local/bin/dsh` (`dsh_launcher`), and `tezgah-import-ai-research` is dev-time only (its docstring). It also carries one link per host adapter. A host's hook config runs that link by its farm path: `tezgah-codex-hook`, `tezgah-cursor-hook`, `tezgah-qwen-hook`, `tezgah-droid-hook`, `tezgah-commandcode-hook`, `tezgah-copilot-hook`, `tezgah-gemini-hook`, `tezgah-antigravity-hook` and `tezgah-goose-hook` (`bin/tezgah-setup::install_common`). The report's `config.json hosts (...) match the hosts wired (...)` row (`hosts_row`) reads `MISS` when the recorded host list differs from the hosts whose wiring is on disk; `--upgrade`, `--enable`/`--disable` and a full uninstall act on the recorded list, so the row only reports it - `--install --hosts ...` records the list you choose.
|
|
148
150
|
|
|
149
151
|
A successful run prints, in order: `dependencies:`, a line per missing optional
|
|
150
152
|
tool with the vendor command it runs — over the network, no sudo
|
|
@@ -172,7 +174,7 @@ or `default`), a common block (config, the code-graph binary, the consult option
|
|
|
172
174
|
`orx`, `npx`, the artifacts dir, `git`, the manifest, a stale-contract line), one
|
|
173
175
|
block per host, then the budget (`bin/tezgah-setup::report`). Each block is
|
|
174
176
|
`host_checks_<host>` (`bin/tezgah-setup::HOST_CHECKS`), and those rows are the source of truth for
|
|
175
|
-
"is this host armed" (`AGENTS.md:
|
|
177
|
+
"is this host armed" (`AGENTS.md:177-178`) — not the presence of a directory, and
|
|
176
178
|
not the status line. A row can read ` MISS ` too: claude's `plugin copy current`
|
|
177
179
|
row fails on a machine with no copy at all, because an absent copy is not a
|
|
178
180
|
current one (`bin/tezgah-setup::host_checks_claude`).
|
|
@@ -195,7 +197,7 @@ a host's config, so a wiring row there would belong to a different tool.
|
|
|
195
197
|
|
|
196
198
|
The version lives in `.claude-plugin/plugin.json` when that untracked local
|
|
197
199
|
manifest is present, else in the newest `## [x.y.z]` heading of `CHANGELOG.md`
|
|
198
|
-
(`hooks/tezgah_context.py::version`); `.gitignore:
|
|
200
|
+
(`hooks/tezgah_context.py::version`); `.gitignore:28` is why it is not in the repository, and
|
|
199
201
|
`RELEASING.md:19-24` is how a release bumps it. A `--install` that registers
|
|
200
202
|
Claude renders the pair into its tree. The version comes from `VERSION` or that
|
|
201
203
|
heading, never from the pair itself. A pair the install rendered follows the
|
|
@@ -239,7 +241,7 @@ After a change to the contract text (`hooks/tezgah_policy.py`,
|
|
|
239
241
|
- `--refresh` does that without a reinstall, for every armed host's static
|
|
240
242
|
block and not only opencode's (`refresh_contract`). opencode has no
|
|
241
243
|
session-start hook, so its plugin runs it once per session
|
|
242
|
-
(`hosts/opencode/plugins/tezgah.js
|
|
244
|
+
(`oncePerSession` in the `chat.message` hook of `hosts/opencode/plugins/tezgah.js`). Until 2026-10-02 it
|
|
243
245
|
re-rendered opencode's files alone and reset the one shared hash, so a rule
|
|
244
246
|
edit left the other static files stale while `--report` went green (audit
|
|
245
247
|
M-5).
|
|
@@ -278,7 +280,15 @@ materialised where a platform refuses symlinks — counts as tezgah's only when
|
|
|
278
280
|
resolves inside this checkout or the install root, so a file you put in the same
|
|
279
281
|
place is kept (`is_tezgah_entry()`, `bin/tezgah-setup::is_tezgah_entry`), and
|
|
280
282
|
`~/.claude/settings.json` loses only the keys tezgah added
|
|
281
|
-
(`bin/tezgah-setup::unwire_claude_settings`).
|
|
283
|
+
(`bin/tezgah-setup::unwire_claude_settings`). Every key tezgah replaced goes
|
|
284
|
+
back to what the install found, from the install record
|
|
285
|
+
(`bin/tezgah-setup::restore_prior`). A directory or host file the install
|
|
286
|
+
created goes once the uninstall leaves it empty. One that was there before
|
|
287
|
+
stays. The codegraph MCP row stays too (OQ-13). A partial run takes only the
|
|
288
|
+
directories under the removed hosts' own roots
|
|
289
|
+
(`bin/tezgah-setup::remove_created_dirs`). A JSON host file keeps its data,
|
|
290
|
+
indent, minified form and non-ASCII text. An inline `[1, 2]` or `{"a": 1}`
|
|
291
|
+
inside an indented file may come back expanded (`bin/tezgah-setup::write_json`).
|
|
282
292
|
|
|
283
293
|
A **full** run also takes everything the wiring was serving from:
|
|
284
294
|
|
|
@@ -301,18 +311,25 @@ A **full** run also takes everything the wiring was serving from:
|
|
|
301
311
|
Kept in every run: the user's own files, every host-file backup under
|
|
302
312
|
`~/.config/tezgah/backups/` (the timestamped ones too), `~/.config/tezgah/adopted/` (the only copy of the
|
|
303
313
|
predecessor wiring adopt moved aside), and every repository's `.tezgah/`
|
|
304
|
-
research state. A **partial** run also keeps config.json
|
|
305
|
-
|
|
314
|
+
research state. A **partial** run also keeps config.json, both state caches
|
|
315
|
+
and the install tree. The hosts still armed read them at runtime, and the run says so. It also leaves
|
|
306
316
|
the gate armed: only a full run writes `pretooluse-off` while it removes the
|
|
307
317
|
hooks. A partial run used to write it too and never take it back, so the hosts
|
|
308
318
|
still armed lost the whole gate (audit H-2, ENV-01). The orx skill
|
|
309
319
|
shims come from orx's own installer, so they are never listed for removal
|
|
310
|
-
(`bin/tezgah-setup::install_openresearch`).
|
|
320
|
+
(`bin/tezgah-setup::install_openresearch`). An uninstall keeps the `codegraph` MCP
|
|
321
|
+
row in Codex's `config.toml`, Cursor's `mcp.json` and opencode's `opencode.json`
|
|
322
|
+
on purpose. It is the graph server's row, not tezgah's, and a user may have
|
|
323
|
+
written the same row. So the uninstall takes only the `tezgah` row beside it
|
|
324
|
+
(`tests/test_tezgah_mcp_wiring.py::Rows.test_uninstall_removes_only_the_tezgah_row`).
|
|
311
325
|
|
|
312
326
|
Every run ends in a verify pass that re-derives the removal from the filesystem
|
|
313
327
|
alone — one line per claim, and the exit code is 1 while anything tezgah wrote
|
|
314
328
|
survives (`verify_uninstall()`, `bin/tezgah-setup::verify_uninstall`), so a leftover can
|
|
315
|
-
never read as a clean uninstall.
|
|
329
|
+
never read as a clean uninstall. Each claim counts only tezgah's own links and
|
|
330
|
+
markers. Those are its farm path, raw or JSON-escaped, its MCP server names and
|
|
331
|
+
its managed-block markers. A user's own file that names tezgah is no
|
|
332
|
+
leftover (B-123). It ends by naming what needs a restart and, in
|
|
316
333
|
a git checkout whose own `.mcp.json` registers the tezgah MCP server, saying that
|
|
317
334
|
that file is the source tree's dev config, not an install artifact. An npm or
|
|
318
335
|
artifact install does not ship `.mcp.json` (it is neither in package.json
|
|
@@ -414,6 +431,31 @@ repository a sync over the copy took 1 s, and a fresh `init` took 10 s.
|
|
|
414
431
|
way (`bin/tezgah-task::cmd_start`). A copy that fails leaves the worktree to
|
|
415
432
|
`init`, as before.
|
|
416
433
|
|
|
434
|
+
## tezgah's own MCP server and the decision record: `tezgah-mcp`, `tezgah-decisions`
|
|
435
|
+
|
|
436
|
+
`bin/tezgah-mcp` is one stdio MCP server for every host: `initialize`,
|
|
437
|
+
`tools/list` and `tools/call` over newline-delimited JSON-RPC 2.0. Each host's
|
|
438
|
+
MCP config names it `tezgah` (`bin/tezgah-setup::TEZGAH_MCP_NAME`,
|
|
439
|
+
`bin/tezgah-setup::tezgah_mcp_command`), and the report's `MCP tezgah server wired
|
|
440
|
+
with this tree's argv` row checks that entry (`bin/tezgah-setup::mcp_row`). The server ports no rule. Each tool runs one
|
|
441
|
+
installed CLI and returns its output: one summary line, then the log line by
|
|
442
|
+
line. A CLI that fails comes back as `isError` with its own exit status
|
|
443
|
+
(`bin/tezgah-mcp::call`). A delegate gets 120 s, and `consult` gets 600 s
|
|
444
|
+
(`bin/tezgah-mcp::TIMEOUT`, `bin/tezgah-mcp::CONSULT_TIMEOUT`). The tools are
|
|
445
|
+
`bin/tezgah-mcp::SPECS`, the same list `tools/list` answers with.
|
|
446
|
+
|
|
447
|
+
| Tool | Inputs | Answers from |
|
|
448
|
+
|---|---|---|
|
|
449
|
+
| `tezgah_status` | `path` (default: the server's working directory) | `tezgah-status`: the armed, used and off contracts, one segment per line |
|
|
450
|
+
| `tezgah_gate_check` | `command` (required), `path` | `tezgah-gate check`: the refusal reason, or nothing when the call passes; nothing runs |
|
|
451
|
+
| `tezgah_features` | none | `tezgah-setup --features`: each feature id, its default, its selection and whether its dependency is installed |
|
|
452
|
+
| `tezgah_consult` | `question` (required), `use` (the members the user chose, e.g. `cli:codex,openrouter`) | `consult`; exit 4 or 5 lists the members to offer the user |
|
|
453
|
+
| `tezgah_research_check` | `path`, `slug` (one line in full; without it the open lines in full and each done line by its seal) | `tezgah-research check` |
|
|
454
|
+
|
|
455
|
+
| Command | What it does | Exit |
|
|
456
|
+
|---|---|---|
|
|
457
|
+
| `tezgah-decisions [check] [PATH] [--json]` | reads every ADR at `.tezgah/decisions/NNN-slug.md` in the repository at PATH (default: the working directory) and prints how many it read. It refuses three things: a record with no `status:`, a status outside `proposed`, `accepted`, `deprecated` and `superseded`, and a `supersedes:` naming no record (`bin/tezgah-decisions::problems`). `--json` prints the records and the problems | 0 every record whole or none, 1 a record refused (one `FAIL <path>: <what>` line each), 2 misuse |
|
|
458
|
+
|
|
417
459
|
## Opt-in taste: capture, learn, apply
|
|
418
460
|
|
|
419
461
|
Taste learns the user's coding preferences from their own corrections. It is
|
|
@@ -440,8 +482,8 @@ and a session rollback reaches about half as far back. The after blob writes no
|
|
|
440
482
|
ledger row. A rollback therefore never reads it as a pre-state (`capture_after`,
|
|
441
483
|
`hooks/tezgah_snapshot.py::capture_after`). A file outside the repository gets no
|
|
442
484
|
after blob.
|
|
443
|
-
opencode reaches the same call through a process (`noteTaste
|
|
444
|
-
`hosts/opencode/plugins/tezgah.js
|
|
485
|
+
opencode reaches the same call through a process (`noteTaste` in
|
|
486
|
+
`hosts/opencode/plugins/tezgah.js`).
|
|
445
487
|
|
|
446
488
|
```sh
|
|
447
489
|
touch ~/.config/tezgah/taste-on # arm; rm it to disarm
|
|
@@ -727,7 +769,8 @@ OCR then reviews on omp's model with omp's own credentials.
|
|
|
727
769
|
tracked, untracked and ignored files. In range or commit mode it covers the
|
|
728
770
|
tree of the reviewed ref. `--allow-secrets` skips the check. The patterns are
|
|
729
771
|
OCR's own (`bin/tezgah-ocr-review::SECRET_PATTERNS`).
|
|
730
|
-
-
|
|
772
|
+
- Exit 2 is misuse or a missing tool: an unknown flag, `--commit` beside
|
|
773
|
+
`--from`/`--to`, or no `ocr` or `omp` on PATH. A setup failure of the tool itself exits 4
|
|
731
774
|
with one line (`bin/tezgah-ocr-review::SetupError`). Three failures count:
|
|
732
775
|
no git repository, omp cannot start, the gateway never reports ready.
|
|
733
776
|
|
package/docs/research.md
CHANGED
|
@@ -314,7 +314,7 @@ through its seal.
|
|
|
314
314
|
|
|
315
315
|
| Command | What it does | Exit |
|
|
316
316
|
|---|---|---|
|
|
317
|
-
| `tezgah-research init <slug>
|
|
317
|
+
| `tezgah-research init <slug> --ask "<the user's words>" [--tier quick\|study\|program] [--method qualitative\|measured\|exploratory] [--class R1..R12] [--in "<covered>"]... [--out "<left alone>"]... [--question "..."] [--allow-open "<reason>"] [--supersedes <slug>] [--arena "<problem>" --budget "<budget>" [--evaluator <path> --metric "<metric>"]]` | scaffolds the line and makes sure `.tezgah/` is ignored by the project and has its own git repository (`tezgah_paths.ensure_workspace`). `--ask` is required: the user's words, verbatim, and a run without it exits 1 (`no --ask`). `--tier`, `--method`, `--in` and `--out` are the scope lock above, and `--arena`/`--budget` (with `--evaluator`/`--metric` for a measurable one) open an exploratory line ("The arena lock"). It refuses while another line is still open (see below), names each open line and its reasons one per line, and `--allow-open "<reason>"` is the way past unless an open line has `check` errors: the reason lands in the new line's `log.md` as its first entry, and an empty reason is misuse. A `--question` another line already asks is refused unless `--supersedes <that line>` says the new line is its next version (`serial_twins`). `--class` writes `state.question_class` and prints `obligations: <step>, ...` (`obligations`), the tier's `budget <slug>: sources a/b searches c/d runs e/f` (`budget`) and `overlap: <line>/<claim> <status> <similarity>` for each done claim close to the question (`overlaps`); `--class R0` exits 1 (`R0 gets no line`) before any other argument is read. Its next-step message names the commit loop: `tezgah-research commit` for the protocol, then again for the results in a later commit (`cmd_init`, `bin/tezgah-research`) | 0, 1 refused, 2 misuse |
|
|
318
318
|
| `tezgah-research commit <slug> "<message>"` | stages and commits only that line's path in `.tezgah`'s private repository - the commit the order rule reads (`cmd_commit`, `bin/tezgah-research`). With nothing left to commit it prints `nothing to commit: <paths> is already committed in <workspace>` and exits 0 | 0 (also with nothing to commit), 1 not a work tree or git failed, 2 misuse |
|
|
319
319
|
| `tezgah-research check [<slug>] [--json] [--strict] [--orx] [--online] [--all-lines] [-- <slug>]` | the discipline checks below. With no slug it checks the open lines in full and a line under `done/` by its order seal alone (`done_seal`, `hooks/tezgah_research.py::done_seal`), naming it only when the seal fails or its `state.json` or seal cannot be read, then prints `research: <n> done line(s): <s> checked by their seal, <u> unsealed (not checked); ...`; `--all-lines` checks every line in full, and `check <slug>` always checks that line in full (ADR 015). `--json` prints the report (no-slug default: the open lines and any done line whose seal read fails), `--strict` turns the unverifiable class into a refusal, `--orx` adds the registry check that asks `orx project view` for this repository (`check_orx`, `hooks/tezgah_research.py::check_orx`) and, on a rules-8 line, the run census; `--online` re-fetches a rules-8 line's citation receipts; a slug after `--` is never read as a flag, which is how the MCP tool `tezgah_research_check` passes its validated `slug`. A rules-8 line (`_check_rules8`) also prints report lines that are never counted - `certainty`, `stats`, `effect`, `instrument`, `ppi`, `stopping`, `online`, `budget` - and `--json` adds its per-claim `certainty` | 0 clean, 1 a line failed a rule or names no line |
|
|
320
320
|
| `tezgah-research status` | one line per line: an open line `ok` or a problem count, a line under `done/` read by its seal like the no-slug `check` - `done, seal ok`, `done, <n> seal problem(s)` or `done, unsealed (not checked)` (`summary`, `hooks/tezgah_research.py::summary`, plan 058 part 4); then one `FAIL` line per hidden dir under `research/` (`stray_dirs`) | 0 |
|