Skip to content

Plugins API

The Plugins API provides functionality for managing plugins, plugin lifecycle, and plugin communication in Trae.

Overview

The Plugins API enables you to:

  • Load and manage plugins dynamically
  • Handle plugin lifecycle events
  • Provide plugin communication mechanisms
  • Manage plugin dependencies and versions
  • Implement plugin sandboxing and security
  • Handle plugin configuration and settings
  • Provide plugin discovery and installation
  • Monitor plugin performance and health

Basic Usage

Plugin Manager

typescript
import { TraeAPI } from '@trae/api';
import * as path from 'path';
import * as fs from 'fs';

// Plugin manager
class PluginManager {
  private plugins: Map<string, PluginInstance> = new Map();
  private pluginConfigs: Map<string, PluginConfiguration> = new Map();
  private eventEmitter = new TraeAPI.EventEmitter<PluginEvent>();
  private disposables: TraeAPI.Disposable[] = [];
  private pluginRegistry: PluginRegistry;
  private pluginLoader: PluginLoader;
  private pluginSandbox: PluginSandbox;
  private dependencyResolver: DependencyResolver;

  constructor() {
    this.pluginRegistry = new PluginRegistry();
    this.pluginLoader = new PluginLoader();
    this.pluginSandbox = new PluginSandbox();
    this.dependencyResolver = new DependencyResolver();
    this.setupEventHandlers();
  }

  // Plugin discovery and registration
  async discoverPlugins(searchPaths: string[]): Promise<PluginDescriptor[]> {
    const discovered: PluginDescriptor[] = [];
    
    for (const searchPath of searchPaths) {
      if (!fs.existsSync(searchPath)) {
        continue;
      }
      
      const entries = fs.readdirSync(searchPath, { withFileTypes: true });
      
      for (const entry of entries) {
        if (entry.isDirectory()) {
          const pluginPath = path.join(searchPath, entry.name);
          const descriptor = await this.loadPluginDescriptor(pluginPath);
          
          if (descriptor) {
            discovered.push(descriptor);
          }
        }
      }
    }
    
    return discovered;
  }

  private async loadPluginDescriptor(pluginPath: string): Promise<PluginDescriptor | null> {
    const manifestPath = path.join(pluginPath, 'plugin.json');
    
    if (!fs.existsSync(manifestPath)) {
      return null;
    }
    
    try {
      const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
      const manifest = JSON.parse(manifestContent);
      
      return {
        id: manifest.id,
        name: manifest.name,
        version: manifest.version,
        description: manifest.description,
        author: manifest.author,
        main: manifest.main,
        dependencies: manifest.dependencies || {},
        engines: manifest.engines || {},
        activationEvents: manifest.activationEvents || [],
        contributes: manifest.contributes || {},
        path: pluginPath,
        manifest
      };
    } catch (error) {
      console.warn(`Failed to load plugin descriptor from ${pluginPath}:`, error);
      return null;
    }
  }

  async registerPlugin(descriptor: PluginDescriptor): Promise<void> {
    // Validate plugin descriptor
    this.validatePluginDescriptor(descriptor);
    
    // Check for conflicts
    if (this.plugins.has(descriptor.id)) {
      throw new Error(`Plugin with ID '${descriptor.id}' is already registered`);
    }
    
    // Register in registry
    this.pluginRegistry.register(descriptor);
    
    this.eventEmitter.fire({ type: 'pluginRegistered', descriptor });
  }

  async unregisterPlugin(pluginId: string): Promise<void> {
    const plugin = this.plugins.get(pluginId);
    
    if (plugin && plugin.isActive) {
      await this.deactivatePlugin(pluginId);
    }
    
    this.plugins.delete(pluginId);
    this.pluginRegistry.unregister(pluginId);
    
    this.eventEmitter.fire({ type: 'pluginUnregistered', pluginId });
  }

