Plugin System Architecture

How to extend Bootspring with custom plugins.

Overview#

The plugin system allows extending Bootspring with:

  • Custom agents
  • Custom skills
  • Custom workflows
  • CLI commands
  • Integrations

Plugin Architecture#

┌─────────────────────────────────────────────────────────────────────┐ │ PLUGIN SYSTEM │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ Plugin Manager │ │ │ │ Load | Register | Lifecycle | Dependencies │ │ │ └──────────────────────────┬──────────────────────────────────┘ │ │ │ │ │ ┌──────────────────┼──────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Agent │ │ Skill │ │ Workflow │ │ │ │ Registry │ │ Registry │ │ Registry │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Command │ │ Tool │ │ Hook │ │ │ │ Registry │ │ Registry │ │ Registry │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘

Plugin Interface#

Core Interface#

1// @bootspring/plugin-sdk 2 3export interface Plugin { 4 // Metadata 5 name: string; 6 version: string; 7 description?: string; 8 author?: string; 9 10 // Dependencies 11 dependencies?: PluginDependency[]; 12 13 // Lifecycle hooks 14 activate(context: PluginContext): Promise<void>; 15 deactivate(): Promise<void>; 16} 17 18export interface PluginContext { 19 // Registration 20 registerAgent(profile: AgentProfile): void; 21 registerSkill(skill: SkillDefinition): void; 22 registerWorkflow(workflow: WorkflowDefinition): void; 23 registerCommand(name: string, handler: CommandHandler): void; 24 registerTool(tool: ToolDefinition): void; 25 registerHook(event: string, handler: HookHandler): void; 26 27 // Services 28 getService<T>(name: string): T; 29 getConfig<T>(key: string): T; 30 31 // Storage 32 getStorage(): PluginStorage; 33 34 // Logging 35 getLogger(): Logger; 36}

Example Plugin#

1// my-plugin/src/index.ts 2 3import { Plugin, PluginContext } from '@bootspring/plugin-sdk'; 4 5export default class MyPlugin implements Plugin { 6 name = 'my-plugin'; 7 version = '1.0.0'; 8 description = 'Example Bootspring plugin'; 9 10 private context!: PluginContext; 11 12 async activate(context: PluginContext): Promise<void> { 13 this.context = context; 14 const logger = context.getLogger(); 15 16 logger.info('Activating my-plugin'); 17 18 // Register custom agent 19 context.registerAgent({ 20 name: 'my-custom-agent', 21 displayName: 'My Custom Agent', 22 description: 'A custom agent for specific tasks', 23 category: 'development', 24 tier: 'pro', 25 systemPrompt: 'You are a specialized assistant...', 26 capabilities: ['custom-capability'], 27 }); 28 29 // Register custom command 30 context.registerCommand('my-command', this.handleMyCommand.bind(this)); 31 32 // Register hook 33 context.registerHook('agent.invoked', this.onAgentInvoked.bind(this)); 34 35 logger.info('my-plugin activated'); 36 } 37 38 async deactivate(): Promise<void> { 39 const logger = this.context.getLogger(); 40 logger.info('Deactivating my-plugin'); 41 // Cleanup resources 42 } 43 44 private async handleMyCommand(args: string[]): Promise<void> { 45 console.log('My command executed with:', args); 46 } 47 48 private async onAgentInvoked(event: AgentInvokedEvent): Promise<void> { 49 console.log(`Agent ${event.agent} was invoked`); 50 } 51}

Plugin Manager#

Manager Implementation#

