mcp-code-context 2.2.0 → 2.4.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 (132) hide show
  1. package/.io/RULES.md +12 -0
  2. package/CHANGELOG.md +114 -1
  3. package/README.md +28 -1
  4. package/dist/src/ast/writers/safeRename.d.ts +14 -0
  5. package/dist/src/ast/writers/safeRename.js +258 -0
  6. package/dist/src/ast/writers/safeRename.js.map +1 -0
  7. package/dist/src/ast/writers/symbolWriter.d.ts +2 -0
  8. package/dist/src/ast/writers/symbolWriter.js +9 -14
  9. package/dist/src/ast/writers/symbolWriter.js.map +1 -1
  10. package/dist/src/cache/astCache.d.ts +33 -0
  11. package/dist/src/cache/astCache.js +113 -0
  12. package/dist/src/cache/astCache.js.map +1 -0
  13. package/dist/src/handlers/readHandlers.d.ts +70 -0
  14. package/dist/src/handlers/readHandlers.js +431 -0
  15. package/dist/src/handlers/readHandlers.js.map +1 -0
  16. package/dist/src/handlers/utilHandlers.d.ts +31 -0
  17. package/dist/src/handlers/utilHandlers.js +69 -0
  18. package/dist/src/handlers/utilHandlers.js.map +1 -0
  19. package/dist/src/handlers/writeHandlers.d.ts +57 -0
  20. package/dist/src/handlers/writeHandlers.js +475 -0
  21. package/dist/src/handlers/writeHandlers.js.map +1 -0
  22. package/dist/src/index.d.ts +6 -143
  23. package/dist/src/index.js +77 -1091
  24. package/dist/src/index.js.map +1 -1
  25. package/dist/src/tools/searchCodePattern.d.ts +7 -2
  26. package/dist/src/tools/searchCodePattern.js +68 -4
  27. package/dist/src/tools/searchCodePattern.js.map +1 -1
  28. package/dist/src/utils/arrayUtils.d.ts +12 -0
  29. package/dist/src/utils/arrayUtils.js +27 -0
  30. package/dist/src/utils/arrayUtils.js.map +1 -0
  31. package/dist/src/utils/constants.d.ts +46 -0
  32. package/dist/src/utils/constants.js +112 -0
  33. package/dist/src/utils/constants.js.map +1 -0
  34. package/dist/src/utils/ignoreManager.d.ts +8 -0
  35. package/dist/src/utils/ignoreManager.js +49 -0
  36. package/dist/src/utils/ignoreManager.js.map +1 -1
  37. package/dist/src/utils/normalization.d.ts +49 -0
  38. package/dist/src/utils/normalization.js +173 -0
  39. package/dist/src/utils/normalization.js.map +1 -0
  40. package/dist/src/utils/projectRoot.d.ts +12 -5
  41. package/dist/src/utils/projectRoot.js +22 -20
  42. package/dist/src/utils/projectRoot.js.map +1 -1
  43. package/dist/src/utils/secureValidation.d.ts +15 -0
  44. package/dist/src/utils/secureValidation.js +41 -0
  45. package/dist/src/utils/secureValidation.js.map +1 -0
  46. package/dist/src/utils/transactionManager.d.ts +45 -0
  47. package/dist/src/utils/transactionManager.js +102 -0
  48. package/dist/src/utils/transactionManager.js.map +1 -0
  49. package/dist/src/utils/validation.d.ts +87 -0
  50. package/dist/src/utils/validation.js +231 -0
  51. package/dist/src/utils/validation.js.map +1 -0
  52. package/docs/ARCHITECTURE.md +300 -0
  53. package/docs/architecture/ADR-001-modular-handlers.md +119 -0
  54. package/llms.txt +10 -1
  55. package/package.json +5 -5
  56. package/AGENT_INSTRUCTIONS.md +0 -135
  57. package/DEPLOYMENT_INSTRUCTIONS.md +0 -110
  58. package/MEJORAS_PROPUESTAS.md +0 -324
  59. package/RELEASE_NOTES_v2.1.3.md +0 -78
  60. package/RELEASE_NOTES_v2.2.0.md +0 -286
  61. package/dist/src/tests/test-confirmation-flow.d.ts +0 -1
  62. package/dist/src/tests/test-confirmation-flow.js +0 -43
  63. package/dist/src/tests/test-confirmation-flow.js.map +0 -1
  64. package/dist/tests/debug-replace.d.ts +0 -1
  65. package/dist/tests/debug-replace.js +0 -17
  66. package/dist/tests/debug-replace.js.map +0 -1
  67. package/dist/tests/test-backup-system.d.ts +0 -7
  68. package/dist/tests/test-backup-system.js +0 -152
  69. package/dist/tests/test-backup-system.js.map +0 -1
  70. package/dist/tests/test-confirmation-flow.d.ts +0 -1
  71. package/dist/tests/test-confirmation-flow.js +0 -43
  72. package/dist/tests/test-confirmation-flow.js.map +0 -1
  73. package/dist/tests/test-dart.d.ts +0 -1
  74. package/dist/tests/test-dart.js +0 -242
  75. package/dist/tests/test-dart.js.map +0 -1
  76. package/dist/tests/test-new-tools.d.ts +0 -4
  77. package/dist/tests/test-new-tools.js +0 -147
  78. package/dist/tests/test-new-tools.js.map +0 -1
  79. package/dist/tests/test-php-ast.d.ts +0 -1
  80. package/dist/tests/test-php-ast.js +0 -152
  81. package/dist/tests/test-php-ast.js.map +0 -1
  82. package/dist/tests/test-php.d.ts +0 -1
  83. package/dist/tests/test-php.js +0 -234
  84. package/dist/tests/test-php.js.map +0 -1
  85. package/dist/tests/test-smoke.d.ts +0 -5
  86. package/dist/tests/test-smoke.js +0 -83
  87. package/dist/tests/test-smoke.js.map +0 -1
  88. package/dist/tests/writers/test-crlf-handling.d.ts +0 -4
  89. package/dist/tests/writers/test-crlf-handling.js +0 -73
  90. package/dist/tests/writers/test-crlf-handling.js.map +0 -1
  91. package/dist/tests/writers/test-write-smoke.d.ts +0 -9
  92. package/dist/tests/writers/test-write-smoke.js +0 -346
  93. package/dist/tests/writers/test-write-smoke.js.map +0 -1
  94. package/src/ast/dartCompressor.ts +0 -1013
  95. package/src/ast/phpCompressor.ts +0 -814
  96. package/src/ast/semanticCompressor.ts +0 -808
  97. package/src/ast/writers/dartWriter.ts +0 -648
  98. package/src/ast/writers/phpWriter.ts +0 -508
  99. package/src/ast/writers/pyWriter.ts +0 -446
  100. package/src/ast/writers/symbolWriter.ts +0 -277
  101. package/src/ast/writers/tsWriter.ts +0 -418
  102. package/src/index.ts +0 -1784
  103. package/src/tools/readFileLines.ts +0 -104
  104. package/src/tools/searchCodePattern.ts +0 -129
  105. package/src/utils/backupManager.ts +0 -141
  106. package/src/utils/confirmationCache.ts +0 -61
  107. package/src/utils/diffEngine.ts +0 -223
  108. package/src/utils/fuzzyMatch.ts +0 -86
  109. package/src/utils/ignoreManager.ts +0 -144
  110. package/src/utils/projectRoot.ts +0 -44
  111. package/tests/test-backup-system.ts +0 -174
  112. package/tests/test-confirmation-flow.ts +0 -49
  113. package/tests/test-dart.d.ts +0 -1
  114. package/tests/test-dart.js +0 -209
  115. package/tests/test-dart.js.map +0 -1
  116. package/tests/test-dart.ts +0 -265
  117. package/tests/test-new-tools.ts +0 -165
  118. package/tests/test-php-ast.d.ts +0 -1
  119. package/tests/test-php-ast.js +0 -152
  120. package/tests/test-php-ast.js.map +0 -1
  121. package/tests/test-php-ast.ts +0 -155
  122. package/tests/test-php.d.ts +0 -1
  123. package/tests/test-php.js +0 -234
  124. package/tests/test-php.js.map +0 -1
  125. package/tests/test-php.ts +0 -261
  126. package/tests/test-smoke.d.ts +0 -5
  127. package/tests/test-smoke.js +0 -83
  128. package/tests/test-smoke.js.map +0 -1
  129. package/tests/test-smoke.ts +0 -93
  130. package/tests/writers/test-crlf-handling.ts +0 -100
  131. package/tests/writers/test-write-smoke.ts +0 -568
  132. package/tsconfig.json +0 -19
