#!/usr/bin/env bash
#
# Cosmos installer.
#
# Usage:
#   curl -fsSL https://cosmos.polarity-lab.com/install.sh | sh
#   curl -fsSL https://cosmos.polarity-lab.com/install.sh | sh -s -- --dry-run
#   curl -fsSL https://cosmos.polarity-lab.com/install.sh | sh -s -- --no-app
#
# Flags:
#   --dry-run   Print every step without making changes.
#   --no-app    Skip the macOS daemon app. Install MCP server only.
#   --help      Show this message.
#
# This script will:
#   1. Install @polarity-lab/cosmos-mcp from npm
#   2. Drop Cosmos.app into ~/Applications and set it to launch at login
#      (macOS only, skipped with --no-app)
#   3. Register the cosmos-mcp:// URL scheme for one-tap key handoff
#   4. Merge cosmos into every MCP client you have installed (Claude Desktop,
#      Claude Code, Cursor, Codex, Zed, Continue) without touching any other
#      server you already have set up
#   5. Install codebase-memory-mcp (local code graph) and wire it into Cursor
#      with a global always-on rule synced into every git repo
#   6. Open Thread in your browser so you can sign in and finish connect
#
# The script is idempotent. Re-running it never duplicates entries and
# never overwrites existing MCP server config.
#

set -euo pipefail

DRY_RUN=0
SKIP_APP=0
NPM_PKG="@polarity-lab/cosmos-mcp"
COSMOS_URL="https://cosmos.polarity-lab.com/?connect=1&install=1"

while [ $# -gt 0 ]; do
  case "$1" in
    --dry-run) DRY_RUN=1; shift ;;
    --no-app)  SKIP_APP=1; shift ;;
    --help|-h)
      sed -n '2,30p' "$0" 2>/dev/null || true
      exit 0
      ;;
    *) echo "unknown flag $1" >&2; exit 2 ;;
  esac
done

# ----- pretty output -----
BOLD=$(printf '\033[1m'); DIM=$(printf '\033[2m'); RESET=$(printf '\033[0m')
GREEN=$(printf '\033[32m'); YELLOW=$(printf '\033[33m'); RED=$(printf '\033[31m')

step()  { printf "${BOLD}%s${RESET} %s\n" "$1" "$2"; }
ok()    { printf "  ${GREEN}ok${RESET}    %s\n" "$1"; }
skip()  { printf "  ${DIM}skip${RESET}  %s\n" "$1"; }
warn()  { printf "  ${YELLOW}warn${RESET}  %s\n" "$1"; }
fail()  { printf "  ${RED}fail${RESET}  %s\n" "$1"; exit 1; }
note()  { printf "        ${DIM}%s${RESET}\n" "$1"; }

run() {
  if [ $DRY_RUN -eq 1 ]; then
    printf "  ${DIM}would run${RESET} %s\n" "$*"
  else
    "$@"
  fi
}

run_sh() {
  if [ $DRY_RUN -eq 1 ]; then
    printf "  ${DIM}would run${RESET} %s\n" "$*"
  else
    sh -c "$*"
  fi
}

backup_file() {
  local f="$1"
  [ -f "$f" ] || return 0
  local ts
  ts=$(date +%Y%m%d-%H%M%S)
  if [ $DRY_RUN -eq 1 ]; then
    printf "  ${DIM}would back up${RESET} %s -> %s\n" "$f" "$f.bak.$ts"
  else
    cp -p "$f" "$f.bak.$ts"
  fi
}

# ----- platform detection -----
PLATFORM="other"
case "$(uname -s)" in
  Darwin) PLATFORM="mac" ;;
  Linux)  PLATFORM="linux" ;;
esac

if [ "$PLATFORM" != "mac" ]; then
  SKIP_APP=1
fi