1// core/plugins/manager.ts 2 3export class PluginManager { 4 private plugins: Map<string, LoadedPlugin> = new Map(); 5 private context: PluginContext; 6 7 async loadPlugin(pluginPath: string): Promise<void> { 8 // Import plugin module 9 const PluginClass = await import(pluginPath); 10 const plugin: Plugin = new PluginClass.default(); 11 12 // Check dependencies 13 await this.checkDependencies(plugin); 14 15 // Create context 16 const context = this.createContext(plugin); 17 18 // Activate plugin 19 await plugin.activate(context); 20 21 // Store loaded plugin 22 this.plugins.set(plugin.name, { 23 plugin, 24 context, 25 status: 'active', 26 }); 27 } 28 29 async unloadPlugin(name: string): Promise<void> { 30 const loaded = this.plugins.get(name); 31 if (!loaded) return; 32 33 // Deactivate 34 await loaded.plugin.deactivate(); 35 36 // Remove registrations 37 this.removeRegistrations(name); 38 39 // Remove from map 40 this.plugins.delete(name); 41 } 42 43 private createContext(plugin: Plugin): PluginContext { 44 return { 45 registerAgent: (profile) => { 46 this.agentRegistry.register({ ...profile, plugin: plugin.name }); 47 }, 48 registerSkill: (skill) => { 49 this.skillRegistry.register({ ...skill, plugin: plugin.name }); 50 }, 51 registerWorkflow: (workflow) => { 52 this.workflowRegistry.register({ ...workflow, plugin: plugin.name }); 53 }, 54 registerCommand: (name, handler) => { 55 this.commandRegistry.register(name, handler, plugin.name); 56 }, 57 registerTool: (tool) => { 58 this.toolRegistry.register({ ...tool, plugin: plugin.name }); 59 }, 60 registerHook: (event, handler) => { 61 this.hookRegistry.register(event, handler, plugin.name); 62 }, 63 getService: (name) => this.serviceContainer.get(name), 64 getConfig: (key) => this.configLoader.get(`plugins.${plugin.name}.${key}`), 65 getStorage: () => new PluginStorage(plugin.name), 66 getLogger: () => new PluginLogger(plugin.name), 67 }; 68 } 69}

Plugin Types#

Agent Plugin#

1// plugins/agent/index.ts 2 3export default class AgentPlugin implements Plugin { 4 name = 'my-agent-plugin'; 5 version = '1.0.0'; 6 7 async activate(context: PluginContext): Promise<void> { 8 context.registerAgent({ 9 name: 'specialized-expert', 10 displayName: 'Specialized Expert', 11 description: 'Expert in a specialized domain', 12 category: 'development', 13 tier: 'pro', 14 15 systemPrompt: ` 16 You are an expert in specialized domain X. 17 Your expertise includes: 18 - Capability 1 19 - Capability 2 20 - Capability 3 21 22 When helping users, always: 23 1. Understand the context 24 2. Provide clear explanations 25 3. Give working code examples 26 `, 27 28 capabilities: [ 29 'Specialized skill 1', 30 'Specialized skill 2', 31 ], 32 33 collaborates: ['frontend-expert', 'backend-expert'], 34 }); 35 } 36 37 async deactivate(): Promise<void> {} 38}

Skill Plugin#

1// plugins/skill/index.ts 2 3export default class SkillPlugin implements Plugin { 4 name = 'my-skill-plugin'; 5 version = '1.0.0'; 6 7 async activate(context: PluginContext): Promise<void> { 8 context.registerSkill({ 9 name: 'custom-pattern', 10 category: 'patterns', 11 description: 'A custom code pattern', 12 tier: 'pro', 13 14 files: [ 15 { 16 path: 'components/CustomComponent.tsx', 17 template: ` 18import React from 'react'; 19 20interface {{ComponentName}}Props { 21 {{#each props}} 22 {{name}}: {{type}}; 23 {{/each}} 24} 25 26export function {{ComponentName}}({ {{propNames}} }: {{ComponentName}}Props) { 27 return ( 28 <div> 29 {/* Component implementation */} 30 </div> 31 ); 32} 33 `, 34 }, 35 ], 36 37 variables: [ 38 { name: 'ComponentName', description: 'Component name', required: true }, 39 { name: 'props', description: 'Component props', type: 'array' }, 40 ], 41 }); 42 } 43 44 async deactivate(): Promise<void> {} 45}

Integration Plugin#

