#!/bin/bash
# UV Suite — One-command installer
# Installs agents, skills, hooks, guardrails, and settings into a project or globally.
#
# Usage:
#   ./install.sh                              # Install into current project (Professional)
#   ./install.sh --persona sport              # Fast & lightweight for new projects
#   ./install.sh --persona professional       # Full rigor for production code (default)
#   ./install.sh --persona auto               # Maximum autonomy, minimal human gates
#   ./install.sh --persona spike              # Read-only mode for understanding
#   ./install.sh --global               # Install globally (~/.claude/)
#   ./install.sh --project /path        # Install into specific project

set -e

UV_SUITE_DIR="$(cd "$(dirname "$0")" && pwd)"
INSTALL_MODE="project"
PERSONA="professional"
TARGET_DIR=""

# Parse arguments
while [[ $# -gt 0 ]]; do
  case $1 in
    --global)
      INSTALL_MODE="global"
      TARGET_DIR="$HOME/.claude"
      shift
      ;;
    --project)
      INSTALL_MODE="project"
      TARGET_DIR="$2/.claude"
      shift 2
      ;;
    --persona)
      PERSONA="$2"
      if [[ ! "$PERSONA" =~ ^(sport|professional|auto|spike)$ ]]; then
        echo "Unknown persona: $PERSONA"
        echo "Available: sport (lightweight), professional (full rigor), auto (autonomous), spike (research & docs)"
        exit 1
      fi
      shift 2
      ;;
    *)
      echo "Unknown option: $1"
      echo "Usage: ./install.sh [--persona sport|professional|auto|spike] [--global | --project /path]"
      exit 1
      ;;
  esac
done

# Default: current directory
if [ -z "$TARGET_DIR" ]; then
  TARGET_DIR="$(pwd)/.claude"
fi

PERSONA_LABEL="Professional (full rigor)"
if [ "$PERSONA" = "sport" ]; then PERSONA_LABEL="Sport (lightweight)"; fi
if [ "$PERSONA" = "auto" ]; then PERSONA_LABEL="Auto (maximum autonomy)"; fi
if [ "$PERSONA" = "spike" ]; then PERSONA_LABEL="Spike (research & docs)"; fi

echo "╔══════════════════════════════════════╗"
echo "║         UV Suite Installer           ║"
echo "╚══════════════════════════════════════╝"
echo ""
echo "Installing to: $TARGET_DIR"
echo "Mode: $INSTALL_MODE"
echo "Persona: $PERSONA_LABEL"
echo ""

# --- Create directory structure ---
echo "Creating directory structure..."
mkdir -p "$TARGET_DIR/agents"
mkdir -p "$TARGET_DIR/skills"
mkdir -p "$TARGET_DIR/hooks"
mkdir -p "$TARGET_DIR/rules"

# --- Install agents (Claude Code subagent definitions are the canonical source) ---
echo "Installing 8 Claude Code agent definitions..."
cp "$UV_SUITE_DIR/agents/claude-code/"*.md "$TARGET_DIR/agents/"
echo "  ✓ cartographer, spec-writer, architect, reviewer, test-writer"
echo "  ✓ eval-writer, anti-slop-guard, prototype-builder"

# Codex (.toml) and Cursor (.mdc) agents are GENERATED from the canonical .md files
# so there is a single source of truth — edit agents/claude-code/<name>.md only.
PROJECT_ROOT="$(dirname "$TARGET_DIR")"
if ! command -v python3 &>/dev/null; then
  echo "  ⚠ python3 not found — skipping Codex/Cursor agent generation (Claude Code agents installed)"
else
  echo "Generating Codex agent definitions from canonical..."
  python3 "$UV_SUITE_DIR/agents/generate.py" codex "$PROJECT_ROOT/.codex/agents"
  echo "Generating Cursor rule definitions from canonical..."
  python3 "$UV_SUITE_DIR/agents/generate.py" cursor "$PROJECT_ROOT/.cursor/rules"
fi