  // Plugin lifecycle management
  async loadPlugin(pluginId: string): Promise<PluginInstance> {
    const descriptor = this.pluginRegistry.get(pluginId);
    if (!descriptor) {
      throw new Error(`Plugin '${pluginId}' not found in registry`);
    }
    
    // Check if already loaded
    const existingPlugin = this.plugins.get(pluginId);
    if (existingPlugin) {
      return existingPlugin;
    }
    
    try {
      // Resolve dependencies
      await this.resolveDependencies(descriptor);
      
      // Load plugin code
      const pluginModule = await this.pluginLoader.load(descriptor);
      
      // Create plugin instance
      const instance = new PluginInstance(descriptor, pluginModule);
      
      // Setup sandbox
      const sandbox = this.pluginSandbox.create(instance);
      instance.setSandbox(sandbox);
      
      this.plugins.set(pluginId, instance);
      
      this.eventEmitter.fire({ type: 'pluginLoaded', pluginId, instance });
      
      return instance;
    } catch (error) {
      this.eventEmitter.fire({ type: 'pluginLoadError', pluginId, error });
      throw error;
    }
  }

  async unloadPlugin(pluginId: string): Promise<void> {
    const plugin = this.plugins.get(pluginId);
    if (!plugin) {
      return;
    }
    
    try {
      // Deactivate if active
      if (plugin.isActive) {
        await this.deactivatePlugin(pluginId);
      }
      
      // Cleanup plugin resources
      await plugin.cleanup();
      
      // Remove from plugins map
      this.plugins.delete(pluginId);
      
      this.eventEmitter.fire({ type: 'pluginUnloaded', pluginId });
    } catch (error) {
      this.eventEmitter.fire({ type: 'pluginUnloadError', pluginId, error });
      throw error;
    }
  }

  async activatePlugin(pluginId: string): Promise<void> {
    const plugin = this.plugins.get(pluginId);
    if (!plugin) {
      // Try to load the plugin first
      await this.loadPlugin(pluginId);
      return this.activatePlugin(pluginId);
    }
    
    if (plugin.isActive) {
      return;
    }
    
    try {
      // Activate dependencies first
      await this.activateDependencies(plugin.descriptor);
      
      // Create plugin context
      const context = this.createPluginContext(plugin);
      
      // Activate plugin
      await plugin.activate(context);
      
      this.eventEmitter.fire({ type: 'pluginActivated', pluginId, plugin });
    } catch (error) {
      this.eventEmitter.fire({ type: 'pluginActivationError', pluginId, error });
      throw error;
    }
  }

  async deactivatePlugin(pluginId: string): Promise<void> {
    const plugin = this.plugins.get(pluginId);
    if (!plugin || !plugin.isActive) {
      return;
    }
    
    try {
      // Check for dependent plugins
      const dependents = this.findDependentPlugins(pluginId);
      if (dependents.length > 0) {
        throw new Error(`Cannot deactivate plugin '${pluginId}' because it has active dependents: ${dependents.join(', ')}`);
      }
      
      // Deactivate plugin
      await plugin.deactivate();
      
      this.eventEmitter.fire({ type: 'pluginDeactivated', pluginId, plugin });
    } catch (error) {
      this.eventEmitter.fire({ type: 'pluginDeactivationError', pluginId, error });
      throw error;
    }
  }

  async reloadPlugin(pluginId: string): Promise<void> {
    const wasActive = this.isPluginActive(pluginId);
    
    // Unload plugin
    await this.unloadPlugin(pluginId);
    
    // Load plugin again
    await this.loadPlugin(pluginId);
    
    // Activate if it was active before
    if (wasActive) {
      await this.activatePlugin(pluginId);
    }
  }

  // Plugin communication
  async sendMessage(
    fromPluginId: string,
    toPluginId: string,
    message: PluginMessage
  ): Promise<any> {
    const fromPlugin = this.plugins.get(fromPluginId);
    const toPlugin = this.plugins.get(toPluginId);
    
    if (!fromPlugin || !toPlugin) {
      throw new Error('Source or target plugin not found');
    }
    
    if (!toPlugin.isActive) {
      throw new Error(`Target plugin '${toPluginId}' is not active`);
    }
    
    try {
      const response = await toPlugin.handleMessage(message, fromPluginId);
      
      this.eventEmitter.fire({
        type: 'pluginMessageSent',
        fromPluginId,
        toPluginId,
        message,
        response
      });
      
      return response;
    } catch (error) {
      this.eventEmitter.fire({
        type: 'pluginMessageError',
        fromPluginId,
        toPluginId,
        message,
        error
      });
      throw error;
    }
  }

