#!/bin/bash # coord-count.sh - count PENDING directed messages per mailbox WITHOUT # delivering anything. Prints one "\t" line per mailbox that # has unhandled mail, sorted by name; prints nothing when none do. # # WHY THIS IS NOT coord-inbox.sh --repo : reading IS delivery. The read path # prints a broadcast and then records it as seen, so asking it "what is pending # for x" would consume x's broadcast backlog as a side effect - once, silently, # and unrecoverably (the seen set is delivery history, and retraction # deliberately leaves it alone). This script only counts files. # # It keys on MAILBOXES, not on repos: it enumerates $COORD/* and never scans a # filesystem for checkouts. A repo without a mailbox has no pending messages by # definition - it is not missing from the count, it is absent from the domain. # # Usage: coord-count.sh [--exclude ] # --exclude omit one mailbox (the caller's own, whose inbox is # already injected in full). # Env: CLAUDE_COORD_DIR overrides the mailbox root. # Exit: always 0 - this runs at session start and must never fail one. # ASCII only, bash 3.2 safe. set -u export LC_ALL=C COORD="${CLAUDE_COORD_DIR:-$HOME/.claude/coord}" EXCLUDE="" while [ $# -gt 0 ]; do case "$1" in # bash 3.2: `shift 2` past the end of $# is a no-op -> would loop forever. --exclude) [ $# -ge 2 ] || { echo "coord-count: --exclude requires a value" >&2; exit 2; } EXCLUDE="$2"; shift 2 ;; -h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; # Lenient but not silent, exactly as coord-inbox.sh: failing here would fail # a SessionStart over a stray flag, and staying silent would make a typo # look like a working invocation. The hook discards stderr. *) echo "coord-count: unknown argument: $1 (ignored)" >&2; shift ;; esac done [ -d "$COORD" ] || exit 0 # Glob expansion under LC_ALL=C is already name-sorted. An unmatched glob # expands to the literal pattern, which fails the -d test and is skipped. for d in "$COORD"/*; do [ -d "$d" ] || continue name="$(basename "$d")" # Reserved engine namespace (_broadcast): storage, not a correspondent. case "$name" in _*) continue ;; esac [ -n "$EXCLUDE" ] && [ "$name" = "$EXCLUDE" ] && continue [ -d "$d/inbox" ] || continue # *.md is the message grammar; a stray file must not inflate a total the # operator reads as "replies owed". n="$(ls "$d/inbox"/*.md 2>/dev/null | wc -l | tr -d ' ')" [ -n "$n" ] || n=0 [ "$n" -gt 0 ] || continue # Absent, not zero: the question is "who is owed a reply", and a list of # zeroes answers a different one at every reader's expense. printf '%s\t%s\n' "$name" "$n" done exit 0