首页/文章/CLAUDE.md 管风格,硬规则放 hooks:在新加坡 Mac mini 上分清软提示与必拦门

CLAUDE.md 管风格,硬规则放 hooks:在新加坡 Mac mini 上分清软提示与必拦门

· Zumac.Ai

写进 CLAUDE.md 的「禁止」,为什么有时不灵

很多人把项目约定一股脑塞进 CLAUDE.md:命名风格、目录习惯,以及「不要改 .env」「不要 force-push」「不要外联未知地址」。风格类提示通常够用;真正要命的硬规则,却偶发失效——尤其是长工具循环之后,或会话做完一次 compact 压缩之后。

这不是「你没写清楚」那么简单。CLAUDE.md 本质是软提示:它进入上下文,引导 Claude 怎么写、怎么拆任务。上下文变长、被压缩、或中途插入大量工具输出时,模型对某一条「禁止」的注意力会下降。你事后问它,它可能还记得文件里写过;但当时那次 Bash,已经跑出去了。

社区里也有人把分工说得很直白:CLAUDE.md 管风格;必须每次生效的硬规则,放到 hooks。 本文就按这个分工,在一台常开的新加坡 Mac mini 上落地——不重复「升级后怎么冒烟测试」那篇,只讲什么该进 hooks、怎么写、怎么验证真的挂上了。

Claude 账号由你自己开通和管理;机器提供稳定合规的使用环境。下列步骤以当前 Claude Code 的 hooks 机制为准,字段名以你本机 /hooks 与官方文档为准。

一张表:软提示 vs 硬门

| 放这里 | 适合什么 | 不适合什么 | |--------|----------|------------| | CLAUDE.md | 命名、目录约定、评审口吻、默认分支策略说明、文档风格 | 「绝对不能」类安全闸:写密钥、危险 shell、推送规则 | | Skills / 参考文档 | 按需查阅的长说明、命令手册 | 每次工具调用前的强制拦截 | | PreToolUse hooks | 写文件前扫描、Bash 命令模式匹配、推送目标检查 | 事后才发现的审计(那是 PostToolUse) | | 权限 deny 列表 | 粗粒度禁用某类工具 | 复杂变体(伪装参数、间接调用)——往往不如 hook 稳 |

经验法则:

  1. 漏一次可以接受、下次提醒就行 → CLAUDE.md / Skills。
  2. 漏一次就伤数据或密钥 → PreToolUse hook,失败要能拦住(exit 2 或 JSON deny)。
  3. 只想事后记账 → PostToolUse;它拦不住已经发生的动作。
相关阅读:安全 hook 要失败即拦截、PreToolUse 拦 force-push 比纯 deny 更稳、升级后给 hook 做冒烟测试。本文补的是「放哪一层」;那三篇补的是「怎么写硬、怎么测」。

为什么长循环和 compact 会让软提示变脆

典型路径:

  1. 会话开始时,CLAUDE.md 完整进上下文,硬规则看起来都在。
  2. 几十轮读文件、跑测试、改代码后,上下文被工具输出挤满。
  3. compact / 摘要会保留任务目标,但某一条否定句不一定稳稳留在「下一步工具调用」的注意力里。
  4. 模型仍可能「大体按风格写」,却在某次 Bash 上忘记「禁止某类命令」。

另外,若你在同一次会话里改过 CLAUDE.md(或 MEMORY 类文件),旧版本 Claude Code 在 compact 后仍可能继续用压缩前的旧副本。较新的版本(例如 2.1.311 一带)修过这类「压缩后仍用旧 CLAUDE.md」的问题。实务上:硬规则不要只赌「文件改了就立刻全局生效」——拦截逻辑放 hooks;软文档改完后必要时新开会话,并先确认版本:

claude --version
# 或你本机等价命令;把版本记进项目笔记

版本过旧就先升级,再谈「改完 CLAUDE.md 是否被 compact 吃掉」。升级之后,再用你已有的冒烟命令确认 hooks 仍注册——那是另一篇的清单,这里不展开。

在新加坡 Mac mini 上落地:三层目录

把权威环境固定在一台整机独享的常开机器上,避免笔记本 A 改了 hooks、笔记本 B 还在靠一份过时的 CLAUDE.md。

mkdir -p ~/work/your-repo/.claude/hooks
cd ~/work/your-repo

建议分层:

  1. 根目录 CLAUDE.md:只留短的风格与导航(「怎么命名」「文档放哪」「默认用哪条分支说明」)。硬禁止改成一句话链接:「安全闸见 .claude/hooks/」。
  2. .claude/settings.json:注册 PreToolUse / 需要的 PostToolUse。
  3. .claude/hooks/*.sh:真正的匹配与 exit 2。

用 git 管这三层;日常只在这台 Mac mini 上 git pull 后开 Claude Code。SSH 看终端;需要桌面批准时用 UU 远程连接。

示例一:禁止写入密钥类文件(硬门)

CLAUDE.md 里写「不要提交密钥」不够。用 PreToolUse 在写文件前拦:

# .claude/hooks/deny-secret-paths.sh
#!/bin/bash
set -euo pipefail
# stdin 为工具调用 JSON(字段名以你当前版本为准)
payload=$(cat)
path=$(printf '%s' "$payload" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null || true)
case "$path" in
  *.env|*/.env|*/.env.*|*.pem|*/id_rsa|*/.mcp.json)
    echo "blocked: refuse writing secret-like path: $path" >&2
    exit 2
    ;;