  broadcastMessage(fromPluginId: string, message: PluginMessage): void {
    const fromPlugin = this.plugins.get(fromPluginId);
    if (!fromPlugin) {
      throw new Error(`Source plugin '${fromPluginId}' not found`);
    }
    
    for (const [pluginId, plugin] of this.plugins) {
      if (pluginId !== fromPluginId && plugin.isActive) {
        try {
          plugin.handleMessage(message, fromPluginId);
        } catch (error) {
          console.warn(`Error broadcasting message to plugin '${pluginId}':`, error);
        }
      }
    }
    
    this.eventEmitter.fire({
      type: 'pluginMessageBroadcast',
      fromPluginId,
      message
    });
  }

  // Plugin queries and management
  getPlugin(pluginId: string): PluginInstance | undefined {
    return this.plugins.get(pluginId);
  }

  getAllPlugins(): PluginInstance[] {
    return Array.from(this.plugins.values());
  }

  getActivePlugins(): PluginInstance[] {
    return Array.from(this.plugins.values()).filter(plugin => plugin.isActive);
  }

  isPluginLoaded(pluginId: string): boolean {
    return this.plugins.has(pluginId);
  }

  isPluginActive(pluginId: string): boolean {
    const plugin = this.plugins.get(pluginId);
    return plugin ? plugin.isActive : false;
  }

  getPluginDescriptor(pluginId: string): PluginDescriptor | undefined {
    return this.pluginRegistry.get(pluginId);
  }

  // Plugin configuration
  setPluginConfiguration(pluginId: string, config: PluginConfiguration): void {
    this.pluginConfigs.set(pluginId, config);
    
    const plugin = this.plugins.get(pluginId);
    if (plugin && plugin.isActive) {
      plugin.updateConfiguration(config);
    }
    
    this.eventEmitter.fire({ type: 'pluginConfigurationChanged', pluginId, config });
  }

  getPluginConfiguration(pluginId: string): PluginConfiguration | undefined {
    return this.pluginConfigs.get(pluginId);
  }

  // Dependency management
  private async resolveDependencies(descriptor: PluginDescriptor): Promise<void> {
    const dependencies = descriptor.dependencies || {};
    
    for (const [depId, versionRange] of Object.entries(dependencies)) {
      const depDescriptor = this.pluginRegistry.get(depId);
      
      if (!depDescriptor) {
        throw new Error(`Dependency '${depId}' not found for plugin '${descriptor.id}'`);
      }
      
      if (!this.dependencyResolver.satisfies(depDescriptor.version, versionRange)) {
        throw new Error(`Dependency version mismatch: ${depId}@${depDescriptor.version} does not satisfy ${versionRange}`);
      }
      
      // Ensure dependency is loaded
      if (!this.isPluginLoaded(depId)) {
        await this.loadPlugin(depId);
      }
    }
  }

  private async activateDependencies(descriptor: PluginDescriptor): Promise<void> {
    const dependencies = descriptor.dependencies || {};
    
    for (const depId of Object.keys(dependencies)) {
      if (!this.isPluginActive(depId)) {
        await this.activatePlugin(depId);
      }
    }
  }

  private findDependentPlugins(pluginId: string): string[] {
    const dependents: string[] = [];
    
    for (const [id, plugin] of this.plugins) {
      if (plugin.isActive && plugin.descriptor.dependencies?.[pluginId]) {
        dependents.push(id);
      }
    }
    
    return dependents;
  }

