chore: sync local Mosaic changes

This commit is contained in:
Jason Woltje
2026-02-21 09:55:34 -06:00
parent 1e4eefeca3
commit e3ec3e32e5
82 changed files with 5398 additions and 1969 deletions

View File

@@ -3,16 +3,19 @@ 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.
# AGENTS.md is the global policy source for all agent sessions.
# The launcher injects a composed runtime contract (AGENTS + runtime reference).
#
# 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 claude [args...] Launch Claude Code with runtime contract injected
# mosaic opencode [args...] Launch OpenCode with runtime contract injected
# mosaic codex [args...] Launch Codex with runtime contract injected
# mosaic yolo <runtime> [args...] Launch runtime in dangerous-permissions mode
# mosaic --yolo <runtime> [args...] Alias for yolo
# mosaic init [args...] Generate SOUL.md interactively
# mosaic doctor [args...] Health audit
# mosaic sync [args...] Sync skills
# mosaic seq [subcommand] sequential-thinking MCP management (check/fix/start)
# mosaic bootstrap <path> Bootstrap a repo
# mosaic upgrade release Upgrade installed Mosaic release
# mosaic upgrade check Check release upgrade status (no changes)
@@ -28,14 +31,20 @@ mosaic $VERSION — Unified agent launcher
Usage: mosaic <command> [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
claude [args...] Launch Claude Code with runtime contract injected
opencode [args...] Launch OpenCode with runtime contract injected
codex [args...] Launch Codex with runtime contract injected
yolo <runtime> [args...] Dangerous mode for claude|codex|opencode
--yolo <runtime> [args...] Alias for yolo
Management:
init [args...] Generate SOUL.md (agent identity contract)
doctor [args...] Audit runtime state and detect drift
sync [args...] Sync skills from canonical source
seq [subcommand] sequential-thinking MCP management:
check [--runtime <r>] [--strict]
fix [--runtime <r>]
start
bootstrap <path> Bootstrap a repo with Mosaic standards
upgrade [mode] [args] Upgrade release (default) or project files
upgrade check Check release upgrade status (no changes)
@@ -83,14 +92,79 @@ check_runtime() {
fi
}
# Ensure AGENTS.md is present at the runtime's native config path.
# Used for runtimes that don't support CLI prompt injection.
check_sequential_thinking() {
local runtime="${1:-all}"
local checker="$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking"
if [[ ! -x "$checker" ]]; then
echo "[mosaic] ERROR: sequential-thinking checker missing: $checker" >&2
exit 1
fi
if ! "$checker" --check --runtime "$runtime" >/dev/null 2>&1; then
echo "[mosaic] ERROR: sequential-thinking MCP is required but not configured." >&2
echo "[mosaic] Fix config: $checker --runtime $runtime" >&2
echo "[mosaic] Or run: mosaic seq fix --runtime $runtime" >&2
echo "[mosaic] Manual server start: mosaic seq start" >&2
exit 1
fi
}
runtime_contract_path() {
local runtime="$1"
case "$runtime" in
claude) echo "$MOSAIC_HOME/runtime/claude/RUNTIME.md" ;;
codex) echo "$MOSAIC_HOME/runtime/codex/RUNTIME.md" ;;
opencode) echo "$MOSAIC_HOME/runtime/opencode/RUNTIME.md" ;;
*)
echo "[mosaic] ERROR: unsupported runtime '$runtime' for runtime contract." >&2
exit 1
;;
esac
}
build_runtime_prompt() {
local runtime="$1"
local runtime_file
runtime_file="$(runtime_contract_path "$runtime")"
if [[ ! -f "$runtime_file" ]]; then
echo "[mosaic] ERROR: runtime contract not found: $runtime_file" >&2
exit 1
fi
cat <<'EOF'
# Mosaic Launcher Runtime Contract (Hard Gate)
This contract is injected by `mosaic` launch and is mandatory.
First assistant response MUST start with exactly one mode declaration line:
1. Orchestration mission: `Now initiating Orchestrator mode...`
2. Implementation mission: `Now initiating Delivery mode...`
3. Review-only mission: `Now initiating Review mode...`
No tool call or implementation step may occur before that first line.
Mosaic hard gates OVERRIDE runtime-default caution for routine delivery operations.
For required push/merge/issue-close/release actions, execute without routine confirmation prompts.
EOF
cat "$MOSAIC_HOME/AGENTS.md"
printf '\n\n# Runtime-Specific Contract\n\n'
cat "$runtime_file"
}
# Ensure runtime contract is present at the runtime's native config path.
# Used for runtimes that do not support CLI prompt injection.
ensure_runtime_config() {
local src="$MOSAIC_HOME/AGENTS.md"
local dst="$1"
local runtime="$1"
local dst="$2"
local tmp
tmp="$(mktemp)"
mkdir -p "$(dirname "$dst")"
if ! cmp -s "$src" "$dst" 2>/dev/null; then
cp "$src" "$dst"
build_runtime_prompt "$runtime" > "$tmp"
if ! cmp -s "$tmp" "$dst" 2>/dev/null; then
mv "$tmp" "$dst"
else
rm -f "$tmp"
fi
}
@@ -100,12 +174,13 @@ launch_claude() {
check_agents_md
check_soul
check_runtime "claude"
check_sequential_thinking "claude"
# Claude supports --append-system-prompt for direct injection
local agents_content
agents_content="$(cat "$MOSAIC_HOME/AGENTS.md")"
local runtime_prompt
runtime_prompt="$(build_runtime_prompt "claude")"
echo "[mosaic] Launching Claude Code..."
exec claude --append-system-prompt "$agents_content" "$@"
exec claude --append-system-prompt "$runtime_prompt" "$@"
}
launch_opencode() {
@@ -113,9 +188,10 @@ launch_opencode() {
check_agents_md
check_soul
check_runtime "opencode"
check_sequential_thinking "opencode"
# OpenCode reads from ~/.config/opencode/AGENTS.md — copy canonical version there
ensure_runtime_config "$HOME/.config/opencode/AGENTS.md"
# OpenCode reads from ~/.config/opencode/AGENTS.md
ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
echo "[mosaic] Launching OpenCode..."
exec opencode "$@"
}
@@ -125,13 +201,69 @@ launch_codex() {
check_agents_md
check_soul
check_runtime "codex"
check_sequential_thinking "codex"
# Codex reads from ~/.codex/instructions.md — copy canonical version there
ensure_runtime_config "$HOME/.codex/instructions.md"
# Codex reads from ~/.codex/instructions.md
ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
echo "[mosaic] Launching Codex..."
exec codex "$@"
}
launch_yolo() {
if [[ $# -eq 0 ]]; then
echo "[mosaic] ERROR: yolo requires a runtime (claude|codex|opencode)." >&2
echo "[mosaic] Example: mosaic yolo claude" >&2
exit 1
fi
local runtime="$1"
shift
case "$runtime" in
claude)
check_mosaic_home
check_agents_md
check_soul
check_runtime "claude"
check_sequential_thinking "claude"
# Claude uses an explicit dangerous permissions flag.
local runtime_prompt
runtime_prompt="$(build_runtime_prompt "claude")"
echo "[mosaic] Launching Claude Code in YOLO mode (dangerous permissions enabled)..."
exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$@"
;;
codex)
check_mosaic_home
check_agents_md
check_soul
check_runtime "codex"
check_sequential_thinking "codex"
# Codex reads instructions.md from ~/.codex and supports a direct dangerous flag.
ensure_runtime_config "codex" "$HOME/.codex/instructions.md"
echo "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..."
exec codex --dangerously-bypass-approvals-and-sandbox "$@"
;;
opencode)
check_mosaic_home
check_agents_md
check_soul
check_runtime "opencode"
check_sequential_thinking "opencode"
# OpenCode defaults to allow-all permissions unless user config restricts them.
ensure_runtime_config "opencode" "$HOME/.config/opencode/AGENTS.md"
echo "[mosaic] Launching OpenCode in YOLO mode..."
exec opencode "$@"
;;
*)
echo "[mosaic] ERROR: Unsupported yolo runtime '$runtime'. Use claude|codex|opencode." >&2
exit 1
;;
esac
}
# Delegate to existing scripts
run_init() {
check_mosaic_home
@@ -148,6 +280,34 @@ run_sync() {
exec "$MOSAIC_HOME/bin/mosaic-sync-skills" "$@"
}
run_seq() {
check_mosaic_home
local checker="$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking"
local action="${1:-check}"
case "$action" in
check)
shift || true
exec "$checker" --check "$@"
;;
fix|apply)
shift || true
exec "$checker" "$@"
;;
start)
shift || true
check_runtime "npx"
echo "[mosaic] Starting sequential-thinking MCP server..."
exec npx -y @modelcontextprotocol/server-sequential-thinking "$@"
;;
*)
echo "[mosaic] ERROR: Unknown seq subcommand '$action'." >&2
echo "[mosaic] Use: mosaic seq check|fix|start" >&2
exit 1
;;
esac
}
run_bootstrap() {
check_mosaic_home
exec "$MOSAIC_HOME/bin/mosaic-bootstrap-repo" "$@"
@@ -214,9 +374,11 @@ case "$command" in
claude) launch_claude "$@" ;;
opencode) launch_opencode "$@" ;;
codex) launch_codex "$@" ;;
yolo|--yolo) launch_yolo "$@" ;;
init) run_init "$@" ;;
doctor) run_doctor "$@" ;;
sync) run_sync "$@" ;;
seq) run_seq "$@" ;;
bootstrap) run_bootstrap "$@" ;;
upgrade) run_upgrade "$@" ;;
release-upgrade) run_release_upgrade "$@" ;;

