From 7a1efadf802b1441f12d481e1c5e5f57a4f4820e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 01:31:01 +0000 Subject: [PATCH] Auto-surface the /issues ledger via a SessionStart hook Add .claude/hooks/issues-surface.sh, wired into the existing SessionStart group in .claude/settings.json, so every session starts with the open outstanding-work items already in context (P1/P2 listed, P3 collapsed to a count). On a context reset (compact/resume/clear) it also nudges a /issues capture so in-flight follow-ups aren't lost. The hook is read-only: it never writes or commits the ledger, and always exits 0 so it can't break a session. Documented in the AGENTS.md /issues section. --- .claude/hooks/issues-surface.sh | 91 +++++++++++++++++++++++++++++++++ .claude/settings.json | 4 ++ AGENTS.md | 4 ++ 3 files changed, 99 insertions(+) create mode 100755 .claude/hooks/issues-surface.sh diff --git a/.claude/hooks/issues-surface.sh b/.claude/hooks/issues-surface.sh new file mode 100755 index 000000000..dab08f83d --- /dev/null +++ b/.claude/hooks/issues-surface.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +# SessionStart hook — surface the outstanding-work memory into context. +# +# Reads docs/outstanding-issues.md (the /issues ledger) and prints a compact, +# glanceable summary of the OPEN items so every session starts already aware of +# what is outstanding. When the trigger is a context reset (compact / resume / +# clear) it also emits a reminder to run `/issues capture` — that is the moment +# a session's in-flight follow-ups are most likely to be lost. +# +# Contract: READ-ONLY. Never writes, never commits, never fails a session — it +# always exits 0, and every step is guarded so a parse error just yields less +# output. SessionStart hook stdout is injected into the model's context. +set -uo pipefail + +# --- locate the repo + ledger ------------------------------------------------- +root="${CLAUDE_PROJECT_DIR:-}" +[ -z "$root" ] && root="$(git rev-parse --show-toplevel 2>/dev/null || true)" +[ -z "$root" ] && root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." 2>/dev/null && pwd || true)" +ledger="$root/docs/outstanding-issues.md" +[ -f "$ledger" ] || exit 0 + +# --- read the hook payload (stdin JSON) to learn the trigger source ----------- +payload="$(cat 2>/dev/null || true)" +source_val="$(printf '%s' "$payload" \ + | grep -o '"source"[[:space:]]*:[[:space:]]*"[^"]*"' \ + | head -n1 | sed -E 's/.*"([^"]*)"$/\1/')" + +# --- parse the "Open items" table only --------------------------------------- +# Emit "PRIIDTYPESUMMARY" per open row. Scoped between the +# "## Open items" heading and the next "## " heading so the Resolved/archive +# table (different columns) is never counted. +rows="$(awk ' + /^## Open items/ { inopen=1; next } + /^## / { if (inopen) inopen=0 } + inopen && /^\| #[0-9]/ { + n=split($0, c, "|") + id=c[2]; pri=c[3]; typ=c[4]; sum=c[5] + gsub(/^[ \t]+|[ \t]+$/, "", id) + gsub(/^[ \t]+|[ \t]+$/, "", pri) + gsub(/^[ \t]+|[ \t]+$/, "", typ) + gsub(/^[ \t]+|[ \t]+$/, "", sum) + printf "%s\t%s\t%s\t%s\n", pri, id, typ, sum + } +' "$ledger" 2>/dev/null || true)" + +total="$(printf '%s' "$rows" | grep -c . || true)" +if [ "${total:-0}" -eq 0 ]; then + echo "[issues] Outstanding-work memory (docs/outstanding-issues.md): no open items. Record one with /issues add …" + exit 0 +fi + +group() { printf '%s\n' "$rows" | awk -F'\t' -v p="$1" '$1==p'; } +count() { printf '%s' "$1" | grep -c . || true; } +p1="$(group P1)"; p2="$(group P2)"; p3="$(group P3)" +c1="$(count "$p1")"; c2="$(count "$p2")"; c3="$(count "$p3")" + +echo "[issues] Outstanding-work memory — ${total} open (${c1}×P1, ${c2}×P2, ${c3}×P3). Source of truth: docs/outstanding-issues.md · read the full list back with /issues." + +print_group() { # $1=rows $2=max-to-list + local data="$1" limit="$2" shown=0 more=0 pri id typ sum + [ -z "$data" ] && return 0 + while IFS=$'\t' read -r pri id typ sum; do + [ -z "$pri" ] && continue + if [ "$shown" -lt "$limit" ]; then + echo " ${pri} ${id} ${typ} — ${sum}" + shown=$((shown + 1)) + else + more=$((more + 1)) + fi + done <