Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .claude/hooks/issues-surface.sh
Original file line number Diff line number Diff line change
@@ -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 "PRI<TAB>ID<TAB>TYPE<TAB>SUMMARY" 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 <<EOF
$data
EOF
[ "$more" -gt 0 ] && echo " … +${more} more at this priority (see /issues)"
return 0
}

# P1 = do-next, list all. P2 = should-do, list up to 8. P3 = collapse to a count.
[ "$c1" -gt 0 ] && print_group "$p1" 999
[ "$c2" -gt 0 ] && print_group "$p2" 8
[ "$c3" -gt 0 ] && echo " ${c3} × P3 (nice-to-have / revisit-when) — see /issues"
Comment on lines +59 to +80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Bound each rendered issue summary.

At Line 65, ${sum} is injected into SessionStart output without truncation. A long ledger summary can overwhelm the “compact” context surface; normalize and cap it with an ellipsis before echoing it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/hooks/issues-surface.sh around lines 59 - 80, Update print_group so
each issue summary in sum is normalized and truncated to a bounded length before
rendering in the echo output. Preserve the existing priority, ID, type,
counting, and “more” behavior, and append an ellipsis when the summary exceeds
the cap.


# --- capture reminder ---------------------------------------------------------
case "$source_val" in
compact | resume | clear)
echo "[issues] Context was just reset (${source_val}). Before this session wraps up, run /issues capture to record any new follow-ups, deferrals, or risks that surfaced so they aren't lost from memory."
;;
*)
echo "[issues] When the work in this session wraps up, offer to run /issues capture for any new follow-ups before the context is lost."
;;
esac
exit 0
4 changes: 4 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
{
"type": "command",
"command": "node \"$CLAUDE_PROJECT_DIR/scripts/check-base-freshness.mjs\""
},
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/issues-surface.sh\""
}
]
}
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ not, so anything worth remembering after a session ends belongs there.
`docs/outstanding-issues.md` (no push unless the user asks or you are already handing off).
- Proactively offer to `capture` unresolved follow-ups, deferrals, and known risks into the ledger
before a session's context is lost — that is what keeps it a memory rather than a stale list.
- A `SessionStart` hook (`.claude/hooks/issues-surface.sh`, wired in `.claude/settings.json`)
auto-surfaces the open items into context at the start of every session and, on a context reset
(`compact`/`resume`/`clear`), nudges a `/issues capture`. It is read-only — it never writes the
ledger. `/issues` is still the way to read the full list or mutate it.

## Codex GitHub review behavior

Expand Down
Loading