Skip to content

हुक (Hooks): विशिष्ट समय पर स्वचालित रूप से ट्रिगर होना

📚 सीरीज नेविगेशन: पिछला लेख 32 输出样式(Output Styles) आपको सिखाता है कि Claude को अपनी पसंद की टोन में काम करने के लिए "व्यक्तित्व" के एक सेट को कैसे बदलना है। यह लेख एक अन्य प्रकार के "स्वचालन" (automation) के बारे में बात करता है—यह नहीं कि यह कैसे बोलता है, बल्कि जैसे ही कोई विशिष्ट घटना घटित होती, यह आपके लिए बिना किसी असफलता के एक क्रिया चलाता है: फ़ाइलों को संशोधित करने के बाद स्वचालित स्वरूपण (formatting), खतरनाक कमांड्स को सीधे ब्लॉक करना, या काम पूरा होने पर आपको अधिसूचना भेजना। ये हुक (Hooks) हैं।

इस संख्या की कल्पना करें: एक सप्ताह के भीतर, Claude द्वारा कोड को संशोधित करने के बाद, prettier --write को मैन्युअल रूप से चलाने की संख्या 23 बार थी

23 बार। एक ही क्रिया को यांत्रिक रूप से 23 बार दोहराया गया। इससे भी बदतर, बीच में दो बार यह छूट गया—जिसके कारण सबमिट करने के बाद इसे सीआई (CI) के स्वरूपण जांच द्वारा अस्वीकार कर दिया गया, और पूरी प्रक्रिया को फिर से चलाना पड़ा।

इस बिंदु पर, आपको एक बात सोचनी चाहिए: यह क्रिया जिसे "हर बार किया जाना चाहिए, और सामग्री बिल्कुल वही है", इसके लिए हमें मनुष्यों के याद रखने या Claude के स्व-अनुशासन पर क्यों निर्भर रहना चाहिए? CLAUDE.md में लिखा गया था कि "फ़ाइलों को संशोधित करने के बाद prettier चलाना याद रखें", लेकिन तीन बार में से एक बार यह इसे भूल जाता है—क्योंकि वह केवल एक अनुरोध था, कोई आश्वासन नहीं।

और एक हुक (Hook) को कॉन्फ़िगर करने से, केवल एक पंक्ति के कॉन्फ़िगरेशन से, यह समस्या पूरी तरह से हल हो जाती है: उसके बाद से, हर बार जब Claude किसी फ़ाइल को संपादित करता है, तो स्वरूपण स्वचालित रूप से चलता है, और हमें कभी भी मैन्युअल रूप से prettier नहीं चलाना पड़ा, और न ही सीआई द्वारा कभी अस्वीकार किया गया। यह लेख समझाएगा कि यह "स्वचालित ट्रिगर" क्या है, इसे कैसे कॉन्फ़िगर करें, और इसका उपयोग कैसे करें।

इस लेख को पढ़ने के बाद, आपको मिलेगा:

  • एक वाक्य में स्पष्टीकरण कि Hook क्या है और यह CLAUDE.md में लिखे गए अनुरोधों से बुनियादी रूप से कैसे भिन्न है
  • Claude के कार्य लाइफसाइकल में आखिरकार कौन से "समय" हैं जहाँ हुक को जोड़ा जा सकता है (PreToolUse / PostToolUse / Stop / SessionStart आदि)
  • हुक किस फ़ाइल में कॉन्फ़िगर किया जाता है, और matcher इसे कैसे संकुचित करता है ताकि यह "केवल फ़ाइल संशोधित होने पर ट्रिगर हो"
  • तीन वास्तविक उदाहरण जिन्हें आप सीधे उपयोग कर सकते हैं: संशोधन के बाद स्वचालित स्वरूपण, खतरनाक कमांड्स को ब्लॉक करना, और काम पूरा होने पर सूचना भेजना
  • हुक और Claude के बीच संचार कैसे होता है (stdin का JSON, निकास कोड (exit code), stdout) — यह सब कुछ समझने की कुंजी है
  • जब हुक ट्रिगर न हो या त्रुटि उत्पन्न हो, तो चरण-दर-चरण समस्या का पता कैसे लगाएं

01 पहले समझें: Hook आखिरकार क्या है, और यह किस प्रकार "आश्वासन" प्रदान करता है

