Files
Jason Woltje b38cfac760
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
feat: integrate framework files into monorepo under packages/mosaic/framework/
Moves all Mosaic framework runtime files from the separate bootstrap repo
into the monorepo as canonical source. The @mosaic/mosaic npm package now
ships the complete framework — bin scripts, runtime configs, tools, and
templates — enabling standalone installation via npm install.

Structure:
  packages/mosaic/framework/
  ├── bin/          28 CLI scripts (mosaic, mosaic-doctor, mosaic-sync-skills, etc.)
  ├── runtime/      Runtime adapters (claude, codex, opencode, pi, mcp)
  ├── tools/        Shell tooling (git, prdy, orchestrator, quality, etc.)
  ├── templates/    Agent and repo templates
  ├── defaults/     Default identity files (AGENTS.md, STANDARDS.md, SOUL.md, etc.)
  ├── install.sh    Legacy bash installer
  └── remote-install.sh  One-liner remote installer

Key files with Pi support and recent fixes:
- bin/mosaic: launch_pi() with skills-local loop
- bin/mosaic-doctor: --fix auto-wiring for all 4 harnesses
- bin/mosaic-sync-skills: Pi as 4th link target, symlink-aware find
- bin/mosaic-link-runtime-assets: Pi settings.json patching
- bin/mosaic-migrate-local-skills: Pi skill roots, symlink find
- runtime/pi/RUNTIME.md + mosaic-extension.ts

Package ships 251 framework files in the npm tarball (278KB compressed).
2026-04-01 21:19:21 -05:00

