- Add SOUL.md.template with {{PLACEHOLDERS}} for interactive generation
- Add mosaic-init (bash + PS1) for interactive SOUL.md creation with flag overrides
- Add mosaic launcher (bash + PS1) — unified CLI for claude/opencode/codex with SOUL.md injection
- Add codex runtime adapter (runtime/codex/instructions.md)
- Update runtime adapters (claude, opencode) with SOUL.md read directive
- Update mosaic-link-runtime-assets to push codex adapter to ~/.codex/
- Update installers to prompt for mosaic init when SOUL.md is missing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
211 lines
6.4 KiB
Bash
Executable File
211 lines
6.4 KiB
Bash
Executable File
#!/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 <<USAGE
|
|
Usage: $(basename "$0") [options]
|
|
|
|
Generate ~/.config/mosaic/SOUL.md — the universal agent identity contract.
|
|
|
|
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)
|
|
--non-interactive Fail if any required value is missing (no prompts)
|
|
-h, --help Show help
|
|
USAGE
|
|
}
|
|
|
|
NON_INTERACTIVE=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 ;;
|
|
--non-interactive) NON_INTERACTIVE=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\""
|
|
}
|
|
|
|
echo "[mosaic-init] Generating SOUL.md — your universal 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."
|
|
;;
|
|
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."
|
|
;;
|
|
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."
|
|
;;
|
|
esac
|
|
|
|
if [[ "$ACCESSIBILITY" != "none" && -n "$ACCESSIBILITY" ]]; then
|
|
BEHAVIORAL_PRINCIPLES="$BEHAVIORAL_PRINCIPLES
|
|
5. $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 "$TEMPLATE" ]]; then
|
|
echo "[mosaic-init] ERROR: Template not found: $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
|
|
}' "$TEMPLATE" > "$OUTPUT"
|
|
|
|
echo ""
|
|
echo "[mosaic-init] Generated: $OUTPUT"
|
|
echo "[mosaic-init] Agent name: $AGENT_NAME"
|
|
echo "[mosaic-init] Style: $STYLE"
|
|
|
|
# Push to runtime adapters
|
|
if [[ -x "$MOSAIC_HOME/bin/mosaic-link-runtime-assets" ]]; then
|
|
echo "[mosaic-init] Updating runtime adapters..."
|
|
"$MOSAIC_HOME/bin/mosaic-link-runtime-assets"
|
|
fi
|
|
|
|
echo "[mosaic-init] Done. Launch with: mosaic claude"
|