把Claude Code调成自己想要的样子

把Claude Code调成自己想要的样子

目录
  1. 起点:为什么动默认配置
  2. 三层防御 hooks
  3. 代码图谱 + hooks 的组合拳
  4. 技能库:把一次性脚本沉淀成资产
  5. 输出风格:插件与全局 CLAUDE.md
  6. 总结

01起点:为什么动默认配置

学习黄兴老师的《Claude Code 工程化实战》课程和查看一些 Claude Code 相关技术博客后,在日常使用 Claude Code 的过程中,我意识到一个很大的风险:大模型执行本地命令时,一旦失控就是灾难。

最直接的场景是 rm -rf。模型本意可能是删某个项目的 node_modules,但如果路径拼接出错、变量为空、或者干脆理解错了上下文,rm -rf / 或 rm -rf ~ 就会直接送到 shell。文件系统损坏只要一次。

更隐蔽的风险来自 skill 和 MCP。这些扩展机制让模型能调用任意脚本和服务,如果某个 skill 里写了”清理临时目录”的逻辑,而路径参数被错误注入,等发现时已经晚了。

所以我需要一层确定性的防护:不依赖模型的判断,而是在工具调用前用规则硬性拦截。这就是 hooks 的用武之地。

另一个现实问题是网络。我司访问 Anthropic 官方端点要走代理,所以 settings.json 里这两项是基本功:

~/.claude/settings.json
"env": {
  "ANTHROPIC_BASE_URL": "https://your-proxy.example.com",
  "CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1"
},
"model": "kimi-k3"

DISABLE_EXPERIMENTAL_BETAS 是后来加的——某次 beta 功能把上下文截断了,排查半天才发现是这个开关。

02三层防御 hooks

Claude Code 的 hooks 机制允许在工具调用前后插入自己的 shell 脚本。我在 PreToolUse 阶段拦危险命令和敏感文件写入,在 PostToolUse 阶段写审计日志。三层各自独立,任何一层失效不会拖垮其他层。

第一层:危险命令拦截

这是最核心的一层,直接给出完整代码——目前这套配置只有我自己在用,还在不断完善和精进中:

~/.claude/hooks/block-dangerous.sh
#!/bin/bash
set -euo pipefail

# Claude Code 通过 stdin 传入 JSON,包含 tool_input 等
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')

DANGEROUS_PATTERNS=(
    'rm -rf /'
    'rm -rf ~'
    'rm -rf \$HOME'
    '> /dev/sd'
    'mkfs\.'
    ':()\{:|:&\};:'              # fork bomb
    'chmod -R 777 /'
    'git push --force origin main'
    'git reset --hard origin'
    'DROP DATABASE'
    'DROP TABLE'
    'TRUNCATE'
    'curl.*\| *sh'
    'curl.*\| *bash'
    # 重定向到敏感文件
    '>\s*[^|&;]*\.env($|[[:space:]])'
    '>>\s*[^|&;]*\.env($|[[:space:]])'
    '>\s*[^|&;]*credentials\.json($|[[:space:]])'
    '>\s*[^|&;]*id_rsa($|[[:space:]])'
    # cp/mv 覆盖到敏感文件
    'cp\s+.*\s+[^|&;]*\.env($|[[:space:]])'
    'mv\s+.*\s+[^|&;]*\.env($|[[:space:]])'
)

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
    if [[ "$COMMAND" =~ $pattern ]]; then
        cat <<EOF
{
    "hookSpecificOutput": {
        "hookEventName": "PreToolUse",
        "permissionDecision": "deny",
        "permissionDecisionReason": "拦截危险命令模式: $pattern"
    }
}
EOF
        exit 2
    fi
done

echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'

几个值得说的细节:

  • 用 JSON 输出而不是 exit code:Claude Code 识别 permissionDecision: "deny" 之后会把原因返回给模型,让它换一种方式完成任务,而不是直接报错给用户。这点比单纯 exit 2 体验好太多。
  • SQL 用大写匹配:DROP TABLE 故意大小写敏感,因为小写 drop table 可能出现在注释或字符串里。这是误伤和漏防之间的权衡。
  • 正则字符类 [[:space:]]:macOS 的 BSD bash 对 \s 支持不稳定,用 POSIX 字符类最稳。

第二层:敏感文件保护

对 Edit / Write / MultiEdit 工具生效,按文件名、扩展名、目录三层防护:

按文件名
.env .env.local credentials.json id_rsa id_ed25519
按扩展名
.pem .key .p12 .pfx
按目录
.git/ .ssh/ node_modules/
为什么拦 node_modules
让模型改 node_modules 几乎总是误判——它该改的是 package.json。

第三层:全量审计

每次工具调用都追加一行到本地日志,17 行 bash:

~/.claude/hooks/audit-log.sh
INPUT=$(cat)
LOG_DIR="${CLAUDE_PROJECT_DIR:-.}/.claude/logs"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/audit-$(date +%Y-%m-%d).log"

TIMESTAMP=$(date -Iseconds)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "unknown"')
TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')

echo "[$TIMESTAMP] $TOOL_NAME: $TOOL_INPUT" >> "$LOG_FILE"

