mcp-code-context 2.2.0 → 2.3.1

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 (128) hide show
  1. package/.io/RULES.md +12 -0
  2. package/CHANGELOG.md +88 -1
  3. package/README.md +27 -0
  4. package/RELEASE_NOTES_v2.3.0.md +228 -0
  5. package/RELEASE_NOTES_v2.3.1.md +110 -0
  6. package/dist/src/ast/writers/safeRename.d.ts +14 -0
  7. package/dist/src/ast/writers/safeRename.js +258 -0
  8. package/dist/src/ast/writers/safeRename.js.map +1 -0
  9. package/dist/src/ast/writers/symbolWriter.d.ts +2 -0
  10. package/dist/src/ast/writers/symbolWriter.js +9 -14
  11. package/dist/src/ast/writers/symbolWriter.js.map +1 -1
  12. package/dist/src/cache/astCache.d.ts +33 -0
  13. package/dist/src/cache/astCache.js +113 -0
  14. package/dist/src/cache/astCache.js.map +1 -0
  15. package/dist/src/handlers/readHandlers.d.ts +70 -0
  16. package/dist/src/handlers/readHandlers.js +431 -0
  17. package/dist/src/handlers/readHandlers.js.map +1 -0
  18. package/dist/src/handlers/utilHandlers.d.ts +31 -0
  19. package/dist/src/handlers/utilHandlers.js +69 -0
  20. package/dist/src/handlers/utilHandlers.js.map +1 -0
  21. package/dist/src/handlers/writeHandlers.d.ts +57 -0
  22. package/dist/src/handlers/writeHandlers.js +475 -0
  23. package/dist/src/handlers/writeHandlers.js.map +1 -0
  24. package/dist/src/index.d.ts +6 -143
  25. package/dist/src/index.js +77 -1091
  26. package/dist/src/index.js.map +1 -1
  27. package/dist/src/utils/arrayUtils.d.ts +12 -0
  28. package/dist/src/utils/arrayUtils.js +27 -0
  29. package/dist/src/utils/arrayUtils.js.map +1 -0
  30. package/dist/src/utils/constants.d.ts +46 -0
  31. package/dist/src/utils/constants.js +112 -0
  32. package/dist/src/utils/constants.js.map +1 -0
  33. package/dist/src/utils/ignoreManager.d.ts +8 -0
  34. package/dist/src/utils/ignoreManager.js +49 -0
  35. package/dist/src/utils/ignoreManager.js.map +1 -1
  36. package/dist/src/utils/normalization.d.ts +49 -0
  37. package/dist/src/utils/normalization.js +173 -0
  38. package/dist/src/utils/normalization.js.map +1 -0
  39. package/dist/src/utils/projectRoot.d.ts +12 -5
  40. package/dist/src/utils/projectRoot.js +22 -20
  41. package/dist/src/utils/projectRoot.js.map +1 -1
  42. package/dist/src/utils/secureValidation.d.ts +15 -0
  43. package/dist/src/utils/secureValidation.js +41 -0
  44. package/dist/src/utils/secureValidation.js.map +1 -0
  45. package/dist/src/utils/transactionManager.d.ts +45 -0
  46. package/dist/src/utils/transactionManager.js +102 -0
  47. package/dist/src/utils/transactionManager.js.map +1 -0
  48. package/dist/src/utils/validation.d.ts +87 -0
  49. package/dist/src/utils/validation.js +231 -0
  50. package/dist/src/utils/validation.js.map +1 -0
  51. package/docs/ARCHITECTURE.md +300 -0
  52. package/docs/architecture/ADR-001-modular-handlers.md +119 -0
  53. package/package.json +5 -5
  54. package/AGENT_INSTRUCTIONS.md +0 -135
  55. package/DEPLOYMENT_INSTRUCTIONS.md +0 -110
  56. package/MEJORAS_PROPUESTAS.md +0 -324
  57. package/dist/src/tests/test-confirmation-flow.d.ts +0 -1
  58. package/dist/src/tests/test-confirmation-flow.js +0 -43
  59. package/dist/src/tests/test-confirmation-flow.js.map +0 -1
  60. package/dist/tests/debug-replace.d.ts +0 -1
  61. package/dist/tests/debug-replace.js +0 -17
  62. package/dist/tests/debug-replace.js.map +0 -1
  63. package/dist/tests/test-backup-system.d.ts +0 -7
  64. package/dist/tests/test-backup-system.js +0 -152
  65. package/dist/tests/test-backup-system.js.map +0 -1
  66. package/dist/tests/test-confirmation-flow.d.ts +0 -1
  67. package/dist/tests/test-confirmation-flow.js +0 -43
  68. package/dist/tests/test-confirmation-flow.js.map +0 -1
  69. package/dist/tests/test-dart.d.ts +0 -1
  70. package/dist/tests/test-dart.js +0 -242
  71. package/dist/tests/test-dart.js.map +0 -1
  72. package/dist/tests/test-new-tools.d.ts +0 -4
  73. package/dist/tests/test-new-tools.js +0 -147
  74. package/dist/tests/test-new-tools.js.map +0 -1
  75. package/dist/tests/test-php-ast.d.ts +0 -1
  76. package/dist/tests/test-php-ast.js +0 -152
  77. package/dist/tests/test-php-ast.js.map +0 -1
  78. package/dist/tests/test-php.d.ts +0 -1
  79. package/dist/tests/test-php.js +0 -234
  80. package/dist/tests/test-php.js.map +0 -1
  81. package/dist/tests/test-smoke.d.ts +0 -5
  82. package/dist/tests/test-smoke.js +0 -83
  83. package/dist/tests/test-smoke.js.map +0 -1
  84. package/dist/tests/writers/test-crlf-handling.d.ts +0 -4
  85. package/dist/tests/writers/test-crlf-handling.js +0 -73
  86. package/dist/tests/writers/test-crlf-handling.js.map +0 -1
  87. package/dist/tests/writers/test-write-smoke.d.ts +0 -9
  88. package/dist/tests/writers/test-write-smoke.js +0 -346
  89. package/dist/tests/writers/test-write-smoke.js.map +0 -1
  90. package/src/ast/dartCompressor.ts +0 -1013
  91. package/src/ast/phpCompressor.ts +0 -814
  92. package/src/ast/semanticCompressor.ts +0 -808
  93. package/src/ast/writers/dartWriter.ts +0 -648
  94. package/src/ast/writers/phpWriter.ts +0 -508
  95. package/src/ast/writers/pyWriter.ts +0 -446
  96. package/src/ast/writers/symbolWriter.ts +0 -277
  97. package/src/ast/writers/tsWriter.ts +0 -418
  98. package/src/index.ts +0 -1784
  99. package/src/tools/readFileLines.ts +0 -104
  100. package/src/tools/searchCodePattern.ts +0 -129
  101. package/src/utils/backupManager.ts +0 -141
  102. package/src/utils/confirmationCache.ts +0 -61
  103. package/src/utils/diffEngine.ts +0 -223
  104. package/src/utils/fuzzyMatch.ts +0 -86
  105. package/src/utils/ignoreManager.ts +0 -144
  106. package/src/utils/projectRoot.ts +0 -44
  107. package/tests/test-backup-system.ts +0 -174
  108. package/tests/test-confirmation-flow.ts +0 -49
  109. package/tests/test-dart.d.ts +0 -1
  110. package/tests/test-dart.js +0 -209
  111. package/tests/test-dart.js.map +0 -1
  112. package/tests/test-dart.ts +0 -265
  113. package/tests/test-new-tools.ts +0 -165
  114. package/tests/test-php-ast.d.ts +0 -1
  115. package/tests/test-php-ast.js +0 -152
  116. package/tests/test-php-ast.js.map +0 -1
  117. package/tests/test-php-ast.ts +0 -155
  118. package/tests/test-php.d.ts +0 -1
  119. package/tests/test-php.js +0 -234
  120. package/tests/test-php.js.map +0 -1
  121. package/tests/test-php.ts +0 -261
  122. package/tests/test-smoke.d.ts +0 -5
  123. package/tests/test-smoke.js +0 -83
  124. package/tests/test-smoke.js.map +0 -1
  125. package/tests/test-smoke.ts +0 -93
  126. package/tests/writers/test-crlf-handling.ts +0 -100
  127. package/tests/writers/test-write-smoke.ts +0 -568
  128. 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
- }