  // Plugin context creation
  private createPluginContext(plugin: PluginInstance): PluginContext {
    return {
      pluginId: plugin.descriptor.id,
      pluginPath: plugin.descriptor.path,
      globalState: new PluginGlobalState(plugin.descriptor.id),
      workspaceState: new PluginWorkspaceState(plugin.descriptor.id),
      subscriptions: [],
      logger: new PluginLogger(plugin.descriptor.id),
      
      // API access
      trae: {
        window: TraeAPI.window,
        workspace: TraeAPI.workspace,
        commands: TraeAPI.commands,
        languages: TraeAPI.languages,
        debug: TraeAPI.debug,
        tasks: TraeAPI.tasks,
        extensions: TraeAPI.extensions
      },
      
      // Plugin communication
      sendMessage: (toPluginId: string, message: PluginMessage) => {
        return this.sendMessage(plugin.descriptor.id, toPluginId, message);
      },
      
      broadcastMessage: (message: PluginMessage) => {
        this.broadcastMessage(plugin.descriptor.id, message);
      },
      
      // Configuration access
      getConfiguration: () => {
        return this.getPluginConfiguration(plugin.descriptor.id);
      },
      
      updateConfiguration: (config: PluginConfiguration) => {
        this.setPluginConfiguration(plugin.descriptor.id, config);
      }
    };
  }

  // Plugin validation
  private validatePluginDescriptor(descriptor: PluginDescriptor): void {
    if (!descriptor.id) {
      throw new Error('Plugin descriptor must have an ID');
    }
    
    if (!descriptor.name) {
      throw new Error('Plugin descriptor must have a name');
    }
    
    if (!descriptor.version) {
      throw new Error('Plugin descriptor must have a version');
    }
    
    if (!descriptor.main) {
      throw new Error('Plugin descriptor must specify a main entry point');
    }
    
    // Validate version format
    if (!/^\d+\.\d+\.\d+/.test(descriptor.version)) {
      throw new Error('Plugin version must follow semantic versioning (x.y.z)');
    }
    
    // Validate ID format
    if (!/^[a-z0-9-_.]+$/.test(descriptor.id)) {
      throw new Error('Plugin ID must contain only lowercase letters, numbers, hyphens, underscores, and dots');
    }
  }

  private setupEventHandlers(): void {
    // Setup plugin event handlers
  }

  // Event handling
  onPluginLoaded(listener: (event: PluginLoadedEvent) => void): TraeAPI.Disposable {
    return this.eventEmitter.event(event => {
      if (event.type === 'pluginLoaded') {
        listener(event as PluginLoadedEvent);
      }
    });
  }

  onPluginActivated(listener: (event: PluginActivatedEvent) => void): TraeAPI.Disposable {
    return this.eventEmitter.event(event => {
      if (event.type === 'pluginActivated') {
        listener(event as PluginActivatedEvent);
      }
    });
  }

  onPluginDeactivated(listener: (event: PluginDeactivatedEvent) => void): TraeAPI.Disposable {
    return this.eventEmitter.event(event => {
      if (event.type === 'pluginDeactivated') {
        listener(event as PluginDeactivatedEvent);
      }
    });
  }

  onPluginError(listener: (event: PluginErrorEvent) => void): TraeAPI.Disposable {
    return this.eventEmitter.event(event => {
      if (event.type === 'pluginLoadError' || event.type === 'pluginActivationError' || event.type === 'pluginDeactivationError') {
        listener(event as PluginErrorEvent);
      }
    });
  }

  // Dispose
  dispose(): void {
    // Deactivate all plugins
    for (const [pluginId, plugin] of this.plugins) {
      if (plugin.isActive) {
        plugin.deactivate().catch(console.error);
      }
    }
    
    // Clear plugins
    this.plugins.clear();
    
    // Clear configurations
    this.pluginConfigs.clear();
    
    // Dispose components
    this.pluginRegistry.dispose();
    this.pluginLoader.dispose();
    this.pluginSandbox.dispose();
    
    // Dispose other resources
    this.disposables.forEach(d => d.dispose());
    this.disposables = [];
    
    this.eventEmitter.dispose();
  }
}

