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).
This commit is contained in:
241
packages/mosaic/framework/tools/orchestrator/session-status.sh
Executable file
241
packages/mosaic/framework/tools/orchestrator/session-status.sh
Executable file
@@ -0,0 +1,241 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
#
|
||||
# session-status.sh — Check agent session health
|
||||
#
|
||||
# Usage:
|
||||
# session-status.sh [--project <path>] [--format table|json]
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 = running
|
||||
# 2 = stale (recently died)
|
||||
# 3 = dead (no longer running)
|
||||
# 4 = no session
|
||||
|
||||
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: session-status.sh [--project <path>] [--format table|json]"
|
||||
exit 0
|
||||
;;
|
||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
_require_jq
|
||||
runtime_cmd="$(coord_launch_command)"
|
||||
run_cmd="$(coord_run_command)"
|
||||
|
||||
# ─── Check session lock ─────────────────────────────────────────────────────
|
||||
|
||||
lock_data=""
|
||||
if ! lock_data="$(session_lock_read "$PROJECT")"; then
|
||||
# No active session — but check if a mission exists
|
||||
mp="$(mission_path "$PROJECT")"
|
||||
if [[ -f "$mp" ]]; then
|
||||
m_status="$(jq -r '.status // "inactive"' "$mp")"
|
||||
m_name="$(jq -r '.name // "unnamed"' "$mp")"
|
||||
m_id="$(jq -r '.mission_id // ""' "$mp")"
|
||||
m_total="$(jq '.milestones | length' "$mp")"
|
||||
m_done="$(jq '[.milestones[] | select(.status == "completed")] | length' "$mp")"
|
||||
m_current="$(jq -r '[.milestones[] | select(.status == "active" or .status == "pending")][0].name // "none"' "$mp")"
|
||||
|
||||
# Task counts if TASKS.md exists
|
||||
task_json="$(count_tasks_md "$PROJECT")"
|
||||
t_total="$(echo "$task_json" | jq '.total')"
|
||||
t_done="$(echo "$task_json" | jq '.done')"
|
||||
t_pending="$(echo "$task_json" | jq '.pending')"
|
||||
t_inprog="$(echo "$task_json" | jq '.in_progress')"
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
jq -n \
|
||||
--arg status "no-session" \
|
||||
--arg mission_status "$m_status" \
|
||||
--arg mission_name "$m_name" \
|
||||
--arg mission_id "$m_id" \
|
||||
--argjson milestones_total "$m_total" \
|
||||
--argjson milestones_done "$m_done" \
|
||||
--argjson tasks_total "$t_total" \
|
||||
--argjson tasks_done "$t_done" \
|
||||
'{
|
||||
status: $status,
|
||||
mission: {
|
||||
status: $mission_status,
|
||||
name: $mission_name,
|
||||
id: $mission_id,
|
||||
milestones_total: $milestones_total,
|
||||
milestones_done: $milestones_done,
|
||||
tasks_total: $tasks_total,
|
||||
tasks_done: $tasks_done
|
||||
}
|
||||
}'
|
||||
else
|
||||
echo ""
|
||||
echo -e " ${C_DIM}No active agent session.${C_RESET}"
|
||||
echo ""
|
||||
|
||||
# Mission info
|
||||
case "$m_status" in
|
||||
active) ms_color="${C_GREEN}ACTIVE${C_RESET}" ;;
|
||||
paused) ms_color="${C_YELLOW}PAUSED${C_RESET}" ;;
|
||||
completed) ms_color="${C_CYAN}COMPLETED${C_RESET}" ;;
|
||||
*) ms_color="${C_DIM}${m_status}${C_RESET}" ;;
|
||||
esac
|
||||
|
||||
echo -e " ${C_BOLD}Mission:${C_RESET} $m_name"
|
||||
echo -e " ${C_CYAN}Status:${C_RESET} $ms_color"
|
||||
echo -e " ${C_CYAN}ID:${C_RESET} $m_id"
|
||||
echo -e " ${C_CYAN}Milestones:${C_RESET} $m_done / $m_total completed"
|
||||
[[ "$m_current" != "none" ]] && echo -e " ${C_CYAN}Current:${C_RESET} $m_current"
|
||||
|
||||
if (( t_total > 0 )); then
|
||||
echo -e " ${C_CYAN}Tasks:${C_RESET} $t_done / $t_total done ($t_pending pending, $t_inprog in-progress)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then
|
||||
echo -e " ${C_BOLD}Next steps:${C_RESET}"
|
||||
echo " $run_cmd Auto-generate context and launch"
|
||||
echo " mosaic coord continue Generate continuation prompt"
|
||||
echo " $runtime_cmd Launch agent session"
|
||||
elif [[ "$m_status" == "completed" ]]; then
|
||||
echo -e " ${C_DIM}Mission completed. Start a new one with: mosaic coord init${C_RESET}"
|
||||
else
|
||||
echo -e " ${C_DIM}Initialize with: mosaic coord init --name \"Mission Name\"${C_RESET}"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
echo '{"status":"no-session","mission":null}'
|
||||
else
|
||||
echo ""
|
||||
echo -e " ${C_DIM}No active session.${C_RESET}"
|
||||
echo -e " ${C_DIM}No mission found.${C_RESET}"
|
||||
echo ""
|
||||
echo " Initialize with: mosaic coord init --name \"Mission Name\""
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
exit 4
|
||||
fi
|
||||
|
||||
# Parse lock
|
||||
session_id="$(echo "$lock_data" | jq -r '.session_id // "unknown"')"
|
||||
runtime="$(echo "$lock_data" | jq -r '.runtime // "unknown"')"
|
||||
pid="$(echo "$lock_data" | jq -r '.pid // 0')"
|
||||
started_at="$(echo "$lock_data" | jq -r '.started_at // ""')"
|
||||
milestone_id="$(echo "$lock_data" | jq -r '.milestone_id // ""')"
|
||||
|
||||
# ─── Determine status ───────────────────────────────────────────────────────
|
||||
|
||||
status="unknown"
|
||||
exit_code=1
|
||||
|
||||
if is_pid_alive "$pid"; then
|
||||
status="running"
|
||||
exit_code=0
|
||||
else
|
||||
# PID is dead — check how recently
|
||||
last_act="$(last_activity_time "$PROJECT")"
|
||||
now="$(epoch_now)"
|
||||
age=$(( now - last_act ))
|
||||
|
||||
if (( age < STALE_THRESHOLD )); then
|
||||
status="stale"
|
||||
exit_code=2
|
||||
elif (( age < DEAD_THRESHOLD )); then
|
||||
status="stale"
|
||||
exit_code=2
|
||||
else
|
||||
status="dead"
|
||||
exit_code=3
|
||||
fi
|
||||
fi
|
||||
|
||||
# ─── Gather supplementary info ──────────────────────────────────────────────
|
||||
|
||||
duration_secs=0
|
||||
if [[ -n "$started_at" ]]; then
|
||||
start_epoch="$(iso_to_epoch "$started_at")"
|
||||
now="$(epoch_now)"
|
||||
duration_secs=$(( now - start_epoch ))
|
||||
fi
|
||||
|
||||
last_act="$(last_activity_time "$PROJECT")"
|
||||
|
||||
# Current milestone from mission.json
|
||||
current_ms=""
|
||||
if [[ -f "$(mission_path "$PROJECT")" ]]; then
|
||||
current_ms="$(current_milestone_id "$PROJECT")"
|
||||
if [[ -n "$current_ms" ]]; then
|
||||
ms_name="$(milestone_name "$PROJECT" "$current_ms")"
|
||||
[[ -n "$ms_name" ]] && current_ms="$current_ms ($ms_name)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Next task from TASKS.md
|
||||
next_task="$(find_next_task "$PROJECT")"
|
||||
|
||||
# ─── Output ──────────────────────────────────────────────────────────────────
|
||||
|
||||
if [[ "$FORMAT" == "json" ]]; then
|
||||
jq -n \
|
||||
--arg status "$status" \
|
||||
--arg session_id "$session_id" \
|
||||
--arg runtime "$runtime" \
|
||||
--arg pid "$pid" \
|
||||
--arg started_at "$started_at" \
|
||||
--arg duration "$duration_secs" \
|
||||
--arg milestone "$current_ms" \
|
||||
--arg next_task "$next_task" \
|
||||
--arg last_activity "$last_act" \
|
||||
'{
|
||||
status: $status,
|
||||
session_id: $session_id,
|
||||
runtime: $runtime,
|
||||
pid: ($pid | tonumber),
|
||||
started_at: $started_at,
|
||||
duration_seconds: ($duration | tonumber),
|
||||
milestone: $milestone,
|
||||
next_task: $next_task,
|
||||
last_activity_epoch: ($last_activity | tonumber)
|
||||
}'
|
||||
else
|
||||
# Color the status
|
||||
case "$status" in
|
||||
running) status_color="${C_GREEN}RUNNING${C_RESET}" ;;
|
||||
stale) status_color="${C_YELLOW}STALE${C_RESET}" ;;
|
||||
dead) status_color="${C_RED}DEAD${C_RESET}" ;;
|
||||
*) status_color="$status" ;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo -e " Session Status: $status_color ($runtime)"
|
||||
echo -e " ${C_CYAN}Session ID:${C_RESET} $session_id"
|
||||
echo -e " ${C_CYAN}Started:${C_RESET} $started_at ($(format_duration "$duration_secs"))"
|
||||
echo -e " ${C_CYAN}PID:${C_RESET} $pid"
|
||||
[[ -n "$current_ms" ]] && echo -e " ${C_CYAN}Milestone:${C_RESET} $current_ms"
|
||||
[[ -n "$next_task" ]] && echo -e " ${C_CYAN}Next task:${C_RESET} $next_task"
|
||||
echo -e " ${C_CYAN}Last activity:${C_RESET} $(format_ago "$last_act")"
|
||||
echo ""
|
||||
|
||||
if [[ "$status" == "stale" || "$status" == "dead" ]]; then
|
||||
echo -e " ${C_YELLOW}Session is no longer running.${C_RESET}"
|
||||
echo " Recovery: mosaic coord resume"
|
||||
echo " Continue: mosaic coord continue"
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
exit "$exit_code"
|
||||
Reference in New Issue
Block a user