Initial commit
This commit is contained in:
796
skills/claude-code-plugin/references/best-practices.md
Normal file
796
skills/claude-code-plugin/references/best-practices.md
Normal 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._
|
||||
201
skills/claude-code-plugin/references/quick-start.md
Normal file
201
skills/claude-code-plugin/references/quick-start.md
Normal 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)
|
||||
585
skills/claude-code-plugin/references/troubleshooting-guide.md
Normal file
585
skills/claude-code-plugin/references/troubleshooting-guide.md
Normal 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
|
||||
```
|
||||
Reference in New Issue
Block a user