Claude Code hooks कैसे काम करते हैं: पूरी जानकारी
Claude Code hooks का उपयोग कैसे करें। यह लेख बताता है कि hooks कब चलते हैं, exit code 2 का क्या अर्थ है और सुरक्षा के लिए इनका सही तरीके से उपयोग कैसे सुनिश्चित किया जाए।
Claude Code hook क्या है
Claude Code hooks वे shell commands हैं जिन्हें Claude Code अपने lifecycle के निश्चित बिंदुओं पर स्वयं चलाता है। hook और rules file के बीच यही मुख्य अंतर है। CLAUDE.md में दिया गया निर्देश एक सलाह है, और model इसे अपने context में मौजूद अन्य सभी चीजों के साथ तौलकर देखता है। hook एक code है, और यह model की सहमति हो या न हो, चलता ही है। यदि आपका agent बार-बार उस formatter को छोड़ देता है जिसके बारे में आपने उसे दो बार बताया है, तो आपको और अधिक सख्त निर्देश की आवश्यकता नहीं है। आपको एक hook की आवश्यकता है।
यह mechanism बहुत छोटा है। आप एक settings file में event name के अंतर्गत एक command register करते हैं। जब वह event trigger होता है, तो Claude Code आपकी command चलाता है और event data को JSON (JavaScript object notation) के रूप में उसके standard input (stdin) में लिख देता है। आपकी command उस data को पढ़ती है, अपना काम करती है, और एक exit status के साथ उत्तर देती है। PreToolUse hook से exit 2 मिलने पर tool call चलने से पहले ही रद्द हो जाता है, और आपकी script ने standard error (stderr) में जो भी लिखा होता है, वह कारण के रूप में model को वापस भेज दिया जाता है।
यहाँ दिए गए event names और field names Claude Code hooks reference से लिए गए हैं, जिसे अगस्त 2026 में release 2.1.232 के आधार पर जाँचा गया था। यह surface बहुत तेजी से बदलता है, इसलिए किसी भी blog post (जिसमें यह भी शामिल है) से JSON copy करने से पहले अपने version के लिए reference जरूर देखें। अपना JSON claude --version के साथ print करें।
हुक कॉन्फ़िगरेशन कहाँ स्थित होता है
हुक एक सेटिंग्स फ़ाइल में मौजूद JSON ब्लॉक होता है। यह छह स्थानों पर स्थित हो सकता है, और फ़ाइल का स्कोप ही हुक का स्कोप होता है।
~/.claude/settings.json: आपकी मशीन पर मौजूद हर प्रोजेक्ट, और किसी अन्य की मशीन पर नहीं।.claude/settings.json: एक प्रोजेक्ट, जिसे रिपॉजिटरी में कमिट किया गया है, ताकि इसे क्लोन करने वाले हर व्यक्ति को वह हुक मिल जाए।.claude/settings.local.json: एक प्रोजेक्ट, केवल आपकी मशीन के लिए।- प्रबंधित नीति सेटिंग्स (Managed policy settings): पूरे संगठन के लिए, जिसे एक एडमिनिस्ट्रेटर द्वारा सेट किया जाता है।
hooks/hooks.jsonएक प्लगइन के अंदर, जो तब तक सक्रिय रहता है जब तक वह प्लगइन इनेबल्ड है।- स्किल या सब-एजेंट फ्रंटमैटर, जो तब तक सक्रिय रहता है जब तक वह घटक (component) सक्रिय है।
इन फ़ाइलों से प्राप्त हुक प्रविष्टियाँ एक-दूसरे को ओवरराइड करने के बजाय आपस में मर्ज हो जाती हैं। एक प्रोजेक्ट सेटिंग्स फ़ाइल आपके हुक को आपकी यूजर सेटिंग्स में मौजूद हुक के साथ जोड़ती है, न कि उन्हें बदलती है, इसलिए एक इवेंट में कई फ़ाइलों से आए कई हुक हो सकते हैं। "disableAllHooks": true सेटिंग को सेट करने से वे बंद हो जाते हैं, लेकिन एक अपवाद है: प्रबंधित नीति सेटिंग्स (managed policy settings) से आए हुक तब तक चलते रहते हैं जब तक कि उस सेटिंग को प्रबंधित सेटिंग्स में भी लागू न किया जाए।
वर्तमान में पंजीकृत हर हुक को सूचीबद्ध करने के लिए एक सेशन के अंदर /hooks चलाएं। ये हुक इवेंट के अनुसार समूहीकृत (grouped) होते हैं और प्रत्येक के लिए सोर्स फ़ाइल और मैचिंग जानकारी दिखाई देती है। यह मेनू केवल पढ़ने (read-only) के लिए है, इसलिए हुक को बदलने के लिए आपको सेटिंग्स फ़ाइल को एडिट करना होगा। फ़ाइल वॉचर आमतौर पर बिना रीस्टार्ट किए ही बदलावों को लागू कर देता है।
Claude Code में कौन से hook events मौजूद हैं
Release 2.1.232 में 31 events सूचीबद्ध हैं, जो SessionStart से लेकर SessionEnd तक हैं, और इनमें compaction, subagents, worktrees तथा configuration files शामिल हैं। Server work इनमें से कुछ का ही उपयोग करता है।
PreToolUse: tool call निष्पादित होने से पहले। यह वह hook है जो प्रक्रिया को रोक (block) सकता है।PostToolUse: tool call सफल होने के बाद। यदि यह विफल हो जाता है तोPostToolUseFailureट्रिगर होता है, इसलिए जिस hook को हर परिणाम देखना हो, उसे दोनों की आवश्यकता होती है।PermissionRequest: जब tool call को अनुमति निर्णय की आवश्यकता होती है, यह वही क्षण है जब approval prompt दिखाई देता है।UserPromptSubmit: जब आप prompt सबमिट करते हैं, Claude द्वारा उसे process करने से पहले। यह hook stdout पर जो कुछ भी print करता है, वह model के context में जुड़ जाता है।SessionStartऔरSessionEnd: session के प्रत्येक छोर पर।SessionStart, compaction के बाद भी fire होता है, जिसका matcher valuecompactहै।Stop: जब Claude प्रतिक्रिया देना समाप्त करता है। यह प्रति turn एक बार होता है, न कि प्रत्येक पूर्ण कार्य पर एक बार।
प्रत्येक group में एक matcher होता है जो यह तय करता है कि कौन सी घटनाएँ hook को run करेंगी। Tool events पर यह tool के नाम के आधार पर filter करता है, इसलिए "Edit|Write" केवल file edits पर fire होता है और किसी अन्य पर नहीं। Matchers case-sensitive होते हैं। एक खाली matcher हर घटना पर fire होता है। MCP (model context protocol) server के tools का नाम mcp__<server>__<tool> होता है, इसलिए "mcp__github__.*" का matcher एक server के tools को पकड़ता है और बाकी को छोड़ देता है।
Stop hooks में एक trap है जिसे लिखने से पहले जानना आवश्यक है। एक Stop hook जो block करता है, वह model को वापस काम पर भेज देता है, और Claude Code लगातार आठ बार block करने के बाद hook को override कर देता है। Hook input से stop_hook_active field को पढ़ें और जब यह true हो तो exit 0 करें, अन्यथा आपका hook उस सीमा तक पहुँचने तक loop में चलता रहेगा।
हुक stdin पर क्या प्राप्त करता है
जब Claude npm test चलाने वाला होता है, तो Bash पर स्थित एक PreToolUse हुक stdin पर इसे पढ़ता है:
{
"session_id": "abc123",
"cwd": "/home/deploy/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}प्रत्येक इवेंट में session_id, cwd, permission_mode, transcript_path और hook_event_name होते हैं। टूल इवेंट्स में tool_name, tool_input और tool_use_id जुड़ जाते हैं। अन्य इवेंट्स में उनके अपने फील्ड्स होते हैं: UserPromptSubmit में prompt टेक्स्ट मिलता है, और SessionStart में startup, resume, clear, compact या fork का एक source मिलता है।
शेल स्क्रिप्ट के भीतर इसे पढ़ने का सामान्य तरीका jq है, और एक न्यूनतम सर्वर इमेज में यह उपलब्ध नहीं होता है। इसे पहले Ubuntu और Debian पर sudo apt install -y jq के साथ इंस्टॉल करें।
Exit status का tool call पर प्रभाव
इसके तीन परिणाम होते हैं।
- Exit 0 का अर्थ है कि आपके hook को कोई आपत्ति नहीं है।
PreToolUseपर इसका अर्थ स्वीकृति नहीं है, और सामान्य permission flow वैसे ही चलता रहता है।UserPromptSubmitऔरSessionStartपर, stdout को model के context में जोड़ दिया जाता है। - Exit 2 उन events पर action को रोकता है जिन्हें रोका जा सकता है, जिनमें
PreToolUseशामिल है, और stderr वह कारण बन जाता है जो model को दिखाया जाता है। जिन events को रोका नहीं जा सकता, जैसे किPostToolUse, उन पर block को अनदेखा कर दिया जाता है, हालाँकि stderr फिर भी feedback के रूप में model तक पहुँचता है। - कोई अन्य exit code एक non-blocking error है। Action जारी रहता है। Transcript में
Failed with non-blocking status code:टेक्स्ट के बाद stderr की पहली पंक्ति के साथ एक hook error सूचना दिखाई देती है।
Block करने या चुप रहने के अलावा किसी अन्य कार्य के लिए, exit 0 का उपयोग करें और stdout पर एक JSON object print करें। एक PreToolUse hook permissionDecision के साथ निर्णय लेता है:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Database drops go through a migration, not through the agent."
}
}"allow" interactive prompt को छोड़ देता है, "deny" call को रद्द कर देता है और कारण model को भेज देता है, और "ask" prompt को सामान्य रूप से दिखाता है। प्रति hook एक शैली चुनें। Exit 2 को stdout पर JSON निर्णय के साथ मिलाने पर आपको ऐसा परिणाम मिलेगा जिसे आपको खोजना होगा।
जब कई hooks एक event से मेल खाते हैं, तो वे समानांतर (parallel) चलते हैं और उनमें से प्रत्येक पूर्ण होने तक चलता है। एक hook से प्राप्त deny अपने साथियों को नहीं रोकता है, इसलिए एक logging hook अपनी पंक्ति लिखता रहता है जबकि एक guardrail hook उसी call को अस्वीकार कर देता है। Claude Code फिर उत्तरों को मिलाता है और सबसे प्रतिबंधात्मक (restrictive) को चुनता है, जिसका क्रम deny, defer, ask, allow है।
उदाहरण 1: किसी विनाशकारी कमांड को चलने से पहले ब्लॉक करना
इसे अपने प्रोजेक्ट में .claude/hooks/block-destructive.sh के रूप में सेव करें:
#!/bin/bash
# Deny a Bash tool call whose command matches a banned pattern.
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
for pattern in 'rm -rf /' 'mkfs' 'dd if=' 'DROP TABLE'; do
if printf '%s' "$COMMAND" | grep -qiF -- "$pattern"; then
echo "Blocked by policy: the command matches '$pattern'. A human runs this one." >&2
exit 2
fi
done
exit 0इसे executable बनाएँ, फिर इसे .claude/settings.json में PreToolUse पर रजिस्टर करें:
chmod +x .claude/hooks/block-destructive.sh{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive.sh",
"timeout": 10,
"statusMessage": "Checking the command against policy"
}
]
}
]
}
}इस पर भरोसा करने से पहले स्क्रिप्ट को मैन्युअल रूप से टेस्ट करें, क्योंकि यदि कोई हुक अपने ही इनपुट पर क्रैश हो जाता है, तो वह सुरक्षा को खुला छोड़ देता है:
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /var/lib/postgresql"}}' \
| .claude/hooks/block-destructive.sh
echo $?आपको stderr पर Blocked by policy: लाइन और 2 का exit code दिखाई देना चाहिए। इसे ls -la जैसा कोई हानिरहित कमांड दें, तो आपको कोई आउटपुट नहीं दिखना चाहिए और exit code 0 होना चाहिए। एक सेशन में, अस्वीकृत कॉल ट्रांसक्रिप्ट में आपके संदेश के साथ कारण के रूप में दिखाई देती है, और मॉडल उस संदेश को पढ़कर खुद को अनुकूलित कर लेता है।
एक विशेषता इसे उपयोगी बनाती है: PreToolUse हुक permission-mode चेक से पहले, हर permission mode में चलते हैं, इसलिए bypassPermissions के तहत भी अस्वीकृति (deny) प्रभावी रहती है। यही बात इसे Claude Code auto mode और इसकी permission settings के साथ उपयोगी बनाती है, जहाँ प्रॉम्प्ट्स को कम कर दिया जाता है लेकिन हुक फिर भी चलता है।
यह क्या है, इसके बारे में स्पष्ट रहें। कमांड स्ट्रिंग पर पैटर्न मैचिंग एक एजेंट की लापरवाही के खिलाफ एक सुरक्षा कवच (guardrail) है, न कि किसी चतुर एजेंट के खिलाफ कोई सीमा, क्योंकि उसी कमांड को ऐसे रूप में लिखा जा सकता है जिसे आपका grep कभी देख ही न पाए। सख्त नियम permission system और उस अकाउंट में होने चाहिए जिसके तहत प्रोसेस चलती है।
उदाहरण 2: प्रत्येक edit के बाद format और lint करना
PostToolUse एक Edit|Write matcher के साथ किसी भी file-editing tool के बाद चलता है। इसे .claude/hooks/after-edit.sh के रूप में save करें:
#!/bin/bash
# Format the edited file, then report lint failures back to the model.
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
case "$FILE" in
*.py)
ruff format "$FILE" >/dev/null 2>&1
if ! ruff check "$FILE" >&2; then
exit 2
fi
;;
*.sh)
if ! shellcheck "$FILE" >&2; then
exit 2
fi
;;
esac
exit 0{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/after-edit.sh",
"timeout": 60
}
]
}
]
}
}Claude से किसी Python file में गलत indentation वाला function जोड़ने के लिए कहें, फिर file खोलें। यह formatted होकर वापस आती है। यह इस बात की जाँच है कि hook चल गया है, क्योंकि एक सफल hook conversation में कुछ भी नहीं दिखाता है।
यहाँ exit 2 किसी भी चीज़ को undo नहीं करता है। PostToolUse तब चलता है जब tool पहले ही execute हो चुका होता है, इसलिए edit किसी भी स्थिति में disk पर मौजूद रहता है। exit 2 का लाभ यह है कि ruff check का output feedback के रूप में model तक पहुँच जाता है, जिससे वह आगे बढ़ने के बजाय अपनी गलती को तुरंत सुधार लेता है। यही commit के समय मिलने वाली lint failure और agent द्वारा उसी turn में सुधारी गई failure के बीच का अंतर है।
यहाँ दो matcher limits महत्वपूर्ण हैं। Edit|Write shell command द्वारा बदली गई files को नहीं देख पाता है, और Claude अक्सर Bash के माध्यम से files लिखता है, इसलिए यह कमी वास्तविक है। प्रति-call coverage के लिए, Bash को भी match करें और script से git status --porcelain का उपयोग करके बदली गई files की सूची प्राप्त करें। प्रति-turn coverage के लिए, scan को Stop hook में रखें।
उदाहरण 3: ऑडिट के लिए प्रत्येक टूल कॉल को लॉग करना
PostToolUse पर एक खाली matcher हर टूल पर ट्रिगर होता है। रिकॉर्ड को home directory की किसी फाइल के बजाय system journal में भेजने से यह agent के अपने shell की पहुँच से बाहर रहता है:
{
"hooks": {
"PostToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "jq -c '{time: now|todate, session: .session_id, cwd: .cwd, tool: .tool_name, input: .tool_input}' | logger -t claude-code -p local0.info"
}
]
}
]
}
}इसे journalctl -t claude-code -o cat | tail -n 5 के साथ वापस पढ़ें। आपको प्रति टूल कॉल एक JSON लाइन दिखनी चाहिए, जिसमें सबसे नई लाइन अंत में होगी। यदि कुछ भी दिखाई नहीं देता है, तो इसका मतलब है कि hook नहीं चला है, और नीचे दिया गया troubleshooting अनुभाग इसे कवर करता है।
विफल हुए कॉल्स को कैप्चर करने के लिए PostToolUseFailure के अंतर्गत समान ब्लॉक जोड़ें, क्योंकि PostToolUse केवल सफलता पर ट्रिगर होता है और एक विफल कमांड आमतौर पर अधिक महत्वपूर्ण होती है। अपनी home directory में फाइल में append करने के बजाय logger का उपयोग करने का कारण ownership है: एक hook उसी user के रूप में चलता है जिस user का agent का shell है, इसलिए जिस फाइल में वह user append कर सकता है, उसे वह user truncate भी कर सकता है। Journal को systemd-journald द्वारा उसके अपने account के अंतर्गत लिखा जाता है।
हुक (hook) कितनी देर तक चल सकता है
The data behind this chart
[
{
"label": "command, http or mcp_tool hook",
"default_timeout_seconds": 600
},
{
"label": "agent hook",
"default_timeout_seconds": 60
},
{
"label": "prompt hook",
"default_timeout_seconds": 30
},
{
"label": "command hook on UserPromptSubmit",
"default_timeout_seconds": 30
},
{
"label": "command hook on MessageDisplay",
"default_timeout_seconds": 10
},
{
"label": "any hook on SessionEnd",
"default_timeout_seconds": 1.5
}
]एक command हुक को डिफ़ॉल्ट रूप से 600 सेकंड मिलते हैं, जो कि दस मिनट है। कुछ events इस समय को काफी कम कर देते हैं। SessionEnd हुक आपस में 1.5 सेकंड का एक साझा बजट साझा करते हैं, इसलिए session के अंत में होने वाली cleanup प्रक्रिया को तेज़ होना चाहिए। हालाँकि, हुक पर एक लंबा timeout सेट करने से यह साझा बजट बढ़कर 60 सेकंड तक हो सकता है।
जो हुक अपने timeout तक पहुँच जाता है, उसे रद्द कर दिया जाता है और वह कोई निर्णय नहीं देता है। PreToolUse गार्डरेल के लिए इसका अर्थ है कि यह किसी प्रक्रिया को रोकता नहीं है: tool call सामान्य अनुमति प्रवाह (permission flow) में आगे बढ़ जाता है। इसी कारण से गार्डरेल स्क्रिप्ट्स को छोटा रखें। ऐसे धीमे कार्यों के लिए जिनका इंतज़ार कोई नहीं कर रहा है, जैसे कि कहीं log भेजना, "async": true सेट करें। इससे हुक background में चलेगा और tool call को रोकेगा नहीं।
Hooks, rules files, skills और MCP servers
चार चीजें आपस में भ्रमित करती हैं क्योंकि वे सभी एजेंट के कार्य करने के तरीके को बदलती हैं। उनमें से केवल एक ही सुझाव बने रहने से रुकती है।
एक rules file (CLAUDE.md, या .claude/rules/ के अंतर्गत कोई फाइल) मॉडल के context में लोड किया गया टेक्स्ट है। यह व्यवहार को आकार देती है और किसी चीज को लागू (enforce) नहीं करती है। एक लंबी बातचीत, एक बड़े diff और एक नए user request के सामने, इसकी एक लाइन अपना प्रभाव खो सकती है। यही वह सामान्य कारण है जिसके पीछे एजेंट आपके द्वारा लिखे गए निर्देशों को अनदेखा करते हैं।
एक skill निर्देशों और scripts का एक फोल्डर है जिसे मॉडल तब लोड करता है जब उसे लगता है कि skill प्रासंगिक है। वह निर्णय ही skill का मुख्य बिंदु है, और यही इसकी सीमा भी है: निर्णय अभी भी मॉडल ही लेता है। आप Ponytail, जो एजेंट को सबसे छोटे प्रभावी बदलाव की ओर धकेलता है जैसी skill में दोनों पहलुओं को देख सकते हैं, क्योंकि यह किसी कार्य के दृष्टिकोण को उस तरह से आकार देती है जैसा कोई hook नहीं कर सकता, और केवल तब तक जब तक मॉडल इसे लोड करना चुनता है।
एक MCP (model context protocol) server मॉडल को कॉल करने के लिए नए tools देता है। यह एजेंट की पहुँच को बढ़ाता है। यह एजेंट को किसी चीज के लिए मजबूर नहीं करता है, और यह एक अलग process है जिसे आपको संचालित करना होता है, जो अपने आप में एक काम है: देखें VPS पर MCP servers चलाना।
एक hook इन चारों में से एकमात्र ऐसी चीज है जो मॉडल के चयन के बिना चलती है। प्राथमिकता के लिए rules file का उपयोग करें और उस प्रक्रिया के लिए skill का उपयोग करें जिसका पालन मॉडल को तब करना चाहिए जब वह उसे लागू करे। उस चरण के लिए hook का उपयोग करें जो हर बार होना चाहिए, या उस चीज के लिए जो कभी नहीं होनी चाहिए। अधिक विस्तृत तुलना, जिसमें यह भी शामिल है कि कब एक skill, rules file से बेहतर होती है, skills, MCP और rules files की तुलना में दी गई है।
एक plugin पांचवीं कार्यप्रणाली के बजाय पैकेजिंग है। यह hooks को skills के साथ एक installable unit में बंडल करता है, जो कि वह तरीका है जिससे एक टीम हर machine पर समान guardrail भेजती है: देखें Claude Code plugins कैसे काम करते हैं।
Shared VPS पर सुरक्षा संबंधी निर्णय
Hook वह कोड है जिसे agent trigger करता है, और यह उसी user के रूप में चलता है जिसने Claude Code शुरू किया है। यह उस user के environment और file permissions को inherit करता है। लैपटॉप पर यह एक workflow का प्रश्न है। ऐसे VPS पर जहाँ agent बिना निगरानी के चलता है, यह चार व्यावहारिक भागों वाला एक सुरक्षा प्रश्न है।
Repository में मौजूद hook वह कोड है जिसे आपने नहीं लिखा है। .claude/settings.json commit किया जाता है, इसलिए repository को clone करना और उसके अंदर session शुरू करना उन hooks को register कर सकता है जो repository के साथ आए थे। Claude Code उस folder के लिए workspace trust dialog के पीछे project hooks को रोकता है, जिसका अर्थ है कि trust स्वीकार करना ही वह क्षण है जब आप उन्हें चलाने का निर्णय लेते हैं। पहले hooks block को पढ़ें।
एक hook पूरा tool input देख सकता है। एक audit hook जो tool_input को log करता है, हर command के हर argument को एक file में लिखता है, जिसमें command line पर मौजूद कोई भी token शामिल हो सकता है। उस log को फिर उसी सुरक्षा की आवश्यकता होती है जिसकी secret को है, जो AI agent की पहुँच से secrets को दूर रखने की व्यापक समस्या का हिस्सा है।
एक hook model के context में लिख सकता है। जो कुछ भी SessionStart या UserPromptSubmit hook stdout पर print करता है, उसे conversation में जोड़ दिया जाता है। बाहर से, किसी issue tracker या log file से text pipe करने वाला hook, untrusted text को model को ऐसे सौंप रहा है जैसे आपने इसे खुद type किया हो। उस stdout को output के बजाय input के रूप में मानें।
Privilege ही वास्तविक नियंत्रण है। Agent को केवल उन sudo rules के साथ एक समर्पित unprivileged user के रूप में चलाएं जिनकी उसे आवश्यकता है। PreToolUse deny का होना फायदेमंद है, और यह design के अनुसार best effort है: reference में if filter के बारे में भी यही कहा गया है और यह सलाह दी गई है कि जब आपको hard deny की आवश्यकता हो तो permission system का उपयोग करें। Permission rules और वह account जिसके तहत process चलती है, वे हिस्से हैं जो दबाव में भी टिके रहते हैं।
हर configuration में एक गुण कायम रहता है। PreToolUse hooks हर permission mode में permission-mode check से पहले fire होते हैं, इसलिए deny return करने वाला hook bypassPermissions के तहत भी tool को block कर देता है। Hooks उन चीजों को सीमित कर सकते हैं जिनकी permission rules अनुमति देते हैं। वे उन्हें ढीला नहीं कर सकते।
मेरा हुक (hook) क्यों नहीं चल रहा है?
इसे क्रमवार तरीके से जाँचें। प्रत्येक चरण में वह लक्षण बताया गया है जो आपको वास्तव में दिखाई देगा।
/hooksचलाएँ और देखें कि क्या हुक उस इवेंट के अंतर्गत दिखाई दे रहा है जिसकी आपने अपेक्षा की थी। यदि मेनू में हुक गायब है, तो इसका मतलब आमतौर पर यह है कि सेटिंग्स फ़ाइल में JSON सिंटैक्स त्रुटि है, क्योंकि इसमें ट्रेलिंग कॉमा (trailing commas) और टिप्पणियों (comments) की अनुमति नहीं है, या फ़ाइल ऊपर बताए गए छह स्थानों में से किसी में भी नहीं है।- मैचर (matcher) की तुलना टूल के नाम से बिल्कुल सटीक रूप से करें। मैचर्स केस-सेंसिटिव होते हैं, इसलिए
"bash"कभी भीBashटूल से मैच नहीं करेगा। - स्क्रिप्ट को ऊपर दिए गए उदाहरण 1 की तरह सैंपल इनपुट के साथ मैन्युअल रूप से चलाएँ। यदि आपको कोई अनपेक्षित एग्जिट कोड (exit code) मिलता है, तो यह आपकी स्क्रिप्ट में एक बग है, और Claude Code इसे निर्णय (decision) के बजाय हुक त्रुटि के रूप में रिपोर्ट करता है।
jq: command not foundका नोटिस मिलने का मतलब है कि उस मशीन परjqमौजूद नहीं है। आपकी अपनी स्क्रिप्ट के लिएcommand not foundका मतलब है कि पाथ (path) रिज़ॉल्व नहीं हुआ, इसलिए${CLAUDE_PROJECT_DIR}या एब्सोल्यूट पाथ (absolute path) का उपयोग करें। यदि स्क्रिप्ट बिल्कुल भी नहीं चलती है, तो संभवतः वह एक्जीक्यूटेबल (executable) नहीं है।- हुक वैध JSON प्रिंट करता है लेकिन कुछ नहीं होता है। शेल-फॉर्म हुक
sh -cके माध्यम से चलता है, और यदि आपका शेल प्रोफ़ाइल कोई बैनर प्रिंट करता है, तो वह बैनर आपके JSON के आगे जुड़ जाता है। Stdout अब{से शुरू नहीं होता है, इसलिए Claude Code पूरी चीज़ को प्लेन टेक्स्ट के रूप में पढ़ता है और निर्णय को अनदेखा कर देता है। एग्जिट 0 पर डिबग लॉग के अलावा कहीं भी कुछ भी रिपोर्ट नहीं किया जाता है। अपनी प्रोफ़ाइल में किसी भीechoको रैप (wrap) करें ताकि वह केवल इंटरैक्टिव शेल में ही चले। - यदि अभी भी समस्या बनी हुई है: सेशन को
claude --debug-file /tmp/claude.logके साथ शुरू करें और दूसरे टर्मिनल मेंtail -f /tmp/claude.logचलाएँ। डिबग लॉग यह रिकॉर्ड करता है कि कौन से हुक मैच हुए, प्रत्येक ने क्या एग्जिट कोड लौटाया, और उन्होंने stdout और stderr में क्या लिखा।
FAQ
Claude Code hook और CLAUDE.md instruction में क्या अंतर है?
CLAUDE.md instruction मॉडल के context में मौजूद टेक्स्ट है, इसलिए यह बातचीत और वर्तमान अनुरोध के साथ ध्यान आकर्षित करने के लिए प्रतिस्पर्धा करता है, और मॉडल इसे उनके मुकाबले तौल सकता है। एक hook वह shell command है जिसे Claude Code अपने lifecycle के एक निश्चित बिंदु पर चलाता है, इसलिए यह मॉडल के निर्णय की परवाह किए बिना हर बार उस event के होने पर निष्पादित होता है। किसी प्राथमिकता (preference) के लिए instruction का उपयोग करें। किसी ऐसे चरण के लिए hook का उपयोग करें जिसे हमेशा होना चाहिए या कोई ऐसी क्रिया जिसे कभी नहीं होना चाहिए।
मैं Claude Code को किसी विशिष्ट shell command को चलाने से कैसे रोकूँ?
एक PreToolUse hook को Bash matcher के साथ रजिस्टर करें जो .tool_input.command से कमांड को पढ़ता है, stderr पर एक कारण लिखता है और 2 के साथ exit करता है। Claude Code कॉल को रद्द कर देता है और मॉडल को आपका कारण दिखाता है, और यह permission-mode चेक से पहले होता है, इसलिए यह निषेध bypassPermissions मोड में भी प्रभावी रहता है। कमांड स्ट्रिंग पर pattern matching एक सुरक्षा सीमा (security boundary) के बजाय एक guardrail है, क्योंकि उसी कमांड को ऐसे रूप में लिखा जा सकता है जिसे pattern न पकड़ पाए, इसलिए इसे permission rules और एक unprivileged account के साथ सुरक्षित करें।
मेरा hook वैध JSON प्रिंट करता है लेकिन कुछ नहीं होता। क्यों?
सबसे सामान्य कारण आपकी shell profile है। बिना args फ़ील्ड वाला hook sh -c के माध्यम से चलता है, और कुछ profiles हर shell पर एक बैनर प्रिंट करती हैं, जो आपके JSON से पहले stdout पर आ जाता है। चूँकि आउटपुट अब { से शुरू नहीं होता, Claude Code इसे सामान्य टेक्स्ट मानता है और निर्णय को अनदेखा कर देता है, और 0 के साथ exit होने पर transcript में कुछ भी रिपोर्ट नहीं किया जाता है। अपनी profile में किसी भी echo को interactive-shell टेस्ट के साथ सुरक्षित करें, फिर claude --debug-file /tmp/claude.log से debug log पढ़कर सुधार की पुष्टि करें।
क्या shared सर्वर पर Claude Code hooks चलाना सुरक्षित है?
Hooks उस उपयोगकर्ता के रूप में चलते हैं जिसने Claude Code शुरू किया है, उस उपयोगकर्ता की file permissions के साथ, इसलिए एक hook वह सब कुछ कर सकता है जो वह account कर सकता है। दो आदतें अधिकांश जोखिम को कवर करती हैं: agent को एक सीमित sudo नीति वाले समर्पित unprivileged account के रूप में चलाएं, और किसी भी repository के workspace trust dialog को स्वीकार करने से पहले उसके hooks ब्लॉक को पढ़ें, क्योंकि project hooks .claude/settings.json के अंदर आते हैं। जब आप नहीं चाहते कि उनमें से कोई भी चले, तो अपनी settings फ़ाइल में "disableAllHooks": true सेट करें।