SSD Nodes Learn 🎉 VPS $5.50/ماہ سے
تعلیمی Matt Connorتحریر: Matt Connor · اپ ڈیٹ شدہ 2026-08-15

Claude Code hooks کی مکمل وضاحت

Claude Code hooks model کی رضامندی کے بغیر چلتے ہیں۔ جانیں یہ کہاں محفوظ ہوتے ہیں، کون سے events trigger ہوتے ہیں، اور exit code 2 tool call کو کیسے روکتا ہے۔

Claude Code hook کیا ہے

Claude Code hooks وہ shell commands ہیں جنہیں Claude Code اپنے lifecycle کے متعین مراحل پر خود چلاتا ہے۔ یہی hook اور rules file کے درمیان بنیادی فرق ہے۔ CLAUDE.md میں موجود instruction ایک ہدایت ہے، اور model اسے اپنے context میں موجود دوسری تمام معلومات کے مقابلے میں پرکھتا ہے۔ Hook code ہوتا ہے، اور model اس سے متفق ہو یا نہ ہو، یہ چلتا ہے۔ اگر آپ کا agent اس formatter کو بار بار نظرانداز کرتا ہے جس کے بارے میں آپ اسے دو مرتبہ بتا چکے ہیں، تو آپ کو زیادہ سخت instruction کی ضرورت نہیں۔ آپ کو hook کی ضرورت ہے۔

یہ mechanism مختصر ہے۔ آپ settings file میں کسی event name کے تحت ایک command رجسٹر کرتے ہیں۔ جب وہ event trigger ہوتا ہے، Claude Code آپ کی command چلاتا ہے اور event data کو JSON (JavaScript object notation) کی صورت میں اس کے standard input (stdin) پر لکھتا ہے۔ آپ کی command یہ data پڑھتی ہے، اپنا کام کرتی ہے، اور exit status کے ذریعے جواب دیتی ہے۔ PreToolUse hook سے exit 2 tool call کو چلنے سے پہلے منسوخ کر دیتا ہے، اور آپ کے script نے standard error (stderr) پر جو کچھ لکھا ہو، وہ وجہ کے طور پر model کو واپس دیا جاتا ہے۔

یہاں event names اور field names Claude Code کے hooks reference سے لیے گئے ہیں۔ ان کی August 2026 میں release 2.1.232 کے خلاف تصدیق کی گئی تھی۔ یہ surface تیزی سے تبدیل ہوتا ہے، اس لیے کسی بھی blog post، بشمول اس تحریر، سے JSON copy کرنے سے پہلے اپنے version کے لیے reference ضرور دیکھیں۔ اپنی version claude --version سے print کریں۔

ہُک کی configuration کہاں موجود ہوتی ہے

ہُک settings file میں موجود ایک JSON block ہوتا ہے۔ چھ مقامات میں ہُک رکھا جا سکتا ہے، اور file کا scope ہی ہُک کا scope ہوتا ہے۔

  • ~/.claude/settings.json: آپ کی machine کے ہر project کے لیے، اور کسی دوسرے شخص کے project کے لیے نہیں۔
  • .claude/settings.json: ایک project کے لیے، repository میں commit کیا جاتا ہے، اس لیے اسے clone کرنے والے ہر شخص کو یہ ہُک مل جاتا ہے۔
  • .claude/settings.local.json: ایک project کے لیے، صرف آپ کی machine پر۔
  • Managed policy settings: پوری organisation کے لیے، administrator کے ذریعے مقرر کی جاتی ہیں۔
  • hooks/hooks.json plugin کے اندر: plugin کے enabled رہنے تک فعال رہتا ہے۔
  • Skill یا subagent frontmatter: اس component کے active رہنے تک فعال رہتا ہے۔

ان files کے hook entries ایک دوسرے کو override کرنے کے بجائے merge ہو جاتے ہیں۔ Project settings file اپنے hooks کو user settings کے hooks میں شامل کرتی ہے، انہیں replace نہیں کرتی۔ اس لیے ایک event میں کئی files کے کئی hooks ہو سکتے ہیں۔ "disableAllHooks": true مقرر کرنے سے یہ hooks بند ہو جاتے ہیں۔ تاہم ایک استثنا ہے: managed policy settings کے hooks چلتے رہتے ہیں، جب تک یہی setting managed settings میں بھی لاگو نہ کی جائے۔

