diff --git a/bin/mosaic b/bin/mosaic new file mode 100755 index 0000000..fb87524 --- /dev/null +++ b/bin/mosaic @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +set -euo pipefail + +# mosaic — Unified agent launcher and management CLI +# +# Usage: +# mosaic claude [args...] Launch Claude Code with SOUL.md injected +# mosaic opencode [args...] Launch OpenCode +# mosaic codex [args...] Launch Codex +# 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 SOUL.md injected + opencode [args...] Launch OpenCode + codex [args...] Launch Codex + +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_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 +} + +# Launcher functions +launch_claude() { + check_mosaic_home + check_soul + check_runtime "claude" + + local soul_file="$MOSAIC_HOME/SOUL.md" + echo "[mosaic] Launching Claude Code with SOUL.md injection..." + exec claude --append-system-prompt-file "$soul_file" "$@" +} + +launch_opencode() { + check_mosaic_home + check_soul + check_runtime "opencode" + + echo "[mosaic] Launching OpenCode..." + exec opencode "$@" +} + +launch_codex() { + check_mosaic_home + check_soul + check_runtime "codex" + + 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 "$@" ;; + -h|--help) usage ;; + -v|--version) echo "mosaic $VERSION" ;; + *) + echo "[mosaic] Unknown command: $command" >&2 + echo "[mosaic] Run 'mosaic --help' for usage." >&2 + exit 1 + ;; +esac diff --git a/bin/mosaic-init b/bin/mosaic-init new file mode 100755 index 0000000..2763abe --- /dev/null +++ b/bin/mosaic-init @@ -0,0 +1,210 @@ +#!/usr/bin/env bash +set -euo pipefail + +# mosaic-init — Interactive SOUL.md generator +# +# Usage: +# mosaic-init # Interactive mode +# mosaic-init --name "Jarvis" --style direct # Flag overrides +# mosaic-init --name "Jarvis" --role "memory steward" --style direct \ +# --accessibility "ADHD-friendly chunking" --guardrails "Never auto-commit" + +MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}" +TEMPLATE="$MOSAIC_HOME/templates/SOUL.md.template" +OUTPUT="$MOSAIC_HOME/SOUL.md" + +# Defaults +AGENT_NAME="" +ROLE_DESCRIPTION="" +STYLE="" +ACCESSIBILITY="" +CUSTOM_GUARDRAILS="" + +usage() { + cat < Agent name (e.g., "Jarvis", "Assistant") + --role Role description (e.g., "memory steward, execution partner") + --style