157 lines
5.2 KiB
Bash
Executable File
157 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
PROJECT_DIR="/home/long/project/蚊子"
|
||
REPORT_DIR="$PROJECT_DIR/logs/prd-review"
|
||
RUN_LOG="$PROJECT_DIR/logs/prd_review_cycle.log"
|
||
LOCK_FILE="/tmp/mosquito_prd_review_cycle.lock"
|
||
PID_FILE="$PROJECT_DIR/logs/prd-review/cycle.pid"
|
||
FORCE_RERUN="${FORCE_RERUN:-0}"
|
||
STALE_SECONDS="${STALE_SECONDS:-10800}"
|
||
CLAUDE_BIN="$HOME/.cursor/extensions/anthropic.claude-code-2.1.15-linux-x64/resources/native-binary/claude"
|
||
EVIDENCE_CHECK_SCRIPT="$PROJECT_DIR/scripts/verify_review_evidence.sh"
|
||
|
||
# cron环境下PATH通常不完整,显式补齐nvm/codex
|
||
export HOME="${HOME:-/home/long}"
|
||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||
export PATH="$HOME/.local/bin:$HOME/bin:/usr/local/bin:/usr/bin:/bin:$PATH"
|
||
|
||
if ! command -v codex >/dev/null 2>&1; then
|
||
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
||
# shellcheck disable=SC1090
|
||
. "$NVM_DIR/nvm.sh" >/dev/null 2>&1 || true
|
||
nvm use --silent 20 >/dev/null 2>&1 || true
|
||
nvm use --silent default >/dev/null 2>&1 || true
|
||
fi
|
||
fi
|
||
|
||
CODEX_BIN="$(command -v codex || true)"
|
||
if [ -z "$CODEX_BIN" ] && [ -x "$HOME/.nvm/versions/node/v20.19.0/bin/codex" ]; then
|
||
CODEX_BIN="$HOME/.nvm/versions/node/v20.19.0/bin/codex"
|
||
fi
|
||
|
||
mkdir -p "$REPORT_DIR" "$PROJECT_DIR/logs"
|
||
|
||
if [ -f "$PID_FILE" ]; then
|
||
prev_pid="$(cat "$PID_FILE" 2>/dev/null || true)"
|
||
if [ -n "${prev_pid:-}" ] && kill -0 "$prev_pid" 2>/dev/null; then
|
||
now_ts=$(date +%s)
|
||
pid_age=$((now_ts - $(stat -c %Y "$PID_FILE" 2>/dev/null || echo "$now_ts")))
|
||
if [ "$FORCE_RERUN" = "1" ] || [ "$pid_age" -gt "$STALE_SECONDS" ]; then
|
||
echo "[$(date '+%F %T')] force/stale rerun: killing previous pid=$prev_pid age=${pid_age}s" >> "$RUN_LOG"
|
||
kill "$prev_pid" 2>/dev/null || true
|
||
sleep 2
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
exec 9>"$LOCK_FILE"
|
||
if ! flock -n 9; then
|
||
echo "[$(date '+%F %T')] skip: previous cycle still running" >> "$RUN_LOG"
|
||
exit 0
|
||
fi
|
||
|
||
echo $$ > "$PID_FILE"
|
||
trap 'rm -f "$PID_FILE"' EXIT
|
||
|
||
cd "$PROJECT_DIR"
|
||
|
||
TIMESTAMP="$(date '+%Y%m%d_%H%M%S')"
|
||
REPORT_FILE="$REPORT_DIR/review_${TIMESTAMP}.md"
|
||
LATEST_REPORT="$REPORT_DIR/latest_review.md"
|
||
CLAUDE_SUMMARY="$REPORT_DIR/claude_apply_${TIMESTAMP}.md"
|
||
|
||
PRD_FILES=()
|
||
for f in "$PROJECT_DIR/docs/PRD.md" "$PROJECT_DIR/docs/prd"/*.md; do
|
||
if [ -f "$f" ]; then
|
||
PRD_FILES+=("$f")
|
||
fi
|
||
done
|
||
|
||
if [ ${#PRD_FILES[@]} -eq 0 ]; then
|
||
echo "[$(date '+%F %T')] error: no PRD files found" >> "$RUN_LOG"
|
||
exit 1
|
||
fi
|
||
|
||
PRD_LIST=$(printf '%s\n' "${PRD_FILES[@]}")
|
||
|
||
CODEX_PROMPT=$(cat <<EOF
|
||
你是严格的项目评审官。请在仓库 $PROJECT_DIR 执行全面review,并以 PRD 为基准进行差距分析。
|
||
|
||
PRD文件如下:
|
||
$PRD_LIST
|
||
|
||
输出要求(中文,Markdown):
|
||
1. 总体结论(是否满足最新PRD)
|
||
2. PRD事项对照矩阵(事项/当前状态/证据文件路径/优先级)
|
||
3. 未完成清单(P0/P1/P2)
|
||
4. 给Claude可直接执行的优化任务清单(按顺序,包含具体文件与验收标准)
|
||
5. 建议执行命令(构建、测试、e2e)
|
||
|
||
请务必面向“可执行落地”,不要空泛建议。
|
||
EOF
|
||
)
|
||
|
||
if [ -z "$CODEX_BIN" ]; then
|
||
echo "[$(date '+%F %T')] error: codex binary not found (PATH=$PATH)" >> "$RUN_LOG"
|
||
exit 1
|
||
fi
|
||
|
||
echo "[$(date '+%F %T')] cycle start: generating codex report with $CODEX_BIN" >> "$RUN_LOG"
|
||
"$CODEX_BIN" exec \
|
||
--cd "$PROJECT_DIR" \
|
||
--dangerously-bypass-approvals-and-sandbox \
|
||
--output-last-message "$REPORT_FILE" \
|
||
"$CODEX_PROMPT" >> "$RUN_LOG" 2>&1
|
||
|
||
if [ ! -s "$REPORT_FILE" ]; then
|
||
echo "[$(date '+%F %T')] error: codex report is empty: $REPORT_FILE" >> "$RUN_LOG"
|
||
exit 1
|
||
fi
|
||
|
||
cp -f "$REPORT_FILE" "$LATEST_REPORT"
|
||
|
||
echo "[$(date '+%F %T')] codex report ready: $REPORT_FILE" >> "$RUN_LOG"
|
||
|
||
CLAUDE_PROMPT=$(cat <<EOF
|
||
请读取并执行该评审报告中的优化任务:
|
||
$REPORT_FILE
|
||
|
||
要求:
|
||
1) 按优先级执行修复,优先完成P0/P1
|
||
2) 可以直接修改代码并运行必要命令(含后端测试、前端测试、端到端测试)
|
||
3) 若修复后仍有失败,继续迭代直到通过或给出明确阻塞原因
|
||
4) 输出最终摘要:执行命令、修改文件、测试结果、剩余未完成PRD项
|
||
5) 必须输出“证据矩阵”,每条包含:命令 | 退出码 | 原始日志路径 | 结果结论
|
||
6) 所有结论都要附可追溯日志路径(如 logs/*.log、target/surefire-reports/*.xml)
|
||
|
||
若无法提供可追溯证据,必须明确写“证据不足-本轮无效”。
|
||
请直接开始执行,不要只给计划。
|
||
EOF
|
||
)
|
||
|
||
echo "[$(date '+%F %T')] cycle continue: running claude apply" >> "$RUN_LOG"
|
||
"$CLAUDE_BIN" -p \
|
||
--permission-mode dontAsk \
|
||
--dangerously-skip-permissions \
|
||
"$CLAUDE_PROMPT" > "$CLAUDE_SUMMARY" 2>> "$RUN_LOG"
|
||
|
||
if [ ! -s "$CLAUDE_SUMMARY" ]; then
|
||
echo "[$(date '+%F %T')] error: claude summary is empty: $CLAUDE_SUMMARY" >> "$RUN_LOG"
|
||
exit 1
|
||
fi
|
||
|
||
if [ -x "$EVIDENCE_CHECK_SCRIPT" ]; then
|
||
if "$EVIDENCE_CHECK_SCRIPT" "$CLAUDE_SUMMARY" "$PROJECT_DIR" >> "$RUN_LOG" 2>&1; then
|
||
echo "[$(date '+%F %T')] evidence gate pass" >> "$RUN_LOG"
|
||
else
|
||
echo "[$(date '+%F %T')] error: evidence gate failed, mark cycle invalid" >> "$RUN_LOG"
|
||
exit 1
|
||
fi
|
||
else
|
||
echo "[$(date '+%F %T')] warn: evidence checker missing: $EVIDENCE_CHECK_SCRIPT" >> "$RUN_LOG"
|
||
exit 1
|
||
fi
|
||
|
||
echo "[$(date '+%F %T')] cycle done: $CLAUDE_SUMMARY" >> "$RUN_LOG"
|