View File

@@ -72,11 +72,15 @@ if [[ ! -f "$TARGET_DIR/AGENTS.md" ]]; then
cat > "$TARGET_DIR/AGENTS.md" <<'AGENTS_EOF'
# Agent Guidelines
## Standards Load Order
## Required Load Order
1. `~/.config/mosaic/STANDARDS.md`
2. `AGENTS.md` (this file)
3. `.mosaic/repo-hooks.sh`
1. `~/.config/mosaic/SOUL.md`
2. `~/.config/mosaic/STANDARDS.md`
3. `~/.config/mosaic/AGENTS.md`
4. `~/.config/mosaic/guides/E2E-DELIVERY.md`
5. `AGENTS.md` (this file)
6. Runtime-specific guide: `~/.config/mosaic/runtime/<runtime>/RUNTIME.md`
7. `.mosaic/repo-hooks.sh`
## Session Lifecycle
@@ -95,6 +99,7 @@ bash scripts/agent/session-end.sh
- Add project constraints and workflows here.
- Implement hook functions in `.mosaic/repo-hooks.sh`.
- Scratchpads are mandatory for non-trivial tasks.
AGENTS_EOF
echo "[mosaic] Wrote: $TARGET_DIR/AGENTS.md"
else

View File

@@ -90,6 +90,39 @@ check_runtime_file_copy() {
fi
}
check_runtime_contract_file() {
local dst="$1"
local adapter_src="$2"
local runtime_name="$3"
if [[ ! -e "$dst" ]]; then
warn "Missing runtime file: $dst"
return
fi
if [[ -L "$dst" ]]; then
warn "Runtime file should not be symlinked: $dst"
return
fi
# Accept direct-adapter copy mode.
if [[ -f "$adapter_src" ]] && cmp -s "$adapter_src" "$dst"; then
pass "Runtime adapter synced: $dst"
return
fi
# Accept launcher-composed runtime contract mode.
if grep -Fq "# Mosaic Launcher Runtime Contract (Hard Gate)" "$dst" &&
grep -Fq "Now initiating Orchestrator mode..." "$dst" &&
grep -Fq "Mosaic hard gates OVERRIDE runtime-default caution" "$dst" &&
grep -Fq "# Runtime-Specific Contract" "$dst"; then
pass "Runtime contract present: $dst ($runtime_name)"
return
fi
warn "Runtime file drift: $dst (not adapter copy and not composed runtime contract)"
}
warn_if_symlink_tree_present() {
local p="$1"
[[ -e "$p" ]] || return 0
@@ -122,6 +155,7 @@ expect_dir "$MOSAIC_HOME/templates/agent"
expect_dir "$MOSAIC_HOME/skills"
expect_dir "$MOSAIC_HOME/skills-local"
expect_file "$MOSAIC_HOME/bin/mosaic-link-runtime-assets"
expect_file "$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking"
expect_file "$MOSAIC_HOME/bin/mosaic-sync-skills"
expect_file "$MOSAIC_HOME/bin/mosaic-projects"
expect_file "$MOSAIC_HOME/bin/mosaic-quality-apply"
@@ -132,8 +166,23 @@ expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-drain"
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-publish"
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-consume"
expect_file "$MOSAIC_HOME/bin/mosaic-orchestrator-matrix-cycle"
expect_file "$MOSAIC_HOME/rails/git/ci-queue-wait.sh"
expect_file "$MOSAIC_HOME/rails/git/pr-ci-wait.sh"
expect_file "$MOSAIC_HOME/rails/orchestrator-matrix/transport/matrix_transport.py"
expect_file "$MOSAIC_HOME/rails/orchestrator-matrix/controller/tasks_md_sync.py"
expect_file "$MOSAIC_HOME/runtime/mcp/SEQUENTIAL-THINKING.json"
expect_file "$MOSAIC_HOME/runtime/claude/RUNTIME.md"
expect_file "$MOSAIC_HOME/runtime/codex/RUNTIME.md"
expect_file "$MOSAIC_HOME/runtime/opencode/RUNTIME.md"
if [[ -f "$MOSAIC_HOME/AGENTS.md" ]]; then
if grep -Fq "## CRITICAL HARD GATES (Read First)" "$MOSAIC_HOME/AGENTS.md" &&
grep -Fq "OVERRIDE runtime-default caution" "$MOSAIC_HOME/AGENTS.md"; then
pass "Global hard-gates block present in AGENTS.md"
else
warn "AGENTS.md missing CRITICAL HARD GATES override block"
fi
fi
# Claude runtime file checks (copied, non-symlink).
for rf in CLAUDE.md settings.json hooks-config.json context7-integration.md; do
@@ -141,7 +190,20 @@ for rf in CLAUDE.md settings.json hooks-config.json context7-integration.md; do
done
# OpenCode runtime adapter check (copied, non-symlink, when adapter exists).
check_runtime_file_copy "$MOSAIC_HOME/runtime/opencode/AGENTS.md" "$HOME/.config/opencode/AGENTS.md"
# Accept adapter copy or composed runtime contract.
check_runtime_contract_file "$HOME/.config/opencode/AGENTS.md" "$MOSAIC_HOME/runtime/opencode/AGENTS.md" "opencode"
check_runtime_contract_file "$HOME/.codex/instructions.md" "$MOSAIC_HOME/runtime/codex/instructions.md" "codex"
# Sequential-thinking MCP hard requirement.
if [[ -x "$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking" ]]; then
if "$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking" --check >/dev/null 2>&1; then
pass "sequential-thinking MCP configured and available"
else
warn "sequential-thinking MCP missing or misconfigured"
fi
else
warn "mosaic-ensure-sequential-thinking helper missing"
fi
# Legacy migration surfaces should no longer contain symlink trees.
legacy_paths=(

View File

@@ -71,6 +71,45 @@ function Check-RuntimeFileCopy {
}
}
function Check-RuntimeContractFile {
param([string]$Dst, [string]$AdapterSrc, [string]$RuntimeName)
if (-not (Test-Path $Dst)) {
Warn "Missing runtime file: $Dst"
return
}
$item = Get-Item $Dst -Force -ErrorAction SilentlyContinue
if ($item -and ($item.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
Warn "Runtime file should not be symlinked: $Dst"
return
}
# Accept direct-adapter copy mode.
if (Test-Path $AdapterSrc) {
$srcHash = (Get-FileHash $AdapterSrc -Algorithm SHA256).Hash
$dstHash = (Get-FileHash $Dst -Algorithm SHA256).Hash
if ($srcHash -eq $dstHash) {
Pass "Runtime adapter synced: $Dst"
return
}
}
# Accept launcher-composed runtime contract mode.
$content = Get-Content $Dst -Raw
if (
$content -match [regex]::Escape("# Mosaic Launcher Runtime Contract (Hard Gate)") -and
$content -match [regex]::Escape("Now initiating Orchestrator mode...") -and
$content -match [regex]::Escape("Mosaic hard gates OVERRIDE runtime-default caution") -and
$content -match [regex]::Escape("# Runtime-Specific Contract")
) {
Pass "Runtime contract present: $Dst ($RuntimeName)"
return
}
Warn "Runtime file drift: $Dst (not adapter copy and not composed runtime contract)"
}
function Warn-IfReparsePresent {
param([string]$Path)
if (-not (Test-Path $Path)) { return }
@@ -107,6 +146,7 @@ Expect-Dir (Join-Path $MosaicHome "templates\agent")
Expect-Dir (Join-Path $MosaicHome "skills")
Expect-Dir (Join-Path $MosaicHome "skills-local")
Expect-File (Join-Path $MosaicHome "bin\mosaic-link-runtime-assets")
Expect-File (Join-Path $MosaicHome "bin\mosaic-ensure-sequential-thinking.ps1")
Expect-File (Join-Path $MosaicHome "bin\mosaic-sync-skills")
Expect-File (Join-Path $MosaicHome "bin\mosaic-projects")
Expect-File (Join-Path $MosaicHome "bin\mosaic-quality-apply")
@@ -117,8 +157,29 @@ Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-drain")
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-publish")
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-consume")
Expect-File (Join-Path $MosaicHome "bin\mosaic-orchestrator-matrix-cycle")
Expect-File (Join-Path $MosaicHome "rails\git\ci-queue-wait.ps1")
Expect-File (Join-Path $MosaicHome "rails\git\ci-queue-wait.sh")
Expect-File (Join-Path $MosaicHome "rails\git\pr-ci-wait.sh")
Expect-File (Join-Path $MosaicHome "rails\orchestrator-matrix\transport\matrix_transport.py")
Expect-File (Join-Path $MosaicHome "rails\orchestrator-matrix\controller\tasks_md_sync.py")
Expect-File (Join-Path $MosaicHome "runtime\mcp\SEQUENTIAL-THINKING.json")
Expect-File (Join-Path $MosaicHome "runtime\claude\RUNTIME.md")
Expect-File (Join-Path $MosaicHome "runtime\codex\RUNTIME.md")
Expect-File (Join-Path $MosaicHome "runtime\opencode\RUNTIME.md")
$agentsMd = Join-Path $MosaicHome "AGENTS.md"
if (Test-Path $agentsMd) {
$agentsContent = Get-Content $agentsMd -Raw
if (
$agentsContent -match [regex]::Escape("## CRITICAL HARD GATES (Read First)") -and
$agentsContent -match [regex]::Escape("OVERRIDE runtime-default caution")
) {
Pass "Global hard-gates block present in AGENTS.md"
}
else {
Warn "AGENTS.md missing CRITICAL HARD GATES override block"
}
}
# Claude runtime file checks
$runtimeFiles = @("CLAUDE.md", "settings.json", "hooks-config.json", "context7-integration.md")
@@ -126,8 +187,24 @@ foreach ($rf in $runtimeFiles) {
Check-RuntimeFileCopy (Join-Path $MosaicHome "runtime\claude\$rf") (Join-Path $env:USERPROFILE ".claude\$rf")
}
# OpenCode runtime adapter
Check-RuntimeFileCopy (Join-Path $MosaicHome "runtime\opencode\AGENTS.md") (Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md")
# OpenCode/Codex runtime contract checks
Check-RuntimeContractFile (Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md") (Join-Path $MosaicHome "runtime\opencode\AGENTS.md") "opencode"
Check-RuntimeContractFile (Join-Path $env:USERPROFILE ".codex\instructions.md") (Join-Path $MosaicHome "runtime\codex\instructions.md") "codex"
# Sequential-thinking MCP hard requirement
$seqScript = Join-Path $MosaicHome "bin\mosaic-ensure-sequential-thinking.ps1"
if (Test-Path $seqScript) {
try {
& $seqScript -Check *>$null
Pass "sequential-thinking MCP configured and available"
}
catch {
Warn "sequential-thinking MCP missing or misconfigured"
}
}
else {
Warn "mosaic-ensure-sequential-thinking helper missing"
}
# Legacy migration surfaces
$legacyPaths = @(

View File

@@ -0,0 +1,262 @@
#!/usr/bin/env bash
set -euo pipefail
MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}"
MODE="apply"
RUNTIME="all"
STRICT_CHECK=0
PKG="@modelcontextprotocol/server-sequential-thinking"
err() { echo "[mosaic-seq] ERROR: $*" >&2; }
log() { echo "[mosaic-seq] $*"; }
while [[ $# -gt 0 ]]; do
case "$1" in
--check)
MODE="check"
shift
;;
--runtime)
if [[ $# -lt 2 ]]; then
err "--runtime requires a value: claude|codex|opencode|all"
exit 2
fi
RUNTIME="$2"
shift 2
;;
--strict)
STRICT_CHECK=1
shift
;;
*)
err "Unknown argument: $1"
exit 2
;;
esac
done
case "$RUNTIME" in
all|claude|codex|opencode) ;;
*)
err "Invalid runtime: $RUNTIME (expected claude|codex|opencode|all)"
exit 2
;;
esac
require_binary() {
local name="$1"
if ! command -v "$name" >/dev/null 2>&1; then
err "Required binary missing: $name"
return 1
fi
}
check_software() {
require_binary node
require_binary npx
}
warm_package() {
local timeout_sec="${MOSAIC_SEQ_WARM_TIMEOUT_SEC:-15}"
if command -v timeout >/dev/null 2>&1; then
timeout "$timeout_sec" npx -y "$PKG" --help >/dev/null 2>&1
else
npx -y "$PKG" --help >/dev/null 2>&1
fi
}
check_claude_config() {
python3 - <<'PY'
import json
from pathlib import Path
p = Path.home() / ".claude" / "settings.json"
if not p.exists():
raise SystemExit(1)
try:
data = json.loads(p.read_text(encoding="utf-8"))
except Exception:
raise SystemExit(1)
mcp = data.get("mcpServers")
if not isinstance(mcp, dict):
raise SystemExit(1)
entry = mcp.get("sequential-thinking")
if not isinstance(entry, dict):
raise SystemExit(1)
if entry.get("command") != "npx":
raise SystemExit(1)
args = entry.get("args")
if args != ["-y", "@modelcontextprotocol/server-sequential-thinking"]:
raise SystemExit(1)
PY
}
apply_claude_config() {
python3 - <<'PY'
import json
from pathlib import Path
p = Path.home() / ".claude" / "settings.json"
p.parent.mkdir(parents=True, exist_ok=True)
if p.exists():
try:
data = json.loads(p.read_text(encoding="utf-8"))
except Exception:
data = {}
else:
data = {}
mcp = data.get("mcpServers")
if not isinstance(mcp, dict):
mcp = {}
mcp["sequential-thinking"] = {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sequential-thinking"]
}
data["mcpServers"] = mcp
p.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
PY
}
check_codex_config() {
local cfg="$HOME/.codex/config.toml"
[[ -f "$cfg" ]] || return 1
grep -Eq '^\[mcp_servers\.(sequential-thinking|sequential_thinking)\]' "$cfg" && \
grep -q '^command = "npx"' "$cfg" && \
grep -q '@modelcontextprotocol/server-sequential-thinking' "$cfg"
}
apply_codex_config() {
local cfg="$HOME/.codex/config.toml"
mkdir -p "$(dirname "$cfg")"
[[ -f "$cfg" ]] || touch "$cfg"
local tmp
tmp="$(mktemp)"
awk '
BEGIN { skip = 0 }
/^\[mcp_servers\.(sequential-thinking|sequential_thinking)\]/ { skip = 1; next }
skip && /^\[/ { skip = 0 }
!skip { print }
' "$cfg" > "$tmp"
mv "$tmp" "$cfg"
{
echo ""
echo "[mcp_servers.sequential-thinking]"
echo "command = \"npx\""
echo "args = [\"-y\", \"@modelcontextprotocol/server-sequential-thinking\"]"
} >> "$cfg"
}
check_opencode_config() {
python3 - <<'PY'
import json
from pathlib import Path
p = Path.home() / ".config" / "opencode" / "config.json"
if not p.exists():
raise SystemExit(1)
try:
data = json.loads(p.read_text(encoding="utf-8"))
except Exception:
raise SystemExit(1)
mcp = data.get("mcp")
if not isinstance(mcp, dict):
raise SystemExit(1)
entry = mcp.get("sequential-thinking")
if not isinstance(entry, dict):
raise SystemExit(1)
if entry.get("type") != "local":
raise SystemExit(1)
if entry.get("command") != ["npx", "-y", "@modelcontextprotocol/server-sequential-thinking"]:
raise SystemExit(1)
if entry.get("enabled") is not True:
raise SystemExit(1)
PY
}
apply_opencode_config() {
python3 - <<'PY'
import json
from pathlib import Path
p = Path.home() / ".config" / "opencode" / "config.json"
p.parent.mkdir(parents=True, exist_ok=True)
if p.exists():
try:
data = json.loads(p.read_text(encoding="utf-8"))
except Exception:
data = {}
else:
data = {}
mcp = data.get("mcp")
if not isinstance(mcp, dict):
mcp = {}
mcp["sequential-thinking"] = {
"type": "local",
"command": ["npx", "-y", "@modelcontextprotocol/server-sequential-thinking"],
"enabled": True
}
data["mcp"] = mcp
p.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")
PY
}
check_runtime_config() {
case "$RUNTIME" in
all)
check_claude_config
check_codex_config
check_opencode_config
;;
claude)
check_claude_config
;;
codex)
check_codex_config
;;
opencode)
check_opencode_config
;;
esac
}
apply_runtime_config() {
case "$RUNTIME" in
all)
apply_claude_config
apply_codex_config
apply_opencode_config
;;
claude)
apply_claude_config
;;
codex)
apply_codex_config
;;
opencode)
apply_opencode_config
;;
esac
}
if [[ "$MODE" == "check" ]]; then
check_software
check_runtime_config
# Runtime launch checks should be local/fast by default.
if [[ "$STRICT_CHECK" -eq 1 || "${MOSAIC_SEQ_CHECK_WARM:-0}" == "1" ]]; then
if ! warm_package; then
err "sequential-thinking package warm-up failed in strict mode"
exit 1
fi
fi
log "sequential-thinking MCP is configured and available (${RUNTIME})"
exit 0
fi
check_software
if ! warm_package; then
err "Unable to warm sequential-thinking package (npx timeout/failure)"
exit 1
fi
apply_runtime_config
log "sequential-thinking MCP configured (${RUNTIME})"

