sdd-mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +256 -0
  3. package/dist/__tests__/setup.d.ts +44 -0
  4. package/dist/__tests__/setup.js +178 -0
  5. package/dist/__tests__/setup.js.map +1 -0
  6. package/dist/__tests__/test-helpers/mock-factories.d.ts +26 -0
  7. package/dist/__tests__/test-helpers/mock-factories.js +466 -0
  8. package/dist/__tests__/test-helpers/mock-factories.js.map +1 -0
  9. package/dist/adapters/cli/SDDToolAdapter.d.ts +26 -0
  10. package/dist/adapters/cli/SDDToolAdapter.js +273 -0
  11. package/dist/adapters/cli/SDDToolAdapter.js.map +1 -0
  12. package/dist/application/services/CodebaseAnalysisService.d.ts +38 -0
  13. package/dist/application/services/CodebaseAnalysisService.js +737 -0
  14. package/dist/application/services/CodebaseAnalysisService.js.map +1 -0
  15. package/dist/application/services/LocalizationService.d.ts +184 -0
  16. package/dist/application/services/LocalizationService.js +536 -0
  17. package/dist/application/services/LocalizationService.js.map +1 -0
  18. package/dist/application/services/ProjectContextService.d.ts +61 -0
  19. package/dist/application/services/ProjectContextService.js +550 -0
  20. package/dist/application/services/ProjectContextService.js.map +1 -0
  21. package/dist/application/services/ProjectInitializationService.d.ts +57 -0
  22. package/dist/application/services/ProjectInitializationService.js +485 -0
  23. package/dist/application/services/ProjectInitializationService.js.map +1 -0
  24. package/dist/application/services/ProjectService.d.ts +19 -0
  25. package/dist/application/services/ProjectService.js +159 -0
  26. package/dist/application/services/ProjectService.js.map +1 -0
  27. package/dist/application/services/QualityGateService.d.ts +62 -0
  28. package/dist/application/services/QualityGateService.js +428 -0
  29. package/dist/application/services/QualityGateService.js.map +1 -0
  30. package/dist/application/services/QualityService.d.ts +43 -0
  31. package/dist/application/services/QualityService.js +245 -0
  32. package/dist/application/services/QualityService.js.map +1 -0
  33. package/dist/application/services/SteeringDocumentService.d.ts +62 -0
  34. package/dist/application/services/SteeringDocumentService.js +694 -0
  35. package/dist/application/services/SteeringDocumentService.js.map +1 -0
  36. package/dist/application/services/TemplateService.d.ts +47 -0
  37. package/dist/application/services/TemplateService.js +438 -0
  38. package/dist/application/services/TemplateService.js.map +1 -0
  39. package/dist/application/services/WorkflowEngineService.d.ts +56 -0
  40. package/dist/application/services/WorkflowEngineService.js +348 -0
  41. package/dist/application/services/WorkflowEngineService.js.map +1 -0
  42. package/dist/application/services/WorkflowService.d.ts +22 -0
  43. package/dist/application/services/WorkflowService.js +147 -0
  44. package/dist/application/services/WorkflowService.js.map +1 -0
  45. package/dist/application/services/WorkflowValidationService.d.ts +51 -0
  46. package/dist/application/services/WorkflowValidationService.js +665 -0
  47. package/dist/application/services/WorkflowValidationService.js.map +1 -0
  48. package/dist/domain/context/ProjectContext.d.ts +350 -0
  49. package/dist/domain/context/ProjectContext.js +138 -0
  50. package/dist/domain/context/ProjectContext.js.map +1 -0
  51. package/dist/domain/i18n/index.d.ts +286 -0
  52. package/dist/domain/i18n/index.js +97 -0
  53. package/dist/domain/i18n/index.js.map +1 -0
  54. package/dist/domain/plugins/index.d.ts +498 -0
  55. package/dist/domain/plugins/index.js +157 -0
  56. package/dist/domain/plugins/index.js.map +1 -0
  57. package/dist/domain/ports.d.ts +54 -0
  58. package/dist/domain/ports.js +3 -0
  59. package/dist/domain/ports.js.map +1 -0
  60. package/dist/domain/quality/index.d.ts +361 -0
  61. package/dist/domain/quality/index.js +113 -0
  62. package/dist/domain/quality/index.js.map +1 -0
  63. package/dist/domain/services/DomainService.d.ts +18 -0
  64. package/dist/domain/services/DomainService.js +71 -0
  65. package/dist/domain/services/DomainService.js.map +1 -0
  66. package/dist/domain/templates/index.d.ts +158 -0
  67. package/dist/domain/templates/index.js +22 -0
  68. package/dist/domain/templates/index.js.map +1 -0
  69. package/dist/domain/types.d.ts +115 -0
  70. package/dist/domain/types.js +37 -0
  71. package/dist/domain/types.js.map +1 -0
  72. package/dist/domain/workflow/WorkflowStateMachine.d.ts +62 -0
  73. package/dist/domain/workflow/WorkflowStateMachine.js +286 -0
  74. package/dist/domain/workflow/WorkflowStateMachine.js.map +1 -0
  75. package/dist/index.d.ts +19 -0
  76. package/dist/index.js +97 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/infrastructure/adapters/AjvValidationAdapter.d.ts +7 -0
  79. package/dist/infrastructure/adapters/AjvValidationAdapter.js +37 -0
  80. package/dist/infrastructure/adapters/AjvValidationAdapter.js.map +1 -0
  81. package/dist/infrastructure/adapters/ConsoleLoggerAdapter.d.ts +8 -0
  82. package/dist/infrastructure/adapters/ConsoleLoggerAdapter.js +41 -0
  83. package/dist/infrastructure/adapters/ConsoleLoggerAdapter.js.map +1 -0
  84. package/dist/infrastructure/adapters/FileBasedTaskTracker.d.ts +9 -0
  85. package/dist/infrastructure/adapters/FileBasedTaskTracker.js +41 -0
  86. package/dist/infrastructure/adapters/FileBasedTaskTracker.js.map +1 -0
  87. package/dist/infrastructure/adapters/HandlebarsTemplateEngine.d.ts +8 -0
  88. package/dist/infrastructure/adapters/HandlebarsTemplateEngine.js +38 -0
  89. package/dist/infrastructure/adapters/HandlebarsTemplateEngine.js.map +1 -0
  90. package/dist/infrastructure/adapters/JsonConfigurationAdapter.d.ts +7 -0
  91. package/dist/infrastructure/adapters/JsonConfigurationAdapter.js +24 -0
  92. package/dist/infrastructure/adapters/JsonConfigurationAdapter.js.map +1 -0
  93. package/dist/infrastructure/adapters/LinusQualityAnalyzer.d.ts +9 -0
  94. package/dist/infrastructure/adapters/LinusQualityAnalyzer.js +75 -0
  95. package/dist/infrastructure/adapters/LinusQualityAnalyzer.js.map +1 -0
  96. package/dist/infrastructure/adapters/NodeFileSystemAdapter.d.ts +12 -0
  97. package/dist/infrastructure/adapters/NodeFileSystemAdapter.js +39 -0
  98. package/dist/infrastructure/adapters/NodeFileSystemAdapter.js.map +1 -0
  99. package/dist/infrastructure/di/container.d.ts +3 -0
  100. package/dist/infrastructure/di/container.js +98 -0
  101. package/dist/infrastructure/di/container.js.map +1 -0
  102. package/dist/infrastructure/di/types.d.ts +39 -0
  103. package/dist/infrastructure/di/types.js +45 -0
  104. package/dist/infrastructure/di/types.js.map +1 -0
  105. package/dist/infrastructure/i18n/I18nextService.d.ts +27 -0
  106. package/dist/infrastructure/i18n/I18nextService.js +357 -0
  107. package/dist/infrastructure/i18n/I18nextService.js.map +1 -0
  108. package/dist/infrastructure/mcp/CapabilityNegotiator.d.ts +21 -0
  109. package/dist/infrastructure/mcp/CapabilityNegotiator.js +75 -0
  110. package/dist/infrastructure/mcp/CapabilityNegotiator.js.map +1 -0
  111. package/dist/infrastructure/mcp/ErrorHandler.d.ts +29 -0
  112. package/dist/infrastructure/mcp/ErrorHandler.js +101 -0
  113. package/dist/infrastructure/mcp/ErrorHandler.js.map +1 -0
  114. package/dist/infrastructure/mcp/MCPServer.d.ts +25 -0
  115. package/dist/infrastructure/mcp/MCPServer.js +246 -0
  116. package/dist/infrastructure/mcp/MCPServer.js.map +1 -0
  117. package/dist/infrastructure/mcp/PromptManager.d.ts +18 -0
  118. package/dist/infrastructure/mcp/PromptManager.js +373 -0
  119. package/dist/infrastructure/mcp/PromptManager.js.map +1 -0
  120. package/dist/infrastructure/mcp/ResourceManager.d.ts +15 -0
  121. package/dist/infrastructure/mcp/ResourceManager.js +229 -0
  122. package/dist/infrastructure/mcp/ResourceManager.js.map +1 -0
  123. package/dist/infrastructure/mcp/SessionManager.d.ts +64 -0
  124. package/dist/infrastructure/mcp/SessionManager.js +221 -0
  125. package/dist/infrastructure/mcp/SessionManager.js.map +1 -0
  126. package/dist/infrastructure/mcp/ToolRegistry.d.ts +48 -0
  127. package/dist/infrastructure/mcp/ToolRegistry.js +235 -0
  128. package/dist/infrastructure/mcp/ToolRegistry.js.map +1 -0
  129. package/dist/infrastructure/platform/PlatformAdapter.d.ts +46 -0
  130. package/dist/infrastructure/platform/PlatformAdapter.js +355 -0
  131. package/dist/infrastructure/platform/PlatformAdapter.js.map +1 -0
  132. package/dist/infrastructure/plugins/HookSystem.d.ts +40 -0
  133. package/dist/infrastructure/plugins/HookSystem.js +415 -0
  134. package/dist/infrastructure/plugins/HookSystem.js.map +1 -0
  135. package/dist/infrastructure/plugins/PluginManager.d.ts +51 -0
  136. package/dist/infrastructure/plugins/PluginManager.js +650 -0
  137. package/dist/infrastructure/plugins/PluginManager.js.map +1 -0
  138. package/dist/infrastructure/plugins/PluginSteeringRegistry.d.ts +63 -0
  139. package/dist/infrastructure/plugins/PluginSteeringRegistry.js +439 -0
  140. package/dist/infrastructure/plugins/PluginSteeringRegistry.js.map +1 -0
  141. package/dist/infrastructure/plugins/PluginToolRegistry.d.ts +54 -0
  142. package/dist/infrastructure/plugins/PluginToolRegistry.js +490 -0
  143. package/dist/infrastructure/plugins/PluginToolRegistry.js.map +1 -0
  144. package/dist/infrastructure/quality/ASTAnalyzer.d.ts +65 -0
  145. package/dist/infrastructure/quality/ASTAnalyzer.js +439 -0
  146. package/dist/infrastructure/quality/ASTAnalyzer.js.map +1 -0
  147. package/dist/infrastructure/quality/LinusCodeReviewer.d.ts +52 -0
  148. package/dist/infrastructure/quality/LinusCodeReviewer.js +551 -0
  149. package/dist/infrastructure/quality/LinusCodeReviewer.js.map +1 -0
  150. package/dist/infrastructure/repositories/InMemoryProjectRepository.d.ts +10 -0
  151. package/dist/infrastructure/repositories/InMemoryProjectRepository.js +35 -0
  152. package/dist/infrastructure/repositories/InMemoryProjectRepository.js.map +1 -0
  153. package/dist/infrastructure/schemas/project.schema.d.ts +192 -0
  154. package/dist/infrastructure/schemas/project.schema.js +114 -0
  155. package/dist/infrastructure/schemas/project.schema.js.map +1 -0
  156. package/dist/infrastructure/templates/FileGenerator.d.ts +15 -0
  157. package/dist/infrastructure/templates/FileGenerator.js +385 -0
  158. package/dist/infrastructure/templates/FileGenerator.js.map +1 -0
  159. package/dist/infrastructure/templates/HandlebarsRenderer.d.ts +16 -0
  160. package/dist/infrastructure/templates/HandlebarsRenderer.js +252 -0
  161. package/dist/infrastructure/templates/HandlebarsRenderer.js.map +1 -0
  162. package/dist/infrastructure/templates/TemplateManager.d.ts +36 -0
  163. package/dist/infrastructure/templates/TemplateManager.js +777 -0
  164. package/dist/infrastructure/templates/TemplateManager.js.map +1 -0
  165. package/package.json +89 -0