पहले निष्कर्ष: Hook "एक विशिष्ट घटना घटित होने पर स्वचालित रूप से निष्पादित होने वाली कमांड या अनुरोध है"—यह निर्णय लेने के लिए Claude की सोच पर निर्भर नहीं करता है, इसका ट्रिगर होना सुनिश्चित है। (सबसे आम उपयोग shell कमांड है, और यह HTTP एंडपॉइंट्स, MCP टूल्स, और LLM संकेतों आदि का भी समर्थन करता है।)

आधिकारिक परिभाषा बहुत स्पष्ट है, इसे पहले देखें:

Hooks 是用户定义的 shell 命令,在 Claude Code 生命周期中的特定点执行。它们对 Claude Code 的行为提供确定性控制,确保某些操作始终发生,而不是依赖 LLM 选择运行它们。

इसमें दो शब्दों पर ध्यान दें: "निश्चित नियंत्रण", "हमेशा घटित होना"。这就是 Hook 的命门所在。

एनालॉजी: घर पर सेट किए गए स्वचालन नियम ("जब... तब...")। आपने स्मार्ट होम उपकरणों के लिए इस प्रकार के नियम सेट किए होंगे—"जब कोई दरवाजा खोले, तब लाइट चालू करें", "जब मैं घर से बाहर जाऊँ, तब सभी प्लग बंद करें"। जैसे ही शर्त पूरी होती है, क्रिया अनिवार्य रूप से घटित होती है, किसी को याद रखने की आवश्यकता नहीं होती। Hook, Claude Code के लिए इसी प्रकार के नियमों का सेट है—आप यह परिभाषित करते हैं कि "जब कोई विशिष्ट घटना घटित हो, तब इस कमांड को चलाएं", और यह बिना किसी असफलता के स्वचालित रूप से निष्पादित होता है।

这里要把一个最关键的区别钉死:写进 CLAUDE.md 的是「请求」——它大概率照办,但可能漏;配成 Hook 的是「保证」——只要那个事件触发,动作一定执行,跟 Claude 记不记得没半点关系。官方的话:

CLAUDE.md 或 skill 中的「永远不要编辑 .env」之类的说明是请求,而不是保证。阻止编辑的 PreToolUse hook 是强制执行。

这就是开头那 23 次的根源:「改完跑 prettier」写在 CLAUDE.md 里是请求,三次漏一次;配成 Hook 是保证,一次不漏。

几个你大概率会遇到、值得「上保证」的场景,先感受一下:

  • "हर बार फ़ाइल को संशोधित करने के बाद, स्वचालित रूप से स्वरूपण / lint चलाएं" — इसे दोबारा मैन्युअल रूप से न चलाएं, और न ही इसके स्व-अनुशासन पर भरोसा करें
  • "rm -rf या उत्पादन डेटाबेस को हटाने जैसी कमांड्स को पूरी तरह से ब्लॉक करें" — इसे ब्लॉक करना सुनिश्चित करना होगा, संकेतों पर भरोसा नहीं किया जा सकता
  • "काम पूरा होने पर, या जब यह मेरे इनपुट की प्रतीक्षा कर रहा हो, मुझे एक डेस्कटॉप अधिसूचना भेजें" — ताकि आप अन्य काम कर सकें और आपको टर्मिनल पर नज़र रखने की आवश्यकता न हो

💡 一句话总结:Hook 是「事件触发的自动动作」,核心价值是把「请求」变成「保证」——CLAUDE.md 拜托它做的事可能漏,Hook 挂上的事件一触发就必然执行。


02 हुक जोड़ने के लिए कौन से "समय" उपलब्ध हैं: लाइफसाइकल घटनाओं को समझना

Hook 不是随便什么时候都能挂,它得挂在 Claude 干活流程里特定的「时机」上。这些时机,官方叫事件(event)。想用好 Hook,第一步就是认清「你想让这事在什么时候发生」。

回想第 03 篇讲的「代理循环」——Claude 干活是「想 → 做 → 看」转圈。这些事件,正好散布在这个循环的前前后后。官方把它们按触发频率分成了三档,这个分法特别好记:

  • प्रत्येक सत्र में एक बार: SessionStart (sत्र शुरू या बहाल होने पर), SessionEnd (सत्र समाप्त होने पर)
  • प्रत्येक दौर (turn) में एक बार: UserPromptSubmit (जब आपने अभी-अभी संकेत सबमिट किया है और Claude ने अभी प्रसंस्करण शुरू नहीं किया है), Stop (जब Claude ने इस दौर का उत्तर दे दिया है)
  • एजेंट लूप में प्रत्येक टूल कॉल पर: PreToolUse (किसी टूल के चलने से ठीक पहले), PostToolUse (किसी टूल के सफलतापूर्वक चलने के बाद)