کسی session کے اندر /hooks چلائیں تاکہ اس وقت registered ہر ہُک کی فہرست event کے لحاظ سے grouped شکل میں دیکھ سکیں۔ فہرست read-only ہے، اس لیے ہُک تبدیل کرنے کے لیے settings file میں ترمیم کریں۔ File watcher عموماً restart کے بغیر ترمیم کو اٹھا لیتا ہے۔

Claude Code میں hook events کون سے موجود ہیں

Release 2.1.232 میں اکتیس events شامل ہیں، جو SessionStart سے SessionEnd تک ہیں۔ ان میں compaction، subagents، worktrees اور configuration files شامل ہیں۔ Server کے کام میں ان میں سے چند events زیادہ استعمال ہوتے ہیں۔

  • PreToolUse: tool call کے اجرا سے پہلے۔ یہی وہ event ہے جو کارروائی روک سکتا ہے۔
  • PostToolUse: tool call کامیاب ہونے کے بعد۔ ناکامی کی صورت میں PostToolUseFailure fire ہوتا ہے، اس لیے ہر نتیجے کو دیکھنے والے hook کو دونوں events درکار ہوتے ہیں۔
  • PermissionRequest: جب tool call کے لیے permission decision درکار ہو۔ اسی وقت approval prompt ظاہر ہوتا۔
  • UserPromptSubmit: prompt submit کرنے پر، Claude کے اسے process کرنے سے پہلے۔ یہ hook stdout پر جو کچھ print کرتا ہے، وہ model کے context میں شامل ہو جاتا ہے۔
  • SessionStart اور SessionEnd: session کے ہر اختتام پر۔ compaction کے بعد SessionStart بھی fire ہوتا ہے، جس میں matcher value compact ہوتی ہے۔
  • Stop: Claude کے جواب کی تکمیل پر۔ یہ ہر turn میں ایک بار fire ہوتا ہے، مکمل ہونے والے ہر task میں ایک بار نہیں۔

ہر group میں matcher شامل ہوتا ہے، جو یہ طے کرتا ہے کہ کن occurrences پر hook چلایا جائے۔ tool events میں یہ tool name کے مطابق filter کرتا ہے، اس لیے "Edit|Write" صرف file edits پر fire ہوتا ہے، کسی اور کارروائی پر نہیں۔ Matchers میں حروف کی صورت اہم ہوتی ہے۔ خالی matcher ہر occurrence پر fire ہوتا ہے۔ MCP (model context protocol) server کے tools کے نام mcp__<server>__<tool> کی صورت میں ہوتے ہیں، اس لیے "mcp__github__.*" matcher ایک server کے tools کو منتخب کرتا ہے اور باقی tools کو شامل نہیں کرتا۔

Stop hooks میں ایک اہم احتیاط ہے جسے hook لکھنے سے پہلے سمجھنا ضروری ہے۔ ایسا Stop hook جو کارروائی روکتا ہے، model کو دوبارہ کام کرنے کے لیے بھیج دیتا ہے، اور Claude Code مسلسل آٹھ بار block ہونے کے بعد hook کو override کر دیتا ہے۔ hook input سے stop_hook_active field پڑھیں اور جب اس کی قدر true ہو تو exit 0 کریں، ورنہ hook اس حد تک پہنچنے تک loop کرتا رہے گا۔

stdin پر hook کو کیا موصول ہوتا ہے

جب Claude، npm test چلانے والا ہو تو Bash پر موجود PreToolUse hook، stdin سے یہ ڈیٹا پڑھتا ہے:

{
  "session_id": "abc123",
  "cwd": "/home/deploy/myproject",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}

ہر event میں session_id، cwd، permission_mode، transcript_path اور hook_event_name شامل ہوتے ہیں۔ tool events میں tool_name، tool_input اور tool_use_id بھی شامل ہوتے ہیں۔ دیگر events میں اپنے متعلقہ fields ہوتے ہیں: UserPromptSubmit میں prompt کا متن ہوتا ہے، جبکہ SessionStart میں startup، resume، clear، compact یا fork کا source ہوتا ہے۔

