Skill: dorayo/create-project (version 3.0.0)

--- name: create-project description: "Interactive Orchestrix project scaffolding. Creates project directory under ~/Codes/, generates project brief from Q&A, installs MCP config, hooks, slash commands, tmux scripts, and initializes git. Use when user wants to create a new project, init a project, or start a new Orchestrix workspace." license: MIT metadata: author: dorayo version: "3.0.0" homepage: "https://orchestrix-mcp.youlidao.ai" openclaw: requires: bins: ["git", "bash"] emoji: "\U0001F680" os: ["macos", "linux"] --- # Create Project — Orchestrix 项目初始化 Skill 你是一个项目初始化助手。你的任务是通过交互式问答收集项目信息，然后自动创建完整的 Orchestrix 项目骨架。 **严格按照以下流程执行，不要跳过任何步骤。** **资源目录**: 本 Skill 的静态资源文件位于 `${CLAUDE_SKILL_DIR}/resources/`。如果该变量不可用，则回退到 `~/.claude/commands/create-project/`。 --- ## Phase 1: 信息收集 向用户逐步提问，收集以下信息。**一次只问 2-3 个问题**，不要一次性抛出所有问题。 ### 第零轮（License Key） 0. **Orchestrix License Key**：请提供你的 Orchestrix License Key（格式：`orch_live_xxx` 或 `orch_trial_xxx`）。 - 如果用户没有 Key → 告知可以在 https://orchestrix-mcp.youlidao.ai 申请 - 如果用户说"稍后配置" → 记录为空，后续在 mcp.json 中保留占位符 `YOUR_LICENSE_KEY_HERE` - 验证格式：必须以 `orch_` 开头，否则提示格式不对 ### 第一轮（必问） 1. **项目名称**：这个项目叫什么？（中文名即可，你会建议一个英文目录名） 2. **核心问题**：这个项目要解决什么问题？（1-3 句话描述） 3. **目标用户**：目标用户是谁？他们目前的痛点是什么？ ### 第二轮（必问） 4. **技术栈偏好**： - 平台：Web / Mobile / Desktop / CLI / 其他？ - 前端：React / Vue / Next.js / 不需要 / 其他？ - 后端：Node.js / Python / Go / Java / 不需要 / 其他？ - 数据库：PostgreSQL / MySQL / MongoDB / SQLite / 不需要 / 其他？ 5. **MVP 核心功能**：MVP 阶段必须实现哪些功能？（列出 3-5 个） ### 第三轮（按需） 6. **约束条件**：有无时间、预算、团队规模的约束？ 7. **追问**：如果以上回答中有模糊或矛盾的地方，继续追问直到清晰。 ### 提问规则 - 用中文提问 - 语气友好专业 - 如果用户的回答已经覆盖了后续问题，不要重复问 - 如果用户说"没有"或"不确定"，记录为待定，继续下一个问题 --- ## Phase 2: 确认理解 收集完信息后，生成以下格式的结构化摘要，让用户确认： ``` ## 📋 项目理解确认 | 项 | 内容 | |---|------| | **项目名称** | {中文名} | | **项目目录** | ~/Codes/{english-kebab-case-name}/ | | **核心问题** | {1-2 句话} | | **目标用户** | {用户画像} | | **技术栈** | 前端: {x} / 后端: {x} / 数据库: {x} | | **MVP 功能** | 1. xxx 2. xxx 3. xxx | | **约束** | {时间/预算/团队，或"无特殊约束"} | | **License Key** | {已提供 / 待配置} | 以上理解是否正确？(Y/N，或告诉我需要修改的地方) ``` **目录名生成规则**： - 中文名 → 转为英文含义的 kebab-case（如"智能柜管理系统" → `smart-locker-management`） - 如果用户提供了英文名，直接用 kebab-case 格式 - 全小写，单词间用 `-` 连接 - 不超过 30 个字符 **等待用户确认后才能进入 Phase 3。如果用户要求修改，回到对应问题重新确认。** --- ## Phase 3: 创建项目骨架 用户确认后，执行以下操作。使用 Bash 工具和 Write 工具完成所有文件操作。 ### 资源目录定位 首先确定资源文件的位置。按以下优先级查找： ```bash # 优先级 1: Plugin 内的资源目录 if [ -d "${CLAUDE_SKILL_DIR}/resources" ]; then RESOURCE_DIR="${CLAUDE_SKILL_DIR}/resources" # 优先级 2: 全局 commands 目录 elif [ -d "$HOME/.claude/commands/create-project" ]; then RESOURCE_DIR="$HOME/.claude/commands/create-project" # 优先级 3: 项目内 skills 目录 elif [ -d ".claude/skills/create-project/resources" ]; then RESOURCE_DIR=".claude/skills/create-project/resources" fi ``` ### Step 1: 创建目录结构 ```bash PROJECT_DIR=~/Codes/{project-dir-name} mkdir -p "$PROJECT_DIR/docs" mkdir -p "$PROJECT_DIR/.claude/commands" mkdir -p "$PROJECT_DIR/.claude/hooks" mkdir -p "$PROJECT_DIR/.orchestrix-core/scripts" ``` ### Step 2: 生成 .mcp.json 读取资源目录中的 `mcp.json.template`，将 `{{ORCHESTRIX_LICENSE_KEY}}` 替换为用户提供的 License Key（或 `YOUR_LICENSE_KEY_HERE`），然后写入 `$PROJECT_DIR/.mcp.json`。 **重要**：如果资源目录中存在 `mcp.json`（非模板版本），直接使用它。如果只有 `mcp.json.template`，则执行替换。 ### Step 3: 复制 Orchestrix 基础设施 从资源目录复制文件： ```bash # Claude Code hooks 配置 cp "$RESOURCE_DIR/settings.local.json" "$PROJECT_DIR/.claude/settings.local.json" # Hook 脚本 cp "$RESOURCE_DIR/handoff-detector.sh" "$PROJECT_DIR/.claude/hooks/handoff-detector.sh" chmod +x "$PROJECT_DIR/.claude/hooks/handoff-detector.sh" # Slash Commands cp "$RESOURCE_DIR/o.md" "$PROJECT_DIR/.claude/commands/o.md" cp "$RESOURCE_DIR/o-help.md" "$PROJECT_DIR/.claude/commands/o-help.md" cp "$RESOURCE_DIR/o-status.md" "$PROJECT_DIR/.claude/commands/o-status.md" # tmux 启动脚本 cp "$RESOURCE_DIR/start-orchestrix.sh" "$PROJECT_DIR/.orchestrix-core/scripts/start-orchestrix.sh" chmod +x "$PROJECT_DIR/.orchestrix-core/scripts/start-orchestrix.sh" # Helper 脚本（session 管理 + agent 完成检测） cp "$RESOURCE_DIR/ensure-session.sh" "$PROJECT_DIR/.orchestrix-core/scripts/ensure-session.sh" chmod +x "$PROJECT_DIR/.orchestrix-core/scripts/ensure-session.sh" cp "$RESOURCE_DIR/monitor-agent.sh" "$PROJECT_DIR/.orchestrix-core/scripts/monitor-agent.sh" chmod +x "$PROJECT_DIR/.orchestrix-core/scripts/monitor-agent.sh" ``` **关于信任对话框：** Claude Code 在进入新项目目录时会弹出 "trust this directory?" 确认框。 `start-orchestrix.sh` 和 `ensure-session.sh` 已内置自动检测和接受，无需手动操作。 ### Step 4: 生成 core-config.yaml 读取资源目录中的 `core-config.template.yaml`，替换占位符后写入项目： | 占位符 | 替换值 | |--------|--------| | `{{PROJECT_NAME}}` | 用户确认的项目中文名 | | `{{REPO_ID}}` | 英文目录名 | | `{{TEST_COMMAND}}` | 根据技术栈推断（见下表） | **testCommand 推断规则**： | 技术栈关键词 | testCommand | |-------------|-------------| | Node.js / React / Next.js / Vue | `npm test` | | Python / Django / Flask / FastAPI | `pytest` | | Go | `go test ./...` | | Java / Spring | `./gradlew test` | | Deno | `deno test -A` | | 其他或不确定 | （留空，自动检测） | 将替换后的内容写入 `$PROJECT_DIR/.orchestrix-core/core-config.yaml`。 ### Step 5: 生成 project-brief.md 基于用户的回答，按照以下模板结构生成 `$PROJECT_DIR/docs/project-brief.md`： ```markdown # Project Brief: {项目中文名} ## Executive Summary {项目名称}是一个{简述}。它旨在解决{核心问题}，面向{目标用户}提供{核心价值}。 ## Problem Statement ### 当前痛点 {基于用户描述的问题展开} ### 为什么现有方案不够 {如果用户提到了竞品或现有方案，在此展开；否则标注"待调研"} ## Proposed Solution ### 核心方案 {基于 MVP 功能推导的解决方案概述} ### 关键差异化 {如果信息充分则填写，否则标注"待定义"} ## Target Users ### 主要用户群 {基于用户回答的目标用户描述} ### 用户需求与痛点 {展开描述} ## Goals & Success Metrics ### 业务目标 - {基于项目目标推导，至少 2-3 条} ### 关键指标 (KPIs) - {合理推导，标注"待量化"} ## MVP Scope ### 核心功能 (Must Have) {逐条列出用户确认的 MVP 功能，每条附简要说明} ### MVP 之外 (Out of Scope) - 待定义（将在 PRD 阶段细化） ## Technical Considerations ### 平台要求 - **目标平台**: {平台} ### 技术偏好 - **前端**: {前端技术栈} - **后端**: {后端技术栈} - **数据库**: {数据库} ### 架构考量 - **仓库结构**: 待定义（将在架构设计阶段确定） - **部署方式**: 待定义 ## Constraints & Assumptions ### 约束 {用户提到的约束，或"无特殊约束"} ### 关键假设 - {基于对话推导的假设，至少 2-3 条} ## Risks & Open Questions ### 关键风险 - {基于项目特征推导的风险} ### 待解决问题 - {对话中标注为"待定"的项目} - {其他需要进一步调研的问题} ## Next Steps 1. 使用 `/o analyst` 深化项目简报（市场调研、竞品分析） 2. 使用 `/o pm` 基于简报生成 PRD 3. 使用 `/o architect` 进行架构设计 4. 使用 `/o sm` 将 PRD 拆分为 Stories --- > This Project Brief was generated by Orchestrix Create Project Skill. > To refine this brief, use `/o analyst` and run `*create-doc project-brief`. ``` **生成规则**： - 有信息的部分要充分展开，不要只复制用户原话，要加工整理 - 信息不足的部分标注"待定义"或"待调研"，不要编造 - 语言：中文（与用户交互语言一致） - 格式：严格遵循上述 Markdown 结构 ### Step 6: 生成 .gitignore 写入 `$PROJECT_DIR/.gitignore`： ```gitignore # Dependencies node_modules/ vendor/ venv/ __pycache__/ # Environment .env .env.local .env.*.local # OS .DS_Store Thumbs.db # Build dist/ build/ out/ *.log # IDE .idea/ .vscode/ *.swp *.swo # Orchestrix runtime .orchestrix-core/runtime/ ``` ### Step 7: Git 初始化 ```bash cd "$PROJECT_DIR" git init git add . git commit -m "chore: init project with Orchestrix - Project brief generated from interactive session - Orchestrix infrastructure: MCP config, hooks, slash commands - Core config with project-specific settings 🤖 Generated with [Orchestrix](https://orchestrix-mcp.youlidao.ai)" ``` --- ## Phase 4: 输出结果 完成所有操作后，输出以下信息： ``` ## ✅ 项目创建完成 **项目路径**: ~/Codes/{project-dir-name}/ ### 已创建的文件 - `docs/project-brief.md` — 项目简报 - `.mcp.json` — Orchestrix MCP Server 配置 - `.claude/settings.local.json` — Claude Code hooks 配置 - `.claude/hooks/handoff-detector.sh` — HANDOFF 自动检测 hook - `.claude/commands/o.md` — /o 指令（Agent 激活） - `.claude/commands/o-help.md` — /o-help 指令 - `.claude/commands/o-status.md` — /o-status 指令 - `.orchestrix-core/core-config.yaml` — 项目配置 - `.orchestrix-core/scripts/start-orchestrix.sh` — tmux 多窗口启动脚本 - `.orchestrix-core/scripts/ensure-session.sh` — tmux session 懒创建 - `.orchestrix-core/scripts/monitor-agent.sh` — agent 完成检测轮询 - `.gitignore` — Git 忽略规则 ### 下一步 项目骨架已就绪，下一步是进入 **规划阶段**（生成 PRD、架构文档等），完成后再启动自动化开发。 ``` **如果 License Key 为空**，额外提示： ``` ⚠️ 提醒：.mcp.json 中的 License Key 尚未配置。 请编辑 ~/Codes/{project-dir-name}/.mcp.json，将 YOUR_LICENSE_KEY_HERE 替换为你的 License Key。 申请地址：https://orchestrix-mcp.youlidao.ai ``` --- ## Phase 5: 询问是否启动规划 **输出完成信息后，必须向用户询问**： ``` --- 🚀 **是否立即启动项目规划？** 项目简报已生成，规划阶段将按以下流程自动执行（约 10-20 分钟）： | 步骤 | Agent | 任务 | 产出 | 备注 | |------|-------|------|------|------| | 0 | Analyst | 深化项目简报 | `docs/project-brief.md` | **可选** — 增加市场调研、竞品分析 | | 1 | PM | 生成 PRD | `docs/prd/*.md` | 基于已有项目简报 | | 2 | UX Expert | 前端规格 | `docs/front-end-spec*.md` | 仅当项目包含前端 | | 3 | Architect | 架构文档 | `docs/architecture*.md` | | | 4 | PO | 验证 + 分片 | 验证报告 + 分片文件 | | 完成后即可启动 tmux 多窗口自动化开发。 请回复： - **Y** — 立即开始规划（推荐，跳过 Analyst 深化，直接从 PM 开始） - **YA** — 立即开始规划（含 Analyst 深化项目简报） - **N** — 稍后手动规划 ``` ### 用户回复 Y 或 YA：执行规划阶段 如果用户确认，**立即按照以下顺序在当前会话中逐个执行**（无需创建 tmux，因为当前已在 Claude Code 中）： ``` # Step 0: Analyst — 深化项目简报（仅 YA 模式） /o analyst *create-doc project-brief # 等待完成 # Step 1: PM — 生成 PRD（Y 模式从此步开始） /clear → /o pm *create-doc prd # 等待完成 # Step 2: UX Expert — 前端规格（仅当项目有前端需求时） /clear → /o ux-expert *create-doc front-end-spec # 等待完成 # Step 3: Architect — 架构文档 /clear → /o architect *create-doc fullstack-architecture # 等待完成 # Step 4: PO — 验证 + 分片 /clear → /o po *execute-checklist po-master-validation # 等待完成 *shard # 等待完成 ``` **执行规则**： - **Y 模式**：跳过 Step 0，直接从 Step 1 (PM) 开始。项目简报已在 Phase 3 Step 5 生成，PM 可直接使用。 - **YA 模式**：从 Step 0 (Analyst) 开始，先深化项目简报（加市场调研、竞品分析），再继续后续步骤。 - 每个 Agent 切换前必须先 `/clear` - 按顺序逐个执行，上一个完成后再执行下一个 - Step 2 (UX Expert) 仅在项目有前端需求时执行（根据 Phase 1 收集的技术栈判断） - Step 4 的两个命令发给同一个 Agent (PO)，中间不需要 `/clear` **全部完成后输出**： ``` ## ✅ 规划阶段完成 所有规划文档已生成： - `docs/project-brief.md` — 深化后的项目简报 - `docs/prd/*.md` — 产品需求文档 - `docs/front-end-spec*.md` — 前端规格（如适用） - `docs/architecture*.md` — 架构文档 - 分片上下文文件 ### 启动自动化开发 打开新终端执行： ```bash cd ~/Codes/{project-dir-name}/ bash .orchestrix-core/scripts/start-orchestrix.sh ``` 这将启动 tmux 多窗口模式，4 个 Agent (SM/Architect/Dev/QA) 通过 HANDOFF 自动协作开发。 ``` ### 用户回复 N：仅输出手动操作指引 ``` 好的，你可以稍后手动执行规划： 1. 进入项目目录启动 Claude Code： ``` cd ~/Codes/{project-dir-name}/ && claude ``` 2. 按顺序执行规划（每步之间先 /clear）： - `/o analyst` → `*create-doc project-brief` - `/o pm` → `*create-doc prd` - `/o ux-expert` → `*create-doc front-end-spec`（如需前端） - `/o architect` → `*create-doc fullstack-architecture` - `/o po` → `*execute-checklist po-master-validation` → `*shard` 3. 规划完成后启动自动化开发： ``` bash .orchestrix-core/scripts/start-orchestrix.sh ``` ``` --- ## 错误处理 - 如果 `~/Codes/` 目录不存在 → 创建它 - 如果项目目录已存在 → **停止并询问用户**：覆盖、换名、还是取消 - 如果任何文件复制失败 → 报告具体错误，不要静默跳过 - 如果 git init 失败 → 报告错误，但项目文件仍然可用

