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

- 起点:为什么动默认配置
- 三层防御 hooks
- 代码图谱 + hooks 的组合拳
- 技能库:把一次性脚本沉淀成资产
- 输出风格:插件与全局 CLAUDE.md
- 总结
01起点:为什么动默认配置
学习黄兴老师的《Claude Code 工程化实战》课程和查看一些 Claude Code 相关技术博客后,在日常使用 Claude Code 的过程中,我意识到一个很大的风险:大模型执行本地命令时,一旦失控就是灾难。
最直接的场景是 rm -rf。模型本意可能是删某个项目的 node_modules,但如果路径拼接出错、变量为空、或者干脆理解错了上下文,rm -rf / 或 rm -rf ~ 就会直接送到 shell。文件系统损坏只要一次。
更隐蔽的风险来自 skill 和 MCP。这些扩展机制让模型能调用任意脚本和服务,如果某个 skill 里写了”清理临时目录”的逻辑,而路径参数被错误注入,等发现时已经晚了。
所以我需要一层确定性的防护:不依赖模型的判断,而是在工具调用前用规则硬性拦截。这就是 hooks 的用武之地。
另一个现实问题是网络。我司访问 Anthropic 官方端点要走代理,所以 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/第三层:全量审计
每次工具调用都追加一行到本地日志,17 行 bash:
~/.claude/hooks/audit-log.shINPUT=$(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 × 4 | startup / 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 行:
# 个人偏好 ## 沟通方式 - 使用中文回复 - 代码注释使用英文 - 解释简洁直接,不要过多铺垫 ## 通用代码风格 - 缩进使用 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 的默认配置能干活,但只是毛坯。它真正的杠杆在于:你花在配置上的每一小时,会被以后每一次工具调用放大。这是少数几种”前期投入=长期复利”的事情之一。
