claude_hooks 1.0.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/skills/ci-monitoring/SKILL.md +103 -0
  3. data/.agents/skills/run-tests/SKILL.md +45 -0
  4. data/.claude/auto-fix.md +7 -0
  5. data/.claude/settings.local.json +21 -0
  6. data/AGENTS.md +46 -0
  7. data/CHANGELOG.md +93 -0
  8. data/README.md +159 -21
  9. data/docs/1.1.0_UPGRADE_GUIDE.md +269 -0
  10. data/docs/API/COMMON.md +7 -0
  11. data/docs/API/CONFIG_CHANGE.md +39 -0
  12. data/docs/API/CWD_CHANGED.md +34 -0
  13. data/docs/API/ELICITATION.md +44 -0
  14. data/docs/API/ELICITATION_RESULT.md +43 -0
  15. data/docs/API/FILE_CHANGED.md +37 -0
  16. data/docs/API/INSTRUCTIONS_LOADED.md +30 -0
  17. data/docs/API/MESSAGE_DISPLAY.md +38 -0
  18. data/docs/API/NOTIFICATION.md +1 -0
  19. data/docs/API/PERMISSION_DENIED.md +37 -0
  20. data/docs/API/PERMISSION_REQUEST.md +199 -0
  21. data/docs/API/POST_COMPACT.md +30 -0
  22. data/docs/API/POST_TOOL_BATCH.md +39 -0
  23. data/docs/API/POST_TOOL_USE.md +1 -0
  24. data/docs/API/POST_TOOL_USE_FAILURE.md +31 -0
  25. data/docs/API/PRE_COMPACT.md +10 -2
  26. data/docs/API/PRE_TOOL_USE.md +9 -1
  27. data/docs/API/SETUP.md +37 -0
  28. data/docs/API/STOP_FAILURE.md +19 -0
  29. data/docs/API/SUBAGENT_START.md +40 -0
  30. data/docs/API/TASK_COMPLETED.md +43 -0
  31. data/docs/API/TASK_CREATED.md +43 -0
  32. data/docs/API/TEAMMATE_IDLE.md +40 -0
  33. data/docs/API/USER_PROMPT_EXPANSION.md +43 -0
  34. data/docs/API/WORKTREE_CREATE.md +39 -0
  35. data/docs/API/WORKTREE_REMOVE.md +33 -0
  36. data/docs/PROMPT_BASED_HOOKS.md +386 -0
  37. data/docs/external/claude-hooks-reference.md +2846 -18
  38. data/docs/mitts/ideabox.md +12 -0
  39. data/docs/mitts/prd.md +417 -0
  40. data/docs/mitts/setup.md +303 -0
  41. data/docs/mitts/task.md +44 -0
  42. data/lib/claude_hooks/base.rb +30 -1
  43. data/lib/claude_hooks/config_change.rb +28 -0
  44. data/lib/claude_hooks/cwd_changed.rb +28 -0
  45. data/lib/claude_hooks/elicitation.rb +61 -0
  46. data/lib/claude_hooks/elicitation_result.rb +57 -0
  47. data/lib/claude_hooks/file_changed.rb +41 -0
  48. data/lib/claude_hooks/instructions_loaded.rb +23 -0
  49. data/lib/claude_hooks/message_display.rb +58 -0
  50. data/lib/claude_hooks/notification.rb +5 -1
  51. data/lib/claude_hooks/output/base.rb +48 -1
  52. data/lib/claude_hooks/output/config_change.rb +47 -0
  53. data/lib/claude_hooks/output/cwd_changed.rb +42 -0
  54. data/lib/claude_hooks/output/elicitation.rb +67 -0
  55. data/lib/claude_hooks/output/elicitation_result.rb +63 -0
  56. data/lib/claude_hooks/output/file_changed.rb +42 -0
  57. data/lib/claude_hooks/output/instructions_loaded.rb +19 -0
  58. data/lib/claude_hooks/output/message_display.rb +49 -0
  59. data/lib/claude_hooks/output/permission_denied.rb +42 -0
  60. data/lib/claude_hooks/output/permission_request.rb +120 -0
  61. data/lib/claude_hooks/output/post_compact.rb +18 -0
  62. data/lib/claude_hooks/output/post_tool_batch.rb +51 -0
  63. data/lib/claude_hooks/output/post_tool_use.rb +12 -0
  64. data/lib/claude_hooks/output/post_tool_use_failure.rb +42 -0
  65. data/lib/claude_hooks/output/pre_compact.rb +34 -3
  66. data/lib/claude_hooks/output/pre_tool_use.rb +26 -2
  67. data/lib/claude_hooks/output/session_start.rb +30 -8
  68. data/lib/claude_hooks/output/setup.rb +42 -0
  69. data/lib/claude_hooks/output/stop.rb +15 -0
  70. data/lib/claude_hooks/output/stop_failure.rb +19 -0
  71. data/lib/claude_hooks/output/subagent_start.rb +42 -0
  72. data/lib/claude_hooks/output/task_completed.rb +18 -0
  73. data/lib/claude_hooks/output/task_created.rb +18 -0
  74. data/lib/claude_hooks/output/teammate_idle.rb +18 -0
  75. data/lib/claude_hooks/output/user_prompt_expansion.rb +51 -0
  76. data/lib/claude_hooks/output/worktree_create.rb +60 -0
  77. data/lib/claude_hooks/output/worktree_remove.rb +19 -0
  78. data/lib/claude_hooks/permission_denied.rb +41 -0
  79. data/lib/claude_hooks/permission_request.rb +63 -0
  80. data/lib/claude_hooks/post_compact.rb +23 -0
  81. data/lib/claude_hooks/post_tool_batch.rb +59 -0
  82. data/lib/claude_hooks/post_tool_use.rb +15 -1
  83. data/lib/claude_hooks/post_tool_use_failure.rb +45 -0
  84. data/lib/claude_hooks/pre_compact.rb +7 -0
  85. data/lib/claude_hooks/pre_tool_use.rb +34 -1
  86. data/lib/claude_hooks/session_start.rb +28 -0
  87. data/lib/claude_hooks/setup.rb +25 -0
  88. data/lib/claude_hooks/stop.rb +17 -0
  89. data/lib/claude_hooks/stop_failure.rb +27 -0
  90. data/lib/claude_hooks/subagent_start.rb +23 -0
  91. data/lib/claude_hooks/subagent_stop.rb +5 -1
  92. data/lib/claude_hooks/task_completed.rb +35 -0
  93. data/lib/claude_hooks/task_created.rb +35 -0
  94. data/lib/claude_hooks/teammate_idle.rb +23 -0
  95. data/lib/claude_hooks/user_prompt_expansion.rb +45 -0
  96. data/lib/claude_hooks/version.rb +1 -1
  97. data/lib/claude_hooks/worktree_create.rb +27 -0
  98. data/lib/claude_hooks/worktree_remove.rb +19 -0
  99. data/lib/claude_hooks.rb +42 -0
  100. metadata +76 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d82c5deaa5426bc2ca898300f7772fa58c56a2998fd00af4eb1a93de25d3336e