path: resources/core-config.template.yaml
content: title: Orchestrix Core System
description: >
  The core agent framework for Orchestrix, providing the essential agents,
  workflows, and data for building and managing AI-driven development teams.
markdownExploder: true
project:
  name: '{{PROJECT_NAME}}'
  mode: monolith
  testCommand: '{{TEST_COMMAND}}'
  multi_repo:
    role: implementation
    repository_id: '{{REPO_ID}}'
    product_repo_path: ''
    auto_filter_stories: false
    assigned_stories: []
prd:
  prdFile: docs/prd.md
  prdSharded: false
  prdShardedLocation: docs/prd
architecture:
  architectureFile: docs/architecture.md
  architectureSharded: false
  architectureShardedLocation: docs/architecture
  storyReviewsLocation: docs/architecture/story-reviews
customTechnicalDocuments: null
devLoadAlwaysFiles:
  - docs/architecture/*coding-standards.md
  - docs/architecture/*tech-stack.md
  - docs/architecture/*source-tree.md
devDebugLog: .ai/debug-log.md
devStoryLocation: docs/stories
verticalSliceStrategy: true
dev:
  devLogLocation: docs/dev/logs
  devLogEnabled: true
architect:
  storyReviewsLocation: docs/architecture/story-reviews
qa:
  qaLocation: docs/qa
  qaReviewsLocation: docs/qa/reviews
  evidenceLocation: docs/qa/evidence
  testDesignThresholds:
    simple:
      maxComplexity: 0
      minQualityScore: 8.5
      securitySensitive: false
      tasks: []
    standard:
      maxComplexity: 2
      minQualityScore: 7
      tasks:
        - test-design
    comprehensive:
      minComplexity: 3
      securitySensitive: true
      tasks:
        - test-design
        - risk-profile
slashPrefix: Orchestrix
version: 15.14.0
i18n:
  default_language: en
  supported_languages:
    - en
    - zh
  fallback_to_default: true
  file_pattern: '{name}.{lang}.{ext}'


path: resources/ensure-session.sh
content: #!/bin/bash
# Yuri Session Ensurer — lazy recreation of tmux sessions
# Usage: ensure-session.sh <type> <project_root>
#   type: "planning" or "dev"
set +e

TYPE="$1"
PROJECT_ROOT="$2"

if [ -z "$TYPE" ] || [ -z "$PROJECT_ROOT" ]; then
  echo "Usage: ensure-session.sh <planning|dev> <project_root>"
  exit 1
fi

PROJECT_ROOT=$(cd "$PROJECT_ROOT" && pwd)

if [ "$TYPE" = "planning" ]; then
  PROJECT_NAME=$(basename "$PROJECT_ROOT")
  SESSION="op-$(echo "$PROJECT_NAME" | tr -cd 'a-zA-Z0-9_-')"

  if tmux has-session -t "$SESSION" 2>/dev/null; then
    echo "$SESSION"
    exit 0
  fi

  # Recreate planning session
  tmux new-session -d -s "$SESSION" -n "Plan" -c "$PROJECT_ROOT"
  tmux send-keys -t "$SESSION:0" "cc" C-m

  # Wait for Claude Code to start, auto-accepting trust dialog if it appears.
  # The trust prompt shows "Do you trust" and blocks until user responds.
  # We poll the pane every 2s and send Enter if we detect trust-related text.
  for i in $(seq 1 6); do
    sleep 2
    PANE_TEXT=$(tmux capture-pane -t "$SESSION:0" -p -S -10 2>/dev/null || true)
    if echo "$PANE_TEXT" | grep -qi "trust this folder\|safety check"; then
      tmux send-keys -t "$SESSION:0" Enter
      sleep 2
      break
    fi
    # Already past trust dialog (❯ prompt visible)
    if echo "$PANE_TEXT" | grep -q "❯"; then
      break
    fi
  done

  echo "$SESSION"

elif [ "$TYPE" = "dev" ]; then
  CONFIG="$PROJECT_ROOT/.orchestrix-core/core-config.yaml"
  REPO_ID=""

  if [ -f "$CONFIG" ]; then
    REPO_ID=$(grep -E '^\s*repository_id:' "$CONFIG" 2>/dev/null | head -1 | sed "s/.*repository_id:[[:space:]]*//" | tr -d "'" | tr -d '"' | tr -d ' ')
  fi

  if [ -z "$REPO_ID" ]; then
    REPO_ID=$(basename "$PROJECT_ROOT")
  fi

  REPO_ID=$(echo "$REPO_ID" | tr -cd 'a-zA-Z0-9_-')
  SESSION="orchestrix-${REPO_ID}"

  if tmux has-session -t "$SESSION" 2>/dev/null; then
    echo "$SESSION"
    exit 0
  fi

  # Recreate dev session
  cd "$PROJECT_ROOT"
  bash .orchestrix-core/scripts/start-orchestrix.sh &
  sleep 30
  echo "$SESSION"