// Plugin instance implementation
class PluginInstance {
  private _isActive = false;
  private _module: any;
  private _context: PluginContext | undefined;
  private _sandbox: PluginSandbox | undefined;

  constructor(
    public readonly descriptor: PluginDescriptor,
    module: any
  ) {
    this._module = module;
  }

  get isActive(): boolean {
    return this._isActive;
  }

  get module(): any {
    return this._module;
  }

  setSandbox(sandbox: PluginSandbox): void {
    this._sandbox = sandbox;
  }

  async activate(context: PluginContext): Promise<void> {
    if (this._isActive) {
      return;
    }
    
    this._context = context;
    
    if (this._module.activate) {
      await this._module.activate(context);
    }
    
    this._isActive = true;
  }

  async deactivate(): Promise<void> {
    if (!this._isActive) {
      return;
    }
    
    if (this._module.deactivate) {
      await this._module.deactivate();
    }
    
    // Dispose subscriptions
    if (this._context?.subscriptions) {
      for (const subscription of this._context.subscriptions) {
        if (subscription.dispose) {
          subscription.dispose();
        }
      }
      this._context.subscriptions = [];
    }
    
    this._isActive = false;
    this._context = undefined;
  }

  async handleMessage(message: PluginMessage, fromPluginId: string): Promise<any> {
    if (!this._isActive) {
      throw new Error('Plugin is not active');
    }
    
    if (this._module.handleMessage) {
      return await this._module.handleMessage(message, fromPluginId);
    }
    
    return undefined;
  }

  updateConfiguration(config: PluginConfiguration): void {
    if (this._module.updateConfiguration) {
      this._module.updateConfiguration(config);
    }
  }

  async cleanup(): Promise<void> {
    if (this._module.cleanup) {
      await this._module.cleanup();
    }
    
    if (this._sandbox) {
      this._sandbox.cleanup();
    }
  }
}

// Plugin registry implementation
class PluginRegistry {
  private plugins: Map<string, PluginDescriptor> = new Map();

  register(descriptor: PluginDescriptor): void {
    this.plugins.set(descriptor.id, descriptor);
  }

  unregister(pluginId: string): void {
    this.plugins.delete(pluginId);
  }

  get(pluginId: string): PluginDescriptor | undefined {
    return this.plugins.get(pluginId);
  }

  getAll(): PluginDescriptor[] {
    return Array.from(this.plugins.values());
  }

  has(pluginId: string): boolean {
    return this.plugins.has(pluginId);
  }

  dispose(): void {
    this.plugins.clear();
  }
}

// Plugin loader implementation
class PluginLoader {
  async load(descriptor: PluginDescriptor): Promise<any> {
    const mainPath = path.resolve(descriptor.path, descriptor.main);
    
    if (!fs.existsSync(mainPath)) {
      throw new Error(`Plugin main file not found: ${mainPath}`);
    }
    
    try {
      // Clear require cache for hot reloading
      delete require.cache[require.resolve(mainPath)];
      
      // Load plugin module
      const module = require(mainPath);
      
      return module;
    } catch (error) {
      throw new Error(`Failed to load plugin module: ${error.message}`);
    }
  }

  dispose(): void {
    // Cleanup loader resources
  }
}

// Plugin sandbox implementation
class PluginSandbox {
  private sandboxes: Map<string, any> = new Map();

  create(plugin: PluginInstance): any {
    const sandbox = {
      pluginId: plugin.descriptor.id,
      // Add sandbox restrictions and APIs
    };
    
    this.sandboxes.set(plugin.descriptor.id, sandbox);
    
    return sandbox;
  }

  get(pluginId: string): any {
    return this.sandboxes.get(pluginId);
  }

  cleanup(): void {
    this.sandboxes.clear();
  }

  dispose(): void {
    this.cleanup();
  }
}

// Dependency resolver implementation
class DependencyResolver {
  satisfies(version: string, range: string): boolean {
    // Simple version matching - in real implementation, use semver
    if (range === '*') {
      return true;
    }
    
    if (range.startsWith('^')) {
      const targetVersion = range.slice(1);
      return this.isCompatibleVersion(version, targetVersion);
    }
    
    if (range.startsWith('~')) {
      const targetVersion = range.slice(1);
      return this.isPatchCompatible(version, targetVersion);
    }
    
    return version === range;
  }

