Files
boshu2__agentops/scripts/rpi-stream.sh
T
Bo 922b7c908a fix(scripts): clean 3 more pre-existing shellcheck warnings (round 2, soc-ruvk) (#353)
## Summary

Round 2 of pre-existing shellcheck cleanup. PR #349 (soc-j026) cleaned 6
files; the full pre-push gate's broader shellcheck step caught 3 more
pre-existing warnings on canonical post-#350.

Closes: soc-ruvk
Discovered-from: soc-j026

## Fixes

| File | Warning | Fix |
|---|---|---|
| `rpi-stream.sh:177` | SC2154 'p referenced but not assigned' inside
trap body | disable directive (`p` is the loop variable in the
single-quoted trap, not an outer reference) |
| `smoke-test-codex-skills.sh:125,141` | SC2088 tilde-in-quotes |
disable directives (intentional literal pattern match of the documented
path) |
| `cleanup-global-agents.sh:22` | SC2034 `LEAKED_DIRS` unused | convert
dead array to comment (script uses `ALLOWED_DIRS` whitelist; LEAKED_DIRS
was orphaned documentation) |

## Bundled sync

Includes `cp CHANGELOG.md docs/CHANGELOG.md` — same pre-existing sync
drift from PR #351 that PR #352 also fixes. Bundling lets this PR's gate
pass independently of #352's merge order. If #352 lands first, this
becomes a trivial conflict-resolve.

## Validation

- `shellcheck -S warning` clean on all 3 named files.
- `scripts/ship.sh` fast gate: PASSED.
- This is the same coherent-arc shape as PR #349 — different file set,
identical disposition (atomic, mechanical, no behavioral change).

Bounded-context: BC0-foundations (scripts)
Evidence: `shellcheck -S warning
scripts/{rpi-stream,smoke-test-codex-skills,cleanup-global-agents}.sh`
(clean)

Co-authored-by: Codex <codex@example.invalid>
2026-05-19 13:44:32 +00:00

185 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# rpi-stream.sh
# Tail/filter structured per-worker runtime output written by tmux executor.
#
# Log source files:
# .agents/rpi/runs/<run-id>/phase-<n>-exit.w<k>.jsonl
usage() {
cat <<'EOF'
Usage:
scripts/rpi-stream.sh [run-id] [options]
Options:
--phase N Filter to one phase number
--worker N Filter to one worker number
--raw Print raw lines (no jq filter)
--no-follow Print snapshot only (no tail -F)
--lines N Initial line count per file (default: 20)
-h, --help Show help
Examples:
scripts/rpi-stream.sh
scripts/rpi-stream.sh 60d881b65b19
scripts/rpi-stream.sh 60d881b65b19 --phase 2 --worker 1
scripts/rpi-stream.sh 60d881b65b19 --raw --lines 200
EOF
}
RUN_ID=""
PHASE=""
WORKER=""
RAW=0
FOLLOW=1
LINES=20
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
--phase)
PHASE="${2:-}"
shift 2
;;
--worker)
WORKER="${2:-}"
shift 2
;;
--raw)
RAW=1
shift
;;
--no-follow)
FOLLOW=0
shift
;;
--lines)
LINES="${2:-20}"
shift 2
;;
*)
if [[ -z "$RUN_ID" ]]; then
RUN_ID="$1"
shift
else
echo "error: unexpected argument: $1" >&2
usage
exit 1
fi
;;
esac
done
if ! command -v jq >/dev/null 2>&1; then
echo "error: jq is required" >&2
exit 1
fi
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
RUNS_DIR="$REPO_ROOT/.agents/rpi/runs"
if [[ ! -d "$RUNS_DIR" ]]; then
echo "error: runs directory not found: $RUNS_DIR" >&2
exit 1
fi
if [[ -z "$RUN_ID" ]]; then
RUN_ID="$(find "$RUNS_DIR" -mindepth 1 -maxdepth 1 -type d -print0 \
| xargs -0 stat -f '%m %N' \
| sort -nr \
| awk 'NR==1 {print $2}')"
RUN_ID="$(basename "${RUN_ID:-}")"
fi
if [[ -z "$RUN_ID" ]]; then
echo "error: could not determine run id" >&2
exit 1
fi
RUN_DIR="$RUNS_DIR/$RUN_ID"
if [[ ! -d "$RUN_DIR" ]]; then
echo "error: run directory not found: $RUN_DIR" >&2
exit 1
fi
phase_glob="*"
worker_glob="*"
if [[ -n "$PHASE" ]]; then
phase_glob="$PHASE"
fi
if [[ -n "$WORKER" ]]; then
worker_glob="$WORKER"
fi
mapfile -t LOG_FILES < <(find "$RUN_DIR" -maxdepth 1 -type f -name "phase-${phase_glob}-exit.w${worker_glob}.jsonl" | sort)
if [[ ${#LOG_FILES[@]} -eq 0 ]]; then
echo "error: no worker stream logs found for run $RUN_ID in $RUN_DIR" >&2
echo "hint: expected files like phase-<n>-exit.w<k>.jsonl" >&2
exit 1
fi
print_line() {
local label="$1"
local line="$2"
local output="$line"
if [[ "$RAW" -eq 0 && "$line" == \{* ]]; then
local parsed
parsed="$(printf '%s\n' "$line" | jq -r '
def ev: .type // .event // .event_type // .message?.type // .kind // "event";
def tool: .tool_name // .tool?.name // .call?.name // .function?.name // .name // "";
def body: .delta // .text // .content // .message?.content // .reasoning // .summary // .error // .details // "";
[ev, tool, (if (body|type) == "string" then body else (body|tostring) end)] | @tsv
' 2>/dev/null || true)"
if [[ -n "$parsed" ]]; then
local ev tool body
IFS=$'\t' read -r ev tool body <<<"$parsed"
body="${body//$'\n'/\\n}"
if [[ ${#body} -gt 220 ]]; then
body="${body:0:220}..."
fi
output="$(printf '%-28s %-24s %s' "$ev" "${tool:--}" "$body")"
fi
fi
printf '[%s] %s\n' "$label" "$output"
}
stream_file() {
local file="$1"
local label
label="$(basename "$file")"
if [[ "$FOLLOW" -eq 1 ]]; then
tail -n "$LINES" -F "$file" 2>/dev/null | while IFS= read -r line; do
print_line "$label" "$line"
done
else
tail -n "$LINES" "$file" 2>/dev/null | while IFS= read -r line; do
print_line "$label" "$line"
done
fi
}
echo "Run: $RUN_ID"
echo "Logs:"
printf ' - %s\n' "${LOG_FILES[@]}"
echo
if [[ "$FOLLOW" -eq 1 ]]; then
pids=()
for file in "${LOG_FILES[@]}"; do
stream_file "$file" &
pids+=("$!")
done
# shellcheck disable=SC2154 # 'p' is the loop variable inside the trap body
trap 'for p in "${pids[@]}"; do kill "$p" 2>/dev/null || true; done' EXIT INT TERM
wait
else
for file in "${LOG_FILES[@]}"; do
stream_file "$file"
done
fi