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