shell script کے اندر اسے پڑھنے کا عام طریقہ jq ہے، لیکن minimal server image میں یہ موجود نہیں ہوتا۔ Ubuntu اور Debian پر اسے پہلے sudo apt install -y jq کے ذریعے install کریں۔

ٹول کال کے دوران exit status کے اثرات

تین نتائج ممکن ہیں۔

  • Exit 0 کا مطلب ہے کہ آپ کے hook نے کوئی اعتراض نہیں کیا۔ PreToolUse میں یہ منظوری کے برابر نہیں ہے، اور معمول کا permission flow پھر بھی چلتا ہے۔ UserPromptSubmit اور SessionStart میں stdout کو model کے context میں شامل کر دیا جاتا ہے۔
  • Exit 2 ان events پر action کو روک دیتا ہے جنہیں block کیا جا سکتا ہے، جن میں PreToolUse بھی شامل ہے، اور stderr کو model کو دکھائی جانے والی وجہ کے طور پر استعمال کیا جاتا ہے۔ جن events کو block نہیں کیا جا سکتا، جیسے PostToolUse، ان میں block نظر انداز کر دیا جاتا ہے، لیکن stderr پھر بھی feedback کے طور پر model تک پہنچتا ہے۔
  • کوئی بھی دوسرا exit code non-blocking error ہوتا ہے۔ Action جاری رہتا ہے۔ Transcript میں hook error notice دکھایا جاتا ہے، جس میں stderr کی پہلی line متن Failed with non-blocking status code: کے بعد شامل ہوتی ہے۔

اگر block کرنے یا خاموش رہنے کے علاوہ کوئی اور نتیجہ درکار ہو تو exit 0 استعمال کریں اور stdout پر JSON object پرنٹ کریں۔ PreToolUse hook permissionDecision کے ذریعے فیصلہ کرتا ہے:

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

"allow" interactive prompt کو چھوڑ دیتا ہے، "deny" call منسوخ کر کے وجہ model کو بھیجتا ہے، اور "ask" prompt کو معمول کے مطابق دکھاتا ہے۔ ہر hook کے لیے ایک ہی style منتخب کریں۔ stdout پر exit 2 کو JSON decision کے ساتھ ملانے سے ایسا نتیجہ پیدا ہوتا ہے جس کی تشریح آپ کو الگ سے کرنی پڑتی ہے۔

جب ایک event سے کئی hooks match ہوں تو وہ parallel چلتے ہیں، اور ہر hook completion تک چلتا رہتا ہے۔ ایک hook کا deny اس کے sibling hooks کو نہیں روکتا، اس لیے logging hook اپنی line لکھتا رہتا ہے جبکہ guardrail hook اسی call کو deny کر دیتا ہے۔ اس کے بعد Claude Code تمام جوابات merge کرتا ہے اور زیادہ restrictive نتیجہ برقرار رکھتا ہے۔ ترجیح کی ترتیب deny، defer، ask، allow ہے۔

مثال 1: تباہ کن کمانڈ کے چلنے سے پہلے اسے روکیں

اسے اپنے project میں .claude/hooks/block-destructive.sh کے طور پر محفوظ کریں:

#!/bin/bash
# Deny a Bash tool call whose command matches a banned pattern.
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

for pattern in 'rm -rf /' 'mkfs' 'dd if=' 'DROP TABLE'; do
  if printf '%s' "$COMMAND" | grep -qiF -- "$pattern"; then
    echo "Blocked by policy: the command matches '$pattern'. A human runs this one." >&2
    exit 2
  fi
done

exit 0

اسے executable بنائیں، پھر .claude/settings.json میں PreToolUse پر رجسٹر کریں:

chmod +x .claude/hooks/block-destructive.sh
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive.sh",
            "timeout": 10,
            "statusMessage": "Checking the command against policy"
          }
        ]
      }
    ]
  }
}

اس script پر اعتماد کرنے سے پہلے اسے دستی طور پر test کریں، کیونکہ جو hook اپنی input پر crash ہو جائے وہ open حالت میں fail ہوتا ہے:

echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /var/lib/postgresql"}}' \
  | .claude/hooks/block-destructive.sh
echo $?

آپ کو stderr پر Blocked by policy: والی line اور 2 کا exit code نظر آنا چاہیے۔ ls -la جیسی بے ضرر command دیں؛ آپ کو کوئی output نہیں ملنا چاہیے اور exit code 0 ہونا چاہیے۔ session میں denied call، آپ کے message کو وجہ کے طور پر دکھاتے ہوئے transcript میں ظاہر ہوتی ہے، اور model وہ message پڑھ کر اپنا طریقہ تبدیل کرتا ہے۔

ایک خصوصیت اس عمل کو مفید بناتی ہے: PreToolUse hooks ہر permission mode میں permission-mode check سے پہلے چلتے ہیں، اس لیے bypassPermissions کے تحت بھی deny مؤثر رہتا ہے۔ اسی وجہ سے hook Claude Code کے auto mode اور اس کی permission settings کے ساتھ مفید ہے، جہاں prompts کم کر دیے جاتے ہیں لیکن hook پھر بھی چلتا ہے۔

اس کی حدود کے بارے میں واضح رہیں۔ command string پر pattern matching، agent کی لاپرواہی کے خلاف ایک guardrail ہے۔ یہ agent کی چالاکی کے خلاف boundary نہیں ہے، کیونکہ وہی command ایسی شکل میں لکھی جا سکتی ہے جسے آپ کا grep کبھی دیکھ ہی نہ سکے۔ سخت قواعد permission system اور اس account میں نافذ ہونے چاہییں جس کے تحت process چلتا ہے۔

مثال 2: ہر ترمیم کے بعد format اور lint چلائیں

PostToolUse میں موجود Edit|Write matcher ہر file-editing tool کے بعد چلتا ہے۔ اسے .claude/hooks/after-edit.sh کے طور پر محفوظ کریں:

#!/bin/bash
# Format the edited file, then report lint failures back to the model.
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0

case "$FILE" in
  *.py)
    ruff format "$FILE" >/dev/null 2>&1
    if ! ruff check "$FILE" >&2; then
      exit 2
    fi
    ;;
  *.sh)
    if ! shellcheck "$FILE" >&2; then
      exit 2
    fi
    ;;
esac

exit 0
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/after-edit.sh",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

Claude سے کہیں کہ Python file میں غلط indentation والا function شامل کرے، پھر file کھولیں۔ file formatted حالت میں واپس آتی ہے۔ اس سے تصدیق ہوتی ہے کہ hook چلا، کیونکہ کامیاب hook گفتگو میں کچھ بھی ظاہر نہیں کرتا۔

یہاں exit 2 کسی چیز کو واپس نہیں کرتا۔ PostToolUse tool کے پہلے ہی execute ہونے کے بعد چلتا ہے، اس لیے edit ہر صورت disk پر موجود رہتی ہے۔ exit 2 کا فائدہ یہ ہے کہ ruff check کا output model تک feedback کے طور پر پہنچتا ہے۔ اس طرح model آگے بڑھنے کے بجائے اپنی ابھی متعارف کردہ error درست کرتا ہے۔ یہی فرق ہے اس lint failure میں جو آپ commit کے وقت دریافت کرتے ہیں، اور اس failure میں جسے agent اسی turn میں درست کر دیتا ہے۔

یہاں دو matcher limits اہم ہیں۔ Edit|Write shell command سے تبدیل ہونے والی files کو نہیں دیکھتا، اور Claude اتنی کثرت سے Bash کے ذریعے files لکھتا ہے کہ یہ خلا حقیقی اہمیت رکھتا ہے۔ ہر call کی coverage کے لیے Bash کو بھی match کریں اور script سے کہیں کہ git status --porcelain کے ذریعے تبدیل شدہ files کی فہرست بنائے۔ ہر turn میں ایک بار coverage کے لیے scan کو Stop hook میں رکھیں۔

مثال 3: audit کے لیے ہر tool call کا log درج کریں