4
- data.tar.gz: ffde711d996ad0f43f7f75a3002f5a1e74e84f28ed2197ddf9c53c8b982a5152
3
+ metadata.gz: c1803cc66c5f4c6e0d2ca706c4106654a2aa961e8a8e968d3198acab8ff95a7d
4
+ data.tar.gz: 5dba30b7bce90d4a420cb1c83be1b9e2e2c3a1b0b1dfc42f47b0edd01e08941c
5
5
  SHA512:
6
- metadata.gz: 8256eeed53e717873271ea94b9d85610e741ade7d6009b41a9df6eccc8ea58ec4af3b4ab8883d8dfba3d0ef849afdcae6939786d7cb7ad4d000dd7dc749a73ab
7
- data.tar.gz: 306144f71bd099d70df514ee21eb9e63a11389562322927465b1aecff447c78eaafc602aa973db8a925bf0885c4e212fbcbfacb410c6d34c30a7b56eb9e51145
6
+ metadata.gz: f5b394c9005bea73bdb66db595e3783fc7cb2e67889dbf2923b945899bd8c907edf0df241a534ac4a2679c887a07538ec9e1d65d58d24f2b9313cded33e5ca18
7
+ data.tar.gz: b9867904501454ef867f5be72a0c82e63de3d7bcfdfe6981c8cfbbf946fdd50cc2968671dfd341a1811736da51302c67e412e09302e295b61b30abca25790b23
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: ci-monitoring
3
+ description: >
4
+ How to wait for GitHub Actions CI to finish on a PR after pushing commits,
5
+ without hanging forever or giving up too early. Use this skill whenever you
6
+ have pushed to a PR branch and need to confirm the checks pass before
7
+ reporting back or approving.
8
+ triggers:
9
+ - ci
10
+ - checks
11
+ - status check
12
+ - wait for ci
13
+ - green
14
+ - pipeline
15
+ ---
16
+
17
+ # Waiting for CI on a PR
18
+
19
+ After you push commits to a PR branch, the PR's checks (`test.yml`, `ruby-gem.yml`)
20
+ re-run. You often need to confirm they pass before you comment "done" or before a
21
+ review approves. This is easy to get wrong in two opposite ways — hanging forever,
22
+ or declaring success while checks are still pending. Follow this pattern.
23
+
24
+ ## Do NOT use the blocking watchers
25
+
26
+ These hang until completion with no timeout and will burn the whole run's time
27
+ budget (the agent's bash step is capped at ~10 minutes):
28
+
29
+ ```bash
30
+ gh run watch # ❌ blocks indefinitely
31
+ gh pr checks --watch # ❌ blocks indefinitely
32
+ ```
33
+
34
+ ## Poll `statusCheckRollup` in a bounded foreground loop
35
+
36
+ Query the PR's aggregated check state and sleep between polls. Use
37
+ `statusCheckRollup`, **not** `gh pr checks --required`: the latter only returns
38
+ contexts that are *already registered*, so a check that registers late (a matrix
39
+ job, or a summary job with a long `needs:` list) can make it exit green early and
40
+ miss a failure.
41
+
42
+ ```bash
43
+ PR=<pr-number>
44
+ for i in $(seq 1 9); do
45
+ # One JSON blob with every check's name, status, and conclusion.
46
+ rollup=$(gh pr view "$PR" --json statusCheckRollup \
47
+ --jq '.statusCheckRollup[] | {name: .name, status: .status, conclusion: .conclusion}')
48
+
49
+ # Are any checks still running / queued?
50
+ pending=$(echo "$rollup" | jq -sr '[.[] | select(.status != "COMPLETED")] | length')
51
+
52
+ if [ "$pending" -eq 0 ]; then
53
+ echo "All checks completed."
54
+ break
55
+ fi
56
+ echo "Attempt $i/9: $pending check(s) still running; sleeping 60sâ€Ļ"
57
+ sleep 60
58
+ done
59
+ ```
60
+
61
+ That is 9 × 60s = ~9 minutes, which fits inside the bash step cap. If checks are
62
+ still pending after the loop, say so explicitly ("CI was still running when I
63
+ checked — see the PR checks tab") rather than claiming success.
64
+
65
+ ## Decide pass/fail from the final rollup
66
+
67
+ A check passed only when `status == "COMPLETED"` **and**
68
+ `conclusion == "SUCCESS"` (or `NEUTRAL`/`SKIPPED`). Treat `FAILURE`,
69
+ `TIMED_OUT`, `CANCELLED`, `ACTION_REQUIRED`, or `STALE` as failing.
70
+
71
+ ```bash
72
+ failed=$(gh pr view "$PR" --json statusCheckRollup \
73
+ --jq '[.statusCheckRollup[]
74
+ | select(.conclusion != null and .conclusion != "SUCCESS"
75
+ and .conclusion != "NEUTRAL" and .conclusion != "SKIPPED")]
76
+ | length')
77
+ if [ "$failed" -gt 0 ]; then
78
+ echo "CI failed — inspect the failing job before reporting done."
79
+ fi
80
+ ```
81
+
82
+ Do **not** treat `mergeStateStatus == BLOCKED` as a CI failure — it's a catch-all
83
+ (branch protection, pending review, etc.), not a check result.
84
+
85
+ ## When a check fails
86
+
87
+ - Read the failing job's logs (`gh run view <run-id> --log-failed`) and fix the
88
+ cause. For this Ruby gem, most failures are the test suite — see the
89
+ `run-tests` skill.
90
+ - Re-run only the failed jobs if the failure looks transient (rare here):
91
+ `gh run rerun <run-id> --failed`.
92
+ - Never mark a PR done or approve over red CI. If you approve, the empty-body
93
+ approval reads as endorsing a broken build.
94
+
95
+ ## Notes specific to this repo
96
+
97
+ - Relevant workflows on a PR: **`test.yml`** (Ruby 3.2 + 3.3 matrix — the one that
98
+ matters for correctness) and **`ruby-gem.yml`** (its publish job is gated on
99
+ `push` to `main`, so on a PR it only builds — a failure there is still worth
100
+ reading).
101
+ - A PR the bot opened may **not** auto-run these workflows: the built-in
102
+ `GITHUB_TOKEN` does not retrigger other workflows. If the checks tab is empty on
103
+ a bot-opened PR, that's expected, not a hang — note it and move on.
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: run-tests
3
+ description: >
4
+ How to install dependencies and run the claude_hooks test suite.
5
+ Use this skill whenever you need to verify that code changes pass the tests
6
+ before committing.
7
+ triggers:
8
+ - test
9
+ - tests
10
+ - spec
11
+ - run_all_tests
12
+ ---
13
+
14
+ # Running the claude_hooks Test Suite
15
+
16
+ This is a **Ruby gem**. Always run the full test suite after any code change and
17
+ before committing.
18
+
19
+ ## Install dependencies
20
+
21
+ ```bash
22
+ bundle install
23
+ ```
24
+
25
+ ## Run all tests
26
+
27
+ ```bash
28
+ ruby test/run_all_tests.rb
29
+ ```
30
+
31
+ The suite must exit 0 before you commit. All files matching `test/test_*.rb` are
32
+ included automatically.
33
+
34
+ ## Adding or updating tests
35
+
36
+ - Place new test files in `test/` named `test_<feature>.rb`.
37
+ - Use plain `Test::Unit` / `Minitest` — no RSpec.
38
+ - Run the full suite, not just the new file, to catch regressions.
39
+
40
+ ## Common failure patterns
41
+
42
+ - **Missing gem**: run `bundle install` again; check `Gemfile.lock` was updated.
43
+ - **LoadError**: verify the `require` path matches the file under `lib/`.
44
+ - **Unexpected output**: the hooks write to `$stderr`; redirect if your test
45
+ captures stdout only.
@@ -0,0 +1,7 @@
1
+ Context: This gem is a wrapper around Claude Code Hooks system, providing a Ruby DSL to define hooks. The github repository has an automated system through github actions that automatically (weekly) creates a diff of the last known state of Claude Code's hook documentation vs the current (online) Claude Code's hooks documentation. It then generates a diff between those and create an issue with the diff.
2
+
3
+ Intended goal: Take all those issues and all those diffs and evaluate if the gem / code needs to be updated to match the changes made by anthropic to their Claude Code Hooks system.
4
+
5
+ First step: Retrieve all the issues at: https://github.com/gabriel-dehan/claude_hooks/issues
6
+ And assess which issues may implement things that need to be changed (a lot of issues contain useless diff, with wording changes, link changes, etc that are not relevant).
7
+ Second step: Take those relevant issues and look at the diff, then compare it with the current codebase / readme / documentation to see if there is a mismatch Third step: Create a plan to tackle those changes
@@ -0,0 +1,21 @@
1
+ {
2
+ "env": {
3
+ "CLAUDE_CODE_SUBAGENT_MODEL": "sonnet"
4
+ },
5
+ "permissions": {
6
+ "allow": [
7
+ "Read(//Users/gdehan/**)",
8
+ "Read(//Users/gdehan/.config/**)",
9
+ "Bash(herdr --help)",
10
+ "Bash(herdr config *)",
11
+ "Bash(herdr api *)",
12
+ "Bash(python3 -m json.tool)",
13
+ "Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\(list\\(d.keys\\(\\)\\)\\)\")",
14
+ "Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\(json.dumps\\(list\\(d.keys\\(\\)\\), indent=2\\)\\)\")",
15
+ "Bash(python3 -c ' *)",
16
+ "Bash(herdr server *)",
17
+ "WebFetch(domain:opencode.ai)",
18
+ "Bash(ruby *)"
19
+ ]
20
+ }
21
+ }
data/AGENTS.md ADDED
@@ -0,0 +1,46 @@
1
+ # Agent guidance — claude_hooks
2
+
3
+ This file is loaded by the coding agents that run in this repo's GitHub Actions
4
+ bots. It is picked up via `load_available_skills` (which internally calls
5
+ `load_project_skills`, the loader that ingests `AGENTS.md`) in
6
+ `agent_task.py` (issue and PR bots) and by the `pr-review` composite action.
7
+ Keep it short and factual.
8
+
9
+ ## What this project is
10
+
11
+ `claude_hooks` is a **Ruby gem** — a DSL for creating [Claude Code](https://docs.claude.com/en/docs/claude-code)
12
+ hooks (logging, security checks, workflow automation) with composable hook scripts.
13
+ Pure Ruby, no Rails. Public repo: https://github.com/gabriel-dehan/claude_hooks
14
+
15
+ ## Layout
16
+
17
+ - `lib/claude_hooks/` — the gem. One file per hook type (`pre_tool_use.rb`,
18
+ `post_tool_use.rb`, `user_prompt_submit.rb`, `session_start.rb`, etc.), plus
19
+ `base.rb`, `configuration.rb`, `cli.rb`, `logger.rb`, `version.rb`.
20
+ - `test/` — plain Ruby tests (`test_*.rb`), no RSpec.
21
+ - `docs/` — human docs, including `docs/mitts/` for the bot setup itself.
22
+
23
+ ## Running tests
24
+
25
+ ```bash
26
+ bundle install
27
+ ruby test/run_all_tests.rb
28
+ ```
29
+
30
+ Run this before committing any code change; it must pass. Add or update tests in
31
+ `test/` when you change behavior.
32
+
33
+ ## Conventions
34
+
35
+ - Match the surrounding code's style; this is a small, idiomatic Ruby gem.
36
+ - Keep changes minimal and focused on the request.
37
+ - Follow SemVer; note user-facing changes in `CHANGELOG.md`. `v1.0.0` introduced
38
+ breaking changes — check the changelog before assuming an API.
39
+ - Never commit secrets; never modify `.github/workflows/` permissions or CI secrets
40
+ unless the task is explicitly about the workflows.
41
+
42
+ ## For PR review specifically
43
+
44
+ Verify that behavior changes come with tests and that `ruby test/run_all_tests.rb`
45
+ would still pass. Flag anything that breaks the documented DSL API without a
46
+ CHANGELOG note.
data/CHANGELOG.md CHANGED
@@ -5,6 +5,99 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.0] - 2026-07-14
9
+
10
+ ### Added
11
+
12
+ - **20 new hook event classes** bringing full parity with the Claude Code Hooks spec v1.2.0 (30 events total):
13
+ - **Context-only**: `Setup`, `SubagentStart`
14
+ - **Blocking (top-level `decision`)**: `UserPromptExpansion`, `PostToolBatch`, `ConfigChange`
15
+ - **Blocking (exit-2 / `continue:false`)**: `TaskCreated`, `TaskCompleted`, `TeammateIdle`
16
+ - **Non-blocking**: `PostToolUseFailure`, `StopFailure`, `PostCompact`, `CwdChanged`, `FileChanged`, `InstructionsLoaded`, `WorktreeRemove`
17
+ - **JSON-API special**: `PermissionDenied`, `Elicitation`, `ElicitationResult`, `WorktreeCreate`
18
+ - **Display**: `MessageDisplay`
19
+
20
+ - **New common input readers** (available on all hook instances):
21
+ - `prompt_id`, `agent_id`, `agent_type`, `effort`
22
+ - `permission_mode` now documents `'auto'` as a valid value
23
+
24
+ - **`terminal_sequence!(seq)`** builder on all hooks; `terminal_sequence` accessor on output objects; `terminalSequence` (last non-nil wins) in `Output::Base.merge`
25
+
26
+ - **`PreToolUse` additions**: `defer_permission!`, `update_input!(hash)`, `add_additional_context!`; new output accessor `deferred?`
27
+
28
+ - **`PostToolUse` additions**: `update_tool_output!(value)`, `update_mcp_tool_output!(value)`; output accessors `updated_tool_output`, `updated_mcp_tool_output`, `output_updated?`
29
+
30
+ - **`SessionStart` additions**: builders `session_title!`, `initial_user_message!`, `watch_paths!(array)`, `reload_skills!(bool)`; matching output accessors `session_title`, `initial_user_message`, `watch_paths`, `reload_skills?`; input readers `model`, `session_title`
31
+
32
+ - **`Stop` / `SubagentStop` additions**: input readers `last_assistant_message`, `background_tasks`, `session_crons`; `add_additional_context!` builder + `output.additional_context` accessor; `SubagentStop` gains `agent_transcript_path`
33
+
34
+ - **`PreCompact` additions**: `block!(reason)` builder; `Output::PreCompact` gains `decision`, `reason`, `blocked?` accessors
35
+
36
+ - **`PostToolBatch` convenience**: `succeeded_calls` / `failed_calls` partitions over `tool_calls` (success derived from the entry's `tool_response`)
37
+
38
+ ### Changed
39
+
40
+ - **`PermissionRequest` emitted JSON shape** — builders now emit `hookSpecificOutput.decision = { behavior, message?, updatedInput?, updatedPermissions?, interrupt? }` instead of flat `permissionDecision`/`permissionDecisionReason` keys. **Backward-compatible: no hook code changes required** — builder signatures (`allow_permission!`, `deny_permission!`, `update_input_and_allow!`) are unchanged; read-side accessors (`permission_decision`, `allowed?`, `denied?`, etc.) retain legacy fallback for any stored flat JSON.
41
+
42
+ - **`PreToolUse` merge precedence** corrected to `deny > defer > ask > allow` (was `deny > ask > allow`).
43
+
44
+ ### Notes
45
+
46
+ - All changes are additive — existing hooks continue to work without modification
47
+ - Ships as minor `1.2.0` (no source-level breaks for users)
48
+ - Total hook event classes: 30 (added 20 new classes, including `MessageDisplay`)
49
+ - All 13 test files pass (0 failures, 0 errors)
50
+
51
+ ## [1.1.0] - 2026-01-04
52
+
53
+ ### Added
54
+
55
+ - **New PermissionRequest hook type** - Handles permission request events from Claude Code
56
+ - New `ClaudeHooks::PermissionRequest` hook class
57
+ - New `ClaudeHooks::Output::PermissionRequest` output class
58
+ - Methods: `allow_permission!`, `deny_permission!`, `update_input_and_allow!`
59
+ - Semantic helpers: `allowed?`, `denied?`, `input_updated?`
60
+ - Exit code 0 with stdout (JSON API pattern)
61
+ - Merge logic: deny > allow (most restrictive wins)
62
+
63
+ - **permission_mode field** - Added to all hooks as common input field
64
+ - Values: `default`, `plan`, `acceptEdits`, `dontAsk`, `bypassPermissions`
65
+ - Accessible via `permission_mode` method on all hook instances
66
+ - Defaults to `'default'` when not provided
67
+ - Supports both snake_case (`permission_mode`) and camelCase (`permissionMode`)
68
+
69
+ - **tool_use_id field** - Added to PreToolUse and PostToolUse hooks
70
+ - Unique identifier for each tool use event (e.g., `"toolu_01ABC123..."`)
71
+ - Accessible via `tool_use_id` method
72
+ - Supports both snake_case and camelCase input
73
+
74
+ - **notification_type field** - Added to Notification hook
75
+ - Values: `permission_prompt`, `idle_prompt`, `auth_success`, `elicitation_dialog`
76
+ - Accessible via `notification_type` method
77
+ - Enables filtering notifications by type using matchers
78
+
79
+ - **updatedInput support** - Added to PreToolUse hook
80
+ - New method: `update_tool_input!(hash)` for modifying tool input before execution
81
+ - Output helpers: `input_updated?`, `updated_input`
82
+ - Automatically sets `permissionDecision` to `'allow'`
83
+ - Merge logic: last updatedInput wins (most recent transformation)
84
+ - Works seamlessly with existing approval/denial methods
85
+
86
+ ### Changed
87
+
88
+ - Updated all hook classes to include new input fields where applicable
89
+ - Enhanced PreToolUse output class with updatedInput merge logic
90
+ - Updated main module to require PermissionRequest classes
91
+ - Updated output factory to support PermissionRequest hook type
92
+
93
+ ### Notes
94
+
95
+ - **All changes are backward compatible** - Existing hooks continue to work without modification
96
+ - New fields are optional with sensible defaults
97
+ - No breaking changes to existing APIs
98
+ - Aligns with Anthropic's Claude Code Hooks documentation as of January 2026
99
+ - Total hook types: 10 (added PermissionRequest)
100
+
8
101
  ## [1.0.2] - 2026-01-04
9
102
 
10
103
  ### Fixed
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Ruby DSL for Claude Code hooks
2
2
 
3
3
  > [!IMPORTANT]
4
- > v1.0.0 just released and is introducing breaking changes. Please read the [CHANGELOG](CHANGELOG.md) for more information.
4
+ > v1.2.0 just released and is introducing a great number of changes. Please read the [CHANGELOG](CHANGELOG.md) for more information.
5
5
 
6
6
 
7
7
  A Ruby DSL (Domain Specific Language) for creating Claude Code hooks. This will hopefully make creating and configuring new hooks way easier.
@@ -24,6 +24,7 @@ A Ruby DSL (Domain Specific Language) for creating Claude Code hooks. This will
24
24
  - [📝 Example: Tool usage monitor](#-example-tool-usage-monitor)
25
25
  - [🔄 Hook Output](#-hook-output)
26
26
  - [🔌 Plugin Hooks Support](#-plugin-hooks-support)
27
+ - [đŸ› ī¸ MCP Tools Integration](#ī¸-mcp-tools-integration)
27
28
  - [🚨 Advices](#-advices)
28
29
  - [âš ī¸ Troubleshooting](#ī¸-troubleshooting)
29
30
  - [đŸ§Ē CLI Debugging](#-cli-debugging)
@@ -35,7 +36,11 @@ A Ruby DSL (Domain Specific Language) for creating Claude Code hooks. This will
35
36
  > [!TIP]
36
37
  > Examples are available in [`example_dotclaude/hooks/`](example_dotclaude/hooks/). The GithubGuard in particular is a good example of a solid hook. You can also check [Kyle's hooks for some great examples](https://github.com/kylesnowschwartz/dotfiles/blob/main/claude/hooks)
37
38
 
38
- Here's how to create a simple hook:
39
+ Claude Code supports two types of hooks:
40
+ - **Command hooks** (`type: "command"`) - Execute Ruby/bash scripts (what this DSL is for)
41
+ - **Prompt-based hooks** (`type: "prompt"`) - Delegate decisions to an LLM ([see guide](docs/PROMPT_BASED_HOOKS.md))
42
+
43
+ Here's how to create a simple command hook with this DSL:
39
44
 
40
45
  1. **Install the gem:**
41
46
  ```bash
@@ -261,15 +266,36 @@ The framework supports the following hook types:
261
266
 
262
267
  | Hook Type | Class | Description |
263
268
  |-----------|-------|-------------|
264
- | **[SessionStart](docs/API/SESSION_START.md)** | `ClaudeHooks::SessionStart` | Hooks that run when Claude Code starts a new session, resumes, or compacts |
265
- | **[UserPromptSubmit](docs/API/USER_PROMPT_SUBMIT.md)** | `ClaudeHooks::UserPromptSubmit` | Hooks that run before the user's prompt is processed |
266
- | **[Notification](docs/API/NOTIFICATION.md)** | `ClaudeHooks::Notification` | Hooks that run when Claude Code sends notifications |
267
- | **[PreToolUse](docs/API/PRE_TOOL_USE.md)** | `ClaudeHooks::PreToolUse` | Hooks that run before a tool is used |
268
- | **[PostToolUse](docs/API/POST_TOOL_USE.md)** | `ClaudeHooks::PostToolUse` | Hooks that run after a tool is used |
269
- | **[Stop](docs/API/STOP.md)** | `ClaudeHooks::Stop` | Hooks that run when Claude Code finishes responding |
270
- | **[SubagentStop](docs/API/SUBAGENT_STOP.md)** | `ClaudeHooks::SubagentStop` | Hooks that run when subagent tasks complete |
271
- | **[SessionEnd](docs/API/SESSION_END.md)** | `ClaudeHooks::SessionEnd` | Hooks that run when Claude Code sessions end |
272
- | **[PreCompact](docs/API/PRE_COMPACT.md)** | `ClaudeHooks::PreCompact` | Hooks that run before transcript compaction |
269
+ | **[SessionStart](docs/API/SESSION_START.md)** | `ClaudeHooks::SessionStart` | Runs when Claude Code starts, resumes, or compacts a session |
270
+ | **[Setup](docs/API/SETUP.md)** | `ClaudeHooks::Setup` | Runs once on Claude Code startup before any session |
271
+ | **[UserPromptSubmit](docs/API/USER_PROMPT_SUBMIT.md)** | `ClaudeHooks::UserPromptSubmit` | Runs before the user's prompt is processed |
272
+ | **[UserPromptExpansion](docs/API/USER_PROMPT_EXPANSION.md)** | `ClaudeHooks::UserPromptExpansion` | Runs when a slash command or prompt expansion is triggered |
273
+ | **[Notification](docs/API/NOTIFICATION.md)** | `ClaudeHooks::Notification` | Runs when Claude Code sends a notification |
274
+ | **[MessageDisplay](docs/API/MESSAGE_DISPLAY.md)** | `ClaudeHooks::MessageDisplay` | Runs when a message is about to be displayed |
275
+ | **[PreToolUse](docs/API/PRE_TOOL_USE.md)** | `ClaudeHooks::PreToolUse` | Runs before a tool is used; can allow, deny, defer or ask |
276
+ | **[PermissionRequest](docs/API/PERMISSION_REQUEST.md)** | `ClaudeHooks::PermissionRequest` | Runs when Claude requests an explicit permission |
277
+ | **[PermissionDenied](docs/API/PERMISSION_DENIED.md)** | `ClaudeHooks::PermissionDenied` | Runs when a permission request is denied; can request a retry |
278
+ | **[PostToolUse](docs/API/POST_TOOL_USE.md)** | `ClaudeHooks::PostToolUse` | Runs after a tool is used; can rewrite output |
279
+ | **[PostToolBatch](docs/API/POST_TOOL_BATCH.md)** | `ClaudeHooks::PostToolBatch` | Runs after a full batch of tool calls completes |
280
+ | **[PostToolUseFailure](docs/API/POST_TOOL_USE_FAILURE.md)** | `ClaudeHooks::PostToolUseFailure` | Runs when a tool call fails |
281
+ | **[Stop](docs/API/STOP.md)** | `ClaudeHooks::Stop` | Runs when Claude Code finishes responding; can force continuation |
282
+ | **[StopFailure](docs/API/STOP_FAILURE.md)** | `ClaudeHooks::StopFailure` | Runs when the stop phase itself errors; logging only |
283
+ | **[SubagentStart](docs/API/SUBAGENT_START.md)** | `ClaudeHooks::SubagentStart` | Runs when a subagent task starts |
284
+ | **[SubagentStop](docs/API/SUBAGENT_STOP.md)** | `ClaudeHooks::SubagentStop` | Runs when a subagent task completes |
285
+ | **[TaskCreated](docs/API/TASK_CREATED.md)** | `ClaudeHooks::TaskCreated` | Runs when a teammate task is created |
286
+ | **[TaskCompleted](docs/API/TASK_COMPLETED.md)** | `ClaudeHooks::TaskCompleted` | Runs when a teammate task completes |
287
+ | **[TeammateIdle](docs/API/TEAMMATE_IDLE.md)** | `ClaudeHooks::TeammateIdle` | Runs when a teammate goes idle |
288
+ | **[PreCompact](docs/API/PRE_COMPACT.md)** | `ClaudeHooks::PreCompact` | Runs before transcript compaction; can block it |
289
+ | **[PostCompact](docs/API/POST_COMPACT.md)** | `ClaudeHooks::PostCompact` | Runs after transcript compaction completes |
290
+ | **[ConfigChange](docs/API/CONFIG_CHANGE.md)** | `ClaudeHooks::ConfigChange` | Runs when Claude Code configuration changes; can block it |
291
+ | **[CwdChanged](docs/API/CWD_CHANGED.md)** | `ClaudeHooks::CwdChanged` | Runs when the working directory changes |
292
+ | **[FileChanged](docs/API/FILE_CHANGED.md)** | `ClaudeHooks::FileChanged` | Runs when a watched file is created, modified, or deleted |
293
+ | **[InstructionsLoaded](docs/API/INSTRUCTIONS_LOADED.md)** | `ClaudeHooks::InstructionsLoaded` | Runs when a CLAUDE.md instructions file is loaded |
294
+ | **[Elicitation](docs/API/ELICITATION.md)** | `ClaudeHooks::Elicitation` | Runs when an MCP server requests user input |
295
+ | **[ElicitationResult](docs/API/ELICITATION_RESULT.md)** | `ClaudeHooks::ElicitationResult` | Runs after an elicitation response is provided |
296
+ | **[WorktreeCreate](docs/API/WORKTREE_CREATE.md)** | `ClaudeHooks::WorktreeCreate` | Runs when Claude Code creates a git worktree |
297
+ | **[WorktreeRemove](docs/API/WORKTREE_REMOVE.md)** | `ClaudeHooks::WorktreeRemove` | Runs when a git worktree is removed |
298
+ | **[SessionEnd](docs/API/SESSION_END.md)** | `ClaudeHooks::SessionEnd` | Runs when a Claude Code session ends |
273
299
 
274
300
  ## 🚀 Claude Hook Flow
275
301
 
@@ -387,15 +413,36 @@ The framework supports all existing hook types with their respective input field
387
413
 
388
414
  | Hook Type | Input Fields |
389
415
  |-----------|--------------|
390
- | **Common** | `session_id`, `transcript_path`, `cwd`, `hook_event_name` |
416
+ | **Common (all hooks)** | `session_id`, `transcript_path`, `cwd`, `hook_event_name`, `permission_mode`, `prompt_id`, `agent_id`, `agent_type`, `effort` |
417
+ | **SessionStart** | `source`, `model`, `session_title` |
418
+ | **Setup** | `source` |
391
419
  | **UserPromptSubmit** | `prompt` |
392
- | **PreToolUse** | `tool_name`, `tool_input` |
393
- | **PostToolUse** | `tool_name`, `tool_input`, `tool_response` |
394
- | **Notification** | `message` |
395
- | **Stop** | `stop_hook_active` |
396
- | **SubagentStop** | `stop_hook_active` |
420
+ | **UserPromptExpansion** | `expansion_type`, `command_name`, `command_args`, `command_source`, `prompt` |
421
+ | **Notification** | `message`, `notification_type` |
422
+ | **MessageDisplay** | `turn_id`, `message_id`, `index`, `final`, `delta` |
423
+ | **PreToolUse** | `tool_name`, `tool_input`, `tool_use_id` |
424
+ | **PermissionRequest** | `tool_name`, `tool_input`, `tool_use_id`, `permission_suggestions` |
425
+ | **PermissionDenied** | `tool_name`, `tool_input`, `tool_use_id`, `reason` |
426
+ | **PostToolUse** | `tool_name`, `tool_input`, `tool_response`, `tool_use_id` |
427
+ | **PostToolBatch** | `tool_calls` |
428
+ | **PostToolUseFailure** | `tool_name`, `tool_input`, `tool_use_id`, `error`, `is_interrupt`, `duration_ms` |
429
+ | **Stop** | `stop_hook_active`, `last_assistant_message`, `background_tasks`, `session_crons` |
430
+ | **StopFailure** | `error`, `error_details`, `last_assistant_message` |
431
+ | **SubagentStart** | *(common only: `agent_id`, `agent_type`)* |
432
+ | **SubagentStop** | `stop_hook_active`, `agent_transcript_path` + common `agent_id`/`agent_type` |
433
+ | **TaskCreated** | `task_id`, `task_subject`, `task_description`, `teammate_name`, `team_name` |
434
+ | **TaskCompleted** | `task_id`, `task_subject`, `task_description`, `teammate_name`, `team_name` |
435
+ | **TeammateIdle** | `teammate_name`, `team_name` |
397
436
  | **PreCompact** | `trigger`, `custom_instructions` |
398
- | **SessionStart** | `source` |
437
+ | **PostCompact** | `trigger`, `compact_summary` |
438
+ | **ConfigChange** | `source`, `file_path` |
439
+ | **CwdChanged** | `old_cwd`, `new_cwd` |
440
+ | **FileChanged** | `file_path`, `event` |
441
+ | **InstructionsLoaded** | `file_path`, `load_reason` |
442
+ | **Elicitation** | `mcp_server_name`, `message`, `mode`, `url`, `elicitation_id`, `requested_schema` |
443
+ | **ElicitationResult** | `mcp_server_name`, `action`, `mode`, `elicitation_id`, `content` |
444
+ | **WorktreeCreate** | `name` |
445
+ | **WorktreeRemove** | `worktree_path` |
399
446
  | **SessionEnd** | `reason` |
400
447
 
401
448
  ### Hooks API
@@ -403,14 +450,35 @@ The framework supports all existing hook types with their respective input field
403
450
  **All hook types** inherit from `ClaudeHooks::Base` and share a common API, as well as hook specific APIs.
404
451
 
405
452
  - [📚 Common API Methods](docs/API/COMMON.md)
406
- - [🔔 Notification Hooks](docs/API/NOTIFICATION.md)
407
453
  - [🚀 Session Start Hooks](docs/API/SESSION_START.md)
454
+ - [âš™ī¸ Setup Hooks](docs/API/SETUP.md)
408
455
  - [đŸ–‹ī¸ User Prompt Submit Hooks](docs/API/USER_PROMPT_SUBMIT.md)
456
+ - [🔀 User Prompt Expansion Hooks](docs/API/USER_PROMPT_EXPANSION.md)
457
+ - [🔔 Notification Hooks](docs/API/NOTIFICATION.md)
458
+ - [đŸ’Ŧ Message Display Hooks](docs/API/MESSAGE_DISPLAY.md)
409
459
  - [đŸ› ī¸ Pre-Tool Use Hooks](docs/API/PRE_TOOL_USE.md)
460
+ - [🔐 Permission Request Hooks](docs/API/PERMISSION_REQUEST.md)
461
+ - [đŸšĢ Permission Denied Hooks](docs/API/PERMISSION_DENIED.md)
410
462
  - [🔧 Post-Tool Use Hooks](docs/API/POST_TOOL_USE.md)
411
- - [📝 Pre-Compact Hooks](docs/API/PRE_COMPACT.md)
463
+ - [đŸ“Ļ Post-Tool Batch Hooks](docs/API/POST_TOOL_BATCH.md)
464
+ - [❌ Post-Tool Use Failure Hooks](docs/API/POST_TOOL_USE_FAILURE.md)
412
465
  - [âšī¸ Stop Hooks](docs/API/STOP.md)
466
+ - [đŸ’Ĩ Stop Failure Hooks](docs/API/STOP_FAILURE.md)
467
+ - [â–ļī¸ Subagent Start Hooks](docs/API/SUBAGENT_START.md)
413
468
  - [âšī¸ Subagent Stop Hooks](docs/API/SUBAGENT_STOP.md)
469
+ - [✅ Task Created Hooks](docs/API/TASK_CREATED.md)
470
+ - [✅ Task Completed Hooks](docs/API/TASK_COMPLETED.md)
471
+ - [💤 Teammate Idle Hooks](docs/API/TEAMMATE_IDLE.md)
472
+ - [📝 Pre-Compact Hooks](docs/API/PRE_COMPACT.md)
473
+ - [📄 Post-Compact Hooks](docs/API/POST_COMPACT.md)
474
+ - [🔩 Config Change Hooks](docs/API/CONFIG_CHANGE.md)
475
+ - [📂 Cwd Changed Hooks](docs/API/CWD_CHANGED.md)
476
+ - [📄 File Changed Hooks](docs/API/FILE_CHANGED.md)
477
+ - [📋 Instructions Loaded Hooks](docs/API/INSTRUCTIONS_LOADED.md)
478
+ - [đŸ’Ŧ Elicitation Hooks](docs/API/ELICITATION.md)
479
+ - [đŸ’Ŧ Elicitation Result Hooks](docs/API/ELICITATION_RESULT.md)
480
+ - [đŸŒŗ Worktree Create Hooks](docs/API/WORKTREE_CREATE.md)
481
+ - [đŸ—‘ī¸ Worktree Remove Hooks](docs/API/WORKTREE_REMOVE.md)
414
482
  - [🔚 Session End Hooks](docs/API/SESSION_END.md)
415
483
 
416
484
  ### 📝 Logging
@@ -622,6 +690,13 @@ Claude Code hooks support multiple exit codes with different behaviors depending
622
690
  | SessionStart | Operation continues<br/><br />**`STDOUT` added as context to Claude** | Non-blocking error<br/><br />`STDERR` shown to user | N/A<br/><br />`STDERR` shown to user only |
623
691
  | SessionEnd | Operation continues<br/><br />Logged to debug only (`--debug`) | Non-blocking error<br/><br />Logged to debug only (`--debug`) | N/A<br/><br />Logged to debug only (`--debug`) |
624
692
 
693
+ > [!NOTE]
694
+ > The 20 events added in `1.2.0` follow the same families. Their per-event exit-code behavior is documented on each API page under [`docs/API/`](docs/API/):
695
+ > - **Blocking via top-level `decision`** (behave like `PreToolUse`/`Stop`): `UserPromptExpansion`, `PostToolBatch`, `ConfigChange`.
696
+ > - **Blocking via `exit 2` / `continue: false`** (no `decision` field): `TaskCreated`, `TaskCompleted`, `TeammateIdle`.
697
+ > - **JSON-API special** (always `exit 0`, decision in `hookSpecificOutput`): `PermissionDenied`, `Elicitation`, `ElicitationResult`, `WorktreeCreate` (bare-path stdout).
698
+ > - **Non-blocking / context-only** (exit code effectively ignored): `Setup`, `SubagentStart`, `PostToolUseFailure`, `StopFailure`, `PostCompact`, `CwdChanged`, `FileChanged`, `InstructionsLoaded`, `WorktreeRemove`, `MessageDisplay`.
699
+
625
700
 
626
701
  #### Manually outputing and exiting example with success
627
702
  For the operation to continue for a `UserPromptSubmit` hook, you would `STDOUT.puts` structured JSON data followed by `exit 0`:
@@ -719,7 +794,70 @@ end
719
794
  - `${CLAUDE_PROJECT_DIR}`: Project root directory (same as for project hooks)
720
795
  - All standard environment variables and configuration options work the same way
721
796
 
722
- See the [plugin components reference](https://docs.claude.com/en/docs/claude-code/plugins-reference#hooks) for more details on creating plugin hooks.
797
+ See the [plugin components reference](https://code.claude.com/docs/en/plugins-reference#hooks) for more details on creating plugin hooks.
798
+
799
+ ## đŸ› ī¸ MCP Tools Integration
800
+
801
+ [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) tools can be used with Claude Code hooks. When MCP servers are configured, their tools become available and can be intercepted by hooks just like built-in tools.
802
+
803
+ ### MCP Tool Naming Convention
804
+
805
+ MCP tools follow a specific naming pattern: `mcp__<server-name>__<tool-name>`
806
+
807
+ **Example MCP tool names:**
808
+ - `mcp__filesystem__read_file`
809
+ - `mcp__github__create_issue`
810
+ - `mcp__database__query`
811
+
812
+ ### Configuring Hooks for MCP Tools
813
+
814
+ You can use matchers to target specific MCP tools or all tools from a server:
815
+
816
+ ```json
817
+ {
818
+ "hooks": {
819
+ "PreToolUse": [
820
+ {
821
+ "matcher": "mcp__github__.*",
822
+ "hooks": [
823
+ {
824
+ "type": "command",
825
+ "command": "~/.claude/hooks/entrypoints/github_guard.rb"
826
+ }
827
+ ]
828
+ },
829
+ {
830
+ "matcher": "mcp__.*__create.*|mcp__.*__delete.*",
831
+ "hooks": [
832
+ {
833
+ "type": "command",
834
+ "command": "~/.claude/hooks/entrypoints/destructive_operation_guard.rb"
835
+ }
836
+ ]
837
+ }
838
+ ]
839
+ }
840
+ }
841
+ ```
842
+
843
+ ### Common MCP Tool Patterns
844
+
845
+ | Pattern | Matches |
846
+ |---------|---------|
847
+ | `mcp__*__*` | All MCP tools from all servers |
848
+ | `mcp__github__*` | All tools from the github server |
849
+ | `mcp__*__read*` | All read operations from any server |
850
+ | `mcp__.*__create.*\|mcp__.*__delete.*` | All create/delete operations |
851
+
852
+ ### Best Practices for MCP Tools
853
+
854
+ 1. **Use regex matchers** - MCP tool names are predictable, making regex patterns very effective
855
+ 2. **Guard destructive operations** - Always review create/delete/update operations
856
+ 3. **Server-specific rules** - Different MCP servers may need different security policies
857
+ 4. **Log MCP tool usage** - Track which external tools are being used
858
+ 5. **Test with your MCP servers** - Tool names vary by server implementation
859
+
860
+ See the [official MCP documentation](https://modelcontextprotocol.io/) for more information about MCP servers and tools.
723
861
 
724
862
  ## 🚨 Advices
725
863