光说有点抽象,画张图你一眼就懂这几个最常用的事件卡在哪儿:

Claude Code हुक के 7 अवसर: SessionStart → UserPromptSubmit → Pre/Post टूल लूप → Stop → SessionEnd

यह आरेख "सोचना → करना → देखना" के चक्र को दर्शाता है: सत्र में प्रवेश करने पर SessionStart होता है, आपके निर्देश सबमिट करने पर UserPromptSubmit होता है, और फिर यह "टूल का उपयोग करना है या नहीं" के चक्र में प्रवेश करता है। प्रत्येक टूल कॉल से पहले PreToolUse और बाद में PostToolUse होता है। चक्र समाप्त होने पर Stop और सत्र समाप्त होने पर SessionEnd होता है। आप जिस चरण पर क्रिया को घटित करना चाहते हैं, उससे संबंधित घटना को चुनें।

这六个是日常用得最多的。其实官方支持的事件有三十来个(比如压缩前后的 PreCompact/PostCompact、文件落盘变动的 FileChanged、配置被改的 ConfigChange、子代理起停的 SubagentStart/SubagentStop 等),但对小白来说,先把下面这四个吃透,能覆盖九成场景

घटनायह कब ट्रिगर होती हैसबसे विशिष्ट उपयोग
PreToolUse某个工具执行前拦危险命令、保护敏感文件(能阻止操作
PostToolUse某个工具成功执行后改完文件自动格式化 / 跑 lint
StopClaude 答完这一轮提醒「活还没干完,继续」、扫一遍工作区
SessionStart会话开始或恢复时往上下文里注入项目状态(如最近的提交)

记这张表有个窍门:看名字里的 PrePost——Pre 是「之前」,所以只有它能在动作发生前拦住Post 是「之后」,工具都跑完了,它只能「事后补一刀」(格式化、记日志),拦不了。这个差别下一节细说。

💡 一句话总结:Hook 挂在 Claude 生命周期的特定事件上,按频率分三档(每会话 / 每轮 / 每次工具调用);新手先吃透 PreToolUse(前,能拦)、PostToolUse(后,补刀)、Stop(答完)、SessionStart(开场)这四个就够用。


03 हुक कहाँ कॉन्फ़िगर किया जाता है, और matcher इसे कैसे सीमित करता

Hook 写在设置文件(settings.json)里——就是第 31 篇专门讲过的那套配置文件。写在哪个文件,决定了它管多大范围

यह किस फ़ाइल में कॉन्फ़िगर किया जाता हैप्रभाव का दायराक्या इसे टीम के साथ साझा किया जा सकता है
~/.claude/settings.jsonआपके सभी प्रोजेक्ट्सनहीं, केवल आपकी मशीन पर
.claude/settings.json(项目根)仅当前项目,可以提交进 git
.claude/settings.local.json(项目根)仅当前项目否,被 gitignored

这跟第 31 篇讲配置时的「项目档案柜 vs 工位抽屉」是同一套逻辑:全队都该有的 Hook(比如「改完一律格式化」)写进项目的 .claude/settings.json 提交进 git;只是你自己想要的(比如发通知到你的桌面)写进 ~/.claude/settings.json

Hook 配置长什么样

先看一个最小的完整例子——「每次用 Edit 或 Write 改完文件,自动跑 prettier 格式化」,就是治好那 23 次毛病的那段。写进项目根目录的 .claude/settings.json

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

别被这层嵌套吓到,它就三层,对着拆一下你立刻懂:

  1. "PostToolUse" — किस घटना को चुनना है (यहाँ: टूल के निष्पादित होने के बाद)。
  2. "matcher": "Edit|Write"किन टूल्स तक सीमित करना है ताकि यह ट्रिगर हो (यहाँ: केवल Edit या Write टूल के बाद, Bash या Read के बाद नहीं)。
  3. भीतर का hooks सरणी (array) — वास्तव में निष्पादित होने वाली क्रिया: "type": "command" दर्शाता है कि एक shell कमांड चलाई जाएगी, और "command" वही कमांड है।

这条命令里的 jq 是个解析 JSON 的小工具(Mac 用 brew install jq 装,Ubuntu 用 apt-get install jq)。它的作用下一节讲——简单说就是从 Claude 递来的数据里,把刚改的那个文件路径抠出来,喂给 prettier。

matcher:让钩子「只在该触发的时候触发」

matcher 是 Hook 配置里最该搞懂的一个字段。一句话:没有它,钩子会在那个事件的「每一次」都触发;有了它,你能把范围收窄

एनालॉजी: सुरक्षा गार्ड के प्रवेश नियम—जो डिलीवरी के लिए नहीं है, वह प्रवेश नहीं कर सकता। 没有 matcher 的钩子就像让保安「任何人来都登记」,效率很低;有了 matcher,就变成「只有快递员来才登记,其他人直接放行」。matcher 干的就是这个「划定触发范围」的活。

对工具类事件(PreToolUse/PostToolUse),matcher 匹配的是工具名。它的写法有三种,看这张表:

आपके द्वारा लिखा गया matcherअर्थउदाहरण
"Edit|Write"精确匹配这几个工具(| 是「或」)只在 Edit 或 Write 之后触发
"Bash"精确匹配单个工具只在跑 Bash 命令时触发
"" 或省略匹配所有,每次都触发该事件每次发生都跑

注意:matcher 区分大小写,写成 edit 是匹配不上 Edit 工具的——这是新手钩子不触发最常见的原因之一。

还有一点新手容易忽略:有些事件压根不支持 matcher(比如 UserPromptSubmitStop),因为它们没有「工具名」这种东西可筛,总是每次都触发。给这些事件加 matcher,会被静默忽略。

💡 一句话总结:Hook 写进 settings.json(全局放主目录、项目放 .claude/),配置就三层——事件、matcher、动作matcher 负责把钩子收窄到「只在该触发的工具上触发」,且区分大小写


04 हुक और Claude के बीच संचार कैसे होता है: stdin, निकास कोड (exit code), stdout

这一节是看懂一切的钥匙。前面那条 jq 命令为什么能拿到文件路径?钩子怎么「拦」住一条命令?答案全在这套「对话机制」里。

机制本身极简单,就三条管道:Claude 把事件数据从 stdin 喂给你的脚本 → 脚本干活 → 脚本用「退出码 + stdout」告诉 Claude 接下来怎么办。逐个拆。

输入:Claude 从 stdin 递给你一坨 JSON

事件一触发,Claude Code 会把这个事件的相关数据,作为一段 JSON 从标准输入(stdin)塞给你的命令。比如 Claude 要跑 Bash 命令时,PreToolUse 钩子收到的大概长这样:

json
{
  "session_id": "abc123",
  "cwd": "/Users/sarah/myproject",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}

看到了吧——Claude 要干什么、用哪个工具、参数是什么,全在里头。第 03 节那条 jq -r '.tool_input.file_path',干的就是从这坨 JSON 里把 tool_input.file_path(要改的文件路径)抠出来。jq 就是专门解析 JSON 的工具,-r 是让它输出纯文本(不带引号)。

输出:用「退出码」告诉 Claude 下一步

脚本干完活,靠退出码(exit code)给 Claude 下指令。这是 Hook 最核心的约定,记住三个数就行

निकास कोडअर्थप्रभाव
0没意见,正常走操作继续(PreToolUse不等于批准,照常走权限流程)
2ब्लॉक करें!操作被阻止;你写到 stderr 的内容会作为反馈递给 Claude,让它调整
其他(如 1)出错了,但不拦操作继续,终端显示一条 hook 报错提示

重点是 exit 2——这是钩子「踩刹车」的唯一方式。注意一个反直觉的坑:

对于大多数 hook 事件,仅退出代码 2 阻止操作。Claude Code 将退出代码 1 视为非阻止错误并继续操作,尽管 1 是传统的 Unix 失败代码。如果您的 hook 旨在强制执行策略,请使用 exit 2

सरल शब्दों में: यदि आप ऑपरेशन को ब्लॉक करना चाहते हैं, तो आपको exit 2 का उपयोग करना होगा, न कि exit 1 का। 很多人按 Unix 习惯写了 exit 1,结果钩子「报了错但没拦住」,命令照样跑了。这是第一次写拦截钩子时最容易栽的跟头——脚本明明判断出了危险命令、也打印了警告,但因为顺手写成 exit 1,Claude 该跑还是跑了,吓人一身汗。

还有个细节关系到「能不能拦住」:只有 Pre 类事件能真正拦操作PostToolUse 收到 exit 2 也拦不住——因为工具已经跑完了,木已成舟,它只能把 stderr 显示给 Claude 看。这就呼应了上一节那句「Pre 能拦、Post 只能补刀」。

进阶:用 stdout 返回 JSON,做更细的控制

退出码只能「拦 / 不拦」两档。想要更细的控制(比如拦的同时告诉 Claude 具体原因、或往它上下文里注入一段信息),就改成 exit 0 然后往 stdout 打印一段 JSON。

用退出码 2 配 stderr 来「阻止」,或用 JSON 配退出码 0 来做「结构化控制」。两者不要混用:你退出 2 时,Claude Code 会忽略 JSON。

举两个最常见的 JSON 输出:

PreToolUse 想拦截、并说明理由——用 permissionDecision

json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "这条命令会动生产库,禁止执行"
  }
}

