Files
gym-manage/.agents/hooks/check-completeness.sh
T
zhangxiang 5c65139fd8 chore(config): 参考 novavis 完善 Agent 配置体系
- AGENTS.md: 新增 §20 工具脚本复用 / §21 分析结论复用 / §22 Flaky 门禁,更新文档配置表
- .pi/: 新增 settings.json、rules/guardrails.md、prompts/{debug,review,ship}-gym-manage.md
- .agents/: 新增 Hook 配置与 4 个 Hook 脚本(session-start/pre-agent-check/check-completeness/stop-check)
  及 protocols/systematic-debugging.md(check-completeness 含接口链缺口检测)
- scripts/flaky-scan.sh: vitest shuffle 稳定性扫描(gym-manage-web)
- .gitignore: 追踪 AGENTS.md 与 .pi 配置,忽略运行时缓存(todos/taskflows-runs/tokenomy/sessions)
2026-08-04 12:02:12 +08:00

133 lines
5.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# PostToolUse hook: checks a single edited file for:
# 1. Unfinished markers (TODO/FIXME/skeleton/empty fn)
# 2. Integration chain gaps (based on file type)
# Runs after Write/Edit operations only on the modified file.
set -euo pipefail
PROJECT_DIR="${AGENT_PROJECT_DIR:-$(pwd)}"
# Suppress during brainstorming / exploratory sessions
if [ -f "${PROJECT_DIR}/.agents/.suppress-hooks" ]; then
exit 0
fi
f="${AGENT_EDITED_FILE:-$AGENT_FILE}"
if [ ! -f "$f" ]; then
exit 0
fi
issues=()
ext="${f##*.}"
basename=$(basename "$f")
# ============================================================
# PART 1: Unfinished markers scan
# ============================================================
if [[ "$ext" =~ ^(ts|tsx|js|jsx|vue)$ ]]; then
while IFS=: read -r line_no content; do
[ -z "$line_no" ] && continue
trimmed=$(echo "$content" | sed 's/^[[:space:]]*//')
if echo "$trimmed" | grep -qE '^(TODO|FIXME|HACK|XXX):'; then
issues+=(" L${line_no}: TODO/FIXME ${trimmed:0:100}")
elif echo "$trimmed" | grep -qi 'not implemented' && echo "$trimmed" | grep -qi 'throw'; then
issues+=(" L${line_no}: [SKELETON] ${trimmed:0:100}")
elif echo "$trimmed" | grep -qE 'function [a-zA-Z_][a-zA-Z0-9_]*\s*\(\s*\)\s*\{\s*\}'; then
if ! echo "$trimmed" | grep -q 'return'; then
issues+=(" L${line_no}: [EMPTY FN] ${trimmed:0:100}")
fi
elif echo "$trimmed" | grep -qE 'const [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*\(\)\s*=>\s*\{\s*\}'; then
issues+=(" L${line_no}: [EMPTY ARROW FN] ${trimmed:0:100}")
fi
done < <(grep -nE 'TODO:|FIXME:|HACK:|XXX:|not implemented|function [a-zA-Z_][a-zA-Z0-9_]*\s*\(\s*\)\s*\{\s*\}|const [a-zA-Z_][a-zA-Z0-9_]*\s*=\s*\(\)\s*=>\s*\{\s*\}' "$f" 2>/dev/null || true)
elif [[ "$ext" == "java" ]]; then
while IFS=: read -r line_no content; do
[ -z "$line_no" ] && continue
trimmed=$(echo "$content" | sed 's/^[[:space:]]*//')
if echo "$trimmed" | grep -qE '^(TODO|FIXME|HACK|XXX):'; then
issues+=(" L${line_no}: TODO/FIXME ${trimmed:0:100}")
elif echo "$trimmed" | grep -qE 'UnsupportedOperationException'; then
issues+=(" L${line_no}: [SKELETON] UnsupportedOperationException")
fi
done < <(grep -nE 'TODO:|FIXME:|HACK:|XXX:|UnsupportedOperationException' "$f" 2>/dev/null || true)
fi
# ============================================================
# PART 2: Integration chain gap detection
# ============================================================
# Check 1: Java Controller 变更 → 提取映射路径,检查 Web/UniApp 请求层是否有引用
if [[ "$f" == *"gym-manage-api/"*.java ]] && grep -q '@RestController\|@Controller' "$f" 2>/dev/null; then
# 提取 @RequestMapping/@GetMapping/@PostMapping/@PutMapping/@DeleteMapping 的路径
paths=$(grep -oE '@(Request|Get|Post|Put|Delete)Mapping\([^)]*' "$f" 2>/dev/null \
| grep -oE '"/[^"]+"' | tr -d '"' | sort -u || true)
if [ -n "$paths" ]; then
# 与类级 @RequestMapping 前缀合并
prefix=$(grep -oE '@RequestMapping\([^)]*' "$f" 2>/dev/null | grep -oE '"/[^"]+"' | tr -d '"' | head -1 || true)
while IFS= read -r p; do
[ -z "$p" ] && continue
# 已含 /api 绝对路径的不再加类级前缀,避免双写
case "$p" in
/api/*) full="$p" ;;
*) full="${prefix}${p}" ;;
esac
# 去掉 Spring 模板变量({id} 等)用于模糊匹配
key=$(echo "$full" | sed 's/{[^}]*}//g')
[ -z "$key" ] && continue
hits=$(grep -rl -- "$key" "${PROJECT_DIR}/gym-manage-web/src/api" "${PROJECT_DIR}/gym-manage-uniapp/api" "${PROJECT_DIR}/gym-manage-coach-uniapp/api" 2>/dev/null | wc -l | tr -d ' ' || true)
if [ "$hits" -eq 0 ]; then
issues+=(" [GAP] 接口 '${full}' 在 Web/UniApp 请求层无引用(gym-manage-web/src/api、uniapp/api、coach-uniapp/api")
fi
done <<< "$paths"
fi
fi
# Check 2: Web API 请求层变更 → 检查页面/store 有引用
if [[ "$f" == *"gym-manage-web/src/api/"*".api.ts" ]]; then
new_fns=$(grep -oE '^export (async )?function [a-zA-Z_][a-zA-Z0-9_]*|^export const [a-zA-Z_][a-zA-Z0-9_]*\s*=' "$f" 2>/dev/null \
| sed -E 's/^export (async )?function //; s/^export const //; s/[[:space:]]*=.*//' | sort -u || true)
if [ -n "$new_fns" ]; then
while IFS= read -r fn; do
[ -z "$fn" ] && continue
hits=$(grep -rl -- "\b${fn}\b" "${PROJECT_DIR}/gym-manage-web/src/views" "${PROJECT_DIR}/gym-manage-web/src/stores" "${PROJECT_DIR}/gym-manage-web/src/components" 2>/dev/null | wc -l | tr -d ' ' || true)
if [ "$hits" -eq 0 ]; then
issues+=(" [GAP] Web API '${fn}' 在 src/views|stores|components 无调用方")
fi
done <<< "$new_fns"
fi
fi
# Check 3: UniApp 请求层变更 → 检查 pages 有引用
if [[ "$f" == *"gym-manage-uniapp/api/"*.js ]] || [[ "$f" == *"gym-manage-coach-uniapp/api/"*.js ]]; then
uniapp_root="gym-manage-uniapp"
[[ "$f" == *"coach-uniapp"* ]] && uniapp_root="gym-manage-coach-uniapp"
new_fns=$(grep -oE '^export (async )?function [a-zA-Z_][a-zA-Z0-9_]*|^export const [a-zA-Z_][a-zA-Z0-9_]*\s*=' "$f" 2>/dev/null \
| sed -E 's/^export (async )?function //; s/^export const //; s/[[:space:]]*=.*//' | sort -u || true)
if [ -n "$new_fns" ]; then
while IFS= read -r fn; do
[ -z "$fn" ] && continue
hits=$(grep -rl -- "${fn}" "${PROJECT_DIR}/${uniapp_root}/pages" 2>/dev/null | wc -l | tr -d ' ' || true)
if [ "$hits" -eq 0 ]; then
issues+=(" [GAP] ${uniapp_root} API '${fn}' 在 pages/ 无调用方")
fi
done <<< "$new_fns"
fi
fi
# ============================================================
# REPORT
# ============================================================
if [ ${#issues[@]} -gt 0 ]; then
echo ""
echo "[COMPLETENESS] $(basename "$f") 检查发现 ${#issues[@]} 个问题:"
for issue in "${issues[@]:0:20}"; do
echo "$issue"
done
if [ ${#issues[@]} -gt 20 ]; then
echo " ... 及其他 $((${#issues[@]} - 20)) 处"
fi
echo ""
fi