第四节:自定义插件
插件开发与调试

作者:小学子 📚 | 日期:2026年4月2日 | 第五阶段 · 模块五


# 第五阶段 · 模块五 · 第四节:自定义插件开发

核心问题

如何从零开发一个插件?插件的项目结构是什么?如何打包和发布插件?


◇ 本节位置


        Claude Code 全局架构
        
        ┌─────────────────────────────────────────────────────────────────────┐
        │  插件系统                                                            │
        │                                                                      │
        │  自定义插件开发 ← 本节                                               │
        │  ├── 创建项目                                                       │
        │  ├── 编写 manifest                                                  │
        │  └── 打包发布                                                       │
        └─────────────────────────────────────────────────────────────────────┘
        


一、创建插件项目

1.1 项目结构


        my-plugin/
        ├── plugin.json          ← 插件清单(必需)
        ├── package.json         ← Node.js 配置
        ├── index.ts            ← 入口文件
        ├── commands/           ← 命令目录
        │   └── hello.ts
        ├── tools/              ← 工具目录
        │   └── greet.ts
        ├── hooks/              ← 钩子目录
        │   └── index.ts
        └── README.md           ← 文档
        

1.2 初始化项目


        # 创建目录
        mkdir my-plugin && cd my-plugin
        
        # 初始化 package.json
        npm init -y
        
        # 安装依赖
        npm install typescript @claude-code/sdk
        
        # 初始化 TypeScript
        npx tsc --init
        


二、编写 Manifest

2.1 plugin.json


        {
          "name": "my-plugin",
          "version": "1.0.0",
          "description": "A useful plugin for Claude Code",
          "author": "Your Name",
          "license": "MIT",
        
          "commands": [
            {
              "name": "hello",
              "description": "Say hello",
              "path": "./commands/hello.ts"
            }
          ],
        
          "tools": [
            {
              "name": "greet",
              "description": "Greet someone",
              "path": "./tools/greet.ts"
            }
          ],
        
          "hooks": {
            "path": "./hooks/index.ts"
          },
        
          "compatibility": {
            "claude": ">=3.0.0"
          }
        }
        

2.2 字段说明

字段必需说明
name插件名称,唯一标识
version语义版本
description简短描述
commands自定义命令列表
tools自定义工具列表
hooks钩子配置
compatibilityClaude Code 版本要求


三、编写命令

3.1 命令文件


        // commands/hello.ts
        import type { Command } from '../../commands.js';
        
        const helloCommand: Command = {
          type: 'local',
          name: 'hello',
          description: 'Say hello to someone',
        
          async getPromptForCommand(args: string) {
            const name = args || 'World';
            return {
              type: 'text',
              content: `Hello, ${name}! 👋`,
            };
          },
        };
        
        export default helloCommand;
        

3.2 命令参数


        // commands/with-args.ts
        const withArgsCommand: Command = {
          type: 'local',
          name: 'greet',
          description: 'Greet with custom message',
        
          async getPromptForCommand(args: string) {
            // 解析参数
            const parts = args.split(' ');
            const name = parts[0];
            const style = parts[1] || 'formal';
        
            const greetings = {
              formal: `Good day, ${name}.`,
              casual: `Hey ${name}!`,
              friendly: `Hi ${name}! How are you?`,
            };
        
            return {
              type: 'text',
              content: greetings[style] || greetings.formal,
            };
          },
        };
        


四、编写工具

4.1 工具文件


        // tools/greet.ts
        import type { Tool, ToolUseContext } from '../../Tool.js';
        
        const greetTool: Tool = {
          name: 'greet',
          description: 'Generate a greeting message',
        
          inputSchema: {
            type: 'object',
            properties: {
              name: { type: 'string', description: 'Name to greet' },
              style: {
                type: 'string',
                enum: ['formal', 'casual', 'friendly'],
                default: 'friendly',
              },
            },
            required: ['name'],
          },
        
          async execute(input: { name: string; style?: string }, context: ToolUseContext) {
            const { name, style = 'friendly' } = input;
        
            const greetings = {
              formal: `Good day, ${name}. I hope this message finds you well.`,
              casual: `Hey ${name}!`,
              friendly: `Hi ${name}! 🎉 How's it going?`,
            };
        
            return greetings[style];
          },
        };
        
        export default greetTool;
        

