Status line: which plugin this session is working on
Several builder sessions run at once, each in its own worktree, and from the outside every one looks like a terminal printing tool calls. Asking "which plugin are you on?" cost a round trip every time. doxa-maps · bundler-2/work · ▶ research-map-plus-grid (cap-4) What it reports, in order: the bundle with the most recently modified UNCOMMITTED file (that is what "working on" means); else library/ or internal/ named as a scope, because an edit there is not one plugin's business and naming whichever bundle happens to be dirty would be a lie; else the last plugin touched in the recent commits, so the line still says something true at the moment work lands instead of going blank. The capability tier is read from manifest.json with the studio's own formula (5 - PT[type], plugin-studio.vue:293), not a table retyped here. Two copies of that mapping with nothing forcing agreement is the shape check:capability-citations exists to catch — the README once captioned a bundle cap-1 while the manifest said cap-2. Details that were wrong on the first cut and are now measured: the repo name comes from the remote, not the checkout directory, or every session gets labelled with a worktree name nobody recognises (wt-bundler-2); and the commit fallback uses `git log -m`, without which a MERGE commit lists no files and the line read "idle" immediately after landing work. Fail-soft throughout and never exits non-zero: no git, no branch; no manifest, no tier; nothing edited, "idle". A status line runs on every render, so one that errors or prints a stack trace is worse than none. ~172ms per render, no absolute paths (check:no-abs-paths). Verified across all five tiers plus the library/ and internal/ scopes; 7/7 pre, 18/18 post.
This commit is contained in:
parent
8653147cfc
commit
dd81a8818c
2 changed files with 107 additions and 0 deletions
7
.claude/settings.json
Normal file
7
.claude/settings.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"statusLine": {
|
||||
"type": "command",
|
||||
"command": "bash .claude/statusline-plugin.sh",
|
||||
"padding": 0
|
||||
}
|
||||
}
|
||||
100
.claude/statusline-plugin.sh
Executable file
100
.claude/statusline-plugin.sh
Executable file
|
|
@ -0,0 +1,100 @@
|
|||
#!/usr/bin/env bash
|
||||
# statusline-plugin.sh — "which plugin is this agent working on?", in one terminal line.
|
||||
#
|
||||
# WHY: several builder agents run at once against this repo, each in its own worktree, and from the
|
||||
# outside every one of them looks like a Claude terminal printing tool calls. Asking "which plugin
|
||||
# are you on?" cost a round trip every time. This answers it continuously, without being asked.
|
||||
#
|
||||
# Renders: doxa-maps · bundler-2/work · ▶ research-map-plus-grid (cap-4)
|
||||
#
|
||||
# ★ THE TIER COMES FROM THE MANIFEST, NOT FROM A TABLE RETYPED HERE.
|
||||
# plugin-studio.vue derives every badge with PT/capFor off the manifest's profile `type`, so
|
||||
# retyping a profile there moves its badge with zero edits. A second hardcoded copy of that
|
||||
# mapping in this script would be a fact stored twice with nothing forcing agreement — the exact
|
||||
# shape check:capability-citations exists to catch (the README once captioned a bundle as cap-1
|
||||
# while the manifest said cap-2). So this reads manifest.json and applies 5 - PT[type], the one
|
||||
# formula, to the one source.
|
||||
#
|
||||
# ★ FAIL-SOFT EVERYWHERE, ON PURPOSE. A status line runs on every render. If it errors, is slow, or
|
||||
# prints a stack trace, it makes the terminal worse than having none — so every step degrades to a
|
||||
# quieter line instead of failing: no git → no branch, no manifest → no tier, nothing edited → "idle".
|
||||
# It never exits non-zero.
|
||||
#
|
||||
# Wired up by .claude/settings.json (statusLine). No absolute paths: check:no-abs-paths requires a
|
||||
# clone to work on any machine, and a status line is exactly the sort of dotfile that quietly
|
||||
# hardcodes someone's home directory.
|
||||
set -uo pipefail
|
||||
|
||||
IN=$(cat 2>/dev/null || true)
|
||||
# Claude Code hands the hook a JSON blob; workspace.current_dir is the directory the session is in.
|
||||
# Parsed with sed rather than a JSON tool because this runs on every render and must stay cheap.
|
||||
DIR=$(printf '%s' "$IN" | sed -n 's/.*"current_dir"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
|
||||
[ -z "$DIR" ] && DIR=$(printf '%s' "$IN" | sed -n 's/.*"cwd"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
|
||||
[ -z "$DIR" ] && DIR="$PWD"
|
||||
cd "$DIR" 2>/dev/null || true
|
||||
|
||||
ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || ROOT=""
|
||||
if [ -n "$ROOT" ]; then cd "$ROOT" 2>/dev/null || true; fi
|
||||
# The repo NAME, not the checkout's directory name: every builder works in its own worktree
|
||||
# (wt-bundler-2, wt-bundler-1, ...), and printing the folder would label each agent with a name
|
||||
# nobody recognises instead of the project they are all in. The remote is the stable identity.
|
||||
REPO=$(basename -s .git "$(git config --get remote.origin.url 2>/dev/null)" 2>/dev/null)
|
||||
[ -z "$REPO" ] && REPO=$(basename "${ROOT:-$PWD}")
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
||||
|
||||
# WHAT IS BEING WORKED ON — uncommitted work first, because that is what "working on" means.
|
||||
# Ordered by mtime, not by git's alphabetical listing: with several bundles dirty, the one touched
|
||||
# most recently is the honest answer and the alphabetical first is an arbitrary one.
|
||||
DIRTY=$(git status --porcelain 2>/dev/null | sed 's/^...//' | sed 's/^.* -> //')
|
||||
BUNDLE=$(printf '%s\n' "$DIRTY" | grep '^plugin-designs/' 2>/dev/null | xargs -r ls -td 2>/dev/null | head -1 | cut -d/ -f2)
|
||||
|
||||
SCOPE=""
|
||||
if [ -z "$BUNDLE" ]; then
|
||||
# A library/ edit is not one plugin's business — it compiles into every bundle that imports it,
|
||||
# which is the blast radius AGENT-BRIEF makes you name before changing anything in there. Say so
|
||||
# rather than naming whichever bundle happens to be dirty for an unrelated reason.
|
||||
if printf '%s\n' "$DIRTY" | grep -q '^library/'; then
|
||||
SCOPE="library (reaches every bundle)"
|
||||
elif printf '%s\n' "$DIRTY" | grep -q '^internal/'; then
|
||||
SCOPE="internal (build + gates)"
|
||||
else
|
||||
# Nothing uncommitted: fall back to the last commit that touched a plugin, so a line still says
|
||||
# something true right after a commit instead of going blank at the moment work lands.
|
||||
# -m makes a MERGE commit list its files: without it a merge reports nothing changed, so the
|
||||
# line read "idle" immediately after landing work — the least accurate moment to say so. Look
|
||||
# back a few commits too, since the tip is often a merge or a build-output commit.
|
||||
BUNDLE=$(git log -6 --name-only --format= -m 2>/dev/null | grep -m1 '^plugin-designs/' | cut -d/ -f2)
|
||||
[ -n "$BUNDLE" ] && SCOPE="" || SCOPE="idle"
|
||||
fi
|
||||
fi
|
||||
|
||||
TIER=""
|
||||
if [ -n "$BUNDLE" ] && [ -f public/doxa-maps/manifest.json ]; then
|
||||
TIER=$(python3 - "$BUNDLE" <<'PY' 2>/dev/null || true
|
||||
import json, sys
|
||||
# The studio's own table (plugin-studio.vue:293) — tier = 5 - PT[type]; unknown types are cap-1.
|
||||
PT = {'nested-mfe-spa': 0, 'nested-elements-mfe': 1, 'monolithic': 2, 'parameterized': 3, 'single': 4}
|
||||
try:
|
||||
m = json.load(open('public/doxa-maps/manifest.json'))
|
||||
want = sys.argv[1]
|
||||
caps = [5 - PT[p.get('type')] if p.get('type') in PT else 1
|
||||
for b in m.get('bundles', []) if b.get('bundle') == want
|
||||
for p in b.get('profiles', [])]
|
||||
# A bundle may ship several profiles at different tiers; the HEADLINE capability is the highest
|
||||
# one it demonstrates, which is how the studio's left rail groups it.
|
||||
if caps: print(max(caps))
|
||||
except Exception:
|
||||
pass
|
||||
PY
|
||||
)
|
||||
fi
|
||||
|
||||
OUT="$REPO"
|
||||
[ -n "$BRANCH" ] && OUT="$OUT · $BRANCH"
|
||||
if [ -n "$BUNDLE" ]; then
|
||||
if [ -n "$TIER" ]; then OUT="$OUT · ▶ $BUNDLE (cap-$TIER)"; else OUT="$OUT · ▶ $BUNDLE"; fi
|
||||
elif [ -n "$SCOPE" ]; then
|
||||
OUT="$OUT · ▶ $SCOPE"
|
||||
fi
|
||||
printf '%s\n' "$OUT"
|
||||
exit 0
|
||||
Loading…
Reference in a new issue