code-ollama 0.25.0 → 0.26.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/dist/cli.js +214 -188
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -50,7 +50,7 @@ var LIST$1 = [
|
|
|
50
50
|
//#endregion
|
|
51
51
|
//#region package.json
|
|
52
52
|
var name = "code-ollama";
|
|
53
|
-
var version = "0.
|
|
53
|
+
var version = "0.26.0";
|
|
54
54
|
//#endregion
|
|
55
55
|
//#region src/constants/package.ts
|
|
56
56
|
var NAME = name;
|
|
@@ -859,6 +859,10 @@ var TOOLS = [
|
|
|
859
859
|
maxLines: {
|
|
860
860
|
type: "number",
|
|
861
861
|
description: "Optional maximum number of lines to read; cannot be combined with endLine"
|
|
862
|
+
},
|
|
863
|
+
maxChars: {
|
|
864
|
+
type: "number",
|
|
865
|
+
description: `Optional maximum number of characters to return; defaults to 50000; applies after any line-range selection`
|
|
862
866
|
}
|
|
863
867
|
}, ["path"]),
|
|
864
868
|
defineTool(WRITE_FILE, "Write content to a file at the specified path", {
|
|
@@ -965,56 +969,10 @@ var TOOLS = [
|
|
|
965
969
|
var READ_TOOLS = new Set(READ_TOOL_NAMES);
|
|
966
970
|
var WRITE_TOOLS = new Set(WRITE_TOOL_NAMES);
|
|
967
971
|
//#endregion
|
|
968
|
-
//#region src/utils/tools/
|
|
969
|
-
var execAsync = promisify(exec);
|
|
970
|
-
var SHELL_EXEC_OPTIONS = {
|
|
971
|
-
timeout: 3e4,
|
|
972
|
-
maxBuffer: 1024 * 1024
|
|
973
|
-
};
|
|
974
|
-
function getErrorOutput(error) {
|
|
975
|
-
if (typeof error !== "object" || error === null) return "";
|
|
976
|
-
const output = error;
|
|
977
|
-
return [output.stdout, output.stderr].filter((value) => typeof value === "string" && !!value).join("\n");
|
|
978
|
-
}
|
|
979
|
-
/**
|
|
980
|
-
* Execute shell command with shared options (throws on error)
|
|
981
|
-
*/
|
|
982
|
-
function execShell(command) {
|
|
983
|
-
return execAsync(command, SHELL_EXEC_OPTIONS);
|
|
984
|
-
}
|
|
985
|
-
/**
|
|
986
|
-
* Execute shell command
|
|
987
|
-
*/
|
|
988
|
-
async function runShell(command) {
|
|
989
|
-
try {
|
|
990
|
-
const { stdout, stderr } = await execShell(command);
|
|
991
|
-
return { content: stdout || stderr };
|
|
992
|
-
} catch (error) {
|
|
993
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
994
|
-
return {
|
|
995
|
-
content: getErrorOutput(error),
|
|
996
|
-
error: `Command failed: ${message}`,
|
|
997
|
-
// v8 ignore next
|
|
998
|
-
...error instanceof Error && error.stack ? { stack: error.stack } : {}
|
|
999
|
-
};
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
//#endregion
|
|
1003
|
-
//#region src/utils/tools/filesystem.ts
|
|
972
|
+
//#region src/utils/tools/filesystem/diff.ts
|
|
1004
973
|
var DIFF_CONTEXT_LINES = 3;
|
|
1005
974
|
var DIFF_MAX_LINES = 120;
|
|
1006
975
|
var DIFF_MAX_CHARS = 12e3;
|
|
1007
|
-
var DEFAULT_FIND_FILES_IGNORED_DIRS = [
|
|
1008
|
-
"node_modules",
|
|
1009
|
-
"__pycache__",
|
|
1010
|
-
".*cache",
|
|
1011
|
-
".tox",
|
|
1012
|
-
".venv",
|
|
1013
|
-
"venv",
|
|
1014
|
-
"dist",
|
|
1015
|
-
"build",
|
|
1016
|
-
"coverage"
|
|
1017
|
-
];
|
|
1018
976
|
function splitLines(content) {
|
|
1019
977
|
return content.split("\n");
|
|
1020
978
|
}
|
|
@@ -1062,59 +1020,9 @@ function truncateDiff(diff) {
|
|
|
1062
1020
|
visibleLines: Math.min(visibleLines.length, lines.length)
|
|
1063
1021
|
};
|
|
1064
1022
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
const camelCase = words.map((word, index) => index === 0 ? word.toLowerCase() : capitalize(word.toLowerCase())).join("");
|
|
1069
|
-
const pascalCase = words.map((word) => capitalize(word.toLowerCase())).join("");
|
|
1070
|
-
const snakeCase = words.map((word) => word.toLowerCase()).join("_");
|
|
1071
|
-
const upperSnakeCase = snakeCase.toUpperCase();
|
|
1072
|
-
const flexibleWhitespace = words.join(String.raw`\s+`);
|
|
1073
|
-
return Array.from(new Set([
|
|
1074
|
-
pattern,
|
|
1075
|
-
flexibleWhitespace,
|
|
1076
|
-
snakeCase,
|
|
1077
|
-
upperSnakeCase,
|
|
1078
|
-
camelCase,
|
|
1079
|
-
pascalCase
|
|
1080
|
-
]));
|
|
1081
|
-
}
|
|
1082
|
-
function capitalize(value) {
|
|
1083
|
-
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
1084
|
-
}
|
|
1085
|
-
function escapeRegExp(value) {
|
|
1086
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1087
|
-
}
|
|
1088
|
-
function fileMatchesPattern(filePath, pattern) {
|
|
1089
|
-
const trimmedPattern = pattern?.trim();
|
|
1090
|
-
if (!trimmedPattern) return true;
|
|
1091
|
-
const normalizedPath = filePath.toLowerCase();
|
|
1092
|
-
const normalizedFileName = normalizedPath.slice(Math.max(normalizedPath.lastIndexOf("/"), normalizedPath.lastIndexOf("\\")) + 1);
|
|
1093
|
-
const normalizedPattern = trimmedPattern.toLowerCase();
|
|
1094
|
-
if (!normalizedPattern.includes("*") && !normalizedPattern.includes("?")) return normalizedPath.includes(normalizedPattern);
|
|
1095
|
-
const regexPattern = normalizedPattern.split("").map((char) => {
|
|
1096
|
-
if (char === "*") return ".*";
|
|
1097
|
-
if (char === "?") return ".";
|
|
1098
|
-
return escapeRegExp(char);
|
|
1099
|
-
}).join("");
|
|
1100
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
1101
|
-
return regex.test(normalizedPath) || regex.test(normalizedFileName);
|
|
1102
|
-
}
|
|
1103
|
-
function valueMatchesWildcardPattern(value, pattern) {
|
|
1104
|
-
const normalizedValue = value.toLowerCase();
|
|
1105
|
-
const normalizedPattern = pattern.trim().toLowerCase();
|
|
1106
|
-
if (!normalizedPattern.includes("*") && !normalizedPattern.includes("?")) return normalizedValue === normalizedPattern;
|
|
1107
|
-
const regexPattern = normalizedPattern.split("").map((char) => {
|
|
1108
|
-
if (char === "*") return ".*";
|
|
1109
|
-
if (char === "?") return ".";
|
|
1110
|
-
return escapeRegExp(char);
|
|
1111
|
-
}).join("");
|
|
1112
|
-
return new RegExp(`^${regexPattern}$`).test(normalizedValue);
|
|
1113
|
-
}
|
|
1114
|
-
function directoryMatchesIgnoredPattern(dirName, ignoredDirs) {
|
|
1115
|
-
for (const ignoredDir of ignoredDirs) if (valueMatchesWildcardPattern(dirName, ignoredDir)) return true;
|
|
1116
|
-
return false;
|
|
1117
|
-
}
|
|
1023
|
+
//#endregion
|
|
1024
|
+
//#region src/utils/tools/filesystem/files.ts
|
|
1025
|
+
var READ_FILE_MAX_CHARS = 5e4;
|
|
1118
1026
|
function formatNumberedLines(lines, startLine) {
|
|
1119
1027
|
return lines.map((line, index) => `${String(startLine + index)}: ${line}`).join("\n");
|
|
1120
1028
|
}
|
|
@@ -1128,17 +1036,24 @@ function readFile(filePath, options = {}) {
|
|
|
1128
1036
|
error: `File not found: ${filePath}`
|
|
1129
1037
|
};
|
|
1130
1038
|
const content = readFileSync(filePath, "utf8");
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1039
|
+
const isPartialRead = options.startLine !== void 0 || options.endLine !== void 0 || options.maxLines !== void 0;
|
|
1040
|
+
let output;
|
|
1041
|
+
if (!isPartialRead) output = content;
|
|
1042
|
+
else {
|
|
1043
|
+
const lines = content.split("\n");
|
|
1044
|
+
const startLine = options.startLine ?? 1;
|
|
1045
|
+
const endLine = options.endLine ?? startLine + (options.maxLines ?? lines.length) - 1;
|
|
1046
|
+
const startIndex = startLine - 1;
|
|
1047
|
+
const endIndex = Math.min(lines.length, endLine);
|
|
1048
|
+
if (startIndex >= lines.length) return {
|
|
1049
|
+
content: "",
|
|
1050
|
+
error: "Invalid line range"
|
|
1051
|
+
};
|
|
1052
|
+
output = formatNumberedLines(lines.slice(startIndex, endIndex), startLine);
|
|
1053
|
+
}
|
|
1054
|
+
const cap = options.maxChars ?? READ_FILE_MAX_CHARS;
|
|
1055
|
+
if (output.length > cap) return { content: `${output.slice(0, cap)}\n[file truncated: showing ${String(cap)} of ${String(output.length)} chars]` };
|
|
1056
|
+
return { content: output };
|
|
1142
1057
|
} catch (error) {
|
|
1143
1058
|
return {
|
|
1144
1059
|
content: "",
|
|
@@ -1202,6 +1117,186 @@ function editFile(filePath, oldText, newText) {
|
|
|
1202
1117
|
};
|
|
1203
1118
|
}
|
|
1204
1119
|
}
|
|
1120
|
+
//#endregion
|
|
1121
|
+
//#region src/utils/tools/filesystem/find.ts
|
|
1122
|
+
var DEFAULT_FIND_FILES_IGNORED_DIRS = [
|
|
1123
|
+
"node_modules",
|
|
1124
|
+
"__pycache__",
|
|
1125
|
+
".*cache",
|
|
1126
|
+
".tox",
|
|
1127
|
+
".venv",
|
|
1128
|
+
"venv",
|
|
1129
|
+
"dist",
|
|
1130
|
+
"build",
|
|
1131
|
+
"coverage"
|
|
1132
|
+
];
|
|
1133
|
+
function escapeRegExp(value) {
|
|
1134
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1135
|
+
}
|
|
1136
|
+
function fileMatchesPattern(filePath, pattern) {
|
|
1137
|
+
const trimmedPattern = pattern?.trim();
|
|
1138
|
+
if (!trimmedPattern) return true;
|
|
1139
|
+
const normalizedPath = filePath.toLowerCase();
|
|
1140
|
+
const normalizedFileName = normalizedPath.slice(Math.max(normalizedPath.lastIndexOf("/"), normalizedPath.lastIndexOf("\\")) + 1);
|
|
1141
|
+
const normalizedPattern = trimmedPattern.toLowerCase();
|
|
1142
|
+
if (!normalizedPattern.includes("*") && !normalizedPattern.includes("?")) return normalizedPath.includes(normalizedPattern);
|
|
1143
|
+
const regexPattern = normalizedPattern.split("").map((char) => {
|
|
1144
|
+
if (char === "*") return ".*";
|
|
1145
|
+
if (char === "?") return ".";
|
|
1146
|
+
return escapeRegExp(char);
|
|
1147
|
+
}).join("");
|
|
1148
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
1149
|
+
return regex.test(normalizedPath) || regex.test(normalizedFileName);
|
|
1150
|
+
}
|
|
1151
|
+
function valueMatchesWildcardPattern(value, pattern) {
|
|
1152
|
+
const normalizedValue = value.toLowerCase();
|
|
1153
|
+
const normalizedPattern = pattern.trim().toLowerCase();
|
|
1154
|
+
if (!normalizedPattern.includes("*") && !normalizedPattern.includes("?")) return normalizedValue === normalizedPattern;
|
|
1155
|
+
const regexPattern = normalizedPattern.split("").map((char) => {
|
|
1156
|
+
if (char === "*") return ".*";
|
|
1157
|
+
if (char === "?") return ".";
|
|
1158
|
+
return escapeRegExp(char);
|
|
1159
|
+
}).join("");
|
|
1160
|
+
return new RegExp(`^${regexPattern}$`).test(normalizedValue);
|
|
1161
|
+
}
|
|
1162
|
+
function directoryMatchesIgnoredPattern(dirName, ignoredDirs) {
|
|
1163
|
+
for (const ignoredDir of ignoredDirs) if (valueMatchesWildcardPattern(dirName, ignoredDir)) return true;
|
|
1164
|
+
return false;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Recursively find files by path
|
|
1168
|
+
*/
|
|
1169
|
+
function findFiles(dirPath, options = {}) {
|
|
1170
|
+
try {
|
|
1171
|
+
if (!existsSync(dirPath)) return {
|
|
1172
|
+
content: "",
|
|
1173
|
+
error: `Directory not found: ${dirPath}`
|
|
1174
|
+
};
|
|
1175
|
+
if (!statSync(dirPath).isDirectory()) return {
|
|
1176
|
+
content: "",
|
|
1177
|
+
error: `Path is not a directory: ${dirPath}`
|
|
1178
|
+
};
|
|
1179
|
+
const results = [];
|
|
1180
|
+
const includeHidden = options.includeHidden ?? false;
|
|
1181
|
+
const ignoredDirs = new Set(options.ignoredDirs ?? DEFAULT_FIND_FILES_IGNORED_DIRS);
|
|
1182
|
+
function searchDirectory(currentPath) {
|
|
1183
|
+
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
1184
|
+
for (const entry of entries) {
|
|
1185
|
+
const fullPath = join(currentPath, entry.name);
|
|
1186
|
+
if (entry.isDirectory()) {
|
|
1187
|
+
if (entry.name !== ".git" && !directoryMatchesIgnoredPattern(entry.name, ignoredDirs) && (includeHidden || !entry.name.startsWith("."))) searchDirectory(fullPath);
|
|
1188
|
+
} else if (entry.isFile() && (includeHidden || !entry.name.startsWith(".")) && fileMatchesPattern(fullPath, options.pattern)) results.push(fullPath);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
searchDirectory(dirPath);
|
|
1192
|
+
return { content: results.join("\n") };
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
return {
|
|
1195
|
+
content: "",
|
|
1196
|
+
error: `Failed to find files: ${error instanceof Error ? error.message : String(error)}`
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
//#endregion
|
|
1201
|
+
//#region src/utils/tools/shell.ts
|
|
1202
|
+
var execAsync = promisify(exec);
|
|
1203
|
+
var SHELL_EXEC_OPTIONS = {
|
|
1204
|
+
timeout: 3e4,
|
|
1205
|
+
maxBuffer: 1024 * 1024
|
|
1206
|
+
};
|
|
1207
|
+
function getErrorOutput(error) {
|
|
1208
|
+
if (typeof error !== "object" || error === null) return "";
|
|
1209
|
+
const output = error;
|
|
1210
|
+
return [output.stdout, output.stderr].filter((value) => typeof value === "string" && !!value).join("\n");
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Execute shell command with shared options (throws on error)
|
|
1214
|
+
*/
|
|
1215
|
+
function execShell(command) {
|
|
1216
|
+
return execAsync(command, SHELL_EXEC_OPTIONS);
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Execute shell command
|
|
1220
|
+
*/
|
|
1221
|
+
async function runShell(command) {
|
|
1222
|
+
try {
|
|
1223
|
+
const { stdout, stderr } = await execShell(command);
|
|
1224
|
+
return { content: stdout || stderr };
|
|
1225
|
+
} catch (error) {
|
|
1226
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1227
|
+
return {
|
|
1228
|
+
content: getErrorOutput(error),
|
|
1229
|
+
error: `Command failed: ${message}`,
|
|
1230
|
+
// v8 ignore next
|
|
1231
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {}
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
//#endregion
|
|
1236
|
+
//#region src/utils/tools/filesystem/grep.ts
|
|
1237
|
+
function capitalize(value) {
|
|
1238
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
1239
|
+
}
|
|
1240
|
+
function buildSearchPatterns(pattern) {
|
|
1241
|
+
const words = pattern.trim().split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
1242
|
+
if (words.length < 2) return [pattern];
|
|
1243
|
+
const camelCase = words.map((word, index) => index === 0 ? word.toLowerCase() : capitalize(word.toLowerCase())).join("");
|
|
1244
|
+
const pascalCase = words.map((word) => capitalize(word.toLowerCase())).join("");
|
|
1245
|
+
const snakeCase = words.map((word) => word.toLowerCase()).join("_");
|
|
1246
|
+
const upperSnakeCase = snakeCase.toUpperCase();
|
|
1247
|
+
const flexibleWhitespace = words.join(String.raw`\s+`);
|
|
1248
|
+
return Array.from(new Set([
|
|
1249
|
+
pattern,
|
|
1250
|
+
flexibleWhitespace,
|
|
1251
|
+
snakeCase,
|
|
1252
|
+
upperSnakeCase,
|
|
1253
|
+
camelCase,
|
|
1254
|
+
pascalCase
|
|
1255
|
+
]));
|
|
1256
|
+
}
|
|
1257
|
+
/**
|
|
1258
|
+
* Search for pattern in files using ripgrep if available, fallback to Node.js
|
|
1259
|
+
*/
|
|
1260
|
+
async function grepSearch(pattern, dirPath) {
|
|
1261
|
+
const patterns = buildSearchPatterns(pattern);
|
|
1262
|
+
for (const searchPattern of patterns) try {
|
|
1263
|
+
const { stdout } = await execShell(`rg --line-number --no-heading --smart-case "${searchPattern.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}" "${dirPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`);
|
|
1264
|
+
if (stdout) return { content: stdout };
|
|
1265
|
+
} catch {}
|
|
1266
|
+
try {
|
|
1267
|
+
if (!existsSync(dirPath)) return {
|
|
1268
|
+
content: "",
|
|
1269
|
+
error: `Directory not found: ${dirPath}`
|
|
1270
|
+
};
|
|
1271
|
+
const regexes = patterns.map((searchPattern) => new RegExp(searchPattern));
|
|
1272
|
+
const results = [];
|
|
1273
|
+
function searchDirectory(currentPath) {
|
|
1274
|
+
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
1275
|
+
for (const entry of entries) {
|
|
1276
|
+
const fullPath = join(currentPath, entry.name);
|
|
1277
|
+
if (entry.isDirectory()) {
|
|
1278
|
+
if (!entry.name.startsWith(".") && entry.name !== "node_modules") searchDirectory(fullPath);
|
|
1279
|
+
} else if (entry.isFile()) try {
|
|
1280
|
+
const lines = readFileSync(fullPath, "utf8").split("\n");
|
|
1281
|
+
for (let i = 0; i < lines.length; i++) for (const regex of regexes) if (regex.test(lines[i])) {
|
|
1282
|
+
results.push(`${fullPath}:${(i + 1).toString()}: ${lines[i].trim()}`);
|
|
1283
|
+
break;
|
|
1284
|
+
}
|
|
1285
|
+
} catch {}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
searchDirectory(dirPath);
|
|
1289
|
+
if (!results.length) return { content: "No matches found" };
|
|
1290
|
+
return { content: results.join("\n") };
|
|
1291
|
+
} catch (error) {
|
|
1292
|
+
return {
|
|
1293
|
+
content: "",
|
|
1294
|
+
error: `Search failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
//#endregion
|
|
1299
|
+
//#region src/utils/tools/filesystem/paths.ts
|
|
1205
1300
|
/**
|
|
1206
1301
|
* Create a directory and any missing parent directories
|
|
1207
1302
|
*/
|
|
@@ -1292,81 +1387,6 @@ function listDir(dirPath) {
|
|
|
1292
1387
|
};
|
|
1293
1388
|
}
|
|
1294
1389
|
}
|
|
1295
|
-
/**
|
|
1296
|
-
* Recursively find files by path
|
|
1297
|
-
*/
|
|
1298
|
-
function findFiles(dirPath, options = {}) {
|
|
1299
|
-
try {
|
|
1300
|
-
if (!existsSync(dirPath)) return {
|
|
1301
|
-
content: "",
|
|
1302
|
-
error: `Directory not found: ${dirPath}`
|
|
1303
|
-
};
|
|
1304
|
-
if (!statSync(dirPath).isDirectory()) return {
|
|
1305
|
-
content: "",
|
|
1306
|
-
error: `Path is not a directory: ${dirPath}`
|
|
1307
|
-
};
|
|
1308
|
-
const results = [];
|
|
1309
|
-
const includeHidden = options.includeHidden ?? false;
|
|
1310
|
-
const ignoredDirs = new Set(options.ignoredDirs ?? DEFAULT_FIND_FILES_IGNORED_DIRS);
|
|
1311
|
-
function searchDirectory(currentPath) {
|
|
1312
|
-
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
1313
|
-
for (const entry of entries) {
|
|
1314
|
-
const fullPath = join(currentPath, entry.name);
|
|
1315
|
-
if (entry.isDirectory()) {
|
|
1316
|
-
if (entry.name !== ".git" && !directoryMatchesIgnoredPattern(entry.name, ignoredDirs) && (includeHidden || !entry.name.startsWith("."))) searchDirectory(fullPath);
|
|
1317
|
-
} else if (entry.isFile() && (includeHidden || !entry.name.startsWith(".")) && fileMatchesPattern(fullPath, options.pattern)) results.push(fullPath);
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
searchDirectory(dirPath);
|
|
1321
|
-
return { content: results.join("\n") };
|
|
1322
|
-
} catch (error) {
|
|
1323
|
-
return {
|
|
1324
|
-
content: "",
|
|
1325
|
-
error: `Failed to find files: ${error instanceof Error ? error.message : String(error)}`
|
|
1326
|
-
};
|
|
1327
|
-
}
|
|
1328
|
-
}
|
|
1329
|
-
/**
|
|
1330
|
-
* Search for pattern in files using ripgrep if available, fallback to Node.js
|
|
1331
|
-
*/
|
|
1332
|
-
async function grepSearch(pattern, dirPath) {
|
|
1333
|
-
const patterns = buildSearchPatterns(pattern);
|
|
1334
|
-
for (const searchPattern of patterns) try {
|
|
1335
|
-
const { stdout } = await execShell(`rg --line-number --no-heading --smart-case "${searchPattern.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}" "${dirPath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")}"`);
|
|
1336
|
-
if (stdout) return { content: stdout };
|
|
1337
|
-
} catch {}
|
|
1338
|
-
try {
|
|
1339
|
-
if (!existsSync(dirPath)) return {
|
|
1340
|
-
content: "",
|
|
1341
|
-
error: `Directory not found: ${dirPath}`
|
|
1342
|
-
};
|
|
1343
|
-
const regexes = patterns.map((searchPattern) => new RegExp(searchPattern));
|
|
1344
|
-
const results = [];
|
|
1345
|
-
function searchDirectory(currentPath) {
|
|
1346
|
-
const entries = readdirSync(currentPath, { withFileTypes: true });
|
|
1347
|
-
for (const entry of entries) {
|
|
1348
|
-
const fullPath = join(currentPath, entry.name);
|
|
1349
|
-
if (entry.isDirectory()) {
|
|
1350
|
-
if (!entry.name.startsWith(".") && entry.name !== "node_modules") searchDirectory(fullPath);
|
|
1351
|
-
} else if (entry.isFile()) try {
|
|
1352
|
-
const lines = readFileSync(fullPath, "utf8").split("\n");
|
|
1353
|
-
for (let i = 0; i < lines.length; i++) for (const regex of regexes) if (regex.test(lines[i])) {
|
|
1354
|
-
results.push(`${fullPath}:${(i + 1).toString()}: ${lines[i].trim()}`);
|
|
1355
|
-
break;
|
|
1356
|
-
}
|
|
1357
|
-
} catch {}
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
searchDirectory(dirPath);
|
|
1361
|
-
if (!results.length) return { content: "No matches found" };
|
|
1362
|
-
return { content: results.join("\n") };
|
|
1363
|
-
} catch (error) {
|
|
1364
|
-
return {
|
|
1365
|
-
content: "",
|
|
1366
|
-
error: `Search failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1367
|
-
};
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
1390
|
//#endregion
|
|
1371
1391
|
//#region src/utils/tools/web/fetch.ts
|
|
1372
1392
|
var FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -1575,7 +1595,8 @@ function validateArgs(name, args) {
|
|
|
1575
1595
|
for (const key of [
|
|
1576
1596
|
"startLine",
|
|
1577
1597
|
"endLine",
|
|
1578
|
-
"maxLines"
|
|
1598
|
+
"maxLines",
|
|
1599
|
+
"maxChars"
|
|
1579
1600
|
]) if (args[key] !== void 0 && !Number.isInteger(args[key])) return {
|
|
1580
1601
|
content: "",
|
|
1581
1602
|
error: `Invalid optional numeric argument: ${key} (received keys: ${received})`
|
|
@@ -1584,6 +1605,10 @@ function validateArgs(name, args) {
|
|
|
1584
1605
|
content: "",
|
|
1585
1606
|
error: "Invalid read range: startLine, endLine, and maxLines must be >= 1"
|
|
1586
1607
|
};
|
|
1608
|
+
if (typeof args.maxChars === "number" && args.maxChars < 1) return {
|
|
1609
|
+
content: "",
|
|
1610
|
+
error: "Invalid read range: maxChars must be >= 1"
|
|
1611
|
+
};
|
|
1587
1612
|
if (args.endLine !== void 0 && args.maxLines !== void 0) return {
|
|
1588
1613
|
content: "",
|
|
1589
1614
|
error: "Invalid read range: endLine cannot be combined with maxLines"
|
|
@@ -1691,6 +1716,7 @@ async function executeTool(name, args, options) {
|
|
|
1691
1716
|
switch (name) {
|
|
1692
1717
|
case READ_FILE: return readFile(stringArgs.path, {
|
|
1693
1718
|
endLine: args.endLine,
|
|
1719
|
+
maxChars: args.maxChars,
|
|
1694
1720
|
maxLines: args.maxLines,
|
|
1695
1721
|
startLine: args.startLine
|
|
1696
1722
|
});
|