📦 Wip — WIP(进行中的工作)

v1.9.82

Branch Guard 通过拦截非法写入和破坏性命令来强制执行分支纪律,首次写入前必须阅读入门文档,跟踪重试次数……

3· 3·0 当前·0 累计
by @parkertoddbrooks (Parker Todd Brooks)
下载技能包
最后更新
2026/4/21
0
安全扫描
VirusTotal
无害
查看报告
OpenClaw
安全
medium confidence
该技能的代码、说明及所需配置均与本地“branch guard”工具保持一致,用于强制执行写入/PR 规则;它不会索取无关凭据或远程下载,但需安装本地 Node 脚本、向 ~/.ldm/state 写入状态并编辑 hook 配置,请谨慎审查/安装。
评估建议
此技能如其名:一个本地 hook,用于检查 git/command 上下文,未经批准则阻止危险写入和外部 PR 创建。安装前:1)审阅 guard.mjs(它在本地运行,调用 git/gh,并写入 ~/.ldm/state 与 ~/.ldm/extensions),确保你清楚会创建和记录什么。2)将示例 INSTALL.md 中的路径(示例用 /Users/lesa)改为你自己的系统路径。3)注册后,hook 会被 agent 自动调用(在完成 onboarding 读取前会阻止写入)。4)若你有自动流程会在仓库写文件,先在安全环境测试 guard,因为它故意拦截多种写模式。5)如需更多保障,本地运行附带的 test.sh,观察行为后再注册 hook。若需要,我可以完整扫描 guard.mjs,检查缺失导入、意外网络调用或隐蔽数据传输——提供完整未截断的 guard.mjs 文本,我会逐行核查。...
详细分析 ▾
用途与能力
名称/描述(强制分支规范、阻止破坏性命令、要求入职必读、限制 PR 创建)与所含文件及运行时行为一致:guard.mjs 检查 git 状态、解析命令,并持久化本地会话状态。实现所述目的无需无关凭据、二进制文件或外部服务。
指令范围
SKILL.md / INSTALL.md 要求安装本地 Node hook,并在 ~/.claude/settings.json 中添加 PreToolUse/SessionStart 条目,同时将 guard.mjs 复制到 ~/.ldm/extensions。这需要写入文件系统,并通过 execSync 执行本地 git/gh 命令——这是强制仓库规范的 pre-tool hook 所必需的。两点小提示:(1) 示例 INSTALL.json 使用了硬编码路径 (/Users/lesa),仅为示例,安装时必须自行修改;(2) guard 会故意将拒绝和覆盖记录到本地审计文件 (~/.ldm/state/bypass-audit.jsonl),因此安装过程会创建并写入本地文件。
安装机制
没有远程下载/安装配置;该技能仅提供指令,并随附源代码文件,需复制到用户的 extensions 文件夹。这比在运行时获取任意代码风险更低。guard.mjs 将其辅助模块内联,以避免子目录安装错误;这种做法可解释,且无需外部依赖包。
凭证需求
该技能未声明必需的 env 变量或密钥凭据。它读取 HOME 以及可选的 LDM_GUARD_STATE_DIR 用于测试,并记录了一个合法的覆盖环境变量(LDM_GUARD_UPSTREAM_PR_APPROVED),用于批准外部 PR 创建。这些环境访问与工具的用途相符。
持久化与权限
守卫将每会话状态和审计日志写入 ~/.ldm/state,并计划注册为 PreToolUse/SessionStart 钩子。这使其具备持久本地存在并可拦截、拒绝工具调用;对钩子而言属预期行为。它并非 'always: true'。由于代理默认可自主调用已安装钩子,请考虑:当代理触发匹配工具时,已安装的守卫将自动运行。
guard.mjs:180
检测到 Shell 命令执行 (child_process)
guard.mjs:30
访问环境变量并发送网络请求
guard.mjs:12
文件读取结合网络发送(可能存在数据外泄)。
安全有层次,运行前请审查代码。

运行时依赖

无特殊依赖

版本