esac
exit 0
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/deny-secret-paths.sh",
            "onFailure": "block"
          }
        ]
      }
    ]
  }
}

要点:命中用 exit 2;配置里尽量加 onFailure: "block",避免脚本缺依赖时 fail-open。matcher 字符串按你版本支持的工具名调整。

示例二:危险 Bash 模式(硬门)

把「不要 git push --force 到 main」「不要 rm -rf /」从 CLAUDE.md 挪到 hook:

# .claude/hooks/deny-dangerous-bash.sh
#!/bin/bash
set -euo pipefail
payload=$(cat)
cmd=$(printf '%s' "$payload" | jq -r '.tool_input.command // empty' 2>/dev/null || true)
# 示例模式:按需增删;宁可误拦再放行,也不要漏拦
if printf '%s' "$cmd" | grep -Eiq 'git[[:space:]]+push[[:space:]]+.*(--force|-f)|rm[[:space:]]+-rf[[:space:]]+/($|[[:space:]])|curl[[:space:]]+[^|]*\|[[:space:]]*sh'; then
  echo "blocked dangerous bash pattern: $cmd" >&2
  exit 2
fi
exit 0

挂到 PreToolUse,matcher 对准 Bash(或你环境里的 Shell 工具名)。复杂变体比纯 deny 列表更吃 hook;这与「force-push 变体测试」那篇结论一致。

示例三:风格与命名仍留在 CLAUDE.md(软提示)

这些继续放软层即可,例如:

  • 组件用 PascalCase,工具函数用 camelCase。
  • 提交说明用约定前缀;默认别在正文里贴长日志。
  • 改 UI 文案先搜现有词表,避免同义反复造词。

写短、可扫读。长手册拆成 Skills,需要时再加载——别把 CLAUDE.md 做成百科。软提示被 compact「记性变差」时,最多风格飘一点;硬门仍在 hooks 上。

十分钟验收(分工有没有生效)

在仓库根目录开 Claude Code(避免子目录启动漏加载项目 settings):

  1. /hooks(或设置里等价入口)能看到刚注册的 PreToolUse。
  2. 故意让助手「写入 .env」或「git push --force」——应被 hook 拒绝,而不是只靠口头提醒。
  3. 改一处命名风格相关的 CLAUDE.md 句子,确认助手仍大致遵守;这证明软层还在,但你不再把安全赌在它上面。
  4. claude --version 记录版本;若刚升级,再跑你的必拦冒烟命令(见升级冒烟测试文)。
  5. tmux 挂长会话;SSH 断开再连,hooks 配置仍在磁盘上,不依赖某一扇本地窗口。

为什么固定在新加坡常开 Mac mini

  • 一份守卫:hooks 脚本与 settings.json 只维护一份,远程用 SSH / UU 远程连接进入同一台整机。
  • 长任务不断电:笔记本合盖会睡;常开机器上跑长工具循环时,硬门始终在项目目录里。
  • 整机独享:项目钩子与本地密钥路径不与其他客户混用;退租抹机时配置一并结束。
  • 真 macOS 工具链:你测的 Bash / jq / git 与上线环境一致。

Zumac.Ai 提供位于新加坡的实体 Mac mini M6,整机独享,支持 SSH 与 UU 远程连接,FileVault 全盘加密,退租时抹机重装。新客户整机租用 ¥999/月起,季付 ¥2,697.30,半年付 6 送 1 为 ¥5,994,年付 12 送 2 为 ¥11,988;已购买客户续费保持原价。新客户首单自审核开通当天起租,续费及老客户新订单自付款当天起租。建议先租一个月,把「CLAUDE.md 变短 + hooks 变硬」在同一台机器上跑通。实际延迟取决于你本地的网络线路。

小结

  • CLAUDE.md 是软提示,适合风格与约定;长循环和 compact 后可能漏执行硬禁止。
  • 必须每次生效的规则放 PreToolUse hooks:exit 2 / JSON deny,安全类加 onFailure: "block"。
  • 会话中改过 CLAUDE.md 时,核对 Claude Code 版本,避免旧 compact 逻辑继续用旧副本;硬规则仍以 hooks 为准。
  • 把项目 hooks 固定在新加坡常开 Mac mini,用 git 统一,SSH 或 UU 远程连接日常接入。

有问题可以在 Telegram 联系 @singatao,或直接看 价格与付款周期。

Zumac.Ai 新加坡 Mac mini M6,整机独享,¥999/月起,联系 Telegram @singatao。
查看方案与费用 · 帮助中心

返回首页 · 更多文章

Telegram 客服