printf "\n${BOLD}cosmos installer${RESET}\n"
printf "${DIM}platform: %s · dry-run: %s · daemon app: %s${RESET}\n\n" \
  "$PLATFORM" \
  "$([ $DRY_RUN -eq 1 ] && echo yes || echo no)" \
  "$([ $SKIP_APP -eq 1 ] && echo no || echo yes)"

# ----- 1. node / npm -----
step "1." "checking node"
if ! command -v node >/dev/null 2>&1; then
  warn "node not found"
  if [ "$PLATFORM" = "mac" ] && command -v brew >/dev/null 2>&1; then
    note "installing node@20 via Homebrew"
    run brew install node@20
  else
    fail "install Node.js 20+ from https://nodejs.org and re-run"
  fi
fi
NODE_VER=$(node -v | sed 's/v//' | cut -d. -f1)
if [ "$NODE_VER" -lt 20 ]; then
  fail "node $NODE_VER detected. cosmos needs Node 20+. upgrade and re-run"
fi
ok "node $(node -v)"

if ! command -v npm >/dev/null 2>&1; then
  fail "npm not found alongside node. something is wrong with your install"
fi

# ----- 2. npm package -----
step "2." "installing $NPM_PKG"
NPM_GLOBAL_ROOT=$(npm root -g 2>/dev/null || echo "")
if [ -z "$NPM_GLOBAL_ROOT" ]; then
  fail "npm root -g returned empty. check npm config"
fi

# Try a userland-friendly install. If it fails on EACCES, fall back to sudo.
if [ $DRY_RUN -eq 1 ]; then
  note "would run: npm install -g $NPM_PKG"
elif npm install -g "$NPM_PKG" >/tmp/cosmos-install-npm.log 2>&1; then
  ok "installed via npm"
elif grep -q EACCES /tmp/cosmos-install-npm.log; then
  warn "npm global install needs sudo on this machine"
  sudo npm install -g "$NPM_PKG"
  ok "installed via sudo npm"
else
  cat /tmp/cosmos-install-npm.log >&2
  fail "npm install failed. see output above"
fi

PKG_DIR="$NPM_GLOBAL_ROOT/$NPM_PKG"
COSMOS_APP_SRC=""
for _app_name in Cosmos.app CosmosSync.app; do
  if [ -d "$PKG_DIR/dist/$_app_name" ]; then
    COSMOS_APP_SRC="$PKG_DIR/dist/$_app_name"
    break
  fi
done

COSMOS_APP_RELEASE_URL="${COSMOS_APP_RELEASE_URL:-https://cosmos.polarity-lab.com/releases/Cosmos-mac.zip}"
COSMOS_APP_STAGED=""
if [ -z "$COSMOS_APP_SRC" ] && [ "$PLATFORM" = "mac" ] && [ $DRY_RUN -eq 0 ]; then
  TMP_ZIP="$(mktemp /tmp/cosmos-mac.XXXXXX.zip)"
  if curl -fsSL "$COSMOS_APP_RELEASE_URL" -o "$TMP_ZIP" 2>/dev/null; then
    TMP_EXTRACT="$(mktemp -d /tmp/cosmos-mac.XXXXXX)"
    if ditto -x -k "$TMP_ZIP" "$TMP_EXTRACT" 2>/dev/null && [ -d "$TMP_EXTRACT/Cosmos.app" ]; then
      COSMOS_APP_STAGED="$TMP_EXTRACT/Cosmos.app"
      ok "fetched Cosmos.app from $COSMOS_APP_RELEASE_URL"
    fi
    rm -f "$TMP_ZIP"
  fi
fi
if [ -z "$COSMOS_APP_SRC" ] && [ -n "$COSMOS_APP_STAGED" ]; then
  COSMOS_APP_SRC="$COSMOS_APP_STAGED"
fi

