Commit 095364cd7a15a5978144474d55f93fb3a65d1237
1 parent
aee9252a
fix(hooks): 恢复 Stop 钩子自动续跑编码流程(Workflow 重写时被误删)
cb9da34e 转 coding.mjs Workflow 时把 auto-continue Stop 钩子连同 17 个旧 coding 子技能一起删了,导致逐 REQ 手动编码会话每个 REQ 完就停下问 continue。 本提交恢复该机制: - hooks/scripts/auto-continue.sh(Stop):回合中途结束默认 block 并喂「继续 推进」,使各 REQ 子任务连跑、用户无需手敲 continue;仅当 transcript 出现 [ERP-HALT] 或连续空转达上限(8)/无 >4000B 增长时放行停下。 - hooks/scripts/reset-stall-counter.sh(PostToolUse):有工具调用即清零空转 计数,真推进时无限续跑。 - hooks/hooks.json:接线 Stop + PostToolUse(不含已删的 log-cross-module.sh)。 - coding-start 步骤5「Workflow 已启动」横幅补 [ERP-HALT]:Workflow 转后台后 主循环干净停下等通知,不被钩子误推去重启 coding-start。 - .gitattributes:强制 *.sh / hooks.json 用 LF,避免 autocrlf 把 CRLF 注入 shebang 与钩子输出的 JSON。 钩子在会话启动时加载,需重启已运行会话才生效(开发仓即运行源,无需 commit 即生效,commit 仅为持久化)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Showing
5 changed files
with
115 additions
and
0 deletions
.gitattributes
0 → 100644
hooks/hooks.json
0 → 100644
| 1 | +{ | |
| 2 | + "hooks": { | |
| 3 | + "PostToolUse": [ | |
| 4 | + { | |
| 5 | + "matcher": "", | |
| 6 | + "hooks": [ | |
| 7 | + { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/scripts/reset-stall-counter.sh" } | |
| 8 | + ] | |
| 9 | + } | |
| 10 | + ], | |
| 11 | + "Stop": [ | |
| 12 | + { | |
| 13 | + "hooks": [ | |
| 14 | + { "type": "command", "command": "\"${CLAUDE_PLUGIN_ROOT}\"/hooks/scripts/auto-continue.sh" } | |
| 15 | + ] | |
| 16 | + } | |
| 17 | + ] | |
| 18 | + } | |
| 19 | +} | ... | ... |
hooks/scripts/auto-continue.sh
0 → 100755
| 1 | +#!/usr/bin/env bash | |
| 2 | +# Stop 钩子:让 ERP workflow 在回合中途结束时自动续跑,除非命中「终止标记」或「防循环上限」。 | |
| 3 | +# | |
| 4 | +# 背景:编码阶段(逐 REQ:spec→plan→tdd→verify→review→docs/08,或 coding.mjs Workflow 之外的 | |
| 5 | +# 手动推进)做完一段会退回 Claude 默认行为——报告、停下、等用户敲 continue。本钩子在 Stop 事件 | |
| 6 | +# 拦下这次停止并自动喂「继续推进」,使各子任务一口气跑完,用户无需手敲 continue。 | |
| 7 | +# | |
| 8 | +# 设计: | |
| 9 | +# 1. 终止标记 [ERP-HALT] —— 终止/硬护栏点(全部完成、test-gate 持续红、路径护栏、 | |
| 10 | +# Workflow 已启动转后台、缺值阻塞等)打印它。出现即放行停下。 | |
| 11 | +# 2. 防循环 —— 连续「无工具调用」的空转停止累加计数;达到上限放行停下。 | |
| 12 | +# 正常推进时由 PostToolUse 的 reset-stall-counter.sh 清零(或本钩子自检 transcript 增长清零), | |
| 13 | +# 故真流程不受限。 | |
| 14 | +# 3. 否则 —— block 并喂一句「继续推进」指令。 | |
| 15 | +set -u | |
| 16 | + | |
| 17 | +INPUT="$(cat)" | |
| 18 | + | |
| 19 | +SENTINEL='[ERP-HALT]' | |
| 20 | +MAX_STALL=8 | |
| 21 | + | |
| 22 | +# —— 从 stdin JSON 取字段(不依赖 jq)—— | |
| 23 | +get_str() { | |
| 24 | + printf '%s' "$INPUT" \ | |
| 25 | + | sed -n "s/.*\"$1\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" \ | |
| 26 | + | head -n1 | |
| 27 | +} | |
| 28 | +TRANSCRIPT="$(get_str transcript_path)" | |
| 29 | +# 反转义 Windows 路径里的 \\ 与 \/ | |
| 30 | +TRANSCRIPT="${TRANSCRIPT//\\\\//}" | |
| 31 | +TRANSCRIPT="${TRANSCRIPT//\\//}" | |
| 32 | + | |
| 33 | +# 计数器文件(按会话 transcript 路径派生 key,避免多会话互相干扰) | |
| 34 | +KEY="$(printf '%s' "$TRANSCRIPT" | tr -cd 'A-Za-z0-9' | tail -c 40)" | |
| 35 | +[ -z "$KEY" ] && KEY="default" | |
| 36 | +CNT_FILE="${TMPDIR:-/tmp}/erp-autocont-$KEY" | |
| 37 | +SIZE_FILE="${TMPDIR:-/tmp}/erp-autosize-$KEY" | |
| 38 | + | |
| 39 | +allow_stop() { rm -f "$CNT_FILE" "$SIZE_FILE" 2>/dev/null; exit 0; } | |
| 40 | + | |
| 41 | +# 1) 终止标记出现在最近的 transcript 尾部 → 放行停下 | |
| 42 | +if [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ]; then | |
| 43 | + if tail -c 16000 "$TRANSCRIPT" 2>/dev/null | grep -qF "$SENTINEL"; then | |
| 44 | + allow_stop | |
| 45 | + fi | |
| 46 | +fi | |
| 47 | + | |
| 48 | +# 2) 进展自复位(自包含,不依赖 PostToolUse):transcript 自上次停止以来显著增长 | |
| 49 | +# → 说明这一轮做了实质工作(工具调用 + 结果),清零空转计数。 | |
| 50 | +CUR_SIZE=0 | |
| 51 | +[ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ] && CUR_SIZE="$(wc -c < "$TRANSCRIPT" 2>/dev/null | tr -d ' ')" | |
| 52 | +case "$CUR_SIZE" in (''|*[!0-9]*) CUR_SIZE=0 ;; esac | |
| 53 | +LAST_SIZE=0 | |
| 54 | +[ -f "$SIZE_FILE" ] && LAST_SIZE="$(cat "$SIZE_FILE" 2>/dev/null || echo 0)" | |
| 55 | +case "$LAST_SIZE" in (''|*[!0-9]*) LAST_SIZE=0 ;; esac | |
| 56 | +echo "$CUR_SIZE" > "$SIZE_FILE" | |
| 57 | +if [ $((CUR_SIZE - LAST_SIZE)) -gt 4000 ]; then | |
| 58 | + rm -f "$CNT_FILE" 2>/dev/null | |
| 59 | +fi | |
| 60 | + | |
| 61 | +# 3) 防循环:连续空转停止达到上限 → 放行停下 | |
| 62 | +CNT=0 | |
| 63 | +[ -f "$CNT_FILE" ] && CNT="$(cat "$CNT_FILE" 2>/dev/null || echo 0)" | |
| 64 | +case "$CNT" in (''|*[!0-9]*) CNT=0 ;; esac | |
| 65 | +if [ "$CNT" -ge "$MAX_STALL" ]; then | |
| 66 | + allow_stop | |
| 67 | +fi | |
| 68 | +echo $((CNT + 1)) > "$CNT_FILE" | |
| 69 | + | |
| 70 | +# 拦下停止 → 续跑 | |
| 71 | +printf '%s' '{"decision":"block","reason":"继续推进 ERP 编码流程:直接执行下一步——若上一个 REQ/任务已完成且 docs/08 已勾,立即开始下一个未完成 REQ 的完整生命周期(spec→plan→TDD→verify→review→docs/08);若当前 skill 刚结束,用 Skill 工具调用链中的下一个 skill 或对应派发器。不要输出收尾叙述、不要停下问用户是否继续。只有打印了 [ERP-HALT] 的终止/硬护栏点(全部完成、测试闸持续红、缺值阻塞、Workflow 已转后台)才应停下。"}' | |
| 72 | +exit 0 | ... | ... |
hooks/scripts/reset-stall-counter.sh
0 → 100755
| 1 | +#!/usr/bin/env bash | |
| 2 | +# PostToolUse 钩子:任何工具调用都视为「有进展」,清零 auto-continue.sh 的空转计数。 | |
| 3 | +# 这样真正在推进的流程可无限续跑,只有「连续多次纯文字、零工具」的空转才会触发防循环上限。 | |
| 4 | +set -u | |
| 5 | + | |
| 6 | +INPUT="$(cat)" | |
| 7 | +TRANSCRIPT="$(printf '%s' "$INPUT" \ | |
| 8 | + | sed -n 's/.*"transcript_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \ | |
| 9 | + | head -n1)" | |
| 10 | +TRANSCRIPT="${TRANSCRIPT//\\\\//}" | |
| 11 | +TRANSCRIPT="${TRANSCRIPT//\\//}" | |
| 12 | + | |
| 13 | +KEY="$(printf '%s' "$TRANSCRIPT" | tr -cd 'A-Za-z0-9' | tail -c 40)" | |
| 14 | +[ -z "$KEY" ] && KEY="default" | |
| 15 | +rm -f "${TMPDIR:-/tmp}/erp-autocont-$KEY" 2>/dev/null | |
| 16 | +exit 0 | ... | ... |
skills/coding/coding-start/SKILL.md
| ... | ... | @@ -122,9 +122,13 @@ Workflow({ |
| 122 | 122 | halt 后请按诊断修复,再重新运行 /erp-workflow:coding-start 续跑 |
| 123 | 123 | (git 分支由 coding.mjs 自行处理:残留功能分支直接续用、新模块从默认分支建, |
| 124 | 124 | 无需手动 checkout;但工作树不干净会在运行时 HALT,故重跑前请先清理工作树)。 |
| 125 | + | |
| 126 | + [ERP-HALT] Workflow 已转入后台自治,主循环就此停下等待其完成通知。 | |
| 125 | 127 | ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 126 | 128 | ``` |
| 127 | 129 | |
| 130 | +> `[ERP-HALT]` 标记供 Stop 钩子(`hooks/scripts/auto-continue.sh`)识别:Workflow 一旦转后台,主循环应停下等通知,**不**被自动续跑机制误推去重启 coding-start。逐 REQ 手动推进的会话则无此标记,由 Stop 钩子自动续跑,直到打印 `[ERP-HALT]`(全部完成 / 硬护栏)才停。 | |
| 131 | + | |
| 128 | 132 | 本 skill 到此结束,**不调用任何下游 skill**。 |
| 129 | 133 | |
| 130 | 134 | ## 参考 | ... | ... |