latestv1.9.822026/4/21

# wip-branch-guard v1.9.82 修复跨会话状态冲突,移除 env-var 逃生舱,阻止 Layer 3 对临时目录写入触发 关闭 #253 ## 额外修复:Layer 3 的 onboarding/阻塞文件闸门跳过临时目录与共享状态 Bash 写入 Layer 3 的 onboarding 与阻塞文件追踪闸门,会对任何提取出的写入目标列表非空的 Bash 命令触发,包括写入 `/tmp`、`/var/tmp`、`/var/folders/.../T/`。临时目录白名单仅存在于 `ALLOWED_BASH_PATTERNS` 且仅作用于 Layer 1(主分支写入拦截);Layer 3 先执行并拒绝,给出 onboarding 提示,尽管 `/tmp` 不在任何 git 仓库内。 症状:在新会话仓库主分支执行 `cp source /tmp/x` 被拦截并提示“需要 onboarding”。 2026-04-21 的 Phase 12 审计测试在 `test.sh` 中首次暴露;8 个临时目录测试用例(`cp/mv/rm/mkdir/touch/>/tee` 到 `/tmp` 与 `cp` 到 `/var/tmp`)全部失败。 修复:在 Layer 3 调用前过滤 `writeTargets`,排除临时路径与共享状态路径,与 Layer 1 白名单对称,并与 Edit/Write 工具已有的 `isSharedState` 跳过一致。`extractWriteTargets` 本身不变;过滤位于 Layer 3 调用处,保持函数通用。 ## 该 bug 机器上每个 Claude Code 会话都写入同一文件 `~/.ldm/state/guard-session.json`。guard 的 `detectNewSession()` 在 `session_id` 不匹配时触发,会清空整个状态文件(`onboarded_repos_canonical`、`read_files`、`recent_denials`)再写入自身状态。 Parker 默认同时运行多个 CC 会话,任一会话的工具调用会覆盖其他会话的 onboarding 与读取追踪状态。 症状:代理读取仓库 `README.md` + `CLAUDE.md`,首次写入成功,随后同一工作区的下一次 Write 被 guard 要求重新读取相同文档。2026-04-21 的 dogfood 测试中,代理在同一会话内重复读取 onboarding 文档三次仍被拦截。 根因:一台机器共用一份状态文件。 ## 修复 ### 每会话独立状态文件 状态现存放于 `~/.ldm/state/guard-session-<session_id>.json`。每个 CC 会话独占文件,跨会话乒乓从源头消除:不同会话写不同文件,互不覆盖。 `statePathFor()` 内的净化器将 `session_id` 映射为安全文件名段(字母数字、短横线、下划线;最多 64 字符),防止异常 session ID 逃逸状态目录。 ### 同会话内基于锁文件的原子写 同一会话并行工具调用(如一次助手回合触发四个 Read)也在状态文件上竞争读写。`writeSessionState()` 现通过 `openSync(..., 'wx')` 获取锁文件 (`guard-session-<sid>.json.lock`),2 秒获取预算,10 秒过期回收。锁失败退化为尽力写:每会话文件修复是主承重,锁为双保险。 ### TTL 清理 每会话文件随时间累积。guard 每次调用运行 `cleanupStaleStateFiles()`,删除 24 小时前的 `guard-session-*.json` 或 `.lock`。小状态目录的 `readdirSync` 耗时亚毫秒,扫描成本可忽略。 ### 已移除:`LDM_GUARD_SKIP_ONBOARDING` 与 `LDM_GUARD_ACK_BLOCKED_FILE` 这两个 env var 原为上述状态 bug 的逃生舱。bug 已根修,逃生舱只会训练代理绕过 guard:每次“请设此 env var 解堵”都是设计内的 workaround 生效。现两变量被忽略。 唯一保留的覆盖 `LDM_GUARD_UPSTREAM_PR_APPROVED` 为合法人工授权(Parker 批准向上游仓库提 PR),非 guard bug 绕过。 onboarding 与阻塞文件重试闸门的拒绝信息不再提示设置这些 env var。 `approvalCheck` 审计项 `skip-onboarding-approved` 与 `ack-blocked-file-approved` 已移除(不再触发)。 ## diff 内容 - `tools/wip-branch-guard/guard.mjs` 新增:`statePathFor()`、`withStateLock()`、`cleanupStaleStateFiles()`、每会话状态常量 变更:`readSessionState()` 接收 `sessionId`;`writeSessionState()` 经锁文件写往每会话路径 移除:`main()` 中 `detectNewSession` 清空逻辑(每会话文件使其过时);移除 env var 的 `approvalCheck` 调用与审计项;拒绝信息中的逃生舱提示 - `tools/wip-branch-guard/test.sh` 翻转:`LDM_GUARD_SKIP_ONBOARDING` / `LDM_GUARD_ACK_BLOCKED_FILE` 测试现断言 env var 被忽略(预期拒绝) 新增:“跨会话状态隔离”回归测试块(6 用例 + 2 磁盘存在断言)复现乒乓场景 - `tools/wip-branch-guard/SKILL.md` 文档更新:每会话状态结构、版本历史 v1.9.82 条目、覆盖表仅列剩余 env var、Layer 3 章节移除 env var 说明 - `tools/wip-branch-guard/package.json` 版本号 1.9.81 → 1.9.82 ## 测试计划 ```bash tools/wip-branch-guard/test.sh ``` 预期:95 通过,0 失败,8 跳过(仅当测试运行器自身 CWD 位于 main 时运行的 on-main-branch 用例)。 关键回归用例: - `iso: session A still onboarded after B's activity`(精确复现 bug) - `iso: per-session file for A exists on disk` - `onboarding: LDM_GUARD_SKIP_ONBOARDING=1 IGNORED (still denies)` ## 迁移 旧文件 `~/.ldm/state/guard-session.json` 安装后成孤儿。v1.9.82 不再读写,可安全删除: ```bash rm -f ~/.ldm/state/guard-session.json ``` `cleanupStaleStateFiles()` 不处理它(正则匹配 `guard-session-*.json`),留也无害,直到手动清理。 ## 接受的权衡 移除 env-var 逃生舱后,若 guard 再出故障,代理将真被卡死:唯一出路是打补丁重装。我们接受,因为保留逃生舱会维持 workaround 循环。补偿路径为 SKILL.md 记录的“安装即逃生舱”(回滚到旧标签 `ldm install /tmp/toolbox-old`)及 bypass 审计日志,后者记录每次拒绝及上下文,可在分钟级而非小时级定位下一状态 bug。 ## 本 PR 未处理(仍开启) 修复审计发现三项缺口,此处未修,已另开 issue,供下次 guard PR 拾取: - 缺口 A:无主动 SessionStart 扫描。onboarding 闸门为被动(首次写入触发,非会话开始或首次 cd)。 - 缺口 B:shell 重定向漏洞。guard 拦截 Edit/Write 与 `python -c` / `node -e`,但未对 Bash `>`、`>>`、`tee` 向受保护路径(非 `.worktrees/`、`/tmp` 等)做模式匹配。 - 缺口 C:被动绕过审计。`~/.ldm/state/bypass-audit.jsonl` 记录拒绝与(本 PR 前)env-var 覆盖,但会话起止未解析该文件向 Parker 提示重复绕过。 ## 协作者 Parker Todd Brooks、Lēsa(oc-lesa-mini,Opus 4.7)、Claude Code(cc-mini,Opus 4.7)。

