@probelabs/probe 0.6.0-rc254 → 0.6.0-rc256
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/README.md +166 -3
- package/bin/binaries/probe-v0.6.0-rc256-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc256-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc256-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc256-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc256-x86_64-unknown-linux-musl.tar.gz +0 -0
- package/build/agent/ProbeAgent.d.ts +1 -1
- package/build/agent/ProbeAgent.js +39 -23
- package/build/agent/acp/tools.js +2 -1
- package/build/agent/acp/tools.test.js +2 -1
- package/build/agent/bashDefaults.js +75 -6
- package/build/agent/index.js +1752 -426
- package/build/agent/mcp/xmlBridge.js +3 -2
- package/build/agent/schemaUtils.js +127 -0
- package/build/agent/tools.js +0 -28
- package/build/delegate.js +3 -0
- package/build/index.js +2 -0
- package/build/tools/common.js +26 -8
- package/build/tools/edit.js +457 -65
- package/build/tools/fileTracker.js +318 -0
- package/build/tools/fuzzyMatch.js +271 -0
- package/build/tools/hashline.js +131 -0
- package/build/tools/lineEditHeuristics.js +138 -0
- package/build/tools/symbolEdit.js +119 -0
- package/build/tools/vercel.js +40 -9
- package/cjs/agent/ProbeAgent.cjs +1863 -528
- package/cjs/index.cjs +1891 -554
- package/index.d.ts +189 -1
- package/package.json +1 -1
- package/src/agent/ProbeAgent.d.ts +1 -1
- package/src/agent/ProbeAgent.js +39 -23
- package/src/agent/acp/tools.js +2 -1
- package/src/agent/acp/tools.test.js +2 -1
- package/src/agent/bashDefaults.js +75 -6
- package/src/agent/index.js +18 -7
- package/src/agent/mcp/xmlBridge.js +3 -2
- package/src/agent/schemaUtils.js +127 -0
- package/src/agent/tools.js +0 -28
- package/src/delegate.js +3 -0
- package/src/index.js +2 -0
- package/src/tools/common.js +26 -8
- package/src/tools/edit.js +457 -65
- package/src/tools/fileTracker.js +318 -0
- package/src/tools/fuzzyMatch.js +271 -0
- package/src/tools/hashline.js +131 -0
- package/src/tools/lineEditHeuristics.js +138 -0
- package/src/tools/symbolEdit.js +119 -0
- package/src/tools/vercel.js +40 -9
- package/bin/binaries/probe-v0.6.0-rc254-aarch64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc254-aarch64-unknown-linux-musl.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc254-x86_64-apple-darwin.tar.gz +0 -0
- package/bin/binaries/probe-v0.6.0-rc254-x86_64-pc-windows-msvc.zip +0 -0
- package/bin/binaries/probe-v0.6.0-rc254-x86_64-unknown-linux-musl.tar.gz +0 -0
package/build/agent/index.js
CHANGED
|
@@ -2377,7 +2377,7 @@ async function waitForFileLock(lockPath, binaryPath) {
|
|
|
2377
2377
|
}
|
|
2378
2378
|
} catch {
|
|
2379
2379
|
}
|
|
2380
|
-
await new Promise((
|
|
2380
|
+
await new Promise((resolve9) => setTimeout(resolve9, LOCK_POLL_INTERVAL_MS));
|
|
2381
2381
|
}
|
|
2382
2382
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2383
2383
|
console.log(`Timeout waiting for file lock`);
|
|
@@ -3718,7 +3718,7 @@ Cwd: ${cwd}`;
|
|
|
3718
3718
|
}
|
|
3719
3719
|
}
|
|
3720
3720
|
function extractWithStdin(binaryPath, cliArgs, content, options, cwd) {
|
|
3721
|
-
return new Promise((
|
|
3721
|
+
return new Promise((resolve9, reject2) => {
|
|
3722
3722
|
const childProcess = spawn(binaryPath, ["extract", ...cliArgs], {
|
|
3723
3723
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3724
3724
|
cwd
|
|
@@ -3741,7 +3741,7 @@ function extractWithStdin(binaryPath, cliArgs, content, options, cwd) {
|
|
|
3741
3741
|
}
|
|
3742
3742
|
try {
|
|
3743
3743
|
const result = processExtractOutput(stdout, options);
|
|
3744
|
-
|
|
3744
|
+
resolve9(result);
|
|
3745
3745
|
} catch (error) {
|
|
3746
3746
|
reject2(error);
|
|
3747
3747
|
}
|
|
@@ -3849,6 +3849,7 @@ async function delegate({
|
|
|
3849
3849
|
model = null,
|
|
3850
3850
|
enableBash = false,
|
|
3851
3851
|
bashConfig = null,
|
|
3852
|
+
allowEdit = false,
|
|
3852
3853
|
architectureFileName = null,
|
|
3853
3854
|
promptType = "code-researcher",
|
|
3854
3855
|
allowedTools = null,
|
|
@@ -3928,6 +3929,8 @@ async function delegate({
|
|
|
3928
3929
|
// Inherit from parent
|
|
3929
3930
|
bashConfig,
|
|
3930
3931
|
// Inherit from parent
|
|
3932
|
+
allowEdit,
|
|
3933
|
+
// Inherit from parent
|
|
3931
3934
|
architectureFileName,
|
|
3932
3935
|
allowedTools,
|
|
3933
3936
|
disableTools,
|
|
@@ -4112,7 +4115,7 @@ var init_delegate = __esm({
|
|
|
4112
4115
|
if (debug) {
|
|
4113
4116
|
console.error(`[DelegationManager] Slot unavailable (${this.globalActive}/${this.maxConcurrent}), queuing... (queue size: ${this.waitQueue.length}, timeout: ${effectiveTimeout}ms)`);
|
|
4114
4117
|
}
|
|
4115
|
-
return new Promise((
|
|
4118
|
+
return new Promise((resolve9, reject2) => {
|
|
4116
4119
|
const entry = {
|
|
4117
4120
|
resolve: null,
|
|
4118
4121
|
// Will be wrapped below
|
|
@@ -4128,7 +4131,7 @@ var init_delegate = __esm({
|
|
|
4128
4131
|
if (settled) return;
|
|
4129
4132
|
settled = true;
|
|
4130
4133
|
if (entry.timeoutId) clearTimeout(entry.timeoutId);
|
|
4131
|
-
|
|
4134
|
+
resolve9(value);
|
|
4132
4135
|
};
|
|
4133
4136
|
entry.reject = (error) => {
|
|
4134
4137
|
if (settled) return;
|
|
@@ -4178,7 +4181,7 @@ var init_delegate = __esm({
|
|
|
4178
4181
|
while (this.waitQueue.length > 0 && this.globalActive < this.maxConcurrent) {
|
|
4179
4182
|
const next = this.waitQueue.shift();
|
|
4180
4183
|
if (!next) break;
|
|
4181
|
-
const { resolve:
|
|
4184
|
+
const { resolve: resolve9, reject: reject2, parentSessionId, queuedAt } = next;
|
|
4182
4185
|
if (parentSessionId) {
|
|
4183
4186
|
const sessionData = this.sessionDelegations.get(parentSessionId);
|
|
4184
4187
|
const sessionCount = sessionData?.count || 0;
|
|
@@ -4195,12 +4198,12 @@ var init_delegate = __esm({
|
|
|
4195
4198
|
const waitTime = Date.now() - queuedAt;
|
|
4196
4199
|
console.error(`[DelegationManager] Granted slot from queue (waited ${waitTime}ms). Active: ${this.globalActive}/${this.maxConcurrent}`);
|
|
4197
4200
|
}
|
|
4198
|
-
toResolve.push(
|
|
4201
|
+
toResolve.push(resolve9);
|
|
4199
4202
|
}
|
|
4200
4203
|
if (toResolve.length > 0 || toReject.length > 0) {
|
|
4201
4204
|
setImmediate(() => {
|
|
4202
|
-
for (const
|
|
4203
|
-
|
|
4205
|
+
for (const resolve9 of toResolve) {
|
|
4206
|
+
resolve9(true);
|
|
4204
4207
|
}
|
|
4205
4208
|
for (const { reject: reject2, error } of toReject) {
|
|
4206
4209
|
reject2(error);
|
|
@@ -8835,6 +8838,400 @@ var init_zod = __esm({
|
|
|
8835
8838
|
}
|
|
8836
8839
|
});
|
|
8837
8840
|
|
|
8841
|
+
// src/tools/fuzzyMatch.js
|
|
8842
|
+
function findFuzzyMatch(content, searchString) {
|
|
8843
|
+
if (!searchString || searchString.trim().length === 0) {
|
|
8844
|
+
return null;
|
|
8845
|
+
}
|
|
8846
|
+
const normalizedContent = content.replace(/\r\n/g, "\n");
|
|
8847
|
+
const normalizedSearch = searchString.replace(/\r\n/g, "\n");
|
|
8848
|
+
const contentLines = normalizedContent.split("\n");
|
|
8849
|
+
const searchLines = normalizedSearch.split("\n");
|
|
8850
|
+
const trimmed = lineTrimmedMatch(contentLines, searchLines);
|
|
8851
|
+
if (trimmed) return { ...trimmed, strategy: "line-trimmed" };
|
|
8852
|
+
const normalized = whitespaceNormalizedMatch(normalizedContent, normalizedSearch);
|
|
8853
|
+
if (normalized) return { ...normalized, strategy: "whitespace-normalized" };
|
|
8854
|
+
const indentFlex = indentFlexibleMatch(contentLines, searchLines);
|
|
8855
|
+
if (indentFlex) return { ...indentFlex, strategy: "indent-flexible" };
|
|
8856
|
+
return null;
|
|
8857
|
+
}
|
|
8858
|
+
function lineTrimmedMatch(contentLines, searchLines) {
|
|
8859
|
+
if (searchLines.length === 0) return null;
|
|
8860
|
+
const trimmedSearchLines = searchLines.map((line) => line.trim());
|
|
8861
|
+
if (trimmedSearchLines.every((line) => line === "")) return null;
|
|
8862
|
+
const windowSize = searchLines.length;
|
|
8863
|
+
const matches = [];
|
|
8864
|
+
for (let i = 0; i <= contentLines.length - windowSize; i++) {
|
|
8865
|
+
let allMatch = true;
|
|
8866
|
+
for (let j = 0; j < windowSize; j++) {
|
|
8867
|
+
if (contentLines[i + j].trim() !== trimmedSearchLines[j]) {
|
|
8868
|
+
allMatch = false;
|
|
8869
|
+
break;
|
|
8870
|
+
}
|
|
8871
|
+
}
|
|
8872
|
+
if (allMatch) {
|
|
8873
|
+
const matchedText = contentLines.slice(i, i + windowSize).join("\n");
|
|
8874
|
+
matches.push(matchedText);
|
|
8875
|
+
}
|
|
8876
|
+
}
|
|
8877
|
+
if (matches.length === 0) return null;
|
|
8878
|
+
return {
|
|
8879
|
+
matchedText: matches[0],
|
|
8880
|
+
count: matches.length
|
|
8881
|
+
};
|
|
8882
|
+
}
|
|
8883
|
+
function whitespaceNormalizedMatch(content, search2) {
|
|
8884
|
+
if (!search2 || search2.trim().length === 0) return null;
|
|
8885
|
+
const { normalized: normContent, indexMap: contentMap } = buildNormalizedMap(content);
|
|
8886
|
+
const { normalized: normSearch } = buildNormalizedMap(search2);
|
|
8887
|
+
if (normSearch.length === 0) return null;
|
|
8888
|
+
const matches = [];
|
|
8889
|
+
let searchStart = 0;
|
|
8890
|
+
while (searchStart <= normContent.length - normSearch.length) {
|
|
8891
|
+
const idx = normContent.indexOf(normSearch, searchStart);
|
|
8892
|
+
if (idx === -1) break;
|
|
8893
|
+
const originalStart = contentMap[idx];
|
|
8894
|
+
const originalEnd = contentMap[idx + normSearch.length - 1];
|
|
8895
|
+
let actualEnd = originalEnd + 1;
|
|
8896
|
+
while (actualEnd < content.length && /[ \t]/.test(content[actualEnd]) && (actualEnd === originalEnd + 1 || /[ \t]/.test(content[actualEnd - 1]))) {
|
|
8897
|
+
if (contentMap.indexOf(actualEnd) > idx + normSearch.length - 1 || contentMap.indexOf(actualEnd) === -1) {
|
|
8898
|
+
break;
|
|
8899
|
+
}
|
|
8900
|
+
actualEnd++;
|
|
8901
|
+
}
|
|
8902
|
+
const matchedText = content.substring(originalStart, actualEnd);
|
|
8903
|
+
matches.push(matchedText);
|
|
8904
|
+
searchStart = idx + 1;
|
|
8905
|
+
}
|
|
8906
|
+
if (matches.length === 0) return null;
|
|
8907
|
+
return {
|
|
8908
|
+
matchedText: matches[0],
|
|
8909
|
+
count: matches.length
|
|
8910
|
+
};
|
|
8911
|
+
}
|
|
8912
|
+
function buildNormalizedMap(str) {
|
|
8913
|
+
const normalized = [];
|
|
8914
|
+
const indexMap = [];
|
|
8915
|
+
let i = 0;
|
|
8916
|
+
while (i < str.length) {
|
|
8917
|
+
const ch = str[i];
|
|
8918
|
+
if (ch === " " || ch === " ") {
|
|
8919
|
+
normalized.push(" ");
|
|
8920
|
+
indexMap.push(i);
|
|
8921
|
+
while (i < str.length && (str[i] === " " || str[i] === " ")) {
|
|
8922
|
+
i++;
|
|
8923
|
+
}
|
|
8924
|
+
} else {
|
|
8925
|
+
normalized.push(ch);
|
|
8926
|
+
indexMap.push(i);
|
|
8927
|
+
i++;
|
|
8928
|
+
}
|
|
8929
|
+
}
|
|
8930
|
+
return {
|
|
8931
|
+
normalized: normalized.join(""),
|
|
8932
|
+
indexMap
|
|
8933
|
+
};
|
|
8934
|
+
}
|
|
8935
|
+
function indentFlexibleMatch(contentLines, searchLines) {
|
|
8936
|
+
if (searchLines.length === 0) return null;
|
|
8937
|
+
if (searchLines.every((line) => line.trim() === "")) return null;
|
|
8938
|
+
const searchMinIndent = getMinIndent(searchLines);
|
|
8939
|
+
const strippedSearch = searchLines.map((line) => stripIndent(line, searchMinIndent));
|
|
8940
|
+
const windowSize = searchLines.length;
|
|
8941
|
+
const matches = [];
|
|
8942
|
+
for (let i = 0; i <= contentLines.length - windowSize; i++) {
|
|
8943
|
+
const windowLines = contentLines.slice(i, i + windowSize);
|
|
8944
|
+
const windowMinIndent = getMinIndent(windowLines);
|
|
8945
|
+
const strippedWindow = windowLines.map((line) => stripIndent(line, windowMinIndent));
|
|
8946
|
+
let allMatch = true;
|
|
8947
|
+
for (let j = 0; j < windowSize; j++) {
|
|
8948
|
+
if (strippedWindow[j] !== strippedSearch[j]) {
|
|
8949
|
+
allMatch = false;
|
|
8950
|
+
break;
|
|
8951
|
+
}
|
|
8952
|
+
}
|
|
8953
|
+
if (allMatch) {
|
|
8954
|
+
const matchedText = windowLines.join("\n");
|
|
8955
|
+
matches.push(matchedText);
|
|
8956
|
+
}
|
|
8957
|
+
}
|
|
8958
|
+
if (matches.length === 0) return null;
|
|
8959
|
+
return {
|
|
8960
|
+
matchedText: matches[0],
|
|
8961
|
+
count: matches.length
|
|
8962
|
+
};
|
|
8963
|
+
}
|
|
8964
|
+
function getMinIndent(lines) {
|
|
8965
|
+
let min = Infinity;
|
|
8966
|
+
for (const line of lines) {
|
|
8967
|
+
if (line.trim() === "") continue;
|
|
8968
|
+
const match2 = line.match(/^([ \t]*)/);
|
|
8969
|
+
if (match2) {
|
|
8970
|
+
min = Math.min(min, match2[1].length);
|
|
8971
|
+
}
|
|
8972
|
+
}
|
|
8973
|
+
return min === Infinity ? 0 : min;
|
|
8974
|
+
}
|
|
8975
|
+
function stripIndent(line, amount) {
|
|
8976
|
+
if (line.trim() === "") return "";
|
|
8977
|
+
if (amount <= 0) return line;
|
|
8978
|
+
return line.substring(Math.min(amount, line.length));
|
|
8979
|
+
}
|
|
8980
|
+
var init_fuzzyMatch = __esm({
|
|
8981
|
+
"src/tools/fuzzyMatch.js"() {
|
|
8982
|
+
"use strict";
|
|
8983
|
+
}
|
|
8984
|
+
});
|
|
8985
|
+
|
|
8986
|
+
// src/tools/symbolEdit.js
|
|
8987
|
+
async function findSymbol(filePath, symbolName, cwd) {
|
|
8988
|
+
try {
|
|
8989
|
+
const result = await extract({
|
|
8990
|
+
files: [`${filePath}#${symbolName}`],
|
|
8991
|
+
format: "json",
|
|
8992
|
+
json: true,
|
|
8993
|
+
cwd
|
|
8994
|
+
});
|
|
8995
|
+
if (!result || !result.results || result.results.length === 0) {
|
|
8996
|
+
return null;
|
|
8997
|
+
}
|
|
8998
|
+
const match2 = result.results[0];
|
|
8999
|
+
return {
|
|
9000
|
+
startLine: match2.lines[0],
|
|
9001
|
+
// 1-indexed
|
|
9002
|
+
endLine: match2.lines[1],
|
|
9003
|
+
// 1-indexed
|
|
9004
|
+
code: match2.code,
|
|
9005
|
+
nodeType: match2.node_type,
|
|
9006
|
+
file: match2.file
|
|
9007
|
+
};
|
|
9008
|
+
} catch (error) {
|
|
9009
|
+
if (process.env.DEBUG === "1") {
|
|
9010
|
+
console.error(`[SymbolEdit] findSymbol error for "${symbolName}" in ${filePath}: ${error.message}`);
|
|
9011
|
+
}
|
|
9012
|
+
return null;
|
|
9013
|
+
}
|
|
9014
|
+
}
|
|
9015
|
+
async function findAllSymbols(filePath, symbolName, cwd) {
|
|
9016
|
+
try {
|
|
9017
|
+
const result = await extract({
|
|
9018
|
+
files: [`${filePath}#${symbolName}`],
|
|
9019
|
+
format: "json",
|
|
9020
|
+
json: true,
|
|
9021
|
+
cwd
|
|
9022
|
+
});
|
|
9023
|
+
if (!result || !result.results || result.results.length === 0) {
|
|
9024
|
+
return [];
|
|
9025
|
+
}
|
|
9026
|
+
return result.results.map((match2) => ({
|
|
9027
|
+
startLine: match2.lines[0],
|
|
9028
|
+
endLine: match2.lines[1],
|
|
9029
|
+
code: match2.code,
|
|
9030
|
+
nodeType: match2.node_type,
|
|
9031
|
+
file: match2.file,
|
|
9032
|
+
qualifiedName: match2.symbol_signature || symbolName
|
|
9033
|
+
}));
|
|
9034
|
+
} catch (error) {
|
|
9035
|
+
if (process.env.DEBUG === "1") {
|
|
9036
|
+
console.error(`[SymbolEdit] findAllSymbols error for "${symbolName}" in ${filePath}: ${error.message}`);
|
|
9037
|
+
}
|
|
9038
|
+
return [];
|
|
9039
|
+
}
|
|
9040
|
+
}
|
|
9041
|
+
function detectBaseIndent(code) {
|
|
9042
|
+
const lines = code.split("\n");
|
|
9043
|
+
for (const line of lines) {
|
|
9044
|
+
if (line.trim().length > 0) {
|
|
9045
|
+
const match2 = line.match(/^(\s*)/);
|
|
9046
|
+
return match2 ? match2[1] : "";
|
|
9047
|
+
}
|
|
9048
|
+
}
|
|
9049
|
+
return "";
|
|
9050
|
+
}
|
|
9051
|
+
function reindent(newContent, targetIndent) {
|
|
9052
|
+
const lines = newContent.split("\n");
|
|
9053
|
+
const sourceIndent = detectBaseIndent(newContent);
|
|
9054
|
+
return lines.map((line) => {
|
|
9055
|
+
if (line.trim().length === 0) {
|
|
9056
|
+
return "";
|
|
9057
|
+
}
|
|
9058
|
+
if (line.startsWith(sourceIndent)) {
|
|
9059
|
+
return targetIndent + line.slice(sourceIndent.length);
|
|
9060
|
+
}
|
|
9061
|
+
return line;
|
|
9062
|
+
}).join("\n");
|
|
9063
|
+
}
|
|
9064
|
+
var init_symbolEdit = __esm({
|
|
9065
|
+
"src/tools/symbolEdit.js"() {
|
|
9066
|
+
"use strict";
|
|
9067
|
+
init_extract();
|
|
9068
|
+
}
|
|
9069
|
+
});
|
|
9070
|
+
|
|
9071
|
+
// src/tools/hashline.js
|
|
9072
|
+
function computeLineHash(line) {
|
|
9073
|
+
const stripped = (line || "").replace(/\s+/g, "");
|
|
9074
|
+
let h = 5381;
|
|
9075
|
+
for (let i = 0; i < stripped.length; i++) {
|
|
9076
|
+
h = (h << 5) + h + stripped.charCodeAt(i) & 4294967295;
|
|
9077
|
+
}
|
|
9078
|
+
return ((h >>> 0) % 256).toString(16).padStart(2, "0");
|
|
9079
|
+
}
|
|
9080
|
+
function parseLineRef(ref2) {
|
|
9081
|
+
if (ref2 === void 0 || ref2 === null) return null;
|
|
9082
|
+
const str = String(ref2).trim();
|
|
9083
|
+
if (!str) return null;
|
|
9084
|
+
const hashMatch = str.match(/^(\d+):([0-9a-fA-F]{2})$/);
|
|
9085
|
+
if (hashMatch) {
|
|
9086
|
+
const line = parseInt(hashMatch[1], 10);
|
|
9087
|
+
if (line < 1 || !isFinite(line)) return null;
|
|
9088
|
+
return { line, hash: hashMatch[2].toLowerCase() };
|
|
9089
|
+
}
|
|
9090
|
+
const lineMatch = str.match(/^(\d+)$/);
|
|
9091
|
+
if (lineMatch) {
|
|
9092
|
+
const line = parseInt(lineMatch[1], 10);
|
|
9093
|
+
if (line < 1 || !isFinite(line)) return null;
|
|
9094
|
+
return { line, hash: null };
|
|
9095
|
+
}
|
|
9096
|
+
return null;
|
|
9097
|
+
}
|
|
9098
|
+
function validateLineHash(lineNum, hash, fileLines) {
|
|
9099
|
+
const idx = lineNum - 1;
|
|
9100
|
+
if (idx < 0 || idx >= fileLines.length) {
|
|
9101
|
+
return { valid: false, actualHash: "", actualContent: "" };
|
|
9102
|
+
}
|
|
9103
|
+
const actualContent = fileLines[idx];
|
|
9104
|
+
const actualHash = computeLineHash(actualContent);
|
|
9105
|
+
return {
|
|
9106
|
+
valid: actualHash === hash.toLowerCase(),
|
|
9107
|
+
actualHash,
|
|
9108
|
+
actualContent
|
|
9109
|
+
};
|
|
9110
|
+
}
|
|
9111
|
+
function annotateOutputWithHashes(output) {
|
|
9112
|
+
if (!output || typeof output !== "string") return output;
|
|
9113
|
+
return output.split("\n").map((line) => {
|
|
9114
|
+
const cleanLine = line.endsWith("\r") ? line.slice(0, -1) : line;
|
|
9115
|
+
const match2 = cleanLine.match(/^(\s*)(\d+)(\s*\|)(.*)$/);
|
|
9116
|
+
if (!match2) return line;
|
|
9117
|
+
const [, prefix, lineNum, pipeSection, content] = match2;
|
|
9118
|
+
const hash = computeLineHash(content);
|
|
9119
|
+
const cr = line.endsWith("\r") ? "\r" : "";
|
|
9120
|
+
return `${prefix}${lineNum}:${hash}${pipeSection}${content}${cr}`;
|
|
9121
|
+
}).join("\n");
|
|
9122
|
+
}
|
|
9123
|
+
function stripHashlinePrefixes(text) {
|
|
9124
|
+
if (!text || typeof text !== "string") return { cleaned: text || "", stripped: false };
|
|
9125
|
+
const lines = text.split("\n");
|
|
9126
|
+
if (lines.length === 0) return { cleaned: "", stripped: false };
|
|
9127
|
+
const nonEmptyLines = lines.filter((l) => l.trim().length > 0);
|
|
9128
|
+
if (nonEmptyLines.length === 0) return { cleaned: text, stripped: false };
|
|
9129
|
+
const prefixPattern = /^\s*\d+(?::[0-9a-fA-F]{2})?\s*\|\s?/;
|
|
9130
|
+
const matchCount = nonEmptyLines.filter((l) => prefixPattern.test(l)).length;
|
|
9131
|
+
if (matchCount / nonEmptyLines.length <= 0.5) {
|
|
9132
|
+
return { cleaned: text, stripped: false };
|
|
9133
|
+
}
|
|
9134
|
+
const cleaned = lines.map((line) => {
|
|
9135
|
+
if (line.trim().length === 0) return line;
|
|
9136
|
+
return line.replace(prefixPattern, "");
|
|
9137
|
+
}).join("\n");
|
|
9138
|
+
return { cleaned, stripped: true };
|
|
9139
|
+
}
|
|
9140
|
+
var init_hashline = __esm({
|
|
9141
|
+
"src/tools/hashline.js"() {
|
|
9142
|
+
"use strict";
|
|
9143
|
+
}
|
|
9144
|
+
});
|
|
9145
|
+
|
|
9146
|
+
// src/tools/lineEditHeuristics.js
|
|
9147
|
+
function stripEchoedBoundaries(newStr, fileLines, startLine, endLine, position) {
|
|
9148
|
+
const modifications = [];
|
|
9149
|
+
let lines = newStr.split("\n");
|
|
9150
|
+
if (lines.length === 0) return { result: newStr, modifications };
|
|
9151
|
+
if (position === "after") {
|
|
9152
|
+
const anchorIdx = startLine - 1;
|
|
9153
|
+
if (anchorIdx >= 0 && anchorIdx < fileLines.length) {
|
|
9154
|
+
const anchorTrimmed = fileLines[anchorIdx].trim();
|
|
9155
|
+
if (anchorTrimmed.length > 0 && lines.length > 0 && lines[0].trim() === anchorTrimmed) {
|
|
9156
|
+
lines = lines.slice(1);
|
|
9157
|
+
modifications.push("stripped echoed anchor line (insert-after)");
|
|
9158
|
+
}
|
|
9159
|
+
}
|
|
9160
|
+
} else if (position === "before") {
|
|
9161
|
+
const anchorIdx = startLine - 1;
|
|
9162
|
+
if (anchorIdx >= 0 && anchorIdx < fileLines.length) {
|
|
9163
|
+
const anchorTrimmed = fileLines[anchorIdx].trim();
|
|
9164
|
+
if (anchorTrimmed.length > 0 && lines.length > 0 && lines[lines.length - 1].trim() === anchorTrimmed) {
|
|
9165
|
+
lines = lines.slice(0, -1);
|
|
9166
|
+
modifications.push("stripped echoed anchor line (insert-before)");
|
|
9167
|
+
}
|
|
9168
|
+
}
|
|
9169
|
+
} else {
|
|
9170
|
+
const beforeIdx = startLine - 2;
|
|
9171
|
+
if (beforeIdx >= 0 && beforeIdx < fileLines.length) {
|
|
9172
|
+
const beforeTrimmed = fileLines[beforeIdx].trim();
|
|
9173
|
+
if (beforeTrimmed.length > 0 && lines.length > 0 && lines[0].trim() === beforeTrimmed) {
|
|
9174
|
+
lines = lines.slice(1);
|
|
9175
|
+
modifications.push("stripped echoed line before range");
|
|
9176
|
+
}
|
|
9177
|
+
}
|
|
9178
|
+
const afterIdx = endLine;
|
|
9179
|
+
if (afterIdx >= 0 && afterIdx < fileLines.length) {
|
|
9180
|
+
const afterTrimmed = fileLines[afterIdx].trim();
|
|
9181
|
+
if (afterTrimmed.length > 0 && lines.length > 0 && lines[lines.length - 1].trim() === afterTrimmed) {
|
|
9182
|
+
lines = lines.slice(0, -1);
|
|
9183
|
+
modifications.push("stripped echoed line after range");
|
|
9184
|
+
}
|
|
9185
|
+
}
|
|
9186
|
+
}
|
|
9187
|
+
return { result: lines.join("\n"), modifications };
|
|
9188
|
+
}
|
|
9189
|
+
function restoreIndentation(newStr, originalLines) {
|
|
9190
|
+
const modifications = [];
|
|
9191
|
+
if (!newStr || !originalLines || originalLines.length === 0) {
|
|
9192
|
+
return { result: newStr || "", modifications };
|
|
9193
|
+
}
|
|
9194
|
+
const originalCode = originalLines.join("\n");
|
|
9195
|
+
const targetIndent = detectBaseIndent(originalCode);
|
|
9196
|
+
const newIndent = detectBaseIndent(newStr);
|
|
9197
|
+
if (targetIndent !== newIndent) {
|
|
9198
|
+
const reindented = reindent(newStr, targetIndent);
|
|
9199
|
+
if (reindented !== newStr) {
|
|
9200
|
+
modifications.push(`reindented from "${newIndent}" to "${targetIndent}"`);
|
|
9201
|
+
return { result: reindented, modifications };
|
|
9202
|
+
}
|
|
9203
|
+
}
|
|
9204
|
+
return { result: newStr, modifications };
|
|
9205
|
+
}
|
|
9206
|
+
function cleanNewString(newStr, fileLines, startLine, endLine, position) {
|
|
9207
|
+
const modifications = [];
|
|
9208
|
+
if (!newStr && newStr !== "") return { cleaned: "", modifications };
|
|
9209
|
+
const { cleaned: afterPrefixes, stripped } = stripHashlinePrefixes(newStr);
|
|
9210
|
+
if (stripped) modifications.push("stripped line-number prefixes");
|
|
9211
|
+
const { result: afterEchoes, modifications: echoMods } = stripEchoedBoundaries(
|
|
9212
|
+
afterPrefixes,
|
|
9213
|
+
fileLines,
|
|
9214
|
+
startLine,
|
|
9215
|
+
endLine,
|
|
9216
|
+
position
|
|
9217
|
+
);
|
|
9218
|
+
modifications.push(...echoMods);
|
|
9219
|
+
if (!position) {
|
|
9220
|
+
const originalLines = fileLines.slice(startLine - 1, endLine);
|
|
9221
|
+
const { result: afterIndent, modifications: indentMods } = restoreIndentation(afterEchoes, originalLines);
|
|
9222
|
+
modifications.push(...indentMods);
|
|
9223
|
+
return { cleaned: afterIndent, modifications };
|
|
9224
|
+
}
|
|
9225
|
+
return { cleaned: afterEchoes, modifications };
|
|
9226
|
+
}
|
|
9227
|
+
var init_lineEditHeuristics = __esm({
|
|
9228
|
+
"src/tools/lineEditHeuristics.js"() {
|
|
9229
|
+
"use strict";
|
|
9230
|
+
init_symbolEdit();
|
|
9231
|
+
init_hashline();
|
|
9232
|
+
}
|
|
9233
|
+
});
|
|
9234
|
+
|
|
8838
9235
|
// src/tools/edit.js
|
|
8839
9236
|
import { tool } from "ai";
|
|
8840
9237
|
import { promises as fs6 } from "fs";
|
|
@@ -8862,29 +9259,204 @@ function parseFileToolOptions(options = {}) {
|
|
|
8862
9259
|
workspaceRoot: options.workspaceRoot || options.cwd || allowedFolders.length > 0 && allowedFolders[0] || process.cwd()
|
|
8863
9260
|
};
|
|
8864
9261
|
}
|
|
9262
|
+
async function handleSymbolEdit({ resolvedPath, file_path, symbol, new_string, position, debug, cwd, fileTracker }) {
|
|
9263
|
+
if (typeof symbol !== "string" || symbol.trim() === "") {
|
|
9264
|
+
return 'Error editing file: Invalid symbol - must be a non-empty string. Provide the name of a function, class, method, or other named code definition (e.g. "myFunction" or "MyClass.myMethod"). To edit by text matching instead, use old_string + new_string.';
|
|
9265
|
+
}
|
|
9266
|
+
if (position !== void 0 && position !== null && position !== "before" && position !== "after") {
|
|
9267
|
+
return 'Error editing file: Invalid position - must be "before" or "after". Use position="before" to insert code above the symbol, or position="after" to insert code below it. Omit position entirely to replace the symbol with new_string.';
|
|
9268
|
+
}
|
|
9269
|
+
const allMatches = await findAllSymbols(resolvedPath, symbol, cwd || process.cwd());
|
|
9270
|
+
if (allMatches.length === 0) {
|
|
9271
|
+
return `Error editing file: Symbol "${symbol}" not found in ${file_path}. Verify the symbol name matches a top-level function, class, method, or other named definition exactly as declared in the source. Use 'search' or 'extract' to inspect the file and find the correct symbol name. Alternatively, use old_string + new_string for text-based editing instead.`;
|
|
9272
|
+
}
|
|
9273
|
+
if (allMatches.length > 1) {
|
|
9274
|
+
const suggestions = allMatches.map(
|
|
9275
|
+
(m) => ` - ${m.qualifiedName} (${m.nodeType}, line ${m.startLine})`
|
|
9276
|
+
).join("\n");
|
|
9277
|
+
return `Error editing ${file_path}: Found ${allMatches.length} symbols named "${symbol}". Use a qualified name to specify which one:
|
|
9278
|
+
${suggestions}
|
|
9279
|
+
|
|
9280
|
+
Example: <edit><file_path>${file_path}</file_path><symbol>${allMatches[0].qualifiedName}</symbol><new_string>...</new_string></edit>`;
|
|
9281
|
+
}
|
|
9282
|
+
const symbolInfo = allMatches[0];
|
|
9283
|
+
if (fileTracker) {
|
|
9284
|
+
const check = fileTracker.checkSymbolContent(resolvedPath, symbol, symbolInfo.code);
|
|
9285
|
+
if (!check.ok && check.reason === "stale") {
|
|
9286
|
+
return `Error editing ${file_path}: Symbol "${symbol}" has changed since you last read it. Use extract to re-read the current content, then retry.
|
|
9287
|
+
|
|
9288
|
+
Example: <extract><targets>${file_path}#${symbol}</targets></extract>`;
|
|
9289
|
+
}
|
|
9290
|
+
}
|
|
9291
|
+
const content = await fs6.readFile(resolvedPath, "utf-8");
|
|
9292
|
+
const lines = content.split("\n");
|
|
9293
|
+
if (position) {
|
|
9294
|
+
const refIndent = detectBaseIndent(symbolInfo.code);
|
|
9295
|
+
const reindented = reindent(new_string, refIndent);
|
|
9296
|
+
const newLines = reindented.split("\n");
|
|
9297
|
+
if (position === "after") {
|
|
9298
|
+
lines.splice(symbolInfo.endLine, 0, "", ...newLines);
|
|
9299
|
+
} else {
|
|
9300
|
+
lines.splice(symbolInfo.startLine - 1, 0, ...newLines, "");
|
|
9301
|
+
}
|
|
9302
|
+
await fs6.writeFile(resolvedPath, lines.join("\n"), "utf-8");
|
|
9303
|
+
if (fileTracker) {
|
|
9304
|
+
const updated = await findSymbol(resolvedPath, symbol, cwd || process.cwd());
|
|
9305
|
+
if (updated) {
|
|
9306
|
+
fileTracker.trackSymbolAfterWrite(resolvedPath, symbol, updated.code, updated.startLine, updated.endLine);
|
|
9307
|
+
}
|
|
9308
|
+
fileTracker.markFileSeen(resolvedPath);
|
|
9309
|
+
}
|
|
9310
|
+
const insertLine = position === "after" ? symbolInfo.endLine + 1 : symbolInfo.startLine;
|
|
9311
|
+
if (debug) {
|
|
9312
|
+
console.error(`[Edit] Successfully inserted ${newLines.length} lines ${position} "${symbol}" at line ${insertLine} in ${resolvedPath}`);
|
|
9313
|
+
}
|
|
9314
|
+
return `Successfully inserted ${newLines.length} lines ${position} symbol "${symbol}" in ${file_path} (at line ${insertLine})`;
|
|
9315
|
+
} else {
|
|
9316
|
+
const originalIndent = detectBaseIndent(symbolInfo.code);
|
|
9317
|
+
const reindented = reindent(new_string, originalIndent);
|
|
9318
|
+
const newLines = reindented.split("\n");
|
|
9319
|
+
lines.splice(symbolInfo.startLine - 1, symbolInfo.endLine - symbolInfo.startLine + 1, ...newLines);
|
|
9320
|
+
await fs6.writeFile(resolvedPath, lines.join("\n"), "utf-8");
|
|
9321
|
+
if (fileTracker) {
|
|
9322
|
+
const updated = await findSymbol(resolvedPath, symbol, cwd || process.cwd());
|
|
9323
|
+
if (updated) {
|
|
9324
|
+
fileTracker.trackSymbolAfterWrite(resolvedPath, symbol, updated.code, updated.startLine, updated.endLine);
|
|
9325
|
+
}
|
|
9326
|
+
fileTracker.markFileSeen(resolvedPath);
|
|
9327
|
+
}
|
|
9328
|
+
if (debug) {
|
|
9329
|
+
console.error(`[Edit] Successfully replaced symbol "${symbol}" in ${resolvedPath} (lines ${symbolInfo.startLine}-${symbolInfo.endLine})`);
|
|
9330
|
+
}
|
|
9331
|
+
return `Successfully replaced symbol "${symbol}" in ${file_path} (was lines ${symbolInfo.startLine}-${symbolInfo.endLine}, now ${newLines.length} lines)`;
|
|
9332
|
+
}
|
|
9333
|
+
}
|
|
9334
|
+
function buildLineEditResponse(file_path, startLine, endLine, newLineCount, updatedLines, insertOffset, action, heuristicMods) {
|
|
9335
|
+
const contextBefore = 1;
|
|
9336
|
+
const contextAfter = 1;
|
|
9337
|
+
const contextStart = Math.max(0, insertOffset - contextBefore);
|
|
9338
|
+
const contextEnd = Math.min(updatedLines.length, insertOffset + newLineCount + contextAfter);
|
|
9339
|
+
let context = "Context:\n";
|
|
9340
|
+
for (let i = contextStart; i < contextEnd; i++) {
|
|
9341
|
+
const lineNum = i + 1;
|
|
9342
|
+
const hash = computeLineHash(updatedLines[i]);
|
|
9343
|
+
const isNew = i >= insertOffset && i < insertOffset + newLineCount;
|
|
9344
|
+
const marker = isNew ? ">" : " ";
|
|
9345
|
+
context += `${marker} ${lineNum}:${hash} | ${updatedLines[i]}
|
|
9346
|
+
`;
|
|
9347
|
+
}
|
|
9348
|
+
let msg = `Successfully edited ${file_path} (${action})`;
|
|
9349
|
+
if (heuristicMods.length > 0) {
|
|
9350
|
+
msg += ` [auto-corrected: ${heuristicMods.join(", ")}]`;
|
|
9351
|
+
}
|
|
9352
|
+
msg += "\n" + context;
|
|
9353
|
+
return msg;
|
|
9354
|
+
}
|
|
9355
|
+
async function handleLineEdit({ resolvedPath, file_path, start_line, end_line, new_string, position, debug, fileTracker }) {
|
|
9356
|
+
const startRef = parseLineRef(start_line);
|
|
9357
|
+
if (!startRef) {
|
|
9358
|
+
return `Error editing file: Invalid start_line '${start_line}'. Use a line number (e.g. "42") or line:hash (e.g. "42:ab"). Line numbers are 1-indexed.`;
|
|
9359
|
+
}
|
|
9360
|
+
let endRef = null;
|
|
9361
|
+
if (end_line !== void 0 && end_line !== null) {
|
|
9362
|
+
endRef = parseLineRef(end_line);
|
|
9363
|
+
if (!endRef) {
|
|
9364
|
+
return `Error editing file: Invalid end_line '${end_line}'. Use a line number (e.g. "55") or line:hash (e.g. "55:cd"). Must be >= start_line.`;
|
|
9365
|
+
}
|
|
9366
|
+
}
|
|
9367
|
+
const startLine = startRef.line;
|
|
9368
|
+
const endLine = endRef ? endRef.line : startLine;
|
|
9369
|
+
if (endLine < startLine) {
|
|
9370
|
+
return `Error editing file: end_line (${endLine}) must be >= start_line (${startLine}).`;
|
|
9371
|
+
}
|
|
9372
|
+
if (position !== void 0 && position !== null && position !== "before" && position !== "after") {
|
|
9373
|
+
return 'Error editing file: Invalid position - must be "before" or "after". Use position="before" to insert before the line, or position="after" to insert after it.';
|
|
9374
|
+
}
|
|
9375
|
+
const content = await fs6.readFile(resolvedPath, "utf-8");
|
|
9376
|
+
const fileLines = content.split("\n");
|
|
9377
|
+
if (startLine > fileLines.length) {
|
|
9378
|
+
return `Error editing file: Line ${startLine} is beyond file length (${fileLines.length} lines). Use 'extract' to read the current file content.`;
|
|
9379
|
+
}
|
|
9380
|
+
if (endLine > fileLines.length) {
|
|
9381
|
+
return `Error editing file: Line ${endLine} is beyond file length (${fileLines.length} lines). Use 'extract' to read the current file content.`;
|
|
9382
|
+
}
|
|
9383
|
+
if (startRef.hash) {
|
|
9384
|
+
const validation = validateLineHash(startLine, startRef.hash, fileLines);
|
|
9385
|
+
if (!validation.valid) {
|
|
9386
|
+
return `Error editing file: Line ${startLine} has changed since last read. Expected hash '${startRef.hash}' but content is now: ${startLine}:${validation.actualHash} | ${validation.actualContent}. Use '${startLine}:${validation.actualHash}' instead.`;
|
|
9387
|
+
}
|
|
9388
|
+
}
|
|
9389
|
+
if (endRef && endRef.hash) {
|
|
9390
|
+
const validation = validateLineHash(endLine, endRef.hash, fileLines);
|
|
9391
|
+
if (!validation.valid) {
|
|
9392
|
+
return `Error editing file: Line ${endLine} has changed since last read. Expected hash '${endRef.hash}' but content is now: ${endLine}:${validation.actualHash} | ${validation.actualContent}. Use '${endLine}:${validation.actualHash}' instead.`;
|
|
9393
|
+
}
|
|
9394
|
+
}
|
|
9395
|
+
const { cleaned, modifications } = cleanNewString(new_string, fileLines, startLine, endLine, position);
|
|
9396
|
+
if (debug) {
|
|
9397
|
+
if (modifications.length > 0) {
|
|
9398
|
+
console.error(`[Edit] Heuristic corrections: ${modifications.join(", ")}`);
|
|
9399
|
+
}
|
|
9400
|
+
}
|
|
9401
|
+
const newLines = cleaned === "" ? [] : cleaned.split("\n");
|
|
9402
|
+
if (position === "after") {
|
|
9403
|
+
fileLines.splice(startLine, 0, ...newLines);
|
|
9404
|
+
await fs6.writeFile(resolvedPath, fileLines.join("\n"), "utf-8");
|
|
9405
|
+
if (fileTracker) await fileTracker.trackFileAfterWrite(resolvedPath);
|
|
9406
|
+
const action = `${newLines.length} line${newLines.length !== 1 ? "s" : ""} inserted after line ${startLine}`;
|
|
9407
|
+
return buildLineEditResponse(file_path, startLine, startLine, newLines.length, fileLines, startLine, action, modifications);
|
|
9408
|
+
} else if (position === "before") {
|
|
9409
|
+
fileLines.splice(startLine - 1, 0, ...newLines);
|
|
9410
|
+
await fs6.writeFile(resolvedPath, fileLines.join("\n"), "utf-8");
|
|
9411
|
+
if (fileTracker) await fileTracker.trackFileAfterWrite(resolvedPath);
|
|
9412
|
+
const action = `${newLines.length} line${newLines.length !== 1 ? "s" : ""} inserted before line ${startLine}`;
|
|
9413
|
+
return buildLineEditResponse(file_path, startLine, startLine, newLines.length, fileLines, startLine - 1, action, modifications);
|
|
9414
|
+
} else {
|
|
9415
|
+
const replacedCount = endLine - startLine + 1;
|
|
9416
|
+
fileLines.splice(startLine - 1, replacedCount, ...newLines);
|
|
9417
|
+
await fs6.writeFile(resolvedPath, fileLines.join("\n"), "utf-8");
|
|
9418
|
+
if (fileTracker) await fileTracker.trackFileAfterWrite(resolvedPath);
|
|
9419
|
+
let action;
|
|
9420
|
+
if (newLines.length === 0) {
|
|
9421
|
+
action = `${replacedCount} line${replacedCount !== 1 ? "s" : ""} deleted (lines ${startLine}-${endLine})`;
|
|
9422
|
+
} else if (startLine === endLine) {
|
|
9423
|
+
action = `line ${startLine} replaced with ${newLines.length} line${newLines.length !== 1 ? "s" : ""}`;
|
|
9424
|
+
} else {
|
|
9425
|
+
action = `lines ${startLine}-${endLine} replaced with ${newLines.length} line${newLines.length !== 1 ? "s" : ""}`;
|
|
9426
|
+
}
|
|
9427
|
+
return buildLineEditResponse(file_path, startLine, endLine, newLines.length, fileLines, startLine - 1, action, modifications);
|
|
9428
|
+
}
|
|
9429
|
+
}
|
|
8865
9430
|
var editTool, createTool, editSchema, createSchema, editDescription, createDescription, editToolDefinition, createToolDefinition;
|
|
8866
9431
|
var init_edit = __esm({
|
|
8867
9432
|
"src/tools/edit.js"() {
|
|
8868
9433
|
"use strict";
|
|
8869
9434
|
init_path_validation();
|
|
9435
|
+
init_fuzzyMatch();
|
|
9436
|
+
init_symbolEdit();
|
|
9437
|
+
init_hashline();
|
|
9438
|
+
init_lineEditHeuristics();
|
|
8870
9439
|
editTool = (options = {}) => {
|
|
8871
9440
|
const { debug, allowedFolders, cwd, workspaceRoot } = parseFileToolOptions(options);
|
|
8872
9441
|
return tool({
|
|
8873
9442
|
name: "edit",
|
|
8874
|
-
description: `Edit files using
|
|
9443
|
+
description: `Edit files using text replacement, AST-aware symbol operations, or line-targeted editing.
|
|
8875
9444
|
|
|
8876
|
-
|
|
9445
|
+
Modes:
|
|
9446
|
+
1. Text edit: Provide old_string + new_string to find and replace text (with fuzzy matching fallback)
|
|
9447
|
+
2. Symbol replace: Provide symbol + new_string to replace an entire function/class/method by name
|
|
9448
|
+
3. Symbol insert: Provide symbol + new_string + position to insert code before/after a symbol
|
|
9449
|
+
4. Line-targeted edit: Provide start_line + new_string to edit by line number (from extract/search output)
|
|
8877
9450
|
|
|
8878
9451
|
Parameters:
|
|
8879
9452
|
- file_path: Path to the file to edit (absolute or relative)
|
|
8880
|
-
-
|
|
8881
|
-
-
|
|
8882
|
-
- replace_all: (optional) Replace all occurrences
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
-
|
|
8886
|
-
-
|
|
8887
|
-
- Use larger context around the string to ensure uniqueness when needed`,
|
|
9453
|
+
- new_string: Replacement text or new code content
|
|
9454
|
+
- old_string: (optional) Text to find and replace. If omitted, symbol or start_line must be provided.
|
|
9455
|
+
- replace_all: (optional) Replace all occurrences (text mode only)
|
|
9456
|
+
- symbol: (optional) Symbol name for AST-aware editing (e.g. "myFunction", "MyClass.myMethod")
|
|
9457
|
+
- position: (optional) "before" or "after" \u2014 insert code near a symbol or line instead of replacing it
|
|
9458
|
+
- start_line: (optional) Line reference (e.g. "42" or "42:ab") for line-targeted editing
|
|
9459
|
+
- end_line: (optional) End of line range, inclusive (e.g. "55" or "55:cd")`,
|
|
8888
9460
|
inputSchema: {
|
|
8889
9461
|
type: "object",
|
|
8890
9462
|
properties: {
|
|
@@ -8894,30 +9466,44 @@ Important:
|
|
|
8894
9466
|
},
|
|
8895
9467
|
old_string: {
|
|
8896
9468
|
type: "string",
|
|
8897
|
-
description: "
|
|
9469
|
+
description: "Text to find and replace (for text-based editing)"
|
|
8898
9470
|
},
|
|
8899
9471
|
new_string: {
|
|
8900
9472
|
type: "string",
|
|
8901
|
-
description: "
|
|
9473
|
+
description: "Replacement text or new code content"
|
|
8902
9474
|
},
|
|
8903
9475
|
replace_all: {
|
|
8904
9476
|
type: "boolean",
|
|
8905
|
-
description: "Replace all occurrences (default: false)",
|
|
9477
|
+
description: "Replace all occurrences (default: false, text mode only)",
|
|
8906
9478
|
default: false
|
|
9479
|
+
},
|
|
9480
|
+
symbol: {
|
|
9481
|
+
type: "string",
|
|
9482
|
+
description: 'Symbol name for AST-aware editing (e.g. "myFunction", "MyClass.myMethod")'
|
|
9483
|
+
},
|
|
9484
|
+
position: {
|
|
9485
|
+
type: "string",
|
|
9486
|
+
enum: ["before", "after"],
|
|
9487
|
+
description: "Insert before/after symbol or line (requires symbol or start_line, omit to replace)"
|
|
9488
|
+
},
|
|
9489
|
+
start_line: {
|
|
9490
|
+
type: "string",
|
|
9491
|
+
description: 'Line reference for line-targeted editing (e.g. "42" or "42:ab" with hash)'
|
|
9492
|
+
},
|
|
9493
|
+
end_line: {
|
|
9494
|
+
type: "string",
|
|
9495
|
+
description: 'End of line range, inclusive (e.g. "55" or "55:cd"). Defaults to start_line.'
|
|
8907
9496
|
}
|
|
8908
9497
|
},
|
|
8909
|
-
required: ["file_path", "
|
|
9498
|
+
required: ["file_path", "new_string"]
|
|
8910
9499
|
},
|
|
8911
|
-
execute: async ({ file_path, old_string, new_string, replace_all = false }) => {
|
|
9500
|
+
execute: async ({ file_path, old_string, new_string, replace_all = false, symbol, position, start_line, end_line }) => {
|
|
8912
9501
|
try {
|
|
8913
9502
|
if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
|
|
8914
|
-
return `Error editing file: Invalid file_path - must be a non-empty string
|
|
8915
|
-
}
|
|
8916
|
-
if (old_string === void 0 || old_string === null || typeof old_string !== "string") {
|
|
8917
|
-
return `Error editing file: Invalid old_string - must be a string`;
|
|
9503
|
+
return `Error editing file: Invalid file_path - must be a non-empty string. Provide an absolute path or a path relative to the working directory (e.g. "src/main.js").`;
|
|
8918
9504
|
}
|
|
8919
9505
|
if (new_string === void 0 || new_string === null || typeof new_string !== "string") {
|
|
8920
|
-
return `Error editing file: Invalid new_string - must be a string
|
|
9506
|
+
return `Error editing file: Invalid new_string - must be a string. Provide the replacement content as a string value (empty string "" is valid for deletions).`;
|
|
8921
9507
|
}
|
|
8922
9508
|
const resolvedPath = isAbsolute(file_path) ? file_path : resolve(cwd || process.cwd(), file_path);
|
|
8923
9509
|
if (debug) {
|
|
@@ -8925,34 +9511,64 @@ Important:
|
|
|
8925
9511
|
}
|
|
8926
9512
|
if (!isPathAllowed(resolvedPath, allowedFolders)) {
|
|
8927
9513
|
const relativePath = toRelativePath(resolvedPath, workspaceRoot);
|
|
8928
|
-
return `Error editing file: Permission denied - ${relativePath} is outside allowed directories
|
|
9514
|
+
return `Error editing file: Permission denied - ${relativePath} is outside allowed directories. Use a file path within the project workspace.`;
|
|
8929
9515
|
}
|
|
8930
9516
|
if (!existsSync(resolvedPath)) {
|
|
8931
|
-
return `Error editing file: File not found - ${file_path}
|
|
9517
|
+
return `Error editing file: File not found - ${file_path}. Verify the path is correct and the file exists. Use 'search' to find files by name, or 'create' to make a new file.`;
|
|
9518
|
+
}
|
|
9519
|
+
if (options.fileTracker && !options.fileTracker.isFileSeen(resolvedPath)) {
|
|
9520
|
+
const displayPath = toRelativePath(resolvedPath, workspaceRoot);
|
|
9521
|
+
return `Error editing ${displayPath}: This file has not been read yet in this session. Use 'extract' to read the file first, then retry your edit. This ensures you are working with the current file content.
|
|
9522
|
+
|
|
9523
|
+
Example: <extract><targets>${displayPath}</targets></extract>`;
|
|
9524
|
+
}
|
|
9525
|
+
if (symbol !== void 0 && symbol !== null) {
|
|
9526
|
+
return await handleSymbolEdit({ resolvedPath, file_path, symbol, new_string, position, debug, cwd, fileTracker: options.fileTracker });
|
|
9527
|
+
}
|
|
9528
|
+
if (start_line !== void 0 && start_line !== null) {
|
|
9529
|
+
return await handleLineEdit({ resolvedPath, file_path, start_line, end_line, new_string, position, debug, fileTracker: options.fileTracker });
|
|
9530
|
+
}
|
|
9531
|
+
if (old_string === void 0 || old_string === null) {
|
|
9532
|
+
return 'Error editing file: Must provide either old_string (for text edit), symbol (for AST-aware edit), or start_line (for line-targeted edit). For text editing: set old_string to the exact text to find and new_string to its replacement. For symbol editing: set symbol to a function/class/method name (e.g. "myFunction"). For line-targeted editing: set start_line to a line number from extract/search output (e.g. "42" or "42:ab").';
|
|
9533
|
+
}
|
|
9534
|
+
if (typeof old_string !== "string") {
|
|
9535
|
+
return `Error editing file: Invalid old_string - must be a string. Provide the exact text to find in the file, or use the symbol parameter instead for AST-aware editing by name.`;
|
|
8932
9536
|
}
|
|
8933
9537
|
const content = await fs6.readFile(resolvedPath, "utf-8");
|
|
9538
|
+
let matchTarget = old_string;
|
|
9539
|
+
let matchStrategy = "exact";
|
|
8934
9540
|
if (!content.includes(old_string)) {
|
|
8935
|
-
|
|
9541
|
+
const fuzzy = findFuzzyMatch(content, old_string);
|
|
9542
|
+
if (!fuzzy) {
|
|
9543
|
+
return `Error editing file: String not found - the specified old_string was not found in ${file_path}. The text may have changed or differ from what you expected. Try: (1) Use 'search' or 'extract' to read the current file content and copy the exact text. (2) Use the symbol parameter to edit by function/class name instead. (3) Verify the file_path is correct.`;
|
|
9544
|
+
}
|
|
9545
|
+
matchTarget = fuzzy.matchedText;
|
|
9546
|
+
matchStrategy = fuzzy.strategy;
|
|
9547
|
+
if (debug) {
|
|
9548
|
+
console.error(`[Edit] Exact match failed, used ${matchStrategy} matching`);
|
|
9549
|
+
}
|
|
8936
9550
|
}
|
|
8937
|
-
const occurrences = content.split(
|
|
9551
|
+
const occurrences = content.split(matchTarget).length - 1;
|
|
8938
9552
|
if (!replace_all && occurrences > 1) {
|
|
8939
|
-
return `Error editing file: Multiple occurrences found - the old_string appears ${occurrences} times.
|
|
9553
|
+
return `Error editing file: Multiple occurrences found - the old_string appears ${occurrences} times in ${file_path}. To fix: (1) Set replace_all=true to replace all occurrences, or (2) Include more surrounding lines in old_string to make the match unique (add the full line or adjacent lines for context).`;
|
|
8940
9554
|
}
|
|
8941
9555
|
let newContent;
|
|
8942
9556
|
if (replace_all) {
|
|
8943
|
-
newContent = content.replaceAll(
|
|
9557
|
+
newContent = content.replaceAll(matchTarget, new_string);
|
|
8944
9558
|
} else {
|
|
8945
|
-
newContent = content.replace(
|
|
9559
|
+
newContent = content.replace(matchTarget, new_string);
|
|
8946
9560
|
}
|
|
8947
9561
|
if (newContent === content) {
|
|
8948
|
-
return `Error editing file: No changes made - old_string and new_string
|
|
9562
|
+
return `Error editing file: No changes made - the replacement result is identical to the original. Verify that old_string and new_string are actually different. If fuzzy matching was used, the matched text may already equal new_string.`;
|
|
8949
9563
|
}
|
|
8950
9564
|
await fs6.writeFile(resolvedPath, newContent, "utf-8");
|
|
9565
|
+
if (options.fileTracker) await options.fileTracker.trackFileAfterWrite(resolvedPath);
|
|
8951
9566
|
const replacedCount = replace_all ? occurrences : 1;
|
|
8952
9567
|
if (debug) {
|
|
8953
9568
|
console.error(`[Edit] Successfully edited ${resolvedPath}, replaced ${replacedCount} occurrence(s)`);
|
|
8954
9569
|
}
|
|
8955
|
-
|
|
9570
|
+
const strategyNote = matchStrategy !== "exact" ? `, matched via ${matchStrategy}` : "";
|
|
9571
|
+
return `Successfully edited ${file_path} (${replacedCount} replacement${replacedCount !== 1 ? "s" : ""}${strategyNote})`;
|
|
8956
9572
|
} catch (error) {
|
|
8957
9573
|
console.error("[Edit] Error:", error);
|
|
8958
9574
|
return `Error editing file: ${error.message}`;
|
|
@@ -8999,10 +9615,10 @@ Important:
|
|
|
8999
9615
|
execute: async ({ file_path, content, overwrite = false }) => {
|
|
9000
9616
|
try {
|
|
9001
9617
|
if (!file_path || typeof file_path !== "string" || file_path.trim() === "") {
|
|
9002
|
-
return `Error creating file: Invalid file_path - must be a non-empty string
|
|
9618
|
+
return `Error creating file: Invalid file_path - must be a non-empty string. Provide an absolute path or a path relative to the working directory (e.g. "src/newFile.js").`;
|
|
9003
9619
|
}
|
|
9004
9620
|
if (content === void 0 || content === null || typeof content !== "string") {
|
|
9005
|
-
return `Error creating file: Invalid content - must be a string
|
|
9621
|
+
return `Error creating file: Invalid content - must be a string. Provide the file content as a string value (empty string "" is valid for an empty file).`;
|
|
9006
9622
|
}
|
|
9007
9623
|
const resolvedPath = isAbsolute(file_path) ? file_path : resolve(cwd || process.cwd(), file_path);
|
|
9008
9624
|
if (debug) {
|
|
@@ -9010,15 +9626,17 @@ Important:
|
|
|
9010
9626
|
}
|
|
9011
9627
|
if (!isPathAllowed(resolvedPath, allowedFolders)) {
|
|
9012
9628
|
const relativePath = toRelativePath(resolvedPath, workspaceRoot);
|
|
9013
|
-
return `Error creating file: Permission denied - ${relativePath} is outside allowed directories
|
|
9629
|
+
return `Error creating file: Permission denied - ${relativePath} is outside allowed directories. Use a file path within the project workspace.`;
|
|
9014
9630
|
}
|
|
9015
9631
|
if (existsSync(resolvedPath) && !overwrite) {
|
|
9016
9632
|
return `Error creating file: File already exists - ${file_path}. Use overwrite: true to replace it.`;
|
|
9017
9633
|
}
|
|
9634
|
+
const existed = existsSync(resolvedPath);
|
|
9018
9635
|
const dir = dirname(resolvedPath);
|
|
9019
9636
|
await fs6.mkdir(dir, { recursive: true });
|
|
9020
9637
|
await fs6.writeFile(resolvedPath, content, "utf-8");
|
|
9021
|
-
|
|
9638
|
+
if (options.fileTracker) await options.fileTracker.trackFileAfterWrite(resolvedPath);
|
|
9639
|
+
const action = existed && overwrite ? "overwrote" : "created";
|
|
9022
9640
|
const bytes = Buffer.byteLength(content, "utf-8");
|
|
9023
9641
|
if (debug) {
|
|
9024
9642
|
console.error(`[Create] Successfully ${action} ${resolvedPath}`);
|
|
@@ -9040,18 +9658,35 @@ Important:
|
|
|
9040
9658
|
},
|
|
9041
9659
|
old_string: {
|
|
9042
9660
|
type: "string",
|
|
9043
|
-
description: "
|
|
9661
|
+
description: "Text to find and replace (for text-based editing)"
|
|
9044
9662
|
},
|
|
9045
9663
|
new_string: {
|
|
9046
9664
|
type: "string",
|
|
9047
|
-
description: "
|
|
9665
|
+
description: "Replacement text or new code content"
|
|
9048
9666
|
},
|
|
9049
9667
|
replace_all: {
|
|
9050
9668
|
type: "boolean",
|
|
9051
|
-
description: "Replace all occurrences (default: false)"
|
|
9669
|
+
description: "Replace all occurrences (default: false, text mode only)"
|
|
9670
|
+
},
|
|
9671
|
+
symbol: {
|
|
9672
|
+
type: "string",
|
|
9673
|
+
description: 'Symbol name for AST-aware editing (e.g. "myFunction", "MyClass.myMethod")'
|
|
9674
|
+
},
|
|
9675
|
+
position: {
|
|
9676
|
+
type: "string",
|
|
9677
|
+
enum: ["before", "after"],
|
|
9678
|
+
description: "Insert before/after symbol or line (requires symbol or start_line, omit to replace)"
|
|
9679
|
+
},
|
|
9680
|
+
start_line: {
|
|
9681
|
+
type: "string",
|
|
9682
|
+
description: 'Line reference for line-targeted editing (e.g. "42" or "42:ab" with hash)'
|
|
9683
|
+
},
|
|
9684
|
+
end_line: {
|
|
9685
|
+
type: "string",
|
|
9686
|
+
description: 'End of line range, inclusive (e.g. "55" or "55:cd"). Defaults to start_line.'
|
|
9052
9687
|
}
|
|
9053
9688
|
},
|
|
9054
|
-
required: ["file_path", "
|
|
9689
|
+
required: ["file_path", "new_string"]
|
|
9055
9690
|
};
|
|
9056
9691
|
createSchema = {
|
|
9057
9692
|
type: "object",
|
|
@@ -9071,50 +9706,123 @@ Important:
|
|
|
9071
9706
|
},
|
|
9072
9707
|
required: ["file_path", "content"]
|
|
9073
9708
|
};
|
|
9074
|
-
editDescription = "Edit files using
|
|
9709
|
+
editDescription = "Edit files using text replacement, AST-aware symbol operations, or line-targeted editing. Supports fuzzy matching for text edits and optional hash-based integrity verification for line edits.";
|
|
9075
9710
|
createDescription = "Create new files with specified content. Will create parent directories if needed.";
|
|
9076
9711
|
editToolDefinition = `
|
|
9077
9712
|
## edit
|
|
9078
9713
|
Description: ${editDescription}
|
|
9079
9714
|
|
|
9080
|
-
|
|
9081
|
-
- For precise, surgical edits to existing files
|
|
9082
|
-
- When you need to change specific lines or blocks of code
|
|
9083
|
-
- For renaming functions, variables, or updating configuration values
|
|
9084
|
-
- When the exact text to replace is known and unique (or use replace_all for multiple occurrences)
|
|
9715
|
+
Four editing modes \u2014 choose based on the scope of your change:
|
|
9085
9716
|
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9717
|
+
1. **Text edit** (old_string + new_string): For small, precise changes \u2014 fix a condition, rename a variable, update a value. Provide old_string copied verbatim from the file and new_string with the replacement. Fuzzy matching handles minor whitespace/indentation differences automatically, but always try to copy the exact text.
|
|
9718
|
+
|
|
9719
|
+
2. **Symbol replace** (symbol + new_string): For replacing an entire function, class, or method by name. No need to quote the old code \u2014 just provide the symbol name and the full new implementation. Indentation is automatically adjusted to match the original. Prefer this mode when rewriting whole definitions.
|
|
9720
|
+
|
|
9721
|
+
3. **Symbol insert** (symbol + new_string + position): For adding new code before or after an existing symbol. Set position to "before" or "after".
|
|
9722
|
+
|
|
9723
|
+
4. **Line-targeted edit** (start_line + new_string): For precise edits using line numbers from extract/search output. Use start_line with a line number (e.g. "42") or line:hash (e.g. "42:ab") for integrity verification. Add end_line for multi-line ranges. Use position="before" or "after" to insert instead of replace.
|
|
9090
9724
|
|
|
9091
9725
|
Parameters:
|
|
9092
9726
|
- file_path: (required) Path to the file to edit
|
|
9093
|
-
-
|
|
9094
|
-
-
|
|
9095
|
-
- replace_all: (optional, default: false) Replace all occurrences
|
|
9096
|
-
|
|
9097
|
-
|
|
9098
|
-
-
|
|
9099
|
-
-
|
|
9100
|
-
|
|
9727
|
+
- new_string: (required) Replacement text or new code content
|
|
9728
|
+
- old_string: (optional) Text to find and replace \u2014 copy verbatim from the file, do not paraphrase or reformat
|
|
9729
|
+
- replace_all: (optional, default: false) Replace all occurrences of old_string (text mode only)
|
|
9730
|
+
- symbol: (optional) Name of a code symbol (e.g. "myFunction", "MyClass.myMethod") \u2014 must match a function, class, or method definition
|
|
9731
|
+
- position: (optional) "before" or "after" \u2014 insert new_string near the symbol or line instead of replacing it
|
|
9732
|
+
- start_line: (optional) Line reference for line-targeted editing (e.g. "42" or "42:ab")
|
|
9733
|
+
- end_line: (optional) End of line range, inclusive (e.g. "55" or "55:cd"). Defaults to start_line.
|
|
9734
|
+
|
|
9735
|
+
Mode selection rules (priority order):
|
|
9736
|
+
- If symbol is provided, symbol mode is used (old_string and start_line are ignored)
|
|
9737
|
+
- If start_line is provided (without symbol), line-targeted mode is used
|
|
9738
|
+
- If old_string is provided (without symbol or start_line), text mode is used
|
|
9739
|
+
- If none are provided, the tool returns an error with guidance
|
|
9740
|
+
|
|
9741
|
+
When to use each mode:
|
|
9742
|
+
- Small edits (a line or a few lines): use text mode with old_string
|
|
9743
|
+
- Replacing entire functions/classes/methods: use symbol mode \u2014 no exact text matching needed
|
|
9744
|
+
- Editing specific lines from extract/search output: use line-targeted mode with start_line
|
|
9745
|
+
- Editing inside large functions without rewriting them entirely: first use extract with the symbol target (e.g. "file.js#myFunction") to see the function with line numbers, then use start_line/end_line to edit specific lines within it
|
|
9746
|
+
|
|
9747
|
+
Error handling:
|
|
9748
|
+
- If an edit fails, read the error message carefully \u2014 it contains specific instructions for how to fix the call and retry
|
|
9749
|
+
- Common fixes: use 'search'/'extract' to get exact file content, add more context to old_string, switch between text and symbol modes
|
|
9750
|
+
- Line-targeted hash mismatch: the file changed since last read; the error provides updated line:hash references
|
|
9101
9751
|
|
|
9102
9752
|
Examples:
|
|
9753
|
+
|
|
9754
|
+
Text edit (find and replace):
|
|
9103
9755
|
<edit>
|
|
9104
9756
|
<file_path>src/main.js</file_path>
|
|
9105
|
-
<old_string>
|
|
9106
|
-
|
|
9107
|
-
}</old_string>
|
|
9108
|
-
<new_string>function newName() {
|
|
9109
|
-
return 42;
|
|
9110
|
-
}</new_string>
|
|
9757
|
+
<old_string>return false;</old_string>
|
|
9758
|
+
<new_string>return true;</new_string>
|
|
9111
9759
|
</edit>
|
|
9112
9760
|
|
|
9761
|
+
Text edit with replace_all:
|
|
9113
9762
|
<edit>
|
|
9114
9763
|
<file_path>config.json</file_path>
|
|
9115
9764
|
<old_string>"debug": false</old_string>
|
|
9116
9765
|
<new_string>"debug": true</new_string>
|
|
9117
9766
|
<replace_all>true</replace_all>
|
|
9767
|
+
</edit>
|
|
9768
|
+
|
|
9769
|
+
Symbol replace (rewrite entire function by name):
|
|
9770
|
+
<edit>
|
|
9771
|
+
<file_path>src/utils.js</file_path>
|
|
9772
|
+
<symbol>calculateTotal</symbol>
|
|
9773
|
+
<new_string>function calculateTotal(items) {
|
|
9774
|
+
return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
|
9775
|
+
}</new_string>
|
|
9776
|
+
</edit>
|
|
9777
|
+
|
|
9778
|
+
Symbol insert (add new function after existing one):
|
|
9779
|
+
<edit>
|
|
9780
|
+
<file_path>src/utils.js</file_path>
|
|
9781
|
+
<symbol>calculateTotal</symbol>
|
|
9782
|
+
<position>after</position>
|
|
9783
|
+
<new_string>function calculateTax(total, rate) {
|
|
9784
|
+
return total * rate;
|
|
9785
|
+
}</new_string>
|
|
9786
|
+
</edit>
|
|
9787
|
+
|
|
9788
|
+
Line-targeted edit (replace a line):
|
|
9789
|
+
<edit>
|
|
9790
|
+
<file_path>src/main.js</file_path>
|
|
9791
|
+
<start_line>42</start_line>
|
|
9792
|
+
<new_string> return processItems(order.items);</new_string>
|
|
9793
|
+
</edit>
|
|
9794
|
+
|
|
9795
|
+
Line-targeted edit (replace a range of lines):
|
|
9796
|
+
<edit>
|
|
9797
|
+
<file_path>src/main.js</file_path>
|
|
9798
|
+
<start_line>42</start_line>
|
|
9799
|
+
<end_line>55</end_line>
|
|
9800
|
+
<new_string> // simplified implementation
|
|
9801
|
+
return processItems(order.items);</new_string>
|
|
9802
|
+
</edit>
|
|
9803
|
+
|
|
9804
|
+
Line-targeted edit with hash verification:
|
|
9805
|
+
<edit>
|
|
9806
|
+
<file_path>src/main.js</file_path>
|
|
9807
|
+
<start_line>42:ab</start_line>
|
|
9808
|
+
<end_line>55:cd</end_line>
|
|
9809
|
+
<new_string> return processItems(order.items);</new_string>
|
|
9810
|
+
</edit>
|
|
9811
|
+
|
|
9812
|
+
Line-targeted insert (add code after a line):
|
|
9813
|
+
<edit>
|
|
9814
|
+
<file_path>src/main.js</file_path>
|
|
9815
|
+
<start_line>42</start_line>
|
|
9816
|
+
<position>after</position>
|
|
9817
|
+
<new_string> const validated = validate(input);</new_string>
|
|
9818
|
+
</edit>
|
|
9819
|
+
|
|
9820
|
+
Line-targeted delete (remove lines):
|
|
9821
|
+
<edit>
|
|
9822
|
+
<file_path>src/main.js</file_path>
|
|
9823
|
+
<start_line>42</start_line>
|
|
9824
|
+
<end_line>45</end_line>
|
|
9825
|
+
<new_string></new_string>
|
|
9118
9826
|
</edit>`;
|
|
9119
9827
|
createToolDefinition = `
|
|
9120
9828
|
## create
|
|
@@ -9652,7 +10360,7 @@ function getValidParamsForTool(toolName) {
|
|
|
9652
10360
|
};
|
|
9653
10361
|
const schema = schemaMap[toolName];
|
|
9654
10362
|
if (!schema) {
|
|
9655
|
-
return ["path", "directory", "pattern", "recursive", "includeHidden", "task", "files", "
|
|
10363
|
+
return ["path", "directory", "pattern", "recursive", "includeHidden", "task", "files", "result"];
|
|
9656
10364
|
}
|
|
9657
10365
|
if (toolName === "attempt_completion") {
|
|
9658
10366
|
return ["result"];
|
|
@@ -9665,6 +10373,10 @@ function getValidParamsForTool(toolName) {
|
|
|
9665
10373
|
}
|
|
9666
10374
|
return [];
|
|
9667
10375
|
}
|
|
10376
|
+
function unescapeXmlEntities(str) {
|
|
10377
|
+
if (typeof str !== "string") return str;
|
|
10378
|
+
return str.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
10379
|
+
}
|
|
9668
10380
|
function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
9669
10381
|
let earliestToolName = null;
|
|
9670
10382
|
let earliestOpenIndex = Infinity;
|
|
@@ -9721,10 +10433,10 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
9721
10433
|
}
|
|
9722
10434
|
paramCloseIndex = nextTagIndex;
|
|
9723
10435
|
}
|
|
9724
|
-
let paramValue = innerContent.substring(
|
|
10436
|
+
let paramValue = unescapeXmlEntities(innerContent.substring(
|
|
9725
10437
|
paramOpenIndex + paramOpenTag.length,
|
|
9726
10438
|
paramCloseIndex
|
|
9727
|
-
).trim();
|
|
10439
|
+
).trim());
|
|
9728
10440
|
if (paramValue.toLowerCase() === "true") {
|
|
9729
10441
|
paramValue = true;
|
|
9730
10442
|
} else if (paramValue.toLowerCase() === "false") {
|
|
@@ -9738,7 +10450,7 @@ function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
|
|
|
9738
10450
|
params[paramName] = paramValue;
|
|
9739
10451
|
}
|
|
9740
10452
|
if (toolName === "attempt_completion") {
|
|
9741
|
-
params["result"] = innerContent.trim();
|
|
10453
|
+
params["result"] = unescapeXmlEntities(innerContent.trim());
|
|
9742
10454
|
if (params.command) {
|
|
9743
10455
|
delete params.command;
|
|
9744
10456
|
}
|
|
@@ -9773,7 +10485,6 @@ function detectUnrecognizedToolCall(xmlString, validTools) {
|
|
|
9773
10485
|
"listSkills",
|
|
9774
10486
|
"useSkill",
|
|
9775
10487
|
"readImage",
|
|
9776
|
-
"implement",
|
|
9777
10488
|
"edit",
|
|
9778
10489
|
"create",
|
|
9779
10490
|
"delegate",
|
|
@@ -10147,6 +10858,8 @@ User: Read file inside the dependency
|
|
|
10147
10858
|
</extract>
|
|
10148
10859
|
|
|
10149
10860
|
</examples>
|
|
10861
|
+
|
|
10862
|
+
**Edit Integration:** The line numbers shown in extract output (e.g. "42 | code") can be used directly with the edit tool's start_line/end_line parameters for precise line-targeted editing. To edit inside a large function: extract it by symbol name first (e.g. "file.js#myFunction"), then use the line numbers from the output to make surgical edits with start_line/end_line.
|
|
10150
10863
|
`;
|
|
10151
10864
|
delegateToolDefinition = `
|
|
10152
10865
|
## delegate
|
|
@@ -10302,7 +11015,7 @@ Capabilities:
|
|
|
10302
11015
|
`;
|
|
10303
11016
|
searchDescription = "Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions.";
|
|
10304
11017
|
queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
|
|
10305
|
-
extractDescription = "Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files.";
|
|
11018
|
+
extractDescription = "Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files. Line numbers from output can be used with edit start_line/end_line for precise editing.";
|
|
10306
11019
|
delegateDescription = "Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.";
|
|
10307
11020
|
analyzeAllDescription = 'Answer questions that require analyzing ALL matching data in the codebase. Use for aggregate questions like "What features exist?", "List all API endpoints", "Count TODO comments". The AI automatically plans the search strategy, processes all results via map-reduce, and synthesizes a comprehensive answer. WARNING: Slower than search - only use when you need complete coverage.';
|
|
10308
11021
|
DEFAULT_VALID_TOOLS = [
|
|
@@ -10317,7 +11030,6 @@ Capabilities:
|
|
|
10317
11030
|
"useSkill",
|
|
10318
11031
|
"listFiles",
|
|
10319
11032
|
"searchFiles",
|
|
10320
|
-
"implement",
|
|
10321
11033
|
"bash",
|
|
10322
11034
|
"task",
|
|
10323
11035
|
"attempt_completion"
|
|
@@ -10442,6 +11154,7 @@ var init_vercel = __esm({
|
|
|
10442
11154
|
init_analyzeAll();
|
|
10443
11155
|
init_common();
|
|
10444
11156
|
init_error_types();
|
|
11157
|
+
init_hashline();
|
|
10445
11158
|
CODE_SEARCH_SCHEMA = {
|
|
10446
11159
|
type: "object",
|
|
10447
11160
|
properties: {
|
|
@@ -10460,8 +11173,15 @@ var init_vercel = __esm({
|
|
|
10460
11173
|
maxTokens = 2e4,
|
|
10461
11174
|
debug = false,
|
|
10462
11175
|
outline = false,
|
|
10463
|
-
searchDelegate = false
|
|
11176
|
+
searchDelegate = false,
|
|
11177
|
+
hashLines = false
|
|
10464
11178
|
} = options;
|
|
11179
|
+
const maybeAnnotate = (result) => {
|
|
11180
|
+
if (hashLines && typeof result === "string") {
|
|
11181
|
+
return annotateOutputWithHashes(result);
|
|
11182
|
+
}
|
|
11183
|
+
return result;
|
|
11184
|
+
};
|
|
10465
11185
|
return tool2({
|
|
10466
11186
|
name: "search",
|
|
10467
11187
|
description: searchDelegate ? `${searchDescription} (delegates code search to a subagent and returns extracted code blocks)` : searchDescription,
|
|
@@ -10503,7 +11223,12 @@ var init_vercel = __esm({
|
|
|
10503
11223
|
};
|
|
10504
11224
|
if (!searchDelegate) {
|
|
10505
11225
|
try {
|
|
10506
|
-
|
|
11226
|
+
const result = maybeAnnotate(await runRawSearch());
|
|
11227
|
+
if (options.fileTracker && typeof result === "string") {
|
|
11228
|
+
options.fileTracker.trackFilesFromOutput(result, options.cwd || ".").catch(() => {
|
|
11229
|
+
});
|
|
11230
|
+
}
|
|
11231
|
+
return result;
|
|
10507
11232
|
} catch (error) {
|
|
10508
11233
|
console.error("Error executing search command:", error);
|
|
10509
11234
|
return formatErrorForAI(error);
|
|
@@ -10546,7 +11271,12 @@ var init_vercel = __esm({
|
|
|
10546
11271
|
if (debug) {
|
|
10547
11272
|
console.error("Delegated search returned no targets; falling back to raw search");
|
|
10548
11273
|
}
|
|
10549
|
-
|
|
11274
|
+
const fallbackResult = maybeAnnotate(await runRawSearch());
|
|
11275
|
+
if (options.fileTracker && typeof fallbackResult === "string") {
|
|
11276
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult, options.cwd || ".").catch(() => {
|
|
11277
|
+
});
|
|
11278
|
+
}
|
|
11279
|
+
return fallbackResult;
|
|
10550
11280
|
}
|
|
10551
11281
|
const resolutionBase = searchPaths[0] || options.cwd || ".";
|
|
10552
11282
|
const resolvedTargets = targets.map((target) => resolveTargetPath(target, resolutionBase));
|
|
@@ -10561,13 +11291,18 @@ var init_vercel = __esm({
|
|
|
10561
11291
|
const extractResult = await extract(extractOptions);
|
|
10562
11292
|
if (resolutionBase && typeof extractResult === "string") {
|
|
10563
11293
|
const wsPrefix = resolutionBase.endsWith("/") ? resolutionBase : resolutionBase + "/";
|
|
10564
|
-
return extractResult.split(wsPrefix).join("");
|
|
11294
|
+
return maybeAnnotate(extractResult.split(wsPrefix).join(""));
|
|
10565
11295
|
}
|
|
10566
|
-
return extractResult;
|
|
11296
|
+
return maybeAnnotate(extractResult);
|
|
10567
11297
|
} catch (error) {
|
|
10568
11298
|
console.error("Delegated search failed, falling back to raw search:", error);
|
|
10569
11299
|
try {
|
|
10570
|
-
|
|
11300
|
+
const fallbackResult2 = maybeAnnotate(await runRawSearch());
|
|
11301
|
+
if (options.fileTracker && typeof fallbackResult2 === "string") {
|
|
11302
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult2, options.cwd || ".").catch(() => {
|
|
11303
|
+
});
|
|
11304
|
+
}
|
|
11305
|
+
return fallbackResult2;
|
|
10571
11306
|
} catch (fallbackError) {
|
|
10572
11307
|
console.error("Error executing search command:", fallbackError);
|
|
10573
11308
|
return formatErrorForAI(fallbackError);
|
|
@@ -10613,7 +11348,7 @@ var init_vercel = __esm({
|
|
|
10613
11348
|
});
|
|
10614
11349
|
};
|
|
10615
11350
|
extractTool = (options = {}) => {
|
|
10616
|
-
const { debug = false, outline = false } = options;
|
|
11351
|
+
const { debug = false, outline = false, hashLines = false } = options;
|
|
10617
11352
|
return tool2({
|
|
10618
11353
|
name: "extract",
|
|
10619
11354
|
description: extractDescription,
|
|
@@ -10630,6 +11365,7 @@ var init_vercel = __esm({
|
|
|
10630
11365
|
}
|
|
10631
11366
|
let tempFilePath = null;
|
|
10632
11367
|
let extractOptions = { cwd: effectiveCwd };
|
|
11368
|
+
let extractFiles = null;
|
|
10633
11369
|
if (input_content) {
|
|
10634
11370
|
const { writeFileSync: writeFileSync2, unlinkSync } = await import("fs");
|
|
10635
11371
|
const { join: join5 } = await import("path");
|
|
@@ -10653,13 +11389,13 @@ var init_vercel = __esm({
|
|
|
10653
11389
|
};
|
|
10654
11390
|
} else if (targets) {
|
|
10655
11391
|
const parsedTargets = parseTargets(targets);
|
|
10656
|
-
|
|
11392
|
+
extractFiles = parsedTargets.map((target) => resolveTargetPath(target, effectiveCwd));
|
|
10657
11393
|
let effectiveFormat = format;
|
|
10658
11394
|
if (outline && format === "outline-xml") {
|
|
10659
11395
|
effectiveFormat = "xml";
|
|
10660
11396
|
}
|
|
10661
11397
|
extractOptions = {
|
|
10662
|
-
files,
|
|
11398
|
+
files: extractFiles,
|
|
10663
11399
|
cwd: effectiveCwd,
|
|
10664
11400
|
allowTests: allow_tests ?? true,
|
|
10665
11401
|
contextLines: context_lines,
|
|
@@ -10669,6 +11405,10 @@ var init_vercel = __esm({
|
|
|
10669
11405
|
throw new Error("Either targets or input_content must be provided");
|
|
10670
11406
|
}
|
|
10671
11407
|
const results = await extract(extractOptions);
|
|
11408
|
+
if (options.fileTracker && extractFiles && extractFiles.length > 0) {
|
|
11409
|
+
options.fileTracker.trackFilesFromExtract(extractFiles, effectiveCwd).catch(() => {
|
|
11410
|
+
});
|
|
11411
|
+
}
|
|
10672
11412
|
if (tempFilePath) {
|
|
10673
11413
|
const { unlinkSync } = await import("fs");
|
|
10674
11414
|
try {
|
|
@@ -10680,6 +11420,9 @@ var init_vercel = __esm({
|
|
|
10680
11420
|
console.error(`Warning: Failed to remove temporary file: ${cleanupError.message}`);
|
|
10681
11421
|
}
|
|
10682
11422
|
}
|
|
11423
|
+
if (hashLines && typeof results === "string") {
|
|
11424
|
+
return annotateOutputWithHashes(results);
|
|
11425
|
+
}
|
|
10683
11426
|
return results;
|
|
10684
11427
|
} catch (error) {
|
|
10685
11428
|
console.error("Error executing extract command:", error);
|
|
@@ -10879,6 +11622,7 @@ var init_bashDefaults = __esm({
|
|
|
10879
11622
|
"tree:*",
|
|
10880
11623
|
// Git read-only operations
|
|
10881
11624
|
"git:status",
|
|
11625
|
+
"git:status:*",
|
|
10882
11626
|
"git:log",
|
|
10883
11627
|
"git:log:*",
|
|
10884
11628
|
"git:diff",
|
|
@@ -10897,14 +11641,109 @@ var init_bashDefaults = __esm({
|
|
|
10897
11641
|
"git:blame",
|
|
10898
11642
|
"git:blame:*",
|
|
10899
11643
|
"git:shortlog",
|
|
11644
|
+
"git:shortlog:*",
|
|
10900
11645
|
"git:reflog",
|
|
11646
|
+
"git:reflog:*",
|
|
10901
11647
|
"git:ls-files",
|
|
11648
|
+
"git:ls-files:*",
|
|
10902
11649
|
"git:ls-tree",
|
|
11650
|
+
"git:ls-tree:*",
|
|
11651
|
+
"git:ls-remote",
|
|
11652
|
+
"git:ls-remote:*",
|
|
10903
11653
|
"git:rev-parse",
|
|
11654
|
+
"git:rev-parse:*",
|
|
10904
11655
|
"git:rev-list",
|
|
11656
|
+
"git:rev-list:*",
|
|
11657
|
+
"git:cat-file",
|
|
11658
|
+
"git:cat-file:*",
|
|
11659
|
+
"git:diff-tree",
|
|
11660
|
+
"git:diff-tree:*",
|
|
11661
|
+
"git:diff-files",
|
|
11662
|
+
"git:diff-files:*",
|
|
11663
|
+
"git:diff-index",
|
|
11664
|
+
"git:diff-index:*",
|
|
11665
|
+
"git:for-each-ref",
|
|
11666
|
+
"git:for-each-ref:*",
|
|
11667
|
+
"git:merge-base",
|
|
11668
|
+
"git:merge-base:*",
|
|
11669
|
+
"git:name-rev",
|
|
11670
|
+
"git:name-rev:*",
|
|
11671
|
+
"git:count-objects",
|
|
11672
|
+
"git:count-objects:*",
|
|
11673
|
+
"git:verify-commit",
|
|
11674
|
+
"git:verify-commit:*",
|
|
11675
|
+
"git:verify-tag",
|
|
11676
|
+
"git:verify-tag:*",
|
|
11677
|
+
"git:check-ignore",
|
|
11678
|
+
"git:check-ignore:*",
|
|
11679
|
+
"git:check-attr",
|
|
11680
|
+
"git:check-attr:*",
|
|
11681
|
+
"git:stash:list",
|
|
11682
|
+
"git:stash:show",
|
|
11683
|
+
"git:stash:show:*",
|
|
11684
|
+
"git:worktree:list",
|
|
11685
|
+
"git:worktree:list:*",
|
|
11686
|
+
"git:notes:list",
|
|
11687
|
+
"git:notes:show",
|
|
11688
|
+
"git:notes:show:*",
|
|
10905
11689
|
"git:--version",
|
|
10906
11690
|
"git:help",
|
|
10907
11691
|
"git:help:*",
|
|
11692
|
+
// GitHub CLI (gh) read-only operations
|
|
11693
|
+
"gh:--version",
|
|
11694
|
+
"gh:help",
|
|
11695
|
+
"gh:help:*",
|
|
11696
|
+
"gh:status",
|
|
11697
|
+
"gh:auth:status",
|
|
11698
|
+
"gh:auth:status:*",
|
|
11699
|
+
"gh:issue:list",
|
|
11700
|
+
"gh:issue:list:*",
|
|
11701
|
+
"gh:issue:view",
|
|
11702
|
+
"gh:issue:view:*",
|
|
11703
|
+
"gh:issue:status",
|
|
11704
|
+
"gh:issue:status:*",
|
|
11705
|
+
"gh:pr:list",
|
|
11706
|
+
"gh:pr:list:*",
|
|
11707
|
+
"gh:pr:view",
|
|
11708
|
+
"gh:pr:view:*",
|
|
11709
|
+
"gh:pr:status",
|
|
11710
|
+
"gh:pr:status:*",
|
|
11711
|
+
"gh:pr:diff",
|
|
11712
|
+
"gh:pr:diff:*",
|
|
11713
|
+
"gh:pr:checks",
|
|
11714
|
+
"gh:pr:checks:*",
|
|
11715
|
+
"gh:repo:list",
|
|
11716
|
+
"gh:repo:list:*",
|
|
11717
|
+
"gh:repo:view",
|
|
11718
|
+
"gh:repo:view:*",
|
|
11719
|
+
"gh:release:list",
|
|
11720
|
+
"gh:release:list:*",
|
|
11721
|
+
"gh:release:view",
|
|
11722
|
+
"gh:release:view:*",
|
|
11723
|
+
"gh:run:list",
|
|
11724
|
+
"gh:run:list:*",
|
|
11725
|
+
"gh:run:view",
|
|
11726
|
+
"gh:run:view:*",
|
|
11727
|
+
"gh:workflow:list",
|
|
11728
|
+
"gh:workflow:list:*",
|
|
11729
|
+
"gh:workflow:view",
|
|
11730
|
+
"gh:workflow:view:*",
|
|
11731
|
+
"gh:gist:list",
|
|
11732
|
+
"gh:gist:list:*",
|
|
11733
|
+
"gh:gist:view",
|
|
11734
|
+
"gh:gist:view:*",
|
|
11735
|
+
"gh:search:issues",
|
|
11736
|
+
"gh:search:issues:*",
|
|
11737
|
+
"gh:search:prs",
|
|
11738
|
+
"gh:search:prs:*",
|
|
11739
|
+
"gh:search:repos",
|
|
11740
|
+
"gh:search:repos:*",
|
|
11741
|
+
"gh:search:code",
|
|
11742
|
+
"gh:search:code:*",
|
|
11743
|
+
"gh:search:commits",
|
|
11744
|
+
"gh:search:commits:*",
|
|
11745
|
+
"gh:api",
|
|
11746
|
+
"gh:api:*",
|
|
10908
11747
|
// Package managers (information only)
|
|
10909
11748
|
"npm:list",
|
|
10910
11749
|
"npm:ls",
|
|
@@ -11215,14 +12054,132 @@ var init_bashDefaults = __esm({
|
|
|
11215
12054
|
"git:push",
|
|
11216
12055
|
"git:push:*",
|
|
11217
12056
|
"git:force",
|
|
11218
|
-
"git:reset
|
|
11219
|
-
"git:
|
|
12057
|
+
"git:reset",
|
|
12058
|
+
"git:reset:*",
|
|
12059
|
+
"git:clean",
|
|
12060
|
+
"git:clean:*",
|
|
12061
|
+
"git:rm",
|
|
11220
12062
|
"git:rm:*",
|
|
11221
12063
|
"git:commit",
|
|
12064
|
+
"git:commit:*",
|
|
11222
12065
|
"git:merge",
|
|
12066
|
+
"git:merge:*",
|
|
11223
12067
|
"git:rebase",
|
|
12068
|
+
"git:rebase:*",
|
|
11224
12069
|
"git:cherry-pick",
|
|
12070
|
+
"git:cherry-pick:*",
|
|
11225
12071
|
"git:stash:drop",
|
|
12072
|
+
"git:stash:drop:*",
|
|
12073
|
+
"git:stash:pop",
|
|
12074
|
+
"git:stash:pop:*",
|
|
12075
|
+
"git:stash:push",
|
|
12076
|
+
"git:stash:push:*",
|
|
12077
|
+
"git:stash:clear",
|
|
12078
|
+
"git:branch:-d",
|
|
12079
|
+
"git:branch:-d:*",
|
|
12080
|
+
"git:branch:-D",
|
|
12081
|
+
"git:branch:-D:*",
|
|
12082
|
+
"git:branch:--delete",
|
|
12083
|
+
"git:branch:--delete:*",
|
|
12084
|
+
"git:tag:-d",
|
|
12085
|
+
"git:tag:-d:*",
|
|
12086
|
+
"git:tag:--delete",
|
|
12087
|
+
"git:tag:--delete:*",
|
|
12088
|
+
"git:remote:remove",
|
|
12089
|
+
"git:remote:remove:*",
|
|
12090
|
+
"git:remote:rm",
|
|
12091
|
+
"git:remote:rm:*",
|
|
12092
|
+
"git:checkout:--force",
|
|
12093
|
+
"git:checkout:--force:*",
|
|
12094
|
+
"git:checkout:-f",
|
|
12095
|
+
"git:checkout:-f:*",
|
|
12096
|
+
"git:submodule:deinit",
|
|
12097
|
+
"git:submodule:deinit:*",
|
|
12098
|
+
"git:notes:add",
|
|
12099
|
+
"git:notes:add:*",
|
|
12100
|
+
"git:notes:remove",
|
|
12101
|
+
"git:notes:remove:*",
|
|
12102
|
+
"git:worktree:add",
|
|
12103
|
+
"git:worktree:add:*",
|
|
12104
|
+
"git:worktree:remove",
|
|
12105
|
+
"git:worktree:remove:*",
|
|
12106
|
+
// Dangerous GitHub CLI (gh) write operations
|
|
12107
|
+
"gh:issue:create",
|
|
12108
|
+
"gh:issue:create:*",
|
|
12109
|
+
"gh:issue:close",
|
|
12110
|
+
"gh:issue:close:*",
|
|
12111
|
+
"gh:issue:delete",
|
|
12112
|
+
"gh:issue:delete:*",
|
|
12113
|
+
"gh:issue:edit",
|
|
12114
|
+
"gh:issue:edit:*",
|
|
12115
|
+
"gh:issue:reopen",
|
|
12116
|
+
"gh:issue:reopen:*",
|
|
12117
|
+
"gh:issue:comment",
|
|
12118
|
+
"gh:issue:comment:*",
|
|
12119
|
+
"gh:pr:create",
|
|
12120
|
+
"gh:pr:create:*",
|
|
12121
|
+
"gh:pr:close",
|
|
12122
|
+
"gh:pr:close:*",
|
|
12123
|
+
"gh:pr:merge",
|
|
12124
|
+
"gh:pr:merge:*",
|
|
12125
|
+
"gh:pr:edit",
|
|
12126
|
+
"gh:pr:edit:*",
|
|
12127
|
+
"gh:pr:reopen",
|
|
12128
|
+
"gh:pr:reopen:*",
|
|
12129
|
+
"gh:pr:review",
|
|
12130
|
+
"gh:pr:review:*",
|
|
12131
|
+
"gh:pr:comment",
|
|
12132
|
+
"gh:pr:comment:*",
|
|
12133
|
+
"gh:repo:create",
|
|
12134
|
+
"gh:repo:create:*",
|
|
12135
|
+
"gh:repo:delete",
|
|
12136
|
+
"gh:repo:delete:*",
|
|
12137
|
+
"gh:repo:fork",
|
|
12138
|
+
"gh:repo:fork:*",
|
|
12139
|
+
"gh:repo:rename",
|
|
12140
|
+
"gh:repo:rename:*",
|
|
12141
|
+
"gh:repo:archive",
|
|
12142
|
+
"gh:repo:archive:*",
|
|
12143
|
+
"gh:repo:clone",
|
|
12144
|
+
"gh:repo:clone:*",
|
|
12145
|
+
"gh:release:create",
|
|
12146
|
+
"gh:release:create:*",
|
|
12147
|
+
"gh:release:delete",
|
|
12148
|
+
"gh:release:delete:*",
|
|
12149
|
+
"gh:release:edit",
|
|
12150
|
+
"gh:release:edit:*",
|
|
12151
|
+
"gh:run:cancel",
|
|
12152
|
+
"gh:run:cancel:*",
|
|
12153
|
+
"gh:run:rerun",
|
|
12154
|
+
"gh:run:rerun:*",
|
|
12155
|
+
"gh:workflow:run",
|
|
12156
|
+
"gh:workflow:run:*",
|
|
12157
|
+
"gh:workflow:enable",
|
|
12158
|
+
"gh:workflow:enable:*",
|
|
12159
|
+
"gh:workflow:disable",
|
|
12160
|
+
"gh:workflow:disable:*",
|
|
12161
|
+
"gh:gist:create",
|
|
12162
|
+
"gh:gist:create:*",
|
|
12163
|
+
"gh:gist:delete",
|
|
12164
|
+
"gh:gist:delete:*",
|
|
12165
|
+
"gh:gist:edit",
|
|
12166
|
+
"gh:gist:edit:*",
|
|
12167
|
+
"gh:secret:set",
|
|
12168
|
+
"gh:secret:set:*",
|
|
12169
|
+
"gh:secret:delete",
|
|
12170
|
+
"gh:secret:delete:*",
|
|
12171
|
+
"gh:variable:set",
|
|
12172
|
+
"gh:variable:set:*",
|
|
12173
|
+
"gh:variable:delete",
|
|
12174
|
+
"gh:variable:delete:*",
|
|
12175
|
+
"gh:label:create",
|
|
12176
|
+
"gh:label:create:*",
|
|
12177
|
+
"gh:label:delete",
|
|
12178
|
+
"gh:label:delete:*",
|
|
12179
|
+
"gh:ssh-key:add",
|
|
12180
|
+
"gh:ssh-key:add:*",
|
|
12181
|
+
"gh:ssh-key:delete",
|
|
12182
|
+
"gh:ssh-key:delete:*",
|
|
11226
12183
|
// File system mounting and partitioning
|
|
11227
12184
|
"mount",
|
|
11228
12185
|
"mount:*",
|
|
@@ -12030,7 +12987,7 @@ async function executeBashCommand(command, options = {}) {
|
|
|
12030
12987
|
console.log(`[BashExecutor] Working directory: "${cwd}"`);
|
|
12031
12988
|
console.log(`[BashExecutor] Timeout: ${timeout}ms`);
|
|
12032
12989
|
}
|
|
12033
|
-
return new Promise((
|
|
12990
|
+
return new Promise((resolve9, reject2) => {
|
|
12034
12991
|
const processEnv = {
|
|
12035
12992
|
...process.env,
|
|
12036
12993
|
...env
|
|
@@ -12047,7 +13004,7 @@ async function executeBashCommand(command, options = {}) {
|
|
|
12047
13004
|
} else {
|
|
12048
13005
|
const args = parseCommandForExecution(command);
|
|
12049
13006
|
if (!args || args.length === 0) {
|
|
12050
|
-
|
|
13007
|
+
resolve9({
|
|
12051
13008
|
success: false,
|
|
12052
13009
|
error: "Failed to parse command",
|
|
12053
13010
|
stdout: "",
|
|
@@ -12132,7 +13089,7 @@ async function executeBashCommand(command, options = {}) {
|
|
|
12132
13089
|
success = false;
|
|
12133
13090
|
error = `Command exited with code ${code}`;
|
|
12134
13091
|
}
|
|
12135
|
-
|
|
13092
|
+
resolve9({
|
|
12136
13093
|
success,
|
|
12137
13094
|
error,
|
|
12138
13095
|
stdout: stdout.trim(),
|
|
@@ -12152,7 +13109,7 @@ async function executeBashCommand(command, options = {}) {
|
|
|
12152
13109
|
if (debug) {
|
|
12153
13110
|
console.log(`[BashExecutor] Spawn error:`, error);
|
|
12154
13111
|
}
|
|
12155
|
-
|
|
13112
|
+
resolve9({
|
|
12156
13113
|
success: false,
|
|
12157
13114
|
error: `Failed to execute command: ${error.message}`,
|
|
12158
13115
|
stdout: "",
|
|
@@ -13560,14 +14517,14 @@ var require_executor = __commonJS({
|
|
|
13560
14517
|
function asyncDone(callback) {
|
|
13561
14518
|
let isInstant = false;
|
|
13562
14519
|
let instant;
|
|
13563
|
-
const p = new Promise((
|
|
14520
|
+
const p = new Promise((resolve9, reject2) => {
|
|
13564
14521
|
callback((err, result) => {
|
|
13565
14522
|
if (err)
|
|
13566
14523
|
reject2(err);
|
|
13567
14524
|
else {
|
|
13568
14525
|
isInstant = true;
|
|
13569
14526
|
instant = result;
|
|
13570
|
-
|
|
14527
|
+
resolve9({ result });
|
|
13571
14528
|
}
|
|
13572
14529
|
});
|
|
13573
14530
|
});
|
|
@@ -13590,10 +14547,10 @@ var require_executor = __commonJS({
|
|
|
13590
14547
|
}
|
|
13591
14548
|
async function execAsync4(ticks, tree, scope, context, doneOriginal, inLoopOrSwitch) {
|
|
13592
14549
|
let done = doneOriginal;
|
|
13593
|
-
const p = new Promise((
|
|
14550
|
+
const p = new Promise((resolve9) => {
|
|
13594
14551
|
done = (e, r) => {
|
|
13595
14552
|
doneOriginal(e, r);
|
|
13596
|
-
|
|
14553
|
+
resolve9();
|
|
13597
14554
|
};
|
|
13598
14555
|
});
|
|
13599
14556
|
if (!_execNoneRecurse(ticks, tree, scope, context, done, true, inLoopOrSwitch) && utils.isLisp(tree)) {
|
|
@@ -22357,33 +23314,31 @@ var init_runtime = __esm({
|
|
|
22357
23314
|
}
|
|
22358
23315
|
});
|
|
22359
23316
|
|
|
22360
|
-
// node_modules/balanced-match/index.js
|
|
22361
|
-
var
|
|
22362
|
-
|
|
22363
|
-
|
|
22364
|
-
|
|
22365
|
-
|
|
22366
|
-
|
|
22367
|
-
|
|
22368
|
-
var r = range(a, b, str);
|
|
23317
|
+
// node_modules/balanced-match/dist/esm/index.js
|
|
23318
|
+
var balanced, maybeMatch, range;
|
|
23319
|
+
var init_esm = __esm({
|
|
23320
|
+
"node_modules/balanced-match/dist/esm/index.js"() {
|
|
23321
|
+
balanced = (a, b, str) => {
|
|
23322
|
+
const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
|
|
23323
|
+
const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
|
|
23324
|
+
const r = ma !== null && mb != null && range(ma, mb, str);
|
|
22369
23325
|
return r && {
|
|
22370
23326
|
start: r[0],
|
|
22371
23327
|
end: r[1],
|
|
22372
23328
|
pre: str.slice(0, r[0]),
|
|
22373
|
-
body: str.slice(r[0] +
|
|
22374
|
-
post: str.slice(r[1] +
|
|
23329
|
+
body: str.slice(r[0] + ma.length, r[1]),
|
|
23330
|
+
post: str.slice(r[1] + mb.length)
|
|
22375
23331
|
};
|
|
22376
|
-
}
|
|
22377
|
-
|
|
22378
|
-
|
|
23332
|
+
};
|
|
23333
|
+
maybeMatch = (reg, str) => {
|
|
23334
|
+
const m = str.match(reg);
|
|
22379
23335
|
return m ? m[0] : null;
|
|
22380
|
-
}
|
|
22381
|
-
|
|
22382
|
-
|
|
22383
|
-
|
|
22384
|
-
|
|
22385
|
-
|
|
22386
|
-
var i = ai;
|
|
23336
|
+
};
|
|
23337
|
+
range = (a, b, str) => {
|
|
23338
|
+
let begs, beg, left, right = void 0, result;
|
|
23339
|
+
let ai = str.indexOf(a);
|
|
23340
|
+
let bi = str.indexOf(b, ai + 1);
|
|
23341
|
+
let i = ai;
|
|
22387
23342
|
if (ai >= 0 && bi > 0) {
|
|
22388
23343
|
if (a === b) {
|
|
22389
23344
|
return [ai, bi];
|
|
@@ -22391,14 +23346,16 @@ var require_balanced_match = __commonJS({
|
|
|
22391
23346
|
begs = [];
|
|
22392
23347
|
left = str.length;
|
|
22393
23348
|
while (i >= 0 && !result) {
|
|
22394
|
-
if (i
|
|
23349
|
+
if (i === ai) {
|
|
22395
23350
|
begs.push(i);
|
|
22396
23351
|
ai = str.indexOf(a, i + 1);
|
|
22397
|
-
} else if (begs.length
|
|
22398
|
-
|
|
23352
|
+
} else if (begs.length === 1) {
|
|
23353
|
+
const r = begs.pop();
|
|
23354
|
+
if (r !== void 0)
|
|
23355
|
+
result = [r, bi];
|
|
22399
23356
|
} else {
|
|
22400
23357
|
beg = begs.pop();
|
|
22401
|
-
if (beg < left) {
|
|
23358
|
+
if (beg !== void 0 && beg < left) {
|
|
22402
23359
|
left = beg;
|
|
22403
23360
|
right = bi;
|
|
22404
23361
|
}
|
|
@@ -22406,163 +23363,179 @@ var require_balanced_match = __commonJS({
|
|
|
22406
23363
|
}
|
|
22407
23364
|
i = ai < bi && ai >= 0 ? ai : bi;
|
|
22408
23365
|
}
|
|
22409
|
-
if (begs.length) {
|
|
23366
|
+
if (begs.length && right !== void 0) {
|
|
22410
23367
|
result = [left, right];
|
|
22411
23368
|
}
|
|
22412
23369
|
}
|
|
22413
23370
|
return result;
|
|
22414
|
-
}
|
|
23371
|
+
};
|
|
22415
23372
|
}
|
|
22416
23373
|
});
|
|
22417
23374
|
|
|
22418
|
-
// node_modules/brace-expansion/index.js
|
|
22419
|
-
|
|
22420
|
-
|
|
22421
|
-
|
|
22422
|
-
|
|
22423
|
-
|
|
22424
|
-
|
|
22425
|
-
|
|
22426
|
-
|
|
22427
|
-
|
|
22428
|
-
|
|
22429
|
-
|
|
22430
|
-
|
|
22431
|
-
|
|
22432
|
-
|
|
22433
|
-
|
|
22434
|
-
|
|
22435
|
-
|
|
22436
|
-
|
|
22437
|
-
|
|
22438
|
-
|
|
22439
|
-
|
|
22440
|
-
|
|
22441
|
-
|
|
22442
|
-
|
|
22443
|
-
|
|
22444
|
-
|
|
22445
|
-
|
|
22446
|
-
|
|
22447
|
-
|
|
22448
|
-
|
|
22449
|
-
|
|
22450
|
-
|
|
22451
|
-
|
|
22452
|
-
|
|
22453
|
-
|
|
22454
|
-
|
|
22455
|
-
|
|
22456
|
-
|
|
22457
|
-
|
|
22458
|
-
|
|
22459
|
-
|
|
22460
|
-
|
|
22461
|
-
|
|
22462
|
-
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
|
|
22469
|
-
|
|
22470
|
-
|
|
22471
|
-
|
|
22472
|
-
|
|
22473
|
-
|
|
22474
|
-
|
|
22475
|
-
|
|
23375
|
+
// node_modules/brace-expansion/dist/esm/index.js
|
|
23376
|
+
function numeric(str) {
|
|
23377
|
+
return !isNaN(str) ? parseInt(str, 10) : str.charCodeAt(0);
|
|
23378
|
+
}
|
|
23379
|
+
function escapeBraces(str) {
|
|
23380
|
+
return str.replace(slashPattern, escSlash).replace(openPattern, escOpen).replace(closePattern, escClose).replace(commaPattern, escComma).replace(periodPattern, escPeriod);
|
|
23381
|
+
}
|
|
23382
|
+
function unescapeBraces(str) {
|
|
23383
|
+
return str.replace(escSlashPattern, "\\").replace(escOpenPattern, "{").replace(escClosePattern, "}").replace(escCommaPattern, ",").replace(escPeriodPattern, ".");
|
|
23384
|
+
}
|
|
23385
|
+
function parseCommaParts(str) {
|
|
23386
|
+
if (!str) {
|
|
23387
|
+
return [""];
|
|
23388
|
+
}
|
|
23389
|
+
const parts = [];
|
|
23390
|
+
const m = balanced("{", "}", str);
|
|
23391
|
+
if (!m) {
|
|
23392
|
+
return str.split(",");
|
|
23393
|
+
}
|
|
23394
|
+
const { pre, body, post } = m;
|
|
23395
|
+
const p = pre.split(",");
|
|
23396
|
+
p[p.length - 1] += "{" + body + "}";
|
|
23397
|
+
const postParts = parseCommaParts(post);
|
|
23398
|
+
if (post.length) {
|
|
23399
|
+
;
|
|
23400
|
+
p[p.length - 1] += postParts.shift();
|
|
23401
|
+
p.push.apply(p, postParts);
|
|
23402
|
+
}
|
|
23403
|
+
parts.push.apply(parts, p);
|
|
23404
|
+
return parts;
|
|
23405
|
+
}
|
|
23406
|
+
function expand(str, options = {}) {
|
|
23407
|
+
if (!str) {
|
|
23408
|
+
return [];
|
|
23409
|
+
}
|
|
23410
|
+
const { max = EXPANSION_MAX } = options;
|
|
23411
|
+
if (str.slice(0, 2) === "{}") {
|
|
23412
|
+
str = "\\{\\}" + str.slice(2);
|
|
23413
|
+
}
|
|
23414
|
+
return expand_(escapeBraces(str), max, true).map(unescapeBraces);
|
|
23415
|
+
}
|
|
23416
|
+
function embrace(str) {
|
|
23417
|
+
return "{" + str + "}";
|
|
23418
|
+
}
|
|
23419
|
+
function isPadded(el) {
|
|
23420
|
+
return /^-?0\d/.test(el);
|
|
23421
|
+
}
|
|
23422
|
+
function lte(i, y) {
|
|
23423
|
+
return i <= y;
|
|
23424
|
+
}
|
|
23425
|
+
function gte(i, y) {
|
|
23426
|
+
return i >= y;
|
|
23427
|
+
}
|
|
23428
|
+
function expand_(str, max, isTop) {
|
|
23429
|
+
const expansions = [];
|
|
23430
|
+
const m = balanced("{", "}", str);
|
|
23431
|
+
if (!m)
|
|
23432
|
+
return [str];
|
|
23433
|
+
const pre = m.pre;
|
|
23434
|
+
const post = m.post.length ? expand_(m.post, max, false) : [""];
|
|
23435
|
+
if (/\$$/.test(m.pre)) {
|
|
23436
|
+
for (let k = 0; k < post.length && k < max; k++) {
|
|
23437
|
+
const expansion = pre + "{" + m.body + "}" + post[k];
|
|
23438
|
+
expansions.push(expansion);
|
|
22476
23439
|
}
|
|
22477
|
-
|
|
22478
|
-
|
|
22479
|
-
|
|
22480
|
-
|
|
22481
|
-
|
|
22482
|
-
|
|
22483
|
-
if (
|
|
22484
|
-
|
|
22485
|
-
|
|
22486
|
-
|
|
22487
|
-
|
|
22488
|
-
|
|
22489
|
-
|
|
22490
|
-
|
|
22491
|
-
|
|
22492
|
-
|
|
22493
|
-
|
|
22494
|
-
|
|
22495
|
-
|
|
22496
|
-
|
|
22497
|
-
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
|
|
22501
|
-
|
|
22502
|
-
|
|
22503
|
-
|
|
22504
|
-
|
|
22505
|
-
|
|
22506
|
-
|
|
22507
|
-
|
|
22508
|
-
|
|
22509
|
-
|
|
22510
|
-
|
|
22511
|
-
|
|
23440
|
+
} else {
|
|
23441
|
+
const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
|
|
23442
|
+
const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
|
|
23443
|
+
const isSequence = isNumericSequence || isAlphaSequence;
|
|
23444
|
+
const isOptions = m.body.indexOf(",") >= 0;
|
|
23445
|
+
if (!isSequence && !isOptions) {
|
|
23446
|
+
if (m.post.match(/,(?!,).*\}/)) {
|
|
23447
|
+
str = m.pre + "{" + m.body + escClose + m.post;
|
|
23448
|
+
return expand_(str, max, true);
|
|
23449
|
+
}
|
|
23450
|
+
return [str];
|
|
23451
|
+
}
|
|
23452
|
+
let n;
|
|
23453
|
+
if (isSequence) {
|
|
23454
|
+
n = m.body.split(/\.\./);
|
|
23455
|
+
} else {
|
|
23456
|
+
n = parseCommaParts(m.body);
|
|
23457
|
+
if (n.length === 1 && n[0] !== void 0) {
|
|
23458
|
+
n = expand_(n[0], max, false).map(embrace);
|
|
23459
|
+
if (n.length === 1) {
|
|
23460
|
+
return post.map((p) => m.pre + n[0] + p);
|
|
23461
|
+
}
|
|
23462
|
+
}
|
|
23463
|
+
}
|
|
23464
|
+
let N;
|
|
23465
|
+
if (isSequence && n[0] !== void 0 && n[1] !== void 0) {
|
|
23466
|
+
const x = numeric(n[0]);
|
|
23467
|
+
const y = numeric(n[1]);
|
|
23468
|
+
const width = Math.max(n[0].length, n[1].length);
|
|
23469
|
+
let incr = n.length === 3 && n[2] !== void 0 ? Math.abs(numeric(n[2])) : 1;
|
|
23470
|
+
let test = lte;
|
|
23471
|
+
const reverse = y < x;
|
|
23472
|
+
if (reverse) {
|
|
23473
|
+
incr *= -1;
|
|
23474
|
+
test = gte;
|
|
23475
|
+
}
|
|
23476
|
+
const pad = n.some(isPadded);
|
|
23477
|
+
N = [];
|
|
23478
|
+
for (let i = x; test(i, y); i += incr) {
|
|
23479
|
+
let c;
|
|
23480
|
+
if (isAlphaSequence) {
|
|
23481
|
+
c = String.fromCharCode(i);
|
|
23482
|
+
if (c === "\\") {
|
|
23483
|
+
c = "";
|
|
22512
23484
|
}
|
|
22513
|
-
}
|
|
22514
|
-
|
|
22515
|
-
|
|
22516
|
-
|
|
22517
|
-
|
|
22518
|
-
|
|
22519
|
-
|
|
22520
|
-
|
|
22521
|
-
|
|
22522
|
-
|
|
22523
|
-
incr *= -1;
|
|
22524
|
-
test = gte;
|
|
22525
|
-
}
|
|
22526
|
-
var pad = n.some(isPadded);
|
|
22527
|
-
N = [];
|
|
22528
|
-
for (var i = x; test(i, y); i += incr) {
|
|
22529
|
-
var c;
|
|
22530
|
-
if (isAlphaSequence) {
|
|
22531
|
-
c = String.fromCharCode(i);
|
|
22532
|
-
if (c === "\\")
|
|
22533
|
-
c = "";
|
|
22534
|
-
} else {
|
|
22535
|
-
c = String(i);
|
|
22536
|
-
if (pad) {
|
|
22537
|
-
var need = width - c.length;
|
|
22538
|
-
if (need > 0) {
|
|
22539
|
-
var z = new Array(need + 1).join("0");
|
|
22540
|
-
if (i < 0)
|
|
22541
|
-
c = "-" + z + c.slice(1);
|
|
22542
|
-
else
|
|
22543
|
-
c = z + c;
|
|
22544
|
-
}
|
|
23485
|
+
} else {
|
|
23486
|
+
c = String(i);
|
|
23487
|
+
if (pad) {
|
|
23488
|
+
const need = width - c.length;
|
|
23489
|
+
if (need > 0) {
|
|
23490
|
+
const z = new Array(need + 1).join("0");
|
|
23491
|
+
if (i < 0) {
|
|
23492
|
+
c = "-" + z + c.slice(1);
|
|
23493
|
+
} else {
|
|
23494
|
+
c = z + c;
|
|
22545
23495
|
}
|
|
22546
23496
|
}
|
|
22547
|
-
N.push(c);
|
|
22548
|
-
}
|
|
22549
|
-
} else {
|
|
22550
|
-
N = [];
|
|
22551
|
-
for (var j = 0; j < n.length; j++) {
|
|
22552
|
-
N.push.apply(N, expand2(n[j], false));
|
|
22553
23497
|
}
|
|
22554
23498
|
}
|
|
22555
|
-
|
|
22556
|
-
|
|
22557
|
-
|
|
22558
|
-
|
|
22559
|
-
|
|
22560
|
-
|
|
23499
|
+
N.push(c);
|
|
23500
|
+
}
|
|
23501
|
+
} else {
|
|
23502
|
+
N = [];
|
|
23503
|
+
for (let j = 0; j < n.length; j++) {
|
|
23504
|
+
N.push.apply(N, expand_(n[j], max, false));
|
|
23505
|
+
}
|
|
23506
|
+
}
|
|
23507
|
+
for (let j = 0; j < N.length; j++) {
|
|
23508
|
+
for (let k = 0; k < post.length && expansions.length < max; k++) {
|
|
23509
|
+
const expansion = pre + N[j] + post[k];
|
|
23510
|
+
if (!isTop || isSequence || expansion) {
|
|
23511
|
+
expansions.push(expansion);
|
|
22561
23512
|
}
|
|
22562
23513
|
}
|
|
22563
|
-
return expansions;
|
|
22564
23514
|
}
|
|
22565
23515
|
}
|
|
23516
|
+
return expansions;
|
|
23517
|
+
}
|
|
23518
|
+
var escSlash, escOpen, escClose, escComma, escPeriod, escSlashPattern, escOpenPattern, escClosePattern, escCommaPattern, escPeriodPattern, slashPattern, openPattern, closePattern, commaPattern, periodPattern, EXPANSION_MAX;
|
|
23519
|
+
var init_esm2 = __esm({
|
|
23520
|
+
"node_modules/brace-expansion/dist/esm/index.js"() {
|
|
23521
|
+
init_esm();
|
|
23522
|
+
escSlash = "\0SLASH" + Math.random() + "\0";
|
|
23523
|
+
escOpen = "\0OPEN" + Math.random() + "\0";
|
|
23524
|
+
escClose = "\0CLOSE" + Math.random() + "\0";
|
|
23525
|
+
escComma = "\0COMMA" + Math.random() + "\0";
|
|
23526
|
+
escPeriod = "\0PERIOD" + Math.random() + "\0";
|
|
23527
|
+
escSlashPattern = new RegExp(escSlash, "g");
|
|
23528
|
+
escOpenPattern = new RegExp(escOpen, "g");
|
|
23529
|
+
escClosePattern = new RegExp(escClose, "g");
|
|
23530
|
+
escCommaPattern = new RegExp(escComma, "g");
|
|
23531
|
+
escPeriodPattern = new RegExp(escPeriod, "g");
|
|
23532
|
+
slashPattern = /\\\\/g;
|
|
23533
|
+
openPattern = /\\{/g;
|
|
23534
|
+
closePattern = /\\}/g;
|
|
23535
|
+
commaPattern = /\\,/g;
|
|
23536
|
+
periodPattern = /\\./g;
|
|
23537
|
+
EXPANSION_MAX = 1e5;
|
|
23538
|
+
}
|
|
22566
23539
|
});
|
|
22567
23540
|
|
|
22568
23541
|
// node_modules/minimatch/dist/esm/assert-valid-pattern.js
|
|
@@ -23145,11 +24118,13 @@ var init_ast = __esm({
|
|
|
23145
24118
|
let escaping = false;
|
|
23146
24119
|
let re = "";
|
|
23147
24120
|
let uflag = false;
|
|
24121
|
+
let inStar = false;
|
|
23148
24122
|
for (let i = 0; i < glob2.length; i++) {
|
|
23149
24123
|
const c = glob2.charAt(i);
|
|
23150
24124
|
if (escaping) {
|
|
23151
24125
|
escaping = false;
|
|
23152
24126
|
re += (reSpecials.has(c) ? "\\" : "") + c;
|
|
24127
|
+
inStar = false;
|
|
23153
24128
|
continue;
|
|
23154
24129
|
}
|
|
23155
24130
|
if (c === "\\") {
|
|
@@ -23167,16 +24142,19 @@ var init_ast = __esm({
|
|
|
23167
24142
|
uflag = uflag || needUflag;
|
|
23168
24143
|
i += consumed - 1;
|
|
23169
24144
|
hasMagic2 = hasMagic2 || magic;
|
|
24145
|
+
inStar = false;
|
|
23170
24146
|
continue;
|
|
23171
24147
|
}
|
|
23172
24148
|
}
|
|
23173
24149
|
if (c === "*") {
|
|
23174
|
-
if (
|
|
23175
|
-
|
|
23176
|
-
|
|
23177
|
-
|
|
24150
|
+
if (inStar)
|
|
24151
|
+
continue;
|
|
24152
|
+
inStar = true;
|
|
24153
|
+
re += noEmpty && /^[*]+$/.test(glob2) ? starNoEmpty : star;
|
|
23178
24154
|
hasMagic2 = true;
|
|
23179
24155
|
continue;
|
|
24156
|
+
} else {
|
|
24157
|
+
inStar = false;
|
|
23180
24158
|
}
|
|
23181
24159
|
if (c === "?") {
|
|
23182
24160
|
re += qmark;
|
|
@@ -23202,10 +24180,10 @@ var init_escape = __esm({
|
|
|
23202
24180
|
});
|
|
23203
24181
|
|
|
23204
24182
|
// node_modules/minimatch/dist/esm/index.js
|
|
23205
|
-
var
|
|
23206
|
-
var
|
|
24183
|
+
var minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path5, sep3, GLOBSTAR, qmark2, star2, twoStarDot, twoStarNoDot, filter, ext, defaults, braceExpand, makeRe, match, globMagic, regExpEscape2, Minimatch;
|
|
24184
|
+
var init_esm3 = __esm({
|
|
23207
24185
|
"node_modules/minimatch/dist/esm/index.js"() {
|
|
23208
|
-
|
|
24186
|
+
init_esm2();
|
|
23209
24187
|
init_assert_valid_pattern();
|
|
23210
24188
|
init_ast();
|
|
23211
24189
|
init_escape();
|
|
@@ -23328,7 +24306,7 @@ var init_esm = __esm({
|
|
|
23328
24306
|
if (options.nobrace || !/\{(?:(?!\{).)*\}/.test(pattern)) {
|
|
23329
24307
|
return [pattern];
|
|
23330
24308
|
}
|
|
23331
|
-
return (
|
|
24309
|
+
return expand(pattern);
|
|
23332
24310
|
};
|
|
23333
24311
|
minimatch.braceExpand = braceExpand;
|
|
23334
24312
|
makeRe = (pattern, options = {}) => new Minimatch(pattern, options).makeRe();
|
|
@@ -23934,7 +24912,7 @@ var init_esm = __esm({
|
|
|
23934
24912
|
|
|
23935
24913
|
// node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js
|
|
23936
24914
|
var perf, warned, PROCESS, emitWarning, AC, AS, shouldWarn, TYPE, isPosInt, getUintArray, ZeroArray, Stack, LRUCache;
|
|
23937
|
-
var
|
|
24915
|
+
var init_esm4 = __esm({
|
|
23938
24916
|
"node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js"() {
|
|
23939
24917
|
perf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
|
|
23940
24918
|
warned = /* @__PURE__ */ new Set();
|
|
@@ -25311,7 +26289,7 @@ import { EventEmitter } from "node:events";
|
|
|
25311
26289
|
import Stream from "node:stream";
|
|
25312
26290
|
import { StringDecoder } from "node:string_decoder";
|
|
25313
26291
|
var proc, isStream, isReadable, isWritable, EOF, MAYBE_EMIT_END, EMITTED_END, EMITTING_END, EMITTED_ERROR, CLOSED, READ, FLUSH, FLUSHCHUNK, ENCODING, DECODER, FLOWING, PAUSED, RESUME, BUFFER, PIPES, BUFFERLENGTH, BUFFERPUSH, BUFFERSHIFT, OBJECTMODE, DESTROYED, ERROR, EMITDATA, EMITEND, EMITEND2, ASYNC, ABORT, ABORTED, SIGNAL, DATALISTENERS, DISCARDED, defer, nodefer, isEndish, isArrayBufferLike, isArrayBufferView, Pipe, PipeProxyErrors, isObjectModeOptions, isEncodingOptions, Minipass;
|
|
25314
|
-
var
|
|
26292
|
+
var init_esm5 = __esm({
|
|
25315
26293
|
"node_modules/minipass/dist/esm/index.js"() {
|
|
25316
26294
|
proc = typeof process === "object" && process ? process : {
|
|
25317
26295
|
stdout: null,
|
|
@@ -26038,10 +27016,10 @@ var init_esm3 = __esm({
|
|
|
26038
27016
|
* Return a void Promise that resolves once the stream ends.
|
|
26039
27017
|
*/
|
|
26040
27018
|
async promise() {
|
|
26041
|
-
return new Promise((
|
|
27019
|
+
return new Promise((resolve9, reject2) => {
|
|
26042
27020
|
this.on(DESTROYED, () => reject2(new Error("stream destroyed")));
|
|
26043
27021
|
this.on("error", (er) => reject2(er));
|
|
26044
|
-
this.on("end", () =>
|
|
27022
|
+
this.on("end", () => resolve9());
|
|
26045
27023
|
});
|
|
26046
27024
|
}
|
|
26047
27025
|
/**
|
|
@@ -26065,7 +27043,7 @@ var init_esm3 = __esm({
|
|
|
26065
27043
|
return Promise.resolve({ done: false, value: res });
|
|
26066
27044
|
if (this[EOF])
|
|
26067
27045
|
return stop();
|
|
26068
|
-
let
|
|
27046
|
+
let resolve9;
|
|
26069
27047
|
let reject2;
|
|
26070
27048
|
const onerr = (er) => {
|
|
26071
27049
|
this.off("data", ondata);
|
|
@@ -26079,19 +27057,19 @@ var init_esm3 = __esm({
|
|
|
26079
27057
|
this.off("end", onend);
|
|
26080
27058
|
this.off(DESTROYED, ondestroy);
|
|
26081
27059
|
this.pause();
|
|
26082
|
-
|
|
27060
|
+
resolve9({ value, done: !!this[EOF] });
|
|
26083
27061
|
};
|
|
26084
27062
|
const onend = () => {
|
|
26085
27063
|
this.off("error", onerr);
|
|
26086
27064
|
this.off("data", ondata);
|
|
26087
27065
|
this.off(DESTROYED, ondestroy);
|
|
26088
27066
|
stop();
|
|
26089
|
-
|
|
27067
|
+
resolve9({ done: true, value: void 0 });
|
|
26090
27068
|
};
|
|
26091
27069
|
const ondestroy = () => onerr(new Error("stream destroyed"));
|
|
26092
27070
|
return new Promise((res2, rej) => {
|
|
26093
27071
|
reject2 = rej;
|
|
26094
|
-
|
|
27072
|
+
resolve9 = res2;
|
|
26095
27073
|
this.once(DESTROYED, ondestroy);
|
|
26096
27074
|
this.once("error", onerr);
|
|
26097
27075
|
this.once("end", onend);
|
|
@@ -26200,10 +27178,10 @@ import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync, realpathSyn
|
|
|
26200
27178
|
import * as actualFS from "node:fs";
|
|
26201
27179
|
import { lstat, readdir, readlink, realpath } from "node:fs/promises";
|
|
26202
27180
|
var realpathSync2, defaultFS, fsFromOption, uncDriveRegexp, uncToDrive, eitherSep, UNKNOWN, IFIFO, IFCHR, IFDIR, IFBLK, IFREG, IFLNK, IFSOCK, IFMT, IFMT_UNKNOWN, READDIR_CALLED, LSTAT_CALLED, ENOTDIR, ENOENT, ENOREADLINK, ENOREALPATH, ENOCHILD, TYPEMASK, entToType, normalizeCache, normalize, normalizeNocaseCache, normalizeNocase, ResolveCache, ChildrenCache, setAsCwd, PathBase, PathWin32, PathPosix, PathScurryBase, PathScurryWin32, PathScurryPosix, PathScurryDarwin, Path, PathScurry;
|
|
26203
|
-
var
|
|
27181
|
+
var init_esm6 = __esm({
|
|
26204
27182
|
"node_modules/path-scurry/dist/esm/index.js"() {
|
|
26205
|
-
|
|
26206
|
-
|
|
27183
|
+
init_esm4();
|
|
27184
|
+
init_esm5();
|
|
26207
27185
|
realpathSync2 = rps.native;
|
|
26208
27186
|
defaultFS = {
|
|
26209
27187
|
lstatSync,
|
|
@@ -27075,9 +28053,9 @@ var init_esm4 = __esm({
|
|
|
27075
28053
|
if (this.#asyncReaddirInFlight) {
|
|
27076
28054
|
await this.#asyncReaddirInFlight;
|
|
27077
28055
|
} else {
|
|
27078
|
-
let
|
|
28056
|
+
let resolve9 = () => {
|
|
27079
28057
|
};
|
|
27080
|
-
this.#asyncReaddirInFlight = new Promise((res) =>
|
|
28058
|
+
this.#asyncReaddirInFlight = new Promise((res) => resolve9 = res);
|
|
27081
28059
|
try {
|
|
27082
28060
|
for (const e of await this.#fs.promises.readdir(fullpath, {
|
|
27083
28061
|
withFileTypes: true
|
|
@@ -27090,7 +28068,7 @@ var init_esm4 = __esm({
|
|
|
27090
28068
|
children.provisional = 0;
|
|
27091
28069
|
}
|
|
27092
28070
|
this.#asyncReaddirInFlight = void 0;
|
|
27093
|
-
|
|
28071
|
+
resolve9();
|
|
27094
28072
|
}
|
|
27095
28073
|
return children.slice(0, children.provisional);
|
|
27096
28074
|
}
|
|
@@ -27933,7 +28911,7 @@ var init_esm4 = __esm({
|
|
|
27933
28911
|
var isPatternList, isGlobList, Pattern;
|
|
27934
28912
|
var init_pattern = __esm({
|
|
27935
28913
|
"node_modules/glob/dist/esm/pattern.js"() {
|
|
27936
|
-
|
|
28914
|
+
init_esm3();
|
|
27937
28915
|
isPatternList = (pl) => pl.length >= 1;
|
|
27938
28916
|
isGlobList = (gl) => gl.length >= 1;
|
|
27939
28917
|
Pattern = class _Pattern {
|
|
@@ -28104,7 +29082,7 @@ var init_pattern = __esm({
|
|
|
28104
29082
|
var defaultPlatform2, Ignore;
|
|
28105
29083
|
var init_ignore = __esm({
|
|
28106
29084
|
"node_modules/glob/dist/esm/ignore.js"() {
|
|
28107
|
-
|
|
29085
|
+
init_esm3();
|
|
28108
29086
|
init_pattern();
|
|
28109
29087
|
defaultPlatform2 = typeof process === "object" && process && typeof process.platform === "string" ? process.platform : "linux";
|
|
28110
29088
|
Ignore = class {
|
|
@@ -28198,7 +29176,7 @@ var init_ignore = __esm({
|
|
|
28198
29176
|
var HasWalkedCache, MatchRecord, SubWalks, Processor;
|
|
28199
29177
|
var init_processor = __esm({
|
|
28200
29178
|
"node_modules/glob/dist/esm/processor.js"() {
|
|
28201
|
-
|
|
29179
|
+
init_esm3();
|
|
28202
29180
|
HasWalkedCache = class _HasWalkedCache {
|
|
28203
29181
|
store;
|
|
28204
29182
|
constructor(store = /* @__PURE__ */ new Map()) {
|
|
@@ -28425,7 +29403,7 @@ var init_processor = __esm({
|
|
|
28425
29403
|
var makeIgnore, GlobUtil, GlobWalker, GlobStream;
|
|
28426
29404
|
var init_walker = __esm({
|
|
28427
29405
|
"node_modules/glob/dist/esm/walker.js"() {
|
|
28428
|
-
|
|
29406
|
+
init_esm5();
|
|
28429
29407
|
init_ignore();
|
|
28430
29408
|
init_processor();
|
|
28431
29409
|
makeIgnore = (ignore2, opts) => typeof ignore2 === "string" ? new Ignore([ignore2], opts) : Array.isArray(ignore2) ? new Ignore(ignore2, opts) : ignore2;
|
|
@@ -28761,8 +29739,8 @@ import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
|
28761
29739
|
var defaultPlatform3, Glob;
|
|
28762
29740
|
var init_glob = __esm({
|
|
28763
29741
|
"node_modules/glob/dist/esm/glob.js"() {
|
|
28764
|
-
|
|
28765
|
-
|
|
29742
|
+
init_esm3();
|
|
29743
|
+
init_esm6();
|
|
28766
29744
|
init_pattern();
|
|
28767
29745
|
init_walker();
|
|
28768
29746
|
defaultPlatform3 = typeof process === "object" && process && typeof process.platform === "string" ? process.platform : "linux";
|
|
@@ -28970,7 +29948,7 @@ var init_glob = __esm({
|
|
|
28970
29948
|
var hasMagic;
|
|
28971
29949
|
var init_has_magic = __esm({
|
|
28972
29950
|
"node_modules/glob/dist/esm/has-magic.js"() {
|
|
28973
|
-
|
|
29951
|
+
init_esm3();
|
|
28974
29952
|
hasMagic = (pattern, options = {}) => {
|
|
28975
29953
|
if (!Array.isArray(pattern)) {
|
|
28976
29954
|
pattern = [pattern];
|
|
@@ -29004,12 +29982,12 @@ function globIterate(pattern, options = {}) {
|
|
|
29004
29982
|
return new Glob(pattern, options).iterate();
|
|
29005
29983
|
}
|
|
29006
29984
|
var streamSync, stream, iterateSync, iterate, sync, glob;
|
|
29007
|
-
var
|
|
29985
|
+
var init_esm7 = __esm({
|
|
29008
29986
|
"node_modules/glob/dist/esm/index.js"() {
|
|
29009
|
-
|
|
29987
|
+
init_esm3();
|
|
29010
29988
|
init_glob();
|
|
29011
29989
|
init_has_magic();
|
|
29012
|
-
|
|
29990
|
+
init_esm3();
|
|
29013
29991
|
init_glob();
|
|
29014
29992
|
init_has_magic();
|
|
29015
29993
|
init_ignore();
|
|
@@ -29969,7 +30947,7 @@ var init_executePlan = __esm({
|
|
|
29969
30947
|
init_query();
|
|
29970
30948
|
init_extract();
|
|
29971
30949
|
init_delegate();
|
|
29972
|
-
|
|
30950
|
+
init_esm7();
|
|
29973
30951
|
init_bash();
|
|
29974
30952
|
RAW_OUTPUT_START = "<<<RAW_OUTPUT>>>";
|
|
29975
30953
|
RAW_OUTPUT_END = "<<<END_RAW_OUTPUT>>>";
|
|
@@ -30241,6 +31219,264 @@ var init_file_lister = __esm({
|
|
|
30241
31219
|
}
|
|
30242
31220
|
});
|
|
30243
31221
|
|
|
31222
|
+
// src/tools/fileTracker.js
|
|
31223
|
+
import { createHash as createHash2 } from "crypto";
|
|
31224
|
+
import { resolve as resolve5, isAbsolute as isAbsolute4 } from "path";
|
|
31225
|
+
function computeContentHash(content) {
|
|
31226
|
+
const normalized = (content || "").split("\n").map((l) => l.trimEnd()).join("\n");
|
|
31227
|
+
return createHash2("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
31228
|
+
}
|
|
31229
|
+
function extractFilePath(target) {
|
|
31230
|
+
const hashIdx = target.indexOf("#");
|
|
31231
|
+
if (hashIdx !== -1) {
|
|
31232
|
+
return target.slice(0, hashIdx);
|
|
31233
|
+
}
|
|
31234
|
+
const colonIdx = target.lastIndexOf(":");
|
|
31235
|
+
if (colonIdx !== -1) {
|
|
31236
|
+
const after = target.slice(colonIdx + 1);
|
|
31237
|
+
if (/^\d+(-\d+)?$/.test(after)) {
|
|
31238
|
+
return target.slice(0, colonIdx);
|
|
31239
|
+
}
|
|
31240
|
+
}
|
|
31241
|
+
return target;
|
|
31242
|
+
}
|
|
31243
|
+
function extractSymbolName(target) {
|
|
31244
|
+
const hashIdx = target.indexOf("#");
|
|
31245
|
+
if (hashIdx !== -1) {
|
|
31246
|
+
const symbol = target.slice(hashIdx + 1);
|
|
31247
|
+
return symbol || null;
|
|
31248
|
+
}
|
|
31249
|
+
return null;
|
|
31250
|
+
}
|
|
31251
|
+
function parseFilePathsFromOutput(output) {
|
|
31252
|
+
const paths = [];
|
|
31253
|
+
const regex = /^(?:File:\s+|---\s+)([^\s].*?)(?:\s+---)?$/gm;
|
|
31254
|
+
let match2;
|
|
31255
|
+
while ((match2 = regex.exec(output)) !== null) {
|
|
31256
|
+
const path9 = match2[1].trim();
|
|
31257
|
+
if (path9 && !path9.startsWith("Results") && !path9.startsWith("Page") && (path9.includes("/") || path9.includes(".") || path9.includes("\\"))) {
|
|
31258
|
+
paths.push(path9);
|
|
31259
|
+
}
|
|
31260
|
+
}
|
|
31261
|
+
return paths;
|
|
31262
|
+
}
|
|
31263
|
+
var FileTracker;
|
|
31264
|
+
var init_fileTracker = __esm({
|
|
31265
|
+
"src/tools/fileTracker.js"() {
|
|
31266
|
+
"use strict";
|
|
31267
|
+
init_symbolEdit();
|
|
31268
|
+
FileTracker = class {
|
|
31269
|
+
/**
|
|
31270
|
+
* @param {Object} [options]
|
|
31271
|
+
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
31272
|
+
*/
|
|
31273
|
+
constructor(options = {}) {
|
|
31274
|
+
this.debug = options.debug || false;
|
|
31275
|
+
this._seenFiles = /* @__PURE__ */ new Set();
|
|
31276
|
+
this._contentRecords = /* @__PURE__ */ new Map();
|
|
31277
|
+
}
|
|
31278
|
+
/**
|
|
31279
|
+
* Mark a file as "seen" — the LLM has read its content.
|
|
31280
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31281
|
+
*/
|
|
31282
|
+
markFileSeen(resolvedPath) {
|
|
31283
|
+
this._seenFiles.add(resolvedPath);
|
|
31284
|
+
if (this.debug) {
|
|
31285
|
+
console.error(`[FileTracker] Marked as seen: ${resolvedPath}`);
|
|
31286
|
+
}
|
|
31287
|
+
}
|
|
31288
|
+
/**
|
|
31289
|
+
* Check if a file has been seen in this session.
|
|
31290
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31291
|
+
* @returns {boolean}
|
|
31292
|
+
*/
|
|
31293
|
+
isFileSeen(resolvedPath) {
|
|
31294
|
+
return this._seenFiles.has(resolvedPath);
|
|
31295
|
+
}
|
|
31296
|
+
/**
|
|
31297
|
+
* Store a content hash for a symbol in a file.
|
|
31298
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31299
|
+
* @param {string} symbolName - Symbol name (e.g. "calculateTotal")
|
|
31300
|
+
* @param {string} code - The symbol's source code
|
|
31301
|
+
* @param {number} startLine - 1-indexed start line
|
|
31302
|
+
* @param {number} endLine - 1-indexed end line
|
|
31303
|
+
* @param {string} [source='extract'] - How the content was obtained
|
|
31304
|
+
*/
|
|
31305
|
+
trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, source = "extract") {
|
|
31306
|
+
const key = `${resolvedPath}#${symbolName}`;
|
|
31307
|
+
const contentHash = computeContentHash(code);
|
|
31308
|
+
this._contentRecords.set(key, {
|
|
31309
|
+
contentHash,
|
|
31310
|
+
startLine,
|
|
31311
|
+
endLine,
|
|
31312
|
+
symbolName,
|
|
31313
|
+
source,
|
|
31314
|
+
timestamp: Date.now()
|
|
31315
|
+
});
|
|
31316
|
+
if (this.debug) {
|
|
31317
|
+
console.error(`[FileTracker] Tracked symbol ${key} (hash: ${contentHash}, lines ${startLine}-${endLine})`);
|
|
31318
|
+
}
|
|
31319
|
+
}
|
|
31320
|
+
/**
|
|
31321
|
+
* Look up a stored content record for a symbol.
|
|
31322
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31323
|
+
* @param {string} symbolName - Symbol name
|
|
31324
|
+
* @returns {Object|null} The stored record or null
|
|
31325
|
+
*/
|
|
31326
|
+
getSymbolRecord(resolvedPath, symbolName) {
|
|
31327
|
+
return this._contentRecords.get(`${resolvedPath}#${symbolName}`) || null;
|
|
31328
|
+
}
|
|
31329
|
+
/**
|
|
31330
|
+
* Check if a symbol's current content matches what was stored.
|
|
31331
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31332
|
+
* @param {string} symbolName - Symbol name
|
|
31333
|
+
* @param {string} currentCode - The symbol's current source code (from findSymbol)
|
|
31334
|
+
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
31335
|
+
*/
|
|
31336
|
+
checkSymbolContent(resolvedPath, symbolName, currentCode) {
|
|
31337
|
+
const key = `${resolvedPath}#${symbolName}`;
|
|
31338
|
+
const record = this._contentRecords.get(key);
|
|
31339
|
+
if (!record) {
|
|
31340
|
+
return { ok: true };
|
|
31341
|
+
}
|
|
31342
|
+
const currentHash = computeContentHash(currentCode);
|
|
31343
|
+
if (currentHash === record.contentHash) {
|
|
31344
|
+
return { ok: true };
|
|
31345
|
+
}
|
|
31346
|
+
return {
|
|
31347
|
+
ok: false,
|
|
31348
|
+
reason: "stale",
|
|
31349
|
+
message: `Symbol "${symbolName}" has changed since you last read it (hash: ${record.contentHash} \u2192 ${currentHash}).`
|
|
31350
|
+
};
|
|
31351
|
+
}
|
|
31352
|
+
/**
|
|
31353
|
+
* Track files from extract target strings.
|
|
31354
|
+
* Marks each file as seen. For #symbol targets, calls findSymbol to get and hash the code.
|
|
31355
|
+
* @param {string[]} targets - Array of extract targets (e.g. ["file.js#fn", "file.js:10-20"])
|
|
31356
|
+
* @param {string} cwd - Working directory for resolving relative paths
|
|
31357
|
+
*/
|
|
31358
|
+
async trackFilesFromExtract(targets, cwd) {
|
|
31359
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
31360
|
+
const symbolPromises = [];
|
|
31361
|
+
for (const target of targets) {
|
|
31362
|
+
const filePath = extractFilePath(target);
|
|
31363
|
+
const resolved = isAbsolute4(filePath) ? filePath : resolve5(cwd, filePath);
|
|
31364
|
+
if (!seenPaths.has(resolved)) {
|
|
31365
|
+
seenPaths.add(resolved);
|
|
31366
|
+
this.markFileSeen(resolved);
|
|
31367
|
+
}
|
|
31368
|
+
const symbolName = extractSymbolName(target);
|
|
31369
|
+
if (symbolName) {
|
|
31370
|
+
symbolPromises.push(
|
|
31371
|
+
findSymbol(resolved, symbolName, cwd).then((symbolInfo) => {
|
|
31372
|
+
if (symbolInfo) {
|
|
31373
|
+
this.trackSymbolContent(
|
|
31374
|
+
resolved,
|
|
31375
|
+
symbolName,
|
|
31376
|
+
symbolInfo.code,
|
|
31377
|
+
symbolInfo.startLine,
|
|
31378
|
+
symbolInfo.endLine,
|
|
31379
|
+
"extract"
|
|
31380
|
+
);
|
|
31381
|
+
}
|
|
31382
|
+
}).catch((err) => {
|
|
31383
|
+
if (this.debug) {
|
|
31384
|
+
console.error(`[FileTracker] Failed to track symbol "${symbolName}" in ${resolved}: ${err.message}`);
|
|
31385
|
+
}
|
|
31386
|
+
})
|
|
31387
|
+
);
|
|
31388
|
+
}
|
|
31389
|
+
}
|
|
31390
|
+
if (symbolPromises.length > 0) {
|
|
31391
|
+
await Promise.all(symbolPromises);
|
|
31392
|
+
}
|
|
31393
|
+
}
|
|
31394
|
+
/**
|
|
31395
|
+
* Track files discovered in probe search/extract output.
|
|
31396
|
+
* Parses "File: path" headers and "--- path ---" separators, marks each as "seen".
|
|
31397
|
+
* @param {string} output - Probe output text
|
|
31398
|
+
* @param {string} cwd - Working directory for resolving relative paths
|
|
31399
|
+
*/
|
|
31400
|
+
async trackFilesFromOutput(output, cwd) {
|
|
31401
|
+
const paths = parseFilePathsFromOutput(output);
|
|
31402
|
+
for (const filePath of paths) {
|
|
31403
|
+
const resolved = isAbsolute4(filePath) ? filePath : resolve5(cwd, filePath);
|
|
31404
|
+
this.markFileSeen(resolved);
|
|
31405
|
+
}
|
|
31406
|
+
}
|
|
31407
|
+
/**
|
|
31408
|
+
* Check if a file is safe to edit (seen-check only).
|
|
31409
|
+
* Mode-specific content verification happens in edit handlers.
|
|
31410
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31411
|
+
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
31412
|
+
*/
|
|
31413
|
+
checkBeforeEdit(resolvedPath) {
|
|
31414
|
+
if (!this._seenFiles.has(resolvedPath)) {
|
|
31415
|
+
return {
|
|
31416
|
+
ok: false,
|
|
31417
|
+
reason: "untracked",
|
|
31418
|
+
message: "This file has not been read yet in this session. Use extract or search to read the file first."
|
|
31419
|
+
};
|
|
31420
|
+
}
|
|
31421
|
+
return { ok: true };
|
|
31422
|
+
}
|
|
31423
|
+
/**
|
|
31424
|
+
* Mark a file as seen after a successful write (backward compat).
|
|
31425
|
+
* Also invalidates content records for the file since its content changed.
|
|
31426
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31427
|
+
*/
|
|
31428
|
+
async trackFileAfterWrite(resolvedPath) {
|
|
31429
|
+
this.markFileSeen(resolvedPath);
|
|
31430
|
+
this.invalidateFileRecords(resolvedPath);
|
|
31431
|
+
}
|
|
31432
|
+
/**
|
|
31433
|
+
* Update the stored hash for a symbol after a successful write.
|
|
31434
|
+
* Enables chained edits to the same symbol.
|
|
31435
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31436
|
+
* @param {string} symbolName - Symbol name
|
|
31437
|
+
* @param {string} code - The symbol's new source code
|
|
31438
|
+
* @param {number} startLine - 1-indexed start line (new position)
|
|
31439
|
+
* @param {number} endLine - 1-indexed end line (new position)
|
|
31440
|
+
*/
|
|
31441
|
+
trackSymbolAfterWrite(resolvedPath, symbolName, code, startLine, endLine) {
|
|
31442
|
+
this.trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, "edit");
|
|
31443
|
+
}
|
|
31444
|
+
/**
|
|
31445
|
+
* Remove all content records for a file.
|
|
31446
|
+
* Called after non-symbol edits (text/line mode) since those change content
|
|
31447
|
+
* without providing a symbol-level update.
|
|
31448
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31449
|
+
*/
|
|
31450
|
+
invalidateFileRecords(resolvedPath) {
|
|
31451
|
+
const prefix = resolvedPath + "#";
|
|
31452
|
+
for (const key of this._contentRecords.keys()) {
|
|
31453
|
+
if (key.startsWith(prefix)) {
|
|
31454
|
+
this._contentRecords.delete(key);
|
|
31455
|
+
}
|
|
31456
|
+
}
|
|
31457
|
+
if (this.debug) {
|
|
31458
|
+
console.error(`[FileTracker] Invalidated content records for ${resolvedPath}`);
|
|
31459
|
+
}
|
|
31460
|
+
}
|
|
31461
|
+
/**
|
|
31462
|
+
* Quick sync check if a file is being tracked (alias for isFileSeen).
|
|
31463
|
+
* @param {string} resolvedPath - Absolute path to the file
|
|
31464
|
+
* @returns {boolean}
|
|
31465
|
+
*/
|
|
31466
|
+
isTracked(resolvedPath) {
|
|
31467
|
+
return this.isFileSeen(resolvedPath);
|
|
31468
|
+
}
|
|
31469
|
+
/**
|
|
31470
|
+
* Clear all tracking state.
|
|
31471
|
+
*/
|
|
31472
|
+
clear() {
|
|
31473
|
+
this._seenFiles.clear();
|
|
31474
|
+
this._contentRecords.clear();
|
|
31475
|
+
}
|
|
31476
|
+
};
|
|
31477
|
+
}
|
|
31478
|
+
});
|
|
31479
|
+
|
|
30244
31480
|
// src/agent/simpleTelemetry.js
|
|
30245
31481
|
import { existsSync as existsSync3, mkdirSync, createWriteStream } from "fs";
|
|
30246
31482
|
import { dirname as dirname2 } from "path";
|
|
@@ -30335,20 +31571,20 @@ var init_simpleTelemetry = __esm({
|
|
|
30335
31571
|
}
|
|
30336
31572
|
async flush() {
|
|
30337
31573
|
if (this.stream) {
|
|
30338
|
-
return new Promise((
|
|
30339
|
-
this.stream.once("drain",
|
|
31574
|
+
return new Promise((resolve9) => {
|
|
31575
|
+
this.stream.once("drain", resolve9);
|
|
30340
31576
|
if (!this.stream.writableNeedDrain) {
|
|
30341
|
-
|
|
31577
|
+
resolve9();
|
|
30342
31578
|
}
|
|
30343
31579
|
});
|
|
30344
31580
|
}
|
|
30345
31581
|
}
|
|
30346
31582
|
async shutdown() {
|
|
30347
31583
|
if (this.stream) {
|
|
30348
|
-
return new Promise((
|
|
31584
|
+
return new Promise((resolve9) => {
|
|
30349
31585
|
this.stream.end(() => {
|
|
30350
31586
|
console.log(`[SimpleTelemetry] File stream closed: ${this.filePath}`);
|
|
30351
|
-
|
|
31587
|
+
resolve9();
|
|
30352
31588
|
});
|
|
30353
31589
|
});
|
|
30354
31590
|
}
|
|
@@ -30793,7 +32029,7 @@ var init_probeTool = __esm({
|
|
|
30793
32029
|
"src/agent/probeTool.js"() {
|
|
30794
32030
|
"use strict";
|
|
30795
32031
|
init_index();
|
|
30796
|
-
|
|
32032
|
+
init_esm7();
|
|
30797
32033
|
init_symlink_utils();
|
|
30798
32034
|
toolCallEmitter = new EventEmitter2();
|
|
30799
32035
|
activeToolExecutions = /* @__PURE__ */ new Map();
|
|
@@ -31531,6 +32767,7 @@ var init_index = __esm({
|
|
|
31531
32767
|
init_executePlan();
|
|
31532
32768
|
init_bash();
|
|
31533
32769
|
init_edit();
|
|
32770
|
+
init_fileTracker();
|
|
31534
32771
|
init_ProbeAgent();
|
|
31535
32772
|
init_simpleTelemetry();
|
|
31536
32773
|
init_probeTool();
|
|
@@ -31713,38 +32950,13 @@ function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
|
31713
32950
|
const toolCall = parseXmlToolCall(cleanedXmlString, validTools);
|
|
31714
32951
|
return toolCall ? { ...toolCall, thinkingContent } : null;
|
|
31715
32952
|
}
|
|
31716
|
-
var
|
|
32953
|
+
var listFilesToolDefinition, searchFilesToolDefinition, listSkillsToolDefinition, useSkillToolDefinition, readImageToolDefinition;
|
|
31717
32954
|
var init_tools2 = __esm({
|
|
31718
32955
|
"src/agent/tools.js"() {
|
|
31719
32956
|
"use strict";
|
|
31720
32957
|
init_index();
|
|
31721
32958
|
init_xmlParsingUtils();
|
|
31722
32959
|
init_tasks();
|
|
31723
|
-
implementToolDefinition = `
|
|
31724
|
-
## implement
|
|
31725
|
-
Description: Implement a given task. Can modify files. Can be used ONLY if task explicitly stated that something requires modification or implementation.
|
|
31726
|
-
|
|
31727
|
-
Parameters:
|
|
31728
|
-
- task: (required) The task description. Should be as detailed as possible, ideally pointing to exact files which needs be modified or created.
|
|
31729
|
-
- autoCommits: (optional) Whether to enable auto-commits in aider. Default is false.
|
|
31730
|
-
|
|
31731
|
-
Usage Example:
|
|
31732
|
-
|
|
31733
|
-
<examples>
|
|
31734
|
-
|
|
31735
|
-
User: Can you implement a function to calculate Fibonacci numbers in main.js?
|
|
31736
|
-
<implement>
|
|
31737
|
-
<task>Implement a recursive function to calculate the nth Fibonacci number in main.js</task>
|
|
31738
|
-
</implement>
|
|
31739
|
-
|
|
31740
|
-
User: Can you implement a function to calculate Fibonacci numbers in main.js with auto-commits?
|
|
31741
|
-
<implement>
|
|
31742
|
-
<task>Implement a recursive function to calculate the nth Fibonacci number in main.js</task>
|
|
31743
|
-
<autoCommits>true</autoCommits>
|
|
31744
|
-
</implement>
|
|
31745
|
-
|
|
31746
|
-
</examples>
|
|
31747
|
-
`;
|
|
31748
32960
|
listFilesToolDefinition = `
|
|
31749
32961
|
## listFiles
|
|
31750
32962
|
Description: List files and directories in a specified location.
|
|
@@ -31868,7 +33080,7 @@ function createMockProvider() {
|
|
|
31868
33080
|
provider: "mock",
|
|
31869
33081
|
// Mock the doGenerate method used by Vercel AI SDK
|
|
31870
33082
|
doGenerate: async ({ messages, tools: tools2 }) => {
|
|
31871
|
-
await new Promise((
|
|
33083
|
+
await new Promise((resolve9) => setTimeout(resolve9, 10));
|
|
31872
33084
|
return {
|
|
31873
33085
|
text: "This is a mock response for testing",
|
|
31874
33086
|
toolCalls: [],
|
|
@@ -37071,23 +38283,23 @@ var init_regexp_parser = __esm({
|
|
|
37071
38283
|
return ASSERT_NEVER_REACH_HERE();
|
|
37072
38284
|
}
|
|
37073
38285
|
quantifier(isBacktracking = false) {
|
|
37074
|
-
let
|
|
38286
|
+
let range2 = void 0;
|
|
37075
38287
|
const begin = this.idx;
|
|
37076
38288
|
switch (this.popChar()) {
|
|
37077
38289
|
case "*":
|
|
37078
|
-
|
|
38290
|
+
range2 = {
|
|
37079
38291
|
atLeast: 0,
|
|
37080
38292
|
atMost: Infinity
|
|
37081
38293
|
};
|
|
37082
38294
|
break;
|
|
37083
38295
|
case "+":
|
|
37084
|
-
|
|
38296
|
+
range2 = {
|
|
37085
38297
|
atLeast: 1,
|
|
37086
38298
|
atMost: Infinity
|
|
37087
38299
|
};
|
|
37088
38300
|
break;
|
|
37089
38301
|
case "?":
|
|
37090
|
-
|
|
38302
|
+
range2 = {
|
|
37091
38303
|
atLeast: 0,
|
|
37092
38304
|
atMost: 1
|
|
37093
38305
|
};
|
|
@@ -37096,7 +38308,7 @@ var init_regexp_parser = __esm({
|
|
|
37096
38308
|
const atLeast = this.integerIncludingZero();
|
|
37097
38309
|
switch (this.popChar()) {
|
|
37098
38310
|
case "}":
|
|
37099
|
-
|
|
38311
|
+
range2 = {
|
|
37100
38312
|
atLeast,
|
|
37101
38313
|
atMost: atLeast
|
|
37102
38314
|
};
|
|
@@ -37105,12 +38317,12 @@ var init_regexp_parser = __esm({
|
|
|
37105
38317
|
let atMost;
|
|
37106
38318
|
if (this.isDigit()) {
|
|
37107
38319
|
atMost = this.integerIncludingZero();
|
|
37108
|
-
|
|
38320
|
+
range2 = {
|
|
37109
38321
|
atLeast,
|
|
37110
38322
|
atMost
|
|
37111
38323
|
};
|
|
37112
38324
|
} else {
|
|
37113
|
-
|
|
38325
|
+
range2 = {
|
|
37114
38326
|
atLeast,
|
|
37115
38327
|
atMost: Infinity
|
|
37116
38328
|
};
|
|
@@ -37118,25 +38330,25 @@ var init_regexp_parser = __esm({
|
|
|
37118
38330
|
this.consumeChar("}");
|
|
37119
38331
|
break;
|
|
37120
38332
|
}
|
|
37121
|
-
if (isBacktracking === true &&
|
|
38333
|
+
if (isBacktracking === true && range2 === void 0) {
|
|
37122
38334
|
return void 0;
|
|
37123
38335
|
}
|
|
37124
|
-
ASSERT_EXISTS(
|
|
38336
|
+
ASSERT_EXISTS(range2);
|
|
37125
38337
|
break;
|
|
37126
38338
|
}
|
|
37127
|
-
if (isBacktracking === true &&
|
|
38339
|
+
if (isBacktracking === true && range2 === void 0) {
|
|
37128
38340
|
return void 0;
|
|
37129
38341
|
}
|
|
37130
|
-
if (ASSERT_EXISTS(
|
|
38342
|
+
if (ASSERT_EXISTS(range2)) {
|
|
37131
38343
|
if (this.peekChar(0) === "?") {
|
|
37132
38344
|
this.consumeChar("?");
|
|
37133
|
-
|
|
38345
|
+
range2.greedy = false;
|
|
37134
38346
|
} else {
|
|
37135
|
-
|
|
38347
|
+
range2.greedy = true;
|
|
37136
38348
|
}
|
|
37137
|
-
|
|
37138
|
-
|
|
37139
|
-
return
|
|
38349
|
+
range2.type = "Quantifier";
|
|
38350
|
+
range2.loc = this.loc(begin);
|
|
38351
|
+
return range2;
|
|
37140
38352
|
}
|
|
37141
38353
|
}
|
|
37142
38354
|
atom() {
|
|
@@ -37838,18 +39050,18 @@ function firstCharOptimizedIndices(ast, result, ignoreCase) {
|
|
|
37838
39050
|
if (typeof code === "number") {
|
|
37839
39051
|
addOptimizedIdxToResult(code, result, ignoreCase);
|
|
37840
39052
|
} else {
|
|
37841
|
-
const
|
|
39053
|
+
const range2 = code;
|
|
37842
39054
|
if (ignoreCase === true) {
|
|
37843
|
-
for (let rangeCode =
|
|
39055
|
+
for (let rangeCode = range2.from; rangeCode <= range2.to; rangeCode++) {
|
|
37844
39056
|
addOptimizedIdxToResult(rangeCode, result, ignoreCase);
|
|
37845
39057
|
}
|
|
37846
39058
|
} else {
|
|
37847
|
-
for (let rangeCode =
|
|
39059
|
+
for (let rangeCode = range2.from; rangeCode <= range2.to && rangeCode < minOptimizationVal; rangeCode++) {
|
|
37848
39060
|
addOptimizedIdxToResult(rangeCode, result, ignoreCase);
|
|
37849
39061
|
}
|
|
37850
|
-
if (
|
|
37851
|
-
const minUnOptVal =
|
|
37852
|
-
const maxUnOptVal =
|
|
39062
|
+
if (range2.to >= minOptimizationVal) {
|
|
39063
|
+
const minUnOptVal = range2.from >= minOptimizationVal ? range2.from : minOptimizationVal;
|
|
39064
|
+
const maxUnOptVal = range2.to;
|
|
37853
39065
|
const minOptIdx = charCodeToOptimizedIndex(minUnOptVal);
|
|
37854
39066
|
const maxOptIdx = charCodeToOptimizedIndex(maxUnOptVal);
|
|
37855
39067
|
for (let currOptIdx = minOptIdx; currOptIdx <= maxOptIdx; currOptIdx++) {
|
|
@@ -37910,8 +39122,8 @@ function findCode(setNode, targetCharCodes) {
|
|
|
37910
39122
|
if (typeof codeOrRange === "number") {
|
|
37911
39123
|
return includes_default(targetCharCodes, codeOrRange);
|
|
37912
39124
|
} else {
|
|
37913
|
-
const
|
|
37914
|
-
return find_default(targetCharCodes, (targetCode) =>
|
|
39125
|
+
const range2 = codeOrRange;
|
|
39126
|
+
return find_default(targetCharCodes, (targetCode) => range2.from <= targetCode && targetCode <= range2.to) !== void 0;
|
|
37915
39127
|
}
|
|
37916
39128
|
});
|
|
37917
39129
|
}
|
|
@@ -55816,8 +57028,8 @@ var require_createRange = __commonJS({
|
|
|
55816
57028
|
var require_range = __commonJS({
|
|
55817
57029
|
"node_modules/lodash/range.js"(exports2, module2) {
|
|
55818
57030
|
var createRange = require_createRange();
|
|
55819
|
-
var
|
|
55820
|
-
module2.exports =
|
|
57031
|
+
var range2 = createRange();
|
|
57032
|
+
module2.exports = range2;
|
|
55821
57033
|
}
|
|
55822
57034
|
});
|
|
55823
57035
|
|
|
@@ -65058,7 +66270,7 @@ var require_compile = __commonJS({
|
|
|
65058
66270
|
const schOrFunc = root2.refs[ref2];
|
|
65059
66271
|
if (schOrFunc)
|
|
65060
66272
|
return schOrFunc;
|
|
65061
|
-
let _sch =
|
|
66273
|
+
let _sch = resolve9.call(this, root2, ref2);
|
|
65062
66274
|
if (_sch === void 0) {
|
|
65063
66275
|
const schema = (_a = root2.localRefs) === null || _a === void 0 ? void 0 : _a[ref2];
|
|
65064
66276
|
const { schemaId } = this.opts;
|
|
@@ -65085,7 +66297,7 @@ var require_compile = __commonJS({
|
|
|
65085
66297
|
function sameSchemaEnv(s1, s2) {
|
|
65086
66298
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
65087
66299
|
}
|
|
65088
|
-
function
|
|
66300
|
+
function resolve9(root2, ref2) {
|
|
65089
66301
|
let sch;
|
|
65090
66302
|
while (typeof (sch = this.refs[ref2]) == "string")
|
|
65091
66303
|
ref2 = sch;
|
|
@@ -65660,7 +66872,7 @@ var require_fast_uri = __commonJS({
|
|
|
65660
66872
|
}
|
|
65661
66873
|
return uri;
|
|
65662
66874
|
}
|
|
65663
|
-
function
|
|
66875
|
+
function resolve9(baseURI, relativeURI, options) {
|
|
65664
66876
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
65665
66877
|
const resolved = resolveComponent(parse9(baseURI, schemelessOptions), parse9(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
65666
66878
|
schemelessOptions.skipEscape = true;
|
|
@@ -65887,7 +67099,7 @@ var require_fast_uri = __commonJS({
|
|
|
65887
67099
|
var fastUri = {
|
|
65888
67100
|
SCHEMES,
|
|
65889
67101
|
normalize: normalize3,
|
|
65890
|
-
resolve:
|
|
67102
|
+
resolve: resolve9,
|
|
65891
67103
|
resolveComponent,
|
|
65892
67104
|
equal,
|
|
65893
67105
|
serialize,
|
|
@@ -68582,6 +69794,7 @@ __export(schemaUtils_exports, {
|
|
|
68582
69794
|
replaceMermaidDiagramsInMarkdown: () => replaceMermaidDiagramsInMarkdown,
|
|
68583
69795
|
sanitizeMarkdownEscapesInJson: () => sanitizeMarkdownEscapesInJson,
|
|
68584
69796
|
tryAutoWrapForSimpleSchema: () => tryAutoWrapForSimpleSchema,
|
|
69797
|
+
tryExtractValidJsonPrefix: () => tryExtractValidJsonPrefix,
|
|
68585
69798
|
tryMaidAutoFix: () => tryMaidAutoFix,
|
|
68586
69799
|
validateAndFixMermaidResponse: () => validateAndFixMermaidResponse,
|
|
68587
69800
|
validateJsonResponse: () => validateJsonResponse,
|
|
@@ -68968,6 +70181,13 @@ function validateJsonResponse(response, options = {}) {
|
|
|
68968
70181
|
errorPosition = response.indexOf(problematicToken);
|
|
68969
70182
|
}
|
|
68970
70183
|
}
|
|
70184
|
+
const prefixResult = tryExtractValidJsonPrefix(responseToValidate, { schema, debug });
|
|
70185
|
+
if (prefixResult && prefixResult.isValid) {
|
|
70186
|
+
if (debug) {
|
|
70187
|
+
console.log(`[DEBUG] JSON validation: Recovered valid JSON prefix (${prefixResult.extracted.length} chars) from response with trailing content`);
|
|
70188
|
+
}
|
|
70189
|
+
return { isValid: true, parsed: prefixResult.parsed };
|
|
70190
|
+
}
|
|
68971
70191
|
let enhancedError = error.message;
|
|
68972
70192
|
let errorContext = null;
|
|
68973
70193
|
if (errorPosition !== null && errorPosition >= 0 && response && response.length > 0) {
|
|
@@ -69018,6 +70238,84 @@ ${errorContext.pointer}`);
|
|
|
69018
70238
|
};
|
|
69019
70239
|
}
|
|
69020
70240
|
}
|
|
70241
|
+
function tryExtractValidJsonPrefix(response, options = {}) {
|
|
70242
|
+
const { schema = null, debug = false } = options;
|
|
70243
|
+
if (!response || typeof response !== "string") {
|
|
70244
|
+
return null;
|
|
70245
|
+
}
|
|
70246
|
+
const trimmed = response.trim();
|
|
70247
|
+
if (trimmed.length === 0) {
|
|
70248
|
+
return null;
|
|
70249
|
+
}
|
|
70250
|
+
const firstChar = trimmed[0];
|
|
70251
|
+
if (firstChar !== "{" && firstChar !== "[") {
|
|
70252
|
+
return null;
|
|
70253
|
+
}
|
|
70254
|
+
try {
|
|
70255
|
+
JSON.parse(trimmed);
|
|
70256
|
+
return null;
|
|
70257
|
+
} catch {
|
|
70258
|
+
}
|
|
70259
|
+
const openChar = firstChar;
|
|
70260
|
+
const closeChar = openChar === "{" ? "}" : "]";
|
|
70261
|
+
let depth = 0;
|
|
70262
|
+
let inString = false;
|
|
70263
|
+
let escapeNext = false;
|
|
70264
|
+
let endPos = -1;
|
|
70265
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
70266
|
+
const char = trimmed[i];
|
|
70267
|
+
if (escapeNext) {
|
|
70268
|
+
escapeNext = false;
|
|
70269
|
+
continue;
|
|
70270
|
+
}
|
|
70271
|
+
if (char === "\\" && inString) {
|
|
70272
|
+
escapeNext = true;
|
|
70273
|
+
continue;
|
|
70274
|
+
}
|
|
70275
|
+
if (char === '"') {
|
|
70276
|
+
inString = !inString;
|
|
70277
|
+
continue;
|
|
70278
|
+
}
|
|
70279
|
+
if (inString) {
|
|
70280
|
+
continue;
|
|
70281
|
+
}
|
|
70282
|
+
if (char === openChar) {
|
|
70283
|
+
depth++;
|
|
70284
|
+
} else if (char === closeChar) {
|
|
70285
|
+
depth--;
|
|
70286
|
+
if (depth === 0) {
|
|
70287
|
+
endPos = i + 1;
|
|
70288
|
+
break;
|
|
70289
|
+
}
|
|
70290
|
+
}
|
|
70291
|
+
}
|
|
70292
|
+
if (endPos <= 0 || endPos >= trimmed.length) {
|
|
70293
|
+
return null;
|
|
70294
|
+
}
|
|
70295
|
+
const remainder = trimmed.substring(endPos).trim();
|
|
70296
|
+
if (remainder.length === 0) {
|
|
70297
|
+
return null;
|
|
70298
|
+
}
|
|
70299
|
+
const prefix = trimmed.substring(0, endPos);
|
|
70300
|
+
try {
|
|
70301
|
+
const parsed = JSON.parse(prefix);
|
|
70302
|
+
if (debug) {
|
|
70303
|
+
console.log(`[DEBUG] tryExtractValidJsonPrefix: Extracted valid JSON prefix (${prefix.length} chars), stripped trailing content (${remainder.length} chars)`);
|
|
70304
|
+
}
|
|
70305
|
+
if (schema) {
|
|
70306
|
+
const schemaValidation = validateJsonResponse(prefix, { debug, schema });
|
|
70307
|
+
if (!schemaValidation.isValid) {
|
|
70308
|
+
if (debug) {
|
|
70309
|
+
console.log(`[DEBUG] tryExtractValidJsonPrefix: Prefix is valid JSON but fails schema validation: ${schemaValidation.error}`);
|
|
70310
|
+
}
|
|
70311
|
+
return null;
|
|
70312
|
+
}
|
|
70313
|
+
}
|
|
70314
|
+
return { isValid: true, parsed, extracted: prefix };
|
|
70315
|
+
} catch {
|
|
70316
|
+
return null;
|
|
70317
|
+
}
|
|
70318
|
+
}
|
|
69021
70319
|
function validateXmlResponse(response) {
|
|
69022
70320
|
const xmlPattern = /<\/?[\w\s="'.-]+>/g;
|
|
69023
70321
|
const tags = response.match(xmlPattern);
|
|
@@ -71119,7 +72417,7 @@ function parseXmlMcpToolCall(xmlString, mcpToolNames = []) {
|
|
|
71119
72417
|
let match2;
|
|
71120
72418
|
while ((match2 = paramPattern.exec(content)) !== null) {
|
|
71121
72419
|
const [, paramName, paramValue] = match2;
|
|
71122
|
-
params[paramName] = paramValue.trim();
|
|
72420
|
+
params[paramName] = unescapeXmlEntities(paramValue.trim());
|
|
71123
72421
|
}
|
|
71124
72422
|
}
|
|
71125
72423
|
return { toolName, params };
|
|
@@ -71169,7 +72467,7 @@ function parseNativeXmlTool(xmlString, toolName) {
|
|
|
71169
72467
|
while ((match2 = paramPattern.exec(content)) !== null) {
|
|
71170
72468
|
const [, paramName, paramValue] = match2;
|
|
71171
72469
|
if (paramName !== "params") {
|
|
71172
|
-
params[paramName] = paramValue.trim();
|
|
72470
|
+
params[paramName] = unescapeXmlEntities(paramValue.trim());
|
|
71173
72471
|
}
|
|
71174
72472
|
}
|
|
71175
72473
|
if (Object.keys(params).length > 0) {
|
|
@@ -71184,6 +72482,7 @@ var init_xmlBridge = __esm({
|
|
|
71184
72482
|
init_client();
|
|
71185
72483
|
init_config();
|
|
71186
72484
|
init_xmlParsingUtils();
|
|
72485
|
+
init_common();
|
|
71187
72486
|
MCPXmlBridge = class {
|
|
71188
72487
|
constructor(options = {}) {
|
|
71189
72488
|
this.debug = options.debug || false;
|
|
@@ -76149,7 +77448,7 @@ var require_compose_scalar = __commonJS({
|
|
|
76149
77448
|
var resolveBlockScalar = require_resolve_block_scalar();
|
|
76150
77449
|
var resolveFlowScalar = require_resolve_flow_scalar();
|
|
76151
77450
|
function composeScalar(ctx, token, tagToken, onError) {
|
|
76152
|
-
const { value, type, comment, range } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError);
|
|
77451
|
+
const { value, type, comment, range: range2 } = token.type === "block-scalar" ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError) : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError);
|
|
76153
77452
|
const tagName = tagToken ? ctx.directives.tagName(tagToken.source, (msg) => onError(tagToken, "TAG_RESOLVE_FAILED", msg)) : null;
|
|
76154
77453
|
let tag;
|
|
76155
77454
|
if (ctx.options.stringKeys && ctx.atKey) {
|
|
@@ -76169,7 +77468,7 @@ var require_compose_scalar = __commonJS({
|
|
|
76169
77468
|
onError(tagToken ?? token, "TAG_RESOLVE_FAILED", msg);
|
|
76170
77469
|
scalar = new Scalar.Scalar(value);
|
|
76171
77470
|
}
|
|
76172
|
-
scalar.range =
|
|
77471
|
+
scalar.range = range2;
|
|
76173
77472
|
scalar.source = value;
|
|
76174
77473
|
if (type)
|
|
76175
77474
|
scalar.type = type;
|
|
@@ -78781,14 +80080,14 @@ var init_parser7 = __esm({
|
|
|
78781
80080
|
// src/agent/skills/registry.js
|
|
78782
80081
|
import { existsSync as existsSync5 } from "fs";
|
|
78783
80082
|
import { readdir as readdir2, readFile as readFile2, realpath as realpath2, lstat as lstat2 } from "fs/promises";
|
|
78784
|
-
import { resolve as
|
|
80083
|
+
import { resolve as resolve6, join as join3, isAbsolute as isAbsolute5, sep as sep4, relative } from "path";
|
|
78785
80084
|
function isPathInside(basePath, targetPath) {
|
|
78786
|
-
const base2 =
|
|
78787
|
-
const target =
|
|
80085
|
+
const base2 = resolve6(basePath);
|
|
80086
|
+
const target = resolve6(targetPath);
|
|
78788
80087
|
const rel = relative(base2, target);
|
|
78789
80088
|
if (rel === "") return true;
|
|
78790
80089
|
if (rel === ".." || rel.startsWith(`..${sep4}`)) return false;
|
|
78791
|
-
if (
|
|
80090
|
+
if (isAbsolute5(rel)) return false;
|
|
78792
80091
|
return true;
|
|
78793
80092
|
}
|
|
78794
80093
|
function isSafeEntryName(name) {
|
|
@@ -78805,7 +80104,7 @@ var init_registry = __esm({
|
|
|
78805
80104
|
SKILL_FILE_NAME = "SKILL.md";
|
|
78806
80105
|
SkillRegistry = class {
|
|
78807
80106
|
constructor({ repoRoot, skillDirs = DEFAULT_SKILL_DIRS, debug = false } = {}) {
|
|
78808
|
-
this.repoRoot = repoRoot ?
|
|
80107
|
+
this.repoRoot = repoRoot ? resolve6(repoRoot) : process.cwd();
|
|
78809
80108
|
this.repoRootReal = null;
|
|
78810
80109
|
this.skillDirs = Array.isArray(skillDirs) && skillDirs.length > 0 ? skillDirs : DEFAULT_SKILL_DIRS;
|
|
78811
80110
|
this.debug = debug;
|
|
@@ -78859,8 +80158,8 @@ var init_registry = __esm({
|
|
|
78859
80158
|
}
|
|
78860
80159
|
}
|
|
78861
80160
|
async _resolveSkillDir(skillDir) {
|
|
78862
|
-
const resolved =
|
|
78863
|
-
const repoRoot = this.repoRootReal ||
|
|
80161
|
+
const resolved = isAbsolute5(skillDir) ? resolve6(skillDir) : resolve6(this.repoRoot, skillDir);
|
|
80162
|
+
const repoRoot = this.repoRootReal || resolve6(this.repoRoot);
|
|
78864
80163
|
const resolvedReal = await this._resolveRealPath(resolved);
|
|
78865
80164
|
if (!resolvedReal) return null;
|
|
78866
80165
|
if (!isPathInside(repoRoot, resolvedReal)) {
|
|
@@ -79050,7 +80349,7 @@ function extractErrorInfo(error) {
|
|
|
79050
80349
|
};
|
|
79051
80350
|
}
|
|
79052
80351
|
function sleep(ms) {
|
|
79053
|
-
return new Promise((
|
|
80352
|
+
return new Promise((resolve9) => setTimeout(resolve9, ms));
|
|
79054
80353
|
}
|
|
79055
80354
|
var DEFAULT_RETRYABLE_ERRORS, RetryManager;
|
|
79056
80355
|
var init_RetryManager = __esm({
|
|
@@ -79982,7 +81281,7 @@ var init_built_in_server = __esm({
|
|
|
79982
81281
|
}
|
|
79983
81282
|
});
|
|
79984
81283
|
this.registerHandlers();
|
|
79985
|
-
return new Promise((
|
|
81284
|
+
return new Promise((resolve9, reject2) => {
|
|
79986
81285
|
this.httpServer.listen(this.port, this.host, async () => {
|
|
79987
81286
|
const address = this.httpServer.address();
|
|
79988
81287
|
this.port = address.port;
|
|
@@ -79992,7 +81291,7 @@ var init_built_in_server = __esm({
|
|
|
79992
81291
|
console.log(`[MCP] Messages endpoint: http://${this.host}:${this.port}/messages`);
|
|
79993
81292
|
}
|
|
79994
81293
|
this.emit("ready", { host: this.host, port: this.port });
|
|
79995
|
-
|
|
81294
|
+
resolve9({ host: this.host, port: this.port });
|
|
79996
81295
|
});
|
|
79997
81296
|
this.httpServer.on("error", reject2);
|
|
79998
81297
|
});
|
|
@@ -80211,7 +81510,7 @@ var init_built_in_server = __esm({
|
|
|
80211
81510
|
* Parse request body as JSON
|
|
80212
81511
|
*/
|
|
80213
81512
|
async parseRequestBody(req) {
|
|
80214
|
-
return new Promise((
|
|
81513
|
+
return new Promise((resolve9, reject2) => {
|
|
80215
81514
|
let body = "";
|
|
80216
81515
|
req.on("data", (chunk) => {
|
|
80217
81516
|
body += chunk.toString();
|
|
@@ -80219,7 +81518,7 @@ var init_built_in_server = __esm({
|
|
|
80219
81518
|
req.on("end", () => {
|
|
80220
81519
|
try {
|
|
80221
81520
|
const parsed = body ? JSON.parse(body) : null;
|
|
80222
|
-
|
|
81521
|
+
resolve9(parsed);
|
|
80223
81522
|
} catch (error) {
|
|
80224
81523
|
reject2(error);
|
|
80225
81524
|
}
|
|
@@ -80526,12 +81825,12 @@ data: ${JSON.stringify(data2)}
|
|
|
80526
81825
|
}
|
|
80527
81826
|
this.connections.clear();
|
|
80528
81827
|
if (this.httpServer) {
|
|
80529
|
-
return new Promise((
|
|
81828
|
+
return new Promise((resolve9) => {
|
|
80530
81829
|
this.httpServer.close(() => {
|
|
80531
81830
|
if (this.debug) {
|
|
80532
81831
|
console.log("[MCP] Built-in server stopped");
|
|
80533
81832
|
}
|
|
80534
|
-
|
|
81833
|
+
resolve9();
|
|
80535
81834
|
});
|
|
80536
81835
|
});
|
|
80537
81836
|
}
|
|
@@ -80866,8 +82165,8 @@ ${opts.schema}`;
|
|
|
80866
82165
|
break;
|
|
80867
82166
|
}
|
|
80868
82167
|
} else if (!processEnded) {
|
|
80869
|
-
await new Promise((
|
|
80870
|
-
resolver =
|
|
82168
|
+
await new Promise((resolve9) => {
|
|
82169
|
+
resolver = resolve9;
|
|
80871
82170
|
});
|
|
80872
82171
|
}
|
|
80873
82172
|
}
|
|
@@ -81127,12 +82426,12 @@ async function createCodexEngine(options = {}) {
|
|
|
81127
82426
|
}
|
|
81128
82427
|
}
|
|
81129
82428
|
if (message.id !== void 0 && pendingRequests.has(message.id)) {
|
|
81130
|
-
const { resolve:
|
|
82429
|
+
const { resolve: resolve9, reject: reject2 } = pendingRequests.get(message.id);
|
|
81131
82430
|
pendingRequests.delete(message.id);
|
|
81132
82431
|
if (message.error) {
|
|
81133
82432
|
reject2(new Error(message.error.message || JSON.stringify(message.error)));
|
|
81134
82433
|
} else {
|
|
81135
|
-
|
|
82434
|
+
resolve9(message.result);
|
|
81136
82435
|
}
|
|
81137
82436
|
}
|
|
81138
82437
|
if (message.method === "codex/event" && message.params) {
|
|
@@ -81153,7 +82452,7 @@ async function createCodexEngine(options = {}) {
|
|
|
81153
82452
|
});
|
|
81154
82453
|
}
|
|
81155
82454
|
function sendRequest(method, params = {}) {
|
|
81156
|
-
return new Promise((
|
|
82455
|
+
return new Promise((resolve9, reject2) => {
|
|
81157
82456
|
const id = ++requestId;
|
|
81158
82457
|
const request = {
|
|
81159
82458
|
jsonrpc: "2.0",
|
|
@@ -81161,7 +82460,7 @@ async function createCodexEngine(options = {}) {
|
|
|
81161
82460
|
method,
|
|
81162
82461
|
params
|
|
81163
82462
|
};
|
|
81164
|
-
pendingRequests.set(id, { resolve:
|
|
82463
|
+
pendingRequests.set(id, { resolve: resolve9, reject: reject2 });
|
|
81165
82464
|
setTimeout(() => {
|
|
81166
82465
|
if (pendingRequests.has(id)) {
|
|
81167
82466
|
pendingRequests.delete(id);
|
|
@@ -81224,7 +82523,7 @@ ${prompt}`;
|
|
|
81224
82523
|
const reqId = requestId + 1;
|
|
81225
82524
|
let fullResponse = "";
|
|
81226
82525
|
let gotSessionId = false;
|
|
81227
|
-
const eventPromise = new Promise((
|
|
82526
|
+
const eventPromise = new Promise((resolve9) => {
|
|
81228
82527
|
eventHandlers.set(reqId, (eventParams) => {
|
|
81229
82528
|
const msg = eventParams.msg;
|
|
81230
82529
|
if (msg.type === "session_configured" && msg.session_id && !gotSessionId) {
|
|
@@ -81244,7 +82543,7 @@ ${prompt}`;
|
|
|
81244
82543
|
});
|
|
81245
82544
|
setTimeout(() => {
|
|
81246
82545
|
eventHandlers.delete(reqId);
|
|
81247
|
-
|
|
82546
|
+
resolve9();
|
|
81248
82547
|
}, 6e5);
|
|
81249
82548
|
});
|
|
81250
82549
|
const resultPromise = sendRequest("tools/call", {
|
|
@@ -81434,7 +82733,7 @@ import { randomUUID as randomUUID6 } from "crypto";
|
|
|
81434
82733
|
import { EventEmitter as EventEmitter5 } from "events";
|
|
81435
82734
|
import { existsSync as existsSync6 } from "fs";
|
|
81436
82735
|
import { readFile as readFile3, stat, readdir as readdir3 } from "fs/promises";
|
|
81437
|
-
import { resolve as
|
|
82736
|
+
import { resolve as resolve7, isAbsolute as isAbsolute6, dirname as dirname5, basename, normalize as normalize2, sep as sep5 } from "path";
|
|
81438
82737
|
function extractWrappedToolName(wrappedToolError) {
|
|
81439
82738
|
if (!wrappedToolError || typeof wrappedToolError !== "string") {
|
|
81440
82739
|
return "unknown";
|
|
@@ -81466,6 +82765,7 @@ var init_ProbeAgent = __esm({
|
|
|
81466
82765
|
init_imageConfig();
|
|
81467
82766
|
init_tools2();
|
|
81468
82767
|
init_common();
|
|
82768
|
+
init_fileTracker();
|
|
81469
82769
|
init_probeTool();
|
|
81470
82770
|
init_mockProvider();
|
|
81471
82771
|
init_index();
|
|
@@ -81507,7 +82807,7 @@ var init_ProbeAgent = __esm({
|
|
|
81507
82807
|
* @param {string} [options.customPrompt] - Custom prompt to replace the default system message
|
|
81508
82808
|
* @param {string} [options.systemPrompt] - Alias for customPrompt; takes precedence when both are provided
|
|
81509
82809
|
* @param {string} [options.promptType] - Predefined prompt type (code-explorer, code-searcher, architect, code-review, support)
|
|
81510
|
-
* @param {boolean} [options.allowEdit=false] - Allow the use of the '
|
|
82810
|
+
* @param {boolean} [options.allowEdit=false] - Allow the use of the 'edit' and 'create' tools
|
|
81511
82811
|
* @param {boolean} [options.enableDelegate=false] - Enable the delegate tool for task distribution to subagents
|
|
81512
82812
|
* @param {boolean} [options.enableExecutePlan=false] - Enable the execute_plan DSL orchestration tool
|
|
81513
82813
|
* @param {string} [options.architectureFileName] - Architecture context filename to embed from repo root (defaults to AGENTS.md with CLAUDE.md fallback; ARCHITECTURE.md is always included when present)
|
|
@@ -81556,6 +82856,7 @@ var init_ProbeAgent = __esm({
|
|
|
81556
82856
|
this.customPrompt = options.systemPrompt || options.customPrompt || null;
|
|
81557
82857
|
this.promptType = options.promptType || "code-explorer";
|
|
81558
82858
|
this.allowEdit = !!options.allowEdit;
|
|
82859
|
+
this.hashLines = options.hashLines !== void 0 ? !!options.hashLines : this.allowEdit;
|
|
81559
82860
|
this.enableDelegate = !!options.enableDelegate;
|
|
81560
82861
|
this.enableExecutePlan = !!options.enableExecutePlan;
|
|
81561
82862
|
this.debug = options.debug || process.env.DEBUG === "1";
|
|
@@ -81617,7 +82918,8 @@ var init_ProbeAgent = __esm({
|
|
|
81617
82918
|
if (this.debug) {
|
|
81618
82919
|
console.log(`[DEBUG] Generated session ID for agent: ${this.sessionId}`);
|
|
81619
82920
|
console.log(`[DEBUG] Maximum tool iterations configured: ${MAX_TOOL_ITERATIONS}`);
|
|
81620
|
-
console.log(`[DEBUG] Allow Edit
|
|
82921
|
+
console.log(`[DEBUG] Allow Edit: ${this.allowEdit}`);
|
|
82922
|
+
console.log(`[DEBUG] Hash Lines: ${this.hashLines}`);
|
|
81621
82923
|
console.log(`[DEBUG] Search delegation enabled: ${this.searchDelegate}`);
|
|
81622
82924
|
console.log(`[DEBUG] Workspace root: ${this.workspaceRoot}`);
|
|
81623
82925
|
console.log(`[DEBUG] Working directory (cwd): ${this.cwd}`);
|
|
@@ -82002,9 +83304,12 @@ var init_ProbeAgent = __esm({
|
|
|
82002
83304
|
cwd: this.cwd,
|
|
82003
83305
|
workspaceRoot: this.workspaceRoot,
|
|
82004
83306
|
allowedFolders: this.allowedFolders,
|
|
83307
|
+
// File state tracking for safe multi-edit workflows (only when editing is enabled)
|
|
83308
|
+
fileTracker: this.allowEdit ? new FileTracker({ debug: this.debug }) : null,
|
|
82005
83309
|
outline: this.outline,
|
|
82006
83310
|
searchDelegate: this.searchDelegate,
|
|
82007
83311
|
allowEdit: this.allowEdit,
|
|
83312
|
+
hashLines: this.hashLines,
|
|
82008
83313
|
enableDelegate: this.enableDelegate,
|
|
82009
83314
|
enableExecutePlan: this.enableExecutePlan,
|
|
82010
83315
|
enableBash: this.enableBash,
|
|
@@ -82789,7 +84094,7 @@ var init_ProbeAgent = __esm({
|
|
|
82789
84094
|
let resolvedPath = imagePath;
|
|
82790
84095
|
if (!imagePath.includes("/") && !imagePath.includes("\\")) {
|
|
82791
84096
|
for (const dir of listFilesDirectories) {
|
|
82792
|
-
const potentialPath =
|
|
84097
|
+
const potentialPath = resolve7(dir, imagePath);
|
|
82793
84098
|
const loaded = await this.loadImageIfValid(potentialPath);
|
|
82794
84099
|
if (loaded) {
|
|
82795
84100
|
if (this.debug) {
|
|
@@ -82859,8 +84164,8 @@ var init_ProbeAgent = __esm({
|
|
|
82859
84164
|
const allowedDirs = this.allowedFolders && this.allowedFolders.length > 0 ? this.allowedFolders : [process.cwd()];
|
|
82860
84165
|
let absolutePath;
|
|
82861
84166
|
let isPathAllowed2 = false;
|
|
82862
|
-
if (
|
|
82863
|
-
absolutePath = safeRealpath(
|
|
84167
|
+
if (isAbsolute6(imagePath)) {
|
|
84168
|
+
absolutePath = safeRealpath(resolve7(imagePath));
|
|
82864
84169
|
isPathAllowed2 = allowedDirs.some((dir) => {
|
|
82865
84170
|
const resolvedDir = safeRealpath(dir);
|
|
82866
84171
|
return absolutePath === resolvedDir || absolutePath.startsWith(resolvedDir + sep5);
|
|
@@ -82868,7 +84173,7 @@ var init_ProbeAgent = __esm({
|
|
|
82868
84173
|
} else {
|
|
82869
84174
|
for (const dir of allowedDirs) {
|
|
82870
84175
|
const resolvedDir = safeRealpath(dir);
|
|
82871
|
-
const resolvedPath = safeRealpath(
|
|
84176
|
+
const resolvedPath = safeRealpath(resolve7(dir, imagePath));
|
|
82872
84177
|
if (resolvedPath === resolvedDir || resolvedPath.startsWith(resolvedDir + sep5)) {
|
|
82873
84178
|
absolutePath = resolvedPath;
|
|
82874
84179
|
isPathAllowed2 = true;
|
|
@@ -83058,7 +84363,7 @@ var init_ProbeAgent = __esm({
|
|
|
83058
84363
|
let guidanceCandidates = [];
|
|
83059
84364
|
if (hasConfiguredName) {
|
|
83060
84365
|
const targetName = basename(configuredName);
|
|
83061
|
-
if (configuredName !== targetName || configuredName.includes("/") || configuredName.includes("\\") || configuredName.includes("..") ||
|
|
84366
|
+
if (configuredName !== targetName || configuredName.includes("/") || configuredName.includes("\\") || configuredName.includes("..") || isAbsolute6(configuredName)) {
|
|
83062
84367
|
console.warn(`[WARN] Invalid architectureFileName (must be a simple filename): ${configuredName}`);
|
|
83063
84368
|
} else if (targetName) {
|
|
83064
84369
|
const targetLower = targetName.toLowerCase();
|
|
@@ -83125,7 +84430,7 @@ var init_ProbeAgent = __esm({
|
|
|
83125
84430
|
pushEntry(architectureMatch);
|
|
83126
84431
|
const contexts = [];
|
|
83127
84432
|
for (const entry of uniqueEntries) {
|
|
83128
|
-
const filePath =
|
|
84433
|
+
const filePath = resolve7(rootDirectory, entry.name);
|
|
83129
84434
|
try {
|
|
83130
84435
|
const content = await readFile3(filePath, "utf8");
|
|
83131
84436
|
let kind = "other";
|
|
@@ -83190,10 +84495,10 @@ ${this.architectureContext.content}
|
|
|
83190
84495
|
}
|
|
83191
84496
|
_getSkillsRepoRoot() {
|
|
83192
84497
|
if (this.workspaceRoot) {
|
|
83193
|
-
return
|
|
84498
|
+
return resolve7(this.workspaceRoot);
|
|
83194
84499
|
}
|
|
83195
84500
|
if (this.allowedFolders && this.allowedFolders.length > 0) {
|
|
83196
|
-
return
|
|
84501
|
+
return resolve7(this.allowedFolders[0]);
|
|
83197
84502
|
}
|
|
83198
84503
|
return process.cwd();
|
|
83199
84504
|
}
|
|
@@ -83382,10 +84687,6 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
83382
84687
|
}
|
|
83383
84688
|
if (isToolAllowed("readImage")) {
|
|
83384
84689
|
toolDefinitions += `${readImageToolDefinition}
|
|
83385
|
-
`;
|
|
83386
|
-
}
|
|
83387
|
-
if (this.allowEdit && isToolAllowed("implement")) {
|
|
83388
|
-
toolDefinitions += `${implementToolDefinition}
|
|
83389
84690
|
`;
|
|
83390
84691
|
}
|
|
83391
84692
|
if (this.allowEdit && isToolAllowed("edit")) {
|
|
@@ -83467,7 +84768,7 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
|
|
|
83467
84768
|
availableToolsList += "- query: Search code using structural AST patterns.\n";
|
|
83468
84769
|
}
|
|
83469
84770
|
if (isToolAllowed("extract")) {
|
|
83470
|
-
availableToolsList +=
|
|
84771
|
+
availableToolsList += '- extract: Extract specific code blocks or lines from files. Use with symbol targets (e.g. "file.js#funcName") to get line numbers for line-targeted editing.\n';
|
|
83471
84772
|
}
|
|
83472
84773
|
if (isToolAllowed("listFiles")) {
|
|
83473
84774
|
availableToolsList += "- listFiles: List files and directories in a specified location.\n";
|
|
@@ -83484,11 +84785,8 @@ The configuration is loaded from src/config.js lines 15-25 which contains the da
|
|
|
83484
84785
|
if (isToolAllowed("readImage")) {
|
|
83485
84786
|
availableToolsList += "- readImage: Read and load an image file for AI analysis.\n";
|
|
83486
84787
|
}
|
|
83487
|
-
if (this.allowEdit && isToolAllowed("implement")) {
|
|
83488
|
-
availableToolsList += "- implement: Implement a feature or fix a bug using aider.\n";
|
|
83489
|
-
}
|
|
83490
84788
|
if (this.allowEdit && isToolAllowed("edit")) {
|
|
83491
|
-
availableToolsList += "- edit: Edit files using
|
|
84789
|
+
availableToolsList += "- edit: Edit files using text replacement, AST-aware symbol operations, or line-targeted editing.\n";
|
|
83492
84790
|
}
|
|
83493
84791
|
if (this.allowEdit && isToolAllowed("create")) {
|
|
83494
84792
|
availableToolsList += "- create: Create new files with specified content.\n";
|
|
@@ -83576,8 +84874,14 @@ Follow these instructions carefully:
|
|
|
83576
84874
|
8. Once the task is fully completed, use the '<attempt_completion>' tool to provide the final result. This is the ONLY way to signal completion.
|
|
83577
84875
|
9. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results.${this.allowEdit ? `
|
|
83578
84876
|
10. When modifying files, choose the appropriate tool:
|
|
83579
|
-
- Use 'edit' for
|
|
83580
|
-
|
|
84877
|
+
- Use 'edit' for all code modifications:
|
|
84878
|
+
* For small changes (a line or a few lines), use old_string + new_string \u2014 copy old_string verbatim from the file.
|
|
84879
|
+
* For rewriting entire functions/classes/methods, use the symbol parameter instead (no exact text matching needed).
|
|
84880
|
+
* For editing specific lines from search/extract output, use start_line (and optionally end_line) with the line numbers shown in the output.${this.hashLines ? ' Line references include content hashes (e.g. "42:ab") for integrity verification.' : ""}
|
|
84881
|
+
* For editing inside large functions: first use extract with the symbol target (e.g. "file.js#myFunction") to see the function with line numbers${this.hashLines ? " and hashes" : ""}, then use start_line/end_line to surgically edit specific lines within it.
|
|
84882
|
+
- Use 'create' for new files or complete file rewrites.
|
|
84883
|
+
- If an edit fails, read the error message \u2014 it tells you exactly how to fix the call and retry.
|
|
84884
|
+
- The system tracks which files you've seen via search/extract. If you try to edit a file you haven't read, or one that changed since you last read it, the edit will fail with instructions to re-read first. Always use extract before editing to ensure you have current file content.` : ""}
|
|
83581
84885
|
</instructions>
|
|
83582
84886
|
`;
|
|
83583
84887
|
let systemMessage = "";
|
|
@@ -83803,8 +85107,8 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
83803
85107
|
let currentIteration = 0;
|
|
83804
85108
|
let completionAttempted = false;
|
|
83805
85109
|
let finalResult = "I was unable to complete your request due to reaching the maximum number of tool iterations.";
|
|
83806
|
-
const baseMaxIterations = this.maxIterations || MAX_TOOL_ITERATIONS;
|
|
83807
|
-
const maxIterations = options.schema ? baseMaxIterations + 4 : baseMaxIterations;
|
|
85110
|
+
const baseMaxIterations = options._maxIterationsOverride || this.maxIterations || MAX_TOOL_ITERATIONS;
|
|
85111
|
+
const maxIterations = options._maxIterationsOverride ? baseMaxIterations : options.schema ? baseMaxIterations + 4 : baseMaxIterations;
|
|
83808
85112
|
const isClaudeCode = this.clientApiProvider === "claude-code" || process.env.USE_CLAUDE_CODE === "true";
|
|
83809
85113
|
const isCodex = this.clientApiProvider === "codex" || process.env.USE_CODEX === "true";
|
|
83810
85114
|
if (isClaudeCode) {
|
|
@@ -84077,8 +85381,11 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
84077
85381
|
if (this.enableSkills && this.allowedTools.isEnabled("useSkill")) validTools.push("useSkill");
|
|
84078
85382
|
if (this.allowedTools.isEnabled("readImage")) validTools.push("readImage");
|
|
84079
85383
|
validTools.push("attempt_completion");
|
|
84080
|
-
if (this.allowEdit && this.allowedTools.isEnabled("
|
|
84081
|
-
validTools.push("
|
|
85384
|
+
if (this.allowEdit && this.allowedTools.isEnabled("edit")) {
|
|
85385
|
+
validTools.push("edit");
|
|
85386
|
+
}
|
|
85387
|
+
if (this.allowEdit && this.allowedTools.isEnabled("create")) {
|
|
85388
|
+
validTools.push("create");
|
|
84082
85389
|
}
|
|
84083
85390
|
if (this.enableBash && this.allowedTools.isEnabled("bash")) {
|
|
84084
85391
|
validTools.push("bash");
|
|
@@ -84300,7 +85607,7 @@ ${errorXml}
|
|
|
84300
85607
|
try {
|
|
84301
85608
|
let resolvedWorkingDirectory = this.workspaceRoot || this.cwd || this.allowedFolders && this.allowedFolders[0] || process.cwd();
|
|
84302
85609
|
if (params.workingDirectory) {
|
|
84303
|
-
const requestedDir = safeRealpath(
|
|
85610
|
+
const requestedDir = safeRealpath(isAbsolute6(params.workingDirectory) ? resolve7(params.workingDirectory) : resolve7(resolvedWorkingDirectory, params.workingDirectory));
|
|
84304
85611
|
const isWithinAllowed = !this.allowedFolders || this.allowedFolders.length === 0 || this.allowedFolders.some((folder) => {
|
|
84305
85612
|
const resolvedFolder = safeRealpath(folder);
|
|
84306
85613
|
return requestedDir === resolvedFolder || requestedDir.startsWith(resolvedFolder + sep5);
|
|
@@ -84371,6 +85678,8 @@ ${errorXml}
|
|
|
84371
85678
|
// Inherit bash enablement
|
|
84372
85679
|
bashConfig: this.bashConfig,
|
|
84373
85680
|
// Inherit bash configuration
|
|
85681
|
+
allowEdit: this.allowEdit,
|
|
85682
|
+
// Inherit edit/create permission
|
|
84374
85683
|
allowedTools: allowedToolsForDelegate,
|
|
84375
85684
|
// Inherit allowed tools from parent
|
|
84376
85685
|
debug: this.debug,
|
|
@@ -85036,13 +86345,16 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
85036
86345
|
options.schema,
|
|
85037
86346
|
0
|
|
85038
86347
|
);
|
|
86348
|
+
const { schema: _unusedSchema1, ...schemaDefCorrectionOptions } = options;
|
|
85039
86349
|
finalResult = await this.answer(schemaDefinitionPrompt, [], {
|
|
85040
|
-
...
|
|
86350
|
+
...schemaDefCorrectionOptions,
|
|
85041
86351
|
_schemaFormatted: true,
|
|
85042
86352
|
_skipValidation: true,
|
|
85043
86353
|
// Skip validation in recursive correction calls to prevent loops
|
|
85044
|
-
_completionPromptProcessed: true
|
|
86354
|
+
_completionPromptProcessed: true,
|
|
85045
86355
|
// Prevent cascading completion prompts in retry calls
|
|
86356
|
+
_maxIterationsOverride: 3
|
|
86357
|
+
// Correction should complete in 1-2 iterations (issue #447)
|
|
85046
86358
|
});
|
|
85047
86359
|
finalResult = cleanSchemaResponse(finalResult);
|
|
85048
86360
|
validation = validateJsonResponse(finalResult);
|
|
@@ -85090,15 +86402,18 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
85090
86402
|
retryCount
|
|
85091
86403
|
);
|
|
85092
86404
|
}
|
|
86405
|
+
const { schema: _unusedSchema2, ...correctionOptions } = options;
|
|
85093
86406
|
finalResult = await this.answer(correctionPrompt, [], {
|
|
85094
|
-
...
|
|
86407
|
+
...correctionOptions,
|
|
85095
86408
|
_schemaFormatted: true,
|
|
85096
86409
|
_skipValidation: true,
|
|
85097
86410
|
// Skip validation in recursive correction calls to prevent loops
|
|
85098
86411
|
_disableTools: true,
|
|
85099
86412
|
// Only allow attempt_completion - prevent AI from using search/query tools
|
|
85100
|
-
_completionPromptProcessed: true
|
|
86413
|
+
_completionPromptProcessed: true,
|
|
85101
86414
|
// Prevent cascading completion prompts in retry calls
|
|
86415
|
+
_maxIterationsOverride: 3
|
|
86416
|
+
// Correction should complete in 1-2 iterations (issue #447)
|
|
85102
86417
|
});
|
|
85103
86418
|
finalResult = cleanSchemaResponse(finalResult);
|
|
85104
86419
|
validation = validateJsonResponse(finalResult, { debug: this.debug });
|
|
@@ -85527,7 +86842,7 @@ import {
|
|
|
85527
86842
|
McpError
|
|
85528
86843
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
85529
86844
|
import { readFileSync as readFileSync2, existsSync as existsSync7 } from "fs";
|
|
85530
|
-
import { resolve as
|
|
86845
|
+
import { resolve as resolve8 } from "path";
|
|
85531
86846
|
|
|
85532
86847
|
// src/agent/acp/server.js
|
|
85533
86848
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
@@ -85786,8 +87101,8 @@ var ACPConnection = class extends EventEmitter6 {
|
|
|
85786
87101
|
if (params !== null) {
|
|
85787
87102
|
message.params = params;
|
|
85788
87103
|
}
|
|
85789
|
-
return new Promise((
|
|
85790
|
-
this.pendingRequests.set(id, { resolve:
|
|
87104
|
+
return new Promise((resolve9, reject2) => {
|
|
87105
|
+
this.pendingRequests.set(id, { resolve: resolve9, reject: reject2 });
|
|
85791
87106
|
this.sendMessage(message);
|
|
85792
87107
|
setTimeout(() => {
|
|
85793
87108
|
if (this.pendingRequests.has(id)) {
|
|
@@ -86201,7 +87516,7 @@ dotenv3.config();
|
|
|
86201
87516
|
function readInputContent(input) {
|
|
86202
87517
|
if (!input) return null;
|
|
86203
87518
|
try {
|
|
86204
|
-
const resolvedPath =
|
|
87519
|
+
const resolvedPath = resolve8(input);
|
|
86205
87520
|
if (existsSync7(resolvedPath)) {
|
|
86206
87521
|
return readFileSync2(resolvedPath, "utf-8").trim();
|
|
86207
87522
|
}
|
|
@@ -86210,7 +87525,7 @@ function readInputContent(input) {
|
|
|
86210
87525
|
return input;
|
|
86211
87526
|
}
|
|
86212
87527
|
function readFromStdin() {
|
|
86213
|
-
return new Promise((
|
|
87528
|
+
return new Promise((resolve9, reject2) => {
|
|
86214
87529
|
let data2 = "";
|
|
86215
87530
|
let hasReceivedData = false;
|
|
86216
87531
|
let dataChunks = [];
|
|
@@ -86235,7 +87550,7 @@ function readFromStdin() {
|
|
|
86235
87550
|
if (!trimmed && dataChunks.length === 0) {
|
|
86236
87551
|
reject2(new Error("No input received from stdin"));
|
|
86237
87552
|
} else {
|
|
86238
|
-
|
|
87553
|
+
resolve9(trimmed);
|
|
86239
87554
|
}
|
|
86240
87555
|
});
|
|
86241
87556
|
process.stdin.on("error", (error) => {
|
|
@@ -86267,7 +87582,8 @@ function parseArgs() {
|
|
|
86267
87582
|
schema: null,
|
|
86268
87583
|
provider: null,
|
|
86269
87584
|
model: null,
|
|
86270
|
-
allowEdit: false,
|
|
87585
|
+
allowEdit: process.env.ALLOW_EDIT === "1" || false,
|
|
87586
|
+
hashLines: process.env.HASH_LINES !== void 0 ? process.env.HASH_LINES === "1" : void 0,
|
|
86271
87587
|
enableDelegate: false,
|
|
86272
87588
|
verbose: false,
|
|
86273
87589
|
help: false,
|
|
@@ -86316,6 +87632,10 @@ function parseArgs() {
|
|
|
86316
87632
|
config.verbose = true;
|
|
86317
87633
|
} else if (arg === "--allow-edit") {
|
|
86318
87634
|
config.allowEdit = true;
|
|
87635
|
+
} else if (arg === "--hash-lines") {
|
|
87636
|
+
config.hashLines = true;
|
|
87637
|
+
} else if (arg === "--no-hash-lines") {
|
|
87638
|
+
config.hashLines = false;
|
|
86319
87639
|
} else if (arg === "--enable-delegate") {
|
|
86320
87640
|
config.enableDelegate = true;
|
|
86321
87641
|
} else if (arg === "--no-delegate") {
|
|
@@ -86412,12 +87732,14 @@ Options:
|
|
|
86412
87732
|
--schema <schema|file> Output schema (JSON, XML, any format - text or file path)
|
|
86413
87733
|
--provider <name> Force AI provider: anthropic, openai, google
|
|
86414
87734
|
--model <name> Override model name
|
|
86415
|
-
--allow-edit Enable code modification capabilities
|
|
87735
|
+
--allow-edit Enable code modification capabilities (edit + create tools)
|
|
87736
|
+
--hash-lines Annotate search/extract output with line hashes (default: on when --allow-edit)
|
|
87737
|
+
--no-hash-lines Disable line hash annotations even with --allow-edit
|
|
86416
87738
|
--enable-delegate Enable delegate tool for task distribution to subagents
|
|
86417
87739
|
--allowed-tools <tools> Filter available tools (comma-separated list)
|
|
86418
87740
|
Use '*' or 'all' for all tools (default)
|
|
86419
87741
|
Use 'none' or '' for no tools (raw AI mode)
|
|
86420
|
-
Specific tools: search,query,extract,listFiles,searchFiles,listSkills,useSkill
|
|
87742
|
+
Specific tools: search,query,extract,edit,create,listFiles,searchFiles,listSkills,useSkill
|
|
86421
87743
|
Supports exclusion: '*,!bash' (all except bash)
|
|
86422
87744
|
--disable-tools Disable all tools (raw AI mode, no code analysis)
|
|
86423
87745
|
Convenience flag equivalent to --allowed-tools none
|
|
@@ -86455,6 +87777,8 @@ Environment Variables:
|
|
|
86455
87777
|
FORCE_PROVIDER Force specific provider (anthropic, openai, google)
|
|
86456
87778
|
MODEL_NAME Override model name
|
|
86457
87779
|
MAX_RESPONSE_TOKENS Maximum tokens for AI response
|
|
87780
|
+
ALLOW_EDIT Enable code modification (set to '1')
|
|
87781
|
+
HASH_LINES Annotate output with line hashes (set to '1'; default: on with ALLOW_EDIT)
|
|
86458
87782
|
DEBUG Enable verbose mode (set to '1')
|
|
86459
87783
|
|
|
86460
87784
|
Examples:
|
|
@@ -86471,6 +87795,8 @@ Examples:
|
|
|
86471
87795
|
probe agent "Explain this code" --allowed-tools search,extract # Only search and extract
|
|
86472
87796
|
probe agent "What is this project about?" --allowed-tools none # Raw AI mode (no tools)
|
|
86473
87797
|
probe agent "Tell me about this project" --disable-tools # Raw AI mode (convenience flag)
|
|
87798
|
+
probe agent "Fix the off-by-one error" --allow-edit --path ./src # Enable code editing
|
|
87799
|
+
ALLOW_EDIT=1 probe agent "Refactor the login flow" # Edit via env var
|
|
86474
87800
|
probe agent --mcp # Start MCP server mode
|
|
86475
87801
|
probe agent --acp # Start ACP server mode
|
|
86476
87802
|
|
|
@@ -86706,7 +88032,7 @@ Please reformat your previous response to match this schema exactly. Only return
|
|
|
86706
88032
|
if (!validation.isValid) {
|
|
86707
88033
|
const correctionPrompt = createJsonCorrectionPrompt(result, schema, validation.error);
|
|
86708
88034
|
try {
|
|
86709
|
-
result = await agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true, _disableTools: true });
|
|
88035
|
+
result = await agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true, _disableTools: true, _maxIterationsOverride: 3 });
|
|
86710
88036
|
result = cleanSchemaResponse(result);
|
|
86711
88037
|
const finalValidation = validateJsonResponse(result);
|
|
86712
88038
|
if (!finalValidation.isValid && args.debug) {
|
|
@@ -86997,11 +88323,11 @@ Please reformat your previous response to match this schema exactly. Only return
|
|
|
86997
88323
|
if (appTracer) {
|
|
86998
88324
|
result = await appTracer.withSpan(
|
|
86999
88325
|
"agent.json_correction",
|
|
87000
|
-
() => agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true, _disableTools: true }),
|
|
88326
|
+
() => agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true, _disableTools: true, _maxIterationsOverride: 3 }),
|
|
87001
88327
|
{ "original_error": validation.error }
|
|
87002
88328
|
);
|
|
87003
88329
|
} else {
|
|
87004
|
-
result = await agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true, _disableTools: true });
|
|
88330
|
+
result = await agent.answer(correctionPrompt, [], { schema, _schemaFormatted: true, _disableTools: true, _maxIterationsOverride: 3 });
|
|
87005
88331
|
}
|
|
87006
88332
|
result = cleanSchemaResponse(result);
|
|
87007
88333
|
const finalValidation = validateJsonResponse(result);
|