如果你和我一样经常要把同一个 Claude Code 工作流从 A 项目搬到 B 项目大概率会经历这样的阶段先花半小时把 CLAUDE.md 里的项目规则复制粘贴一遍接着把熟练使用的几个斜杠命令拷进.claude/commands/再想想还有哪些 Hook 脚本需要同步最后用 CtrlC / CtrlV 勉强拼出一个“差不多的环境”。这套流程在单项目里没问题但一旦你开始维护多个仓库、或者想把这些能力分享给团队配置就会以肉眼可见的速度腐烂这个项目改了 review 规则那个项目还停在旧版本命令文件散落各处永远不知道哪个版本生效。Claude Code 的 Plugins插件就是冲着这个痛点来的。它的核心思路一句话能讲明白把已经定义好的 AI 能力技能、命令、钩子、子代理打包成一个标准化的、可安装、可分发的单元。你不再往每个项目里拷贝零散配置而是直接安装一个插件包让子代理、技能、命令一次性就位。这篇文章我会从插件内部结构、动手开发一个完整插件、到安装分发和排障把这条链路捋一遍适合想把 Claude Code 从个人玩具提升成团队工具的朋友。1. 插件到底解决什么问题为什么不是又一个“配置中心”1.1 先搞清楚 Claude Code 里的“AI 能力”有哪些形态Claude Code 之所以强大是因为它不只是一个“聊几句生成代码”的终端工具它本身就提供了好几种规范化扩展点。我平时最常用到的大概是这四类CLAUDE.md项目的人设与规则文件告诉 Claude 这个仓库的背景、技术栈、代码规范、禁止事项。一个高质量的 CLAUDE.md 能明显提升生成的准确率。Commands斜杠命令以/xxx触发的自定义指令本质上是把一段精心编排的 prompt 固化下来比如/commit、/review、/test。Hooks事件钩子在 Claude 调用工具前后、对话结束、会话开始等时机触发脚本用来做自动化的校检、拦截、通知。比如提交代码前必须跑测试发现不过就直接 block 住工具调用。Skills / Subagents技能与子代理把某类专业任务的执行方法、步骤、知识写进SKILL.md或者创造一个专职子代理让它用特定系统提示词和工具集完成细分任务。这四种能力单独拎出来每个都不难用。真正难的是跨项目复用。我见过很多团队的做法是把整理好的 commands 目录直接在 Git 仓库里维护哪个项目需要就 fork 过去CLAUDE.md 更是各写各的风格迥异。这样搞的后果很直接——配置漂移。上半年你给 A 项目加了一条“禁止直接改数据库 schema必须先出迁移脚本”的规矩下半年 B 项目完全不知道这条规矩的存在。1.2 插件的本质给 AI 能力套上“集装箱”回到主题Plugins 做的是什么事我认为它实际上把前面那堆零散的扩展点塞进了集装箱。一个插件就是一个文件夹里面有固定的元数据文件.claude-plugin/plugin.json有可选的skills/、commands/、agents/、hooks/子目录以及自己的交互边界配置。集装箱的最大优点是标准化。Claude Code 看到.claude-plugin/plugin.json就知道这是一个插件看到里面的目录名就知道往哪里加载看到permissions字段就知道这个插件申请了哪些操作权限。你不用再关心“这个仓库的 CLAUDE.md 有没有被另一个仓库里的同名 command 覆盖”因为插件替你把这些隔离和优先级都处理掉了。类比一下就是如果你要跨国运送一车货物最靠谱的方式不是自己去找船、找仓库、订包装——而是直接把货装进标准集装箱港口、货轮、吊车全都按统一规格来接。插件就是 Claude Code 世界里的集装箱一个 deep-dive 代码审查工具它把“审查规则 prompt”“专用 skills”“风险拦截 hook”全部装进同一个包任何项目只要执行一次安装命令就能获得完整能力。在我看来插件的定位绝不是“又一个配置中心”。配置中心是集中式的所有项目都来读同一份配置插件更像是 npm 包或 pip 包它面向的是复用与分发你可以自己维护私有插件也可以把它发布给团队、社区。这个区别决定了插件机制的设计重心——不是“如何存配置”而是“如何让一个能力包像软件一样有版本、有元信息、有安装入口”。2. 插件目录结构与核心文件逐一拆解2.1 plugin.json插件的“身份证”任何一个插件首先得有一个合法的.claude-plugin/plugin.json文件。这是 Claude Code 识别插件的第一道关卡。目录结构通常是这样的my-plugin/ ├── .claude-plugin/ │ └── plugin.json ├── skills/ │ ├── sql-review/ │ │ └── SKILL.md │ └── api-design/ │ └── SKILL.md ├── commands/ │ └── review.md ├── agents/ │ └── security-auditor.md └── hooks/ └── check-format.jsplugin.json里我最常关注的字段有这些字段说明我的建议name插件唯一名称安装时用于标识用scope/plugin-name风格避免冲突description一句话描述插件能力写清楚“解决什么问题”别写“一个工具集”这种废话author作者信息团队内部分享时写清楚负责人version语义化版本号每次改动都要升版本否则队友装了等于没装permissions插件运行时需要的权限声明见下文尽量最小化hooks声明要监听哪些事件也可以放在 settings 里但我习惯集中声明一个最小的plugin.json大概是这个样子{ name: team/deep-review, description: 团队代码审查助手pull request 分析、危险变更识别、自动生成审查意见, author: backend-team, version: 0.3.0, permissions: { allow: [ Bash(npm test), Read ], deny: [ Write ] }, hooks: { PostToolUse: [ { matcher: Edit, hooks: [ { type: command, command: python3 .claude-plugin/hooks/check-lint.py } ] } ] } }permissions重点解释一下。Claude Code 有权限提示机制AI 想调用某个工具时它会先看权限配置允许就直接跑拒绝就跳过没配的会弹交互确认。插件同样遵循这套逻辑。你声明了Bash(npm test)那这个插件里的 hook 或 command 就只被允许执行npm test这条 Bash 命令想跑rm -rf就得走确认流程。我见过很多插件写得很爽快permissions.allow直接写Bash等于放开所有 shell 命令的闸门——这对一个要分享给他人的插件来说是非常危险的做法。2.2 skills/ 模块让 Claude 知道“这件事该怎么干”skills/目录放的是 Agent Skills。每个技能就是一个子目录内含一个SKILL.md。这个文件的格式很关键头部 YAML frontmatter 里放name和description正文写具体的执行方法论。--- name: sql-review description: 审查 SQL 迁移脚本和查询语句识别性能隐患、锁竞争和破坏性变更。在代码审查涉及数据库改动时使用。 --- # SQL 迁移审查指南 ## 定位目标 首先确认所有改动涉及的表、索引、以及数据量级。 ## 检查清单 1. 是否有 ALTER TABLE 阻塞性操作 2. 是否缺少索引迁移 3. ...description的作用经常被低估。Claude 不是按文件名找技能的它是根据当前对话的上下文结合你的描述来“召用”某个技能。如果你的description写得太笼统比如“处理数据库相关的事情”那 Claude 很可能把其他更匹配的技能拉出来反过来如果描述里包含明确的触发条件召用准确率会高很多。我的经验是描述里一定带上“什么场景下用”比如“在代码审查涉及数据库改动时使用”召用率会明显提升。2.3 commands/ 模块把常用操作做成斜杠命令插件里的commands/就是标准格式的命令文件每个.md文件最终会变成一个斜杠命令。比起项目内建的.claude/commands/插件中的命令会自动带上插件名做前缀比如装了名为deep-review的插件里面定义review命令实际使用可能是/deep-review review或者/review取决于版本和前缀策略。这个设计避免了不同插件间的命令撞名。命令文件的 frontmatter 支持这些字段--- name: review description: 基于当前分支和 diff生成结构化 code review 报告 argument-hint: [optional] review scope allowed-tools: Bash(git diff), Read model: sonnet ---然后是正文 prompt。需要注意命令正文本身就是转给 Claude 的系统提示词你可以在这里引用其他插件资源比如告诉它“按 sql-review 技能的流程检查变更中的 SQL”。这就把命令和技能串起来了。2.4 agents/ 与 hooks/定制执行主体和自动化闸门agents/目录每个文件对应一个 subagent 定义。和命令一样subagent 也有自己的 system prompt、工具白名单、模型选择。适合把频繁使用的工作流抽象成“专门干一件事的成员”比如security-auditor只负责安全审查docs-writer只负责写文档。hooks/则是插件的“自动化手脚”。Claude Code 支持多种 Hook 事件我实际开发中常用的是PostToolUse工具调用完毕后触发和PreToolUse工具调用前触发。Hook 脚本必须往 stdout 输出特定 JSONClaude Code 根据 JSON 决定是放行、拦截还是询问。这个机制后面实战部分会展开。3. 从零搭建一个插件完整的“代码审查助手”实战这一节我手把手带你搭一个可用的插件。前面讲概念后面直接动手。我们的目标是构建一个插件它能在你发起/review时自动分析当前分支的变更如果变更中含有 SQL 迁移文件会触发sql-review技能做专项审查同时在 Claude 尝试直接改动锁定目录时hook 会直接拦截并给出提示。3.1 初始化插件目录先建目录骨架mkdir -p deep-review/.claude-plugin mkdir -p deep-review/skills/sql-review mkdir -p deep-review/commands mkdir -p deep-review/agents mkdir -p deep-review/hooks我的习惯是插件目录独立成一个 Git 仓库方便版本管理。里面的内容只放插件本身不要把业务代码混进来。一个小细节.claude-plugin这个目录名是固定要求写成别的不会被识别。我第一次写插件的教训就是把它命名成了.claude/plugin结果 Claude Code 压根没加载调试了半小时才反应过来。创建deep-review/.claude-plugin/plugin.json{ name: backend/deep-review, description: 深度代码审查助手PR 分析、SQL 专项审查、锁定目录保护, author: backend-team, version: 0.1.0, permissions: { allow: [ Read, Bash(git diff), Bash(git log), Bash(git status) ], deny: [ Write, Bash(rm *), Bash(git push) ] }, hooks: { PreToolUse: [ { matcher: Write, hooks: [ { type: command, command: python3 .claude-plugin/hooks/protect-sensitive.py } ] } ] } }权限这里我刻意做得很克制只允许读以及跑几个只读 git 命令写操作直接 deny。如果 AI 要写文件就走确认流程。对插件来说权限宁缺毋滥因为你不知道使用者在什么项目里装这个插件线放开写操作等于把你的插件变成不可控的破坏工具。3.2 编写 sql-review 技能然后写deep-review/skills/sql-review/SKILL.md--- name: sql-review description: SQL 迁移脚本与查询审查。适用于包含 .sql 文件或 ALTER TABLE / CREATE INDEX / UPDATE 等语句的代码审查场景。 --- # SQL 变更专项审查 ## 前置信息收集 - 找出所有新增或修改的 SQL 迁移文件。 - 确定涉及的数据表、数据量级、以及表上现有索引。 ## 审查重点 1. **阻塞性 DDL**: ALTER TABLE 在 MySQL 8.0 以上的大多数操作是否使用 ALGORITHMINPLACE? 如果在锁表窗口内执行标注阻塞风险。 2. **索引合理性**: 新增查询条件是否有对应索引? 索引缺失会导致全表扫描。 3. **数据迁移回滚**: 反向迁移脚本是否存在? 没有回滚计划的迁移视为高风险。 4. **事务粒度**: 数据修复脚本是否开启事务? 大事务会导致主从延迟。 ## 输出格式 以表格形式输出: 文件路径、风险级别高/中/低、问题描述、修复建议。这里的一个关键点技能不是简单的“知识库”它更像一份带流程的“操作手册”。如果 Claude 看到你的 SKILL.md 只有一堆零散的注意事项它的执行步骤会很不稳定。因此我在文档里明确给了“前置信息收集 → 审查重点 → 输出格式”三段式结构把执行路径固化下来。3.3 写一个调用技能的斜杠命令接下来创建deep-review/commands/review.md--- name: review description: 执行一次完整的代码审查覆盖常规 diff 与专项 SQL 风险 argument-hint: 可指定审查范围如 src/models allowed-tools: Bash(git diff), Bash(git status), Read model: sonnet --- 你是资深代码审查工程师。执行以下流程 1. 先运行 git status 和 git diff --cached如果暂存区为空则使用 git diff 对比当前分支与主干。 2. 对变更文件做分类业务逻辑、测试、配置、SQL。 3. 如果变更涉及 SQL 文件或数据库相关代码必须先调用 sql-review 技能按技能中的清单逐条检查。 4. 最终输出 Markdown 格式的审查报告包含问题列表、风险等级、建议修复方案。这个命令的价值在于它把“审查流程”固化成了一个指令。你不需要每次手动唠叨“先看 diff再分分类遇到 SQL 要重点检查索引……”只要敲/reviewClaude 就会按部就班执行。3.4 加一个 PreToolUse 拦截钩子最后写deep-review/hooks/protect-sensitive.py。这个脚本的作用检测 Claude 是否尝试修改migrations/目录以外的文件不对我们反过来保护migrations/目录不让 AI 直接改。执行时机是 Claude 尝试调用Write工具之前。#!/usr/bin/env python3 import json import sys # 从 stdin 读取 Claude Code 传入的 JSON 上下文 try: data json.load(sys.stdin) except Exception: # 解析失败时采取保守策略: 拦截 print(json.dumps({hookSpecificOutput: { hookEventName: PreToolUse, permissionDecision: block, permissionDecisionReason: 无法解析 hook 输入默认拦截写操作 }})) sys.exit(0) tool_name data.get(tool_name, ) tool_input data.get(tool_input, {}) file_path tool_input.get(file_path, ) if isinstance(tool_input, dict) else # 如果写的是迁移脚本直接阻止 if tool_name Write and file_path.startswith(migrations/): result { hookSpecificOutput: { hookEventName: PreToolUse, permissionDecision: block, permissionDecisionReason: 迁移脚本必须人工审批禁止 AI 直接修改, permissionDecisionMessage: 检测到正在修改 migrations/ 目录下的文件已拦截。请人工处理后重试。 } } else: # 放行 result { hookSpecificOutput: { hookEventName: PreToolUse, permissionDecision: allow } } print(json.dumps(result))这里有个值得注意的地方hook 脚本接收的 JSON 字段在不同版本里略有差异。我建议你在tool_name和tool_input之外顺手打印出整个 data先跑一次看看实际结构再做匹配逻辑。写 hook 时永远记得脚本要稳挂了就往“拦截”方向偏而不是放行。因为对于保护性 hook 来说宁可误杀也不可放跑。3.5 在项目里加载和调试插件开发好之后在你自己的项目里安装。Claude Code 支持本地路径安装你可以在项目根目录直接指定插件仓库目录claude plugin install /path/to/deep-review或者如果插件已经放到 Git 仓库也可以这样claude plugin install https://github.com/yourname/deep-review.git安装完成后进入 Claude Code 交互界面输入/plugin可以看到已安装的插件列表和启用状态输入/review就能触发命令。如果没生效先看日志。Claude Code 的日志通常在~/.claude/logs/下排查时优先看debug级别日志里有没有plugin关键字定位比瞎猜快得多。4. 安装、加载方式与生态共享套路4.1 本地安装、市场安装、更新与卸载插件的安装来源主要有三种本地路径、Git 仓库、以及插件市场Marketplace。本地路径安装适合开发调试阶段改完代码立刻能看到效果。Git 仓库安装适合团队内部共享把插件推到私有仓库其他人直接装。Marketplace 安装适合分发到社区。市场本质上是一个索引仓库里面记录了一批插件的地址和版本信息。从命令上看它们都统一走claude plugin install。值得提醒的是频繁改插件代码时claude plugin install之后往往需要重启正在运行的 Claude Code 会话才能生效。不要以为装完就万事大吉我自己就踩过改完 hook 不重启、旧逻辑持续生效的坑。卸载方面claude plugin uninstall plugin-name可以移除插件。注意它不一定会删掉你可能手动放进去的配置文件如果在项目的.claude/settings.json里引用过插件相关路径也要顺势清理。4.2 插件与现有配置的优先级Claude Code 同时支持项目内配置.claude/目录、用户级配置~/.claude/、以及插件配置。这三者叠加时优先级我做了一个梳理配置来源优先级表现项目.claude/settings.json最高直接作用于当前仓库插件内 settings / plugin.json次之作用于插件加载后用户级~/.claude/settings.json兜底所有项目生效命令与 Skills 的冲突规则更复杂一些。不同来源的同名 command 可能存在覆盖关系为了避免被坑插件命令通常会带命名空间。假设我的插件名为deep-review那么命令文件注册后实际命令可能是deep-review/review。我在开发插件时都会刻意用name前缀把命令区分开这样即便项目里有人已经定义了一个review命令也不会互相踩踏。4.3 发布到社区时要注意的几个问题既然插件可以被分享就必然涉及发布素养。我总结了几条实际经验第一写得像开源项目一样。README 里写清楚这个插件解决什么问题、支持哪些 hook、权限声明是什么。很多开发者技术不错但插件 README 懒得写结果别人装完一脸懵。第二权限声明是门面。如果插件宣称自己是“安全审查工具”结果permissions.allow里躺着Bash和Write那这个信任感直接就崩了。用户安装前会看权限就像你手机上装 App 会看应用权限一样。第三版本号别懒。改一行 prompt 也建议升 patch 版本因为插件发布到市场后用户侧可能需要明确升级才会拉新版本。版本号不动你改了等于没改。第四别埋环境变量。插件的 hook 脚本如果需要访问某些密钥不要硬编码在仓库里。让用户通过环境变量注入并在 README 里写清楚。把这些做到了你的插件才真正具备“可持续分发”的属性而不只是自己项目里的一堆神秘文件夹。5. 常见问题排查与避坑实录5.1 插件加载失败的三个高频原因原因一plugin.json 格式错误。少了一个逗号、多了一个尾逗号、或者 JSON 里写了注释都会导致加载失败。这种错误最好定位终端里会有明确报错但就怕你压根没注意到。我建议写完plugin.json后先跑一下python3 -m json.tool .claude-plugin/plugin.json验证格式。原因二目录位置放错。插件依赖的.claude-plugin/必须在插件根目录下。如果你把插件仓库 clone 下来把里面的子目录当成了插件根目录那 Claude Code 自然找不到.claude-plugin就无法识别。原因三权限配置缺失导致安装被拦。有些版本的 Claude Code 在安装插件时会询问是否信任该插件以及是否接受它的权限声明。如果你在无人值守环境里用命令行操作可能没有交互入口导致安装没有完成。解决方法是确保命令行终端能正常交互或者预先在配置里声明信任。5.2 Skill 不生效可能不是“写错了”而是“没被选中”Skill 不生效很多时候不是文件内容错而是 Claude 在本次任务中压根没有“召用”这个技能。Claude 决定是否调用一个技能靠的是SKILL.md里的description和当前任务的匹配度。如果你的 description 写得像功能说明书“对数据库进行审查”Claude 不会自动联想到“在代码审查中遇到 SQL 改写时也应该用”。描述里少一个“when to use”的触发语境效果天差地别。另外注意同名技能冲突。如果项目里.claude/skills/和插件skills/里都存在同名技能生效规则会变得微妙。建议优先检查是不是有同名技能在“带着别人的帽子”互相干扰通过给插件技能加更明确的名字前缀来隔离。5.3 Hook 脚本被反复触发或权限拒绝PostToolUse 和 PreToolUse 最经典的坑是递归触发。你的 hook 脚本如果本身执行了一条 Bash 命令而这条命令又触发了同一个 hook 事件就会陷入无限循环。举一个我实际遇到的例子PostToolUse匹配了Bash事件脚本里跑了一句python3 my_hook.py然后这个python3命令又触发了一次Bash事件从而又调用了一次脚本……解决方法是脚本执行时通过环境变量设置一个标记比如RUNNING_HOOK1然后在 hook 脚本开头判断“如果已经处于 hook 执行中就别往下走了”。还有一个常被忽略的权限问题Hook 脚本要从 stdin 接收 JSON、往 stdout 输出 JSON如果你的脚本不小心在输出里混入了额外日志比如print(debug...)Claude Code 会解析失败然后默认走拦截或拒绝逻辑。我的建议是hook 脚本里任何调试日志都写到 stderrstdout 只留给 JSON一行多余都不能有。5.4 插件跨项目复用的隐形坑绝对路径是头号杀手。写 hook 或命令时不要用绝对路径引用脚本位置。因为你无法假设每个使用者的克隆路径。应该永远基于当前项目相对路径来定位比如python3 .claude-plugin/hooks/xxx.py或者通过环境变量来定位插件根目录。外部依赖最好内嵌。你的 hook 如果依赖requests、pydantic这类第三方库装到别人机器上很容易因为缺依赖而挂掉。最省心的方案是用 Python 标准库重写逻辑或者在插件仓库里提供requirements.txt并在 README 里写清楚安装步骤。一个复杂的依赖树会让插件的分发成本指数级上升。跨平台兼容性这个问题说多了都是泪。一个#!/usr/bin/env bash的 hook 在 macOS 上好好的到了 Linux CI 环境里路径全变。如果你知道插件主要跑在什么系统就在文档里写明如果要做通用分发hook 最好写成 Python 或 Node 这种跨平台脚本而不是 shell。最后再分享一点我的体会我最初用 Claude Code 插件时以为它只是个“配置文件打包工具”用熟练之后才意识到它真正改变的是 AI 能力的治理方式能力有了边界、版本、作者和权限声明你开始像对待代码模块一样对待每一个 AI 扩展。插件开发的门槛确实不高——一个 JSON 加一个 Markdown 就能跑起来——但把它做好却又需要仔细拿捏权限、描述和兼容性。我给团队的建议是先从一个小而具体的场景开始比如只打包一个命令加一个 hook跑顺之后再往里面加技能和子代理不要一上来就想做个“全家桶”因为每一个扩展点都是需要持续维护的。你可以现在就把手头使用最频繁的一套 AI 工作流打成插件试一次那种“装完就能在新项目里复刻能力”的体感会在下一个无聊的配置搬运日里救你一次。