@@ -1,418 +0,0 @@
1
- /**
2
- * tsWriter.ts — TypeScript / JavaScript Surgical Writer
3
- *
4
- * Uses ts-morph to perform AST-aware code modifications on TypeScript
5
- * and JavaScript files. This is the most robust writer because ts-morph
6
- * provides a full read-write AST with source manipulation capabilities.
7
- *
8
- * Supports: .ts, .tsx, .js, .jsx, .mts, .mjs, .cts, .cjs
9
- */
10
-
11
- import {
12
- Project,
13
- type SourceFile,
14
- type ClassDeclaration,
15
- SyntaxKind,
16
- IndentationText,
17
- } from "ts-morph";
18
-
19
- import * as path from "node:path";
20
-
21
- // ─── Types ──────────────────────────────────────────────────────────
22
-
23
- export interface WriteResult {
24
- success: boolean;
25
- newContent: string;
26
- symbolsAffected: string[];
27
- error?: string;
28
- }
29
-
30
- // ─── Singleton Project ──────────────────────────────────────────────
31
-
32
- let _project: Project | null = null;
33
-
34
- function getProject(): Project {
35
- if (!_project) {
36
- _project = new Project({
37
- compilerOptions: {
38
- allowJs: true,
39
- jsx: 2, // React
40
- },
41
- useInMemoryFileSystem: true,
42
- });
43
- }
44
- return _project;
45
- }
46
-
47
- function createSourceFile(content: string, filePath: string): SourceFile {
48
- const project = getProject();
49
- const ext = path.extname(filePath);
50
- const tempName = `write_${Date.now()}_${Math.random().toString(36).slice(2)}${ext}`;
51
- return project.createSourceFile(tempName, content, { overwrite: true });
52
- }
53
-
54
- function cleanupSourceFile(sf: SourceFile): void {
55
- try {
56
- sf.getProject().removeSourceFile(sf);
57
- } catch {
58
- // Ignore cleanup errors
59
- }
60
- }
61
-
62
- // ─── Replace Symbol ─────────────────────────────────────────────────
63
-
64
- /**
65
- * Replace the full text of a named symbol in a TypeScript/JavaScript file.
66
- * The newContent should be the complete replacement (signature + body).
67
- * When className is provided, scopes the search to within that class only.
68
- */
69
- export function replaceTSSymbol(
70
- content: string,
71
- symbolName: string,
72
- newContent: string,
73
- filePath: string,
74
- className?: string,
75
- ): WriteResult {
76
- const sf = createSourceFile(content, filePath);
77
-
78
- try {
79
- const node = findNode(sf, symbolName, className);
80
- if (!node) {
81
- return {
82
- success: false,
83
- newContent: content,
84
- symbolsAffected: [],
85
- error: className
86
- ? `Symbol "${symbolName}" not found in class "${className}"`
87
- : `Symbol "${symbolName}" not found in file`,
88
- };
89
- }
90
-
91
- // Replace the node's full text with the new content
92
- node.replaceWithText(newContent.trim());
93
-
94
- const result = sf.getFullText();
95
-
96
- // Validate that the result is parseable
97
- const validationSf = createSourceFile(result, filePath);
98
- const diagnostics = validationSf.getPreEmitDiagnostics();
99
- const hasSyntaxErrors = diagnostics.some(
100
- (d) => d.getCategory() === 1, // DiagnosticCategory.Error
101
- );
102
- cleanupSourceFile(validationSf);
103
-
104
- if (hasSyntaxErrors) {
105
- // Still return the result but mark potential syntax issues
106
- // We don't reject because some "errors" are just type errors, not syntax
107
- }
108
-
109
- return {
110
- success: true,
111
- newContent: result,
112
- symbolsAffected: [symbolName],
113
- };
114
- } catch (error: unknown) {
115
- const msg = error instanceof Error ? error.message : String(error);
116
- return {
117
- success: false,
118
- newContent: content,
119
- symbolsAffected: [],
120
- error: `Failed to replace symbol "${symbolName}": ${msg}`,
121
- };
122
- } finally {
123
- cleanupSourceFile(sf);
124
- }
125
- }
126
-
127
- // ─── Insert Code ────────────────────────────────────────────────────
128
-
129
- export type InsertPosition = "before" | "after" | "inside_start" | "inside_end";
130
-
131
- /**
132
- * Insert new code at a precise location relative to an anchor symbol.
133
- * When className is provided, scopes the anchor search to within that class only.
134
- */
135
- export function insertTSCode(
136
- content: string,
137
- code: string,
138
- anchorSymbol: string | null,
139
- position: InsertPosition,
140
- filePath: string,
141
- className?: string,
142
- ): WriteResult {
143
- const sf = createSourceFile(content, filePath);
144
-
145
- try {
146
- if (!anchorSymbol) {
147
- // No anchor — append at end of file
148
- sf.addStatements(code.trim());
149
- return {
150
- success: true,
151
- newContent: sf.getFullText(),
152
- symbolsAffected: [],
153
- };
154
- }
155
-
156
- const node = findNode(sf, anchorSymbol, className);
157
- if (!node) {
158
- return {
159
- success: false,
160
- newContent: content,
161
- symbolsAffected: [],
162
- error: className
163
- ? `Anchor symbol "${anchorSymbol}" not found in class "${className}"`
164
- : `Anchor symbol "${anchorSymbol}" not found`,
165
- };
166
- }
167
-
168
- const trimmedCode = code.trim();
169
-
170
- switch (position) {
171
- case "before": {
172
- const startPos = node.getStart();
173
- sf.insertText(startPos, trimmedCode + "\n\n");
174
- break;
175
- }
176
- case "after": {
177
- const endPos = node.getEnd();
178
- sf.insertText(endPos, "\n\n" + trimmedCode);
179
- break;
180
- }
181
- case "inside_start": {
182
- // Only valid for classes/interfaces
183
- const cls =
184
- sf.getClass(anchorSymbol) || sf.getInterface(anchorSymbol);
185
- if (!cls) {
186
- return {
187
- success: false,
188
- newContent: content,
189
- symbolsAffected: [],
190
- error: `"inside_start" requires a class or interface. "${anchorSymbol}" is not one.`,
191
- };
192
- }
193
- if ("insertMember" in cls) {
194
- (cls as ClassDeclaration).insertMember(0, trimmedCode);
195
- }
196
- break;
197
- }
198
- case "inside_end": {
199
- const cls2 =
200
- sf.getClass(anchorSymbol) || sf.getInterface(anchorSymbol);
201
- if (!cls2) {
202
- return {
203
- success: false,
204
- newContent: content,
205
- symbolsAffected: [],
206
- error: `"inside_end" requires a class or interface. "${anchorSymbol}" is not one.`,
207
- };
208
- }
209
- if ("addMember" in cls2) {
210
- (cls2 as ClassDeclaration).addMember(trimmedCode);
211
- }
212
- break;
213
- }
214
- }
215
-
216
- return {
217
- success: true,
218
- newContent: sf.getFullText(),
219
- symbolsAffected: [anchorSymbol],
220
- };
221
- } catch (error: unknown) {
222
- const msg = error instanceof Error ? error.message : String(error);
223
- return {
224
- success: false,
225
- newContent: content,
226
- symbolsAffected: [],
227
- error: `Failed to insert code: ${msg}`,
228
- };
229
- } finally {
230
- cleanupSourceFile(sf);
231
- }
232
- }
233
-
234
- // ─── Rename Symbol ──────────────────────────────────────────────────
235
-
236
- /**
237
- * Rename all occurrences of a symbol within a single TypeScript/JavaScript file.
238
- * This performs a smart rename that understands scoping.
239
- */
240
- export function renameTSSymbol(
241
- content: string,
242
- oldName: string,
243
- newName: string,
244
- filePath: string,
245
- ): WriteResult {
246
- const sf = createSourceFile(content, filePath);
247
-
248
- try {
249
- const node = findNode(sf, oldName);
250
- if (!node) {
251
- return {
252
- success: false,
253
- newContent: content,
254
- symbolsAffected: [],
255
- error: `Symbol "${oldName}" not found for renaming`,
256
- };
257
- }
258
-
259
- // Use regex-based replacement scoped to identifiers
260
- // ts-morph rename in single-file mode: find all references within the file
261
- const fullText = sf.getFullText();
262
-
263
- // Smart rename: replace whole-word occurrences of oldName with newName
264
- // This is not a naive replace — it respects word boundaries
265
- const escaped = oldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
266
- const regex = new RegExp(`\\b${escaped}\\b`, "g");
267
- const newText = fullText.replace(regex, newName);
268
-
269
- // Validate result is parseable
270
- const validationSf = createSourceFile(newText, filePath);
271
- cleanupSourceFile(validationSf);
272
-
273
- return {
274
- success: true,
275
- newContent: newText,
276
- symbolsAffected: [oldName],
277
- };
278
- } catch (error: unknown) {
279
- const msg = error instanceof Error ? error.message : String(error);
280
- return {
281
- success: false,
282
- newContent: content,
283
- symbolsAffected: [],
284
- error: `Failed to rename "${oldName}" to "${newName}": ${msg}`,
285
- };
286
- } finally {
287
- cleanupSourceFile(sf);
288
- }
289
- }
290
-
291
- // ─── Remove Symbol ──────────────────────────────────────────────────
292
-
293
- /**
294
- * Remove a named symbol from a TypeScript/JavaScript file.
295
- * Returns the file content without the symbol.
296
- * When className is provided, scopes the search to within that class only.
297
- */
298
- export function removeTSSymbol(
299
- content: string,
300
- symbolName: string,
301
- filePath: string,
302
- className?: string,
303
- ): WriteResult {
304
- const sf = createSourceFile(content, filePath);
305
-
306
- try {
307
- const node = findNode(sf, symbolName, className);
308
- if (!node) {
309
- return {
310
- success: false,
311
- newContent: content,
312
- symbolsAffected: [],
313
- error: className
314
- ? `Symbol "${symbolName}" not found in class "${className}" for removal`
315
- : `Symbol "${symbolName}" not found for removal`,
316
- };
317
- }
318
-
319
- node.remove();
320
-
321
- // Clean up double blank lines left behind
322
- let result = sf.getFullText();
323
- result = result.replace(/\n{3,}/g, "\n\n");
324
-
325
- return {
326
- success: true,
327
- newContent: result,
328
- symbolsAffected: [symbolName],
329
- };
330
- } catch (error: unknown) {
331
- const msg = error instanceof Error ? error.message : String(error);
332
- return {
333
- success: false,
334
- newContent: content,
335
- symbolsAffected: [],
336
- error: `Failed to remove symbol "${symbolName}": ${msg}`,
337
- };
338
- } finally {
339
- cleanupSourceFile(sf);
340
- }
341
- }
342
-
343
- // ─── Node Finder ────────────────────────────────────────────────────
344
-
345
- /**
346
- * Find a named declaration node in a source file.
347
- * Searches: functions, classes, interfaces, type aliases, enums,
348
- * class methods, getters, setters, and variable declarations.
349
- * When className is provided, scopes method/property search to within that class only.
350
- */
351
- function findNode(sf: SourceFile, symbolName: string, className?: string) {
352
- // If className is provided, scope search to within that class
353
- if (className) {
354
- const cls = sf.getClass(className);
355
- if (!cls) return null;
356
-
357
- const method = cls.getMethod(symbolName);
358
- if (method) return method;
359
-
360
- const getter = cls.getGetAccessor(symbolName);
361
- if (getter) return getter;
362
-
363
- const setter = cls.getSetAccessor(symbolName);
364
- if (setter) return setter;
365
-
366
- const prop = cls.getProperty(symbolName);
367
- if (prop) return prop;
368
-
369
- return null;
370
- }
371
-
372
- // Top-level function
373
- const func = sf.getFunction(symbolName);
374
- if (func) return func;
375
-
376
- // Class
377
- const cls = sf.getClass(symbolName);
378
- if (cls) return cls;
379
-
380
- // Interface
381
- const iface = sf.getInterface(symbolName);
382
- if (iface) return iface;
383
-
384
- // Type alias
385
- const typeAlias = sf.getTypeAlias(symbolName);
386
- if (typeAlias) return typeAlias;
387
-
388
- // Enum
389
- const enumDecl = sf.getEnum(symbolName);
390
- if (enumDecl) return enumDecl;
391
-
392
- // Methods inside classes
393
- for (const c of sf.getClasses()) {
394
- const method = c.getMethod(symbolName);
395
- if (method) return method;
396
-
397
- const getter = c.getGetAccessor(symbolName);
398
- if (getter) return getter;
399
-
400
- const setter = c.getSetAccessor(symbolName);
401
- if (setter) return setter;
402
-
403
- // Properties
404
- const prop = c.getProperty(symbolName);
405
- if (prop) return prop;
406
- }
407
-
408
- // Variable declarations (const, let, var — including arrow functions)
409
- for (const varStmt of sf.getVariableStatements()) {
410
- for (const decl of varStmt.getDeclarations()) {
411
- if (decl.getName() === symbolName) {
412
- return varStmt;
413
- }
414
- }
415
- }
416
-
417
- return null;
418
- }