SSD Nodes Learn Hosting plans →
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-23

Claude Code hooks कैसे काम करते हैं और इनका उपयोग

Claude Code hooks के कार्य करने के तरीके को समझें। जानें कि ये कमांड्स मॉडल की सहमति के बिना कैसे चलते हैं, exit code 2 का प्रभाव क्या है और सुरक्षा के लिए इनका महत्व क्या है।

Claude Code hook क्या है

Claude Code hooks वे shell commands हैं जिन्हें Claude Code अपने lifecycle के निश्चित बिंदुओं पर स्वयं चलाता है। hook और rules file के बीच यही मुख्य अंतर है। CLAUDE.md में दिया गया निर्देश एक सलाह है, और model इसे अपने context में मौजूद अन्य सभी बातों के साथ तौलकर देखता है। एक hook कोड होता है, और यह तब भी चलता है चाहे 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 के आधार पर जाँचा गया था। यह क्षेत्र तेजी से बदल रहा है, इसलिए किसी भी blog post (जिसमें यह भी शामिल है) से JSON कॉपी करने से पहले अपने 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 सेटिंग उन्हें बंद कर देती है, लेकिन एक अपवाद है: प्रबंधित नीति सेटिंग्स से आने वाले हुक तब तक चलते रहते हैं जब तक कि वह सेटिंग प्रबंधित सेटिंग्स में भी लागू न की गई हो।

वर्तमान में पंजीकृत प्रत्येक हुक को सूचीबद्ध करने के लिए एक सेशन के अंदर /hooks चलाएं। ये इवेंट के अनुसार समूहीकृत होते हैं और प्रत्येक के लिए स्रोत फ़ाइल और मैचर प्रदर्शित करते हैं। मेनू केवल पढ़ने के लिए (read-only) है, इसलिए आप सेटिंग्स फ़ाइल को एडिट करके हुक में बदलाव करते हैं। फ़ाइल वॉचर आमतौर पर बिना रीस्टार्ट किए ही बदलावों को लागू कर देता है।

Claude Code में कौन-कौन से hook events मौजूद हैं

Release 2.1.232 में इकतीस events सूचीबद्ध हैं, जो SessionStart से लेकर SessionEnd तक हैं, और इनमें compaction, subagents, worktrees और configuration files शामिल हैं। सर्वर के काम में इनमें से कुछ का ही उपयोग होता है।

  • PreToolUse: tool call निष्पादित होने से पहले। यह वह event है जो प्रक्रिया को रोक (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 होता है, जो compact matcher value के अंतर्गत आता है।
  • Stop: जब Claude प्रतिक्रिया देना समाप्त करता है। यह प्रति turn एक बार होता है, न कि पूर्ण कार्य समाप्त होने पर।

प्रत्येक समूह में एक matcher होता है जो यह तय करता है कि कौन सी घटनाएँ hook को run करेंगी। tool events पर यह tool के नाम के आधार पर filter करता है, इसलिए "Edit|Write" केवल file edits पर fire होता है और किसी अन्य पर नहीं। Matchers case-sensitive होते हैं। एक खाली matcher हर घटना पर fire होता है। MCP (model context protocol) सर्वर के tools का नाम mcp__<server>__<tool> होता है, इसलिए "mcp__github__.*" का matcher एक सर्वर के 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 0 का अर्थ है कि आपका हुक कोई आपत्ति नहीं जताता है। PreToolUse पर यह स्वीकृति के समान नहीं है, और सामान्य अनुमति प्रवाह (permission flow) अभी भी चलता है। UserPromptSubmit और SessionStart पर, stdout को मॉडल के संदर्भ (context) में जोड़ दिया जाता है।
  • Exit 2 उन इवेंट्स पर कार्रवाई को रोकता है जिन्हें रोका जा सकता है, जिनमें PreToolUse शामिल है, और stderr वह कारण बन जाता है जो मॉडल को दिखाया जाता है। जिन इवेंट्स को रोका नहीं जा सकता, जैसे कि PostToolUse, उन पर ब्लॉक को अनदेखा कर दिया जाता है, हालांकि stderr अभी भी फीडबैक के रूप में मॉडल तक पहुँचता है।
  • कोई अन्य एग्जिट कोड एक नॉन-ब्लॉकिंग त्रुटि है। कार्रवाई जारी रहती है। ट्रांसक्रिप्ट में Failed with non-blocking status code: टेक्स्ट के बाद stderr की पहली पंक्ति के साथ एक हुक त्रुटि सूचना दिखाई देती है।

ब्लॉक करने या चुप रहने के अलावा किसी भी अन्य स्थिति के लिए, exit 0 का उपयोग करें और इसके बजाय stdout पर एक JSON ऑब्जेक्ट प्रिंट करें। एक PreToolUse हुक permissionDecision के साथ निर्णय लेता है:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Database drops go through a migration, not through the agent."
  }
}

