#!/usr/bin/env bash set -euo pipefail # mosaic — Unified agent launcher and management CLI # # AGENTS.md is the single source of truth for all agent sessions. # The launcher injects it into every runtime consistently. # # Usage: # mosaic claude [args...] Launch Claude Code with AGENTS.md injected # mosaic opencode [args...] Launch OpenCode with AGENTS.md injected # mosaic codex [args...] Launch Codex with AGENTS.md injected # mosaic init [args...] Generate SOUL.md interactively # mosaic doctor [args...] Health audit # mosaic sync [args...] Sync skills # mosaic bootstrap Bootstrap a repo MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}" VERSION="0.1.0" usage() { cat < [args...] Agent Launchers: claude [args...] Launch Claude Code with AGENTS.md injected opencode [args...] Launch OpenCode with AGENTS.md injected codex [args...] Launch Codex with AGENTS.md injected Management: init [args...] Generate SOUL.md (agent identity contract) doctor [args...] Audit runtime state and detect drift sync [args...] Sync skills from canonical source bootstrap Bootstrap a repo with Mosaic standards 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 } # Ensure AGENTS.md is present at the runtime's native config path. # Used for runtimes that don't support CLI prompt injection. ensure_runtime_config() { local src="$MOSAIC_HOME/AGENTS.md" local dst="$1" mkdir -p "$(dirname "$dst")" if ! cmp -s "$src" "$dst" 2>/dev/null; then cp "$src" "$dst" fi } # Launcher functions launch_claude() { check_mosaic_home check_agents_md check_soul check_runtime "claude" # Claude supports --append-system-prompt for direct injection local agents_content agents_content="$(cat "$MOSAIC_HOME/AGENTS.md")" echo "[mosaic] Launching Claude Code..." exec claude --append-system-prompt "$agents_content" "$@" } launch_opencode() { check_mosaic_home check_agents_md check_soul check_runtime "opencode" # OpenCode reads from ~/.config/opencode/AGENTS.md — copy canonical version there ensure_runtime_config "$HOME/.config/opencode/AGENTS.md" echo "[mosaic] Launching OpenCode..." exec opencode "$@" } launch_codex() { check_mosaic_home check_agents_md check_soul check_runtime "codex" # Codex reads from ~/.codex/instructions.md — copy canonical version there ensure_runtime_config "$HOME/.codex/instructions.md" echo "[mosaic] Launching Codex..." exec codex "$@" } # Delegate to existing scripts run_init() { 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_bootstrap() { check_mosaic_home exec "$MOSAIC_HOME/bin/mosaic-bootstrap-repo" "$@" } # 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 "$@" ;; init) run_init "$@" ;; doctor) run_doctor "$@" ;; sync) run_sync "$@" ;; bootstrap) run_bootstrap "$@" ;; 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