182 lines
6.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
#
# mission-status.sh — Show mission progress dashboard
#
# Usage:
# mission-status.sh [--project <path>] [--format table|json|markdown]
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/_lib.sh"
# ─── Parse arguments ─────────────────────────────────────────────────────────
PROJECT="."
FORMAT="table"
while [[ $# -gt 0 ]]; do
case "$1" in
--project) PROJECT="$2"; shift 2 ;;
--format) FORMAT="$2"; shift 2 ;;
-h|--help)
echo "Usage: mission-status.sh [--project <path>] [--format table|json|markdown]"
exit 0
;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
_require_jq
require_mission "$PROJECT"
# ─── Load data ───────────────────────────────────────────────────────────────
mission="$(load_mission "$PROJECT")"
mission_name="$(echo "$mission" | jq -r '.name')"
mission_id="$(echo "$mission" | jq -r '.mission_id')"
mission_status="$(echo "$mission" | jq -r '.status')"
version="$(echo "$mission" | jq -r '.milestone_version // "—"')"
created_at="$(echo "$mission" | jq -r '.created_at // "—"')"
session_count="$(echo "$mission" | jq '.sessions | length')"
milestone_count="$(echo "$mission" | jq '.milestones | length')"
completed_milestones="$(echo "$mission" | jq '[.milestones[] | select(.status == "completed")] | length')"
# Task counts
task_counts="$(count_tasks_md "$PROJECT")"
tasks_total="$(echo "$task_counts" | jq '.total')"
tasks_done="$(echo "$task_counts" | jq '.done')"
tasks_inprog="$(echo "$task_counts" | jq '.in_progress')"
tasks_pending="$(echo "$task_counts" | jq '.pending')"
tasks_blocked="$(echo "$task_counts" | jq '.blocked')"
tasks_failed="$(echo "$task_counts" | jq '.failed')"
# Next task
next_task="$(find_next_task "$PROJECT")"
# ─── JSON output ─────────────────────────────────────────────────────────────
if [[ "$FORMAT" == "json" ]]; then
echo "$mission" | jq \
--argjson tasks "$task_counts" \
--arg next "$next_task" \
'. + {task_counts: $tasks, next_task: $next}'
exit 0
fi
# ─── Progress bar ────────────────────────────────────────────────────────────
progress_bar() {
local done=$1
local total=$2
local width=30
if (( total == 0 )); then
printf "[%${width}s]" ""
return
fi
local filled=$(( (done * width) / total ))
local empty=$(( width - filled ))
local bar=""
for (( i=0; i<filled; i++ )); do bar+="="; done
if (( empty > 0 && filled > 0 )); then
bar+=">"
empty=$(( empty - 1 ))
fi
for (( i=0; i<empty; i++ )); do bar+="."; done
printf "[%s]" "$bar"
}
# ─── Table / Markdown output ────────────────────────────────────────────────
# Header
echo ""
echo "=================================================="
echo -e " ${C_BOLD}Mission: $mission_name${C_RESET}"
echo -e " Status: ${C_CYAN}$mission_status${C_RESET} Version: $version"
echo -e " Started: ${created_at:0:10} Sessions: $session_count"
echo "=================================================="
echo ""
# Milestones
echo -e "${C_BOLD}Milestones:${C_RESET}"
for i in $(seq 0 $(( milestone_count - 1 ))); do
ms_id="$(echo "$mission" | jq -r ".milestones[$i].id")"
ms_name="$(echo "$mission" | jq -r ".milestones[$i].name")"
ms_status="$(echo "$mission" | jq -r ".milestones[$i].status")"
ms_issue="$(echo "$mission" | jq -r ".milestones[$i].issue_ref // \"\"")"
case "$ms_status" in
completed) icon="${C_GREEN}[x]${C_RESET}" ;;
in-progress) icon="${C_YELLOW}[>]${C_RESET}" ;;
blocked) icon="${C_RED}[!]${C_RESET}" ;;
*) icon="${C_DIM}[ ]${C_RESET}" ;;
esac
issue_str=""
[[ -n "$ms_issue" ]] && issue_str="$ms_issue"
printf " %b %-40s %s\n" "$icon" "$ms_name" "$issue_str"
done
echo ""
# Tasks progress
pct=0
(( tasks_total > 0 )) && pct=$(( (tasks_done * 100) / tasks_total ))
echo -e "${C_BOLD}Tasks:${C_RESET} $(progress_bar "$tasks_done" "$tasks_total") ${tasks_done}/${tasks_total} (${pct}%)"
echo -e " done: ${C_GREEN}$tasks_done${C_RESET} in-progress: ${C_YELLOW}$tasks_inprog${C_RESET} pending: $tasks_pending blocked: ${C_RED}$tasks_blocked${C_RESET} failed: ${C_RED}$tasks_failed${C_RESET}"
echo ""
# Session history (last 5)
if (( session_count > 0 )); then
echo -e "${C_BOLD}Recent Sessions:${C_RESET}"
start_idx=$(( session_count > 5 ? session_count - 5 : 0 ))
for i in $(seq "$start_idx" $(( session_count - 1 ))); do
s_id="$(echo "$mission" | jq -r ".sessions[$i].session_id")"
s_rt="$(echo "$mission" | jq -r ".sessions[$i].runtime // \"—\"")"
s_start="$(echo "$mission" | jq -r ".sessions[$i].started_at // \"\"")"
s_end="$(echo "$mission" | jq -r ".sessions[$i].ended_at // \"\"")"
s_reason="$(echo "$mission" | jq -r ".sessions[$i].ended_reason // \"—\"")"
s_last="$(echo "$mission" | jq -r ".sessions[$i].last_task_id // \"—\"")"
duration_str="—"
if [[ -n "$s_start" && -n "$s_end" && "$s_end" != "" ]]; then
s_epoch="$(iso_to_epoch "$s_start")"
e_epoch="$(iso_to_epoch "$s_end")"
if (( e_epoch > 0 && s_epoch > 0 )); then
duration_str="$(format_duration $(( e_epoch - s_epoch )))"
fi
fi
printf " %-10s %-8s %-10s %-18s → %s\n" "$s_id" "$s_rt" "$duration_str" "$s_reason" "$s_last"
done
echo ""
fi
# Current session check
lock_data=""
if lock_data="$(session_lock_read "$PROJECT" 2>/dev/null)"; then
lock_pid="$(echo "$lock_data" | jq -r '.pid // 0')"
lock_rt="$(echo "$lock_data" | jq -r '.runtime // "unknown"')"
lock_start="$(echo "$lock_data" | jq -r '.started_at // ""')"
if is_pid_alive "$lock_pid"; then
dur=0
if [[ -n "$lock_start" ]]; then
dur=$(( $(epoch_now) - $(iso_to_epoch "$lock_start") ))
fi
echo -e "${C_GREEN}Current: running ($lock_rt, PID $lock_pid, $(format_duration "$dur"))${C_RESET}"
else
echo -e "${C_RED}Stale session lock: $lock_rt (PID $lock_pid, not running)${C_RESET}"
echo " Run: mosaic coord resume --clean-lock"
fi
else
echo -e "${C_DIM}No active session.${C_RESET}"
fi
[[ -n "$next_task" ]] && echo -e "Next unblocked task: ${C_CYAN}$next_task${C_RESET}"
echo ""