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,737 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ 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;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { injectable, inject } from 'inversify';
14
+ import { v4 as uuidv4 } from 'uuid';
15
+ import path from 'path';
16
+ import { TYPES } from '../../infrastructure/di/types.js';
17
+ import { DirectoryPurpose, ProgrammingLanguage, FileType, TechnologyCategory, ArchitecturePatternType, ComplexityType, DependencyType } from '../../domain/context/ProjectContext.js';
18
+ let CodebaseAnalysisService = class CodebaseAnalysisService {
19
+ fileSystem;
20
+ logger;
21
+ validation;
22
+ defaultIgnorePatterns = [
23
+ 'node_modules',
24
+ '.git',
25
+ 'dist',
26
+ 'build',
27
+ 'coverage',
28
+ '.next',
29
+ '.nuxt',
30
+ '__pycache__',
31
+ '.pytest_cache',
32
+ 'target',
33
+ 'bin',
34
+ 'obj'
35
+ ];
36
+ constructor(fileSystem, logger, validation) {
37
+ this.fileSystem = fileSystem;
38
+ this.logger = logger;
39
+ this.validation = validation;
40
+ }
41
+ async analyzeCodebase(projectPath, options = {}) {
42
+ const correlationId = uuidv4();
43
+ this.logger.info('Starting codebase analysis', {
44
+ correlationId,
45
+ projectPath,
46
+ options
47
+ });
48
+ try {
49
+ const startTime = Date.now();
50
+ // Analyze file structure
51
+ const structure = await this.analyzeFileStructure(projectPath, options);
52
+ // Detect technology stack
53
+ const technologyStack = await this.detectTechnologyStack(structure);
54
+ // Analyze dependencies
55
+ const dependencies = await this.analyzeDependencies(projectPath, structure);
56
+ // Detect architecture patterns
57
+ const patterns = await this.detectArchitecturePatterns(structure, technologyStack);
58
+ // Calculate code metrics
59
+ const metrics = await this.calculateCodeMetrics(structure, projectPath);
60
+ // Identify complexity hotspots
61
+ const hotspots = await this.identifyComplexityHotspots(structure, projectPath);
62
+ const analysisTime = Date.now() - startTime;
63
+ this.logger.info('Codebase analysis completed', {
64
+ correlationId,
65
+ analysisTime,
66
+ fileCount: structure.totalFiles,
67
+ directoryCount: structure.totalDirectories,
68
+ technologyCount: technologyStack.primary.length,
69
+ patternCount: patterns.length,
70
+ hotspotCount: hotspots.length
71
+ });
72
+ return {
73
+ structure,
74
+ dependencies,
75
+ patterns,
76
+ metrics,
77
+ hotspots,
78
+ lastAnalyzed: new Date()
79
+ };
80
+ }
81
+ catch (error) {
82
+ this.logger.error('Codebase analysis failed', error, {
83
+ correlationId,
84
+ projectPath
85
+ });
86
+ // Return minimal analysis on failure
87
+ return {
88
+ structure: {
89
+ root: projectPath,
90
+ directories: [],
91
+ files: [],
92
+ totalFiles: 0,
93
+ totalDirectories: 0,
94
+ gitIgnored: []
95
+ },
96
+ dependencies: {
97
+ external: [],
98
+ internal: [],
99
+ devDependencies: [],
100
+ peerDependencies: [],
101
+ graph: { nodes: [], edges: [], cycles: [], orphans: [] }
102
+ },
103
+ patterns: [],
104
+ metrics: {
105
+ linesOfCode: 0,
106
+ complexity: {
107
+ cyclomatic: 0,
108
+ cognitive: 0,
109
+ halstead: {
110
+ vocabulary: 0,
111
+ length: 0,
112
+ difficulty: 0,
113
+ effort: 0,
114
+ timeToCode: 0,
115
+ bugsDelivered: 0
116
+ },
117
+ nesting: { average: 0, maximum: 0, violationsOver3: 0 }
118
+ },
119
+ maintainability: {
120
+ index: 0,
121
+ duplication: 0,
122
+ cohesion: 0,
123
+ coupling: 0,
124
+ debtRatio: 0
125
+ },
126
+ qualityScore: 0
127
+ },
128
+ hotspots: [],
129
+ lastAnalyzed: new Date()
130
+ };
131
+ }
132
+ }
133
+ async analyzeFileStructure(projectPath, options) {
134
+ const directories = [];
135
+ const files = [];
136
+ const gitIgnored = [];
137
+ let totalFiles = 0;
138
+ let totalDirectories = 0;
139
+ const ignorePatterns = [
140
+ ...this.defaultIgnorePatterns,
141
+ ...(options.skipPatterns || [])
142
+ ];
143
+ await this.traverseDirectory(projectPath, projectPath, directories, files, ignorePatterns, options.maxDepth || 10, 0);
144
+ totalFiles = files.length;
145
+ totalDirectories = directories.length;
146
+ // Read .gitignore if exists
147
+ const gitignorePath = path.join(projectPath, '.gitignore');
148
+ if (await this.fileSystem.exists(gitignorePath)) {
149
+ try {
150
+ const gitignoreContent = await this.fileSystem.readFile(gitignorePath);
151
+ gitIgnored.push(...gitignoreContent.split('\n').filter(line => line.trim() && !line.startsWith('#')));
152
+ }
153
+ catch (error) {
154
+ this.logger.warn('Failed to read .gitignore', { projectPath });
155
+ }
156
+ }
157
+ return {
158
+ root: projectPath,
159
+ directories,
160
+ files,
161
+ totalFiles,
162
+ totalDirectories,
163
+ gitIgnored
164
+ };
165
+ }
166
+ async traverseDirectory(basePath, currentPath, directories, files, ignorePatterns, maxDepth, currentDepth) {
167
+ if (currentDepth >= maxDepth)
168
+ return;
169
+ try {
170
+ const items = await this.fileSystem.readdir(currentPath);
171
+ const children = [];
172
+ let fileCount = 0;
173
+ const relativePath = path.relative(basePath, currentPath);
174
+ for (const item of items) {
175
+ const itemPath = path.join(currentPath, item);
176
+ const itemRelativePath = path.relative(basePath, itemPath);
177
+ // Skip ignored patterns
178
+ if (ignorePatterns.some(pattern => itemRelativePath.includes(pattern))) {
179
+ continue;
180
+ }
181
+ try {
182
+ const stat = await this.fileSystem.stat(itemPath);
183
+ if (stat.isDirectory()) {
184
+ children.push(item);
185
+ // Recursively analyze subdirectory
186
+ await this.traverseDirectory(basePath, itemPath, directories, files, ignorePatterns, maxDepth, currentDepth + 1);
187
+ fileCount += await this.countFilesInDirectory(itemPath, ignorePatterns);
188
+ }
189
+ else if (stat.isFile()) {
190
+ const fileNode = await this.analyzeFile(basePath, itemPath);
191
+ files.push(fileNode);
192
+ children.push(item);
193
+ fileCount++;
194
+ }
195
+ }
196
+ catch (error) {
197
+ this.logger.debug('Failed to analyze item', { itemPath });
198
+ }
199
+ }
200
+ // Create directory node
201
+ const directoryNode = {
202
+ path: relativePath || '.',
203
+ name: path.basename(currentPath),
204
+ children,
205
+ fileCount,
206
+ purpose: this.determineDirectoryPurpose(currentPath, children)
207
+ };
208
+ directories.push(directoryNode);
209
+ }
210
+ catch (error) {
211
+ this.logger.debug('Failed to read directory', { currentPath });
212
+ }
213
+ }
214
+ async analyzeFile(basePath, filePath) {
215
+ const relativePath = path.relative(basePath, filePath);
216
+ const fileName = path.basename(filePath);
217
+ const extension = path.extname(filePath).toLowerCase();
218
+ let size = 0;
219
+ let lastModified = new Date();
220
+ try {
221
+ const stat = await this.fileSystem.stat(filePath);
222
+ size = stat.size || 0;
223
+ lastModified = stat.mtime || new Date();
224
+ }
225
+ catch (error) {
226
+ this.logger.debug('Failed to get file stats', { filePath });
227
+ }
228
+ const language = this.detectFileLanguage(extension, fileName);
229
+ const type = this.determineFileType(relativePath, fileName, extension);
230
+ const dependencies = await this.extractFileDependencies(filePath, language);
231
+ const complexity = await this.calculateFileComplexity(filePath, language);
232
+ return {
233
+ path: relativePath,
234
+ name: fileName,
235
+ extension,
236
+ size,
237
+ language,
238
+ type,
239
+ complexity,
240
+ dependencies,
241
+ lastModified
242
+ };
243
+ }
244
+ detectFileLanguage(extension, fileName) {
245
+ const languageMap = {
246
+ '.ts': ProgrammingLanguage.TYPESCRIPT,
247
+ '.tsx': ProgrammingLanguage.TYPESCRIPT,
248
+ '.js': ProgrammingLanguage.JAVASCRIPT,
249
+ '.jsx': ProgrammingLanguage.JAVASCRIPT,
250
+ '.mjs': ProgrammingLanguage.JAVASCRIPT,
251
+ '.py': ProgrammingLanguage.PYTHON,
252
+ '.java': ProgrammingLanguage.JAVA,
253
+ '.go': ProgrammingLanguage.GO,
254
+ '.rs': ProgrammingLanguage.RUST,
255
+ '.cpp': ProgrammingLanguage.CPP,
256
+ '.cc': ProgrammingLanguage.CPP,
257
+ '.cxx': ProgrammingLanguage.CPP,
258
+ '.cs': ProgrammingLanguage.CSHARP,
259
+ '.php': ProgrammingLanguage.PHP,
260
+ '.rb': ProgrammingLanguage.RUBY,
261
+ '.swift': ProgrammingLanguage.SWIFT,
262
+ '.kt': ProgrammingLanguage.KOTLIN,
263
+ '.dart': ProgrammingLanguage.DART
264
+ };
265
+ return languageMap[extension] || ProgrammingLanguage.UNKNOWN;
266
+ }
267
+ determineFileType(relativePath, fileName, extension) {
268
+ // Configuration files
269
+ if (fileName.includes('config') ||
270
+ ['package.json', 'tsconfig.json', '.eslintrc', 'webpack.config'].some(config => fileName.includes(config))) {
271
+ return FileType.CONFIG;
272
+ }
273
+ // Test files
274
+ if (fileName.includes('.test.') ||
275
+ fileName.includes('.spec.') ||
276
+ relativePath.includes('test') ||
277
+ relativePath.includes('__tests__')) {
278
+ return FileType.TEST;
279
+ }
280
+ // Documentation
281
+ if (['.md', '.txt', '.rst', '.adoc'].includes(extension) ||
282
+ fileName.toLowerCase().includes('readme') ||
283
+ relativePath.includes('docs')) {
284
+ return FileType.DOCUMENTATION;
285
+ }
286
+ // Build outputs
287
+ if (relativePath.includes('dist') ||
288
+ relativePath.includes('build') ||
289
+ relativePath.includes('target')) {
290
+ return FileType.BUILD;
291
+ }
292
+ // Source files
293
+ if (['.ts', '.tsx', '.js', '.jsx', '.py', '.java', '.go', '.rs', '.cpp', '.cs'].includes(extension)) {
294
+ return FileType.SOURCE;
295
+ }
296
+ // Assets
297
+ if (['.png', '.jpg', '.jpeg', '.gif', '.svg', '.css', '.scss', '.less'].includes(extension)) {
298
+ return FileType.ASSET;
299
+ }
300
+ return FileType.UNKNOWN;
301
+ }
302
+ determineDirectoryPurpose(dirPath, children) {
303
+ const dirName = path.basename(dirPath).toLowerCase();
304
+ // Source directories
305
+ if (['src', 'source', 'lib', 'app'].includes(dirName)) {
306
+ return DirectoryPurpose.SOURCE;
307
+ }
308
+ // Test directories
309
+ if (['test', 'tests', '__tests__', 'spec', 'specs'].includes(dirName)) {
310
+ return DirectoryPurpose.TEST;
311
+ }
312
+ // Configuration directories
313
+ if (['config', 'configuration', '.vscode', '.github'].includes(dirName)) {
314
+ return DirectoryPurpose.CONFIG;
315
+ }
316
+ // Documentation directories
317
+ if (['docs', 'doc', 'documentation'].includes(dirName)) {
318
+ return DirectoryPurpose.DOCS;
319
+ }
320
+ // Build directories
321
+ if (['dist', 'build', 'target', 'bin', 'out'].includes(dirName)) {
322
+ return DirectoryPurpose.BUILD;
323
+ }
324
+ // Asset directories
325
+ if (['assets', 'static', 'public', 'resources'].includes(dirName)) {
326
+ return DirectoryPurpose.ASSETS;
327
+ }
328
+ // Vendor directories
329
+ if (['vendor', 'third_party', 'external'].includes(dirName)) {
330
+ return DirectoryPurpose.VENDOR;
331
+ }
332
+ return DirectoryPurpose.UNKNOWN;
333
+ }
334
+ async extractFileDependencies(filePath, language) {
335
+ try {
336
+ const content = await this.fileSystem.readFile(filePath);
337
+ const dependencies = [];
338
+ switch (language) {
339
+ case ProgrammingLanguage.TYPESCRIPT:
340
+ case ProgrammingLanguage.JAVASCRIPT:
341
+ // Extract import/require statements
342
+ const importRegex = /(?:import.*from\s+['"`]([^'"`]+)['"`]|require\(['"`]([^'"`]+)['"`]\))/g;
343
+ let match;
344
+ while ((match = importRegex.exec(content)) !== null) {
345
+ const dep = match[1] || match[2];
346
+ if (dep && !dep.startsWith('.')) {
347
+ const packageName = dep.split('/')[0];
348
+ if (packageName) {
349
+ dependencies.push(packageName); // Get package name
350
+ }
351
+ }
352
+ }
353
+ break;
354
+ case ProgrammingLanguage.PYTHON:
355
+ // Extract import statements
356
+ const pythonImportRegex = /(?:^|\n)\s*(?:import\s+([a-zA-Z_][a-zA-Z0-9_.]*)|from\s+([a-zA-Z_][a-zA-Z0-9_.]*)\s+import)/gm;
357
+ let pythonMatch;
358
+ while ((pythonMatch = pythonImportRegex.exec(content)) !== null) {
359
+ const dep = pythonMatch[1] || pythonMatch[2];
360
+ if (dep && !dep.startsWith('.')) {
361
+ const packageName = dep.split('.')[0];
362
+ if (packageName) {
363
+ dependencies.push(packageName);
364
+ }
365
+ }
366
+ }
367
+ break;
368
+ case ProgrammingLanguage.JAVA:
369
+ // Extract import statements
370
+ const javaImportRegex = /import\s+([a-zA-Z_][a-zA-Z0-9_.]*)/g;
371
+ let javaMatch;
372
+ while ((javaMatch = javaImportRegex.exec(content)) !== null) {
373
+ const dep = javaMatch[1];
374
+ if (dep && !dep.startsWith('java.') && !dep.startsWith('javax.')) {
375
+ const packageName = dep.split('.')[0];
376
+ if (packageName) {
377
+ dependencies.push(packageName);
378
+ }
379
+ }
380
+ }
381
+ break;
382
+ }
383
+ return [...new Set(dependencies)]; // Remove duplicates
384
+ }
385
+ catch (error) {
386
+ return [];
387
+ }
388
+ }
389
+ async calculateFileComplexity(filePath, language) {
390
+ try {
391
+ const content = await this.fileSystem.readFile(filePath);
392
+ // Simple cyclomatic complexity calculation
393
+ const complexityKeywords = {
394
+ [ProgrammingLanguage.TYPESCRIPT]: ['if', 'else', 'while', 'for', 'switch', 'case', 'catch', '&&', '||'],
395
+ [ProgrammingLanguage.JAVASCRIPT]: ['if', 'else', 'while', 'for', 'switch', 'case', 'catch', '&&', '||'],
396
+ [ProgrammingLanguage.PYTHON]: ['if', 'elif', 'else', 'while', 'for', 'try', 'except', 'and', 'or'],
397
+ [ProgrammingLanguage.JAVA]: ['if', 'else', 'while', 'for', 'switch', 'case', 'catch', '&&', '||'],
398
+ [ProgrammingLanguage.GO]: ['if', 'else', 'for', 'switch', 'case', '&&', '||'],
399
+ [ProgrammingLanguage.RUST]: ['if', 'else', 'while', 'for', 'match', '&&', '||'],
400
+ [ProgrammingLanguage.CPP]: ['if', 'else', 'while', 'for', 'switch', 'case', 'catch', '&&', '||'],
401
+ [ProgrammingLanguage.CSHARP]: ['if', 'else', 'while', 'for', 'switch', 'case', 'catch', '&&', '||'],
402
+ [ProgrammingLanguage.PHP]: ['if', 'else', 'while', 'for', 'switch', 'case', 'catch', '&&', '||'],
403
+ [ProgrammingLanguage.RUBY]: ['if', 'else', 'while', 'for', 'case', '&&', '||'],
404
+ [ProgrammingLanguage.SWIFT]: ['if', 'else', 'while', 'for', 'switch', 'case', '&&', '||'],
405
+ [ProgrammingLanguage.KOTLIN]: ['if', 'else', 'while', 'for', 'when', '&&', '||'],
406
+ [ProgrammingLanguage.DART]: ['if', 'else', 'while', 'for', 'switch', 'case', '&&', '||'],
407
+ [ProgrammingLanguage.UNKNOWN]: []
408
+ };
409
+ const keywords = complexityKeywords[language] || [];
410
+ let complexity = 1; // Base complexity
411
+ for (const keyword of keywords) {
412
+ const regex = new RegExp(`\\b${keyword}\\b`, 'g');
413
+ const matches = content.match(regex);
414
+ if (matches) {
415
+ complexity += matches.length;
416
+ }
417
+ }
418
+ return complexity;
419
+ }
420
+ catch (error) {
421
+ return 1;
422
+ }
423
+ }
424
+ async detectTechnologyStack(structure) {
425
+ const technologies = [];
426
+ // Language detection based on file extensions
427
+ const languageCount = new Map();
428
+ for (const file of structure.files) {
429
+ if (file.language !== ProgrammingLanguage.UNKNOWN) {
430
+ languageCount.set(file.language, (languageCount.get(file.language) || 0) + 1);
431
+ }
432
+ }
433
+ // Convert to technologies
434
+ const totalFiles = structure.files.length;
435
+ for (const [lang, count] of languageCount.entries()) {
436
+ const confidence = count / totalFiles;
437
+ if (confidence > 0.1) { // At least 10% of files
438
+ technologies.push({
439
+ name: lang,
440
+ category: TechnologyCategory.LANGUAGE,
441
+ confidence,
442
+ evidence: [`${count} files use ${lang}`]
443
+ });
444
+ }
445
+ }
446
+ // Framework detection based on package.json or specific files
447
+ const frameworks = await this.detectFrameworks(structure);
448
+ technologies.push(...frameworks);
449
+ // Sort by confidence
450
+ technologies.sort((a, b) => b.confidence - a.confidence);
451
+ return {
452
+ primary: technologies.filter(t => t.category === TechnologyCategory.LANGUAGE),
453
+ frameworks: technologies.filter(t => t.category === TechnologyCategory.FRAMEWORK),
454
+ tools: technologies.filter(t => t.category === TechnologyCategory.TOOL),
455
+ runtime: technologies.filter(t => t.category === TechnologyCategory.RUNTIME)
456
+ };
457
+ }
458
+ async detectFrameworks(structure) {
459
+ const frameworks = [];
460
+ // Look for framework-specific files
461
+ const frameworkIndicators = {
462
+ 'package.json': { name: 'Node.js', category: TechnologyCategory.RUNTIME, evidence: 'package.json found' },
463
+ 'requirements.txt': { name: 'Python', category: TechnologyCategory.RUNTIME, evidence: 'requirements.txt found' },
464
+ 'pom.xml': { name: 'Maven', category: TechnologyCategory.TOOL, evidence: 'pom.xml found' },
465
+ 'build.gradle': { name: 'Gradle', category: TechnologyCategory.TOOL, evidence: 'build.gradle found' },
466
+ 'Cargo.toml': { name: 'Cargo', category: TechnologyCategory.TOOL, evidence: 'Cargo.toml found' },
467
+ 'go.mod': { name: 'Go Modules', category: TechnologyCategory.TOOL, evidence: 'go.mod found' },
468
+ 'tsconfig.json': { name: 'TypeScript', category: TechnologyCategory.LANGUAGE, evidence: 'tsconfig.json found' },
469
+ 'webpack.config.js': { name: 'Webpack', category: TechnologyCategory.TOOL, evidence: 'webpack.config.js found' },
470
+ 'next.config.js': { name: 'Next.js', category: TechnologyCategory.FRAMEWORK, evidence: 'next.config.js found' },
471
+ 'nuxt.config.js': { name: 'Nuxt.js', category: TechnologyCategory.FRAMEWORK, evidence: 'nuxt.config.js found' },
472
+ 'angular.json': { name: 'Angular', category: TechnologyCategory.FRAMEWORK, evidence: 'angular.json found' },
473
+ 'vue.config.js': { name: 'Vue.js', category: TechnologyCategory.FRAMEWORK, evidence: 'vue.config.js found' }
474
+ };
475
+ for (const file of structure.files) {
476
+ const indicator = frameworkIndicators[file.name];
477
+ if (indicator) {
478
+ frameworks.push({
479
+ name: indicator.name,
480
+ category: indicator.category,
481
+ confidence: 0.9,
482
+ evidence: [indicator.evidence]
483
+ });
484
+ }
485
+ }
486
+ return frameworks;
487
+ }
488
+ async detectArchitecturePatterns(structure, technologyStack) {
489
+ const patterns = [];
490
+ // Detect Clean Architecture
491
+ const hasLayers = ['domain', 'application', 'infrastructure'].every(layer => structure.directories.some(dir => dir.name.toLowerCase().includes(layer)));
492
+ if (hasLayers) {
493
+ patterns.push({
494
+ name: ArchitecturePatternType.CLEAN,
495
+ confidence: 0.8,
496
+ evidence: [
497
+ { description: 'Domain layer found', location: 'src/domain', strength: 'strong' },
498
+ { description: 'Application layer found', location: 'src/application', strength: 'strong' },
499
+ { description: 'Infrastructure layer found', location: 'src/infrastructure', strength: 'strong' }
500
+ ],
501
+ violations: []
502
+ });
503
+ }
504
+ // Detect MVC pattern
505
+ const hasMVC = ['models', 'views', 'controllers'].every(component => structure.directories.some(dir => dir.name.toLowerCase().includes(component)) ||
506
+ structure.files.some(file => file.name.toLowerCase().includes(component)));
507
+ if (hasMVC) {
508
+ patterns.push({
509
+ name: ArchitecturePatternType.MVC,
510
+ confidence: 0.7,
511
+ evidence: [
512
+ { description: 'MVC structure detected', location: 'project root', strength: 'medium' }
513
+ ],
514
+ violations: []
515
+ });
516
+ }
517
+ // Detect Microservices (based on multiple service directories or docker-compose)
518
+ const hasServices = structure.directories.filter(dir => dir.name.includes('service') || dir.name.includes('microservice')).length > 1;
519
+ const hasDockerCompose = structure.files.some(file => file.name === 'docker-compose.yml' || file.name === 'docker-compose.yaml');
520
+ if (hasServices || hasDockerCompose) {
521
+ patterns.push({
522
+ name: ArchitecturePatternType.MICROSERVICES,
523
+ confidence: hasServices ? 0.8 : 0.6,
524
+ evidence: [
525
+ { description: 'Multiple services detected', location: 'project structure', strength: 'medium' }
526
+ ],
527
+ violations: []
528
+ });
529
+ }
530
+ return patterns;
531
+ }
532
+ async analyzeDependencies(projectPath, structure) {
533
+ const external = [];
534
+ const devDependencies = [];
535
+ const peerDependencies = [];
536
+ const internal = [];
537
+ // Analyze package.json if it exists
538
+ const packageJsonPath = path.join(projectPath, 'package.json');
539
+ if (await this.fileSystem.exists(packageJsonPath)) {
540
+ try {
541
+ const packageContent = await this.fileSystem.readFile(packageJsonPath);
542
+ const packageJson = JSON.parse(packageContent);
543
+ // External dependencies
544
+ if (packageJson.dependencies) {
545
+ for (const [name, version] of Object.entries(packageJson.dependencies)) {
546
+ external.push({
547
+ name,
548
+ version: version,
549
+ type: DependencyType.RUNTIME,
550
+ purpose: 'Runtime dependency'
551
+ });
552
+ }
553
+ }
554
+ // Dev dependencies
555
+ if (packageJson.devDependencies) {
556
+ for (const [name, version] of Object.entries(packageJson.devDependencies)) {
557
+ devDependencies.push({
558
+ name,
559
+ version: version,
560
+ type: DependencyType.DEVELOPMENT,
561
+ purpose: 'Development dependency'
562
+ });
563
+ }
564
+ }
565
+ // Peer dependencies
566
+ if (packageJson.peerDependencies) {
567
+ for (const [name, version] of Object.entries(packageJson.peerDependencies)) {
568
+ peerDependencies.push({
569
+ name,
570
+ version: version,
571
+ type: DependencyType.PEER,
572
+ purpose: 'Peer dependency'
573
+ });
574
+ }
575
+ }
576
+ }
577
+ catch (error) {
578
+ this.logger.debug('Failed to parse package.json', { packageJsonPath });
579
+ }
580
+ }
581
+ // Build dependency graph
582
+ const graph = this.buildDependencyGraph(structure, internal);
583
+ return {
584
+ external,
585
+ internal,
586
+ devDependencies,
587
+ peerDependencies,
588
+ graph
589
+ };
590
+ }
591
+ buildDependencyGraph(structure, internal) {
592
+ const nodes = [];
593
+ const edges = [];
594
+ // Add all source files as nodes
595
+ for (const file of structure.files) {
596
+ if (file.type === FileType.SOURCE) {
597
+ nodes.push(file.path);
598
+ }
599
+ }
600
+ // Build edges from file dependencies
601
+ for (const file of structure.files) {
602
+ if (file.type === FileType.SOURCE && file.dependencies.length > 0) {
603
+ for (const dep of file.dependencies) {
604
+ const depFile = structure.files.find(f => f.name.includes(dep) || f.path.includes(dep));
605
+ if (depFile) {
606
+ edges.push({
607
+ from: file.path,
608
+ to: depFile.path,
609
+ weight: 1
610
+ });
611
+ internal.push({
612
+ from: file.path,
613
+ to: depFile.path,
614
+ type: 'import',
615
+ strength: 'medium'
616
+ });
617
+ }
618
+ }
619
+ }
620
+ }
621
+ // Detect cycles (simplified)
622
+ const cycles = [];
623
+ // TODO: Implement proper cycle detection algorithm
624
+ // Find orphan nodes (no dependencies)
625
+ const orphans = nodes.filter(node => !edges.some(edge => edge.from === node || edge.to === node));
626
+ return {
627
+ nodes,
628
+ edges,
629
+ cycles,
630
+ orphans
631
+ };
632
+ }
633
+ async calculateCodeMetrics(structure, projectPath) {
634
+ let totalLines = 0;
635
+ let totalComplexity = 0;
636
+ let sourceFiles = 0;
637
+ for (const file of structure.files) {
638
+ if (file.type === FileType.SOURCE) {
639
+ try {
640
+ const filePath = path.join(projectPath, file.path);
641
+ const content = await this.fileSystem.readFile(filePath);
642
+ const lines = content.split('\n').length;
643
+ totalLines += lines;
644
+ totalComplexity += file.complexity || 1;
645
+ sourceFiles++;
646
+ }
647
+ catch (error) {
648
+ this.logger.debug('Failed to read file for metrics', { filePath: file.path });
649
+ }
650
+ }
651
+ }
652
+ const averageComplexity = sourceFiles > 0 ? totalComplexity / sourceFiles : 0;
653
+ // Calculate quality score (simplified)
654
+ const qualityScore = Math.max(0, Math.min(100, 100 - (averageComplexity - 10) * 5));
655
+ return {
656
+ linesOfCode: totalLines,
657
+ complexity: {
658
+ cyclomatic: totalComplexity,
659
+ cognitive: totalComplexity * 0.8, // Approximation
660
+ halstead: {
661
+ vocabulary: sourceFiles * 10, // Approximation
662
+ length: totalLines,
663
+ difficulty: averageComplexity,
664
+ effort: totalLines * averageComplexity,
665
+ timeToCode: (totalLines * averageComplexity) / 100,
666
+ bugsDelivered: totalLines / 3000 // Approximation
667
+ },
668
+ nesting: {
669
+ average: 2, // Default approximation
670
+ maximum: Math.ceil(averageComplexity / 2),
671
+ violationsOver3: Math.max(0, Math.ceil(averageComplexity - 10))
672
+ }
673
+ },
674
+ maintainability: {
675
+ index: qualityScore,
676
+ duplication: Math.min(20, sourceFiles * 0.1), // Approximation
677
+ cohesion: Math.max(0.3, 1 - (averageComplexity / 20)),
678
+ coupling: Math.min(0.8, averageComplexity / 20),
679
+ debtRatio: Math.min(0.3, (averageComplexity - 5) / 20)
680
+ },
681
+ qualityScore
682
+ };
683
+ }
684
+ async identifyComplexityHotspots(structure, projectPath) {
685
+ const hotspots = [];
686
+ for (const file of structure.files) {
687
+ if (file.type === FileType.SOURCE && (file.complexity || 0) > 10) {
688
+ hotspots.push({
689
+ file: file.path,
690
+ line: 1,
691
+ type: ComplexityType.CYCLOMATIC,
692
+ score: file.complexity || 0,
693
+ reason: `High cyclomatic complexity: ${file.complexity}`,
694
+ suggestion: 'Consider breaking this file into smaller, focused modules'
695
+ });
696
+ }
697
+ }
698
+ return hotspots.sort((a, b) => b.score - a.score);
699
+ }
700
+ async countFilesInDirectory(dirPath, ignorePatterns) {
701
+ try {
702
+ const items = await this.fileSystem.readdir(dirPath);
703
+ let count = 0;
704
+ for (const item of items) {
705
+ const itemPath = path.join(dirPath, item);
706
+ if (ignorePatterns.some(pattern => itemPath.includes(pattern))) {
707
+ continue;
708
+ }
709
+ try {
710
+ const stat = await this.fileSystem.stat(itemPath);
711
+ if (stat.isFile()) {
712
+ count++;
713
+ }
714
+ else if (stat.isDirectory()) {
715
+ count += await this.countFilesInDirectory(itemPath, ignorePatterns);
716
+ }
717
+ }
718
+ catch (error) {
719
+ // Skip items that can't be accessed
720
+ }
721
+ }
722
+ return count;
723
+ }
724
+ catch (error) {
725
+ return 0;
726
+ }
727
+ }
728
+ };
729
+ CodebaseAnalysisService = __decorate([
730
+ injectable(),
731
+ __param(0, inject(TYPES.FileSystemPort)),
732
+ __param(1, inject(TYPES.LoggerPort)),
733
+ __param(2, inject(TYPES.ValidationPort)),
734
+ __metadata("design:paramtypes", [Object, Object, Object])
735
+ ], CodebaseAnalysisService);
736
+ export { CodebaseAnalysisService };
737
+ //# sourceMappingURL=CodebaseAnalysisService.js.map