Used by the manifests
| Feature | Claude Code | Codex | Where |
|---|---|---|---|
statusMessage on a command handler | yes | yes | Every handler in hooks/hooks.json and hooks/codex.json |
SessionStart matcher fork | yes | no: startup, resume, clear, compact | hooks/hooks.json |
if on a handler | yes | no | hooks/hooks.json shell hooks only |
if with a permission rule per handler, such as Bash(cat *), matched against each subcommand | yes | no | hooks/hooks.json: the heredoc-write handlers of check-edit, the jj handlers of check-bash, and the PowerShell handlers |
NotebookEdit input: notebook_path, cell_id, new_source, cell_type, edit_mode | yes | no | hooks/hooks.json PreToolUse, read by check-edit.mjs |
PowerShell tool with tool_input.command | yes | no | hooks/hooks.json PreToolUse, read by check-bash.mjs |
A matcher with characters outside letters, digits, _, -, |, and , is a regex, so mcp__.* matches every mcp__<server>__<tool> call | yes | yes: every matcher is a regex, and PreToolUse fires for MCP tools | PreToolUse in both manifests, read by check-tool-text.mjs |
PreToolUse on ExitPlanMode (plan, which Claude Code fills from the plan file), on TaskCreate and TaskUpdate (subject, description, activeForm) and on AskUserQuestion (questions[].question, options[].label, options[].description) | yes | no | hooks/hooks.json, read by check-tool-text.mjs |
PostToolUse input with duration_ms, and decision: "block", which adds the reason next to the tool result | yes | not used | hooks/hooks.json PostToolUse on Bash|PowerShell, read by check-shell-writes.mjs |
PostToolUse updatedToolOutput replaces the Bash output (stdout, stderr, interrupted, isImage, noOutputExpected) that Claude sees; a passing run fires PostToolUse and a failed run fires PostToolUseFailure (checked live with Claude Code 2.1.304 on 2026-10-21) | yes | not used | hooks/hooks.json PostToolUse on Bash, read by post-test-filter.mjs |
SubagentHandback tool carries the subagent report in tool_input.message (auto mode, v2.1.271 or later) | yes | no | hooks/hooks.json PreToolUse |
SubagentStop fires for internal agents, with an empty agent_type when the session has no --agent | yes | no | check-reply.mjs skips them |
Plugin MCP server file with ${CLAUDE_PLUGIN_ROOT} in args | yes | no: see below | .mcp.json (Claude Code), .codex-mcp.json (Codex) |
${CLAUDE_PLUGIN_ROOT} in plugin skill text | yes | not documented | skills/*/SKILL.md, with a relative path for Codex |
interface.composerIcon and interface.logo | n/a | yes | .codex-plugin/plugin.json |
displayName, homepage, repository, and keywords | yes | n/a | .claude-plugin/plugin.json |
Directory listing fields icon, documentationUrl, supportUrl, and privacyPolicyUrl, which Anthropic’s directory reads and Claude Code ignores (checked 2026-10-21) | yes | n/a | .claude-plugin/plugin.json |
Marketplace description | yes | n/a | .claude-plugin/marketplace.json |
The hooks docs say a handler defined in more than one settings file runs once. They do not say whether two handlers in one plugin with the same command and different if rules both run for one call. When their rules can overlap, check-edit and check-bash claim the call’s tool_use_id, so a second run exits without output.