lawless/docs/ai-collab/collab.sh

185 行
6.0 KiB
Bash

#!/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