else
  echo "Unknown type: $TYPE. Use 'planning' or 'dev'."
  exit 1
fi


path: resources/handoff-detector.sh
content: #!/bin/bash
# Orchestrix HANDOFF Detector - tmux Automation Hook (MCP Version)
# Triggers on Claude Code Stop event, detects HANDOFF and routes to target agent
#
# Design principles:
# - NO dependency on environment variables (robust)
# - Scans ALL tmux windows to find HANDOFF message
# - Uses hash-based deduplication to prevent re-processing
# - Background process handles cleanup and lock release
#
# Pro/Team Feature: This script is only available for Pro and Team subscribers.

set +e  # Don't exit on errors

# ============================================
# Configuration
# ============================================
# Try to get session from env, otherwise detect from tmux context
SESSION_NAME="${ORCHESTRIX_SESSION:-}"

# Agent mappings
get_agent_name() {
    case "$1" in
        0) echo "architect" ;;
        1) echo "sm" ;;
        2) echo "dev" ;;
        3) echo "qa" ;;
        *) echo "" ;;
    esac
}

get_window_num() {
    case "$1" in
        architect) echo "0" ;;
        sm) echo "1" ;;
        dev) echo "2" ;;
        qa) echo "3" ;;
        *) echo "" ;;
    esac
}

