feat(@scripts/session-tools): update priority convention to p0-p4 triage system

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-05-17 07:23:49 -07:00
parent 4daae305ab
commit a5cb5d74c1
2 changed files with 166 additions and 12 deletions

View file

@ -142,7 +142,7 @@ SYSTEM_PROMPT = """You triage Claude Code coding sessions. For each session in t
- ref_index: integer matching the input's ref_index
- summary: ONE short sentence describing what's happening
- status: one of done, in_progress, blocked, waiting_on_user, abandoned
- priority: integer 1-5 (5 = critical to resume now, 1 = abandonable)
- priority: integer 0-4 (0 = critical to resume now, 4 = abandonable). Follows the P0/P1 incident convention — LOWER number means HIGHER priority.
- next_action: ONE short imperative phrase, or empty string if status is done/abandoned
Output ONLY a JSON array. No markdown, no prose."""
@ -217,7 +217,8 @@ async def main_async(args: argparse.Namespace) -> None:
# the cache BEFORE reading the JSONL (the expensive part for a triage run
# over hundreds of sessions where most are unchanged).
cache = ResponseCache(CACHE_DIR)
template_id = "rclaude-triage-v1"
# v2: priority scale flipped to P0=critical / P4=abandonable convention.
template_id = "rclaude-triage-v2"
cached_results: list[dict] = []
uncached: list[tuple[int, Path, str]] = []
for mtime, jsonl in candidates:
@ -288,14 +289,17 @@ async def main_async(args: argparse.Namespace) -> None:
await client.close()
results.extend(new_results)
# Lower priority number = higher importance (P0 convention). Sort
# ascending by priority, descending by mtime so the most-recent within
# each priority bucket floats up.
def sort_key(r: dict) -> tuple[int, int]:
try:
prio = int(r.get("priority", 0))
prio = int(r.get("priority", 9))
except (TypeError, ValueError):
prio = 0
return (prio, int(r.get("mtime", 0)))
prio = 9
return (prio, -int(r.get("mtime", 0)))
for r in sorted(results, key=sort_key, reverse=True):
for r in sorted(results, key=sort_key):
line = "\t".join(
[
str(r.get("mtime", 0)),

View file

@ -326,6 +326,121 @@ _REMOTE_TRIAGE_BOOT='export PATH=$HOME/.local/bin:/opt/homebrew/bin:$PATH; '"$_P
# work itself is already protected by tmux on the remote.
_SSH_LIVE_OPTS='-o ServerAliveInterval=30 -o ServerAliveCountMax=6 -o TCPKeepAlive=yes'
# ---------------------------------------------------------------------------
# Setup / dependency auto-install
# ---------------------------------------------------------------------------
# Cache dir for per-host setup markers. A marker file means we've already
# probed + installed deps on that host within RCLAUDE_SETUP_TTL days.
_SETUP_CACHE_DIR=${XDG_CACHE_HOME:-$HOME/.cache}/rclaude
_SETUP_TTL_DAYS=${RCLAUDE_SETUP_TTL:-7}
# Detect package-manager family on <host>. Output: macos | rhel | debian | unknown.
detect_os_on() {
_h=$1
_probe='if [ "$(uname -s)" = "Darwin" ]; then echo macos
elif command -v dnf >/dev/null 2>&1; then echo rhel
elif command -v apt-get >/dev/null 2>&1; then echo debian
else echo unknown; fi'
if is_local "$_h"; then
sh -c "$_probe" 2>/dev/null
else
ssh -o BatchMode=yes -o ConnectTimeout=5 "$_h" "$_probe" 2>/dev/null
fi
}
# Install <pkgs> on <host> using its native package manager. Uses sudo for
# system pkgs on Linux; brew (no sudo) on macOS.
install_pkgs_on() {
_h=$1; _os=$2; shift 2
_pkgs=$*
[ -z "$_pkgs" ] && return 0
case $_os in
macos) _cmd="brew install $_pkgs" ;;
rhel) _cmd="sudo dnf install -y $_pkgs" ;;
debian) _cmd="sudo apt-get update && sudo apt-get install -y $_pkgs" ;;
*)
echo "rclaude: don't know how to install $_pkgs on $_h ($_os) — do it manually" >&2
return 1 ;;
esac
printf 'rclaude: installing on %s: %s\n' "$_h" "$_pkgs" >&2
if is_local "$_h"; then
sh -c "$_cmd" >&2
else
ssh -t "$_h" "$_cmd" >&2
fi
}
# Run a probe command on <host>, return its stdout. Used by setup_host.
_probe_on() {
_h=$1; _cmd=$2
if is_local "$_h"; then sh -c "$_cmd" 2>/dev/null
else ssh -o BatchMode=yes -o ConnectTimeout=5 "$_h" "$_cmd" 2>/dev/null
fi
}
# Idempotently install rclaude's deps on <host>. Honors a per-host marker so
# we don't re-probe on every invocation. Pass `force` to bypass the marker.
setup_host() {
_h=$1; _force=${2:-}
mkdir -p "$_SETUP_CACHE_DIR" 2>/dev/null
_marker_id=$(printf %s "$_h" | tr -c 'A-Za-z0-9' '_')
_marker="$_SETUP_CACHE_DIR/setup-$_marker_id"
if [ "$_force" != "force" ] && [ -f "$_marker" ]; then
# Marker exists and is recent enough → assume deps are fine.
if [ -z "$(find "$_marker" -mtime +"$_SETUP_TTL_DAYS" 2>/dev/null)" ]; then
return 0
fi
fi
_os=$(detect_os_on "$_h")
if [ "$_os" = "unknown" ] || [ -z "$_os" ]; then
echo "rclaude: couldn't detect OS on $_h; skipping setup" >&2
return 0
fi
# Probe system binaries.
_missing=""
_have=$(_probe_on "$_h" 'for c in tmux rsync mosh; do command -v "$c" >/dev/null 2>&1 && echo "$c"; done')
for c in tmux rsync mosh; do
case " $_have " in *" $c "*) ;; *) _missing="$_missing $c" ;; esac
done
if [ -n "$_missing" ]; then
install_pkgs_on "$_h" "$_os" $_missing || true
fi
# Python SDK for triage. Try to install per-user without sudo.
_has_sdk=$(_probe_on "$_h" 'for p in python3.13 python3.12 python3.11 python3; do b=$(command -v "$p" 2>/dev/null) || continue; "$b" -c "import claude_code_batch_sdk" 2>/dev/null && echo "$b" && break; done')
if [ -z "$_has_sdk" ]; then
_pick=$(_probe_on "$_h" 'for p in python3.12 python3.11 python3; do command -v "$p" 2>/dev/null && break; done | head -1')
if [ -n "$_pick" ]; then
printf 'rclaude: installing claude-code-batch-sdk via %s on %s\n' "$_pick" "$_h" >&2
if is_local "$_h"; then
"$_pick" -m pip install --user --quiet claude-code-batch-sdk >&2 || true
else
ssh "$_h" "$_pick -m pip install --user --quiet claude-code-batch-sdk" >&2 || true
fi
fi
fi
touch "$_marker" 2>/dev/null
}
# Prefer mosh when available on both ends — unless explicitly disabled via
# RCLAUDE_TRANSPORT=ssh. Echoes "mosh" or "ssh". Caches result per host.
pick_transport() {
_h=$1
case ${RCLAUDE_TRANSPORT:-auto} in
ssh) echo ssh; return ;;
mosh) echo mosh; return ;;
esac
if ! command -v mosh >/dev/null 2>&1; then echo ssh; return; fi
_cache="/tmp/rclaude-transport.$(whoami).$(printf %s "$_h" | tr -c 'A-Za-z0-9' '_')"
if [ -s "$_cache" ]; then cat "$_cache"; return; fi
if _probe_on "$_h" 'command -v mosh-server >/dev/null 2>&1' | grep -q . \
|| _probe_on "$_h" 'command -v mosh-server' >/dev/null; then
echo mosh > "$_cache"; echo mosh
else
echo ssh > "$_cache"; echo ssh
fi
}
# Run the triage helper on <host> with the supplied extra args. Stdout is the
# raw TSV emitted by _claude-triage (one row per session).
list_triage_on() {
@ -488,7 +603,7 @@ cmd_resume() {
# Re-sort globally by (priority desc, mtime desc) so the top of the
# picker is the actual highest priority across the fleet.
_triage=$(scan_hosts | while IFS= read -r h; do list_triage_on "$h"; done \
| sort -t"$(printf '\t')" -k4,4nr -k9,9nr)
| sort -t"$(printf '\t')" -k4,4n -k9,9nr)
_t_count=0
[ -n "$_tmux" ] && _t_count=$(printf '%s\n' "$_tmux" | wc -l | tr -d ' ')
[ -n "$_triage" ] && _d_total=$(printf '%s\n' "$_triage" | wc -l | tr -d ' ')
@ -566,6 +681,9 @@ cmd_resume() {
_R=$(printf '\033[0m')
_Chost=$(printf '\033[36m'); _Ctmux=$(printf '\033[1;32m')
_Cdim=$(printf '\033[2m'); _Ckey=$(printf '\033[1;35m')
# Triage uses P0=critical / P4=abandonable (P0 incident convention).
# _Cp5 / _Cp4 names are kept for diff size; what they color is the
# *top two* priority levels, whatever the scale is.
_Cp5=$(printf '\033[1;31m'); _Cp4=$(printf '\033[33m')
_Cblk=$(printf '\033[31m'); _Cwait=$(printf '\033[33m')
_Cinp=$(printf '\033[36m'); _Cdone=$(printf '\033[32m')
@ -577,7 +695,7 @@ cmd_resume() {
# same kind. Kind is now encoded by row shape: tmux has a ▶ marker,
# triage shows P<n> + status, session shows the snippet plain.
_fmt_row='
function prio_c(p) { if (p=="5") return c_p5; if (p=="4") return c_p4; return "" }
function prio_c(p) { if (p=="0") return c_p5; if (p=="1") return c_p4; return "" }
function stat_c(s) {
if (s=="blocked") return c_blk
if (s=="waiting_on_user") return c_wait
@ -700,6 +818,10 @@ cmd_resume() {
if is_local "$_host"; then
exec tmux attach -t "$_target"
else
setup_host "$_host"
if [ "$(pick_transport "$_host")" = "mosh" ]; then
exec mosh "$_host" -- tmux attach -t "$_target"
fi
exec ssh -t $_SSH_LIVE_OPTS "$_host" tmux attach -t "$_target"
fi
;;
@ -765,11 +887,33 @@ cmd_version() {
fi
}
cmd_setup() {
# Args:
# (none) → install on every host in scan_hosts
# <host> [<host>...] → install on each named host
# --on <host> → install on a single host (parity with `resume --on`)
_hosts=""
while [ $# -gt 0 ]; do
case $1 in
--on) shift; _hosts="$_hosts $1"; shift ;;
--on=*) _hosts="$_hosts ${1#--on=}"; shift ;;
*) _hosts="$_hosts $1"; shift ;;
esac
done
if [ -z "$_hosts" ]; then
scan_hosts | while IFS= read -r h; do setup_host "$h" force; done
else
for h in $_hosts; do setup_host "$h" force; done
fi
}
case ${1:-} in
list) shift; cmd_list "$@"; exit ;;
resume) shift; cmd_resume "$@"; exit ;;
triage) shift; cmd_triage "$@"; exit ;;
setup) shift; cmd_setup "$@"; exit ;;
-v|--version) cmd_version; exit ;;
-h|--help|help) cmd_help; exit ;;
esac
# ---------------------------------------------------------------------------
@ -912,10 +1056,16 @@ if ! ssh -o BatchMode=yes -o ConnectTimeout=5 "$host" "test -d ${dir}" 2>/dev/nu
fi
fi
setup_host "$host"
sync_tmux_conf "$host"
inner=$(build_inner "$dir")
# `new-session -A` attaches if a session of that name already exists, so
# re-running rclaude after a broken pipe lands you back in the same tmux
# session instead of erroring with "duplicate session". Combined with
# _SSH_LIVE_OPTS this tolerates short network drops without losing work.
# Mosh is preferred when available (handles sleep/roam/long blips natively);
# falls back to ssh+keepalives otherwise.
if [ "$(pick_transport "$host")" = "mosh" ]; then
exec mosh "$host" -- tmux new-session -A -s "${session}" "${inner}"
fi
exec ssh -t $_SSH_LIVE_OPTS "$host" "tmux new-session -A -s '${session}' \"${inner}\""