@@ -0,0 +1,650 @@
1
+ // Plugin manager implementation for discovery, loading, and lifecycle management
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ import { injectable, inject } from 'inversify';
15
+ import { promises as fs } from 'fs';
16
+ import path from 'path';
17
+ import { EventEmitter } from 'events';
18
+ import { TYPES } from '../di/types.js';
19
+ import { createHash, randomBytes, createCipher, createDecipher } from 'crypto';
20
+ const DEFAULT_CONFIG = {
21
+ pluginsDirectory: './plugins',
22
+ dataDirectory: './data/plugins',
23
+ maxPlugins: 100,
24
+ securityEnabled: true,
25
+ autoDiscovery: true,
26
+ autoLoad: false,
27
+ isolationMode: 'sandbox'
28
+ };
29
+ let PluginManager = class PluginManager {
30
+ logger;
31
+ fileSystem;
32
+ hookSystem;
33
+ toolRegistry;
34
+ steeringRegistry;
35
+ config;
36
+ plugins = new Map();
37
+ eventEmitter = new EventEmitter();
38
+ initialized = false;
39
+ constructor(logger, fileSystem, hookSystem, toolRegistry, steeringRegistry, config = {}) {
40
+ this.logger = logger;
41
+ this.fileSystem = fileSystem;
42
+ this.hookSystem = hookSystem;
43
+ this.toolRegistry = toolRegistry;
44
+ this.steeringRegistry = steeringRegistry;
45
+ this.config = { ...DEFAULT_CONFIG, ...config };
46
+ }
47
+ async initialize() {
48
+ if (this.initialized)
49
+ return;
50
+ try {
51
+ // Ensure plugin directories exist
52
+ await this.ensureDirectories();
53
+ // Auto-discovery if enabled
54
+ if (this.config.autoDiscovery) {
55
+ const discovered = await this.discover();
56
+ this.logger.info('Auto-discovery completed', {
57
+ pluginsFound: discovered.length,
58
+ validPlugins: discovered.filter(d => d.valid).length
59
+ });
60
+ // Auto-load valid plugins if enabled
61
+ if (this.config.autoLoad) {
62
+ for (const descriptor of discovered.filter(d => d.valid)) {
63
+ try {
64
+ await this.install(descriptor.path);
65
+ await this.load(descriptor.manifest.id);
66
+ await this.enable(descriptor.manifest.id);
67
+ }
68
+ catch (error) {
69
+ this.logger.warn('Failed to auto-load plugin', {
70
+ pluginId: descriptor.manifest.id,
71
+ error: error instanceof Error ? error.message : String(error)
72
+ });
73
+ }
74
+ }
75
+ }
76
+ }
77
+ this.initialized = true;
78
+ this.logger.info('Plugin manager initialized', {
79
+ pluginsDirectory: this.config.pluginsDirectory,
80
+ loadedPlugins: this.plugins.size
81
+ });
82
+ }
83
+ catch (error) {
84
+ this.logger.error('Plugin manager initialization failed', {
85
+ error: error instanceof Error ? error.message : String(error)
86
+ });
87
+ throw error;
88
+ }
89
+ }
90
+ async discover(directory) {
91
+ const searchDirectory = directory || this.config.pluginsDirectory;
92
+ const descriptors = [];
93
+ try {
94
+ await fs.mkdir(searchDirectory, { recursive: true });
95
+ const entries = await fs.readdir(searchDirectory, { withFileTypes: true });
96
+ for (const entry of entries) {
97
+ if (entry.isDirectory()) {
98
+ const pluginPath = path.join(searchDirectory, entry.name);
99
+ const manifestPath = path.join(pluginPath, 'plugin.json');
100
+ try {
101
+ const manifestContent = await fs.readFile(manifestPath, 'utf-8');
102
+ const manifest = JSON.parse(manifestContent);
103
+ const validation = await this.validatePlugin(manifest);
104
+ descriptors.push({
105
+ path: pluginPath,
106
+ manifest,
107
+ valid: validation.valid,
108
+ errors: validation.errors.map(e => e.message),
109
+ warnings: validation.warnings.map(w => w.message)
110
+ });
111
+ this.logger.debug('Plugin discovered', {
112
+ id: manifest.id,
113
+ name: manifest.name,
114
+ valid: validation.valid,
115
+ path: pluginPath
116
+ });
117
+ }
118
+ catch (error) {
119
+ descriptors.push({
120
+ path: pluginPath,
121
+ manifest: {},
122
+ valid: false,
123
+ errors: [`Failed to read manifest: ${error instanceof Error ? error.message : String(error)}`],
124
+ warnings: []
125
+ });
126
+ }
127
+ }
128
+ }
129
+ this.logger.info('Plugin discovery completed', {
130
+ directory: searchDirectory,
131
+ totalPlugins: descriptors.length,
132
+ validPlugins: descriptors.filter(d => d.valid).length
133
+ });
134
+ return descriptors;
135
+ }
136
+ catch (error) {
137
+ this.logger.error('Plugin discovery failed', {
138
+ directory: searchDirectory,
139
+ error: error instanceof Error ? error.message : String(error)
140
+ });
141
+ throw error;
142
+ }
143
+ }
144
+ async install(pluginPath) {
145
+ const manifestPath = path.join(pluginPath, 'plugin.json');
146
+ try {
147
+ const manifestContent = await fs.readFile(manifestPath, 'utf-8');
148
+ const manifest = JSON.parse(manifestContent);
149
+ // Validate plugin
150
+ const validation = await this.validatePlugin(manifest);
151
+ if (!validation.valid) {
152
+ throw new Error(`Plugin validation failed: ${validation.errors.map(e => e.message).join(', ')}`);
153
+ }
154
+ // Check for conflicts
155
+ if (this.plugins.has(manifest.id)) {
156
+ throw new Error(`Plugin ${manifest.id} is already installed`);
157
+ }
158
+ // Check capacity
159
+ if (this.plugins.size >= this.config.maxPlugins) {
160
+ throw new Error(`Maximum number of plugins (${this.config.maxPlugins}) reached`);
161
+ }
162
+ // Resolve dependencies
163
+ const dependencies = await this.resolveDependencies(manifest);
164
+ this.logger.debug('Plugin dependencies resolved', {
165
+ pluginId: manifest.id,
166
+ dependencies: dependencies.length
167
+ });
168
+ // Create plugin context
169
+ const context = await this.createPluginContext(manifest, pluginPath);
170
+ // Load plugin implementation
171
+ const implementation = await this.loadPluginImplementation(pluginPath, context);
172
+ // Create plugin instance
173
+ const instance = {
174
+ plugin: manifest,
175
+ instance: implementation,
176
+ state: PluginState.INSTALLED,
177
+ loadedAt: new Date(),
178
+ configuration: { ...manifest.configuration.defaults },
179
+ hooks: [],
180
+ tools: []
181
+ };
182
+ this.plugins.set(manifest.id, instance);
183
+ this.eventEmitter.emit('plugin-installed', { pluginId: manifest.id, instance });
184
+ this.logger.info('Plugin installed successfully', {
185
+ id: manifest.id,
186
+ name: manifest.name,
187
+ version: manifest.version
188
+ });
189
+ return instance;
190
+ }
191
+ catch (error) {
192
+ this.logger.error('Plugin installation failed', {
193
+ pluginPath,
194
+ error: error instanceof Error ? error.message : String(error)
195
+ });
196
+ throw error;
197
+ }
198
+ }
199
+ async uninstall(pluginId) {
200
+ const instance = this.plugins.get(pluginId);
201
+ if (!instance) {
202
+ throw new Error(`Plugin ${pluginId} is not installed`);
203
+ }
204
+ try {
205
+ // Disable plugin if active
206
+ if (instance.state === PluginState.ACTIVE) {
207
+ await this.disable(pluginId);
208
+ }
209
+ // Unload plugin if loaded
210
+ if (instance.state === PluginState.LOADED) {
211
+ await this.unload(pluginId);
212
+ }
213
+ // Clear all plugin registrations (defensive cleanup)
214
+ await this.hookSystem.clearHooks(pluginId);
215
+ await this.toolRegistry.clearTools(pluginId);
216
+ await this.steeringRegistry.clearSteeringDocuments(pluginId);
217
+ // Dispose plugin resources
218
+ await instance.instance.dispose();
219
+ // Remove from registry
220
+ this.plugins.delete(pluginId);
221
+ this.eventEmitter.emit('plugin-uninstalled', { pluginId });
222
+ this.logger.info('Plugin uninstalled successfully', { pluginId });
223
+ }
224
+ catch (error) {
225
+ this.logger.error('Plugin uninstallation failed', {
226
+ pluginId,
227
+ error: error instanceof Error ? error.message : String(error)
228
+ });
229
+ throw error;
230
+ }
231
+ }
232
+ async enable(pluginId) {
233
+ const instance = this.plugins.get(pluginId);
234
+ if (!instance) {
235
+ throw new Error(`Plugin ${pluginId} is not installed`);
236
+ }
237
+ if (instance.state === PluginState.ACTIVE) {
238
+ this.logger.debug('Plugin is already enabled', { pluginId });
239
+ return;
240
+ }
241
+ try {
242
+ instance.state = PluginState.LOADING;
243
+ // Initialize plugin if not already done
244
+ if (instance.state !== PluginState.LOADED) {
245
+ await instance.instance.initialize(await this.createPluginContext(instance.plugin, ''));
246
+ }
247
+ // Register plugin hooks
248
+ for (const hookDeclaration of instance.plugin.hooks) {
249
+ const hookRegistration = {
250
+ pluginId,
251
+ name: hookDeclaration.name,
252
+ type: hookDeclaration.type,
253
+ phase: hookDeclaration.phase,
254
+ priority: hookDeclaration.priority,
255
+ handler: async (context) => {
256
+ return await instance.instance.executeHook(hookDeclaration.name, context);
257
+ },
258
+ conditions: []
259
+ };
260
+ await this.hookSystem.register(pluginId, hookRegistration);
261
+ }
262
+ // Register plugin tools
263
+ for (const toolDeclaration of instance.plugin.tools) {
264
+ const toolRegistration = {
265
+ pluginId,
266
+ name: toolDeclaration.name,
267
+ description: toolDeclaration.description,
268
+ category: toolDeclaration.category,
269
+ handler: async (input, context) => {
270
+ return await instance.instance.executeTool(toolDeclaration.name, input);
271
+ },
272
+ inputSchema: toolDeclaration.inputSchema,
273
+ outputSchema: toolDeclaration.outputSchema,
274
+ permissions: toolDeclaration.permissions
275
+ };
276
+ await this.toolRegistry.register(pluginId, toolRegistration);
277
+ }
278
+ // Register plugin steering documents
279
+ for (const steeringDeclaration of instance.plugin.steeringDocuments) {
280
+ await this.steeringRegistry.registerSteeringDocument(pluginId, steeringDeclaration);
281
+ }
282
+ // Activate plugin
283
+ await instance.instance.activate();
284
+ instance.state = PluginState.ACTIVE;
285
+ this.eventEmitter.emit('plugin-enabled', { pluginId, instance });
286
+ this.logger.info('Plugin enabled successfully', {
287
+ pluginId,
288
+ hooks: instance.plugin.hooks.length,
289
+ tools: instance.plugin.tools.length,
290
+ steeringDocuments: instance.plugin.steeringDocuments.length
291
+ });
292
+ }
293
+ catch (error) {
294
+ instance.state = PluginState.ERROR;
295
+ instance.lastError = error instanceof Error ? error : new Error(String(error));
296
+ this.logger.error('Plugin enable failed', {
297
+ pluginId,
298
+ error: error instanceof Error ? error.message : String(error)
299
+ });
300
+ throw error;
301
+ }
302
+ }
303
+ async disable(pluginId) {
304
+ const instance = this.plugins.get(pluginId);
305
+ if (!instance) {
306
+ throw new Error(`Plugin ${pluginId} is not installed`);
307
+ }
308
+ if (instance.state !== PluginState.ACTIVE) {
309
+ this.logger.debug('Plugin is not active', { pluginId, state: instance.state });
310
+ return;
311
+ }
312
+ try {
313
+ // Deactivate plugin first
314
+ await instance.instance.deactivate();
315
+ // Unregister plugin hooks
316
+ for (const hookDeclaration of instance.plugin.hooks) {
317
+ await this.hookSystem.unregister(pluginId, hookDeclaration.name);
318
+ }
319
+ // Unregister plugin tools
320
+ for (const toolDeclaration of instance.plugin.tools) {
321
+ await this.toolRegistry.unregister(pluginId, toolDeclaration.name);
322
+ }
323
+ // Unregister plugin steering documents
324
+ for (const steeringDeclaration of instance.plugin.steeringDocuments) {
325
+ await this.steeringRegistry.unregisterSteeringDocument(pluginId, steeringDeclaration.name);
326
+ }
327
+ instance.state = PluginState.INACTIVE;
328
+ this.eventEmitter.emit('plugin-disabled', { pluginId, instance });
329
+ this.logger.info('Plugin disabled successfully', {
330
+ pluginId,
331
+ hooks: instance.plugin.hooks.length,
332
+ tools: instance.plugin.tools.length,
333
+ steeringDocuments: instance.plugin.steeringDocuments.length
334
+ });
335
+ }
336
+ catch (error) {
337
+ instance.state = PluginState.ERROR;
338
+ instance.lastError = error instanceof Error ? error : new Error(String(error));
339
+ this.logger.error('Plugin disable failed', {
340
+ pluginId,
341
+ error: error instanceof Error ? error.message : String(error)
342
+ });
343
+ throw error;
344
+ }
345
+ }
346
+ async load(pluginId) {
347
+ const instance = this.plugins.get(pluginId);
348
+ if (!instance) {
349
+ throw new Error(`Plugin ${pluginId} is not installed`);
350
+ }
351
+ if (instance.state === PluginState.LOADED || instance.state === PluginState.ACTIVE) {
352
+ return instance;
353
+ }
354
+ try {
355
+ instance.state = PluginState.LOADING;
356
+ const context = await this.createPluginContext(instance.plugin, '');
357
+ await instance.instance.initialize(context);
358
+ instance.state = PluginState.LOADED;
359
+ this.eventEmitter.emit('plugin-loaded', { pluginId, instance });
360
+ this.logger.info('Plugin loaded successfully', { pluginId });
361
+ return instance;
362
+ }
363
+ catch (error) {
364
+ instance.state = PluginState.ERROR;
365
+ instance.lastError = error instanceof Error ? error : new Error(String(error));
366
+ this.logger.error('Plugin load failed', {
367
+ pluginId,
368
+ error: error instanceof Error ? error.message : String(error)
369
+ });
370
+ throw error;
371
+ }
372
+ }
373
+ async unload(pluginId) {
374
+ const instance = this.plugins.get(pluginId);
375
+ if (!instance) {
376
+ throw new Error(`Plugin ${pluginId} is not installed`);
377
+ }
378
+ try {
379
+ if (instance.state === PluginState.ACTIVE) {
380
+ await this.disable(pluginId);
381
+ }
382
+ await instance.instance.dispose();
383
+ instance.state = PluginState.INSTALLED;
384
+ this.eventEmitter.emit('plugin-unloaded', { pluginId, instance });
385
+ this.logger.info('Plugin unloaded successfully', { pluginId });
386
+ }
387
+ catch (error) {
388
+ this.logger.error('Plugin unload failed', {
389
+ pluginId,
390
+ error: error instanceof Error ? error.message : String(error)
391
+ });
392
+ throw error;
393
+ }
394
+ }
395
+ async getPlugin(pluginId) {
396
+ return this.plugins.get(pluginId) || null;
397
+ }
398
+ async getAllPlugins() {
399
+ return Array.from(this.plugins.values());
400
+ }
401
+ async getEnabledPlugins() {
402
+ return Array.from(this.plugins.values()).filter(p => p.state === PluginState.ACTIVE);
403
+ }
404
+ async validatePlugin(plugin) {
405
+ const errors = [];
406
+ const warnings = [];
407
+ const securityIssues = [];
408
+ const compatibilityIssues = [];
409
+ // Basic validation
410
+ if (!plugin.id) {
411
+ errors.push({
412
+ type: 'missing-field',
413
+ message: 'Plugin ID is required',
414
+ field: 'id',
415
+ severity: ValidationSeverity.CRITICAL
416
+ });
417
+ }
418
+ if (!plugin.name) {
419
+ errors.push({
420
+ type: 'missing-field',
421
+ message: 'Plugin name is required',
422
+ field: 'name',
423
+ severity: ValidationSeverity.HIGH
424
+ });
425
+ }
426
+ if (!plugin.version) {
427
+ errors.push({
428
+ type: 'missing-field',
429
+ message: 'Plugin version is required',
430
+ field: 'version',
431
+ severity: ValidationSeverity.HIGH
432
+ });
433
+ }
434
+ // Security validation
435
+ if (this.config.securityEnabled) {
436
+ const security = await this.performSecurityValidation(plugin);
437
+ securityIssues.push(...security);
438
+ }
439
+ // Compatibility validation
440
+ const compatibility = await this.performCompatibilityValidation(plugin);
441
+ compatibilityIssues.push(...compatibility);
442
+ return {
443
+ valid: errors.filter(e => e.severity === ValidationSeverity.CRITICAL).length === 0,
444
+ errors,
445
+ warnings,
446
+ securityIssues,
447
+ compatibilityIssues
448
+ };
449
+ }
450
+ async resolveDependencies(plugin) {
451
+ const resolved = [];
452
+ for (const dep of plugin.dependencies) {
453
+ // Check if dependency is already installed
454
+ const installedPlugin = this.plugins.get(dep.name);
455
+ if (installedPlugin) {
456
+ // Version compatibility check would go here
457
+ resolved.push(dep);
458
+ }
459
+ else if (!dep.optional) {
460
+ throw new Error(`Required dependency ${dep.name} is not installed`);
461
+ }
462
+ }
463
+ return resolved;
464
+ }
465
+ // Private helper methods
466
+ async ensureDirectories() {
467
+ await fs.mkdir(this.config.pluginsDirectory, { recursive: true });
468
+ await fs.mkdir(this.config.dataDirectory, { recursive: true });
469
+ }
470
+ async createPluginContext(plugin, pluginPath) {
471
+ const dataDirectory = path.join(this.config.dataDirectory, plugin.id);
472
+ await fs.mkdir(dataDirectory, { recursive: true });
473
+ return {
474
+ pluginId: plugin.id,
475
+ workingDirectory: pluginPath,
476
+ configurationDirectory: path.join(dataDirectory, 'config'),
477
+ dataDirectory,
478
+ logger: this.createPluginLogger(plugin.id),
479
+ services: this.createPluginServices(plugin.id),
480
+ events: this.createPluginEventEmitter(plugin.id)
481
+ };
482
+ }
483
+ async loadPluginImplementation(pluginPath, context) {
484
+ const indexPath = path.join(pluginPath, 'index.js');
485
+ try {
486
+ // In a production environment, this would use a more secure module loading system
487
+ const pluginModule = await import(`file://${indexPath}`);
488
+ if (!pluginModule.default) {
489
+ throw new Error('Plugin must export a default class implementing PluginImplementation');
490
+ }
491
+ const PluginClass = pluginModule.default;
492
+ return new PluginClass();
493
+ }
494
+ catch (error) {
495
+ throw new Error(`Failed to load plugin implementation: ${error instanceof Error ? error.message : String(error)}`);
496
+ }
497
+ }
498
+ createPluginLogger(pluginId) {
499
+ return {
500
+ debug: (message, data) => {
501
+ this.logger.debug(`[${pluginId}] ${message}`, data);
502
+ },
503
+ info: (message, data) => {
504
+ this.logger.info(`[${pluginId}] ${message}`, data);
505
+ },
506
+ warn: (message, data) => {
507
+ this.logger.warn(`[${pluginId}] ${message}`, data);
508
+ },
509
+ error: (message, error, data) => {
510
+ this.logger.error(`[${pluginId}] ${message}`, { error, ...data });
511
+ }
512
+ };
513
+ }
514
+ createPluginServices(pluginId) {
515
+ return {
516
+ fileSystem: this.createPluginFileSystem(pluginId),
517
+ http: this.createPluginHttpClient(pluginId),
518
+ crypto: this.createPluginCrypto(pluginId),
519
+ utils: this.createPluginUtils(pluginId)
520
+ };
521
+ }
522
+ createPluginEventEmitter(pluginId) {
523
+ const emitter = new EventEmitter();
524
+ return {
525
+ on: (event, listener) => {
526
+ emitter.on(event, listener);
527
+ },
528
+ off: (event, listener) => {
529
+ emitter.off(event, listener);
530
+ },
531
+ emit: (event, ...args) => {
532
+ emitter.emit(event, ...args);
533
+ }
534
+ };
535
+ }
536
+ createPluginFileSystem(pluginId) {
537
+ // This would include security restrictions for plugin file access
538
+ return {
539
+ readFile: async (path) => this.fileSystem.readFile(path),
540
+ writeFile: async (path, content) => this.fileSystem.writeFile(path, content),
541
+ exists: async (path) => this.fileSystem.exists(path),
542
+ mkdir: async (path) => this.fileSystem.mkdir(path),
543
+ readdir: async (path) => [], // Simplified
544
+ stat: async (path) => ({ size: 0, mtime: new Date(), isFile: () => true, isDirectory: () => false })
545
+ };
546
+ }
547
+ createPluginHttpClient(pluginId) {
548
+ // Simplified HTTP client implementation
549
+ return {
550
+ get: async () => ({ status: 200, statusText: 'OK', headers: {}, data: {} }),
551
+ post: async () => ({ status: 200, statusText: 'OK', headers: {}, data: {} }),
552
+ put: async () => ({ status: 200, statusText: 'OK', headers: {}, data: {} }),
553
+ delete: async () => ({ status: 200, statusText: 'OK', headers: {}, data: {} })
554
+ };
555
+ }
556
+ createPluginCrypto(pluginId) {
557
+ return {
558
+ hash: (data, algorithm = 'sha256') => createHash(algorithm).update(data).digest('hex'),
559
+ encrypt: (data, key) => {
560
+ const cipher = createCipher('aes192', key);
561
+ let encrypted = cipher.update(data, 'utf8', 'hex');
562
+ encrypted += cipher.final('hex');
563
+ return encrypted;
564
+ },
565
+ decrypt: (data, key) => {
566
+ const decipher = createDecipher('aes192', key);
567
+ let decrypted = decipher.update(data, 'hex', 'utf8');
568
+ decrypted += decipher.final('utf8');
569
+ return decrypted;
570
+ },
571
+ generateId: () => randomBytes(16).toString('hex'),
572
+ generateSecret: (length = 32) => randomBytes(length).toString('hex')
573
+ };
574
+ }
575
+ createPluginUtils(pluginId) {
576
+ return {
577
+ validateSchema: () => ({ valid: true, errors: [], warnings: [] }),
578
+ formatDate: (date, format) => date.toISOString(),
579
+ parseTemplate: (template, data) => template,
580
+ debounce: (fn, delay) => {
581
+ let timeoutId;
582
+ return ((...args) => {
583
+ clearTimeout(timeoutId);
584
+ timeoutId = setTimeout(() => fn(...args), delay);
585
+ });
586
+ },
587
+ throttle: (fn, interval) => {
588
+ let lastCall = 0;
589
+ return ((...args) => {
590
+ const now = Date.now();
591
+ if (now - lastCall >= interval) {
592
+ lastCall = now;
593
+ return fn(...args);
594
+ }
595
+ });
596
+ }
597
+ };
598
+ }
599
+ async performSecurityValidation(plugin) {
600
+ const issues = [];
601
+ // Check for dangerous permissions
602
+ const dangerousPermissions = ['file:execute', 'system:info'];
603
+ for (const tool of plugin.tools) {
604
+ for (const permission of tool.permissions) {
605
+ if (dangerousPermissions.includes(permission.type)) {
606
+ issues.push({
607
+ type: SecurityIssueType.PRIVILEGE_ESCALATION,
608
+ severity: SecuritySeverity.HIGH,
609
+ message: `Tool '${tool.name}' requests dangerous permission '${permission.type}'`,
610
+ recommendation: 'Review if this permission is necessary and ensure proper validation'
611
+ });
612
+ }
613
+ }
614
+ }
615
+ return issues;
616
+ }
617
+ async performCompatibilityValidation(plugin) {
618
+ const issues = [];
619
+ // Check engine requirements
620
+ for (const engine of plugin.engines) {
621
+ if (engine.name === 'node' && engine.version) {
622
+ const currentNodeVersion = process.version;
623
+ if (!this.isVersionCompatible(currentNodeVersion, engine.version)) {
624
+ issues.push({
625
+ type: 'engine-incompatible',
626
+ message: `Plugin requires Node.js ${engine.version}, but current version is ${currentNodeVersion}`,
627
+ affectedVersions: [currentNodeVersion],
628
+ workaround: `Upgrade Node.js to ${engine.version} or later`
629
+ });
630
+ }
631
+ }
632
+ }
633
+ return issues;
634
+ }
635
+ isVersionCompatible(current, required) {
636
+ // Simplified version comparison - would use proper semver in production
637
+ return current >= required;
638
+ }
639
+ };
640
+ PluginManager = __decorate([
641
+ injectable(),
642
+ __param(0, inject(TYPES.LoggerPort)),
643
+ __param(1, inject(TYPES.FileSystemPort)),
644
+ __param(2, inject(TYPES.HookSystem)),
645
+ __param(3, inject(TYPES.PluginToolRegistry)),
646
+ __param(4, inject(TYPES.PluginSteeringRegistry)),
647
+ __metadata("design:paramtypes", [Object, Object, Object, Object, Function, Object])
648
+ ], PluginManager);
649
+ export { PluginManager };
650
+ //# sourceMappingURL=PluginManager.js.map