feat(session-handoff): add PostToolUse commit-detector hook [v0.3.0]
Hooks live in skills/session-handoff/hooks/:
- commit-detector.ps1 Windows/PowerShell
- commit-detector.sh POSIX (python3 for JSON parsing)
- README.md opt-in instructions, cross-platform settings.json
snippets, smoke procedure
Hook reads PostToolUse stdin, detects substantive `git commit` (prefix
not in meta/docs/style/chore + fix typo, AND body >200 chars OR files >3).
On hit, emits hookSpecificOutput.additionalContext so Claude Code
surfaces a system reminder next iteration. Silent skip on --amend, failed
commits, non-git cwd, trivial prefix, below thresholds.
install.sh deliberately does NOT mutate settings.json — opt-in via the
README hook config snippet, applied once per machine. SKILL.md body
mentions the hook as an optional alternative to the agent-side
behavioral heuristic.
Bump 0.2.1 -> 0.3.0 MINOR (new opt-in capability, backward-compatible —
SKILL keeps working without the hook).
Deferred (separate follow-ups if needed):
- live-hook e2e smoke (would interfere with current session commits)
- rebase/cherry-pick batch deduplication
Closes [session-handoff-posttooluse-hook] (partial: stdin smoke,
live-hook deferred).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
91
skills/session-handoff/hooks/README.md
Normal file
91
skills/session-handoff/hooks/README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# session-handoff hooks
|
||||
|
||||
Opt-in PostToolUse hook that detects substantive `git commit` invocations and signals the agent ("consider running session-handoff write-mode") via a system reminder. Replaces the agent-side commit-detection heuristic in the SKILL.md `When to use` section with a deterministic harness-side trigger.
|
||||
|
||||
## Why opt-in (not auto-installed)
|
||||
|
||||
`install.sh` deliberately does **not** mutate `~/.claude/settings.json`. Auto-rewriting the user's hook config on every skill install is the wrong shape — user expects `install.sh` to copy files, nothing more. The hook is shipped as scripts; user enables it once per machine.
|
||||
|
||||
## Enable on Windows (PowerShell)
|
||||
|
||||
Add to `~/.claude/settings.json`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "pwsh -NoProfile -ExecutionPolicy Bypass -File \"$env:USERPROFILE\\.claude\\skills\\session-handoff\\hooks\\commit-detector.ps1\""
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `hooks.PostToolUse` already exists — append the matcher block to the array. Don't overwrite existing entries.
|
||||
|
||||
## Enable on Linux / macOS (bash)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash ~/.claude/skills/session-handoff/hooks/commit-detector.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The bash variant needs `python3` on PATH (used to parse the PostToolUse JSON stdin).
|
||||
|
||||
## What gets signalled
|
||||
|
||||
On a successful `git commit` whose subject prefix is not in {`meta`, `docs`, `style`, `chore`} or `fix typo`, **and** whose body exceeds 200 characters or which touches more than 3 files — the hook emits a `hookSpecificOutput.additionalContext` system reminder of shape:
|
||||
|
||||
> Substantive commit detected on `<cwd>`: `<subject>` (N files changed, body M chars). Consider invoking session-handoff write-mode to update `.tasks/NEXT_SESSION.md`.
|
||||
|
||||
On any of these → silent skip (exit 0, no JSON):
|
||||
|
||||
- malformed PostToolUse stdin
|
||||
- Bash command isn't `git commit`
|
||||
- command is `git commit --amend`
|
||||
- commit returned non-zero exit
|
||||
- cwd isn't a git work-tree
|
||||
- subject prefix is in trivial set
|
||||
- body ≤ 200 chars AND files ≤ 3
|
||||
|
||||
## Smoke test (without enabling the hook)
|
||||
|
||||
Pipe a synthetic PostToolUse JSON to the script. On Windows:
|
||||
|
||||
```powershell
|
||||
$payload = @{
|
||||
tool_input = @{ command = 'git commit -m "subject"' }
|
||||
tool_response = @{ exit_code = 0 }
|
||||
cwd = (Get-Location).Path
|
||||
} | ConvertTo-Json -Compress
|
||||
|
||||
$payload | pwsh -NoProfile -File .\skills\session-handoff\hooks\commit-detector.ps1
|
||||
```
|
||||
|
||||
If your `HEAD` is a non-trivial commit (e.g. recent `feat:` with > 200-char body or > 3 files), output is JSON containing `additionalContext`. Otherwise — empty stdout (silent skip).
|
||||
|
||||
## Caveats
|
||||
|
||||
- **Rebase / cherry-pick noise.** Every commit in a rebase or cherry-pick batch will re-fire the hook. Deferred to a follow-up if it actually annoys in practice; the hook is opt-in so the cost is bounded.
|
||||
- **First-non-trivial commit of session.** The agent-side heuristic in SKILL.md treats the *first* non-trivial commit of a session as "always substantive" regardless of thresholds. The hook can't see session boundaries — uses only body/file thresholds. Slight under-detection on small first commits; acceptable trade-off for harness-side determinism.
|
||||
- **No automatic write-mode invocation.** Hook only signals. The agent still decides whether to run session-handoff write-mode in response — keeps the user-agency invariant from SKILL.md `What NOT to do`.
|
||||
Reference in New Issue
Block a user