Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:39:56 +08:00
commit e862bbb341
40 changed files with 10597 additions and 0 deletions

View File

@@ -0,0 +1,453 @@
---
name: cc-plugin-expert
description: 'Comprehensive Claude Code plugin development expert providing guidance for creation, maintenance, installation, configuration, and troubleshooting of plugins and skills'
category: development
tags:
[
'claude-code',
'plugin-development',
'skill-creation',
'maintenance',
'troubleshooting',
'best-practices',
]
triggers:
- type: keyword
pattern: 'create plugin'
priority: 3
- type: keyword
pattern: 'plugin development'
priority: 3
- type: keyword
pattern: 'claude code plugin'
priority: 3
- type: keyword
pattern: 'skill creation'
priority: 3
- type: keyword
pattern: 'plugin troubleshooting'
priority: 2
- type: keyword
pattern: 'plugin installation'
priority: 2
- type: keyword
pattern: 'plugin configuration'
priority: 2
- type: keyword
pattern: 'plugin maintenance'
priority: 2
- type: pattern
pattern: 'how to (create|build|develop) (a )?claude code plugin'
priority: 3
- type: pattern
pattern: '(fix|troubleshoot|debug) (my )?claude code plugin'
priority: 2
- type: pattern
pattern: '(install|configure|setup) (a )?claude code plugin'
priority: 2
- type: context
pattern: 'plugin-development'
priority: 2
- type: context
pattern: 'claude-code-ecosystem'
priority: 1
---
# Claude Code Plugin Expert
This skill provides comprehensive expertise for Claude Code plugin development, including creation, installation, configuration, maintenance, and troubleshooting. It serves as the definitive resource for developers working with Claude Code plugins and skills.
## When to Use This Skill
Use this skill when you need to:
- **Create new plugins** from scratch or templates
- **Develop custom skills** for specific workflows
- **Install and configure** plugins in various environments
- **Troubleshoot plugin issues** and errors
- **Maintain and update** existing plugins
- **Optimize plugin performance** and security
- **Understand best practices** for plugin development
- **Debug plugin execution** problems
- **Configure plugin permissions** and security
- **Manage plugin dependencies** and versions
## Core Capabilities
### 1. Plugin Development Guidance
- Complete plugin architecture understanding
- Step-by-step plugin creation workflows
- Code structure and organization patterns
- TypeScript/JavaScript best practices
- Plugin manifest configuration
- Command and skill implementation
### 2. Installation and Configuration
- Multiple installation methods (marketplace, local, git)
- Environment-specific configuration
- Permission management and security
- Dependency resolution and versioning
- Marketplace configuration
- Enterprise deployment strategies
### 3. Troubleshooting and Debugging
- Common plugin issues and solutions
- Performance optimization techniques
- Error handling and logging strategies
- Debugging tools and methodologies
- Plugin isolation and testing
- Health monitoring and analytics
### 4. Best Practices and Standards
- Code quality standards and patterns
- Security considerations and validation
- Performance optimization strategies
- Testing methodologies and frameworks
- Documentation standards
- Community guidelines and contribution
## Development Framework
### Phase 1: Requirements Analysis
- Understand plugin purpose and scope
- Identify target users and use cases
- Determine required permissions and dependencies
- Plan plugin architecture and components
- Define success criteria and metrics
### Phase 2: Design and Planning
- Create plugin structure and manifest
- Design command and skill interfaces
- Plan configuration and settings
- Consider security and performance requirements
- Document technical specifications
### Phase 3: Implementation
- Set up development environment
- Create plugin directory structure
- Implement core plugin functionality
- Add commands and skills
- Include error handling and logging
### Phase 4: Testing and Validation
- Unit testing of components
- Integration testing with Claude Code
- Performance testing and optimization
- Security testing and validation
- User acceptance testing
### Phase 5: Deployment and Maintenance
- Package plugin for distribution
- Install and configure in target environments
- Monitor performance and usage
- Update and maintain over time
- Handle user feedback and issues
## Plugin Architecture Understanding
### Core Components
- **Plugin Manifest**: Metadata and configuration
- **Commands**: Slash commands for user interaction
- **Skills**: AI-triggered capabilities
- **MCP Servers**: External tool integrations
- **Event Hooks**: Automated response handlers
- **Custom Agents**: Specialized AI agents
### Directory Structure
```
my-plugin/
├── .claude-plugin/
│ ├── plugin.json # Plugin metadata
│ └── marketplace.json # Distribution config
├── commands/ # Custom slash commands
│ ├── command1.md
│ └── command2.md
├── skills/ # Agent skills
│ └── my-skill/
│ └── SKILL.md
├── agents/ # Custom agents
│ └── custom-agent.json
├── hooks/ # Event handlers
│ └── hooks.json
├── mcp/ # MCP server configs
│ └── server.json
├── scripts/ # Helper scripts
├── templates/ # Code templates
└── README.md # Plugin documentation
```
## Installation Methods
### 1. Interactive Marketplace Installation
```bash
claude
/plugin
```
### 2. Direct Command Installation
```bash
# Install from marketplace
claude marketplace install plugin-name@marketplace-name
# Install from local directory
claude marketplace install ./my-plugin
# Install from Git repository
claude marketplace install https://github.com/user/plugin-repo.git
# Install specific version
claude marketplace install plugin-name@1.2.3
```
### 3. Configuration File Installation
```json
{
"plugins": ["my-awesome-plugin@latest", "code-formatter@2.1.0", "database-tools@github:user/repo"]
}
```
## Configuration Management
### Settings File Structure
```json
{
"version": "1.0.0",
"plugins": {
"sources": [
{
"type": "marketplace",
"name": "official",
"url": "https://github.com/claude-official/marketplace",
"enabled": true
}
],
"installed": [
{
"name": "code-formatter",
"version": "2.1.0",
"source": "official",
"installedAt": "2024-01-15T10:30:00Z",
"enabled": true,
"autoUpdate": true
}
]
},
"permissions": {
"allowedDomains": ["github.com", "api.github.com"],
"allowedCommands": ["git", "npm", "node"],
"filesystemAccess": ["read", "write"],
"networkAccess": true
}
}
```
## Common Issues and Solutions
### Plugin Not Found
- Verify marketplace configuration
- Check plugin name spelling
- Confirm plugin exists in marketplace
- Test network connectivity
### Permission Denied
- Check file system permissions
- Verify plugin permissions
- Review security settings
- Use alternative installation directory
### Version Conflicts
- Check dependency tree
- Use specific version constraints
- Resolve conflicts automatically
- Force reinstall if needed
### Performance Issues
- Implement lazy loading
- Add caching strategies
- Monitor resource usage
- Optimize code execution
## Development Best Practices
### Code Quality
- Use TypeScript with strict configuration
- Implement comprehensive error handling
- Follow consistent naming conventions
- Add proper documentation and comments
- Use ESLint and Prettier for code formatting
### Security
- Validate all input parameters
- Implement proper permission management
- Use secure plugin execution patterns
- Scan dependencies for vulnerabilities
- Follow principle of least privilege
### Performance
- Use lazy loading for resources
- Implement caching strategies
- Monitor memory usage
- Optimize database queries
- Use asynchronous operations
### Testing
- Write comprehensive unit tests
- Implement integration testing
- Test error scenarios
- Monitor plugin performance
- Test security boundaries
## Debugging Tools and Techniques
### Debug Logging
```typescript
// Enable debug logging
export CLAUDE_DEBUG=true
claude --verbose
// Check plugin status
claude plugin list
claude plugin status plugin-name
```
### Performance Monitoring
```typescript
class PerformanceMonitor {
async measure<T>(
operation: () => Promise<T>,
operationName: string
): Promise<{ result: T; metrics: PerformanceMetrics }> {
// Implementation for performance measurement
}
}
```
### Error Analysis
- Review error logs and stack traces
- Check plugin initialization sequence
- Validate configuration files
- Test with minimal dependencies
- Use isolated test environments
## Security Considerations
### Input Validation
- Sanitize all user inputs
- Validate parameter types and ranges
- Prevent injection attacks
- Use whitelist validation
### Permission Management
- Request minimum required permissions
- Implement permission checks
- Use secure file access patterns
- Validate network requests
### Dependency Security
- Scan dependencies for vulnerabilities
- Keep dependencies updated
- Use trusted sources
- Review license compatibility
## Advanced Features
### Plugin Communication
- Event-driven architecture
- Inter-plugin messaging
- Shared resource management
- Dependency injection
### Enterprise Features
- Centralized configuration management
- Team-based plugin distribution
- Security policy enforcement
- Usage analytics and reporting
### Performance Optimization
- Lazy loading strategies
- Memory management
- Caching implementations
- Resource pooling
## Community Resources
### Documentation
- Official Claude Code documentation
- Plugin development guides
- API reference documentation
- Best practice guides
### Support Channels
- GitHub discussions and issues
- Community forums
- Stack Overflow
- Discord/Slack communities
### Contributing
- Fork and clone repositories
- Create feature branches
- Submit pull requests
- Participate in code reviews
## Quality Assurance Checklist
### Before Release
- [ ] Code follows all style guidelines
- [ ] All tests pass successfully
- [ ] Documentation is complete and accurate
- [ ] Security review passed
- [ ] Performance benchmarks met
- [ ] Plugin tested in multiple environments
- [ ] Error handling comprehensive
- [ ] Dependencies validated
### After Installation
- [ ] Plugin loads without errors
- [ ] Commands function correctly
- [ ] Skills trigger appropriately
- [ ] Configuration works as expected
- [ ] Permissions are properly enforced
- [ ] Performance is acceptable
- [ ] Logs are informative
- [ ] User documentation is helpful
---
_This skill serves as the comprehensive resource for Claude Code plugin development, providing guidance from initial concept through deployment and maintenance. For specific implementation details, refer to the reference materials in the references/ directory._