PostToolUse پر خالی matcher ہر tool پر فعال ہوتا ہے۔ ریکارڈ کو home directory کی file کے بجائے system journal میں بھیجنے سے یہ agent کے اپنے shell کی رسائی سے باہر رہتا ہے:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{time: now|todate, session: .session_id, cwd: .cwd, tool: .tool_name, input: .tool_input}' | logger -t claude-code -p local0.info"
          }
        ]
      }
    ]
  }
}

اسے journalctl -t claude-code -o cat | tail -n 5 کے ذریعے دوبارہ پڑھیں۔ ہر tool call کے لیے JSON کی ایک سطر نظر آنی چاہیے، جس میں تازہ ترین اندراج آخر میں ہو۔ کچھ بھی ظاہر نہ ہونے کا مطلب ہے کہ hook نہیں چلا۔ ذیل کا troubleshooting section اس مسئلے کا احاطہ کرتا ہے۔

failed calls کو بھی capture کرنے کے لیے PostToolUseFailure کے تحت یہی block شامل کریں، کیونکہ PostToolUse صرف کامیابی پر فعال ہوتا ہے، جبکہ ناکام command عموماً زیادہ اہم ہوتی ہے۔ home directory کی file میں append کرنے کے بجائے logger استعمال کرنے کی وجہ ownership ہے: hook agent کے shell کے same user کے طور پر چلتا ہے، اس لیے جس چیز میں وہ user append کر سکتا ہے، اسے truncate بھی کر سکتا ہے۔ journal کو systemd-journald اپنے account کے تحت لکھتا ہے۔

Hook کتنی دیر چل سکتا ہے

ChartDefault hook timeout in seconds, by hook type and event
The data behind this chart
[
  {
    "label": "command, http or mcp_tool hook",
    "default_timeout_seconds": 600
  },
  {
    "label": "agent hook",
    "default_timeout_seconds": 60
  },
  {
    "label": "prompt hook",
    "default_timeout_seconds": 30
  },
  {
    "label": "command hook on UserPromptSubmit",
    "default_timeout_seconds": 30
  },
  {
    "label": "command hook on MessageDisplay",
    "default_timeout_seconds": 10
  },
  {
    "label": "any hook on SessionEnd",
    "default_timeout_seconds": 1.5
  }
]

ایک command hook کو default طور پر 600 seconds ملتے ہیں، یعنی دس منٹ۔ کچھ events اس مدت کو کافی کم کر دیتے ہیں۔ SessionEnd hooks سب مل کر 1.5 seconds کا مشترکہ budget استعمال کرتے ہیں، اس لیے session کے اختتام پر ہونے والی cleanup تیزی سے مکمل ہونی چاہیے۔ تاہم hook پر طویل timeout مقرر کرنے سے یہ مشترکہ budget بھی اسی مدت تک بڑھ جاتا ہے، جو زیادہ سے زیادہ 60 seconds ہو سکتا ہے۔

جو hook اپنے timeout تک پہنچ جائے، اسے cancel کر دیا جاتا ہے اور وہ کوئی decision نہیں دیتا۔ PreToolUse guardrail کے لیے اس کا مطلب یہ ہے کہ وہ tool call کو block نہیں کرتا؛ tool call معمول کے permission flow میں جاری رہتی ہے۔ اسی وجہ سے guardrail scripts مختصر رکھیں۔ ایسے سست کام کے لیے جن کا کوئی منتظر نہ ہو، مثلاً log کو کہیں اور بھیجنے کے لیے، "async": true مقرر کریں۔ اس صورت میں hook background میں چلتا ہے اور tool call کو روکتا نہیں۔

Hooks، rules files، skills اور MCP servers

یہ چاروں چیزیں آپس میں اس لیے خلط ملط ہو جاتی ہیں کہ یہ سب agent کے کام کرنے کے طریقے کو بدلتی ہیں۔ ان میں سے صرف ایک چیز suggestion نہیں رہتی۔

