
gstack /guard 全安全模式用 PreToolUse 钩子组合实现破坏性命令警告与目录级编辑边界【免费下载链接】gstackUse Garry Tans exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstack/guard是 gstack 技能套件中的“最大安全”技能它把/careful破坏性命令警告与/freeze目录级编辑边界两条防护组合到一条命令里用于触碰生产环境或调试线上系统时的“锁死”场景。本文基于 guard/SKILL.md 与 guard/SKILL.md.tmpl 展开并结合 careful/bin/check-careful.sh、freeze/bin/check-freeze.sh 及共享解析器 careful/bin/hook-extract.sh 的源码讲清这套“警告 边界”双保险的实际判定逻辑、fail-closed 设计取向与状态文件机制读完你可以复现完整的启用流程并理解每一层防护为什么这样实现。/guard 的定位与触发条件/guard的官方描述是 “Full safety mode: destructive command warnings directory-scoped edits”即激活两层保护破坏性命令守卫——rm -rf、DROP TABLE、force-push 等命令在执行前弹出警告可被用户覆盖其中灾难级形态递归删除/或~、对默认分支 force-push是硬拒绝hard-deny编辑边界—— 文件编辑被限制在用户指定的目录内边界外的 Edit/Write 直接拦截。从 guard/SKILL.md.tmpl 的 frontmatter 可以看到它的完整注册信息name: guardversion: 0.1.0并标记sensitive: true触发词triggersfull safety mode、guard against mistakes、maximum safetyallowed-toolsBash、Read、AskUserQuestion关键机制在hooks.PreToolUse按 matcher 注册了三个 PreToolUse 钩子——hooks: PreToolUse: - matcher: Bash hooks: - type: command command: bash $HOME/.claude/skills/gstack/careful/bin/check-careful.sh statusMessage: Checking for destructive commands... - matcher: Edit hooks: - type: command command: bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh statusMessage: Checking freeze boundary... - matcher: Write hooks: - type: command command: bash $HOME/.claude/skills/gstack/freeze/bin/check-freeze.sh statusMessage: Checking freeze boundary...也就是说/guard本身不携带新的检测逻辑它的核心动作是同时注册两套钩子Bash 工具调用前执行check-careful.shEdit/Write 工具调用前执行check-freeze.sh。依赖说明原文档明示该技能引用的是同级/careful与/freeze技能目录下的钩子脚本两者必须已安装——gstack 的 setup 脚本会把它们一起装好。这也解释了为什么 careful/ 与 freeze/ 目录下各有一个bin/子目录存放钩子脚本而guard/下只有 SKILL.md。技能激活时还会追加一条使用遥测原文档自带的标准片段mkdir -p ~/.gstack/analytics echo {skill:guard,ts:$(date -u %Y-%m-%dT%H:%M:%SZ),repo:$(basename $(git rev-parse --show-toplevel 2/dev/null) 2/dev/null || echo unknown)} ~/.gstack/analytics/skill-usage.jsonl 2/dev/null || true注意 SKILL.md 头部标注了AUTO-GENERATED from SKILL.md.tmpl — do not edit directly真正的维护入口是.tmpl模板文件通过bun run gen:skill-docs重新生成。启用流程Setup 与状态文件/guard的启用流程由技能正文驱动步骤如下完整继承自原文档询问边界目录。使用 AskUserQuestion 向用户提问且必须是文本输入不是多选Guard mode: which directory should edits be restricted to? Destructive command warnings are always on. Files outside the chosen path will be blocked from editing.把用户提供的路径解析为绝对路径FREEZE_DIR$(cd user-provided-path 2/dev/null pwd) echo $FREEZE_DIR补上结尾斜杠并写入 freeze 状态文件FREEZE_DIR${FREEZE_DIR%/}/ eval $(~/.claude/skills/gstack/bin/gstack-paths) STATE_DIR$GSTACK_STATE_ROOT mkdir -p $STATE_DIR echo $FREEZE_DIR $STATE_DIR/freeze-dir.txt echo Freeze boundary set: $FREEZE_DIR其中gstack-paths提供$GSTACK_STATE_ROOTgstack 的状态根目录freeze-dir.txt是边界的唯一持久化载体——check-freeze.sh每次 Edit/Write 前都会重新读取它。向用户播报激活结果原文档给出的标准话术“Guard mode active.Two protections are now running:”“1.Destructive command guard— rm -rf, DROP TABLE, force-push, etc. warn before executing (overridable); catastrophic shapes (recursive delete of / or ~, force-push to the default branch) are hard-denied”“2.Edit boundary— file edits restricted topath/. Edits outside this directory are blocked.”“To remove the edit boundary, run/unfreeze. To deactivate everything, end the session.”退出路径分两级/unfreeze只清除编辑边界删除freeze-dir.txt见 unfreeze/SKILL.md而整个 guard 会话的破坏性命令警告随会话结束自动失效——钩子是 session-scoped 的。第一层防护/careful 破坏性命令守卫被保护的命令族完整清单模式示例风险rm -rf/rm -r/rm --recursiverm -rf /var/data递归删除DROP TABLE/DROP DATABASEDROP TABLE users;数据丢失TRUNCATETRUNCATE orders;数据丢失git push --force/-fgit push -f origin main历史重写git reset --hardgit reset --hard HEAD~3丢失未提交工作git checkout ./git restore .git checkout .丢失未提交工作kubectl deletekubectl delete pod生产环境影响docker rm -f/docker system prunedocker system prune -a容器/镜像丢失安全例外单独清理构建产物不触发警告——rm -rf node_modules/.next/dist/__pycache__/.cache/build/.turbo/coverage直接放行。工作机制hookSpecificOutput 与两级决策careful/SKILL.md 对机制的原始描述是钩子从工具输入 JSON 中读取命令匹配上述模式后返回permissionDecision: ask的hookSpecificOutput负载决策必须嵌套在hookSpecificOutput下——顶层的permissionDecision会被 Claude Code 忽略警告会静默失效。所有 MEDIUM 警告均可被用户覆盖放行。落到 careful/bin/check-careful.sh 源码判定管线是这样的1真 JSON 解析解析失败即 fail-closed。脚本不再用早期的grep -o command\s*:\s*[^]*提取器——那种写法在 JSON 字符串内第一个转义引号处截断git commit -m wip rm -rf /会被截成git commit -m 从而整体漏检脚本注释里专门记录了这三个历史漏检样例。现在通过共享函数gstack_hook_extract_fieldpython3 优先、node 兜底提取command字段payload 非空却解析失败时返回ask决策“Cannot safety-check this command. Approve only if you know what it does.”。2shell 混淆绊线obfuscation tripwire。由于所有检查都是“按字符串匹配”而 bash 执行的是“展开后的含义”脚本对${IFS}字面量与 base64 解码管道到 shell 的形态直接触发ask。注释里举的例子很直白rm${IFS}-rf${IFS}/不匹配任何rm\s模式但执行时就是完整的递归删除。3HIGH 级硬拒绝。只有 SIMPLE 命令不含;、、||、|、换行有资格进入硬拒绝复合命令一律降级为 MEDIUM 询问——“字符串匹配无法解析复合命令到底会做什么”。两个灾难形态递归删除/、~、$HOME源码先强制set -fnoglob防止/*被展开再对每个 token 做逐词判定——跳过选项与重定向装饰所有非选项 token 都必须是根级目标/、~、$HOME、/*等且允许剥一层引号同时必须存在递归标志-r/-R/--recursive大小写任意位置。check-careful.sh L101-L123 命中后返回deny对默认分支 force-pushforce 既可由-f/--force携带也可由 git 的ref语法如main、HEAD:main携带而无需任何标志--force-with-lease从不匹配它是安全变体。默认分支通过git symbolic-ref refs/remotes/origin/HEAD探测worktree 等缺少该符号引用的场景会回退探测origin/main、origin/master分支名比较用固定字符串 token 相等而非正则插值避免元字符导致误判。check-careful.sh L127-L176文档特别强调 HIGH 级是“尽力而为的建议性硬停止不是策略边界”逃生门就是结束这个 opt-in 的会话。4MEDIUM 级询问族。八个命令族依次匹配SQL 类先对命令做小写化每族有固定警告文案例如rm\s(-[a-zA-Z]*[rR]|--recursive)也覆盖了 BSD/macOS 的大写-R。命中后记录一次hook_fire分析事件只记模式名从不记命令内容然后返回ask。5安全例外的严格化。放行“单独删除构建产物”的规则是全命令锚定匹配而非只看最后一个rm——否则rm -rf / # rm -rf node_modules这种用注释藏前缀的写法会借白名单逃脱多行命令一律不走白名单目标 token 中出现(或反引号命令替换也不能骑白名单。check-careful.sh L198-L2066项目自定义规则只增不减。内置规则全未命中时才查阅~/.gstack/careful-patterns.txt全局或~/.gstack/projects/slug/careful-patterns.txt按项目slug 由gstack-slug解析。每行一条 POSIX ERE#注释与空行跳过非法正则整行跳过而不致命。由于这些文件只在内置族之后被咨询配置只能新增规则永远无法压制基线警告。性能上有个细节解析项目 slug 有子进程开销所以只有当projects/下确实存在某个careful-patterns.txt时才去解析。第二层防护/freeze 编辑边界/freeze 侧的行为定义在 freeze/SKILL.md任何指向允许路径之外的 Edit 或 Write 都会被拦截不是警告。/guard复用同一份状态文件freeze-dir.txt与同一份钩子脚本。freeze/bin/check-freeze.sh 的执行逻辑与源码级要点定位状态文件STATE_DIR${CLAUDE_PLUGIN_DATA:-$HOME/.gstack}状态文件为$STATE_DIR/freeze-dir.txtcheck-freeze.sh L34-L42。文件不存在 未配置边界 放行一切存在则读取首行只修剪首尾空白——历史上tr -d [:space:]会连内部空格一起删掉导致~/My Project/src这样的边界永远匹配不上。字面~/开头在这里被显式展开为$HOME变量不会自动展开波浪号。提取file_path并解析为绝对路径相对路径拼上pwd随后折叠重复斜杠、去掉结尾斜杠。符号链接按“最终组件”解析_resolve_path会把路径最后一个组件若是 symlink 时循环跟随上限 40 次防环最终组件尚不存在新建文件则退化为父目录解析目录部分用pwd -P归一化。这样“边界内的软链接指向边界外目标”的写操作会被按目标检查check-freeze.sh L92-L116。前缀匹配判定case $FILE_PATH in ${FREEZE_DIR}/* | ${FREEZE_DIR})命中则放行否则返回deny并记录一次boundary_deny分析事件check-freeze.sh L118-L134。freeze 目录名带结尾/正是为了防止/src误匹配/src-old。fail-closed 极性是这里最值得注意的设计payload 无法解析时返回deny“一个对读不懂的输入放行一切的边界钩子就称不上边界”能解析但没有file_path字段的非文件类工具负载则放行。这与 careful 的 ask 极性恰好相反——guard 同时运行两者所以两边各自的兜底方向都是“更保守的那一边”。原文档给出的几点边界说明必须保留结尾/防止前缀误匹配如/src匹配到/src-oldfreeze 只作用于 Edit 与 Write——Read、Bash、Glob、Grep 不受影响它防的是“误编辑”不是安全边界——sed这类 Bash 命令仍可修改边界外文件这正是一层 careful 存在的意义;退出方式/unfreeze或删除状态文件后钩子虽仍注册但无状态可读、放行一切结束会话则全部失效。共享 JSON 助手两个钩子只有一份解析实现careful/bin/hook-extract.sh 被两个钩子以source方式加载而非执行它存在本身就是对一次“实现漂移”事故的修复两个钩子曾各自维护一份 grep 提取器副本careful 的修掉了转义引号截断 bug 后freeze 那份静默地留着没修。现在任何解析修复落在这一个文件里两个钩子按构造同时受益。它提供四个函数函数职责关键细节gstack_hook_extract_field PAYLOAD FIELD从tool_input提取字符串字段python3 优先macOS 与多数 Linux 自带node 兜底解析失败返回非零极性由调用方决定careful→askfreeze→denygstack_hook_json_string TEXT把任意文本编码为 JSON 字符串字面量注释明确禁止用 printf/sed 拼接钩子 JSON路径含引号或换行会产出畸形 JSONClaude Code 会整体忽略该决策——“deny 在最关键的时刻静默失效”gstack_hook_decision DECISION REASON输出完整hookSpecificOutput信封决策必须嵌套在hookSpecificOutput下gstack_hook_log_fire SKILL PATTERN追加hook_fire分析事件只记模式名、从不记命令内容尊重GSTACK_HOME使测试不污染真实分析文件失败绝不影响钩子决策另外两个“安装残缺”场景的处理也体现了极性设计check-careful.sh在共享助手缺失时降级为ask它是 ask 级钩子绝不能静默check-freeze.sh在助手缺失时返回内联的denyJSON它是 deny 级钩子必须 fail closed且此时编码器就在加载失败的那个文件里只能内联。小结与延伸阅读/guard的价值不在于新增任何检测规则而在于用一次注册把“命令侧的 ask/deny 双级守卫”和“文件侧的 fail-closed 边界”组合成一个可一键启用的最大安全姿态并且两层共用一套带完整事故修复历史的 JSON 解析与 JSON 编码基础设施。关键事实对照技能定义与钩子注册guard/SKILL.md、guard/SKILL.md.tmpl命令侧实现careful/bin/check-careful.shHIGH 硬拒绝、MEDIUM 询问、混淆绊线、只增不减的项目规则与 careful/SKILL.md 的模式清单文件侧实现freeze/bin/check-freeze.sh 与 freeze/SKILL.md 的边界语义共享解析/编码/日志careful/bin/hook-extract.sh边界清除unfreeze/SKILL.md相关测试test/hook-scripts.test.ts版本演进背景CHANGELOG.md 中有关于/guard、/freeze、/careful在特定宿主版本下恢复工作、以及“guard 边界在曾经静默失效的路径上真正生效”的条目。适用前提与限制以上行为均针对当前仓库所携带的钩子脚本实现/guard依赖 gstack setup 脚本把/careful与/freeze一并安装到~/.claude/skills/gstack/下所有钩子均为会话级作用域且 careful 侧文档明确其硬拒绝是“建议性的、非策略边界”真正的硬保证来自结束 opt-in 会话这一逃生门本身。【免费下载链接】gstackUse Garry Tans exact Claude Code setup: 23 opinionated tools that serve as CEO, Designer, Eng Manager, Release Manager, Doc Engineer, and QA项目地址: https://gitcode.com/GitHub_Trending/gs/gstack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考