View File

@@ -0,0 +1,796 @@
# Claude Code Plugin Development Best Practices
This guide covers best practices for developing high-quality, maintainable, and secure Claude Code plugins.
## Code Quality Standards
### TypeScript Best Practices
#### Configuration
```json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
}
```
#### Interface Design
```typescript
// Use clear interfaces
interface PluginConfig {
name: string;
version: string;
settings: PluginSettings;
}
interface PluginSettings {
enabled: boolean;
autoUpdate: boolean;
customOptions: Record<string, unknown>;
}
// Use generic types for flexibility
class PluginManager<T extends PluginConfig> {
private plugins: Map<string, T> = new Map();
register(plugin: T): void {
this.plugins.set(plugin.name, plugin);
}
get<K extends keyof T>(name: string, key: K): T[K] | undefined {
const plugin = this.plugins.get(name);
return plugin?.[key];
}
}
```
#### Error Handling
```typescript
// Implement proper error handling
class PluginError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly pluginName: string
) {
super(message);
this.name = 'PluginError';
}
}
// Use async/await for asynchronous operations
async function loadPlugin(pluginPath: string): Promise<Plugin> {
try {
const manifest = await loadManifest(pluginPath);
const plugin = await import(pluginPath);
return new plugin.default(manifest);
} catch (error) {
throw new PluginError(`Failed to load plugin from ${pluginPath}`, 'LOAD_ERROR', pluginPath);
}
}
```
### Code Organization
#### Directory Structure
```
src/
├── types/
│ ├── plugin.ts
│ ├── command.ts
│ └── skill.ts
├── core/
│ ├── plugin-manager.ts
│ ├── command-registry.ts
│ └── skill-loader.ts
├── utils/
│ ├── file-utils.ts
│ ├── validation.ts
│ └── logger.ts
├── commands/
│ ├── base-command.ts
│ └── implementations/
├── skills/
│ ├── base-skill.ts
│ └── implementations/
└── index.ts
```
#### Naming Conventions
```typescript
// Use descriptive names
class PluginConfigurationValidator {} // Good
class PCV {} // Bad
// Use consistent patterns
interface PluginManifest {} // Interface
class PluginLoader {} // Class
const DEFAULT_PLUGIN_PATH = '/plugins'; // Constant
function validatePluginManifest() {} // Function
// File naming
plugin - manager.ts; // kebab-case for files
PluginManager; // PascalCase for classes
validatePlugin(); // camelCase for functions
```
## Performance Optimization
### Lazy Loading
```typescript
class PluginRegistry {
private plugins = new Map<string, () => Promise<Plugin>>();
private loadedPlugins = new Map<string, Plugin>();
async get(name: string): Promise<Plugin> {
// Check if already loaded
if (this.loadedPlugins.has(name)) {
return this.loadedPlugins.get(name)!;
}
// Load plugin on demand
const loader = this.plugins.get(name);
if (!loader) {
throw new Error(`Plugin not found: ${name}`);
}
const plugin = await loader();
this.loadedPlugins.set(name, plugin);
return plugin;
}
}
```
### Caching Strategies
```typescript
interface CacheEntry<T> {
value: T;
timestamp: number;
ttl: number;
}
class Cache<T> {
private cache = new Map<string, CacheEntry<T>>();
set(key: string, value: T, ttl: number = 300000): void {
// 5 minutes default
this.cache.set(key, {
value,
timestamp: Date.now(),
ttl,
});
}
get(key: string): T | undefined {
const entry = this.cache.get(key);
if (!entry) {
return undefined;
}
if (Date.now() - entry.timestamp > entry.ttl) {
this.cache.delete(key);
return undefined;
}
return entry.value;
}
// Cleanup expired entries
cleanup(): void {
const now = Date.now();
for (const [key, entry] of this.cache.entries()) {
if (now - entry.timestamp > entry.ttl) {
this.cache.delete(key);
}
}
}
}
```
### Resource Management
```typescript
class ResourceManager {
private resources = new Set<() => Promise<void>>();
register(cleanup: () => Promise<void>): void {
this.resources.add(cleanup);
}
async cleanup(): Promise<void> {
const cleanupPromises = Array.from(this.resources).map(async cleanup => {
try {
await cleanup();
} catch (error) {
console.error('Cleanup error:', error);
}
});
await Promise.allSettled(cleanupPromises);
this.resources.clear();
}
}
// Usage with automatic cleanup
class Plugin {
private resourceManager = new ResourceManager();
async initialize(): Promise<void> {
// Register cleanup functions
this.resourceManager.register(async () => {
await this.closeConnections();
});
this.resourceManager.register(async () => {
await this.cleanupTempFiles();
});
}
async destroy(): Promise<void> {
await this.resourceManager.cleanup();
}
}
```
## Security Considerations
### Input Validation
```typescript
import Joi from 'joi';
const pluginConfigSchema = Joi.object({
name: Joi.string().alphanum().min(1).max(50).required(),
version: Joi.string()
.pattern(/^\d+\.\d+\.\d+$/)
.required(),
description: Joi.string().max(500).optional(),
permissions: Joi.array().items(Joi.string()).optional(),
});
class PluginValidator {
static validateConfig(config: unknown): PluginConfig {
const { error, value } = pluginConfigSchema.validate(config);
if (error) {
throw new ValidationError(`Invalid plugin configuration: ${error.message}`);
}
return value;
}
static sanitizeInput(input: string): string {
return input
.replace(/[<>]/g, '') // Remove HTML tags
.replace(/javascript:/gi, '') // Remove javascript protocol
.trim()
.substring(0, 1000); // Limit length
}
}
```
### Permission Management
```typescript
enum Permission {
FILE_READ = 'file:read',
FILE_WRITE = 'file:write',
NETWORK_REQUEST = 'network:request',
SYSTEM_EXEC = 'system:exec',
ENV_READ = 'env:read',
}
class PermissionManager {
private permissions = new Set<Permission>();
constructor(permissions: Permission[]) {
this.permissions = new Set(permissions);
}
has(permission: Permission): boolean {
return this.permissions.has(permission);
}
require(permission: Permission): void {
if (!this.has(permission)) {
throw new SecurityError(`Permission required: ${permission}`);
}
}
checkFileAccess(path: string, mode: 'read' | 'write'): void {
const permission = mode === 'read' ? Permission.FILE_READ : Permission.FILE_WRITE;
this.require(permission);
// Additional path validation
if (path.includes('..')) {
throw new SecurityError('Path traversal detected');
}
if (path.startsWith('/etc/') || path.startsWith('/sys/')) {
throw new SecurityError('Access to system directories denied');
}
}
}
```
### Secure Plugin Execution
```typescript
interface SecureExecutionContext {
permissions: Permission[];
timeout: number;
memoryLimit: number;
}
class SecurePluginRunner {
async executePlugin(plugin: Plugin, context: SecureExecutionContext): Promise<unknown> {
const permissionManager = new PermissionManager(context.permissions);
const monitor = new ResourceMonitor(context.memoryLimit);
try {
// Set up timeout
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Plugin execution timeout')), context.timeout);
});
// Execute plugin with monitoring
const executionPromise = this.executeWithMonitoring(plugin, permissionManager, monitor);
const result = await Promise.race([executionPromise, timeoutPromise]);
return result;
} finally {
monitor.stop();
}
}
}
```
## Error Handling and Logging
### Structured Error Handling
```typescript
abstract class PluginError extends Error {
abstract readonly code: string;
abstract readonly category: ErrorCategory;
constructor(
message: string,
public readonly context?: Record<string, unknown>
) {
super(message);
this.name = this.constructor.name;
}
toJSON(): ErrorRecord {
return {
name: this.name,
message: this.message,
code: this.code,
category: this.category,
context: this.context,
stack: this.stack,
timestamp: new Date().toISOString(),
};
}
}
enum ErrorCategory {
CONFIGURATION = 'configuration',
EXECUTION = 'execution',
VALIDATION = 'validation',
NETWORK = 'network',
FILESYSTEM = 'filesystem',
SECURITY = 'security',
}
```
### Comprehensive Logging
```typescript
interface LogEntry {
level: LogLevel;
message: string;
timestamp: string;
context?: Record<string, unknown>;
error?: ErrorRecord;
plugin?: string;
operation?: string;
}
class Logger {
private transports: LogTransport[] = [];
constructor(private minLevel: LogLevel = LogLevel.INFO) {}
addTransport(transport: LogTransport): void {
this.transports.push(transport);
}
debug(message: string, context?: Record<string, unknown>): void {
this.log(LogLevel.DEBUG, message, context);
}
info(message: string, context?: Record<string, unknown>): void {
this.log(LogLevel.INFO, message, context);
}
warn(message: string, context?: Record<string, unknown>): void {
this.log(LogLevel.WARN, message, context);
}
error(message: string, error?: Error, context?: Record<string, unknown>): void {
const errorRecord = error
? {
name: error.name,
message: error.message,
stack: error.stack,
}
: undefined;
this.log(LogLevel.ERROR, message, context, errorRecord);
}
private log(
level: LogLevel,
message: string,
context?: Record<string, unknown>,
error?: ErrorRecord
): void {
if (level < this.minLevel) {
return;
}
const entry: LogEntry = {
level,
message,
timestamp: new Date().toISOString(),
context,
error,
plugin: context?.plugin as string,
operation: context?.operation as string,
};
this.transports.forEach(transport => {
try {
transport.log(entry);
} catch (transportError) {
console.error('Transport error:', transportError);
}
});
}
}
```
## Testing Strategies
### Unit Testing
```typescript
import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
import { PluginManager } from '../src/plugin-manager';
import { MockPlugin } from './mocks/mock-plugin';
describe('PluginManager', () => {
let pluginManager: PluginManager;
beforeEach(() => {
pluginManager = new PluginManager();
});
afterEach(() => {
pluginManager.cleanup();
});
describe('registerPlugin', () => {
it('should register a valid plugin', () => {
const plugin = new MockPlugin('test-plugin', '1.0.0');
expect(() => pluginManager.register(plugin)).not.toThrow();
expect(pluginManager.isRegistered('test-plugin')).toBe(true);
});
it('should reject plugin with invalid name', () => {
const plugin = new MockPlugin('', '1.0.0');
expect(() => pluginManager.register(plugin)).toThrow(
'Plugin name must be a non-empty string'
);
});
});
});
```
### Integration Testing
```typescript
describe('Plugin Integration', () => {
let client: ClaudeCodeClient;
let server: TestServer;
beforeAll(async () => {
server = new TestServer();
await server.start();
client = new ClaudeCodeClient({
endpoint: server.getUrl(),
timeout: 5000,
});
});
afterAll(async () => {
await server.stop();
});
it('should install and execute plugin end-to-end', async () => {
// Install plugin
const installResult = await client.installPlugin({
name: 'integration-test-plugin',
version: '1.0.0',
source: './test-fixtures/integration-plugin',
});
expect(installResult.success).toBe(true);
// Execute command
const commandResult = await client.executeCommand('/integration-test', {
input: 'test data',
});
expect(commandResult.success).toBe(true);
expect(commandResult.output).toContain('processed: test data');
});
});
```
### Performance Testing
```typescript
describe('Performance Tests', () => {
it('should handle high load without memory leaks', async () => {
const monitor = new PerformanceMonitor();
const plugin = new TestPlugin();
const initialMemory = process.memoryUsage().heapUsed;
const iterations = 1000;
for (let i = 0; i < iterations; i++) {
await monitor.measure(() => plugin.process(`test-data-${i}`), 'process-operation');
}
const finalMemory = process.memoryUsage().heapUsed;
const memoryIncrease = finalMemory - initialMemory;
// Memory increase should be reasonable (less than 10MB)
expect(memoryIncrease).toBeLessThan(10 * 1024 * 1024);
});
});
```
## Documentation Standards
### Code Documentation
````typescript
/**
* Plugin manager for handling plugin lifecycle and execution.
*
* This class provides a centralized way to manage Claude Code plugins,
* including registration, execution, and cleanup operations.
*
* @example
* ```typescript
* const manager = new PluginManager();
* const plugin = new MyPlugin();
*
* manager.register(plugin);
* const result = await manager.executeCommand('my-command', { param: 'value' });
* ```
*/
export class PluginManager {
/**
* Registers a plugin with the manager.
*
* @param plugin - The plugin to register
* @throws {ValidationError} If plugin validation fails
* @throws {DuplicateError} If a plugin with the same name is already registered
*/
register(plugin: Plugin): void {
this.validatePlugin(plugin);
this.checkDuplicate(plugin.name);
this.plugins.set(plugin.name, plugin);
}
}
````
### README Template
````markdown
# Plugin Name
> Brief description of what the plugin does
## Features
- Feature 1
- Feature 2
- Feature 3
## Installation
```bash
claude marketplace install plugin-name
```
````
## Usage
### Basic Usage
```bash
/command-name --param=value
```
## Configuration
Add to your `.claude/settings.json`:
```json
{
"plugins": {
"plugin-name": {
"setting1": "value1",
"setting2": "value2"
}
}
}
```
## Development
### Building
```bash
npm run build
```
### Testing
```bash
npm test
```
## License
License information.
````
## Version Management
### Semantic Versioning
```typescript
class VersionManager {
static parseVersion(version: string): Version {
const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
if (!match) {
throw new Error(`Invalid version format: ${version}`);
}
return {
major: parseInt(match[1], 10),
minor: parseInt(match[2], 10),
patch: parseInt(match[3], 10),
prerelease: match[4] || null,
};
}
static compareVersions(v1: string, v2: string): number {
const version1 = this.parseVersion(v1);
const version2 = this.parseVersion(v2);
if (version1.major !== version2.major) {
return version1.major - version2.major;
}
if (version1.minor !== version2.minor) {
return version1.minor - version2.minor;
}
if (version1.patch !== version2.patch) {
return version1.patch - version2.patch;
}
return 0;
}
}
````
## Design Patterns
### Plugin Factory Pattern
```typescript
abstract class PluginFactory {
abstract create(config: PluginConfig): Plugin;
static register(type: string, factory: PluginFactory): void {
this.factories.set(type, factory);
}
static create(type: string, config: PluginConfig): Plugin {
const factory = this.factories.get(type);
if (!factory) {
throw new Error(`Unknown plugin type: ${type}`);
}
return factory.create(config);
}
private static factories = new Map<string, PluginFactory>();
}
```
### Observer Pattern for Plugin Events
```typescript
class PluginEventEmitter {
private handlers = new Map<string, PluginEventHandler[]>();
on(eventType: string, handler: PluginEventHandler): void {
if (!this.handlers.has(eventType)) {
this.handlers.set(eventType, []);
}
this.handlers.get(eventType)!.push(handler);
}
emit(event: PluginEvent): void {
const handlers = this.handlers.get(event.type);
if (handlers) {
handlers.forEach(handler => {
try {
handler(event);
} catch (error) {
console.error(`Error in event handler for ${event.type}:`, error);
}
});
}
}
}
```
## Quality Assurance Checklist
### Before Release
- [ ] Code follows all style guidelines
- [ ] All tests pass successfully
- [ ] Documentation is complete and accurate
- [ ] Security review passed
- [ ] Performance benchmarks met
- [ ] Plugin tested in multiple environments
- [ ] Error handling comprehensive
- [ ] Dependencies validated
### Code Review
- [ ] Functions have clear single responsibilities
- [ ] Error handling is comprehensive
- [ ] Logging is appropriate and informative
- [ ] Tests cover edge cases
- [ ] Security considerations are addressed
- [ ] Performance implications are considered
---
_Following these best practices will help ensure your Claude Code plugins are high-quality, secure, and well-maintained._

View File

@@ -0,0 +1,201 @@
# Claude Code Plugin Quick Start
This reference provides a quick-start guide for creating your first Claude Code plugin.
## Prerequisites
- Claude Code installed and working
- Basic knowledge of TypeScript/JavaScript
- Text editor (VS Code recommended)
- Git for version control
## Step 1: Create Plugin Structure
```bash
mkdir my-first-plugin
cd my-first-plugin
mkdir -p .claude-plugin commands skills agents hooks
```
## Step 2: Create Plugin Manifest
Create `.claude-plugin/plugin.json`:
```json
{
"name": "my-first-plugin",
"version": "1.0.0",
"description": "My first Claude Code plugin",
"author": "Your Name",
"license": "MIT",
"repository": "https://github.com/username/my-first-plugin",
"main": "index.js",
"claude": {
"minVersion": "1.0.0",
"maxVersion": "2.0.0"
},
"permissions": ["file:read", "file:write", "network:request"],
"dependencies": {},
"keywords": ["utility", "productivity"]
}
```
## Step 3: Add a Custom Command
Create `commands/hello.md`:
````markdown
---
name: hello
description: 'Say hello with a custom message'
parameters:
- name: name
type: string
description: 'Name to greet'
required: false
default: 'World'
---
Hello! This is a custom command from my first plugin.
## Usage
```bash
/hello --name="Claude"
```
````
## Output
```
Hello, Claude! This message comes from my-first-plugin.
```
````
## Step 4: Create a Skill
Create `skills/my-skill/SKILL.md`:
```markdown
---
name: my-skill
description: "A simple skill for demonstration"
category: utility
tags: ["demo", "example"]
triggers:
- type: keyword
pattern: "demo task"
priority: 2
---
This skill demonstrates basic plugin functionality.
## When to Use
Use this skill when you need to perform simple demonstration tasks.
## Capabilities
- Basic text processing
- Simple calculations
- File operations
- Example workflows
````
## Step 5: Test Your Plugin
```bash
# Install plugin locally
claude marketplace install ./my-first-plugin
# Test the command
claude
/hello --name="Test User"
# Test the skill
claude
I need help with a demo task
```
## Step 6: Package for Distribution
```bash
# Create distribution package
claude plugin package
# Or manually zip the plugin
zip -r my-first-plugin.zip . -x ".git/*" "node_modules/*" "dist/*"
```
## Common Templates
### Basic Command Template
````markdown
---
name: command-name
description: 'Brief description of the command'
parameters:
- name: param1
type: string
description: 'Description of parameter'
required: true
- name: param2
type: boolean
description: 'Description of optional parameter'
required: false
default: false
---
Command description and usage examples.
## Examples
```bash
/command-name --param1="value" --param2
```
````
````
### Basic Skill Template
```markdown
---
name: skill-name
description: "Brief description of the skill"
category: utility
tags: ["tag1", "tag2"]
triggers:
- type: keyword
pattern: "trigger phrase"
priority: 2
---
Skill description explaining when and how to use it.
## When to Use
Use this skill when you need to...
## Capabilities
List of what the skill can do.
````
## Next Steps
1. Add more commands and skills
2. Implement custom logic
3. Add configuration options
4. Write tests
5. Create documentation
6. Publish to marketplace
## Resources
- [Full Plugin Development Guide](../plugin-development-guide.md)
- [API Reference](../api-reference.md)
- [Best Practices](../best-practices.md)
- [Troubleshooting Guide](../troubleshooting.md)

View File

@@ -0,0 +1,585 @@
# Claude Code Plugin Troubleshooting Guide
This guide provides solutions to common problems encountered when developing, installing, or using Claude Code plugins.
## Installation Issues
### Plugin Not Found
**Symptoms**: Plugin cannot be found in marketplace or installation fails with "plugin not found" error.
**Causes**:
- Incorrect plugin name or marketplace
- Marketplace not configured properly
- Network connectivity issues
- Plugin repository not accessible
**Solutions**:
```bash
# Check available marketplaces
claude marketplace list
# Search for the plugin
claude marketplace search plugin-name
# Add missing marketplace
claude marketplace add https://github.com/marketplace-url
# Verify marketplace connectivity
curl -I https://github.com/marketplace-url
# Install with full specification
claude marketplace install plugin-name@marketplace-name
```
**Debug Steps**:
1. Verify marketplace configuration in `~/.claude/settings.json`
2. Check network connectivity
3. Validate plugin name spelling
4. Confirm plugin exists in specified marketplace
### Permission Denied During Installation
**Symptoms**: Installation fails with permission errors.
**Causes**:
- Insufficient file system permissions
- Protected directories
- Antivirus software blocking installation
**Solutions**:
```bash
# Check directory permissions
ls -la ~/.claude/
ls -la ~/.claude/plugins/
# Fix permissions (use with caution)
chmod -R 755 ~/.claude/
chmod 600 ~/.claude/settings.json
# Install in alternative directory
mkdir -p ~/claude-plugins
export CLAUDE_PLUGIN_DIR=~/claude-plugins
claude marketplace install plugin-name
```
### Version Conflicts
**Symptoms**: Installation fails due to version conflicts with dependencies.
**Causes**:
- Incompatible dependency versions
- Semantic versioning constraints
- Circular dependencies
**Solutions**:
```bash
# Check dependency tree
claude plugin deps plugin-name
# Force specific version
claude marketplace install plugin-name@1.2.3
# Resolve conflicts automatically
claude plugin resolve-conflicts
# Clean installation
claude plugin uninstall plugin-name
claude marketplace install plugin-name --force
```
## Runtime Errors
### Plugin Loading Failures
**Symptoms**: Plugin fails to load during startup.
**Causes**:
- Missing dependencies
- Code syntax errors
- Initialization failures
**Debug Commands**:
```bash
# Enable debug logging
export CLAUDE_DEBUG=true
claude --verbose
# Check plugin status
claude plugin list
claude plugin status plugin-name
# Load plugin manually
claude plugin load plugin-name --debug
# Check logs
tail -f ~/.claude/logs/plugin-loading.log
```
**Example Debug Code**:
```typescript
// plugin-loader.ts
class PluginLoader {
async loadPlugin(pluginPath: string): Promise<Plugin> {
try {
console.log(`Loading plugin from: ${pluginPath}`);
// Validate manifest
const manifest = await this.loadManifest(pluginPath);
console.log(`Plugin manifest loaded: ${manifest.name}@${manifest.version}`);
// Check dependencies
await this.checkDependencies(manifest);
console.log('Dependencies verified');
// Load plugin module
const PluginClass = await import(path.join(pluginPath, manifest.main));
const plugin = new PluginClass.default(manifest);
// Initialize plugin
await plugin.initialize(this.createPluginContext(manifest));
console.log(`Plugin initialized successfully: ${manifest.name}`);
return plugin;
} catch (error) {
console.error(`Failed to load plugin from ${pluginPath}:`, error);
throw error;
}
}
}
```
### Command Execution Failures
**Symptoms**: Plugin commands fail to execute or return errors.
**Debug Steps**:
```bash
# Execute command with debug information
claude /command-name --param=value --debug
# Check command registration
claude plugin commands plugin-name
# Test command in isolation
claude plugin test-command plugin-name command-name --params '{"key":"value"}'
```
**Error Handling Template**:
```typescript
// command-handler.ts
class CommandHandler {
async handleCommand(
command: Command,
parameters: Record<string, unknown>,
context: CommandContext
): Promise<CommandResult> {
try {
context.logger.debug(`Executing command: ${command.name}`, { parameters });
// Validate parameters
await this.validateParameters(command, parameters);
// Execute command
const result = await command.handler(parameters, context);
context.logger.debug(`Command executed successfully: ${command.name}`);
return result;
} catch (error) {
context.logger.error(`Command execution failed: ${command.name}`, error);
return {
success: false,
error: {
message: error.message,
code: error.code || 'COMMAND_ERROR',
details: this.extractErrorDetails(error),
},
};
}
}
}
```
### Skill Invocation Issues
**Symptoms**: Skills are not being triggered or are failing to execute.
**Debug Solutions**:
```bash
# Check available skills
claude skill list
# Test skill manually
claude skill test skill-name "test input"
# Check skill triggers
claude skill triggers skill-name
# Enable skill debugging
export CLAUDE_SKILL_DEBUG=true
```
## Performance Issues
### Slow Plugin Loading
**Optimization Strategies**:
1. **Lazy Loading**:
```typescript
class LazyPluginManager {
private plugins = new Map<string, () => Promise<Plugin>>();
private loadedPlugins = new Map<string, Plugin>();
async getPlugin(name: string): Promise<Plugin> {
if (this.loadedPlugins.has(name)) {
return this.loadedPlugins.get(name)!;
}
const loader = this.plugins.get(name);
if (!loader) {
throw new Error(`Plugin not found: ${name}`);
}
const plugin = await loader();
this.loadedPlugins.set(name, plugin);
return plugin;
}
}
```
2. **Async Initialization**:
```typescript
class AsyncPluginInitializer {
async initializePlugins(plugins: Plugin[]): Promise<void> {
const initPromises = plugins.map(plugin =>
this.initializePlugin(plugin).catch(error => {
console.error(`Failed to initialize plugin ${plugin.name}:`, error);
return null;
})
);
await Promise.allSettled(initPromises);
}
}
```
### Memory Leaks
**Detection and Solutions**:
```typescript
// memory-monitor.ts
class MemoryMonitor {
private snapshots: MemorySnapshot[] = [];
private readonly maxSnapshots = 100;
takeSnapshot(label: string): void {
const usage = process.memoryUsage();
const snapshot: MemorySnapshot = {
label,
timestamp: Date.now(),
heapUsed: usage.heapUsed,
heapTotal: usage.heapTotal,
external: usage.external,
};
this.snapshots.push(snapshot);
this.checkMemoryGrowth();
}
private checkMemoryGrowth(): void {
if (this.snapshots.length < 10) return;
const recent = this.snapshots.slice(-10);
const older = this.snapshots.slice(-20, -10);
if (older.length === 0) return;
const recentAvg = recent.reduce((sum, s) => sum + s.heapUsed, 0) / recent.length;
const olderAvg = older.reduce((sum, s) => sum + s.heapUsed, 0) / older.length;
const growth = (recentAvg - olderAvg) / olderAvg;
if (growth > 0.5) {
// 50% growth
console.warn(`Memory growth detected: ${(growth * 100).toFixed(1)}%`);
}
}
}
```
## Security and Permissions
### Permission Denied Errors
**Solutions**:
```bash
# Check current permissions
claude permissions list
# Grant specific permissions
claude permissions grant plugin-name filesystem:read
claude permissions grant plugin-name network:request
# Check permission usage
claude permissions audit plugin-name
# Reset permissions
claude permissions reset plugin-name
```
**Permission Management Implementation**:
```typescript
// permission-manager.ts
class PermissionManager {
private permissions = new Set<Permission>();
has(permission: Permission): boolean {
return this.permissions.has(permission);
}
require(permission: Permission): void {
if (!this.has(permission)) {
throw new PermissionError(`Permission required: ${permission}`);
}
}
checkFileAccess(path: string, mode: 'read' | 'write'): void {
const permission = mode === 'read' ? Permission.FILE_READ : Permission.FILE_WRITE;
this.require(permission);
// Security checks
this.validatePath(path);
}
private validatePath(path: string): void {
// Prevent path traversal
if (path.includes('..')) {
throw new SecurityError('Path traversal detected');
}
// Prevent access to sensitive directories
const restrictedPaths = ['/etc', '/sys', '/proc', '~/.ssh'];
for (const restricted of restrictedPaths) {
if (path.startsWith(restricted)) {
throw new SecurityError(`Access to ${restricted} is not allowed`);
}
}
}
}
```
## Development Debugging
### Local Development Setup
**Development Environment Setup**:
```bash
# Create development workspace
mkdir claude-plugin-dev
cd claude-plugin-dev
# Initialize development environment
npm init -y
npm install --save-dev typescript @types/node ts-node nodemon
# Create development configuration
cat > tsconfig.json << EOF
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
EOF
```
### Debug Logging
**Implementation**:
```typescript
// debug-logger.ts
class DebugLogger {
private logLevel: LogLevel;
private logFile?: string;
constructor(logLevel: LogLevel = LogLevel.INFO, logFile?: string) {
this.logLevel = logLevel;
this.logFile = logFile;
}
debug(message: string, data?: unknown): void {
this.log(LogLevel.DEBUG, message, data);
}
info(message: string, data?: unknown): void {
this.log(LogLevel.INFO, message, data);
}
warn(message: string, data?: unknown): void {
this.log(LogLevel.WARN, message, data);
}
error(message: string, error?: Error | unknown): void {
this.log(LogLevel.ERROR, message, error);
}
private log(level: LogLevel, message: string, data?: unknown): void {
if (level < this.logLevel) return;
const timestamp = new Date().toISOString();
const levelName = LogLevel[level];
const dataStr = data ? ` ${JSON.stringify(data)}` : '';
const logEntry = `[${timestamp}] ${levelName}: ${message}${dataStr}`;
console.log(logEntry);
if (this.logFile) {
this.writeToFile(logEntry);
}
}
}
```
## Advanced Troubleshooting
### Plugin Isolation
**Create isolated test environment**:
```bash
# Create isolated environment
claude environment create test-env
claude environment activate test-env
# Install only required plugins
claude marketplace install plugin-to-test
# Test in isolation
claude /test-command
# Clean up environment
claude environment deactivate
claude environment delete test-env
```
### Emergency Recovery
**Reset and recovery procedures**:
```bash
# Emergency plugin reset
claude plugin reset --all
# Backup current configuration
cp -r ~/.claude ~/.claude.backup.$(date +%Y%m%d-%H%M%S)
# Clean installation
rm -rf ~/.claude/plugins
claude marketplace reinstall-all
# Verify functionality
claude --help
claude plugin list
```
**Recovery Script**:
```bash
#!/bin/bash
# emergency-recovery.sh
set -euo pipefail
BACKUP_DIR="$HOME/.claude.backup.$(date +%Y%m%d-%H%M%S)"
CONFIG_DIR="$HOME/.claude"
echo "Claude Code Emergency Recovery"
echo "=============================="
# Create backup
echo "Creating backup in $BACKUP_DIR..."
if [ -d "$CONFIG_DIR" ]; then
cp -r "$CONFIG_DIR" "$BACKUP_DIR"
echo "✅ Backup created"
else
echo "⚠️ No existing configuration to backup"
fi
# Reset plugin configuration
echo "Resetting plugin configuration..."
rm -rf "$CONFIG_DIR/plugins"
mkdir -p "$CONFIG_DIR/plugins"
echo "✅ Emergency recovery completed"
echo "📁 Backup available at: $BACKUP_DIR"
echo "🔄 Please restart Claude Code"
```
## Getting Help
### Support Resources
1. **Official Documentation**: https://docs.claude.com
2. **Community Forums**: https://community.anthropic.com
3. **GitHub Issues**: https://github.com/anthropics/claude-code/issues
4. **Discord Community**: Claude Code Discord server
### Reporting Issues
When reporting issues, include:
- Claude Code version
- Plugin name and version
- Operating system and Node.js version
- Complete error messages and stack traces
- Steps to reproduce the issue
- Expected vs actual behavior
### Debug Information Collection
```bash
# Collect system information
claude --version
node --version
npm --version
# Collect plugin information
claude plugin list
claude plugin status
# Collect configuration
cat ~/.claude/settings.json
cat ~/.claude/marketplaces.json
# Collect logs
tail -n 100 ~/.claude/logs/*.log
```

View File

@@ -0,0 +1,380 @@
#!/usr/bin/env node
/**
* Claude Code Plugin Validator
*
* This script validates a plugin structure and configuration
* to ensure it meets Claude Code plugin standards.
*/
const fs = require('fs');
const path = require('path');
class PluginValidator {
constructor(pluginPath) {
this.pluginPath = pluginPath;
this.errors = [];
this.warnings = [];
}
async validate() {
console.log(`🔍 Validating plugin at: ${this.pluginPath}`);
await this.validateStructure();
await this.validateManifest();
await this.validateCommands();
await this.validateSkills();
await this.validatePermissions();
this.printResults();
return this.errors.length === 0;
}
async validateStructure() {
console.log('\n📁 Checking plugin structure...');
const requiredDirs = ['.claude-plugin'];
const optionalDirs = ['commands', 'skills', 'agents', 'hooks', 'mcp', 'scripts', 'templates'];
for (const dir of requiredDirs) {
const dirPath = path.join(this.pluginPath, dir);
if (!fs.existsSync(dirPath)) {
this.errors.push(`Missing required directory: ${dir}`);
} else {
console.log(`${dir} directory exists`);
}
}
for (const dir of optionalDirs) {
const dirPath = path.join(this.pluginPath, dir);
if (fs.existsSync(dirPath)) {
console.log(`${dir} directory exists`);
}
}
}
async validateManifest() {
console.log('\n📋 Validating plugin manifest...');
const manifestPath = path.join(this.pluginPath, '.claude-plugin', 'plugin.json');
if (!fs.existsSync(manifestPath)) {
this.errors.push('Missing plugin.json manifest file');
return;
}
try {
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
// Required fields
const requiredFields = ['name', 'version', 'description'];
for (const field of requiredFields) {
if (!manifest[field]) {
this.errors.push(`Missing required field in manifest: ${field}`);
} else {
console.log(`✅ Manifest field ${field}: ${manifest[field]}`);
}
}
// Validate name format
if (manifest.name && !/^[a-z0-9-]+$/.test(manifest.name)) {
this.errors.push('Plugin name should only contain lowercase letters, numbers, and hyphens');
}
// Validate version format
if (manifest.version && !/^\d+\.\d+\.\d+/.test(manifest.version)) {
this.errors.push('Plugin version should follow semantic versioning (e.g., 1.0.0)');
}
// Validate Claude version constraints
if (manifest.claude) {
if (manifest.claude.minVersion && !/^\d+\.\d+\.\d+/.test(manifest.claude.minVersion)) {
this.warnings.push('Claude minVersion should follow semantic versioning');
}
if (manifest.claude.maxVersion && !/^\d+\.\d+\.\d+/.test(manifest.claude.maxVersion)) {
this.warnings.push('Claude maxVersion should follow semantic versioning');
}
}
// Validate permissions
if (manifest.permissions && Array.isArray(manifest.permissions)) {
const validPermissions = [
'file:read',
'file:write',
'network:request',
'system:exec',
'env:read',
'env:write',
];
for (const permission of manifest.permissions) {
if (!validPermissions.includes(permission)) {
this.warnings.push(`Unknown permission: ${permission}`);
}
}
console.log(`✅ Permissions: ${manifest.permissions.join(', ')}`);
}
} catch (error) {
this.errors.push(`Invalid JSON in plugin.json: ${error.message}`);
}
}
async validateCommands() {
console.log('\n⚡ Validating commands...');
const commandsDir = path.join(this.pluginPath, 'commands');
if (!fs.existsSync(commandsDir)) {
console.log(' No commands directory found');
return;
}
const commandFiles = fs.readdirSync(commandsDir).filter(file => file.endsWith('.md'));
if (commandFiles.length === 0) {
console.log(' No command files found');
return;
}
for (const file of commandFiles) {
const filePath = path.join(commandsDir, file);
const content = fs.readFileSync(filePath, 'utf8');
// Check for YAML frontmatter
if (!content.startsWith('---')) {
this.warnings.push(`Command ${file} missing YAML frontmatter`);
continue;
}
try {
const frontmatter = this.extractFrontmatter(content);
// Required frontmatter fields
if (!frontmatter.name) {
this.errors.push(`Command ${file} missing name in frontmatter`);
} else {
console.log(`✅ Command: ${frontmatter.name}`);
}
if (!frontmatter.description) {
this.warnings.push(`Command ${file} missing description`);
}
// Validate name format
if (frontmatter.name && !/^[a-z0-9-]+$/.test(frontmatter.name)) {
this.warnings.push(
`Command name should only contain lowercase letters, numbers, and hyphens: ${frontmatter.name}`
);
}
} catch (error) {
this.errors.push(`Error parsing command ${file}: ${error.message}`);
}
}
}
async validateSkills() {
console.log('\n🧠 Validating skills...');
const skillsDir = path.join(this.pluginPath, 'skills');
if (!fs.existsSync(skillsDir)) {
console.log(' No skills directory found');
return;
}
const skillDirs = fs
.readdirSync(skillsDir)
.filter(file => fs.statSync(path.join(skillsDir, file)).isDirectory());
if (skillDirs.length === 0) {
console.log(' No skill directories found');
return;
}
for (const skillDir of skillDirs) {
const skillPath = path.join(skillsDir, skillDir);
const skillFile = path.join(skillPath, 'SKILL.md');
if (!fs.existsSync(skillFile)) {
this.errors.push(`Skill ${skillDir} missing SKILL.md file`);
continue;
}
const content = fs.readFileSync(skillFile, 'utf8');
// Check for YAML frontmatter
if (!content.startsWith('---')) {
this.errors.push(`Skill ${skillDir} missing YAML frontmatter`);
continue;
}
try {
const frontmatter = this.extractFrontmatter(content);
// Required frontmatter fields
if (!frontmatter.name) {
this.errors.push(`Skill ${skillDir} missing name in frontmatter`);
} else {
console.log(`✅ Skill: ${frontmatter.name}`);
}
if (!frontmatter.description) {
this.warnings.push(`Skill ${skillDir} missing description`);
}
// Check for triggers
if (!frontmatter.triggers || !Array.isArray(frontmatter.triggers)) {
this.warnings.push(`Skill ${skillDir} missing triggers array`);
} else {
for (const trigger of frontmatter.triggers) {
if (!trigger.type) {
this.warnings.push(`Skill ${skillDir} has trigger missing type`);
}
if (!trigger.pattern) {
this.warnings.push(`Skill ${skillDir} has trigger missing pattern`);
}
}
}
} catch (error) {
this.errors.push(`Error parsing skill ${skillDir}: ${error.message}`);
}
}
}
async validatePermissions() {
console.log('\n🔒 Checking permissions...');
const manifestPath = path.join(this.pluginPath, '.claude-plugin', 'plugin.json');
if (!fs.existsSync(manifestPath)) {
return;
}
try {
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
if (!manifest.permissions || manifest.permissions.length === 0) {
this.warnings.push('Plugin requests no permissions - ensure this is intentional');
return;
}
// Check if permissions match actual usage
const hasNetworkPermissions = manifest.permissions.includes('network:request');
const hasFilePermissions = manifest.permissions.some(p => p.startsWith('file:'));
// Scan for network usage in commands
if (hasNetworkPermissions) {
const commandsDir = path.join(this.pluginPath, 'commands');
if (fs.existsSync(commandsDir)) {
const commandFiles = fs.readdirSync(commandsDir).filter(file => file.endsWith('.md'));
let networkUsageFound = false;
for (const file of commandFiles) {
const content = fs.readFileSync(path.join(commandsDir, file), 'utf8');
if (
content.includes('http') ||
content.includes('fetch') ||
content.includes('request')
) {
networkUsageFound = true;
break;
}
}
if (!networkUsageFound) {
this.warnings.push(
'Plugin requests network permission but no obvious network usage found in commands'
);
}
}
}
console.log('✅ Permissions validated');
} catch (error) {
// Manifest validation errors already caught in validateManifest
}
}
extractFrontmatter(content) {
const match = content.match(/^---\n([\s\S]*?)\n---/);
if (!match) {
throw new Error('No frontmatter found');
}
// Simple YAML parser for basic fields
const frontmatter = {};
const lines = match[1].split('\n');
for (const line of lines) {
const colonIndex = line.indexOf(':');
if (colonIndex > 0) {
const key = line.substring(0, colonIndex).trim();
let value = line.substring(colonIndex + 1).trim();
// Handle quoted strings
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
// Handle arrays
if (value.startsWith('[') && value.endsWith(']')) {
value = value
.slice(1, -1)
.split(',')
.map(item => item.trim().replace(/['"]/g, ''));
}
frontmatter[key] = value;
}
}
return frontmatter;
}
printResults() {
console.log('\n📊 Validation Results');
console.log('=====================');
if (this.errors.length === 0 && this.warnings.length === 0) {
console.log('✅ Plugin validation passed with no issues!');
} else {
if (this.errors.length > 0) {
console.log(`\n❌ Errors (${this.errors.length}):`);
this.errors.forEach(error => console.log(`${error}`));
}
if (this.warnings.length > 0) {
console.log(`\n⚠️ Warnings (${this.warnings.length}):`);
this.warnings.forEach(warning => console.log(`${warning}`));
}
}
console.log(`\nSummary: ${this.errors.length} errors, ${this.warnings.length} warnings`);
}
}
// CLI usage
if (require.main === module) {
const pluginPath = process.argv[2] || '.';
if (!fs.existsSync(pluginPath)) {
console.error(`❌ Plugin path does not exist: ${pluginPath}`);
process.exit(1);
}
const validator = new PluginValidator(pluginPath);
validator
.validate()
.then(success => {
process.exit(success ? 0 : 1);
})
.catch(error => {
console.error('❌ Validation failed:', error.message);
process.exit(1);
});
}
module.exports = PluginValidator;