Files
bootstrap/bin/mosaic
Jason Woltje b1403703b1 feat: add prdy-status command and PRD status injection into system prompt
- Add prdy-status.sh for quick one-liner PRD health check (short/json output)
- Inject PRD section count and assumption count into agent system prompt
  so the agent knows PRD state at session start without running validate
- Add status subcommand to mosaic prdy routing and help text

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 18:33:33 -06:00

792 lines
24 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# mosaic — Unified agent launcher and management CLI
#
# AGENTS.md is the global policy source for all agent sessions.
# The launcher injects a composed runtime contract (AGENTS + runtime reference).
#
# Usage:
# mosaic claude [args...] Launch Claude Code with runtime contract injected
# mosaic opencode [args...] Launch OpenCode with runtime contract injected
# mosaic codex [args...] Launch Codex with runtime contract injected
# mosaic yolo <runtime> [args...] Launch runtime in dangerous-permissions mode
# mosaic --yolo <runtime> [args...] Alias for yolo
# mosaic init [args...] Generate SOUL.md interactively
# mosaic doctor [args...] Health audit
# mosaic sync [args...] Sync skills
# mosaic seq [subcommand] sequential-thinking MCP management (check/fix/start)
# mosaic bootstrap <path> Bootstrap a repo
# mosaic upgrade release Upgrade installed Mosaic release
# mosaic upgrade check Check release upgrade status (no changes)
# mosaic upgrade project [args] Upgrade project-local stale files
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
VERSION="0.1.0"
usage() {
cat <<USAGE
mosaic $VERSION — Unified agent launcher
Usage: mosaic <command> [args...]
Agent Launchers:
claude [args...] Launch Claude Code with runtime contract injected
opencode [args...] Launch OpenCode with runtime contract injected
codex [args...] Launch Codex with runtime contract injected
yolo <runtime> [args...] Dangerous mode for claude|codex|opencode
--yolo <runtime> [args...] Alias for yolo
Management:
init [args...] Generate SOUL.md (agent identity contract)
doctor [args...] Audit runtime state and detect drift
sync [args...] Sync skills from canonical source
seq [subcommand] sequential-thinking MCP management:
check [--runtime <r>] [--strict]
fix [--runtime <r>]
start
bootstrap <path> Bootstrap a repo with Mosaic standards
upgrade [mode] [args] Upgrade release (default) or project files
upgrade check Check release upgrade status (no changes)
release-upgrade [...] Upgrade installed Mosaic release
project-upgrade [...] Clean up stale SOUL.md/CLAUDE.md in a project
PRD:
prdy <subcommand> PRD creation and validation
init Create docs/PRD.md via guided runtime session
update Update existing PRD via guided runtime session
validate Check PRD completeness (bash-only)
status Quick PRD health check (one-liner)
Coordinator (r0):
coord <subcommand> Manual coordinator tools
init Initialize a new mission
mission Show mission progress dashboard
status Check agent session health
continue Generate continuation prompt
run Generate context and launch selected runtime
resume Crash recovery
Options:
-h, --help Show this help
-v, --version Show version
All arguments after the command are forwarded to the target CLI.
USAGE
}
# Pre-flight checks
check_mosaic_home() {
if [[ ! -d "$MOSAIC_HOME" ]]; then
echo "[mosaic] ERROR: ~/.config/mosaic not found." >&2
echo "[mosaic] Install with: curl -sL https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.sh | sh" >&2
exit 1
fi
}
check_agents_md() {
if [[ ! -f "$MOSAIC_HOME/AGENTS.md" ]]; then
echo "[mosaic] ERROR: ~/.config/mosaic/AGENTS.md not found." >&2
echo "[mosaic] Re-run the installer: cd ~/src/mosaic-bootstrap && bash install.sh" >&2
exit 1
fi
}
check_soul() {
if [[ ! -f "$MOSAIC_HOME/SOUL.md" ]]; then
echo "[mosaic] SOUL.md not found. Running mosaic init..."
"$MOSAIC_HOME/bin/mosaic-init"
fi
}
check_runtime() {
local cmd="$1"
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "[mosaic] ERROR: '$cmd' not found in PATH." >&2
echo "[mosaic] Install $cmd before launching." >&2
exit 1
fi
}
check_sequential_thinking() {
local runtime="${1:-all}"
local checker="$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking"
if [[ ! -x "$checker" ]]; then
echo "[mosaic] ERROR: sequential-thinking checker missing: $checker" >&2
exit 1
fi
if ! "$checker" --check --runtime "$runtime" >/dev/null 2>&1; then
echo "[mosaic] ERROR: sequential-thinking MCP is required but not configured." >&2
echo "[mosaic] Fix config: $checker --runtime $runtime" >&2
echo "[mosaic] Or run: mosaic seq fix --runtime $runtime" >&2
echo "[mosaic] Manual server start: mosaic seq start" >&2
exit 1
fi
}
runtime_contract_path() {
local runtime="$1"
case "$runtime" in
claude) echo "$MOSAIC_HOME/runtime/claude/RUNTIME.md" ;;
codex) echo "$MOSAIC_HOME/runtime/codex/RUNTIME.md" ;;
opencode) echo "$MOSAIC_HOME/runtime/opencode/RUNTIME.md" ;;
*)
echo "[mosaic] ERROR: unsupported runtime '$runtime' for runtime contract." >&2
exit 1
;;
esac
}
build_runtime_prompt() {
local runtime="$1"
local runtime_file
runtime_file="$(runtime_contract_path "$runtime")"
if [[ ! -f "$runtime_file" ]]; then
echo "[mosaic] ERROR: runtime contract not found: $runtime_file" >&2
exit 1
fi
# Inject active mission context FIRST so the agent sees it immediately
local mission_file=".mosaic/orchestrator/mission.json"
if [[ -f "$mission_file" ]] && command -v jq &>/dev/null; then
local m_status
m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then
local m_name m_id m_count m_completed
m_name="$(jq -r '.name // "unnamed"' "$mission_file")"
m_id="$(jq -r '.mission_id // ""' "$mission_file")"
m_count="$(jq '.milestones | length' "$mission_file")"
m_completed="$(jq '[.milestones[] | select(.status == "completed")] | length' "$mission_file")"
cat <<MISSION_EOF
# ACTIVE MISSION — HARD GATE (Read Before Anything Else)
An active orchestration mission exists in this project. This is a BLOCKING requirement.
**Mission:** $m_name
**ID:** $m_id
**Status:** $m_status
**Milestones:** $m_completed / $m_count completed
## MANDATORY — Before ANY Response to the User
You MUST complete these steps before responding to any user message, including simple greetings:
1. Read \`~/.config/mosaic/guides/ORCHESTRATOR-PROTOCOL.md\` (mission lifecycle protocol)
2. Read \`docs/MISSION-MANIFEST.md\` for full mission scope, milestones, and success criteria
3. Read the latest scratchpad in \`docs/scratchpads/\` for session history, decisions, and corrections
4. Read \`docs/TASKS.md\` for current task state (what is done, what is next)
5. After reading all four, acknowledge the mission state to the user before proceeding
If the user gives a task, execute it within the mission context. If no task is given, present mission status and ask how to proceed.
MISSION_EOF
fi
fi
# Inject PRD status so the agent knows requirements state
local prd_file="docs/PRD.md"
if [[ -f "$prd_file" ]]; then
local prd_sections=0
local prd_assumptions=0
for entry in "Problem Statement|^#{2,3} .*(problem statement|objective)" \
"Scope / Non-Goals|^#{2,3} .*(scope|non.goal|out of scope|in.scope)" \
"User Stories / Requirements|^#{2,3} .*(user stor|stakeholder|user.*requirement)" \
"Functional Requirements|^#{2,3} .*functional requirement" \
"Non-Functional Requirements|^#{2,3} .*non.functional" \
"Acceptance Criteria|^#{2,3} .*acceptance criteria" \
"Technical Considerations|^#{2,3} .*(technical consideration|constraint|dependenc)" \
"Risks / Open Questions|^#{2,3} .*(risk|open question)" \
"Success Metrics / Testing|^#{2,3} .*(success metric|test|verification)" \
"Milestones / Delivery|^#{2,3} .*(milestone|delivery|scope version)"; do
local pattern="${entry#*|}"
grep -qiE "$pattern" "$prd_file" 2>/dev/null && prd_sections=$((prd_sections + 1))
done
prd_assumptions=$(grep -c 'ASSUMPTION:' "$prd_file" 2>/dev/null || echo 0)
local prd_status="ready"
(( prd_sections < 10 )) && prd_status="incomplete ($prd_sections/10 sections)"
cat <<PRD_EOF
# PRD Status
- **File:** docs/PRD.md
- **Status:** $prd_status
- **Assumptions:** $prd_assumptions
PRD_EOF
fi
cat <<'EOF'
# Mosaic Launcher Runtime Contract (Hard Gate)
This contract is injected by `mosaic` launch and is mandatory.
First assistant response MUST start with exactly one mode declaration line:
1. Orchestration mission: `Now initiating Orchestrator mode...`
2. Implementation mission: `Now initiating Delivery mode...`
3. Review-only mission: `Now initiating Review mode...`
No tool call or implementation step may occur before that first line.
Mosaic hard gates OVERRIDE runtime-default caution for routine delivery operations.
For required push/merge/issue-close/release actions, execute without routine confirmation prompts.
EOF
cat "$MOSAIC_HOME/AGENTS.md"
if [[ -f "$MOSAIC_HOME/USER.md" ]]; then
printf '\n\n# User Profile\n\n'
cat "$MOSAIC_HOME/USER.md"
fi
if [[ -f "$MOSAIC_HOME/TOOLS.md" ]]; then
printf '\n\n# Machine Tools\n\n'
cat "$MOSAIC_HOME/TOOLS.md"
fi
printf '\n\n# Runtime-Specific Contract\n\n'
cat "$runtime_file"
}
# Ensure runtime contract is present at the runtime's native config path.
# Used for runtimes that do not support CLI prompt injection.
ensure_runtime_config() {
local runtime="$1"
local dst="$2"
local tmp
tmp="$(mktemp)"
mkdir -p "$(dirname "$dst")"
build_runtime_prompt "$runtime" > "$tmp"
if ! cmp -s "$tmp" "$dst" 2>/dev/null; then
mv "$tmp" "$dst"
else
rm -f "$tmp"
fi
}
# Detect active mission and return an initial prompt if one exists.
# Sets MOSAIC_MISSION_PROMPT as a side effect.
_detect_mission_prompt() {
MOSAIC_MISSION_PROMPT=""
local mission_file=".mosaic/orchestrator/mission.json"
if [[ -f "$mission_file" ]] && command -v jq &>/dev/null; then
local m_status
m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then
local m_name
m_name="$(jq -r '.name // "unnamed"' "$mission_file")"
MOSAIC_MISSION_PROMPT="Active mission detected: ${m_name}. Read the mission state files and report status."
fi
fi
}
# Write a session lock if an active mission exists in the current directory.
# Called before exec so $$ captures the PID that will become the agent process.
_write_launcher_session_lock() {
local runtime="$1"
local mission_file=".mosaic/orchestrator/mission.json"
local lock_file=".mosaic/orchestrator/session.lock"
# Only write lock if mission exists and is active
[[ -f "$mission_file" ]] || return 0
command -v jq &>/dev/null || return 0
local m_status
m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
[[ "$m_status" == "active" || "$m_status" == "paused" ]] || return 0
local session_id
session_id="${runtime}-$(date +%Y%m%d-%H%M%S)-$$"
jq -n \
--arg sid "$session_id" \
--arg rt "$runtime" \
--arg pid "$$" \
--arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg pp "$(pwd)" \
--arg mid "" \
'{
session_id: $sid,
runtime: $rt,
pid: ($pid | tonumber),
started_at: $ts,
project_path: $pp,
milestone_id: $mid
}' > "$lock_file"
}
# Clean up session lock on exit (covers normal exit + signals).
# Registered via trap after _write_launcher_session_lock succeeds.
_cleanup_session_lock() {
rm -f ".mosaic/orchestrator/session.lock" 2>/dev/null
}
# Launcher functions
launch_claude() {
check_mosaic_home
check_agents_md
check_soul
check_runtime "claude"
check_sequential_thinking "claude"
_check_resumable_session
# Claude supports --append-system-prompt for direct injection
local runtime_prompt
runtime_prompt="$(build_runtime_prompt "claude")"
# If active mission exists and no user prompt was given, inject initial prompt
_detect_mission_prompt
_write_launcher_session_lock "claude"
trap _cleanup_session_lock EXIT INT TERM
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
echo "[mosaic] Launching Claude Code (active mission detected)..."
exec claude --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
else
echo "[mosaic] Launching Claude Code..."
exec claude --append-system-prompt "$runtime_prompt" "$@"
fi
}
launch_opencode() {
check_mosaic_home
check_agents_md
check_soul
check_runtime "opencode"
check_sequential_thinking "opencode"
_check_resumable_session
# OpenCode reads from ~/.config/opencode/AGENTS.md
ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
_write_launcher_session_lock "opencode"
trap _cleanup_session_lock EXIT INT TERM
echo "[mosaic] Launching OpenCode..."
exec opencode "$@"
}
launch_codex() {
check_mosaic_home
check_agents_md
check_soul
check_runtime "codex"
check_sequential_thinking "codex"
_check_resumable_session
# Codex reads from ~/.codex/instructions.md
ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
_detect_mission_prompt
_write_launcher_session_lock "codex"
trap _cleanup_session_lock EXIT INT TERM
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
echo "[mosaic] Launching Codex (active mission detected)..."
exec codex "$MOSAIC_MISSION_PROMPT"
else
echo "[mosaic] Launching Codex..."
exec codex "$@"
fi
}
launch_yolo() {
if [[ $# -eq 0 ]]; then
echo "[mosaic] ERROR: yolo requires a runtime (claude|codex|opencode)." >&2
echo "[mosaic] Example: mosaic yolo claude" >&2
exit 1
fi
local runtime="$1"
shift
case "$runtime" in
claude)
check_mosaic_home
check_agents_md
check_soul
check_runtime "claude"
check_sequential_thinking "claude"
# Claude uses an explicit dangerous permissions flag.
local runtime_prompt
runtime_prompt="$(build_runtime_prompt "claude")"
_detect_mission_prompt
_write_launcher_session_lock "claude"
trap _cleanup_session_lock EXIT INT TERM
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
echo "[mosaic] Launching Claude Code in YOLO mode (active mission detected)..."
exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT"
else
echo "[mosaic] Launching Claude Code in YOLO mode (dangerous permissions enabled)..."
exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$@"
fi
;;
codex)
check_mosaic_home
check_agents_md
check_soul
check_runtime "codex"
check_sequential_thinking "codex"
# Codex reads instructions.md from ~/.codex and supports a direct dangerous flag.
ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
_detect_mission_prompt
_write_launcher_session_lock "codex"
trap _cleanup_session_lock EXIT INT TERM
if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then
echo "[mosaic] Launching Codex in YOLO mode (active mission detected)..."
exec codex --dangerously-bypass-approvals-and-sandbox "$MOSAIC_MISSION_PROMPT"
else
echo "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..."
exec codex --dangerously-bypass-approvals-and-sandbox "$@"
fi
;;
opencode)
check_mosaic_home
check_agents_md
check_soul
check_runtime "opencode"
check_sequential_thinking "opencode"
# OpenCode defaults to allow-all permissions unless user config restricts them.
ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
_write_launcher_session_lock "opencode"
trap _cleanup_session_lock EXIT INT TERM
echo "[mosaic] Launching OpenCode in YOLO mode..."
exec opencode "$@"
;;
*)
echo "[mosaic] ERROR: Unsupported yolo runtime '$runtime'. Use claude|codex|opencode." >&2
exit 1
;;
esac
}
# Delegate to existing scripts
run_init() {
# Prefer wizard if Node.js and bundle are available
local wizard_bin="$MOSAIC_HOME/dist/mosaic-wizard.mjs"
if command -v node >/dev/null 2>&1 && [[ -f "$wizard_bin" ]]; then
exec node "$wizard_bin" "$@"
fi
# Fallback to legacy bash wizard
check_mosaic_home
exec "$MOSAIC_HOME/bin/mosaic-init" "$@"
}
run_doctor() {
check_mosaic_home
exec "$MOSAIC_HOME/bin/mosaic-doctor" "$@"
}
run_sync() {
check_mosaic_home
exec "$MOSAIC_HOME/bin/mosaic-sync-skills" "$@"
}
run_seq() {
check_mosaic_home
local checker="$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking"
local action="${1:-check}"
case "$action" in
check)
shift || true
exec "$checker" --check "$@"
;;
fix|apply)
shift || true
exec "$checker" "$@"
;;
start)
shift || true
check_runtime "npx"
echo "[mosaic] Starting sequential-thinking MCP server..."
exec npx -y @modelcontextprotocol/server-sequential-thinking "$@"
;;
*)
echo "[mosaic] ERROR: Unknown seq subcommand '$action'." >&2
echo "[mosaic] Use: mosaic seq check|fix|start" >&2
exit 1
;;
esac
}
run_coord() {
check_mosaic_home
local runtime="claude"
local runtime_flag=""
local -a coord_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
--claude|--codex)
local selected_runtime="${1#--}"
if [[ -n "$runtime_flag" ]] && [[ "$runtime" != "$selected_runtime" ]]; then
echo "[mosaic] ERROR: --claude and --codex are mutually exclusive for 'mosaic coord'." >&2
exit 1
fi
runtime="$selected_runtime"
runtime_flag="$1"
shift
;;
*)
coord_args+=("$1")
shift
;;
esac
done
local subcmd="${coord_args[0]:-help}"
if (( ${#coord_args[@]} > 1 )); then
set -- "${coord_args[@]:1}"
else
set --
fi
local tool_dir="$MOSAIC_HOME/tools/orchestrator"
case "$subcmd" in
status|session)
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-status.sh" "$@"
;;
init)
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/mission-init.sh" "$@"
;;
mission|progress)
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/mission-status.sh" "$@"
;;
continue|next)
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/continue-prompt.sh" "$@"
;;
run|start)
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-run.sh" "$@"
;;
smoke|test)
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/smoke-test.sh" "$@"
;;
resume|recover)
MOSAIC_COORD_RUNTIME="$runtime" exec bash "$tool_dir/session-resume.sh" "$@"
;;
help|*)
cat <<COORD_USAGE
mosaic coord — r0 manual coordinator tools
Commands:
init --name <name> [opts] Initialize a new mission
mission [--project <path>] Show mission progress dashboard
status [--project <path>] Check agent session health
continue [--project <path>] Generate continuation prompt for next session
run [--project <path>] Generate context and launch selected runtime
smoke Run orchestration behavior smoke checks
resume [--project <path>] Crash recovery (detect dirty state, generate fix)
Runtime:
--claude Use Claude runtime hints/prompts (default)
--codex Use Codex runtime hints/prompts
Examples:
mosaic coord init --name "Security Fix" --milestones "Critical,High,Medium"
mosaic coord mission
mosaic coord --codex mission
mosaic coord continue --copy
mosaic coord run
mosaic coord run --codex
mosaic coord smoke
mosaic coord continue --codex --copy
COORD_USAGE
;;
esac
}
# Resume advisory — prints warning if active mission or stale session detected
_check_resumable_session() {
local mission_file=".mosaic/orchestrator/mission.json"
local lock_file=".mosaic/orchestrator/session.lock"
command -v jq &>/dev/null || return 0
if [[ -f "$lock_file" ]]; then
local pid
pid="$(jq -r '.pid // 0' "$lock_file" 2>/dev/null)"
if [[ -n "$pid" ]] && [[ "$pid" != "0" ]] && ! kill -0 "$pid" 2>/dev/null; then
# Stale lock from a dead session — clean it up
rm -f "$lock_file"
echo "[mosaic] Cleaned up stale session lock (PID $pid no longer running)."
echo ""
fi
elif [[ -f "$mission_file" ]]; then
local status
status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)"
if [[ "$status" == "active" ]]; then
echo "[mosaic] Active mission detected. Generate continuation prompt with:"
echo "[mosaic] mosaic coord continue"
echo ""
fi
fi
}
run_prdy() {
check_mosaic_home
local runtime="claude"
local runtime_flag=""
local -a prdy_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
--claude|--codex)
local selected_runtime="${1#--}"
if [[ -n "$runtime_flag" ]] && [[ "$runtime" != "$selected_runtime" ]]; then
echo "[mosaic] ERROR: --claude and --codex are mutually exclusive for 'mosaic prdy'." >&2
exit 1
fi
runtime="$selected_runtime"
runtime_flag="$1"
shift
;;
*)
prdy_args+=("$1")
shift
;;
esac
done
local subcmd="${prdy_args[0]:-help}"
if (( ${#prdy_args[@]} > 1 )); then
set -- "${prdy_args[@]:1}"
else
set --
fi
local tool_dir="$MOSAIC_HOME/tools/prdy"
case "$subcmd" in
init)
MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-init.sh" "$@"
;;
update)
MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-update.sh" "$@"
;;
validate|check)
MOSAIC_PRDY_RUNTIME="$runtime" exec bash "$tool_dir/prdy-validate.sh" "$@"
;;
status)
exec bash "$tool_dir/prdy-status.sh" "$@"
;;
help|*)
cat <<PRDY_USAGE
mosaic prdy — PRD creation and validation tools
Commands:
init [--project <path>] [--name <feature>] Create docs/PRD.md via guided runtime session
update [--project <path>] Update existing docs/PRD.md via guided runtime session
validate [--project <path>] Check PRD completeness against Mosaic guide (bash-only)
status [--project <path>] [--format short|json] Quick PRD health check (one-liner)
Runtime:
--claude Use Claude runtime (default)
--codex Use Codex runtime
Examples:
mosaic prdy init --name "User Authentication"
mosaic prdy update
mosaic prdy --codex init --name "User Authentication"
mosaic prdy validate
Output location: docs/PRD.md (per Mosaic PRD guide)
PRDY_USAGE
;;
esac
}
run_bootstrap() {
check_mosaic_home
exec "$MOSAIC_HOME/bin/mosaic-bootstrap-repo" "$@"
}
run_release_upgrade() {
check_mosaic_home
exec "$MOSAIC_HOME/bin/mosaic-release-upgrade" "$@"
}
run_project_upgrade() {
check_mosaic_home
exec "$MOSAIC_HOME/bin/mosaic-upgrade" "$@"
}
run_upgrade() {
check_mosaic_home
# Default: upgrade installed release
if [[ $# -eq 0 ]]; then
run_release_upgrade
fi
case "$1" in
release)
shift
run_release_upgrade "$@"
;;
check)
shift
run_release_upgrade --dry-run "$@"
;;
project)
shift
run_project_upgrade "$@"
;;
# Backward compatibility for historical project-upgrade usage.
--all|--root)
run_project_upgrade "$@"
;;
--dry-run|--ref|--keep|--overwrite|-y|--yes)
run_release_upgrade "$@"
;;
-*)
run_release_upgrade "$@"
;;
*)
run_project_upgrade "$@"
;;
esac
}
# Main router
if [[ $# -eq 0 ]]; then
usage
exit 0
fi
command="$1"
shift
case "$command" in
claude) launch_claude "$@" ;;
opencode) launch_opencode "$@" ;;
codex) launch_codex "$@" ;;
yolo|--yolo) launch_yolo "$@" ;;
init) run_init "$@" ;;
doctor) run_doctor "$@" ;;
sync) run_sync "$@" ;;
seq) run_seq "$@" ;;
bootstrap) run_bootstrap "$@" ;;
prdy) run_prdy "$@" ;;
coord) run_coord "$@" ;;
upgrade) run_upgrade "$@" ;;
release-upgrade) run_release_upgrade "$@" ;;
project-upgrade) run_project_upgrade "$@" ;;
help|-h|--help) usage ;;
version|-v|--version) echo "mosaic $VERSION" ;;
*)
echo "[mosaic] Unknown command: $command" >&2
echo "[mosaic] Run 'mosaic --help' for usage." >&2
exit 1
;;
esac