mcp-code-context 3.6.3 → 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.
- package/CHANGELOG.md +140 -278
- package/INSTRUCTIONS.md +23 -9
- package/README.md +217 -97
- package/SECURITY.md +55 -21
- package/dist/src/core/cacheManager.d.ts +4 -5
- package/dist/src/core/cacheManager.js +49 -33
- package/dist/src/core/cacheManager.js.map +1 -1
- package/dist/src/core/engine.d.ts +1 -1
- package/dist/src/core/engine.js +3 -3
- package/dist/src/core/engine.js.map +1 -1
- package/dist/src/core/indexManager.d.ts +58 -0
- package/dist/src/core/indexManager.js +252 -0
- package/dist/src/core/indexManager.js.map +1 -0
- package/dist/src/core/indexManagerQueries.d.ts +14 -0
- package/dist/src/core/indexManagerQueries.js +95 -0
- package/dist/src/core/indexManagerQueries.js.map +1 -0
- package/dist/src/core/sessionManager.d.ts +117 -0
- package/dist/src/core/sessionManager.js +231 -0
- package/dist/src/core/sessionManager.js.map +1 -0
- package/dist/src/core/validator.d.ts +1 -1
- package/dist/src/core/validator.js +18 -10
- package/dist/src/core/validator.js.map +1 -1
- package/dist/src/index.d.ts +1 -13
- package/dist/src/index.js +25 -999
- package/dist/src/index.js.map +1 -1
- package/dist/src/operations/astTransform.d.ts +38 -0
- package/dist/src/operations/astTransform.js +239 -0
- package/dist/src/operations/astTransform.js.map +1 -0
- package/dist/src/operations/compress.d.ts +5 -3
- package/dist/src/operations/compress.js +73 -7
- package/dist/src/operations/compress.js.map +1 -1
- package/dist/src/operations/confirmationStore.d.ts +35 -8
- package/dist/src/operations/confirmationStore.js +112 -14
- package/dist/src/operations/confirmationStore.js.map +1 -1
- package/dist/src/operations/impactAnalysis.d.ts +9 -0
- package/dist/src/operations/impactAnalysis.js +61 -0
- package/dist/src/operations/impactAnalysis.js.map +1 -0
- package/dist/src/operations/pendingStore.d.ts +51 -0
- package/dist/src/operations/pendingStore.js +201 -0
- package/dist/src/operations/pendingStore.js.map +1 -0
- package/dist/src/operations/pendingStoreInit.d.ts +6 -0
- package/dist/src/operations/pendingStoreInit.js +42 -0
- package/dist/src/operations/pendingStoreInit.js.map +1 -0
- package/dist/src/operations/read.d.ts +6 -92
- package/dist/src/operations/read.js +4 -449
- package/dist/src/operations/read.js.map +1 -1
- package/dist/src/operations/readCore.d.ts +33 -0
- package/dist/src/operations/readCore.js +142 -0
- package/dist/src/operations/readCore.js.map +1 -0
- package/dist/src/operations/rename.d.ts +13 -0
- package/dist/src/operations/rename.js +130 -0
- package/dist/src/operations/rename.js.map +1 -0
- package/dist/src/operations/searchOperation.d.ts +16 -0
- package/dist/src/operations/searchOperation.js +113 -0
- package/dist/src/operations/searchOperation.js.map +1 -0
- package/dist/src/operations/symbolQuery.d.ts +38 -0
- package/dist/src/operations/symbolQuery.js +194 -0
- package/dist/src/operations/symbolQuery.js.map +1 -0
- package/dist/src/operations/write.d.ts +11 -14
- package/dist/src/operations/write.js +6 -125
- package/dist/src/operations/write.js.map +1 -1
- package/dist/src/parsers/base.d.ts +1 -1
- package/dist/src/parsers/base.js +1 -1
- package/dist/src/parsers/csharp.d.ts +12 -0
- package/dist/src/parsers/csharp.js +67 -0
- package/dist/src/parsers/csharp.js.map +1 -0
- package/dist/src/parsers/dartTreeSitter.d.ts +1 -1
- package/dist/src/parsers/dartTreeSitter.js +1 -1
- package/dist/src/parsers/go.d.ts +11 -0
- package/dist/src/parsers/go.js +74 -0
- package/dist/src/parsers/go.js.map +1 -0
- package/dist/src/parsers/java.d.ts +12 -0
- package/dist/src/parsers/java.js +62 -0
- package/dist/src/parsers/java.js.map +1 -0
- package/dist/src/parsers/kotlin.d.ts +12 -0
- package/dist/src/parsers/kotlin.js +75 -0
- package/dist/src/parsers/kotlin.js.map +1 -0
- package/dist/src/parsers/php.d.ts +1 -1
- package/dist/src/parsers/php.js +1 -1
- package/dist/src/parsers/python.d.ts +1 -1
- package/dist/src/parsers/python.js +1 -1
- package/dist/src/parsers/registry.d.ts +1 -1
- package/dist/src/parsers/registry.js +63 -2
- package/dist/src/parsers/registry.js.map +1 -1
- package/dist/src/parsers/ruby.d.ts +12 -0
- package/dist/src/parsers/ruby.js +73 -0
- package/dist/src/parsers/ruby.js.map +1 -0
- package/dist/src/parsers/rust.d.ts +12 -0
- package/dist/src/parsers/rust.js +66 -0
- package/dist/src/parsers/rust.js.map +1 -0
- package/dist/src/parsers/typescript.d.ts +1 -1
- package/dist/src/parsers/typescript.js +28 -38
- package/dist/src/parsers/typescript.js.map +1 -1
- package/dist/src/tools/adminHandlers.d.ts +67 -0
- package/dist/src/tools/adminHandlers.js +129 -0
- package/dist/src/tools/adminHandlers.js.map +1 -0
- package/dist/src/tools/context.d.ts +9 -0
- package/dist/src/tools/context.js +21 -0
- package/dist/src/tools/context.js.map +1 -0
- package/dist/src/tools/handlers.d.ts +4 -0
- package/dist/src/tools/handlers.js +5 -0
- package/dist/src/tools/handlers.js.map +1 -0
- package/dist/src/tools/pipeline.d.ts +6 -0
- package/dist/src/tools/pipeline.js +100 -0
- package/dist/src/tools/pipeline.js.map +1 -0
- package/dist/src/tools/readHandlers.d.ts +55 -0
- package/dist/src/tools/readHandlers.js +220 -0
- package/dist/src/tools/readHandlers.js.map +1 -0
- package/dist/src/tools/toolDefinitions.d.ts +1037 -0
- package/dist/src/tools/toolDefinitions.js +13 -0
- package/dist/src/tools/toolDefinitions.js.map +1 -0
- package/dist/src/tools/toolDefinitionsAdmin.d.ts +89 -0
- package/dist/src/tools/toolDefinitionsAdmin.js +99 -0
- package/dist/src/tools/toolDefinitionsAdmin.js.map +1 -0
- package/dist/src/tools/toolDefinitionsCore.d.ts +949 -0
- package/dist/src/tools/toolDefinitionsCore.js +267 -0
- package/dist/src/tools/toolDefinitionsCore.js.map +1 -0
- package/dist/src/tools/writeHandlers.d.ts +37 -0
- package/dist/src/tools/writeHandlers.js +199 -0
- package/dist/src/tools/writeHandlers.js.map +1 -0
- package/dist/src/utils/appDir.d.ts +19 -0
- package/dist/src/utils/appDir.js +50 -0
- package/dist/src/utils/appDir.js.map +1 -0
- package/dist/src/utils/auditLogWriter.d.ts +9 -0
- package/dist/src/utils/auditLogWriter.js +49 -0
- package/dist/src/utils/auditLogWriter.js.map +1 -0
- package/dist/src/utils/auditLogger.d.ts +1 -10
- package/dist/src/utils/auditLogger.js +14 -109
- package/dist/src/utils/auditLogger.js.map +1 -1
- package/dist/src/utils/auditQuery.d.ts +23 -0
- package/dist/src/utils/auditQuery.js +67 -0
- package/dist/src/utils/auditQuery.js.map +1 -0
- package/dist/src/utils/backupManager.d.ts +2 -2
- package/dist/src/utils/backupManager.js +29 -12
- package/dist/src/utils/backupManager.js.map +1 -1
- package/dist/src/utils/constants.d.ts +1 -1
- package/dist/src/utils/constants.js +8 -1
- package/dist/src/utils/constants.js.map +1 -1
- package/dist/src/utils/diff.d.ts +12 -1
- package/dist/src/utils/diff.js +41 -1
- package/dist/src/utils/diff.js.map +1 -1
- package/dist/src/utils/fileLock.d.ts +7 -2
- package/dist/src/utils/fileLock.js +14 -11
- package/dist/src/utils/fileLock.js.map +1 -1
- package/dist/src/utils/fileWalker.d.ts +1 -1
- package/dist/src/utils/fileWalker.js +1 -1
- package/dist/src/utils/fileWatcher.d.ts +1 -1
- package/dist/src/utils/fileWatcher.js +1 -1
- package/dist/src/utils/fuzzySearch.d.ts +1 -1
- package/dist/src/utils/fuzzySearch.js +1 -1
- package/dist/src/utils/logger.d.ts +1 -1
- package/dist/src/utils/logger.js +2 -2
- package/dist/src/utils/logger.js.map +1 -1
- package/dist/src/utils/rateLimiter.d.ts +9 -1
- package/dist/src/utils/rateLimiter.js +12 -1
- package/dist/src/utils/rateLimiter.js.map +1 -1
- package/dist/src/utils/regexValidator.d.ts +12 -0
- package/dist/src/utils/regexValidator.js +48 -0
- package/dist/src/utils/regexValidator.js.map +1 -0
- package/dist/src/utils/safeRegex.d.ts +2 -92
- package/dist/src/utils/safeRegex.js +41 -222
- package/dist/src/utils/safeRegex.js.map +1 -1
- package/dist/src/utils/safeRegexApi.d.ts +56 -0
- package/dist/src/utils/safeRegexApi.js +99 -0
- package/dist/src/utils/safeRegexApi.js.map +1 -0
- package/dist/src/utils/safeRegexPool.d.ts +7 -0
- package/dist/src/utils/safeRegexPool.js +102 -0
- package/dist/src/utils/safeRegexPool.js.map +1 -0
- package/dist/src/utils/searchTools.js +6 -6
- package/dist/src/utils/searchTools.js.map +1 -1
- package/dist/src/utils/streaming.d.ts +4 -3
- package/dist/src/utils/streaming.js +1 -1
- package/dist/src/utils/streaming.js.map +1 -1
- package/dist/src/utils/telemetry.d.ts +1 -1
- package/dist/src/utils/telemetry.js +1 -1
- package/dist/src/utils/toctou.d.ts +7 -0
- package/dist/src/utils/toctou.js +24 -0
- package/dist/src/utils/toctou.js.map +1 -0
- package/llms.txt +173 -21
- package/package.json +2 -2
- 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>;
|