# MCP version: use /o {agent} format
get_agent_command() {
    case "$1" in
        architect) echo "/o architect" ;;
        sm) echo "/o sm" ;;
        dev) echo "/o dev" ;;
        qa) echo "/o qa" ;;
        *) echo "" ;;
    esac
}

# Infer target agent from command (for simplified format like "*develop-story 10.4")
get_target_from_command() {
    local cmd="$1"
    case "$cmd" in
        develop-story|apply-qa-fixes|quick-develop)
            echo "dev" ;;
        review|quick-verify|test-design|finalize-commit)
            echo "qa" ;;
        draft|revise-story|revise|apply-proposal|create-next-story)
            echo "sm" ;;
        review-escalation|resolve-change)
            echo "architect" ;;
        *)
            echo "" ;;
    esac
}

# ============================================
# Find Orchestrix Session (before any logging)
# ============================================

# Priority 1: Explicit env var
# Priority 2: Detect current tmux session (if inside tmux and it's an orchestrix session)
# Priority 3: Scan all tmux sessions for orchestrix prefix
if [[ -z "$SESSION_NAME" && -n "$TMUX" ]]; then
    CURRENT_SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null)
    if [[ "$CURRENT_SESSION" == orchestrix* ]]; then
        SESSION_NAME="$CURRENT_SESSION"
    fi
fi

if [[ -z "$SESSION_NAME" ]]; then
    SESSION_NAME=$(tmux list-sessions -F '#{session_name}' 2>/dev/null | grep -E '^orchestrix' | head -1)
fi

if [[ -z "$SESSION_NAME" ]]; then
    exit 0
fi

if ! tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
    exit 0
fi

# Session-specific log file (all logging starts AFTER session detection)
LOG_FILE="/tmp/${SESSION_NAME}-handoff.log"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"; }

log "========== Hook triggered =========="
log "Session: $SESSION_NAME"

# ============================================
# Scan All Windows for HANDOFF
# ============================================
PROCESSED_FILE="/tmp/${SESSION_NAME}-processed.txt"
touch "$PROCESSED_FILE"

SOURCE_WIN=""
TARGET=""
CMD=""
HANDOFF_HASH=""

for win in 0 1 2 3; do
    OUTPUT=$(tmux capture-pane -t "$SESSION_NAME:$win" -p -S - 2>/dev/null)
    [[ -z "$OUTPUT" ]] && continue

    # Pattern 1: Standard HANDOFF format (🎯 HANDOFF TO agent: *command args)
    LINE=$(echo "$OUTPUT" | grep -E '🎯.*HANDOFF.*TO' | tail -1)
    if [[ -n "$LINE" ]]; then
        # Calculate hash to avoid re-processing
        HASH=$(echo "$LINE" | md5 2>/dev/null || echo "$LINE" | md5sum 2>/dev/null | cut -d' ' -f1)

        # Skip if already processed
        if grep -q "$HASH" "$PROCESSED_FILE" 2>/dev/null; then
            continue
        fi

        # Parse HANDOFF message
        if [[ "$LINE" =~ HANDOFF[[:space:]]+TO[[:space:]]+([a-zA-Z0-9_-]+):[[:space:]]*\*?([a-z0-9-]+)([[:space:]]+([0-9]+\.[0-9]+))? ]]; then
            SOURCE_WIN=$win
            TARGET=$(echo "${BASH_REMATCH[1]}" | tr '[:upper:]' '[:lower:]')
            CMD="*${BASH_REMATCH[2]}${BASH_REMATCH[4]:+ ${BASH_REMATCH[4]}}"
            HANDOFF_HASH=$HASH
            log "Found HANDOFF in window $win: $LINE"
            break
        fi
    fi

    # Pattern 2: Simplified format - more flexible detection
    # Handles: "*develop-story 10.4", "*draft", "* review 10.4", etc.
    if [[ -z "$TARGET" ]]; then
        # Search last 30 lines for command patterns (more tolerant)
        LAST_LINES=$(echo "$OUTPUT" | tail -30)

        # Try multiple patterns in order of specificity
        # Pattern 2a: *command story_id (e.g., "*develop-story 10.4")
        SIMPLE_LINE=$(echo "$LAST_LINES" | grep -E '\*[a-z]+-?[a-z-]*\s+[0-9]+\.[0-9]+' | tail -1)

        if [[ -z "$SIMPLE_LINE" ]]; then
            # Pattern 2b: *command without story_id (e.g., "*draft")
            SIMPLE_LINE=$(echo "$LAST_LINES" | grep -E '^\s*\*[a-z]+-?[a-z-]*\s*$' | tail -1)
        fi

        if [[ -n "$SIMPLE_LINE" ]]; then
            # Extract command and optional story_id
            # Handle: "*develop-story 10.4", "* review 10.4", "*draft"
            if [[ "$SIMPLE_LINE" =~ \*[[:space:]]*([a-z][a-z-]*)[[:space:]]*([0-9]+\.[0-9]+)? ]]; then
                simple_cmd="${BASH_REMATCH[1]}"
                story_id="${BASH_REMATCH[2]}"
                inferred_target=$(get_target_from_command "$simple_cmd")

                if [[ -n "$inferred_target" ]]; then
                    # Calculate hash
                    HASH=$(echo "$SIMPLE_LINE" | md5 2>/dev/null || echo "$SIMPLE_LINE" | md5sum 2>/dev/null | cut -d' ' -f1)

                    # Skip if already processed
                    if grep -q "$HASH" "$PROCESSED_FILE" 2>/dev/null; then
                        continue
                    fi

                    SOURCE_WIN=$win
                    TARGET=$inferred_target
                    if [[ -n "$story_id" ]]; then
                        CMD="*${simple_cmd} ${story_id}"
                    else
                        CMD="*${simple_cmd}"
                    fi
                    HANDOFF_HASH=$HASH
                    log "[SIMPLE] Found in window $win: '$SIMPLE_LINE' -> $TARGET: $CMD"
                    break
                fi
            fi
        fi
    fi
done

