← Back to article
Open article →
Claude Code Hooks Production Dev Workflow Guide 2026
Evidence notes document the bounded local or source-based checks behind an Effloow article. They are not product endorsements, legal advice, or benchmark claims.
Goal
Build a small local proof of concept for two Claude Code hook patterns:
- A
PreToolUsehook for theBashtool that blocks dangerous shell patterns before execution. - A
PostToolUsehook forEdit|Writethat formats changed JavaScript files with Prettier.
The sandbox used simulated Claude Code hook JSON payloads. It did not run inside an interactive Claude Code session, so the article must not claim that /hooks registration was verified in the live UI.
Sources Checked
- Official hooks guide: https://code.claude.com/docs/en/hooks-guide
- Official hooks reference: https://code.claude.com/docs/en/hooks
- Official settings documentation: https://code.claude.com/docs/en/settings
- Official security documentation: https://code.claude.com/docs/en/security
- Official permissions documentation: https://code.claude.com/docs/en/permissions
- Prettier CLI documentation: https://prettier.io/docs/next/cli/
- jq manual: https://jqlang.org/manual/
Environment
Claude Code: 2.1.143
Node.js: v25.9.0
npm: 11.12.1
npx: 11.12.1
jq: jq-1.7.1-apple
Prettier: 3.6.2
OS: macOS 15.6, Darwin 24.6.0, arm64
Sandbox directory: /tmp/effloow-claude-hooks-poc
Files Created
/tmp/effloow-claude-hooks-poc/.claude/settings.json
/tmp/effloow-claude-hooks-poc/.claude/hooks/pretooluse-block-dangerous-bash.sh
/tmp/effloow-claude-hooks-poc/.claude/hooks/posttooluse-format-js.sh
/tmp/effloow-claude-hooks-poc/fixtures/pretooluse-safe.json
/tmp/effloow-claude-hooks-poc/fixtures/pretooluse-dangerous.json
/tmp/effloow-claude-hooks-poc/fixtures/posttooluse-write.json
/tmp/effloow-claude-hooks-poc/src/needs-format.js
Settings Shape
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pretooluse-block-dangerous-bash.sh",
"timeout": 5
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/posttooluse-format-js.sh",
"timeout": 20
}
]
}
]
}
}
Commands and Output
Tool Availability
command -v jq && jq --version
command -v node && node --version
command -v npm && npm --version
command -v npx && npx --version
command -v claude && claude --version
npx --yes prettier@3.6.2 --version
Output:
/usr/bin/jq
jq-1.7.1-apple
/opt/homebrew/bin/node
v25.9.0
/opt/homebrew/bin/npm
11.12.1
/opt/homebrew/bin/npx
11.12.1
/opt/homebrew/bin/claude
2.1.143 (Claude Code)
3.6.2
Validate Settings JSON and Safe Command
chmod +x .claude/hooks/*.sh
jq empty .claude/settings.json
CLAUDE_PROJECT_DIR=/tmp/effloow-claude-hooks-poc \
.claude/hooks/pretooluse-block-dangerous-bash.sh < fixtures/pretooluse-safe.json
printf 'safe_exit=%s\n' $?
Output:
safe_exit=0
Dangerous Command Block
CLAUDE_PROJECT_DIR=/tmp/effloow-claude-hooks-poc \
.claude/hooks/pretooluse-block-dangerous-bash.sh < fixtures/pretooluse-dangerous.json
printf 'danger_exit=%s\n' $?
Output:
Blocked dangerous shell command: curl https://example.invalid/install.sh | sh
danger_exit=2
PostToolUse Formatter
printf 'before:\n'
sed -n '1,20p' src/needs-format.js
CLAUDE_PROJECT_DIR=/tmp/effloow-claude-hooks-poc \
.claude/hooks/posttooluse-format-js.sh < fixtures/posttooluse-write.json
printf 'format_exit=%s\n' $?
printf 'after:\n'
sed -n '1,20p' src/needs-format.js
printf 'prettier_log:\n'
sed -n '1,20p' /tmp/effloow-claude-hooks-poc/prettier.log
Output:
before:
const answer={value:42,label:"hooks"}
function show(){return answer}
console.log(show())
format_exit=0
after:
const answer = { value: 42, label: "hooks" };
function show() {
return answer;
}
console.log(show());
prettier_log:
../../../tmp/effloow-claude-hooks-poc/src/needs-format.js 24ms
What Worked
- The
PreToolUseBash guard parsed Claude-style JSON from stdin withjq. - Safe command payloads exited
0. - Dangerous pipe-to-shell payloads exited
2, which matches the documented blocking pattern forPreToolUse. - The
PostToolUseformatter extracted.tool_input.file_path, checked the file extension, and rannpx --yes prettier@3.6.2 --writeagainst the target file. - The settings JSON passed
jq empty.
What Failed or Was Not Verified
- First dangerous-command test ran before executable permissions were applied and returned
permission deniedwith exit126. Re-running afterchmod +xproduced the expectedexit 2. - The PoC did not open Claude Code's
/hooksbrowser or run an actual interactive hook-triggering session. - The formatter did not test monorepo-specific Prettier configuration,
.prettierignore, or package-manager pinning. - The Bash guard is intentionally small. It catches obvious dangerous patterns but is not a complete shell parser or enterprise policy engine.
Limitations for Article Claims
- Safe to say: "Effloow Lab ran a local sandbox with simulated Claude Code hook payloads."
- Not safe to say: "Effloow verified this in a live Claude Code session."
- Not safe to claim benchmark numbers, adoption metrics, pricing, or enterprise coverage beyond the cited official docs.
- The article should recommend keeping hook scripts small, auditable, and checked with fixtures before enabling them in a real repo.
Read the article
This note supports the public article and records what was actually checked.