Skip to content

21.4 基本外掛示例

python
## 21.4.1 简单的 Hello World 插件

### 插件实现

// src/plugin.ts import { Plugin, PluginConfig, PluginContext } from '@claude-code/plugin-sdk';

export class HelloWorldPlugin extends Plugin { constructor() { super({ name: 'hello-world', version: '1.0.0', description: 'A simple Hello World plugin' }); }

async initialize(config: PluginConfig): Promise<void> { console.log('Hello World Plugin initialized'); }

async start(): Promise<void> { console.log('Hello World Plugin started'); }

async stop(): Promise<void> { console.log('Hello World Plugin stopped'); }

async cleanup(): Promise<void> { console.log('Hello World Plugin cleaned up'); } }

### 插件清单

    bash


    yaml

    # plugin.yaml
    name: hello-world
    version: 1.0.0
    description: A simple Hello World plugin
    author: Your Name
    license: MIT
    main: dist/plugin.js
    types: dist/plugin.d.ts

    ### 使用示例

    // 使用插件
    import { HelloWorldPlugin } from './plugin';
    const plugin = new HelloWorldPlugin();
    // 初始化插件
    await plugin.initialize({});
    // 启动插件
    await plugin.start();
    // 获取插件状态
    const status = plugin.getStatus();
    console.log(status);
    // 停止插件
    await plugin.stop();
    // 清理插件
    await plugin.cleanup();

## 21.4.2 带工具的插件

### 插件实现

    bash


    typescript

    // src/plugin.ts
    import {
      Plugin,
      PluginConfig,
      Tool,
      ToolResult,
      PluginContext
    } from '@claude-code/plugin-sdk';
    import { GreetingTool } from './tools/greeting';
    import { TimeTool } from './tools/time';

    export class ToolsPlugin extends Plugin {
      private toolManager: any;

      constructor() {
        super({
          name: 'tools-plugin',
          version: '1.0.0',
          description: 'A plugin with tools'
        });

        this.toolManager = {
          register: (tool: Tool) => {},
          execute: async (name: string, params: any, context: PluginContext) => {
            return { success: true, data: {} };
          }
        };
      }

      async initialize(config: PluginConfig): Promise<void> {
        console.log('Tools Plugin initialized');

        // 注册工具
        this.toolManager.register(new GreetingTool());
        this.toolManager.register(new TimeTool());
      }

      async start(): Promise<void> {
        console.log('Tools Plugin started');
      }

      async stop(): Promise<void> {
        console.log('Tools Plugin stopped');
      }

      async cleanup(): Promise<void> {
        console.log('Tools Plugin cleaned up');
      }
    }

    ### Greeting 工具

    // src/tools/greeting.ts
    import { Tool, ToolResult, PluginContext } from '@claude-code/plugin-sdk';
    export class GreetingTool extends Tool {
    constructor() {
    super({
    name: 'greeting',
    description: 'Generate a greeting message',
    parameters: [
    {
    name: 'name',
    type: 'string',
    description: 'The name to greet',
    required: true
    },
    {
    name: 'language',
    type: 'string',
    description: 'The language of the greeting',
    required: false,
    default: 'english'
    }
    ]
    });
    }
    async execute(params: Record<string, any>, context: PluginContext): Promise<ToolResult> {
    const name = params.name;
    const language = params.language || 'english';
    let greeting: string;
    switch (language.toLowerCase()) {
    case 'english':
    greeting = `Hello, ${name}!`;
    break;
    case 'spanish':
    greeting = `¡Hola, ${name}!`;
    break;
    case 'french':
    greeting = `Bonjour, ${name}!`;
    break;
    case 'german':
    greeting = `Hallo, ${name}!`;
    break;
    case 'chinese':
    greeting = `你好,${name}!`;
    break;
    default:
    greeting = `Hello, ${name}!`;
    }
    return {
    success: true,
    data: {
    greeting,
    language
    }
    };
    }
    }