permissionDecision 有四个值:"deny"(拦掉,把理由发给 Claude)、"ask"(照常弹权限框问你)、"allow"(跳过权限框直接放行)、"defer"(延迟执行,让工具稍后恢复,适合非交互模式下的异步审批场景)。

这里有条安全红线必须讲清楚,呼应第 20、21 篇的权限与安全:钩子返回 "allow",不能绕过你设置里的拒绝规则。官方原话——

返回 "allow" 跳过交互式提示但不覆盖权限规则。如果拒绝规则与工具调用匹配,即使你的 hook 返回 "allow",调用也会被阻止。

也就是说:Hook 只能「收紧」限制,不能「放松」到超过权限规则允许的范围。这是个很重要的安全设计——它保证了恶意钩子没法靠返回 allow 把你的安全护栏拆了。反过来,PreToolUse 钩子的拦截优先级极高:哪怕你开了 --dangerously-skip-permissions(跳过所有权限),一个返回 deny 的钩子照样能拦住。所以拿 Hook 来强制团队红线,是真·拦得死。

SessionStart 想往上下文里塞点信息——直接往 stdout 打印文本就行(这几个事件特殊,stdout 会被当成上下文喂给 Claude)。比如会话一开就把最近 5 条提交告诉它:

json
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "git log --oneline -5"
          }
        ]
      }
    ]
  }
}

