feat!: unify mosaic CLI — native launcher, no bin/ directory
BREAKING CHANGE: ~/.config/mosaic/bin/ is removed entirely. The mosaic npm CLI is now the only executable. ## What changed - **bin/ → deleted**: All scripts moved to tools/_scripts/ (internal) - **mosaic-launch → deleted**: Launcher logic is native TypeScript in packages/cli/src/commands/launch.ts - **mosaic.ps1 → deleted**: PowerShell launcher removed - **Framework install.sh**: Complete rewrite with migration system - **Version tracking**: .framework-version file (schema v2) - **Migration v1→v2**: Auto-removes bin/, cleans old PATH entries from shell profiles ## Native TypeScript launcher (commands/launch.ts) All runtime launch logic ported from bash: - Runtime prompt builder (AGENTS.md + RUNTIME.md + USER.md + TOOLS.md) - Mission context injection (reads .mosaic/orchestrator/mission.json) - PRD status injection (scans docs/PRD.md) - Pre-flight checks (MOSAIC_HOME, AGENTS.md, SOUL.md, runtime binary) - Session lock management with signal cleanup - Per-runtime launch: Claude, Codex, OpenCode, Pi - Yolo mode flags per runtime - Pi skill discovery + extension loading - Framework management (init, doctor, sync, bootstrap) delegates to tools/_scripts/ bash implementations ## Installer - tools/install.sh: detects framework by .framework-version or AGENTS.md - Framework install.sh: migration system with schema versioning - Forward-compatible: add migrations as numbered blocks - No PATH manipulation for framework (npm bin is the only PATH entry)
This commit is contained in:
424
packages/mosaic/framework/tools/_scripts/mosaic-init
Executable file
424
packages/mosaic/framework/tools/_scripts/mosaic-init
Executable file
@@ -0,0 +1,424 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# mosaic-init — Interactive agent identity, user profile, and tool config 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}"
|
||||
SOUL_TEMPLATE="$MOSAIC_HOME/templates/SOUL.md.template"
|
||||
USER_TEMPLATE="$MOSAIC_HOME/templates/USER.md.template"
|
||||
TOOLS_TEMPLATE="$MOSAIC_HOME/templates/TOOLS.md.template"
|
||||
SOUL_OUTPUT="$MOSAIC_HOME/SOUL.md"
|
||||
USER_OUTPUT="$MOSAIC_HOME/USER.md"
|
||||
TOOLS_OUTPUT="$MOSAIC_HOME/TOOLS.md"
|
||||
|
||||
# Defaults
|
||||
AGENT_NAME=""
|
||||
ROLE_DESCRIPTION=""
|
||||
STYLE=""
|
||||
ACCESSIBILITY=""
|
||||
CUSTOM_GUARDRAILS=""
|
||||
|
||||
# USER.md defaults
|
||||
USER_NAME=""
|
||||
PRONOUNS=""
|
||||
TIMEZONE=""
|
||||
BACKGROUND=""
|
||||
COMMUNICATION_PREFS=""
|
||||
PERSONAL_BOUNDARIES=""
|
||||
PROJECTS_TABLE=""
|
||||
|
||||
# TOOLS.md defaults
|
||||
GIT_PROVIDERS_TABLE=""
|
||||
CREDENTIALS_LOCATION=""
|
||||
CUSTOM_TOOLS_SECTION=""
|
||||
|
||||
usage() {
|
||||
cat <<USAGE
|
||||
Usage: $(basename "$0") [options]
|
||||
|
||||
Generate Mosaic identity and configuration files:
|
||||
- SOUL.md — Agent identity contract
|
||||
- USER.md — User profile and accessibility
|
||||
- TOOLS.md — Machine-level tool reference
|
||||
|
||||
Interactive by default. Use flags to skip prompts.
|
||||
|
||||
Options:
|
||||
--name <name> Agent name (e.g., "Jarvis", "Assistant")
|
||||
--role <description> Role description (e.g., "memory steward, execution partner")
|
||||
--style <style> Communication style: direct, friendly, or formal
|
||||
--accessibility <prefs> Accessibility preferences (e.g., "ADHD-friendly chunking")
|
||||
--guardrails <rules> Custom guardrails (appended to defaults)
|
||||
--user-name <name> Your name for USER.md
|
||||
--pronouns <pronouns> Your pronouns (e.g., "He/Him")
|
||||
--timezone <tz> Your timezone (e.g., "America/Chicago")
|
||||
--non-interactive Fail if any required value is missing (no prompts)
|
||||
--soul-only Only generate SOUL.md
|
||||
-h, --help Show help
|
||||
USAGE
|
||||
}
|
||||
|
||||
NON_INTERACTIVE=0
|
||||
SOUL_ONLY=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--name) AGENT_NAME="$2"; shift 2 ;;
|
||||
--role) ROLE_DESCRIPTION="$2"; shift 2 ;;
|
||||
--style) STYLE="$2"; shift 2 ;;
|
||||
--accessibility) ACCESSIBILITY="$2"; shift 2 ;;
|
||||
--guardrails) CUSTOM_GUARDRAILS="$2"; shift 2 ;;
|
||||
--user-name) USER_NAME="$2"; shift 2 ;;
|
||||
--pronouns) PRONOUNS="$2"; shift 2 ;;
|
||||
--timezone) TIMEZONE="$2"; shift 2 ;;
|
||||
--non-interactive) NON_INTERACTIVE=1; shift ;;
|
||||
--soul-only) SOUL_ONLY=1; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "Unknown argument: $1" >&2; usage >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
prompt_if_empty() {
|
||||
local var_name="$1"
|
||||
local prompt_text="$2"
|
||||
local default_value="${3:-}"
|
||||
local current_value="${!var_name}"
|
||||
|
||||
if [[ -n "$current_value" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ $NON_INTERACTIVE -eq 1 ]]; then
|
||||
if [[ -n "$default_value" ]]; then
|
||||
eval "$var_name=\"$default_value\""
|
||||
return
|
||||
fi
|
||||
echo "[mosaic-init] ERROR: --$var_name is required in non-interactive mode" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n "$default_value" ]]; then
|
||||
prompt_text="$prompt_text [$default_value]"
|
||||
fi
|
||||
|
||||
printf "%s: " "$prompt_text"
|
||||
read -r value
|
||||
if [[ -z "$value" && -n "$default_value" ]]; then
|
||||
value="$default_value"
|
||||
fi
|
||||
eval "$var_name=\"$value\""
|
||||
}
|
||||
|
||||
prompt_multiline() {
|
||||
local var_name="$1"
|
||||
local prompt_text="$2"
|
||||
local default_value="${3:-}"
|
||||
local current_value="${!var_name}"
|
||||
|
||||
if [[ -n "$current_value" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ $NON_INTERACTIVE -eq 1 ]]; then
|
||||
eval "$var_name=\"$default_value\""
|
||||
return
|
||||
fi
|
||||
|
||||
echo "$prompt_text"
|
||||
printf "(Press Enter to skip, or type your response): "
|
||||
read -r value
|
||||
if [[ -z "$value" ]]; then
|
||||
value="$default_value"
|
||||
fi
|
||||
eval "$var_name=\"$value\""
|
||||
}
|
||||
|
||||
# ── SOUL.md Generation ────────────────────────────────────────
|
||||
echo "[mosaic-init] Generating SOUL.md — agent identity contract"
|
||||
echo ""
|
||||
|
||||
prompt_if_empty AGENT_NAME "What name should agents use" "Assistant"
|
||||
prompt_if_empty ROLE_DESCRIPTION "Agent role description" "execution partner and visibility engine"
|
||||
|
||||
if [[ -z "$STYLE" && $NON_INTERACTIVE -eq 0 ]]; then
|
||||
echo ""
|
||||
echo "Communication style:"
|
||||
echo " 1) direct — Concise, no fluff, actionable"
|
||||
echo " 2) friendly — Warm but efficient, conversational"
|
||||
echo " 3) formal — Professional, structured, thorough"
|
||||
printf "Choose [1/2/3] (default: 1): "
|
||||
read -r style_choice
|
||||
case "${style_choice:-1}" in
|
||||
1|direct) STYLE="direct" ;;
|
||||
2|friendly) STYLE="friendly" ;;
|
||||
3|formal) STYLE="formal" ;;
|
||||
*) STYLE="direct" ;;
|
||||
esac
|
||||
elif [[ -z "$STYLE" ]]; then
|
||||
STYLE="direct"
|
||||
fi
|
||||
|
||||
prompt_if_empty ACCESSIBILITY "Accessibility preferences (or 'none')" "none"
|
||||
|
||||
if [[ $NON_INTERACTIVE -eq 0 && -z "$CUSTOM_GUARDRAILS" ]]; then
|
||||
echo ""
|
||||
printf "Custom guardrails (optional, press Enter to skip): "
|
||||
read -r CUSTOM_GUARDRAILS
|
||||
fi
|
||||
|
||||
# Build behavioral principles based on style + accessibility
|
||||
BEHAVIORAL_PRINCIPLES=""
|
||||
case "$STYLE" in
|
||||
direct)
|
||||
BEHAVIORAL_PRINCIPLES="1. Clarity over performance theater.
|
||||
2. Practical execution over abstract planning.
|
||||
3. Truthfulness over confidence: state uncertainty explicitly.
|
||||
4. Visible state over hidden assumptions.
|
||||
5. Accessibility-aware — see \`~/.config/mosaic/USER.md\` for user-specific accommodations."
|
||||
;;
|
||||
friendly)
|
||||
BEHAVIORAL_PRINCIPLES="1. Be helpful and approachable while staying efficient.
|
||||
2. Provide context and explain reasoning when helpful.
|
||||
3. Truthfulness over confidence: state uncertainty explicitly.
|
||||
4. Visible state over hidden assumptions.
|
||||
5. Accessibility-aware — see \`~/.config/mosaic/USER.md\` for user-specific accommodations."
|
||||
;;
|
||||
formal)
|
||||
BEHAVIORAL_PRINCIPLES="1. Maintain professional, structured communication.
|
||||
2. Provide thorough analysis with explicit tradeoffs.
|
||||
3. Truthfulness over confidence: state uncertainty explicitly.
|
||||
4. Document decisions and rationale clearly.
|
||||
5. Accessibility-aware — see \`~/.config/mosaic/USER.md\` for user-specific accommodations."
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$ACCESSIBILITY" != "none" && -n "$ACCESSIBILITY" ]]; then
|
||||
BEHAVIORAL_PRINCIPLES="$BEHAVIORAL_PRINCIPLES
|
||||
6. $ACCESSIBILITY."
|
||||
fi
|
||||
|
||||
# Build communication style section
|
||||
COMMUNICATION_STYLE=""
|
||||
case "$STYLE" in
|
||||
direct)
|
||||
COMMUNICATION_STYLE="- Be direct, concise, and concrete.
|
||||
- Avoid fluff, hype, and anthropomorphic roleplay.
|
||||
- Do not simulate certainty when facts are missing.
|
||||
- Prefer actionable next steps and explicit tradeoffs."
|
||||
;;
|
||||
friendly)
|
||||
COMMUNICATION_STYLE="- Be warm and conversational while staying focused.
|
||||
- Explain your reasoning when it helps the user.
|
||||
- Do not simulate certainty when facts are missing.
|
||||
- Prefer actionable next steps with clear context."
|
||||
;;
|
||||
formal)
|
||||
COMMUNICATION_STYLE="- Use professional, structured language.
|
||||
- Provide thorough explanations with supporting detail.
|
||||
- Do not simulate certainty when facts are missing.
|
||||
- Present options with explicit tradeoffs and recommendations."
|
||||
;;
|
||||
esac
|
||||
|
||||
# Format custom guardrails
|
||||
FORMATTED_GUARDRAILS=""
|
||||
if [[ -n "$CUSTOM_GUARDRAILS" ]]; then
|
||||
FORMATTED_GUARDRAILS="- $CUSTOM_GUARDRAILS"
|
||||
fi
|
||||
|
||||
# Verify template exists
|
||||
if [[ ! -f "$SOUL_TEMPLATE" ]]; then
|
||||
echo "[mosaic-init] ERROR: Template not found: $SOUL_TEMPLATE" >&2
|
||||
echo "[mosaic-init] Run the Mosaic installer first." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate SOUL.md from template using awk (handles multi-line values)
|
||||
awk -v name="$AGENT_NAME" \
|
||||
-v role="$ROLE_DESCRIPTION" \
|
||||
-v principles="$BEHAVIORAL_PRINCIPLES" \
|
||||
-v comms="$COMMUNICATION_STYLE" \
|
||||
-v guardrails="$FORMATTED_GUARDRAILS" \
|
||||
'{
|
||||
gsub(/\{\{AGENT_NAME\}\}/, name)
|
||||
gsub(/\{\{ROLE_DESCRIPTION\}\}/, role)
|
||||
gsub(/\{\{BEHAVIORAL_PRINCIPLES\}\}/, principles)
|
||||
gsub(/\{\{COMMUNICATION_STYLE\}\}/, comms)
|
||||
gsub(/\{\{CUSTOM_GUARDRAILS\}\}/, guardrails)
|
||||
print
|
||||
}' "$SOUL_TEMPLATE" > "$SOUL_OUTPUT"
|
||||
|
||||
echo ""
|
||||
echo "[mosaic-init] Generated: $SOUL_OUTPUT"
|
||||
echo "[mosaic-init] Agent name: $AGENT_NAME"
|
||||
echo "[mosaic-init] Style: $STYLE"
|
||||
|
||||
if [[ $SOUL_ONLY -eq 1 ]]; then
|
||||
# Push to runtime adapters and exit
|
||||
if [[ -x "$MOSAIC_HOME/tools/_scripts/mosaic-link-runtime-assets" ]]; then
|
||||
echo "[mosaic-init] Updating runtime adapters..."
|
||||
"$MOSAIC_HOME/tools/_scripts/mosaic-link-runtime-assets"
|
||||
fi
|
||||
echo "[mosaic-init] Done. Launch with: mosaic claude"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── USER.md Generation ────────────────────────────────────────
|
||||
echo ""
|
||||
echo "[mosaic-init] Generating USER.md — user profile"
|
||||
echo ""
|
||||
|
||||
prompt_if_empty USER_NAME "Your name" ""
|
||||
prompt_if_empty PRONOUNS "Your pronouns" "They/Them"
|
||||
prompt_if_empty TIMEZONE "Your timezone" "UTC"
|
||||
|
||||
prompt_multiline BACKGROUND "Your professional background (brief summary)" "(not configured)"
|
||||
|
||||
# Build accessibility section
|
||||
ACCESSIBILITY_SECTION=""
|
||||
if [[ "$ACCESSIBILITY" != "none" && -n "$ACCESSIBILITY" ]]; then
|
||||
ACCESSIBILITY_SECTION="$ACCESSIBILITY"
|
||||
else
|
||||
if [[ $NON_INTERACTIVE -eq 0 ]]; then
|
||||
echo ""
|
||||
prompt_multiline ACCESSIBILITY_SECTION \
|
||||
"Accessibility or neurodivergence accommodations (or press Enter to skip)" \
|
||||
"(No specific accommodations configured. Edit this section to add any.)"
|
||||
else
|
||||
ACCESSIBILITY_SECTION="(No specific accommodations configured. Edit this section to add any.)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build communication preferences
|
||||
if [[ -z "$COMMUNICATION_PREFS" ]]; then
|
||||
case "$STYLE" in
|
||||
direct)
|
||||
COMMUNICATION_PREFS="- Direct and concise
|
||||
- No sycophancy
|
||||
- Executive summaries and tables for overview"
|
||||
;;
|
||||
friendly)
|
||||
COMMUNICATION_PREFS="- Warm and conversational
|
||||
- Explain reasoning when helpful
|
||||
- Balance thoroughness with brevity"
|
||||
;;
|
||||
formal)
|
||||
COMMUNICATION_PREFS="- Professional and structured
|
||||
- Thorough explanations with supporting detail
|
||||
- Formal tone with explicit recommendations"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
prompt_multiline PERSONAL_BOUNDARIES \
|
||||
"Personal boundaries or preferences agents should respect" \
|
||||
"(Edit this section to add any personal boundaries.)"
|
||||
|
||||
if [[ -z "$PROJECTS_TABLE" ]]; then
|
||||
PROJECTS_TABLE="| Project | Stack | Registry |
|
||||
|---------|-------|----------|
|
||||
| (none configured) | | |"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$USER_TEMPLATE" ]]; then
|
||||
echo "[mosaic-init] WARN: USER.md template not found: $USER_TEMPLATE" >&2
|
||||
echo "[mosaic-init] Skipping USER.md generation." >&2
|
||||
else
|
||||
awk -v user_name="$USER_NAME" \
|
||||
-v pronouns="$PRONOUNS" \
|
||||
-v timezone="$TIMEZONE" \
|
||||
-v background="$BACKGROUND" \
|
||||
-v accessibility="$ACCESSIBILITY_SECTION" \
|
||||
-v comms="$COMMUNICATION_PREFS" \
|
||||
-v boundaries="$PERSONAL_BOUNDARIES" \
|
||||
-v projects="$PROJECTS_TABLE" \
|
||||
'{
|
||||
gsub(/\{\{USER_NAME\}\}/, user_name)
|
||||
gsub(/\{\{PRONOUNS\}\}/, pronouns)
|
||||
gsub(/\{\{TIMEZONE\}\}/, timezone)
|
||||
gsub(/\{\{BACKGROUND\}\}/, background)
|
||||
gsub(/\{\{ACCESSIBILITY_SECTION\}\}/, accessibility)
|
||||
gsub(/\{\{COMMUNICATION_PREFS\}\}/, comms)
|
||||
gsub(/\{\{PERSONAL_BOUNDARIES\}\}/, boundaries)
|
||||
gsub(/\{\{PROJECTS_TABLE\}\}/, projects)
|
||||
print
|
||||
}' "$USER_TEMPLATE" > "$USER_OUTPUT"
|
||||
|
||||
echo "[mosaic-init] Generated: $USER_OUTPUT"
|
||||
fi
|
||||
|
||||
# ── TOOLS.md Generation ───────────────────────────────────────
|
||||
echo ""
|
||||
echo "[mosaic-init] Generating TOOLS.md — machine-level tool reference"
|
||||
echo ""
|
||||
|
||||
if [[ -z "$GIT_PROVIDERS_TABLE" ]]; then
|
||||
if [[ $NON_INTERACTIVE -eq 0 ]]; then
|
||||
echo "Git providers (add rows for your Gitea/GitHub/GitLab instances):"
|
||||
printf "Primary git provider URL (or press Enter to skip): "
|
||||
read -r git_url
|
||||
if [[ -n "$git_url" ]]; then
|
||||
printf "Provider name: "
|
||||
read -r git_name
|
||||
printf "CLI tool (tea/gh/glab): "
|
||||
read -r git_cli
|
||||
printf "Purpose: "
|
||||
read -r git_purpose
|
||||
GIT_PROVIDERS_TABLE="| Instance | URL | CLI | Purpose |
|
||||
|----------|-----|-----|---------|
|
||||
| $git_name | $git_url | \`$git_cli\` | $git_purpose |"
|
||||
else
|
||||
GIT_PROVIDERS_TABLE="| Instance | URL | CLI | Purpose |
|
||||
|----------|-----|-----|---------|
|
||||
| (add your git providers here) | | | |"
|
||||
fi
|
||||
else
|
||||
GIT_PROVIDERS_TABLE="| Instance | URL | CLI | Purpose |
|
||||
|----------|-----|-----|---------|
|
||||
| (add your git providers here) | | | |"
|
||||
fi
|
||||
fi
|
||||
|
||||
prompt_if_empty CREDENTIALS_LOCATION "Credential file path (or 'none')" "none"
|
||||
|
||||
if [[ -z "$CUSTOM_TOOLS_SECTION" ]]; then
|
||||
CUSTOM_TOOLS_SECTION="## Custom Tools
|
||||
|
||||
(Add any machine-specific tools, scripts, or workflows here.)"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$TOOLS_TEMPLATE" ]]; then
|
||||
echo "[mosaic-init] WARN: TOOLS.md template not found: $TOOLS_TEMPLATE" >&2
|
||||
echo "[mosaic-init] Skipping TOOLS.md generation." >&2
|
||||
else
|
||||
awk -v providers="$GIT_PROVIDERS_TABLE" \
|
||||
-v creds="$CREDENTIALS_LOCATION" \
|
||||
-v custom="$CUSTOM_TOOLS_SECTION" \
|
||||
'{
|
||||
gsub(/\{\{GIT_PROVIDERS_TABLE\}\}/, providers)
|
||||
gsub(/\{\{CREDENTIALS_LOCATION\}\}/, creds)
|
||||
gsub(/\{\{CUSTOM_TOOLS_SECTION\}\}/, custom)
|
||||
print
|
||||
}' "$TOOLS_TEMPLATE" > "$TOOLS_OUTPUT"
|
||||
|
||||
echo "[mosaic-init] Generated: $TOOLS_OUTPUT"
|
||||
fi
|
||||
|
||||
# ── Finalize ──────────────────────────────────────────────────
|
||||
|
||||
# Push to runtime adapters
|
||||
if [[ -x "$MOSAIC_HOME/tools/_scripts/mosaic-link-runtime-assets" ]]; then
|
||||
echo ""
|
||||
echo "[mosaic-init] Updating runtime adapters..."
|
||||
"$MOSAIC_HOME/tools/_scripts/mosaic-link-runtime-assets"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "[mosaic-init] Done. Launch with: mosaic claude"
|
||||
echo "[mosaic-init] Edit USER.md and TOOLS.md directly for further customization."
|
||||
Reference in New Issue
Block a user