
1. Claude Code Mods 到底是个什么东西第一次听到“Claude Code Mods”这个词很多人会以为是某个第三方插件市场或者像 VS Code 扩展那样点一下就能装的东西。实际上它更接近一套“约定大于配置”的扩展机制Claude Code 本身是一个跑在终端里的编码助手而 Mods 是围绕它构建的一层可插拔能力让你能往这个终端助手里塞自定义工具、改它的行为、甚至用 JS/TS 在终端里画出交互界面。我最初接触它是因为一个很具体的痛点默认的 Claude Code 只能读写文件、跑命令但我想让它调用公司内部的接口、解析特定格式的日志、在终端里弹一个可选列表让我挑分支。这些需求官方 CLI 不会内置而 Mods 正好补上了这块。它解决的核心问题是——把“通用编码助手”变成“贴合你自己工作流的助手”而且不用你去改 Claude Code 的源码。适合谁来参考三类人最受益。第一类是天天泡在终端里的后端/运维想让 AI 直接对接自己的脚本和内部工具第二类是前端尤其是熟悉 JS/TS 的同学因为 Mods 的扩展逻辑大量用 JS/TS 写你已有的技能直接复用第三类是喜欢折腾效率工具的人哪怕你不写复杂逻辑光是用 hook 在特定时机触发动作就能省下大量重复操作。需要先厘清一个概念Mods 不是“给 Claude 换模型”也不是“破解什么限制”。它是在 Claude Code 这个宿主之上通过工具注册、hook 挂载、终端 UI 渲染这几条路径扩展它的能力边界。理解了这三条路径后面所有实操你都能自己对号入座。2. 核心机制拆解工具、Hook 与终端 UI 三条主线2.1 工具注册让 Claude 多长几只手Claude Code 默认能做的事是有限的它本质上是一个“会调用工具的对话循环”。Mods 最直接的用法就是注册新工具。一个工具通常包含三部分名称与描述、参数 schema、执行函数。描述写得好不好直接决定 Claude 会不会在合适的时机调用它——这点和写 prompt 一样描述就是给模型看的“使用说明书”。我踩过的第一个坑就是描述写得太含糊。当时我注册了一个叫queryLog的工具描述只写了“查询日志”结果 Claude 几乎从不主动调用它因为它不知道什么场景该用。后来我把描述改成“根据服务名和时间范围查询最近 N 条错误日志当用户提到排查线上问题、看报错时使用”调用率立刻上来了。所以工具描述要写清楚触发场景而不只是功能。参数 schema 建议用 JSON Schema 严格定义类型、必填项、枚举值都写全。模型对结构化 schema 的遵循度远高于自然语言描述。执行函数里要做防御性校验因为模型偶尔会传错参数你不校验就会在运行时炸掉体验很差。2.2 Hook 机制在关键节点插入你的逻辑Hook 是 Mods 里最容易被低估、但威力最大的部分。它允许你在 Claude Code 的生命周期节点上挂载回调比如会话开始、工具调用前、工具调用后、消息发送前等。热词里反复出现的“hook”和“hook技术是啥”本质就是这个思路——在既有流程的固定位置插入自定义代码。为什么 Hook 比直接改源码优雅因为它是非侵入式的。你不需要 fork 整个项目升级 Claude Code 时你的 hook 逻辑依然有效。我常用的一个 hook 是“工具调用后自动格式化”每次 Claude 写完文件hook 触发 prettier 或 gofmt保证代码风格统一。另一个是“敏感操作二次确认”当 Claude 要执行删除类命令时hook 拦截并弹一个确认避免误删。Hook 的执行要快这是铁律。如果你在 hook 里做网络请求、跑重计算整个交互会卡住。我的经验是把耗时逻辑异步化或者只做轻量的判断和转发重活交给独立进程。2.3 终端 UI 渲染用 JS/TS 在命令行画界面“在终端画界面”听起来玄乎其实就是用字符在终端里拼出可交互的界面。Mods 提供了渲染能力你可以用 JS/TS 描述一个界面结构它负责在终端里画出来并处理键盘输入。常见形态有列表选择、多选、输入框、进度条、表格。为什么要在终端画界面而不是直接命令行参数因为交互式选择体验好太多。比如让 Claude 帮你切分支与其让它猜不如弹一个分支列表让你上下键选。实现上通常是把界面描述成组件树渲染层负责 diff 和重绘你只管状态和事件。这里要注意终端宽度适配窄终端下布局会错乱得做响应式处理。3. 从零搭建一个 Mod环境、结构与第一个工具3.1 环境准备与项目初始化动手前先把基础环境理顺。你需要 Node.js建议 LTS 版本和包管理器Claude Code 本身要能正常跑起来。热词里“claude code安装”“claude code 安装教程”出现频率很高说明不少人是第一次配这里给一个稳妥的顺序先确认 Node 版本再全局装 Claude Code最后验证命令可用。node -v npm -v npm install -g anthropic-ai/claude-code claude --version如果安装时报auto-update failed: no write permission to npm prefix这是热词里真实出现过的报错根因是 npm 全局目录没有写权限。解决办法是改 npm 的 prefix 到一个你有权限的目录或者用管理员权限重装。我一般推荐前者避免长期用高权限跑包管理。npm config get prefix npm config set prefix ~/.npm-global export PATH~/.npm-global/bin:$PATH项目结构上一个 Mod 通常包含入口文件、工具定义目录、hook 目录、UI 组件目录。入口负责注册各目录各司其职。我习惯用 TS 写因为类型能帮你在编译期发现参数不匹配的问题比运行时炸掉强太多。3.2 写第一个自定义工具先写一个最简单的工具练手查询当前目录下最大的几个文件。这个需求真实且不复杂适合理解工具注册的完整链路。import { defineTool } from claude-code-mods; import fs from node:fs; import path from node:path; export const largestFiles defineTool({ name: largestFiles, description: 列出当前项目目录下体积最大的文件当用户想清理仓库、排查大文件、优化打包体积时使用, parameters: { type: object, properties: { limit: { type: number, description: 返回条数默认 10 }, dir: { type: string, description: 扫描目录默认当前目录 }, }, required: [], }, async execute({ limit 10, dir . }) { const results: { file: string; size: number }[] []; const walk (d: string) { for (const entry of fs.readdirSync(d, { withFileTypes: true })) { if (entry.name node_modules || entry.name .git) continue; const full path.join(d, entry.name); if (entry.isDirectory()) walk(full); else results.push({ file: full, size: fs.statSync(full).size }); } }; walk(dir); results.sort((a, b) b.size - a.size); return results.slice(0, limit); }, });几个关键点。描述里明确写了“清理仓库、排查大文件、优化打包体积”这就是给模型的触发信号。参数用 JSON Schema 定义required为空数组表示都可选。执行函数里跳过了node_modules和.git否则结果全是依赖包没有参考价值——这是实操中必须做的过滤。3.3 注册与验证工具写完后要在入口注册然后重启 Claude Code 让它加载。验证方式是直接问它“帮我看看项目里最大的文件”观察它是否调用了你的工具。如果没调用八成是描述不够明确回去改描述。注意工具名建议用驼峰或下划线避免特殊字符否则某些终端环境下解析会出问题。4. Hook 实战把重复劳动交给自动化4.1 工具调用后自动格式化这是我最推荐的第一个 hook收益立竿见影。思路是在工具执行完成后判断改动的文件类型触发对应的格式化命令。import { defineHook } from claude-code-mods; import { execSync } from node:child_process; export const autoFormat defineHook({ event: afterToolCall, async handler({ toolName, result }) { if (toolName ! writeFile toolName ! editFile) return; const file (result as any)?.path; if (!file) return; if (file.endsWith(.ts) || file.endsWith(.js)) { execSync(npx prettier --write ${file}, { stdio: ignore }); } }, });这里判断了工具名只在写文件类操作后触发。为什么要判断因为如果每个工具调用后都跑格式化纯查询操作也会被拖慢。stdio: ignore是为了不污染终端输出否则格式化日志会刷屏。4.2 敏感命令拦截第二个实用 hook 是拦截危险命令。Claude 偶尔会生成rm -rf之类的命令虽然它通常会先问但多一层保险不亏。export const guardDangerous defineHook({ event: beforeToolCall, async handler({ toolName, args }) { if (toolName ! runCommand) return; const cmd String((args as any)?.command ?? ); const dangerous [/rm\s-rf/, /drop\stable/i, /git\spush\s--force/]; if (dangerous.some((re) re.test(cmd))) { throw new Error(检测到高风险命令已拦截${cmd}); } }, });抛出错误会让这次工具调用中止Claude 会收到错误信息并调整策略。实测下来这种拦截比事后补救靠谱得多。4.3 Hook 的性能红线再强调一次hook 里别做重活。我见过有人在beforeToolCall里同步请求远程接口做权限校验结果每次工具调用都卡两秒体验直接崩了。正确做法是本地缓存权限结果或者异步预取。Hook 的定位是“轻量拦截与转发”不是“业务处理中心”。5. 终端 UI用 JS/TS 画出可交互界面5.1 界面描述与渲染模型终端 UI 的核心是把“状态”映射成“字符画面”。Mods 的渲染层通常采用类似虚拟 DOM 的思路你描述界面结构它负责 diff 和重绘。一个列表选择界面大致是这样组织的状态里有items和selectedIndex渲染时根据索引高亮对应行键盘事件更新索引。import { defineUI } from claude-code-mods; export const branchPicker defineUI({ async render({ items, selectedIndex }) { return items .map((item, i) (i selectedIndex ? ${item} : ${item})) .join(\n); }, async onKey({ key, selectedIndex, items }) { if (key up) return { selectedIndex: Math.max(0, selectedIndex - 1) }; if (key down) return { selectedIndex: Math.min(items.length - 1, selectedIndex 1) }; if (key enter) return { done: true, value: items[selectedIndex] }; return {}; }, });这个模式的好处是逻辑和渲染分离你改状态就行画面自动更新。onKey返回新状态或完成信号渲染层据此重绘或退出。5.2 终端宽度适配终端宽度是动态的用户随时可能拉窗口。硬编码宽度在窄终端下会换行错乱。我的做法是读取process.stdout.columns布局按比例分配超长文本截断加省略号。const width process.stdout.columns || 80; const label item.length width - 4 ? item.slice(0, width - 7) ... : item;这个细节看着小但不处理的话界面在 80 列终端下会惨不忍睹。5.3 键盘事件处理终端键盘事件和浏览器不一样方向键是转义序列需要解析。Mods 一般帮你封装好了up/down/left/right/enter/esc这些语义化按键你直接用即可。但要注意某些终端对组合键支持不一致别设计太复杂的快捷键方向键加回车基本够用。6. 常见问题与排查速查实操中遇到的问题大多集中在加载、权限、类型这几类。下面这张表是我自己踩坑后整理的遇到问题先对号入座。现象可能原因排查与解决Mod 不生效入口未注册或未重启检查入口注册代码重启 Claude Code工具不被调用描述不清晰在描述中补充触发场景关键词安装报权限错误npm prefix 无写权限改 prefix 到用户目录并更新 PATHHook 卡顿hook 内做同步重活改为异步或本地缓存终端界面错乱未适配终端宽度读取 columns 做响应式布局TS 类型报错类型定义不匹配检查 schema 与执行函数签名一致性参数传错模型未遵循 schema补全 required 与枚举执行函数加校验关于“claude code harness 可以不登录用其他模型吗”这类问题我的建议是先把官方默认链路跑通再考虑扩展。基础没通就折腾替换排查成本会翻倍。提示每次改完 Mod 都要重启宿主热更新不是所有版本都支持别指望改完立刻生效。7. 我踩过的坑与几条实在经验第一条经验先跑通最小闭环再加功能。我一开始就想做一个功能齐全的 Mod结果注册、hook、UI 三块同时出问题排查起来毫无头绪。后来退回去先只注册一个工具跑通再加 hook最后加 UI问题定位快得多。第二条日志是你的救命稻草。Mod 出问题时终端往往只给一句模糊报错。我在关键路径上都加了写文件的日志出问题直接看日志文件比猜快十倍。日志记得带上时间戳和上下文别只写“error”。第三条别在 hook 里抛未捕获异常。一个 hook 崩了可能连带整个会话挂掉。所有 hook 的 handler 我都包了 try/catch出错就记录并放行保证主流程不受影响。第四条关于 TS热词里“若依vue3 ts报错”“ts jsonvalue”这类问题很常见本质是类型定义和实际数据对不上。写 Mod 时工具参数和返回值尽量用明确的类型别图省事用any否则运行时才发现问题调试成本高得多。最后分享一个扩展思路Mods 的能力可以组合。比如把“查询日志工具”和“终端 UI”结合做一个交互式日志浏览器把“hook 拦截”和“工具注册”结合做一个带权限校验的内部命令集。单看每个能力都不复杂组合起来就能贴合你自己的完整工作流。这个方向后续还能继续挖比如把常用操作沉淀成一套团队共享的 Mod 集合新人入职直接装省去大量口头交接。