skills/create-hook/SKILL.md
Build Claude Code hooks
npx skillsauth add laststance/skills create-hookInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
When running this skill in Codex, translate Claude Code-only primitives before acting: AskUserQuestion -> chat/request_user_input, TodoWrite -> update_plan, Task/TaskCreate/TeamCreate/SendMessage -> spawn_agent/send_input/wait_agent when available and allowed, and EnterPlanMode/ExitPlanMode -> a concise chat plan plus explicit approval.
Resolve Read/Write/Edit/Bash/WebSearch/WebFetch to Codex file/shell/web tools, and map ~/.claude/... paths to ~/.agents/... or ~/.codex/... unless the task explicitly targets Claude Code.
When running this skill in Cursor Agent, translate Claude Code-only primitives before acting: AskUserQuestion -> AskQuestion; TodoWrite -> Cursor TodoWrite or an equivalent checklist; Task/TaskCreate/TeamCreate/SendMessage/multi-agent flows -> Cursor Task (subagents), parallel Tasks, or run_in_background when allowed (TeamCreate/SendMessage may have no exact match); EnterPlanMode/ExitPlanMode -> Plan mode (SwitchMode / CreatePlan) plus explicit user approval.
Resolve Read/Write/Edit/StrReplace/Bash/web/search/MCP via Cursor Composer or Agent equivalents. MCP names written as mcp__server__tool typically map to call_mcp_tool with configured server identifiers. Map ~/.claude/... to ~/.cursor/skills/, .cursor/skills/, and .cursor/rules/ unless the task explicitly targets Claude Code.
Build reliable Claude Code hooks that survive real-world event firing — not just clean dry-runs.
additionalContext injection, context recovery, tool gating, notifications~/.claude/settings.json hooks blockecho '{}'; exit 0. A broken hook must never block the user.printf '%s' "$PAYLOAD" | bash hook.sh can produce different output than the real hook invocation (locale, TTY, stdin semantics, environment). Validate with a real trigger before declaring done.mktemp + mv on the same filesystem, so concurrent sessions cannot double-read or corrupt.shasum -a 256 | cut -c1-16 of $CWD gives a stable, collision-resistant filename.1. Pick your event + matcher. See references/hook-events.md for the catalog. Key empirically-validated ones:
| Event | Matcher | Fires when | Stdin has |
|-------|---------|------------|-----------|
| SessionStart | startup | first launch | session_id, cwd, transcript_path |
| SessionStart | resume | claude -r | same |
| SessionStart | clear | user types /clear | same |
| SessionStart | compact | after auto/manual compaction | same |
| PostCompact | (none) | after /compact or auto-compact | + compact_reason |
| UserPromptSubmit | (none) | user sends a message | + prompt |
| PreToolUse | tool name regex | before a tool call | + tool_name, tool_input |
2. Copy the skeleton from templates/hook-skeleton.sh. It has the mandatory boilerplate: set -uo pipefail, dependency checks, stdin parsing, logger, fail-safe exits.
3. Wire it in ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"matcher": "clear",
"hooks": [
{ "type": "command", "command": "$HOME/.claude/hooks/my-hook.sh" }
]
}
]
}
}
Omit matcher for events that don't support it. Multiple hooks per event run in parallel — order is not guaranteed.
4. Test live, not via dry-run. See references/testing-hooks.md. The TL;DR: trigger the real event (/clear, /compact, a real prompt) and inspect both your log AND the visible effect on Claude's behavior. Subprocess pipe testing catches syntax errors but misses integration issues.
Copy this checklist and tick items:
- [ ] 1. Pick event + matcher (references/hook-events.md)
- [ ] 2. Draft input schema (what stdin fields will I read?)
- [ ] 3. Draft output shape ({} vs hookSpecificOutput)
- [ ] 4. Start from templates/hook-skeleton.sh
- [ ] 5. Add fail-safe wrappers on every failure path
- [ ] 6. Add timestamped logging to ~/.claude/hooks/logs/<name>.log
- [ ] 7. Register in ~/.claude/settings.json (NOT settings.local.json if shared)
- [ ] 8. Live-trigger the event (real /clear, real prompt, etc.)
- [ ] 9. Confirm Claude's observable behavior changed (not just the log)
- [ ] 10. Check logs for the expected [INFO] lines
Logs can mislead. [INFO] injected size=9851 means the script's echo/jq -nc ran — not that Claude Code accepted the JSON. For SessionStart-with-additionalContext, confirm by asking the new session a question only the injected context could answer.
additionalContext has a ~10k char budget. Truncate with a trailing marker so you (and Claude) can tell when it happened. See references/additional-context.md.
matcher scopes the event, type: "command" runs the hook. A missing or mistyped matcher silently skips execution — no error, no log. Always echo + timestamp at the top of the script to confirm firing.
Exit codes matter for PreToolUse only. Exit 2 blocks the tool call; any other exit is treated as success. Other events ignore exit codes beyond != 0 = failure.
Don't use set -e. Use set -uo pipefail + explicit || { log; echo '{}'; exit 0; } on every risky command. This keeps the fail-safe contract intact.
settings.json is gitignored in most .claude setups. Edits won't appear in git status. To confirm your changes persisted, stat the file or grep the disk copy directly.
hookSpecificOutput.additionalContext injection mechanics, size limits, verificationThis skill is intentionally incomplete. As new event types are validated empirically, append to references/hook-events.md with a maturity marker:
✅ empirically verified — real trigger produced real effect; include date + session evidence📖 docs-only — haven't personally triggered; cite the doc URL❓ suspected — inferred from source or analogy; label clearlyKeep SKILL.md stable; push event-specific knowledge into references. The decision framework here applies to every event type — only the schemas differ.
testing
Cited research briefs
development
Daily coding habit prompts JP
development
React core deep-dive JP
data-ai
Copy last agent reply