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