Install universal secrets-read guard hook for Claude, Cursor, and Codex#8
Open
njaltran wants to merge 1 commit into
Open
Install universal secrets-read guard hook for Claude, Cursor, and Codex#8njaltran wants to merge 1 commit into
njaltran wants to merge 1 commit into
Conversation
Bundles the workbench's secrets_guard.py (one stdlib-only script that answers in each agent's hook dialect) and registers it at scaffold time: .claude/settings.json (PreToolUse, Read|Grep|Bash), .cursor/hooks.json (beforeReadFile/beforeShellExecution), and .codex/hooks.json (PreToolUse, Bash only — Codex reads files via shell). All configs point at one copy in .agents/hooks/, mirroring the skills install pattern. Config writes merge into existing JSON without clobbering unrelated keys, skip agents already referencing the guard, and leave invalid JSON files untouched. Cursor/Codex commands locate the project root via git rev-parse (no CLAUDE_PROJECT_DIR equivalent exists there); Codex uses hooks.json rather than config.toml [hooks] due to openai/codex#17532. generate_skills.py now also syncs workbench/init/hooks/*.py into hooks/, tolerating pinned refs that predate the hooks directory; the initial hooks/secrets_guard.py is committed manually until the workbench PR (dlt-hub/dlthub-ai-workbench#79) merges and the ref is bumped.
njaltran
added a commit
to dlt-hub/dlthub-ai-workbench
that referenced
this pull request
Jul 3, 2026
dlt-hub/dlthub-init#8 registers the guard for all three agents at scaffold time, generating config shapes in its hooks.py. Keeping template JSONs here would duplicate that definition and drift. The workbench now ships only the script, the Claude plugin hooks.json, and docs; README notes the harmless double-registration when a project uses both the scaffolder and the marketplace plugin.
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
dlthub-initnow installs a secrets-read guard hook into every scaffolded workspace, for all three supported agents. The guard blocks direct reads of dlt secrets (secrets.toml,*.secrets.toml) and dotenv files (.env,.env.*except.env.example/.env.template/.env.sample) — turning thesetup-secretsskill's prompt-level policy into technical enforcement. Since the scaffold ships.dlt/secrets.toml, protection is active from the first prompt.Companion workbench PR (source of truth for the script): dlt-hub/dlthub-ai-workbench#79.
Design
One universal script, three agents.
secrets_guard.pyis stdlib-only, single-file, no imports — it detects the calling agent's dialect from the stdin payload (Cursor by itshook_event_namevalues, Claude/Codex bytool_name) and answers in that dialect. It follows the exact bundling pattern skills already use: repo-roothooks/(synced from the workbench) → force-included in the wheel as_bundled_hooks/→ copied to the workspace's.agents/hooks/at scaffold time.Per-agent configs, one target:
.claude/settings.jsonPreToolUse, matcherRead|Grep|Bash.cursor/hooks.jsonbeforeReadFile+beforeShellExecution.codex/hooks.jsonPreToolUse, matcherBashonly (no dedicated Read/Grep tool — file access goes through shell)Key decisions / tradeoffs
"$CLAUDE_PROJECT_DIR"; Cursor/Codex expose no equivalent, so their commands resolve the project root via"$(git rev-parse --show-toplevel 2>/dev/null || pwd)". Works anywhere inside the repo; falls back to cwd for non-git workspaces..codex/hooks.json, not.codex/config.toml [hooks]— repo-local config.toml hooks don't fire in interactive sessions (open upstream bug codex_hooks do not fire in interactive sessions when configured via repo-local .codex/config.toml openai/codex#17532); hooks.json is confirmed working..claude/settings.jsonpermissions survive), skip any agent already referencing the guard (idempotent re-runs), and leave files with invalid JSON completely untouched..agents/hooks/secrets_guard.pyalready exists, it's kept — same no-clobber rule as skills.generate_skills.pynow also syncsworkbench/init/hooks/*.py, but the current pinnedWORKBENCH_REFpredates the hooks directory (workbench PR #79 not yet merged), so the initialhooks/secrets_guard.pyis committed manually and the sync script warns-and-keeps rather than failing when hooks are absent at the ref. After #79 merges,make update-skillstakes over normally.Test plan
tests/test_hooks.py): three-agent config generation, merge-preserves-existing-keys, idempotency, invalid-JSON untouched, user-owned script preserved, bundled/source resolution — full suite 113 tests greenmake lint(ruff + mypy) andmake format-checkcleandlthub-init --no-sync -y <tmp>), then executed the exact command strings from each generated config — Cursor dialect returned{"permission": "deny", ...}for.dlt/secrets.toml, Claude dialect returned thehookSpecificOutputdeny forcat .env, with$CLAUDE_PROJECT_DIR/git rev-parseresolution both workingRead(".env")denied)