### Time 工具

    bash


    typescript

    // src/tools/time.ts
    import { Tool, ToolResult, PluginContext } from '@claude-code/plugin-sdk';

    export class TimeTool extends Tool {
      constructor() {
        super({
          name: 'time',
          description: 'Get current time in various formats',
          parameters: [
            {
              name: 'format',
              type: 'string',
              description: 'The format of the time (iso, unix, readable)',
              required: false,
              default: 'iso'
            },
            {
              name: 'timezone',
              type: 'string',
              description: 'The timezone (e.g., UTC, America/New_York)',
              required: false
            }
          ]
        });
      }

      async execute(params: Record<string, any>, context: PluginContext): Promise<ToolResult> {
        const format = params.format || 'iso';
        const timezone = params.timezone;

        let date: Date;
        if (timezone) {
          // 使用指定时区
          date = new Date();
        } else {
          date = new Date();
        }

        let time: string;

        switch (format.toLowerCase()) {
          case 'iso':
            time = date.toISOString();
            break;
          case 'unix':
            time = Math.floor(date.getTime() / 1000).toString();
            break;
          case 'readable':
            time = date.toLocaleString();
            break;
          default:
            time = date.toISOString();
        }

        return {
          success: true,
          data: {
            time,
            format,
            timezone: timezone || 'local'
          }
        };
      }
    }

    ### 使用示例

    // 使用插件
    import { ToolsPlugin } from './plugin';
    const plugin = new ToolsPlugin();
    // 初始化插件
    await plugin.initialize({});
    // 启动插件
    await plugin.start();
    // 执行工具
    const greetingResult = await plugin.toolManager.execute(
    'greeting',
    { name: 'World', language: 'chinese' },
    {}
    );
    console.log(greetingResult.data.greeting); // 你好,World!
    const timeResult = await plugin.toolManager.execute(
    'time',
    { format: 'readable' },
    {}
    );
    console.log(timeResult.data.time); // 2024-01-15 10:30:00
    // 停止插件
    await plugin.stop();
    // 清理插件
    await plugin.cleanup();

## 21.4.3 带命令的插件

### 插件实现

    bash


    typescript

    // src/plugin.ts
    import {
      Plugin,
      PluginConfig,
      Command,
      CommandResult,
      PluginContext
    } from '@claude-code/plugin-sdk';
    import { GreetCommand } from './commands/greet';
    import { CalcCommand } from './commands/calc';

    export class CommandsPlugin extends Plugin {
      private commandManager: any;

      constructor() {
        super({
          name: 'commands-plugin',
          version: '1.0.0',
          description: 'A plugin with commands'
        });

        this.commandManager = {
          register: (command: Command) => {},
          execute: async (name: string, args: string[], context: PluginContext) => {
            return { success: true, output: '' };
          }
        };
      }

      async initialize(config: PluginConfig): Promise<void> {
        console.log('Commands Plugin initialized');

        // 注册命令
        this.commandManager.register(new GreetCommand());
        this.commandManager.register(new CalcCommand());
      }

      async start(): Promise<void> {
        console.log('Commands Plugin started');
      }

      async stop(): Promise<void> {
        console.log('Commands Plugin stopped');
      }

      async cleanup(): Promise<void> {
        console.log('Commands Plugin cleaned up');
      }
    }

    ### Greet 命令

    // src/commands/greet.ts
    import { Command, CommandResult, PluginContext } from '@claude-code/plugin-sdk';
    export class GreetCommand extends Command {
    constructor() {
    super({
    name: 'greet',
    description: 'Greet someone',
    parameters: [
    {
    name: 'name',
    type: 'string',
    description: 'The name to greet',
    required: true,
    short: 'n'
    },
    {
    name: 'formal',
    type: 'flag',
    description: 'Use formal greeting',
    required: false,
    short: 'f'
    }
    ]
    });
    }
    async execute(args: string[], context: PluginContext): Promise<CommandResult> {
    const parsed = this.parseArgs(args);
    const name = parsed.name;
    const formal = parsed.formal || false;
    let greeting: string;
    if (formal) {
    greeting = `Good day, ${name}. It is a pleasure to meet you.`;
    } else {
    greeting = `Hey, ${name}! How's it going?`;
    }
    return {
    success: true,
    output: greeting
    };
    }
    }

