#!/usr/bin/env bash # phase-status.sh — phase/task pipeline state for the Phase Architect. # # Finds the project root (nearest ancestor with .agent/phases/todo), prints # the todo/ and complete/ phase listings with per-task state, the next free # phase number NN (counting both directories together, zero-padded to 2 # digits), and the next pending unit in pipeline order. # # Phase layout: todo/NN_name/ holds 00_phase.md (overview) + NN_task.md task # files; a legacy flat todo/NN_name.md is a single-unit phase. # # Usage: bash phase-status.sh # from anywhere in the project tree set -euo pipefail root="$(pwd)" while :; do if [[ -d "$root/.agent/phases/todo" ]]; then break; fi [[ "$root" == "/" ]] && { echo "✗ ERROR: no .agent/phases/todo found at or above $(pwd)" >&2; exit 1; } root="$(dirname "$root")" done phases="$root/.agent/phases" # --- todo: per-task state ------------------------------------------------------ echo "todo:" found=0 for entry in "$phases/todo/"*; do [[ -e "$entry" ]] || continue name="$(basename "$entry")" [[ "$name" =~ ^[0-9] ]] || continue found=1 if [[ -d "$entry" ]]; then # union of this phase's files across todo/ and complete/ (completed units # move to complete/, so pending = present in todo/, done = in complete/) comp_dir="$phases/complete/$name" comp_files="" if [[ -d "$comp_dir" ]]; then comp_files="$(ls -1 "$comp_dir" 2>/dev/null || true)"; fi names="$( { ls -1 "$entry" 2>/dev/null || true; printf '%s\n' "$comp_files"; } | grep -E '\.md$' | sort -u || true)" total=0 done_n=0 while IFS= read -r t; do [[ -n "$t" ]] || continue total=$((total + 1)) [[ -f "$phases/complete/$name/$t" ]] && done_n=$((done_n + 1)) done <<< "$names" printf ' %s/ (%s of %s done)\n' "$name" "$done_n" "$total" # tasks first (sorted), then 00_phase.md as the final pass while IFS= read -r t; do [[ -n "$t" ]] || continue [[ "$t" == 00_phase.md ]] && continue if [[ -f "$phases/complete/$name/$t" ]]; then printf ' [x] %s\n' "$t" else printf ' [ ] %s\n' "$t" fi done <<< "$names" if grep -qxF '00_phase.md' <<< "$names"; then if [[ -f "$phases/complete/$name/00_phase.md" ]]; then printf ' [x] 00_phase.md (final pass)\n' else printf ' [ ] 00_phase.md (final pass)\n' fi fi else printf ' %s (legacy single-file phase)\n' "$name" fi done (( found )) || echo " (empty)" # --- complete ------------------------------------------------------------------- echo "complete:" found=0 for entry in "$phases/complete/"*; do [[ -e "$entry" ]] || continue name="$(basename "$entry")" [[ "$name" =~ ^[0-9] ]] || continue found=1 if [[ -d "$entry" ]]; then n=0 for f in "$entry"/*.md; do [[ -e "$f" ]] && n=$((n + 1)); done printf ' %s/ (%s files)\n' "$name" "$n" else printf ' %s (legacy single-file phase)\n' "$name" fi done (( found )) || echo " (empty)" # --- next free phase number ----------------------------------------------------- max=0 for d in todo complete; do for entry in "$phases/$d/"*; do [[ -e "$entry" ]] || continue name="$(basename "$entry" .md)" if [[ "$name" =~ ^([0-9]+) ]]; then n=$((10#${BASH_REMATCH[1]})) (( n > max )) && max=$n fi done done echo "next phase number: $(printf '%02d' $((max + 1)))" # --- next pending unit ----------------------------------------------------------- next="" for entry in "$phases/todo/"*; do [[ -e "$entry" ]] || continue name="$(basename "$entry")" [[ "$name" =~ ^[0-9] ]] || continue if [[ -d "$entry" ]]; then # first task present in todo/ and not yet in complete/ (mirrors the harness) t="" while IFS= read -r f; do [[ -n "$f" ]] || continue [[ "$f" =~ ^[0-9] && "$f" == *.md ]] || continue [[ "$f" == 00_phase.md ]] && continue [[ -f "$phases/complete/$name/$f" ]] && continue t="$f"; break done < <(ls -1 "$entry" 2>/dev/null || true) if [[ -n "$t" ]]; then next="$name/$t"; break; fi if [[ -f "$entry/00_phase.md" && ! -f "$phases/complete/$name/00_phase.md" ]]; then next="$name/00_phase.md"; break fi elif [[ -f "$entry" ]]; then next="$name"; break fi done echo "next unit: ${next:-(none — pipeline complete)}"