#!/bin/bash # 多 AI 协作触发器 v2.0 · 独立任务文件模式 # # 用法: # ./collab.sh --kimi tasks/active/task-xxx.md → Kimi 执行指定任务 # ./collab.sh --mimo tasks/active/task-xxx.md → MiMo 执行指定任务 # ./collab.sh --both tasks/active/task-xxx.md → Kimi 先/MiMo 补(串行) # ./collab.sh --new "任务标题" [--kimi|--mimo] → 新建任务文件(不立即执行) # ./collab.sh --list → 列出当前待执行任务 # # 目录结构: # context/project-brief.md 项目简报(每次随任务一起发送,≤60行) # tasks/active/ 待执行任务(每个文件独立上下文) # tasks/done/ 已完成任务(执行后自动归档) # BOARD.md 结果日志(只追加,不作为 AI 上下文) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TASKS_ACTIVE="$SCRIPT_DIR/tasks/active" TASKS_DONE="$SCRIPT_DIR/tasks/done" CONTEXT_FILE="$SCRIPT_DIR/context/project-brief.md" BOARD="$SCRIPT_DIR/BOARD.md" TIMESTAMP=$(date "+%Y-%m-%d %H:%M") # ── 参数解析 ────────────────────────────────────────────────────────────────── AI_TARGET="" TASK_FILE="" NEW_TITLE="" DO_LIST=false while [[ $# -gt 0 ]]; do case "$1" in --kimi) AI_TARGET="kimi"; shift ;; --mimo) AI_TARGET="mimo"; shift ;; --both) AI_TARGET="both"; shift ;; --new) NEW_TITLE="$2"; shift 2 ;; --list) DO_LIST=true; shift ;; *.md) TASK_FILE="$1"; shift ;; *) shift ;; esac done # ── 列出任务 ────────────────────────────────────────────────────────────────── if $DO_LIST; then echo "=== 待执行任务 ===" ls -1 "$TASKS_ACTIVE"/*.md 2>/dev/null | while read -r f; do head -1 "$f" | sed "s/# 任务: / · /" echo " 文件: $(basename "$f")" done || echo " (无待执行任务)" exit 0 fi # ── 新建任务文件 ────────────────────────────────────────────────────────────── if [ -n "$NEW_TITLE" ]; then TASK_ID="task-$(date +%Y%m%d-%H%M%S)" TASK_FILE="$TASKS_ACTIVE/${TASK_ID}.md" TARGET_HINT="${AI_TARGET:-kimi}" cat > "$TASK_FILE" << TASK_TEMPLATE # 任务: ${NEW_TITLE} > 创建时间: ${TIMESTAMP} > 执行者: ${TARGET_HINT} > 状态: pending ## 任务描述 (在此描述需要完成的具体内容) ## 预期输出 - 格式: Markdown GDD 章节 - 字数: 约 xxx 字 - 结构: (列出需要的章节) ## 约束 - 参考文档: GDD-xx - 数值需与 ✅xx 对齐 - 禁止方向: (不允许的设计思路) TASK_TEMPLATE echo "任务文件已创建: $TASK_FILE" echo "请编辑后执行: ./collab.sh --${TARGET_HINT} $TASK_FILE" exit 0 fi # ── 执行任务 ────────────────────────────────────────────────────────────────── if [ -z "$TASK_FILE" ] || [ ! -f "$TASK_FILE" ]; then echo "错误: 请指定任务文件" >&2 echo "用法: ./collab.sh [--kimi|--mimo|--both] tasks/active/task-xxx.md" >&2 exit 1 fi if [ -z "$AI_TARGET" ]; then AI_TARGET="kimi" fi # 读取项目简报 BRIEF="" if [ -f "$CONTEXT_FILE" ]; then BRIEF=$(cat "$CONTEXT_FILE") else BRIEF="(未找到 context/project-brief.md,请创建)" fi TASK_CONTENT=$(cat "$TASK_FILE") TASK_NAME=$(basename "$TASK_FILE" .md) # ── AI 调用函数 ─────────────────────────────────────────────────────────────── call_ai() { local AI_NAME="$1" local CLI_CMD="$2" local TMPFILE TMPFILE=$(mktemp) cat > "$TMPFILE" << PROMPT_EOF 你是「洪荒大陆」挂机手游设计协作者 ${AI_NAME},与 Claude Sonnet 共同完善 GDD。 ## 分工 - Claude Sonnet: 质量把控 / 最终落笔 / 与用户交互 - ${AI_NAME}: 高效产出设计草稿,可持有独立观点并说明理由 ## 项目简报(必读,勿忽略) ${BRIEF} --- ## 当前任务(仅执行此任务,忽略其他历史) ${TASK_CONTENT} --- 请直接输出任务要求的内容,以 \`### [${AI_NAME} · ${TIMESTAMP}]\` 开头。 不要重复任务描述。直接给出设计内容,越完整越好。 PROMPT_EOF echo "=== [${TIMESTAMP}] 调用 ${AI_NAME} | 任务: ${TASK_NAME} ===" >&2 local RESPONSE RESPONSE=$(eval "${CLI_CMD}" -p "$(cat "$TMPFILE")" 2>/dev/null) rm -f "$TMPFILE" if [ -z "$RESPONSE" ]; then echo "[错误] ${AI_NAME} 未返回内容" >&2 return 1 fi # 追加到 BOARD(仅作历史日志,不再作为上下文) printf '\n---\n## [%s] 任务: %s | 执行者: %s\n\n%s\n' \ "$TIMESTAMP" "$TASK_NAME" "$AI_NAME" "$RESPONSE" >> "$BOARD" # 将回复追加到任务文件(便于 Claude 后续审查) printf '\n---\n\n## %s 输出 (%s)\n\n%s\n' \ "$AI_NAME" "$TIMESTAMP" "$RESPONSE" >> "$TASK_FILE" echo "=== ${AI_NAME} 完成,回复已写入任务文件 ===" >&2 echo "--- 前 30 行预览 ---" >&2 echo "$RESPONSE" | head -30 >&2 } # ── 执行 ────────────────────────────────────────────────────────────────────── case "$AI_TARGET" in kimi) call_ai "Kimi" "~/claude-kimi" ;; mimo) call_ai "MiMo" "~/claude-mimo" ;; both) call_ai "Kimi" "~/claude-kimi" echo "" >&2 echo ">>> MiMo 开始补充..." >&2 call_ai "MiMo" "~/claude-mimo" ;; esac # 归档任务文件 ARCHIVE_NAME="${TASK_NAME}-done-$(date +%H%M%S).md" mv "$TASK_FILE" "$TASKS_DONE/$ARCHIVE_NAME" echo "=== 任务已归档: tasks/done/$ARCHIVE_NAME ===" >&2