SSD Nodes Learn Hosting plans →
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-26

Claude Code Hooks: কীভাবে কাজ করে ও কেন দরকার

Claude Code hooks model-এর সম্মতি ছাড়াই চলে। কোথায় থাকে, কোন event কখন fire হয়, exit code 2 কীভাবে tool call বাতিল করে এবং security cost কী, জানুন।

Claude Code hook কী

Claude Code hooks হলো shell command, যা Claude Code তার নিজস্ব lifecycle-এর নির্দিষ্ট পর্যায়ে স্বয়ংক্রিয়ভাবে চালায়। Hook এবং rules file-এর মধ্যে এটিই মূল পার্থক্য। CLAUDE.md-এর কোনো instruction হলো পরামর্শ, এবং model তার context-এর অন্য সব কিছুর সঙ্গে সেটির গুরুত্ব বিবেচনা করে। Hook হলো code, এবং model সম্মত হোক বা না হোক, এটি চলবে। আপনি agent-কে formatter চালানোর কথা দুবার বলার পরও যদি সে formatter চালানো এড়িয়ে যায়, তাহলে আরও কঠোর instruction দরকার নেই। আপনার hook দরকার।

ব্যবস্থাটি ছোট। একটি settings file-এ event name-এর অধীনে একটি command নিবন্ধন করুন। সেই event ঘটলে Claude Code আপনার command চালায় এবং event-এর data JSON (JavaScript object notation) হিসেবে standard input (stdin)-এ লিখে দেয়। আপনার command সেই data পড়ে কাজ সম্পন্ন করে এবং exit status-এর মাধ্যমে ফল জানায়। PreToolUse hook থেকে exit status 2 দিলে tool call চালু হওয়ার আগেই বাতিল হয়। আপনার script standard error (stderr)-এ যা লিখেছে, তা কারণ হিসেবে model-এর কাছে ফেরত পাঠানো হয়।

এখানে ব্যবহৃত event name এবং field name Claude Code hooks reference থেকে নেওয়া হয়েছে। এগুলো August 2026-এ release 2.1.232-এর সঙ্গে মিলিয়ে দেখা হয়েছে। এই interface দ্রুত পরিবর্তিত হয়। তাই কোনো blog post, এমনকি এই লেখাটি থেকেও JSON কপি করার আগে আপনার ব্যবহৃত version-এর reference পরীক্ষা করুন। claude --version দিয়ে নিজের version দেখুন।

হুক configuration কোথায় থাকে

হুক হলো একটি settings file-এর JSON block। এটি 6টি স্থানে রাখা যেতে পারে। যে file-এ হুকটি থাকে, তার scope-ই হুকটির scope নির্ধারণ করে।

  • ~/.claude/settings.json: আপনার machine-এর প্রতিটি project-এ প্রযোজ্য, অন্য কারও machine-এ নয়।
  • .claude/settings.json: একটি project-এর জন্য প্রযোজ্য এবং repository-তে commit করা থাকে, তাই যে কেউ project-টি clone করলে হুকটি পায়।
  • .claude/settings.local.json: একটি project-এর জন্য প্রযোজ্য, শুধু আপনার machine-এ।
  • Managed policy settings: organisation-wide, administrator দ্বারা নির্ধারিত।
  • hooks/hooks.json একটি plugin-এর মধ্যে: plugin enabled থাকা পর্যন্ত সক্রিয়।
  • Skill বা subagent frontmatter: সংশ্লিষ্ট component active থাকা পর্যন্ত সক্রিয়।

এই file-গুলোর হুক entry একে অপরকে override না করে merge হয়। কোনো project settings file আপনার user settings-এর হুক replace না করে তার সঙ্গে নিজের হুক যোগ করে। তাই একটি event-এ একাধিক file থেকে আসা একাধিক হুক থাকতে পারে। "disableAllHooks": true সেট করলে হুকগুলো বন্ধ হয়। তবে একটি ব্যতিক্রম আছে: managed policy settings-এর হুক চলতে থাকে, যদি না একই setting managed settings-এও প্রয়োগ করা হয়।