# ----- 3. macOS app -----
if [ $SKIP_APP -eq 0 ]; then
  step "3." "installing Cosmos.app"
  if [ -z "$COSMOS_APP_SRC" ] && [ $DRY_RUN -eq 0 ]; then
    warn "Cosmos.app not found in npm package or releases download"
    note "skipping app install. MCP server alone will still work"
    SKIP_APP=1
  else
    mkdir -p "$HOME/Applications"
    APP_DEST="$HOME/Applications/Cosmos.app"
    LEGACY_APP="$HOME/Applications/Cosmos Sync.app"
    if [ -d "$LEGACY_APP" ]; then
      run rm -rf "$LEGACY_APP"
      ok "removed legacy Cosmos Sync.app"
    fi
    if [ -d "$APP_DEST" ]; then
      if [ $DRY_RUN -eq 1 ]; then
        note "would replace $APP_DEST"
      else
        run rm -rf "$APP_DEST"
        run cp -R "$COSMOS_APP_SRC" "$APP_DEST"
        ok "updated ~/Applications/Cosmos.app"
      fi
    elif [ $DRY_RUN -eq 1 ]; then
      note "would copy $COSMOS_APP_SRC -> $APP_DEST"
    else
      run cp -R "$COSMOS_APP_SRC" "$APP_DEST"
      ok "copied to ~/Applications/Cosmos.app"
    fi

    LOGIN_SCRIPT='tell application "System Events"
      try
        delete login item "Cosmos Sync"
      end try
      if not (exists login item "Cosmos") then
        make login item at end with properties {path:"'"$HOME"'/Applications/Cosmos.app", hidden:false}
      end if
    end tell'
    if [ $DRY_RUN -eq 1 ]; then
      note "would register login item via osascript"
    else
      if osascript -e "$LOGIN_SCRIPT" >/dev/null 2>&1; then
        ok "set to launch at login"
      else
        warn "could not add login item (System Events permission?)"
      fi
    fi
  fi
else
  step "3." "skipping daemon app"
  if [ "$PLATFORM" != "mac" ]; then
    note "the Sync.app is macOS only. MCP server still works everywhere"
  fi
fi

# ----- 4. URL scheme handler -----
if [ "$PLATFORM" = "mac" ]; then
  step "4." "registering cosmos-mcp:// handler"
  if [ $DRY_RUN -eq 1 ]; then
    note "would run: npx -y $NPM_PKG install-handler"
  else
    npx -y "$NPM_PKG" install-handler >/dev/null 2>&1 \
      && ok "handler registered" \
      || warn "handler registration returned non-zero (re-run later if deep links do not work)"
  fi
else
  step "4." "skipping URL handler (mac only)"
fi

# ----- 5. MCP config merging -----
step "5." "wiring cosmos into your MCP clients"

# Use python3 for JSON because jq is not always installed.
PY=python3
if ! command -v $PY >/dev/null 2>&1; then
  warn "python3 not found. skipping MCP merge. add cosmos manually per https://cosmos.polarity-lab.com/install"
