@noyrax/5d-database-plugin 0.1.8 → 0.1.11-beta.1

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 (92) hide show
  1. package/MCP_SERVER_SETUP.md +14 -0
  2. package/README.md +49 -3
  3. package/SETUP_NEW_PROJECT.md +245 -14
  4. package/out/api/adr-api.d.ts +15 -2
  5. package/out/api/adr-api.d.ts.map +1 -1
  6. package/out/api/adr-api.js +98 -4
  7. package/out/api/adr-api.js.map +1 -1
  8. package/out/api/index.d.ts +1 -0
  9. package/out/api/index.d.ts.map +1 -1
  10. package/out/api/index.js +3 -1
  11. package/out/api/index.js.map +1 -1
  12. package/out/api/ingestion-api.d.ts +42 -0
  13. package/out/api/ingestion-api.d.ts.map +1 -0
  14. package/out/api/ingestion-api.js +148 -0
  15. package/out/api/ingestion-api.js.map +1 -0
  16. package/out/api/module-api.d.ts +11 -1
  17. package/out/api/module-api.d.ts.map +1 -1
  18. package/out/api/module-api.js +54 -2
  19. package/out/api/module-api.js.map +1 -1
  20. package/out/cli/ingest-cli.js +43 -19
  21. package/out/cli/ingest-cli.js.map +1 -1
  22. package/out/cli/merge-workspaces-cli.d.ts +3 -0
  23. package/out/cli/merge-workspaces-cli.d.ts.map +1 -0
  24. package/out/cli/merge-workspaces-cli.js +199 -0
  25. package/out/cli/merge-workspaces-cli.js.map +1 -0
  26. package/out/cli/tool-cli.js +59 -4
  27. package/out/cli/tool-cli.js.map +1 -1
  28. package/out/core/chromadb-vector-database.d.ts.map +1 -1
  29. package/out/core/chromadb-vector-database.js +6 -2
  30. package/out/core/chromadb-vector-database.js.map +1 -1
  31. package/out/core/migration-manager.d.ts +42 -2
  32. package/out/core/migration-manager.d.ts.map +1 -1
  33. package/out/core/migration-manager.js +218 -11
  34. package/out/core/migration-manager.js.map +1 -1
  35. package/out/core/multi-db-manager.d.ts +10 -0
  36. package/out/core/multi-db-manager.d.ts.map +1 -1
  37. package/out/core/multi-db-manager.js +51 -0
  38. package/out/core/multi-db-manager.js.map +1 -1
  39. package/out/ingestors/adr-ingestor.d.ts +7 -0
  40. package/out/ingestors/adr-ingestor.d.ts.map +1 -1
  41. package/out/ingestors/adr-ingestor.js +41 -9
  42. package/out/ingestors/adr-ingestor.js.map +1 -1
  43. package/out/mcp/server.d.ts +3 -0
  44. package/out/mcp/server.d.ts.map +1 -1
  45. package/out/mcp/server.js +106 -2
  46. package/out/mcp/server.js.map +1 -1
  47. package/out/mcp/tools/adr-generator.d.ts +123 -0
  48. package/out/mcp/tools/adr-generator.d.ts.map +1 -0
  49. package/out/mcp/tools/adr-generator.js +1113 -0
  50. package/out/mcp/tools/adr-generator.js.map +1 -0
  51. package/out/mcp/tools/gap-analysis.d.ts +6 -2
  52. package/out/mcp/tools/gap-analysis.d.ts.map +1 -1
  53. package/out/mcp/tools/gap-analysis.js +32 -5
  54. package/out/mcp/tools/gap-analysis.js.map +1 -1
  55. package/out/repositories/module-repository.d.ts +5 -0
  56. package/out/repositories/module-repository.d.ts.map +1 -1
  57. package/out/repositories/module-repository.js +24 -0
  58. package/out/repositories/module-repository.js.map +1 -1
  59. package/out/services/adr-context-builder.d.ts +70 -0
  60. package/out/services/adr-context-builder.d.ts.map +1 -0
  61. package/out/services/adr-context-builder.js +141 -0
  62. package/out/services/adr-context-builder.js.map +1 -0
  63. package/out/services/adr-pattern-analyzer.d.ts +75 -0
  64. package/out/services/adr-pattern-analyzer.d.ts.map +1 -0
  65. package/out/services/adr-pattern-analyzer.js +339 -0
  66. package/out/services/adr-pattern-analyzer.js.map +1 -0
  67. package/out/services/adr-reasoning-service.d.ts +63 -0
  68. package/out/services/adr-reasoning-service.d.ts.map +1 -0
  69. package/out/services/adr-reasoning-service.js +760 -0
  70. package/out/services/adr-reasoning-service.js.map +1 -0
  71. package/out/services/navigation-builder.d.ts.map +1 -1
  72. package/out/services/navigation-builder.js +70 -11
  73. package/out/services/navigation-builder.js.map +1 -1
  74. package/out/services/noyrax-integration-service.d.ts +51 -0
  75. package/out/services/noyrax-integration-service.d.ts.map +1 -0
  76. package/out/services/noyrax-integration-service.js +215 -0
  77. package/out/services/noyrax-integration-service.js.map +1 -0
  78. package/out/services/semantic-pattern-matcher.d.ts +61 -0
  79. package/out/services/semantic-pattern-matcher.d.ts.map +1 -0
  80. package/out/services/semantic-pattern-matcher.js +183 -0
  81. package/out/services/semantic-pattern-matcher.js.map +1 -0
  82. package/out/services/workflow-orchestrator.d.ts +63 -0
  83. package/out/services/workflow-orchestrator.d.ts.map +1 -0
  84. package/out/services/workflow-orchestrator.js +111 -0
  85. package/out/services/workflow-orchestrator.js.map +1 -0
  86. package/out/ui/commands.d.ts.map +1 -1
  87. package/out/ui/commands.js +112 -1
  88. package/out/ui/commands.js.map +1 -1
  89. package/out/ui/database-explorer.d.ts.map +1 -1
  90. package/out/ui/database-explorer.js +47 -1
  91. package/out/ui/database-explorer.js.map +1 -1
  92. package/package.json +22 -4