View File

@@ -0,0 +1,114 @@
# mosaic-ensure-sequential-thinking.ps1
$ErrorActionPreference = "Stop"
param(
[switch]$Check
)
$Pkg = "@modelcontextprotocol/server-sequential-thinking"
function Require-Binary {
param([string]$Name)
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
throw "Required binary missing: $Name"
}
}
function Warm-Package {
$null = & npx -y $Pkg --help 2>$null
}
function Set-ClaudeConfig {
$path = Join-Path $env:USERPROFILE ".claude\settings.json"
New-Item -ItemType Directory -Path (Split-Path $path -Parent) -Force | Out-Null
$data = @{}
if (Test-Path $path) {
try { $data = Get-Content $path -Raw | ConvertFrom-Json -AsHashtable } catch { $data = @{} }
}
if (-not $data.ContainsKey("mcpServers") -or -not ($data["mcpServers"] -is [hashtable])) {
$data["mcpServers"] = @{}
}
$data["mcpServers"]["sequential-thinking"] = @{
command = "npx"
args = @("-y", "@modelcontextprotocol/server-sequential-thinking")
}
$data | ConvertTo-Json -Depth 20 | Set-Content -Path $path -Encoding UTF8
}
function Set-CodexConfig {
$path = Join-Path $env:USERPROFILE ".codex\config.toml"
New-Item -ItemType Directory -Path (Split-Path $path -Parent) -Force | Out-Null
if (-not (Test-Path $path)) { New-Item -ItemType File -Path $path -Force | Out-Null }
$content = Get-Content $path -Raw
$content = [regex]::Replace($content, "(?ms)^\[mcp_servers\.(sequential-thinking|sequential_thinking)\].*?(?=^\[|\z)", "")
$content = $content.TrimEnd() + "`n`n[mcp_servers.sequential-thinking]`ncommand = \"npx\"`nargs = [\"-y\", \"@modelcontextprotocol/server-sequential-thinking\"]`n"
Set-Content -Path $path -Value $content -Encoding UTF8
}
function Set-OpenCodeConfig {
$path = Join-Path $env:USERPROFILE ".config\opencode\config.json"
New-Item -ItemType Directory -Path (Split-Path $path -Parent) -Force | Out-Null
$data = @{}
if (Test-Path $path) {
try { $data = Get-Content $path -Raw | ConvertFrom-Json -AsHashtable } catch { $data = @{} }
}
if (-not $data.ContainsKey("mcp") -or -not ($data["mcp"] -is [hashtable])) {
$data["mcp"] = @{}
}
$data["mcp"]["sequential-thinking"] = @{
type = "local"
command = @("npx", "-y", "@modelcontextprotocol/server-sequential-thinking")
enabled = $true
}
$data | ConvertTo-Json -Depth 20 | Set-Content -Path $path -Encoding UTF8
}
function Test-Configs {
$claudeOk = $false
$codexOk = $false
$opencodeOk = $false
$claudePath = Join-Path $env:USERPROFILE ".claude\settings.json"
if (Test-Path $claudePath) {
try {
$c = Get-Content $claudePath -Raw | ConvertFrom-Json -AsHashtable
$claudeOk = $c.ContainsKey("mcpServers") -and $c["mcpServers"].ContainsKey("sequential-thinking")
} catch {}
}
$codexPath = Join-Path $env:USERPROFILE ".codex\config.toml"
if (Test-Path $codexPath) {
$raw = Get-Content $codexPath -Raw
$codexOk = $raw -match "\[mcp_servers\.(sequential-thinking|sequential_thinking)\]" -and $raw -match "@modelcontextprotocol/server-sequential-thinking"
}
$opencodePath = Join-Path $env:USERPROFILE ".config\opencode\config.json"
if (Test-Path $opencodePath) {
try {
$o = Get-Content $opencodePath -Raw | ConvertFrom-Json -AsHashtable
$opencodeOk = $o.ContainsKey("mcp") -and $o["mcp"].ContainsKey("sequential-thinking")
} catch {}
}
if (-not ($claudeOk -and $codexOk -and $opencodeOk)) {
throw "Sequential-thinking MCP runtime config is incomplete"
}
}
Require-Binary node
Require-Binary npx
Warm-Package
if ($Check) {
Test-Configs
Write-Host "[mosaic-seq] sequential-thinking MCP is configured and available"
exit 0
}
Set-ClaudeConfig
Set-CodexConfig
Set-OpenCodeConfig
Write-Host "[mosaic-seq] sequential-thinking MCP configured for Claude, Codex, and OpenCode"