else

  merge_json_mcp() {
    # merge_json_mcp <config_path> [<key>]
    # Default key is "mcpServers" (Claude Desktop, Cursor). Pass
    # "context_servers" for Zed. Prints "present" or "added" to stdout.
    local cfg="$1"
    local key="${2:-mcpServers}"
    if [ $DRY_RUN -eq 1 ]; then
      if [ -f "$cfg" ] && "$PY" -c "import json,sys; d=json.load(open(sys.argv[1])); sys.exit(0 if 'cosmos' in d.get(sys.argv[2],{}) else 1)" "$cfg" "$key" 2>/dev/null; then
        echo present
      else
        echo dry-add
      fi
      return
    fi
    mkdir -p "$(dirname "$cfg")"
    backup_file "$cfg"
    KEY="$key" "$PY" - "$cfg" <<'PYEOF'
import json, os, sys
path = sys.argv[1]
key = os.environ["KEY"]
try:
    with open(path) as f:
        data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
    data = {}
servers = data.setdefault(key, {})
if "cosmos" in servers:
    print("present")
    sys.exit(0)
servers["cosmos"] = {
    "command": "npx",
    "args": ["-y", "@polarity-lab/cosmos-mcp"]
}
tmp = path + ".tmp"
with open(tmp, "w") as f:
    json.dump(data, f, indent=2)
os.replace(tmp, path)
print("added")
PYEOF
  }

  report_merge() {
    # report_merge <client_label> <result>
    case "$2" in
      present) skip "$1 already has cosmos" ;;
      added)   ok   "$1 merged (other servers preserved)" ;;
      dry-add) note "would add cosmos to $1 (other servers preserved)" ;;
      *)       warn "$1 merge result unclear" ;;
    esac
  }

  # --- Claude Desktop ---
  CLAUDE_DESKTOP="$HOME/Library/Application Support/Claude/claude_desktop_config.json"
  if [ "$PLATFORM" = "mac" ]; then
    report_merge "Claude Desktop" "$(merge_json_mcp "$CLAUDE_DESKTOP")"
  fi

  # --- Cursor ---
  CURSOR_CFG="$HOME/.cursor/mcp.json"
  if [ -d "$HOME/.cursor" ] || [ -f "$CURSOR_CFG" ]; then
    report_merge "Cursor (cosmos)" "$(merge_json_mcp "$CURSOR_CFG")"
    if [ $DRY_RUN -eq 0 ]; then
      CURSOR_RULES_DIR="$HOME/.cursor/rules"
      CURSOR_RULE="$CURSOR_RULES_DIR/cosmos-mcp-always.mdc"
      CBM_RULE="$CURSOR_RULES_DIR/codebase-memory-always.mdc"
      CURSOR_PERMS="$HOME/.cursor/permissions.json"
      CBM_BIN="$HOME/.local/bin/codebase-memory-mcp"
      mkdir -p "$CURSOR_RULES_DIR" "$HOME/.local/bin"

      if [ ! -x "$CBM_BIN" ]; then
        note "installing codebase-memory-mcp (local code graph)"
        if curl -fsSL https://raw.githubusercontent.com/DeusData/codebase-memory-mcp/main/install.sh 2>/dev/null | bash -s -- -y >/tmp/cosmos-install-cbm.log 2>&1; then
          ok "codebase-memory-mcp installed"
        elif npm install -g codebase-memory-mcp >/tmp/cosmos-install-cbm.log 2>&1; then
          ok "codebase-memory-mcp installed via npm"
        else
          warn "codebase-memory-mcp install failed (optional). see /tmp/cosmos-install-cbm.log"
        fi
      else
        skip "codebase-memory-mcp already installed"
      fi

      if [ -x "$CBM_BIN" ]; then
        BOOT="${HOME}/cosmos/scripts/bootstrap-lab-stack.sh"
        if [ -x "$BOOT" ]; then
          note "bootstrapping Polarity Lab stack (manifest sync + index + CBM ADR)"
          if "$BOOT" >/tmp/cosmos-install-lab-stack.log 2>&1; then
            ok "lab stack indexed (see /tmp/cosmos-install-lab-stack.log)"
          else
            warn "lab stack bootstrap failed (optional). see /tmp/cosmos-install-lab-stack.log"
          fi
        fi
        backup_file "$CURSOR_CFG"
        CBM_MERGE=$("$PY" - "$CURSOR_CFG" <<'PYEOF'
import json, os, sys
path = sys.argv[1]
cbm = os.path.expanduser("~/.local/bin/codebase-memory-mcp")
try:
    with open(path) as f:
        data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
    data = {}
servers = data.setdefault("mcpServers", {})
if "codebase-memory" in servers:
    print("present")
    sys.exit(0)
servers["codebase-memory"] = {"command": cbm}
tmp = path + ".tmp"
with open(tmp, "w") as f:
    json.dump(data, f, indent=2)
os.replace(tmp, path)
print("added")
PYEOF
)
        case "$CBM_MERGE" in
          present) skip "Cursor already has codebase-memory" ;;
          added)   ok "Cursor merged codebase-memory MCP" ;;
          *)       warn "Cursor codebase-memory merge unclear" ;;
        esac
      fi

      if [ ! -f "$CURSOR_RULE" ]; then
        cat > "$CURSOR_RULE" <<'RULEEOF'
