插件系统——命令、Agent、技能与钩子
前置知识
- ›第 1 篇:架构概览
- ›了解 YAML 与 Markdown frontmatter
- ›具备基本的 shell 脚本知识
插件系统——命令、Agent、技能与钩子
在第 1 篇中,我们了解到 claude-code 仓库内置了 13 个官方插件,它们共同构建于"约定优于配置"的架构之上。但理解目录结构是一回事,真正理解这些组件如何运作又是另一回事。命令、Agent、技能、钩子——这四种组件类型各有其独立的发现机制、配置接口和执行语义。
本篇将结合官方插件的真实示例,逐一剖析每种组件类型,重点梳理插件钩子格式与设置钩子格式之间的关键差异,最终将所有内容整合为一个统一的生命周期模型。
命令:用户触发的工作流
命令是插件功能的主要入口。它们是带有 YAML frontmatter 的 Markdown 文件,最终会以斜杠命令的形式呈现在 Claude Code 中。当用户输入 /feature-dev 时,Claude Code 会加载对应的 .md 文件,并将其内容作为提示指令使用。
frontmatter 才是真正的核心所在。以下是 feature-dev 命令的配置:
plugins/feature-dev/commands/feature-dev.md#L1-L4
---
description: Guided feature development with codebase understanding and architecture focus
argument-hint: Optional feature description
---
description 显示在命令列表中,argument-hint 则告诉用户在命令名称后面应该传入什么参数。再来看 code-review 的 frontmatter,其中包含更强大的配置项:
plugins/code-review/commands/code-review.md#L1-L4
---
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh pr comment:*), Bash(gh pr diff:*), Bash(gh pr view:*), Bash(gh pr list:*), mcp__github_inline_comment__create_inline_comment
description: Code review a pull request
---
allowed-tools 字段是一个支持模式匹配的白名单。Bash(gh pr view:*) 的含义是"允许调用 Bash 工具,但仅限于命令以 gh pr view 开头的情况"。这正是插件限制其 Agent 只能执行特定操作的方式——代码审查插件不应该拥有写文件或执行任意命令的权限。
ralph-loop 命令还展示了另外两个 frontmatter 字段:
plugins/ralph-wiggum/commands/ralph-loop.md#L1-L5
---
description: "Start Ralph Wiggum loop in current session"
argument-hint: "PROMPT [--max-iterations N] [--completion-promise TEXT]"
allowed-tools: ["Bash(${CLAUDE_PLUGIN_ROOT}/scripts/setup-ralph-loop.sh:*)"]
hide-from-slash-command-tool: "true"
---
hide-from-slash-command-tool 的作用是阻止 Claude 自主调用此命令——它必须由用户主动触发。另外注意 allowed-tools 中使用了 ${CLAUDE_PLUGIN_ROOT},这将 Bash 的执行范围限制在插件内部的特定脚本上。
提示: 命令正文中的
$ARGUMENTS变量会被替换为用户在斜杠命令后输入的内容。这是命令接受用户输入的方式,无需复杂的参数解析逻辑。
Agent:专职 AI 工作者
Agent 是子 Agent 的定义——命令负责调度的专职 AI 工作者。与命令一样,它们也是带有 YAML frontmatter 的 Markdown 文件,但 frontmatter 配置的是子 Agent 的模型和能力,而非面向用户的元数据。
以下是 feature-dev 中的 code-explorer agent:
plugins/feature-dev/agents/code-explorer.md#L1-L7
---
name: code-explorer
description: Deeply analyzes existing codebase features by tracing execution paths...
tools: Glob, Grep, LS, Read, NotebookRead, WebFetch, TodoWrite, WebSearch, KillShell, BashOutput
model: sonnet
color: yellow
---
每个 frontmatter 字段都有其明确用途:
| 字段 | 用途 | 示例 |
|---|---|---|
name |
日志和界面中显示的名称 | code-explorer |
description |
说明何时使用该 Agent | 详细的能力描述 |
tools |
允许使用的工具白名单 | Glob, Grep, LS, Read, ... |
model |
模型级别选择 | sonnet、haiku、opus |
color |
日志中的视觉标识 | yellow、green、red |
对比 feature-dev 的三个 Agent,可以清楚地看到模型选择和工具限制是如何划分各自职责的:
| Agent | 模型 | 工具 | 职责 |
|---|---|---|---|
code-explorer |
sonnet | 以读取为主(Glob、Grep、Read……) | 快速分析代码库 |
code-architect |
sonnet | 同样以读取为主 | 架构设计 |
code-reviewer |
sonnet | 同样以读取为主 | 带置信度评分的质量审查 |
plugins/feature-dev/agents/code-reviewer.md#L25-L33 中的 code-reviewer agent 引入了一套置信度评分机制(0–100 分),只有评分 ≥80 的问题才会被上报。这套过滤机制完全通过 Agent 的提示指令实现,无需任何特殊的运行时支持。提示词本身就是程序。
flowchart LR
CMD["/feature-dev command"] --> EXPLORE["code-explorer<br/>model: sonnet<br/>color: yellow"]
CMD --> ARCH["code-architect<br/>model: sonnet<br/>color: green"]
CMD --> REVIEW["code-reviewer<br/>model: sonnet<br/>color: red"]
EXPLORE --> |"Read-only tools"| ANALYSIS["Codebase<br/>Analysis"]
ARCH --> |"Read-only tools"| DESIGN["Architecture<br/>Design"]
REVIEW --> |"Read-only tools"| QUALITY["Quality<br/>Review"]
style EXPLORE fill:#FDD835
style ARCH fill:#4CAF50,color:#fff
style REVIEW fill:#E53935,color:#fff
技能:基于上下文自动激活的知识库
技能是最被动的组件类型。它们提供的知识由 Claude Code 根据上下文匹配结果自动激活,无需显式调用。每个技能都位于独立的子目录中,包含一个 SKILL.md 文件。
以下是 frontend-design 技能的 frontmatter:
plugins/frontend-design/skills/frontend-design/SKILL.md#L1-L5
---
name: frontend-design
description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications.
license: Complete terms in LICENSE.txt
---
description 字段身兼两职:它既是技能的说明文档,也是告知 Claude Code 何时激活该技能的依据。当用户询问如何构建 Web 组件时,Claude Code 会将该上下文与技能描述进行匹配,并自动注入相关的技能内容。
plugins/plugin-dev/skills/plugin-structure/SKILL.md#L1-L5 中的 plugin-structure 技能则使用了更明确的描述:
description: This skill should be used when the user asks to "create a plugin", "scaffold a plugin", "understand plugin structure"...
描述越详细,激活的覆盖面就越广。技能作者可以自行控制描述的粒度——描述越模糊,激活越宽泛;描述越精确,激活越聚焦。
技能可以在子目录中包含辅助材料,例如 references/、examples/、scripts/。plugin-dev 插件共有七个技能,每个技能都有各自的参考资料。这正是技能与命令的根本区别所在:命令是一次性指令,而技能是在整个会话中持续有效的知识库。
钩子:事件驱动的拦截器
钩子是功能最强大、也最复杂的组件类型。它们响应九个生命周期事件执行,可以通过拦截操作、注入上下文或强制执行策略来改变 Claude Code 的行为。
这九个钩子事件记录于 plugins/plugin-dev/skills/hook-development/SKILL.md#L634-L644:
| 事件 | 触发时机 | 常见用途 |
|---|---|---|
PreToolUse |
工具执行前 | 校验、拦截 |
PostToolUse |
工具执行后 | 反馈、日志记录 |
Stop |
Agent 准备停止时 | 完整性检查 |
SubagentStop |
子 Agent 准备停止时 | 任务校验 |
SessionStart |
会话开始时 | 加载上下文 |
SessionEnd |
会话结束时 | 清理工作 |
UserPromptSubmit |
用户发送提示时 | 输入校验 |
PreCompact |
上下文压缩前 | 保留关键信息 |
Notification |
发送通知时 | 响应处理、日志记录 |
钩子分为两种类型:命令钩子(shell 脚本,用于确定性检查)和 prompt 钩子(由 LLM 驱动,用于需要上下文感知的决策)。退出码协议至关重要:
- 退出码 0 — 允许操作(stdout 内容会显示在对话记录中)
- 退出码 2 — 阻止操作(stderr 内容会反馈给 Claude)
- 其他退出码 — 非阻塞性错误
下面的时序图展示了 PreToolUse 钩子拦截文件写入操作的过程:
sequenceDiagram
participant C as Claude Code
participant H as PreToolUse Hook
participant T as Write Tool
C->>H: JSON via stdin (tool_name, tool_input)
H->>H: Check patterns/rules
alt Pattern matches (new warning)
H-->>C: stderr: warning message
H->>C: exit 2 (BLOCK)
Note over C: Tool call blocked
else No match or already warned
H->>C: exit 0 (ALLOW)
C->>T: Execute Write tool
T->>C: Tool result
end
Matcher 可以过滤哪些工具会触发钩子。security-guidance 插件使用 "matcher": "Edit|Write|MultiEdit",使钩子只在文件修改类工具上触发:
plugins/security-guidance/hooks/hooks.json#L1-L16
Matcher 支持精确匹配("Write")、管道符 OR("Edit|Write")、通配符("*")以及正则表达式("mcp__.*__delete.*")。
双钩子格式陷阱
这是使用钩子时最常见的开发者误区。根据钩子所在的位置,存在两种不同的 JSON 格式,格式用错会导致钩子静默失效。
插件格式(位于插件内部的 hooks/hooks.json)将事件包裹在 hooks 键中:
{
"description": "Optional description",
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/validate.sh"
}
]
}
]
}
}
设置格式(位于 .claude/settings.json 或用户设置中)将事件直接放在顶层:
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 /path/to/validator.py"
}
]
}
]
}
可以在 plugins/hookify/hooks/hooks.json#L1-L49 中看到 hookify 使用的插件格式——注意最外层的 "hooks" 包装。examples/hooks/bash_command_validator_example.py#L13-L27 的文档注释中也对插件格式有所说明。
提示: 如果钩子没有触发,首先检查格式是否正确。插件的
hooks.json需要{"hooks": {...}}包装,而用户设置则不需要。plugins/plugin-dev/skills/hook-development/SKILL.md中并排记录了这两种格式,可供参考对比。
组件生命周期:从发现到执行
理解了四种组件类型之后,再来看它们在统一生命周期中是如何协作的:
flowchart TD
INSTALL["Plugin Installed"] --> READ["Read plugin.json manifest"]
READ --> SCAN["Scan Convention Directories"]
SCAN --> CMD_SCAN["commands/*.md"]
SCAN --> AGT_SCAN["agents/*.md"]
SCAN --> SKL_SCAN["skills/*/SKILL.md"]
SCAN --> HK_SCAN["hooks/hooks.json"]
SCAN --> MCP_SCAN[".mcp.json"]
CMD_SCAN --> CMD_REG["Register as<br/>slash commands"]
AGT_SCAN --> AGT_REG["Register as<br/>available agents"]
SKL_SCAN --> SKL_REG["Register for<br/>context matching"]
HK_SCAN --> HK_REG["Register on<br/>event bus"]
MCP_SCAN --> MCP_REG["Start MCP<br/>servers"]
CMD_REG --> USER["User types /command"]
AGT_REG --> ORCH["Command orchestrates agents"]
SKL_REG --> AUTO["Auto-activated by context"]
HK_REG --> EVENT["Fired by lifecycle events"]
MCP_REG --> TOOL["Available as tools"]
USER --> EXEC["Execution"]
ORCH --> EXEC
AUTO --> EXEC
EVENT --> EXEC
TOOL --> EXEC
style INSTALL fill:#FF5722,color:#fff
style EXEC fill:#4CAF50,color:#fff
核心规律一目了然:命令由用户主动触发,Agent 由命令调度,技能由上下文自动激活,钩子则由事件驱动。这四种调用模型覆盖了从用户显式控制到完全自动化行为的全部场景。
plugin.json 中的自定义路径是对默认目录的补充,而非替换。若指定了 "commands": "./custom-commands",Claude Code 会同时扫描 commands/ 和 custom-commands/ 两个目录。这种叠加行为意味着你可以在遵循约定的前提下自由扩展。
配置变更会在下次启动 Claude Code 时生效——插件配置不支持热重载。修改 hooks.json 后,重启 Claude Code 即可加载新的钩子。
下一篇预告
掌握了组件模型之后,我们已经准备好去观察它在更大规模场景下的协作方式。第 3 篇将深入分析两种复杂的多 Agent 模式:feature-dev 的七阶段人机协作工作流(包含并行 Agent 启动)、以及 code-review 的多轮校验流水线(每条发现结果都由全新的子 Agent 独立核实)。此外,我们还将解析 ralph-wiggum 插件极具创意的自引用循环模式——一个读取对话记录并重新注入原始提示的 Stop 钩子,从而实现迭代式 Agent 执行。