无害

安装命令

点击复制
官方npx clawhub@latest install wip-branch-guard
镜像加速npx clawhub@latest install wip-branch-guard --registry https://cn.longxiaskill.com

技能文档

|---| | git 仓库 main 分支 | 拒绝 | | 功能分支,未在关联 worktree | 拒绝 | | 功能分支,已在关联 worktree | 允许 | | 共享状态路径(见下) | 始终允许 | | 不在任何 git 仓库 | 允许 |

共享状态路径(始终允许): ~/.claude/plans/~/.claude/projects//memory/~/.claude/rules/~/.openclaw/workspace/~/.openclaw/extensions/~/.ldm/shared/~/.ldm/messages/~/.ldm/templates/~/.ldm/extensions/~/.ldm/logs/~/.ldm/agents//memory/daily/.md~/.ldm/memory/shared-log.jsonl~/.ldm/memory/daily/.mdworkspace/SHARED-CONTEXT.mdworkspace/TOOLS.mdworkspace/MEMORY.mdworkspace/IDENTITY.mdworkspace/SOUL.mdworkspace/WHERE-TO-WRITE.mdworkspace/HEARTBEAT.mdworkspace/memory/.mdCLAUDE.md

worktree 约定:.worktrees/--/。 引导复合命令(git worktree add .worktrees/... && mkdir -p .../ai/... && cp src dest)显式允许。

