Refactor code structure for improved readability and maintainability
This commit is contained in:
1 parent
6bfd8009a2
commit
a710477be2
47 files changed
+5357
-5
No files matched your search
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env bash
|
||||
# agent 侧守卫(PreToolUse,本机件不入 git;2026-10-31 车道恢复随动——worktree 铁律回归)
|
||||
# 拦四路:①Edit/Write 族写主工作区(主工作区只作合并落点;豁免 .zcode/ 与 .qoder/)
|
||||
# ②Bash 在主工作区一切 git commit 一律拦(直提走 worktree;冲突/红态续合=git merge --abort → 回 worktree → 重新 git merge --no-ff,合并收尾提交不再例外)
|
||||
# ③Bash 在主工作区 git commit 带 --no-verify/-n(agent 禁用,旗标仅属用户)
|
||||
# (合回 merge 零机检,全量验证归合回后本地 CI:tools/hooks/post-merge)
|
||||
# ④规则闸:Edit/Write 写 design/、game/,或 Bash 以文件触碰动词+路径命中两域时,
|
||||
# 本会话读取记录须含对应规约 read: 标记(PostToolUse 规约投递闸读规约本体时记入;
|
||||
# 记录文件不存在=投递闸未运行→本闸不启用;子代理不经守卫层,2026-10-26 实测)
|
||||
# 附快速通道:Edit/Write 族目标在主工作区外(worktree)→ 免 python+git 直接过
|
||||
# pre-commit 直改闸已随主区直改车道退役(commit 点零执法,全量验证归合回后本地 CI);
|
||||
# 本守卫只约束 agent,用户手工改动不受限
|
||||
# 退出码 2 = 拦截(stderr 回传模型);0 = 放行;任何解析异常一律放行(守卫是兜底,不能挡死会话收口)
|
||||
proj="${ZCODE_PROJECT_DIR:-$PWD}"
|
||||
_payload=""
|
||||
IFS= read -r -d '' _payload || true # 内建全量读 stdin;临时文件只在走 python 路时才落
|
||||
|
||||
# ── 规则闸+快速通道(2026-10-26 立,规约投递闸配套;纯 bash 内建,零额外进程)────────────
|
||||
# 规则闸:Edit/Write 写 design/、game/,或 Bash 命令以文件触碰动词+路径命中两域时,
|
||||
# 本会话读取记录须含对应规约 read: 标记(PostToolUse 规约投递闸读规约本体时记入);
|
||||
# 记录文件不存在=投递闸未运行(含子代理——不经守卫层,2026-10-26 实测)→ 本闸不启用。
|
||||
# 快速通道:Edit/Write 族目标在主工作区外(worktree)→ 车道判定本就放行,免 python+git 直接过。
|
||||
# 两段都遵守总纲:解析异常一律放行。
|
||||
_rg_norm() { # 路径归一:反斜杠→斜杠、小写、/d/ 形转 d:/ 形、折叠重复斜杠(内建零进程)
|
||||
local p="${1//\\//}"
|
||||
p="${p,,}"
|
||||
[[ $p =~ ^/([a-z])/(.*)$ ]] && p="${BASH_REMATCH[1]}:/${BASH_REMATCH[2]}"
|
||||
while [[ $p == *"//"* ]]; do p="${p//\/\//\/}"; done
|
||||
printf '%s' "$p"
|
||||
}
|
||||
_rg_tool=""; _rg_fp=""
|
||||
[[ $_payload =~ \"tool_name\"[[:space:]]*:[[:space:]]*\"([^\"]*)\" ]] && _rg_tool="${BASH_REMATCH[1]}"
|
||||
[[ $_payload =~ \"file_path\"[[:space:]]*:[[:space:]]*\"([^\"]*)\" ]] && _rg_fp="${BASH_REMATCH[1]}"
|
||||
[ -z "$_rg_fp" ] && [[ $_payload =~ \"notebook_path\"[[:space:]]*:[[:space:]]*\"([^\"]*)\" ]] && _rg_fp="${BASH_REMATCH[1]}"
|
||||
_rg_low="$(_rg_norm "$_rg_fp")"
|
||||
_rl_sid="${CLAUDE_SESSION_ID:-}"
|
||||
_rl_ledger="${TMPDIR:-/tmp}/zcode-rule-ledger/${_rl_sid}.ledger"
|
||||
if [ -n "$_rl_sid" ] && [ -f "$_rl_ledger" ]; then
|
||||
_rl_marks="$(<"$_rl_ledger")"
|
||||
_rg_need() { # 域规约标记齐备→0(内建子串匹配,零进程)
|
||||
if [ "$1" = doc ]; then
|
||||
[[ $_rl_marks == *read:doc-conventions.md* && $_rl_marks == *read:concept-conventions.md* ]]
|
||||
return
|
||||
fi
|
||||
[[ $_rl_marks == *read:code-conventions.md* && $_rl_marks == *read:test-conventions.md* ]]
|
||||
}
|
||||
_rg_block() { printf '%s\n' "$1" >&2; exit 2; }
|
||||
case "$_rg_tool" in
|
||||
Edit|Write|MultiEdit|NotebookEdit)
|
||||
case "$_rg_low" in
|
||||
*/design/*|design/*)
|
||||
_rg_need doc || _rg_block "[规则闸] 写入 design/ 前本会话未读过 doc-conventions.md 与 concept-conventions.md——先用 Read 完整读这两份规约再继续(读完自动记读取记录)。" ;;
|
||||
*/game/*|game/*)
|
||||
_rg_need code || _rg_block "[规则闸] 写入 game/ 前本会话未读过 code-conventions.md 与 test-conventions.md——先用 Read 完整读这两份(或把读文件操作改用 Read 工具),读完自动记读取记录。" ;;
|
||||
esac
|
||||
;;
|
||||
Bash)
|
||||
# 命令文本(含描述字段)里「文件触碰动词+域路径」同现 → 按域核对读取记录
|
||||
_lp="${_payload,,}"
|
||||
if [[ $_lp =~ (^|[^a-z])(cat|head|tail|sed|less|more|grep|rg|awk|find|cp|mv|tee|wc|sort|diff)([^a-z]|$) || $_lp == *">"* ]]; then
|
||||
[[ $_lp =~ (^|[^a-z])design/ ]] && { _rg_need doc || _rg_block "[规则闸] 用 Bash 触碰 design/ 前本会话未读过 doc-conventions.md 与 concept-conventions.md——先用 Read 完整读这两份(或把读文件操作改用 Read 工具),读完自动记读取记录。"; }
|
||||
[[ $_lp =~ (^|[^a-z])game/ ]] && { _rg_need code || _rg_block "[规则闸] 用 Bash 触碰 game/ 前本会话未读过 code-conventions.md 与 test-conventions.md——先用 Read 完整读这两份(或把读文件操作改用 Read 工具),读完自动记读取记录。"; }
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
# 快速通道:Edit/Write 族且目标路径在主工作区外(worktree)——车道判定本就放行,免 python+git
|
||||
case "$_rg_tool" in
|
||||
Edit|Write|MultiEdit|NotebookEdit)
|
||||
_rg_projn="$(_rg_norm "$proj")"
|
||||
if [ -n "$_rg_fp" ] && [ "${_rg_low#"$_rg_projn"/}" = "$_rg_low" ]; then
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
tmp="$(mktemp)"
|
||||
printf '%s' "$_payload" > "$tmp"
|
||||
projw="$(cygpath -w "$proj" 2>/dev/null || echo "$proj")"
|
||||
python - "$projw" "$(cygpath -w "$tmp" 2>/dev/null || echo "$tmp")" <<'PY'
|
||||
import json, os, re, subprocess, sys
|
||||
|
||||
try:
|
||||
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def winpath(p):
|
||||
# MSYS 风格路径(/d/...)原生 Windows Python 不识别,统一转 Windows 形式
|
||||
if isinstance(p, str) and re.match(r"^/[A-Za-z](/|$)", p):
|
||||
try:
|
||||
r = subprocess.run(["cygpath", "-w", p], capture_output=True, text=True, timeout=5,
|
||||
encoding="utf-8", errors="replace")
|
||||
return r.stdout.strip() or p
|
||||
except Exception:
|
||||
return p
|
||||
return p
|
||||
|
||||
proj = os.path.abspath(winpath(sys.argv[1]))
|
||||
try:
|
||||
with open(sys.argv[2], encoding='utf-8') as f:
|
||||
d = json.load(f)
|
||||
except Exception:
|
||||
sys.exit(0)
|
||||
|
||||
tool = d.get("tool_name") or d.get("tool") or ""
|
||||
ti = d.get("tool_input") or {}
|
||||
cwd = os.path.abspath(winpath(d.get("cwd") or proj))
|
||||
|
||||
def in_dir(p, root):
|
||||
p = os.path.normcase(os.path.abspath(p))
|
||||
root = os.path.normcase(os.path.abspath(root))
|
||||
return p == root or p.startswith(root.rstrip('\\/') + os.sep)
|
||||
|
||||
def trunk_branch(repo):
|
||||
try:
|
||||
r = subprocess.run(["git", "-C", repo, "branch", "--show-current"],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
encoding="utf-8", errors="replace")
|
||||
return r.stdout.strip()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
TRUNK = {"master", "main"}
|
||||
|
||||
# 车道铁律(2026-10-31 恢复):主工作区只作合并落点——agent 的 Edit/Write 族写主工作区即拦;
|
||||
# 豁免 .zcode/ 与 .qoder/(本机件不入 git,worktree 带不进去);worktree 目标已在快速通道放行
|
||||
if tool in ("Edit", "Write", "MultiEdit", "NotebookEdit"):
|
||||
fp = ti.get("file_path") or ti.get("notebook_path") or ""
|
||||
if fp:
|
||||
fpn = os.path.abspath(winpath(fp))
|
||||
if in_dir(fpn, proj):
|
||||
rel = os.path.relpath(fpn, proj).replace("\\", "/").lower()
|
||||
if not rel.startswith(".zcode/") and not rel.startswith(".qoder/"):
|
||||
sys.stderr.write(
|
||||
"[车道铁律] 主工作区只作合并落点,改动走独立 worktree——"
|
||||
"git worktree add ../g-plan2-<任务名> -b <任务分支>,在区里改与提交;"
|
||||
"豁免面仅 .zcode/ 与 .qoder/。\n")
|
||||
sys.exit(2)
|
||||
sys.exit(0)
|
||||
|
||||
cmd = ti.get("command", "") or ""
|
||||
toks = cmd.split()
|
||||
has_commit = False
|
||||
for i, t in enumerate(toks):
|
||||
if t.rstrip(';') != 'git':
|
||||
continue
|
||||
j = i + 1
|
||||
while j < len(toks) and j - i <= 5:
|
||||
u = toks[j].rstrip(';')
|
||||
if u == "commit":
|
||||
has_commit = True
|
||||
break
|
||||
if u.startswith("-"):
|
||||
if u in ("-C", "--git-dir", "-c") and j + 1 < len(toks):
|
||||
j += 2
|
||||
continue
|
||||
j += 1
|
||||
continue
|
||||
break
|
||||
# 主工作区判定:cwd 在内,或命令文本里显式指到主工作区路径(不含 worktree 兄弟目录)
|
||||
norm = cmd.replace("\\", "/").lower()
|
||||
ref_main = re.search(r"/d/code/g-plan2(?![\w-])", norm) or re.search(r"d:/code/g-plan2(?![\w-])", norm)
|
||||
ref_wt = re.search(r"/g-plan2-[\w-]+", norm)
|
||||
sibling = bool(ref_wt) and not ref_main
|
||||
on_trunk = trunk_branch(proj) in TRUNK
|
||||
in_main = (in_dir(cwd, proj) or bool(ref_main)) and on_trunk
|
||||
if not in_main or sibling:
|
||||
sys.exit(0)
|
||||
# 车道铁律(2026-10-31 恢复):主工作区禁止一切 git commit——令回 worktree;合并类命令不在本分支,
|
||||
# 冲突/前置检查红态(MERGE_HEAD 在)的 git commit 续合同拦——一律 abort → 回 worktree → 重新 git merge --no-ff
|
||||
if has_commit:
|
||||
# --no-verify/-n 仅属用户紧急出口,agent 禁用(词法匹配兜底;
|
||||
# 只扫 -m 之前的参数区,防 commit 消息里提及该旗标名造成误拦)
|
||||
_flag_zone = toks
|
||||
for _i, _t in enumerate(toks):
|
||||
if _t.rstrip(';') == "-m":
|
||||
_flag_zone = toks[:_i]
|
||||
break
|
||||
if any(u.rstrip(';') in ("--no-verify", "-n") for u in _flag_zone):
|
||||
sys.stderr.write(
|
||||
"[车道铁律] git commit --no-verify 仅属用户紧急出口,agent 禁用——"
|
||||
"移除该旗标重新提交;改动走独立 worktree。\n")
|
||||
sys.exit(2)
|
||||
# 主区一切 git commit 一律拦(含 MERGE_HEAD 态续合),不再探测 MERGE_HEAD;
|
||||
# 合并提交只由 git merge --no-ff 一步落地,两合回钩子必触发。
|
||||
sys.stderr.write(
|
||||
"[车道铁律] 主工作区禁止一切 git commit(含 MERGE_HEAD 态续合)——改动走独立 worktree;"
|
||||
"主区唯一合法写动作=git merge --no-ff。"
|
||||
"若正处合并冲突/前置检查红态:一律 git merge --abort → 回 worktree 处理 → 重新 git merge --no-ff <任务分支>。\n")
|
||||
sys.exit(2)
|
||||
sys.exit(0)
|
||||
PY
|
||||
rc=$?
|
||||
rm -f "$tmp"
|
||||
exit $rc
|
||||
Reference in new issue
Block a user