### Calc 命令

    bash


    typescript

    // src/commands/calc.ts
    import { Command, CommandResult, PluginContext } from '@claude-code/plugin-sdk';

    export class CalcCommand extends Command {
      constructor() {
        super({
          name: 'calc',
          description: 'Perform mathematical calculations',
          parameters: [
            {
              name: 'expression',
              type: 'string',
              description: 'The mathematical expression to evaluate',
              required: true,
              short: 'e'
            },
            {
              name: 'precision',
              type: 'number',
              description: 'Number of decimal places',
              required: false,
              default: 2,
              short: 'p'
            }
          ]
        });
      }

      async execute(args: string[], context: PluginContext): Promise<CommandResult> {
        const parsed = this.parseArgs(args);

        const expression = parsed.expression;
        const precision = parsed.precision || 2;

        try {
          // 安全地评估表达式
          const result = this.evaluateExpression(expression);

          // 格式化结果
          const formatted = result.toFixed(precision);

          return {
            success: true,
            output: `${expression} = ${formatted}`
          };
        } catch (error) {
          return {
            success: false,
            output: '',
            error: error.message,
            exitCode: 1
          };
        }
      }

      private evaluateExpression(expression: string): number {
        // 只允许数字和基本运算符
        const sanitized = expression.replace(/[^0-9+\-*/().]/g, '');

        // 使用 Function 构造函数安全地评估
        return new Function(`return ${sanitized}`)();
      }
    }

    ### 使用示例

    // 使用插件
    import { CommandsPlugin } from './plugin';
    const plugin = new CommandsPlugin();
    // 初始化插件
    await plugin.initialize({});
    // 启动插件
    await plugin.start();
    // 执行命令
    const greetResult = await plugin.commandManager.execute(
    'greet',
    ['--name', 'World', '--formal'],
    {}
    );
    console.log(greetResult.output); // Good day, World. It is a pleasure to meet you.
    const calcResult = await plugin.commandManager.execute(
    'calc',
    ['--expression', '2 + 2 * 3', '--precision', '0'],
    {}
    );
    console.log(calcResult.output); // 2 + 2 * 3 = 8
    // 停止插件
    await plugin.stop();
    // 清理插件
    await plugin.cleanup();

## 21.4.4 带钩子的插件

### 插件实现

    bash


    typescript

    // src/plugin.ts
    import {
      Plugin,
      PluginConfig,
      Hook,
      HookResult,
      HookEvent,
      PluginContext
    } from '@claude-code/plugin-sdk';
    import { LoggingHook } from './hooks/logging';
    import { ErrorHandlingHook } from './hooks/error-handling';

    export class HooksPlugin extends Plugin {
      private hookManager: any;

      constructor() {
        super({
          name: 'hooks-plugin',
          version: '1.0.0',
          description: 'A plugin with hooks'
        });

        this.hookManager = {
          register: (hook: Hook) => {},
          execute: async (type: string, event: HookEvent, context: PluginContext) => {
            return { success: true };
          }
        };
      }

      async initialize(config: PluginConfig): Promise<void> {
        console.log('Hooks Plugin initialized');

        // 注册钩子
        this.hookManager.register(new LoggingHook());
        this.hookManager.register(new ErrorHandlingHook());
      }

      async start(): Promise<void> {
        console.log('Hooks Plugin started');
      }

      async stop(): Promise<void> {
        console.log('Hooks Plugin stopped');
      }

      async cleanup(): Promise<void> {
        console.log('Hooks Plugin cleaned up');
      }
    }

    ### Logging 钩子

    // src/hooks/logging.ts
    import {
    Hook,
    HookResult,
    HookEvent,
    PluginContext,
    HookType
    } from '@claude-code/plugin-sdk';
    export class LoggingHook extends Hook {
    constructor() {
    super({
    name: 'logging',
    type: 'before_command' as HookType,
    description: 'Log all commands before execution',
    priority: 10
    });
    }
    async execute(event: HookEvent, context: PluginContext): Promise<HookResult> {
    const command = event.data.command;
    const args = event.data.args;
    console.log(`[LoggingHook] Executing command: ${command} ${args.join(' ')}`);
    return {
    success: true
    };
    }
    }