---
description: Always use Cosmos MCP for memory read/write in Cursor chats
alwaysApply: true
---

# Cosmos MCP (always on)

The **cosmos** MCP server (`user-cosmos`) is the user's personal knowledge graph. Use it in every conversation when its tools are available.

## At conversation start

When the thread is substantive (not a one-liner like "hi" or "thanks"):

1. Confirm connectivity with `polarity_whoami` if you have not used cosmos MCP this session.
2. Pull context before advising: `polarity_ask` with a query distilled from the user's goal, or `polarity_get_graph` with `entity: "user"` when you need raw graph shape.

Do this proactively — do not wait for the user to ask for memory.

## During the conversation

- Prefer cosmos graph context over guessing about the user's history, preferences, or relationships.
- Use `polarity_observe`, `polarity_record_event`, or `polarity_record_preference` for single durable facts worth persisting without a full turn capture.

## At end of substantive exchanges

Call `polarity_capture_turn` with `user_text`, `assistant_text`, and `source: "cursor"`. Skip only trivial meta chatter or explicit "don't save this" requests.

## If tools are missing

Tell the user **cosmos** MCP is disconnected: **Settings → MCP → cosmos → refresh**, or **Developer: Reload Window**.
RULEEOF
        ok "Cursor global rule written ($CURSOR_RULE)"
      else
        skip "Cursor cosmos rule already present"
      fi

      if [ ! -f "$CBM_RULE" ]; then
        cat > "$CBM_RULE" <<'CBMRULEEOF'
---
description: Use codebase-memory MCP for structural code search in every repo
alwaysApply: true
---

# Codebase memory (always on)

The **codebase-memory** MCP server (`user-codebase-memory`) indexes repos into a local knowledge graph. It is **user-global** in `~/.cursor/mcp.json` but **indexed per repo** — not automatic until indexed.

## Project key

Derive from workspace root: `/Users/you/your-repo` → `Users-you-your-repo` (drop leading `/`, replace `/` with `-`).

Or call `list_projects` and match `root_path` to the open workspace.

## Session start (substantive code work)

1. **`list_projects`** — is this workspace indexed?
2. **`index_status`** with the project key — if missing or stale, **`index_repository`** with `repo_path` = workspace root and `mode: "fast"` (use `full` after large refactors).
3. Prefer **`search_graph`** / **`trace_path`** over grep for definitions, call chains, and routes.

## When to use which memory

| Question | Tool |
|----------|------|
| Where is X defined / who calls Y? | codebase-memory `search_graph`, `trace_path` |
| User intent, life context, product decisions | cosmos `cosmos_briefing`, `cosmos_query`, `cosmos_turn` |
| Sprint tasks, blockers | beads `bd ready` (cosmos repo only) |

## Cross-repo (Polarity Lab stack)

After indexing sibling repos, run from cosmos:

\`\`\`bash
~/cosmos/scripts/bootstrap-lab-stack.sh        # fast
~/cosmos/scripts/bootstrap-lab-stack.sh --full # after manifest or API surface changes
\`\`\`

Runtime stack edges: **cosmos_stack** or **GET /api/me/lab-stack**. CBM CROSS_* may stay 0 until linker supports fetch/httpx literals — read \`.codebase-memory/lab-stack-links.json\` sidecars per repo.

## If tools are missing

**Settings → MCP → codebase-memory → refresh** or **Developer: Reload Window**. Binary: `~/.local/bin/codebase-memory-mcp`.
CBMRULEEOF
        ok "Cursor codebase-memory rule written ($CBM_RULE)"
      else
        skip "Cursor codebase-memory rule already present"
      fi

      PERM_RESULT=$("$PY" - "$CURSOR_PERMS" <<'PYEOF'
import json, os, sys
path = sys.argv[1]
default = {
    "mcpAllowlist": ["cosmos:*", "codebase-memory:*"],
    "autoRun": {
        "allow_instructions": [
            "Cosmos MCP read tools (polarity_whoami, polarity_ask, polarity_get_graph) at the start of substantive conversations.",
            "Cosmos MCP write tools (polarity_capture_turn, polarity_observe, polarity_record_preference) after substantive user/assistant exchanges.",
            "Codebase-memory MCP (list_projects, index_status, index_repository, search_graph, trace_path) for structural code exploration.",
        ]
    },
}
try:
    with open(path) as f:
        data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
    data = default
    with open(path, "w") as f:
        json.dump(data, f, indent=2)
    print("written")
    sys.exit(0)
changed = False
allow = data.setdefault("mcpAllowlist", [])
for entry in default["mcpAllowlist"]:
    if entry not in allow:
        allow.append(entry)
        changed = True
auto = data.setdefault("autoRun", {})
instr = auto.setdefault("allow_instructions", [])
for line in default["autoRun"]["allow_instructions"]:
    if line not in instr:
        instr.append(line)
        changed = True
if changed:
    tmp = path + ".tmp"
    with open(tmp, "w") as f:
        json.dump(data, f, indent=2)
    os.replace(tmp, path)
    print("merged")
else:
    print("present")
PYEOF
)
      case "$PERM_RESULT" in
        written) ok "Cursor permissions.json written ($CURSOR_PERMS)" ;;
        merged)  ok "Cursor permissions.json merged ($CURSOR_PERMS)" ;;
        present) skip "Cursor permissions.json already has cosmos + codebase-memory" ;;
        *)       warn "Cursor permissions merge unclear" ;;
      esac

      SYNC_ALL="$HOME/cosmos/scripts/sync-cursor-all.sh"
      SYNC_COSMOS="$HOME/cosmos/scripts/sync-cursor-cosmos.sh"
      if [ -x "$SYNC_ALL" ]; then
        "$SYNC_ALL" >/dev/null && ok "Cursor rules linked into all git repos" || warn "sync-cursor-all.sh failed"
      elif [ -x "$SYNC_COSMOS" ]; then
        "$SYNC_COSMOS" >/dev/null && ok "Cursor cosmos rule linked into all git repos" || warn "sync-cursor-cosmos.sh failed"
        note "run scripts/sync-cursor-codebase-memory.sh after cloning cosmos for codebase-memory rule"
      else
        note "clone cosmos and run scripts/sync-cursor-all.sh to link rules into every repo"
      fi
    else
      note "would write ~/.cursor/rules (cosmos + codebase-memory) and merge permissions.json"
    fi
  else
    skip "Cursor not installed"
  fi

  # --- Zed ---
  ZED_CFG="$HOME/.config/zed/settings.json"
  if [ -d "$HOME/.config/zed" ] || command -v zed >/dev/null 2>&1; then
    report_merge "Zed" "$(merge_json_mcp "$ZED_CFG" "context_servers")"
  else
    skip "Zed not installed"
  fi

  # --- Continue ---
  # Continue uses YAML now (config.yaml), with config.json as legacy. YAML
  # merge in a portable shell script is fragile (comment + format
  # preservation), so we only auto-merge the legacy JSON form. For YAML
  # users we print the copy-paste snippet they need.
  CONTINUE_YAML="$HOME/.continue/config.yaml"
  CONTINUE_JSON="$HOME/.continue/config.json"
  if [ -f "$CONTINUE_JSON" ]; then
    # Legacy JSON form. mcpServers is an array, not an object — handle
    # inline rather than via the object-shaped merge_json_mcp helper.
    if [ $DRY_RUN -eq 1 ]; then
      note "would add cosmos to Continue (legacy config.json)"
    else
      backup_file "$CONTINUE_JSON"
      "$PY" - "$CONTINUE_JSON" <<'PYEOF'
import json, os, sys
path = sys.argv[1]
with open(path) as f:
    data = json.load(f)
servers = data.setdefault("mcpServers", [])
if any(s.get("name") == "cosmos" for s in servers):
    print("Continue (json) already has cosmos")
    sys.exit(0)
servers.append({
    "name": "cosmos",
    "command": "npx",
    "args": ["-y", "@polarity-lab/cosmos-mcp"]
})
tmp = path + ".tmp"
with open(tmp, "w") as f:
    json.dump(data, f, indent=2)
os.replace(tmp, path)
print("Continue (json) merged (other servers preserved)")
PYEOF
    fi
  elif [ -f "$CONTINUE_YAML" ]; then
    warn "Continue uses YAML config; auto-merge skipped"
    note "add this block under mcpServers in $CONTINUE_YAML:"
    note "  - name: cosmos"
    note "    command: npx"
    note "    args: [\"-y\", \"@polarity-lab/cosmos-mcp\"]"
  elif [ -d "$HOME/.continue" ]; then
    skip "Continue installed but no config found"
  else
    skip "Continue not installed"
  fi

  # --- Claude Code (CLI) ---
  # Prefer the CLI's own command so we do not hand-edit ~/.claude.json.
  if command -v claude >/dev/null 2>&1; then
    if claude mcp list 2>/dev/null | grep -q "^cosmos\b"; then
      skip "Claude Code already has cosmos"
    elif [ $DRY_RUN -eq 1 ]; then
      note "would run: claude mcp add cosmos --scope user -- npx -y $NPM_PKG"
    else
      claude mcp add cosmos --scope user -- npx -y "$NPM_PKG" >/dev/null 2>&1 \
        && ok "Claude Code wired (user scope)" \
        || warn "claude mcp add returned non-zero. add manually if needed"
    fi
  else
    skip "Claude Code CLI not installed"
  fi

  # --- Codex ---
  CODEX_CFG="$HOME/.codex/config.toml"
  if [ -f "$CODEX_CFG" ] || command -v codex >/dev/null 2>&1; then
    mkdir -p "$HOME/.codex"
    if grep -q '^\[mcp_servers\.cosmos\]' "$CODEX_CFG" 2>/dev/null; then
      skip "Codex already has cosmos"
    elif [ $DRY_RUN -eq 1 ]; then
      note "would append [mcp_servers.cosmos] block to $CODEX_CFG"
    else
      backup_file "$CODEX_CFG"
      cat >> "$CODEX_CFG" <<TOMLEOF

[mcp_servers.cosmos]
command = "npx"
args = ["-y", "@polarity-lab/cosmos-mcp"]
TOMLEOF
      ok "Codex appended (existing servers preserved)"
    fi
  else
    skip "Codex not installed"
  fi
fi

# ----- 6. open browser for key handoff -----
step "6." "opening cosmos.polarity-lab.com for key handoff"
if [ $DRY_RUN -eq 1 ]; then
  note "would open $COSMOS_URL"
elif [ "$PLATFORM" = "mac" ]; then
  open "$COSMOS_URL" 2>/dev/null || note "open this in your browser when ready: $COSMOS_URL"
elif command -v xdg-open >/dev/null 2>&1; then
  xdg-open "$COSMOS_URL" >/dev/null 2>&1 || true
else
  note "open this in your browser: $COSMOS_URL"
fi

# ----- done -----
printf "\n${BOLD}${GREEN}done.${RESET}\n"
printf "open Claude Code or Cursor and ask cosmos what it knows about you.\n\n"
printf "${DIM}docs:        https://cosmos.polarity-lab.com/install${RESET}\n"
printf "${DIM}source:      https://github.com/teampolarity/cosmos-mcp${RESET}\n"
printf "${DIM}every config above has a .bak.<timestamp> beside it if you want to roll back.${RESET}\n\n"