# No HANDOFF found - try fallback from pending-handoff file
if [[ -z "$TARGET" || -z "$CMD" ]]; then
    log "No HANDOFF in terminal output, checking fallback file..."

    # ============================================
    # Fallback: Check pending-handoff.json
    # ============================================
    # Find project root (look for .orchestrix-core directory)
    FALLBACK_FILE=""
    for win in 0 1 2 3; do
        # Get the pane's current directory
        PANE_DIR=$(tmux display-message -t "$SESSION_NAME:$win" -p '#{pane_current_path}' 2>/dev/null)
        if [[ -n "$PANE_DIR" && -f "$PANE_DIR/.orchestrix-core/runtime/pending-handoff.json" ]]; then
            FALLBACK_FILE="$PANE_DIR/.orchestrix-core/runtime/pending-handoff.json"
            SOURCE_WIN=$win
            break
        fi
    done

    if [[ -z "$FALLBACK_FILE" ]]; then
        log "No pending-handoff.json found"
        exit 0
    fi

    log "Found fallback file: $FALLBACK_FILE"

    # Read and parse the JSON file
    if command -v jq &>/dev/null; then
        # Use jq if available
        STATUS=$(jq -r '.status // "unknown"' "$FALLBACK_FILE" 2>/dev/null)
        if [[ "$STATUS" != "pending" ]]; then
            log "Fallback file status is '$STATUS', not 'pending'. Skipping."
            exit 0
        fi

        TARGET=$(jq -r '.target_agent // ""' "$FALLBACK_FILE" 2>/dev/null)
        CMD=$(jq -r '.command // ""' "$FALLBACK_FILE" 2>/dev/null)
        STORY_ID=$(jq -r '.story_id // ""' "$FALLBACK_FILE" 2>/dev/null)
        SOURCE_AGENT=$(jq -r '.source_agent // ""' "$FALLBACK_FILE" 2>/dev/null)
    else
        # Fallback to grep/sed parsing
        STATUS=$(grep -o '"status"[[:space:]]*:[[:space:]]*"[^"]*"' "$FALLBACK_FILE" | sed 's/.*"\([^"]*\)"$/\1/')
        if [[ "$STATUS" != "pending" ]]; then
            log "Fallback file status is '$STATUS', not 'pending'. Skipping."
            exit 0
        fi

        TARGET=$(grep -o '"target_agent"[[:space:]]*:[[:space:]]*"[^"]*"' "$FALLBACK_FILE" | sed 's/.*"\([^"]*\)"$/\1/')
        CMD=$(grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' "$FALLBACK_FILE" | sed 's/.*"\([^"]*\)"$/\1/')
        STORY_ID=$(grep -o '"story_id"[[:space:]]*:[[:space:]]*"[^"]*"' "$FALLBACK_FILE" | sed 's/.*"\([^"]*\)"$/\1/')
        SOURCE_AGENT=$(grep -o '"source_agent"[[:space:]]*:[[:space:]]*"[^"]*"' "$FALLBACK_FILE" | sed 's/.*"\([^"]*\)"$/\1/')
    fi

    if [[ -z "$TARGET" || -z "$CMD" ]]; then
        log "Invalid fallback file content"
        exit 0
    fi

    # Create a hash to prevent re-processing
    # Include STORY_ID to differentiate handoffs for different stories with same command
    HANDOFF_HASH=$(echo "fallback-$SOURCE_AGENT-$TARGET-$CMD-$STORY_ID" | md5 2>/dev/null || echo "fallback-$SOURCE_AGENT-$TARGET-$CMD-$STORY_ID" | md5sum 2>/dev/null | cut -d' ' -f1)

    # Skip if already processed
    if grep -q "$HANDOFF_HASH" "$PROCESSED_FILE" 2>/dev/null; then
        log "Fallback handoff already processed"
        exit 0
    fi

    # Get SOURCE_WIN from SOURCE_AGENT (override the window where file was found)
    SOURCE_WIN=$(get_window_num "$SOURCE_AGENT")

    log "[FALLBACK] Recovered handoff from file: $SOURCE_AGENT (win $SOURCE_WIN) -> $TARGET: $CMD"

    # Mark the fallback file as completed
    if command -v jq &>/dev/null; then
        jq --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '.status = "completed_by_fallback" | .completed_at = $ts' "$FALLBACK_FILE" > "$FALLBACK_FILE.tmp.$$" 2>/dev/null && mv "$FALLBACK_FILE.tmp.$$" "$FALLBACK_FILE"
    else
        sed -i.bak 's/"status"[[:space:]]*:[[:space:]]*"pending"/"status": "completed_by_fallback"/' "$FALLBACK_FILE" 2>/dev/null
        rm -f "$FALLBACK_FILE.bak" 2>/dev/null
    fi
fi

# Mark as processed
echo "$HANDOFF_HASH" >> "$PROCESSED_FILE"

# Keep processed file small (last 100 entries)
# Use PID in tmp filename to avoid race with concurrent hook instances or background processes
tail -100 "$PROCESSED_FILE" > "$PROCESSED_FILE.tmp.$$" 2>/dev/null && mv "$PROCESSED_FILE.tmp.$$" "$PROCESSED_FILE"

# Get source agent name (only if not already set by fallback)
if [[ -z "$SOURCE_AGENT" ]]; then
    SOURCE_AGENT=$(get_agent_name "$SOURCE_WIN")
fi
TARGET_WIN=$(get_window_num "$TARGET")

# Validate
if [[ -z "$TARGET_WIN" ]]; then
    log "ERROR: Unknown target agent '$TARGET'"
    exit 0
fi

if [[ "$TARGET_WIN" == "$SOURCE_WIN" ]]; then
    log "ERROR: Source and target are same window"
    exit 0
fi

log "HANDOFF: $SOURCE_AGENT (win $SOURCE_WIN) -> $TARGET (win $TARGET_WIN)"
log "Command: $CMD"

# ============================================
# Atomic Lock
# ============================================
LOCK="/tmp/${SESSION_NAME}-${SOURCE_WIN}.lock"
LOCK_TIMEOUT=60

if ! mkdir "$LOCK" 2>/dev/null; then
    if [[ -f "$LOCK/ts" ]]; then
        ts=$(cat "$LOCK/ts" 2>/dev/null || echo 0)
        now=$(date +%s)
        age=$((now - ts))
        if [[ $age -lt $LOCK_TIMEOUT ]]; then
            log "SKIP: Window $SOURCE_WIN locked (${age}s ago)"
            exit 0
        fi
        log "Stale lock (${age}s), cleaning"
    fi
    rm -rf "$LOCK" 2>/dev/null
    mkdir "$LOCK" 2>/dev/null || { log "SKIP: lock race"; exit 0; }
fi
date +%s > "$LOCK/ts"

# ============================================
# Send to Target
# ============================================
log "Sending '$CMD' to $TARGET (window $TARGET_WIN)..."

if tmux send-keys -t "$SESSION_NAME:$TARGET_WIN" "$CMD" 2>/dev/null; then
    sleep 0.5
    tmux send-keys -t "$SESSION_NAME:$TARGET_WIN" Enter
    log "SUCCESS: Command sent to $TARGET"
else
    log "ERROR: Failed to send command"
    rm -rf "$LOCK"
    exit 0
fi

# ============================================
# Background: Clear & Reload Source Agent
# ============================================
RELOAD_CMD=$(get_agent_command "$SOURCE_AGENT")

(
    log "[BG] Starting cleanup for $SOURCE_AGENT (window $SOURCE_WIN)"
    sleep 2

    # Clear
    tmux send-keys -t "$SESSION_NAME:$SOURCE_WIN" "/clear" 2>/dev/null
    sleep 0.5
    tmux send-keys -t "$SESSION_NAME:$SOURCE_WIN" Enter
    log "[BG] /clear sent to $SOURCE_AGENT"

    sleep 5

    # Reload
    if [[ -n "$RELOAD_CMD" ]]; then
        tmux send-keys -t "$SESSION_NAME:$SOURCE_WIN" "$RELOAD_CMD" 2>/dev/null
        sleep 0.5
        tmux send-keys -t "$SESSION_NAME:$SOURCE_WIN" Enter
        log "[BG] Reload sent: $RELOAD_CMD"
        sleep 15
    fi

    # Remove hash from processed file to allow future same-message HANDOFF
    # This fixes the issue where repeated identical messages (e.g., "*draft") are skipped
    if [[ -n "$HANDOFF_HASH" && -f "$PROCESSED_FILE" ]]; then
        grep -v "^${HANDOFF_HASH}$" "$PROCESSED_FILE" > "$PROCESSED_FILE.tmp.$$" 2>/dev/null && mv -f "$PROCESSED_FILE.tmp.$$" "$PROCESSED_FILE"
        log "[BG] Hash removed from processed file: $HANDOFF_HASH"
    fi

    # Release lock
    rm -rf "$LOCK"
    log "[BG] Cleanup complete, lock released"
) >> "$LOG_FILE" 2>&1 &

log "Background process started (PID $!)"
log "========== Hook complete =========="
exit 0


path: resources/monitor-agent.sh
content: #!/bin/bash
# Yuri Agent Completion Monitor
# Polls a tmux pane for agent completion signals.
# Usage: monitor-agent.sh <session> <window> [expected_file] [timeout_min] [poll_sec]
set +e

SESSION="$1"
WINDOW="$2"
EXPECTED_FILE="${3:-}"
TIMEOUT_MIN="${4:-30}"
POLL_SEC="${5:-30}"

MAX_POLLS=$((TIMEOUT_MIN * 60 / POLL_SEC))
STABLE_COUNT=0
LAST_HASH=""