### Error Handling 钩子

    bash


    typescript

    // src/hooks/error-handling.ts
    import {
      Hook,
      HookResult,
      HookEvent,
      PluginContext,
      HookType
    } from '@claude-code/plugin-sdk';

    export class ErrorHandlingHook extends Hook {
      constructor() {
        super({
          name: 'error-handling',
          type: 'on_error' as HookType,
          description: 'Handle errors and provide helpful messages',
          priority: 100
        });
      }

      async execute(event: HookEvent, context: PluginContext): Promise<HookResult> {
        const error = event.data.error;

        console.error(`[ErrorHandlingHook] Error occurred: ${error.message}`);

        // 提供错误建议
        const suggestions = this.getErrorSuggestions(error);

        if (suggestions.length > 0) {
          console.log('[ErrorHandlingHook] Suggestions:');
          suggestions.forEach((suggestion, index) => {
            console.log(`  ${index + 1}. ${suggestion}`);
          });
        }

        return {
          success: true
        };
      }

      private getErrorSuggestions(error: Error): string[] {
        const suggestions: string[] = [];

        if (error.message.includes('permission')) {
          suggestions.push('Check if you have the necessary permissions');
          suggestions.push('Try running with elevated privileges');
        }

        if (error.message.includes('not found')) {
          suggestions.push('Verify the file or resource exists');
          suggestions.push('Check the spelling of the file name');
        }

        if (error.message.includes('network')) {
          suggestions.push('Check your internet connection');
          suggestions.push('Verify the server is running');
        }

        return suggestions;
      }
    }

    ### 使用示例

    // 使用插件
    import { HooksPlugin } from './plugin';
    const plugin = new HooksPlugin();
    // 初始化插件
    await plugin.initialize({});
    // 启动插件
    await plugin.start();
    // 触发 before_command 钩子
    await plugin.hookManager.execute(
    'before_command',
    {
    type: 'before_command',
    data: {
    command: 'greet',
    args: ['--name', 'World']
    },
    timestamp: new Date()
    },
    {}
    );
    // 触发 on_error 钩子
    await plugin.hookManager.execute(
    'on_error',
    {
    type: 'on_error',
    data: {
    error: new Error('Permission denied')
    },
    timestamp: new Date()
    },
    {}
    );
    // 停止插件
    await plugin.stop();
    // 清理插件
    await plugin.cleanup();

## 21.4.5 完整的插件示例

### 插件实现

    bash


    typescript

    // src/plugin.ts
    import {
      Plugin,
      PluginConfig,
      Tool,
      Command,
      Hook,
      ToolResult,
      CommandResult,
      HookResult,
      HookEvent,
      PluginContext
    } from '@claude-code/plugin-sdk';
    import { GreetingTool } from './tools/greeting';
    import { TimeTool } from './tools/time';
    import { GreetCommand } from './commands/greet';
    import { CalcCommand } from './commands/calc';
    import { LoggingHook } from './hooks/logging';
    import { ErrorHandlingHook } from './hooks/error-handling';

    export class CompletePlugin extends Plugin {
      private toolManager: any;
      private commandManager: any;
      private hookManager: any;
      private logger: any;

      constructor() {
        super({
          name: 'complete-plugin',
          version: '1.0.0',
          description: 'A complete plugin with tools, commands, and hooks'
        });

        this.toolManager = {
          register: (tool: Tool) => {},
          execute: async (name: string, params: any, context: PluginContext) => {
            return { success: true, data: {} };
          }
        };

        this.commandManager = {
          register: (command: Command) => {},
          execute: async (name: string, args: string[], context: PluginContext) => {
            return { success: true, output: '' };
          }
        };

        this.hookManager = {
          register: (hook: Hook) => {},
          execute: async (type: string, event: HookEvent, context: PluginContext) => {
            return { success: true };
          }
        };

        this.logger = {
          info: (message: string) => console.log(`[INFO] ${message}`),
          error: (message: string) => console.error(`[ERROR] ${message}`)
        };
      }

      async initialize(config: PluginConfig): Promise<void> {
        this.logger.info('Complete Plugin initialized');

        // 注册工具
        this.toolManager.register(new GreetingTool());
        this.toolManager.register(new TimeTool());

        // 注册命令
        this.commandManager.register(new GreetCommand());
        this.commandManager.register(new CalcCommand());

        // 注册钩子
        this.hookManager.register(new LoggingHook());
        this.hookManager.register(new ErrorHandlingHook());
      }

      async start(): Promise<void> {
        this.logger.info('Complete Plugin started');
      }

      async stop(): Promise<void> {
        this.logger.info('Complete Plugin stopped');
      }

      async cleanup(): Promise<void> {
        this.logger.info('Complete Plugin cleaned up');
      }
    }

    ### 使用示例

    // 使用插件
    import { CompletePlugin } from './plugin';
    const plugin = new CompletePlugin();
    // 初始化插件
    await plugin.initialize({});
    // 启动插件
    await plugin.start();
    // 使用工具
    const greetingResult = await plugin.toolManager.execute(
    'greeting',
    { name: 'World', language: 'chinese' },
    {}
    );
    console.log(greetingResult.data.greeting); // 你好,World!
    // 使用命令
    const greetResult = await plugin.commandManager.execute(
    'greet',
    ['--name', 'World', '--formal'],
    {}
    );
    console.log(greetResult.output); // Good day, World. It is a pleasure to meet you.
    // 触发钩子
    await plugin.hookManager.execute(
    'before_command',
    {
    type: 'before_command',
    data: {
    command: 'greet',
    args: ['--name', 'World']
    },
    timestamp: new Date()
    },
    {}
    );
    // 停止插件
    await plugin.stop();
    // 清理插件
    await plugin.cleanup();