1// plugins/integration/index.ts 2 3export default class IntegrationPlugin implements Plugin { 4 name = 'linear-integration'; 5 version = '1.0.0'; 6 7 async activate(context: PluginContext): Promise<void> { 8 const config = context.getConfig<LinearConfig>('linear'); 9 const logger = context.getLogger(); 10 11 // Register CLI commands 12 context.registerCommand('linear', async (args) => { 13 const subcommand = args[0]; 14 15 switch (subcommand) { 16 case 'sync': 17 await this.syncWithLinear(config); 18 break; 19 case 'import': 20 await this.importFromLinear(config); 21 break; 22 case 'export': 23 await this.exportToLinear(config); 24 break; 25 default: 26 console.log('Usage: bootspring linear <sync|import|export>'); 27 } 28 }); 29 30 // Register hook for PRD changes 31 context.registerHook('prd.updated', async (event) => { 32 if (config.autoSync) { 33 await this.syncWithLinear(config); 34 } 35 }); 36 37 logger.info('Linear integration activated'); 38 } 39 40 private async syncWithLinear(config: LinearConfig): Promise<void> { 41 // Sync implementation 42 } 43 44 async deactivate(): Promise<void> {} 45}

Plugin Configuration#

Configuration Schema#

1// Plugin config in bootspring.config.js 2module.exports = { 3 plugins: { 4 'linear-integration': { 5 apiKey: process.env.LINEAR_API_KEY, 6 teamId: 'TEAM', 7 autoSync: true, 8 }, 9 10 'my-agent-plugin': { 11 enabled: true, 12 customSetting: 'value', 13 }, 14 }, 15};

Config Access#

// In plugin const config = context.getConfig<MyPluginConfig>(''); const apiKey = context.getConfig<string>('apiKey');

Plugin Storage#

Storage Interface#

1export interface PluginStorage { 2 get<T>(key: string): Promise<T | null>; 3 set<T>(key: string, value: T): Promise<void>; 4 delete(key: string): Promise<void>; 5 list(): Promise<string[]>; 6}

Storage Usage#

1// In plugin 2const storage = context.getStorage(); 3 4// Store data 5await storage.set('lastSync', new Date().toISOString()); 6 7// Retrieve data 8const lastSync = await storage.get<string>('lastSync');

Hook System#

Available Hooks#

HookEvent
agent.invokedAgent invocation started
agent.completedAgent invocation finished
skill.appliedSkill pattern applied
workflow.startedWorkflow began
workflow.completedWorkflow finished
context.generatedContext regenerated
prd.updatedPRD changed
quality.passedQuality gate passed
quality.failedQuality gate failed

Hook Handler#

1interface HookHandler<T = unknown> { 2 (event: T): Promise<void>; 3} 4 5// Usage 6context.registerHook('agent.completed', async (event: AgentCompletedEvent) => { 7 console.log(`Agent ${event.agent} completed in ${event.duration}ms`); 8 9 // Track in external system 10 await analytics.track('agent_completed', { 11 agent: event.agent, 12 duration: event.duration, 13 }); 14});

Publishing Plugins#

Package Structure#

my-plugin/ ├── package.json ├── tsconfig.json ├── src/ │ ├── index.ts # Plugin entry │ ├── commands/ # CLI commands │ ├── agents/ # Agent definitions │ ├── skills/ # Skill definitions │ └── hooks/ # Hook handlers ├── templates/ # File templates ├── tests/ └── README.md

Package.json#

1{ 2 "name": "@bootspring/plugin-linear", 3 "version": "1.0.0", 4 "main": "dist/index.js", 5 "types": "dist/index.d.ts", 6 "keywords": ["bootspring-plugin"], 7 "peerDependencies": { 8 "@bootspring/plugin-sdk": "^1.0.0" 9 }, 10 "bootspring": { 11 "type": "plugin", 12 "category": "integration" 13 } 14}

Publishing#

1# Build 2npm run build 3 4# Test locally 5bootspring plugin install ./my-plugin 6 7# Publish to npm 8npm publish 9 10# Publish to Bootspring registry (optional) 11bootspring plugin publish