مرجع Hooks
Hookها فرمانهای شِلِ تعریفشده توسطِ کاربر، endpointهای HTTP یا پرامپتهای LLM هستند که بهصورت خودکار در نقاطِ مشخصی از چرخهی حیاتِ Claude Code اجرا میشوند. از این مرجع برای پیداکردنِ اسکیمای رویدادها، گزینههای پیکربندی، فرمتهای ورودی/خروجی JSON و قابلیتهای پیشرفته مثل hookهای async، hookهای HTTP و hookهای ابزارِ MCP استفاده کن. اگر برای اولین بار hook راه میاندازی، بهجای این مرجع از راهنما شروع کن.
چرخهی حیاتِ hook
Section titled “چرخهی حیاتِ hook”Hookها در نقاطِ مشخصی در طولِ یک نشستِ Claude Code فعال میشوند. وقتی یک رویداد فعال میشود و یک matcher مطابقت پیدا میکند، Claude Code کانتکستِ JSON مربوط به آن رویداد را به handlerِ hookِ تو پاس میدهد. برای hookهای فرمان، ورودی روی stdin میرسد. برای hookهای HTTP، ورودی بهصورتِ بدنهی درخواستِ POST میرسد. handlerِ تو میتواند سپس ورودی را بررسی کند، اکشنی انجام دهد و اختیاراً یک تصمیم برگرداند. رویدادها در سه ریتم قرار میگیرند: یکبار در هر نشست (SessionStart, SessionEnd)، یکبار در هر نوبت (UserPromptSubmit, Stop, StopFailure)، و در هر فراخوانیِ ابزار داخلِ حلقهی ایجنتیک (PreToolUse, PostToolUse):
جدولِ زیر خلاصه میکند که هر رویداد چه زمانی فعال میشود. بخشِ رویدادهای hook اسکیمای کاملِ ورودی و گزینههای کنترلِ تصمیمِ هرکدام را مستند میکند.
| رویداد | چه زمانی فعال میشود |
|---|---|
SessionStart | وقتی یک نشست آغاز یا از سر گرفته میشود |
Setup | وقتی Claude Code را با --init-only، یا با --init یا --maintenance در حالتِ -p اجرا میکنی. برای آمادهسازیِ یکباره در CI یا اسکریپتها |
UserPromptSubmit | وقتی یک پرامپت ثبت میکنی، پیش از آنکه Claude پردازشش کند |
UserPromptExpansion | وقتی یک فرمانِ تایپشده توسطِ کاربر به یک پرامپت بسط پیدا میکند، پیش از رسیدن به Claude. میتواند بسط را مسدود کند |
PreToolUse | پیش از اجرای یک فراخوانیِ ابزار. میتواند مسدودش کند |
PermissionRequest | وقتی یک دیالوگِ مجوز ظاهر میشود |
PermissionDenied | وقتی یک فراخوانیِ ابزار توسطِ طبقهبندِ حالتِ auto رد میشود. {retry: true} برگردان تا به مدل بگویی میتواند فراخوانیِ ردشده را دوباره امتحان کند |
PostToolUse | پس از موفقیتِ یک فراخوانیِ ابزار |
PostToolUseFailure | پس از شکستِ یک فراخوانیِ ابزار |
PostToolBatch | پس از resolveشدنِ یک دستهی کاملِ فراخوانیهای موازیِ ابزار، پیش از فراخوانیِ بعدیِ مدل |
Notification | وقتی Claude Code یک اعلان میفرستد |
MessageDisplay | حین نمایشِ متنِ پیامِ دستیار |
SubagentStart | وقتی یک سابایجنت spawn میشود |
SubagentStop | وقتی یک سابایجنت تمام میشود |
TaskCreated | وقتی یک task از طریقِ TaskCreate در حالِ ساختهشدن است |
TaskCompleted | وقتی یک task در حالِ علامتخوردن بهعنوانِ تکمیلشده است |
Stop | وقتی Claude پاسخدادن را تمام میکند |
StopFailure | وقتی نوبت بهخاطرِ یک خطای API پایان مییابد. خروجی و کدِ خروج نادیده گرفته میشوند |
TeammateIdle | وقتی یک همتیمیِ تیمِ ایجنت قرار است idle شود |
InstructionsLoaded | وقتی یک فایلِ CLAUDE.md یا .claude/rules/*.md در کانتکست بارگذاری میشود. در آغازِ نشست و وقتی فایلها در طولِ نشست بهصورتِ lazy بارگذاری میشوند فعال میشود |
ConfigChange | وقتی یک فایلِ پیکربندی در طولِ یک نشست تغییر میکند |
CwdChanged | وقتی دایرکتوریِ کاری تغییر میکند، مثلاً وقتی Claude یک فرمانِ cd اجرا میکند. برای مدیریتِ واکنشیِ محیط با ابزارهایی مثل direnv مفید است |
FileChanged | وقتی یک فایلِ تحتِ نظر روی دیسک تغییر میکند. فیلدِ matcher مشخص میکند کدام نامفایلها تحتِ نظر باشند |
WorktreeCreate | وقتی یک worktree از طریقِ --worktree یا isolation: "worktree" در حالِ ساختهشدن است. رفتارِ پیشفرضِ git را جایگزین میکند |
WorktreeRemove | وقتی یک worktree در حالِ حذف است، چه هنگامِ خروج از نشست چه وقتی یک سابایجنت تمام میشود |
PreCompact | پیش از فشردهسازیِ کانتکست |
PostCompact | پس از تکمیلِ فشردهسازیِ کانتکست |
Elicitation | وقتی یک سرورِ MCP در حینِ یک فراخوانیِ ابزار درخواستِ ورودیِ کاربر میکند |
ElicitationResult | پس از آنکه کاربر به یک elicitationِ MCP پاسخ میدهد، پیش از آنکه پاسخ به سرور بازگردانده شود |
SessionEnd | وقتی یک نشست خاتمه مییابد |
چطور یک hook resolve میشود
Section titled “چطور یک hook resolve میشود”برای دیدنِ اینکه این قطعهها چطور کنار هم مینشینند، این hookِ PreToolUse را در نظر بگیر که فرمانهای شِلِ مخرب را مسدود میکند. matcher دامنه را به فراخوانیهای ابزارِ Bash محدود میکند و شرطِ if آن را بیشتر محدود میکند به زیرفرمانهای Bash که با rm * مطابقت دارند، پس block-rm.sh فقط وقتی هر دو فیلتر مطابقت کنند spawn میشود:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "if": "Bash(rm *)", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh", "args": [] } ] } ] }}اسکریپت ورودیِ JSON را از stdin میخواند، فرمان را استخراج میکند و اگر شاملِ rm -rf باشد یک permissionDecision با مقدارِ "deny" برمیگرداند:
#!/bin/bashCOMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: "Destructive command blocked by hook" } }'else exit 0 # no decision; normal permission flow appliesfiحالا فرض کن Claude Code تصمیم میگیرد Bash "rm -rf /tmp/build" را اجرا کند. این اتفاقی است که میافتد:
رویداد فعال میشود
رویدادِ PreToolUse فعال میشود. Claude Code ورودیِ ابزار را بهصورتِ JSON روی stdin به hook میفرستد:
{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }بررسیِ matcher
matcherِ "Bash" با نامِ ابزار مطابقت دارد، پس این گروهِ hook فعال میشود. اگر matcher را حذف کنی یا از "*" استفاده کنی، این گروه در هر رخدادِ این رویداد فعال میشود.
بررسیِ شرطِ if
شرطِ if با مقدارِ "Bash(rm *)" مطابقت دارد، چون rm -rf /tmp/build یک زیرفرمانِ مطابق با rm * است، پس این handler spawn میشود. اگر فرمان npm test بود، بررسیِ if شکست میخورد و block-rm.sh هرگز اجرا نمیشد و از سربارِ spawnِ پروسه جلوگیری میشد. فیلدِ if اختیاری است؛ بدونِ آن، هر handler در گروهِ مطابقتیافته اجرا میشود.
handlerِ hook اجرا میشود
اسکریپت فرمانِ کامل را بررسی میکند و rm -rf را پیدا میکند، پس یک تصمیم روی stdout چاپ میکند:
{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Destructive command blocked by hook" }}اگر فرمان یک نسخهی امنترِ rm مثل rm file.txt بود، اسکریپت بهجای آن به exit 0 میرسید. کدِ خروجِ 0 بدونِ هیچ خروجی یعنی hook تصمیمی برای گزارش ندارد، پس فراخوانیِ ابزار از مسیرِ عادیِ جریانِ مجوز ادامه مییابد. hook میتواند فراخوانی را رد کند، اما سکوتکردن آن را تأیید نمیکند.
Claude Code بر اساسِ نتیجه عمل میکند
Claude Code تصمیمِ JSON را میخواند، فراخوانیِ ابزار را مسدود میکند و دلیل را به Claude نشان میدهد.
بخشِ پیکربندی در ادامه اسکیمای کامل را مستند میکند، و هر بخشِ رویدادِ hook مستند میکند که فرمانت چه ورودیای دریافت میکند و چه خروجیای میتواند برگرداند.
پیکربندی
Section titled “پیکربندی”Hookها در فایلهای تنظیماتِ JSON تعریف میشوند. پیکربندی سه سطحِ تودرتو دارد:
- یک رویدادِ hook برای پاسخدادن انتخاب کن، مثل
PreToolUseیاStop - یک گروهِ matcher اضافه کن تا فیلتر کنی کِی فعال شود، مثل «فقط برای ابزارِ Bash»
- یک یا چند handlerِ hook تعریف کن که هنگامِ مطابقت اجرا شوند
برای راهنماییِ کامل با مثالِ شرحدادهشده، چطور یک hook resolve میشود در بالا را ببین.
محلهای hook
Section titled “محلهای hook”جایی که یک hook را تعریف میکنی دامنهاش را تعیین میکند:
| محل | دامنه | قابلِ اشتراک |
|---|---|---|
~/.claude/settings.json | همهی پروژههایت | خیر، محلی روی دستگاهت |
.claude/settings.json | یک پروژهی واحد | بله، میتواند در مخزن کامیت شود |
.claude/settings.local.json | یک پروژهی واحد | خیر، وقتی Claude Code میسازدش gitignore میشود |
| تنظیماتِ سیاستِ مدیریتشده | سطحِ کلِ سازمان | بله، تحتِ کنترلِ ادمین |
Plugin hooks/hooks.json | وقتی پلاگین فعال است | بله، همراه با پلاگین بستهبندی میشود |
| frontmatterِ Skill یا agent | تا وقتی کامپوننت فعال است | بله، در فایلِ کامپوننت تعریف میشود |
برای جزئیاتِ resolveِ فایلِ تنظیمات، settings را ببین. ادمینهای سازمانی میتوانند از allowManagedHooksOnly برای مسدودکردنِ hookهای کاربر، پروژه و پلاگین استفاده کنند. hookهای پلاگینهایی که در تنظیماتِ مدیریتشدهی enabledPlugins بهاجبار فعال شدهاند مستثنا هستند، پس ادمینها میتوانند hookهای بررسیشده را از طریقِ یک marketplaceِ سازمانی توزیع کنند. پیکربندیِ Hook را ببین.
الگوهای matcher
Section titled “الگوهای matcher”فیلدِ matcher فیلتر میکند کِی hookها فعال شوند. اینکه یک matcher چطور ارزیابی میشود به کاراکترهایی که در آن هست بستگی دارد:
| مقدارِ matcher | ارزیابی بهصورتِ | مثال |
|---|---|---|
"*"، ""، یا حذفشده | مطابقت با همه | در هر رخدادِ این رویداد فعال میشود |
فقط حروف، ارقام، _ و | | رشتهی دقیق، یا فهرستِ رشتههای دقیقِ جداشده با | | Bash فقط با ابزارِ Bash مطابقت دارد؛ Edit|Write دقیقاً با هر کدام از این دو ابزار مطابقت دارد |
| شاملِ هر کاراکترِ دیگری | عبارتِ منظمِ JavaScript | ^Notebook با هر ابزاری که با Notebook شروع شود مطابقت دارد؛ mcp__memory__.* با هر ابزاری از سرورِ memory مطابقت دارد |
رویدادِ FileChanged هنگامِ ساختنِ فهرستِ watchِ خود از این قواعد پیروی نمیکند. FileChanged را ببین.
هر نوعِ رویداد روی فیلدِ متفاوتی مطابقت میکند:
| رویداد | matcher چه چیزی را فیلتر میکند | نمونه مقادیرِ matcher |
|---|---|---|
PreToolUse، PostToolUse، PostToolUseFailure، PermissionRequest، PermissionDenied | نامِ ابزار | Bash، Edit|Write، mcp__.* |
SessionStart | چطور نشست آغاز شد | startup، resume، clear، compact |
Setup | کدام پرچمِ CLI، setup را فعال کرد | init، maintenance |
SessionEnd | چرا نشست پایان یافت | clear، resume، logout، prompt_input_exit، bypass_permissions_disabled، other |
Notification | نوعِ اعلان | permission_prompt، idle_prompt، auth_success، elicitation_dialog، elicitation_complete، elicitation_response |
SubagentStart | نوعِ ایجنت | general-purpose، Explore، Plan، یا نامهای ایجنتِ سفارشی |
PreCompact، PostCompact | چه چیزی فشردهسازی را فعال کرد | manual، auto |
SubagentStop | نوعِ ایجنت | همان مقادیرِ SubagentStart |
ConfigChange | منبعِ پیکربندی | user_settings، project_settings، local_settings، policy_settings، skills |
CwdChanged | بدونِ پشتیبانی از matcher | همیشه در هر تغییرِ دایرکتوری فعال میشود |
FileChanged | نامفایلهای واقعی برای watch (نگاه کن به FileChanged) | .envrc|.env |
StopFailure | نوعِ خطا | rate_limit، overloaded، authentication_failed، oauth_org_not_allowed، billing_error، invalid_request، model_not_found، server_error، max_output_tokens، unknown |
InstructionsLoaded | دلیلِ بارگذاری | session_start، nested_traversal، path_glob_match، include، compact |
UserPromptExpansion | نامِ فرمان | نامهای skill یا فرمانِ تو |
Elicitation | نامِ سرورِ MCP | نامهای سرورِ MCPِ پیکربندیشدهات |
ElicitationResult | نامِ سرورِ MCP | همان مقادیرِ Elicitation |
UserPromptSubmit، PostToolBatch، Stop، TeammateIdle، TaskCreated، TaskCompleted، WorktreeCreate، WorktreeRemove، MessageDisplay | بدونِ پشتیبانی از matcher | همیشه در هر رخداد فعال میشود |
matcher روی فیلدی از ورودیِ JSON اجرا میشود که Claude Code روی stdin به hookِ تو میفرستد. برای رویدادهای ابزار، آن فیلد tool_name است. هر بخشِ رویدادِ hook مجموعهی کاملِ مقادیرِ matcher و اسکیمای ورودیِ آن رویداد را فهرست میکند.
این مثال یک اسکریپتِ lint را فقط وقتی Claude فایلی مینویسد یا ویرایش میکند اجرا میکند:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "/path/to/lint-check.sh" } ] } ] }}UserPromptSubmit، PostToolBatch، Stop، TeammateIdle، TaskCreated، TaskCompleted، WorktreeCreate، WorktreeRemove و CwdChanged از matcher پشتیبانی نمیکنند و همیشه در هر رخداد فعال میشوند. اگر فیلدِ matcher به این رویدادها اضافه کنی، بیصدا نادیده گرفته میشود.
برای رویدادهای ابزار، میتوانی با تنظیمِ فیلدِ if روی handlerهای منفردِ hook دقیقتر فیلتر کنی. if از نحوِ قاعدهی مجوز استفاده میکند تا روی نامِ ابزار و آرگومانها با هم مطابقت کند، پس "Bash(git *)" وقتی اجرا میشود که هر زیرفرمانی از ورودیِ Bash با git * مطابقت کند و "Edit(*.ts)" فقط برای فایلهای TypeScript اجرا میشود.
مطابقت با ابزارهای MCP
Section titled “مطابقت با ابزارهای MCP”ابزارهای سرورِ MCP در رویدادهای ابزار (PreToolUse، PostToolUse، PostToolUseFailure، PermissionRequest، PermissionDenied) مثل ابزارهای عادی ظاهر میشوند، پس میتوانی به همان شیوهای که با هر نامِ ابزارِ دیگری مطابقت میکنی با آنها مطابقت کنی.
ابزارهای MCP از الگوی نامگذاریِ mcp__<server>__<tool> پیروی میکنند، برای مثال:
mcp__memory__create_entities: ابزارِ create entitiesِ سرورِ Memorymcp__filesystem__read_file: ابزارِ read fileِ سرورِ Filesystemmcp__github__search_repositories: ابزارِ searchِ سرورِ GitHub
برای مطابقت با هر ابزاری از یک سرور، .* را به پیشوندِ سرور اضافه کن. .* الزامی است: matcherی مثل mcp__memory فقط شاملِ حروف و آندرلاین است، پس بهعنوانِ رشتهی دقیق مقایسه میشود و با هیچ ابزاری مطابقت ندارد.
mcp__memory__.*با همهی ابزارهای سرورِmemoryمطابقت داردmcp__.*__write.*با هر ابزاری که نامش باwriteشروع شود از هر سروری مطابقت دارد
این مثال همهی عملیاتِ سرورِ memory را لاگ میکند و عملیاتِ writeِ هر سرورِ MCP را اعتبارسنجی میکند:
{ "hooks": { "PreToolUse": [ { "matcher": "mcp__memory__.*", "hooks": [ { "type": "command", "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log" } ] }, { "matcher": "mcp__.*__write.*", "hooks": [ { "type": "command", "command": "/home/user/scripts/validate-mcp-write.py" } ] } ] }}فیلدهای handlerِ hook
Section titled “فیلدهای handlerِ hook”هر شیء در آرایهی داخلیِ hooks یک handlerِ hook است: فرمانِ شِل، endpointِ HTTP، ابزارِ MCP، پرامپتِ LLM یا ایجنتی که هنگامِ مطابقتِ matcher اجرا میشود. پنج نوع وجود دارد:
- hookهای فرمان (
type: "command"): یک فرمانِ شِل اجرا میکنند. اسکریپتت ورودیِ JSONِ رویداد را روی stdin دریافت میکند و نتایج را از طریقِ کدهای خروج و stdout بازمیگرداند. - hookهای HTTP (
type: "http"): ورودیِ JSONِ رویداد را بهصورتِ یک درخواستِ HTTP POST به یک URL میفرستند. endpoint نتایج را از طریقِ بدنهی پاسخ و با همان فرمتِ خروجیِ JSONِ hookهای فرمان بازمیگرداند. - hookهای ابزارِ MCP (
type: "mcp_tool"): ابزاری را روی یک سرورِ MCPِ از پیشمتصل فراخوانی میکنند. خروجیِ متنیِ ابزار مثلِ stdoutِ hookِ فرمان رفتار میشود. - hookهای پرامپت (
type: "prompt"): یک پرامپت برای ارزیابیِ تکنوبتی به یک مدلِ Claude میفرستند. مدل یک تصمیمِ بله/خیر بهصورتِ JSON برمیگرداند. hookهای مبتنی بر پرامپت را ببین. - hookهای ایجنت (
type: "agent"): یک سابایجنت spawn میکنند که میتواند از ابزارهایی مثل Read، Grep و Glob استفاده کند تا پیش از برگرداندنِ یک تصمیم شرایط را تأیید کند. hookهای ایجنت آزمایشی هستند و ممکن است تغییر کنند. hookهای مبتنی بر ایجنت را ببین.
فیلدهای مشترک
Section titled “فیلدهای مشترک”این فیلدها برای همهی انواعِ hook اعمال میشوند:
| فیلد | الزامی | توضیح |
|---|---|---|
type | بله | "command"، "http"، "mcp_tool"، "prompt"، یا "agent" |
if | خیر | نحوِ قاعدهی مجوز برای فیلترکردنِ اینکه این hook کِی اجرا شود، مثل "Bash(git *)" یا "Edit(*.ts)". فرمانِ hook فقط اگر فراخوانیِ ابزار با الگو مطابقت کند اجرا میشود. برای اینکه الگوهای Bash چطور روی زیرفرمانها، $() و backtickها ارزیابی میشوند، جدولِ مطابقتِ Bash را در پایین ببین. فقط روی رویدادهای ابزار ارزیابی میشود: PreToolUse، PostToolUse، PostToolUseFailure، PermissionRequest و PermissionDenied. روی رویدادهای دیگر، hookی که if تنظیمشده دارد هرگز اجرا نمیشود. از همان نحوِ قواعدِ مجوز استفاده میکند |
timeout | خیر | ثانیهها پیش از لغو. پیشفرضها: 600 برای command، http و mcp_tool؛ 30 برای prompt؛ 60 برای agent. UserPromptSubmit پیشفرضِ command، http و mcp_tool را به 30 کاهش میدهد، و MessageDisplay آن را به 10 کاهش میدهد |
statusMessage | خیر | پیامِ سفارشیِ spinner که حین اجرای hook نمایش داده میشود |
once | خیر | اگر true باشد، یکبار در هر نشست اجرا میشود و سپس حذف میشود. فقط برای hookهای اعلامشده در frontmatterِ skill رعایت میشود؛ در فایلهای تنظیمات و frontmatterِ ایجنت نادیده گرفته میشود |
فیلدِ if دقیقاً یک قاعدهی مجوز نگه میدارد. هیچ نحوِ &&، || یا فهرستی برای ترکیبِ قواعد وجود ندارد؛ برای اعمالِ چند شرط، برای هرکدام یک handlerِ hookِ جداگانه تعریف کن.
برای الگوهای Bash، اینکه فرمانِ hookت اجرا شود یا نه به شکلِ الگو و فرمانِ Bashی که Claude فراخوانی میکند بستگی دارد. انتسابات VAR=valueِ ابتدایی پیش از مطابقت حذف میشوند.
الگوی if | فرمانِ Bash | hook اجرا میشود؟ | چرا |
|---|---|---|---|
Bash(git *) | FOO=bar git push | بله | انتسابات ابتدایی حذف میشوند؛ git push مطابقت دارد |
Bash(git *) | npm test && git push | بله | هر زیرفرمان بررسی میشود؛ git push مطابقت دارد |
Bash(rm *) | echo $(rm -rf /) | بله | فرمانهای داخلِ $() و backtickها بررسی میشوند؛ rm -rf / مطابقت دارد |
Bash(rm *) | echo $(date) | خیر | هیچ زیرفرمانی با rm * مطابقت ندارد |
Bash(git push *) | echo $(date) | بله | الگوهایی که بیش از نامِ فرمان را مشخص میکنند، روی $()، backtickها یا $VAR بههرحال hook را اجرا میکنند |
این فیلتر هنگامی که فرمانِ Bash قابلِ پارس نباشد نیز fail open میکند و hookت را صرفِنظر از الگو اجرا میکند. چون فیلترِ if بهترینتلاش (best-effort) است، برای اعمالِ یک allow یا deny قطعی بهجای hook از سیستمِ مجوز استفاده کن.
فیلدهای hookِ فرمان
Section titled “فیلدهای hookِ فرمان”علاوه بر فیلدهای مشترک، hookهای فرمان این فیلدها را میپذیرند:
| فیلد | الزامی | توضیح |
|---|---|---|
command | بله | فرمانِ شِل برای اجرا. به همراه args، فایلِ اجراییای که مستقیماً spawn میشود. فرمِ exec و فرمِ shell را ببین |
args | خیر | فهرستِ آرگومانها. وقتی حاضر باشد، command بهعنوانِ یک فایلِ اجرایی resolve میشود و مستقیماً با args بهعنوانِ بردارِ آرگومان، بدونِ دخالتِ هیچ شِلی spawn میشود. فرمِ exec و فرمِ shell را ببین |
async | خیر | اگر true باشد، در پسزمینه و بدونِ مسدودکردن اجرا میشود. اجرای hookها در پسزمینه را ببین |
asyncRewake | خیر | اگر true باشد، در پسزمینه اجرا میشود و در کدِ خروجِ 2 Claude را بیدار میکند. بهطورِ ضمنی async را در پی دارد. stderrِ hook، یا stdout اگر stderr خالی باشد، بهصورتِ یک system reminder به Claude نشان داده میشود تا بتواند به یک شکستِ پسزمینهی طولانی واکنش نشان دهد |
shell | خیر | شِلی که برای این hook استفاده میشود. "bash" (پیشفرض) یا "powershell" را میپذیرد. تنظیمِ "powershell" فرمان را روی Windows از طریقِ PowerShell اجرا میکند. چون hookها مستقیماً PowerShell را spawn میکنند، به CLAUDE_CODE_USE_POWERSHELL_TOOL نیاز ندارد. وقتی args تنظیم شده باشد نادیده گرفته میشود |
فرمِ exec و فرمِ shell
Section titled “فرمِ exec و فرمِ shell”یک hookِ فرمان وقتی args تنظیم شده باشد بهصورتِ فرمِ exec اجرا میشود، و وقتی args حذف شده باشد بهصورتِ فرمِ shell. هر وقت hook به یک جانگهدارِ مسیر اشاره میکند args را تنظیم کن، چون هر عنصر بهعنوانِ یک آرگومان بدونِ هیچ نقلقولی پاس داده میشود. وقتی به قابلیتهای شِل مثل pipe یا && نیاز داری، یا وقتی هیچکدام از این دغدغهها مطرح نیست، args را حذف کن.
فرمِ exec وقتی args حاضر باشد اجرا میشود. Claude Code command را بهعنوانِ یک فایلِ اجرایی روی PATH resolve میکند و مستقیماً با args بهعنوانِ بردارِ آرگومان spawnش میکند. هیچ شِلی در کار نیست، پس هر عنصرِ args دقیقاً همانطور که نوشته شده یک آرگومان است، و جانگهدارهای مسیر مثل ${CLAUDE_PLUGIN_ROOT} بهصورتِ رشتههای ساده در command و در هر عنصرِ args جایگزین میشوند. کاراکترهای خاص مثل apostrophe، $ و backtick بیکموکاست عبور میکنند چون هیچ شِلی برای تفسیرشان نیست. هیچ tokenization شِلی روی هیچ پلتفرمی رخ نمیدهد.
فرمِ shell وقتی args غایب باشد اجرا میشود. رشتهی command به یک شِل پاس داده میشود: sh -c روی macOS و Linux، Git Bash روی Windows، یا PowerShell وقتی Git Bash نصب نباشد. فیلدِ shell را تنظیم کن تا صریحاً انتخاب کنی. شِل رشته را tokenize میکند، متغیرها را بسط میدهد و pipeها، &&، redirectها و globها را تفسیر میکند.
این مثال یک اسکریپتِ Node را که همراه با یک پلاگین بستهبندی شده اجرا میکند. فرمِ exec مسیرِ resolveشدهی اسکریپت را بهعنوانِ یک آرگومانِ بدونِ نقلقول پاس میدهد:
{ "type": "command", "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js", "--fix"]}فرمِ shellِ معادل برای مدیریتِ مسیرهای دارای فاصله یا کاراکترهای خاص به نقلقول نیاز دارد:
{ "type": "command", "command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/format.js --fix"}هر دو فرم همان جانگهدارهای مسیر را پشتیبانی میکنند، و هر دو آنها را بهعنوانِ متغیرهای محیطیِ CLAUDE_PROJECT_DIR، CLAUDE_PLUGIN_ROOT و CLAUDE_PLUGIN_DATA روی پروسهی spawnشده export میکنند، پس یک اسکریپت میتواند process.env.CLAUDE_PLUGIN_ROOT را بخواند صرفِنظر از اینکه چطور راهاندازی شده. hookهای پلاگین علاوه بر این مقادیرِ ${user_config.*} را جایگزین میکنند؛ پیکربندیِ کاربر را ببین.
فیلدهای hookِ HTTP
Section titled “فیلدهای hookِ HTTP”علاوه بر فیلدهای مشترک، hookهای HTTP این فیلدها را میپذیرند:
| فیلد | الزامی | توضیح |
|---|---|---|
url | بله | URLی که درخواستِ POST به آن فرستاده میشود |
headers | خیر | هدرهای اضافیِ HTTP بهصورتِ جفتهای کلید-مقدار. مقادیر از درونیابیِ متغیرِ محیطی با نحوِ $VAR_NAME یا ${VAR_NAME} پشتیبانی میکنند. فقط متغیرهای فهرستشده در allowedEnvVars resolve میشوند |
allowedEnvVars | خیر | فهرستِ نامهای متغیرِ محیطی که میتوانند در مقادیرِ هدر درونیابی شوند. ارجاع به متغیرهای فهرستنشده با رشتهی خالی جایگزین میشوند. برای کارکردنِ هرگونه درونیابیِ متغیرِ محیطی الزامی است |
Claude Code ورودیِ JSONِ hook را بهعنوانِ بدنهی درخواستِ POST با Content-Type: application/json میفرستد. بدنهی پاسخ از همان فرمتِ خروجیِ JSONِ hookهای فرمان استفاده میکند.
مدیریتِ خطا با hookهای فرمان فرق دارد: پاسخهای غیرِ-2xx، شکستِ اتصال و timeoutها همگی خطاهای غیرمسدودکننده تولید میکنند که اجازه میدهند اجرا ادامه یابد. برای مسدودکردنِ یک فراخوانیِ ابزار یا ردِ یک مجوز، یک پاسخِ 2xx با بدنهی JSONِ شاملِ decision: "block" یا یک hookSpecificOutput با permissionDecision: "deny" برگردان.
این مثال رویدادهای PreToolUse را به یک سرویسِ اعتبارسنجیِ محلی میفرستد و با یک توکن از متغیرِ محیطیِ MY_TOKEN احراز هویت میکند:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "http", "url": "http://localhost:8080/hooks/pre-tool-use", "timeout": 30, "headers": { "Authorization": "Bearer $MY_TOKEN" }, "allowedEnvVars": ["MY_TOKEN"] } ] } ] }}فیلدهای hookِ ابزارِ MCP
Section titled “فیلدهای hookِ ابزارِ MCP”علاوه بر فیلدهای مشترک، hookهای ابزارِ MCP این فیلدها را میپذیرند:
| فیلد | الزامی | توضیح |
|---|---|---|
server | بله | نامِ یک سرورِ MCPِ پیکربندیشده. سرور باید از پیش متصل باشد؛ hook هرگز یک جریانِ OAuth یا اتصال را راه نمیاندازد |
tool | بله | نامِ ابزاری که روی آن سرور فراخوانی میشود |
input | خیر | آرگومانهای پاسدادهشده به ابزار. مقادیرِ رشتهای از جایگزینیِ ${path} از ورودیِ JSONِ hook پشتیبانی میکنند، مثلِ "${tool_input.file_path}" |
محتوای متنیِ ابزار مثلِ stdoutِ hookِ فرمان رفتار میشود: اگر بهعنوانِ خروجیِ JSONِ معتبر پارس شود بهعنوانِ یک تصمیم پردازش میشود، در غیرِ اینصورت بهصورتِ متنِ ساده نشان داده میشود. اگر سرورِ نامبردهشده متصل نباشد، یا ابزار isError: true برگرداند، hook یک خطای غیرمسدودکننده تولید میکند و اجرا ادامه مییابد.
hookهای ابزارِ MCP روی هر رویدادِ hook در دسترساند بهمحض اینکه Claude Code به سرورهای MCPت متصل شده باشد. SessionStart و Setup معمولاً پیش از اتمامِ اتصالِ سرورها فعال میشوند، پس hookهای آن رویدادها باید انتظارِ خطای «متصل نیست» را در اولین اجرا داشته باشند.
این مثال ابزارِ security_scan را روی سرورِ MCPِ my_server پس از هر Write یا Edit فراخوانی میکند و مسیرِ فایلِ ویرایششده را پاس میدهد:
{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "mcp_tool", "server": "my_server", "tool": "security_scan", "input": { "file_path": "${tool_input.file_path}" } } ] } ] }}فیلدهای hookِ پرامپت و ایجنت
Section titled “فیلدهای hookِ پرامپت و ایجنت”علاوه بر فیلدهای مشترک، hookهای پرامپت و ایجنت این فیلدها را میپذیرند:
| فیلد | الزامی | توضیح |
|---|---|---|
prompt | بله | متنِ پرامپتی که به مدل فرستاده میشود. از $ARGUMENTS بهعنوانِ جانگهدارِ JSONِ ورودیِ hook استفاده کن. برای گنجاندنِ متنِ تحتاللفظی با backslash escape کن: \$1.00 بهصورتِ $1.00 رندر میشود |
model | خیر | مدلی که برای ارزیابی استفاده میشود. پیشفرض یک مدلِ سریع |
همهی hookهای مطابقتیافته بهصورتِ موازی اجرا میشوند، و handlerهای یکسان بهصورتِ خودکار حذفِ تکراری میشوند. hookهای فرمان بر اساسِ رشتهی فرمان و args حذفِ تکراری میشوند، و hookهای HTTP بر اساسِ URL. handlerها در دایرکتوریِ جاری و با محیطِ Claude Code اجرا میشوند. متغیرِ محیطیِ $CLAUDE_CODE_REMOTE در محیطهای وبِ ریموت روی "true" تنظیم میشود و در CLIِ محلی تنظیم نمیشود.
ارجاع به اسکریپتها از طریقِ مسیر
Section titled “ارجاع به اسکریپتها از طریقِ مسیر”از این جانگهدارها برای ارجاع به اسکریپتهای hook نسبت به ریشهی پروژه یا پلاگین استفاده کن، صرفِنظر از دایرکتوریِ کاری هنگامِ اجرای hook:
${CLAUDE_PROJECT_DIR}: ریشهی پروژه. Claude Code این متغیر را در محیطِ سرورهای MCPِ stdio و سرورهای LSPِ پلاگین نیز تنظیم میکند.${CLAUDE_PLUGIN_ROOT}: دایرکتوریِ نصبِ پلاگین، برای اسکریپتهایی که همراه با یک پلاگین بستهبندی شدهاند. در هر بهروزرسانیِ پلاگین تغییر میکند.${CLAUDE_PLUGIN_DATA}: دایرکتوریِ دادهی پایدارِ پلاگین، برای وابستگیها و حالتی که باید از بهروزرسانیهای پلاگین جان بهدر ببرند.
برای هر hookی که به یک جانگهدارِ مسیر اشاره میکند، فرمِ exec را ترجیح بده. فرمِ exec هر عنصرِ args را بهعنوانِ یک آرگومان بدونِ tokenization شِل پاس میدهد، پس مسیرهای دارای فاصله یا کاراکترهای خاص به هیچ نقلقولی نیاز ندارند. در فرمِ shell، هر جانگهدار را در نقلقولِ دوگانه بپیچ.
این مثال از ${CLAUDE_PROJECT_DIR} استفاده میکند تا یک بررسیکنندهی استایل را از دایرکتوریِ .claude/hooks/ِ پروژه پس از هر فراخوانیِ ابزارِ Write یا Edit اجرا کند:
{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh", "args": [] } ] } ] }}hookهای پلاگین را در hooks/hooks.json با یک فیلدِ اختیاریِ descriptionِ سطحِ بالا تعریف کن. وقتی یک پلاگین فعال است، hookهایش با hookهای کاربر و پروژهی تو ادغام میشوند.
این مثال یک اسکریپتِ فرمتکردن را که همراه با پلاگین بستهبندی شده اجرا میکند:
{ "description": "Automatic code formatting", "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh", "args": [], "timeout": 30 } ] } ] }}برای جزئیاتِ ساختنِ hookهای پلاگین، مرجعِ کامپوننتهای پلاگین را ببین.
hookها در skillها و ایجنتها
Section titled “hookها در skillها و ایجنتها”علاوه بر فایلهای تنظیمات و پلاگینها، hookها را میتوان مستقیماً در skillها و سابایجنتها با استفاده از frontmatter تعریف کرد. این hookها به چرخهی حیاتِ کامپوننت محدود میشوند و فقط وقتی آن کامپوننت فعال است اجرا میشوند.
همهی رویدادهای hook پشتیبانی میشوند. برای سابایجنتها، hookهای Stop بهصورتِ خودکار به SubagentStop تبدیل میشوند چون آن رویدادی است که هنگامِ تکمیلِ یک سابایجنت فعال میشود.
hookها از همان فرمتِ پیکربندیِ hookهای مبتنی بر تنظیمات استفاده میکنند اما به طولِ عمرِ کامپوننت محدود میشوند و وقتی کامپوننت تمام میشود پاکسازی میشوند.
این skill یک hookِ PreToolUse تعریف میکند که پیش از هر فرمانِ Bash یک اسکریپتِ اعتبارسنجیِ امنیتی اجرا میکند:
---name: secure-operationsdescription: Perform operations with security checkshooks: PreToolUse: - matcher: "Bash" hooks: - type: command command: "./scripts/security-check.sh"---ایجنتها از همان فرمت در frontmatterِ YAMLِ خود استفاده میکنند.
منوی /hooks
Section titled “منوی /hooks”در Claude Code /hooks را تایپ کن تا یک مرورگرِ فقطخواندنی برای hookهای پیکربندیشدهات باز شود. منو هر رویدادِ hook را با شمارشِ hookهای پیکربندیشده نشان میدهد، اجازه میدهد به matcherها وارد شوی، و جزئیاتِ کاملِ هر handlerِ hook را نشان میدهد. از آن برای تأییدِ پیکربندی، بررسیِ اینکه یک hook از کدام فایلِ تنظیمات آمده، یا بازرسیِ فرمان، پرامپت یا URLِ یک hook استفاده کن.
منو هر پنج نوعِ hook را نمایش میدهد: command، prompt، agent، http و mcp_tool. هر hook با یک پیشوندِ [type] و یک منبع که نشان میدهد کجا تعریف شده برچسب میخورد:
User: از~/.claude/settings.jsonProject: از.claude/settings.jsonLocal: از.claude/settings.local.jsonPlugin: ازhooks/hooks.jsonِ یک پلاگینSession: ثبتشده در حافظه برای نشستِ جاریBuilt-in: ثبتشده بهصورتِ داخلی توسطِ Claude Code
انتخابِ یک hook یک نمای جزئیات باز میکند که رویداد، matcher، نوع، فایلِ منبع و فرمان، پرامپت یا URLِ کاملش را نشان میدهد. منو فقطخواندنی است: برای افزودن، تغییر یا حذفِ hookها، تنظیماتِ JSON را مستقیماً ویرایش کن یا از Claude بخواه تغییر را اعمال کند.
غیرفعالکردن یا حذفِ hookها
Section titled “غیرفعالکردن یا حذفِ hookها”برای حذفِ یک hook، ورودیاش را از فایلِ تنظیماتِ JSON پاک کن.
برای غیرفعالکردنِ موقتِ همهی hookها بدونِ حذفشان، "disableAllHooks": true را در فایلِ تنظیماتت تنظیم کن. هیچ راهی برای غیرفعالکردنِ یک hookِ منفرد ضمنِ نگهداشتنش در پیکربندی وجود ندارد.
تنظیمِ disableAllHooks سلسلهمراتبِ تنظیماتِ مدیریتشده را رعایت میکند. اگر یک ادمین hookهایی را از طریقِ تنظیماتِ سیاستِ مدیریتشده پیکربندی کرده باشد، disableAllHooksِ تنظیمشده در تنظیماتِ کاربر، پروژه یا محلی نمیتواند آن hookهای مدیریتشده را غیرفعال کند. فقط disableAllHooksِ تنظیمشده در سطحِ تنظیماتِ مدیریتشده میتواند hookهای مدیریتشده را غیرفعال کند.
ویرایشهای مستقیمِ hookها در فایلهای تنظیمات معمولاً بهصورتِ خودکار توسطِ ناظرِ فایل (file watcher) برداشته میشوند.
ورودی و خروجیِ hook
Section titled “ورودی و خروجیِ hook”hookهای فرمان دادهی JSON را از طریقِ stdin دریافت میکنند و نتایج را از طریقِ کدهای خروج، stdout و stderr بازمیگردانند. hookهای HTTP همان JSON را بهعنوانِ بدنهی درخواستِ POST دریافت میکنند و نتایج را از طریقِ بدنهی پاسخِ HTTP بازمیگردانند. این بخش فیلدها و رفتارِ مشترکِ همهی رویدادها را پوشش میدهد. هر بخشِ رویداد زیرِ رویدادهای hook اسکیمای ورودیِ خاصِ خود و گزینههای کنترلِ تصمیمش را شامل میشود.
روی macOS و Linux، از نسخهی v2.1.139 به بعد hookهای فرمان در نشستِ خودشان و بدونِ ترمینالِ کنترلکننده اجرا میشوند. پروسهی hook و هر پروسهی فرزندی نمیتوانند /dev/tty را باز کنند یا توالیهای escape را مستقیماً به رابطِ Claude Code بفرستند. Windows هیچ /dev/ttyای ندارد. برای آشکارکردنِ یک پیام به کاربر روی هر پلتفرمی، systemMessage را در خروجیِ JSON برگردان. برای راهاندازیِ یک اعلانِ دسکتاپ، تنظیمِ عنوانِ پنجره یا زدنِ زنگ، بهجای آن terminalSequence را برگردان.
فیلدهای ورودیِ مشترک
Section titled “فیلدهای ورودیِ مشترک”رویدادهای hook این فیلدها را بهصورتِ JSON دریافت میکنند، علاوه بر فیلدهای خاصِ رویداد که در هر بخشِ رویدادِ hook مستند شدهاند. برای hookهای فرمان، این JSON از طریقِ stdin میرسد. برای hookهای HTTP، بهصورتِ بدنهی درخواستِ POST میرسد.
| فیلد | توضیح |
|---|---|
session_id | شناسهی نشستِ جاری |
transcript_path | مسیرِ JSONِ گفتوگو |
cwd | دایرکتوریِ کاریِ جاری هنگامِ فراخوانیِ hook |
permission_mode | حالتِ مجوزِ جاری: "default"، "plan"، "acceptEdits"، "auto"، "dontAsk"، یا "bypassPermissions". همهی رویدادها این فیلد را دریافت نمیکنند: مثالِ JSONِ هر رویداد در پایین را برای بررسی ببین |
effort | شیئی با فیلدِ level که سطحِ effortِ فعالِ این نوبت را نگه میدارد: "low"، "medium"، "high"، "xhigh"، یا "max". اگر effortِ درخواستیِ مدل از آنچه مدلِ جاری پشتیبانی میکند فراتر برود، این همان سطحِ کاهشیافتهای است که مدل واقعاً استفاده کرد. Ultracode یک سطحِ مجزا نیست و بهصورتِ "xhigh" گزارش میشود. این شیء با فیلدِ effortِ status line مطابقت دارد. برای رویدادهایی که در یک کانتکستِ استفادهی ابزار فعال میشوند، مثلِ PreToolUse، PostToolUse، Stop و SubagentStop، وقتی مدلِ جاری از پارامترِ effort پشتیبانی میکند حاضر است. این سطح برای فرمانهای hook و ابزارِ Bash نیز بهصورتِ متغیرِ محیطیِ $CLAUDE_EFFORT در دسترس است. |
hook_event_name | نامِ رویدادی که فعال شد |
وقتی با --agent یا داخلِ یک سابایجنت اجرا میشوی، دو فیلدِ اضافی گنجانده میشوند:
| فیلد | توضیح |
|---|---|
agent_id | شناسهی یکتای سابایجنت. فقط وقتی hook داخلِ یک فراخوانیِ سابایجنت فعال میشود حاضر است. از این برای تمایزِ فراخوانیهای hookِ سابایجنت از فراخوانیهای نخِ اصلی استفاده کن. |
agent_type | نامِ ایجنت (برای مثال، "Explore" یا "security-reviewer"). وقتی نشست از --agent استفاده میکند یا hook داخلِ یک سابایجنت فعال میشود حاضر است. برای سابایجنتها، نوعِ سابایجنت بر مقدارِ --agentِ نشست اولویت دارد. برای سابایجنتهای سفارشی، این فیلدِ name از frontmatterِ ایجنت است، نه نامفایل. |
فقط hookهای SessionStart میتوانند یک فیلدِ model دریافت کنند، و حضورش تضمین نشده است. هیچ متغیرِ محیطیِ $CLAUDE_MODEL وجود ندارد. یک پروسهی hook محیطِ والد را به ارث میبرد، پس میتواند $ANTHROPIC_MODEL را بخواند اگر آن را در شِلت تنظیم کنی، اما آن مقدار وقتی مدل را با /model در طولِ یک نشست عوض میکنی تغییر نمیکند.
برای مثال، یک hookِ PreToolUse برای یک فرمانِ Bash این را روی stdin دریافت میکند:
{ "session_id": "abc123", "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl", "cwd": "/home/user/my-project", "permission_mode": "default", "hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": { "command": "npm test" }}فیلدهای tool_name و tool_input خاصِ رویداد هستند. هر بخشِ رویدادِ hook فیلدهای اضافیِ آن رویداد را مستند میکند.
خروجیِ کدِ خروج
Section titled “خروجیِ کدِ خروج”کدِ خروج از فرمانِ hookت به Claude Code میگوید که اکشن باید ادامه یابد، مسدود شود، یا نادیده گرفته شود.
خروجِ 0 یعنی موفقیت. Claude Code stdout را برای فیلدهای خروجیِ JSON پارس میکند. خروجیِ JSON فقط در خروجِ 0 پردازش میشود. برای بیشترِ رویدادها، stdout در لاگِ دیباگ نوشته میشود اما در transcript نشان داده نمیشود. استثناها UserPromptSubmit، UserPromptExpansion و SessionStart هستند، که در آنها stdout بهعنوانِ کانتکستی که Claude میتواند ببیند و بر اساسش عمل کند اضافه میشود.
خروجِ 2 یعنی یک خطای مسدودکننده. Claude Code stdout و هر JSONی در آن را نادیده میگیرد. در عوض، متنِ stderr بهعنوانِ یک پیامِ خطا به Claude بازخورانده میشود. اثرش به رویداد بستگی دارد: PreToolUse فراخوانیِ ابزار را مسدود میکند، UserPromptSubmit پرامپت را رد میکند، و الی آخر. برای فهرستِ کامل، رفتارِ کدِ خروجِ 2 را ببین.
هر کدِ خروجِ دیگری برای بیشترِ رویدادهای hook یک خطای غیرمسدودکننده است. transcript یک اعلانِ <hook name> hook error و سپس خطِ اولِ stderr را نشان میدهد، تا بتوانی علت را بدونِ --debug شناسایی کنی. اجرا ادامه مییابد و stderrِ کامل در لاگِ دیباگ نوشته میشود.
برای مثال، یک اسکریپتِ فرمانِ hook که فرمانهای خطرناکِ Bash را مسدود میکند:
#!/bin/bash# Reads JSON input from stdin, checks the commandcommand=$(jq -r '.tool_input.command' < /dev/stdin)
if [[ "$command" == rm* ]]; then echo "Blocked: rm commands are not allowed" >&2 exit 2 # Blocking error: tool call is preventedfi
exit 0 # No decision: the normal permission flow appliesرفتارِ کدِ خروجِ 2 بهازای هر رویداد
Section titled “رفتارِ کدِ خروجِ 2 بهازای هر رویداد”کدِ خروجِ 2 راهی است که یک hook با آن سیگنالِ «بایست، این کار را نکن» میدهد. اثرش به رویداد بستگی دارد، چون برخی رویدادها اکشنهایی را نمایندگی میکنند که میتوان مسدودشان کرد (مثلِ یک فراخوانیِ ابزار که هنوز اتفاق نیفتاده) و برخی دیگر چیزهایی را نمایندگی میکنند که قبلاً اتفاق افتادهاند یا قابلِ جلوگیری نیستند.
| رویدادِ hook | قابلِ مسدودکردن؟ | در خروجِ 2 چه میشود |
|---|---|---|
PreToolUse | بله | فراخوانیِ ابزار را مسدود میکند |
PermissionRequest | بله | مجوز را رد میکند |
UserPromptSubmit | بله | پردازشِ پرامپت را مسدود میکند و پرامپت را پاک میکند |
UserPromptExpansion | بله | بسط را مسدود میکند |
Stop | بله | از توقفِ Claude جلوگیری میکند، گفتوگو را ادامه میدهد |
SubagentStop | بله | از توقفِ سابایجنت جلوگیری میکند |
TeammateIdle | بله | از idleشدنِ همتیمی جلوگیری میکند (همتیمی به کارکردن ادامه میدهد) |
TaskCreated | بله | ساختِ task را برمیگرداند |
TaskCompleted | بله | از علامتخوردنِ task بهعنوانِ تکمیلشده جلوگیری میکند |
ConfigChange | بله | از اعمالشدنِ تغییرِ پیکربندی جلوگیری میکند (بهجز policy_settings) |
StopFailure | خیر | خروجی و کدِ خروج نادیده گرفته میشوند |
PostToolUse | خیر | stderr را به Claude نشان میدهد (ابزار قبلاً اجرا شده) |
PostToolUseFailure | خیر | stderr را به Claude نشان میدهد (ابزار قبلاً شکست خورده) |
PostToolBatch | بله | حلقهی ایجنتیک را پیش از فراخوانیِ بعدیِ مدل متوقف میکند |
PermissionDenied | خیر | کدِ خروج و stderr نادیده گرفته میشوند (رد قبلاً رخ داده). از JSONِ hookSpecificOutput.retry: true برای گفتن به مدل که میتواند دوباره امتحان کند استفاده کن |
Notification | خیر | stderr را فقط به کاربر نشان میدهد |
SubagentStart | خیر | stderr را فقط به کاربر نشان میدهد |
SessionStart | خیر | stderr را فقط به کاربر نشان میدهد |
Setup | خیر | stderr را فقط به کاربر نشان میدهد |
SessionEnd | خیر | stderr را فقط به کاربر نشان میدهد |
CwdChanged | خیر | stderr را فقط به کاربر نشان میدهد |
FileChanged | خیر | stderr را فقط به کاربر نشان میدهد |
PreCompact | بله | فشردهسازی را مسدود میکند |
PostCompact | خیر | stderr را فقط به کاربر نشان میدهد |
Elicitation | بله | elicitation را رد میکند |
ElicitationResult | بله | پاسخ را مسدود میکند (اکشن تبدیل به decline میشود) |
WorktreeCreate | بله | هر کدِ خروجِ غیرصفر باعثِ شکستِ ساختِ worktree میشود |
WorktreeRemove | خیر | شکستها فقط در حالتِ دیباگ لاگ میشوند |
InstructionsLoaded | خیر | کدِ خروج نادیده گرفته میشود |
MessageDisplay | خیر | متنِ اصلی نمایش داده میشود |
مدیریتِ پاسخِ HTTP
Section titled “مدیریتِ پاسخِ HTTP”hookهای HTTP بهجای کدهای خروج و stdout از کدهای وضعیتِ HTTP و بدنههای پاسخ استفاده میکنند:
- 2xx با بدنهی خالی: موفقیت، معادلِ کدِ خروجِ 0 بدونِ خروجی
- 2xx با بدنهی متنِ ساده: موفقیت، متن بهعنوانِ کانتکست اضافه میشود
- 2xx با بدنهی JSON: موفقیت، با همان اسکیمای خروجیِ JSONِ hookهای فرمان پارس میشود
- وضعیتِ غیرِ-2xx: خطای غیرمسدودکننده، اجرا ادامه مییابد
- شکستِ اتصال یا timeout: خطای غیرمسدودکننده، اجرا ادامه مییابد
برخلافِ hookهای فرمان، hookهای HTTP نمیتوانند تنها از طریقِ کدهای وضعیت یک خطای مسدودکننده سیگنال بدهند. برای مسدودکردنِ یک فراخوانیِ ابزار یا ردِ یک مجوز، یک پاسخِ 2xx با بدنهی JSONِ شاملِ فیلدهای تصمیمِ مناسب برگردان.
خروجیِ JSON
Section titled “خروجیِ JSON”کدهای خروج فقط اجازه میدهند مسدود کنی یا ساکت بمانی، اما خروجیِ JSON کنترلِ ریزدانهتری میدهد. بهجای خروج با کدِ 2 برای مسدودکردن، با کدِ 0 خارج شو و یک شیء JSON روی stdout چاپ کن. Claude Code فیلدهای مشخصی از آن JSON را برای کنترلِ رفتار میخواند، از جمله کنترلِ تصمیم برای مسدودکردن، مجازشمردن یا ارجاع به کاربر.
stdoutِ hookت باید فقط شاملِ شیء JSON باشد. اگر پروفایلِ شِلت هنگامِ راهاندازی متنی چاپ کند، میتواند در پارسِ JSON اختلال ایجاد کند. JSON validation failed در راهنمای عیبیابی را ببین.
رشتههای خروجیِ hook، از جمله additionalContext، systemMessage و stdoutِ ساده، به ۱۰٬۰۰۰ کاراکتر محدود میشوند. خروجیای که از این سقف فراتر رود در یک فایل ذخیره و با یک پیشنمایش و مسیرِ فایل جایگزین میشود، درست همانطور که نتایجِ بزرگِ ابزار مدیریت میشوند.
شیء JSON سه دسته فیلد را پشتیبانی میکند:
- فیلدهای جهانی مثلِ
continueدر همهی رویدادها کار میکنند. اینها در جدولِ زیر فهرست شدهاند. decisionوreasonِ سطحِ بالا توسطِ برخی رویدادها برای مسدودکردن یا ارائهی بازخورد استفاده میشوند.hookSpecificOutputیک شیء تودرتو برای رویدادهایی است که به کنترلِ غنیتری نیاز دارند. به یک فیلدِhookEventNameتنظیمشده به نامِ رویداد نیاز دارد.
| فیلد | پیشفرض | توضیح |
|---|---|---|
continue | true | اگر false باشد، Claude پس از اجرای hook پردازش را کاملاً متوقف میکند. بر هر فیلدِ تصمیمِ خاصِ رویداد اولویت دارد |
stopReason | هیچ | پیامی که وقتی continue برابرِ false است به کاربر نشان داده میشود. به Claude نشان داده نمیشود |
suppressOutput | false | اگر true باشد، stdoutِ hook را از transcript پنهان میکند. stdout همچنان در لاگِ دیباگ ظاهر میشود |
systemMessage | هیچ | پیامِ هشداری که به کاربر نشان داده میشود |
terminalSequence | هیچ | یک توالیِ escapeِ ترمینال که Claude Code از طرفِ تو منتشر میکند، مثلِ یک اعلانِ دسکتاپ، عنوانِ پنجره یا زنگ. به OSCِ 0/1/2/9/99/777 و BEL محدود است. اگر مقدار شاملِ چیزی خارج از فهرستِ مجاز باشد، فیلد نادیده گرفته میشود. از این بهجای نوشتن در /dev/tty که برای hookها در دسترس نیست استفاده کن |
برای متوقفکردنِ کاملِ Claude صرفِنظر از نوعِ رویداد:
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }انتشارِ اعلانهای ترمینال
Section titled “انتشارِ اعلانهای ترمینال”فیلدِ terminalSequence به Claude Code نسخهی v2.1.141 یا بالاتر نیاز دارد.
hookها بدونِ ترمینالِ کنترلکننده اجرا میشوند، پس نوشتنِ توالیهای escape مستقیماً در /dev/tty شکست میخورد. در عوض، توالیِ escape را در فیلدِ terminalSequence برگردان و Claude Code آن را از طریقِ مسیرِ نوشتنِ ترمینالِ خودش از طرفِ تو منتشر میکند. این بدونِ race است، داخلِ tmux و GNU screen کار میکند، و روی Windows که /dev/ttyای ندارد کار میکند.
این فیلد یک رشته از یک یا چند توالیِ escapeِ مجاز را میپذیرد:
- OSCِ
0،1،2: عنوانهای پنجره و آیکون - OSCِ
9: اعلانهای iTerm2، ConEmu، Windows Terminal و WezTerm، از جمله پیشرفتِ نوارِ وظیفهی9;4 - OSCِ
99: اعلانهای Kitty - OSCِ
777: اعلانهای urxvt، Ghostty و Warp - BELِ خالی
توالیها میتوانند با BEL یا با ST خاتمه یابند. هر چیزی خارج از فهرستِ مجاز، از جمله توالیهای مکاننما و رنگِ CSI، توالیهای paletteی OSC، hyperlinkهای OSC 8، نوشتنهای clipboardِ OSC 52 و OSC 1337، رد میشود و فیلد نادیده گرفته میشود.
مثالِ زیر یک اعلانِ دسکتاپ را از یک hookِ Notification فعال میکند. توالیِ escape با escapeهای اوکتالِ printf ساخته میشود تا بایتهای کنترلی هرگز روی خطِ فرمانِ شِل ظاهر نشوند، و jq -n --arg خروجیِ JSON را میسازد تا نقلقولها، backslashها و newlineها در پیامِ اعلان درست escape شوند:
#!/bin/bash# Notification hook: ping the desktop when Claude Code needs attention.input=$(cat)title="Claude Code"body=$(jq -r '.message // "Needs your attention"' <<<"$input")seq=$(printf '\033]777;notify;%s;%s\007' "$title" "$body")jq -nc --arg seq "$seq" '{terminalSequence: $seq}'شکلِ { "terminalSequence": "..." } از هر شِل یا زبانی یکسان است. روی Windows، رشتهی escape را در PowerShell یا یک اسکریپت بساز و همان شیء JSON را منتشر کن.
افزودنِ کانتکست برای Claude
Section titled “افزودنِ کانتکست برای Claude”فیلدِ additionalContext یک رشته را از hookت به پنجرهی کانتکستِ Claude پاس میدهد. Claude Code رشته را در یک system reminder میپیچد و آن را در گفتوگو در نقطهای که hook فعال شد درج میکند. Claude reminder را در درخواستِ بعدیِ مدل میخواند، اما بهصورتِ یک پیامِ چت در رابط ظاهر نمیشود.
additionalContext را داخلِ hookSpecificOutput در کنارِ نامِ رویداد برگردان:
{ "hookSpecificOutput": { "hookEventName": "PostToolUse", "additionalContext": "This file is generated. Edit src/schema.ts and run `bun generate` instead." }}اینکه reminder کجا ظاهر میشود به رویداد بستگی دارد:
- SessionStart، Setup و SubagentStart: در آغازِ گفتوگو، پیش از اولین پرامپت
- UserPromptSubmit و UserPromptExpansion: در کنارِ پرامپتِ ثبتشده
- PreToolUse، PostToolUse، PostToolUseFailure و PostToolBatch: کنارِ نتیجهی ابزار
- Stop و SubagentStop: در پایانِ نوبت. گفتوگو ادامه مییابد تا Claude بتواند بر اساسِ بازخورد عمل کند. کنترلِ تصمیمِ Stop را ببین
وقتی چند hook برای یک رویداد additionalContext برمیگردانند، Claude همهی مقادیر را دریافت میکند. اگر یک مقدار از ۱۰٬۰۰۰ کاراکتر فراتر رود، Claude Code متنِ کامل را در فایلی در دایرکتوریِ نشست مینویسد و بهجایش مسیرِ فایل را با یک پیشنمایشِ کوتاه به Claude پاس میدهد.
از additionalContext برای اطلاعاتی استفاده کن که Claude باید دربارهی وضعیتِ جاریِ محیطت یا عملیاتی که همینالان اجرا شد بداند:
- وضعیتِ محیط: شاخهی جاری، مقصدِ استقرار، یا feature flagهای فعال
- قواعدِ مشروطِ پروژه: کدام فرمانِ تست برای فایلی که همینالان ویرایش شد اعمال میشود، کدام دایرکتوریها در این worktree فقطخواندنیاند
- دادهی بیرونی: issueهای بازِ منتسب به تو، نتایجِ اخیرِ CI، محتوای واکشیشده از یک سرویسِ داخلی
برای دستورالعملهایی که هرگز تغییر نمیکنند، CLAUDE.md را ترجیح بده. بدونِ اجرای یک اسکریپت بارگذاری میشود و جای استانداردِ قراردادهای ایستای پروژه است.
متن را بهصورتِ گزارههای واقعی بنویس نه دستورالعملهای سیستمیِ امری. عباراتی مثلِ «مقصدِ استقرار production است» یا «این مخزن از bun test استفاده میکند» بهعنوانِ اطلاعاتِ پروژه خوانده میشوند. متنی که بهصورتِ فرمانهای سیستمیِ خارج از باند چارچوببندی شود میتواند دفاعهای ضدِ تزریقِ پرامپتِ Claude را فعال کند، که باعث میشود Claude بهجای رفتارِ کانتکستی، متن را به تو آشکار کند.
بهمحضِ تزریق، متن در transcriptِ نشست ذخیره میشود. برای رویدادهای میاننشستی مثلِ PostToolUse یا UserPromptSubmit، از سرگیری با --continue یا --resume بهجای اجرای دوبارهی hook برای نوبتهای گذشته، متنِ ذخیرهشده را بازپخش میکند، پس مقادیری مثلِ timestamp یا SHAهای کامیت هنگامِ از سرگیری کهنه میشوند. hookهای SessionStart هنگامِ از سرگیری دوباره اجرا میشوند با source تنظیمشده به "resume"، پس میتوانند کانتکستشان را تازه کنند.
کنترلِ تصمیم
Section titled “کنترلِ تصمیم”هر رویداد مسدودکردن یا کنترلِ رفتار از طریقِ JSON را پشتیبانی نمیکند. رویدادهایی که میکنند هرکدام مجموعهی متفاوتی از فیلدها را برای بیانِ آن تصمیم به کار میبرند. از این جدول بهعنوانِ مرجعِ سریع پیش از نوشتنِ یک hook استفاده کن:
| رویدادها | الگوی تصمیم | فیلدهای کلیدی |
|---|---|---|
| UserPromptSubmit، UserPromptExpansion، PostToolUse، PostToolUseFailure، PostToolBatch، Stop، SubagentStop، ConfigChange، PreCompact | decisionِ سطحِ بالا | decision: "block"، reason. Stop و SubagentStop همچنین hookSpecificOutput.additionalContext را برای بازخوردِ غیرخطایی که گفتوگو را ادامه میدهد میپذیرند |
| TeammateIdle، TaskCreated، TaskCompleted | کدِ خروج یا continue: false | کدِ خروجِ 2 اکشن را با بازخوردِ stderr مسدود میکند. JSONِ {"continue": false, "stopReason": "..."} نیز همتیمی را کاملاً متوقف میکند، مطابق با رفتارِ hookِ Stop |
| PreToolUse | hookSpecificOutput | permissionDecision (allow/deny/ask/defer)، permissionDecisionReason |
| PermissionRequest | hookSpecificOutput | decision.behavior (allow/deny) |
| PermissionDenied | hookSpecificOutput | retry: true به مدل میگوید میتواند فراخوانیِ ردشدهی ابزار را دوباره امتحان کند |
| WorktreeCreate | بازگشتِ مسیر | hookِ فرمان مسیر را روی stdout چاپ میکند؛ hookِ HTTP hookSpecificOutput.worktreePath را برمیگرداند. شکستِ hook یا نبودِ مسیر باعثِ شکستِ ساخت میشود |
| Elicitation | hookSpecificOutput | action (accept/decline/cancel)، content (مقادیرِ فیلدهای فرم برای accept) |
| ElicitationResult | hookSpecificOutput | action (accept/decline/cancel)، content (بازنویسیِ مقادیرِ فیلدهای فرم) |
| MessageDisplay | hookSpecificOutput | displayContent متنِ نمایشدادهشده روی صفحه را جایگزین میکند. فقط-نمایش: transcript و آنچه Claude میبیند اصل را نگه میدارند |
| SessionStart، Setup، SubagentStart | فقط کانتکست | hookSpecificOutput.additionalContext کانتکست برای Claude اضافه میکند. SessionStart همچنین initialUserMessage، watchPaths، sessionTitle و reloadSkills را میپذیرد. بدونِ مسدودکردن یا کنترلِ تصمیم |
| WorktreeRemove، Notification، SessionEnd، PostCompact، InstructionsLoaded، StopFailure، CwdChanged، FileChanged | هیچ | بدونِ کنترلِ تصمیم. برای اثرهای جانبی مثلِ لاگکردن یا پاکسازی استفاده میشود |
چند رویداد میتوانند محتوا را بازنویسی نیز بکنند بهجای آنکه فقط مجاز یا مسدودش کنند:
PreToolUse—updatedInputمستقیماً زیرِhookSpecificOutputآرگومانهای یک ابزار را پیش از اجرا جایگزین میکند (جزئیات)PermissionRequest—updatedInputداخلِ شیءdecision(جزئیات)PostToolUse—updatedToolOutputنتیجهی ابزار را جایگزین میکند (جزئیات)UserPromptSubmit— نمیتواند پرامپت را جایگزین کند؛ فقطadditionalContextرا در کنارش تزریق میکند
برای مواردِ استفادهی ویرایش (redaction) یا تبدیل، در PreToolUse برای ورودیهای خروجیِ ابزار و در PostToolUse برای نتایجِ ورودیِ ابزار میانجیگری کن.
اینها مثالهایی از هر الگو در عمل هستند:
توسطِ UserPromptSubmit، UserPromptExpansion، PostToolUse، PostToolUseFailure، PostToolBatch، Stop، SubagentStop، ConfigChange و PreCompact استفاده میشود. تنها مقدار "block" است. برای اجازهدادن به ادامهی اکشن، decision را از JSONت حذف کن، یا با کدِ 0 بدونِ هیچ JSONی خارج شو:
{ "decision": "block", "reason": "Test suite must pass before proceeding"}برای کنترلِ غنیتر از hookSpecificOutput استفاده میکند: مجاز، رد، یا ارجاع به کاربر. میتوانی ورودیِ ابزار را پیش از اجرا تغییر دهی یا کانتکستِ اضافی برای Claude تزریق کنی. برای مجموعهی کاملِ گزینهها کنترلِ تصمیمِ PreToolUse را ببین.
{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Database writes are not allowed" }}از hookSpecificOutput برای مجاز یا ردِ یک درخواستِ مجوز از طرفِ کاربر استفاده میکند. هنگامِ اجازهدادن، میتوانی ورودیِ ابزار را نیز تغییر دهی یا قواعدِ مجوز اعمال کنی تا کاربر دوباره پرسیده نشود. برای مجموعهی کاملِ گزینهها کنترلِ تصمیمِ PermissionRequest را ببین.
{ "hookSpecificOutput": { "hookEventName": "PermissionRequest", "decision": { "behavior": "allow", "updatedInput": { "command": "npm run lint" } } }}برای مثالهای گستردهتر شاملِ اعتبارسنجیِ فرمانِ Bash، فیلترِ پرامپت و اسکریپتهای تأییدِ خودکار، چه چیزهایی را میتوانی خودکار کنی در راهنما و پیادهسازیِ مرجعِ اعتبارسنجِ فرمانِ Bash را ببین.
رویدادهای hook
Section titled “رویدادهای hook”هر رویداد متناظر با نقطهای در چرخهی حیاتِ Claude Code است که hookها میتوانند در آن اجرا شوند. بخشهای زیر مرتب شدهاند تا با چرخهی حیات مطابقت کنند: از راهاندازیِ نشست تا حلقهی ایجنتیک تا پایانِ نشست. هر بخش توصیف میکند که رویداد کِی فعال میشود، چه matcherهایی را پشتیبانی میکند، چه ورودیِ JSONی دریافت میکند، و چطور رفتار را از طریقِ خروجی کنترل کند.
SessionStart
Section titled “SessionStart”وقتی Claude Code یک نشستِ تازه آغاز میکند یا یک نشستِ موجود را از سر میگیرد اجرا میشود. برای بارگذاریِ کانتکستِ توسعه مثلِ issueهای موجود یا تغییراتِ اخیرِ کدبیست، یا تنظیمِ متغیرهای محیطی مفید است. برای کانتکستِ ایستا که به اسکریپت نیاز ندارد، بهجایش از CLAUDE.md استفاده کن.
SessionStart در هر نشست اجرا میشود، پس این hookها را سریع نگه دار. فقط hookهای type: "command" و type: "mcp_tool" پشتیبانی میشوند.
مقدارِ matcher متناظر با این است که نشست چطور آغاز شده:
| Matcher | چه زمانی فعال میشود |
|---|---|
startup | نشستِ تازه |
resume | --resume، --continue، یا /resume |
clear | /clear |
compact | فشردهسازیِ خودکار یا دستی |
ورودیِ SessionStart
Section titled “ورودیِ SessionStart”علاوه بر فیلدهای ورودیِ مشترک، hookهای SessionStart فیلدِ source و اختیاراً model، agent_type و session_title را دریافت میکنند. فیلدِ source نشان میدهد نشست چطور آغاز شده: "startup" برای نشستهای تازه، "resume" برای نشستهای از سر گرفتهشده، "clear" پس از /clear، یا "compact" پس از فشردهسازی. فیلدِ model شناسهی مدلِ فعال را شامل میشود. میتواند حذف شود، برای مثال پس از /clear یا وقتی یک نشست از طریقِ بازیابیِ گفتوگو احیا میشود، پس پیش از خواندن فیلد را بررسی کن. اگر Claude Code را با claude --agent <name> اجرا کنی، یک فیلدِ agent_type نامِ ایجنت را شامل میشود. فیلدِ session_title عنوانِ نشستِ جاری را حمل میکند اگر یکی از قبل تنظیم شده باشد، برای مثال از طریقِ --name یا /rename. hookی که sessionTitle منتشر میکند میتواند ابتدا session_title را بررسی کند تا عنوانی را که کاربر صریحاً تنظیم کرده بازنویسی نکند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "SessionStart", "source": "startup", "model": "claude-sonnet-4-6"}کنترلِ تصمیمِ SessionStart
Section titled “کنترلِ تصمیمِ SessionStart”هر متنی که اسکریپتِ hookت روی stdout چاپ کند بهعنوانِ کانتکست برای Claude اضافه میشود. علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، میتوانی این فیلدهای خاصِ رویداد را برگردانی:
| فیلد | توضیح |
|---|---|
additionalContext | رشتهای که در آغازِ گفتوگو، پیش از اولین پرامپت، به کانتکستِ Claude اضافه میشود. برای اینکه متن چطور تحویل داده میشود و چه چیزی در آن بگذاری، افزودنِ کانتکست برای Claude را ببین |
initialUserMessage | رشتهای که بهعنوانِ اولین پیامِ کاربرِ نشست استفاده میشود. در حالتِ غیرتعاملی (-p) اعمال میشود، جایی که حتی اگر پرامپتی ارائه نشود اولین نوبت میشود. اگر پرامپتی ارائه شود، بهعنوانِ نوبتِ بعدی دنبالش میآید. برخلافِ additionalContext که به یک نوبتِ موجود میچسبد، این یکی نوبت را میسازد |
sessionTitle | عنوانِ نشست را تنظیم میکند، با همان اثرِ /rename. برای نامگذاریِ خودکارِ نشستها از پوشهی راهاندازی، شاخهی git یا نامِ worktree استفاده کن. فقط وقتی source برابرِ "startup" یا "resume" است اعمال میشود؛ روی "clear" و "compact" نادیده گرفته میشود |
watchPaths | آرایهای از مسیرهای مطلق برای watch جهتِ رویدادهای FileChanged در طولِ این نشست |
reloadSkills | بولین. وقتی true باشد، Claude Code پس از تکمیلِ hookهای SessionStart دایرکتوریهای skill و فرمان را دوباره اسکن میکند، پس skillهایی که hook نصب کرده در همان نشست در دسترساند، از اولین پرامپت |
{ "hookSpecificOutput": { "hookEventName": "SessionStart", "additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2", "sessionTitle": "auth-refactor" }}چون stdoutِ ساده برای این رویداد همینحالا به Claude میرسد، hookی که فقط کانتکست بارگذاری میکند میتواند مستقیماً روی stdout چاپ کند بدونِ ساختنِ JSON. وقتی به ترکیبِ کانتکست با فیلدهای دیگری مثلِ suppressOutput یا sessionTitle نیاز داری از فرمِ JSON استفاده کن.
از reloadSkills وقتی استفاده کن که یک hookِ SessionStart skillها را نصب یا بهروز میکند. کشفِ skill معمولاً پیش از اتمامِ hookهای SessionStart اجرا میشود، پس فایلهایی که hook در ~/.claude/skills/ یا .claude/skills/ مینویسد در غیرِ اینصورت فقط در نشستِ بعدی ظاهر میشوند. این مثال یک مخزنِ skillِ مشترک را همگام و درخواستِ اسکنِ دوباره میکند:
#!/bin/bash
git -C ~/.claude/skills/team-skills pull --quiet 2>/dev/null || \ git clone --quiet https://git.example.com/your-org/team-skills.git ~/.claude/skills/team-skills
echo '{"hookSpecificOutput": {"hookEventName": "SessionStart", "reloadSkills": true}}'پایداریبخشیدن به متغیرهای محیطی
Section titled “پایداریبخشیدن به متغیرهای محیطی”hookهای SessionStart به متغیرِ محیطیِ CLAUDE_ENV_FILE دسترسی دارند، که مسیرِ فایلی را فراهم میکند که در آن میتوانی متغیرهای محیطی را برای فرمانهای Bashِ بعدی پایدار کنی.
برای تنظیمِ متغیرهای محیطیِ منفرد، گزارههای export را در CLAUDE_ENV_FILE بنویس. از append (>>) استفاده کن تا متغیرهای تنظیمشده توسطِ hookهای دیگر حفظ شوند:
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE" echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE" echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"fi
exit 0برای ضبطِ همهی تغییراتِ محیطی از فرمانهای راهاندازی، متغیرهای exportشده را پیش و پس مقایسه کن:
#!/bin/bash
ENV_BEFORE=$(export -p | sort)
# Run your setup commands that modify the environmentsource ~/.nvm/nvm.shnvm use 20
if [ -n "$CLAUDE_ENV_FILE" ]; then ENV_AFTER=$(export -p | sort) comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"fi
exit 0هر متغیری که در این فایل نوشته شود در همهی فرمانهای Bashِ بعدی که Claude Code در طولِ نشست اجرا میکند در دسترس خواهد بود.
فقط وقتی فعال میشود که Claude Code را با --init-only، یا با --init یا --maintenance در حالتِ print (-p) راه بیندازی. روی راهاندازیِ عادی فعال نمیشود. از آن برای نصبِ یکبارهی وابستگی یا پاکسازیِ زمانبندیشدهای استفاده کن که صریحاً از CI یا اسکریپتها فعال میکنی، جدا از راهاندازیِ عادیِ نشست. برای راهاندازیِ هر-نشست، بهجایش از SessionStart استفاده کن.
مقدارِ matcher متناظر با پرچمِ CLIی است که hook را فعال کرد:
| Matcher | چه زمانی فعال میشود |
|---|---|
init | claude --init-only یا claude -p --init |
maintenance | claude -p --maintenance |
--init-only hookهای Setup و hookهای SessionStart را با matcherِ startup اجرا میکند، سپس بدونِ شروعِ یک گفتوگو خارج میشود. --init و --maintenance فقط وقتی با -p (حالتِ print) ترکیب شوند hookهای Setup را فعال میکنند؛ در یک نشستِ تعاملی این دو پرچم در حالِ حاضر hookهای Setup را فعال نمیکنند.
چون Setup در هر راهاندازی فعال نمیشود، پلاگینی که به نصبِ یک وابستگی نیاز دارد نمیتواند فقط به Setup تکیه کند. الگوی عملی این است که در اولین استفاده وابستگی را بررسی و در صورتِ نبود نصب کنی، برای مثال یک hook یا skill که ${CLAUDE_PLUGIN_DATA}/node_modules را آزمایش میکند و در صورتِ نبود npm install را اجرا میکند. برای اینکه وابستگیهای نصبشده را کجا ذخیره کنی، دایرکتوریِ دادهی پایدار را ببین.
ورودیِ Setup
Section titled “ورودیِ Setup”علاوه بر فیلدهای ورودیِ مشترک، hookهای Setup یک فیلدِ trigger تنظیمشده به یا "init" یا "maintenance" دریافت میکنند:
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "Setup", "trigger": "init"}کنترلِ تصمیمِ Setup
Section titled “کنترلِ تصمیمِ Setup”hookهای Setup نمیتوانند مسدود کنند. در کدِ خروجِ 2، stderr به کاربر نشان داده میشود؛ در هر کدِ خروجِ غیرصفرِ دیگری، stderr فقط وقتی با --verbose راه بیندازی ظاهر میشود. در هر دو حالت اجرا ادامه مییابد. برای پاسدادنِ اطلاعات به کانتکستِ Claude، additionalContext را در خروجیِ JSON برگردان؛ stdoutِ ساده فقط در لاگِ دیباگ نوشته میشود. علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، میتوانی این فیلدهای خاصِ رویداد را برگردانی:
| فیلد | توضیح |
|---|---|
additionalContext | رشتهای که به کانتکستِ Claude اضافه میشود. مقادیرِ چند hook به هم متصل میشوند |
{ "hookSpecificOutput": { "hookEventName": "Setup", "additionalContext": "Dependencies installed: node_modules, .venv" }}hookهای Setup به CLAUDE_ENV_FILE دسترسی دارند. متغیرهای نوشتهشده در آن فایل به فرمانهای Bashِ بعدیِ نشست پایدار میمانند، درست مثلِ hookهای SessionStart. فقط hookهای type: "command" و type: "mcp_tool" پشتیبانی میشوند.
InstructionsLoaded
Section titled “InstructionsLoaded”وقتی یک فایلِ CLAUDE.md یا .claude/rules/*.md در کانتکست بارگذاری میشود فعال میشود. این رویداد در آغازِ نشست برای فایلهای eager-loaded و دوباره بعداً وقتی فایلها بهصورتِ lazy بارگذاری میشوند فعال میشود، برای مثال وقتی Claude به زیردایرکتوریای دسترسی پیدا میکند که شاملِ یک CLAUDE.mdِ تودرتو است یا وقتی قواعدِ مشروط با frontmatterِ paths: مطابقت میکنند. hook از مسدودکردن یا کنترلِ تصمیم پشتیبانی نمیکند. برای اهدافِ مشاهدهپذیری بهصورتِ async اجرا میشود.
matcher روی load_reason اجرا میشود. برای مثال، از "matcher": "session_start" استفاده کن تا فقط برای فایلهای بارگذاریشده در آغازِ نشست فعال شود، یا از "matcher": "path_glob_match|nested_traversal" تا فقط برای بارگذاریهای lazy فعال شود.
ورودیِ InstructionsLoaded
Section titled “ورودیِ InstructionsLoaded”علاوه بر فیلدهای ورودیِ مشترک، hookهای InstructionsLoaded این فیلدها را دریافت میکنند:
| فیلد | توضیح |
|---|---|
file_path | مسیرِ مطلق به فایلِ دستورالعملی که بارگذاری شد |
memory_type | دامنهی فایل: "User"، "Project"، "Local"، یا "Managed" |
load_reason | چرا فایل بارگذاری شد: "session_start"، "nested_traversal"، "path_glob_match"، "include"، یا "compact". مقدارِ "compact" وقتی فعال میشود که فایلهای دستورالعمل پس از یک رویدادِ فشردهسازی دوباره بارگذاری میشوند |
globs | الگوهای globِ مسیر از frontmatterِ paths:ِ فایل، در صورتِ وجود. فقط برای بارگذاریهای path_glob_match حاضر است |
trigger_file_path | مسیر به فایلی که دسترسی به آن این بارگذاری را فعال کرد، برای بارگذاریهای lazy |
parent_file_path | مسیر به فایلِ دستورالعملِ والدی که این یکی را include کرد، برای بارگذاریهای include |
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl", "cwd": "/Users/my-project", "hook_event_name": "InstructionsLoaded", "file_path": "/Users/my-project/CLAUDE.md", "memory_type": "Project", "load_reason": "session_start"}کنترلِ تصمیمِ InstructionsLoaded
Section titled “کنترلِ تصمیمِ InstructionsLoaded”hookهای InstructionsLoaded کنترلِ تصمیم ندارند. نمیتوانند بارگذاریِ دستورالعمل را مسدود یا تغییر دهند. از این رویداد برای لاگِ ممیزی، ردیابیِ انطباق یا مشاهدهپذیری استفاده کن.
UserPromptSubmit
Section titled “UserPromptSubmit”وقتی کاربر یک پرامپت ثبت میکند، پیش از آنکه Claude پردازشش کند، اجرا میشود. این به تو اجازه میدهد کانتکستِ اضافی بر اساسِ پرامپت/گفتوگو اضافه کنی، پرامپتها را اعتبارسنجی کنی، یا انواعِ خاصی از پرامپتها را مسدود کنی.
hookهای UserPromptSubmit برای نوعهای command، http و mcp_tool یک timeoutِ پیشفرضِ ۳۰ ثانیه دارند، کوتاهتر از پیشفرضِ ۶۰۰ ثانیهای برای آن نوعها در بیشترِ رویدادهای دیگر. چون این hook پیش از هر پرامپت اجرا میشود و پردازشِ مدل را تا تکمیلش مسدود میکند، یک hookِ گیرکرده نشست را متوقف میکند. اگر hookت به زمانِ بیشتری نیاز دارد، فیلدِ timeout را در ورودیِ hook تنظیم کن.
ورودیِ UserPromptSubmit
Section titled “ورودیِ UserPromptSubmit”علاوه بر فیلدهای ورودیِ مشترک، hookهای UserPromptSubmit فیلدِ prompt را که شاملِ متنِ ثبتشده توسطِ کاربر است دریافت میکنند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "UserPromptSubmit", "prompt": "Write a function to calculate the factorial of a number"}کنترلِ تصمیمِ UserPromptSubmit
Section titled “کنترلِ تصمیمِ UserPromptSubmit”hookهای UserPromptSubmit میتوانند کنترل کنند که یک پرامپتِ کاربر پردازش شود و کانتکست اضافه کنند. همهی فیلدهای خروجیِ JSON در دسترساند.
دو راه برای افزودنِ کانتکست به گفتوگو در کدِ خروجِ 0 وجود دارد:
- stdoutِ متنِ ساده: هر متنِ غیر-JSONی که روی stdout نوشته شود بهعنوانِ کانتکست اضافه میشود
- JSON با
additionalContext: از فرمتِ JSONِ زیر برای کنترلِ بیشتر استفاده کن. فیلدِadditionalContextبهعنوانِ کانتکست اضافه میشود
stdoutِ ساده بهعنوانِ خروجیِ hook در transcript نشان داده میشود. فیلدِ additionalContext با احتیاطِ بیشتری اضافه میشود.
برای مسدودکردنِ یک پرامپت، یک شیء JSON با decision تنظیمشده به "block" برگردان:
| فیلد | توضیح |
|---|---|
decision | "block" از پردازشِ پرامپت جلوگیری میکند و آن را از کانتکست پاک میکند. برای اجازهی ادامهی پرامپت حذفش کن |
reason | وقتی decision برابرِ "block" است به کاربر نشان داده میشود. به کانتکست اضافه نمیشود |
additionalContext | رشتهای که در کنارِ پرامپتِ ثبتشده به کانتکستِ Claude اضافه میشود. افزودنِ کانتکست برای Claude را ببین |
sessionTitle | عنوانِ نشست را تنظیم میکند. برای نامگذاریِ خودکارِ نشستها بر اساسِ محتوای پرامپت استفاده کن |
suppressOriginalPrompt | اگر true باشد وقتی decision برابرِ "block" است، متنِ پرامپتِ اصلی را از پیامِ مسدودسازیِ نشاندادهشده به کاربر حذف میکند |
{ "decision": "block", "reason": "Explanation for decision", "hookSpecificOutput": { "hookEventName": "UserPromptSubmit", "additionalContext": "My additional context here", "sessionTitle": "My session title" }}UserPromptExpansion
Section titled “UserPromptExpansion”وقتی یک slash commandی تایپشده توسطِ کاربر پیش از رسیدن به Claude به یک پرامپت بسط پیدا میکند اجرا میشود. از این برای مسدودکردنِ فراخوانیِ مستقیمِ فرمانهای مشخص، تزریقِ کانتکست برای یک skillِ خاص، یا لاگکردنِ اینکه کاربران چه فرمانهایی را فراخوانی میکنند استفاده کن. برای مثال، یک hookِ مطابق با deploy میتواند /deploy را مسدود کند مگر آنکه یک فایلِ تأیید حاضر باشد، یا یک hookِ مطابق با یک skillِ بازبینی میتواند چکلیستِ بازبینیِ تیم را بهعنوانِ additionalContext اضافه کند.
این رویداد مسیری را پوشش میدهد که PreToolUse پوشش نمیدهد: یک hookِ PreToolUseِ مطابق با ابزارِ Skill فقط وقتی Claude ابزار را فراخوانی میکند فعال میشود، اما تایپکردنِ مستقیمِ /skillname از PreToolUse عبور میکند. UserPromptExpansion روی آن مسیرِ مستقیم فعال میشود.
روی command_name مطابقت میکند. matcher را خالی بگذار تا روی هر slash commandی از نوعِ پرامپت فعال شود.
ورودیِ UserPromptExpansion
Section titled “ورودیِ UserPromptExpansion”علاوه بر فیلدهای ورودیِ مشترک، hookهای UserPromptExpansion فیلدهای expansion_type، command_name، command_args، command_source و رشتهی promptِ اصلی را دریافت میکنند. فیلدِ expansion_type برای فرمانهای skill و سفارشی slash_command است، یا mcp_prompt برای پرامپتهای سرورِ MCP.
{ "session_id": "abc123", "transcript_path": "/Users/.../00893aaf.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "UserPromptExpansion", "expansion_type": "slash_command", "command_name": "example-skill", "command_args": "arg1 arg2", "command_source": "plugin", "prompt": "/example-skill arg1 arg2"}کنترلِ تصمیمِ UserPromptExpansion
Section titled “کنترلِ تصمیمِ UserPromptExpansion”hookهای UserPromptExpansion میتوانند بسط را مسدود کنند یا کانتکست اضافه کنند. همهی فیلدهای خروجیِ JSON در دسترساند.
| فیلد | توضیح |
|---|---|
decision | "block" از بسطِ slash command جلوگیری میکند. برای اجازهی ادامه حذفش کن |
reason | وقتی decision برابرِ "block" است به کاربر نشان داده میشود |
additionalContext | رشتهای که در کنارِ پرامپتِ بسطیافته به کانتکستِ Claude اضافه میشود. افزودنِ کانتکست برای Claude را ببین |
{ "decision": "block", "reason": "This slash command is not available", "hookSpecificOutput": { "hookEventName": "UserPromptExpansion", "additionalContext": "Additional context for this expansion" }}MessageDisplay
Section titled “MessageDisplay”حین جریانیافتنِ یک پیامِ دستیار روی صفحه اجرا میشود. Claude Code پیام را در گامهایی نمایش میدهد: هر بار که دستهای از خطوطِ تازهتکمیلشده آمادهی رندر است، hook یکبار با آن خطوط اجرا میشود و Claude Code متنِ جایگزینِ hook را بهجای آنها رندر میکند. یک پیامِ طولانی چند فراخوانی تولید میکند؛ یک پیامِ کوتاه ممکن است فقط یکی تولید کند.
از MessageDisplay برای اینها استفاده کن:
- حذفِ markdown برای یک نمایشِ کمینه
- تبدیلِ متنی که یک اپلیکیشنِ Agent SDK به کاربرانش نشان میدهد
- ویرایشِ کلیدهای API یا hostnameهای داخلی از پاسخهای Claude
Claude Code هر دسته را تا بازگشتِ hookت نگه میدارد، پس hook را سریع نگه دار. اگر hook شکست بخورد یا timeout شود، Claude Code متنِ اصلی را نمایش میدهد. timeoutِ پیشفرض برای این رویداد ۱۰ ثانیه است؛ اگر hookت به زمانِ بیشتری نیاز دارد، فیلدِ timeout را در ورودیِ hook تنظیم کن.
MessageDisplay فقط-نمایش است: متنِ جایگزین فقط آنچه را روی صفحه رندر میشود تغییر میدهد. transcript و آنچه Claude میبیند متنِ اصلی را نگه میدارند، پس Claude هرگز جایگزین را نمیبیند، و حالتِ verbose اصل را نشان میدهد. hook فقط متنِ پیامِ دستیار را دریافت میکند، پس نتایجِ ابزار و متنی که تو تایپ میکنی بدونِ تغییر رندر میشوند.
MessageDisplay از matcher پشتیبانی نمیکند و برای هر پیامِ دستیاری که متن جریان میدهد فعال میشود؛ پیامهای بدونِ متن، مثلِ پاسخهای صرفاً فراخوانیِ ابزار، آن را فعال نمیکنند.
در اجراهای غیرتعاملی، از جمله کوئریهای Agent SDK و claude -p، MessageDisplay بهجای یکبار در هر دستهی خطوط، یکبار در هر پیامِ دستیار اجرا میشود. این فراخوانیِ واحد پس از تکمیلِ پیام میرسد و متنِ کاملِ پیام را حمل میکند: index برابرِ 0 است، final برابرِ true است، و delta کلِ پیام را نگه میدارد. hookی که متنِ delta را برای هر پیام جمع میکند همان متنِ کلی را در هر دو حالت دریافت میکند.
ورودیِ MessageDisplay
Section titled “ورودیِ MessageDisplay”علاوه بر فیلدهای ورودیِ مشترک، hookهای MessageDisplay شناسههای نوبت و پیام، موقعیتِ این فراخوانی درونِ پیام، و متنِ تازه را در delta دریافت میکنند. مرزهای دسته به این بستگی دارند که متن چطور جریان مییابد، پس از index و final برای ردیابیِ پیشرفت در یک پیام استفاده کن بهجای انتظارِ اینکه خطوط به شیوهی خاصی گروهبندی شوند.
| فیلد | توضیح |
|---|---|
turn_id | UUIDِ نوبتِ جاری |
message_id | UUIDِ پیامِ دستیاری که نمایش داده میشود. در هر دستهی همان پیام پایدار است. این شناسهی APIای msg_… نیست، پس نمیتواند با شناسههای پیامِ transcript همبسته شود |
index | اندیسِ صفرپایهی این دسته درونِ پیام |
final | روی آخرین دستهی پیام true است. هر پیام دقیقاً یک دستهی نهایی دارد |
delta | خطوطِ تازهتکمیلشده از دستهی قبلی، با newlineهای پایانی. همیشه خطوطِ کامل، بهجز دستهی نهایی که ممکن است وسطِ خط تمام شود. در اجراهای تعاملی، deltaی دستهی نهایی وقتی پیام روی یک newline تمام میشود خالی است، پس final را، نه deltaی غیرخالی را، سیگنالِ پایانِ پیام بگیر. در اجراهای Agent SDK و claude -p، فراخوانیِ واحد کلِ پیام را حمل میکند |
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl", "cwd": "/Users/my-project", "hook_event_name": "MessageDisplay", "turn_id": "0c9e6a2f-7d41-4f4e-9a15-3f4f7c2b8d10", "message_id": "5b2a9c8e-1f63-4d8a-b7c4-9e0d2a6f1c3b", "index": 0, "final": false, "delta": "Here is the plan:\n"}خروجیِ MessageDisplay
Section titled “خروجیِ MessageDisplay”علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، hookهای MessageDisplay میتوانند displayContent را برای جایگزینیِ delta روی صفحه برگردانند:
| فیلد | توضیح |
|---|---|
displayContent | متنی که بهجای delta نمایش داده میشود. برای نمایشِ اصل حذفش کن |
hookهای MessageDisplay کنترلِ تصمیم ندارند. نمیتوانند پیام را مسدود کنند یا آنچه را در transcript ذخیره میشود یا به Claude فرستاده میشود تغییر دهند.
این مثال فرمتبندیِ markdown را از پاسخهای Claude برای یک نمایشِ متنِ ساده حذف میکند. اسکریپت هر دسته را از stdin میخواند، نشانگرهای bold و backtickهای کدِ خطی را از delta حذف میکند، و نتیجه را بهعنوانِ displayContent برمیگرداند.
یک hookِ فرمان برای این رویداد در فایلِ تنظیماتت ثبت کن:
{ "hooks": { "MessageDisplay": [ { "hooks": [ { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.sh", "args": [] } ] } ] }}این اسکریپت را در .claude/hooks/plain-display.sh در پروژهات ذخیره کن و با chmod +x اجراشدنیاش کن:
#!/bin/bashjq '{hookSpecificOutput: {hookEventName: "MessageDisplay", displayContent: (.delta | gsub("\\*\\*"; "") | gsub("`"; ""))}}'اسکریپت به jq روی PATHت نیاز دارد.
یک hookِ فرمان ثبت کن که اسکریپت را از طریقِ PowerShell اجرا میکند:
{ "hooks": { "MessageDisplay": [ { "hooks": [ { "type": "command", "command": "powershell.exe", "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.ps1" ] } ] } ] }}پرچمِ -NoProfile از بارگذاریِ پروفایلِ PowerShellت میگذرد تا hook سریع شروع شود، و -ExecutionPolicy Bypass به PowerShell اجازه میدهد فایلِ اسکریپتِ محلی را اجرا کند.
این اسکریپت را در .claude/hooks/plain-display.ps1 در پروژهات ذخیره کن:
$batch = [Console]::In.ReadToEnd() | ConvertFrom-Json$text = $batch.delta -replace '\*\*', '' -replace '`', ''@{ hookSpecificOutput = @{ hookEventName = "MessageDisplay" displayContent = $text }} | ConvertTo-Jsonدستههای بدونِ markdown بدونِ تغییر عبور میکنند. اگر اسکریپت شکست بخورد، برای مثال چون jq نیست، Claude Code متنِ اصلی را نمایش میدهد و شکست را فقط در خروجیِ دیباگ یادداشت میکند، نه در نشست.
PreToolUse
Section titled “PreToolUse”پس از اینکه Claude پارامترهای ابزار را میسازد و پیش از پردازشِ فراخوانیِ ابزار اجرا میشود. روی نامِ ابزار مطابقت میکند: Bash، Edit، Write، Read، Glob، Grep، Agent، WebFetch، WebSearch، AskUserQuestion، ExitPlanMode، و هر نامِ ابزارِ MCP.
از کنترلِ تصمیمِ PreToolUse برای مجاز، رد، ask یا defer کردنِ فراخوانیِ ابزار استفاده کن.
ورودیِ PreToolUse
Section titled “ورودیِ PreToolUse”علاوه بر فیلدهای ورودیِ مشترک، hookهای PreToolUse فیلدهای tool_name، tool_input و tool_use_id را دریافت میکنند. فیلدهای tool_input به ابزار بستگی دارند:
فرمانهای شِل را اجرا میکند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
command | string | "npm test" | فرمانِ شِلی که اجرا میشود |
description | string | "Run test suite" | توضیحِ اختیاریِ کارِ فرمان |
timeout | number | 120000 | timeoutِ اختیاری به میلیثانیه |
run_in_background | boolean | false | اینکه فرمان در پسزمینه اجرا شود یا نه |
یک فایل میسازد یا بازنویسی میکند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
file_path | string | "/path/to/file.txt" | مسیرِ مطلق به فایلی که نوشته میشود |
content | string | "file content" | محتوایی که در فایل نوشته میشود |
یک رشته را در یک فایلِ موجود جایگزین میکند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
file_path | string | "/path/to/file.txt" | مسیرِ مطلق به فایلی که ویرایش میشود |
old_string | string | "original text" | متنی که پیدا و جایگزین میشود |
new_string | string | "replacement text" | متنِ جایگزین |
replace_all | boolean | false | اینکه همهی رخدادها جایگزین شوند یا نه |
محتوای فایل را میخواند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
file_path | string | "/path/to/file.txt" | مسیرِ مطلق به فایلی که خوانده میشود |
offset | number | 10 | شمارهخطِ اختیاری برای شروعِ خواندن |
limit | number | 50 | تعدادِ اختیاریِ خطوط برای خواندن |
فایلهای مطابق با یک الگوی glob را پیدا میکند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
pattern | string | "**/*.ts" | الگوی glob برای مطابقتِ فایلها |
path | string | "/path/to/dir" | دایرکتوریِ اختیاری برای جستوجو. پیشفرض به دایرکتوریِ کاریِ جاری |
محتوای فایل را با عباراتِ منظم جستوجو میکند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
pattern | string | "TODO.*fix" | الگوی عبارتِ منظم برای جستوجو |
path | string | "/path/to/dir" | فایل یا دایرکتوریِ اختیاری برای جستوجو |
glob | string | "*.ts" | الگوی globِ اختیاری برای فیلترکردنِ فایلها |
output_mode | string | "content" | "content"، "files_with_matches"، یا "count". پیشفرض به "files_with_matches" |
-i | boolean | true | جستوجوی بیحساسیت به حروف |
multiline | boolean | false | فعالکردنِ مطابقتِ چندخطی |
WebFetch
Section titled “WebFetch”محتوای وب را واکشی و پردازش میکند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
url | string | "https://example.com/api" | URLی که محتوا از آن واکشی میشود |
prompt | string | "Extract the API endpoints" | پرامپتی که روی محتوای واکشیشده اجرا میشود |
WebSearch
Section titled “WebSearch”وب را جستوجو میکند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
query | string | "react hooks best practices" | عبارتِ جستوجو |
allowed_domains | array | ["docs.example.com"] | اختیاری: فقط نتایجِ این دامنهها را شامل کن |
blocked_domains | array | ["spam.example.com"] | اختیاری: نتایجِ این دامنهها را کنار بگذار |
یک سابایجنت را spawn میکند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
prompt | string | "Find all API endpoints" | وظیفهای که ایجنت انجام میدهد |
description | string | "Find API endpoints" | توضیحِ کوتاهِ وظیفه |
subagent_type | string | "Explore" | نوعِ ایجنتِ تخصصیِ مورداستفاده |
model | string | "sonnet" | ناممستعارِ مدلِ اختیاری برای بازنویسیِ پیشفرض |
در PostToolUse، tool_response برای یک فراخوانیِ تکمیلشدهی Agent متنِ نهاییِ سابایجنت را بههمراهِ تلهمتریِ استفاده حمل میکند. این فیلدها را بخوان تا هزینهی هر-سابایجنت را از یک hook ثبت کنی:
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
status | string | "completed" | "completed" برای فراخوانیهای همگام، "async_launched" برای run_in_background: true |
agentId | string | "a4d2c8f1e0b3a297" | شناسه برای اجرای سابایجنت |
content | array | [{"type": "text", "text": "Found 12 endpoints..."}] | بلاکهای متنِ نهاییِ سابایجنت |
resolvedModel | string | "claude-sonnet-4-5" | مدلی که سابایجنت روی آن اجرا شد، که ممکن است با مدلِ درخواستی فرق کند. {/* min-version: 2.1.174 */}به Claude Code نسخهی v2.1.174 یا بالاتر نیاز دارد |
totalTokens | number | 12450 | کلِ توکنهای صورتحسابشده در نوبتهای سابایجنت |
totalDurationMs | number | 48211 | مدتِ ساعتِ دیواریِ اجرای سابایجنت |
totalToolUseCount | number | 7 | شمارشِ فراخوانیهای ابزاری که سابایجنت انجام داد |
usage | object | {"input_tokens": 8320, ...} | تفکیکِ توکن بهازای نوع: input_tokens، output_tokens، cache_creation_input_tokens، cache_read_input_tokens |
برای فراخوانیهای run_in_background: true، ابزار بلافاصله پس از راهاندازیِ سابایجنت بازمیگردد، پس tool_response هیچ فیلدِ usage حمل نمیکند. status: "async_launched"، agentId، description، prompt، outputFile و resolvedModel دارد.
فیلدِ resolvedModel مدلی را نام میبرد که سابایجنت واقعاً روی آن اجرا میشود، که میتواند با مقدارِ model در tool_input فرق کند. به Claude Code نسخهی v2.1.174 یا بالاتر نیاز دارد.
AskUserQuestion
Section titled “AskUserQuestion”یک تا چهار سؤالِ چندگزینهای از کاربر میپرسد.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
questions | array | [{"question": "Which framework?", "header": "Framework", "options": [{"label": "React"}], "multiSelect": false}] | سؤالهایی که ارائه میشوند، هرکدام با یک رشتهی question، headerِ کوتاه، آرایهی options و پرچمِ اختیاریِ multiSelect |
answers | object | {"Which framework?": "React"} | اختیاری. متنِ سؤال را به برچسبِ گزینهی انتخابشده نگاشت میکند. پاسخهای چندانتخابی برچسبها را با کاما به هم میپیوندند. Claude این فیلد را تنظیم نمیکند؛ آن را از طریقِ updatedInput فراهم کن تا برنامهای پاسخ دهی |
ExitPlanMode
Section titled “ExitPlanMode”یک طرح ارائه میدهد و از کاربر میخواهد پیش از آنکه Claude از حالتِ plan خارج شود آن را تأیید کند. Claude پیش از فراخوانیِ ابزار طرح را در فایلی روی دیسک مینویسد، پس tool_inputِ تحتاللفظی از مدل فقط allowedPrompts را حمل میکند. Claude Code محتوای طرح و مسیرِ فایل را پیش از پاسدادنِ ورودی به hookها تزریق میکند.
| فیلد | نوع | مثال | توضیح |
|---|---|---|---|
plan | string | "## Refactor auth\n1. Extract..." | محتوای طرح در Markdown. از فایلِ طرح روی دیسک تزریق میشود |
planFilePath | string | "/Users/.../plans/refactor-auth.md" | مسیر به فایلِ طرح. تزریقشده |
allowedPrompts | array | [{"tool": "Bash", "prompt": "run tests"}] | اختیاری. مجوزهای مبتنی بر پرامپتی که Claude برای پیادهسازیِ طرح درخواست میکند، هرکدام با یک نامِ tool و یک prompt که دستهی اکشن را توصیف میکند |
در PostToolUse، tool_response شیئی با فیلدهای plan و filePath است که طرحِ تأییدشده را نگه میدارند، بهعلاوهی پرچمهای وضعیتِ داخلی. برای محتوای طرح بهجای خواندنِ دوبارهی فایل از دیسک tool_response.plan را بخوان.
کنترلِ تصمیمِ PreToolUse
Section titled “کنترلِ تصمیمِ PreToolUse”hookهای PreToolUse میتوانند کنترل کنند که یک فراخوانیِ ابزار ادامه یابد. برخلافِ hookهای دیگری که از فیلدِ decisionِ سطحِ بالا استفاده میکنند، PreToolUse تصمیمش را داخلِ یک شیء hookSpecificOutput برمیگرداند. این به آن کنترلِ غنیتری میدهد: چهار نتیجه (allow، deny، ask یا defer) بهعلاوهی توانایی تغییرِ ورودیِ ابزار پیش از اجرا.
| فیلد | توضیح |
|---|---|
permissionDecision | "allow" از پرامپتِ مجوز میگذرد. "deny" از فراخوانیِ ابزار جلوگیری میکند. "ask" کاربر را به تأیید فرامیخواند. "defer" بهنرمی خارج میشود تا ابزار بعداً از سر گرفته شود. قواعدِ deny و ask صرفِنظر از آنچه hook برمیگرداند همچنان ارزیابی میشوند |
permissionDecisionReason | برای "allow" و "ask"، به کاربر نشان داده میشود نه به Claude. برای "deny"، به Claude نشان داده میشود. برای "defer"، نادیده گرفته میشود |
updatedInput | پارامترهای ورودیِ ابزار را پیش از اجرا تغییر میدهد. کلِ شیء ورودی را جایگزین میکند، پس فیلدهای بدونتغییر را در کنارِ فیلدهای تغییریافته بگنجان. با "allow" ترکیب کن تا خودکار تأیید شود، یا با "ask" تا ورودیِ تغییریافته را به کاربر نشان دهی. برای "defer"، نادیده گرفته میشود |
additionalContext | رشتهای که در کنارِ نتیجهی ابزار به کانتکستِ Claude اضافه میشود. وقتی permissionDecision برابرِ "defer" است نادیده گرفته میشود. افزودنِ کانتکست برای Claude را ببین |
وقتی چند hookِ PreToolUse تصمیماتِ متفاوتی برمیگردانند، اولویت deny > defer > ask > allow است.
وقتی یک hook "ask" برمیگرداند، پرامپتِ مجوزِ نشاندادهشده به کاربر شاملِ برچسبی است که مشخص میکند hook از کجا آمده: برای مثال، [User]، [Project]، [Plugin] یا [Local]. این به کاربران کمک میکند بفهمند کدام منبعِ پیکربندی درخواستِ تأیید میکند.
{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "allow", "permissionDecisionReason": "My reason here", "updatedInput": { "field_to_modify": "new value" }, "additionalContext": "Current environment: production. Proceed with caution." }}AskUserQuestion و ExitPlanMode به تعاملِ کاربر نیاز دارند و معمولاً در حالتِ غیرتعاملی با پرچمِ -p مسدود میشوند. برگرداندنِ permissionDecision: "allow" بههمراهِ updatedInput آن نیاز را برآورده میکند: hook ورودیِ ابزار را از stdin میخواند، پاسخ را از طریقِ رابطِ خودت جمع میکند، و آن را در updatedInput برمیگرداند تا ابزار بدونِ پرامپت اجرا شود. برگرداندنِ تنهای "allow" برای این ابزارها کافی نیست. برای AskUserQuestion، آرایهی questionsِ اصلی را بازتاب بده و یک شیء answers اضافه کن که متنِ هر سؤال را به پاسخِ انتخابشده نگاشت میکند.
deferکردنِ یک فراخوانیِ ابزار برای بعد
Section titled “deferکردنِ یک فراخوانیِ ابزار برای بعد”"defer" برای ادغامهایی است که claude -p را بهعنوانِ یک زیرپروسه اجرا میکنند و خروجیِ JSONش را میخوانند، مثلِ یک اپلیکیشنِ Agent SDK یا یک رابطِ سفارشی ساختهشده روی Claude Code. به آن پروسهی فراخواننده اجازه میدهد Claude را در یک فراخوانیِ ابزار متوقف کند، ورودی را از طریقِ رابطِ خودش جمع کند، و از همانجا که رها کرده ادامه دهد. Claude Code این مقدار را فقط در حالتِ غیرتعاملی با پرچمِ -p رعایت میکند. در نشستهای تعاملی یک هشدار لاگ میکند و نتیجهی hook را نادیده میگیرد.
ابزارِ AskUserQuestion موردِ معمول است: Claude میخواهد چیزی از کاربر بپرسد، اما ترمینالی برای پاسخدادن نیست. رفتوبرگشت اینطور کار میکند:
- Claude
AskUserQuestionرا فراخوانی میکند. hookِPreToolUseفعال میشود. - hook
permissionDecision: "defer"برمیگرداند. ابزار اجرا نمیشود. پروسه باstop_reason: "tool_deferred"خارج میشود و فراخوانیِ ابزارِ معلق در transcript حفظ میشود. - پروسهی فراخواننده
deferred_tool_useرا از نتیجهی SDK میخواند، سؤال را در رابطِ خودش آشکار میکند، و منتظرِ یک پاسخ میماند. - پروسهی فراخواننده
claude -p --resume <session-id>را اجرا میکند. همان فراخوانیِ ابزار دوبارهPreToolUseرا فعال میکند. - hook
permissionDecision: "allow"را با پاسخ درupdatedInputبرمیگرداند. ابزار اجرا میشود و Claude ادامه میدهد.
فیلدِ deferred_tool_use id، name و inputِ ابزار را حمل میکند. input پارامترهایی است که Claude برای فراخوانیِ ابزار تولید کرد، که پیش از اجرا ضبط شده:
{ "type": "result", "subtype": "success", "stop_reason": "tool_deferred", "session_id": "abc123", "deferred_tool_use": { "id": "toolu_01abc", "name": "AskUserQuestion", "input": { "questions": [{ "question": "Which framework?", "header": "Framework", "options": [{"label": "React"}, {"label": "Vue"}], "multiSelect": false }] } }}هیچ timeout یا محدودیتِ retryای نیست. نشست تا وقتی از سرش بگیری روی دیسک میماند، تابعِ پاکسازیِ نگهداریِ cleanupPeriodDays که بهصورتِ پیشفرض فایلهای نشست را پس از ۳۰ روز حذف میکند. اگر هنگامِ از سرگیری پاسخ آماده نباشد، hook میتواند دوباره "defer" برگرداند و پروسه به همان شیوه خارج میشود. پروسهی فراخواننده کنترل میکند که چه زمانی حلقه را با برگرداندنِ نهاییِ "allow" یا "deny" از hook بشکند.
"defer" فقط وقتی کار میکند که Claude یک فراخوانیِ ابزارِ واحد در نوبت انجام دهد. اگر Claude چند فراخوانیِ ابزار را یکجا انجام دهد، "defer" با یک هشدار نادیده گرفته میشود و ابزار از مسیرِ عادیِ جریانِ مجوز پیش میرود. این محدودیت وجود دارد چون از سرگیری فقط میتواند یک ابزار را دوباره اجرا کند: راهی برای deferکردنِ یک فراخوانی از یک دسته بدونِ رهاکردنِ بقیه بهصورتِ حلنشده نیست.
اگر ابزارِ deferشده هنگامِ از سرگیری دیگر در دسترس نباشد، پروسه پیش از فعالشدنِ hook با stop_reason: "tool_deferred_unavailable" و is_error: true خارج میشود. این وقتی اتفاق میافتد که یک سرورِ MCP که آن ابزار را فراهم میکرد برای نشستِ از سر گرفتهشده متصل نباشد. بارِ deferred_tool_use همچنان گنجانده میشود تا بتوانی شناسایی کنی کدام ابزار گم شده.
PermissionRequest
Section titled “PermissionRequest”وقتی یک دیالوگِ مجوز به کاربر نشان داده میشود اجرا میشود. از کنترلِ تصمیمِ PermissionRequest برای مجاز یا ردکردن از طرفِ کاربر استفاده کن.
روی نامِ ابزار مطابقت میکند، همان مقادیرِ PreToolUse.
ورودیِ PermissionRequest
Section titled “ورودیِ PermissionRequest”hookهای PermissionRequest فیلدهای tool_name و tool_input را مثلِ hookهای PreToolUse دریافت میکنند، اما بدونِ tool_use_id. یک آرایهی اختیاریِ permission_suggestions شاملِ گزینههای «همیشه مجاز»ی است که کاربر معمولاً در دیالوگِ مجوز میبیند. تفاوت در زمانِ فعالشدنِ hook است: hookهای PermissionRequest وقتی اجرا میشوند که یک دیالوگِ مجوز قرار است به کاربر نشان داده شود، در حالی که hookهای PreToolUse پیش از اجرای ابزار صرفِنظر از وضعیتِ مجوز اجرا میشوند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "PermissionRequest", "tool_name": "Bash", "tool_input": { "command": "rm -rf node_modules", "description": "Remove node_modules directory" }, "permission_suggestions": [ { "type": "addRules", "rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }], "behavior": "allow", "destination": "localSettings" } ]}کنترلِ تصمیمِ PermissionRequest
Section titled “کنترلِ تصمیمِ PermissionRequest”hookهای PermissionRequest میتوانند درخواستهای مجوز را مجاز یا رد کنند. علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، اسکریپتِ hookت میتواند یک شیء decision با این فیلدهای خاصِ رویداد برگرداند:
| فیلد | توضیح |
|---|---|
behavior | "allow" مجوز را اعطا میکند، "deny" ردش میکند. قواعدِ deny و ask همچنان ارزیابی میشوند، پس hookی که "allow" برمیگرداند یک قاعدهی denyِ مطابق را بازنویسی نمیکند |
updatedInput | فقط برای "allow": پارامترهای ورودیِ ابزار را پیش از اجرا تغییر میدهد. کلِ شیء ورودی را جایگزین میکند، پس فیلدهای بدونتغییر را در کنارِ فیلدهای تغییریافته بگنجان. ورودیِ تغییریافته دوباره در برابرِ قواعدِ deny و ask ارزیابی میشود |
updatedPermissions | فقط برای "allow": آرایهای از ورودیهای بهروزرسانیِ مجوز برای اعمال، مثلِ افزودنِ یک قاعدهی allow یا تغییرِ حالتِ مجوزِ نشست |
message | فقط برای "deny": به Claude میگوید چرا مجوز رد شد |
interrupt | فقط برای "deny": اگر true باشد، Claude را متوقف میکند |
{ "hookSpecificOutput": { "hookEventName": "PermissionRequest", "decision": { "behavior": "allow", "updatedInput": { "command": "npm run lint" } } }}ورودیهای بهروزرسانیِ مجوز
Section titled “ورودیهای بهروزرسانیِ مجوز”فیلدِ خروجیِ updatedPermissions و فیلدِ ورودیِ permission_suggestions هر دو از همان آرایهی شیءهای ورودی استفاده میکنند. هر ورودی یک type دارد که فیلدهای دیگرش را تعیین میکند، و یک destination که کنترل میکند تغییر کجا نوشته شود.
type | فیلدها | اثر |
|---|---|---|
addRules | rules، behavior، destination | قواعدِ مجوز اضافه میکند. rules آرایهای از شیءهای {toolName, ruleContent?} است. برای مطابقت با کلِ ابزار، ruleContent را حذف کن. behavior یا "allow"، "deny" یا "ask" است |
replaceRules | rules، behavior، destination | همهی قواعدِ behaviorِ دادهشده را در destination با rulesِ ارائهشده جایگزین میکند |
removeRules | rules، behavior، destination | قواعدِ مطابقِ behaviorِ دادهشده را حذف میکند |
setMode | mode، destination | حالتِ مجوز را تغییر میدهد. حالتهای معتبر default، auto، acceptEdits، dontAsk، bypassPermissions و plan هستند |
addDirectories | directories، destination | دایرکتوریهای کاری اضافه میکند. directories آرایهای از رشتههای مسیر است |
removeDirectories | directories، destination | دایرکتوریهای کاری را حذف میکند |
فیلدِ destination روی هر ورودی تعیین میکند که تغییر در حافظه بماند یا در یک فایلِ تنظیمات پایدار شود.
destination | مینویسد در |
|---|---|
session | فقط در حافظه، وقتی نشست تمام میشود دور ریخته میشود |
localSettings | .claude/settings.local.json |
projectSettings | .claude/settings.json |
userSettings | ~/.claude/settings.json |
یک hook میتواند یکی از permission_suggestionsهایی را که دریافت کرده بهعنوانِ خروجیِ updatedPermissionsِ خودش بازتاب دهد، که معادلِ انتخابِ آن گزینهی «همیشه مجاز» توسطِ کاربر در دیالوگ است.
PostToolUse
Section titled “PostToolUse”بلافاصله پس از موفقیتِ یک ابزار اجرا میشود.
روی نامِ ابزار مطابقت میکند، همان مقادیرِ PreToolUse.
ورودیِ PostToolUse
Section titled “ورودیِ PostToolUse”hookهای PostToolUse پس از اینکه یک ابزار از پیش با موفقیت اجرا شده فعال میشوند. ورودی هم tool_input، آرگومانهای فرستادهشده به ابزار، و هم tool_response، نتیجهای که برگرداند، را شامل میشود. اسکیمای دقیقِ هر دو به ابزار بستگی دارد.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "PostToolUse", "tool_name": "Write", "tool_input": { "file_path": "/path/to/file.txt", "content": "file content" }, "tool_response": { "filePath": "/path/to/file.txt", "success": true }, "tool_use_id": "toolu_01ABC123...", "duration_ms": 12}| فیلد | توضیح |
|---|---|
duration_ms | اختیاری. زمانِ اجرای ابزار به میلیثانیه. زمانِ صرفشده در پرامپتهای مجوز و hookهای PreToolUse را شامل نمیشود |
کنترلِ تصمیمِ PostToolUse
Section titled “کنترلِ تصمیمِ PostToolUse”hookهای PostToolUse میتوانند پس از اجرای ابزار به Claude بازخورد بدهند. علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، اسکریپتِ hookت میتواند این فیلدهای خاصِ رویداد را برگرداند:
| فیلد | توضیح |
|---|---|
decision | "block" reason را کنارِ نتیجهی ابزار اضافه میکند. Claude همچنان خروجیِ اصلی را میبیند؛ برای جایگزینیاش از updatedToolOutput استفاده کن |
reason | توضیحی که وقتی decision برابرِ "block" است به Claude نشان داده میشود |
additionalContext | رشتهای که در کنارِ نتیجهی ابزار به کانتکستِ Claude اضافه میشود. افزودنِ کانتکست برای Claude را ببین |
updatedToolOutput | خروجیِ ابزار را پیش از فرستادن به Claude با مقدارِ ارائهشده جایگزین میکند. مقدار باید با شکلِ خروجیِ ابزار مطابقت کند |
updatedMCPToolOutput | خروجی را فقط برای ابزارهای MCP جایگزین میکند. updatedToolOutput را که برای همهی ابزارها کار میکند ترجیح بده |
مثالِ زیر خروجیِ یک فراخوانیِ Bash را جایگزین میکند. مقدارِ جایگزین با شکلِ خروجیِ ابزارِ Bash مطابقت دارد:
{ "hookSpecificOutput": { "hookEventName": "PostToolUse", "additionalContext": "Additional information for Claude", "updatedToolOutput": { "stdout": "[redacted]", "stderr": "", "interrupted": false, "isImage": false } }}PostToolUseFailure
Section titled “PostToolUseFailure”وقتی اجرای یک ابزار شکست میخورد اجرا میشود. این رویداد برای فراخوانیهای ابزاری که خطا پرتاب میکنند یا نتایجِ شکست برمیگردانند فعال میشود. از این برای لاگکردنِ شکستها، فرستادنِ هشدار، یا ارائهی بازخوردِ اصلاحی به Claude استفاده کن.
روی نامِ ابزار مطابقت میکند، همان مقادیرِ PreToolUse.
ورودیِ PostToolUseFailure
Section titled “ورودیِ PostToolUseFailure”hookهای PostToolUseFailure همان فیلدهای tool_name و tool_inputِ PostToolUse را، بههمراهِ اطلاعاتِ خطا بهعنوانِ فیلدهای سطحِ بالا، دریافت میکنند:
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "PostToolUseFailure", "tool_name": "Bash", "tool_input": { "command": "npm test", "description": "Run test suite" }, "tool_use_id": "toolu_01ABC123...", "error": "Command exited with non-zero status code 1", "is_interrupt": false, "duration_ms": 4187}| فیلد | توضیح |
|---|---|
error | رشتهای که توصیف میکند چه چیزی اشتباه شد |
is_interrupt | بولینِ اختیاری که نشان میدهد آیا شکست بهخاطرِ وقفهی کاربر بود |
duration_ms | اختیاری. زمانِ اجرای ابزار به میلیثانیه. زمانِ صرفشده در پرامپتهای مجوز و hookهای PreToolUse را شامل نمیشود |
کنترلِ تصمیمِ PostToolUseFailure
Section titled “کنترلِ تصمیمِ PostToolUseFailure”hookهای PostToolUseFailure میتوانند پس از شکستِ یک ابزار به Claude کانتکست بدهند. علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، اسکریپتِ hookت میتواند این فیلدهای خاصِ رویداد را برگرداند:
| فیلد | توضیح |
|---|---|
additionalContext | رشتهای که در کنارِ خطا به کانتکستِ Claude اضافه میشود. افزودنِ کانتکست برای Claude را ببین |
{ "hookSpecificOutput": { "hookEventName": "PostToolUseFailure", "additionalContext": "Additional information about the failure for Claude" }}PostToolBatch
Section titled “PostToolBatch”یکبار پس از resolveشدنِ هر فراخوانیِ ابزار در یک دسته، پیش از آنکه Claude Code درخواستِ بعدی را به مدل بفرستد، اجرا میشود. PostToolUse یکبار در هر ابزار فعال میشود، یعنی وقتی Claude فراخوانیهای موازیِ ابزار انجام میدهد همزمان فعال میشود. PostToolBatch دقیقاً یکبار با کلِ دسته فعال میشود، پس جای درستی برای تزریقِ کانتکستی است که به مجموعهی ابزارهای اجراشده بستگی دارد نه به هیچ ابزارِ منفردی. برای این رویداد matcheری نیست.
ورودیِ PostToolBatch
Section titled “ورودیِ PostToolBatch”علاوه بر فیلدهای ورودیِ مشترک، hookهای PostToolBatch tool_calls را دریافت میکنند، آرایهای که هر فراخوانیِ ابزار در دسته را توصیف میکند:
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "PostToolBatch", "tool_calls": [ { "tool_name": "Read", "tool_input": {"file_path": "/.../ledger/accounts.py"}, "tool_use_id": "toolu_01...", "tool_response": " 1\tfrom __future__ import annotations\n 2\t..." }, { "tool_name": "Read", "tool_input": {"file_path": "/.../ledger/transactions.py"}, "tool_use_id": "toolu_02...", "tool_response": " 1\tfrom __future__ import annotations\n 2\t..." } ]}tool_response همان محتوایی را شامل میشود که مدل در بلاکِ tool_resultِ متناظر دریافت میکند. مقدار یک رشتهی سریالایزشده یا آرایهی بلاکِ محتوا است، دقیقاً همانطور که ابزار منتشرش کرد. برای Read، یعنی متنِ پیشوندشده با شمارهخط نه محتوای خامِ فایل. پاسخها میتوانند بزرگ باشند، پس فقط فیلدهایی را که نیاز داری پارس کن.
کنترلِ تصمیمِ PostToolBatch
Section titled “کنترلِ تصمیمِ PostToolBatch”hookهای PostToolBatch میتوانند برای Claude کانتکست تزریق کنند. علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، اسکریپتِ hookت میتواند این فیلدهای خاصِ رویداد را برگرداند:
| فیلد | توضیح |
|---|---|
additionalContext | رشتهی کانتکستی که یکبار پیش از فراخوانیِ بعدیِ مدل تزریق میشود. برای جزئیاتِ تحویل، آنچه در آن بگذاری، و اینکه نشستهای از سر گرفتهشده چطور مقادیرِ گذشته را مدیریت میکنند افزودنِ کانتکست برای Claude را ببین |
{ "hookSpecificOutput": { "hookEventName": "PostToolBatch", "additionalContext": "These files are part of the ledger module. Run pytest before marking the task complete." }}برگرداندنِ decision: "block" یا continue: false حلقهی ایجنتیک را پیش از فراخوانیِ بعدیِ مدل متوقف میکند.
PermissionDenied
Section titled “PermissionDenied”وقتی طبقهبندِ حالتِ auto یک فراخوانیِ ابزار را رد میکند اجرا میشود. این hook فقط در حالتِ auto فعال میشود: وقتی یک دیالوگِ مجوز را دستی رد میکنی، وقتی یک hookِ PreToolUse یک فراخوانی را مسدود میکند، یا وقتی یک قاعدهی deny مطابقت میکند اجرا نمیشود. از آن برای لاگکردنِ ردهای طبقهبند، تنظیمِ پیکربندی، یا گفتن به مدل که میتواند فراخوانیِ ابزار را دوباره امتحان کند استفاده کن.
روی نامِ ابزار مطابقت میکند، همان مقادیرِ PreToolUse.
ورودیِ PermissionDenied
Section titled “ورودیِ PermissionDenied”علاوه بر فیلدهای ورودیِ مشترک، hookهای PermissionDenied فیلدهای tool_name، tool_input، tool_use_id و reason را دریافت میکنند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "auto", "hook_event_name": "PermissionDenied", "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build", "description": "Clean build directory" }, "tool_use_id": "toolu_01ABC123...", "reason": "Auto mode denied: command targets a path outside the project"}| فیلد | توضیح |
|---|---|
reason | توضیحِ طبقهبند برای اینکه چرا فراخوانیِ ابزار رد شد |
کنترلِ تصمیمِ PermissionDenied
Section titled “کنترلِ تصمیمِ PermissionDenied”hookهای PermissionDenied میتوانند به مدل بگویند که میتواند فراخوانیِ ردشدهی ابزار را دوباره امتحان کند. یک شیء JSON با hookSpecificOutput.retry تنظیمشده به true برگردان:
{ "hookSpecificOutput": { "hookEventName": "PermissionDenied", "retry": true }}وقتی retry برابرِ true است، Claude Code یک پیام به گفتوگو اضافه میکند که به مدل میگوید میتواند فراخوانیِ ابزار را دوباره امتحان کند. خودِ رد برگردانده نمیشود. اگر hookت JSON برنگرداند، یا retry: false برگرداند، رد پابرجا میماند و مدل پیامِ ردِ اصلی را دریافت میکند.
Notification
Section titled “Notification”وقتی Claude Code اعلان میفرستد اجرا میشود. روی نوعِ اعلان مطابقت میکند: permission_prompt، idle_prompt، auth_success، elicitation_dialog، elicitation_complete، elicitation_response. matcher را حذف کن تا hookها برای همهی انواعِ اعلان اجرا شوند.
از matcherهای جداگانه استفاده کن تا بسته به نوعِ اعلان handlerهای متفاوتی اجرا شوند. این پیکربندی وقتی Claude به تأییدِ مجوز نیاز دارد یک اسکریپتِ هشدارِ خاصِ مجوز و وقتی Claude idle بوده یک اعلانِ متفاوت فعال میکند:
{ "hooks": { "Notification": [ { "matcher": "permission_prompt", "hooks": [ { "type": "command", "command": "/path/to/permission-alert.sh" } ] }, { "matcher": "idle_prompt", "hooks": [ { "type": "command", "command": "/path/to/idle-notification.sh" } ] } ] }}ورودیِ Notification
Section titled “ورودیِ Notification”علاوه بر فیلدهای ورودیِ مشترک، hookهای Notification message را با متنِ اعلان، یک titleِ اختیاری، و notification_type را که نشان میدهد کدام نوع فعال شده دریافت میکنند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "Notification", "message": "Claude needs your permission", "title": "Permission needed", "notification_type": "permission_prompt"}hookهای Notification نمیتوانند اعلانها را مسدود یا تغییر دهند. برای اثرهای جانبی مثلِ ارجاعِ اعلان به یک سرویسِ بیرونی در نظر گرفته شدهاند. فیلدهای خروجیِ JSONِ مشترک مثلِ systemMessage اعمال میشوند.
SubagentStart
Section titled “SubagentStart”وقتی یک سابایجنتِ Claude Code از طریقِ ابزارِ Agent spawn میشود اجرا میشود. از matcherها برای فیلترکردن بر اساسِ نامِ نوعِ ایجنت پشتیبانی میکند. برای ایجنتهای داخلی، این نامِ ایجنت است مثلِ general-purpose، Explore یا Plan. برای سابایجنتهای سفارشی، این فیلدِ name از frontmatterِ ایجنت است، نه نامفایل.
ورودیِ SubagentStart
Section titled “ورودیِ SubagentStart”علاوه بر فیلدهای ورودیِ مشترک، hookهای SubagentStart agent_id را با شناسهی یکتای سابایجنت و agent_type را با نامِ ایجنت (ایجنتهای داخلی مثلِ "general-purpose"، "Explore"، "Plan"، یا نامهای ایجنتِ سفارشی) دریافت میکنند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "SubagentStart", "agent_id": "agent-abc123", "agent_type": "Explore"}hookهای SubagentStart نمیتوانند ساختِ سابایجنت را مسدود کنند، اما میتوانند به سابایجنت کانتکست تزریق کنند. علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، میتوانی این را برگردانی:
| فیلد | توضیح |
|---|---|
additionalContext | رشتهای که در آغازِ گفتوگوی سابایجنت، پیش از اولین پرامپتش، به کانتکستِ آن اضافه میشود. افزودنِ کانتکست برای Claude را ببین |
{ "hookSpecificOutput": { "hookEventName": "SubagentStart", "additionalContext": "Follow security guidelines for this task" }}SubagentStop
Section titled “SubagentStop”وقتی یک سابایجنتِ Claude Code پاسخدادن را تمام کرده اجرا میشود. روی نوعِ ایجنت مطابقت میکند، همان مقادیرِ SubagentStart.
ورودیِ SubagentStop
Section titled “ورودیِ SubagentStop”علاوه بر فیلدهای ورودیِ مشترک، hookهای SubagentStop stop_hook_active، agent_id، agent_type، agent_transcript_path و last_assistant_message را دریافت میکنند. فیلدِ agent_type مقداری است که برای فیلترِ matcher استفاده میشود. transcript_path transcriptِ نشستِ اصلی است، در حالی که agent_transcript_path transcriptِ خودِ سابایجنت است که در یک پوشهی تودرتوی subagents/ ذخیره شده. فیلدِ last_assistant_message محتوای متنیِ پاسخِ نهاییِ سابایجنت را شامل میشود، پس hookها میتوانند بدونِ پارسِ فایلِ transcript به آن دسترسی پیدا کنند.
hookهای SubagentStop آرایههای background_tasks و session_cronsِ توصیفشده زیرِ ورودیِ Stop را نیز دریافت میکنند، که در Claude Code نسخهی v2.1.145 یا بالاتر در دسترساند. هر دو آرایه به نشستِ والد محدودند، نه سابایجنت.
{ "session_id": "abc123", "transcript_path": "~/.claude/projects/.../abc123.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "SubagentStop", "stop_hook_active": false, "agent_id": "def456", "agent_type": "Explore", "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl", "last_assistant_message": "Analysis complete. Found 3 potential issues...", "background_tasks": [], "session_crons": []}hookهای SubagentStop از همان فرمتِ کنترلِ تصمیمِ hookهای Stop استفاده میکنند، از جمله hookSpecificOutput.additionalContext با hookEventName تنظیمشده به "SubagentStop"، برای بازخوردِ غیرخطایی که سابایجنت را در حالِ اجرا نگه میدارد. برگرداندنِ decision: "block" با یک reason سابایجنت را در حالِ اجرا نگه میدارد و reason را بهعنوانِ دستورالعملِ بعدیاش به سابایجنت تحویل میدهد. برای تزریقِ کانتکست به نشستِ والد پس از بازگشتِ یک سابایجنت، بهجایش از یک hookِ PostToolUse روی ابزارِ Agent استفاده کن.
TaskCreated
Section titled “TaskCreated”وقتی یک task از طریقِ ابزارِ TaskCreate در حالِ ساختهشدن است اجرا میشود. از این برای اعمالِ قراردادهای نامگذاری، الزامِ توضیحاتِ task، یا جلوگیری از ساختهشدنِ taskهای خاص استفاده کن.
وقتی یک hookِ TaskCreated با کدِ 2 خارج میشود، task ساخته نمیشود و پیامِ stderr بهعنوانِ بازخورد به مدل بازخورانده میشود. برای متوقفکردنِ کاملِ همتیمی بهجای اجرای دوبارهاش، JSON با {"continue": false, "stopReason": "..."} برگردان. hookهای TaskCreated از matcher پشتیبانی نمیکنند و در هر رخداد فعال میشوند.
ورودیِ TaskCreated
Section titled “ورودیِ TaskCreated”علاوه بر فیلدهای ورودیِ مشترک، hookهای TaskCreated task_id، task_subject و اختیاراً task_description، teammate_name و team_name را دریافت میکنند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "TaskCreated", "task_id": "task-001", "task_subject": "Implement user authentication", "task_description": "Add login and signup endpoints", "teammate_name": "implementer", "team_name": "my-project"}| فیلد | توضیح |
|---|---|
task_id | شناسهی taskی که ساخته میشود |
task_subject | عنوانِ task |
task_description | توضیحِ مفصلِ task. ممکن است غایب باشد |
teammate_name | نامِ همتیمیای که task را میسازد. ممکن است غایب باشد |
team_name | نامِ تیم. ممکن است غایب باشد |
کنترلِ تصمیمِ TaskCreated
Section titled “کنترلِ تصمیمِ TaskCreated”hookهای TaskCreated دو راه برای کنترلِ ساختِ task پشتیبانی میکنند:
- کدِ خروجِ 2: task ساخته نمیشود و پیامِ stderr بهعنوانِ بازخورد به مدل بازخورانده میشود.
- JSONِ
{"continue": false, "stopReason": "..."}: همتیمی را کاملاً متوقف میکند، مطابق با رفتارِ hookِStop.stopReasonبه کاربر نشان داده میشود.
این مثال taskهایی را که عنوانشان از فرمتِ موردنیاز پیروی نمیکند مسدود میکند:
#!/bin/bashINPUT=$(cat)TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&2 exit 2fi
exit 0TaskCompleted
Section titled “TaskCompleted”وقتی یک task در حالِ علامتخوردن بهعنوانِ تکمیلشده است اجرا میشود. این در دو موقعیت فعال میشود: وقتی هر ایجنتی صریحاً یک task را از طریقِ ابزارِ TaskUpdate بهعنوانِ تکمیلشده علامت میزند، یا وقتی یک همتیمیِ تیمِ ایجنت نوبتش را با taskهای در حالِ انجام تمام میکند. از این برای اعمالِ معیارهای تکمیل مثلِ گذرکردنِ تستها یا بررسیهای lint پیش از بستهشدنِ یک task استفاده کن.
وقتی یک hookِ TaskCompleted با کدِ 2 خارج میشود، task بهعنوانِ تکمیلشده علامت نمیخورد و پیامِ stderr بهعنوانِ بازخورد به مدل بازخورانده میشود. برای متوقفکردنِ کاملِ همتیمی بهجای اجرای دوبارهاش، JSON با {"continue": false, "stopReason": "..."} برگردان. hookهای TaskCompleted از matcher پشتیبانی نمیکنند و در هر رخداد فعال میشوند.
ورودیِ TaskCompleted
Section titled “ورودیِ TaskCompleted”علاوه بر فیلدهای ورودیِ مشترک، hookهای TaskCompleted task_id، task_subject و اختیاراً task_description، teammate_name و team_name را دریافت میکنند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "TaskCompleted", "task_id": "task-001", "task_subject": "Implement user authentication", "task_description": "Add login and signup endpoints", "teammate_name": "implementer", "team_name": "my-project"}| فیلد | توضیح |
|---|---|
task_id | شناسهی taskی که تکمیل میشود |
task_subject | عنوانِ task |
task_description | توضیحِ مفصلِ task. ممکن است غایب باشد |
teammate_name | نامِ همتیمیای که task را تکمیل میکند. ممکن است غایب باشد |
team_name | نامِ تیم. ممکن است غایب باشد |
کنترلِ تصمیمِ TaskCompleted
Section titled “کنترلِ تصمیمِ TaskCompleted”hookهای TaskCompleted دو راه برای کنترلِ تکمیلِ task پشتیبانی میکنند:
- کدِ خروجِ 2: task بهعنوانِ تکمیلشده علامت نمیخورد و پیامِ stderr بهعنوانِ بازخورد به مدل بازخورانده میشود.
- JSONِ
{"continue": false, "stopReason": "..."}: همتیمی را کاملاً متوقف میکند، مطابق با رفتارِ hookِStop.stopReasonبه کاربر نشان داده میشود.
این مثال تستها را اجرا میکند و اگر شکست بخورند تکمیلِ task را مسدود میکند:
#!/bin/bashINPUT=$(cat)TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
# Run the test suiteif ! npm test 2>&1; then echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2 exit 2fi
exit 0وقتی ایجنتِ اصلیِ Claude Code پاسخدادن را تمام کرده اجرا میشود. اگر توقف بهخاطرِ یک وقفهی کاربر رخ داده باشد اجرا نمیشود. خطاهای API بهجایش StopFailure را فعال میکنند.
ورودیِ Stop
Section titled “ورودیِ Stop”علاوه بر فیلدهای ورودیِ مشترک، hookهای Stop stop_hook_active، last_assistant_message، background_tasks و session_crons را دریافت میکنند. فیلدِ stop_hook_active وقتی true است که Claude Code از پیش در نتیجهی یک hookِ stop در حالِ ادامه است. این مقدار را بررسی کن یا transcript را پردازش کن تا از مسدودشدن روی شرطی که هرگز برقرار نمیشود اجتناب کنی. Claude Code پس از ۸ مسدودسازیِ متوالی hook را بازنویسی میکند و نوبت را پایان میدهد.
فیلدِ last_assistant_message محتوای متنیِ پاسخِ نهاییِ Claude را شامل میشود، پس hookها میتوانند بدونِ پارسِ فایلِ transcript به آن دسترسی پیدا کنند.
آرایههای background_tasks و session_crons، که در Claude Code نسخهی v2.1.145 یا بالاتر در دسترساند، به hookها اجازه میدهند «نشست تمام شده» را از «نشست متوقف شده و منتظرِ کارِ پسزمینه برای بیدارکردنِ دوبارهاش» تشخیص دهند. هر دو آرایه وقتی رجیستریِ task قابلِدسترس باشد حاضرند و وقتی چیزی در جریان یا زمانبندیشده نباشد خالیاند.
هر ورودی در background_tasks یک taskِ در جریان را توصیف میکند و از این فیلدها استفاده میکند:
| فیلد | توضیح |
|---|---|
id | شناسهی task |
type | برچسبِ صمیمیِ نوعِ task مثلِ shell، subagent، monitor، workflow، teammate، cloud session یا MCP task. هر برچسب مشخص میکند کدام قابلیتِ Claude Code آن task را ساخته. برای نوعهای ناشناخته به discriminantِ خام برمیگردد |
status | وضعیتِ جاریِ task |
description | توضیحِ متنِ آزاد، با سقفِ ۱۰۰۰ کاراکتر همراه با نشانگرِ درونرشتهایِ … [+N chars] هنگامِ بریدهشدن |
command | خطِ فرمانِ شِل، با سقفِ ۱۰۰۰ کاراکتر. فقط برای taskهای shell حاضر است |
agent_type | نامِ نوعِ سابایجنت. فقط برای taskهای subagent حاضر است |
server | نامِ سرورِ MCP. فقط برای taskهای monitor و MCP task حاضر است |
tool | نامِ ابزارِ MCP. فقط برای taskهای monitor و MCP task حاضر است |
name | نامِ workflow. فقط برای taskهای workflow حاضر است |
هر ورودی در session_crons یک بیدارباشِ زمانبندیشدهی محدود به نشست را توصیف میکند، که منبعش CronCreate، ScheduleWakeup و /loop است:
| فیلد | توضیح |
|---|---|
id | شناسهی taskِ cron |
schedule | عبارتِ cron، برای مثال 0 9 * * 1-5 |
recurring | برای بیدارباشهای تکشلیکی که scheduleشان یک زمانِ شلیکِ واحد را کد میکند false است، برای taskهایی که در هر مطابقت دوباره شلیک میکنند true |
prompt | پرامپتی که هنگامِ شلیکِ cron ثبت میشود، با سقفِ ۱۰۰۰ کاراکتر و همان نشانگرِ … [+N chars] |
این مثال یک ورودیِ Stop را با یک taskِ shellِ در جریان و یک cronِ recurring نشان میدهد:
{ "session_id": "abc123", "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "Stop", "stop_hook_active": true, "last_assistant_message": "I've completed the refactoring. Here's a summary...", "background_tasks": [ { "id": "task-001", "type": "shell", "status": "running", "description": "tail logs", "command": "tail -f /var/log/syslog" } ], "session_crons": [ { "id": "cron-001", "schedule": "0 9 * * 1-5", "recurring": true, "prompt": "check the build" } ]}کنترلِ تصمیمِ Stop
Section titled “کنترلِ تصمیمِ Stop”hookهای Stop و SubagentStop میتوانند کنترل کنند که Claude ادامه دهد. علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، اسکریپتِ hookت میتواند این فیلدهای خاصِ رویداد را برگرداند:
| فیلد | توضیح |
|---|---|
decision | "block" از توقفِ Claude جلوگیری میکند. برای اجازهی توقفِ Claude حذفش کن |
reason | وقتی decision برابرِ "block" است الزامی. به Claude میگوید چرا باید ادامه دهد |
hookSpecificOutput.additionalContext | بازخوردِ غیرخطا برای Claude. گفتوگو ادامه مییابد تا Claude بتواند بر اساسش عمل کند، اما برخلافِ decision: "block" در transcript بهعنوانِ بازخوردِ hook نشان داده میشود نه یک خطای hook |
{ "decision": "block", "reason": "Must be provided when Claude is blocked from stopping"}از additionalContext وقتی استفاده کن که hook طبقِ طراحی کار میکند و به Claude راهنمایی میدهد، مثلِ «پیش از پایان، مجموعهی تست را اجرا کن». گفتوگو را از طریقِ همان محافظتهای حلقهی decision: "block"، یعنی ورودیِ stop_hook_active و سقفِ ۸-ادامهی-متوالی، ادامه میدهد، اما transcript آن را Stop hook feedback برچسب میزند و هیچ اعلانِ خطای hookی نشان داده نمیشود:
{ "hookSpecificOutput": { "hookEventName": "Stop", "additionalContext": "Please run the test suite before finishing" }}StopFailure
Section titled “StopFailure”بهجای Stop وقتی نوبت بهخاطرِ یک خطای API پایان مییابد اجرا میشود. خروجی و کدِ خروج نادیده گرفته میشوند. از این برای لاگکردنِ شکستها، فرستادنِ هشدار، یا انجامِ اکشنهای بازیابی وقتی Claude بهخاطرِ محدودیتهای نرخ، مشکلاتِ احراز هویت یا دیگر خطاهای API نمیتواند یک پاسخ را تکمیل کند استفاده کن.
ورودیِ StopFailure
Section titled “ورودیِ StopFailure”علاوه بر فیلدهای ورودیِ مشترک، hookهای StopFailure error، error_detailsِ اختیاری و last_assistant_messageِ اختیاری را دریافت میکنند. فیلدِ error نوعِ خطا را مشخص میکند و برای فیلترِ matcher استفاده میشود.
| فیلد | توضیح |
|---|---|
error | نوعِ خطا: rate_limit، overloaded، authentication_failed، oauth_org_not_allowed، billing_error، invalid_request، model_not_found، server_error، max_output_tokens، یا unknown |
error_details | جزئیاتِ اضافی دربارهی خطا، در صورتِ موجودبودن |
last_assistant_message | متنِ خطای رندرشدهی نشاندادهشده در گفتوگو. برخلافِ Stop و SubagentStop که این فیلد خروجیِ گفتوگوییِ Claude را نگه میدارد، برای StopFailure خودِ رشتهی خطای API را شامل میشود، مثلِ "API Error: Rate limit reached" |
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "StopFailure", "error": "rate_limit", "error_details": "429 Too Many Requests", "last_assistant_message": "API Error: Rate limit reached"}hookهای StopFailure کنترلِ تصمیم ندارند. فقط برای اهدافِ اعلان و لاگ اجرا میشوند.
TeammateIdle
Section titled “TeammateIdle”وقتی یک همتیمیِ تیمِ ایجنت پس از تمامکردنِ نوبتش قرار است idle شود اجرا میشود. از این برای اعمالِ دروازههای کیفیت پیش از اینکه همتیمی از کارکردن بایستد استفاده کن، مثلِ الزامِ گذرکردنِ بررسیهای lint یا تأییدِ وجودِ فایلهای خروجی.
وقتی یک hookِ TeammateIdle با کدِ 2 خارج میشود، همتیمی پیامِ stderr را بهعنوانِ بازخورد دریافت میکند و بهجای idleشدن به کارکردن ادامه میدهد. برای متوقفکردنِ کاملِ همتیمی بهجای اجرای دوبارهاش، JSON با {"continue": false, "stopReason": "..."} برگردان. hookهای TeammateIdle از matcher پشتیبانی نمیکنند و در هر رخداد فعال میشوند.
ورودیِ TeammateIdle
Section titled “ورودیِ TeammateIdle”علاوه بر فیلدهای ورودیِ مشترک، hookهای TeammateIdle teammate_name و team_name را دریافت میکنند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "TeammateIdle", "teammate_name": "researcher", "team_name": "my-project"}| فیلد | توضیح |
|---|---|
teammate_name | نامِ همتیمیای که قرار است idle شود |
team_name | نامِ تیم |
کنترلِ تصمیمِ TeammateIdle
Section titled “کنترلِ تصمیمِ TeammateIdle”hookهای TeammateIdle دو راه برای کنترلِ رفتارِ همتیمی پشتیبانی میکنند:
- کدِ خروجِ 2: همتیمی پیامِ stderr را بهعنوانِ بازخورد دریافت میکند و بهجای idleشدن به کارکردن ادامه میدهد.
- JSONِ
{"continue": false, "stopReason": "..."}: همتیمی را کاملاً متوقف میکند، مطابق با رفتارِ hookِStop.stopReasonبه کاربر نشان داده میشود.
این مثال پیش از اجازهی idleشدنِ همتیمی بررسی میکند که یک artifactِ build وجود دارد:
#!/bin/bash
if [ ! -f "./dist/output.js" ]; then echo "Build artifact missing. Run the build before stopping." >&2 exit 2fi
exit 0ConfigChange
Section titled “ConfigChange”وقتی یک فایلِ پیکربندی در طولِ یک نشست تغییر میکند اجرا میشود. از این برای ممیزیِ تغییراتِ تنظیمات، اعمالِ سیاستهای امنیتی، یا مسدودکردنِ تغییراتِ غیرمجاز در فایلهای پیکربندی استفاده کن.
hookهای ConfigChange برای تغییراتِ فایلهای تنظیمات، تنظیماتِ سیاستِ مدیریتشده و فایلهای skill فعال میشوند. فیلدِ source در ورودی به تو میگوید کدام نوعِ پیکربندی تغییر کرده، و فیلدِ اختیاریِ file_path مسیرِ فایلِ تغییریافته را فراهم میکند.
matcher روی منبعِ پیکربندی فیلتر میکند:
| Matcher | چه زمانی فعال میشود |
|---|---|
user_settings | تغییراتِ ~/.claude/settings.json |
project_settings | تغییراتِ .claude/settings.json |
local_settings | تغییراتِ .claude/settings.local.json |
policy_settings | تغییرِ تنظیماتِ سیاستِ مدیریتشده |
skills | تغییرِ یک فایلِ skill در .claude/skills/ |
این مثال همهی تغییراتِ پیکربندی را برای ممیزیِ امنیتی لاگ میکند:
{ "hooks": { "ConfigChange": [ { "hooks": [ { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-config-change.sh", "args": [] } ] } ] }}ورودیِ ConfigChange
Section titled “ورودیِ ConfigChange”علاوه بر فیلدهای ورودیِ مشترک، hookهای ConfigChange source و اختیاراً file_path را دریافت میکنند. فیلدِ source نشان میدهد کدام نوعِ پیکربندی تغییر کرده، و file_path مسیرِ فایلِ خاصی را که اصلاح شده فراهم میکند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "ConfigChange", "source": "project_settings", "file_path": "/Users/.../my-project/.claude/settings.json"}کنترلِ تصمیمِ ConfigChange
Section titled “کنترلِ تصمیمِ ConfigChange”hookهای ConfigChange میتوانند از اعمالشدنِ تغییراتِ پیکربندی جلوگیری کنند. از کدِ خروجِ 2 یا یک decisionِ JSON برای جلوگیری از تغییر استفاده کن. وقتی مسدود شود، تنظیماتِ جدید به نشستِ در حالِ اجرا اعمال نمیشوند.
| فیلد | توضیح |
|---|---|
decision | "block" از اعمالشدنِ تغییرِ پیکربندی جلوگیری میکند. برای اجازهی تغییر حذفش کن |
reason | توضیحی که وقتی decision برابرِ "block" است به کاربر نشان داده میشود |
{ "decision": "block", "reason": "Configuration changes to project settings require admin approval"}تغییراتِ policy_settings را نمیتوان مسدود کرد. hookها همچنان برای منابعِ policy_settings فعال میشوند، پس میتوانی از آنها برای ممیزیِ لاگ استفاده کنی، اما هر تصمیمِ مسدودسازی نادیده گرفته میشود. این تضمین میکند تنظیماتِ مدیریتشدهی سازمانی همیشه اعمال شوند.
CwdChanged
Section titled “CwdChanged”وقتی دایرکتوریِ کاری در طولِ یک نشست تغییر میکند اجرا میشود، برای مثال وقتی Claude یک فرمانِ cd اجرا میکند. از این برای واکنش به تغییراتِ دایرکتوری استفاده کن: بارگذاریِ دوبارهی متغیرهای محیطی، فعالکردنِ toolchainهای خاصِ پروژه، یا اجرای خودکارِ اسکریپتهای راهاندازی. با FileChanged برای ابزارهایی مثلِ direnv که محیطِ هر-دایرکتوری را مدیریت میکنند جفت میشود.
hookهای CwdChanged به CLAUDE_ENV_FILE دسترسی دارند. متغیرهای نوشتهشده در آن فایل به فرمانهای Bashِ بعدیِ نشست پایدار میمانند، درست مثلِ hookهای SessionStart.
CwdChanged از matcher پشتیبانی نمیکند و در هر تغییرِ دایرکتوری فعال میشود.
ورودیِ CwdChanged
Section titled “ورودیِ CwdChanged”علاوه بر فیلدهای ورودیِ مشترک، hookهای CwdChanged old_cwd و new_cwd را دریافت میکنند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl", "cwd": "/Users/my-project/src", "hook_event_name": "CwdChanged", "old_cwd": "/Users/my-project", "new_cwd": "/Users/my-project/src"}خروجیِ CwdChanged
Section titled “خروجیِ CwdChanged”علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، hookهای CwdChanged میتوانند watchPaths را برای تنظیمِ پویای اینکه FileChanged کدام مسیرهای فایل را watch میکند برگردانند:
| فیلد | توضیح |
|---|---|
watchPaths | آرایهای از مسیرهای مطلق. فهرستِ watchِ پویای جاری را جایگزین میکند (مسیرهای پیکربندیِ matcherت همیشه watch میشوند). برگرداندنِ یک آرایهی خالی فهرستِ پویا را پاک میکند، که هنگامِ ورود به یک دایرکتوریِ جدید معمول است |
hookهای CwdChanged کنترلِ تصمیم ندارند. نمیتوانند تغییرِ دایرکتوری را مسدود کنند.
FileChanged
Section titled “FileChanged”وقتی یک فایلِ تحتِ نظر روی دیسک تغییر میکند اجرا میشود. برای بارگذاریِ دوبارهی متغیرهای محیطی وقتی فایلهای پیکربندیِ پروژه اصلاح میشوند مفید است.
matcher برای این رویداد دو نقش ایفا میکند:
- ساختنِ فهرستِ watch: مقدار روی
|تقسیم میشود و هر بخش بهعنوانِ یک نامفایلِ واقعی در دایرکتوریِ کاری ثبت میشود، پس".envrc|.env"دقیقاً همان دو فایل را watch میکند. الگوهای regex اینجا مفید نیستند: مقداری مثلِ^\.envفایلی را که نامش واقعاً^\.envاست watch میکند. - فیلترکردنِ اینکه کدام hookها اجرا شوند: وقتی یک فایلِ تحتِ نظر تغییر میکند، همان مقدار با استفاده از قواعدِ استانداردِ matcher در برابرِ basenameِ فایلِ تغییریافته فیلتر میکند که کدام گروههای hook اجرا شوند.
hookهای FileChanged به CLAUDE_ENV_FILE دسترسی دارند. متغیرهای نوشتهشده در آن فایل به فرمانهای Bashِ بعدیِ نشست پایدار میمانند، درست مثلِ hookهای SessionStart.
ورودیِ FileChanged
Section titled “ورودیِ FileChanged”علاوه بر فیلدهای ورودیِ مشترک، hookهای FileChanged file_path و event را دریافت میکنند.
| فیلد | توضیح |
|---|---|
file_path | مسیرِ مطلق به فایلی که تغییر کرد |
event | چه اتفاقی افتاد: "change" (فایل اصلاح شد)، "add" (فایل ساخته شد)، یا "unlink" (فایل حذف شد) |
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl", "cwd": "/Users/my-project", "hook_event_name": "FileChanged", "file_path": "/Users/my-project/.envrc", "event": "change"}خروجیِ FileChanged
Section titled “خروجیِ FileChanged”علاوه بر فیلدهای خروجیِ JSONِ در دسترسِ همهی hookها، hookهای FileChanged میتوانند watchPaths را برای بهروزرسانیِ پویای اینکه کدام مسیرهای فایل watch میشوند برگردانند:
| فیلد | توضیح |
|---|---|
watchPaths | آرایهای از مسیرهای مطلق. فهرستِ watchِ پویای جاری را جایگزین میکند (مسیرهای پیکربندیِ matcherت همیشه watch میشوند). از این وقتی استفاده کن که اسکریپتِ hookت فایلهای اضافی برای watch بر اساسِ فایلِ تغییریافته کشف میکند |
hookهای FileChanged کنترلِ تصمیم ندارند. نمیتوانند از رخدادنِ تغییرِ فایل جلوگیری کنند.
WorktreeCreate
Section titled “WorktreeCreate”وقتی claude --worktree را اجرا میکنی یا یک سابایجنت از isolation: "worktree" استفاده میکند، Claude Code با استفاده از git worktree یک نسخهی کاریِ ایزوله میسازد. اگر یک hookِ WorktreeCreate پیکربندی کنی، رفتارِ پیشفرضِ git را جایگزین میکند و به تو اجازه میدهد از یک سیستمِ کنترلِ نسخهی متفاوت مثلِ SVN، Perforce یا Mercurial استفاده کنی.
چون hook رفتارِ پیشفرض را کاملاً جایگزین میکند، .worktreeinclude پردازش نمیشود. اگر نیاز داری فایلهای پیکربندیِ محلی مثلِ .env را به worktreeِ جدید کپی کنی، آن را داخلِ اسکریپتِ hookت انجام بده.
hook باید مسیرِ مطلق به دایرکتوریِ worktreeِ ساختهشده را برگرداند. Claude Code از این مسیر بهعنوانِ دایرکتوریِ کاری برای نشستِ ایزوله استفاده میکند. hookهای فرمان آن را روی stdout چاپ میکنند؛ hookهای HTTP آن را از طریقِ hookSpecificOutput.worktreePath برمیگردانند.
این مثال یک نسخهی کاریِ SVN میسازد و مسیر را برای استفادهی Claude Code چاپ میکند. URLِ مخزن را با مالِ خودت جایگزین کن:
{ "hooks": { "WorktreeCreate": [ { "hooks": [ { "type": "command", "command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'" } ] } ] }}hook نامِ worktree یعنی name را از ورودیِ JSON روی stdin میخواند، یک نسخهی تازه را در یک دایرکتوریِ جدید checkout میکند، و مسیرِ دایرکتوری را چاپ میکند. echo روی خطِ آخر همان چیزی است که Claude Code بهعنوانِ مسیرِ worktree میخواند. هر خروجیِ دیگری را به stderr هدایت کن تا با مسیر تداخل نکند.
ورودیِ WorktreeCreate
Section titled “ورودیِ WorktreeCreate”علاوه بر فیلدهای ورودیِ مشترک، hookهای WorktreeCreate فیلدِ name را دریافت میکنند. این یک شناسهی slug برای worktreeِ جدید است، یا توسطِ کاربر مشخصشده یا خودکار-تولیدشده (برای مثال، bold-oak-a3f2).
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "WorktreeCreate", "name": "feature-auth"}خروجیِ WorktreeCreate
Section titled “خروجیِ WorktreeCreate”hookهای WorktreeCreate از مدلِ استانداردِ تصمیمِ allow/block استفاده نمیکنند. در عوض، موفقیت یا شکستِ hook نتیجه را تعیین میکند. hook باید مسیرِ مطلق به دایرکتوریِ worktreeِ ساختهشده را برگرداند:
- hookهای فرمان (
type: "command"): مسیر را روی stdout چاپ کن. - hookهای HTTP (
type: "http"):{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }را در بدنهی پاسخ برگردان.
اگر hook شکست بخورد یا هیچ مسیری تولید نکند، ساختِ worktree با یک خطا شکست میخورد.
WorktreeRemove
Section titled “WorktreeRemove”همتای پاکسازیِ WorktreeCreate. این hook وقتی یک worktree در حالِ حذف است فعال میشود، چه وقتی از یک نشستِ --worktree خارج میشوی و حذفش را انتخاب میکنی، چه وقتی یک سابایجنت با isolation: "worktree" تمام میشود. برای worktreeهای مبتنی بر git، Claude پاکسازی را بهصورتِ خودکار با git worktree remove انجام میدهد. اگر یک hookِ WorktreeCreate برای یک سیستمِ کنترلِ نسخهی غیر-git پیکربندی کردی، آن را با یک hookِ WorktreeRemove جفت کن تا پاکسازی را مدیریت کند. بدونِ آن، دایرکتوریِ worktree روی دیسک باقی میماند.
Claude Code مسیرِ بازگرداندهشده توسطِ WorktreeCreate را بهعنوانِ worktree_path در ورودیِ hook پاس میدهد. این مثال آن مسیر را میخواند و دایرکتوری را حذف میکند:
{ "hooks": { "WorktreeRemove": [ { "hooks": [ { "type": "command", "command": "bash -c 'jq -r .worktree_path | xargs rm -rf'" } ] } ] }}ورودیِ WorktreeRemove
Section titled “ورودیِ WorktreeRemove”علاوه بر فیلدهای ورودیِ مشترک، hookهای WorktreeRemove فیلدِ worktree_path را دریافت میکنند، که مسیرِ مطلق به worktreeِ در حالِ حذف است.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "WorktreeRemove", "worktree_path": "/Users/.../my-project/.claude/worktrees/feature-auth"}hookهای WorktreeRemove کنترلِ تصمیم ندارند. نمیتوانند حذفِ worktree را مسدود کنند اما میتوانند وظایفِ پاکسازی مثلِ حذفِ حالتِ کنترلِ نسخه یا آرشیوِ تغییرات را انجام دهند. شکستهای hook فقط در حالتِ دیباگ لاگ میشوند.
PreCompact
Section titled “PreCompact”پیش از اینکه Claude Code قرار است یک عملیاتِ فشردهسازی اجرا کند اجرا میشود.
مقدارِ matcher نشان میدهد فشردهسازی دستی فعال شده یا خودکار:
| Matcher | چه زمانی فعال میشود |
|---|---|
manual | /compact |
auto | فشردهسازیِ خودکار وقتی پنجرهی کانتکست پر است |
با کدِ 2 خارج شو تا فشردهسازی را مسدود کنی. برای یک /compactِ دستی، پیامِ stderr به کاربر نشان داده میشود. همچنین میتوانی با برگرداندنِ JSON با "decision": "block" مسدود کنی.
مسدودکردنِ فشردهسازیِ خودکار بسته به زمانِ فعالشدنش اثرهای متفاوتی دارد. اگر فشردهسازی بهصورتِ پیشدستانه پیش از محدودیتِ کانتکست فعال شده باشد، Claude Code از آن میگذرد و گفتوگو بدونِ فشردهسازی ادامه مییابد. اگر فشردهسازی برای بازیابی از یک خطای محدودیتِ کانتکست که از پیش توسطِ API برگردانده شده فعال شده باشد، خطای زیرین آشکار میشود و درخواستِ جاری شکست میخورد.
ورودیِ PreCompact
Section titled “ورودیِ PreCompact”علاوه بر فیلدهای ورودیِ مشترک، hookهای PreCompact trigger و custom_instructions را دریافت میکنند. برای manual، custom_instructions آنچه را کاربر به /compact پاس میدهد شامل میشود. برای auto، custom_instructions خالی است.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "PreCompact", "trigger": "manual", "custom_instructions": ""}PostCompact
Section titled “PostCompact”پس از اینکه Claude Code یک عملیاتِ فشردهسازی را تکمیل میکند اجرا میشود. از این رویداد برای واکنش به حالتِ فشردهی جدید استفاده کن، برای مثال برای لاگکردنِ خلاصهی تولیدشده یا بهروزرسانیِ حالتِ بیرونی.
همان مقادیرِ matcherِ PreCompact اعمال میشوند:
| Matcher | چه زمانی فعال میشود |
|---|---|
manual | پس از /compact |
auto | پس از فشردهسازیِ خودکار وقتی پنجرهی کانتکست پر است |
ورودیِ PostCompact
Section titled “ورودیِ PostCompact”علاوه بر فیلدهای ورودیِ مشترک، hookهای PostCompact trigger و compact_summary را دریافت میکنند. فیلدِ compact_summary خلاصهی گفتوگوی تولیدشده توسطِ عملیاتِ فشردهسازی را شامل میشود.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "PostCompact", "trigger": "manual", "compact_summary": "Summary of the compacted conversation..."}hookهای PostCompact کنترلِ تصمیم ندارند. نمیتوانند روی نتیجهی فشردهسازی اثر بگذارند اما میتوانند وظایفِ پیگیری انجام دهند.
SessionEnd
Section titled “SessionEnd”وقتی یک نشستِ Claude Code پایان مییابد اجرا میشود. برای وظایفِ پاکسازی، لاگکردنِ آمارِ نشست، یا ذخیرهی حالتِ نشست مفید است. از matcherها برای فیلترکردن بر اساسِ دلیلِ خروج پشتیبانی میکند.
فیلدِ reason در ورودیِ hook نشان میدهد چرا نشست پایان یافت:
| دلیل | توضیح |
|---|---|
clear | نشست با فرمانِ /clear پاک شد |
resume | نشست از طریقِ /resumeِ تعاملی عوض شد |
logout | کاربر logout کرد |
prompt_input_exit | کاربر هنگامِ نمایانیِ ورودیِ پرامپت خارج شد |
bypass_permissions_disabled | حالتِ bypass permissions غیرفعال شد |
other | دلایلِ خروجِ دیگر |
ورودیِ SessionEnd
Section titled “ورودیِ SessionEnd”علاوه بر فیلدهای ورودیِ مشترک، hookهای SessionEnd یک فیلدِ reason دریافت میکنند که نشان میدهد چرا نشست پایان یافت. برای همهی مقادیر جدولِ reason در بالا را ببین.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "hook_event_name": "SessionEnd", "reason": "other"}hookهای SessionEnd کنترلِ تصمیم ندارند. نمیتوانند خاتمهی نشست را مسدود کنند اما میتوانند وظایفِ پاکسازی انجام دهند.
hookهای SessionEnd یک timeoutِ پیشفرضِ ۱.۵ ثانیه دارند. این برای خروجِ نشست، /clear و عوضکردنِ نشستها از طریقِ /resumeِ تعاملی اعمال میشود. اگر یک hook به زمانِ بیشتری نیاز دارد، یک timeoutِ هر-hook در پیکربندیِ hook تنظیم کن. بودجهی کلی بهصورتِ خودکار تا بالاترین timeoutِ هر-hookِ پیکربندیشده در فایلهای تنظیمات، تا ۶۰ ثانیه، بالا میرود. timeoutهای تنظیمشده روی hookهای فراهمشده توسطِ پلاگین بودجه را بالا نمیبرند. برای بازنویسیِ صریحِ بودجه، متغیرِ محیطیِ CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS را به میلیثانیه تنظیم کن.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claudeElicitation
Section titled “Elicitation”وقتی یک سرورِ MCP در میانِ کار درخواستِ ورودیِ کاربر میکند اجرا میشود. بهصورتِ پیشفرض، Claude Code یک دیالوگِ تعاملی برای پاسخِ کاربر نشان میدهد. hookها میتوانند این درخواست را میانجیگری کنند و بهصورتِ برنامهای پاسخ دهند و دیالوگ را کاملاً کنار بگذارند.
فیلدِ matcher در برابرِ نامِ سرورِ MCP مطابقت میکند.
ورودیِ Elicitation
Section titled “ورودیِ Elicitation”علاوه بر فیلدهای ورودیِ مشترک، hookهای Elicitation mcp_server_name، message و فیلدهای اختیاریِ mode، url، elicitation_id و requested_schema را دریافت میکنند.
برای elicitationِ حالتِ form (معمولترین مورد):
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "Elicitation", "mcp_server_name": "my-mcp-server", "message": "Please provide your credentials", "mode": "form", "requested_schema": { "type": "object", "properties": { "username": { "type": "string", "title": "Username" } } }}برای elicitationِ حالتِ URL (احراز هویتِ مبتنی بر مرورگر):
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "Elicitation", "mcp_server_name": "my-mcp-server", "message": "Please authenticate", "mode": "url", "url": "https://auth.example.com/login"}خروجیِ Elicitation
Section titled “خروجیِ Elicitation”برای پاسخِ برنامهای بدونِ نشاندادنِ دیالوگ، یک شیء JSON با hookSpecificOutput برگردان:
{ "hookSpecificOutput": { "hookEventName": "Elicitation", "action": "accept", "content": { "username": "alice" } }}| فیلد | مقادیر | توضیح |
|---|---|---|
action | accept، decline، cancel | اینکه درخواست را accept، decline یا cancel کنی |
content | object | مقادیرِ فیلدهای فرم برای ثبت. فقط وقتی action برابرِ accept است استفاده میشود |
کدِ خروجِ 2 elicitation را رد میکند و stderr را به کاربر نشان میدهد.
ElicitationResult
Section titled “ElicitationResult”پس از اینکه کاربر به یک elicitationِ MCP پاسخ میدهد اجرا میشود. hookها میتوانند پاسخ را پیش از فرستادنِ دوبارهاش به سرورِ MCP مشاهده، تغییر یا مسدود کنند.
فیلدِ matcher در برابرِ نامِ سرورِ MCP مطابقت میکند.
ورودیِ ElicitationResult
Section titled “ورودیِ ElicitationResult”علاوه بر فیلدهای ورودیِ مشترک، hookهای ElicitationResult mcp_server_name، action و فیلدهای اختیاریِ mode، elicitation_id و content را دریافت میکنند.
{ "session_id": "abc123", "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl", "cwd": "/Users/...", "permission_mode": "default", "hook_event_name": "ElicitationResult", "mcp_server_name": "my-mcp-server", "action": "accept", "content": { "username": "alice" }, "mode": "form", "elicitation_id": "elicit-123"}خروجیِ ElicitationResult
Section titled “خروجیِ ElicitationResult”برای بازنویسیِ پاسخِ کاربر، یک شیء JSON با hookSpecificOutput برگردان:
{ "hookSpecificOutput": { "hookEventName": "ElicitationResult", "action": "decline", "content": {} }}| فیلد | مقادیر | توضیح |
|---|---|---|
action | accept، decline، cancel | اکشنِ کاربر را بازنویسی میکند |
content | object | مقادیرِ فیلدهای فرم را بازنویسی میکند. فقط وقتی action برابرِ accept است معنادار است |
کدِ خروجِ 2 پاسخ را مسدود میکند و اکشنِ مؤثر را به decline تغییر میدهد.
hookهای مبتنی بر پرامپت
Section titled “hookهای مبتنی بر پرامپت”علاوه بر hookهای command، HTTP و ابزارِ MCP، Claude Code از hookهای مبتنی بر پرامپت (type: "prompt") پشتیبانی میکند که از یک LLM برای ارزیابیِ اینکه یک اکشن را مجاز یا مسدود کند استفاده میکنند، و از hookهای ایجنت (type: "agent") که یک تأییدگرِ ایجنتیک با دسترسی به ابزار spawn میکنند. همهی رویدادها هر نوعِ hook را پشتیبانی نمیکنند.
رویدادهایی که هر پنج نوعِ hook (command، http، mcp_tool، prompt و agent) را پشتیبانی میکنند:
PermissionDeniedPermissionRequestPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
رویدادهایی که hookهای command، http و mcp_tool را پشتیبانی میکنند اما prompt یا agent را نه:
ConfigChangeCwdChangedElicitationElicitationResultFileChangedInstructionsLoadedNotificationPostCompactPreCompactSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart و Setup از hookهای command و mcp_tool پشتیبانی میکنند. از hookهای http، prompt یا agent پشتیبانی نمیکنند.
hookهای مبتنی بر پرامپت چطور کار میکنند
Section titled “hookهای مبتنی بر پرامپت چطور کار میکنند”بهجای اجرای یک فرمانِ Bash، hookهای مبتنی بر پرامپت:
- ورودیِ hook و پرامپتت را به یک مدلِ Claude، بهصورتِ پیشفرض Haiku، میفرستند
- LLM با یک JSONِ ساختاریافتهی شاملِ یک تصمیم پاسخ میدهد
- Claude Code تصمیم را بهصورتِ خودکار پردازش میکند
پیکربندیِ hookِ پرامپت
Section titled “پیکربندیِ hookِ پرامپت”type را به "prompt" تنظیم کن و بهجای یک command یک رشتهی prompt فراهم کن. از جانگهدارِ $ARGUMENTS برای تزریقِ دادهی ورودیِ JSONِ hook به متنِ پرامپتت استفاده کن. Claude Code پرامپت و ورودیِ ترکیبشده را به یک مدلِ سریعِ Claude میفرستد، که یک تصمیمِ JSON برمیگرداند.
این hookِ Stop از LLM میخواهد ارزیابی کند که آیا همهی taskها تکمیل شدهاند پیش از اینکه به Claude اجازهی پایان دهد:
{ "hooks": { "Stop": [ { "hooks": [ { "type": "prompt", "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete." } ] } ] }}| فیلد | الزامی | توضیح |
|---|---|---|
type | بله | باید "prompt" باشد |
prompt | بله | متنِ پرامپتی که به LLM فرستاده میشود. از $ARGUMENTS بهعنوانِ جانگهدارِ JSONِ ورودیِ hook استفاده کن. اگر $ARGUMENTS حاضر نباشد، JSONِ ورودی به پرامپت پیوست میشود |
model | خیر | مدلی که برای ارزیابی استفاده میشود. پیشفرض یک مدلِ سریع |
timeout | خیر | timeout به ثانیه. پیشفرض: 30 |
continueOnBlock | خیر | وقتی پرامپت ok: false برمیگرداند، دلیل را به Claude بازخوران و بهجای توقف نوبت را ادامه بده. پیشفرض: false. بهصورتِ continue: true روی decision: "block"ِ حاصل پیادهسازی شده. برای رفتارِ هر-رویداد اسکیمای پاسخ را ببین |
اسکیمای پاسخ
Section titled “اسکیمای پاسخ”LLM باید با JSONِ شاملِ این پاسخ دهد:
{ "ok": true | false, "reason": "Explanation for the decision"}| فیلد | توضیح |
|---|---|
ok | true برای مجازکردن. false یک decision: "block" تولید میکند. رفتارِ هر-رویداد را در پایین ببین |
reason | وقتی ok برابرِ false است الزامی. بهعنوانِ دلیلِ مسدودسازی استفاده میشود |
آنچه در ok: false اتفاق میافتد به رویداد بستگی دارد:
StopوSubagentStop: دلیل بهعنوانِ دستورالعملِ بعدیِ Claude به آن بازخورانده میشود و نوبت ادامه مییابدPreToolUse: فراخوانیِ ابزار رد میشود و دلیل بهعنوانِ خطای ابزار به Claude برگردانده میشود، معادلِpermissionDecision: "deny"ِ یک hookِ فرمانPostToolUse: بهصورتِ پیشفرض نوبت پایان مییابد و دلیل بهعنوانِ یک خطِ هشدار در چت ظاهر میشود.continueOnBlock: trueرا تنظیم کن تا بهجایش دلیل به Claude بازخورانده شود و نوبت ادامه یابدPostToolBatch،UserPromptSubmitوUserPromptExpansion: نوبت پایان مییابد و دلیل بهعنوانِ یک خطِ هشدار ظاهر میشود. این رویدادها رویdecision: "block"صرفِنظر ازcontinueنوبت را پایان میدهندPostToolUseFailure،TaskCreatedوTaskCompleted: دلیل بهعنوانِ خطای ابزار به Claude برگردانده میشود، مشابهِPreToolUseTeammateIdle: بهصورتِ پیشفرض همتیمی میایستد و دلیل بهعنوانِ یک خطِ هشدار ظاهر میشود.continueOnBlock: trueرا تنظیم کن تا بهجایش دلیل به همتیمی بازخورانده شود و در حالِ کارکردن نگه داشته شودPermissionRequest:ok: falseاثری ندارد. برای ردِ یک تأیید از یک hook، از یک hookِ فرمان کهhookSpecificOutput.decision.behavior: "deny"برمیگرداند استفاده کنPermissionDenied:ok: falseاثری ندارد چون رد از پیش اتفاق افتاده. تنها خروجیای که این رویداد میخواندhookSpecificOutput.retryاست، که hookهای پرامپت و ایجنت نمیتوانند تنظیمش کنند — روی این رویداد اجرا میشوند، اما خروجیشان دور ریخته میشود. از یک hookِ فرمان برای برگرداندنِretryاستفاده کن
اگر روی هر رویدادی به کنترلِ دقیقتری نیاز داری، از یک hookِ فرمان با فیلدهای هر-رویدادِ توصیفشده در کنترلِ تصمیم استفاده کن.
مثال: hookِ Stopِ چندمعیاره
Section titled “مثال: hookِ Stopِ چندمعیاره”این hookِ Stop از یک پرامپتِ مفصل برای بررسیِ سه شرط پیش از اجازهی توقفِ Claude استفاده میکند. اگر "ok" برابرِ false باشد، Claude با دلیلِ ارائهشده بهعنوانِ دستورالعملِ بعدیاش به کارکردن ادامه میدهد. hookهای SubagentStop از همان فرمت برای ارزیابیِ اینکه آیا یک سابایجنت باید بایستد استفاده میکنند:
{ "hooks": { "Stop": [ { "hooks": [ { "type": "prompt", "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.", "timeout": 30 } ] } ] }}hookهای مبتنی بر ایجنت
Section titled “hookهای مبتنی بر ایجنت”hookهای مبتنی بر ایجنت (type: "agent") مثلِ hookهای مبتنی بر پرامپتاند اما با دسترسیِ چندنوبتی به ابزار. بهجای یک فراخوانیِ واحدِ LLM، یک hookِ ایجنت یک سابایجنت spawn میکند که میتواند فایلها را بخواند، کد را جستوجو کند و کدبیس را بازرسی کند تا شرایط را تأیید کند. hookهای ایجنت همان رویدادهای hookهای مبتنی بر پرامپت را پشتیبانی میکنند.
hookهای ایجنت چطور کار میکنند
Section titled “hookهای ایجنت چطور کار میکنند”وقتی یک hookِ ایجنت فعال میشود:
- Claude Code یک سابایجنت با پرامپتت و ورودیِ JSONِ hook spawn میکند
- سابایجنت میتواند از ابزارهایی مثلِ Read، Grep و Glob برای بررسی استفاده کند
- پس از حداکثر ۵۰ نوبت، سابایجنت یک تصمیمِ ساختاریافتهی
{ "ok": true/false }برمیگرداند - Claude Code تصمیم را به همان شیوهی یک hookِ پرامپت پردازش میکند
hookهای ایجنت وقتی مفیدند که تأیید نیازمندِ بازرسیِ فایلهای واقعی یا خروجیِ تست باشد، نه فقط ارزیابیِ دادهی ورودیِ hook بهتنهایی.
پیکربندیِ hookِ ایجنت
Section titled “پیکربندیِ hookِ ایجنت”type را به "agent" تنظیم کن و یک رشتهی prompt فراهم کن. فیلدهای پیکربندی همان hookهای پرامپت هستند، با یک timeoutِ پیشفرضِ طولانیتر:
| فیلد | الزامی | توضیح |
|---|---|---|
type | بله | باید "agent" باشد |
prompt | بله | پرامپتی که توصیف میکند چه چیزی را تأیید کند. از $ARGUMENTS بهعنوانِ جانگهدارِ JSONِ ورودیِ hook استفاده کن |
model | خیر | مدلی که استفاده میشود. پیشفرض یک مدلِ سریع |
timeout | خیر | timeout به ثانیه. پیشفرض: 60 |
اسکیمای پاسخ همان hookهای پرامپت است: { "ok": true } برای مجازکردن یا { "ok": false, "reason": "..." } برای مسدودکردن.
این hookِ Stop تأیید میکند که همهی تستهای واحد میگذرند پیش از اینکه به Claude اجازهی پایان دهد:
{ "hooks": { "Stop": [ { "hooks": [ { "type": "agent", "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS", "timeout": 120 } ] } ] }}اجرای hookها در پسزمینه
Section titled “اجرای hookها در پسزمینه”بهصورتِ پیشفرض، hookها اجرای Claude را تا تکمیلشان مسدود میکنند. برای وظایفِ طولانی مثلِ استقرارها، مجموعههای تست یا فراخوانیهای APIِ بیرونی، "async": true را تنظیم کن تا hook در پسزمینه اجرا شود در حالی که Claude به کارکردن ادامه میدهد. hookهای async نمیتوانند رفتارِ Claude را مسدود یا کنترل کنند: فیلدهای پاسخی مثلِ decision، permissionDecision و continue اثری ندارند، چون اکشنی که قرار بود کنترل کنند از پیش تکمیل شده.
پیکربندیِ یک hookِ async
Section titled “پیکربندیِ یک hookِ async”"async": true را به پیکربندیِ یک hookِ فرمان اضافه کن تا در پسزمینه بدونِ مسدودکردنِ Claude اجرا شود. این فیلد فقط روی hookهای type: "command" در دسترس است.
این hook پس از هر فراخوانیِ ابزارِ Write یک اسکریپتِ تست اجرا میکند. Claude بلافاصله به کارکردن ادامه میدهد در حالی که run-tests.sh تا ۱۲۰ ثانیه اجرا میشود. وقتی اسکریپت تمام میشود، خروجیاش در نوبتِ بعدیِ گفتوگو تحویل داده میشود:
{ "hooks": { "PostToolUse": [ { "matcher": "Write", "hooks": [ { "type": "command", "command": "/path/to/run-tests.sh", "async": true, "timeout": 120 } ] } ] }}فیلدِ timeout حداکثر زمان به ثانیه برای پروسهی پسزمینه را تعیین میکند. اگر مشخص نشود، hookهای async از همان پیشفرضِ ۱۰-دقیقهای hookهای sync استفاده میکنند.
hookهای async چطور اجرا میشوند
Section titled “hookهای async چطور اجرا میشوند”وقتی یک hookِ async فعال میشود، Claude Code پروسهی hook را شروع میکند و بلافاصله بدونِ انتظار برای اتمامش ادامه میدهد. hook همان ورودیِ JSON را از طریقِ stdin مثلِ یک hookِ همگام دریافت میکند.
پس از خروجِ پروسهی پسزمینه، اگر hook یک پاسخِ JSON با فیلدِ additionalContext تولید کرده باشد، آن محتوا بهعنوانِ کانتکست در نوبتِ بعدیِ گفتوگو به Claude تحویل میشود. یک فیلدِ systemMessage به تو نشان داده میشود، نه به Claude.
اعلانهای تکمیلِ hookِ async بهصورتِ پیشفرض سرکوب میشوند. برای دیدنشان، حالتِ verbose را با Ctrl+O فعال کن یا Claude Code را با --verbose راه بینداز.
مثال: اجرای تستها پس از تغییراتِ فایل
Section titled “مثال: اجرای تستها پس از تغییراتِ فایل”این hook هر وقت Claude فایلی مینویسد یک مجموعهی تست را در پسزمینه شروع میکند، سپس وقتی تستها تمام میشوند نتایج را به Claude گزارش میدهد. این اسکریپت را در .claude/hooks/run-tests-async.sh در پروژهات ذخیره کن و با chmod +x اجراشدنیاش کن:
#!/bin/bash# Read hook input from stdinINPUT=$(cat)FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Only run tests for source filesif [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then exit 0fi
# Run tests and report results to Claude via additionalContextRESULT=$(npm test 2>&1)EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then MSG="Tests passed after editing $FILE_PATH"else MSG="Tests failed after editing $FILE_PATH: $RESULT"fijq -nc --arg msg "$MSG" '{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'سپس این پیکربندی را به .claude/settings.json در ریشهی پروژهات اضافه کن. پرچمِ async: true به Claude اجازه میدهد در حالی که تستها اجرا میشوند به کارکردن ادامه دهد:
{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/run-tests-async.sh", "args": [], "async": true, "timeout": 300 } ] } ] }}محدودیتها
Section titled “محدودیتها”hookهای async در مقایسه با hookهای همگام چند محدودیت دارند:
- فقط hookهای
type: "command"asyncرا پشتیبانی میکنند. hookهای مبتنی بر پرامپت نمیتوانند بهصورتِ asynchronous اجرا شوند. - hookهای async نمیتوانند فراخوانیهای ابزار را مسدود کنند یا تصمیم برگردانند. تا وقتی hook تکمیل میشود، اکشنِ فعالکننده از پیش پیش رفته.
- خروجیِ hook در نوبتِ بعدیِ گفتوگو تحویل داده میشود. اگر نشست idle باشد، پاسخ تا تعاملِ بعدیِ کاربر منتظر میماند. استثنا: یک hookِ
asyncRewakeکه با کدِ 2 خارج میشود Claude را فوراً بیدار میکند حتی وقتی نشست idle است. - هر اجرا یک پروسهی پسزمینهی جداگانه میسازد. هیچ حذفِ تکراریای میانِ چند شلیکِ همان hookِ async نیست.
ملاحظاتِ امنیتی
Section titled “ملاحظاتِ امنیتی”سلبِ مسئولیت
Section titled “سلبِ مسئولیت”hookهای فرمان با مجوزهای کاملِ کاربرِ سیستمت اجرا میشوند.
بهترین شیوههای امنیتی
Section titled “بهترین شیوههای امنیتی”این شیوهها را هنگامِ نوشتنِ hookها در نظر داشته باش:
- ورودیها را اعتبارسنجی و پاکسازی کن: هرگز به دادهی ورودی کورکورانه اعتماد نکن
- همیشه متغیرهای شِل را نقلقول کن: از
"$VAR"استفاده کن نه$VAR - پیمایشِ مسیر را مسدود کن:
..را در مسیرهای فایل بررسی کن - از مسیرهای مطلق استفاده کن: مسیرهای کاملِ اسکریپتها را مشخص کن. در فرمِ exec، از
${CLAUDE_PROJECT_DIR}استفاده کن و مسیر به نقلقول نیاز ندارد. در فرمِ shell، آن را در نقلقولِ دوگانه بپیچ - از فایلهای حساس بگذر: از
.env،.git/، کلیدها و غیره اجتناب کن
ابزارِ PowerShellِ Windows
Section titled “ابزارِ PowerShellِ Windows”روی Windows، میتوانی hookهای منفرد را در PowerShell با تنظیمِ "shell": "powershell" روی یک hookِ فرمان اجرا کنی. hookها مستقیماً PowerShell را spawn میکنند، پس این صرفِنظر از اینکه CLAUDE_CODE_USE_POWERSHELL_TOOL تنظیم شده یا نه کار میکند. Claude Code بهصورتِ خودکار pwsh.exe (PowerShell 7+) را با بازگشت به powershell.exe (5.1) تشخیص میدهد.
{ "hooks": { "PostToolUse": [ { "matcher": "Write", "hooks": [ { "type": "command", "shell": "powershell", "command": "Write-Host 'File written'" } ] } ] }}دیباگِ hookها
Section titled “دیباگِ hookها”جزئیاتِ اجرای hook، از جمله اینکه کدام hookها مطابقت کردند، کدهای خروجشان، و stdout و stderrِ کامل، در فایلِ لاگِ دیباگ نوشته میشوند. Claude Code را با claude --debug-file <path> راه بینداز تا لاگ در یک محلِ مشخص نوشته شود، یا claude --debug را اجرا کن و لاگ را در ~/.claude/debug/<session-id>.txt بخوان. پرچمِ --debug در ترمینال چاپ نمیکند.
[DEBUG] Executing hooks for PostToolUse:Write[DEBUG] Found 1 hook commands to execute[DEBUG] Executing hook command: <Your command> with timeout 600000ms[DEBUG] Hook command completed with status 0: <Your stdout>برای جزئیاتِ ریزدانهترِ مطابقتِ hook، CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose را تنظیم کن تا خطوطِ لاگِ اضافی مثلِ شمارشِ matcherهای hook و مطابقتِ کوئری را ببینی.
برای عیبیابیِ مشکلاتِ رایج مثلِ فعالنشدنِ hookها، hookهای Stop که مدام مسدود میکنند، یا خطاهای پیکربندی، محدودیتها و عیبیابی در راهنما را ببین. برای راهنماییِ تشخیصیِ گستردهتری که /context، /doctor و تقدمِ تنظیمات را پوشش میدهد، دیباگِ پیکربندیات را ببین.