ایک rules file (CLAUDE.md، یا .claude/rules/ کے اندر موجود file) وہ text ہے جسے model اپنے context میں load کرتا ہے۔ یہ behaviour کو shape کرتی ہے، لیکن کچھ enforce نہیں کرتی۔ طویل conversation، بڑے diff اور user کی نئی request کے مقابلے میں اس کی ایک سطر نظرانداز ہو سکتی ہے۔ یہی عام mechanism ہے جس کی وجہ سے agents آپ کی لکھی ہوئی instructions کو نظرانداز کرتے ہیں۔

ایک skill، instructions اور scripts کا folder ہوتی ہے جسے model اس وقت load کرتا ہے جب وہ skill کو relevant سمجھتا ہے۔ یہی فیصلہ skill کا بنیادی مقصد ہے اور اس کی حد بھی: فیصلہ پھر بھی model ہی کرتا ہے۔ Ponytail، جو agent کو کام کرنے والی کم سے کم تبدیلی کی طرف لے جاتی ہے جیسی skill میں اس کے دونوں پہلو نظر آتے ہیں، کیونکہ یہ پورے task سے نمٹنے کا طریقہ اس طرح shape کرتی ہے جو کوئی hook نہیں کر سکتا، اور صرف اسی وقت فعال ہوتی ہے جب model اسے load کرنے کا انتخاب کرے۔

ایک MCP (model context protocol) server، model کو call کرنے کے لیے نئے tools فراہم کرتا ہے۔ اس سے agent کی رسائی کا دائرہ وسیع ہوتا ہے۔ یہ agent کو کسی چیز تک پہنچنے پر مجبور نہیں کرتا۔ یہ ایک الگ process بھی ہے جسے آپ کو operate کرنا ہوتا ہے، اور یہ خود ایک الگ کام ہے: VPS پر MCP servers چلانے کو دیکھیں۔

ان چاروں میں صرف hook ایسا ہے جو model کے انتخاب کے بغیر run ہوتا ہے۔ preference کے لیے rules file استعمال کریں، اور ایسی procedure کے لیے skill استعمال کریں جسے متعلقہ ہونے پر model کو follow کرنا چاہیے۔ اس step کے لیے hook استعمال کریں جو ہر بار لازماً ہونا چاہیے، یا اس کام کے لیے جو کبھی نہیں ہونا چاہیے۔ مزید تفصیلی comparison، بشمول یہ کہ skill کب rules file سے بہتر ہوتی ہے، skills، MCP اور rules files کے comparison میں موجود ہے۔

plugin پانچواں mechanism نہیں بلکہ packaging ہے۔ یہ hooks اور skills کو ایک installable unit میں bundle کرتا ہے۔ اسی طرح team ہر machine پر ایک ہی guardrail deploy کرتی ہے: Claude Code plugins کیسے کام کرتے ہیں کو دیکھیں۔

مشترکہ VPS پر سکیورٹی سے متعلق فیصلہ

Hook وہ code ہے جسے agent trigger کرتا ہے، اور یہ اسی user کے طور پر چلتا ہے جس نے Claude Code شروع کیا ہو۔ اسے اس user کا environment اور file permissions وراثت میں ملتے ہیں۔ Laptop پر یہ workflow کا سوال ہے۔ ایسے VPS پر، جہاں agent unattended چلتا ہو، یہ سکیورٹی کا سوال ہے جس کے چار عملی پہلو ہیں۔

Repository میں موجود hook وہ code ہے جو آپ نے خود نہیں لکھا۔ .claude/settings.json committed ہوتا ہے، اس لیے کسی repository کو clone کرکے اس کے اندر session شروع کرنے سے repository کے ساتھ آنے والے hooks register ہو سکتے ہیں۔ Claude Code اس folder کے لیے workspace trust dialog کے ذریعے project hooks کو control کرتا ہے۔ اس کا مطلب ہے کہ trust قبول کرنا ہی وہ مرحلہ ہے جب آپ انہیں چلانے کا فیصلہ کرتے ہیں۔ پہلے hooks block پڑھیں۔

Hook کو tool کا مکمل input نظر آتا ہے۔ ایسا audit hook جو tool_input log کرتا ہے، ہر command کی ہر argument کو file میں لکھتا ہے، جس میں وہ token بھی شامل ہو سکتا ہے جو اتفاقاً command line پر موجود ہو۔ اس log کو پھر secret جیسی ہی protection درکار ہوتی ہے۔ یہ اس وسیع مسئلے کا حصہ ہے کہ secrets کو AI agent کی پہنچ سے دور رکھا جائے۔