# Create AGENTS.md for Codex (it reads this instead of CLAUDE.md)
if [ ! -f "$PROJECT_ROOT/AGENTS.md" ]; then
  cp "$PROJECT_ROOT/CLAUDE.md" "$PROJECT_ROOT/AGENTS.md" 2>/dev/null || touch "$PROJECT_ROOT/AGENTS.md"
  echo "  ✓ AGENTS.md created (Codex reads this)"
else
  echo "  ✓ AGENTS.md already exists"
fi

# --- Install skills (slash commands) ---
echo "Installing skills..."
for skill_dir in "$UV_SUITE_DIR/skills/"*/; do
  skill_name=$(basename "$skill_dir")
  mkdir -p "$TARGET_DIR/skills/$skill_name"
  # Copy the whole skill dir — SKILL.md plus any sub-folders the orchestrator
  # routes into (specialists/, modes/, operations/).
  cp -R "$skill_dir"* "$TARGET_DIR/skills/$skill_name/"
done
echo "  ✓ /understand (auto-detects repo vs stack), /spec, /architect, /test (--unit/--integration/--eval)"
echo "  ✓ /review (--security/--slop/--architecture), /prototype, /qa, /investigate"
echo "  ✓ /commit, /session (init|checkpoint|restore|end|auto), /confirm, /uv-help"

# --- Install hooks ---
echo "Installing hook scripts..."
cp "$UV_SUITE_DIR/hooks/"*.sh "$TARGET_DIR/hooks/"
chmod +x "$TARGET_DIR/hooks/"*.sh
echo "  ✓ auto-lint.sh (PostToolUse: auto-format)"
echo "  ✓ danger-zone-check.sh (PreToolUse: warn on danger zone files)"
echo "  ✓ block-destructive.sh (PreToolUse: block rm -rf, force push, etc.)"
echo "  ✓ session-start.sh (SessionStart: track session start time)"
echo "  ✓ session-timer.sh (PostToolUse: warn at 45/90/180 min)"
echo "  ✓ session-end.sh (Stop: reflection + duration + review reminder)"
echo "  ✓ session-label-nag.sh (UserPromptSubmit: nudge to /session init)"
echo "  ✓ session-meta.sh (helper used by /session init)"
echo "  ✓ status-line.sh (statusLine: show session label + time)"
echo "  + Real-time slop check (Haiku prompt hook, wired in settings.json)"

# --- Install guardrail rules (Sport only) ---
if [ "$PERSONA" = "professional" ] || [ "$PERSONA" = "auto" ]; then
  echo "Installing 6 guardrail rules..."
  cp "$UV_SUITE_DIR/guardrails/"*.md "$TARGET_DIR/rules/"
  echo "  ✓ comment-slop, overengineering-slop, error-handling-slop"
  echo "  ✓ test-slop, doc-slop, architecture-slop"
else
  echo "Skipping guardrail rules ($PERSONA persona — guardrails not needed)"
  echo "  Available at: $UV_SUITE_DIR/guardrails/ if you want them later"
fi

# --- Install persona-specific settings ---
echo "Installing persona: $PERSONA_LABEL..."
mkdir -p "$TARGET_DIR/personas"
cp "$UV_SUITE_DIR/personas/"*.json "$TARGET_DIR/personas/" 2>/dev/null || true

# Install settings.json from the selected persona
if [ ! -f "$TARGET_DIR/settings.json" ]; then
  cp "$UV_SUITE_DIR/personas/$PERSONA.json" "$TARGET_DIR/settings.json"
  echo "  ✓ Settings from $PERSONA persona"
else
  # Don't overwrite existing settings.json, use settings.local.json instead
  cp "$UV_SUITE_DIR/personas/$PERSONA.json" "$TARGET_DIR/settings.local.json"
  echo "  ✓ Persona applied via settings.local.json (preserves existing settings.json)"
fi
echo "  ✓ All 4 personas available in $TARGET_DIR/personas/"
echo "    Switch with: cp .claude/personas/sport.json .claude/settings.local.json"