  private isCompatibleVersion(version: string, target: string): boolean {
    const [vMajor, vMinor] = version.split('.').map(Number);
    const [tMajor, tMinor] = target.split('.').map(Number);
    
    return vMajor === tMajor && vMinor >= tMinor;
  }

  private isPatchCompatible(version: string, target: string): boolean {
    const [vMajor, vMinor] = version.split('.').map(Number);
    const [tMajor, tMinor] = target.split('.').map(Number);
    
    return vMajor === tMajor && vMinor === tMinor;
  }
}

// Plugin state implementations
class PluginGlobalState {
  private state: Map<string, any> = new Map();

  constructor(private pluginId: string) {}

  get<T>(key: string, defaultValue?: T): T | undefined {
    return this.state.get(key) ?? defaultValue;
  }

  update(key: string, value: any): void {
    this.state.set(key, value);
  }

  keys(): string[] {
    return Array.from(this.state.keys());
  }
}

class PluginWorkspaceState {
  private state: Map<string, any> = new Map();

  constructor(private pluginId: string) {}

  get<T>(key: string, defaultValue?: T): T | undefined {
    return this.state.get(key) ?? defaultValue;
  }

  update(key: string, value: any): void {
    this.state.set(key, value);
  }

  keys(): string[] {
    return Array.from(this.state.keys());
  }
}

// Plugin logger implementation
class PluginLogger {
  constructor(private pluginId: string) {}

  info(message: string, ...args: any[]): void {
    console.log(`[${this.pluginId}] INFO:`, message, ...args);
  }

  warn(message: string, ...args: any[]): void {
    console.warn(`[${this.pluginId}] WARN:`, message, ...args);
  }

  error(message: string, ...args: any[]): void {
    console.error(`[${this.pluginId}] ERROR:`, message, ...args);
  }

  debug(message: string, ...args: any[]): void {
    console.debug(`[${this.pluginId}] DEBUG:`, message, ...args);
  }
}

// Initialize plugin manager
const pluginManager = new PluginManager();

Interface Definitions

typescript
// Plugin descriptor
interface PluginDescriptor {
  id: string;
  name: string;
  version: string;
  description?: string;
  author?: string;
  main: string;
  dependencies?: Record<string, string>;
  engines?: Record<string, string>;
  activationEvents?: string[];
  contributes?: Record<string, any>;
  path: string;
  manifest: any;
}

// Plugin configuration
interface PluginConfiguration {
  [key: string]: any;
}

// Plugin context
interface PluginContext {
  pluginId: string;
  pluginPath: string;
  globalState: PluginGlobalState;
  workspaceState: PluginWorkspaceState;
  subscriptions: TraeAPI.Disposable[];
  logger: PluginLogger;
  trae: {
    window: typeof TraeAPI.window;
    workspace: typeof TraeAPI.workspace;
    commands: typeof TraeAPI.commands;
    languages: typeof TraeAPI.languages;
    debug: typeof TraeAPI.debug;
    tasks: typeof TraeAPI.tasks;
    extensions: typeof TraeAPI.extensions;
  };
  sendMessage(toPluginId: string, message: PluginMessage): Promise<any>;
  broadcastMessage(message: PluginMessage): void;
  getConfiguration(): PluginConfiguration | undefined;
  updateConfiguration(config: PluginConfiguration): void;
}

// Plugin message
interface PluginMessage {
  type: string;
  data?: any;
  timestamp?: Date;
}

// Plugin events
type PluginEvent = PluginRegisteredEvent | PluginUnregisteredEvent | PluginLoadedEvent | PluginUnloadedEvent |
                  PluginActivatedEvent | PluginDeactivatedEvent | PluginLoadErrorEvent | PluginActivationErrorEvent |
                  PluginDeactivationErrorEvent | PluginUnloadErrorEvent | PluginMessageSentEvent |
                  PluginMessageBroadcastEvent | PluginMessageErrorEvent | PluginConfigurationChangedEvent;