## 21.4.6 插件测试示例

### 测试文件

    bash


    typescript

    // __tests__/plugin.test.ts
    import { CompletePlugin } from '../src/plugin';

    describe('CompletePlugin', () => {
      let plugin: CompletePlugin;

      beforeEach(() => {
        plugin = new CompletePlugin();
      });

      afterEach(async () => {
        try {
          await plugin.cleanup();
        } catch (error) {
          // 忽略清理错误
        }
      });

      test('should initialize successfully', async () => {
        await expect(plugin.initialize({})).resolves.not.toThrow();

        const status = plugin.getStatus();
        expect(status.name).toBe('complete-plugin');
        expect(status.version).toBe('1.0.0');
      });

      test('should start successfully', async () => {
        await plugin.initialize({});
        await expect(plugin.start()).resolves.not.toThrow();
      });

      test('should stop successfully', async () => {
        await plugin.initialize({});
        await plugin.start();
        await expect(plugin.stop()).resolves.not.toThrow();
      });

      test('should cleanup successfully', async () => {
        await plugin.initialize({});
        await plugin.start();
        await plugin.stop();
        await expect(plugin.cleanup()).resolves.not.toThrow();
      });
    });

    ### 工具测试

    // __tests__/tools/greeting.test.ts
    import { GreetingTool } from '../../src/tools/greeting';
    describe('GreetingTool', () => {
    let tool: GreetingTool;
    beforeEach(() => {
    tool = new GreetingTool();
    });
    test('should generate English greeting', async () => {
    const result = await tool.execute(
    { name: 'World', language: 'english' },
    {}
    );
    expect(result.success).toBe(true);
    expect(result.data.greeting).toBe('Hello, World!');
    });
    test('should generate Chinese greeting', async () => {
    const result = await tool.execute(
    { name: 'World', language: 'chinese' },
    {}
    );
    expect(result.success).toBe(true);
    expect(result.data.greeting).toBe('你好,World!');
    });
    test('should validate parameters', () => {
    const result = tool.validate({});
    expect(result.valid).toBe(false);
    expect(result.errors).toContain('Missing required parameter: name');
    });
    });

### 命令测试

    bash


    typescript

    // __tests__/commands/greet.test.ts
    import { GreetCommand } from '../../src/commands/greet';

    describe('GreetCommand', () => {
      let command: GreetCommand;

      beforeEach(() => {
        command = new GreetCommand();
      });

      test('should greet informally', async () => {
        const result = await command.execute(
          ['--name', 'World'],
          {}
        );

        expect(result.success).toBe(true);
        expect(result.output).toContain('Hey, World!');
      });

      test('should greet formally', async () => {
        const result = await command.execute(
          ['--name', 'World', '--formal'],
          {}
        );

        expect(result.success).toBe(true);
        expect(result.output).toContain('Good day, World.');
      });

      test('should parse arguments correctly', () => {
        const parsed = command.parseArgs(['--name', 'World', '--formal']);

        expect(parsed.name).toBe('World');
        expect(parsed.formal).toBe(true);
      });
    });

    ### 运行测试

    # 运行所有测试
    npm test
    # 运行特定测试文件
    npm test -- plugin.test.ts
    # 运行测试并生成覆盖率报告
    npm test -- --coverage
    # 监听模式
    npm run test:watch

基于 MIT 许可发布 | 永久导航