claude_hooks 1.1.0 → 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.
- checksums.yaml +4 -4
- data/.agents/skills/ci-monitoring/SKILL.md +103 -0
- data/.agents/skills/run-tests/SKILL.md +45 -0
- data/.claude/settings.local.json +21 -0
- data/AGENTS.md +46 -0
- data/CHANGELOG.md +43 -0
- data/README.md +156 -21
- data/docs/API/COMMON.md +7 -1
- data/docs/API/CONFIG_CHANGE.md +39 -0
- data/docs/API/CWD_CHANGED.md +34 -0
- data/docs/API/ELICITATION.md +44 -0
- data/docs/API/ELICITATION_RESULT.md +43 -0
- data/docs/API/FILE_CHANGED.md +37 -0
- data/docs/API/INSTRUCTIONS_LOADED.md +30 -0
- data/docs/API/MESSAGE_DISPLAY.md +38 -0
- data/docs/API/PERMISSION_DENIED.md +37 -0
- data/docs/API/PERMISSION_REQUEST.md +11 -8
- data/docs/API/POST_COMPACT.md +30 -0
- data/docs/API/POST_TOOL_BATCH.md +39 -0
- data/docs/API/POST_TOOL_USE_FAILURE.md +31 -0
- data/docs/API/PRE_COMPACT.md +10 -2
- data/docs/API/PRE_TOOL_USE.md +5 -1
- data/docs/API/SETUP.md +37 -0
- data/docs/API/STOP_FAILURE.md +19 -0
- data/docs/API/SUBAGENT_START.md +40 -0
- data/docs/API/TASK_COMPLETED.md +43 -0
- data/docs/API/TASK_CREATED.md +43 -0
- data/docs/API/TEAMMATE_IDLE.md +40 -0
- data/docs/API/USER_PROMPT_EXPANSION.md +43 -0
- data/docs/API/WORKTREE_CREATE.md +39 -0
- data/docs/API/WORKTREE_REMOVE.md +33 -0
- data/docs/PROMPT_BASED_HOOKS.md +386 -0
- data/docs/external/claude-hooks-reference.md +2406 -854
- data/docs/mitts/ideabox.md +12 -0
- data/docs/mitts/prd.md +417 -0
- data/docs/mitts/setup.md +303 -0
- data/docs/mitts/task.md +44 -0
- data/lib/claude_hooks/base.rb +25 -0
- data/lib/claude_hooks/config_change.rb +28 -0
- data/lib/claude_hooks/cwd_changed.rb +28 -0
- data/lib/claude_hooks/elicitation.rb +61 -0
- data/lib/claude_hooks/elicitation_result.rb +57 -0
- data/lib/claude_hooks/file_changed.rb +41 -0
- data/lib/claude_hooks/instructions_loaded.rb +23 -0
- data/lib/claude_hooks/message_display.rb +58 -0
- data/lib/claude_hooks/output/base.rb +46 -1
- data/lib/claude_hooks/output/config_change.rb +47 -0
- data/lib/claude_hooks/output/cwd_changed.rb +42 -0
- data/lib/claude_hooks/output/elicitation.rb +67 -0
- data/lib/claude_hooks/output/elicitation_result.rb +63 -0
- data/lib/claude_hooks/output/file_changed.rb +42 -0
- data/lib/claude_hooks/output/instructions_loaded.rb +19 -0
- data/lib/claude_hooks/output/message_display.rb +49 -0
- data/lib/claude_hooks/output/permission_denied.rb +42 -0
- data/lib/claude_hooks/output/permission_request.rb +50 -25
- data/lib/claude_hooks/output/post_compact.rb +18 -0
- data/lib/claude_hooks/output/post_tool_batch.rb +51 -0
- data/lib/claude_hooks/output/post_tool_use.rb +12 -0
- data/lib/claude_hooks/output/post_tool_use_failure.rb +42 -0
- data/lib/claude_hooks/output/pre_compact.rb +34 -3
- data/lib/claude_hooks/output/pre_tool_use.rb +8 -2
- data/lib/claude_hooks/output/session_start.rb +30 -8
- data/lib/claude_hooks/output/setup.rb +42 -0
- data/lib/claude_hooks/output/stop.rb +15 -0
- data/lib/claude_hooks/output/stop_failure.rb +19 -0
- data/lib/claude_hooks/output/subagent_start.rb +42 -0
- data/lib/claude_hooks/output/task_completed.rb +18 -0
- data/lib/claude_hooks/output/task_created.rb +18 -0
- data/lib/claude_hooks/output/teammate_idle.rb +18 -0
- data/lib/claude_hooks/output/user_prompt_expansion.rb +51 -0
- data/lib/claude_hooks/output/worktree_create.rb +60 -0
- data/lib/claude_hooks/output/worktree_remove.rb +19 -0
- data/lib/claude_hooks/permission_denied.rb +41 -0
- data/lib/claude_hooks/permission_request.rb +16 -9
- data/lib/claude_hooks/post_compact.rb +23 -0
- data/lib/claude_hooks/post_tool_batch.rb +59 -0
- data/lib/claude_hooks/post_tool_use.rb +10 -0
- data/lib/claude_hooks/post_tool_use_failure.rb +45 -0
- data/lib/claude_hooks/pre_compact.rb +7 -0
- data/lib/claude_hooks/pre_tool_use.rb +20 -2
- data/lib/claude_hooks/session_start.rb +28 -0
- data/lib/claude_hooks/setup.rb +25 -0
- data/lib/claude_hooks/stop.rb +17 -0
- data/lib/claude_hooks/stop_failure.rb +27 -0
- data/lib/claude_hooks/subagent_start.rb +23 -0
- data/lib/claude_hooks/subagent_stop.rb +5 -1
- data/lib/claude_hooks/task_completed.rb +35 -0
- data/lib/claude_hooks/task_created.rb +35 -0
- data/lib/claude_hooks/teammate_idle.rb +23 -0
- data/lib/claude_hooks/user_prompt_expansion.rb +45 -0
- data/lib/claude_hooks/version.rb +1 -1
- data/lib/claude_hooks/worktree_create.rb +27 -0
- data/lib/claude_hooks/worktree_remove.rb +19 -0
- data/lib/claude_hooks.rb +40 -0
- metadata +71 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c1803cc66c5f4c6e0d2ca706c4106654a2aa961e8a8e968d3198acab8ff95a7d
|
|
4
|
+
data.tar.gz: 5dba30b7bce90d4a420cb1c83be1b9e2e2c3a1b0b1dfc42f47b0edd01e08941c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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,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,49 @@ 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
|
+
|
|
8
51
|
## [1.1.0] - 2026-01-04
|
|
9
52
|
|
|
10
53
|
### Added
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Ruby DSL for Claude Code hooks
|
|
2
2
|
|
|
3
3
|
> [!IMPORTANT]
|
|
4
|
-
> v1.
|
|
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
|
-
|
|
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,16 +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` |
|
|
265
|
-
| **[
|
|
266
|
-
| **[
|
|
267
|
-
| **[
|
|
268
|
-
| **[
|
|
269
|
-
| **[
|
|
270
|
-
| **[
|
|
271
|
-
| **[
|
|
272
|
-
| **[
|
|
273
|
-
| **[
|
|
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 |
|
|
274
299
|
|
|
275
300
|
## 🚀 Claude Hook Flow
|
|
276
301
|
|
|
@@ -388,16 +413,36 @@ The framework supports all existing hook types with their respective input field
|
|
|
388
413
|
|
|
389
414
|
| Hook Type | Input Fields |
|
|
390
415
|
|-----------|--------------|
|
|
391
|
-
| **Common** | `session_id`, `transcript_path`, `cwd`, `hook_event_name`, `permission_mode` |
|
|
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` |
|
|
392
419
|
| **UserPromptSubmit** | `prompt` |
|
|
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` |
|
|
393
423
|
| **PreToolUse** | `tool_name`, `tool_input`, `tool_use_id` |
|
|
394
|
-
| **PermissionRequest** | `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` |
|
|
395
426
|
| **PostToolUse** | `tool_name`, `tool_input`, `tool_response`, `tool_use_id` |
|
|
396
|
-
| **
|
|
397
|
-
| **
|
|
398
|
-
| **
|
|
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` |
|
|
399
436
|
| **PreCompact** | `trigger`, `custom_instructions` |
|
|
400
|
-
| **
|
|
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` |
|
|
401
446
|
| **SessionEnd** | `reason` |
|
|
402
447
|
|
|
403
448
|
### Hooks API
|
|
@@ -405,15 +450,35 @@ The framework supports all existing hook types with their respective input field
|
|
|
405
450
|
**All hook types** inherit from `ClaudeHooks::Base` and share a common API, as well as hook specific APIs.
|
|
406
451
|
|
|
407
452
|
- [📚 Common API Methods](docs/API/COMMON.md)
|
|
408
|
-
- [🔔 Notification Hooks](docs/API/NOTIFICATION.md)
|
|
409
453
|
- [🚀 Session Start Hooks](docs/API/SESSION_START.md)
|
|
454
|
+
- [⚙️ Setup Hooks](docs/API/SETUP.md)
|
|
410
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)
|
|
411
459
|
- [🛠️ Pre-Tool Use Hooks](docs/API/PRE_TOOL_USE.md)
|
|
412
460
|
- [🔐 Permission Request Hooks](docs/API/PERMISSION_REQUEST.md)
|
|
461
|
+
- [🚫 Permission Denied Hooks](docs/API/PERMISSION_DENIED.md)
|
|
413
462
|
- [🔧 Post-Tool Use Hooks](docs/API/POST_TOOL_USE.md)
|
|
414
|
-
- [
|
|
463
|
+
- [📦 Post-Tool Batch Hooks](docs/API/POST_TOOL_BATCH.md)
|
|
464
|
+
- [❌ Post-Tool Use Failure Hooks](docs/API/POST_TOOL_USE_FAILURE.md)
|
|
415
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)
|
|
416
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)
|
|
417
482
|
- [🔚 Session End Hooks](docs/API/SESSION_END.md)
|
|
418
483
|
|
|
419
484
|
### 📝 Logging
|
|
@@ -625,6 +690,13 @@ Claude Code hooks support multiple exit codes with different behaviors depending
|
|
|
625
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 |
|
|
626
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`) |
|
|
627
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
|
+
|
|
628
700
|
|
|
629
701
|
#### Manually outputing and exiting example with success
|
|
630
702
|
For the operation to continue for a `UserPromptSubmit` hook, you would `STDOUT.puts` structured JSON data followed by `exit 0`:
|
|
@@ -722,7 +794,70 @@ end
|
|
|
722
794
|
- `${CLAUDE_PROJECT_DIR}`: Project root directory (same as for project hooks)
|
|
723
795
|
- All standard environment variables and configuration options work the same way
|
|
724
796
|
|
|
725
|
-
See the [plugin components reference](https://
|
|
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.
|
|
726
861
|
|
|
727
862
|
## 🚨 Advices
|
|
728
863
|
|
data/docs/API/COMMON.md
CHANGED
|
@@ -12,7 +12,11 @@ Input helpers to access the data provided by Claude Code through `STDIN`.
|
|
|
12
12
|
| `transcript_path` | Get path to the transcript file |
|
|
13
13
|
| `cwd` | Get current working directory |
|
|
14
14
|
| `hook_event_name` | Get the hook event name |
|
|
15
|
-
| `permission_mode` | Get the permission mode: `'default'`, `'plan'`, `'acceptEdits'`, `'dontAsk'`, `'bypassPermissions'` (defaults to `'default'`) |
|
|
15
|
+
| `permission_mode` | Get the permission mode: `'default'`, `'plan'`, `'acceptEdits'`, `'auto'`, `'dontAsk'`, `'bypassPermissions'` (defaults to `'default'`) |
|
|
16
|
+
| `prompt_id` | Get the current prompt ID |
|
|
17
|
+
| `agent_id` | Get the agent ID (present in subagent contexts) |
|
|
18
|
+
| `agent_type` | Get the agent type (present in subagent contexts) |
|
|
19
|
+
| `effort` | Get the effort level string (reads `effort.level`) |
|
|
16
20
|
| `read_transcript` | Read the transcript file |
|
|
17
21
|
| `transcript` | Alias for `read_transcript` |
|
|
18
22
|
|
|
@@ -28,6 +32,7 @@ Hook state methods are helpers to modify the hook's internal state (`output_data
|
|
|
28
32
|
| `clear_specifics!` | Clear hook-specific output |
|
|
29
33
|
| `system_message!(message)` | Set a system message shown to the user (not to Claude) |
|
|
30
34
|
| `clear_system_message!` | Clear the system message |
|
|
35
|
+
| `terminal_sequence!(seq)` | Emit an ANSI/terminal escape sequence in the output |
|
|
31
36
|
|
|
32
37
|
## Output Helpers
|
|
33
38
|
|
|
@@ -45,6 +50,7 @@ This object provides helpers to access output data, for merging multiple outputs
|
|
|
45
50
|
| `output.suppress_output?` | Check if output should be suppressed from transcript |
|
|
46
51
|
| `output.hook_specific_output` | Get the hook-specific output data |
|
|
47
52
|
| `output.system_message` | Get the system message if any |
|
|
53
|
+
| `output.terminal_sequence` | Get the terminal escape sequence (if set) |
|
|
48
54
|
|
|
49
55
|
### Output data merging
|
|
50
56
|
For each hook type, the `output` object provides a **class method** `merge` that will try to intelligently merge multiple hook results, e.g. `ClaudeHooks::Output::UserPromptSubmit.merge(output1, output2, output3)`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# ConfigChange API
|
|
2
|
+
|
|
3
|
+
Available when inheriting from `ClaudeHooks::ConfigChange`:
|
|
4
|
+
|
|
5
|
+
Runs when Claude Code configuration changes. Can block the change for most sources (note: `policy_settings` changes cannot be blocked).
|
|
6
|
+
|
|
7
|
+
## Input Helpers
|
|
8
|
+
|
|
9
|
+
[📚 Shared input helpers](COMMON.md#input-helpers)
|
|
10
|
+
|
|
11
|
+
| Method | Description |
|
|
12
|
+
|--------|-------------|
|
|
13
|
+
| `source` | The source of the config change (e.g. `'project'`, `'user'`, `'policy_settings'`) |
|
|
14
|
+
| `file_path` | Path to the changed config file (optional) |
|
|
15
|
+
|
|
16
|
+
## Hook State Helpers
|
|
17
|
+
|
|
18
|
+
[📚 Shared hook state methods](COMMON.md#hook-state-methods)
|
|
19
|
+
|
|
20
|
+
| Method | Description |
|
|
21
|
+
|--------|-------------|
|
|
22
|
+
| `block!(reason)` | Block the config change (ineffective for `policy_settings`) |
|
|
23
|
+
|
|
24
|
+
## Output Helpers
|
|
25
|
+
|
|
26
|
+
[📚 Shared output helpers](COMMON.md#output-helpers)
|
|
27
|
+
|
|
28
|
+
| Method | Description |
|
|
29
|
+
|--------|-------------|
|
|
30
|
+
| `output.blocked?` | Check if the change was blocked |
|
|
31
|
+
| `output.decision` | Get the decision (`'block'` or nil) |
|
|
32
|
+
| `output.reason` | Get the block reason |
|
|
33
|
+
|
|
34
|
+
## Hook Exit Codes
|
|
35
|
+
|
|
36
|
+
| Exit Code | Behavior |
|
|
37
|
+
|-----------|----------|
|
|
38
|
+
| `exit 0` | Config change proceeds |
|
|
39
|
+
| `exit 2` | Blocks the change; `STDERR` shown to Claude |
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# CwdChanged API
|
|
2
|
+
|
|
3
|
+
Available when inheriting from `ClaudeHooks::CwdChanged`:
|
|
4
|
+
|
|
5
|
+
Runs when the working directory changes. Can update the list of watched paths.
|
|
6
|
+
|
|
7
|
+
## Input Helpers
|
|
8
|
+
|
|
9
|
+
[📚 Shared input helpers](COMMON.md#input-helpers)
|
|
10
|
+
|
|
11
|
+
| Method | Description |
|
|
12
|
+
|--------|-------------|
|
|
13
|
+
| `old_cwd` | The previous working directory |
|
|
14
|
+
| `new_cwd` | The new working directory |
|
|
15
|
+
|
|
16
|
+
## Hook State Helpers
|
|
17
|
+
|
|
18
|
+
[📚 Shared hook state methods](COMMON.md#hook-state-methods)
|
|
19
|
+
|
|
20
|
+
| Method | Description |
|
|
21
|
+
|--------|-------------|
|
|
22
|
+
| `watch_paths!(paths)` | Set the list of paths Claude Code should watch for changes |
|
|
23
|
+
|
|
24
|
+
## Output Helpers
|
|
25
|
+
|
|
26
|
+
[📚 Shared output helpers](COMMON.md#output-helpers)
|
|
27
|
+
|
|
28
|
+
| Method | Description |
|
|
29
|
+
|--------|-------------|
|
|
30
|
+
| `output.watch_paths` | Get the configured watch paths |
|
|
31
|
+
|
|
32
|
+
## Hook Exit Codes
|
|
33
|
+
|
|
34
|
+
Non-blocking. Only `watchPaths` output is consumed.
|