mcp-code-context 3.6.2 → 3.7.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 (181) hide show
  1. package/CHANGELOG.md +140 -268
  2. package/INSTRUCTIONS.md +31 -12
  3. package/README.md +223 -107
  4. package/SECURITY.md +55 -21
  5. package/dist/src/core/cacheManager.d.ts +4 -5
  6. package/dist/src/core/cacheManager.js +49 -33
  7. package/dist/src/core/cacheManager.js.map +1 -1
  8. package/dist/src/core/engine.d.ts +1 -1
  9. package/dist/src/core/engine.js +3 -3
  10. package/dist/src/core/engine.js.map +1 -1
  11. package/dist/src/core/indexManager.d.ts +58 -0
  12. package/dist/src/core/indexManager.js +252 -0
  13. package/dist/src/core/indexManager.js.map +1 -0
  14. package/dist/src/core/indexManagerQueries.d.ts +14 -0
  15. package/dist/src/core/indexManagerQueries.js +95 -0
  16. package/dist/src/core/indexManagerQueries.js.map +1 -0
  17. package/dist/src/core/sessionManager.d.ts +117 -0
  18. package/dist/src/core/sessionManager.js +231 -0
  19. package/dist/src/core/sessionManager.js.map +1 -0
  20. package/dist/src/core/validator.d.ts +1 -1
  21. package/dist/src/core/validator.js +18 -10
  22. package/dist/src/core/validator.js.map +1 -1
  23. package/dist/src/index.d.ts +1 -13
  24. package/dist/src/index.js +25 -999
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/operations/astTransform.d.ts +38 -0
  27. package/dist/src/operations/astTransform.js +239 -0
  28. package/dist/src/operations/astTransform.js.map +1 -0
  29. package/dist/src/operations/compress.d.ts +5 -3
  30. package/dist/src/operations/compress.js +73 -7
  31. package/dist/src/operations/compress.js.map +1 -1
  32. package/dist/src/operations/confirmationStore.d.ts +35 -8
  33. package/dist/src/operations/confirmationStore.js +112 -14
  34. package/dist/src/operations/confirmationStore.js.map +1 -1
  35. package/dist/src/operations/impactAnalysis.d.ts +9 -0
  36. package/dist/src/operations/impactAnalysis.js +61 -0
  37. package/dist/src/operations/impactAnalysis.js.map +1 -0
  38. package/dist/src/operations/pendingStore.d.ts +51 -0
  39. package/dist/src/operations/pendingStore.js +201 -0
  40. package/dist/src/operations/pendingStore.js.map +1 -0
  41. package/dist/src/operations/pendingStoreInit.d.ts +6 -0
  42. package/dist/src/operations/pendingStoreInit.js +42 -0
  43. package/dist/src/operations/pendingStoreInit.js.map +1 -0
  44. package/dist/src/operations/read.d.ts +6 -92
  45. package/dist/src/operations/read.js +4 -445
  46. package/dist/src/operations/read.js.map +1 -1
  47. package/dist/src/operations/readCore.d.ts +33 -0
  48. package/dist/src/operations/readCore.js +142 -0
  49. package/dist/src/operations/readCore.js.map +1 -0
  50. package/dist/src/operations/rename.d.ts +13 -0
  51. package/dist/src/operations/rename.js +130 -0
  52. package/dist/src/operations/rename.js.map +1 -0
  53. package/dist/src/operations/searchOperation.d.ts +16 -0
  54. package/dist/src/operations/searchOperation.js +113 -0
  55. package/dist/src/operations/searchOperation.js.map +1 -0
  56. package/dist/src/operations/symbolQuery.d.ts +38 -0
  57. package/dist/src/operations/symbolQuery.js +194 -0
  58. package/dist/src/operations/symbolQuery.js.map +1 -0
  59. package/dist/src/operations/write.d.ts +11 -14
  60. package/dist/src/operations/write.js +6 -125
  61. package/dist/src/operations/write.js.map +1 -1
  62. package/dist/src/parsers/base.d.ts +1 -1
  63. package/dist/src/parsers/base.js +1 -1
  64. package/dist/src/parsers/csharp.d.ts +12 -0
  65. package/dist/src/parsers/csharp.js +67 -0
  66. package/dist/src/parsers/csharp.js.map +1 -0
  67. package/dist/src/parsers/dartTreeSitter.d.ts +1 -1
  68. package/dist/src/parsers/dartTreeSitter.js +1 -1
  69. package/dist/src/parsers/go.d.ts +11 -0
  70. package/dist/src/parsers/go.js +74 -0
  71. package/dist/src/parsers/go.js.map +1 -0
  72. package/dist/src/parsers/java.d.ts +12 -0
  73. package/dist/src/parsers/java.js +62 -0
  74. package/dist/src/parsers/java.js.map +1 -0
  75. package/dist/src/parsers/kotlin.d.ts +12 -0
  76. package/dist/src/parsers/kotlin.js +75 -0
  77. package/dist/src/parsers/kotlin.js.map +1 -0
  78. package/dist/src/parsers/php.d.ts +1 -1
  79. package/dist/src/parsers/php.js +1 -1
  80. package/dist/src/parsers/python.d.ts +1 -1
  81. package/dist/src/parsers/python.js +1 -1
  82. package/dist/src/parsers/registry.d.ts +1 -1
  83. package/dist/src/parsers/registry.js +63 -2
  84. package/dist/src/parsers/registry.js.map +1 -1
  85. package/dist/src/parsers/ruby.d.ts +12 -0
  86. package/dist/src/parsers/ruby.js +73 -0
  87. package/dist/src/parsers/ruby.js.map +1 -0
  88. package/dist/src/parsers/rust.d.ts +12 -0
  89. package/dist/src/parsers/rust.js +66 -0
  90. package/dist/src/parsers/rust.js.map +1 -0
  91. package/dist/src/parsers/typescript.d.ts +1 -1
  92. package/dist/src/parsers/typescript.js +28 -38
  93. package/dist/src/parsers/typescript.js.map +1 -1
  94. package/dist/src/tools/adminHandlers.d.ts +67 -0
  95. package/dist/src/tools/adminHandlers.js +129 -0
  96. package/dist/src/tools/adminHandlers.js.map +1 -0
  97. package/dist/src/tools/context.d.ts +9 -0
  98. package/dist/src/tools/context.js +21 -0
  99. package/dist/src/tools/context.js.map +1 -0
  100. package/dist/src/tools/handlers.d.ts +4 -0
  101. package/dist/src/tools/handlers.js +5 -0
  102. package/dist/src/tools/handlers.js.map +1 -0
  103. package/dist/src/tools/pipeline.d.ts +6 -0
  104. package/dist/src/tools/pipeline.js +100 -0
  105. package/dist/src/tools/pipeline.js.map +1 -0
  106. package/dist/src/tools/readHandlers.d.ts +55 -0
  107. package/dist/src/tools/readHandlers.js +220 -0
  108. package/dist/src/tools/readHandlers.js.map +1 -0
  109. package/dist/src/tools/toolDefinitions.d.ts +1037 -0
  110. package/dist/src/tools/toolDefinitions.js +13 -0
  111. package/dist/src/tools/toolDefinitions.js.map +1 -0
  112. package/dist/src/tools/toolDefinitionsAdmin.d.ts +89 -0
  113. package/dist/src/tools/toolDefinitionsAdmin.js +99 -0
  114. package/dist/src/tools/toolDefinitionsAdmin.js.map +1 -0
  115. package/dist/src/tools/toolDefinitionsCore.d.ts +949 -0
  116. package/dist/src/tools/toolDefinitionsCore.js +267 -0
  117. package/dist/src/tools/toolDefinitionsCore.js.map +1 -0
  118. package/dist/src/tools/writeHandlers.d.ts +37 -0
  119. package/dist/src/tools/writeHandlers.js +199 -0
  120. package/dist/src/tools/writeHandlers.js.map +1 -0
  121. package/dist/src/utils/appDir.d.ts +19 -0
  122. package/dist/src/utils/appDir.js +50 -0
  123. package/dist/src/utils/appDir.js.map +1 -0
  124. package/dist/src/utils/auditLogWriter.d.ts +9 -0
  125. package/dist/src/utils/auditLogWriter.js +49 -0
  126. package/dist/src/utils/auditLogWriter.js.map +1 -0
  127. package/dist/src/utils/auditLogger.d.ts +1 -10
  128. package/dist/src/utils/auditLogger.js +14 -109
  129. package/dist/src/utils/auditLogger.js.map +1 -1
  130. package/dist/src/utils/auditQuery.d.ts +23 -0
  131. package/dist/src/utils/auditQuery.js +67 -0
  132. package/dist/src/utils/auditQuery.js.map +1 -0
  133. package/dist/src/utils/backupManager.d.ts +2 -2
  134. package/dist/src/utils/backupManager.js +29 -12
  135. package/dist/src/utils/backupManager.js.map +1 -1
  136. package/dist/src/utils/constants.d.ts +1 -1
  137. package/dist/src/utils/constants.js +8 -1
  138. package/dist/src/utils/constants.js.map +1 -1
  139. package/dist/src/utils/diff.d.ts +12 -1
  140. package/dist/src/utils/diff.js +41 -1
  141. package/dist/src/utils/diff.js.map +1 -1
  142. package/dist/src/utils/fileLock.d.ts +7 -2
  143. package/dist/src/utils/fileLock.js +14 -11
  144. package/dist/src/utils/fileLock.js.map +1 -1
  145. package/dist/src/utils/fileWalker.d.ts +1 -1
  146. package/dist/src/utils/fileWalker.js +1 -1
  147. package/dist/src/utils/fileWatcher.d.ts +1 -1
  148. package/dist/src/utils/fileWatcher.js +1 -1
  149. package/dist/src/utils/fuzzySearch.d.ts +1 -1
  150. package/dist/src/utils/fuzzySearch.js +1 -1
  151. package/dist/src/utils/logger.d.ts +1 -1
  152. package/dist/src/utils/logger.js +2 -2
  153. package/dist/src/utils/logger.js.map +1 -1
  154. package/dist/src/utils/rateLimiter.d.ts +9 -1
  155. package/dist/src/utils/rateLimiter.js +12 -1
  156. package/dist/src/utils/rateLimiter.js.map +1 -1
  157. package/dist/src/utils/regexValidator.d.ts +12 -0
  158. package/dist/src/utils/regexValidator.js +48 -0
  159. package/dist/src/utils/regexValidator.js.map +1 -0
  160. package/dist/src/utils/safeRegex.d.ts +2 -92
  161. package/dist/src/utils/safeRegex.js +41 -222
  162. package/dist/src/utils/safeRegex.js.map +1 -1
  163. package/dist/src/utils/safeRegexApi.d.ts +56 -0
  164. package/dist/src/utils/safeRegexApi.js +99 -0
  165. package/dist/src/utils/safeRegexApi.js.map +1 -0
  166. package/dist/src/utils/safeRegexPool.d.ts +7 -0
  167. package/dist/src/utils/safeRegexPool.js +102 -0
  168. package/dist/src/utils/safeRegexPool.js.map +1 -0
  169. package/dist/src/utils/searchTools.js +6 -6
  170. package/dist/src/utils/searchTools.js.map +1 -1
  171. package/dist/src/utils/streaming.d.ts +4 -3
  172. package/dist/src/utils/streaming.js +1 -1
  173. package/dist/src/utils/streaming.js.map +1 -1
  174. package/dist/src/utils/telemetry.d.ts +1 -1
  175. package/dist/src/utils/telemetry.js +1 -1
  176. package/dist/src/utils/toctou.d.ts +7 -0
  177. package/dist/src/utils/toctou.js +24 -0
  178. package/dist/src/utils/toctou.js.map +1 -0
  179. package/llms.txt +199 -31
  180. package/package.json +2 -2
  181. package/scripts/replace-version.js +0 -59
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Read core — extractSymbol and readLines
3
+ */
4
+ import * as fs from "node:fs/promises";
5
+ import * as crypto from "node:crypto";
6
+ import { safeRegexFindFirst } from "../utils/safeRegex.js";
7
+ import { validateRegexPattern } from "../utils/regexValidator.js";
8
+ async function getFileHash(filePath) {
9
+ const content = await fs.readFile(filePath, "utf-8");
10
+ return crypto.createHash("sha256").update(content).digest("hex");
11
+ }
12
+ /**
13
+ * Extract a specific symbol from a file
14
+ */
15
+ export async function extractSymbol(params) {
16
+ try {
17
+ const cache = params.cache;
18
+ // Try cache first
19
+ if (cache) {
20
+ const hash = await getFileHash(params.filePath);
21
+ const cached = await cache.get(params.filePath, hash);
22
+ if (cached) {
23
+ const symbol = cached.symbols.find(s => s.name === params.symbolName && (!params.className || s.className === params.className));
24
+ if (symbol) {
25
+ const content = await fs.readFile(params.filePath, "utf-8");
26
+ const tree = params.parser.parse(content);
27
+ const extracted = params.parser.extractSymbol(tree, params.symbolName, params.className);
28
+ if (extracted) {
29
+ return { success: true, content: extracted };
30
+ }
31
+ }
32
+ }
33
+ }
34
+ // Cache miss - parse file
35
+ const content = await fs.readFile(params.filePath, "utf-8");
36
+ const tree = params.parser.parse(content);
37
+ const symbols = params.parser.findSymbols(tree);
38
+ // Update cache
39
+ if (cache) {
40
+ const hash = await getFileHash(params.filePath);
41
+ const stat = await fs.stat(params.filePath);
42
+ await cache.set({
43
+ filePath: params.filePath,
44
+ hash,
45
+ symbols,
46
+ lastModified: stat.mtimeMs,
47
+ cachedAt: Date.now(),
48
+ });
49
+ }
50
+ const extracted = params.parser.extractSymbol(tree, params.symbolName, params.className);
51
+ if (!extracted) {
52
+ const symbols = params.parser.findSymbols(tree);
53
+ return {
54
+ success: false,
55
+ error: `Symbol "${params.symbolName}" not found. Available: ${symbols.map(s => s.name).join(", ")}`,
56
+ symbols,
57
+ };
58
+ }
59
+ return {
60
+ success: true,
61
+ content: extracted,
62
+ };
63
+ }
64
+ catch (error) {
65
+ return {
66
+ success: false,
67
+ error: error instanceof Error ? error.message : String(error),
68
+ };
69
+ }
70
+ }
71
+ /**
72
+ * Read specific line ranges from a file
73
+ */
74
+ export async function readLines(params) {
75
+ try {
76
+ const content = await fs.readFile(params.filePath, "utf-8");
77
+ const lines = content.split("\n");
78
+ if (params.aroundPattern) {
79
+ const validation = validateRegexPattern(params.aroundPattern);
80
+ if (!validation.safe) {
81
+ return {
82
+ success: false,
83
+ error: `Unsafe regex pattern: ${validation.issues.join(", ")}`,
84
+ };
85
+ }
86
+ const findResult = await safeRegexFindFirst(params.aroundPattern, lines);
87
+ if (findResult.timedOut) {
88
+ return {
89
+ success: false,
90
+ error: `Regex timed out searching for pattern (potential ReDoS)`,
91
+ };
92
+ }
93
+ if (!findResult.success) {
94
+ return { success: false, error: findResult.error };
95
+ }
96
+ const matchIndex = findResult.matchIndex;
97
+ if (matchIndex === -1) {
98
+ return {
99
+ success: false,
100
+ error: `Pattern "${params.aroundPattern}" not found`,
101
+ };
102
+ }
103
+ const context = params.contextLines || 5;
104
+ const start = Math.max(0, matchIndex - context);
105
+ const end = Math.min(lines.length, matchIndex + context + 1);
106
+ const matchLine = matchIndex + 1;
107
+ return {
108
+ success: true,
109
+ content: `Match found at line ${matchLine} (showing lines ${start + 1}-${end}):\n${lines.slice(start, end).join("\n")}`,
110
+ };
111
+ }
112
+ if (params.startLine !== undefined && params.endLine !== undefined) {
113
+ const start = Math.max(0, params.startLine - 1);
114
+ // Clamp endLine to actual file length instead of erroring
115
+ const clampedEnd = Math.min(params.endLine, lines.length);
116
+ const clampWarning = params.endLine > lines.length
117
+ ? `\n⚠️ endLine ${params.endLine} clamped to ${lines.length} (file has ${lines.length} lines)\n`
118
+ : "";
119
+ if (start >= lines.length) {
120
+ return {
121
+ success: false,
122
+ error: `startLine ${params.startLine} exceeds file length (${lines.length} lines)`,
123
+ };
124
+ }
125
+ return {
126
+ success: true,
127
+ content: clampWarning + lines.slice(start, clampedEnd).join("\n"),
128
+ };
129
+ }
130
+ return {
131
+ success: false,
132
+ error: "Must provide either startLine+endLine or aroundPattern",
133
+ };
134
+ }
135
+ catch (error) {
136
+ return {
137
+ success: false,
138
+ error: error instanceof Error ? error.message : String(error),
139
+ };
140
+ }
141
+ }
142
+ //# sourceMappingURL=readCore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readCore.js","sourceRoot":"","sources":["../../../src/operations/readCore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Rename symbol operations — AST definition + text-based dependents
3
+ */
4
+ import { BaseParser } from "../parsers/base.js";
5
+ import type { WriteResult } from "./write.js";
6
+ export declare function renameSymbol(params: {
7
+ filePath: string;
8
+ projectRoot: string;
9
+ oldName: string;
10
+ newName: string;
11
+ rootDir: string;
12
+ parser: BaseParser;
13
+ }): Promise<WriteResult>;
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Rename symbol operations — AST definition + text-based dependents
3
+ */
4
+ import * as fs from "node:fs/promises";
5
+ import { createHash } from "node:crypto";
6
+ import { SecurityValidator } from "../core/validator.js";
7
+ import { EXCLUDE_DIRS, SUPPORTED_EXTENSIONS } from "../utils/constants.js";
8
+ import { generateUnifiedDiff } from "../utils/diff.js";
9
+ import { sanitizeRegexPattern } from "../utils/regexValidator.js";
10
+ import { safeRegexAnyTest, safeRegexReplaceAll } from "../utils/safeRegex.js";
11
+ import { walkDir } from "../utils/fileWalker.js";
12
+ async function validateRenameParams(params) {
13
+ const validator = new SecurityValidator(params.projectRoot);
14
+ const defValidation = await validator.validateFilePath(params.filePath);
15
+ if (!defValidation.valid) {
16
+ return { valid: false, error: defValidation.error };
17
+ }
18
+ return {
19
+ valid: true,
20
+ resolvedPath: defValidation.resolvedPath,
21
+ sanitizedOld: sanitizeRegexPattern(params.oldName),
22
+ };
23
+ }
24
+ async function findDependentFiles(rootDir, sanitizedOldName, projectRoot) {
25
+ const dependents = [];
26
+ const validator = new SecurityValidator(projectRoot);
27
+ await walkDir(rootDir, {
28
+ extensions: SUPPORTED_EXTENSIONS,
29
+ excludeDirs: EXCLUDE_DIRS,
30
+ onFile: async (fullPath) => {
31
+ const depValidation = await validator.validateFilePath(fullPath);
32
+ if (!depValidation.valid)
33
+ return;
34
+ const fileContent = await fs.readFile(fullPath, "utf-8");
35
+ const importPatterns = [
36
+ `import.*\\b${sanitizedOldName}\\b`,
37
+ `from.*\\b${sanitizedOldName}\\b`,
38
+ `require.*\\b${sanitizedOldName}\\b`,
39
+ `use.*\\b${sanitizedOldName}\\b`,
40
+ ];
41
+ const matchResult = await safeRegexAnyTest(importPatterns, fileContent);
42
+ if (matchResult.timedOut) {
43
+ throw new Error(matchResult.error ?? "Regex timed out finding rename dependents (ReDoS prevented)");
44
+ }
45
+ if (!matchResult.success) {
46
+ throw new Error(matchResult.error ?? "Rename dependent scan failed");
47
+ }
48
+ if (matchResult.matched) {
49
+ dependents.push({ path: fullPath, content: fileContent });
50
+ }
51
+ },
52
+ });
53
+ return dependents;
54
+ }
55
+ async function generateRenameChanges(oldName, newName, sanitizedOld, definitionPath, definitionContent, definitionNewContent, dependents) {
56
+ const pendingWrites = [];
57
+ const diffParts = [];
58
+ const wordPattern = `\\b${sanitizedOld}\\b`;
59
+ for (const dep of dependents) {
60
+ const replaceResult = await safeRegexReplaceAll(wordPattern, newName, dep.content);
61
+ if (replaceResult.timedOut) {
62
+ throw new Error(replaceResult.error ?? "Regex timed out during rename (ReDoS prevented)");
63
+ }
64
+ if (!replaceResult.success || replaceResult.output === undefined) {
65
+ throw new Error(replaceResult.error ?? "Rename replace failed");
66
+ }
67
+ const depNewContent = replaceResult.output;
68
+ pendingWrites.push({
69
+ filePath: dep.path,
70
+ newContent: depNewContent,
71
+ originalHash: createHash("sha256").update(dep.content).digest("hex"),
72
+ });
73
+ diffParts.push(`--- ${dep.path}\n${generateUnifiedDiff(dep.content, depNewContent)}`);
74
+ }
75
+ pendingWrites.push({
76
+ filePath: definitionPath,
77
+ newContent: definitionNewContent,
78
+ originalHash: createHash("sha256").update(definitionContent).digest("hex"),
79
+ });
80
+ diffParts.push(`--- ${definitionPath}\n${generateUnifiedDiff(definitionContent, definitionNewContent)}`);
81
+ const textWarning = dependents.length > 0
82
+ ? `\n⚠️ Dependent files (${dependents.length}) use text-based rename — strings/comments may be affected. Definition file uses AST.`
83
+ : "";
84
+ const consolidatedDiff = `Renamed "${oldName}" \u2192 "${newName}" in ${pendingWrites.length} files${textWarning}\n\n${diffParts.join("\n\n")}`;
85
+ return { pendingWrites, diff: consolidatedDiff };
86
+ }
87
+ export async function renameSymbol(params) {
88
+ try {
89
+ const validation = await validateRenameParams({
90
+ filePath: params.filePath,
91
+ projectRoot: params.projectRoot,
92
+ oldName: params.oldName,
93
+ });
94
+ if (!validation.valid) {
95
+ return { success: false, error: validation.error };
96
+ }
97
+ const { resolvedPath, sanitizedOld } = validation;
98
+ const content = await fs.readFile(resolvedPath, "utf-8");
99
+ const tree = params.parser.parse(content);
100
+ const symbols = params.parser.findSymbols(tree);
101
+ const targetSymbol = symbols.find((s) => s.name === params.oldName);
102
+ if (!targetSymbol) {
103
+ return { success: false, error: `Symbol "${params.oldName}" not found in ${params.filePath}` };
104
+ }
105
+ const symbolSlice = content.substring(targetSymbol.startIndex, targetSymbol.endIndex);
106
+ const symbolReplace = await safeRegexReplaceAll(`\\b${sanitizedOld}\\b`, params.newName, symbolSlice);
107
+ if (symbolReplace.timedOut) {
108
+ return { success: false, error: symbolReplace.error ?? "Regex timed out during rename (ReDoS prevented)" };
109
+ }
110
+ if (!symbolReplace.success || symbolReplace.output === undefined) {
111
+ return { success: false, error: symbolReplace.error ?? "Rename replace failed" };
112
+ }
113
+ const newContent = content.substring(0, targetSymbol.startIndex) +
114
+ symbolReplace.output +
115
+ content.substring(targetSymbol.endIndex);
116
+ const dependents = await findDependentFiles(params.rootDir, sanitizedOld, params.projectRoot);
117
+ const { pendingWrites, diff } = await generateRenameChanges(params.oldName, params.newName, sanitizedOld, resolvedPath, content, newContent, dependents);
118
+ return {
119
+ success: true,
120
+ newContent,
121
+ diff,
122
+ originalHash: createHash("sha256").update(content).digest("hex"),
123
+ pendingWrites,
124
+ };
125
+ }
126
+ catch (error) {
127
+ return { success: false, error: error instanceof Error ? error.message : String(error) };
128
+ }
129
+ }
130
+ //# sourceMappingURL=rename.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rename.js","sourceRoot":"","sources":["../../../src/operations/rename.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Search pattern operation
3
+ */
4
+ import type { ReadResult } from "./readCore.js";
5
+ export declare function searchPattern(params: {
6
+ rootDir: string;
7
+ pattern: string;
8
+ fileExtensions?: string[];
9
+ excludeDirs?: string[];
10
+ showContext?: boolean;
11
+ contextLines?: number;
12
+ maxResults?: number;
13
+ startIndex?: number;
14
+ fuzzyMatch?: boolean;
15
+ fuzzyThreshold?: number;
16
+ }): Promise<ReadResult>;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Search pattern operation
3
+ */
4
+ import * as fs from "node:fs/promises";
5
+ import { EXCLUDE_DIRS, SUPPORTED_EXTENSIONS, MAX_FILES_SEARCH, OPERATION_TIMEOUT_MS } from "../utils/constants.js";
6
+ import { safeRegexMultiFileBatchTest } from "../utils/safeRegex.js";
7
+ import { validateRegexPattern } from "../utils/regexValidator.js";
8
+ import { walkDir } from "../utils/fileWalker.js";
9
+ import { fuzzySearch } from "../utils/fuzzySearch.js";
10
+ import { searchWithNativeTool } from "../utils/searchTools.js";
11
+ const DEFAULT_MAX_RESULTS = 10;
12
+ const SCAN_TIMEOUT_BASE_MS = 1000;
13
+ const SCAN_TIMEOUT_PER_FILE_MS = 10;
14
+ export async function searchPattern(params) {
15
+ try {
16
+ const maxResults = params.maxResults || DEFAULT_MAX_RESULTS;
17
+ const validation = validateRegexPattern(params.pattern);
18
+ if (!validation.safe) {
19
+ return {
20
+ success: false,
21
+ error: `Unsafe regex pattern: ${validation.issues.join(", ")}`,
22
+ };
23
+ }
24
+ // Try native tools after regex safety gate (ripgrep > ugrep > ag > findstr/grep)
25
+ const nativeResult = await searchWithNativeTool(params.pattern, params.rootDir, maxResults, 10000);
26
+ if (nativeResult && !nativeResult.timedOut && nativeResult.matches.length > 0) {
27
+ const startIdx = params.startIndex || 0;
28
+ const paginatedMatches = nativeResult.matches.slice(startIdx, startIdx + maxResults);
29
+ const results = paginatedMatches.map(m => ({
30
+ file: m.file,
31
+ line: m.line,
32
+ content: m.content,
33
+ }));
34
+ const totalResults = nativeResult.matches.length;
35
+ const hasMore = startIdx + maxResults < totalResults;
36
+ const footer = `\n\nShowing ${startIdx + 1}-${startIdx + results.length} of ${totalResults} results (via ${nativeResult.tool})${hasMore ? ' (use startIndex to see more)' : ''}`;
37
+ return { success: true, content: JSON.stringify(results, null, 2) + footer };
38
+ }
39
+ const extensions = params.fileExtensions || SUPPORTED_EXTENSIONS;
40
+ const excludeDirs = params.excludeDirs || EXCLUDE_DIRS;
41
+ // Step 1: Collect file paths first (preventing memory exhaustion)
42
+ const filePaths = [];
43
+ await walkDir(params.rootDir, {
44
+ extensions,
45
+ excludeDirs,
46
+ maxFiles: MAX_FILES_SEARCH + 1, // Stop early if we exceed the limit
47
+ onFile: (fullPath) => {
48
+ filePaths.push(fullPath);
49
+ },
50
+ });
51
+ // CRITICAL: Prevent OOM in large monorepos
52
+ if (filePaths.length > MAX_FILES_SEARCH) {
53
+ return {
54
+ success: false,
55
+ error: `Too many files to search (${filePaths.length - 1}+). Maximum: ${MAX_FILES_SEARCH}. Try narrowing your search with fileExtensions or excludeDirs.`
56
+ };
57
+ }
58
+ const fileEntries = [];
59
+ for (const filePath of filePaths) {
60
+ const content = await fs.readFile(filePath, "utf-8");
61
+ fileEntries.push({ path: filePath, lines: content.split("\n") });
62
+ }
63
+ // Step 2: ONE worker for ALL files — eliminates N worker spawns
64
+ const scanTimeout = Math.min(OPERATION_TIMEOUT_MS, SCAN_TIMEOUT_BASE_MS + fileEntries.length * SCAN_TIMEOUT_PER_FILE_MS);
65
+ const batchResult = await safeRegexMultiFileBatchTest(params.pattern, fileEntries, scanTimeout);
66
+ if (batchResult.timedOut) {
67
+ console.warn(`⚠️ Regex scan timed out across ${fileEntries.length} files`);
68
+ return { success: false, error: `Regex scan timed out (${fileEntries.length} files, potential ReDoS)` };
69
+ }
70
+ if (!batchResult.success) {
71
+ return { success: false, error: batchResult.error };
72
+ }
73
+ // Step 3: Collect results with pagination
74
+ const startIdx = params.startIndex || 0;
75
+ const maxRes = params.maxResults || DEFAULT_MAX_RESULTS;
76
+ const allMatches = batchResult.results;
77
+ // Apply fuzzy filtering if requested
78
+ let filteredMatches = allMatches;
79
+ if (params.fuzzyMatch) {
80
+ const fuzzyThreshold = params.fuzzyThreshold ?? 0.4;
81
+ const searchItems = allMatches.map(m => ({
82
+ ...m,
83
+ searchText: `${m.file} ${m.content}`,
84
+ }));
85
+ const fuzzyResults = fuzzySearch(searchItems, params.pattern, {
86
+ threshold: fuzzyThreshold,
87
+ keys: ['searchText'],
88
+ });
89
+ filteredMatches = fuzzyResults.map(r => r.item);
90
+ }
91
+ const paginatedMatches = filteredMatches.slice(startIdx, startIdx + maxRes);
92
+ const results = paginatedMatches.map(match => ({
93
+ file: match.file,
94
+ line: match.index + 1,
95
+ content: match.content,
96
+ }));
97
+ // Add pagination footer
98
+ const totalResults = filteredMatches.length;
99
+ const hasMore = startIdx + maxRes < totalResults;
100
+ const footer = `\n\nShowing ${startIdx + 1}-${startIdx + results.length} of ${totalResults} results${hasMore ? ' (use startIndex to see more)' : ''}`;
101
+ return {
102
+ success: true,
103
+ content: JSON.stringify(results, null, 2) + footer,
104
+ };
105
+ }
106
+ catch (error) {
107
+ return {
108
+ success: false,
109
+ error: error instanceof Error ? error.message : String(error),
110
+ };
111
+ }
112
+ }
113
+ //# sourceMappingURL=searchOperation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"searchOperation.js","sourceRoot":"","sources":["../../../src/operations/searchOperation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Symbol query operations — search, explain, batch read
3
+ */
4
+ import { BaseParser } from "../parsers/base.js";
5
+ import { type ReadResult } from "./readCore.js";
6
+ export declare function searchSymbols(params: {
7
+ rootDir: string;
8
+ query: string;
9
+ projectRoot: string;
10
+ fuzzy?: boolean;
11
+ types?: string[];
12
+ fileExtensions?: string[];
13
+ excludeDirs?: string[];
14
+ maxResults?: number;
15
+ }): Promise<ReadResult>;
16
+ /**
17
+ * NEW-03: Explain a symbol — signature + callers + callees in one call
18
+ */
19
+ export declare function explainSymbol(params: {
20
+ filePath: string;
21
+ projectRoot: string;
22
+ symbolName: string;
23
+ className?: string;
24
+ parser: BaseParser;
25
+ rootDir: string;
26
+ }): Promise<ReadResult>;
27
+ /**
28
+ * NEW-05: Batch read multiple symbols in one call
29
+ */
30
+ export declare function batchRead(params: {
31
+ reads: Array<{
32
+ filePath: string;
33
+ symbolName: string;
34
+ className?: string;
35
+ }>;
36
+ projectRoot: string;
37
+ parser: (ext: string) => BaseParser | null;
38
+ }): Promise<ReadResult>;