for i in $(seq 1 $MAX_POLLS); do
  OUTPUT=$(tmux capture-pane -t "$SESSION:$WINDOW" -p -S -200 2>/dev/null)
  LAST_LINES=$(echo "$OUTPUT" | tail -10)

  # Priority 1: Claude Code completion message ("Baked for 31s", "Worked for 2m", etc.)
  if echo "$LAST_LINES" | grep -qE '[A-Z][a-z]*ed for [0-9]'; then
    if [ -z "$EXPECTED_FILE" ] || [ -f "$EXPECTED_FILE" ]; then
      echo "COMPLETE"
      exit 0
    fi
  fi

  # Priority 2: TUI idle indicator
  if echo "$LAST_LINES" | grep -q '○'; then
    if [ -z "$EXPECTED_FILE" ] || [ -f "$EXPECTED_FILE" ]; then
      echo "IDLE"
      exit 0
    fi
  fi

  # Priority 3: Handle approval prompt — auto-approve
  if echo "$LAST_LINES" | grep -q '◐'; then
    tmux send-keys -t "$SESSION:$WINDOW" "y" Enter
    sleep 2
    continue
  fi

  # Priority 4: Content stability fallback
  HASH=$(echo "$OUTPUT" | md5 2>/dev/null || echo "$OUTPUT" | md5sum 2>/dev/null | cut -d' ' -f1)
  if [ "$HASH" = "$LAST_HASH" ]; then
    STABLE_COUNT=$((STABLE_COUNT + 1))
    if [ $STABLE_COUNT -ge 3 ]; then
      echo "STABLE_IDLE"
      exit 0
    fi
  else
    STABLE_COUNT=0
  fi
  LAST_HASH="$HASH"

  sleep "$POLL_SEC"
done

echo "TIMEOUT"
exit 1


path: resources/o-help.md
content: ---
description: Show Orchestrix help and available agents
---

# Orchestrix Help

Please call the Orchestrix MCP Server's `list-agents` tool to display all available agents.

Then display the following usage guide:

## Usage

```
/o [agent_id]     Activate an agent (e.g., /o dev, /o sm)
/o-help           Show this help message
/o-status         Show current Orchestrix status
```

## Quick Start

1. Use `/o dev` to activate the Developer agent for coding tasks
2. Use `/o sm` to activate the Scrum Master for story management
3. Use `/o qa` to activate QA Engineer for testing


path: resources/o-status.md
content: ---
description: Show Orchestrix server status
---

# Orchestrix Status

Please check and display the current Orchestrix status:

## Status Check Items

1. **MCP Server Connection**: Check if Orchestrix MCP Server is connected
2. **Available Agents**: Call `list-agents` tool to verify agents are loaded
3. **License Status**: Display current license tier (if applicable)

## Expected Output

Display the status in this format:

```
Orchestrix Status
=================
Server: Connected / Disconnected
Agents: [count] agents available
License: [tier] mode

Available Commands:
  /o [agent]  - Activate agent
  /o-help     - Show help
  /o-status   - This status
```

## Troubleshooting

If the server is not connected:
1. Check `.mcp.json` configuration
2. Verify the MCP server process is running
3. Check server logs for errors


path: resources/o.md
content: ---
description: Activate Orchestrix Agent (e.g., /o dev, /o sm --lang=zh)
---

# Orchestrix Agent Activation

## Available Agents

| ID | Agent | Description |
|----|-------|-------------|
| `dev` | Full Stack Developer | implementation, debugging, refactoring |
| `sm` | Scrum Master | story creation, epic management, agile guidance |
| `qa` | QA Engineer | E2E testing, quality verification |
| `architect` | Solution Architect | system design, tech selection, API design |
| `pm` | Product Manager | PRDs, product strategy, roadmap planning |
| `po` | Product Owner | backlog management, story refinement |
| `analyst` | Business Analyst | market research, competitive analysis |
| `ux-expert` | UX Expert | UI/UX design, wireframes, prototypes |
| `orchestrix-orchestrator` | Workflow Coordinator | multi-agent tasks |
| `orchestrix-master` | Master Agent | one-off tasks across domains |
| `decision-evaluator` | Decision Evaluator | execute decision rules |

## Language Support

Use `--lang=xx` to load agent content in a specific language:
- `--lang=en` - English (default)
- `--lang=zh` - Chinese / 中文

Examples:
- `/o dev` - Load Developer agent in English (default)
- `/o dev --lang=zh` - Load Developer agent in Chinese
- `/o sm --lang=zh` - Load Scrum Master agent in Chinese

## Action Required

**FIRST**: Parse `$ARGUMENTS` to extract agent ID and language option.

Arguments format: `[agent_id] [--lang=xx]`

Parsing rules:
1. If `$ARGUMENTS` is empty or blank → Show agent table, ask user to select
2. If `$ARGUMENTS` contains `--lang=xx` → Extract language code (en or zh)
3. Extract agent ID (everything before `--lang` or the entire argument if no `--lang`)

If `$ARGUMENTS` is empty or blank:
- Show the table above directly
- Ask user to select an agent
- **DO NOT** call ReadMcpResourceTool with empty arguments

If agent ID is provided, proceed to load the agent:

## Step 1: Read Agent Configuration

**CRITICAL**: You MUST use `ReadMcpResourceTool` (NOT prompts!) with the EXACT parameters below.

`ReadMcpResourceTool` only accepts `server` and `uri` parameters. Language MUST be encoded in the URI as a query parameter.

**Without language** (default English):
- server: `orchestrix`
- uri: `orchestrix://agents/{agent_id}.yaml`

**With language** (e.g., `--lang=zh`):
- server: `orchestrix`
- uri: `orchestrix://agents/{agent_id}.yaml?lang=zh`

### Examples

For `/o pm`:
```
ReadMcpResourceTool(server="orchestrix", uri="orchestrix://agents/pm.yaml")
```

For `/o pm --lang=zh`:
```
ReadMcpResourceTool(server="orchestrix", uri="orchestrix://agents/pm.yaml?lang=zh")
```

**DO NOT** use `orchestrix://prompts/` - agents are exposed as **resources**, not prompts!
**DO NOT** pass `lang` as a separate parameter - it MUST be in the URI query string.

## Step 2: After Loading Agent

1. Adopt the persona defined in the `agent` section completely
2. Follow `activation_instructions` exactly
3. Display greeting with agent name/role
4. Show the numbered command list from `commands.help.output_format`
5. Wait for user selection


path: resources/settings.local.json
content: {
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'cd \"$(git rev-parse --show-toplevel)\" && .claude/hooks/handoff-detector.sh'"
          }
        ]
      }
    ]
  }
}


path: resources/start-orchestrix.sh
content: #!/bin/bash
# Orchestrix tmux Multi-Agent Session Starter (MCP Version)
# Purpose: Create 4 separate windows, each running a Claude Code agent
# Supports multi-repo: Each repo gets its own tmux session based on repository_id
#
# Pro/Team Feature: This script is only available for Pro and Team subscribers.

set -e

# Dynamically get project root directory (where .orchestrix-core is located)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORK_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"

echo "Working directory: $WORK_DIR"

# ============================================
# Dynamic Session Naming (Multi-Repo Support)
# ============================================

# Try to read repository_id from core-config.yaml
CONFIG_FILE="$WORK_DIR/.orchestrix-core/core-config.yaml"
REPO_ID=""

if [ -f "$CONFIG_FILE" ]; then
    # Extract repository_id using grep and sed (POSIX compatible)
    REPO_ID=$(grep -E "^\s*repository_id:" "$CONFIG_FILE" 2>/dev/null | head -1 | sed "s/.*repository_id:[[:space:]]*['\"]*//" | sed "s/['\"].*//")

    # Clean up: remove quotes and whitespace
    REPO_ID=$(echo "$REPO_ID" | tr -d "'" | tr -d '"' | tr -d ' ')