Hook model کے context میں data لکھ سکتا ہے۔ SessionStart یا UserPromptSubmit hook جو کچھ stdout پر print کرتا ہے، وہ conversation میں شامل کر دیا جاتا ہے۔ ایسا hook جو کسی بیرونی source، issue tracker یا log file سے text pipe کرتا ہے، وہ untrusted text کو model کے حوالے کر رہا ہوتا ہے، جیسے آپ نے اسے خود type کیا ہو۔ اس stdout کو output نہیں بلکہ input سمجھیں۔

اصل control privilege ہے۔ Agent کو ایک dedicated unprivileged user کے طور پر چلائیں، جس کے پاس صرف مطلوبہ sudo rules ہوں۔ PreToolUse deny رکھنا مفید ہے، اور اسے design کے لحاظ سے best effort سمجھا جاتا ہے: reference، if filter کے بارے میں بھی یہی کہتا ہے اور hard deny درکار ہونے پر permission system استعمال کرنے کی ہدایت دیتا ہے۔ Permission rules اور وہ account جس کے تحت process چلتا ہے، دباؤ کی صورت میں بھی مؤثر رہنے والے بنیادی حصے ہیں۔

ہر configuration میں ایک property برقرار رہتی ہے۔ PreToolUse hooks ہر permission mode میں permission-mode check سے پہلے run ہوتے ہیں، اس لیے deny واپس کرنے والا hook، bypassPermissions کے تحت بھی tool کو block کر دیتا ہے۔ Hooks، permission rules کی اجازت کو مزید محدود کر سکتے ہیں۔ وہ اسے نرم نہیں کر سکتے۔

میرا hook کیوں نہیں چل رہا؟