interface PluginRegisteredEvent {
  type: 'pluginRegistered';
  descriptor: PluginDescriptor;
}

interface PluginUnregisteredEvent {
  type: 'pluginUnregistered';
  pluginId: string;
}

interface PluginLoadedEvent {
  type: 'pluginLoaded';
  pluginId: string;
  instance: PluginInstance;
}

interface PluginUnloadedEvent {
  type: 'pluginUnloaded';
  pluginId: string;
}

interface PluginActivatedEvent {
  type: 'pluginActivated';
  pluginId: string;
  plugin: PluginInstance;
}

interface PluginDeactivatedEvent {
  type: 'pluginDeactivated';
  pluginId: string;
  plugin: PluginInstance;
}

interface PluginErrorEvent {
  type: 'pluginLoadError' | 'pluginActivationError' | 'pluginDeactivationError' | 'pluginUnloadError';
  pluginId: string;
  error: any;
}

interface PluginMessageSentEvent {
  type: 'pluginMessageSent';
  fromPluginId: string;
  toPluginId: string;
  message: PluginMessage;
  response?: any;
}

interface PluginMessageBroadcastEvent {
  type: 'pluginMessageBroadcast';
  fromPluginId: string;
  message: PluginMessage;
}

interface PluginMessageErrorEvent {
  type: 'pluginMessageError';
  fromPluginId: string;
  toPluginId: string;
  message: PluginMessage;
  error: any;
}

interface PluginConfigurationChangedEvent {
  type: 'pluginConfigurationChanged';
  pluginId: string;
  config: PluginConfiguration;
}

API Reference

PluginManager

Methods

  • discoverPlugins(searchPaths): Promise<PluginDescriptor[]> - Discover plugins in paths
  • registerPlugin(descriptor): Promise<void> - Register plugin
  • unregisterPlugin(pluginId): Promise<void> - Unregister plugin
  • loadPlugin(pluginId): Promise<PluginInstance> - Load plugin
  • unloadPlugin(pluginId): Promise<void> - Unload plugin
  • activatePlugin(pluginId): Promise<void> - Activate plugin
  • deactivatePlugin(pluginId): Promise<void> - Deactivate plugin
  • reloadPlugin(pluginId): Promise<void> - Reload plugin
  • sendMessage(fromPluginId, toPluginId, message): Promise<any> - Send message between plugins
  • broadcastMessage(fromPluginId, message): void - Broadcast message to all plugins
  • getPlugin(pluginId): PluginInstance | undefined - Get plugin instance
  • getAllPlugins(): PluginInstance[] - Get all plugins
  • getActivePlugins(): PluginInstance[] - Get active plugins
  • isPluginLoaded(pluginId): boolean - Check if plugin is loaded
  • isPluginActive(pluginId): boolean - Check if plugin is active
  • setPluginConfiguration(pluginId, config): void - Set plugin configuration
  • getPluginConfiguration(pluginId): PluginConfiguration | undefined - Get plugin configuration

Events

  • onPluginLoaded(listener): Disposable - Plugin loaded
  • onPluginActivated(listener): Disposable - Plugin activated
  • onPluginDeactivated(listener): Disposable - Plugin deactivated
  • onPluginError(listener): Disposable - Plugin error

Best Practices

  1. Plugin Structure: Follow consistent plugin structure and manifest format
  2. Dependencies: Properly declare and manage plugin dependencies
  3. Lifecycle: Implement proper activation and deactivation logic
  4. Error Handling: Handle plugin errors gracefully without affecting the host
  5. Security: Implement proper sandboxing and permission controls
  6. Performance: Lazy load plugins and optimize activation times
  7. Communication: Use structured messaging for plugin communication
  8. Configuration: Provide clear configuration schemas and validation
  9. Documentation: Document plugin APIs and contribution points
  10. Testing: Test plugins in isolation and with dependencies

究極の AI 駆動 IDE 学習ガイド