neuronlayer 0.1.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 (78) hide show
  1. package/CONTRIBUTING.md +127 -0
  2. package/LICENSE +21 -0
  3. package/README.md +305 -0
  4. package/dist/index.js +38016 -0
  5. package/esbuild.config.js +26 -0
  6. package/package.json +63 -0
  7. package/src/cli/commands.ts +382 -0
  8. package/src/core/adr-exporter.ts +253 -0
  9. package/src/core/architecture/architecture-enforcement.ts +228 -0
  10. package/src/core/architecture/duplicate-detector.ts +288 -0
  11. package/src/core/architecture/index.ts +6 -0
  12. package/src/core/architecture/pattern-learner.ts +306 -0
  13. package/src/core/architecture/pattern-library.ts +403 -0
  14. package/src/core/architecture/pattern-validator.ts +324 -0
  15. package/src/core/change-intelligence/bug-correlator.ts +444 -0
  16. package/src/core/change-intelligence/change-intelligence.ts +221 -0
  17. package/src/core/change-intelligence/change-tracker.ts +334 -0
  18. package/src/core/change-intelligence/fix-suggester.ts +340 -0
  19. package/src/core/change-intelligence/index.ts +5 -0
  20. package/src/core/code-verifier.ts +843 -0
  21. package/src/core/confidence/confidence-scorer.ts +251 -0
  22. package/src/core/confidence/conflict-checker.ts +289 -0
  23. package/src/core/confidence/index.ts +5 -0
  24. package/src/core/confidence/source-tracker.ts +263 -0
  25. package/src/core/confidence/warning-detector.ts +241 -0
  26. package/src/core/context-rot/compaction.ts +284 -0
  27. package/src/core/context-rot/context-health.ts +243 -0
  28. package/src/core/context-rot/context-rot-prevention.ts +213 -0
  29. package/src/core/context-rot/critical-context.ts +221 -0
  30. package/src/core/context-rot/drift-detector.ts +255 -0
  31. package/src/core/context-rot/index.ts +7 -0
  32. package/src/core/context.ts +263 -0
  33. package/src/core/decision-extractor.ts +339 -0
  34. package/src/core/decisions.ts +69 -0
  35. package/src/core/deja-vu.ts +421 -0
  36. package/src/core/engine.ts +1455 -0
  37. package/src/core/feature-context.ts +726 -0
  38. package/src/core/ghost-mode.ts +412 -0
  39. package/src/core/learning.ts +485 -0
  40. package/src/core/living-docs/activity-tracker.ts +296 -0
  41. package/src/core/living-docs/architecture-generator.ts +428 -0
  42. package/src/core/living-docs/changelog-generator.ts +348 -0
  43. package/src/core/living-docs/component-generator.ts +230 -0
  44. package/src/core/living-docs/doc-engine.ts +110 -0
  45. package/src/core/living-docs/doc-validator.ts +282 -0
  46. package/src/core/living-docs/index.ts +8 -0
  47. package/src/core/project-manager.ts +297 -0
  48. package/src/core/summarizer.ts +267 -0
  49. package/src/core/test-awareness/change-validator.ts +499 -0
  50. package/src/core/test-awareness/index.ts +5 -0
  51. package/src/index.ts +49 -0
  52. package/src/indexing/ast.ts +563 -0
  53. package/src/indexing/embeddings.ts +85 -0
  54. package/src/indexing/indexer.ts +245 -0
  55. package/src/indexing/watcher.ts +78 -0
  56. package/src/server/gateways/aggregator.ts +374 -0
  57. package/src/server/gateways/index.ts +473 -0
  58. package/src/server/gateways/memory-ghost.ts +343 -0
  59. package/src/server/gateways/memory-query.ts +452 -0
  60. package/src/server/gateways/memory-record.ts +346 -0
  61. package/src/server/gateways/memory-review.ts +410 -0
  62. package/src/server/gateways/memory-status.ts +517 -0
  63. package/src/server/gateways/memory-verify.ts +392 -0
  64. package/src/server/gateways/router.ts +434 -0
  65. package/src/server/gateways/types.ts +610 -0
  66. package/src/server/mcp.ts +154 -0
  67. package/src/server/resources.ts +85 -0
  68. package/src/server/tools.ts +2261 -0
  69. package/src/storage/database.ts +262 -0
  70. package/src/storage/tier1.ts +135 -0
  71. package/src/storage/tier2.ts +764 -0
  72. package/src/storage/tier3.ts +123 -0
  73. package/src/types/documentation.ts +619 -0
  74. package/src/types/index.ts +222 -0
  75. package/src/utils/config.ts +193 -0
  76. package/src/utils/files.ts +117 -0
  77. package/src/utils/time.ts +37 -0
  78. package/src/utils/tokens.ts +52 -0