💡 一句话总结:钩子和 Claude 靠三条管道对话——stdin 喂 JSON 进来、退出码下指令、stdout 做精细控制;记死「exit 2 才拦得住(不是 1)」「退出码和 JSON 别混用」「Hook 只能收紧、不能放松权限」这三条,就抓住了七成。


05 三个能直接抄走的真实例子

理论够了,上三个常用、你也能直接抄的钩子。每个都标清楚「挂哪个事件、收窄到哪、配在哪个文件」。

例子一:改完文件自动格式化(PostToolUse

就是治好那 23 次毛病的那段,最实用、零风险,强烈建议每个项目都配上。写进项目根的 .claude/settings.json

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

तर्क: हर बार जब Claude फ़ाइल को संशोधित करने के लिए Edit/Write का उपयोग करता है → हुक stdin के JSON डेटा से फ़ाइल पथ निकालता है → इसे स्वरूपित करने के लिए prettier --write को भेजता है। इसके बाद स्वरूपण हमेशा सुसंगत रहेगा और आपको परेशान होने की आवश्यकता नहीं होगी। prettier को eslint --fix, gofmt, या black से बदलना भी इसी तर्क पर आधारित है।

例子二:拦掉危险命令(PreToolUse + 脚本)

这个用上了「exit 2 拦截」。命令复杂时,把逻辑写进一个单独的脚本比硬塞进 JSON 清爽得多。

第一步,把脚本存到 .claude/hooks/block-dangerous.sh

bash
#!/bin/bash
# block-dangerous.sh:拦截 rm -rf 这类危险命令
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q "rm -rf"; then
  echo "Blocked: 检测到 rm -rf,已拦截" >&2   # 写到 stderr,会反馈给 Claude
  exit 2                                       # exit 2 = 阻止这次工具调用
fi

exit 0   # 其余命令放行,走正常权限流程

第二步,给脚本加可执行权限(Mac/Linux 必须,否则 Claude 跑不了它):

bash
chmod +x .claude/hooks/block-dangerous.sh

第三步,在 .claude/settings.json 里注册它,挂到 Bash 工具的 PreToolUse 上:

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh"
          }
        ]
      }
    ]
  }
}