View File

@@ -62,7 +62,7 @@ legacy_paths=(
"$HOME/.claude/presets/domains"
"$HOME/.claude/presets/tech-stacks"
"$HOME/.claude/presets/workflows"
"$HOME/.claude/presets/jarvis-ralph.json"
"$HOME/.claude/presets/jarvis-loop.json"
)
for p in "${legacy_paths[@]}"; do
@@ -93,5 +93,9 @@ if [[ -f "$codex_adapter" ]]; then
copy_file_managed "$codex_adapter" "$HOME/.codex/instructions.md"
fi
if [[ -x "$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking" ]]; then
"$MOSAIC_HOME/bin/mosaic-ensure-sequential-thinking"
fi
echo "[mosaic-link] Runtime assets synced (non-symlink mode)"
echo "[mosaic-link] Canonical source: $MOSAIC_HOME"

View File

@@ -70,7 +70,7 @@ $legacyPaths = @(
(Join-Path $env:USERPROFILE ".claude\presets\domains"),
(Join-Path $env:USERPROFILE ".claude\presets\tech-stacks"),
(Join-Path $env:USERPROFILE ".claude\presets\workflows"),
(Join-Path $env:USERPROFILE ".claude\presets\jarvis-ralph.json")
(Join-Path $env:USERPROFILE ".claude\presets\jarvis-loop.json")
)
foreach ($p in $legacyPaths) {
@@ -102,5 +102,10 @@ if (Test-Path $codexSrc) {
Copy-FileManaged $codexSrc $codexDst
}
$seqScript = Join-Path $MosaicHome "bin\mosaic-ensure-sequential-thinking.ps1"
if (Test-Path $seqScript) {
& $seqScript
}
Write-Host "[mosaic-link] Runtime assets synced (non-symlink mode)"
Write-Host "[mosaic-link] Canonical source: $MosaicHome"

View File

@@ -121,6 +121,38 @@ link_skill_into_target() {
ln -s "$skill_path" "$link_path"
}
is_mosaic_skill_name() {
local name="$1"
[[ -d "$MOSAIC_SKILLS_DIR/$name" ]] && return 0
[[ -d "$MOSAIC_LOCAL_SKILLS_DIR/$name" ]] && return 0
return 1
}
prune_stale_links_in_target() {
local target_dir="$1"
while IFS= read -r -d '' link_path; do
local name resolved
name="$(basename "$link_path")"
if is_mosaic_skill_name "$name"; then
continue
fi
resolved="$(readlink -f "$link_path" 2>/dev/null || true)"
if [[ -z "$resolved" ]]; then
rm -f "$link_path"
echo "[mosaic-skills] Removed stale broken skill link: $link_path"
continue
fi
if [[ "$resolved" == "$MOSAIC_HOME/"* ]]; then
rm -f "$link_path"
echo "[mosaic-skills] Removed stale retired skill link: $link_path"
fi
done < <(find "$target_dir" -mindepth 1 -maxdepth 1 -type l -print0)
}
for target in "${link_targets[@]}"; do
mkdir -p "$target"
@@ -131,6 +163,8 @@ for target in "${link_targets[@]}"; do
continue
fi
prune_stale_links_in_target "$target"
while IFS= read -r -d '' skill; do
link_skill_into_target "$skill" "$target"
done < <(find "$MOSAIC_SKILLS_DIR" -mindepth 1 -maxdepth 1 -type d -print0)

View File

@@ -1,12 +1,14 @@
# 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.
# AGENTS.md is the global policy source for all agent sessions.
# The launcher injects a composed runtime contract (AGENTS + runtime reference).
#
# 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 claude [args...] Launch Claude Code with runtime contract injected
# mosaic opencode [args...] Launch OpenCode with runtime contract injected
# mosaic codex [args...] Launch Codex with runtime contract injected
# mosaic yolo <runtime> [args...] Launch runtime in dangerous-permissions mode
# mosaic --yolo <runtime> [args...] Alias for yolo
# mosaic init [args...] Generate SOUL.md interactively
# mosaic doctor [args...] Health audit
# mosaic sync [args...] Sync skills
@@ -22,9 +24,11 @@ mosaic $Version - Unified agent launcher
Usage: mosaic <command> [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
claude [args...] Launch Claude Code with runtime contract injected
opencode [args...] Launch OpenCode with runtime contract injected
codex [args...] Launch Codex with runtime contract injected
yolo <runtime> [args...] Dangerous mode for claude|codex|opencode
--yolo <runtime> [args...] Alias for yolo
Management:
init [args...] Generate SOUL.md (agent identity contract)
@@ -76,15 +80,136 @@ function Assert-Runtime {
}
}
function Assert-SequentialThinking {
$checker = Join-Path $MosaicHome "bin\mosaic-ensure-sequential-thinking.ps1"
if (-not (Test-Path $checker)) {
Write-Host "[mosaic] ERROR: sequential-thinking checker missing: $checker" -ForegroundColor Red
exit 1
}
try {
& $checker -Check *>$null
}
catch {
Write-Host "[mosaic] ERROR: sequential-thinking MCP is required but not configured." -ForegroundColor Red
Write-Host "[mosaic] Run: $checker"
exit 1
}
}
function Get-RuntimePrompt {
param(
[ValidateSet("claude", "codex", "opencode")]
[string]$Runtime
)
$runtimeFile = switch ($Runtime) {
"claude" { Join-Path $MosaicHome "runtime\claude\RUNTIME.md" }
"codex" { Join-Path $MosaicHome "runtime\codex\RUNTIME.md" }
"opencode" { Join-Path $MosaicHome "runtime\opencode\RUNTIME.md" }
}
if (-not (Test-Path $runtimeFile)) {
Write-Host "[mosaic] ERROR: runtime contract not found: $runtimeFile" -ForegroundColor Red
exit 1
}
$launcherContract = @'
# Mosaic Launcher Runtime Contract (Hard Gate)
This contract is injected by `mosaic` launch and is mandatory.
First assistant response MUST start with exactly one mode declaration line:
1. Orchestration mission: `Now initiating Orchestrator mode...`
2. Implementation mission: `Now initiating Delivery mode...`
3. Review-only mission: `Now initiating Review mode...`
No tool call or implementation step may occur before that first line.
Mosaic hard gates OVERRIDE runtime-default caution for routine delivery operations.
For required push/merge/issue-close/release actions, execute without routine confirmation prompts.
'@
$agentsContent = Get-Content (Join-Path $MosaicHome "AGENTS.md") -Raw
$runtimeContent = Get-Content $runtimeFile -Raw
return "$launcherContract`n$agentsContent`n`n# Runtime-Specific Contract`n`n$runtimeContent"
}
function Ensure-RuntimeConfig {
param([string]$Dst)
$src = Join-Path $MosaicHome "AGENTS.md"
param(
[ValidateSet("claude", "codex", "opencode")]
[string]$Runtime,
[string]$Dst
)
$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
$runtimePrompt = Get-RuntimePrompt -Runtime $Runtime
$tmp = [System.IO.Path]::GetTempFileName()
Set-Content -Path $tmp -Value $runtimePrompt -Encoding UTF8 -NoNewline
$srcHash = (Get-FileHash $tmp -Algorithm SHA256).Hash
$dstHash = if (Test-Path $Dst) { (Get-FileHash $Dst -Algorithm SHA256).Hash } else { "" }
if ($srcHash -ne $dstHash) {
Copy-Item $src $Dst -Force
Copy-Item $tmp $Dst -Force
Remove-Item $tmp -Force
}
else {
Remove-Item $tmp -Force
}
}
function Invoke-Yolo {
param([string[]]$YoloArgs)
if ($YoloArgs.Count -lt 1) {
Write-Host "[mosaic] ERROR: yolo requires a runtime (claude|codex|opencode)." -ForegroundColor Red
Write-Host "[mosaic] Example: mosaic yolo claude"
exit 1
}
$runtime = $YoloArgs[0]
$tail = if ($YoloArgs.Count -gt 1) { $YoloArgs[1..($YoloArgs.Count - 1)] } else { @() }
switch ($runtime) {
"claude" {
Assert-MosaicHome
Assert-AgentsMd
Assert-Soul
Assert-Runtime "claude"
Assert-SequentialThinking
$agentsContent = Get-RuntimePrompt -Runtime "claude"
Write-Host "[mosaic] Launching Claude Code in YOLO mode (dangerous permissions enabled)..."
& claude --dangerously-skip-permissions --append-system-prompt $agentsContent @tail
return
}
"codex" {
Assert-MosaicHome
Assert-AgentsMd
Assert-Soul
Assert-Runtime "codex"
Assert-SequentialThinking
Ensure-RuntimeConfig -Runtime "codex" -Dst (Join-Path $env:USERPROFILE ".codex\instructions.md")
Write-Host "[mosaic] Launching Codex in YOLO mode (dangerous permissions enabled)..."
& codex --dangerously-bypass-approvals-and-sandbox @tail
return
}
"opencode" {
Assert-MosaicHome
Assert-AgentsMd
Assert-Soul
Assert-Runtime "opencode"
Assert-SequentialThinking
Ensure-RuntimeConfig -Runtime "opencode" -Dst (Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md")
Write-Host "[mosaic] Launching OpenCode in YOLO mode..."
& opencode @tail
return
}
default {
Write-Host "[mosaic] ERROR: Unsupported yolo runtime '$runtime'. Use claude|codex|opencode." -ForegroundColor Red
exit 1
}
}
}
@@ -102,8 +227,9 @@ switch ($command) {
Assert-AgentsMd
Assert-Soul
Assert-Runtime "claude"
Assert-SequentialThinking
# Claude supports --append-system-prompt for direct injection
$agentsContent = Get-Content (Join-Path $MosaicHome "AGENTS.md") -Raw
$agentsContent = Get-RuntimePrompt -Runtime "claude"
Write-Host "[mosaic] Launching Claude Code..."
& claude --append-system-prompt $agentsContent @remaining
}
@@ -112,8 +238,9 @@ switch ($command) {
Assert-AgentsMd
Assert-Soul
Assert-Runtime "opencode"
Assert-SequentialThinking
# OpenCode reads from ~/.config/opencode/AGENTS.md
Ensure-RuntimeConfig (Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md")
Ensure-RuntimeConfig -Runtime "opencode" -Dst (Join-Path $env:USERPROFILE ".config\opencode\AGENTS.md")
Write-Host "[mosaic] Launching OpenCode..."
& opencode @remaining
}
@@ -122,11 +249,18 @@ switch ($command) {
Assert-AgentsMd
Assert-Soul
Assert-Runtime "codex"
Assert-SequentialThinking
# Codex reads from ~/.codex/instructions.md
Ensure-RuntimeConfig (Join-Path $env:USERPROFILE ".codex\instructions.md")
Ensure-RuntimeConfig -Runtime "codex" -Dst (Join-Path $env:USERPROFILE ".codex\instructions.md")
Write-Host "[mosaic] Launching Codex..."
& codex @remaining
}
"yolo" {
Invoke-Yolo -YoloArgs $remaining
}
"--yolo" {
Invoke-Yolo -YoloArgs $remaining
}
"init" {
Assert-MosaicHome
& (Join-Path $MosaicHome "bin\mosaic-init.ps1") @remaining