# mosaic.ps1 — Unified agent launcher and management CLI (Windows) # # 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 $ErrorActionPreference = "Stop" $MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" } $Version = "0.1.0" function Show-Usage { Write-Host @" mosaic $Version - Unified agent launcher Usage: mosaic [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 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 Options: -h, --help Show this help -v, --version Show version "@ } function Assert-MosaicHome { if (-not (Test-Path $MosaicHome)) { Write-Host "[mosaic] ERROR: ~/.config/mosaic not found." -ForegroundColor Red Write-Host "[mosaic] Install with: irm https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.ps1 | iex" exit 1 } } function Assert-AgentsMd { $agentsPath = Join-Path $MosaicHome "AGENTS.md" if (-not (Test-Path $agentsPath)) { Write-Host "[mosaic] ERROR: ~/.config/mosaic/AGENTS.md not found." -ForegroundColor Red Write-Host "[mosaic] Re-run the installer." exit 1 } } function Assert-Soul { $soulPath = Join-Path $MosaicHome "SOUL.md" if (-not (Test-Path $soulPath)) { Write-Host "[mosaic] SOUL.md not found. Running mosaic init..." & (Join-Path $MosaicHome "bin\mosaic-init.ps1") } } function Assert-Runtime { param([string]$Cmd) if (-not (Get-Command $Cmd -ErrorAction SilentlyContinue)) { Write-Host "[mosaic] ERROR: '$Cmd' not found in PATH." -ForegroundColor Red Write-Host "[mosaic] Install $Cmd before launching." exit 1 } } function Ensure-RuntimeConfig { param([string]$Dst) $src = Join-Path $MosaicHome "AGENTS.md" $parent = Split-Path $Dst -Parent if (-not (Test-Path $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } $srcHash = (Get-FileHash $src -Algorithm SHA256).Hash $dstHash = if (Test-Path $Dst) { (Get-FileHash $Dst -Algorithm SHA256).Hash } else { "" } if ($srcHash -ne $dstHash) { Copy-Item $src $Dst -Force } } if ($args.Count -eq 0) { Show-Usage exit 0 } $command = $args[0] $remaining = if ($args.Count -gt 1) { $args[1..($args.Count - 1)] } else { @() } switch ($command) { "claude" { Assert-MosaicHome Assert-AgentsMd Assert-Soul Assert-Runtime "claude" # Claude supports --append-system-prompt for direct injection $agentsContent = Get-Content (Join-Path $MosaicHome "AGENTS.md") -Raw Write-Host "[mosaic] Launching Claude Code..." & claude --append-system-prompt $agentsContent @remaining } "opencode" { Assert-MosaicHome Assert-AgentsMd Assert-Soul Assert-Runtime "opencode" # OpenCode reads from ~/.config/opencode/AGENTS.md Ensure-RuntimeConfig (Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md") Write-Host "[mosaic] Launching OpenCode..." & opencode @remaining } "codex" { Assert-MosaicHome Assert-AgentsMd Assert-Soul Assert-Runtime "codex" # Codex reads from ~/.codex/instructions.md Ensure-RuntimeConfig (Join-Path $env:USERPROFILE ".codex\instructions.md") Write-Host "[mosaic] Launching Codex..." & codex @remaining } "init" { Assert-MosaicHome & (Join-Path $MosaicHome "bin\mosaic-init.ps1") @remaining } "doctor" { Assert-MosaicHome & (Join-Path $MosaicHome "bin\mosaic-doctor.ps1") @remaining } "sync" { Assert-MosaicHome & (Join-Path $MosaicHome "bin\mosaic-sync-skills.ps1") @remaining } "bootstrap" { Assert-MosaicHome Write-Host "[mosaic] NOTE: mosaic-bootstrap-repo requires bash. Use Git Bash or WSL." -ForegroundColor Yellow & (Join-Path $MosaicHome "bin\mosaic-bootstrap-repo") @remaining } "upgrade" { Assert-MosaicHome if ($remaining.Count -eq 0) { & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") break } $mode = $remaining[0] $tail = if ($remaining.Count -gt 1) { $remaining[1..($remaining.Count - 1)] } else { @() } switch -Regex ($mode) { "^release$" { & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @tail } "^check$" { & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") -DryRun @tail } "^project$" { Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow & (Join-Path $MosaicHome "bin\mosaic-upgrade") @tail } "^(--all|--root)$" { Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow & (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining } "^(--dry-run|--ref|--keep|--overwrite|-y|--yes)$" { & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining } "^-.*" { & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining } default { Write-Host "[mosaic] NOTE: treating positional argument as project path." -ForegroundColor Yellow Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow & (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining } } } "release-upgrade" { Assert-MosaicHome & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining } "project-upgrade" { Assert-MosaicHome Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow & (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining } { $_ -in "help", "-h", "--help" } { Show-Usage } { $_ -in "version", "-v", "--version" } { Write-Host "mosaic $Version" } default { Write-Host "[mosaic] Unknown command: $command" -ForegroundColor Red Write-Host "[mosaic] Run 'mosaic --help' for usage." exit 1 } }