这里那个 $CLAUDE_PROJECT_DIR 是 Claude Code 提供的环境变量,指向项目根目录——用它拼路径,钩子不管在哪个子目录跑都能找到脚本,比写死的相对路径稳。

⚠️ 这一节回到第 21 篇的安全主线:钩子是用你的完整用户权限跑的 shell,能删你能删的任何文件。官方反复强调,加任何钩子前先审一遍它的命令,意其别从来路不明的地方抄整段脚本就往设置里塞。

例子三:它需要你输入时,发条桌面通知(Notification

Claude 干到一半要你批准、或者答完等你下一句时,你可能早切去刷别的了。挂个通知钩子,它就主动喊你。这个用 Notification 事件(Claude 发通知时触发)。

macOS 写进 ~/.claude/settings.json(这种「叫我」的钩子是你个人偏好,放全局):

json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code 在等你\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

平台差异(官方给的原生命令,照抄即可):

प्लेटफ़ॉर्मअधिसूचना कमांड (command में भरें)
macOSosascript -e 'display notification "..." with title "Claude Code"'
Linuxnotify-send 'Claude Code' '...'
WindowsPowerShell के MessageBox का उपयोग करें (आधिकारिक दस्तावेज़ में पूरी स्क्रिप्ट शामिल है)

macOS 上如果通知没弹出来,多半是 Script Editor 没拿到通知权限——去「系统设置 → 通知」里找到 Script Editor 把开关打开。第一次配很容易死活没声响,折腾半天才发现是这个权限没给。

💡 一句话总结:三个钩子按风险递增——格式化(PostToolUse,零风险,建议人人配)、拦命令(PreToolUse+脚本,记得 chmod +xexit 2)、发通知(Notification,平台命令不同);脚本路径用 $CLAUDE_PROJECT_DIR 拼最稳。


06 动手:5 分钟配一个钩子并亲眼看它触发

光看不练记不住。下面带你配一个最安全、最容易看到效果的钩子——每次 Claude 跑完 Bash 命令,就把这条命令记进一个日志文件。全程不动你任何代码,纯加一段配置,跑完能亲眼验证。

这个练习用到 jq。没装的话:Mac 跑 brew install jq,Ubuntu 跑 sudo apt-get install jq。装不装得上不需要魔法上网。

第一步:找一个练手目录,打开它的项目设置文件

随便找个空目录(别在重要项目里练),在里头建 .claude/settings.json。如果文件已存在且有别的内容,把 hooks 这块作为新键加进去,别整个覆盖。文件内容:

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/claude-bash-log.txt"
          }
        ]
      }
    ]
  }
}

条钩子:每次 Bash 工具跑完(PostToolUse + matcher: "Bash")→ 从 stdin 的 JSON 里抠出命令 → 用 >> 追加写进主目录的 claude-bash-log.txt

第二步:在这个目录里启动 Claude,确认钩子已注册

bash
claude

进去后输入 /hooks

text
/hooks

अपेक्षा: एक केवल-पठन (read-only) हुक ब्राउज़र प्रदर्शित होगा जो सभी घटनाओं को सूचीबद्ध करेगा। PostToolUse ढूंढें, जिसके बगल में 1 हुक प्रदर्शित होना चाहिए। विवरण देखने के लिए इसे चुनें: घटना, matcher (Bash), स्रोत फ़ाइल (Project, यानी प्रोजेक्ट की .claude/settings.json), और वह कमांड। सूची में इसे देखना = हुक सफलतापूर्वक पंजीकृत हो गया है।