fi

# Fallback: use directory name if repository_id is empty
if [ -z "$REPO_ID" ]; then
    REPO_ID=$(basename "$WORK_DIR")
    echo "Warning: No repository_id in config, using directory name: $REPO_ID"
fi

# Sanitize REPO_ID for tmux session name (alphanumeric, dash, underscore only)
REPO_ID=$(echo "$REPO_ID" | tr -cd 'a-zA-Z0-9_-')

# Sanitized REPO_ID must not be empty
if [ -z "$REPO_ID" ]; then
    REPO_ID="default"
    echo "⚠️  REPO_ID is empty after sanitization, using fallback: $REPO_ID"
fi

# Generate dynamic session name and log file
SESSION_NAME="orchestrix-${REPO_ID}"
# IMPORTANT: Must match handoff-detector.sh pattern: /tmp/${SESSION_NAME}-handoff.log
LOG_FILE="/tmp/${SESSION_NAME}-handoff.log"

echo "🏷️  Repository ID: $REPO_ID"
echo "📺 tmux Session: $SESSION_NAME"
echo "📝 Log file: $LOG_FILE"

# Check if tmux is installed
if ! command -v tmux &> /dev/null; then
echo "❌ Error: tmux is not installed"
    echo "Please run: brew install tmux"
    exit 1
fi

# Check if cc command is available
if ! command -v cc &> /dev/null; then
    echo "❌ Error: cc command not available"
    echo "Please ensure Claude Code alias is configured: alias cc='claude'"
    exit 1
fi

# If session already exists, kill it first
if tmux has-session -t "$SESSION_NAME" 2>/dev/null; then
    echo "⚠️  Session '$SESSION_NAME' already exists, closing..."
    tmux kill-session -t "$SESSION_NAME"
fi

# Create new session with first window (Architect)
echo "🚀 Creating tmux session: $SESSION_NAME"
tmux new-session -d -s "$SESSION_NAME" -n "Arch" -c "$WORK_DIR"

# ============================================
# Inject handoff-detector hook into target project
# ============================================
# CRITICAL: The tmux agents run Claude Code in WORK_DIR, so their Stop hooks
# come from WORK_DIR/.claude/settings*.json, NOT from the orchestrix-mcp-server project.
# We must inject the handoff-detector hook into the target project's settings.local.json
# so that agent Stop events trigger local handoff detection.
SETTINGS_LOCAL="$WORK_DIR/.claude/settings.local.json"
HANDOFF_HOOK_CMD="bash -c 'cd \"\$(git rev-parse --show-toplevel)\" && .orchestrix-core/scripts/handoff-detector.sh'"

# Ensure .claude directory exists
mkdir -p "$WORK_DIR/.claude"

# Check if settings.local.json already has handoff-detector configured
if [ -f "$SETTINGS_LOCAL" ]; then
    if grep -q "handoff-detector" "$SETTINGS_LOCAL" 2>/dev/null; then
        echo "✅ Handoff hook already configured in $SETTINGS_LOCAL"
    else
        echo "⚠️  settings.local.json exists but missing handoff hook, injecting..."
        # Use jq to merge if available, otherwise create new
        if command -v jq &>/dev/null; then
            EXISTING=$(cat "$SETTINGS_LOCAL")
            echo "$EXISTING" | jq --arg cmd "$HANDOFF_HOOK_CMD" \
                '.hooks.Stop = (.hooks.Stop // []) + [{"hooks": [{"type": "command", "command": $cmd}]}]' \
                > "$SETTINGS_LOCAL.tmp" && mv "$SETTINGS_LOCAL.tmp" "$SETTINGS_LOCAL"
            echo "✅ Handoff hook injected into existing settings.local.json"
        else
            echo "⚠️  jq not found, cannot safely merge. Please add handoff hook manually."
        fi
    fi
else
    # Create new settings.local.json with handoff hook
    cat > "$SETTINGS_LOCAL" << SETTINGS_EOF
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$HANDOFF_HOOK_CMD"
          }
        ]
      }
    ]
  }
}
SETTINGS_EOF
    echo "✅ Created $SETTINGS_LOCAL with handoff hook"
fi

# ============================================
# Ensure handoff-detector.sh exists in target project
# ============================================
HANDOFF_SCRIPT="$WORK_DIR/.orchestrix-core/scripts/handoff-detector.sh"
if [ ! -f "$HANDOFF_SCRIPT" ]; then
    SCRIPT_DIR_SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    if [ -f "$SCRIPT_DIR_SRC/handoff-detector.sh" ]; then
        mkdir -p "$(dirname "$HANDOFF_SCRIPT")"
        cp "$SCRIPT_DIR_SRC/handoff-detector.sh" "$HANDOFF_SCRIPT"
        chmod +x "$HANDOFF_SCRIPT"
        echo "✅ Copied handoff-detector.sh to target project"
    else
        echo "⚠️  handoff-detector.sh not found in setup directory"
    fi
fi

# ============================================
# Create tmux automation marker
# ============================================
# This file signals to agents that they're running in tmux automation mode
# Agents check for this file to decide whether to register pending-handoff fallback
RUNTIME_DIR="$WORK_DIR/.orchestrix-core/runtime"
TMUX_MARKER="$RUNTIME_DIR/tmux-automation-active"

mkdir -p "$RUNTIME_DIR"
echo "{\"session\": \"$SESSION_NAME\", \"started_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" > "$TMUX_MARKER"
echo "📌 tmux automation marker: $TMUX_MARKER"

# Cleanup marker file on script exit (detach, kill, Ctrl+C)
cleanup() {
    rm -f "$TMUX_MARKER"
    echo "🧹 Cleaned up tmux automation marker"
}
trap cleanup EXIT INT TERM

# Configure status bar for better display
tmux set-option -t "$SESSION_NAME" status-left-length 20
tmux set-option -t "$SESSION_NAME" status-right-length 60
tmux set-option -t "$SESSION_NAME" window-status-format "#I:#W"
tmux set-option -t "$SESSION_NAME" window-status-current-format "#I:#W*"

# Set environment variables for Architect window
# ORCHESTRIX_SESSION and ORCHESTRIX_LOG are used by handoff-detector.sh
tmux send-keys -t "$SESSION_NAME:0" "export AGENT_ID=architect" C-m
tmux send-keys -t "$SESSION_NAME:0" "export ORCHESTRIX_SESSION=$SESSION_NAME" C-m
tmux send-keys -t "$SESSION_NAME:0" "export ORCHESTRIX_LOG=$LOG_FILE" C-m
tmux send-keys -t "$SESSION_NAME:0" "clear" C-m
tmux send-keys -t "$SESSION_NAME:0" "echo '╔════════════════════════════════════════╗'" C-m
tmux send-keys -t "$SESSION_NAME:0" "echo '║  🏛️  Architect Agent (Window 0)      ║'" C-m
tmux send-keys -t "$SESSION_NAME:0" "echo '╚════════════════════════════════════════╝'" C-m
tmux send-keys -t "$SESSION_NAME:0" "echo ''" C-m

# Create window 1 - SM
tmux new-window -t "$SESSION_NAME:1" -n "SM" -c "$WORK_DIR"
tmux send-keys -t "$SESSION_NAME:1" "export AGENT_ID=sm" C-m
tmux send-keys -t "$SESSION_NAME:1" "export ORCHESTRIX_SESSION=$SESSION_NAME" C-m
tmux send-keys -t "$SESSION_NAME:1" "export ORCHESTRIX_LOG=$LOG_FILE" C-m
tmux send-keys -t "$SESSION_NAME:1" "clear" C-m
tmux send-keys -t "$SESSION_NAME:1" "echo '╔════════════════════════════════════════╗'" C-m
tmux send-keys -t "$SESSION_NAME:1" "echo '║  📋 SM Agent (Window 1)               ║'" C-m
tmux send-keys -t "$SESSION_NAME:1" "echo '╚════════════════════════════════════════╝'" C-m
tmux send-keys -t "$SESSION_NAME:1" "echo ''" C-m