@@ -0,0 +1,288 @@
1
+ import type { Tier2Storage } from '../../storage/tier2.js';
2
+ import type { EmbeddingGenerator } from '../../indexing/embeddings.js';
3
+ import type { ExistingFunction, FunctionIndex } from '../../types/documentation.js';
4
+
5
+ // Common function purpose keywords
6
+ const PURPOSE_KEYWORDS: Record<string, string[]> = {
7
+ authentication: ['auth', 'login', 'logout', 'token', 'session', 'jwt', 'credential'],
8
+ validation: ['validate', 'check', 'verify', 'assert', 'ensure', 'is', 'has'],
9
+ formatting: ['format', 'parse', 'stringify', 'serialize', 'convert', 'transform'],
10
+ fetching: ['fetch', 'get', 'load', 'retrieve', 'request', 'api', 'http'],
11
+ storage: ['save', 'store', 'persist', 'cache', 'set', 'put'],
12
+ utility: ['util', 'helper', 'tool', 'common', 'shared'],
13
+ error: ['error', 'exception', 'throw', 'catch', 'handle'],
14
+ logging: ['log', 'logger', 'debug', 'info', 'warn', 'error'],
15
+ date: ['date', 'time', 'moment', 'day', 'month', 'year', 'timestamp']
16
+ };
17
+
18
+ export class DuplicateDetector {
19
+ private tier2: Tier2Storage;
20
+ private embeddingGenerator: EmbeddingGenerator;
21
+ private functionIndex: Map<string, FunctionIndex> = new Map();
22
+
23
+ constructor(tier2: Tier2Storage, embeddingGenerator: EmbeddingGenerator) {
24
+ this.tier2 = tier2;
25
+ this.embeddingGenerator = embeddingGenerator;
26
+ this.buildFunctionIndex();
27
+ }
28
+
29
+ // Build index of all functions in codebase
30
+ private buildFunctionIndex(): void {
31
+ const files = this.tier2.getAllFiles();
32
+
33
+ for (const file of files) {
34
+ const symbols = this.tier2.getSymbolsByFile(file.id);
35
+
36
+ for (const symbol of symbols) {
37
+ if (symbol.kind === 'function' || symbol.kind === 'method') {
38
+ const key = `${file.path}:${symbol.name}`;
39
+ const dependents = this.tier2.getFileDependents(file.path);
40
+
41
+ this.functionIndex.set(key, {
42
+ name: symbol.name,
43
+ file: file.path,
44
+ line: symbol.lineStart,
45
+ signature: symbol.signature || `${symbol.name}()`,
46
+ exported: symbol.exported,
47
+ usageCount: dependents.length + 1,
48
+ parameters: this.extractParameters(symbol.signature || ''),
49
+ returnType: this.extractReturnType(symbol.signature || ''),
50
+ docstring: symbol.docstring
51
+ });
52
+ }
53
+ }
54
+ }
55
+ }
56
+
57
+ // Find duplicate or similar functions
58
+ findDuplicates(code: string, threshold: number = 60): Array<FunctionIndex & { similarity: number }> {
59
+ const duplicates: Array<FunctionIndex & { similarity: number }> = [];
60
+
61
+ // Extract function name from code
62
+ const funcNameMatch = code.match(/(?:function|const|let|var)\s+(\w+)/);
63
+ const funcName = funcNameMatch ? funcNameMatch[1] : null;
64
+
65
+ // Extract purpose from code
66
+ const purpose = this.detectPurpose(code);
67
+
68
+ for (const [_key, func] of this.functionIndex) {
69
+ let similarity = 0;
70
+
71
+ // Check name similarity
72
+ if (funcName) {
73
+ const nameSimilarity = this.calculateNameSimilarity(funcName, func.name);
74
+ similarity += nameSimilarity * 0.3;
75
+ }
76
+
77
+ // Check purpose similarity
78
+ const funcPurpose = this.detectPurpose(func.signature + ' ' + (func.docstring || ''));
79
+ if (purpose && funcPurpose && purpose === funcPurpose) {
80
+ similarity += 40;
81
+ }
82
+
83
+ // Check code structure similarity
84
+ const structureSimilarity = this.calculateStructureSimilarity(code, func.signature);
85
+ similarity += structureSimilarity * 0.3;
86
+
87
+ if (similarity >= threshold) {
88
+ duplicates.push({
89
+ ...func,
90
+ similarity: Math.round(similarity)
91
+ });
92
+ }
93
+ }
94
+
95
+ // Sort by similarity
96
+ duplicates.sort((a, b) => b.similarity - a.similarity);
97
+
98
+ return duplicates.slice(0, 5);
99
+ }
100
+
101
+ // Suggest existing functions based on intent
102
+ suggestExisting(intent: string, limit: number = 5): ExistingFunction[] {
103
+ const suggestions: Array<FunctionIndex & { relevance: number }> = [];
104
+ const intentLower = intent.toLowerCase();
105
+ const intentWords = intentLower.split(/\s+/);
106
+
107
+ for (const [_key, func] of this.functionIndex) {
108
+ if (!func.exported) continue;
109
+
110
+ let relevance = 0;
111
+ const funcLower = func.name.toLowerCase();
112
+ const docLower = (func.docstring || '').toLowerCase();
113
+ const combined = `${funcLower} ${docLower}`;
114
+
115
+ // Check direct name match
116
+ for (const word of intentWords) {
117
+ if (word.length < 3) continue;
118
+
119
+ if (funcLower.includes(word)) {
120
+ relevance += 30;
121
+ }
122
+ if (docLower.includes(word)) {
123
+ relevance += 20;
124
+ }
125
+ }
126
+
127
+ // Check purpose match
128
+ const purpose = this.detectPurpose(intent);
129
+ const funcPurpose = this.detectPurpose(combined);
130
+ if (purpose && funcPurpose && purpose === funcPurpose) {
131
+ relevance += 25;
132
+ }
133
+
134
+ // Boost for commonly used functions
135
+ relevance += Math.min(func.usageCount * 2, 15);
136
+
137
+ if (relevance > 20) {
138
+ suggestions.push({ ...func, relevance });
139
+ }
140
+ }
141
+
142
+ // Sort by relevance
143
+ suggestions.sort((a, b) => b.relevance - a.relevance);
144
+
145
+ return suggestions.slice(0, limit).map(s => ({
146
+ name: s.name,
147
+ file: s.file,
148
+ line: s.line,
149
+ signature: s.signature,
150
+ description: s.docstring,
151
+ usageCount: s.usageCount,
152
+ purpose: this.detectPurpose(s.name + ' ' + (s.docstring || '')) || 'utility',
153
+ similarity: s.relevance
154
+ }));
155
+ }
156
+
157
+ // Detect the purpose of code/function
158
+ private detectPurpose(text: string): string | null {
159
+ const lower = text.toLowerCase();
160
+
161
+ for (const [purpose, keywords] of Object.entries(PURPOSE_KEYWORDS)) {
162
+ if (keywords.some(k => lower.includes(k))) {
163
+ return purpose;
164
+ }
165
+ }
166
+
167
+ return null;
168
+ }
169
+
170
+ // Calculate name similarity
171
+ private calculateNameSimilarity(name1: string, name2: string): number {
172
+ const lower1 = name1.toLowerCase();
173
+ const lower2 = name2.toLowerCase();
174
+
175
+ // Exact match
176
+ if (lower1 === lower2) return 100;
177
+
178
+ // Contains
179
+ if (lower1.includes(lower2) || lower2.includes(lower1)) return 70;
180
+
181
+ // Token overlap
182
+ const tokens1 = this.camelCaseToTokens(name1);
183
+ const tokens2 = this.camelCaseToTokens(name2);
184
+
185
+ let matches = 0;
186
+ for (const t1 of tokens1) {
187
+ for (const t2 of tokens2) {
188
+ if (t1 === t2) matches++;
189
+ }
190
+ }
191
+
192
+ const totalTokens = Math.max(tokens1.length, tokens2.length);
193
+ return totalTokens > 0 ? (matches / totalTokens) * 100 : 0;
194
+ }
195
+
196
+ // Calculate structure similarity
197
+ private calculateStructureSimilarity(code1: string, code2: string): number {
198
+ // Extract structural elements
199
+ const struct1 = this.extractStructure(code1);
200
+ const struct2 = this.extractStructure(code2);
201
+
202
+ let matches = 0;
203
+ let total = 0;
204
+
205
+ for (const key of Object.keys(struct1) as Array<keyof typeof struct1>) {
206
+ total++;
207
+ if (struct1[key] === struct2[key]) matches++;
208
+ }
209
+
210
+ return total > 0 ? (matches / total) * 100 : 0;
211
+ }
212
+
213
+ // Extract structural features
214
+ private extractStructure(code: string): {
215
+ hasAsync: boolean;
216
+ hasReturn: boolean;
217
+ hasTryCatch: boolean;
218
+ hasLoop: boolean;
219
+ hasConditional: boolean;
220
+ paramCount: number;
221
+ } {
222
+ return {
223
+ hasAsync: /async\s+/.test(code),
224
+ hasReturn: /return\s+/.test(code),
225
+ hasTryCatch: /try\s*\{/.test(code),
226
+ hasLoop: /(?:for|while|do)\s*[\(\{]/.test(code),
227
+ hasConditional: /if\s*\(/.test(code),
228
+ paramCount: (code.match(/\([^)]*\)/)?.[0]?.split(',').length || 0)
229
+ };
230
+ }
231
+
232
+ // Split camelCase to tokens
233
+ private camelCaseToTokens(name: string): string[] {
234
+ return name
235
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
236
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
237
+ .toLowerCase()
238
+ .split(/\s+/)
239
+ .filter(t => t.length > 1);
240
+ }
241
+
242
+ // Extract parameters from signature
243
+ private extractParameters(signature: string): string[] {
244
+ const match = signature.match(/\(([^)]*)\)/);
245
+ if (!match) return [];
246
+
247
+ return match[1]
248
+ .split(',')
249
+ .map(p => p.trim())
250
+ .filter(Boolean)
251
+ .map(p => p.split(':')[0].trim());
252
+ }
253
+
254
+ // Extract return type from signature
255
+ private extractReturnType(signature: string): string | undefined {
256
+ const match = signature.match(/\):\s*(.+)$/);
257
+ return match ? match[1].trim() : undefined;
258
+ }
259
+
260
+ // Refresh the function index
261
+ refresh(): void {
262
+ this.functionIndex.clear();
263
+ this.buildFunctionIndex();
264
+ }
265
+
266
+ // Get index statistics
267
+ getStats(): {
268
+ totalFunctions: number;
269
+ exportedFunctions: number;
270
+ byPurpose: Record<string, number>;
271
+ } {
272
+ let exportedFunctions = 0;
273
+ const byPurpose: Record<string, number> = {};
274
+
275
+ for (const [_key, func] of this.functionIndex) {
276
+ if (func.exported) exportedFunctions++;
277
+
278
+ const purpose = this.detectPurpose(func.name) || 'other';
279
+ byPurpose[purpose] = (byPurpose[purpose] || 0) + 1;
280
+ }
281
+
282
+ return {
283
+ totalFunctions: this.functionIndex.size,
284
+ exportedFunctions,
285
+ byPurpose
286
+ };
287
+ }
288
+ }
@@ -0,0 +1,6 @@
1
+ // Architecture Enforcement Module - Phase 10
2
+ export { ArchitectureEnforcement } from './architecture-enforcement.js';
3
+ export { PatternLibrary } from './pattern-library.js';
4
+ export { PatternLearner } from './pattern-learner.js';
5
+ export { PatternValidator } from './pattern-validator.js';
6
+ export { DuplicateDetector } from './duplicate-detector.js';
@@ -0,0 +1,306 @@
1
+ import type { Tier2Storage } from '../../storage/tier2.js';
2
+ import type { PatternCategory, CodeExample, PatternRule } from '../../types/documentation.js';
3
+ import type { PatternLibrary } from './pattern-library.js';
4
+
5
+ // Pattern detection rules
6
+ const PATTERN_DETECTORS: Array<{
7
+ category: PatternCategory;
8
+ name: string;
9
+ detect: RegExp;
10
+ extractExample: (code: string) => string | null;
11
+ rules: PatternRule[];
12
+ }> = [
13
+ {
14
+ category: 'error_handling',
15
+ name: 'Try-Catch Pattern',
16
+ detect: /try\s*\{[\s\S]*?\}\s*catch\s*\(/,
17
+ extractExample: (code: string) => {
18
+ const match = code.match(/try\s*\{[\s\S]*?\}\s*catch\s*\([^)]*\)\s*\{[\s\S]*?\}/);
19
+ return match ? match[0] : null;
20
+ },
21
+ rules: [
22
+ { rule: 'Use try-catch for error-prone operations', severity: 'warning' },
23
+ { rule: 'Log errors with context', severity: 'warning' }
24
+ ]
25
+ },
26
+ {
27
+ category: 'api_call',
28
+ name: 'Fetch API Pattern',
29
+ detect: /fetch\s*\(|axios\.|api\./,
30
+ extractExample: (code: string) => {
31
+ const match = code.match(/(?:await\s+)?(?:fetch|axios\.\w+|api\.\w+)\s*\([^)]*\)[\s\S]*?(?:\.json\(\)|;)/);
32
+ return match ? match[0] : null;
33
+ },
34
+ rules: [
35
+ { rule: 'Check response status', severity: 'critical' },
36
+ { rule: 'Handle network errors', severity: 'critical' }
37
+ ]
38
+ },
39
+ {
40
+ category: 'component',
41
+ name: 'React Component Pattern',
42
+ detect: /(?:function|const)\s+\w+\s*(?::\s*React\.FC|\([^)]*\))\s*(?:=>|{)/,
43
+ extractExample: (code: string) => {
44
+ const match = code.match(/(?:interface|type)\s+\w*Props[\s\S]*?}[\s\S]*?(?:function|const)\s+\w+[\s\S]*?(?:return\s*\([\s\S]*?\);|\))/);
45
+ return match ? match[0] : null;
46
+ },
47
+ rules: [
48
+ { rule: 'Define Props interface', severity: 'warning' },
49
+ { rule: 'Use functional components', severity: 'info' }
50
+ ]
51
+ },
52
+ {
53
+ category: 'validation',
54
+ name: 'Input Validation Pattern',
55
+ detect: /if\s*\(\s*!?\w+\s*(?:===?|!==?)\s*(?:null|undefined|''|""|0)\s*\)/,
56
+ extractExample: (code: string) => {
57
+ const match = code.match(/if\s*\([^)]*(?:null|undefined)[^)]*\)\s*\{[^}]*\}/);
58
+ return match ? match[0] : null;
59
+ },
60
+ rules: [
61
+ { rule: 'Validate inputs before use', severity: 'warning' },
62
+ { rule: 'Use optional chaining for nested access', severity: 'info' }
63
+ ]
64
+ },
65
+ {
66
+ category: 'data_fetching',
67
+ name: 'Async/Await Pattern',
68
+ detect: /async\s+(?:function|\([^)]*\)\s*=>)/,
69
+ extractExample: (code: string) => {
70
+ const match = code.match(/async\s+(?:function\s+\w+)?\s*\([^)]*\)\s*(?::\s*Promise<[^>]+>)?\s*\{[\s\S]*?await[\s\S]*?\}/);
71
+ return match ? match[0] : null;
72
+ },
73
+ rules: [
74
+ { rule: 'Use async/await for asynchronous code', severity: 'info' },
75
+ { rule: 'Always await promises', severity: 'warning' }
76
+ ]
77
+ },
78
+ {
79
+ category: 'logging',
80
+ name: 'Logging Pattern',
81
+ detect: /console\.(log|error|warn|info)|logger\./,
82
+ extractExample: (code: string) => {
83
+ const match = code.match(/(?:console|logger)\.\w+\s*\([^)]*\)/);
84
+ return match ? match[0] : null;
85
+ },
86
+ rules: [
87
+ { rule: 'Use structured logging', severity: 'info' },
88
+ { rule: 'Include context in log messages', severity: 'info' }
89
+ ]
90
+ }
91
+ ];
92
+
93
+ export class PatternLearner {
94
+ private tier2: Tier2Storage;
95
+ private patternLibrary: PatternLibrary;
96
+
97
+ constructor(tier2: Tier2Storage, patternLibrary: PatternLibrary) {
98
+ this.tier2 = tier2;
99
+ this.patternLibrary = patternLibrary;
100
+ }
101
+
102
+ // Learn patterns from the entire codebase
103
+ learnFromCodebase(): {
104
+ patternsLearned: number;
105
+ examplesAdded: number;
106
+ categories: Record<string, number>;
107
+ } {
108
+ const files = this.tier2.getAllFiles();
109
+ const categories: Record<string, number> = {};
110
+ let patternsLearned = 0;
111
+ let examplesAdded = 0;
112
+
113
+ for (const file of files) {
114
+ if (!file.preview) continue;
115
+
116
+ const filePatterns = this.detectPatterns(file.preview);
117
+
118
+ for (const detected of filePatterns) {
119
+ categories[detected.category] = (categories[detected.category] || 0) + 1;
120
+
121
+ // Check if we already have this pattern
122
+ const existingPatterns = this.patternLibrary.getPatternsByCategory(detected.category);
123
+ const existing = existingPatterns.find(p => p.name === detected.name);
124
+
125
+ if (existing) {
126
+ // Add as example if different enough
127
+ if (detected.example && !this.isDuplicate(existing.examples, detected.example)) {
128
+ this.patternLibrary.addExample(existing.id, {
129
+ code: detected.example,
130
+ explanation: `Extracted from ${file.path}`,
131
+ file: file.path
132
+ });
133
+ examplesAdded++;
134
+ }
135
+ } else {
136
+ // Create new pattern
137
+ this.patternLibrary.addPattern(
138
+ detected.name,
139
+ detected.category,
140
+ `${detected.name} detected in codebase`,
141
+ detected.example ? [{
142
+ code: detected.example,
143
+ explanation: `Extracted from ${file.path}`,
144
+ file: file.path
145
+ }] : [],
146
+ [],
147
+ detected.rules
148
+ );
149
+ patternsLearned++;
150
+ }
151
+ }
152
+ }
153
+
154
+ return { patternsLearned, examplesAdded, categories };
155
+ }
156
+
157
+ // Detect patterns in a code snippet
158
+ detectPatterns(code: string): Array<{
159
+ category: PatternCategory;
160
+ name: string;
161
+ example: string | null;
162
+ rules: PatternRule[];
163
+ }> {
164
+ const detected: Array<{
165
+ category: PatternCategory;
166
+ name: string;
167
+ example: string | null;
168
+ rules: PatternRule[];
169
+ }> = [];
170
+
171
+ for (const detector of PATTERN_DETECTORS) {
172
+ if (detector.detect.test(code)) {
173
+ detected.push({
174
+ category: detector.category,
175
+ name: detector.name,
176
+ example: detector.extractExample(code),
177
+ rules: detector.rules
178
+ });
179
+ }
180
+ }
181
+
182
+ return detected;
183
+ }
184
+
185
+ // Learn a specific pattern from user input
186
+ learnPattern(
187
+ code: string,
188
+ name: string,
189
+ description?: string,
190
+ category?: PatternCategory
191
+ ): { success: boolean; patternId?: string; message: string } {
192
+ // Auto-detect category if not provided
193
+ const detectedCategory = category || this.inferCategory(code);
194
+
195
+ // Check for existing similar pattern
196
+ const existingPatterns = this.patternLibrary.searchPatterns(name);
197
+ if (existingPatterns.some(p => p.name.toLowerCase() === name.toLowerCase())) {
198
+ return {
199
+ success: false,
200
+ message: `Pattern "${name}" already exists. Use add_example to add to existing pattern.`
201
+ };
202
+ }
203
+
204
+ // Extract rules from code
205
+ const rules = this.extractRules(code);
206
+
207
+ // Create the pattern
208
+ const pattern = this.patternLibrary.addPattern(
209
+ name,
210
+ detectedCategory,
211
+ description || `User-defined pattern: ${name}`,
212
+ [{
213
+ code,
214
+ explanation: 'User-provided example'
215
+ }],
216
+ [],
217
+ rules
218
+ );
219
+
220
+ return {
221
+ success: true,
222
+ patternId: pattern.id,
223
+ message: `Pattern "${name}" created successfully`
224
+ };
225
+ }
226
+
227
+ // Infer category from code
228
+ private inferCategory(code: string): PatternCategory {
229
+ for (const detector of PATTERN_DETECTORS) {
230
+ if (detector.detect.test(code)) {
231
+ return detector.category;
232
+ }
233
+ }
234
+ return 'custom';
235
+ }
236
+
237
+ // Extract rules from code patterns
238
+ private extractRules(code: string): PatternRule[] {
239
+ const rules: PatternRule[] = [];
240
+
241
+ // Detect common patterns and generate rules
242
+ if (/try\s*\{/.test(code)) {
243
+ rules.push({ rule: 'Use try-catch for error handling', severity: 'warning' });
244
+ }
245
+ if (/catch\s*\([^)]*\)\s*\{[\s\S]*console\.error/.test(code)) {
246
+ rules.push({ rule: 'Log errors in catch blocks', severity: 'info' });
247
+ }
248
+ if (/async\s+/.test(code)) {
249
+ rules.push({ rule: 'Use async functions for asynchronous operations', severity: 'info' });
250
+ }
251
+ if (/await\s+/.test(code)) {
252
+ rules.push({ rule: 'Await all promises', severity: 'warning' });
253
+ }
254
+ if (/interface\s+\w+Props/.test(code)) {
255
+ rules.push({ rule: 'Define Props interfaces for components', severity: 'warning' });
256
+ }
257
+ if (/\?\.\w+/.test(code)) {
258
+ rules.push({ rule: 'Use optional chaining for safe property access', severity: 'info' });
259
+ }
260
+ if (/\?\?/.test(code)) {
261
+ rules.push({ rule: 'Use nullish coalescing for default values', severity: 'info' });
262
+ }
263
+
264
+ return rules;
265
+ }
266
+
267
+ // Check if example is duplicate
268
+ private isDuplicate(examples: CodeExample[], newExample: string): boolean {
269
+ const normalized = this.normalizeCode(newExample);
270
+ return examples.some(e => this.normalizeCode(e.code) === normalized);
271
+ }
272
+
273
+ // Normalize code for comparison
274
+ private normalizeCode(code: string): string {
275
+ return code
276
+ .replace(/\s+/g, ' ')
277
+ .replace(/['"`]/g, '"')
278
+ .trim()
279
+ .toLowerCase();
280
+ }
281
+
282
+ // Get learning statistics
283
+ getStats(): {
284
+ totalPatterns: number;
285
+ byCategory: Record<string, number>;
286
+ topPatterns: Array<{ name: string; usageCount: number }>;
287
+ } {
288
+ const patterns = this.patternLibrary.getAllPatterns();
289
+ const byCategory: Record<string, number> = {};
290
+
291
+ for (const pattern of patterns) {
292
+ byCategory[pattern.category] = (byCategory[pattern.category] || 0) + 1;
293
+ }
294
+
295
+ const topPatterns = patterns
296
+ .sort((a, b) => b.usageCount - a.usageCount)
297
+ .slice(0, 5)
298
+ .map(p => ({ name: p.name, usageCount: p.usageCount }));
299
+
300
+ return {
301
+ totalPatterns: patterns.length,
302
+ byCategory,
303
+ topPatterns
304
+ };
305
+ }
306
+ }