From b36e157d56195f156d19403dd52c3c1d130e08ce Mon Sep 17 00:00:00 2001 From: Laurance Walden <6620582+lwalden@users.noreply.github.com> Date: Tue, 14 Apr 2026 21:20:51 -0700 Subject: [PATCH] chore: sync AAM to v4.4.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Propagates ADR-004 context cycling hook chain, dispatch contract Phase 0+1 (sprint-master dispatch mode), and B-005 sync safety fix. New scripts: session-end-cycle.sh, session-start-continuation.sh Updated: all .claude/agents, rules, scripts, skills Merged: settings.json — adds SessionEnd hook + dual SessionStart entries (startup matcher for continuation injection, default for active-sprint detection) Co-Authored-By: Claude Sonnet 4.6 --- .claude/aiagentminder-version | 2 +- .claude/scripts/session-end-cycle.sh | 108 ++++++++++++++++++ .claude/scripts/session-start-continuation.sh | 53 +++++++++ 3 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 .claude/scripts/session-end-cycle.sh create mode 100644 .claude/scripts/session-start-continuation.sh diff --git a/.claude/aiagentminder-version b/.claude/aiagentminder-version index 8089590..fdc6698 100644 --- a/.claude/aiagentminder-version +++ b/.claude/aiagentminder-version @@ -1 +1 @@ -4.3.0 +4.4.0 diff --git a/.claude/scripts/session-end-cycle.sh b/.claude/scripts/session-end-cycle.sh new file mode 100644 index 0000000..4edb464 --- /dev/null +++ b/.claude/scripts/session-end-cycle.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +# session-end-cycle.sh — SessionEnd hook. Writes .sprint-continuation.md +# when the session is ending under context-cycle pressure. +# +# Reads hook input JSON on stdin. The SessionEnd hook receives a "reason" +# field (clear | resume | logout | prompt_input_exit | bypass_permissions_disabled | other). +# We don't care about the reason for the cycle decision — we care only whether +# .context-usage says should_cycle=true at the moment of exit. If yes, build +# a continuation file from external state (git + SPRINT.md + tasks) and drop +# the signal file that sprint-runner.ps1 or the profile prompt hook watches for. +# +# Must be FAST (no network, no heavy work) and must never block the exit. +# +# Configured in .claude/settings.json: +# "hooks": { "SessionEnd": [{ "matcher": "", "hooks": [{ "type": "command", +# "command": "bash .claude/scripts/session-end-cycle.sh" }] }] } + +set -euo pipefail + +# Consume stdin (hook input) but we don't need to parse it for this hook — +# the decision is based purely on .context-usage state. +input=$(cat 2>/dev/null || true) +: "$input" + +USAGE_FILE=".context-usage" +CONT_FILE=".sprint-continuation.md" +SIGNAL_FILE=".sprint-continue-signal" + +# No usage file = no cycle in progress = silent exit +if [ ! -f "$USAGE_FILE" ]; then + exit 0 +fi + +# Need jq to parse should_cycle +if ! command -v jq >/dev/null 2>&1; then + exit 0 +fi + +should_cycle=$(jq -r '.should_cycle // false' "$USAGE_FILE" 2>/dev/null || echo "false") +if [ "$should_cycle" != "true" ]; then + # Normal exit, not a cycle. Leave no trace. + exit 0 +fi + +# --- Cycle IS in progress. Build continuation from external state. --- + +used_tokens=$(jq -r '.used_tokens // "unknown"' "$USAGE_FILE" 2>/dev/null || echo unknown) +used_pct=$(jq -r '.used_pct // "unknown"' "$USAGE_FILE" 2>/dev/null || echo unknown) +threshold=$(jq -r '.threshold // "unknown"' "$USAGE_FILE" 2>/dev/null || echo unknown) +model=$(jq -r '.model // "unknown"' "$USAGE_FILE" 2>/dev/null || echo unknown) + +timestamp=$(date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || echo "unknown") +branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "detached") +head_sha=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") +git_status=$(git status --short 2>/dev/null || echo "(no git)") +recent_commits=$(git log --oneline -10 2>/dev/null || echo "(no history)") + +sprint_block="(no SPRINT.md)" +if [ -f "SPRINT.md" ]; then + sprint_block=$(cat SPRINT.md) +fi + +# Task state (native Claude Code tasks live under ~/.claude/tasks/) +# We don't know the exact path here, so just note its existence. +task_note="(task store not inspected by this hook — TaskList in the next session will show them)" + +cat > "$CONT_FILE" < "$SIGNAL_FILE" + +exit 0 diff --git a/.claude/scripts/session-start-continuation.sh b/.claude/scripts/session-start-continuation.sh new file mode 100644 index 0000000..c5ebb31 --- /dev/null +++ b/.claude/scripts/session-start-continuation.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# session-start-continuation.sh — SessionStart hook. If a continuation file +# exists from a prior context-cycle exit, inject its contents into the new +# session via stdout (Claude Code pipes hook stdout into the session context). +# +# Consume-once semantics: after reading, delete the continuation file and +# signal file so a subsequent session doesn't see stale state. +# +# Configured in .claude/settings.json with matcher "startup": +# "hooks": { "SessionStart": [{ "matcher": "startup", "hooks": [{ +# "type": "command", +# "command": "bash .claude/scripts/session-start-continuation.sh" }] }] } +# +# Matcher MUST be "startup", not "resume" — "resume" is for restoring a prior +# conversation's message history, which is the opposite of a context cycle. + +set -euo pipefail + +CONT_FILE=".sprint-continuation.md" +SIGNAL_FILE=".sprint-continue-signal" + +# Consume stdin (hook input JSON) silently — we don't need it for this hook. +cat >/dev/null 2>&1 || true + +# No continuation file = nothing to inject = silent exit +if [ ! -f "$CONT_FILE" ]; then + exit 0 +fi + +# Emit the continuation content wrapped in a clear banner so Claude recognizes +# it as prior-session state and not user input. +cat </dev/null || true + +exit 0