48 lines
3.3 KiB
Bash
48 lines
3.3 KiB
Bash
#!/usr/bin/env bash
|
||
# 规约投递闸(PostToolUse:Read | mcp__node_repl__js,2026-10-26 立)
|
||
# 干两件事:①读到 design/、game/ 下的文件时把对应规约要点注入上下文(每会话每域一次);
|
||
# ②读到四份规约本体时在读取记录里记 read: 标记——worktree-guard 的规则闸据此放行写入。
|
||
# 记录文件:${TMPDIR:-/tmp}/zcode-rule-ledger/<会话ID>.ledger,纯文本,会话结束自然失效。
|
||
# 本钩子创建记录文件这一行为本身是规则闸的启用条件(闸只在记录文件存在时生效,
|
||
# 保证「写标记的钩子活着」与「闸执法」永远成对出现)。解析异常一律静默放行。
|
||
SID="${CLAUDE_SESSION_ID:-}"
|
||
[ -z "$SID" ] && exit 0
|
||
LEDGER_DIR="${TMPDIR:-/tmp}/zcode-rule-ledger"
|
||
LEDGER="$LEDGER_DIR/$SID.ledger"
|
||
mkdir -p "$LEDGER_DIR" 2>/dev/null
|
||
touch "$LEDGER" 2>/dev/null || exit 0
|
||
payload="$(cat)"
|
||
|
||
# 目标路径:优先 file_path 字段;node_repl 等无 file_path 的载荷退化为全文找 design/、game/ 路径段
|
||
target="$(printf '%s' "$payload" | sed -n 's/.*"file_path" *: *"\([^"]*\)".*/\1/p' | head -1 | sed 's#\\\\#/#g; s#\\#/#g')"
|
||
[ -z "$target" ] && target="$(printf '%s' "$payload" | grep -oE '(design|game)/[A-Za-z0-9_./-]+' | head -1)"
|
||
[ -z "$target" ] && exit 0
|
||
low="$(printf '%s' "$target" | tr 'A-Z' 'a-z')"
|
||
|
||
# 规约本体:记 read: 标记(每份只记一次),不投递
|
||
case "$low" in
|
||
*doc-conventions.md) grep -q '^read:doc-conventions.md$' "$LEDGER" 2>/dev/null || printf 'read:doc-conventions.md\n' >> "$LEDGER"; exit 0 ;;
|
||
*concept-conventions.md) grep -q '^read:concept-conventions.md$' "$LEDGER" 2>/dev/null || printf 'read:concept-conventions.md\n' >> "$LEDGER"; exit 0 ;;
|
||
*code-conventions.md) grep -q '^read:code-conventions.md$' "$LEDGER" 2>/dev/null || printf 'read:code-conventions.md\n' >> "$LEDGER"; exit 0 ;;
|
||
*test-conventions.md) grep -q '^read:test-conventions.md$' "$LEDGER" 2>/dev/null || printf 'read:test-conventions.md\n' >> "$LEDGER"; exit 0 ;;
|
||
esac
|
||
|
||
domain=""
|
||
case "$low" in
|
||
*/design/*|design/*) domain=doc ;;
|
||
*/game/*|game/*) domain=code ;;
|
||
esac
|
||
[ -z "$domain" ] && exit 0
|
||
|
||
# 域投递:每会话每域一次(文案单行、无内部双引号,保 JSON 字面合法)
|
||
if ! grep -q "^DELIVERED:$domain$" "$LEDGER" 2>/dev/null; then
|
||
if [ "$domain" = doc ]; then
|
||
msg='【规约投递】你正在读 design/ 下的文件。定文档方案/动笔前必须先完整读 design/engineering/doc-conventions.md 与 concept-conventions.md。要点提醒:正文单点维护(引用写指针不复制具体值);附加物独立成对象(禁摊平成父概念属性);概念状态用封闭枚举;禁自造黑话;文档增删同步根索引 02-系统清单。写 design/ 前无读取记录会被规则闸拦截。'
|
||
else
|
||
msg='【规约投递】你正在读 game/ 下的文件。定代码/测试方案前必须先完整读 design/engineering/code-conventions.md 与 test-conventions.md。要点提醒:数值单一事实源(data/tables 的 .tres 经 ConfigDB);RNG 单点(禁 randomize 与自建 RNG);Core 层零 UI 依赖;fail-fast;验收断言从真实入口进入,禁变异类测试与撤销类取证。写 game/ 前无读取记录会被规则闸拦截。'
|
||
fi
|
||
printf '{"additionalContext":"%s"}\n' "$msg"
|
||
printf 'DELIVERED:%s\n' "$domain" >> "$LEDGER"
|
||
fi
|
||
exit 0
|