/hooks 菜单是**只读**的——它只能让你查、不能加和改。要改钩子,直接编辑 settings.json`,或者直接让 Claude 帮你改。

第三步:让 Claude 跑条 Bash 命令,触发钩子

Esc 回到对话,让它跑个无害的命令:

text
ls का उपयोग करके मुझे दिखाएं कि वर्तमान निर्देशिका में कौन सी फ़ाइलें हैं।

它会调用 Bash 工具跑 ls这一跑,PostToolUse 钩子就该触发了。(钩子成功执行时是「静默」的,终端不会特意提示——这是正常的。)

第四步:验证钩子真跑了——查日志文件

新开一个终端,看日志:

bash
cat ~/claude-bash-log.txt

अपेक्षा: फ़ाइल में अभी चलाई गई ls कमांड दिखाई देगी (और इस सत्र में Claude द्वारा चलाई गई अन्य Bash कमांड भी)। कमांड को दर्ज देखना = हुक वास्तव में हर बार Bash के बाद स्वचालित रूप से ट्रिगर हुआ है, आपका "स्वचालन नियम" स्थापित हो गया है।

第五步:清理(可选)

练完拆掉很简单——把 .claude/settings.json 里那段 hooks 删掉就行(钩子没有单独的「删除命令」,从配置里移除条目即可)。日志文件 rm ~/claude-bash-log.txt 删掉。

跑通这五步,你就把「写配置 → /hooks 确认注册 → 触发事件 → 验证副作用」这条完整链路亲手走了一遍。以后配任何钩子,本质都是这套流程,无非换个事件、换个 matcher、换条命令。

💡 一句话总结:练手就配「记录 Bash 命令」这种零风险钩子最稳——写进 .claude/settings.json、用 /hooks 看它注册、让 Claude 跑命令触发、查日志文件验证;亲眼看到副作用发生,比记十条定义都顶用。


07 हुक ट्रिगर नहीं हो रहा है या त्रुटि संदेश प्रदर्शित कर रहा है, समस्या निवारण कैसे करें

Hook 配好了不灵,是新手最常见的卡点。别瞎猜,按下面这个顺序查,基本都能定位。我把官方的排查清单整理成了「症状 → 怎么查」:

लक्षणसबसे संभावित कारण / कैसे जांचें
हुक बिल्कुल ट्रिगर नहीं हो रहा है① पंजीकृत है या नहीं यह देखने के लिए /hooks चलाएं; ② matcher केस-संवेदी है, edit लिखने पर यह Edit से मेल नहीं खाएगा; ③ गलत घटना चुनी गई है (ऑपरेशन को ब्लॉक करने के लिए PreToolUse का उपयोग करें, PostToolUse का नहीं)
/hooks सूची में मेरा कॉन्फ़िगर किया गया हुक नहीं है① JSON प्रारूप गलत है (JSON में अंतिम अल्पविराम (trailing comma) या टिप्पणियों की अनुमति नहीं है); ② फ़ाइल स्थान गलत है (प्रोजेक्ट हुक .claude/settings.json में है, वैश्विक ~/.claude/settings.json में); ③ बदलने के बाद प्रभावी करने के लिए सत्र पुनरारंभ करें
टर्मिनल पर hook error दिखाई दे रहा हैस्क्रिप्ट अप्रत्याशित रूप से गैर-शून्य निकास कोड के साथ समाप्त हुई। इसे मैन्युअल रूप से चलाकर देखें (नीचे कमांड देखें); command not found त्रुटि का अर्थ फ़ाइल पथ गलत होना है, पूर्ण पथ या $CLAUDE_PROJECT_DIR का उपयोग करें; jq: command not found का अर्थ jq इंस्टॉल न होना है
स्क्रिप्ट नहीं चल रही हैMac/Linux पर स्क्रिप्ट को निष्पादन योग्य अनुमतियां देना भूल गए, chmod +x चलाएं
ब्लॉक करना चाहते थे लेकिन ब्लॉक नहीं हुआसंभवतः आपने exit 1 लिखा है, इसे बदलकर exit 2 करें (धारा 04 में वर्णित समस्या)

两个最实用的排查手段单独拎出来:

① 手动喂假数据测脚本。不用真在 Claude 里触发,自己造一段 JSON 管道喂给脚本,看它退出码对不对:

bash
echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /tmp/x"}}' | ./block-dangerous.sh
echo $?   # 看退出码:拦截脚本这里应该输出 2

这是调拦截钩子的标准动作——先把脚本单独喂熟了,再挂到 Claude 上,省得在真实会话里反复试。

② 开调试日志看细节。想看「到底哪些钩子匹配了、退出码多少、stdout/stderr 是啥」,用 --debug 启动,日志会写进 ~/.claude/debug/<会话id>.txt

bash
claude --debug

或者会话里直接敲 /debug 也能开。日志里会有类似这样的行,一眼看出钩子有没有跑、跑成啥样:

text
[DEBUG] Executing hooks for PostToolUse:Bash
[DEBUG] Hook command completed with status 0

还有个一键开关值得知道:想临时关掉所有钩子(比如怀疑某个钩子在捣乱),在设置文件里加一句 "disableAllHooks": true 就行,不用一个个删。

💡 一句话总结:钩子不灵别瞎猜,按「/hooks 看注册 → 查 matcher 大小写和事件选对没 → 手动喂 JSON 测脚本 → --debug 看日志」的顺序查;想拦没拦住先看是不是写了 exit 1


08 सारांश

इस लेख में हमने Hook को "यह क्या है" से लेकर "इसे कैसे कॉन्फ़िगर करें और उपयोग करें" तक पूरी तरह से समझ लिया है—यह Claude Code के लिए एक स्वचालन नियम है: जैसे ही कोई घटना ट्रिगर होती है, यह बिना किसी असफलता के आपके लिए एक क्रिया निष्पादित करता है

समीक्षा के लिए मुख्य बिंदुओं को एक साथ जोड़ें:

वह कार्य जो आप करना चाहते हैंइसे कैसे लागू करेंमुख्य बिंदु
समझना कि Hook क्या हैघटना-ट्रिगर स्वचालित shell कमांड"अनुरोध" को "आश्वासन" में बदलना, Claude के स्व-अनुशासन पर निर्भर नहीं
सही समय चुननालाइफसाइकल घटनाओं को पहचाननाPre ब्लॉक कर सकता है, Post अतिरिक्त क्रिया, Stop उत्तर के बाद, SessionStart सत्र प्रारंभ
एक हुक लिखनाsettings.json में पंजीकृत करनातीन स्तर: घटना + matcher (केस-संवेदी) + क्रिया
हुक और Claude के बीच संचारstdin / निकास कोड / stdoutकेवल exit 2 ही ब्लॉक कर सकता है, निकास कोड और JSON को न मिलाएं
खतरनाक ऑपरेशन्स को ब्लॉक करनाPreToolUse + स्क्रिप्टchmod +x याद रखें, Hook केवल प्रतिबंधों को कड़ा कर सकता है, ढीला नहीं
हुक काम नहीं कर रहा हैक्रम में समस्या निवारण करें/hooks से पंजीकरण देखें, मॉक JSON से स्क्रिप्ट का परीक्षण, --debug से लॉग जांचें

你现在应该能: 说清 Hook 和「写进 CLAUDE.md 的请求」差在哪个根本点(保证 vs 请求);知道 PreToolUse/PostToolUse/Stop/SessionStart 各自挂在 Claude 干活流程的哪一步;照着模板往 settings.json 里写一个带 matcher 的钩子;看懂钩子靠 stdin/退出码/stdout 跟 Claude 对话,并记死「exit 2 才能拦」;钩子不触发时知道从 /hooks--debug 入手查。开头那 23 次手敲 prettier 的苦差,到这儿你已经有能力用一行配置永久解决了。

Hook "सिस्टम कॉन्फ़िगरेशन और ऑप्टिमाइज़ेशन" समूह का एक बहुत ही शक्तिशाली घटक है—यह आपको Claude के व्यवहार पर निश्चित नियंत्रण प्रदान करता है, न कि केवल "अनुरोध" करने तक सीमित रखता है।


अगला लेख 34 "CLI संदर्भ नियमावली: कमांड और सभी फ़्लैग" — इस यात्रा में आपने claude से शुरू होने वाली कई कमांड्स चलाई हैं, और --debug, --dangerously-skip-permissions जैसे फ़्लैग्स का उपयोग किया है, लेकिन ये केवल हिमशैल (iceberg) का सिरा हैं। अगले लेख में हम claude कमांड लाइन के सभी कमांड्स और फ़्लैग्स की विस्तृत समीक्षा करेंगे, जिसे आप एक संदर्भ गाइड के रूप में उपयोग कर सकते हैं। इसके बारे में सोचें: आप वर्तमान में कितने claude फ़्लैग्स को याद रख सकते हैं? अगले लेख को पढ़ने के बाद, आप पाएंगे कि आपने कम से कम आधे उपयोगी स्विचों को छोड़ दिया है जो आपके काम को आसान बना सकते थे।


अनुशंसित पठन