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#
| Hook | Event |
|---|---|
agent.invoked | Agent invocation started |
agent.completed | Agent invocation finished |
skill.applied | Skill pattern applied |
workflow.started | Workflow began |
workflow.completed | Workflow finished |
context.generated | Context regenerated |
prd.updated | PRD changed |
quality.passed | Quality gate passed |
quality.failed | Quality 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