chore: 参考 novavis 优化 Agent 配置体系

新增 AGENTS.md 规则(§1-§22)、Hook 脚本和可复用工具脚本:

- 重构 AGENTS.md:新增 16 条规则(任务分解、双轨验证、骨架占位禁止、
  测试命令退出码保留、工具脚本可复用、完成前验证门禁、Flaky 测试门禁等)
- 新增 .trae/hooks/:session-start / pre-agent-check / check-completeness / stop-check
- 新增 .trae/settings.json:关联 4 个 Hook 到 SessionStart/PreToolUse/PostToolUse/Stop
- 新增 scripts/:flaky-scan.sh / check-task-checklist.sh / check-issue-checklist.sh
- 调整 .gitignore:保留 hooks/settings 纳入版本控制,忽略运行时状态文件
This commit is contained in:
2026-08-12 22:07:22 +08:00
parent 64a1ce7bcd
commit 5445c343b3
8 changed files with 617 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# Issue 关闭门禁检查 — 扫描已关闭的 Issue 文档是否存在未勾选子项
# 用法:
# bash scripts/check-issue-checklist.sh # 检查所有 Issue
# bash scripts/check-issue-checklist.sh --quiet # 静默模式(仅退出码)
# bash scripts/check-issue-checklist.sh --help # 显示帮助
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
QUIET=0
usage() {
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " --quiet 静默模式(仅通过退出码表示结果)"
echo " --help 显示此帮助"
exit 0
}
while [[ $# -gt 0 ]]; do
case "$1" in
--quiet) QUIET=1; shift ;;
--help) usage ;;
*) echo "Unknown option: $1"; usage ;;
esac
done
# 检查 docs/plans/ 下的 Issue 文档
PLANS_DIR="${PROJECT_DIR}/docs/plans"
HAS_ERRORS=0
VIOLATIONS=()
if [ ! -d "$PLANS_DIR" ]; then
[ "$QUIET" -eq 0 ] && echo " 检查: ${PLANS_DIR} 不存在,跳过"
echo '{"status":"ok"}'
exit 0
fi
# 扫描已关闭的文档(包含 "status: closed" 或 "已关闭" 标记)
while IFS= read -r plan_file; do
[ -z "$plan_file" ] && continue
basename=$(basename "$plan_file" .md)
# 检查是否包含 closed 标记
if grep -qi "status.*closed\|已关闭\|已完成" "$plan_file" 2>/dev/null; then
# 检查是否有未勾选的 checklist 项
UNCHECKED=$(grep -cE '^\s*[-*]\s+\[[ ]\]' "$plan_file" 2>/dev/null || true)
if [ "$UNCHECKED" -gt 0 ]; then
VIOLATIONS+=("${basename}: ${UNCHECKED} 个未勾选子项")
HAS_ERRORS=1
fi
fi
done < <(find "$PLANS_DIR" -name '*.md' -type f 2>/dev/null)
if [ "$HAS_ERRORS" -eq 0 ]; then
[ "$QUIET" -eq 0 ] && echo " Issue 门禁: PASS"
echo '{"status":"ok"}'
exit 0
else
[ "$QUIET" -eq 0 ] && echo " Issue 门禁: FAIL"
for v in "${VIOLATIONS[@]}"; do
echo " - $v"
done
echo '{"status":"error"}'
exit 1
fi
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# Task 完成门禁检查 — 扫描已标记完成的 Task 是否存在未勾选子项
# 用法:
# bash scripts/check-task-checklist.sh # 检查所有 Task
# bash scripts/check-task-checklist.sh --quiet # 静默模式(仅退出码)
# bash scripts/check-task-checklist.sh --help # 显示帮助
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
QUIET=0
usage() {
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " --quiet 静默模式(仅通过退出码表示结果)"
echo " --help 显示此帮助"
exit 0
}
while [[ $# -gt 0 ]]; do
case "$1" in
--quiet) QUIET=1; shift ;;
--help) usage ;;
*) echo "Unknown option: $1"; usage ;;
esac
done
# 检查 docs/plans/ 下的 Task 文档
PLANS_DIR="${PROJECT_DIR}/docs/plans"
HAS_ERRORS=0
VIOLATIONS=()
if [ ! -d "$PLANS_DIR" ]; then
[ "$QUIET" -eq 0 ] && echo " 检查: ${PLANS_DIR} 不存在,跳过"
echo '{"status":"ok"}'
exit 0
fi
# 扫描已完成的计划文档
while IFS= read -r plan_file; do
[ -z "$plan_file" ] && continue
basename=$(basename "$plan_file" .md)
# 检查是否包含"已完成"标记
if grep -qi "已完成\|completed" "$plan_file" 2>/dev/null; then
# 检查是否有未勾选的 checklist 项
UNCHECKED=$(grep -cE '^\s*[-*]\s+\[[ ]\]' "$plan_file" 2>/dev/null || true)
if [ "$UNCHECKED" -gt 0 ]; then
VIOLATIONS+=("${basename}: ${UNCHECKED} 个未勾选子项")
HAS_ERRORS=1
fi
fi
done < <(find "$PLANS_DIR" -name '*.md' -type f 2>/dev/null)
if [ "$HAS_ERRORS" -eq 0 ]; then
[ "$QUIET" -eq 0 ] && echo " Task 门禁: PASS"
echo '{"status":"ok"}'
exit 0
else
[ "$QUIET" -eq 0 ] && echo " Task 门禁: FAIL"
for v in "${VIOLATIONS[@]}"; do
echo " - $v"
done
echo '{"status":"error"}'
exit 1
fi
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env bash
# Flaky test scanner — 连续运行测试检测不稳定测试
# 用法:
# bash scripts/flaky-scan.sh # 随机顺序运行 3 次全量测试
# bash scripts/flaky-scan.sh --runs 5 # 指定运行次数
# bash scripts/flaky-scan.sh --spec <文件> # 指定测试文件
# bash scripts/flaky-scan.sh --help # 显示帮助
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
APP_DIR="${PROJECT_DIR}/everything-is-suitable-uniapp"
RUNS=3
SPEC=""
VERBOSE=0
# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
usage() {
echo "Usage: $0 [options]"
echo ""
echo "Options:"
echo " --runs <N> 运行次数 (default: 3)"
echo " --spec <file> 指定测试文件"
echo " --verbose 详细输出"
echo " --help 显示此帮助"
exit 0
}
while [[ $# -gt 0 ]]; do
case "$1" in
--runs) RUNS="$2"; shift 2 ;;
--spec) SPEC="$2"; shift 2 ;;
--verbose) VERBOSE=1; shift ;;
--help) usage ;;
*) echo "Unknown option: $1"; usage ;;
esac
done
if [ ! -d "$APP_DIR" ]; then
echo "错误: 未找到应用目录 ${APP_DIR}"
exit 1
fi
cd "$APP_DIR"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Flaky Test Scanner"
echo " 运行次数: ${RUNS}"
echo " 测试文件: ${SPEC:-全部}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
PASS_COUNT=0
FAIL_COUNT=0
declare -a FAILED_RUNS
for i in $(seq 1 "$RUNS"); do
echo ""
echo " [${i}/${RUNS}] 运行中..."
if [ -n "$SPEC" ]; then
CMD="npx vitest run --reporter=verbose \"$SPEC\""
else
CMD="npx vitest run --reporter=verbose"
fi
if [ "$VERBOSE" -eq 1 ]; then
echo "$CMD"
fi
set +e
OUTPUT=$(eval "$CMD" 2>&1)
EXIT_CODE=$?
set -e
if [ "$EXIT_CODE" -eq 0 ]; then
echo -e " [${i}/${RUNS}] ${GREEN}PASS${NC}"
PASS_COUNT=$((PASS_COUNT + 1))
else
echo -e " [${i}/${RUNS}] ${RED}FAIL${NC} (exit code: ${EXIT_CODE})"
FAIL_COUNT=$((FAIL_COUNT + 1))
FAILED_RUNS+=("$i")
if [ "$VERBOSE" -eq 1 ]; then
echo " --- 失败输出 ---"
echo "$OUTPUT" | tail -50
echo " ---"
fi
fi
done
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " 结果汇总"
echo " PASS: ${PASS_COUNT}/${RUNS}"
echo " FAIL: ${FAIL_COUNT}/${RUNS}"
if [ "$FAIL_COUNT" -eq 0 ]; then
echo -e " ${GREEN}结论: 未检测到 flaky 测试,测试稳定 ✓${NC}"
exit 0
else
echo -e " ${RED}结论: 检测到 flaky 测试${NC}"
echo " 失败运行: ${FAILED_RUNS[*]}"
echo ""
echo " 常见 flaky 根因:"
echo " ① vi.clearAllMocks() 不清除 mockResolvedValue 实现"
echo " ② 模块级可变 let mock 变量被测试修改未还原"
echo " ③ vi.stubGlobal 全局替换未在 afterEach 恢复"
echo " ④ waitFor 未 import"
echo ""
echo " 建议: 对失败运行使用 --verbose 查看详细输出"
exit 1
fi