#!/usr/bin/env bash # session-handoff PostToolUse hook (POSIX). See commit-detector.ps1 for prose. set -euo pipefail raw=$(cat) [[ -z "$raw" ]] && exit 0 # Helper: extract a JSON path via python3 jget() { python3 -c " import sys, json try: d = json.loads(sys.argv[1]) out = d for k in sys.argv[2].split('.'): if isinstance(out, dict): out = out.get(k) else: out = None break print('' if out is None else out) " "$raw" "$1" 2>/dev/null || echo '' } cmd=$(jget tool_input.command) [[ -z "$cmd" ]] && exit 0 # Only `git commit`, not `--amend` if ! echo "$cmd" | grep -qE '(^|[^[:alnum:]_-])git[[:space:]]+commit($|[^[:alnum:]_-])'; then exit 0; fi if echo "$cmd" | grep -qE '(^|[^[:alnum:]_-])git[[:space:]]+commit\b.*--amend'; then exit 0; fi # Only on successful commit (if exit_code present and non-zero, skip) exit_code=$(jget tool_response.exit_code) if [[ -n "$exit_code" && "$exit_code" != "0" ]]; then exit 0; fi cwd=$(jget cwd) [[ -z "$cwd" ]] && cwd=$(pwd) # Require git work-tree git -C "$cwd" rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0 subject=$(git -C "$cwd" log -1 --format='%s') body=$(git -C "$cwd" log -1 --format='%b') files=$(git -C "$cwd" diff-tree --no-commit-id --name-only -r HEAD | wc -l | tr -d ' ') # Trivial-prefix check prefix=$(echo "$subject" | sed -E 's/^([a-z]+)(\([^)]+\))?:.*$/\1/') case "$prefix" in meta|docs|style|chore) exit 0 ;; esac echo "$subject" | grep -qE 'fix[[:space:]]+typo' && exit 0 # Threshold body_len=${#body} if [[ "$body_len" -le 200 && "$files" -le 3 ]]; then exit 0; fi # Substantive — emit JSON via python3 to handle quoting safely python3 -c " import json, sys msg = ( 'Substantive commit detected on $cwd: \`' + '''$subject''' + '\` ' + '($files files changed, body $body_len chars). ' + 'Consider invoking session-handoff write-mode to update .tasks/NEXT_SESSION.md.' ) print(json.dumps({ 'hookSpecificOutput': { 'hookEventName': 'PostToolUse', 'additionalContext': msg, }, 'systemMessage': 'session-handoff: substantive commit detected', 'suppressOutput': False, })) "