Files
claude-skills/skills/session-handoff/hooks/commit-detector.ps1
vitya d089df7e9f fix(session-handoff): PowerShell hook body char count [v0.3.1]
`(& git log -1 --format='%b')` in PowerShell collapses multi-line subprocess
output into string[]. The threshold check used `$body.Length` which on a
string[] returns the line count, not char count — so the body>200 condition
was effectively comparing "more than 200 lines", which is much harder to
meet. Files-count saves it in practice for big commits, but small-file
big-message commits were under-detected.

Fix: join the array back into a single string with `-join "`n"` before
measuring length. Verified via stdin-pipe smoke against current HEAD:
body chars now report 1255 (vs 29 before — the line count).

POSIX `.sh` variant unaffected — `$()` collapses output and `${#var}` is
char count.

Bump 0.3.0 → 0.3.1 PATCH (bugfix, no behavior contract change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 23:41:26 +03:00

73 lines
2.8 KiB
PowerShell

#!/usr/bin/env pwsh
# session-handoff PostToolUse hook (PowerShell).
#
# Reads PostToolUse JSON from stdin, detects whether the just-completed
# Bash tool call was a substantive `git commit`, and on hit emits JSON to
# stdout with `additionalContext` so Claude Code surfaces a system reminder
# in the next agent iteration ("substantive commit — consider session-handoff
# write-mode").
#
# Substantive heuristic (mirrors session-handoff SKILL.md):
# prefix NOT in (meta:|docs:|style:|chore:|fix typo) AND
# (body > 200 chars OR files > 3)
#
# Silent skip on: malformed JSON, no command, --amend, failed commit,
# non-git cwd, trivial prefix, below thresholds. Never blocks the tool call
# (PostToolUse cannot, by design).
$ErrorActionPreference = 'Stop'
# Read stdin
try {
$raw = [Console]::In.ReadToEnd()
if ([string]::IsNullOrWhiteSpace($raw)) { exit 0 }
$hook = $raw | ConvertFrom-Json -ErrorAction Stop
} catch {
exit 0
}
# Only Bash tool, only git commit (not --amend)
$cmd = $hook.tool_input.command
if (-not $cmd) { exit 0 }
if ($cmd -notmatch '(?<![\w-])git\s+commit(?![\w-])') { exit 0 }
if ($cmd -match '(?<![\w-])git\s+commit\b.*--amend') { exit 0 }
# Only on successful commit
if ($null -ne $hook.tool_response.exit_code -and $hook.tool_response.exit_code -ne 0) { exit 0 }
# Resolve cwd; require a git work-tree
$cwd = $hook.cwd
if (-not $cwd) { $cwd = (Get-Location).Path }
$inside = & git -C $cwd rev-parse --is-inside-work-tree 2>$null
if ($inside -ne 'true') { exit 0 }
# Parse last commit
$subject = (& git -C $cwd log -1 --format='%s').Trim()
# PowerShell collapses multi-line subprocess output into string[] — join back so
# .Length below is char count, not line count.
$body = ((& git -C $cwd log -1 --format='%b') -join "`n")
$files = ((& git -C $cwd diff-tree --no-commit-id --name-only -r HEAD) | Measure-Object).Count
# Trivial-prefix check (Conventional Commits prefix before optional scope + colon)
$prefix = $subject -replace '^([a-z]+)(\([^)]+\))?:.*$','$1'
$trivial = @('meta','docs','style','chore')
if ($trivial -contains $prefix) { exit 0 }
if ($subject -match 'fix\s+typo') { exit 0 }
# Threshold check (chars for body, count for files)
$bodyLen = if ($body) { $body.Length } else { 0 }
if ($bodyLen -le 200 -and $files -le 3) { exit 0 }
# Substantive — emit JSON
$msg = "Substantive commit detected on " + $cwd + ": ``" + $subject + "`` (" + $files + " files changed, body " + $bodyLen + " chars). Consider invoking session-handoff write-mode to update .tasks/NEXT_SESSION.md."
@{
hookSpecificOutput = @{
hookEventName = 'PostToolUse'
additionalContext = $msg
}
systemMessage = 'session-handoff: substantive commit detected'
suppressOutput = $false
} | ConvertTo-Json -Compress -Depth 5 | Write-Output