@@ -0,0 +1,1113 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AdrGeneratorTool = void 0;
37
+ const cross_dimension_api_1 = require("../../api/cross-dimension-api");
38
+ const module_api_1 = require("../../api/module-api");
39
+ const embedding_generator_1 = require("../../embedding/embedding-generator");
40
+ const adr_pattern_analyzer_1 = require("../../services/adr-pattern-analyzer");
41
+ const semantic_pattern_matcher_1 = require("../../services/semantic-pattern-matcher");
42
+ const adr_context_builder_1 = require("../../services/adr-context-builder");
43
+ const adr_reasoning_service_1 = require("../../services/adr-reasoning-service");
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ /**
47
+ * MCP Tool: adr_generator
48
+ * Deterministically reconstructs ADRs from 5D dimensions.
49
+ * Uses similar modules, existing ADRs, and patterns to reconstruct implicit knowledge.
50
+ */
51
+ class AdrGeneratorTool {
52
+ constructor(dbManager, idMapper, workspaceRoot) {
53
+ this.dbManager = dbManager;
54
+ this.idMapper = idMapper;
55
+ this.workspaceRoot = workspaceRoot;
56
+ this.crossDimensionApi = new cross_dimension_api_1.CrossDimensionApi(dbManager, idMapper);
57
+ this.moduleApi = new module_api_1.ModuleApi(dbManager);
58
+ this.patternAnalyzer = new adr_pattern_analyzer_1.AdrPatternAnalyzer(dbManager);
59
+ const embeddingGenerator = new embedding_generator_1.EmbeddingGenerator();
60
+ this.patternMatcher = new semantic_pattern_matcher_1.SemanticPatternMatcher(dbManager, idMapper, embeddingGenerator);
61
+ this.contextBuilder = new adr_context_builder_1.AdrContextBuilder(dbManager, idMapper, this.patternMatcher);
62
+ this.reasoningService = new adr_reasoning_service_1.AdrReasoningService();
63
+ }
64
+ /**
65
+ * Executes the adr_generator tool.
66
+ * Reconstructs ADRs for modules without ADRs.
67
+ *
68
+ * @param args Arguments: pluginId, minDependencies (default: 5), dryRun (default: false), limit (default: 10), useLLM (default: false), llmModel (default: gpt-4o-mini)
69
+ * @returns JSON string with generation results
70
+ */
71
+ async execute(args) {
72
+ // Default: Only generate ADRs for modules with at least 5 dependencies
73
+ // This prevents system overload from too many simple modules
74
+ const minDeps = args.minDependencies !== undefined ? args.minDependencies : 5;
75
+ const dryRun = args.dryRun !== undefined ? args.dryRun : false;
76
+ const limit = args.limit || 10;
77
+ // Get architectural view to find gaps
78
+ const architecturalView = await this.crossDimensionApi.buildArchitecturalView(args.pluginId);
79
+ // Find modules without ADRs
80
+ // Filter: must have at least minDeps dependencies (default: 3)
81
+ // This prevents system overload from generating ADRs for too many simple modules
82
+ const modulesWithoutAdrs = architecturalView
83
+ .filter(view => view.dependencies.length >= minDeps && view.adrs.length === 0)
84
+ .sort((a, b) => b.dependencies.length - a.dependencies.length)
85
+ .slice(0, limit);
86
+ const results = [];
87
+ // Find next available ADR number
88
+ const nextAdrNumber = await this.findNextAdrNumber(args.pluginId);
89
+ for (let i = 0; i < modulesWithoutAdrs.length; i++) {
90
+ const view = modulesWithoutAdrs[i];
91
+ const module = view.module;
92
+ const adrNumber = this.formatAdrNumber(nextAdrNumber + i);
93
+ const adrFileName = `${adrNumber}-${this.generateSlug(module.file_path)}.md`;
94
+ const adrPath = path.join(this.workspaceRoot, 'docs', 'adr', adrFileName);
95
+ // Reconstruct ADR content (with or without LLM)
96
+ const useLLM = args.useLLM === true && this.reasoningService.isAvailable();
97
+ const adrContent = await this.reconstructAdr(module, args.pluginId, adrNumber, useLLM);
98
+ if (!dryRun) {
99
+ // Ensure docs/adr directory exists
100
+ const adrDir = path.dirname(adrPath);
101
+ if (!fs.existsSync(adrDir)) {
102
+ fs.mkdirSync(adrDir, { recursive: true });
103
+ }
104
+ // Write ADR file
105
+ fs.writeFileSync(adrPath, adrContent, 'utf-8');
106
+ }
107
+ results.push({
108
+ module_path: module.file_path,
109
+ adr_number: adrNumber,
110
+ adr_file: adrFileName,
111
+ generated: !dryRun,
112
+ content_preview: adrContent.substring(0, 500) + '...'
113
+ });
114
+ }
115
+ return JSON.stringify({
116
+ summary: {
117
+ modules_analyzed: architecturalView.length,
118
+ modules_without_adrs: modulesWithoutAdrs.length,
119
+ adrs_generated: results.length,
120
+ dry_run: dryRun,
121
+ next_adr_number: nextAdrNumber
122
+ },
123
+ generated_adrs: results,
124
+ recommendations: dryRun
125
+ ? [`Run without --dry-run to actually generate ${results.length} ADR(s)`]
126
+ : [`Generated ${results.length} ADR(s) deterministically from 5D dimensions.`]
127
+ }, null, 2);
128
+ }
129
+ /**
130
+ * Calculates module complexity based on dependencies, symbols, and incoming dependencies.
131
+ * Returns 'simple', 'medium', or 'complex'.
132
+ *
133
+ * Rules:
134
+ * - >= 20 dependencies → automatically 'complex'
135
+ * - >= 15 incoming dependencies → automatically 'complex'
136
+ * - >= 30 symbols → automatically 'complex'
137
+ * - Otherwise: scoring system
138
+ */
139
+ calculateComplexity(context) {
140
+ const depCount = context.dependencies.length;
141
+ const incomingCount = context.incomingDependencies.length;
142
+ const symbolCount = context.symbols.length;
143
+ // Automatic 'complex' for very high numbers
144
+ if (depCount >= 20)
145
+ return 'complex';
146
+ if (incomingCount >= 15)
147
+ return 'complex';
148
+ if (symbolCount >= 30)
149
+ return 'complex';
150
+ // Scoring: 0-2 = simple, 3-5 = medium, 6+ = complex
151
+ let score = 0;
152
+ // Dependencies scoring (more granular)
153
+ if (depCount >= 5)
154
+ score++;
155
+ if (depCount >= 10)
156
+ score++;
157
+ if (depCount >= 15)
158
+ score++; // Additional point for high dependency count
159
+ // Incoming dependencies scoring
160
+ if (incomingCount >= 3)
161
+ score++;
162
+ if (incomingCount >= 8)
163
+ score++; // Increased threshold
164
+ // Symbols scoring
165
+ if (symbolCount >= 5)
166
+ score++;
167
+ if (symbolCount >= 15)
168
+ score++;
169
+ if (symbolCount >= 25)
170
+ score++; // Additional point for very high symbol count
171
+ if (score <= 2)
172
+ return 'simple';
173
+ if (score <= 5)
174
+ return 'medium';
175
+ return 'complex';
176
+ }
177
+ /**
178
+ * Reconstructs an ADR from 5D dimensions (with optional LLM for "Why").
179
+ */
180
+ async reconstructAdr(module, pluginId, adrNumber, useLLM = false) {
181
+ // Build complete context from all dimensions
182
+ const context = await this.contextBuilder.buildModuleContext(module, pluginId);
183
+ // Calculate complexity
184
+ const complexity = this.calculateComplexity(context);
185
+ // Find similar ADR patterns
186
+ const similarPatterns = await this.patternAnalyzer.findSimilarAdrPatterns(module, pluginId);
187
+ // Get ADRs for similar modules
188
+ const similarAdrs = await this.patternMatcher.findAdrsForSimilarModules(module, pluginId);
189
+ // Extract title from module or similar ADRs
190
+ const title = this.extractTitle(module, similarAdrs);
191
+ // Use LLM for "Why" reconstruction if available and requested
192
+ let reasoning = null;
193
+ if (useLLM) {
194
+ if (!this.reasoningService.isAvailable()) {
195
+ console.warn(`[AdrGeneratorTool] LLM not available (OpenAI API key not configured). Falling back to deterministic method.`);
196
+ }
197
+ else {
198
+ try {
199
+ const similarAdrList = similarAdrs.map(sa => sa.adr);
200
+ reasoning = await this.reasoningService.reconstructWhy(context, similarAdrList, context.patterns, complexity);
201
+ console.log(`[AdrGeneratorTool] Successfully reconstructed reasoning using LLM for ${module.file_path}`);
202
+ }
203
+ catch (error) {
204
+ const errorMsg = error instanceof Error ? error.message : String(error);
205
+ console.warn(`[AdrGeneratorTool] LLM reasoning failed, falling back to deterministic method: ${errorMsg}`);
206
+ // Fall through to deterministic method
207
+ }
208
+ }
209
+ }
210
+ // Reconstruct each section (use LLM reasoning if available)
211
+ const status = this.reconstructStatus();
212
+ const contextSection = await this.reconstructContext(context, similarAdrs, reasoning, complexity);
213
+ const decisionSection = await this.reconstructDecision(context, similarAdrs, reasoning, complexity);
214
+ const consequencesSection = await this.reconstructConsequences(context, reasoning, complexity);
215
+ const refactoringSection = this.reconstructRefactoringRecommendations(reasoning, complexity);
216
+ const verweiseSection = this.reconstructVerweise(context, similarAdrs);
217
+ // Build ADR content
218
+ let adrContent = `# ADR-${adrNumber}: ${title}
219
+
220
+ ## Status
221
+
222
+ ${status}
223
+
224
+ ## Kontext
225
+
226
+ ${contextSection}
227
+
228
+ ## Entscheidung
229
+
230
+ ${decisionSection}
231
+
232
+ ## Konsequenzen
233
+
234
+ ${consequencesSection}
235
+
236
+ ## Verweise
237
+
238
+ ${verweiseSection}
239
+ `;
240
+ // Add refactoring recommendations if available
241
+ if (refactoringSection) {
242
+ adrContent += `\n${refactoringSection}\n`;
243
+ }
244
+ // Add implementation section for complex modules (similar to ADR-038)
245
+ if (complexity === 'complex') {
246
+ const implementationSection = this.reconstructImplementation(context, reasoning);
247
+ if (implementationSection) {
248
+ adrContent += `\n${implementationSection}\n`;
249
+ }
250
+ }
251
+ return adrContent;
252
+ }
253
+ /**
254
+ * Reconstructs Refactoring Recommendations section from LLM reasoning.
255
+ * Skips recommendations for simple modules.
256
+ */
257
+ reconstructRefactoringRecommendations(reasoning, complexity = 'complex') {
258
+ // Skip refactoring recommendations for simple modules
259
+ if (complexity === 'simple') {
260
+ return '';
261
+ }
262
+ if (!reasoning || !reasoning.refactoring_recommendations || reasoning.refactoring_recommendations.length === 0) {
263
+ return '';
264
+ }
265
+ let refactoringText = `## Refactoring-Empfehlungen\n\n`;
266
+ // Limit recommendations based on complexity
267
+ const maxRecommendations = complexity === 'medium' ? 2 : reasoning.refactoring_recommendations.length;
268
+ for (const recommendation of reasoning.refactoring_recommendations.slice(0, maxRecommendations)) {
269
+ refactoringText += `- ${recommendation}\n`;
270
+ }
271
+ return refactoringText;
272
+ }
273
+ /**
274
+ * Reconstructs Status section.
275
+ */
276
+ reconstructStatus() {
277
+ return 'Proposed';
278
+ }
279
+ /**
280
+ * Reconstructs Context section from module context and similar ADRs.
281
+ * Uses LLM reasoning if available for problem identification.
282
+ * Adjusts detail level based on module complexity.
283
+ */
284
+ async reconstructContext(context, similarAdrs, reasoning = null, complexity = 'complex') {
285
+ const module = context.module;
286
+ const moduleName = path.basename(module.file_path, path.extname(module.file_path));
287
+ const moduleDir = path.dirname(module.file_path);
288
+ // Parse module documentation for detailed information
289
+ const moduleDoc = this.parseModuleDocumentation(module.content_markdown);
290
+ let contextText = `Dieses Modul (\`${module.file_path}\`) ist Teil der Architektur.`;
291
+ // Add problems from LLM reasoning if available
292
+ // Limit problems based on complexity
293
+ if (reasoning && reasoning.problems && reasoning.problems.length > 0) {
294
+ contextText += `\n\n**Problem:**\n\n`;
295
+ const maxProblems = complexity === 'simple' ? 2 : complexity === 'medium' ? 3 : reasoning.problems.length;
296
+ for (let i = 0; i < Math.min(reasoning.problems.length, maxProblems); i++) {
297
+ contextText += `**Problem ${i + 1}:** ${reasoning.problems[i]}\n\n`;
298
+ }
299
+ }
300
+ // Add module purpose from documentation
301
+ if (moduleDoc.purpose) {
302
+ contextText += `**Zweck:** ${moduleDoc.purpose}\n\n`;
303
+ }
304
+ // Add detailed class/interface information (limit based on complexity)
305
+ if (moduleDoc.classes.length > 0) {
306
+ const maxClasses = complexity === 'simple' ? 2 : complexity === 'medium' ? 3 : 5;
307
+ contextText += `\n\n**Hauptklassen/Interfaces:**`;
308
+ for (const cls of moduleDoc.classes.slice(0, maxClasses)) {
309
+ contextText += `\n- \`${cls.name}\` (${cls.role || 'other'})`;
310
+ if (cls.methodCount > 0 && complexity !== 'simple') {
311
+ contextText += ` - ${cls.methodCount} Methoden`;
312
+ }
313
+ }
314
+ }
315
+ // Add key functions/methods (limit based on complexity)
316
+ if (moduleDoc.keyFunctions.length > 0) {
317
+ const maxFunctions = complexity === 'simple' ? 2 : complexity === 'medium' ? 5 : 10;
318
+ contextText += `\n\n**Hauptfunktionen:**`;
319
+ for (const func of moduleDoc.keyFunctions.slice(0, maxFunctions)) {
320
+ contextText += `\n- \`${func.name}()\` - ${func.role || 'other'}`;
321
+ if (func.signature && complexity !== 'simple') {
322
+ const sigPreview = func.signature.length > 80 ? func.signature.substring(0, 80) + '...' : func.signature;
323
+ contextText += `\n \`${sigPreview}\``;
324
+ }
325
+ }
326
+ }
327
+ // Add detailed dependencies analysis (simplified for simple modules)
328
+ if (context.dependencies.length > 0) {
329
+ if (complexity === 'simple') {
330
+ // For simple modules, just show count
331
+ contextText += `\n\n**Dependencies (${context.dependencies.length}):**`;
332
+ }
333
+ else {
334
+ contextText += `\n\n**Dependencies (${context.dependencies.length}):**`;
335
+ // Group by dependency category
336
+ const coreDeps = [];
337
+ const apiDeps = [];
338
+ const serviceDeps = [];
339
+ const otherDeps = [];
340
+ for (const dep of context.dependencies) {
341
+ const toModule = dep.to_module || '';
342
+ if (toModule.includes('/core/')) {
343
+ coreDeps.push(toModule);
344
+ }
345
+ else if (toModule.includes('/api/')) {
346
+ apiDeps.push(toModule);
347
+ }
348
+ else if (toModule.includes('/services/')) {
349
+ serviceDeps.push(toModule);
350
+ }
351
+ else {
352
+ otherDeps.push(toModule);
353
+ }
354
+ }
355
+ const maxDeps = complexity === 'medium' ? 5 : 10;
356
+ if (coreDeps.length > 0) {
357
+ contextText += `\n\n**Core Dependencies (${coreDeps.length}):**`;
358
+ for (const dep of coreDeps.slice(0, maxDeps)) {
359
+ contextText += `\n- \`${dep}\``;
360
+ }
361
+ }
362
+ if (apiDeps.length > 0) {
363
+ contextText += `\n\n**API Dependencies (${apiDeps.length}):**`;
364
+ for (const dep of apiDeps.slice(0, maxDeps)) {
365
+ contextText += `\n- \`${dep}\``;
366
+ }
367
+ }
368
+ if (serviceDeps.length > 0) {
369
+ contextText += `\n\n**Service Dependencies (${serviceDeps.length}):**`;
370
+ for (const dep of serviceDeps.slice(0, maxDeps)) {
371
+ contextText += `\n- \`${dep}\``;
372
+ }
373
+ }
374
+ if (otherDeps.length > 0 && (complexity === 'complex' || otherDeps.length <= 10)) {
375
+ contextText += `\n\n**Weitere Dependencies (${otherDeps.length}):**`;
376
+ const maxOtherDeps = complexity === 'medium' ? 5 : otherDeps.length;
377
+ for (const dep of otherDeps.slice(0, maxOtherDeps)) {
378
+ contextText += `\n- \`${dep}\``;
379
+ }
380
+ }
381
+ }
382
+ }
383
+ // Add incoming dependencies (who uses this module) - skip for simple modules
384
+ if (context.incomingDependencies.length > 0 && complexity !== 'simple') {
385
+ const maxUsers = complexity === 'medium' ? 5 : 10;
386
+ contextText += `\n\n**Wird genutzt von (${context.incomingDependencies.length} Modulen):**`;
387
+ const uniqueUsers = new Set(context.incomingDependencies.map((d) => d.from_module || d.from));
388
+ for (const user of Array.from(uniqueUsers).slice(0, maxUsers)) {
389
+ contextText += `\n- \`${user}\``;
390
+ }
391
+ }
392
+ // Add pattern information with evidence (skip for simple modules)
393
+ if (context.patterns.length > 0 && complexity !== 'simple') {
394
+ const primaryPattern = context.patterns[0];
395
+ contextText += `\n\n**Erkanntes Pattern:** ${primaryPattern.pattern} (${primaryPattern.confidence} confidence)`;
396
+ if (primaryPattern.evidence.length > 0 && complexity === 'complex') {
397
+ contextText += `\n\n**Evidence:**`;
398
+ for (const evidence of primaryPattern.evidence.slice(0, 5)) {
399
+ contextText += `\n- ${evidence}`;
400
+ }
401
+ }
402
+ }
403
+ // Add similar modules with context (skip for simple modules)
404
+ if (context.similarModules.length > 0 && complexity !== 'simple') {
405
+ contextText += `\n\n**Ähnliche Module:**`;
406
+ const maxSimilar = complexity === 'medium' ? 2 : 3;
407
+ for (const similar of context.similarModules.slice(0, maxSimilar)) {
408
+ contextText += `\n- \`${similar.module.file_path}\` (Similarity: ${(similar.score * 100).toFixed(0)}%)`;
409
+ }
410
+ }
411
+ // Add symbols summary with details (simplified for simple modules)
412
+ if (context.symbols.length > 0 && complexity !== 'simple') {
413
+ contextText += `\n\n**Symbole (${context.symbols.length}):**`;
414
+ const symbolKinds = new Map();
415
+ for (const symbol of context.symbols) {
416
+ const kind = symbol.kind || 'unknown';
417
+ if (!symbolKinds.has(kind)) {
418
+ symbolKinds.set(kind, []);
419
+ }
420
+ const sig = symbol.signature_json ? JSON.parse(symbol.signature_json) : null;
421
+ symbolKinds.get(kind).push({
422
+ name: symbol.name || 'unknown',
423
+ signature: sig ? JSON.stringify(sig).substring(0, 100) : undefined
424
+ });
425
+ }
426
+ const maxSymbolsPerKind = complexity === 'medium' ? 3 : 5;
427
+ for (const [kind, symbols] of symbolKinds.entries()) {
428
+ contextText += `\n\n**${kind} (${symbols.length}):**`;
429
+ for (const sym of symbols.slice(0, maxSymbolsPerKind)) {
430
+ contextText += `\n- \`${sym.name}\``;
431
+ }
432
+ if (symbols.length > maxSymbolsPerKind) {
433
+ contextText += `\n- ... und ${symbols.length - maxSymbolsPerKind} weitere`;
434
+ }
435
+ }
436
+ }
437
+ return contextText;
438
+ }
439
+ /**
440
+ * Parses module documentation markdown to extract structured information.
441
+ */
442
+ parseModuleDocumentation(content) {
443
+ const lines = content.split('\n');
444
+ const result = {
445
+ purpose: null,
446
+ classes: [],
447
+ keyFunctions: []
448
+ };
449
+ let currentClass = null;
450
+ let methodCount = 0;
451
+ for (let i = 0; i < lines.length; i++) {
452
+ const line = lines[i];
453
+ // Extract purpose from first paragraph after title
454
+ if (line.startsWith('# Modul:') && i + 1 < lines.length) {
455
+ const nextLine = lines[i + 1].trim();
456
+ if (nextLine && !nextLine.startsWith('#')) {
457
+ result.purpose = nextLine.substring(0, 200);
458
+ }
459
+ }
460
+ // Extract class information
461
+ const classMatch = line.match(/^###\s+(?:class|interface):\s*(.+)$/);
462
+ if (classMatch) {
463
+ if (currentClass) {
464
+ currentClass.methodCount = methodCount;
465
+ result.classes.push(currentClass);
466
+ }
467
+ const className = classMatch[1].trim();
468
+ const roleMatch = line.match(/Rolle:\s*([^,]+)/);
469
+ const role = roleMatch ? roleMatch[1].trim() : null;
470
+ currentClass = { name: className, role, methodCount: 0 };
471
+ methodCount = 0;
472
+ }
473
+ // Extract method information
474
+ const methodMatch = line.match(/^###\s+(?:method|function):\s*(.+)$/);
475
+ if (methodMatch) {
476
+ methodCount++;
477
+ const methodName = methodMatch[1].trim();
478
+ const roleMatch = line.match(/Rolle:\s*([^,]+)/);
479
+ const role = roleMatch ? roleMatch[1].trim() : null;
480
+ // Try to find signature in next lines
481
+ let signature = null;
482
+ for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
483
+ if (lines[j].includes('Signatur:') || lines[j].includes('```')) {
484
+ const sigMatch = lines[j].match(/Signatur:\s*`(.+)`/) ||
485
+ lines[j + 1]?.match(/```ts\n(.+)\n```/);
486
+ if (sigMatch) {
487
+ signature = sigMatch[1].trim();
488
+ break;
489
+ }
490
+ }
491
+ }
492
+ result.keyFunctions.push({ name: methodName, role, signature });
493
+ }
494
+ }
495
+ if (currentClass) {
496
+ currentClass.methodCount = methodCount;
497
+ result.classes.push(currentClass);
498
+ }
499
+ return result;
500
+ }
501
+ /**
502
+ * Reconstructs Decision section from patterns and similar ADRs.
503
+ * Uses LLM reasoning if available for decision and rationale.
504
+ * Adjusts detail level based on module complexity.
505
+ */
506
+ async reconstructDecision(context, similarAdrs, reasoning = null, complexity = 'complex') {
507
+ let decisionText = '';
508
+ // Use LLM reasoning if available
509
+ if (reasoning && reasoning.decision) {
510
+ decisionText += reasoning.decision;
511
+ if (reasoning.rationale) {
512
+ decisionText += `\n\n**Begründung:**\n\n${reasoning.rationale}`;
513
+ }
514
+ // Limit alternatives based on complexity
515
+ if (reasoning.alternatives && reasoning.alternatives.length > 0) {
516
+ const maxAlternatives = complexity === 'simple' ? 1 : complexity === 'medium' ? 2 : reasoning.alternatives.length;
517
+ if (maxAlternatives > 0) {
518
+ decisionText += `\n\n**Alternativen, die erwogen wurden:**\n\n`;
519
+ for (const alt of reasoning.alternatives.slice(0, maxAlternatives)) {
520
+ decisionText += `- ${alt}\n`;
521
+ }
522
+ }
523
+ }
524
+ return decisionText;
525
+ }
526
+ // Fallback to deterministic method
527
+ // Parse module documentation for decision context
528
+ const moduleDoc = this.parseModuleDocumentation(context.module.content_markdown);
529
+ // Use similar ADRs as template with deeper analysis
530
+ if (similarAdrs.length > 0) {
531
+ const bestMatch = similarAdrs[0];
532
+ const similarAdrContent = bestMatch.adr.content_markdown;
533
+ // Extract full Context and Decision sections from similar ADR
534
+ const contextMatch = similarAdrContent.match(/##\s+Kontext\s*\n([\s\S]*?)(?=##\s+(?:Entscheidung|Decision)|$)/i);
535
+ const decisionMatch = similarAdrContent.match(/##\s+Entscheidung\s*\n([\s\S]*?)(?=##|$)/i);
536
+ if (decisionMatch) {
537
+ decisionText += `Basierend auf ähnlichem Modul \`${bestMatch.module.file_path}\` (ADR-${bestMatch.adr.adr_number}):\n\n`;
538
+ // Extract decision structure from similar ADR
539
+ const similarDecision = decisionMatch[1].trim();
540
+ // Analyze decision structure (subsections, implementation details)
541
+ const decisionStructure = this.analyzeDecisionStructure(similarDecision);
542
+ // Adapt decision with module-specific details
543
+ decisionText += this.adaptDecisionTextDeep(similarDecision, decisionStructure, context.module, bestMatch.module, moduleDoc, context);
544
+ }
545
+ else {
546
+ decisionText += this.generateDecisionFromPatterns(context, moduleDoc);
547
+ }
548
+ }
549
+ else {
550
+ decisionText += this.generateDecisionFromPatterns(context, moduleDoc);
551
+ }
552
+ return decisionText;
553
+ }
554
+ /**
555
+ * Analyzes decision structure from similar ADR.
556
+ */
557
+ analyzeDecisionStructure(decisionText) {
558
+ const lines = decisionText.split('\n');
559
+ const result = {
560
+ subsections: [],
561
+ implementationDetails: [],
562
+ rationale: null
563
+ };
564
+ let currentSubsection = null;
565
+ let currentContent = [];
566
+ for (const line of lines) {
567
+ // Check for subsection (### or ####)
568
+ const subsectionMatch = line.match(/^###+\s+(.+)$/);
569
+ if (subsectionMatch) {
570
+ if (currentSubsection) {
571
+ currentSubsection.content = currentContent.join('\n').trim();
572
+ result.subsections.push(currentSubsection);
573
+ }
574
+ currentSubsection = { title: subsectionMatch[1].trim(), content: '' };
575
+ currentContent = [];
576
+ }
577
+ else if (currentSubsection) {
578
+ currentContent.push(line);
579
+ }
580
+ else {
581
+ // Main rationale
582
+ if (line.trim() && !line.startsWith('#')) {
583
+ if (!result.rationale) {
584
+ result.rationale = '';
585
+ }
586
+ result.rationale += line + '\n';
587
+ }
588
+ }
589
+ // Extract implementation details (file paths, function names)
590
+ const fileMatch = line.match(/`([^`]+\.ts)`/);
591
+ if (fileMatch) {
592
+ result.implementationDetails.push(fileMatch[1]);
593
+ }
594
+ }
595
+ if (currentSubsection) {
596
+ currentSubsection.content = currentContent.join('\n').trim();
597
+ result.subsections.push(currentSubsection);
598
+ }
599
+ if (result.rationale) {
600
+ result.rationale = result.rationale.trim();
601
+ }
602
+ return result;
603
+ }
604
+ /**
605
+ * Deep adaptation of decision text with module-specific details.
606
+ */
607
+ adaptDecisionTextDeep(similarDecision, decisionStructure, targetModule, similarModule, moduleDoc, context) {
608
+ let adapted = similarDecision;
609
+ // Replace module paths
610
+ adapted = adapted.replace(new RegExp(similarModule.file_path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), targetModule.file_path);
611
+ // Replace module names
612
+ const similarName = path.basename(similarModule.file_path, path.extname(similarModule.file_path));
613
+ const targetName = path.basename(targetModule.file_path, path.extname(targetModule.file_path));
614
+ adapted = adapted.replace(new RegExp(similarName, 'gi'), targetName);
615
+ // Add module-specific implementation details
616
+ if (moduleDoc.classes.length > 0) {
617
+ adapted += `\n\n**Implementierung:**\n\n`;
618
+ for (const cls of moduleDoc.classes.slice(0, 3)) {
619
+ adapted += `- **\`${cls.name}\`** (${cls.role || 'other'})`;
620
+ if (cls.methodCount > 0) {
621
+ adapted += ` - ${cls.methodCount} Methoden`;
622
+ }
623
+ adapted += '\n';
624
+ }
625
+ }
626
+ // Add key functions
627
+ if (moduleDoc.keyFunctions.length > 0) {
628
+ adapted += `\n\n**Hauptfunktionen:**\n\n`;
629
+ for (const func of moduleDoc.keyFunctions.slice(0, 5)) {
630
+ adapted += `- \`${func.name}()\``;
631
+ if (func.role) {
632
+ adapted += ` - ${func.role}`;
633
+ }
634
+ adapted += '\n';
635
+ }
636
+ }
637
+ // Add dependency rationale
638
+ if (context.dependencies.length > 0) {
639
+ const coreDeps = context.dependencies.filter((d) => (d.to_module || '').includes('/core/'));
640
+ const apiDeps = context.dependencies.filter((d) => (d.to_module || '').includes('/api/'));
641
+ if (coreDeps.length > 0 || apiDeps.length > 0) {
642
+ adapted += `\n\n**Dependency-Struktur:**\n\n`;
643
+ if (coreDeps.length > 0) {
644
+ adapted += `- Nutzt ${coreDeps.length} Core-Module für Basis-Funktionalität\n`;
645
+ }
646
+ if (apiDeps.length > 0) {
647
+ adapted += `- Nutzt ${apiDeps.length} API-Module für Datenzugriff\n`;
648
+ }
649
+ }
650
+ }
651
+ return adapted;
652
+ }
653
+ /**
654
+ * Adapts decision text from similar ADR to target module.
655
+ */
656
+ adaptDecisionText(similarDecision, targetModule, similarModule) {
657
+ // Replace module paths
658
+ let adapted = similarDecision.replace(new RegExp(similarModule.file_path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), targetModule.file_path);
659
+ // Replace module names
660
+ const similarName = path.basename(similarModule.file_path, path.extname(similarModule.file_path));
661
+ const targetName = path.basename(targetModule.file_path, path.extname(targetModule.file_path));
662
+ adapted = adapted.replace(new RegExp(similarName, 'gi'), targetName);
663
+ return adapted;
664
+ }
665
+ /**
666
+ * Generates decision text from patterns with module-specific details.
667
+ */
668
+ generateDecisionFromPatterns(context, moduleDoc) {
669
+ let decisionText = '';
670
+ if (context.patterns.length > 0) {
671
+ const primaryPattern = context.patterns[0];
672
+ decisionText += `**Pattern:** ${primaryPattern.pattern}\n\n`;
673
+ // Generate decision based on pattern type with module details
674
+ if (primaryPattern.pattern.includes('Repository')) {
675
+ decisionText += 'Repository Pattern wurde gewählt für Datenbank-Zugriff und Abstraktion der Datenpersistenz.\n\n';
676
+ if (moduleDoc.classes.length > 0) {
677
+ decisionText += `**Implementierung:**\n`;
678
+ decisionText += `- \`${moduleDoc.classes[0].name}\` Klasse implementiert Repository-Interface\n`;
679
+ if (moduleDoc.classes[0].methodCount > 0) {
680
+ decisionText += `- ${moduleDoc.classes[0].methodCount} Methoden für CRUD-Operationen\n`;
681
+ }
682
+ }
683
+ }
684
+ else if (primaryPattern.pattern.includes('API')) {
685
+ decisionText += 'API Layer wurde gewählt für öffentliche Schnittstelle und Abstraktion der internen Implementierung.\n\n';
686
+ if (moduleDoc.keyFunctions.length > 0) {
687
+ decisionText += `**Public API:**\n`;
688
+ for (const func of moduleDoc.keyFunctions.slice(0, 5)) {
689
+ decisionText += `- \`${func.name}()\` - ${func.role || 'public method'}\n`;
690
+ }
691
+ }
692
+ }
693
+ else if (primaryPattern.pattern.includes('Service')) {
694
+ decisionText += 'Service Layer wurde gewählt für Geschäftslogik und Orchestrierung.\n\n';
695
+ if (moduleDoc.classes.length > 0) {
696
+ decisionText += `**Service-Klasse:**\n`;
697
+ decisionText += `- \`${moduleDoc.classes[0].name}\` orchestriert Geschäftslogik\n`;
698
+ }
699
+ }
700
+ else if (primaryPattern.pattern.includes('Builder')) {
701
+ decisionText += 'Builder Pattern wurde gewählt für komplexe Objekt-Konstruktion.\n\n';
702
+ if (moduleDoc.classes.length > 0) {
703
+ decisionText += `**Builder-Klasse:**\n`;
704
+ decisionText += `- \`${moduleDoc.classes[0].name}\` ermöglicht schrittweise Objekt-Konstruktion\n`;
705
+ }
706
+ }
707
+ else if (primaryPattern.pattern.includes('UI') || primaryPattern.pattern.includes('View')) {
708
+ decisionText += 'UI-Komponente wurde implementiert für Benutzerinteraktion.\n\n';
709
+ if (moduleDoc.classes.length > 0) {
710
+ decisionText += `**UI-Klasse:**\n`;
711
+ decisionText += `- \`${moduleDoc.classes[0].name}\` stellt UI-Funktionalität bereit\n`;
712
+ if (moduleDoc.classes[0].methodCount > 0) {
713
+ decisionText += `- ${moduleDoc.classes[0].methodCount} Methoden für UI-Operationen\n`;
714
+ }
715
+ }
716
+ }
717
+ else {
718
+ decisionText += `${primaryPattern.pattern} wurde implementiert basierend auf Architektur-Anforderungen.\n\n`;
719
+ if (moduleDoc.purpose) {
720
+ decisionText += `**Zweck:** ${moduleDoc.purpose}\n\n`;
721
+ }
722
+ }
723
+ // Add evidence from pattern
724
+ if (primaryPattern.evidence.length > 0) {
725
+ decisionText += `**Begründung:**\n`;
726
+ for (const evidence of primaryPattern.evidence.slice(0, 3)) {
727
+ decisionText += `- ${evidence}\n`;
728
+ }
729
+ }
730
+ }
731
+ else {
732
+ // Fallback: Generate from module structure
733
+ decisionText += 'Dieses Modul wurde implementiert als Teil der System-Architektur.\n\n';
734
+ if (moduleDoc.purpose) {
735
+ decisionText += `**Zweck:** ${moduleDoc.purpose}\n\n`;
736
+ }
737
+ if (moduleDoc.classes.length > 0) {
738
+ decisionText += `**Implementierung:**\n`;
739
+ for (const cls of moduleDoc.classes.slice(0, 3)) {
740
+ decisionText += `- \`${cls.name}\` Klasse`;
741
+ if (cls.role) {
742
+ decisionText += ` (${cls.role})`;
743
+ }
744
+ if (cls.methodCount > 0) {
745
+ decisionText += ` - ${cls.methodCount} Methoden`;
746
+ }
747
+ decisionText += '\n';
748
+ }
749
+ }
750
+ if (context.dependencies.length > 0) {
751
+ const coreDeps = context.dependencies.filter((d) => (d.to_module || '').includes('/core/'));
752
+ const apiDeps = context.dependencies.filter((d) => (d.to_module || '').includes('/api/'));
753
+ decisionText += `\n**Dependencies:**\n`;
754
+ if (coreDeps.length > 0) {
755
+ decisionText += `- ${coreDeps.length} Core-Module für Basis-Funktionalität\n`;
756
+ }
757
+ if (apiDeps.length > 0) {
758
+ decisionText += `- ${apiDeps.length} API-Module für Datenzugriff\n`;
759
+ }
760
+ if (context.dependencies.length > coreDeps.length + apiDeps.length) {
761
+ decisionText += `- ${context.dependencies.length - coreDeps.length - apiDeps.length} weitere Module\n`;
762
+ }
763
+ }
764
+ if (context.incomingDependencies.length > 0) {
765
+ decisionText += `\n**Nutzung:** Wird von ${context.incomingDependencies.length} anderen Modulen genutzt.\n`;
766
+ }
767
+ }
768
+ return decisionText;
769
+ }
770
+ /**
771
+ * Reconstructs Consequences section from dependencies and context.
772
+ * Uses LLM reasoning if available for trade-offs.
773
+ * Adjusts detail level based on module complexity.
774
+ * For complex modules, uses structured format with ✅ and ⚠️ (similar to ADR-038).
775
+ */
776
+ async reconstructConsequences(context, reasoning = null, complexity = 'complex') {
777
+ // For complex modules, use structured format like ADR-038
778
+ if (complexity === 'complex') {
779
+ let consequencesText = '### Vorteile\n\n';
780
+ // Use LLM reasoning if available
781
+ if (reasoning && reasoning.tradeoffs) {
782
+ const positive = reasoning.tradeoffs.positive || [];
783
+ const negative = reasoning.tradeoffs.negative || [];
784
+ if (positive.length > 0) {
785
+ consequencesText += positive.map((p) => `✅ ${p}`).join('\n');
786
+ }
787
+ else {
788
+ consequencesText += '✅ Keine spezifischen Vorteile identifiziert\n';
789
+ }
790
+ consequencesText += '\n\n### Nachteile\n\n';
791
+ if (negative.length > 0) {
792
+ consequencesText += negative.map((n) => `⚠️ ${n}`).join('\n');
793
+ }
794
+ else {
795
+ consequencesText += '⚠️ Keine spezifischen Nachteile identifiziert\n';
796
+ }
797
+ // Don't add technical details here - they're in the Implementation section
798
+ return consequencesText;
799
+ }
800
+ }
801
+ // For simple/medium modules, use simpler format
802
+ let consequencesText = '### Positive\n\n';
803
+ // Use LLM reasoning if available
804
+ if (reasoning && reasoning.tradeoffs) {
805
+ const positive = reasoning.tradeoffs.positive || [];
806
+ const negative = reasoning.tradeoffs.negative || [];
807
+ // Limit based on complexity
808
+ const maxPositive = complexity === 'simple' ? 2 : complexity === 'medium' ? 3 : positive.length;
809
+ const maxNegative = complexity === 'simple' ? 1 : complexity === 'medium' ? 2 : negative.length;
810
+ if (positive.length > 0) {
811
+ consequencesText += positive.slice(0, maxPositive).map((p) => `- ${p}`).join('\n');
812
+ }
813
+ else {
814
+ consequencesText += '- Keine spezifischen Vorteile identifiziert\n';
815
+ }
816
+ consequencesText += '\n\n### Negative\n\n';
817
+ if (negative.length > 0) {
818
+ consequencesText += negative.slice(0, maxNegative).map((n) => `- ${n}`).join('\n');
819
+ }
820
+ else {
821
+ if (complexity !== 'simple') {
822
+ consequencesText += '- Keine spezifischen Nachteile identifiziert\n';
823
+ }
824
+ }
825
+ return consequencesText;
826
+ }
827
+ // Fallback to deterministic method
828
+ const moduleDoc = this.parseModuleDocumentation(context.module.content_markdown);
829
+ const positive = [];
830
+ const negative = [];
831
+ // Positive: Based on incoming dependencies (who uses this module?) - skip for simple modules
832
+ if (context.incomingDependencies.length > 0 && complexity !== 'simple') {
833
+ const uniqueUsers = new Set(context.incomingDependencies.map((d) => d.from_module || d.from));
834
+ positive.push(`Wird von ${uniqueUsers.size} Modul(en) genutzt - zentrale Funktionalität`);
835
+ // Show which modules use it (only for medium/complex)
836
+ if (uniqueUsers.size <= 5) {
837
+ positive.push(` - Genutzt von: ${Array.from(uniqueUsers).map(u => `\`${u}\``).join(', ')}`);
838
+ }
839
+ }
840
+ // Positive: Based on module structure
841
+ if (moduleDoc.classes.length > 0) {
842
+ const publicClasses = moduleDoc.classes.filter(c => c.role === 'service-api' || c.role === 'public');
843
+ if (publicClasses.length > 0) {
844
+ positive.push(`Klare öffentliche API durch ${publicClasses.length} öffentliche Klasse(n)`);
845
+ }
846
+ }
847
+ if (moduleDoc.keyFunctions.length > 0) {
848
+ const publicFunctions = moduleDoc.keyFunctions.filter(f => f.role === 'service-api' || f.role === 'public');
849
+ if (publicFunctions.length > 0) {
850
+ positive.push(`${publicFunctions.length} öffentliche Funktion(en) für externe Nutzung`);
851
+ }
852
+ }
853
+ // Positive: Based on patterns
854
+ if (context.patterns.length > 0) {
855
+ const primaryPattern = context.patterns[0];
856
+ if (primaryPattern.pattern.includes('Repository')) {
857
+ positive.push('Abstraktion der Datenpersistenz');
858
+ positive.push('Testbarkeit durch Mocking');
859
+ positive.push('Einheitliche Datenzugriffs-Schnittstelle');
860
+ }
861
+ else if (primaryPattern.pattern.includes('API')) {
862
+ positive.push('Klare öffentliche Schnittstelle');
863
+ positive.push('Abstraktion der internen Implementierung');
864
+ positive.push('Stabile API für externe Nutzer');
865
+ }
866
+ else if (primaryPattern.pattern.includes('Service')) {
867
+ positive.push('Zentrale Geschäftslogik');
868
+ positive.push('Wiederverwendbarkeit');
869
+ positive.push('Orchestrierung komplexer Operationen');
870
+ }
871
+ else if (primaryPattern.pattern.includes('UI') || primaryPattern.pattern.includes('View')) {
872
+ positive.push('Benutzerfreundliche Oberfläche');
873
+ positive.push('Zentrale UI-Logik');
874
+ }
875
+ }
876
+ // Positive: Based on symbol count and structure
877
+ if (context.symbols.length > 0) {
878
+ const methodCount = context.symbols.filter((s) => (s.kind || '').includes('method') || (s.kind || '').includes('function')).length;
879
+ if (methodCount > 10) {
880
+ positive.push(`Umfangreiche Funktionalität (${methodCount} Methoden/Funktionen)`);
881
+ }
882
+ }
883
+ // Negative: Based on dependencies count and structure
884
+ if (context.dependencies.length > 10) {
885
+ negative.push(`Viele Dependencies (${context.dependencies.length}) - erhöhte Komplexität und Kopplung`);
886
+ // Analyze dependency depth
887
+ const coreDeps = context.dependencies.filter((d) => (d.to_module || '').includes('/core/'));
888
+ const apiDeps = context.dependencies.filter((d) => (d.to_module || '').includes('/api/'));
889
+ const serviceDeps = context.dependencies.filter((d) => (d.to_module || '').includes('/services/'));
890
+ if (coreDeps.length + apiDeps.length + serviceDeps.length > 10) {
891
+ negative.push(`Tiefe Dependency-Kette (${coreDeps.length} Core, ${apiDeps.length} API, ${serviceDeps.length} Service)`);
892
+ }
893
+ }
894
+ else if (context.dependencies.length > 5) {
895
+ negative.push(`Mehrere Dependencies (${context.dependencies.length}) - mittlere Komplexität`);
896
+ }
897
+ // Negative: Based on patterns
898
+ if (context.patterns.length > 0) {
899
+ const primaryPattern = context.patterns[0];
900
+ if (primaryPattern.pattern.includes('Repository') || primaryPattern.pattern.includes('API')) {
901
+ negative.push('Zusätzliche Abstraktionsschicht - Overhead bei einfachen Operationen');
902
+ }
903
+ if (primaryPattern.pattern.includes('UI') || primaryPattern.pattern.includes('View')) {
904
+ negative.push('UI-Komponente - Abhängigkeit von VS Code APIs');
905
+ }
906
+ }
907
+ // Negative: Based on module size
908
+ if (context.symbols.length > 50) {
909
+ negative.push(`Große Modul-Größe (${context.symbols.length} Symbole) - mögliche Wartungsprobleme`);
910
+ }
911
+ // Negative: Based on incoming dependencies (high coupling)
912
+ if (context.incomingDependencies.length > 10) {
913
+ negative.push(`Hohe Kopplung - ${context.incomingDependencies.length} Module abhängig - Änderungen haben große Auswirkungen`);
914
+ }
915
+ if (positive.length === 0) {
916
+ positive.push('Modulare Architektur');
917
+ if (moduleDoc.classes.length > 0 && complexity !== 'simple') {
918
+ positive.push(`Strukturierte Implementierung (${moduleDoc.classes.length} Klasse(n))`);
919
+ }
920
+ }
921
+ if (negative.length === 0 && complexity !== 'simple') {
922
+ negative.push('Keine bekannten signifikanten Nachteile');
923
+ }
924
+ // Limit based on complexity
925
+ const maxPositive = complexity === 'simple' ? 2 : complexity === 'medium' ? 3 : positive.length;
926
+ const maxNegative = complexity === 'simple' ? 1 : complexity === 'medium' ? 2 : negative.length;
927
+ consequencesText += positive.slice(0, maxPositive).map(p => `- ${p}`).join('\n');
928
+ consequencesText += '\n\n### Negative\n\n';
929
+ if (maxNegative > 0) {
930
+ consequencesText += negative.slice(0, maxNegative).map(n => `- ${n}`).join('\n');
931
+ }
932
+ else {
933
+ consequencesText += '- Keine spezifischen Nachteile\n';
934
+ }
935
+ return consequencesText;
936
+ }
937
+ /**
938
+ * Reconstructs Verweise section from dependencies and similar ADRs.
939
+ */
940
+ reconstructVerweise(context, similarAdrs) {
941
+ const verweise = [];
942
+ // Add similar ADRs
943
+ if (similarAdrs.length > 0) {
944
+ verweise.push('**Ähnliche ADRs:**');
945
+ for (const similar of similarAdrs.slice(0, 3)) {
946
+ verweise.push(`- ADR-${similar.adr.adr_number}: ${similar.adr.title} (\`${similar.module.file_path}\`)`);
947
+ }
948
+ }
949
+ // Add dependencies
950
+ if (context.dependencies.length > 0) {
951
+ verweise.push('\n**Verwandte Module (Dependencies):**');
952
+ const uniqueDeps = new Set(context.dependencies.map((d) => d.to_module || d.to));
953
+ for (const dep of Array.from(uniqueDeps).slice(0, 10)) {
954
+ verweise.push(`- \`${dep}\``);
955
+ }
956
+ }
957
+ // Add incoming dependencies
958
+ if (context.incomingDependencies.length > 0) {
959
+ verweise.push('\n**Nutzer dieses Moduls:**');
960
+ const uniqueUsers = new Set(context.incomingDependencies.map((d) => d.from_module || d.from));
961
+ for (const user of Array.from(uniqueUsers).slice(0, 5)) {
962
+ verweise.push(`- \`${user}\``);
963
+ }
964
+ }
965
+ return verweise.length > 0 ? verweise.join('\n') : 'Keine Verweise verfügbar.';
966
+ }
967
+ /**
968
+ * Reconstructs Implementation section for complex modules (similar to ADR-038).
969
+ * Includes technical details, affected components, and implementation notes.
970
+ */
971
+ reconstructImplementation(context, reasoning = null) {
972
+ let implementationText = '## Implementierung\n\n';
973
+ // Affected components
974
+ const affectedComponents = [];
975
+ affectedComponents.push(context.module.file_path);
976
+ // Add ALL dependencies as affected components (no limit for complex modules)
977
+ for (const dep of context.dependencies) {
978
+ const depPath = typeof dep === 'string' ? dep : (dep.to_module || dep.to);
979
+ if (depPath && !affectedComponents.includes(depPath)) {
980
+ affectedComponents.push(depPath);
981
+ }
982
+ }
983
+ if (affectedComponents.length > 0) {
984
+ implementationText += '**Betroffene Komponenten:**\n';
985
+ for (const component of affectedComponents) {
986
+ implementationText += `- \`${component}\`\n`;
987
+ }
988
+ implementationText += '\n';
989
+ }
990
+ // Technical details
991
+ implementationText += '**Technische Details:**\n\n';
992
+ implementationText += `**Implementierung:**\n`;
993
+ // Detect VS Code Runtime as incoming dependency if 'vscode' is imported
994
+ let incomingDepsCount = context.incomingDependencies.length;
995
+ const hasVscodeImport = context.dependencies.some((dep) => {
996
+ const depPath = typeof dep === 'string' ? dep : (dep.to_module || dep.to);
997
+ return depPath === 'vscode' || depPath?.includes('vscode');
998
+ });
999
+ if (hasVscodeImport && incomingDepsCount === 0) {
1000
+ incomingDepsCount = 1; // VS Code Runtime
1001
+ }
1002
+ // Extract symbol count from module documentation if not in DB
1003
+ let symbolCount = context.symbols.length;
1004
+ if (symbolCount === 0) {
1005
+ // Try to extract from module documentation
1006
+ const moduleDoc = this.parseModuleDocumentation(context.module.content_markdown);
1007
+ symbolCount = moduleDoc.classes.length + moduleDoc.keyFunctions.length;
1008
+ }
1009
+ // Add implementation details
1010
+ implementationText += `- Modul: \`${context.module.file_path}\`\n`;
1011
+ implementationText += `- Dependencies: ${context.dependencies.length} (ausgehend)\n`;
1012
+ if (hasVscodeImport && context.incomingDependencies.length === 0) {
1013
+ implementationText += `- Incoming Dependencies: ${incomingDepsCount} (VS Code Runtime)\n`;
1014
+ }
1015
+ else {
1016
+ implementationText += `- Incoming Dependencies: ${incomingDepsCount}\n`;
1017
+ }
1018
+ implementationText += `- Symbole: ${symbolCount}\n`;
1019
+ // Add pattern information
1020
+ if (context.patterns.length > 0) {
1021
+ const primaryPattern = context.patterns[0];
1022
+ implementationText += `- Pattern: ${primaryPattern.pattern} (${primaryPattern.confidence} confidence)\n`;
1023
+ }
1024
+ // Add architectural characteristics
1025
+ if (context.dependencies.length >= 20) {
1026
+ implementationText += `- God Object Pattern: ${context.dependencies.length} Dependencies (höchster Wert im System)\n`;
1027
+ }
1028
+ if (symbolCount >= 20) {
1029
+ implementationText += `- Hohe Symbol-Dichte: ${symbolCount} Symbole in einem Modul\n`;
1030
+ }
1031
+ if (incomingDepsCount >= 10) {
1032
+ implementationText += `- Single Point of Failure: ${incomingDepsCount} Module abhängig\n`;
1033
+ }
1034
+ implementationText += '\n';
1035
+ // Add architectural notes if available from reasoning
1036
+ if (reasoning && reasoning.decision) {
1037
+ implementationText += '**Architektur-Notizen:**\n';
1038
+ implementationText += `- ${reasoning.decision.split('\n')[0]}\n\n`;
1039
+ }
1040
+ return implementationText;
1041
+ }
1042
+ /**
1043
+ * Extracts title from module or similar ADRs.
1044
+ */
1045
+ extractTitle(module, similarAdrs) {
1046
+ // Try to extract from module markdown
1047
+ const moduleLines = module.content_markdown?.split('\n') || [];
1048
+ for (const line of moduleLines) {
1049
+ if (line.startsWith('# Modul:')) {
1050
+ const title = line.replace('# Modul:', '').trim();
1051
+ if (title) {
1052
+ return title;
1053
+ }
1054
+ }
1055
+ }
1056
+ // Use similar ADR title as template
1057
+ if (similarAdrs.length > 0) {
1058
+ const similarTitle = similarAdrs[0].adr.title;
1059
+ const similarModule = similarAdrs[0].module;
1060
+ const targetModuleName = path.basename(module.file_path, path.extname(module.file_path));
1061
+ const similarModuleName = path.basename(similarModule.file_path, path.extname(similarModule.file_path));
1062
+ // Replace module name in title
1063
+ return similarTitle.replace(similarModuleName, targetModuleName);
1064
+ }
1065
+ // Fallback: Generate from file path
1066
+ const moduleName = path.basename(module.file_path, path.extname(module.file_path));
1067
+ const dirName = path.dirname(module.file_path).split('/').pop() || '';
1068
+ return `${dirName ? dirName + ' - ' : ''}${moduleName}`;
1069
+ }
1070
+ /**
1071
+ * Finds the next available ADR number.
1072
+ */
1073
+ async findNextAdrNumber(pluginId) {
1074
+ const adrDir = path.join(this.workspaceRoot, 'docs', 'adr');
1075
+ if (!fs.existsSync(adrDir)) {
1076
+ return 1;
1077
+ }
1078
+ const files = fs.readdirSync(adrDir)
1079
+ .filter(file => file.endsWith('.md') && file.match(/^\d{3}-/))
1080
+ .map(file => {
1081
+ const match = file.match(/^(\d{3})-/);
1082
+ return match ? parseInt(match[1], 10) : 0;
1083
+ })
1084
+ .filter(num => num > 0);
1085
+ if (files.length === 0) {
1086
+ return 1;
1087
+ }
1088
+ return Math.max(...files) + 1;
1089
+ }
1090
+ /**
1091
+ * Formats ADR number with leading zeros.
1092
+ */
1093
+ formatAdrNumber(num) {
1094
+ return num.toString().padStart(3, '0');
1095
+ }
1096
+ /**
1097
+ * Generates a URL-friendly slug from file path.
1098
+ */
1099
+ generateSlug(filePath) {
1100
+ // Remove extension and path separators
1101
+ const baseName = path.basename(filePath, path.extname(filePath));
1102
+ const dirName = path.dirname(filePath).replace(/[\/\\]/g, '-');
1103
+ // Combine and sanitize
1104
+ const slug = dirName ? `${dirName}-${baseName}` : baseName;
1105
+ return slug
1106
+ .toLowerCase()
1107
+ .replace(/[^a-z0-9-]/g, '-')
1108
+ .replace(/-+/g, '-')
1109
+ .replace(/^-|-$/g, '');
1110
+ }
1111
+ }
1112
+ exports.AdrGeneratorTool = AdrGeneratorTool;
1113
+ //# sourceMappingURL=adr-generator.js.map