# --- Install portable standards (project root, not .claude/) ---
if [ "$INSTALL_MODE" = "project" ]; then
  PROJECT_ROOT="$(dirname "$TARGET_DIR")"
  echo "Installing portable standards to project root..."

  for std_file in CODING-STANDARDS.md REVIEW-CHECKLIST.md SPEC-TEMPLATE.md ACTS-TEMPLATE.md; do
    # Only install portable standards if we have them extracted
    if [ -f "$UV_SUITE_DIR/portable-standards/$std_file" ]; then
      cp "$UV_SUITE_DIR/portable-standards/$std_file" "$PROJECT_ROOT/"
      echo "  ✓ $std_file"
    fi
  done
fi

# --- Write UV Suite context to CLAUDE.md (before bundled tools, which can be slow) ---
if [ "$INSTALL_MODE" = "project" ]; then
  PROJECT_ROOT="$(dirname "$TARGET_DIR")"
  CLAUDE_MD="$PROJECT_ROOT/CLAUDE.md"
  UV_VERSION=$(grep '"version"' "$UV_SUITE_DIR/package.json" 2>/dev/null | head -1 | sed 's/.*": "//;s/".*//')

  # Remove existing UV Suite section if present
  if [ -f "$CLAUDE_MD" ] && grep -q "## UV Suite" "$CLAUDE_MD" 2>/dev/null; then
    echo "Updating UV Suite section in CLAUDE.md..."
    # Create temp file without UV Suite section
    awk '/^## UV Suite$/{found=1; next} /^## [^U]/{if(found){found=0}} !found' "$CLAUDE_MD" > "$CLAUDE_MD.tmp"
    mv "$CLAUDE_MD.tmp" "$CLAUDE_MD"
  else
    echo "Adding UV Suite section to CLAUDE.md..."
    # Create CLAUDE.md if it doesn't exist
    touch "$CLAUDE_MD"
  fi

  # Determine active hooks text
  HOOKS_TEXT=""
  case "$PERSONA" in
    professional)
      HOOKS_TEXT="- auto-lint (on file write), slop check (on file write), danger zone (on file edit), destructive block (on bash), review reminder (on session end)" ;;
    auto)
      HOOKS_TEXT="- auto-lint (on file write), destructive block (on bash)" ;;
    sport)
      HOOKS_TEXT="- auto-lint (on file write)" ;;
    spike)
      HOOKS_TEXT="- doc slop check (on file write)" ;;
  esac

  cat >> "$CLAUDE_MD" << EOF

## UV Suite

