-
Notifications
You must be signed in to change notification settings - Fork 1
Pipeline Design 4
The shipwright status command (scripts/sw-status.sh, 605 lines) renders a rich terminal dashboard with 8 sections: tmux windows, team configs, task lists, daemon pipelines, issue tracker, heartbeats, remote machines, and connected developers. Currently it only produces ANSI-colored human-readable output.
Users and automated tooling (the web dashboard, CI scripts, sw-connect.sh) need machine-readable access to the same data. The codebase already has --json flag precedents in sw-cost.sh, sw-fleet.sh, and sw-pipeline-vitals.sh — all using the "early-exit separate code path" pattern where JSON collection runs before display code, assembles via jq -n, prints, and exits.
Constraints:
- Bash 3.2 compatible (no associative arrays, no
readarray, no${var,,}) -
set -euo pipefailthroughout -
jqis the only JSON tool available (already a dependency across the project) - The existing 600-line display code path must remain completely untouched to avoid regression
- Output helpers (
info(),success(),warn(),error()) write to stderr, which naturally keeps JSON stdout clean
Separate early-exit code path, matching the established pattern from sw-cost.sh:550-670.
- Parse
--jsonflag in the existing argument loop at the top ofsw-status.sh - When
--jsonis set, run a dedicated collection block that gathers each section into shell variables holding JSON strings (e.g.,WINDOWS_JSON,TEAMS_JSON) - Each collector function reads the same files/commands the display code does (tmux list-windows, daemon-state.json, heartbeat files, etc.) but produces JSON fragments via
jq - Assemble all fragments into a single object with
jq -n --argjson ...and print to stdout -
exit 0before the display code path ever executes
{
"version": "string",
"timestamp": "ISO-8601 UTC",
"tmux_windows": [{"name": "string", "panes": N, "active": bool}],
"teams": [{"name": "string", "template": "string", "agents": N}],
"task_lists": [{"team": "string", "total": N, "completed": N, "tasks": [...]}],
"daemon": {"running": bool, "pid": N|null, "active_jobs": [...], "queued": [...], "recent_completions": [...]},
"issue_tracker": {"provider": "string|null", "url": "string|null"},
"heartbeats": [{"agent": "string", "last_seen": "ISO-8601", "status": "string"}],
"remote_machines": [{"name": "string", "host": "string", "status": "string"}],
"connected_developers": {"reachable": bool, "total_online": N, "developers": [...]}
}Missing/unavailable sections produce null or [] — never omitted keys. This guarantees consumers can always reference any top-level key without existence checks.
- If
jqis not installed, emiterror "jq is required for --json output"to stderr andexit 1— check happens immediately after flag parsing, before any collection work - If tmux is not running,
tmux_windowsbecomes[](not an error) - If daemon-state.json is missing or malformed,
daemon.runningisfalsewith remaining fields null/empty - Each collector is wrapped in a guard: if the source data is absent, emit the empty/null default. No collector failure should abort the entire JSON output
JSON_OUTPUT="false"
while [[ $# -gt 0 ]]; do
case "$1" in
--json) JSON_OUTPUT="true"; shift ;;
--help|-h) usage; exit 0 ;;
*) error "Unknown option: $1"; usage; exit 1 ;;
esac
doneThe help text in scripts/sw line ~70 updates to: status [--json] Show dashboard of running teams and agents
-
Refactor into collect/render pairs — Pros: Cleaner separation, each section gets
collect_X()andrender_X()functions, the JSON path callscollect_*only. Cons: Touching all 600 lines of existing display code to refactor into render functions creates significant regression risk. The plan initially proposed this but the "early exit" approach achieves the same result with zero changes to existing code. Refactoring can happen later as a separate PR. -
Inline JSON into each display section (interleaved) — Pros: Single code path, no duplication. Cons: Mixes display and data concerns, every section gets
if $JSON; then ... fiblocks that double the complexity of every section. Much harder to maintain. Fragile — any display change risks breaking JSON output. -
External wrapper script — Pros: Zero changes to
sw-status.sh. Cons: Would need to screen-scrape ANSI output or duplicate all the data collection logic in a new file. Not maintainable. Violates the "source of truth" principle.
-
scripts/sw-status.sh— Add--jsonflag parsing,jqcheck, 8 collector blocks, JSON assembly, early exit -
scripts/sw— Update help text forstatussubcommand (~line 70) -
completions/shipwright.bash— Add--jsonto status completions -
completions/_shipwright— Add--jsonto status completions (zsh) -
completions/shipwright.fish— Add--jsonto status completions (fish) -
package.json— Register new test suite
-
scripts/sw-status-test.sh— Test suite (mock environment, 10+ test cases, PASS/FAIL counters, ERR trap)
- None new.
jqis already required by the project (used in 20+ scripts)
-
jq --argjsonwith large daemon state: Ifdaemon-state.jsoncontains hundreds of completed jobs, the assembled JSON could be large. Mitigate by limitingrecent_completionsto the last 20 entries (matching the dashboard's visual limit). -
tmux not available: The
tmux list-windowscall will fail if tmux isn't running. The collector must handle this gracefully (return[]). - Shell variable size: Each JSON fragment is stored in a bash variable. Extremely large task lists could theoretically hit shell limits. In practice, shipwright task lists are small (< 100 items). No mitigation needed for v1.
-
Pipefail + jq chains: Under
set -eo pipefail, ajqparse error in a collector could abort the entire script. Each collector should use|| echo '[]'/|| echo 'null'fallbacks.
-
shipwright status(no flags) produces byte-identical output to the current version -
shipwright status --jsonoutputs valid JSON (jq emptyexits 0) - JSON output contains zero ANSI escape sequences (
grep -P '\x1b\['finds nothing) - All 10 top-level keys present:
version,timestamp,tmux_windows,teams,task_lists,daemon,issue_tracker,heartbeats,remote_machines,connected_developers - Empty state (no tmux, no daemon, no teams) produces valid JSON with
[]/null— not errors, not missing keys -
shipwright status --json | jq .daemon.active_jobsreturns a valid array (subsections independently queryable) -
sw-status-test.shpasses all cases (valid JSON, key presence, empty state, active daemon, heartbeats, human-readable section headers preserved) - Test suite registered in
package.jsonand runs vianpm test - No Bash 3.2 incompatibilities (
shellcheckclean, no associative arrays) -
--jsonwithoutjqinstalled prints clear error to stderr and exits 1 - Tab completion works for
shipwright status --jsonin bash, zsh, and fish