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,2261 @@
|
|
|
1
|
+
import type { MemoryLayerEngine } from '../core/engine.js';
|
|
2
|
+
|
|
3
|
+
export interface ToolDefinition {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object';
|
|
8
|
+
properties: Record<string, unknown>;
|
|
9
|
+
required?: string[];
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const toolDefinitions: ToolDefinition[] = [
|
|
14
|
+
{
|
|
15
|
+
name: 'get_context',
|
|
16
|
+
description: 'Get relevant codebase context for a query. Use this to understand code, find related files, and get architectural decisions.',
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
query: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'What you are trying to understand or do'
|
|
23
|
+
},
|
|
24
|
+
current_file: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'Path to the file currently being discussed (optional)'
|
|
27
|
+
},
|
|
28
|
+
max_tokens: {
|
|
29
|
+
type: 'number',
|
|
30
|
+
description: 'Maximum tokens to return (default: 6000)'
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
required: ['query']
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'search_codebase',
|
|
38
|
+
description: 'Search the codebase semantically. Returns files and code snippets matching the query.',
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: 'object',
|
|
41
|
+
properties: {
|
|
42
|
+
query: {
|
|
43
|
+
type: 'string',
|
|
44
|
+
description: 'Search query'
|
|
45
|
+
},
|
|
46
|
+
limit: {
|
|
47
|
+
type: 'number',
|
|
48
|
+
description: 'Maximum number of results (default: 10)'
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
required: ['query']
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'record_decision',
|
|
56
|
+
description: 'Record an architectural or design decision. Use this to save important decisions about the codebase.',
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
title: {
|
|
61
|
+
type: 'string',
|
|
62
|
+
description: 'Short title for the decision'
|
|
63
|
+
},
|
|
64
|
+
description: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
description: 'Why this decision was made, context, tradeoffs'
|
|
67
|
+
},
|
|
68
|
+
files: {
|
|
69
|
+
type: 'array',
|
|
70
|
+
items: { type: 'string' },
|
|
71
|
+
description: 'Related files (optional)'
|
|
72
|
+
},
|
|
73
|
+
tags: {
|
|
74
|
+
type: 'array',
|
|
75
|
+
items: { type: 'string' },
|
|
76
|
+
description: 'Tags like "architecture", "database", "security" (optional)'
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
required: ['title', 'description']
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'get_file_context',
|
|
84
|
+
description: 'Get the content and metadata of a specific file.',
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
path: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'Relative path to the file'
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
required: ['path']
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'get_project_summary',
|
|
98
|
+
description: 'Get a summary of the project structure, languages, and recent decisions.',
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'get_symbol',
|
|
106
|
+
description: 'Find a function, class, interface, or type by name. Returns the symbol definition and location.',
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {
|
|
110
|
+
name: {
|
|
111
|
+
type: 'string',
|
|
112
|
+
description: 'Name of the symbol to find (function, class, interface, or type)'
|
|
113
|
+
},
|
|
114
|
+
kind: {
|
|
115
|
+
type: 'string',
|
|
116
|
+
enum: ['function', 'class', 'interface', 'type', 'method', 'enum'],
|
|
117
|
+
description: 'Type of symbol to find (optional, searches all if not specified)'
|
|
118
|
+
},
|
|
119
|
+
limit: {
|
|
120
|
+
type: 'number',
|
|
121
|
+
description: 'Maximum number of results (default: 10)'
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
required: ['name']
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'get_dependencies',
|
|
129
|
+
description: 'Get the dependencies of a file - what it imports and what imports it.',
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
path: {
|
|
134
|
+
type: 'string',
|
|
135
|
+
description: 'Relative path to the file'
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
required: ['path']
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'get_file_summary',
|
|
143
|
+
description: 'Get a compressed summary of a file (10x smaller than full content). Use this for quick overview without reading full file.',
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: 'object',
|
|
146
|
+
properties: {
|
|
147
|
+
path: {
|
|
148
|
+
type: 'string',
|
|
149
|
+
description: 'Relative path to the file'
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
required: ['path']
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'get_predicted_files',
|
|
157
|
+
description: 'Get files predicted to be relevant based on current context and query. Useful for proactive exploration.',
|
|
158
|
+
inputSchema: {
|
|
159
|
+
type: 'object',
|
|
160
|
+
properties: {
|
|
161
|
+
current_file: {
|
|
162
|
+
type: 'string',
|
|
163
|
+
description: 'Path to the current file being discussed'
|
|
164
|
+
},
|
|
165
|
+
query: {
|
|
166
|
+
type: 'string',
|
|
167
|
+
description: 'What you are trying to understand or do'
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
required: ['current_file', 'query']
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: 'get_learning_stats',
|
|
175
|
+
description: 'Get usage statistics and learning metrics for the project.',
|
|
176
|
+
inputSchema: {
|
|
177
|
+
type: 'object',
|
|
178
|
+
properties: {}
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: 'mark_context_useful',
|
|
183
|
+
description: 'Provide feedback on whether the retrieved context was useful. Helps improve future retrieval.',
|
|
184
|
+
inputSchema: {
|
|
185
|
+
type: 'object',
|
|
186
|
+
properties: {
|
|
187
|
+
query: {
|
|
188
|
+
type: 'string',
|
|
189
|
+
description: 'The query that was made'
|
|
190
|
+
},
|
|
191
|
+
was_useful: {
|
|
192
|
+
type: 'boolean',
|
|
193
|
+
description: 'Whether the context was useful'
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
required: ['query', 'was_useful']
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
// Phase 4: Multi-project tools
|
|
200
|
+
{
|
|
201
|
+
name: 'list_projects',
|
|
202
|
+
description: 'List all registered projects across your system.',
|
|
203
|
+
inputSchema: {
|
|
204
|
+
type: 'object',
|
|
205
|
+
properties: {}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'switch_project',
|
|
210
|
+
description: 'Switch to a different registered project.',
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: {
|
|
214
|
+
project_id: {
|
|
215
|
+
type: 'string',
|
|
216
|
+
description: 'The project ID to switch to'
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
required: ['project_id']
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: 'search_all_projects',
|
|
224
|
+
description: 'Search across all registered projects for code or files.',
|
|
225
|
+
inputSchema: {
|
|
226
|
+
type: 'object',
|
|
227
|
+
properties: {
|
|
228
|
+
query: {
|
|
229
|
+
type: 'string',
|
|
230
|
+
description: 'Search query'
|
|
231
|
+
},
|
|
232
|
+
limit: {
|
|
233
|
+
type: 'number',
|
|
234
|
+
description: 'Maximum results per project (default: 5)'
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
required: ['query']
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
name: 'record_decision_with_author',
|
|
242
|
+
description: 'Record an architectural decision with author attribution and status.',
|
|
243
|
+
inputSchema: {
|
|
244
|
+
type: 'object',
|
|
245
|
+
properties: {
|
|
246
|
+
title: {
|
|
247
|
+
type: 'string',
|
|
248
|
+
description: 'Short title for the decision'
|
|
249
|
+
},
|
|
250
|
+
description: {
|
|
251
|
+
type: 'string',
|
|
252
|
+
description: 'Why this decision was made, context, tradeoffs'
|
|
253
|
+
},
|
|
254
|
+
author: {
|
|
255
|
+
type: 'string',
|
|
256
|
+
description: 'Author of the decision'
|
|
257
|
+
},
|
|
258
|
+
status: {
|
|
259
|
+
type: 'string',
|
|
260
|
+
enum: ['proposed', 'accepted', 'deprecated', 'superseded'],
|
|
261
|
+
description: 'Status of the decision (default: accepted)'
|
|
262
|
+
},
|
|
263
|
+
files: {
|
|
264
|
+
type: 'array',
|
|
265
|
+
items: { type: 'string' },
|
|
266
|
+
description: 'Related files (optional)'
|
|
267
|
+
},
|
|
268
|
+
tags: {
|
|
269
|
+
type: 'array',
|
|
270
|
+
items: { type: 'string' },
|
|
271
|
+
description: 'Tags (optional)'
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
required: ['title', 'description', 'author']
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: 'update_decision_status',
|
|
279
|
+
description: 'Update the status of an existing decision (e.g., mark as deprecated or superseded).',
|
|
280
|
+
inputSchema: {
|
|
281
|
+
type: 'object',
|
|
282
|
+
properties: {
|
|
283
|
+
decision_id: {
|
|
284
|
+
type: 'string',
|
|
285
|
+
description: 'ID of the decision to update'
|
|
286
|
+
},
|
|
287
|
+
status: {
|
|
288
|
+
type: 'string',
|
|
289
|
+
enum: ['proposed', 'accepted', 'deprecated', 'superseded'],
|
|
290
|
+
description: 'New status for the decision'
|
|
291
|
+
},
|
|
292
|
+
superseded_by: {
|
|
293
|
+
type: 'string',
|
|
294
|
+
description: 'ID of the decision that supersedes this one (if status is superseded)'
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
required: ['decision_id', 'status']
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
name: 'export_decisions_to_adr',
|
|
302
|
+
description: 'Export all decisions to ADR (Architecture Decision Records) markdown files.',
|
|
303
|
+
inputSchema: {
|
|
304
|
+
type: 'object',
|
|
305
|
+
properties: {
|
|
306
|
+
output_dir: {
|
|
307
|
+
type: 'string',
|
|
308
|
+
description: 'Output directory for ADR files (default: docs/decisions)'
|
|
309
|
+
},
|
|
310
|
+
format: {
|
|
311
|
+
type: 'string',
|
|
312
|
+
enum: ['madr', 'nygard', 'simple'],
|
|
313
|
+
description: 'ADR format to use (default: madr)'
|
|
314
|
+
},
|
|
315
|
+
include_index: {
|
|
316
|
+
type: 'boolean',
|
|
317
|
+
description: 'Generate README index file (default: true)'
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: 'discover_projects',
|
|
324
|
+
description: 'Discover potential projects in common locations on your system.',
|
|
325
|
+
inputSchema: {
|
|
326
|
+
type: 'object',
|
|
327
|
+
properties: {}
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
// Phase 5: Active Feature Context tools
|
|
331
|
+
{
|
|
332
|
+
name: 'get_active_context',
|
|
333
|
+
description: 'Get the current feature context including files being worked on, recent changes, and recent questions. Use this to understand what the user is currently working on.',
|
|
334
|
+
inputSchema: {
|
|
335
|
+
type: 'object',
|
|
336
|
+
properties: {}
|
|
337
|
+
}
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: 'set_feature_context',
|
|
341
|
+
description: 'Start tracking a new feature. Tell MemoryLayer what you are working on for better context.',
|
|
342
|
+
inputSchema: {
|
|
343
|
+
type: 'object',
|
|
344
|
+
properties: {
|
|
345
|
+
name: {
|
|
346
|
+
type: 'string',
|
|
347
|
+
description: 'Name of the feature (e.g., "payment integration", "auth refactor")'
|
|
348
|
+
},
|
|
349
|
+
files: {
|
|
350
|
+
type: 'array',
|
|
351
|
+
items: { type: 'string' },
|
|
352
|
+
description: 'Initial files to track (optional)'
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
required: ['name']
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: 'list_recent_contexts',
|
|
360
|
+
description: 'List recently worked on features/contexts that can be switched back to.',
|
|
361
|
+
inputSchema: {
|
|
362
|
+
type: 'object',
|
|
363
|
+
properties: {}
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
name: 'switch_feature_context',
|
|
368
|
+
description: 'Switch back to a previously worked on feature context.',
|
|
369
|
+
inputSchema: {
|
|
370
|
+
type: 'object',
|
|
371
|
+
properties: {
|
|
372
|
+
context_id: {
|
|
373
|
+
type: 'string',
|
|
374
|
+
description: 'ID of the context to switch to (from list_recent_contexts)'
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
required: ['context_id']
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
// Phase 6: Living Documentation tools
|
|
381
|
+
{
|
|
382
|
+
name: 'generate_docs',
|
|
383
|
+
description: 'Generate documentation for a file or the entire architecture.',
|
|
384
|
+
inputSchema: {
|
|
385
|
+
type: 'object',
|
|
386
|
+
properties: {
|
|
387
|
+
path: {
|
|
388
|
+
type: 'string',
|
|
389
|
+
description: 'Path to document (omit for architecture overview)'
|
|
390
|
+
},
|
|
391
|
+
type: {
|
|
392
|
+
type: 'string',
|
|
393
|
+
enum: ['component', 'architecture'],
|
|
394
|
+
description: 'Type of documentation to generate'
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: 'get_architecture',
|
|
401
|
+
description: 'Get project architecture overview with layers, data flow, and ASCII diagram.',
|
|
402
|
+
inputSchema: {
|
|
403
|
+
type: 'object',
|
|
404
|
+
properties: {}
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
name: 'get_component_doc',
|
|
409
|
+
description: 'Get detailed documentation for a component/file including public interface, dependencies, and change history.',
|
|
410
|
+
inputSchema: {
|
|
411
|
+
type: 'object',
|
|
412
|
+
properties: {
|
|
413
|
+
path: {
|
|
414
|
+
type: 'string',
|
|
415
|
+
description: 'Relative path to the file'
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
required: ['path']
|
|
419
|
+
}
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
name: 'get_changelog',
|
|
423
|
+
description: 'Get changelog of recent changes grouped by day.',
|
|
424
|
+
inputSchema: {
|
|
425
|
+
type: 'object',
|
|
426
|
+
properties: {
|
|
427
|
+
since: {
|
|
428
|
+
type: 'string',
|
|
429
|
+
description: 'Time period: "yesterday", "today", "this week", "this month", or a date'
|
|
430
|
+
},
|
|
431
|
+
group_by: {
|
|
432
|
+
type: 'string',
|
|
433
|
+
enum: ['day', 'week'],
|
|
434
|
+
description: 'How to group changes (default: day)'
|
|
435
|
+
},
|
|
436
|
+
include_decisions: {
|
|
437
|
+
type: 'boolean',
|
|
438
|
+
description: 'Include decisions made during this period (default: false)'
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
name: 'validate_docs',
|
|
445
|
+
description: 'Check for outdated documentation and calculate documentation score.',
|
|
446
|
+
inputSchema: {
|
|
447
|
+
type: 'object',
|
|
448
|
+
properties: {}
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
name: 'what_happened',
|
|
453
|
+
description: 'Query recent project activity - commits, file changes, and decisions.',
|
|
454
|
+
inputSchema: {
|
|
455
|
+
type: 'object',
|
|
456
|
+
properties: {
|
|
457
|
+
since: {
|
|
458
|
+
type: 'string',
|
|
459
|
+
description: 'Time period: "yesterday", "today", "this week", "this month", or a date'
|
|
460
|
+
},
|
|
461
|
+
scope: {
|
|
462
|
+
type: 'string',
|
|
463
|
+
description: 'Limit to a specific directory or file path (optional)'
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
required: ['since']
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
name: 'find_undocumented',
|
|
471
|
+
description: 'Find code that lacks documentation - exported functions, classes, and interfaces without docstrings.',
|
|
472
|
+
inputSchema: {
|
|
473
|
+
type: 'object',
|
|
474
|
+
properties: {
|
|
475
|
+
importance: {
|
|
476
|
+
type: 'string',
|
|
477
|
+
enum: ['low', 'medium', 'high', 'all'],
|
|
478
|
+
description: 'Filter by importance level (default: all)'
|
|
479
|
+
},
|
|
480
|
+
type: {
|
|
481
|
+
type: 'string',
|
|
482
|
+
enum: ['file', 'function', 'class', 'interface', 'all'],
|
|
483
|
+
description: 'Filter by symbol type (default: all)'
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
// Phase 7: Context Rot Prevention tools
|
|
489
|
+
{
|
|
490
|
+
name: 'get_context_health',
|
|
491
|
+
description: 'Check current context health, detect drift, and get compaction suggestions.',
|
|
492
|
+
inputSchema: {
|
|
493
|
+
type: 'object',
|
|
494
|
+
properties: {
|
|
495
|
+
include_history: {
|
|
496
|
+
type: 'boolean',
|
|
497
|
+
description: 'Include health history (default: false)'
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
name: 'trigger_compaction',
|
|
504
|
+
description: 'Manually trigger context compaction to reduce token usage.',
|
|
505
|
+
inputSchema: {
|
|
506
|
+
type: 'object',
|
|
507
|
+
properties: {
|
|
508
|
+
strategy: {
|
|
509
|
+
type: 'string',
|
|
510
|
+
enum: ['summarize', 'selective', 'aggressive'],
|
|
511
|
+
description: 'Compaction strategy: summarize (gentle), selective (moderate), aggressive (maximum reduction)'
|
|
512
|
+
},
|
|
513
|
+
preserve_recent: {
|
|
514
|
+
type: 'number',
|
|
515
|
+
description: 'Number of recent messages to preserve (default: 10)'
|
|
516
|
+
},
|
|
517
|
+
target_utilization: {
|
|
518
|
+
type: 'number',
|
|
519
|
+
description: 'Target utilization percentage (e.g., 50 for 50%)'
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: 'mark_critical',
|
|
526
|
+
description: 'Mark content as critical - it will never be compressed or removed.',
|
|
527
|
+
inputSchema: {
|
|
528
|
+
type: 'object',
|
|
529
|
+
properties: {
|
|
530
|
+
content: {
|
|
531
|
+
type: 'string',
|
|
532
|
+
description: 'The critical content to preserve'
|
|
533
|
+
},
|
|
534
|
+
type: {
|
|
535
|
+
type: 'string',
|
|
536
|
+
enum: ['decision', 'requirement', 'instruction', 'custom'],
|
|
537
|
+
description: 'Type of critical content'
|
|
538
|
+
},
|
|
539
|
+
reason: {
|
|
540
|
+
type: 'string',
|
|
541
|
+
description: 'Why this is critical (optional)'
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
required: ['content']
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: 'get_critical_context',
|
|
549
|
+
description: 'Get all marked critical context items.',
|
|
550
|
+
inputSchema: {
|
|
551
|
+
type: 'object',
|
|
552
|
+
properties: {
|
|
553
|
+
type: {
|
|
554
|
+
type: 'string',
|
|
555
|
+
enum: ['decision', 'requirement', 'instruction', 'custom'],
|
|
556
|
+
description: 'Filter by type (optional)'
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
},
|
|
561
|
+
// Phase 8: Confidence Scoring tools
|
|
562
|
+
{
|
|
563
|
+
name: 'get_confidence',
|
|
564
|
+
description: 'Get confidence score for a code suggestion. Shows how confident the AI should be based on codebase matches, decision alignment, and pattern matching.',
|
|
565
|
+
inputSchema: {
|
|
566
|
+
type: 'object',
|
|
567
|
+
properties: {
|
|
568
|
+
code: {
|
|
569
|
+
type: 'string',
|
|
570
|
+
description: 'The code to evaluate confidence for'
|
|
571
|
+
},
|
|
572
|
+
context: {
|
|
573
|
+
type: 'string',
|
|
574
|
+
description: 'What this code is for (optional context)'
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
required: ['code']
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
name: 'list_sources',
|
|
582
|
+
description: 'List all sources used for a code suggestion - codebase matches, related decisions, and matched patterns.',
|
|
583
|
+
inputSchema: {
|
|
584
|
+
type: 'object',
|
|
585
|
+
properties: {
|
|
586
|
+
code: {
|
|
587
|
+
type: 'string',
|
|
588
|
+
description: 'The code to find sources for'
|
|
589
|
+
},
|
|
590
|
+
context: {
|
|
591
|
+
type: 'string',
|
|
592
|
+
description: 'What this code is for (optional)'
|
|
593
|
+
},
|
|
594
|
+
include_snippets: {
|
|
595
|
+
type: 'boolean',
|
|
596
|
+
description: 'Include code snippets from matches (default: false)'
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
required: ['code']
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
name: 'check_conflicts',
|
|
604
|
+
description: 'Check if code conflicts with past architectural decisions.',
|
|
605
|
+
inputSchema: {
|
|
606
|
+
type: 'object',
|
|
607
|
+
properties: {
|
|
608
|
+
code: {
|
|
609
|
+
type: 'string',
|
|
610
|
+
description: 'The code to check for conflicts'
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
required: ['code']
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
// Phase 9: Change Intelligence tools
|
|
617
|
+
{
|
|
618
|
+
name: 'what_changed',
|
|
619
|
+
description: 'Query what changed in the codebase. Returns file changes, authors, and line counts.',
|
|
620
|
+
inputSchema: {
|
|
621
|
+
type: 'object',
|
|
622
|
+
properties: {
|
|
623
|
+
since: {
|
|
624
|
+
type: 'string',
|
|
625
|
+
description: 'Time period: "yesterday", "today", "this week", "last week", or a date'
|
|
626
|
+
},
|
|
627
|
+
file: {
|
|
628
|
+
type: 'string',
|
|
629
|
+
description: 'Filter to specific file or folder (optional)'
|
|
630
|
+
},
|
|
631
|
+
author: {
|
|
632
|
+
type: 'string',
|
|
633
|
+
description: 'Filter by author name (optional)'
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
required: ['since']
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
name: 'why_broke',
|
|
641
|
+
description: 'Diagnose why something broke. Correlates errors with recent changes and finds similar past bugs.',
|
|
642
|
+
inputSchema: {
|
|
643
|
+
type: 'object',
|
|
644
|
+
properties: {
|
|
645
|
+
error: {
|
|
646
|
+
type: 'string',
|
|
647
|
+
description: 'The error message or symptom'
|
|
648
|
+
},
|
|
649
|
+
file: {
|
|
650
|
+
type: 'string',
|
|
651
|
+
description: 'File where error occurs (optional)'
|
|
652
|
+
},
|
|
653
|
+
line: {
|
|
654
|
+
type: 'number',
|
|
655
|
+
description: 'Line number (optional)'
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
required: ['error']
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
name: 'find_similar_bugs',
|
|
663
|
+
description: 'Find similar bugs from history with their fixes.',
|
|
664
|
+
inputSchema: {
|
|
665
|
+
type: 'object',
|
|
666
|
+
properties: {
|
|
667
|
+
error: {
|
|
668
|
+
type: 'string',
|
|
669
|
+
description: 'Error message to search for'
|
|
670
|
+
},
|
|
671
|
+
limit: {
|
|
672
|
+
type: 'number',
|
|
673
|
+
description: 'Max results (default 5)'
|
|
674
|
+
}
|
|
675
|
+
},
|
|
676
|
+
required: ['error']
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
name: 'suggest_fix',
|
|
681
|
+
description: 'Get fix suggestions for an error based on history and patterns.',
|
|
682
|
+
inputSchema: {
|
|
683
|
+
type: 'object',
|
|
684
|
+
properties: {
|
|
685
|
+
error: {
|
|
686
|
+
type: 'string',
|
|
687
|
+
description: 'Error to fix'
|
|
688
|
+
},
|
|
689
|
+
context: {
|
|
690
|
+
type: 'string',
|
|
691
|
+
description: 'Additional context (e.g., "database query", "API call")'
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
required: ['error']
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
// Phase 10: Architecture Enforcement tools
|
|
698
|
+
{
|
|
699
|
+
name: 'validate_pattern',
|
|
700
|
+
description: 'Validate code against established project patterns. Returns a score, violations, and suggestions for improvement.',
|
|
701
|
+
inputSchema: {
|
|
702
|
+
type: 'object',
|
|
703
|
+
properties: {
|
|
704
|
+
code: {
|
|
705
|
+
type: 'string',
|
|
706
|
+
description: 'The code to validate against patterns'
|
|
707
|
+
},
|
|
708
|
+
type: {
|
|
709
|
+
type: 'string',
|
|
710
|
+
enum: ['error_handling', 'api_call', 'component', 'state_management', 'data_fetching', 'authentication', 'validation', 'logging', 'custom', 'auto'],
|
|
711
|
+
description: 'Type of pattern to validate against (default: auto-detect)'
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
required: ['code']
|
|
715
|
+
}
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
name: 'suggest_existing',
|
|
719
|
+
description: 'Find existing functions that match your intent. Prevents creating duplicate functionality.',
|
|
720
|
+
inputSchema: {
|
|
721
|
+
type: 'object',
|
|
722
|
+
properties: {
|
|
723
|
+
intent: {
|
|
724
|
+
type: 'string',
|
|
725
|
+
description: 'What you are trying to do (e.g., "validate email", "format date", "fetch user data")'
|
|
726
|
+
},
|
|
727
|
+
limit: {
|
|
728
|
+
type: 'number',
|
|
729
|
+
description: 'Maximum number of suggestions (default: 5)'
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
required: ['intent']
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
name: 'learn_pattern',
|
|
737
|
+
description: 'Teach a new pattern to the system. The pattern will be stored and used for future validations.',
|
|
738
|
+
inputSchema: {
|
|
739
|
+
type: 'object',
|
|
740
|
+
properties: {
|
|
741
|
+
code: {
|
|
742
|
+
type: 'string',
|
|
743
|
+
description: 'Example code that demonstrates the pattern'
|
|
744
|
+
},
|
|
745
|
+
name: {
|
|
746
|
+
type: 'string',
|
|
747
|
+
description: 'Name for this pattern (e.g., "API Error Handler", "Form Validation")'
|
|
748
|
+
},
|
|
749
|
+
description: {
|
|
750
|
+
type: 'string',
|
|
751
|
+
description: 'Description of when and how to use this pattern'
|
|
752
|
+
},
|
|
753
|
+
category: {
|
|
754
|
+
type: 'string',
|
|
755
|
+
enum: ['error_handling', 'api_call', 'component', 'state_management', 'data_fetching', 'authentication', 'validation', 'logging', 'custom'],
|
|
756
|
+
description: 'Category for this pattern (auto-detected if not provided)'
|
|
757
|
+
}
|
|
758
|
+
},
|
|
759
|
+
required: ['code', 'name']
|
|
760
|
+
}
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
name: 'list_patterns',
|
|
764
|
+
description: 'List all learned patterns in the project. Can filter by category.',
|
|
765
|
+
inputSchema: {
|
|
766
|
+
type: 'object',
|
|
767
|
+
properties: {
|
|
768
|
+
category: {
|
|
769
|
+
type: 'string',
|
|
770
|
+
enum: ['error_handling', 'api_call', 'component', 'state_management', 'data_fetching', 'authentication', 'validation', 'logging', 'custom'],
|
|
771
|
+
description: 'Filter by pattern category (optional)'
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
name: 'get_pattern',
|
|
778
|
+
description: 'Get details of a specific pattern including examples, anti-patterns, and rules.',
|
|
779
|
+
inputSchema: {
|
|
780
|
+
type: 'object',
|
|
781
|
+
properties: {
|
|
782
|
+
id: {
|
|
783
|
+
type: 'string',
|
|
784
|
+
description: 'Pattern ID to retrieve'
|
|
785
|
+
}
|
|
786
|
+
},
|
|
787
|
+
required: ['id']
|
|
788
|
+
}
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
name: 'add_pattern_example',
|
|
792
|
+
description: 'Add an example or anti-pattern to an existing pattern.',
|
|
793
|
+
inputSchema: {
|
|
794
|
+
type: 'object',
|
|
795
|
+
properties: {
|
|
796
|
+
pattern_id: {
|
|
797
|
+
type: 'string',
|
|
798
|
+
description: 'ID of the pattern to add to'
|
|
799
|
+
},
|
|
800
|
+
code: {
|
|
801
|
+
type: 'string',
|
|
802
|
+
description: 'Example code'
|
|
803
|
+
},
|
|
804
|
+
explanation: {
|
|
805
|
+
type: 'string',
|
|
806
|
+
description: 'Explanation of this example'
|
|
807
|
+
},
|
|
808
|
+
is_anti_pattern: {
|
|
809
|
+
type: 'boolean',
|
|
810
|
+
description: 'If true, marks this as what NOT to do (default: false)'
|
|
811
|
+
}
|
|
812
|
+
},
|
|
813
|
+
required: ['pattern_id', 'code', 'explanation']
|
|
814
|
+
}
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
name: 'get_architecture_stats',
|
|
818
|
+
description: 'Get statistics about patterns and functions in the codebase.',
|
|
819
|
+
inputSchema: {
|
|
820
|
+
type: 'object',
|
|
821
|
+
properties: {}
|
|
822
|
+
}
|
|
823
|
+
},
|
|
824
|
+
// Phase 11: Test-Aware Suggestions tools
|
|
825
|
+
{
|
|
826
|
+
name: 'get_related_tests',
|
|
827
|
+
description: 'Get tests related to a file or function. Use this to understand test coverage before making changes.',
|
|
828
|
+
inputSchema: {
|
|
829
|
+
type: 'object',
|
|
830
|
+
properties: {
|
|
831
|
+
file: {
|
|
832
|
+
type: 'string',
|
|
833
|
+
description: 'File path to find tests for'
|
|
834
|
+
},
|
|
835
|
+
function: {
|
|
836
|
+
type: 'string',
|
|
837
|
+
description: 'Function name to find tests for (optional)'
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
required: ['file']
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
name: 'check_tests',
|
|
845
|
+
description: 'Check if a code change would break tests. Returns predicted failures and suggested fixes.',
|
|
846
|
+
inputSchema: {
|
|
847
|
+
type: 'object',
|
|
848
|
+
properties: {
|
|
849
|
+
change: {
|
|
850
|
+
type: 'string',
|
|
851
|
+
description: 'The proposed code change'
|
|
852
|
+
},
|
|
853
|
+
file: {
|
|
854
|
+
type: 'string',
|
|
855
|
+
description: 'File being changed'
|
|
856
|
+
}
|
|
857
|
+
},
|
|
858
|
+
required: ['change', 'file']
|
|
859
|
+
}
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
name: 'suggest_test_update',
|
|
863
|
+
description: 'Get suggested test updates for a code change. Use this after check_tests shows failures.',
|
|
864
|
+
inputSchema: {
|
|
865
|
+
type: 'object',
|
|
866
|
+
properties: {
|
|
867
|
+
change: {
|
|
868
|
+
type: 'string',
|
|
869
|
+
description: 'The code change'
|
|
870
|
+
},
|
|
871
|
+
failing_tests: {
|
|
872
|
+
type: 'array',
|
|
873
|
+
items: { type: 'string' },
|
|
874
|
+
description: 'Test IDs that would fail (optional, from check_tests)'
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
required: ['change']
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
name: 'get_test_coverage',
|
|
882
|
+
description: 'Get test coverage for a file. Shows which functions are covered and which need tests.',
|
|
883
|
+
inputSchema: {
|
|
884
|
+
type: 'object',
|
|
885
|
+
properties: {
|
|
886
|
+
file: {
|
|
887
|
+
type: 'string',
|
|
888
|
+
description: 'File path to check coverage for'
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
required: ['file']
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
];
|
|
895
|
+
|
|
896
|
+
export async function handleToolCall(
|
|
897
|
+
engine: MemoryLayerEngine,
|
|
898
|
+
toolName: string,
|
|
899
|
+
args: Record<string, unknown>
|
|
900
|
+
): Promise<unknown> {
|
|
901
|
+
switch (toolName) {
|
|
902
|
+
case 'get_context': {
|
|
903
|
+
const query = args.query as string;
|
|
904
|
+
const currentFile = args.current_file as string | undefined;
|
|
905
|
+
const maxTokens = args.max_tokens as number | undefined;
|
|
906
|
+
|
|
907
|
+
const result = await engine.getContext(query, currentFile, maxTokens);
|
|
908
|
+
|
|
909
|
+
return {
|
|
910
|
+
context: result.context,
|
|
911
|
+
sources: result.sources,
|
|
912
|
+
token_count: result.tokenCount,
|
|
913
|
+
decisions: result.decisions.map(d => ({
|
|
914
|
+
id: d.id,
|
|
915
|
+
title: d.title,
|
|
916
|
+
description: d.description,
|
|
917
|
+
created_at: d.createdAt.toISOString()
|
|
918
|
+
}))
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
case 'search_codebase': {
|
|
923
|
+
const query = args.query as string;
|
|
924
|
+
const limit = (args.limit as number) || 10;
|
|
925
|
+
|
|
926
|
+
const results = await engine.searchCodebase(query, limit);
|
|
927
|
+
|
|
928
|
+
return {
|
|
929
|
+
results: results.map(r => ({
|
|
930
|
+
file: r.file,
|
|
931
|
+
preview: r.preview,
|
|
932
|
+
relevance: r.similarity,
|
|
933
|
+
line_start: r.lineStart,
|
|
934
|
+
line_end: r.lineEnd
|
|
935
|
+
}))
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
case 'record_decision': {
|
|
940
|
+
const title = args.title as string;
|
|
941
|
+
const description = args.description as string;
|
|
942
|
+
const files = args.files as string[] | undefined;
|
|
943
|
+
const tags = args.tags as string[] | undefined;
|
|
944
|
+
|
|
945
|
+
const decision = await engine.recordDecision(title, description, files, tags);
|
|
946
|
+
|
|
947
|
+
return {
|
|
948
|
+
id: decision.id,
|
|
949
|
+
title: decision.title,
|
|
950
|
+
description: decision.description,
|
|
951
|
+
files: decision.files,
|
|
952
|
+
tags: decision.tags,
|
|
953
|
+
created_at: decision.createdAt.toISOString(),
|
|
954
|
+
message: 'Decision recorded successfully'
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
case 'get_file_context': {
|
|
959
|
+
const path = args.path as string;
|
|
960
|
+
|
|
961
|
+
const result = await engine.getFileContext(path);
|
|
962
|
+
|
|
963
|
+
if (!result) {
|
|
964
|
+
return {
|
|
965
|
+
error: `File not found: ${path}`
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
return {
|
|
970
|
+
path,
|
|
971
|
+
content: result.content,
|
|
972
|
+
language: result.language,
|
|
973
|
+
lines: result.lines
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
case 'get_project_summary': {
|
|
978
|
+
const summary = engine.getProjectSummary();
|
|
979
|
+
|
|
980
|
+
return {
|
|
981
|
+
name: summary.name,
|
|
982
|
+
description: summary.description,
|
|
983
|
+
languages: summary.languages,
|
|
984
|
+
total_files: summary.totalFiles,
|
|
985
|
+
total_lines: summary.totalLines,
|
|
986
|
+
key_directories: summary.keyDirectories,
|
|
987
|
+
recent_decisions: summary.recentDecisions.map(d => ({
|
|
988
|
+
id: d.id,
|
|
989
|
+
title: d.title,
|
|
990
|
+
description: d.description,
|
|
991
|
+
created_at: d.createdAt.toISOString()
|
|
992
|
+
})),
|
|
993
|
+
dependencies: summary.dependencies,
|
|
994
|
+
architecture_notes: summary.architectureNotes
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
case 'get_symbol': {
|
|
999
|
+
const name = args.name as string;
|
|
1000
|
+
const kind = args.kind as string | undefined;
|
|
1001
|
+
const limit = (args.limit as number) || 10;
|
|
1002
|
+
|
|
1003
|
+
const results = await engine.searchSymbols(name, kind, limit);
|
|
1004
|
+
|
|
1005
|
+
if (results.length === 0) {
|
|
1006
|
+
return {
|
|
1007
|
+
message: `No symbols found matching "${name}"`,
|
|
1008
|
+
results: []
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
return {
|
|
1013
|
+
results: results.map(s => ({
|
|
1014
|
+
name: s.name,
|
|
1015
|
+
kind: s.kind,
|
|
1016
|
+
file: s.filePath,
|
|
1017
|
+
line_start: s.lineStart,
|
|
1018
|
+
line_end: s.lineEnd,
|
|
1019
|
+
signature: s.signature || null,
|
|
1020
|
+
exported: s.exported
|
|
1021
|
+
}))
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
case 'get_dependencies': {
|
|
1026
|
+
const path = args.path as string;
|
|
1027
|
+
|
|
1028
|
+
const result = engine.getFileDependencies(path);
|
|
1029
|
+
|
|
1030
|
+
return {
|
|
1031
|
+
file: path,
|
|
1032
|
+
imports: result.imports,
|
|
1033
|
+
imported_by: result.importedBy,
|
|
1034
|
+
symbols: result.symbols
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
case 'get_file_summary': {
|
|
1039
|
+
const path = args.path as string;
|
|
1040
|
+
|
|
1041
|
+
const summary = engine.getFileSummary(path);
|
|
1042
|
+
|
|
1043
|
+
if (!summary) {
|
|
1044
|
+
return {
|
|
1045
|
+
error: `File not found or no summary available: ${path}`
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
return {
|
|
1050
|
+
path,
|
|
1051
|
+
summary,
|
|
1052
|
+
message: 'Compressed summary (use get_file_context for full content)'
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
case 'get_predicted_files': {
|
|
1057
|
+
const currentFile = args.current_file as string;
|
|
1058
|
+
const query = args.query as string;
|
|
1059
|
+
|
|
1060
|
+
const predicted = engine.getPredictedFiles(currentFile, query);
|
|
1061
|
+
|
|
1062
|
+
// Pre-fetch these files into hot cache
|
|
1063
|
+
const preFetched = await engine.preFetchFiles(currentFile, query);
|
|
1064
|
+
|
|
1065
|
+
return {
|
|
1066
|
+
predicted_files: predicted,
|
|
1067
|
+
pre_fetched: preFetched,
|
|
1068
|
+
message: `Predicted ${predicted.length} relevant files, pre-fetched ${preFetched} into cache`
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
case 'get_learning_stats': {
|
|
1073
|
+
const stats = engine.getLearningStats();
|
|
1074
|
+
|
|
1075
|
+
return {
|
|
1076
|
+
usage: {
|
|
1077
|
+
total_queries: stats.usageStats.totalQueries,
|
|
1078
|
+
total_file_views: stats.usageStats.totalFileViews,
|
|
1079
|
+
recent_activity: stats.usageStats.recentActivity,
|
|
1080
|
+
top_files: stats.usageStats.topFiles
|
|
1081
|
+
},
|
|
1082
|
+
compression: {
|
|
1083
|
+
files_with_summaries: stats.compressionStats.totalFiles,
|
|
1084
|
+
avg_compression_ratio: stats.compressionStats.avgCompression.toFixed(1) + 'x',
|
|
1085
|
+
tokens_saved: stats.compressionStats.totalTokensSaved
|
|
1086
|
+
},
|
|
1087
|
+
cache: {
|
|
1088
|
+
hot_cache_size: stats.hotCacheStats.size,
|
|
1089
|
+
cached_files: stats.hotCacheStats.files
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
case 'mark_context_useful': {
|
|
1095
|
+
const query = args.query as string;
|
|
1096
|
+
const wasUseful = args.was_useful as boolean;
|
|
1097
|
+
|
|
1098
|
+
engine.markContextUsefulness(query, wasUseful);
|
|
1099
|
+
|
|
1100
|
+
return {
|
|
1101
|
+
message: `Feedback recorded: context was ${wasUseful ? 'useful' : 'not useful'}`,
|
|
1102
|
+
query
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// Phase 4: Multi-project tools
|
|
1107
|
+
case 'list_projects': {
|
|
1108
|
+
const projects = engine.listProjects();
|
|
1109
|
+
const activeProject = engine.getActiveProject();
|
|
1110
|
+
|
|
1111
|
+
return {
|
|
1112
|
+
projects: projects.map(p => ({
|
|
1113
|
+
id: p.id,
|
|
1114
|
+
name: p.name,
|
|
1115
|
+
path: p.path,
|
|
1116
|
+
is_active: activeProject?.id === p.id,
|
|
1117
|
+
total_files: p.totalFiles,
|
|
1118
|
+
total_decisions: p.totalDecisions,
|
|
1119
|
+
languages: p.languages,
|
|
1120
|
+
last_accessed: new Date(p.lastAccessed).toISOString()
|
|
1121
|
+
})),
|
|
1122
|
+
active_project: activeProject?.name || null
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
case 'switch_project': {
|
|
1127
|
+
const projectId = args.project_id as string;
|
|
1128
|
+
const success = engine.switchProject(projectId);
|
|
1129
|
+
|
|
1130
|
+
if (!success) {
|
|
1131
|
+
return {
|
|
1132
|
+
error: `Project not found: ${projectId}`,
|
|
1133
|
+
message: 'Use list_projects to see available projects'
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
const project = engine.getProject(projectId);
|
|
1138
|
+
return {
|
|
1139
|
+
message: `Switched to project: ${project?.name}`,
|
|
1140
|
+
project: project ? {
|
|
1141
|
+
id: project.id,
|
|
1142
|
+
name: project.name,
|
|
1143
|
+
path: project.path
|
|
1144
|
+
} : null
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
case 'search_all_projects': {
|
|
1149
|
+
const query = args.query as string;
|
|
1150
|
+
const limit = (args.limit as number) || 5;
|
|
1151
|
+
|
|
1152
|
+
const results = await engine.searchAllProjects(query, limit);
|
|
1153
|
+
|
|
1154
|
+
return {
|
|
1155
|
+
results: results.map(r => ({
|
|
1156
|
+
project: r.project,
|
|
1157
|
+
project_id: r.projectId,
|
|
1158
|
+
matches: r.results.map(m => ({
|
|
1159
|
+
file: m.file,
|
|
1160
|
+
preview: m.preview,
|
|
1161
|
+
relevance: m.similarity
|
|
1162
|
+
}))
|
|
1163
|
+
})),
|
|
1164
|
+
total_projects_searched: results.length
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
case 'record_decision_with_author': {
|
|
1169
|
+
const title = args.title as string;
|
|
1170
|
+
const description = args.description as string;
|
|
1171
|
+
const author = args.author as string;
|
|
1172
|
+
const status = (args.status as 'proposed' | 'accepted' | 'deprecated' | 'superseded') || 'accepted';
|
|
1173
|
+
const files = args.files as string[] | undefined;
|
|
1174
|
+
const tags = args.tags as string[] | undefined;
|
|
1175
|
+
|
|
1176
|
+
const decision = await engine.recordDecisionWithAuthor(
|
|
1177
|
+
title,
|
|
1178
|
+
description,
|
|
1179
|
+
author,
|
|
1180
|
+
files,
|
|
1181
|
+
tags,
|
|
1182
|
+
status
|
|
1183
|
+
);
|
|
1184
|
+
|
|
1185
|
+
return {
|
|
1186
|
+
id: decision.id,
|
|
1187
|
+
title: decision.title,
|
|
1188
|
+
description: decision.description,
|
|
1189
|
+
author: decision.author,
|
|
1190
|
+
status: decision.status,
|
|
1191
|
+
files: decision.files,
|
|
1192
|
+
tags: decision.tags,
|
|
1193
|
+
created_at: decision.createdAt.toISOString(),
|
|
1194
|
+
message: 'Decision recorded with author attribution'
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
case 'update_decision_status': {
|
|
1199
|
+
const decisionId = args.decision_id as string;
|
|
1200
|
+
const status = args.status as 'proposed' | 'accepted' | 'deprecated' | 'superseded';
|
|
1201
|
+
const supersededBy = args.superseded_by as string | undefined;
|
|
1202
|
+
|
|
1203
|
+
const success = engine.updateDecisionStatus(decisionId, status, supersededBy);
|
|
1204
|
+
|
|
1205
|
+
if (!success) {
|
|
1206
|
+
return {
|
|
1207
|
+
error: `Decision not found: ${decisionId}`
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
return {
|
|
1212
|
+
message: `Decision ${decisionId} status updated to: ${status}`,
|
|
1213
|
+
decision_id: decisionId,
|
|
1214
|
+
new_status: status,
|
|
1215
|
+
superseded_by: supersededBy || null
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
case 'export_decisions_to_adr': {
|
|
1220
|
+
const outputDir = args.output_dir as string | undefined;
|
|
1221
|
+
const format = args.format as 'madr' | 'nygard' | 'simple' | undefined;
|
|
1222
|
+
const includeIndex = args.include_index as boolean | undefined;
|
|
1223
|
+
|
|
1224
|
+
const exportedFiles = engine.exportAllDecisionsToADR({
|
|
1225
|
+
outputDir,
|
|
1226
|
+
format,
|
|
1227
|
+
includeIndex
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
return {
|
|
1231
|
+
message: `Exported ${exportedFiles.length} ADR files`,
|
|
1232
|
+
files: exportedFiles,
|
|
1233
|
+
format: format || 'madr'
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
case 'discover_projects': {
|
|
1238
|
+
const discovered = engine.discoverProjects();
|
|
1239
|
+
|
|
1240
|
+
return {
|
|
1241
|
+
message: `Discovered ${discovered.length} potential projects`,
|
|
1242
|
+
projects: discovered.map(p => ({
|
|
1243
|
+
path: p,
|
|
1244
|
+
name: p.split(/[/\\]/).pop()
|
|
1245
|
+
}))
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// Phase 5: Active Feature Context tools
|
|
1250
|
+
case 'get_active_context': {
|
|
1251
|
+
const hotContext = engine.getHotContext();
|
|
1252
|
+
const summary = engine.getActiveContextSummary();
|
|
1253
|
+
|
|
1254
|
+
return {
|
|
1255
|
+
summary: hotContext.summary || 'No active context',
|
|
1256
|
+
current_feature: summary ? {
|
|
1257
|
+
name: summary.name,
|
|
1258
|
+
files_count: summary.files,
|
|
1259
|
+
changes_count: summary.changes,
|
|
1260
|
+
duration_minutes: summary.duration
|
|
1261
|
+
} : null,
|
|
1262
|
+
active_files: hotContext.files.map(f => ({
|
|
1263
|
+
path: f.path,
|
|
1264
|
+
touch_count: f.touchCount,
|
|
1265
|
+
has_content: f.content !== null
|
|
1266
|
+
})),
|
|
1267
|
+
recent_changes: hotContext.changes.slice(0, 5).map(c => ({
|
|
1268
|
+
file: c.file,
|
|
1269
|
+
diff: c.diff,
|
|
1270
|
+
when: c.timestamp.toISOString()
|
|
1271
|
+
})),
|
|
1272
|
+
recent_queries: hotContext.queries.map(q => ({
|
|
1273
|
+
query: q.query,
|
|
1274
|
+
files_used: q.filesUsed,
|
|
1275
|
+
when: q.timestamp.toISOString()
|
|
1276
|
+
}))
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
case 'set_feature_context': {
|
|
1281
|
+
const name = args.name as string;
|
|
1282
|
+
const files = args.files as string[] | undefined;
|
|
1283
|
+
|
|
1284
|
+
const context = engine.startFeatureContext(name);
|
|
1285
|
+
|
|
1286
|
+
// Track initial files if provided
|
|
1287
|
+
if (files && files.length > 0) {
|
|
1288
|
+
for (const file of files) {
|
|
1289
|
+
engine.trackFileOpened(file);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
return {
|
|
1294
|
+
success: true,
|
|
1295
|
+
message: `Now tracking: ${name}`,
|
|
1296
|
+
context_id: context.id,
|
|
1297
|
+
name: context.name
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
case 'list_recent_contexts': {
|
|
1302
|
+
const current = engine.getActiveContextSummary();
|
|
1303
|
+
const recent = engine.getRecentFeatureContexts();
|
|
1304
|
+
|
|
1305
|
+
return {
|
|
1306
|
+
current: current ? {
|
|
1307
|
+
name: current.name,
|
|
1308
|
+
files: current.files,
|
|
1309
|
+
changes: current.changes,
|
|
1310
|
+
duration_minutes: current.duration
|
|
1311
|
+
} : null,
|
|
1312
|
+
recent: recent.map(c => ({
|
|
1313
|
+
id: c.id,
|
|
1314
|
+
name: c.name,
|
|
1315
|
+
files_count: c.files.length,
|
|
1316
|
+
changes_count: c.changes.length,
|
|
1317
|
+
status: c.status,
|
|
1318
|
+
last_active: c.lastActiveAt.toISOString()
|
|
1319
|
+
}))
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
case 'switch_feature_context': {
|
|
1324
|
+
const contextId = args.context_id as string;
|
|
1325
|
+
const success = engine.switchFeatureContext(contextId);
|
|
1326
|
+
|
|
1327
|
+
if (!success) {
|
|
1328
|
+
return {
|
|
1329
|
+
success: false,
|
|
1330
|
+
error: `Context not found: ${contextId}`,
|
|
1331
|
+
message: 'Use list_recent_contexts to see available contexts'
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
const summary = engine.getActiveContextSummary();
|
|
1336
|
+
return {
|
|
1337
|
+
success: true,
|
|
1338
|
+
message: `Switched to: ${summary?.name || 'Unknown'}`,
|
|
1339
|
+
current: summary ? {
|
|
1340
|
+
name: summary.name,
|
|
1341
|
+
files: summary.files,
|
|
1342
|
+
changes: summary.changes
|
|
1343
|
+
} : null
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// Phase 6: Living Documentation tools
|
|
1348
|
+
case 'generate_docs': {
|
|
1349
|
+
const path = args.path as string | undefined;
|
|
1350
|
+
const type = (args.type as string) || (path ? 'component' : 'architecture');
|
|
1351
|
+
|
|
1352
|
+
if (type === 'architecture' || !path) {
|
|
1353
|
+
const arch = await engine.getArchitecture();
|
|
1354
|
+
return {
|
|
1355
|
+
type: 'architecture',
|
|
1356
|
+
name: arch.name,
|
|
1357
|
+
description: arch.description,
|
|
1358
|
+
diagram: arch.diagram,
|
|
1359
|
+
layers: arch.layers.map(l => ({
|
|
1360
|
+
name: l.name,
|
|
1361
|
+
directory: l.directory,
|
|
1362
|
+
files_count: l.files.length,
|
|
1363
|
+
purpose: l.purpose
|
|
1364
|
+
})),
|
|
1365
|
+
data_flow: arch.dataFlow,
|
|
1366
|
+
dependencies_count: arch.dependencies.length,
|
|
1367
|
+
generated_at: arch.generatedAt.toISOString()
|
|
1368
|
+
};
|
|
1369
|
+
} else {
|
|
1370
|
+
const doc = await engine.getComponentDoc(path);
|
|
1371
|
+
return {
|
|
1372
|
+
type: 'component',
|
|
1373
|
+
file: doc.file,
|
|
1374
|
+
name: doc.name,
|
|
1375
|
+
purpose: doc.purpose,
|
|
1376
|
+
public_interface: doc.publicInterface.map(s => ({
|
|
1377
|
+
name: s.name,
|
|
1378
|
+
kind: s.kind,
|
|
1379
|
+
signature: s.signature,
|
|
1380
|
+
line: s.lineStart
|
|
1381
|
+
})),
|
|
1382
|
+
dependencies_count: doc.dependencies.length,
|
|
1383
|
+
dependents_count: doc.dependents.length,
|
|
1384
|
+
complexity: doc.complexity,
|
|
1385
|
+
documentation_score: doc.documentationScore
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
case 'get_architecture': {
|
|
1391
|
+
const arch = await engine.getArchitecture();
|
|
1392
|
+
|
|
1393
|
+
return {
|
|
1394
|
+
name: arch.name,
|
|
1395
|
+
description: arch.description,
|
|
1396
|
+
diagram: arch.diagram,
|
|
1397
|
+
layers: arch.layers.map(l => ({
|
|
1398
|
+
name: l.name,
|
|
1399
|
+
directory: l.directory,
|
|
1400
|
+
files: l.files,
|
|
1401
|
+
purpose: l.purpose
|
|
1402
|
+
})),
|
|
1403
|
+
data_flow: arch.dataFlow,
|
|
1404
|
+
key_components: arch.keyComponents.map(c => ({
|
|
1405
|
+
name: c.name,
|
|
1406
|
+
file: c.file,
|
|
1407
|
+
purpose: c.purpose,
|
|
1408
|
+
exports: c.exports
|
|
1409
|
+
})),
|
|
1410
|
+
dependencies: arch.dependencies.map(d => ({
|
|
1411
|
+
name: d.name,
|
|
1412
|
+
version: d.version,
|
|
1413
|
+
type: d.type
|
|
1414
|
+
})),
|
|
1415
|
+
generated_at: arch.generatedAt.toISOString()
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
case 'get_component_doc': {
|
|
1420
|
+
const path = args.path as string;
|
|
1421
|
+
|
|
1422
|
+
try {
|
|
1423
|
+
const doc = await engine.getComponentDoc(path);
|
|
1424
|
+
|
|
1425
|
+
return {
|
|
1426
|
+
file: doc.file,
|
|
1427
|
+
name: doc.name,
|
|
1428
|
+
purpose: doc.purpose,
|
|
1429
|
+
last_modified: doc.lastModified.toISOString(),
|
|
1430
|
+
public_interface: doc.publicInterface.map(s => ({
|
|
1431
|
+
name: s.name,
|
|
1432
|
+
kind: s.kind,
|
|
1433
|
+
signature: s.signature,
|
|
1434
|
+
description: s.description,
|
|
1435
|
+
line_start: s.lineStart,
|
|
1436
|
+
line_end: s.lineEnd,
|
|
1437
|
+
exported: s.exported
|
|
1438
|
+
})),
|
|
1439
|
+
dependencies: doc.dependencies,
|
|
1440
|
+
dependents: doc.dependents,
|
|
1441
|
+
change_history: doc.changeHistory.slice(0, 10).map(h => ({
|
|
1442
|
+
date: h.date.toISOString(),
|
|
1443
|
+
change: h.change,
|
|
1444
|
+
author: h.author,
|
|
1445
|
+
commit: h.commit,
|
|
1446
|
+
lines_added: h.linesChanged.added,
|
|
1447
|
+
lines_removed: h.linesChanged.removed
|
|
1448
|
+
})),
|
|
1449
|
+
contributors: doc.contributors,
|
|
1450
|
+
complexity: doc.complexity,
|
|
1451
|
+
documentation_score: doc.documentationScore
|
|
1452
|
+
};
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
return {
|
|
1455
|
+
error: `Failed to generate component doc: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
case 'get_changelog': {
|
|
1461
|
+
const since = args.since as string | undefined;
|
|
1462
|
+
const groupBy = args.group_by as 'day' | 'week' | undefined;
|
|
1463
|
+
const includeDecisions = args.include_decisions as boolean | undefined;
|
|
1464
|
+
|
|
1465
|
+
const changelogs = await engine.getChangelog({
|
|
1466
|
+
since,
|
|
1467
|
+
groupBy,
|
|
1468
|
+
includeDecisions
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
return {
|
|
1472
|
+
period: since || 'this week',
|
|
1473
|
+
days: changelogs.map(day => ({
|
|
1474
|
+
date: day.date.toISOString().split('T')[0],
|
|
1475
|
+
summary: day.summary,
|
|
1476
|
+
features: day.features.map(f => ({
|
|
1477
|
+
description: f.description,
|
|
1478
|
+
files: f.files,
|
|
1479
|
+
commit: f.commit
|
|
1480
|
+
})),
|
|
1481
|
+
fixes: day.fixes.map(f => ({
|
|
1482
|
+
description: f.description,
|
|
1483
|
+
files: f.files,
|
|
1484
|
+
commit: f.commit
|
|
1485
|
+
})),
|
|
1486
|
+
refactors: day.refactors.map(r => ({
|
|
1487
|
+
description: r.description,
|
|
1488
|
+
files: r.files,
|
|
1489
|
+
commit: r.commit
|
|
1490
|
+
})),
|
|
1491
|
+
decisions: day.decisions,
|
|
1492
|
+
metrics: {
|
|
1493
|
+
commits: day.metrics.commits,
|
|
1494
|
+
files_changed: day.metrics.filesChanged,
|
|
1495
|
+
lines_added: day.metrics.linesAdded,
|
|
1496
|
+
lines_removed: day.metrics.linesRemoved
|
|
1497
|
+
}
|
|
1498
|
+
})),
|
|
1499
|
+
total_days: changelogs.length
|
|
1500
|
+
};
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
case 'validate_docs': {
|
|
1504
|
+
const result = await engine.validateDocs();
|
|
1505
|
+
|
|
1506
|
+
return {
|
|
1507
|
+
is_valid: result.isValid,
|
|
1508
|
+
score: result.score,
|
|
1509
|
+
outdated_docs: result.outdatedDocs.map(d => ({
|
|
1510
|
+
file: d.file,
|
|
1511
|
+
reason: d.reason,
|
|
1512
|
+
severity: d.severity,
|
|
1513
|
+
last_doc_update: d.lastDocUpdate.toISOString(),
|
|
1514
|
+
last_code_change: d.lastCodeChange.toISOString()
|
|
1515
|
+
})),
|
|
1516
|
+
undocumented_count: result.undocumentedCode.length,
|
|
1517
|
+
suggestions: result.suggestions.slice(0, 10).map(s => ({
|
|
1518
|
+
file: s.file,
|
|
1519
|
+
suggestion: s.suggestion,
|
|
1520
|
+
priority: s.priority
|
|
1521
|
+
})),
|
|
1522
|
+
message: result.isValid
|
|
1523
|
+
? `Documentation score: ${result.score}% - Looking good!`
|
|
1524
|
+
: `Documentation score: ${result.score}% - ${result.suggestions.length} suggestions available`
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
case 'what_happened': {
|
|
1529
|
+
const since = args.since as string;
|
|
1530
|
+
const scope = args.scope as string | undefined;
|
|
1531
|
+
|
|
1532
|
+
const result = await engine.whatHappened(since, scope);
|
|
1533
|
+
|
|
1534
|
+
return {
|
|
1535
|
+
time_range: {
|
|
1536
|
+
since: result.timeRange.since.toISOString(),
|
|
1537
|
+
until: result.timeRange.until.toISOString()
|
|
1538
|
+
},
|
|
1539
|
+
scope: result.scope,
|
|
1540
|
+
summary: result.summary,
|
|
1541
|
+
changes: result.changes.slice(0, 20).map(c => ({
|
|
1542
|
+
timestamp: c.timestamp.toISOString(),
|
|
1543
|
+
type: c.type,
|
|
1544
|
+
description: c.description,
|
|
1545
|
+
details: c.details
|
|
1546
|
+
})),
|
|
1547
|
+
decisions: result.decisions.map(d => ({
|
|
1548
|
+
id: d.id,
|
|
1549
|
+
title: d.title,
|
|
1550
|
+
date: d.date.toISOString()
|
|
1551
|
+
})),
|
|
1552
|
+
files_affected: result.filesAffected.slice(0, 20),
|
|
1553
|
+
total_changes: result.changes.length,
|
|
1554
|
+
total_files: result.filesAffected.length
|
|
1555
|
+
};
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
case 'find_undocumented': {
|
|
1559
|
+
const importance = args.importance as 'low' | 'medium' | 'high' | 'all' | undefined;
|
|
1560
|
+
const type = args.type as 'file' | 'function' | 'class' | 'interface' | 'all' | undefined;
|
|
1561
|
+
|
|
1562
|
+
const items = await engine.findUndocumented({ importance, type });
|
|
1563
|
+
|
|
1564
|
+
// Group by file for better readability
|
|
1565
|
+
const byFile = new Map<string, typeof items>();
|
|
1566
|
+
for (const item of items) {
|
|
1567
|
+
if (!byFile.has(item.file)) {
|
|
1568
|
+
byFile.set(item.file, []);
|
|
1569
|
+
}
|
|
1570
|
+
byFile.get(item.file)!.push(item);
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
return {
|
|
1574
|
+
total: items.length,
|
|
1575
|
+
by_importance: {
|
|
1576
|
+
high: items.filter(i => i.importance === 'high').length,
|
|
1577
|
+
medium: items.filter(i => i.importance === 'medium').length,
|
|
1578
|
+
low: items.filter(i => i.importance === 'low').length
|
|
1579
|
+
},
|
|
1580
|
+
items: items.slice(0, 30).map(i => ({
|
|
1581
|
+
file: i.file,
|
|
1582
|
+
symbol: i.symbol,
|
|
1583
|
+
type: i.type,
|
|
1584
|
+
importance: i.importance
|
|
1585
|
+
})),
|
|
1586
|
+
files_affected: byFile.size,
|
|
1587
|
+
message: items.length === 0
|
|
1588
|
+
? 'All exported code is documented!'
|
|
1589
|
+
: `Found ${items.length} undocumented items across ${byFile.size} files`
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
// Phase 7: Context Rot Prevention tools
|
|
1594
|
+
case 'get_context_health': {
|
|
1595
|
+
const includeHistory = args.include_history as boolean | undefined;
|
|
1596
|
+
|
|
1597
|
+
const health = engine.getContextHealth();
|
|
1598
|
+
const drift = engine.detectDrift();
|
|
1599
|
+
|
|
1600
|
+
const result: Record<string, unknown> = {
|
|
1601
|
+
health: health.health,
|
|
1602
|
+
tokens_used: health.tokensUsed,
|
|
1603
|
+
tokens_limit: health.tokensLimit,
|
|
1604
|
+
utilization: `${health.utilizationPercent}%`,
|
|
1605
|
+
drift_score: health.driftScore,
|
|
1606
|
+
drift_detected: health.driftDetected,
|
|
1607
|
+
relevance_score: health.relevanceScore,
|
|
1608
|
+
critical_context_count: health.criticalContextCount,
|
|
1609
|
+
compaction_needed: health.compactionNeeded,
|
|
1610
|
+
suggestions: health.suggestions,
|
|
1611
|
+
drift_details: {
|
|
1612
|
+
missing_requirements: drift.missingRequirements,
|
|
1613
|
+
contradictions: drift.contradictions.length,
|
|
1614
|
+
topic_shift: drift.topicShift,
|
|
1615
|
+
suggested_reminders: drift.suggestedReminders
|
|
1616
|
+
}
|
|
1617
|
+
};
|
|
1618
|
+
|
|
1619
|
+
if (includeHistory) {
|
|
1620
|
+
// Note: getHealthHistory would need to be exposed from engine
|
|
1621
|
+
result.message = 'Health history tracking available';
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
return result;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
case 'trigger_compaction': {
|
|
1628
|
+
const strategy = (args.strategy as 'summarize' | 'selective' | 'aggressive') || 'summarize';
|
|
1629
|
+
const preserveRecent = args.preserve_recent as number | undefined;
|
|
1630
|
+
const targetUtilization = args.target_utilization as number | undefined;
|
|
1631
|
+
|
|
1632
|
+
const result = engine.triggerCompaction({
|
|
1633
|
+
strategy,
|
|
1634
|
+
preserveRecent,
|
|
1635
|
+
targetUtilization,
|
|
1636
|
+
preserveCritical: true
|
|
1637
|
+
});
|
|
1638
|
+
|
|
1639
|
+
return {
|
|
1640
|
+
success: result.success,
|
|
1641
|
+
strategy: result.strategy,
|
|
1642
|
+
tokens_before: result.tokensBefore,
|
|
1643
|
+
tokens_after: result.tokensAfter,
|
|
1644
|
+
tokens_saved: result.tokensSaved,
|
|
1645
|
+
reduction: `${Math.round((result.tokensSaved / result.tokensBefore) * 100)}%`,
|
|
1646
|
+
preserved_critical: result.preservedCritical,
|
|
1647
|
+
summarized_chunks: result.summarizedChunks,
|
|
1648
|
+
removed_chunks: result.removedChunks,
|
|
1649
|
+
summaries: result.summaries,
|
|
1650
|
+
message: result.success
|
|
1651
|
+
? `Compaction successful: saved ${result.tokensSaved} tokens (${Math.round((result.tokensSaved / result.tokensBefore) * 100)}% reduction)`
|
|
1652
|
+
: 'Compaction failed'
|
|
1653
|
+
};
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
case 'mark_critical': {
|
|
1657
|
+
const content = args.content as string;
|
|
1658
|
+
const type = args.type as 'decision' | 'requirement' | 'instruction' | 'custom' | undefined;
|
|
1659
|
+
const reason = args.reason as string | undefined;
|
|
1660
|
+
|
|
1661
|
+
const critical = engine.markCritical(content, { type, reason });
|
|
1662
|
+
|
|
1663
|
+
return {
|
|
1664
|
+
id: critical.id,
|
|
1665
|
+
type: critical.type,
|
|
1666
|
+
content: critical.content,
|
|
1667
|
+
reason: critical.reason,
|
|
1668
|
+
created_at: critical.createdAt.toISOString(),
|
|
1669
|
+
never_compress: critical.neverCompress,
|
|
1670
|
+
message: `Marked as critical ${critical.type}: "${content.slice(0, 50)}${content.length > 50 ? '...' : ''}"`
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
case 'get_critical_context': {
|
|
1675
|
+
const type = args.type as 'decision' | 'requirement' | 'instruction' | 'custom' | undefined;
|
|
1676
|
+
|
|
1677
|
+
const items = engine.getCriticalContext(type);
|
|
1678
|
+
|
|
1679
|
+
// Group by type
|
|
1680
|
+
const byType: Record<string, number> = {
|
|
1681
|
+
decision: 0,
|
|
1682
|
+
requirement: 0,
|
|
1683
|
+
instruction: 0,
|
|
1684
|
+
custom: 0
|
|
1685
|
+
};
|
|
1686
|
+
|
|
1687
|
+
for (const item of items) {
|
|
1688
|
+
byType[item.type] = (byType[item.type] || 0) + 1;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
return {
|
|
1692
|
+
total: items.length,
|
|
1693
|
+
by_type: byType,
|
|
1694
|
+
items: items.map(item => ({
|
|
1695
|
+
id: item.id,
|
|
1696
|
+
type: item.type,
|
|
1697
|
+
content: item.content,
|
|
1698
|
+
reason: item.reason,
|
|
1699
|
+
created_at: item.createdAt.toISOString()
|
|
1700
|
+
})),
|
|
1701
|
+
summary: engine.getContextSummaryForAI(),
|
|
1702
|
+
message: items.length === 0
|
|
1703
|
+
? 'No critical context marked. Consider marking important decisions and requirements.'
|
|
1704
|
+
: `${items.length} critical items will be preserved during compaction`
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
// Phase 8: Confidence Scoring tools
|
|
1709
|
+
case 'get_confidence': {
|
|
1710
|
+
const code = args.code as string;
|
|
1711
|
+
const context = args.context as string | undefined;
|
|
1712
|
+
|
|
1713
|
+
const result = await engine.getConfidence(code, context);
|
|
1714
|
+
|
|
1715
|
+
return {
|
|
1716
|
+
confidence: result.confidence,
|
|
1717
|
+
score: result.score,
|
|
1718
|
+
reasoning: result.reasoning,
|
|
1719
|
+
indicator: engine.getConfidenceIndicator(result.confidence),
|
|
1720
|
+
sources: {
|
|
1721
|
+
codebase: result.sources.codebase.map(m => ({
|
|
1722
|
+
file: m.file,
|
|
1723
|
+
line: m.line,
|
|
1724
|
+
function: m.function,
|
|
1725
|
+
similarity: m.similarity,
|
|
1726
|
+
usage_count: m.usageCount
|
|
1727
|
+
})),
|
|
1728
|
+
decisions: result.sources.decisions.map(d => ({
|
|
1729
|
+
id: d.id,
|
|
1730
|
+
title: d.title,
|
|
1731
|
+
relevance: d.relevance,
|
|
1732
|
+
date: d.date.toISOString()
|
|
1733
|
+
})),
|
|
1734
|
+
patterns: result.sources.patterns.map(p => ({
|
|
1735
|
+
pattern: p.pattern,
|
|
1736
|
+
confidence: p.confidence,
|
|
1737
|
+
examples: p.examples
|
|
1738
|
+
})),
|
|
1739
|
+
used_general_knowledge: result.sources.usedGeneralKnowledge
|
|
1740
|
+
},
|
|
1741
|
+
warnings: result.warnings.map(w => ({
|
|
1742
|
+
type: w.type,
|
|
1743
|
+
message: w.message,
|
|
1744
|
+
severity: w.severity,
|
|
1745
|
+
suggestion: w.suggestion,
|
|
1746
|
+
related_decision: w.relatedDecision
|
|
1747
|
+
})),
|
|
1748
|
+
formatted: engine.formatConfidenceResult(result),
|
|
1749
|
+
message: `${result.confidence.toUpperCase()} confidence (${result.score}%): ${result.reasoning}`
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
case 'list_sources': {
|
|
1754
|
+
const code = args.code as string;
|
|
1755
|
+
const context = args.context as string | undefined;
|
|
1756
|
+
const includeSnippets = args.include_snippets as boolean | undefined;
|
|
1757
|
+
|
|
1758
|
+
const sources = await engine.listConfidenceSources(code, context, includeSnippets);
|
|
1759
|
+
|
|
1760
|
+
// Calculate weights
|
|
1761
|
+
const hasCodebase = sources.codebase.length > 0;
|
|
1762
|
+
const hasDecisions = sources.decisions.length > 0;
|
|
1763
|
+
|
|
1764
|
+
let codebaseWeight = 50;
|
|
1765
|
+
let decisionWeight = 30;
|
|
1766
|
+
let patternWeight = 20;
|
|
1767
|
+
|
|
1768
|
+
if (!hasCodebase) {
|
|
1769
|
+
codebaseWeight = 0;
|
|
1770
|
+
decisionWeight += 25;
|
|
1771
|
+
patternWeight += 25;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
if (!hasDecisions) {
|
|
1775
|
+
decisionWeight = 0;
|
|
1776
|
+
codebaseWeight += 15;
|
|
1777
|
+
patternWeight += 15;
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
return {
|
|
1781
|
+
codebase: {
|
|
1782
|
+
weight: `${codebaseWeight}%`,
|
|
1783
|
+
matches: sources.codebase.map(m => ({
|
|
1784
|
+
file: m.file,
|
|
1785
|
+
line: m.line,
|
|
1786
|
+
function: m.function,
|
|
1787
|
+
similarity: m.similarity,
|
|
1788
|
+
snippet: m.snippet,
|
|
1789
|
+
usage_count: m.usageCount
|
|
1790
|
+
}))
|
|
1791
|
+
},
|
|
1792
|
+
decisions: {
|
|
1793
|
+
weight: `${decisionWeight}%`,
|
|
1794
|
+
matches: sources.decisions.map(d => ({
|
|
1795
|
+
id: d.id,
|
|
1796
|
+
title: d.title,
|
|
1797
|
+
relevance: d.relevance,
|
|
1798
|
+
date: d.date.toISOString()
|
|
1799
|
+
}))
|
|
1800
|
+
},
|
|
1801
|
+
patterns: {
|
|
1802
|
+
weight: `${patternWeight}%`,
|
|
1803
|
+
matches: sources.patterns.map(p => ({
|
|
1804
|
+
pattern: p.pattern,
|
|
1805
|
+
confidence: p.confidence,
|
|
1806
|
+
examples: p.examples
|
|
1807
|
+
}))
|
|
1808
|
+
},
|
|
1809
|
+
used_general_knowledge: sources.usedGeneralKnowledge,
|
|
1810
|
+
message: sources.usedGeneralKnowledge
|
|
1811
|
+
? 'Sources: Based primarily on general knowledge (no strong codebase matches)'
|
|
1812
|
+
: `Sources: Found ${sources.codebase.length} codebase matches, ${sources.decisions.length} related decisions, ${sources.patterns.length} patterns`
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
case 'check_conflicts': {
|
|
1817
|
+
const code = args.code as string;
|
|
1818
|
+
|
|
1819
|
+
const result = await engine.checkCodeConflicts(code);
|
|
1820
|
+
|
|
1821
|
+
return {
|
|
1822
|
+
has_conflicts: result.hasConflicts,
|
|
1823
|
+
conflicts: result.conflicts.map(c => ({
|
|
1824
|
+
decision_id: c.decisionId,
|
|
1825
|
+
decision_title: c.decisionTitle,
|
|
1826
|
+
decision_date: c.decisionDate.toISOString(),
|
|
1827
|
+
conflict_description: c.conflictDescription,
|
|
1828
|
+
severity: c.severity
|
|
1829
|
+
})),
|
|
1830
|
+
message: result.hasConflicts
|
|
1831
|
+
? `Found ${result.conflicts.length} conflict(s) with past decisions`
|
|
1832
|
+
: 'No conflicts with past decisions'
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
// Phase 9: Change Intelligence tools
|
|
1837
|
+
case 'what_changed': {
|
|
1838
|
+
const since = args.since as string;
|
|
1839
|
+
const file = args.file as string | undefined;
|
|
1840
|
+
const author = args.author as string | undefined;
|
|
1841
|
+
|
|
1842
|
+
const result = engine.whatChanged({ since, file, author });
|
|
1843
|
+
|
|
1844
|
+
return {
|
|
1845
|
+
period: result.period,
|
|
1846
|
+
since: result.since.toISOString(),
|
|
1847
|
+
until: result.until.toISOString(),
|
|
1848
|
+
total_files: result.totalFiles,
|
|
1849
|
+
total_lines_added: result.totalLinesAdded,
|
|
1850
|
+
total_lines_removed: result.totalLinesRemoved,
|
|
1851
|
+
by_author: result.byAuthor,
|
|
1852
|
+
by_type: result.byType,
|
|
1853
|
+
changes: result.changes.slice(0, 20).map(c => ({
|
|
1854
|
+
file: c.file,
|
|
1855
|
+
type: c.type,
|
|
1856
|
+
lines_added: c.linesAdded,
|
|
1857
|
+
lines_removed: c.linesRemoved,
|
|
1858
|
+
author: c.author,
|
|
1859
|
+
timestamp: c.timestamp.toISOString(),
|
|
1860
|
+
commit_message: c.commitMessage,
|
|
1861
|
+
commit_hash: c.commitHash.slice(0, 7)
|
|
1862
|
+
})),
|
|
1863
|
+
formatted: engine.formatChanges(result),
|
|
1864
|
+
message: result.changes.length === 0
|
|
1865
|
+
? `No changes found since ${since}`
|
|
1866
|
+
: `Found ${result.changes.length} changes across ${result.totalFiles} files`
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
case 'why_broke': {
|
|
1871
|
+
const error = args.error as string;
|
|
1872
|
+
const file = args.file as string | undefined;
|
|
1873
|
+
const line = args.line as number | undefined;
|
|
1874
|
+
|
|
1875
|
+
const diagnosis = engine.whyBroke(error, { file, line });
|
|
1876
|
+
|
|
1877
|
+
return {
|
|
1878
|
+
likely_cause: diagnosis.likelyCause ? {
|
|
1879
|
+
file: diagnosis.likelyCause.file,
|
|
1880
|
+
author: diagnosis.likelyCause.author,
|
|
1881
|
+
timestamp: diagnosis.likelyCause.timestamp.toISOString(),
|
|
1882
|
+
commit_message: diagnosis.likelyCause.commitMessage,
|
|
1883
|
+
commit_hash: diagnosis.likelyCause.commitHash.slice(0, 7),
|
|
1884
|
+
diff: diagnosis.likelyCause.diff.slice(0, 500)
|
|
1885
|
+
} : null,
|
|
1886
|
+
confidence: diagnosis.confidence,
|
|
1887
|
+
related_changes: diagnosis.relatedChanges.map(c => ({
|
|
1888
|
+
file: c.file,
|
|
1889
|
+
timestamp: c.timestamp.toISOString(),
|
|
1890
|
+
commit_message: c.commitMessage
|
|
1891
|
+
})),
|
|
1892
|
+
past_similar_bugs: diagnosis.pastSimilarBugs.map(b => ({
|
|
1893
|
+
error: b.error.slice(0, 100),
|
|
1894
|
+
similarity: b.similarity,
|
|
1895
|
+
date: b.date.toISOString(),
|
|
1896
|
+
fix: b.fix,
|
|
1897
|
+
file: b.file
|
|
1898
|
+
})),
|
|
1899
|
+
suggested_fix: diagnosis.suggestedFix,
|
|
1900
|
+
reasoning: diagnosis.reasoning,
|
|
1901
|
+
formatted: engine.formatDiagnosis(diagnosis),
|
|
1902
|
+
message: diagnosis.likelyCause
|
|
1903
|
+
? `Found likely cause in ${diagnosis.likelyCause.file} (${diagnosis.confidence}% confidence)`
|
|
1904
|
+
: 'Could not identify specific cause'
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
case 'find_similar_bugs': {
|
|
1909
|
+
const error = args.error as string;
|
|
1910
|
+
const limit = args.limit as number | undefined;
|
|
1911
|
+
|
|
1912
|
+
const bugs = engine.findSimilarBugs(error, limit);
|
|
1913
|
+
|
|
1914
|
+
return {
|
|
1915
|
+
total: bugs.length,
|
|
1916
|
+
bugs: bugs.map(b => ({
|
|
1917
|
+
id: b.id,
|
|
1918
|
+
error: b.error.slice(0, 100),
|
|
1919
|
+
similarity: b.similarity,
|
|
1920
|
+
date: b.date.toISOString(),
|
|
1921
|
+
cause: b.cause,
|
|
1922
|
+
fix: b.fix,
|
|
1923
|
+
file: b.file,
|
|
1924
|
+
fix_diff: b.fixDiff?.slice(0, 200)
|
|
1925
|
+
})),
|
|
1926
|
+
message: bugs.length === 0
|
|
1927
|
+
? 'No similar bugs found in history'
|
|
1928
|
+
: `Found ${bugs.length} similar bug(s) in history`
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
case 'suggest_fix': {
|
|
1933
|
+
const error = args.error as string;
|
|
1934
|
+
const context = args.context as string | undefined;
|
|
1935
|
+
|
|
1936
|
+
const suggestions = engine.suggestFix(error, context);
|
|
1937
|
+
|
|
1938
|
+
return {
|
|
1939
|
+
total: suggestions.length,
|
|
1940
|
+
suggestions: suggestions.map(s => ({
|
|
1941
|
+
confidence: s.confidence,
|
|
1942
|
+
fix: s.fix,
|
|
1943
|
+
reason: s.reason,
|
|
1944
|
+
diff: s.diff,
|
|
1945
|
+
source: s.source,
|
|
1946
|
+
past_fix: s.pastFix ? {
|
|
1947
|
+
date: s.pastFix.date.toISOString(),
|
|
1948
|
+
file: s.pastFix.file
|
|
1949
|
+
} : null
|
|
1950
|
+
})),
|
|
1951
|
+
formatted: engine.formatFixSuggestions(suggestions),
|
|
1952
|
+
message: suggestions.length === 0
|
|
1953
|
+
? 'No fix suggestions available'
|
|
1954
|
+
: `Found ${suggestions.length} fix suggestion(s)`
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
// Phase 10: Architecture Enforcement tools
|
|
1959
|
+
case 'validate_pattern': {
|
|
1960
|
+
const code = args.code as string;
|
|
1961
|
+
const type = args.type as string | undefined;
|
|
1962
|
+
|
|
1963
|
+
const result = engine.validatePattern(code, type);
|
|
1964
|
+
|
|
1965
|
+
return {
|
|
1966
|
+
valid: result.valid,
|
|
1967
|
+
score: result.score,
|
|
1968
|
+
matched_pattern: result.matchedPattern,
|
|
1969
|
+
violations: result.violations.map(v => ({
|
|
1970
|
+
rule: v.rule,
|
|
1971
|
+
message: v.message,
|
|
1972
|
+
severity: v.severity,
|
|
1973
|
+
suggestion: v.suggestion
|
|
1974
|
+
})),
|
|
1975
|
+
suggestions: result.suggestions.map(s => ({
|
|
1976
|
+
description: s.description,
|
|
1977
|
+
code: s.code,
|
|
1978
|
+
priority: s.priority
|
|
1979
|
+
})),
|
|
1980
|
+
existing_alternatives: result.existingAlternatives.map(a => ({
|
|
1981
|
+
name: a.name,
|
|
1982
|
+
file: a.file,
|
|
1983
|
+
line: a.line,
|
|
1984
|
+
signature: a.signature,
|
|
1985
|
+
similarity: a.similarity
|
|
1986
|
+
})),
|
|
1987
|
+
formatted: engine.formatValidationResult(result),
|
|
1988
|
+
message: result.valid
|
|
1989
|
+
? `Pattern validation passed (score: ${result.score}/100)`
|
|
1990
|
+
: `Pattern validation: ${result.violations.length} issue(s) found (score: ${result.score}/100)`
|
|
1991
|
+
};
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
case 'suggest_existing': {
|
|
1995
|
+
const intent = args.intent as string;
|
|
1996
|
+
const limit = args.limit as number | undefined;
|
|
1997
|
+
|
|
1998
|
+
const suggestions = engine.suggestExisting(intent, limit);
|
|
1999
|
+
|
|
2000
|
+
return {
|
|
2001
|
+
total: suggestions.length,
|
|
2002
|
+
suggestions: suggestions.map(s => ({
|
|
2003
|
+
name: s.name,
|
|
2004
|
+
file: s.file,
|
|
2005
|
+
line: s.line,
|
|
2006
|
+
signature: s.signature,
|
|
2007
|
+
description: s.description,
|
|
2008
|
+
usage_count: s.usageCount,
|
|
2009
|
+
purpose: s.purpose,
|
|
2010
|
+
similarity: s.similarity
|
|
2011
|
+
})),
|
|
2012
|
+
formatted: engine.formatExistingSuggestions(suggestions),
|
|
2013
|
+
message: suggestions.length === 0
|
|
2014
|
+
? 'No existing functions found for this intent - this might be a new use case'
|
|
2015
|
+
: `Found ${suggestions.length} existing function(s) that might help`
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
case 'learn_pattern': {
|
|
2020
|
+
const code = args.code as string;
|
|
2021
|
+
const name = args.name as string;
|
|
2022
|
+
const description = args.description as string | undefined;
|
|
2023
|
+
const category = args.category as string | undefined;
|
|
2024
|
+
|
|
2025
|
+
const result = engine.learnPattern(code, name, description, category);
|
|
2026
|
+
|
|
2027
|
+
return {
|
|
2028
|
+
success: result.success,
|
|
2029
|
+
pattern_id: result.patternId,
|
|
2030
|
+
message: result.message
|
|
2031
|
+
};
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
case 'list_patterns': {
|
|
2035
|
+
const category = args.category as string | undefined;
|
|
2036
|
+
|
|
2037
|
+
const patterns = engine.listPatterns(category);
|
|
2038
|
+
|
|
2039
|
+
return {
|
|
2040
|
+
total: patterns.length,
|
|
2041
|
+
patterns: patterns.map(p => ({
|
|
2042
|
+
id: p.id,
|
|
2043
|
+
name: p.name,
|
|
2044
|
+
category: p.category,
|
|
2045
|
+
description: p.description,
|
|
2046
|
+
examples_count: p.examples.length,
|
|
2047
|
+
anti_patterns_count: p.antiPatterns.length,
|
|
2048
|
+
rules_count: p.rules.length,
|
|
2049
|
+
usage_count: p.usageCount,
|
|
2050
|
+
created_at: p.createdAt.toISOString()
|
|
2051
|
+
})),
|
|
2052
|
+
formatted: engine.formatPatternList(patterns),
|
|
2053
|
+
message: patterns.length === 0
|
|
2054
|
+
? 'No patterns found. Use learn_pattern to teach patterns.'
|
|
2055
|
+
: `Found ${patterns.length} pattern(s)`
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
case 'get_pattern': {
|
|
2060
|
+
const id = args.id as string;
|
|
2061
|
+
|
|
2062
|
+
const pattern = engine.getPattern(id);
|
|
2063
|
+
|
|
2064
|
+
if (!pattern) {
|
|
2065
|
+
return {
|
|
2066
|
+
error: `Pattern not found: ${id}`,
|
|
2067
|
+
message: 'Use list_patterns to see available patterns'
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
return {
|
|
2072
|
+
id: pattern.id,
|
|
2073
|
+
name: pattern.name,
|
|
2074
|
+
category: pattern.category,
|
|
2075
|
+
description: pattern.description,
|
|
2076
|
+
examples: pattern.examples.map(e => ({
|
|
2077
|
+
code: e.code,
|
|
2078
|
+
explanation: e.explanation,
|
|
2079
|
+
file: e.file
|
|
2080
|
+
})),
|
|
2081
|
+
anti_patterns: pattern.antiPatterns.map(a => ({
|
|
2082
|
+
code: a.code,
|
|
2083
|
+
explanation: a.explanation
|
|
2084
|
+
})),
|
|
2085
|
+
rules: pattern.rules.map(r => ({
|
|
2086
|
+
rule: r.rule,
|
|
2087
|
+
severity: r.severity
|
|
2088
|
+
})),
|
|
2089
|
+
usage_count: pattern.usageCount,
|
|
2090
|
+
created_at: pattern.createdAt.toISOString()
|
|
2091
|
+
};
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
case 'add_pattern_example': {
|
|
2095
|
+
const patternId = args.pattern_id as string;
|
|
2096
|
+
const code = args.code as string;
|
|
2097
|
+
const explanation = args.explanation as string;
|
|
2098
|
+
const isAntiPattern = args.is_anti_pattern as boolean | undefined;
|
|
2099
|
+
|
|
2100
|
+
const success = engine.addPatternExample(patternId, code, explanation, isAntiPattern);
|
|
2101
|
+
|
|
2102
|
+
if (!success) {
|
|
2103
|
+
return {
|
|
2104
|
+
success: false,
|
|
2105
|
+
error: `Pattern not found: ${patternId}`,
|
|
2106
|
+
message: 'Use list_patterns to see available patterns'
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
return {
|
|
2111
|
+
success: true,
|
|
2112
|
+
pattern_id: patternId,
|
|
2113
|
+
type: isAntiPattern ? 'anti-pattern' : 'example',
|
|
2114
|
+
message: `${isAntiPattern ? 'Anti-pattern' : 'Example'} added successfully`
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
case 'get_architecture_stats': {
|
|
2119
|
+
const stats = engine.getArchitectureStats();
|
|
2120
|
+
|
|
2121
|
+
return {
|
|
2122
|
+
patterns: {
|
|
2123
|
+
total: stats.patterns.total,
|
|
2124
|
+
by_category: stats.patterns.byCategory,
|
|
2125
|
+
top_patterns: stats.patterns.topPatterns
|
|
2126
|
+
},
|
|
2127
|
+
functions: {
|
|
2128
|
+
total: stats.functions.total,
|
|
2129
|
+
exported: stats.functions.exported,
|
|
2130
|
+
by_purpose: stats.functions.byPurpose
|
|
2131
|
+
},
|
|
2132
|
+
message: `${stats.patterns.total} patterns learned, ${stats.functions.total} functions indexed`
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
// Phase 11: Test-Aware Suggestions tools
|
|
2137
|
+
case 'get_related_tests': {
|
|
2138
|
+
const file = args.file as string;
|
|
2139
|
+
const fn = args.function as string | undefined;
|
|
2140
|
+
|
|
2141
|
+
const tests = engine.getRelatedTests(file, fn);
|
|
2142
|
+
|
|
2143
|
+
return {
|
|
2144
|
+
file,
|
|
2145
|
+
function: fn || null,
|
|
2146
|
+
total: tests.length,
|
|
2147
|
+
tests: tests.map(t => ({
|
|
2148
|
+
id: t.id,
|
|
2149
|
+
name: t.name,
|
|
2150
|
+
file: t.file,
|
|
2151
|
+
describes: t.describes,
|
|
2152
|
+
line_start: t.lineStart,
|
|
2153
|
+
line_end: t.lineEnd,
|
|
2154
|
+
assertions_count: t.assertions.length,
|
|
2155
|
+
covers_files: t.coversFiles,
|
|
2156
|
+
covers_functions: t.coversFunctions,
|
|
2157
|
+
last_status: t.lastStatus,
|
|
2158
|
+
last_run: t.lastRun?.toISOString()
|
|
2159
|
+
})),
|
|
2160
|
+
formatted: engine.formatTestList(tests),
|
|
2161
|
+
message: tests.length === 0
|
|
2162
|
+
? `No tests found for ${file}${fn ? ` function ${fn}` : ''}`
|
|
2163
|
+
: `Found ${tests.length} test(s) related to ${file}${fn ? ` function ${fn}` : ''}`
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
case 'check_tests': {
|
|
2168
|
+
const change = args.change as string;
|
|
2169
|
+
const file = args.file as string;
|
|
2170
|
+
|
|
2171
|
+
const result = engine.checkTests(change, file);
|
|
2172
|
+
|
|
2173
|
+
return {
|
|
2174
|
+
safe: result.safe,
|
|
2175
|
+
coverage_percent: result.coveragePercent,
|
|
2176
|
+
related_tests: result.relatedTests.length,
|
|
2177
|
+
would_pass: result.wouldPass.map(t => ({
|
|
2178
|
+
id: t.id,
|
|
2179
|
+
name: t.name,
|
|
2180
|
+
file: t.file
|
|
2181
|
+
})),
|
|
2182
|
+
would_fail: result.wouldFail.map(f => ({
|
|
2183
|
+
test_id: f.test.id,
|
|
2184
|
+
test_name: f.test.name,
|
|
2185
|
+
test_file: f.test.file,
|
|
2186
|
+
reason: f.reason,
|
|
2187
|
+
confidence: f.confidence,
|
|
2188
|
+
suggested_fix: f.suggestedFix,
|
|
2189
|
+
assertion: f.assertion ? {
|
|
2190
|
+
type: f.assertion.type,
|
|
2191
|
+
code: f.assertion.code,
|
|
2192
|
+
line: f.assertion.line
|
|
2193
|
+
} : null
|
|
2194
|
+
})),
|
|
2195
|
+
uncertain: result.uncertain.map(t => ({
|
|
2196
|
+
id: t.id,
|
|
2197
|
+
name: t.name,
|
|
2198
|
+
file: t.file
|
|
2199
|
+
})),
|
|
2200
|
+
suggested_updates: result.suggestedTestUpdates.map(u => ({
|
|
2201
|
+
file: u.file,
|
|
2202
|
+
test_name: u.testName,
|
|
2203
|
+
line: u.line,
|
|
2204
|
+
before: u.before,
|
|
2205
|
+
after: u.after,
|
|
2206
|
+
reason: u.reason
|
|
2207
|
+
})),
|
|
2208
|
+
formatted: engine.formatTestValidationResult(result),
|
|
2209
|
+
message: result.safe
|
|
2210
|
+
? `Change is safe - ${result.coveragePercent}% test coverage, ${result.wouldPass.length} tests would pass`
|
|
2211
|
+
: `Change may break ${result.wouldFail.length} test(s) - review suggested updates`
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
case 'suggest_test_update': {
|
|
2216
|
+
const change = args.change as string;
|
|
2217
|
+
const failingTests = args.failing_tests as string[] | undefined;
|
|
2218
|
+
|
|
2219
|
+
const updates = engine.suggestTestUpdate(change, failingTests);
|
|
2220
|
+
|
|
2221
|
+
return {
|
|
2222
|
+
total: updates.length,
|
|
2223
|
+
updates: updates.map(u => ({
|
|
2224
|
+
file: u.file,
|
|
2225
|
+
test_name: u.testName,
|
|
2226
|
+
line: u.line,
|
|
2227
|
+
before: u.before,
|
|
2228
|
+
after: u.after,
|
|
2229
|
+
reason: u.reason
|
|
2230
|
+
})),
|
|
2231
|
+
formatted: engine.formatTestUpdates(updates),
|
|
2232
|
+
message: updates.length === 0
|
|
2233
|
+
? 'No test updates needed'
|
|
2234
|
+
: `Generated ${updates.length} suggested test update(s)`
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
case 'get_test_coverage': {
|
|
2239
|
+
const file = args.file as string;
|
|
2240
|
+
|
|
2241
|
+
const coverage = engine.getTestCoverage(file);
|
|
2242
|
+
|
|
2243
|
+
return {
|
|
2244
|
+
file: coverage.file,
|
|
2245
|
+
total_tests: coverage.totalTests,
|
|
2246
|
+
coverage_percent: coverage.coveragePercent,
|
|
2247
|
+
covered_functions: coverage.coveredFunctions,
|
|
2248
|
+
uncovered_functions: coverage.uncoveredFunctions,
|
|
2249
|
+
formatted: engine.formatTestCoverage(coverage),
|
|
2250
|
+
message: coverage.coveragePercent === 100
|
|
2251
|
+
? `Full test coverage for ${file}`
|
|
2252
|
+
: coverage.uncoveredFunctions.length > 0
|
|
2253
|
+
? `${coverage.coveragePercent}% coverage - ${coverage.uncoveredFunctions.length} function(s) need tests`
|
|
2254
|
+
: `${coverage.totalTests} test(s) cover ${file}`
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
default:
|
|
2259
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
2260
|
+
}
|
|
2261
|
+
}
|