اسے درج ذیل ترتیب سے جانچیں۔ ہر مرحلہ وہ علامت بتاتا ہے جو آپ کو حقیقت میں نظر آئے گی۔

  • /hooks چلائیں اور دیکھیں کہ hook متوقع event کے تحت دکھائی دیتا ہے۔ اگر hook menu میں موجود نہ ہو تو عموماً settings file میں JSON syntax error ہوتا ہے، کیونکہ trailing commas اور comments کی اجازت نہیں، یا file اوپر دی گئی چھ locations میں سے کسی ایک میں موجود نہیں۔
  • Matcher کا tool name کے ساتھ بالکل درست موازنہ کریں۔ Matchers case-sensitive ہوتے ہیں، اس لیے "bash" کبھی بھی Bash tool سے match نہیں کرتا۔
  • مثال 1 کی طرح sample input کے ساتھ script کو دستی طور پر چلائیں۔ اگر exit code آپ کی توقع کے مطابق نہ ہو تو مسئلہ script میں ہے، اور Claude Code اسے decision کے بجائے hook error کے طور پر رپورٹ کرتا ہے۔
  • jq: command not found کا notice ظاہر ہو تو اس کا مطلب ہے کہ اس machine پر jq موجود نہیں۔ اپنی script کے لیے command not found کا مطلب ہے کہ path resolve نہیں ہوا، اس لیے ${CLAUDE_PROJECT_DIR} یا absolute path استعمال کریں۔ اگر script بالکل نہ چلے تو غالباً اس کے executable permissions نہیں ہیں۔
  • Hook درست JSON پرنٹ کرتا ہے، لیکن کچھ نہیں ہوتا۔ Shell-form hook sh -c کے ذریعے چلتا ہے، اور اگر آپ کا shell profile banner پرنٹ کرتا ہے تو وہ banner آپ کے JSON کے شروع میں شامل ہو جاتا ہے۔ اب stdout { سے شروع نہیں ہوتا، اس لیے Claude Code پوری output کو plain text سمجھ کر decision نظرانداز کر دیتا ہے۔ Exit 0 پر debug log کے علاوہ کہیں بھی کچھ رپورٹ نہیں ہوتا۔ اپنے profile میں موجود ہر echo کو اس طرح wrap کریں کہ وہ صرف interactive shells میں چلے۔
  • پھر بھی مسئلہ برقرار ہو تو session کو claude --debug-file /tmp/claude.log کے ساتھ شروع کریں اور دوسرے terminal میں tail -f /tmp/claude.log چلائیں۔ Debug log میں درج ہوتا ہے کہ کون سے hooks match ہوئے، ہر hook نے کون سا exit code واپس کیا، اور انہوں نے stdout اور stderr پر کیا کچھ لکھا۔

FAQ

Claude Code hook اور CLAUDE.md instruction میں کیا فرق ہے؟

CLAUDE.md instruction ماڈل کے context میں موجود متن ہوتی ہے۔ اس لیے اس کی توجہ conversation اور موجودہ request کے ساتھ تقسیم ہوتی ہے، اور ماڈل ان سب کے مقابلے میں اس instruction کو ترجیح دے سکتا ہے۔ hook ایک shell command ہوتی ہے جسے Claude Code اپنے lifecycle کے مقررہ مرحلے پر چلاتا ہے۔ اس لیے event کے ہر وقوع پر یہ command چلتی ہے، خواہ ماڈل نے کوئی بھی فیصلہ کیا ہو۔ ترجیح یا معمول کے لیے instruction استعمال کریں۔ ایسے مرحلے کے لیے hook استعمال کریں جو ہمیشہ ہونا چاہیے یا ایسا action جو کبھی نہیں ہونا چاہیے۔

Claude Code کو کسی مخصوص shell command کو چلانے سے کیسے روکوں؟

PreToolUse hook کو Bash matcher کے ساتھ register کریں۔ یہ matcher command کو .tool_input.command سے پڑھتا ہے، stderr میں وجہ لکھتا ہے اور exit 2 کرتا ہے۔ Claude Code call منسوخ کر دیتا ہے اور ماڈل کو آپ کی وجہ دکھاتا ہے۔ یہ permission-mode check سے پہلے ہوتا ہے، اس لیے deny bypassPermissions mode میں بھی نافذ رہتا ہے۔ Command string پر pattern matching ایک guardrail ہے، security boundary نہیں۔ ایک ہی command ایسی شکل میں لکھی جا سکتی ہے جسے pattern نہ پہچان سکے۔ اس لیے permission rules اور unprivileged account بھی استعمال کریں۔

میری hook درست JSON print کرتی ہے، لیکن کچھ نہیں ہوتا۔ کیوں؟

اس کی سب سے عام وجہ shell profile ہوتی ہے۔ args field کے بغیر hook sh -c کے ذریعے چلتی ہے۔ کچھ profiles ہر shell میں banner print کرتی ہیں، جو آپ کے JSON سے پہلے stdout میں شامل ہو جاتا ہے۔ چونکہ output اب { سے شروع نہیں ہوتا، Claude Code پوری output کو plain text سمجھ کر decision نظرانداز کر دیتا ہے۔ exit 0 پر transcript میں کچھ بھی رپورٹ نہیں ہوتا۔ اپنی profile میں موجود ہر echo کو interactive-shell test کے ساتھ مشروط کریں۔ پھر claude --debug-file /tmp/claude.log سے debug log پڑھ کر fix کی تصدیق کریں۔

کیا shared server پر Claude Code hooks چلانا محفوظ ہے؟

Hooks اس user کے طور پر چلتی ہیں جس نے Claude Code شروع کیا ہو۔ ان کے پاس اسی user کی file permissions ہوتی ہیں، اس لیے hook وہ تمام کام کر سکتی ہے جو یہ account کر سکتا ہے۔ زیادہ تر خطرات کم کرنے کے لیے دو عادات کافی ہیں: agent کو محدود sudo policy والے dedicated unprivileged account کے طور پر چلائیں، اور workspace trust dialog قبول کرنے سے پہلے repository کے hooks block کو پڑھیں، کیونکہ project hooks .claude/settings.json کے اندر شامل ہوتی ہیں۔ جب آپ ان میں سے کسی کو بھی چلنے سے روکنا چاہیں تو اپنی settings file میں "disableAllHooks": true مقرر کریں۔