# Create window 2 - Dev
tmux new-window -t "$SESSION_NAME:2" -n "Dev" -c "$WORK_DIR"
tmux send-keys -t "$SESSION_NAME:2" "export AGENT_ID=dev" C-m
tmux send-keys -t "$SESSION_NAME:2" "export ORCHESTRIX_SESSION=$SESSION_NAME" C-m
tmux send-keys -t "$SESSION_NAME:2" "export ORCHESTRIX_LOG=$LOG_FILE" C-m
tmux send-keys -t "$SESSION_NAME:2" "clear" C-m
tmux send-keys -t "$SESSION_NAME:2" "echo '╔════════════════════════════════════════╗'" C-m
tmux send-keys -t "$SESSION_NAME:2" "echo '║  💻 Dev Agent (Window 2)              ║'" C-m
tmux send-keys -t "$SESSION_NAME:2" "echo '╚════════════════════════════════════════╝'" C-m
tmux send-keys -t "$SESSION_NAME:2" "echo ''" C-m

# Create window 3 - QA
tmux new-window -t "$SESSION_NAME:3" -n "QA" -c "$WORK_DIR"
tmux send-keys -t "$SESSION_NAME:3" "export AGENT_ID=qa" C-m
tmux send-keys -t "$SESSION_NAME:3" "export ORCHESTRIX_SESSION=$SESSION_NAME" C-m
tmux send-keys -t "$SESSION_NAME:3" "export ORCHESTRIX_LOG=$LOG_FILE" C-m
tmux send-keys -t "$SESSION_NAME:3" "clear" C-m
tmux send-keys -t "$SESSION_NAME:3" "echo '╔════════════════════════════════════════╗'" C-m
tmux send-keys -t "$SESSION_NAME:3" "echo '║  🧪 QA Agent (Window 3)               ║'" C-m
tmux send-keys -t "$SESSION_NAME:3" "echo '╚════════════════════════════════════════╝'" C-m
tmux send-keys -t "$SESSION_NAME:3" "echo ''" C-m

# ============================================
# Configuration
# ============================================

# Wait time for Claude Code to start (seconds)
CC_STARTUP_WAIT=12

# Wait time between command text and Enter key (seconds)
COMMAND_ENTER_DELAY=1

# Wait time between activating agents (seconds)
AGENT_ACTIVATION_DELAY=2

# Wait time for agents to fully load before starting workflow (seconds)
AGENT_LOAD_WAIT=15

# Auto-start workflow command (sent to SM window)
AUTO_START_COMMAND="1"

# Agent activation commands (MCP version uses /o command)
declare -a AGENT_COMMANDS=(
    "/o architect"   # Window 0 - Architect
    "/o sm"          # Window 1 - SM
    "/o dev"         # Window 2 - Dev
    "/o qa"          # Window 3 - QA
)

declare -a AGENT_NAMES=(
    "Architect"
    "SM"
    "Dev"
    "QA"
)

# ============================================
# Function: Send command with delay before Enter
# Usage: send_command_with_delay <window> <command>
# ============================================
send_command_with_delay() {
    local window="$1"
    local command="$2"

    # Send command text first
    tmux send-keys -t "$SESSION_NAME:$window" "$command"

    # Wait before sending Enter (prevents race condition)
    sleep "$COMMAND_ENTER_DELAY"

    # Send Enter key
    tmux send-keys -t "$SESSION_NAME:$window" "Enter"
}

# ============================================
# Start Claude Code in all windows
# ============================================

echo "🤖 Starting Claude Code in all windows..."

# Window 0 - Architect
tmux send-keys -t "$SESSION_NAME:0" "cc" C-m

# Window 1 - SM
tmux send-keys -t "$SESSION_NAME:1" "cc" C-m

# Window 2 - Dev
tmux send-keys -t "$SESSION_NAME:2" "cc" C-m

# Window 3 - QA
tmux send-keys -t "$SESSION_NAME:3" "cc" C-m

# ============================================
# Wait for Claude Code to fully initialize
# ============================================

echo ""
echo "⏳ Waiting ${CC_STARTUP_WAIT}s for Claude Code to start..."
echo ""

# Countdown + auto-accept trust dialogs in all windows.
# Claude Code shows "Do you trust" when entering a new project directory.
# We detect it and send Enter to accept. Checked every 2 seconds.
TRUST_HANDLED=""
for i in $(seq "$CC_STARTUP_WAIT" -1 1); do
    printf "\r   %2d seconds remaining..." "$i"
    sleep 1

    # Check every 2 seconds for trust dialog in any window
    if [ $((i % 2)) -eq 0 ] && [ -z "$TRUST_HANDLED" ]; then
        for w in 0 1 2 3; do
            PANE=$(tmux capture-pane -t "$SESSION_NAME:$w" -p -S -10 2>/dev/null || true)
            if echo "$PANE" | grep -qi "trust this folder\|safety check"; then
                tmux send-keys -t "$SESSION_NAME:$w" Enter
                echo ""
                echo "   🔓 Auto-accepted trust dialog in window $w"
            fi
        done
        # Check if any window has ❯ (past trust dialog)
        PANE0=$(tmux capture-pane -t "$SESSION_NAME:0" -p -S -5 2>/dev/null || true)
        if echo "$PANE0" | grep -q "❯"; then
            TRUST_HANDLED="yes"
        fi
    fi
done
printf "\r   ✓ Claude Code should be ready now!      \n"
echo ""

# ============================================
# Auto-activate agents in each window
# ============================================

echo "🚀 Auto-activating agents..."
echo ""

for window in 0 1 2 3; do
    agent_name="${AGENT_NAMES[$window]}"
    agent_cmd="${AGENT_COMMANDS[$window]}"

    echo "   [Window $window] Activating $agent_name..."
    send_command_with_delay "$window" "$agent_cmd"

    # Wait before activating next agent (avoid overwhelming the system)
    if [ "$window" -lt 3 ]; then
        sleep "$AGENT_ACTIVATION_DELAY"
    fi
done

echo ""
echo "✅ All agents activated!"

# ============================================
# Wait for agents to fully load
# ============================================

echo ""
echo "⏳ Waiting ${AGENT_LOAD_WAIT}s for agents to load..."
echo ""

for i in $(seq "$AGENT_LOAD_WAIT" -1 1); do
    printf "\r   %2d seconds remaining..." "$i"
    sleep 1
done
printf "\r   ✓ Agents should be ready now!           \n"

# ============================================
# Auto-start workflow in SM window
# ============================================

echo ""
echo "🎬 Starting workflow in SM window..."
send_command_with_delay "1" "$AUTO_START_COMMAND"

# Select SM window (window 1) as starting point
tmux select-window -t "$SESSION_NAME:1"

# Display startup completion message
echo ""
echo "═══════════════════════════════════════════════════════════════"
echo "✅ Orchestrix automation started!"
echo "═══════════════════════════════════════════════════════════════"
echo ""
echo "📋 Window Layout:"
echo "  Window 0: 🏛️  Architect"
echo "  Window 1: 📋 SM (current window) ← workflow started"
echo "  Window 2: 💻 Dev"
echo "  Window 3: 🧪 QA"
echo ""
echo "⌨️  tmux navigation:"
echo "  Ctrl+b → 0/1/2/3   Jump to window"
echo "  Ctrl+b → n/p       Next/Previous window"
echo "  Ctrl+b → d         Detach (runs in background)"
echo "  Ctrl+b → [         Scroll mode (q to exit)"
echo ""
echo "📝 Monitor: tail -f $LOG_FILE"
echo "📝 Reconnect: tmux attach -t $SESSION_NAME"
echo ""

# Attach to session
tmux attach-session -t "$SESSION_NAME"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)