"allow" इंटरैक्टिव प्रॉम्प्ट को छोड़ देता है, "deny" कॉल को रद्द कर देता है और मॉडल को कारण भेजता है, और "ask" प्रॉम्प्ट को सामान्य रूप से दिखाता है। प्रति हुक एक शैली चुनें। exit 2 को stdout पर JSON निर्णय के साथ मिलाने से आपको ऐसा परिणाम मिलेगा जिसे आपको खोजना होगा।

जब कई हुक एक ही इवेंट से मेल खाते हैं, तो वे समानांतर (parallel) रूप से चलते हैं और उनमें से प्रत्येक पूर्ण होने तक चलता है। एक हुक से प्राप्त deny अपने समकक्षों (siblings) को नहीं रोकता है, इसलिए एक लॉगिंग हुक अपनी लाइन लिखता रहता है जबकि एक गार्डरेल हुक उसी कॉल को अस्वीकार कर देता है। इसके बाद Claude Code उत्तरों को मर्ज करता है और सबसे अधिक प्रतिबंधात्मक को बनाए रखता है, जिसका क्रम इस प्रकार है: अस्वीकार (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: प्रत्येक संपादन के बाद format और lint करना

PostToolUse को एक Edit|Write matcher के साथ किसी भी फाइल-एडिटिंग टूल के बाद चलाने के लिए सेट करें। इसे .claude/hooks/after-edit.sh के रूप में सेव करें:

#!/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 फाइल में गलत indentation वाला फंक्शन जोड़ने के लिए कहें, फिर फाइल खोलें। यह फॉर्मेट होकर वापस आती है। यह इस बात की पुष्टि है कि hook चल गया है, क्योंकि एक सफल hook बातचीत में कुछ भी प्रदर्शित नहीं करता है।

यहाँ exit 2 किसी भी कार्य को undo नहीं करता है। PostToolUse तब चलता है जब टूल पहले ही निष्पादित हो चुका होता है, इसलिए संपादन किसी भी स्थिति में डिस्क पर मौजूद रहता है। exit 2 का लाभ यह है कि ruff check का आउटपुट फीडबैक के रूप में मॉडल तक पहुँच जाता है, जिससे वह आगे बढ़ने के बजाय अपने द्वारा की गई त्रुटि को ठीक कर लेता है। यही वह अंतर है जो commit के समय मिलने वाली lint विफलता और उस विफलता के बीच है जिसे एजेंट उसी टर्न में सुधार देता है।

यहाँ दो matcher सीमाएँ महत्वपूर्ण हैं। Edit|Write शेल कमांड द्वारा बदली गई फाइलों को नहीं देख पाता है, और Claude अक्सर Bash के माध्यम से फाइलें लिखता है, इसलिए यह कमी वास्तविक है। प्रति-कॉल कवरेज के लिए, Bash को भी मैच करें और स्क्रिप्ट को git status --porcelain के साथ बदली गई फाइलों की सूची बनाने के लिए कहें। प्रति-टर्न कवरेज के लिए, स्कैन को Stop hook में रखें।

उदाहरण 3: ऑडिट के लिए प्रत्येक tool call को लॉग करना

PostToolUse पर एक खाली matcher हर tool पर सक्रिय हो जाता है। रिकॉर्ड को 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 के साथ पढ़ें। आपको प्रति tool call एक JSON लाइन दिखाई देनी चाहिए, जिसमें सबसे नई लाइन अंत में होगी। यदि कुछ भी दिखाई नहीं देता है, तो इसका मतलब है कि hook नहीं चला है; नीचे दिया गया troubleshooting अनुभाग इसे कवर करता है।

विफल (failed) calls को कैप्चर करने के लिए PostToolUseFailure के अंतर्गत समान ब्लॉक जोड़ें, क्योंकि PostToolUse केवल सफलता पर ही सक्रिय होता है और एक विफल कमांड आमतौर पर अधिक महत्वपूर्ण होती है। अपनी home directory में फाइल को append करने के बजाय logger का उपयोग करने का कारण ownership है: एक hook उसी user के रूप में चलता है जिस user का agent का shell है, इसलिए जिस फाइल में वह user कुछ लिख सकता है, उसे वह user truncate भी कर सकता है। Journal को systemd-journald द्वारा उसके अपने account के तहत लिखा जाता है।

हुक (hook) कितने समय तक चल सकता है

ChartDefault hook timeout in seconds, by hook type and event
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 hook को डिफ़ॉल्ट रूप से 600 सेकंड का समय मिलता है, जो कि दस मिनट है। कुछ events इस समय को काफी कम कर देते हैं। SessionEnd हुक आपस में 1.5 सेकंड का एक साझा बजट साझा करते हैं, इसलिए session के अंत में होने वाली cleanup प्रक्रिया को तेज़ होना चाहिए। हालाँकि, हुक पर एक लंबा timeout सेट करने से यह साझा बजट बढ़कर 60 सेकंड तक हो सकता है।

जो हुक अपने timeout तक पहुँच जाता है, उसे रद्द कर दिया जाता है और वह कोई निर्णय नहीं देता है। PreToolUse guardrail के लिए इसका अर्थ है कि यह किसी प्रक्रिया को रोकता नहीं है: tool call सामान्य permission flow में आगे बढ़ जाता है। इसी कारण से guardrail scripts को छोटा रखें। ऐसे धीमे कार्यों के लिए जिनका इंतज़ार कोई नहीं कर रहा है, जैसे कि कहीं log भेजना, "async": true सेट करें। इससे हुक background में चलता है और tool call को रोकता नहीं है।

Hooks, rules files, skills और MCP servers

चार चीजें आपस में भ्रमित करती हैं क्योंकि वे सभी agent के व्यवहार को बदलती हैं। उनमें से केवल एक ही ऐसी है जो सुझाव देना बंद कर देती है।

एक rules file (CLAUDE.md, या .claude/rules/ के अंतर्गत कोई file) वह text है जिसे model के context में load किया जाता है। यह व्यवहार को आकार देती है, लेकिन किसी चीज को लागू (enforce) नहीं करती। एक लंबी बातचीत, एक बड़े diff और एक नए user request के सामने, इसकी एक पंक्ति अपना प्रभाव खो सकती है। यही वह सामान्य कारण है जिसके पीछे agents द्वारा आपके द्वारा लिखे गए निर्देशों की अनदेखी करना होता है।

एक skill निर्देशों और scripts का एक folder है जिसे model तब load करता है जब उसे लगता है कि वह skill प्रासंगिक है। वह निर्णय ही skill का मुख्य बिंदु है, और यही इसकी सीमा भी है: निर्णय अभी भी model ही लेता है। आप Ponytail, जो agent को सबसे छोटे प्रभावी बदलाव की ओर धकेलता है जैसी skill में दोनों पहलू देख सकते हैं, क्योंकि यह किसी कार्य के दृष्टिकोण को उस तरह से आकार देती है जैसा कोई hook नहीं कर सकता, और यह केवल तब तक होता है जब तक model इसे load करना चुनता है।

एक MCP (model context protocol) server model को call करने के लिए नए tools देता है। यह agent की पहुँच को बढ़ाता है। यह agent को कुछ भी करने के लिए मजबूर नहीं करता, और यह एक अलग process है जिसे आपको operate करना होता है, जो अपने आप में एक काम है: देखें VPS पर MCP servers चलाना।

इन चारों में hook ही एकमात्र ऐसी चीज है जो model के चुनाव के बिना चलती है। किसी प्राथमिकता (preference) के लिए rules file का उपयोग करें और उस प्रक्रिया (procedure) के लिए skill का उपयोग करें जिसका पालन model को तब करना चाहिए जब वह उसे लागू करे। उस चरण के लिए hook का उपयोग करें जो हर बार होना ही चाहिए, या उस चीज के लिए जो कभी नहीं होनी चाहिए। अधिक विस्तृत तुलना, जिसमें यह भी शामिल है कि कब एक skill, rules file से बेहतर होती है, skills, MCP और rules files की तुलना में दी गई है।

एक plugin पांचवीं कार्यप्रणाली के बजाय एक पैकेजिंग है। यह hooks को skills के साथ एक installable unit में बंडल करता है, जिससे एक टीम हर machine पर एक ही guardrail भेजती है: देखें Claude Code plugins कैसे काम करते हैं।

Shared VPS पर सुरक्षा संबंधी निर्णय

Hook वह कोड है जिसे agent ट्रिगर करता है, और यह उस 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 प्रोजेक्ट hooks को उस folder के लिए workspace trust dialog के पीछे रखता है, जिसका अर्थ है कि trust स्वीकार करना ही वह क्षण है जब आप उन्हें चलाने का निर्णय लेते हैं। पहले hooks ब्लॉक को पढ़ें।

एक 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 किया हो। उसी VPS पर एक और Claude Code session से note forward करने वाला hook भी यही कर रहा है, और एक agent के output का आपके भरोसे पर issue tracker से अधिक दावा नहीं है। उस 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 चलाएँ और देखें कि क्या hook उस event के अंतर्गत दिखाई दे रहा है जिसकी आपने अपेक्षा की थी। यदि menu में hook गायब है, तो इसका मतलब आमतौर पर यह है कि settings file में JSON syntax error है, क्योंकि trailing commas और comments की अनुमति नहीं है, या file ऊपर बताए गए छह स्थानों में से किसी में भी नहीं है।
  • Matcher की तुलना tool के नाम से सटीक रूप से करें। Matchers case sensitive होते हैं, इसलिए "bash" कभी भी Bash tool से match नहीं करेगा।
  • ऊपर दिए गए उदाहरण 1 की तरह, sample input के साथ script को मैन्युअल रूप से चलाएँ। यदि exit code आपकी अपेक्षा के अनुरूप नहीं है, तो यह आपकी script में एक bug है, और Claude Code इसे decision के बजाय hook error के रूप में रिपोर्ट करता है।
  • jq: command not found का notice आने का मतलब है कि उस machine पर jq मौजूद नहीं है। आपकी अपनी script के लिए command not found का मतलब है कि path resolve नहीं हुआ, इसलिए ${CLAUDE_PROJECT_DIR} या absolute path का उपयोग करें। यदि script बिल्कुल भी नहीं चलती है, तो संभवतः वह executable नहीं है।
  • Hook वैध JSON print करता है लेकिन कुछ नहीं होता है। Shell-form hook sh -c के माध्यम से चलता है, और यदि आपका shell profile कोई banner print करता है, तो वह banner आपके JSON के आगे जुड़ जाता है। Stdout अब { से शुरू नहीं होता है, इसलिए Claude Code पूरी चीज़ को plain text के रूप में पढ़ता है और decision को अनदेखा कर देता है। Exit 0 पर debug log के अलावा कहीं भी कुछ भी रिपोर्ट नहीं किया जाता है। अपने profile में किसी भी echo को wrap करें ताकि वह केवल interactive shells में ही चले।
  • यदि अभी भी समस्या बनी हुई है: session को claude --debug-file /tmp/claude.log के साथ शुरू करें और दूसरे terminal में tail -f /tmp/claude.log चलाएँ। Debug log यह रिकॉर्ड करता है कि कौन से hooks match हुए, प्रत्येक ने क्या exit code लौटाया, और उन्होंने stdout और stderr पर क्या लिखा।

FAQ

Claude Code hook और CLAUDE.md instruction में क्या अंतर है?

CLAUDE.md instruction मॉडल के context में मौजूद टेक्स्ट होता है, इसलिए यह बातचीत और वर्तमान अनुरोध के साथ ध्यान आकर्षित करने के लिए प्रतिस्पर्धा करता है, और मॉडल इसे उनके मुकाबले तौल सकता है। Hook एक shell command है जिसे Claude Code अपने lifecycle के एक निश्चित बिंदु पर चलाता है, इसलिए यह मॉडल के निर्णय की परवाह किए बिना हर बार उस घटना के घटित होने पर निष्पादित होता है। किसी प्राथमिकता (preference) के लिए instruction का उपयोग करें। किसी ऐसे चरण के लिए hook का उपयोग करें जिसे हमेशा होना चाहिए या किसी ऐसी कार्रवाई के लिए जिसे कभी नहीं होना चाहिए।

मैं Claude Code को एक विशिष्ट shell command चलाने से कैसे रोकूँ?

एक PreToolUse hook को Bash matcher के साथ रजिस्टर करें जो .tool_input.command से command को पढ़ता है, stderr पर एक कारण लिखता है और 2 के साथ exit करता है। Claude Code कॉल को रद्द कर देता है और मॉडल को आपका कारण दिखाता है, और यह permission-mode चेक से पहले होता है, इसलिए यह निषेध bypassPermissions मोड में भी प्रभावी रहता है। command string पर pattern matching एक सुरक्षा सीमा (security boundary) के बजाय एक guardrail है, क्योंकि उसी command को ऐसे रूप में लिखा जा सकता है जिसे pattern न पकड़ पाए, इसलिए इसे permission rules और एक unprivileged account के साथ सुरक्षित करें।

मेरा hook वैध JSON प्रिंट करता है लेकिन कुछ नहीं होता। क्यों?

सबसे सामान्य कारण आपकी shell profile है। args field के बिना एक hook, sh -c के माध्यम से चलता है, और कुछ profiles हर shell पर एक banner प्रिंट करती हैं, जो आपके JSON से पहले stdout पर आ जाता है। चूँकि output अब { से शुरू नहीं होता, इसलिए Claude Code इसे plain text मानता है और निर्णय को अनदेखा कर देता है, और 0 के साथ exit होने पर transcript में कुछ भी रिपोर्ट नहीं किया जाता है। अपनी profile में किसी भी echo को interactive-shell test के साथ सुरक्षित करें, फिर claude --debug-file /tmp/claude.log से debug log पढ़कर सुधार की पुष्टि करें।

क्या shared server पर Claude Code hooks चलाना सुरक्षित है?

Hooks उस user के रूप में चलते हैं जिसने Claude Code शुरू किया है, उस user की file permissions के साथ, इसलिए एक hook वह सब कुछ कर सकता है जो वह account कर सकता है। दो आदतें अधिकांश जोखिम को कवर करती हैं: agent को एक सीमित sudo policy वाले dedicated unprivileged account के रूप में चलाएं, और किसी भी repository के workspace trust dialog को स्वीकार करने से पहले उसके hooks ब्लॉक को पढ़ें, क्योंकि project hooks .claude/settings.json के अंदर आते हैं। जब आप नहीं चाहते कि उनमें से कोई भी चले, तो अपनी settings file में "disableAllHooks": true सेट करें।