একটি session-এর মধ্যে /hooks চালালে বর্তমানে registered প্রতিটি হুক event অনুযায়ী group করে দেখায়। প্রতিটি হুকের source file এবং matcher-ও দেখানো হয়। এই menu read only। তাই হুক পরিবর্তন করতে settings file edit করুন। সাধারণত file watcher restart ছাড়াই পরিবর্তনটি শনাক্ত করে।

Claude Code-এ কোন কোন hook event আছে

Release 2.1.232-এ মোট thirty oneটি event রয়েছে। এগুলো SessionStart থেকে SessionEnd পর্যন্ত বিস্তৃত এবং compaction, subagent, worktree ও configuration file কভার করে। Server-এর কাজে সাধারণত এগুলোর কয়েকটি ব্যবহার করা হয়।

  • PreToolUse: কোনো tool call চালানোর আগে। এটিই tool call আটকে দিতে পারে।
  • PostToolUse: কোনো tool call সফল হওয়ার পরে। ব্যর্থ হলে PostToolUseFailure চালু হয়। তাই প্রতিটি ফলাফল দেখতে হলে hook-এ দুটিই দরকার।
  • PermissionRequest: কোনো tool call-এর জন্য permission সিদ্ধান্ত প্রয়োজন হলে। এই সময়েই approval prompt দেখানোর কথা।
  • UserPromptSubmit: prompt submit করার সময়, Claude সেটি process করার আগে। এই hook stdout-এ যা print করে, তা model-এর context-এ যোগ হয়।
  • SessionStart এবং SessionEnd: session-এর প্রতিটি সমাপ্তিতে। compaction-এর পরেও SessionStart চালু হয়, তখন matcher value হয় compact।
  • Stop: Claude response শেষ করলে। এটি প্রতি turn-এ একবার চালু হয়, সম্পূর্ণ task শেষ হলে একবার নয়।

প্রতিটি group-এ একটি matcher থাকে। এটি নির্ধারণ করে কোন occurrence-এ hook চালু হবে। Tool event-এ এটি tool name অনুযায়ী filter করে। তাই "Edit|Write" শুধু file edit-এর সময় চালু হয়, অন্য কোনো কাজে নয়। Matcher case-sensitive। Empty matcher প্রতিটি occurrence-এ চালু হয়। MCP (model context protocol) server-এর tool-এর নাম mcp__<server>__<tool> আকারে থাকে। তাই "mcp__github__.*" matcher ব্যবহার করলে শুধু একটি server-এর tool ধরা পড়ে এবং অন্যগুলো বাদ থাকে।

Stop hook লেখার আগে একটি গুরুত্বপূর্ণ বিষয় জানা দরকার। কোনো Stop hook block করলে model-কে আবার কাজ করতে পাঠানো হয়। পরপর আটবার block হলে Claude Code hook-টিকে override করে। Hook input-এর stop_hook_active field পড়ুন এবং সেটির মান true হলে exit 0 দিন। তা না হলে cap-এ পৌঁছানো পর্যন্ত hook চলতেই থাকবে।

স্ট্যান্ডার্ড ইনপুটে hook কী পায়

Claude যখন npm test চালাতে যাচ্ছে, তখন Bash-এর একটি PreToolUse hook স্ট্যান্ডার্ড ইনপুটে এটি পড়ে:

{
  "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 event-এ অতিরিক্তভাবে tool_name, tool_input এবং tool_use_id থাকে। অন্যান্য event-এ তাদের নিজস্ব field থাকে: UserPromptSubmit-এ prompt text থাকে, আর SessionStart-এ startup, resume, clear, compact বা fork-এর একটি source থাকে।

Shell script-এর ভিতরে এটি পড়ার প্রচলিত উপায় হলো jq। Minimal server image-এ এটি থাকে না। Ubuntu এবং Debian-এ sudo apt install -y jq ব্যবহার করে প্রথমে এটি install করুন।

একটি চলমান tool call-এ exit status-এর প্রভাব

এখানে তিনটি ফলাফল হতে পারে।

  • Exit 0 মানে আপনার hook কোনো আপত্তি জানায়নি। PreToolUse-এ এটি অনুমোদনের সমতুল্য নয় এবং স্বাভাবিক permission flow এখনও চলে। UserPromptSubmit ও SessionStart-এ stdout model-এর context-এ যোগ হয়।
  • Exit 2 যে event-গুলো block করা যায়, সেগুলোতে action আটকে দেয়; এর মধ্যে PreToolUse-ও রয়েছে। stderr-কে model-কে দেখানো কারণ হিসেবে ব্যবহার করা হয়। PostToolUse-এর মতো যেসব event block করা যায় না, সেগুলোতে block উপেক্ষা করা হয়। তবে stderr feedback হিসেবে model-এর কাছে পৌঁছায়।
  • অন্য যেকোনো exit code non-blocking error নির্দেশ করে। Action চলতে থাকে। Transcript-এ Failed with non-blocking status code: লেখাটির পর stderr-এর প্রথম line-সহ একটি hook error notice দেখা যায়।

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-এ JSON decision দেওয়ার সঙ্গে exit 2 মেশালে এমন একটি ফলাফল তৈরি হয়, যার ব্যাখ্যা আলাদাভাবে খুঁজে দেখতে হবে।

একটি event-এর সঙ্গে একাধিক hook মিলে গেলে সেগুলো parallel-এ চলে এবং প্রতিটি hook সম্পূর্ণ execution শেষ করে। একটি hook-এর deny তার sibling hook-গুলোর execution বন্ধ করে না। তাই একটি logging hook একই call-এর line লিখতে পারে, আর একটি guardrail hook সেই call প্রত্যাখ্যান করতে পারে। এরপর Claude Code উত্তরগুলো একত্র করে এবং deny, defer, ask, allow এই ক্রমে সবচেয়ে restrictive ফলাফলটি রাখে।

উদাহরণ 1: চলার আগে ধ্বংসাত্মক command ব্লক করুন

আপনার 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-এ এটি register করুন:

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-টি হাতে পরীক্ষা করুন। কারণ hook নিজের input-এ crash করলে সেটি fail open করে:

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

stderr-এ Blocked by policy: line এবং exit code হিসেবে 2 দেখতে পাওয়ার কথা। ls -la-এর মতো harmless command দিয়ে পরীক্ষা করুন। সে ক্ষেত্রে কোনো output এবং 0 exit code দেখতে পাওয়ার কথা নয়। একটি session-এ denied call-টি transcript-এ আপনার message-সহ reason হিসেবে দেখা যায়। Model সেই message পড়ে তার আচরণ পরিবর্তন করে।

একটি বৈশিষ্ট্যের কারণে এটি ব্যবহার করা মূল্যবান: PreToolUse hooks প্রতিটি permission mode-এ permission-mode check-এর আগে কার্যকর হয়। তাই bypassPermissions চালু থাকলেও deny কার্যকর থাকে। এই কারণেই Claude Code-এর auto mode এবং এর permission settings-এর সঙ্গে hook ব্যবহার করা উপযোগী। এতে prompts কম দেখানো হলেও hook কার্যকর হয়।

এটি কী, সে বিষয়ে বাস্তবসম্মত থাকুন। কোনো command string-এর ওপর pattern matching agent-এর অসতর্কতার বিরুদ্ধে একটি guardrail। তবে agent ইচ্ছাকৃতভাবে ভিন্নভাবে command লিখলে এটি সুরক্ষা-সীমা হিসেবে কাজ করে না, কারণ আপনার grep সেই form দেখতে নাও পারে। কঠোর নিয়ম permission system-এ এবং process যে account-এর অধীনে চলে, সেখানে প্রয়োগ করুন।

উদাহরণ 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 সফল হলে conversation-এ কিছু দেখায় না।

এখানে exit 2 কোনো পরিবর্তন বাতিল করে না। PostToolUse tool ইতিমধ্যে execute হওয়ার পরে চলে, তাই edit যেকোনো অবস্থাতেই disk-এ থাকে। exit 2 ব্যবহারের সুবিধা হলো, ruff check-এর output feedback হিসেবে model-এর কাছে পৌঁছায়। ফলে model পরের কাজে যাওয়ার পরিবর্তে সদ্য তৈরি করা error ঠিক করে। commit করার সময় শনাক্ত হওয়া lint failure এবং agent একই turn-এ মেরামত করা lint failure-এর মধ্যে এটাই পার্থক্য।

এখানে matcher-এর দুটি সীমাবদ্ধতা গুরুত্বপূর্ণ। shell command-এর মাধ্যমে পরিবর্তিত file Edit|Write দেখতে পায় না। Claude প্রায়ই Bash-এর মাধ্যমে file লেখে, তাই এই সীমাবদ্ধতা বাস্তব। প্রতি call-এ coverage চাইলে Bash-এর সঙ্গেও match করুন এবং script-কে git status --porcelain দিয়ে পরিবর্তিত file-এর তালিকা তৈরি করতে দিন। প্রতি turn-এ একবার coverage চাইলে পরিবর্তে scan-টি Stop hook-এ রাখুন।

উদাহরণ 3: অডিটের জন্য প্রতিটি tool call লগ করুন

PostToolUse-এ একটি খালি matcher সব tool-এর ক্ষেত্রে সক্রিয় হয়। রেকর্ডটি home directory-এর একটি ফাইলে না লিখে system journal-এ পাঠালে agent-এর নিজস্ব shell থেকে এটি পরিবর্তন করা যায় না:

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

journalctl -t claude-code -o cat | tail -n 5 দিয়ে লগটি পড়ুন। প্রতিটি tool call-এর জন্য একটি করে JSON line দেখতে পাবেন; সর্বশেষ কলটি শেষে থাকবে। কিছু দেখা না গেলে hook চালু হয়নি। নিচের troubleshooting section-এ এর কারণ নির্ণয়ের পদ্ধতি দেওয়া আছে।

ব্যর্থ call-গুলোও ধরতে PostToolUseFailure-এর অধীনে একই block যোগ করুন। কারণ PostToolUse শুধু সফলতার ক্ষেত্রে সক্রিয় হয়, অথচ সাধারণত ব্যর্থ command-টিই বেশি গুরুত্বপূর্ণ। আপনার home directory-এর একটি ফাইলে append না করে logger ব্যবহার করার কারণ ownership: hook agent-এর shell-এর মতো একই user হিসেবে চলে। তাই ওই user যে ফাইলে append করতে পারে, সেটি truncate-ও করতে পারে। journal নিজের account ব্যবহার করে systemd-journald লিখে।

একটি hook কতক্ষণ চলতে পারে

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

একটি command hook ডিফল্টভাবে 600 সেকেন্ড সময় পায়, অর্থাৎ দশ মিনিট। কিছু event এই সময়সীমা অনেক কমিয়ে দেয়। SessionEnd hook-গুলো নিজেদের মধ্যে মোট 1.5 সেকেন্ডের একটি budget ভাগ করে নেয়। তাই session শেষ হওয়ার সময়ের cleanup দ্রুত সম্পন্ন করতে হয়। তবে hook-এ দীর্ঘতর timeout সেট করলে এই shared budget-ও একই মানে বাড়ে, সর্বোচ্চ 60 সেকেন্ড পর্যন্ত।

সময়সীমায় পৌঁছালে hook বাতিল হয় এবং কোনো সিদ্ধান্ত দেয় না। PreToolUse guardrail-এর ক্ষেত্রে এর অর্থ হলো এটি কাজটি block করে না। Tool call স্বাভাবিক permission flow-তে এগিয়ে যায়। এই কারণে guardrail script ছোট রাখুন। এমন ধীর কাজের জন্য, যার জন্য কাউকে অপেক্ষা করতে হয় না, যেমন কোনো log অন্যত্র পাঠানো, "async": true সেট করুন। তাহলে tool call আটকে না রেখে hook background-এ চলে।

Hooks, rules file, skill এবং MCP server

চারটি বিষয় একে অপরের সঙ্গে গুলিয়ে যায়, কারণ এগুলো প্রত্যেকটিই agent-এর আচরণ পরিবর্তন করে। তবে suggestion হিসেবে গণ্য না হওয়ার বৈশিষ্ট্য কেবল একটির আছে।

একটি rules file (CLAUDE.md, অথবা .claude/rules/-এর অধীনে থাকা কোনো file) হলো model-এর context-এ লোড হওয়া text। এটি আচরণকে প্রভাবিত করে, কিন্তু কোনো কিছু enforce করে না। দীর্ঘ conversation, বড় diff এবং ব্যবহারকারীর নতুন request-এর সঙ্গে প্রতিযোগিতায় এর একটি line গুরুত্ব হারাতে পারে। আপনার লিখে রাখা নির্দেশনা agent উপেক্ষা করার পেছনে সাধারণত এই প্রক্রিয়াই কাজ করে।

একটি skill হলো instruction এবং script-এর folder, যা model প্রাসঙ্গিক মনে করলে লোড করে। এই বিচারই skill-এর মূল বৈশিষ্ট্য এবং সীমাবদ্ধতাও: সিদ্ধান্তটি এখনও model-ই নেয়। Ponytail-এর মতো skill-এ এই দুই দিকই দেখা যায়। এটি এমনভাবে পুরো task পরিচালনার পদ্ধতি নির্ধারণ করে, যা কোনো hook পারে না; তবে model skill-টি লোড করার সিদ্ধান্ত নিলেই তা কার্যকর হয়।

একটি MCP (model context protocol) server model-কে call করার জন্য নতুন tool দেয়। এটি agent যে সংস্থানগুলিতে পৌঁছাতে পারে, তার পরিসর বাড়ায়। তবে এটি agent-কে কোনো কিছু ব্যবহার করতেই বাধ্য করে না। এটি একটি আলাদা process, যেটি আপনাকে পরিচালনা করতে হয়। সেটি নিজেই একটি পৃথক কাজ: VPS-এ MCP server চালানো দেখুন।

চারটির মধ্যে hook-ই একমাত্র ব্যবস্থা, যা model-এর সিদ্ধান্ত ছাড়াই চলে। কোনো preference-এর জন্য rules file ব্যবহার করুন। Model-এর ক্ষেত্রে প্রযোজ্য হলে অনুসরণ করা উচিত এমন procedure-এর জন্য skill ব্যবহার করুন। যে step প্রতিবার অবশ্যই চলতে হবে, অথবা যে কাজটি কখনোই করা যাবে না, তার জন্য hook ব্যবহার করুন। কখন skill rules file-এর চেয়ে ভালো, সেটিসহ বিস্তারিত তুলনা skill, MCP এবং rules file-এর তুলনায় রয়েছে।

Plugin হলো পঞ্চম কোনো mechanism নয়; এটি packaging। এটি hook এবং skill-কে একটি installable unit-এ একত্র করে। এভাবেই একটি team প্রতিটি machine-এ একই guardrail deploy করতে পারে: Claude Code plugin কীভাবে কাজ করে দেখুন।

একটি shared VPS-এ security সিদ্ধান্ত

Hook হলো এমন code, যা agent trigger করে এবং Claude Code শুরু করা user হিসেবে চালায়। এটি সেই user-এর environment ও file permission উত্তরাধিকারসূত্রে পায়। Laptop-এ এটি workflow-এর বিষয়। কিন্তু কোনো agent unattended অবস্থায় VPS-এ চললে এটি security-এর বিষয়, যার 4টি বাস্তব দিক আছে।

Repository-এর hook হলো এমন code, যা আপনি লেখেননি। .claude/settings.json committed থাকে, তাই কোনো repository clone করে তার ভেতরে session শুরু করলে repository-এর সঙ্গে আসা hook register হতে পারে। Claude Code ওই folder-এর workspace trust dialog-এর মাধ্যমে project hook চালানোর অনুমতি নিয়ন্ত্রণ করে। অর্থাৎ trust গ্রহণ করার সময়ই আপনি hook চালানোর সিদ্ধান্ত নেন। আগে hooks block পড়ুন।

Hook সম্পূর্ণ tool input দেখতে পায়। একটি audit hook যদি tool_input log করে, তাহলে প্রতিটি command-এর প্রতিটি argument file-এ লিখে দেয়। এর মধ্যে command line-এ থাকা কোনো token-ও থাকতে পারে। তাই ওই log-কে secret-এর মতোই সুরক্ষিত রাখতে হবে। এটি AI agent-এর নাগালের বাইরে secret রাখার বৃহত্তর সমস্যার অংশ।

Hook model-এর context-এ লিখতে পারে। কোনো SessionStart বা UserPromptSubmit hook stdout-এ যা print করে, তা conversation-এ যোগ হয়। কোনো hook যদি বাইরের উৎস, issue tracker বা log file থেকে text pipe করে, তাহলে আপনি নিজে লিখেছেন এমনভাবে সেই untrusted text model-এর কাছে পাঠায়। একই VPS-এর অন্য Claude Code session থেকে কোনো note forward করা hook-ও একই কাজ করে। একটি agent-এর output-এর ওপর issue tracker-এর output-এর চেয়ে বেশি trust করার কারণ নেই। ওই stdout-কে output নয়, input হিসেবে বিবেচনা করুন।

Privilege-ই প্রকৃত নিয়ন্ত্রণ। Agent-কে একটি dedicated unprivileged user হিসেবে চালান, যার কেবল প্রয়োজনীয় sudo rules আছে। একটি PreToolUse deny রাখা উপকারী। এটি নকশাগতভাবে best effort। Reference-এ if filter সম্পর্কেও একই কথা বলা হয়েছে এবং hard deny প্রয়োজন হলে permission system ব্যবহার করতে বলা হয়েছে। Permission rules এবং process যে account-এর অধীনে চলে, চাপের পরিস্থিতিতেও কার্যকর থাকে মূলত এই দুটি বিষয়।

একটি বৈশিষ্ট্য সব configuration-এ প্রযোজ্য। PreToolUse hook প্রতিটি permission mode-এ permission-mode check-এর আগে চলে। তাই deny return করা hook bypassPermissions থাকলেও tool block করে। Hook permission rules যে কাজের অনুমতি দেয়, তা আরও সীমিত করতে পারে। কিন্তু তা permission rules শিথিল করতে পারে না।

আমার hook কেন চালু হচ্ছে না?

এটি ক্রমানুসারে পরীক্ষা করুন। প্রতিটি ধাপে আপনি যে লক্ষণটি বাস্তবে দেখবেন, তা উল্লেখ করা হয়েছে।

  • /hooks চালান এবং প্রত্যাশিত event-এর অধীনে hook-টি দেখা যাচ্ছে কি না পরীক্ষা করুন। মেনুতে hook না থাকলে সাধারণত settings file-এ JSON syntax error থাকে, কারণ trailing comma ও comment অনুমোদিত নয়; অথবা file-টি উপরের ছয়টি location-এর কোনো একটিতে নেই।
  • Matcher-এর সঙ্গে tool name হুবহু তুলনা করুন। Matcher-এ বড় হাতের ও ছোট হাতের অক্ষর আলাদা ধরা হয়। তাই "bash" কখনো Bash tool-এর সঙ্গে মেলে না।
  • উপরের example 1-এর মতো sample input দিয়ে script-টি হাতে চালান। প্রত্যাশিত নয় এমন exit code আপনার script-এর bug নির্দেশ করে। Claude Code এটিকে decision হিসেবে নয়, hook error হিসেবে report করে।
  • jq: command not found লেখা notice-এর অর্থ হলো ওই machine-এ jq অনুপস্থিত। আপনার নিজের script-এর জন্য command not found দেখা গেলে path resolve হয়নি। তাই ${CLAUDE_PROJECT_DIR} অথবা একটি absolute path ব্যবহার করুন। Script একেবারেই চালু না হলে সেটি সম্ভবত executable নয়।
  • Hook বৈধ JSON print করে, কিন্তু কিছুই ঘটে না। Shell-form hook sh -c-এর মাধ্যমে চলে। আপনার shell profile যদি banner print করে, সেই banner আপনার JSON-এর আগে যুক্ত হয়। ফলে stdout আর { দিয়ে শুরু হয় না। Claude Code পুরো output-টিকে plain text হিসেবে পড়ে এবং decision উপেক্ষা করে। Exit 0 হলে debug log ছাড়া অন্য কোথাও কোনো তথ্য report করা হয় না। Profile-এ থাকা যেকোনো echo এমনভাবে wrap করুন, যাতে সেটি শুধু interactive shell-এ চলে।
  • এখনও সমস্যা থাকলে claude --debug-file /tmp/claude.log দিয়ে session শুরু করুন এবং দ্বিতীয় terminal-এ tail -f /tmp/claude.log চালান। Debug log-এ কোন hook match করেছে, প্রতিটি hook কোন exit code দিয়েছে, এবং stdout ও stderr-এ কী লিখেছে—সব record হয়।

FAQ

Claude Code hook এবং CLAUDE.md instruction-এর মধ্যে পার্থক্য কী?

একটি CLAUDE.md instruction হলো model-এর context-এর অংশ। তাই এটি conversation এবং বর্তমান request-এর সঙ্গে মনোযোগের জন্য প্রতিযোগিতা করে, এবং model এগুলোর তুলনায় instruction-এর গুরুত্ব নির্ধারণ করতে পারে। একটি hook হলো shell command, যা Claude Code তার lifecycle-এর নির্দিষ্ট পর্যায়ে চালায়। তাই model কী সিদ্ধান্ত নিয়েছে তা নির্বিশেষে event ঘটলেই এটি চালু হয়। কোনো preference-এর জন্য instruction ব্যবহার করুন। যে ধাপ সব সময় চালাতেই হবে বা যে action কখনোই ঘটতে দেওয়া যাবে না, তার জন্য hook ব্যবহার করুন।

Claude Code-কে কীভাবে একটি নির্দিষ্ট shell command চালানো থেকে বিরত রাখব?

একটি PreToolUse hook নিবন্ধন করুন, যেখানে Bash matcher থাকবে। এটি .tool_input.command থেকে command পড়বে, stderr-এ কারণ লিখবে এবং 2 exit code দিয়ে শেষ হবে। Claude Code call বাতিল করে এবং model-কে আপনার কারণ দেখায়। এটি permission-mode check-এর আগে ঘটে, তাই bypassPermissions mode-এও deny কার্যকর থাকে। Command string-এর pattern matching একটি guardrail, security boundary নয়। একই command এমনভাবে লেখা যেতে পারে, যাতে pattern সেটি শনাক্ত না করে। তাই এর সঙ্গে permission rules এবং unprivileged account ব্যবহার করুন।

আমার hook বৈধ JSON print করে, কিন্তু কিছুই ঘটে না। কেন?

সবচেয়ে সাধারণ কারণ হলো আপনার shell profile। args field ছাড়া hook sh -c-এর মাধ্যমে চলে। কিছু profile প্রতিটি shell-এ একটি banner print করে। সেটি আপনার JSON-এর আগে stdout-এ চলে আসে। ফলে output আর { দিয়ে শুরু হয় না। Claude Code পুরো output-কে plain text হিসেবে ধরে এবং decision উপেক্ষা করে। exit 0 হলে transcript-এ কিছুই দেখানো হয় না। আপনার profile-এ থাকা যেকোনো echo-কে interactive-shell test দিয়ে guard করুন। এরপর claude --debug-file /tmp/claude.log থেকে debug log পড়ে পরিবর্তনটি কার্যকর হয়েছে কি না নিশ্চিত করুন।

Shared server-এ Claude Code hooks চালানো কি নিরাপদ?

Hooks Claude Code চালু করা user হিসেবে চলে এবং সেই user-এর file permission ব্যবহার করে। তাই hook ওই account যা কিছু করতে পারে, তা করতে পারে। বেশিরভাগ ঝুঁকি কমাতে দুটি অভ্যাস যথেষ্ট: agent-কে সীমিত sudo policy-সহ dedicated unprivileged account হিসেবে চালান, এবং কোনো repository-এর workspace trust dialog গ্রহণের আগে তার hooks block পড়ুন। কারণ project hooks .claude/settings.json-এর ভেতরে থাকে। কোনো hook চালাতে না চাইলে settings file-এ "disableAllHooks": true নির্ধারণ করুন।