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.
- package/CONTRIBUTING.md +127 -0
- package/LICENSE +21 -0
- package/README.md +305 -0
- package/dist/index.js +38016 -0
- package/esbuild.config.js +26 -0
- package/package.json +63 -0
- package/src/cli/commands.ts +382 -0
- package/src/core/adr-exporter.ts +253 -0
- package/src/core/architecture/architecture-enforcement.ts +228 -0
- package/src/core/architecture/duplicate-detector.ts +288 -0
- package/src/core/architecture/index.ts +6 -0
- package/src/core/architecture/pattern-learner.ts +306 -0
- package/src/core/architecture/pattern-library.ts +403 -0
- package/src/core/architecture/pattern-validator.ts +324 -0
- package/src/core/change-intelligence/bug-correlator.ts +444 -0
- package/src/core/change-intelligence/change-intelligence.ts +221 -0
- package/src/core/change-intelligence/change-tracker.ts +334 -0
- package/src/core/change-intelligence/fix-suggester.ts +340 -0
- package/src/core/change-intelligence/index.ts +5 -0
- package/src/core/code-verifier.ts +843 -0
- package/src/core/confidence/confidence-scorer.ts +251 -0
- package/src/core/confidence/conflict-checker.ts +289 -0
- package/src/core/confidence/index.ts +5 -0
- package/src/core/confidence/source-tracker.ts +263 -0
- package/src/core/confidence/warning-detector.ts +241 -0
- package/src/core/context-rot/compaction.ts +284 -0
- package/src/core/context-rot/context-health.ts +243 -0
- package/src/core/context-rot/context-rot-prevention.ts +213 -0
- package/src/core/context-rot/critical-context.ts +221 -0
- package/src/core/context-rot/drift-detector.ts +255 -0
- package/src/core/context-rot/index.ts +7 -0
- package/src/core/context.ts +263 -0
- package/src/core/decision-extractor.ts +339 -0
- package/src/core/decisions.ts +69 -0
- package/src/core/deja-vu.ts +421 -0
- package/src/core/engine.ts +1455 -0
- package/src/core/feature-context.ts +726 -0
- package/src/core/ghost-mode.ts +412 -0
- package/src/core/learning.ts +485 -0
- package/src/core/living-docs/activity-tracker.ts +296 -0
- package/src/core/living-docs/architecture-generator.ts +428 -0
- package/src/core/living-docs/changelog-generator.ts +348 -0
- package/src/core/living-docs/component-generator.ts +230 -0
- package/src/core/living-docs/doc-engine.ts +110 -0
- package/src/core/living-docs/doc-validator.ts +282 -0
- package/src/core/living-docs/index.ts +8 -0
- package/src/core/project-manager.ts +297 -0
- package/src/core/summarizer.ts +267 -0
- package/src/core/test-awareness/change-validator.ts +499 -0
- package/src/core/test-awareness/index.ts +5 -0
- package/src/index.ts +49 -0
- package/src/indexing/ast.ts +563 -0
- package/src/indexing/embeddings.ts +85 -0
- package/src/indexing/indexer.ts +245 -0
- package/src/indexing/watcher.ts +78 -0
- package/src/server/gateways/aggregator.ts +374 -0
- package/src/server/gateways/index.ts +473 -0
- package/src/server/gateways/memory-ghost.ts +343 -0
- package/src/server/gateways/memory-query.ts +452 -0
- package/src/server/gateways/memory-record.ts +346 -0
- package/src/server/gateways/memory-review.ts +410 -0
- package/src/server/gateways/memory-status.ts +517 -0
- package/src/server/gateways/memory-verify.ts +392 -0
- package/src/server/gateways/router.ts +434 -0
- package/src/server/gateways/types.ts +610 -0
- package/src/server/mcp.ts +154 -0
- package/src/server/resources.ts +85 -0
- package/src/server/tools.ts +2261 -0
- package/src/storage/database.ts +262 -0
- package/src/storage/tier1.ts +135 -0
- package/src/storage/tier2.ts +764 -0
- package/src/storage/tier3.ts +123 -0
- package/src/types/documentation.ts +619 -0
- package/src/types/index.ts +222 -0
- package/src/utils/config.ts +193 -0
- package/src/utils/files.ts +117 -0
- package/src/utils/time.ts +37 -0
- package/src/utils/tokens.ts +52 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Record Gateway
|
|
3
|
+
*
|
|
4
|
+
* Routes to: record_decision, record_decision_with_author, learn_pattern,
|
|
5
|
+
* mark_context_useful, set_feature_context, mark_critical, add_pattern_example
|
|
6
|
+
*
|
|
7
|
+
* Smart Decision Detection: Auto-detects when content looks like a decision
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { MemoryLayerEngine } from '../../core/engine.js';
|
|
11
|
+
import type { MemoryRecordInput, MemoryRecordResponse } from './types.js';
|
|
12
|
+
import { detectRecordType, validateRecordInput } from './router.js';
|
|
13
|
+
|
|
14
|
+
// Patterns that indicate decision-like content
|
|
15
|
+
const DECISION_PATTERNS = [
|
|
16
|
+
/we('ll| will) use/i,
|
|
17
|
+
/decided to/i,
|
|
18
|
+
/going with/i,
|
|
19
|
+
/instead of/i,
|
|
20
|
+
/because.*better/i,
|
|
21
|
+
/chose/i,
|
|
22
|
+
/choosing/i,
|
|
23
|
+
/prefer/i,
|
|
24
|
+
/preferring/i,
|
|
25
|
+
/let's use/i,
|
|
26
|
+
/we should use/i,
|
|
27
|
+
/the approach is/i,
|
|
28
|
+
/our strategy is/i,
|
|
29
|
+
/we're using/i,
|
|
30
|
+
/will implement.*using/i,
|
|
31
|
+
/architecture.*decision/i,
|
|
32
|
+
/technical.*decision/i,
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if content looks like an architectural decision
|
|
37
|
+
*/
|
|
38
|
+
function looksLikeDecision(content: string): boolean {
|
|
39
|
+
return DECISION_PATTERNS.some(re => re.test(content));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract a potential title from decision-like content
|
|
44
|
+
*/
|
|
45
|
+
function extractDecisionTitle(content: string): string | null {
|
|
46
|
+
// Try to find a clear decision statement
|
|
47
|
+
const patterns = [
|
|
48
|
+
/(?:decided to|we'll|we will|going with|chose|choosing)\s+(.+?)(?:\.|$)/i,
|
|
49
|
+
/(?:use|using)\s+(\w+(?:\s+\w+){0,3})\s+(?:for|instead|because)/i,
|
|
50
|
+
/(?:prefer|preferring)\s+(.+?)\s+(?:over|instead|to)/i,
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
for (const pattern of patterns) {
|
|
54
|
+
const match = content.match(pattern);
|
|
55
|
+
if (match && match[1]) {
|
|
56
|
+
// Capitalize first letter and clean up
|
|
57
|
+
const title = match[1].trim();
|
|
58
|
+
return title.charAt(0).toUpperCase() + title.slice(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Fallback: use first sentence, truncated
|
|
63
|
+
const firstSentence = content.split(/[.!?]/)[0];
|
|
64
|
+
if (firstSentence && firstSentence.length > 0) {
|
|
65
|
+
return firstSentence.slice(0, 50) + (firstSentence.length > 50 ? '...' : '');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Handle a memory_record gateway call
|
|
73
|
+
*/
|
|
74
|
+
export async function handleMemoryRecord(
|
|
75
|
+
engine: MemoryLayerEngine,
|
|
76
|
+
input: MemoryRecordInput
|
|
77
|
+
): Promise<MemoryRecordResponse> {
|
|
78
|
+
// Validate input
|
|
79
|
+
const validation = validateRecordInput(input);
|
|
80
|
+
if (!validation.valid) {
|
|
81
|
+
return {
|
|
82
|
+
success: false,
|
|
83
|
+
type: validation.type,
|
|
84
|
+
message: validation.error || 'Invalid input',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let recordType = validation.type;
|
|
89
|
+
let warnings: MemoryRecordResponse['warnings'] = undefined;
|
|
90
|
+
|
|
91
|
+
// Smart Decision Detection: Check if content looks like a decision
|
|
92
|
+
if (recordType !== 'decision' && looksLikeDecision(input.content)) {
|
|
93
|
+
if (!input.title) {
|
|
94
|
+
// Content looks like a decision but no title - add hint
|
|
95
|
+
const suggestedTitle = extractDecisionTitle(input.content);
|
|
96
|
+
warnings = [{
|
|
97
|
+
type: 'hint',
|
|
98
|
+
message: `This looks like an architectural decision. ${suggestedTitle
|
|
99
|
+
? `Suggested title: "${suggestedTitle}". `
|
|
100
|
+
: ''}Add a title to save it as a decision.`,
|
|
101
|
+
severity: 'low',
|
|
102
|
+
}];
|
|
103
|
+
|
|
104
|
+
// If the caller didn't specify a type and it looks like a decision, offer to record it
|
|
105
|
+
if (!input.type && suggestedTitle) {
|
|
106
|
+
// Auto-upgrade to decision if we can extract a title
|
|
107
|
+
recordType = 'decision';
|
|
108
|
+
input.title = suggestedTitle;
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
// Has title and looks like decision - treat as decision
|
|
112
|
+
recordType = 'decision';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Pre-check conflicts for decisions
|
|
117
|
+
if (recordType === 'decision' && input.code) {
|
|
118
|
+
const conflicts = await engine.checkCodeConflicts(input.code);
|
|
119
|
+
if (conflicts.hasConflicts) {
|
|
120
|
+
const conflictWarnings = conflicts.conflicts.map(c => ({
|
|
121
|
+
type: 'conflict' as const,
|
|
122
|
+
message: `Conflicts with decision "${c.decisionTitle}": ${c.conflictDescription}`,
|
|
123
|
+
severity: c.severity as 'low' | 'medium' | 'high',
|
|
124
|
+
}));
|
|
125
|
+
warnings = warnings ? [...warnings, ...conflictWarnings] : conflictWarnings;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Route to appropriate recording method
|
|
130
|
+
switch (recordType) {
|
|
131
|
+
case 'decision':
|
|
132
|
+
return handleRecordDecision(engine, input, warnings);
|
|
133
|
+
|
|
134
|
+
case 'pattern':
|
|
135
|
+
return handleRecordPattern(engine, input);
|
|
136
|
+
|
|
137
|
+
case 'example':
|
|
138
|
+
return handleRecordPatternExample(engine, input);
|
|
139
|
+
|
|
140
|
+
case 'feedback':
|
|
141
|
+
return handleRecordFeedback(engine, input);
|
|
142
|
+
|
|
143
|
+
case 'feature':
|
|
144
|
+
return handleRecordFeature(engine, input);
|
|
145
|
+
|
|
146
|
+
case 'critical':
|
|
147
|
+
return handleRecordCritical(engine, input);
|
|
148
|
+
|
|
149
|
+
default:
|
|
150
|
+
return {
|
|
151
|
+
success: false,
|
|
152
|
+
type: recordType,
|
|
153
|
+
message: `Unknown record type: ${recordType}`,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Record a decision
|
|
160
|
+
*/
|
|
161
|
+
async function handleRecordDecision(
|
|
162
|
+
engine: MemoryLayerEngine,
|
|
163
|
+
input: MemoryRecordInput,
|
|
164
|
+
warnings?: MemoryRecordResponse['warnings']
|
|
165
|
+
): Promise<MemoryRecordResponse> {
|
|
166
|
+
const title = input.title!;
|
|
167
|
+
const description = input.content;
|
|
168
|
+
|
|
169
|
+
let decision;
|
|
170
|
+
|
|
171
|
+
if (input.author) {
|
|
172
|
+
// Use author-attributed decision recording
|
|
173
|
+
decision = await engine.recordDecisionWithAuthor(
|
|
174
|
+
title,
|
|
175
|
+
description,
|
|
176
|
+
input.author,
|
|
177
|
+
input.files,
|
|
178
|
+
input.tags,
|
|
179
|
+
input.status || 'accepted'
|
|
180
|
+
);
|
|
181
|
+
} else {
|
|
182
|
+
// Use simple decision recording
|
|
183
|
+
decision = await engine.recordDecision(
|
|
184
|
+
title,
|
|
185
|
+
description,
|
|
186
|
+
input.files,
|
|
187
|
+
input.tags
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
success: true,
|
|
193
|
+
type: 'decision',
|
|
194
|
+
id: decision.id,
|
|
195
|
+
message: `Decision recorded: "${title}"`,
|
|
196
|
+
warnings,
|
|
197
|
+
record: {
|
|
198
|
+
id: decision.id,
|
|
199
|
+
title: decision.title,
|
|
200
|
+
description: decision.description,
|
|
201
|
+
author: decision.author,
|
|
202
|
+
status: decision.status,
|
|
203
|
+
files: decision.files,
|
|
204
|
+
tags: decision.tags,
|
|
205
|
+
created_at: decision.createdAt.toISOString(),
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Record a pattern
|
|
212
|
+
*/
|
|
213
|
+
async function handleRecordPattern(
|
|
214
|
+
engine: MemoryLayerEngine,
|
|
215
|
+
input: MemoryRecordInput
|
|
216
|
+
): Promise<MemoryRecordResponse> {
|
|
217
|
+
const code = input.code!;
|
|
218
|
+
const name = input.pattern_name || input.title!;
|
|
219
|
+
const description = input.content || undefined;
|
|
220
|
+
const category = input.category || undefined;
|
|
221
|
+
|
|
222
|
+
const result = engine.learnPattern(code, name, description, category);
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
success: result.success,
|
|
226
|
+
type: 'pattern',
|
|
227
|
+
id: result.patternId,
|
|
228
|
+
message: result.message,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Add an example to a pattern
|
|
234
|
+
*/
|
|
235
|
+
async function handleRecordPatternExample(
|
|
236
|
+
engine: MemoryLayerEngine,
|
|
237
|
+
input: MemoryRecordInput
|
|
238
|
+
): Promise<MemoryRecordResponse> {
|
|
239
|
+
const patternId = input.pattern_id!;
|
|
240
|
+
const code = input.code!;
|
|
241
|
+
const explanation = input.explanation || input.content;
|
|
242
|
+
const isAntiPattern = input.is_anti_pattern || false;
|
|
243
|
+
|
|
244
|
+
const success = engine.addPatternExample(patternId, code, explanation, isAntiPattern);
|
|
245
|
+
|
|
246
|
+
if (!success) {
|
|
247
|
+
return {
|
|
248
|
+
success: false,
|
|
249
|
+
type: 'example',
|
|
250
|
+
message: `Pattern not found: ${patternId}`,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
success: true,
|
|
256
|
+
type: 'example',
|
|
257
|
+
message: `${isAntiPattern ? 'Anti-pattern' : 'Example'} added to pattern ${patternId}`,
|
|
258
|
+
record: {
|
|
259
|
+
pattern_id: patternId,
|
|
260
|
+
is_anti_pattern: isAntiPattern,
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Record feedback about context usefulness
|
|
267
|
+
*/
|
|
268
|
+
async function handleRecordFeedback(
|
|
269
|
+
engine: MemoryLayerEngine,
|
|
270
|
+
input: MemoryRecordInput
|
|
271
|
+
): Promise<MemoryRecordResponse> {
|
|
272
|
+
const query = input.query || input.content;
|
|
273
|
+
const wasUseful = input.was_useful!;
|
|
274
|
+
|
|
275
|
+
engine.markContextUsefulness(query, wasUseful);
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
success: true,
|
|
279
|
+
type: 'feedback',
|
|
280
|
+
message: `Feedback recorded: context was ${wasUseful ? 'useful' : 'not useful'}`,
|
|
281
|
+
record: {
|
|
282
|
+
query,
|
|
283
|
+
was_useful: wasUseful,
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Start tracking a feature context
|
|
290
|
+
*/
|
|
291
|
+
async function handleRecordFeature(
|
|
292
|
+
engine: MemoryLayerEngine,
|
|
293
|
+
input: MemoryRecordInput
|
|
294
|
+
): Promise<MemoryRecordResponse> {
|
|
295
|
+
const name = input.content;
|
|
296
|
+
|
|
297
|
+
const context = engine.startFeatureContext(name);
|
|
298
|
+
|
|
299
|
+
// Track initial files if provided
|
|
300
|
+
if (input.files && input.files.length > 0) {
|
|
301
|
+
for (const file of input.files) {
|
|
302
|
+
engine.trackFileOpened(file);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
success: true,
|
|
308
|
+
type: 'feature',
|
|
309
|
+
id: context.id,
|
|
310
|
+
message: `Now tracking feature: "${name}"`,
|
|
311
|
+
record: {
|
|
312
|
+
id: context.id,
|
|
313
|
+
name: context.name,
|
|
314
|
+
files: input.files || [],
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Mark content as critical (never compressed)
|
|
321
|
+
*/
|
|
322
|
+
async function handleRecordCritical(
|
|
323
|
+
engine: MemoryLayerEngine,
|
|
324
|
+
input: MemoryRecordInput
|
|
325
|
+
): Promise<MemoryRecordResponse> {
|
|
326
|
+
const content = input.content;
|
|
327
|
+
const type = input.critical_type;
|
|
328
|
+
const reason = input.reason;
|
|
329
|
+
|
|
330
|
+
const critical = engine.markCritical(content, { type, reason });
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
success: true,
|
|
334
|
+
type: 'critical',
|
|
335
|
+
id: critical.id,
|
|
336
|
+
message: `Marked as critical: "${content.slice(0, 50)}${content.length > 50 ? '...' : ''}"`,
|
|
337
|
+
record: {
|
|
338
|
+
id: critical.id,
|
|
339
|
+
type: critical.type,
|
|
340
|
+
content: critical.content,
|
|
341
|
+
reason: critical.reason,
|
|
342
|
+
never_compress: critical.neverCompress,
|
|
343
|
+
created_at: critical.createdAt.toISOString(),
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|