summaryrefslogtreecommitdiff
path: root/skills/pi-subagent/status.sh
diff options
context:
space:
mode:
authorIgor Soarez <igor@soarez.org>2026-10-21 12:23:28 +0100
committerIgor Soarez <igor@soarez.org>2026-10-21 12:23:28 +0100
commit32123f167dd5a7cdcbdaf6e5c144f40654d236fc (patch)
treeeb2b2585d2f7cb14b03d5a895f17a962821ade52 /skills/pi-subagent/status.sh
parent485aec099b56f2e24fa04600e85d7aba8d66f3fd (diff)
skills: pi-subagent, extracted from pi-review-panel, which now runs on it
The panel's mechanics -- headless pi runs that don't hang, answer extraction, billed cost, retries, verifying what comes back -- are not review-specific. pi-subagent covers one delegated task, one brief fanned out to several models, or a job split into parallel parts; pi-review-panel keeps only what a review adds. run-agent.sh replaces run-member.sh and fixes what it got wrong: - `timeout` is not on a Mac without coreutils, which failed every member with exit 127; it falls back to gtimeout, then perl's alarm (142 -> 124). - pi exits 0 when the provider refuses the request, or the answer is empty; both are now exit 1, with stopReason and error in the cost file. - a retry moves the earlier attempt to <name>.attempt<N>.* instead of overwriting its cost file. New options: --tools (an allowlist; read,grep,find,ls is read-only by construction), --cwd, --context-files, --brief, --deliver. status.sh replaces the `pgrep -f run-member.sh` loop, which counted every panel on the machine, and prints the cost table. From dot request 20261021-114826-work-laptop-2b868c.
Diffstat (limited to 'skills/pi-subagent/status.sh')
-rwxr-xr-xskills/pi-subagent/status.sh85
1 files changed, 85 insertions, 0 deletions
diff --git a/skills/pi-subagent/status.sh b/skills/pi-subagent/status.sh
new file mode 100755
index 0000000..15a75de
--- /dev/null
+++ b/skills/pi-subagent/status.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+# Report on the subagents of one workdir: what is still running, and the
+# cost table of everything that finished, failed attempts included.
+#
+# status.sh <workdir> report once
+# status.sh --wait [secs] <workdir> report every <secs> (default 60) until
+# nothing is running, then the table
+#
+# Exit status: 0 when nothing is running, 3 while something still is.
+#
+# "Running" means <workdir>/out/<name>.running exists and its PID is alive. A
+# marker whose process is gone (run-agent.sh was killed) is reported as stale
+# and removed.
+
+set -u
+
+wait=""; interval=60
+if [[ "${1:-}" == "--wait" ]]
+then
+ wait=y; shift
+ if [[ "${1:-}" =~ ^[0-9]+$ ]]; then interval="$1"; shift; fi
+fi
+P="${1:?workdir}"
+out="$P/out"
+
+running() {
+ local marker pid alive=()
+ for marker in "$out"/*.running
+ do
+ [[ -e "$marker" ]] || continue
+ pid="$(cat "$marker" 2>/dev/null)"
+ if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null
+ then
+ alive+=("$(basename "$marker" .running)")
+ else
+ echo "stale: $(basename "$marker" .running) (process gone)" >&2
+ rm -f "$marker"
+ fi
+ done
+ echo "${alive[*]:-}"
+}
+
+free_gb() {
+ df -k "$P" | awk 'NR==2 {printf "%.1f", $4/1048576}'
+}
+
+if [[ -n "$wait" ]]
+then
+ while :
+ do
+ names="$(running)"
+ [[ -z "$names" ]] && break
+ echo "$(date +%H:%M:%S) running=$(wc -w <<< "$names" | tr -d ' ') freeGB=$(free_gb): $names"
+ sleep "$interval"
+ done
+fi
+
+names="$(running)"
+if [[ -n "$names" ]]
+then
+ echo "running: $names"
+fi
+
+shopt -s nullglob
+costs=("$out"/*.cost.json)
+if [[ ${#costs[@]} -gt 0 ]]
+then
+ {
+ echo -e "name\tprovider\tmodel\texit\tstop\tsecs\tturns\tin\tout\tanswer_bytes\tcost"
+ for f in "${costs[@]}"
+ do
+ n="$(basename "$f" .cost.json)"
+ bytes=0
+ [[ -e "$out/$n.md" ]] && bytes="$(wc -c < "$out/$n.md" | tr -d '[:space:]')"
+ jq -r --arg b "$bytes" '[.name,.provider,.model,(.exit|tostring),(.stopReason // "-"),(.seconds|tostring),
+ (.turns|tostring),(.input|tostring),(.output|tostring),$b,
+ (.cost*10000|round/10000|tostring)] | @tsv' "$f" 2>/dev/null \
+ || echo -e "$n\t?\t?\t?\t?\t?\t?\t?\t?\t$bytes\t(unreadable cost file)"
+ done | sort -t$'\t' -k11 -rn
+ } | column -t -s$'\t'
+ jq -s -r '"runs: \(length) failed: \(map(select(.exit != 0)) | length) total cost: $\(map(.cost) | add | .*10000 | round/10000)"' \
+ "${costs[@]}" 2>/dev/null
+fi
+
+[[ -z "$names" ]] || exit 3