#!/usr/bin/env bash # Stop 钩子:让 ERP workflow 在回合中途结束时自动续跑,除非命中「终止标记」或「防循环上限」。 # # 背景:编码阶段(逐 REQ:spec→plan→tdd→verify→review→docs/08,或 coding.mjs Workflow 之外的 # 手动推进)做完一段会退回 Claude 默认行为——报告、停下、等用户敲 continue。本钩子在 Stop 事件 # 拦下这次停止并自动喂「继续推进」,使各子任务一口气跑完,用户无需手敲 continue。 # # 设计: # 1. 终止标记 [ERP-HALT] —— 终止/硬护栏点(全部完成、test-gate 持续红、路径护栏、 # Workflow 已启动转后台、缺值阻塞等)打印它。出现即放行停下。 # 2. 防循环 —— 连续「无工具调用」的空转停止累加计数;达到上限放行停下。 # 正常推进时由 PostToolUse 的 reset-stall-counter.sh 清零(或本钩子自检 transcript 增长清零), # 故真流程不受限。 # 3. 否则 —— block 并喂一句「继续推进」指令。 set -u INPUT="$(cat)" SENTINEL='[ERP-HALT]' MAX_STALL=8 # —— 从 stdin JSON 取字段(不依赖 jq)—— get_str() { printf '%s' "$INPUT" \ | sed -n "s/.*\"$1\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" \ | head -n1 } TRANSCRIPT="$(get_str transcript_path)" # 反转义 Windows 路径里的 \\ 与 \/ TRANSCRIPT="${TRANSCRIPT//\\\\//}" TRANSCRIPT="${TRANSCRIPT//\\//}" # 计数器文件(按会话 transcript 路径派生 key,避免多会话互相干扰) KEY="$(printf '%s' "$TRANSCRIPT" | tr -cd 'A-Za-z0-9' | tail -c 40)" [ -z "$KEY" ] && KEY="default" CNT_FILE="${TMPDIR:-/tmp}/erp-autocont-$KEY" SIZE_FILE="${TMPDIR:-/tmp}/erp-autosize-$KEY" allow_stop() { rm -f "$CNT_FILE" "$SIZE_FILE" 2>/dev/null; exit 0; } # 1) 终止标记出现在最近的 transcript 尾部 → 放行停下 if [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ]; then if tail -c 16000 "$TRANSCRIPT" 2>/dev/null | grep -qF "$SENTINEL"; then allow_stop fi fi # 2) 进展自复位(自包含,不依赖 PostToolUse):transcript 自上次停止以来显著增长 # → 说明这一轮做了实质工作(工具调用 + 结果),清零空转计数。 CUR_SIZE=0 [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ] && CUR_SIZE="$(wc -c < "$TRANSCRIPT" 2>/dev/null | tr -d ' ')" case "$CUR_SIZE" in (''|*[!0-9]*) CUR_SIZE=0 ;; esac LAST_SIZE=0 [ -f "$SIZE_FILE" ] && LAST_SIZE="$(cat "$SIZE_FILE" 2>/dev/null || echo 0)" case "$LAST_SIZE" in (''|*[!0-9]*) LAST_SIZE=0 ;; esac echo "$CUR_SIZE" > "$SIZE_FILE" if [ $((CUR_SIZE - LAST_SIZE)) -gt 4000 ]; then rm -f "$CNT_FILE" 2>/dev/null fi # 3) 防循环:连续空转停止达到上限 → 放行停下 CNT=0 [ -f "$CNT_FILE" ] && CNT="$(cat "$CNT_FILE" 2>/dev/null || echo 0)" case "$CNT" in (''|*[!0-9]*) CNT=0 ;; esac if [ "$CNT" -ge "$MAX_STALL" ]; then allow_stop fi echo $((CNT + 1)) > "$CNT_FILE" # 拦下停止 → 续跑 printf '%s' '{"decision":"block","reason":"继续推进 ERP 编码流程:直接执行下一步——若上一个 REQ/任务已完成且 docs/08 已勾,立即开始下一个未完成 REQ 的完整生命周期(spec→plan→TDD→verify→review→docs/08);若当前 skill 刚结束,用 Skill 工具调用链中的下一个 skill 或对应派发器。不要输出收尾叙述、不要停下问用户是否继续。只有打印了 [ERP-HALT] 的终止/硬护栏点(全部完成、测试闸持续红、缺值阻塞、Workflow 已转后台)才应停下。"}' exit 0