This project uses [UV Suite](https://github.com/utsavanand/uv-suite) v${UV_VERSION} for AI-assisted development.

**Active persona:** ${PERSONA_LABEL}

### Skills

/understand (auto-detects repo vs stack), /spec, /architect, /test (--unit/--integration/--eval), /review (--security/--slop/--architecture), /prototype, /qa, /investigate, /commit, /session (init|checkpoint|restore|end|auto), /confirm, /uv-help

### When to use which skill (reach for these proactively)

Prefer the matching skill over doing the task ad hoc -- they carry the project's conventions,
anti-slop guardrails, and session-scoped artifacts. Don't wait to be told:
- Entering an unfamiliar codebase, or "how does this work" -> /understand
- New feature or vague requirements -> /spec, then /architect (which gates on the spec)
- After implementing, or coverage is low -> /test (--eval for LLM/AI features)
- Before merging, or reviewing a diff -> /review (--security for auth/payments/data;
  --architecture to audit a design against its recorded constraints)
- A bug you can't explain -> /investigate
- Shipping a change -> /commit
- Session lifecycle -> /session init|checkpoint|restore|end
If unsure which fits, ask the user rather than guessing.

### Artifacts

Agent output is written to uv-out/, scoped by session: uv-out/sessions/<session-id>/.
uv-out/current symlinks to the active session's directory. The session id comes from
UVS_SESSION_ID (or .uv-suite-state/current-session.txt), so every artifact is attributable
to the session that produced it. Each artifact is also stamped with provenance frontmatter
(session, skill, created).

Downstream skills prefer the current session's artifacts but can use a prior session's when
the current one has none (they list current → prior → legacy and ask which to use). All
writers are session-scoped:
- /understand  → uv-out/sessions/<sid>/map-codebase.md (repo) or map-stack.md (stack)
- /spec          → uv-out/sessions/<sid>/specs/
- /architect     → uv-out/sessions/<sid>/architecture/
- /review        → uv-out/sessions/<sid>/review/state.md   (flat pointer: uv-out/review-state.md)
- /review --slop    → uv-out/sessions/<sid>/slop/report.md
- /review --security → uv-out/sessions/<sid>/security/report.md
- /investigate   → uv-out/sessions/<sid>/investigate/report.md
- /test --eval   → uv-out/sessions/<sid>/evals/
- /qa            → uv-out/sessions/<sid>/qa/   (flat pointer: uv-out/qa-state.md)
- /session checkpoint    → uv-out/sessions/<sid>/checkpoints/

Fixed-path consumers (/commit, /ship) read the flat pointer symlinks (uv-out/review-state.md,
uv-out/qa-state.md), which resolve into the current session. The Stop hook uv-out-notify.sh
prints the artifact path (and session id) to the terminal after each run.

### Hooks

${HOOKS_TEXT}

### Working practices

**Honesty:** If you can't find something, say "I did not find X. What should I do?" Don't fabricate. If 2-3 attempts fail, escalate with what you tried.

**Simplicity:** Minimum code that solves the problem. If 200 lines could be 50, rewrite. No abstractions for single-use code. No features beyond what was asked.

**Surgical changes:** Touch only what's relevant. Don't improve adjacent code, comments, or formatting. Every changed line should trace to the request. Match existing style.

**Goal-driven:** Define success criteria before coding. Write the test first, then make it pass. For multi-step tasks, state a brief plan with verification for each step.

**Parallelism:** Spin up parallel agents for independent tasks. Run independent tool calls in the same message. This is the primary speed lever.

**Completion:** "Done" means verified. Run the tests. Run the build. Prefer "I ran it and it works" over "should work."

**Failures:** When you fail, say so. Escalate: what you tried, why each failed, what you need.

**User context:** If something looks wrong, ask why before fixing. Users have constraints you may not see.

**Planning:** Use plan mode for complex tasks. Break work small enough to complete in under 50% context.

**Session:** /compact at ~50% context. Past 90 min, take a break. Run /session checkpoint before ending a session. Run /session restore at the start of the next one.

### Launching sessions

uv claude pro | uv codex pro | uv pro (shorthand)
EOF

  echo "  ✓ UV Suite section added to CLAUDE.md"
fi

# --- Install bundled tools ---
echo "Installing bundled integrations..."

# Python tools (Graphify, Semgrep, DeepEval)
PIP_CMD=""
if command -v pip3 &>/dev/null; then PIP_CMD="pip3"
elif command -v pip &>/dev/null; then PIP_CMD="pip"
fi

# `timeout` is absent on stock macOS (gtimeout via coreutils); fall back to no timeout.
TO=""
if command -v timeout &>/dev/null; then TO="timeout 60"
elif command -v gtimeout &>/dev/null; then TO="gtimeout 60"; fi

if [ -n "$PIP_CMD" ]; then
  for pkg_info in "graphifyy:graphify:Graphify (knowledge graphs for Cartographer)" \
                  "semgrep:semgrep:Semgrep (SAST for Security Agent)" \
                  "deepeval:deepeval:DeepEval (LLM evaluation for Eval Writer)"; do
    pkg=$(echo "$pkg_info" | cut -d: -f1)
    cmd=$(echo "$pkg_info" | cut -d: -f2)
    label=$(echo "$pkg_info" | cut -d: -f3)
    if command -v "$cmd" &>/dev/null; then
      echo "  ✓ $label (already installed)"
    else
      echo "  Installing $label..."
      $TO $PIP_CMD install "$pkg" --quiet 2>/dev/null || true
      if command -v "$cmd" &>/dev/null || $PIP_CMD show "$pkg" &>/dev/null; then
        echo "  ✓ $label installed"
      else
        echo "  ✗ $label failed — install manually: $PIP_CMD install $pkg"
      fi
    fi
  done

  # Graphify needs an extra install step
  if command -v graphify &>/dev/null; then
    graphify install --quiet 2>/dev/null || true
  fi
else
  echo "  ✗ pip not found — skipping Python tools (Graphify, Semgrep, DeepEval)"
  echo "    Install Python 3 and retry, or install manually:"
  echo "    pip install graphifyy semgrep deepeval"
fi

# Node tools (Repomix — installed as npm dependency)
if command -v repomix &>/dev/null; then
  echo "  ✓ Repomix (already installed)"
else
  echo "  Installing Repomix (codebase context packing)..."
  $TO npm install -g repomix --quiet 2>/dev/null || true
  if command -v repomix &>/dev/null; then
    echo "  ✓ Repomix installed"
  else
    echo "  ✗ Repomix failed — install manually: npm install -g repomix"
  fi
fi

# tmux — required for Watchtower to OWN and control uvs sessions (approve/close/send-keys).
if command -v tmux &>/dev/null; then
  echo "  ✓ tmux (Watchtower can own + control uvs sessions)"
else
  echo "  ⚠ tmux not found — Watchtower control (approve/close from the dashboard) needs it."
  echo "    macOS: brew install tmux  ·  Debian/Ubuntu: apt install tmux"
  echo "    Without tmux, uvs sessions launch normally but aren't Watchtower-controllable"
  echo "    (out-of-band checkpoint still works)."
fi

# Go tools (Gitleaks, Trivy — brew or binary)
if command -v brew &>/dev/null; then
  for tool_info in "gitleaks:Gitleaks (secret detection)" \
                   "trivy:Trivy (dependency vulnerability scanning)"; do
    tool=$(echo "$tool_info" | cut -d: -f1)
    label=$(echo "$tool_info" | cut -d: -f2)
    if command -v "$tool" &>/dev/null; then
      echo "  ✓ $label (already installed)"
    else
      echo "  Installing $label..."
      brew install "$tool" --quiet 2>/dev/null
      if command -v "$tool" &>/dev/null; then
        echo "  ✓ $label installed"
      else
        echo "  ✗ $label failed — install manually: brew install $tool"
      fi
    fi
  done
else
  if ! command -v gitleaks &>/dev/null; then
    echo "  · Gitleaks not found — install: brew install gitleaks"
  fi
  if ! command -v trivy &>/dev/null; then
    echo "  · Trivy not found — install: brew install trivy"
  fi
fi

# --- Register Playwright MCP server ---
echo "Registering MCP servers..."
if command -v claude &>/dev/null; then
  # Check if playwright MCP is already registered
  if claude mcp list 2>/dev/null | grep -q "playwright"; then
    echo "  ✓ Playwright MCP (already registered)"
  else
    echo "  Registering Playwright MCP (browser automation for Prototype Builder + Test Writer)..."
    claude mcp add playwright -- npx @playwright/mcp@latest 2>/dev/null
    if [ $? -eq 0 ]; then
      echo "  ✓ Playwright MCP registered"
    else
      echo "  ✗ Playwright MCP failed — register manually: claude mcp add playwright -- npx @playwright/mcp@latest"
    fi
  fi
else
  echo "  · Claude Code CLI not found — skipping MCP registration"
  echo "    Register manually after installing Claude Code: claude mcp add playwright -- npx @playwright/mcp@latest"
fi

# --- Install launcher script ---
echo "Installing session launcher..."
cp "$UV_SUITE_DIR/uv.sh" "$TARGET_DIR/../uv.sh" 2>/dev/null || true
chmod +x "$TARGET_DIR/../uv.sh" 2>/dev/null || true
echo "  ✓ uv.sh (launch sessions with: ./uv.sh sport)"

echo ""
echo "╔══════════════════════════════════════╗"
echo "║      Installation Complete!          ║"
echo "║      Persona: $(printf '%-24s' "$PERSONA_LABEL") ║"
echo "╚══════════════════════════════════════╝"
echo ""
echo "What was installed:"
echo ""
echo "  Claude Code     .claude/agents/*.md (8 canonical) + skills/ + hooks/ + rules/"
echo "  Codex           .codex/agents/*.toml (generated from canonical) + AGENTS.md"
echo "  Cursor          .cursor/rules/*.mdc (generated from canonical)"
echo "  Personas (4)    .claude/personas/*.json"
echo "  Launcher        ./uv.sh"
echo ""

echo "Available slash commands (12 skills):"
echo ""
echo "  /understand                  Map a codebase or full stack (auto-detected) (Cartographer)"
echo "  /spec [requirements]          Write a technical spec (Spec Writer)"
echo "  /architect [spec]             Design architecture + Acts (Architect)"
echo "  /test [--unit|--integration|--eval]  Generate tests/evals (Test Writer, Eval Writer)"
echo "  /review [--security|--slop]   Code review, security audit, slop check (Reviewer, etc.)"
echo "  /prototype [concept]          Build a prototype (Prototype Builder)"
echo "  /qa [target]                  Browser-based QA (Playwright MCP)"
echo "  /investigate [question]       Multi-step codebase investigation"
echo "  /commit                       Stage and commit changes"
echo "  /session <init|checkpoint|restore|end|auto>  Manage session lifecycle"
echo "  /confirm                      Confirm a HITL gate"
echo "  /uv-help                      Show UV Suite help"
echo ""

if [ "$PERSONA" = "sport" ]; then
echo "Active hooks (Sport — minimal):"
echo ""
echo "  On file write → auto-format (prettier/ruff/gofmt)"
echo ""
echo "Sport mode: fast, autonomous, low cost. All other hooks disabled."
elif [ "$PERSONA" = "professional" ]; then
echo "Active hooks (Professional — full rigor):"
echo ""
echo "  On file write → auto-format (prettier/ruff/gofmt)"
echo "  On file write → real-time slop check (Haiku scans for obvious slop)"
echo "  On file edit  → check danger zones (warn if in DANGER-ZONES.md)"
echo "  On bash cmd   → block destructive commands (rm -rf, force push)"
echo "  On session end → remind to review uncommitted changes"
echo ""
echo "Professional mode: all guardrails active, full review rigor."
elif [ "$PERSONA" = "auto" ]; then
echo "Active hooks (Auto — safety net only):"
echo ""
echo "  On file write → auto-format (prettier/ruff/gofmt)"
echo "  On bash cmd   → block truly destructive commands only"
echo ""
echo "Auto mode: maximum autonomy. All tools pre-approved. Guardrails"
echo "active as context rules. No human gates, no review reminders."
echo "The agent builds, tests, reviews, and iterates on its own."
elif [ "$PERSONA" = "spike" ]; then
echo "Active hooks (Spike — doc quality):"
echo ""
echo "  On doc write → slop check (Haiku catches vague adjectives, non-specific claims)"
echo ""
echo "Spike mode: deep understanding + documentation generation. Opus at max effort."
echo "Can write docs and analysis files. Cannot edit existing code, commit, or push."
fi

echo ""
echo "Start a session:"
echo ""
echo "  ./uv.sh claude pro      Claude Code, Professional"
echo "  ./uv.sh claude auto     Claude Code, Auto"
echo "  ./uv.sh codex pro       Codex, Professional"
echo "  ./uv.sh codex auto      Codex, Auto"
echo "  ./uv.sh pro             Shorthand (defaults to Claude Code)"
echo "  ./uv.sh                 Claude Code, Professional"