这份日志用过两次就值回票价:一次是追查谁在某个项目里执行了 git push,一次是发现模型在某个目录反复读同一个文件——后来定位到是那个文件的前 100 行有个注释把模型带进了死循环。

经验

hooks 写完之后,一定要测一遍拒路径。我第一次部署时,block-dangerous.sh 里有个正则写错了,所有命令都返回 allow,跑了一周才发现。现在我的 hook 脚本里都内置一个 --self-test 模式,CI 里跑一遍。

03代码图谱 + hooks 的组合拳

这是配置里最不常规的一部分。我用了一个叫 codebase-memory-mcp 的 MCP,它把代码库索引进一个图数据库,然后暴露 search_graph、trace_path、get_code_snippet 这些工具。

但我没把它当普通 MCP 用。我把它挂到了 5 个 hook 点上:

Hook 点触发时机作用
PreToolUse / Bash,Grep,Glob搜索类工具调用前把图索引结果注入,减少盲目搜索
PostToolUse / Read读文件之后追加这个文件在图谱里的调用关系
SessionStart × 4startup / resume / clear / compact会话开始时注入项目结构
SubagentStart子代理启动子代理也能看到图谱

效果是:模型每次要 Grep 一个函数名之前,hooks 已经把”这个函数在图谱里被谁调用、调用了谁”塞给它了。Grep 从”瞎找”变成了”验证”。

为什么不用纯 MCP?因为 MCP 工具是模型自己决定调不调用,而 hooks 是不请自来。对于一个”反正都要 Grep”的场景,hooks 的确定性更高。代价是每次 Grep 多 5 秒延迟——值得。

04技能库:把一次性脚本沉淀成资产

skills 是 Claude Code 的”按需加载的专业知识”。我的 ~/.claude/skills/ 里有 10 个,分三类:

业务诊断类

把日常运维流程固化成 skill:

  • mongo-diag — MongoDB 副本集诊断,查 Prometheus 指标,定位 writeConcern majority 写入延迟
  • cdn-bandwidth-analysis — CDN 带宽突增分析
  • ios-network-diag — iOS 网络问题排查
  • confluence-doc — 生成符合公司模板的 Confluence 文档

以前这些是 Confluence 上的 SOP 文档,每次执行都要翻。现在是 /mongo-diag 一句话触发,模型自己跑完整个流程。关键转变是:SOP 从”人看的文档”变成了”模型能执行的指令”。

元能力类

最有意思的是 safety-hooks 这个 skill——它把前面说的三层防御 hooks 打包成可一键安装的资产。同事想用,不用复制 bash 脚本,直接 /safety-hooks install。

这是个自指:hooks 保护 Claude,而部署 hooks 的动作本身也是个 skill,由 Claude 执行。

工具类

weread-skills(微信读书)、urlencode 这类小工具,本质是”我不想写临时脚本”的产物。

判断标准

一个操作如果一年里我要做 3 次以上,就值得做成 skill。低于这个频率,skill 的描述维护成本超过收益。

05输出风格:插件与全局 CLAUDE.md

我的全局 ~/.claude/CLAUDE.md 一共 11 行:

~/.claude/CLAUDE.md(完整)
# 个人偏好

## 沟通方式
- 使用中文回复
- 代码注释使用英文
- 解释简洁直接,不要过多铺垫

## 通用代码风格
- 缩进使用 4 空格
- 优先使用 async/await
- 变量命名使用 camelCase
- 常量命名使用 UPPER_SNAKE_CASE

只有个人偏好,没有项目细节。项目细节放项目自己的 CLAUDE.md。这是反复调整后的结论——全局文件里写项目特定的内容,只会在切换项目时互相污染。

插件方面,真正改变日常体验的是 i-have-adhd。它是个输出风格插件,强制模型:

  • 第一句话给行动指令,不要寒暄
  • 多步任务用编号列表
  • 列表超过 5 项必须分组
  • 结尾不要”有问题随时问”

我用一个 .i-have-adhd-always 标记文件让它永远开启。这个文件是空的,只是个开关——插件启动时检查文件是否存在。

起初我以为这只是”输出更简洁”,用了一个月发现真正的影响是:它把模型的回答从”文章”变成了”操作手册”。对于我这种读完就忘的工作记忆,这是质变。

06总结

这套配置的核心思路可以归纳成三条:

  • 安全优先于便利。模型再智能,也不该拥有无条件执行本地命令的权力。用 hooks 在工具调用前加一道确定性拦截,把”模型会不会犯错”变成”即使犯错也拦得住”。这是学习黄兴老师《Claude Code 工程化实战》课程后最重要的实践。
  • 把重复劳动沉淀成资产。SOP 文档、临时脚本、运维流程,凡是做过三次以上的事,都值得做成 skill 或 hook。前期投入一小时,后期每次调用都在省时。
  • 配置是迭代出来的,不是设计出来的。从一开始 5 行的 rm -rf 拦截脚本,到现在的三层防御 + 代码图谱 + 技能库,每一层都是踩坑之后加上的。不要试图一次写完美,先跑起来,再迭代。

Claude Code 的默认配置能干活,但只是毛坯。它真正的杠杆在于:你花在配置上的每一小时,会被以后每一次工具调用放大。这是少数几种”前期投入=长期复利”的事情之一。

留下回复

错误: Content is protected !!