feat(board): cross-repo attention board as script + skill (0.9.0)

The mailbox answers "who wrote to me"; it never answered "which repo
deserves the next session". board.sh scans every discovered repo and reads
three sources each: the STATE.md next-step block with its optional board
line, git status, and that repo's pending mailbox count. Read-only by
construction, pinned by board-selftest.sh (28 checks).

It ships here rather than as a personal script because the mailbox is one
of its three inputs and the two carry the same axis distinction: a pending
count means others are waiting on that repo, while who a repo waits ON comes
only from its board line, since the message format has no reply-to field.
Splitting the board from the mailbox would put that distinction in two
places. It also lets the skill resolve the engine through CLAUDE_PLUGIN_ROOT
like every other script here, instead of depending on a file that exists
only in ~/.claude/scripts (a directory with no remote and no backup).

The skill is a ranking, not a report: re-runs the board every invocation
because counts drift, ranks by what unblocks the most and what is cheapest
to move, then names one repo, the rule that fired, and the real next action
read from that repo's STATE.md. Never the table. Not wired into session
start, which would spend context on repos the session is not in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MubwdTi88yu4hVLAFG1LbM
This commit is contained in:
Kjell Tore Guttormsen 2026-07-28 21:15:03 +02:00
commit 61e224ccc3
10 changed files with 691 additions and 13 deletions

View file

@ -8,10 +8,10 @@
*AI-generated: all code produced by Claude Code through dialog-driven development.*
![Version](https://img.shields.io/badge/version-0.8.0-blue)
![Version](https://img.shields.io/badge/version-0.9.0-blue)
![Platform](https://img.shields.io/badge/platform-Claude_Code_Plugin-purple)
![Hooks](https://img.shields.io/badge/hooks-1-green)
![Skills](https://img.shields.io/badge/skills-1-orange)
![Skills](https://img.shields.io/badge/skills-2-orange)
![License](https://img.shields.io/badge/license-MIT-lightgrey)
---
@ -73,7 +73,11 @@ The plugin ships empty: your mailbox is created lazily on first send, on your ma
Since v0.8.0 it closes with one aggregate line about mail pending in *other* mailboxes, so an empty inbox no longer reads as "all clear" while messages sit unanswered elsewhere. Two integers, never a roster: naming the other mailboxes would put their situation inside your repo's injection, and the line explicitly disclaims the obligation it sits beneath — those counts are not yours to handle, and counting them delivered nothing.
**CLI.** The engine is four bash scripts in the plugin's `scripts/` directory; resolve them as `"${CLAUDE_PLUGIN_ROOT:-$HOME/.claude}/scripts/coord-<name>.sh"` (from a terminal, use the plugin's install path):
**Choosing between repos (the `board` skill).** "What should I work on?", "who is waiting on me?", "what unblocks the most?" — `board.sh` scans every repo it can find and reads three sources per repo: the STATE.md next-step block and its optional board line, `git status`, and the pending count in that repo's mailbox. The skill runs it, ranks by leverage (what unblocks the most, cheapest first) and answers with one repo and the rule that fired, never the table. Read-only: it writes to no repo, no STATE.md, and no mailbox. The board is deliberately *not* wired into session start — it runs when asked.
The mailbox is one of its three inputs, which is why the board lives here. Note the axis: a repo's pending count means *others are waiting on it*, an obligation it owes outward. Who a repo waits *on* comes only from its own board line, because the message format has no reply-to field.
**CLI.** The engine is six bash scripts in the plugin's `scripts/` directory; resolve them as `"${CLAUDE_PLUGIN_ROOT:-$HOME/.claude}/scripts/coord-<name>.sh"` (from a terminal, use the plugin's install path):
coord-send.sh --to <repo> --subject "<subject>" [--message "<text>"] # or body on stdin
coord-send.sh --broadcast --subject "<subject>" <<'BODY' ... BODY
@ -82,6 +86,7 @@ Since v0.8.0 it closes with one aggregate line about mail pending in *other* mai
coord-inbox.sh [--repo <name>] # print pending (what the hook injects)
coord-done.sh <filename>... | --all # archive without replying
coord-count.sh [--exclude <mailbox>] # count pending per mailbox, delivering nothing
board.sh [--roots <dir>[,<dir>...]] # cross-repo attention board (read-only)
The reply/resolve hints the hook injects (`-> reply: coord-send --reply-to … | done without reply: coord-done …`) refer to these scripts.
@ -119,7 +124,8 @@ Note that raising the inbox's priority (Rule 7) deliberately does **not** widen
## Development
bash scripts/coord-selftest.sh # 136 checks against a throwaway mailbox
npm test # same selftest via node --test
bash scripts/board-selftest.sh # 28 checks against a throwaway repo tree
npm test # both selftests via node --test
TDD is the house rule: every behavior change lands with a failing selftest check first.