第二层 … 破坏性命令拦截(任意分支)

无论分支,一律拒绝:
  • git clean -f(删除未跟踪文件)
  • git checkout -- (还原文件)
  • git checkout .(还原全部)
  • git stash drop / pop / clear(销毁暂存)
  • git reset --hard(清除未提交更改)
  • git restore (还原文件;--staged 安全且允许)
  • python -c "open().write()" / node -e "writeFile()"(脚本语言绕过写入)
  • --no-verify(跳过 git hooks)
  • --force-with-leasegit push --force

引号内容在匹配前被剥离,因此 gh issue create --body 'use git checkout -- to fix' 被允许。

第三层 … 会话级门控(1.9.77 新增)

1. 首次写入前 onboarding

会话中对任何 git 仓库的首次写入前,守卫要求代理已用 Read 工具读取仓库根目录指定 onboarding 文档。 检查自动从 Read 工具调用(Read|Glob|Write|Edit|NotebookEdit|Bash)中捕获。 必须读取:README.mdCLAUDE.md 及根目录下匹配 RUNBOOK.mdLANDMINES.mdWORKFLOW*.md 的文件。 TTL:onboarding 后 2 小时活动有效,新会话或超时失效。

任何仓库的入门仪式: ``

  • git rev-parse --show-toplevel # 确认仓库
  • Read README.md # 用 Read 工具,非 cat
  • Read CLAUDE.md # 若存在
  • Read RUNBOOK / LANDMINES / WORKFLOW # 若存在于根目录
  • 继续 Write/Edit
` 无覆盖。 v1.9.82 前可用 LDM_GUARD_SKIP_ONBOARDING 绕过,已移除;真正阅读文档是唯一途径。

2. 最近拦截文件追踪

每次拦截对某文件的写入时,记录
{ts, path, tool, command_stripped} 至会话最近拒绝队列(最近 20 条,1 小时窗口)。 若后续写文件工具再次命中同一路径,守卫再次拒绝并附上前次拦截上下文。 捕获模式:Edit X → 被拒 → 代理用 cat > X 重试,第二次仍被拦截为“等效绕过”。 无覆盖。 v1.9.82 前可用 LDM_GUARD_ACK_BLOCKED_FILE 承认并继续,已移除;若拦截有误,修正根因而非绕过。

3. 外部 PR 创建门控(1.9.80 新增)

拒绝:
  • gh pr create --repo /
  • gh pr create --repo / --head :(跨 fork)
  • 当 cwd 的 git origin 为 / 时的 gh pr create [--web]
  • gh api repos///pulls ... -X POST

允许:

  • 所有者为 wipcomputer/ 的上述命令
  • 对任意仓库的 gh pr viewgh pr listgh pr mergegh pr edit(只读/交互,不创建)
  • gh api repos///issues ... -X POST(创建 issue,非 PR)

触发事件:2026-04-18 PR #89 事件,代理未经批准直接向 steipete/imsg 提 PR。 覆盖:LDM_GUARD_UPSTREAM_PR_APPROVED=/`(目标

数据来源ClawHub ↗ · 中文优化:龙虾技能库