4.2 工具权限


        // 工具默认需要用户授权
        // 可以在 manifest 中声明权限
        
        {
          "tools": [
            {
              "name": "greet",
              "path": "./tools/greet.ts",
              "permission": "granted"  // 自动授权
            }
          ]
        }
        


五、编写钩子

5.1 钩子文件


        // hooks/index.ts
        
        export async function preQuery(params: QueryParams): Promise<void> {
          console.log('Before query:', params.messages.length, 'messages');
        }
        
        export async function postQuery(result: QueryResult): Promise<void> {
          console.log('After query, result:', result.type);
        }
        
        export async function preTool(toolCall: ToolCall): Promise<void> {
          console.log('Before tool:', toolCall.name);
        }
        
        export async function postTool(result: ToolResult): Promise<void> {
          console.log('After tool, result:', result.content.slice(0, 50));
        }
        

5.2 钩子类型


        type PluginHooks = {
          // 在发送查询前调用
          preQuery?: (params: QueryParams) => Promise<void>;
        
          // 在收到回复后调用
          postQuery?: (result: QueryResult) => Promise<void>;
        
          // 在工具执行前调用
          preTool?: (toolCall: ToolCall) => Promise<void>;
        
          // 在工具执行后调用
          postTool?: (result: ToolResult) => Promise<void>;
        };
        


六、测试插件

6.1 本地测试


        # 链接到本地开发目录
        claude plugin link ./my-plugin
        
        # 验证插件
        claude plugin validate ./my-plugin
        
        # 查看插件列表
        claude plugin list
        

6.2 调试日志


        // 在插件中添加日志
        import { logForDebugging } from '../../utils/debug.js';
        
        export async function preQuery(params: QueryParams): Promise<void> {
          logForDebugging('preQuery called', { messageCount: params.messages.length });
        }
        


七、打包与发布

7.1 打包


        # 打包插件
        npm run build
        
        # 创建发布包
        mkdir dist
        cp -r plugin.json package.json dist/
        cp -r commands tools hooks dist/
        

7.2 发布到市场


        # 登录市场
        claude marketplace login
        
        # 发布插件
        claude plugin publish ./dist
        
        # 设置标签
        claude plugin tag my-plugin --tags util,productivity
        

7.3 版本管理


        // 语义版本 (SemVer)
        // major.minor.patch
        
        // 1.0.0 - 初始版本
        // 1.0.1 - 修复bug
        // 1.1.0 - 新功能(向后兼容)
        // 2.0.0 - 破坏性变更
        


八、思考题

思考题 1:插件可以调用外部 API 吗?

答案


        // 可以,插件有完全的网络访问权限
        
        export async function fetchData(): Promise<string> {
          const response = await fetch('https://api.example.com/data');
          return response.text();
        }
        
        // 但要注意:
        // 1. API 密钥不要硬编码
        // 2. 使用环境变量存储敏感信息
        // 3. 处理网络错误
        


思考题 2:如何处理插件错误?

答案


        // 捕获并记录错误
        export async function preQuery(params: QueryParams): Promise<void> {
          try {
            // 操作
          } catch (error) {
            logForDebugging('preQuery error', { error: error.message });
            // 不要抛出,否则会中断流程
          }
        }
        


思考题 3:插件可以修改系统提示词吗?

答案


        // 不能直接修改
        // 但可以通过 appendSystemPrompt 影响
        
        // 在 manifest 中声明
        {
          "systemPromptAppend": "You are working with my custom plugin..."
        }
        
        // 或在钩子中
        export async function preQuery(params: QueryParams): Promise<void> {
          params.appendSystemPrompt = 'Additional context...';
        }
        


九、延伸阅读

资源说明
Claude Code SDKhttps://docs.anthropic.com/claude-code/sdk
插件市场https://claude.com/plugins
插件模板https://github.com/anthropics/claude-code-plugin-template


十、下节预告

下一节我们将进入 第六章:Hooks 系统

- Hooks 的类型

- 预置 Hooks

- 自定义 Hooks


*- 第一轮:□ 事实准确性*

*- 第二轮:□ 深度与洞见*

*- 第三轮:□ 可读性与价值*