From 5d666bdca9e4e22c7f863a87d441df2946aa7371 Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sun, 22 Feb 2026 17:52:44 -0600 Subject: [PATCH] fix: move mission context to top of system prompt + auto-inject initial prompt Mission context was buried at the end of a 21K char system prompt and the agent ignored it. Two fixes: 1. Mission block now emits FIRST in build_runtime_prompt() so it's the most prominent instruction the agent sees 2. When an active mission exists and no user prompt is given, auto-inject an initial user message triggering the agent to read mission state files Co-Authored-By: Claude Opus 4.6 --- bin/mosaic | 111 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 73 insertions(+), 38 deletions(-) diff --git a/bin/mosaic b/bin/mosaic index 771cc3d..f905947 100755 --- a/bin/mosaic +++ b/bin/mosaic @@ -138,6 +138,44 @@ build_runtime_prompt() { exit 1 fi + # Inject active mission context FIRST so the agent sees it immediately + local mission_file=".mosaic/orchestrator/mission.json" + if [[ -f "$mission_file" ]] && command -v jq &>/dev/null; then + local m_status + m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)" + if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then + local m_name m_id m_count m_completed + m_name="$(jq -r '.name // "unnamed"' "$mission_file")" + m_id="$(jq -r '.mission_id // ""' "$mission_file")" + m_count="$(jq '.milestones | length' "$mission_file")" + m_completed="$(jq '[.milestones[] | select(.status == "completed")] | length' "$mission_file")" + + cat </dev/null; then - local status - status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)" - if [[ "$status" == "active" || "$status" == "paused" ]]; then - local m_name m_id m_count m_completed - m_name="$(jq -r '.name // "unnamed"' "$mission_file")" - m_id="$(jq -r '.mission_id // ""' "$mission_file")" - m_count="$(jq '.milestones | length' "$mission_file")" - m_completed="$(jq '[.milestones[] | select(.status == "completed")] | length' "$mission_file")" - - printf '\n\n# Active Mission Context\n\n' - cat </dev/null; then + local m_status + m_status="$(jq -r '.status // "inactive"' "$mission_file" 2>/dev/null)" + if [[ "$m_status" == "active" || "$m_status" == "paused" ]]; then + local m_name + m_name="$(jq -r '.name // "unnamed"' "$mission_file")" + MOSAIC_MISSION_PROMPT="Active mission detected: ${m_name}. Read the mission state files and report status." + fi + fi +} + # Launcher functions launch_claude() { check_mosaic_home @@ -234,8 +254,16 @@ launch_claude() { # Claude supports --append-system-prompt for direct injection local runtime_prompt runtime_prompt="$(build_runtime_prompt "claude")" - echo "[mosaic] Launching Claude Code..." - exec claude --append-system-prompt "$runtime_prompt" "$@" + + # If active mission exists and no user prompt was given, inject initial prompt + _detect_mission_prompt + if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then + echo "[mosaic] Launching Claude Code (active mission detected)..." + exec claude --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT" + else + echo "[mosaic] Launching Claude Code..." + exec claude --append-system-prompt "$runtime_prompt" "$@" + fi } launch_opencode() { @@ -289,8 +317,15 @@ launch_yolo() { # 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" "$@" + + _detect_mission_prompt + if [[ -n "$MOSAIC_MISSION_PROMPT" && $# -eq 0 ]]; then + echo "[mosaic] Launching Claude Code in YOLO mode (active mission detected)..." + exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$MOSAIC_MISSION_PROMPT" + else + echo "[mosaic] Launching Claude Code in YOLO mode (dangerous permissions enabled)..." + exec claude --dangerously-skip-permissions --append-system-prompt "$runtime_prompt" "$@" + fi ;; codex) check_mosaic_home