You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Both hooks/devkit-guard.sh and hooks/devkit-stop-guard.sh depend on python3 being on PATH to parse session.json. PR #64 centralised this in hooks/lib/read-session.sh, but the underlying dependency is still there and carries real costs:
Minimal containers: bare Alpine, distroless-style setups often ship without python3.
Latency: the guard runs on every PreToolUse. A Python subprocess per call is 50-150ms of overhead that compounds fast during high-churn steps.
Fragility: fail-closed path (exit 2 if python3 unavailable) means a broken python install hard-blocks all tool calls during a workflow.
Proposal
Add a devkit-engine guard subcommand in Go that the hooks shell out to. The engine binary is already on PATH during plugin runtime, already has lib.ReadSessionJSON, already has the session lock code, and ships cross-platform via the release Makefile (linux/darwin/windows).
Shape
devkit-engine guard --tool-name <name> [--stop]
Reads \$CLAUDE_PLUGIN_DATA/session.json via lib.ReadSessionJSON (respects the flock, so no torn reads).
Applies the exact same allowlist logic currently in devkit-guard.sh (command/prompt × hard/soft × stale TTL).
Exit codes mirror the PreToolUse contract: 0 = allow, 2 = block (with diagnostic on stderr).
--stop flag switches to the Stop-hook JSON verdict format (`{"decision":"approve|block","reason":"..."}`) for devkit-stop-guard.sh replacement.
(Or parse tool name in Go by reading stdin directly — eliminates jq too.)
Wins
Zero python3 dependency.
~10× faster (single Go process, no interpreter warm-up).
Single source of truth for allowlist logic — no drift between Go engine and bash hook.
Unit-testable in Go rather than via a bash fixture harness.
Timeout can drop back to 2s with margin.
Acceptance criteria
New cmd/guard.go subcommand with `--tool-name` and `--stop` flags.
`devkit-guard.sh` and `devkit-stop-guard.sh` reduced to thin `exec` wrappers (or removed entirely if the plugin manifest can point directly at the binary).
`hooks/lib/read-session.sh` deleted — no longer needed.
`hooks/devkit-guard_test.sh` fixture matrix ported to Go table-driven tests against the new subcommand.
`hooks.json` timeout returned to `2`.
Cross-platform smoke test: macOS, Linux, Windows all pass the fixture matrix.
Plugin manifest changes beyond swapping the hook command path if needed.
Context
Split out from PR #64 (issue #63) to keep that PR focused on the behaviour change. Everything in #64 is correct and the hooks work today — this is a robustness / portability upgrade.
Problem
Both
hooks/devkit-guard.shandhooks/devkit-stop-guard.shdepend onpython3being on PATH to parsesession.json. PR #64 centralised this inhooks/lib/read-session.sh, but the underlying dependency is still there and carries real costs:Proposal
Add a
devkit-engine guardsubcommand in Go that the hooks shell out to. The engine binary is already on PATH during plugin runtime, already haslib.ReadSessionJSON, already has the session lock code, and ships cross-platform via the release Makefile (linux/darwin/windows).Shape
\$CLAUDE_PLUGIN_DATA/session.jsonvialib.ReadSessionJSON(respects the flock, so no torn reads).devkit-guard.sh(command/prompt × hard/soft × stale TTL).--stopflag switches to the Stop-hook JSON verdict format (`{"decision":"approve|block","reason":"..."}`) fordevkit-stop-guard.shreplacement.Hook shrinks to a one-liner
(Or parse tool name in Go by reading stdin directly — eliminates jq too.)
Wins
Acceptance criteria
cmd/guard.gosubcommand with `--tool-name` and `--stop` flags.Out of scope
Context
Split out from PR #64 (issue #63) to keep that PR focused on the behaviour change. Everything in #64 is correct and the hooks work today — this is a robustness / portability upgrade.