#!/usr/bin/env bash # PostToolUse hook: 检测 Edit/Write 是否触及"当前模块以外"的模块路径,若是则在当前模块的跨模块日志中留痕(软规则 S2)。 # CC 在当前模块开发期间改动其他模块(无论目标模块是否已打里程碑)都会在此处留痕。 set -euo pipefail input="$(cat)" tool_name="$(printf '%s' "$input" | jq -r '.tool_name // empty')" case "$tool_name" in Edit|Write) ;; *) exit 0 ;; esac file_path="$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty')" [ -n "$file_path" ] || exit 0 project_dir="${CLAUDE_PROJECT_DIR:-$(pwd)}" docs08="$project_dir/docs/08-模块任务管理.md" [ -f "$docs08" ] || exit 0 # 跳过 docs/08 自身的改动(元数据文件编辑不是代码跨模块改动,不触发 S2) case "$file_path" in *"/docs/08-模块任务管理.md") exit 0 ;; esac # 当前模块 = 当前 git 分支名去掉 `module-` 前缀。 # module-start 步骤 3 保证模块循环期间处于 module- 分支; # 非 module-* 分支(如 main、feature/*、docs/*)不在模块循环内,不触发 S2 留痕。 current_branch="$(cd "$project_dir" 2>/dev/null && git branch --show-current 2>/dev/null || echo '')" case "$current_branch" in module-*) current_module="${current_branch#module-}" ;; *) exit 0 ;; esac [ -n "$current_module" ] || exit 0 # 遍历 docs/08 § 二 的所有 `- module_X` 行,提取 module_id 和其下的"路径:"行。 # docs/08 § 二 的模块块结构: # - module_0 # - 依赖: ... # - 路径: backend/module/xxx/, frontend/pages/xxx/ # - 里程碑: milestone/ 或 — hit_module="" # 用 awk 逐模块解析:每碰到 `- module_` 记录 module_id($2),然后在其下找第一个 `- 路径:` 行 # current_module 已在上文计算,在外层循环被排除 → 只会命中"非当前模块"的路径 while IFS=$'\t' read -r mod paths; do [ "$mod" = "$current_module" ] && continue IFS=',' read -ra scope_arr <<< "$paths" for s in "${scope_arr[@]}"; do s="$(echo "$s" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//; s:/$::')" [ -z "$s" ] && continue case "$file_path" in *"$s"*) hit_module="$mod"; break 2;; esac done done < <(awk ' /^- module_/ { # `- module_xxx ` awk 默认分词:$1=-、$2=module_xxx、$3= mod=$2 next } mod && /^[[:space:]]*- 路径:/ { sub(/^[[:space:]]*- 路径:[[:space:]]*/,"") print mod "\t" $0 mod="" } /^- module_/ && mod { mod="" } ' "$docs08") [ -n "$hit_module" ] || exit 0 log_dir="$project_dir/docs/superpowers/module-reports" mkdir -p "$log_dir" log_file="$log_dir/${current_module}-cross-module.md" if [ ! -f "$log_file" ]; then # 单一来源:hook 是日志文件的唯一创建者,从插件模板渲染表头。 plugin_root="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/../.." && pwd)}" template="$plugin_root/skills/crosscut/cross-module-log/templates/cross-module-log-template.md" if [ -f "$template" ]; then sed "s/{{module_name}}/${current_module}/g" "$template" > "$log_file" else # 模板缺失的兜底(不该发生):写最小可用表头,避免阻塞当前 Edit/Write { echo "# 跨模块改动日志 — ${current_module}" echo "" echo "| 时间戳 | 目标模块 | 文件 | 改动摘要 | 原因 | 影响评估 |" echo "|---|---|---|---|---|---|" } > "$log_file" fi fi ts="$(date -u +%FT%TZ)" rel_path="${file_path#$project_dir/}" echo "| ${ts} | ${hit_module} | ${rel_path} | ${tool_name} | TBD(CC 补) | TBD(CC 补) |" >> "$log_file" jq -n --arg m "$hit_module" --arg f "$log_file" --arg p "$rel_path" \ '{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:("跨模块改动检测:\($p) 属于模块 [\($m)](非当前模块)。存根已写入 \($f),含 TBD(CC 补) 占位。**不需要现在处理**——所有 TBD 由 module-report § ⑦ 一次性调用 cross-module-log skill 批量补齐(软规则 S2)。")}}'