neuronlayer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CONTRIBUTING.md +127 -0
  2. package/LICENSE +21 -0
  3. package/README.md +305 -0
  4. package/dist/index.js +38016 -0
  5. package/esbuild.config.js +26 -0
  6. package/package.json +63 -0
  7. package/src/cli/commands.ts +382 -0
  8. package/src/core/adr-exporter.ts +253 -0
  9. package/src/core/architecture/architecture-enforcement.ts +228 -0
  10. package/src/core/architecture/duplicate-detector.ts +288 -0
  11. package/src/core/architecture/index.ts +6 -0
  12. package/src/core/architecture/pattern-learner.ts +306 -0
  13. package/src/core/architecture/pattern-library.ts +403 -0
  14. package/src/core/architecture/pattern-validator.ts +324 -0
  15. package/src/core/change-intelligence/bug-correlator.ts +444 -0
  16. package/src/core/change-intelligence/change-intelligence.ts +221 -0
  17. package/src/core/change-intelligence/change-tracker.ts +334 -0
  18. package/src/core/change-intelligence/fix-suggester.ts +340 -0
  19. package/src/core/change-intelligence/index.ts +5 -0
  20. package/src/core/code-verifier.ts +843 -0
  21. package/src/core/confidence/confidence-scorer.ts +251 -0
  22. package/src/core/confidence/conflict-checker.ts +289 -0
  23. package/src/core/confidence/index.ts +5 -0
  24. package/src/core/confidence/source-tracker.ts +263 -0
  25. package/src/core/confidence/warning-detector.ts +241 -0
  26. package/src/core/context-rot/compaction.ts +284 -0
  27. package/src/core/context-rot/context-health.ts +243 -0
  28. package/src/core/context-rot/context-rot-prevention.ts +213 -0
  29. package/src/core/context-rot/critical-context.ts +221 -0
  30. package/src/core/context-rot/drift-detector.ts +255 -0
  31. package/src/core/context-rot/index.ts +7 -0
  32. package/src/core/context.ts +263 -0
  33. package/src/core/decision-extractor.ts +339 -0
  34. package/src/core/decisions.ts +69 -0
  35. package/src/core/deja-vu.ts +421 -0
  36. package/src/core/engine.ts +1455 -0
  37. package/src/core/feature-context.ts +726 -0
  38. package/src/core/ghost-mode.ts +412 -0
  39. package/src/core/learning.ts +485 -0
  40. package/src/core/living-docs/activity-tracker.ts +296 -0
  41. package/src/core/living-docs/architecture-generator.ts +428 -0
  42. package/src/core/living-docs/changelog-generator.ts +348 -0
  43. package/src/core/living-docs/component-generator.ts +230 -0
  44. package/src/core/living-docs/doc-engine.ts +110 -0
  45. package/src/core/living-docs/doc-validator.ts +282 -0
  46. package/src/core/living-docs/index.ts +8 -0
  47. package/src/core/project-manager.ts +297 -0
  48. package/src/core/summarizer.ts +267 -0
  49. package/src/core/test-awareness/change-validator.ts +499 -0
  50. package/src/core/test-awareness/index.ts +5 -0
  51. package/src/index.ts +49 -0
  52. package/src/indexing/ast.ts +563 -0
  53. package/src/indexing/embeddings.ts +85 -0
  54. package/src/indexing/indexer.ts +245 -0
  55. package/src/indexing/watcher.ts +78 -0
  56. package/src/server/gateways/aggregator.ts +374 -0
  57. package/src/server/gateways/index.ts +473 -0
  58. package/src/server/gateways/memory-ghost.ts +343 -0
  59. package/src/server/gateways/memory-query.ts +452 -0
  60. package/src/server/gateways/memory-record.ts +346 -0
  61. package/src/server/gateways/memory-review.ts +410 -0
  62. package/src/server/gateways/memory-status.ts +517 -0
  63. package/src/server/gateways/memory-verify.ts +392 -0
  64. package/src/server/gateways/router.ts +434 -0
  65. package/src/server/gateways/types.ts +610 -0
  66. package/src/server/mcp.ts +154 -0
  67. package/src/server/resources.ts +85 -0
  68. package/src/server/tools.ts +2261 -0
  69. package/src/storage/database.ts +262 -0
  70. package/src/storage/tier1.ts +135 -0
  71. package/src/storage/tier2.ts +764 -0
  72. package/src/storage/tier3.ts +123 -0
  73. package/src/types/documentation.ts +619 -0
  74. package/src/types/index.ts +222 -0
  75. package/src/utils/config.ts +193 -0
  76. package/src/utils/files.ts +117 -0
  77. package/src/utils/time.ts +37 -0
  78. package/src/utils/tokens.ts +52 -0
@@ -0,0 +1,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
+ }