@cortexkit/aft-pi 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +701 -284
- package/dist/tool-registration.d.ts +33 -0
- package/dist/tool-registration.d.ts.map +1 -0
- package/dist/tools/_shared.d.ts +5 -3
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/ast.d.ts +1 -1
- package/dist/tools/ast.d.ts.map +1 -1
- package/dist/tools/conflicts.d.ts.map +1 -1
- package/dist/tools/fs.d.ts +1 -1
- package/dist/tools/fs.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts +2 -2
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/imports.d.ts +1 -1
- package/dist/tools/imports.d.ts.map +1 -1
- package/dist/tools/inspect.d.ts.map +1 -1
- package/dist/tools/navigate.d.ts +2 -2
- package/dist/tools/navigate.d.ts.map +1 -1
- package/dist/tools/reading.d.ts +4 -4
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/refactor.d.ts +1 -1
- package/dist/tools/refactor.d.ts.map +1 -1
- package/dist/tools/safety.d.ts +1 -1
- package/dist/tools/safety.d.ts.map +1 -1
- package/dist/tools/semantic.d.ts +0 -1
- package/dist/tools/semantic.d.ts.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -9623,14 +9623,6 @@ function coerceStringArray(value) {
|
|
|
9623
9623
|
}
|
|
9624
9624
|
return [];
|
|
9625
9625
|
}
|
|
9626
|
-
function coerceAliasedStringParam(value, aliasValue) {
|
|
9627
|
-
if (value !== undefined) {
|
|
9628
|
-
return typeof value === "string" ? value : undefined;
|
|
9629
|
-
}
|
|
9630
|
-
if (typeof aliasValue !== "string")
|
|
9631
|
-
return;
|
|
9632
|
-
return aliasValue.trim().length > 0 ? aliasValue : undefined;
|
|
9633
|
-
}
|
|
9634
9626
|
function coerceTargetParam(value) {
|
|
9635
9627
|
if (Array.isArray(value)) {
|
|
9636
9628
|
return value.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
@@ -10185,6 +10177,23 @@ ${reflowText}`;
|
|
|
10185
10177
|
}
|
|
10186
10178
|
return " Auto-formatted.";
|
|
10187
10179
|
}
|
|
10180
|
+
// ../aft-bridge/dist/error-contract.js
|
|
10181
|
+
class AftToolError extends Error {
|
|
10182
|
+
code;
|
|
10183
|
+
response;
|
|
10184
|
+
constructor(message, code, response) {
|
|
10185
|
+
const cause = { code, message, response };
|
|
10186
|
+
super(message, { cause });
|
|
10187
|
+
this.name = "AftToolError";
|
|
10188
|
+
this.code = code;
|
|
10189
|
+
this.response = response;
|
|
10190
|
+
}
|
|
10191
|
+
}
|
|
10192
|
+
function toolErrorFromResponse(command, response) {
|
|
10193
|
+
const code = typeof response.code === "string" && response.code.length > 0 ? response.code : "unknown_error";
|
|
10194
|
+
const message = typeof response.message === "string" && response.message.length > 0 ? response.message : `${command} failed`;
|
|
10195
|
+
return new AftToolError(message, code, response);
|
|
10196
|
+
}
|
|
10188
10197
|
// ../aft-bridge/dist/format.js
|
|
10189
10198
|
function compressionSavingsPercent(original, compressed) {
|
|
10190
10199
|
if (original <= 0)
|
|
@@ -11807,6 +11816,420 @@ function isProcessAlive(pid) {
|
|
|
11807
11816
|
return true;
|
|
11808
11817
|
}
|
|
11809
11818
|
}
|
|
11819
|
+
// ../aft-bridge/dist/path-aliases.js
|
|
11820
|
+
class InvalidRequestError extends AftToolError {
|
|
11821
|
+
constructor(message) {
|
|
11822
|
+
super(message, "invalid_request", {
|
|
11823
|
+
success: false,
|
|
11824
|
+
code: "invalid_request",
|
|
11825
|
+
message
|
|
11826
|
+
});
|
|
11827
|
+
this.name = "InvalidRequestError";
|
|
11828
|
+
}
|
|
11829
|
+
}
|
|
11830
|
+
function isWellFormedUnicodeString(value) {
|
|
11831
|
+
for (let index = 0;index < value.length; index++) {
|
|
11832
|
+
const codeUnit = value.charCodeAt(index);
|
|
11833
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
11834
|
+
const next = value.charCodeAt(index + 1);
|
|
11835
|
+
if (next < 56320 || next > 57343 || Number.isNaN(next))
|
|
11836
|
+
return false;
|
|
11837
|
+
index++;
|
|
11838
|
+
} else if (codeUnit >= 56320 && codeUnit <= 57343) {
|
|
11839
|
+
return false;
|
|
11840
|
+
}
|
|
11841
|
+
}
|
|
11842
|
+
return true;
|
|
11843
|
+
}
|
|
11844
|
+
function hasOwn(record, key) {
|
|
11845
|
+
return Object.hasOwn(record, key);
|
|
11846
|
+
}
|
|
11847
|
+
function invalidPathValue(property) {
|
|
11848
|
+
throw new InvalidRequestError(`'${property}' must be a non-empty well-formed Unicode string`);
|
|
11849
|
+
}
|
|
11850
|
+
function pathValue(record, property) {
|
|
11851
|
+
const value = record[property];
|
|
11852
|
+
if (typeof value !== "string" || value.length === 0 || !isWellFormedUnicodeString(value)) {
|
|
11853
|
+
invalidPathValue(property);
|
|
11854
|
+
}
|
|
11855
|
+
return value;
|
|
11856
|
+
}
|
|
11857
|
+
function normalizeAliasPair(record, canonical, legacy, required) {
|
|
11858
|
+
const hasCanonical = hasOwn(record, canonical);
|
|
11859
|
+
const hasLegacy = hasOwn(record, legacy);
|
|
11860
|
+
if (!hasCanonical && !hasLegacy) {
|
|
11861
|
+
if (required) {
|
|
11862
|
+
throw new InvalidRequestError(`'${canonical}' is required`);
|
|
11863
|
+
}
|
|
11864
|
+
return;
|
|
11865
|
+
}
|
|
11866
|
+
if (hasCanonical && hasLegacy) {
|
|
11867
|
+
let canonicalValue;
|
|
11868
|
+
let legacyValue;
|
|
11869
|
+
try {
|
|
11870
|
+
canonicalValue = pathValue(record, canonical);
|
|
11871
|
+
legacyValue = pathValue(record, legacy);
|
|
11872
|
+
} catch {
|
|
11873
|
+
throw new InvalidRequestError(`Invalid request: '${canonical}' and '${legacy}' must both be non-empty well-formed Unicode strings`);
|
|
11874
|
+
}
|
|
11875
|
+
if (canonicalValue !== legacyValue) {
|
|
11876
|
+
throw new InvalidRequestError(`Invalid request: '${canonical}' and '${legacy}' must contain equal decoded strings`);
|
|
11877
|
+
}
|
|
11878
|
+
delete record[legacy];
|
|
11879
|
+
return;
|
|
11880
|
+
}
|
|
11881
|
+
if (hasCanonical) {
|
|
11882
|
+
pathValue(record, canonical);
|
|
11883
|
+
return;
|
|
11884
|
+
}
|
|
11885
|
+
record[canonical] = pathValue(record, legacy);
|
|
11886
|
+
delete record[legacy];
|
|
11887
|
+
}
|
|
11888
|
+
function validateOptionalCanonicalPath(record, property) {
|
|
11889
|
+
if (hasOwn(record, property))
|
|
11890
|
+
pathValue(record, property);
|
|
11891
|
+
}
|
|
11892
|
+
function normalizeZoomTargets(record) {
|
|
11893
|
+
if (!hasOwn(record, "targets"))
|
|
11894
|
+
return;
|
|
11895
|
+
const targets = record.targets;
|
|
11896
|
+
const normalizeTarget = (target, index) => {
|
|
11897
|
+
if (!target || typeof target !== "object" || Array.isArray(target)) {
|
|
11898
|
+
throw new InvalidRequestError(`'targets[${index}].path' must be a non-empty string`);
|
|
11899
|
+
}
|
|
11900
|
+
const source = target;
|
|
11901
|
+
const emptyTarget = source.symbol === "" && (hasOwn(source, "path") && source.path === "" || hasOwn(source, "filePath") && source.filePath === "");
|
|
11902
|
+
if (emptyTarget)
|
|
11903
|
+
return { ...source };
|
|
11904
|
+
const normalized = { ...source };
|
|
11905
|
+
try {
|
|
11906
|
+
normalizeAliasPair(normalized, "path", "filePath", true);
|
|
11907
|
+
} catch (error2) {
|
|
11908
|
+
if (error2 instanceof InvalidRequestError) {
|
|
11909
|
+
throw new InvalidRequestError(error2.message.replace("'filePath'", `'targets[${index}].filePath'`).replace("'path'", `'targets[${index}].path'`));
|
|
11910
|
+
}
|
|
11911
|
+
throw error2;
|
|
11912
|
+
}
|
|
11913
|
+
return normalized;
|
|
11914
|
+
};
|
|
11915
|
+
if (Array.isArray(targets)) {
|
|
11916
|
+
if (targets.length === 0)
|
|
11917
|
+
return;
|
|
11918
|
+
record.targets = targets.map(normalizeTarget);
|
|
11919
|
+
return;
|
|
11920
|
+
}
|
|
11921
|
+
if (targets && typeof targets === "object") {
|
|
11922
|
+
record.targets = normalizeTarget(targets, 0);
|
|
11923
|
+
}
|
|
11924
|
+
}
|
|
11925
|
+
function bareToolName(toolName) {
|
|
11926
|
+
const bare = toolName.startsWith("aft_") ? toolName.slice(4) : toolName;
|
|
11927
|
+
if (bare === "read" || bare === "write" || bare === "edit" || bare === "zoom" || bare === "callgraph" || bare === "safety" || bare === "move" || bare === "import" || bare === "refactor" || bare === "grep" || bare === "search" || bare === "conflicts") {
|
|
11928
|
+
return bare;
|
|
11929
|
+
}
|
|
11930
|
+
return;
|
|
11931
|
+
}
|
|
11932
|
+
function prepareCanonicalPathArguments(toolName, rawArguments) {
|
|
11933
|
+
if (!rawArguments || typeof rawArguments !== "object" || Array.isArray(rawArguments)) {
|
|
11934
|
+
throw new InvalidRequestError("tool arguments must be an object");
|
|
11935
|
+
}
|
|
11936
|
+
const tool = bareToolName(toolName);
|
|
11937
|
+
const record = { ...rawArguments };
|
|
11938
|
+
if (!tool)
|
|
11939
|
+
return record;
|
|
11940
|
+
switch (tool) {
|
|
11941
|
+
case "read":
|
|
11942
|
+
case "write":
|
|
11943
|
+
case "edit":
|
|
11944
|
+
case "move":
|
|
11945
|
+
case "import":
|
|
11946
|
+
case "refactor":
|
|
11947
|
+
normalizeAliasPair(record, "path", "filePath", true);
|
|
11948
|
+
break;
|
|
11949
|
+
case "zoom":
|
|
11950
|
+
normalizeAliasPair(record, "path", "filePath", false);
|
|
11951
|
+
normalizeZoomTargets(record);
|
|
11952
|
+
break;
|
|
11953
|
+
case "callgraph":
|
|
11954
|
+
normalizeAliasPair(record, "path", "filePath", true);
|
|
11955
|
+
normalizeAliasPair(record, "toPath", "toFile", false);
|
|
11956
|
+
break;
|
|
11957
|
+
case "safety":
|
|
11958
|
+
normalizeAliasPair(record, "path", "filePath", false);
|
|
11959
|
+
break;
|
|
11960
|
+
case "grep":
|
|
11961
|
+
case "search":
|
|
11962
|
+
case "conflicts":
|
|
11963
|
+
validateOptionalCanonicalPath(record, "path");
|
|
11964
|
+
break;
|
|
11965
|
+
}
|
|
11966
|
+
return record;
|
|
11967
|
+
}
|
|
11968
|
+
var EDIT_ROOT_COMPATIBILITY_KEYS = new Set([
|
|
11969
|
+
"oldString",
|
|
11970
|
+
"newString",
|
|
11971
|
+
"replaceAll",
|
|
11972
|
+
"occurrence"
|
|
11973
|
+
]);
|
|
11974
|
+
var EDIT_ROOT_CANONICAL_KEYS = new Set(["path", "appendContent", "edits", "symbol", "content"]);
|
|
11975
|
+
var EDIT_ITEM_KEYS = new Set([
|
|
11976
|
+
"oldString",
|
|
11977
|
+
"newString",
|
|
11978
|
+
"replaceAll",
|
|
11979
|
+
"occurrence",
|
|
11980
|
+
"startLine",
|
|
11981
|
+
"endLine",
|
|
11982
|
+
"content"
|
|
11983
|
+
]);
|
|
11984
|
+
var ASCII_WHITESPACE = /^[\t\n\v\f\r ]+$/;
|
|
11985
|
+
var ASCII_TRIM = /^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g;
|
|
11986
|
+
var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
|
|
11987
|
+
function prepareCanonicalEditArguments(toolName, rawArguments) {
|
|
11988
|
+
if (!rawArguments || typeof rawArguments !== "object" || Array.isArray(rawArguments)) {
|
|
11989
|
+
throw new InvalidRequestError("tool arguments must be an object");
|
|
11990
|
+
}
|
|
11991
|
+
const raw = rawArguments;
|
|
11992
|
+
const record = copyOwnProperties(raw);
|
|
11993
|
+
normalizeEditPathAlias(record);
|
|
11994
|
+
const suppliedLineFields = ["startLine", "endLine"].filter((key) => hasOwn(record, key));
|
|
11995
|
+
if (suppliedLineFields.length > 0) {
|
|
11996
|
+
throw new InvalidRequestError(`edit: top-level ${suppliedLineFields.map((key) => `'${key}'`).join(" and ")} are invalid; ` + "line-range fields are valid only inside 'edits[]'. " + "Use edits: [{ startLine, endLine, content }].");
|
|
11997
|
+
}
|
|
11998
|
+
const isOpenCodeRetiredBoundary = toolName === "aft_edit";
|
|
11999
|
+
const retiredFields = ["file", "mode"].filter((key) => hasOwn(record, key));
|
|
12000
|
+
if (retiredFields.length > 0 && isOpenCodeRetiredBoundary) {
|
|
12001
|
+
throw new InvalidRequestError("aft_edit: the retired `mode`/`file` edit form is no longer supported; use `path` with " + "exactly one of `appendContent`, `edits`, or `symbol` plus `content`.");
|
|
12002
|
+
}
|
|
12003
|
+
const unknownRootKeys = Object.getOwnPropertyNames(record).filter((key) => !EDIT_ROOT_CANONICAL_KEYS.has(key) && !EDIT_ROOT_COMPATIBILITY_KEYS.has(key) && key !== "filePath").sort();
|
|
12004
|
+
if (unknownRootKeys.length > 0) {
|
|
12005
|
+
throw new InvalidRequestError(formatUnknownKeys(unknownRootKeys));
|
|
12006
|
+
}
|
|
12007
|
+
const modes = editModesPresent(record);
|
|
12008
|
+
if (modes.length > 1) {
|
|
12009
|
+
throw new InvalidRequestError(`edit: conflicting modes: ${modes.join(", ")}`);
|
|
12010
|
+
}
|
|
12011
|
+
if (modes.length === 0) {
|
|
12012
|
+
throw new InvalidRequestError("edit: exactly one of `appendContent`, `edits`, or `symbol` plus `content` is required");
|
|
12013
|
+
}
|
|
12014
|
+
const mode = modes[0];
|
|
12015
|
+
if (mode === "appendContent") {
|
|
12016
|
+
if (typeof record.appendContent !== "string") {
|
|
12017
|
+
throw new InvalidRequestError("edit: 'appendContent' must be a string");
|
|
12018
|
+
}
|
|
12019
|
+
} else if (mode === "edits") {
|
|
12020
|
+
const parsedEdits = parseEditArray(record.edits);
|
|
12021
|
+
record.edits = parsedEdits.map((item, index) => normalizeEditItem(item, index));
|
|
12022
|
+
} else if (mode === "symbol/content") {
|
|
12023
|
+
if (!hasOwn(record, "symbol") || typeof record.symbol !== "string") {
|
|
12024
|
+
throw new InvalidRequestError("edit: 'symbol' must be a string when symbol mode is selected");
|
|
12025
|
+
}
|
|
12026
|
+
if (!hasOwn(record, "content") || typeof record.content !== "string") {
|
|
12027
|
+
throw new InvalidRequestError("edit: symbol mode requires both 'symbol' and 'content' string properties");
|
|
12028
|
+
}
|
|
12029
|
+
} else {
|
|
12030
|
+
const item = {};
|
|
12031
|
+
for (const key of EDIT_ROOT_COMPATIBILITY_KEYS) {
|
|
12032
|
+
if (hasOwn(record, key))
|
|
12033
|
+
item[key] = record[key];
|
|
12034
|
+
}
|
|
12035
|
+
record.edits = [normalizeEditItem(item, 0)];
|
|
12036
|
+
for (const key of EDIT_ROOT_COMPATIBILITY_KEYS)
|
|
12037
|
+
delete record[key];
|
|
12038
|
+
}
|
|
12039
|
+
validateEditPath(record);
|
|
12040
|
+
return record;
|
|
12041
|
+
}
|
|
12042
|
+
function normalizeEditPathAlias(record) {
|
|
12043
|
+
const hasCanonical = hasOwn(record, "path");
|
|
12044
|
+
const hasLegacy = hasOwn(record, "filePath");
|
|
12045
|
+
if (!hasCanonical && !hasLegacy)
|
|
12046
|
+
return;
|
|
12047
|
+
if (hasCanonical && hasLegacy) {
|
|
12048
|
+
let canonical;
|
|
12049
|
+
let legacy;
|
|
12050
|
+
try {
|
|
12051
|
+
canonical = pathValue(record, "path");
|
|
12052
|
+
legacy = pathValue(record, "filePath");
|
|
12053
|
+
} catch {
|
|
12054
|
+
throw new InvalidRequestError("Invalid request: 'path' and 'filePath' must both be non-empty well-formed Unicode strings");
|
|
12055
|
+
}
|
|
12056
|
+
if (canonical !== legacy) {
|
|
12057
|
+
throw new InvalidRequestError("Invalid request: 'path' and 'filePath' must contain equal decoded strings");
|
|
12058
|
+
}
|
|
12059
|
+
delete record.filePath;
|
|
12060
|
+
return;
|
|
12061
|
+
}
|
|
12062
|
+
if (!hasCanonical) {
|
|
12063
|
+
record.path = pathValue(record, "filePath");
|
|
12064
|
+
delete record.filePath;
|
|
12065
|
+
}
|
|
12066
|
+
}
|
|
12067
|
+
function validateEditPath(record) {
|
|
12068
|
+
if (!hasOwn(record, "path")) {
|
|
12069
|
+
throw new InvalidRequestError("'path' is required");
|
|
12070
|
+
}
|
|
12071
|
+
pathValue(record, "path");
|
|
12072
|
+
}
|
|
12073
|
+
function formatUnknownKeys(keys) {
|
|
12074
|
+
return `Unrecognized keys: ${keys.map((key) => `"${key}"`).join(", ")}`;
|
|
12075
|
+
}
|
|
12076
|
+
function editModesPresent(record) {
|
|
12077
|
+
const modes = [];
|
|
12078
|
+
if (hasOwn(record, "appendContent"))
|
|
12079
|
+
modes.push("appendContent");
|
|
12080
|
+
if (hasOwn(record, "edits"))
|
|
12081
|
+
modes.push("edits");
|
|
12082
|
+
if (hasOwn(record, "symbol") || hasOwn(record, "content"))
|
|
12083
|
+
modes.push("symbol/content");
|
|
12084
|
+
if (["oldString", "newString", "replaceAll", "occurrence"].some((key) => hasOwn(record, key))) {
|
|
12085
|
+
modes.push("oldString/newString");
|
|
12086
|
+
}
|
|
12087
|
+
return modes;
|
|
12088
|
+
}
|
|
12089
|
+
function parseEditArray(value) {
|
|
12090
|
+
if (typeof value === "string") {
|
|
12091
|
+
let parsed;
|
|
12092
|
+
try {
|
|
12093
|
+
parsed = JSON.parse(value);
|
|
12094
|
+
} catch {
|
|
12095
|
+
throw new InvalidRequestError("edit: 'edits' must contain valid JSON representing an array");
|
|
12096
|
+
}
|
|
12097
|
+
if (!Array.isArray(parsed)) {
|
|
12098
|
+
throw new InvalidRequestError("edit: 'edits' JSON must have an array root");
|
|
12099
|
+
}
|
|
12100
|
+
if (parsed.length === 0) {
|
|
12101
|
+
throw new InvalidRequestError("edit: 'edits' array must not be empty");
|
|
12102
|
+
}
|
|
12103
|
+
return parsed;
|
|
12104
|
+
}
|
|
12105
|
+
if (!Array.isArray(value)) {
|
|
12106
|
+
throw new InvalidRequestError("edit: 'edits' must be a non-empty array");
|
|
12107
|
+
}
|
|
12108
|
+
if (value.length === 0) {
|
|
12109
|
+
throw new InvalidRequestError("edit: 'edits' array must not be empty");
|
|
12110
|
+
}
|
|
12111
|
+
return value;
|
|
12112
|
+
}
|
|
12113
|
+
function normalizeEditItem(value, index) {
|
|
12114
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
12115
|
+
throw new InvalidRequestError(`edit: edits[${index}] must be an object`);
|
|
12116
|
+
}
|
|
12117
|
+
const source = value;
|
|
12118
|
+
const item = copyOwnProperties(source);
|
|
12119
|
+
normalizeItemAlias(item, "oldString", "oldText");
|
|
12120
|
+
normalizeItemAlias(item, "newString", "newText");
|
|
12121
|
+
const hasFindField = ["oldString", "newString", "replaceAll", "occurrence"].some((key) => hasOwn(item, key));
|
|
12122
|
+
const hasRangeField = ["startLine", "endLine", "content"].some((key) => hasOwn(item, key));
|
|
12123
|
+
if (hasFindField && hasRangeField) {
|
|
12124
|
+
throw new InvalidRequestError(`edit: edits[${index}] mixes find/replace and line-range fields`);
|
|
12125
|
+
}
|
|
12126
|
+
if (hasFindField) {
|
|
12127
|
+
if (!hasOwn(item, "oldString") || typeof item.oldString !== "string") {
|
|
12128
|
+
throw new InvalidRequestError(`edit: edits[${index}] requires string 'oldString'`);
|
|
12129
|
+
}
|
|
12130
|
+
if (hasOwn(item, "newString") && typeof item.newString !== "string") {
|
|
12131
|
+
throw new InvalidRequestError(`edit: edits[${index}].newString must be a string`);
|
|
12132
|
+
}
|
|
12133
|
+
coerceEditScalars(item, index);
|
|
12134
|
+
validateEditItemKeys(item, index);
|
|
12135
|
+
return item;
|
|
12136
|
+
}
|
|
12137
|
+
if (hasRangeField) {
|
|
12138
|
+
for (const key of ["startLine", "endLine"]) {
|
|
12139
|
+
const value2 = item[key];
|
|
12140
|
+
if (typeof value2 === "string" && /^[0-9]+$/.test(value2.trim())) {
|
|
12141
|
+
item[key] = Number(value2.trim());
|
|
12142
|
+
}
|
|
12143
|
+
if (!hasOwn(item, key) || !isPositiveSafeInteger(item[key])) {
|
|
12144
|
+
throw new InvalidRequestError(`edit: edits[${index}].${key} must be a positive integer`);
|
|
12145
|
+
}
|
|
12146
|
+
}
|
|
12147
|
+
if (item.startLine > item.endLine) {
|
|
12148
|
+
throw new InvalidRequestError(`edit: edits[${index}] requires startLine <= endLine`);
|
|
12149
|
+
}
|
|
12150
|
+
if (!hasOwn(item, "content") || typeof item.content !== "string") {
|
|
12151
|
+
throw new InvalidRequestError(`edit: edits[${index}] requires string 'content'`);
|
|
12152
|
+
}
|
|
12153
|
+
validateEditItemKeys(item, index);
|
|
12154
|
+
return item;
|
|
12155
|
+
}
|
|
12156
|
+
throw new InvalidRequestError(`edit: edits[${index}] must be a find/replace or line-range item`);
|
|
12157
|
+
}
|
|
12158
|
+
function normalizeItemAlias(item, canonical, legacy) {
|
|
12159
|
+
if (hasOwn(item, legacy)) {
|
|
12160
|
+
if (!hasOwn(item, canonical))
|
|
12161
|
+
item[canonical] = item[legacy];
|
|
12162
|
+
delete item[legacy];
|
|
12163
|
+
}
|
|
12164
|
+
}
|
|
12165
|
+
function validateEditItemKeys(item, index) {
|
|
12166
|
+
const unknown = Object.getOwnPropertyNames(item).filter((key) => !EDIT_ITEM_KEYS.has(key)).sort();
|
|
12167
|
+
if (unknown.length > 0) {
|
|
12168
|
+
throw new InvalidRequestError(`edit: edits[${index}] contains ${formatUnknownKeys(unknown)}`);
|
|
12169
|
+
}
|
|
12170
|
+
}
|
|
12171
|
+
function coerceEditScalars(item, index) {
|
|
12172
|
+
if (hasOwn(item, "replaceAll") && hasOwn(item, "occurrence")) {
|
|
12173
|
+
throw new InvalidRequestError(`edit: edits[${index}] cannot contain both 'replaceAll' and 'occurrence'`);
|
|
12174
|
+
}
|
|
12175
|
+
if (hasOwn(item, "replaceAll"))
|
|
12176
|
+
item.replaceAll = coerceEditBoolean(item.replaceAll, index);
|
|
12177
|
+
if (hasOwn(item, "occurrence")) {
|
|
12178
|
+
const occurrence = coerceEditOccurrence(item.occurrence, index);
|
|
12179
|
+
if (occurrence === undefined)
|
|
12180
|
+
delete item.occurrence;
|
|
12181
|
+
else
|
|
12182
|
+
item.occurrence = occurrence;
|
|
12183
|
+
}
|
|
12184
|
+
}
|
|
12185
|
+
function coerceEditBoolean(value, index) {
|
|
12186
|
+
if (typeof value === "boolean")
|
|
12187
|
+
return value;
|
|
12188
|
+
if (typeof value === "number" && Number.isFinite(value) && (value === 0 || value === 1)) {
|
|
12189
|
+
return value === 1;
|
|
12190
|
+
}
|
|
12191
|
+
if (typeof value === "string") {
|
|
12192
|
+
if (value === "1")
|
|
12193
|
+
return true;
|
|
12194
|
+
if (value === "0")
|
|
12195
|
+
return false;
|
|
12196
|
+
if (/^(?:true|false)$/i.test(value))
|
|
12197
|
+
return value.toLowerCase() === "true";
|
|
12198
|
+
}
|
|
12199
|
+
throw new InvalidRequestError(`edit: edits[${index}].replaceAll must be a boolean, true/false string, or 0/1`);
|
|
12200
|
+
}
|
|
12201
|
+
function coerceEditOccurrence(value, index) {
|
|
12202
|
+
if (value === null)
|
|
12203
|
+
return;
|
|
12204
|
+
if (typeof value === "string") {
|
|
12205
|
+
const trimmed = value.replace(ASCII_TRIM, "");
|
|
12206
|
+
if (trimmed.length === 0 || ASCII_WHITESPACE.test(trimmed))
|
|
12207
|
+
return;
|
|
12208
|
+
if (!/^[+]?[0-9]+$/.test(trimmed)) {
|
|
12209
|
+
throw new InvalidRequestError(`edit: edits[${index}].occurrence must be a positive integer`);
|
|
12210
|
+
}
|
|
12211
|
+
try {
|
|
12212
|
+
const parsed = BigInt(trimmed);
|
|
12213
|
+
if (parsed < 1n || parsed > BigInt(MAX_SAFE_INTEGER))
|
|
12214
|
+
throw new Error("out of range");
|
|
12215
|
+
return Number(parsed);
|
|
12216
|
+
} catch {
|
|
12217
|
+
throw new InvalidRequestError(`edit: edits[${index}].occurrence must be a positive integer`);
|
|
12218
|
+
}
|
|
12219
|
+
}
|
|
12220
|
+
if (typeof value === "number" && isPositiveSafeInteger(value))
|
|
12221
|
+
return value;
|
|
12222
|
+
throw new InvalidRequestError(`edit: edits[${index}].occurrence must be a positive integer`);
|
|
12223
|
+
}
|
|
12224
|
+
function isPositiveSafeInteger(value) {
|
|
12225
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
12226
|
+
}
|
|
12227
|
+
function copyOwnProperties(source) {
|
|
12228
|
+
const copy = Object.create(null);
|
|
12229
|
+
for (const key of Object.getOwnPropertyNames(source))
|
|
12230
|
+
copy[key] = source[key];
|
|
12231
|
+
return copy;
|
|
12232
|
+
}
|
|
11810
12233
|
// ../aft-bridge/dist/pool.js
|
|
11811
12234
|
import { homedir as homedir10 } from "node:os";
|
|
11812
12235
|
|
|
@@ -15795,7 +16218,7 @@ import {
|
|
|
15795
16218
|
// package.json
|
|
15796
16219
|
var package_default = {
|
|
15797
16220
|
name: "@cortexkit/aft-pi",
|
|
15798
|
-
version: "0.
|
|
16221
|
+
version: "0.49.0",
|
|
15799
16222
|
type: "module",
|
|
15800
16223
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
15801
16224
|
main: "dist/index.js",
|
|
@@ -15818,19 +16241,19 @@ var package_default = {
|
|
|
15818
16241
|
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
|
|
15819
16242
|
},
|
|
15820
16243
|
dependencies: {
|
|
15821
|
-
"@cortexkit/aft-bridge": "0.
|
|
16244
|
+
"@cortexkit/aft-bridge": "0.49.0",
|
|
15822
16245
|
"comment-json": "^5.0.0",
|
|
15823
16246
|
diff: "^8.0.4",
|
|
15824
16247
|
typebox: "1.1.38",
|
|
15825
16248
|
zod: "^4.4.3"
|
|
15826
16249
|
},
|
|
15827
16250
|
optionalDependencies: {
|
|
15828
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
15829
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
15830
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
15831
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
15832
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
15833
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
16251
|
+
"@cortexkit/aft-darwin-arm64": "0.49.0",
|
|
16252
|
+
"@cortexkit/aft-darwin-x64": "0.49.0",
|
|
16253
|
+
"@cortexkit/aft-linux-arm64": "0.49.0",
|
|
16254
|
+
"@cortexkit/aft-linux-x64": "0.49.0",
|
|
16255
|
+
"@cortexkit/aft-win32-arm64": "0.49.0",
|
|
16256
|
+
"@cortexkit/aft-win32-x64": "0.49.0"
|
|
15834
16257
|
},
|
|
15835
16258
|
devDependencies: {
|
|
15836
16259
|
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
@@ -16072,6 +16495,20 @@ import { Type } from "typebox";
|
|
|
16072
16495
|
var optionalInt = (min, max, description = "(integer)") => Type.Optional(Type.Union([Type.Integer({ minimum: min, maximum: max }), Type.String()], {
|
|
16073
16496
|
description
|
|
16074
16497
|
}));
|
|
16498
|
+
function withPathAliasPreparation(tool) {
|
|
16499
|
+
const existing = tool.prepareArguments;
|
|
16500
|
+
const prepare = (args) => {
|
|
16501
|
+
const prepared = tool.name === "edit" ? prepareCanonicalEditArguments(tool.name, args) : prepareCanonicalPathArguments(tool.name, args);
|
|
16502
|
+
return existing ? existing(prepared) : prepared;
|
|
16503
|
+
};
|
|
16504
|
+
return {
|
|
16505
|
+
...tool,
|
|
16506
|
+
prepareArguments: prepare,
|
|
16507
|
+
execute(toolCallId, params, signal, onUpdate, context) {
|
|
16508
|
+
return tool.execute(toolCallId, prepare(params), signal, onUpdate, context);
|
|
16509
|
+
}
|
|
16510
|
+
};
|
|
16511
|
+
}
|
|
16075
16512
|
function bridgeFor(ctx, cwd) {
|
|
16076
16513
|
if (!existsSync7(cwd)) {
|
|
16077
16514
|
throw new Error(`project directory no longer exists: ${cwd} (stale restored session?)`);
|
|
@@ -32037,7 +32474,7 @@ async function ensureServerInstalled(spec, config2, fetchImpl, signal) {
|
|
|
32037
32474
|
return null;
|
|
32038
32475
|
});
|
|
32039
32476
|
if (currentHash && currentHash !== installedMeta.sha256) {
|
|
32040
|
-
error2(`[lsp] ${spec.npm}@${version2}: TOFU sha256 mismatch — refusing to use ` + `tampered binary. Recorded ${installedMeta.sha256}, current ${currentHash}. ` + `Run \`aft doctor --clear\` to re-install from scratch.`);
|
|
32477
|
+
error2(`[lsp] ${spec.npm}@${version2}: TOFU sha256 mismatch — refusing to use ` + `tampered binary. Recorded ${installedMeta.sha256}, current ${currentHash}. ` + `Run \`npx @cortexkit/aft doctor --clear\` to re-install from scratch.`);
|
|
32041
32478
|
return {
|
|
32042
32479
|
started: false,
|
|
32043
32480
|
reason: `TOFU sha256 mismatch on ${spec.npm}@${version2} — see plugin log`
|
|
@@ -33517,29 +33954,25 @@ async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
|
|
|
33517
33954
|
throw new Error(`Blocked: '${absoluteTarget}' is outside the project root and restrict_to_project_root is ` + "enabled (AFT full isolation). Not overridable per-call; set restrict_to_project_root: false " + "in aft.jsonc to allow external paths.");
|
|
33518
33955
|
}
|
|
33519
33956
|
var ReadParams = Type2.Object({
|
|
33520
|
-
path: Type2.
|
|
33957
|
+
path: Type2.String({
|
|
33521
33958
|
description: "Path to the file to read (absolute or relative to project root)"
|
|
33522
|
-
})
|
|
33523
|
-
|
|
33524
|
-
|
|
33525
|
-
|
|
33526
|
-
offset: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line number to start reading from (use with limit)")
|
|
33527
|
-
limit: optionalInt(1, Number.MAX_SAFE_INTEGER, "Maximum number of lines to return")
|
|
33959
|
+
}),
|
|
33960
|
+
startLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line to start reading from"),
|
|
33961
|
+
endLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line to stop reading at (inclusive)"),
|
|
33962
|
+
limit: optionalInt(1, Number.MAX_SAFE_INTEGER, "Maximum number of lines to return"),
|
|
33963
|
+
offset: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line number to start reading from (use with limit)")
|
|
33528
33964
|
});
|
|
33529
33965
|
var WriteParams = Type2.Object({
|
|
33530
|
-
|
|
33966
|
+
path: Type2.String({
|
|
33531
33967
|
description: "Path to the file to write (absolute or relative to project root)"
|
|
33532
|
-
})
|
|
33533
|
-
path: Type2.Optional(Type2.String({
|
|
33534
|
-
description: "Alias for `filePath` — provide one of the two."
|
|
33535
|
-
})),
|
|
33968
|
+
}),
|
|
33536
33969
|
content: Type2.String({ description: "Full file contents to write" })
|
|
33537
33970
|
});
|
|
33538
33971
|
var BatchEditParams = Type2.Object({
|
|
33539
33972
|
oldString: Type2.Optional(Type2.String({ description: "Text to find for a batch find/replace edit" })),
|
|
33540
33973
|
newString: Type2.Optional(Type2.String({ description: "Replacement text for a batch find/replace edit" })),
|
|
33541
33974
|
replaceAll: Type2.Optional(Type2.Boolean({ description: "Replace every occurrence for this batch item" })),
|
|
33542
|
-
occurrence: optionalInt(
|
|
33975
|
+
occurrence: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based occurrence for this batch item (1 = first match)"),
|
|
33543
33976
|
startLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based start line for a batch line-range edit"),
|
|
33544
33977
|
endLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based end line for a batch line-range edit"),
|
|
33545
33978
|
content: Type2.Optional(Type2.String({
|
|
@@ -33547,21 +33980,19 @@ var BatchEditParams = Type2.Object({
|
|
|
33547
33980
|
}))
|
|
33548
33981
|
});
|
|
33549
33982
|
var EditParams = Type2.Object({
|
|
33550
|
-
|
|
33983
|
+
path: Type2.String({
|
|
33551
33984
|
description: "Path to the file to edit (absolute or relative to project root)"
|
|
33985
|
+
}),
|
|
33986
|
+
symbol: Type2.Optional(Type2.String({ description: "Named symbol to replace (function, class, type)" })),
|
|
33987
|
+
content: Type2.Optional(Type2.String({
|
|
33988
|
+
description: "Replacement content for symbol mode. For whole-file writes, use the `write` tool."
|
|
33552
33989
|
})),
|
|
33553
|
-
path: Type2.Optional(Type2.String({
|
|
33554
|
-
description: "Alias for `filePath` — provide one of the two."
|
|
33555
|
-
})),
|
|
33556
|
-
oldString: Type2.Optional(Type2.String({ description: "Text to find (exact match, fuzzy fallback)" })),
|
|
33557
|
-
newString: Type2.Optional(Type2.String({ description: "Replacement text (omit to delete match)" })),
|
|
33558
|
-
replaceAll: Type2.Optional(Type2.Boolean({ description: "Replace every occurrence" })),
|
|
33559
|
-
occurrence: optionalInt(0, Number.MAX_SAFE_INTEGER, "0-based occurrence to replace when multiple matches exist"),
|
|
33560
33990
|
appendContent: Type2.Optional(Type2.String({
|
|
33561
|
-
description: "Append text to the end of the file (creates the file if missing, parent dirs auto-created). When set,
|
|
33991
|
+
description: "Append text to the end of the file (creates the file if missing, parent dirs auto-created). When set, other edit modes are ignored."
|
|
33562
33992
|
})),
|
|
33563
33993
|
edits: Type2.Optional(Type2.Array(BatchEditParams, {
|
|
33564
|
-
|
|
33994
|
+
minItems: 1,
|
|
33995
|
+
description: "Batch edits — non-empty array of { oldString, newString }, { oldString, newString, replaceAll: true }, or { startLine, endLine, content } objects applied atomically."
|
|
33565
33996
|
}))
|
|
33566
33997
|
});
|
|
33567
33998
|
var GrepParams = Type2.Object({
|
|
@@ -33569,16 +34000,15 @@ var GrepParams = Type2.Object({
|
|
|
33569
34000
|
path: Type2.Optional(Type2.String({
|
|
33570
34001
|
description: "Path scope (file or directory; absolute or relative to project root)"
|
|
33571
34002
|
})),
|
|
33572
|
-
include: Type2.Optional(Type2.String({ description: "Glob filter for included files (e.g. '*.ts,*.tsx')" }))
|
|
33573
|
-
caseSensitive: Type2.Optional(Type2.Boolean({ description: "Case-sensitive matching" }))
|
|
34003
|
+
include: Type2.Optional(Type2.String({ description: "Glob filter for included files (e.g. '*.ts,*.tsx')" }))
|
|
33574
34004
|
});
|
|
33575
34005
|
function readPathArg(args) {
|
|
33576
|
-
return
|
|
34006
|
+
return typeof args.path === "string" ? args.path : undefined;
|
|
33577
34007
|
}
|
|
33578
34008
|
function mutationFilePathArg(args) {
|
|
33579
|
-
return
|
|
34009
|
+
return typeof args.path === "string" ? args.path : undefined;
|
|
33580
34010
|
}
|
|
33581
|
-
function
|
|
34011
|
+
function hasOwn2(record2, key) {
|
|
33582
34012
|
return Object.hasOwn(record2, key);
|
|
33583
34013
|
}
|
|
33584
34014
|
function validateBatchEdit(edit, index) {
|
|
@@ -33589,7 +34019,7 @@ function validateBatchEdit(edit, index) {
|
|
|
33589
34019
|
if (typeof record2.oldString === "string") {
|
|
33590
34020
|
return;
|
|
33591
34021
|
}
|
|
33592
|
-
if (
|
|
34022
|
+
if (hasOwn2(record2, "startLine")) {
|
|
33593
34023
|
if (typeof record2.startLine !== "number" || !Number.isInteger(record2.startLine) || record2.startLine < 0) {
|
|
33594
34024
|
throw new Error(`batch: edit[${index}] 'startLine' must be a positive integer (1-based)`);
|
|
33595
34025
|
}
|
|
@@ -33628,7 +34058,7 @@ function renderReadCall(args, theme, context) {
|
|
|
33628
34058
|
}
|
|
33629
34059
|
function registerHoistedTools(pi, ctx, surface) {
|
|
33630
34060
|
if (surface.hoistRead) {
|
|
33631
|
-
pi.registerTool({
|
|
34061
|
+
pi.registerTool(withPathAliasPreparation({
|
|
33632
34062
|
name: "read",
|
|
33633
34063
|
label: "read",
|
|
33634
34064
|
description: "Read file contents with line numbers. Backed by AFT's indexed Rust reader — faster than the built-in `read` on large repos. Images are returned as attachments on vision-capable models; PDFs and non-vision models are not yet supported.",
|
|
@@ -33641,6 +34071,8 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
33641
34071
|
if (typeof pathArg !== "string") {
|
|
33642
34072
|
throw new Error("read: missing required parameter `path`");
|
|
33643
34073
|
}
|
|
34074
|
+
const startLine = coerceOptionalInt(params.startLine, "startLine", 1, Number.MAX_SAFE_INTEGER);
|
|
34075
|
+
const endLine = coerceOptionalInt(params.endLine, "endLine", 1, Number.MAX_SAFE_INTEGER);
|
|
33644
34076
|
const offset = coerceOptionalInt(params.offset, "offset", 1, Number.MAX_SAFE_INTEGER);
|
|
33645
34077
|
const limit = coerceOptionalInt(params.limit, "limit", 1, Number.MAX_SAFE_INTEGER);
|
|
33646
34078
|
const filePath = await resolvePathArg(extCtx.cwd, pathArg);
|
|
@@ -33649,6 +34081,10 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
33649
34081
|
serverValidatedRead: true
|
|
33650
34082
|
});
|
|
33651
34083
|
const rawArgs = { filePath: pathArg };
|
|
34084
|
+
if (startLine !== undefined)
|
|
34085
|
+
rawArgs.startLine = startLine;
|
|
34086
|
+
if (endLine !== undefined)
|
|
34087
|
+
rawArgs.endLine = endLine;
|
|
33652
34088
|
if (offset !== undefined)
|
|
33653
34089
|
rawArgs.offset = offset;
|
|
33654
34090
|
if (limit !== undefined)
|
|
@@ -33684,21 +34120,21 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33684
34120
|
renderCall(args, theme, context) {
|
|
33685
34121
|
return renderReadCall(args, theme, context);
|
|
33686
34122
|
}
|
|
33687
|
-
});
|
|
34123
|
+
}));
|
|
33688
34124
|
}
|
|
33689
34125
|
if (surface.hoistWrite) {
|
|
33690
34126
|
const writeBackupText = ctx.config.backup?.enabled === false ? "Backup capture is disabled by user config." : "Existing files are backed up before overwriting (undo via aft_safety).";
|
|
33691
|
-
pi.registerTool({
|
|
34127
|
+
pi.registerTool(withPathAliasPreparation({
|
|
33692
34128
|
name: "write",
|
|
33693
34129
|
label: "write",
|
|
33694
|
-
description: `Write content to a file, creating it and parent directories automatically. ${writeBackupText} Auto-formats when the project has a formatter configured. Uses \`
|
|
33695
|
-
promptSnippet: "Create or overwrite files (uses
|
|
34130
|
+
description: `Write content to a file, creating it and parent directories automatically. ${writeBackupText} Auto-formats when the project has a formatter configured. Uses \`path\`. For partial edits, use the \`edit\` tool.`,
|
|
34131
|
+
promptSnippet: "Create or overwrite files (uses path; auto-formats)",
|
|
33696
34132
|
promptGuidelines: ["Use write only for new files or complete rewrites."],
|
|
33697
34133
|
parameters: WriteParams,
|
|
33698
34134
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
33699
34135
|
const filePathArg = mutationFilePathArg(params);
|
|
33700
34136
|
if (typeof filePathArg !== "string") {
|
|
33701
|
-
throw new Error("write: missing required parameter `
|
|
34137
|
+
throw new Error("write: missing required parameter `path`");
|
|
33702
34138
|
}
|
|
33703
34139
|
const filePath = await resolvePathArg(extCtx.cwd, filePathArg);
|
|
33704
34140
|
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
@@ -33711,7 +34147,7 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33711
34147
|
};
|
|
33712
34148
|
const response = await callToolCall(bridge, "write", rawArgs, extCtx);
|
|
33713
34149
|
if (response.success === false) {
|
|
33714
|
-
throw
|
|
34150
|
+
throw toolErrorFromResponse("write", response);
|
|
33715
34151
|
}
|
|
33716
34152
|
return buildMutationResult(response);
|
|
33717
34153
|
},
|
|
@@ -33721,60 +34157,45 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33721
34157
|
renderResult(result, _options, theme, context) {
|
|
33722
34158
|
return renderMutationResult(result, theme, context);
|
|
33723
34159
|
}
|
|
33724
|
-
});
|
|
34160
|
+
}));
|
|
33725
34161
|
}
|
|
33726
34162
|
if (surface.hoistEdit) {
|
|
33727
|
-
pi.registerTool({
|
|
34163
|
+
pi.registerTool(withPathAliasPreparation({
|
|
33728
34164
|
name: "edit",
|
|
33729
34165
|
label: "edit",
|
|
33730
|
-
description: "Edit part of a file via `appendContent`, batch `edits[]`, or `
|
|
33731
|
-
promptSnippet: "Partial file edits via appendContent, edits[], or
|
|
34166
|
+
description: "Edit part of a file via `appendContent`, batch `edits[]`, or symbol plus `content`. Batch `{ oldString, newString, replaceAll: true }` replaces every match. Provide exactly one mode per call: appendContent, edits[], or symbol plus content (mixing modes is rejected).",
|
|
34167
|
+
promptSnippet: "Partial file edits via appendContent, edits[], or symbol plus content (exactly one mode per call).",
|
|
33732
34168
|
promptGuidelines: [
|
|
33733
34169
|
"Prefer edit over write when changing part of an existing file.",
|
|
33734
34170
|
"Use appendContent when adding text to the end of a file.",
|
|
33735
34171
|
"Use edits[] for multiple atomic changes in one file.",
|
|
33736
|
-
"Include enough surrounding context in
|
|
34172
|
+
"Include enough surrounding context in an edits[] find/replace item to make the match unique, or set replaceAll/occurrence explicitly."
|
|
33737
34173
|
],
|
|
33738
34174
|
parameters: EditParams,
|
|
33739
34175
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
33740
34176
|
const argsRecord = params;
|
|
33741
34177
|
if (argsRecord.startLine !== undefined || argsRecord.endLine !== undefined) {
|
|
33742
|
-
throw new Error("edit: 'startLine'/'endLine' are not top-level parameters. " + "For line-range edits, nest them inside the `edits` array: " + '`edits: [{ startLine: N, endLine: M, content: "..." }]`. ' + "For find/replace, use `
|
|
34178
|
+
throw new Error("edit: 'startLine'/'endLine' are not top-level parameters. " + "For line-range edits, nest them inside the `edits` array: " + '`edits: [{ startLine: N, endLine: M, content: "..." }]`. ' + "For find/replace, use an item in `edits[]` instead.");
|
|
33743
34179
|
}
|
|
33744
34180
|
const filePathArg = mutationFilePathArg(params);
|
|
33745
34181
|
if (typeof filePathArg !== "string") {
|
|
33746
|
-
throw new Error("edit: missing required parameter `
|
|
34182
|
+
throw new Error("edit: missing required parameter `path`");
|
|
33747
34183
|
}
|
|
33748
|
-
if (params.
|
|
34184
|
+
if (params.edits !== undefined)
|
|
33749
34185
|
validateBatchEdits(params.edits);
|
|
33750
34186
|
const filePath = await resolvePathArg(extCtx.cwd, filePathArg);
|
|
33751
34187
|
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
33752
34188
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
33753
34189
|
});
|
|
33754
34190
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33755
|
-
const rawArgs = {
|
|
33756
|
-
for (const key of ["appendContent", "
|
|
34191
|
+
const rawArgs = { path: filePathArg };
|
|
34192
|
+
for (const key of ["appendContent", "edits", "symbol", "content"]) {
|
|
33757
34193
|
if (argsRecord[key] !== undefined)
|
|
33758
34194
|
rawArgs[key] = argsRecord[key];
|
|
33759
34195
|
}
|
|
33760
|
-
if (Array.isArray(argsRecord.edits)) {
|
|
33761
|
-
rawArgs.edits = argsRecord.edits.map((item) => {
|
|
33762
|
-
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
33763
|
-
return item;
|
|
33764
|
-
const batchItem = item;
|
|
33765
|
-
return batchItem.replaceAll === undefined ? batchItem : { ...batchItem, replaceAll: coerceBoolean(batchItem.replaceAll) };
|
|
33766
|
-
});
|
|
33767
|
-
} else if (argsRecord.edits !== undefined) {
|
|
33768
|
-
rawArgs.edits = argsRecord.edits;
|
|
33769
|
-
}
|
|
33770
|
-
if (params.replaceAll !== undefined)
|
|
33771
|
-
rawArgs.replaceAll = coerceBoolean(params.replaceAll);
|
|
33772
|
-
const occurrence = coerceOptionalInt(params.occurrence, "occurrence", 0, Number.MAX_SAFE_INTEGER);
|
|
33773
|
-
if (occurrence !== undefined)
|
|
33774
|
-
rawArgs.occurrence = occurrence;
|
|
33775
34196
|
const response = await callToolCall(bridge, "edit", rawArgs, extCtx);
|
|
33776
34197
|
if (response.success === false) {
|
|
33777
|
-
throw
|
|
34198
|
+
throw toolErrorFromResponse("edit", response);
|
|
33778
34199
|
}
|
|
33779
34200
|
return buildMutationResult(response);
|
|
33780
34201
|
},
|
|
@@ -33784,10 +34205,10 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33784
34205
|
renderResult(result, _options, theme, context) {
|
|
33785
34206
|
return renderMutationResult(result, theme, context);
|
|
33786
34207
|
}
|
|
33787
|
-
});
|
|
34208
|
+
}));
|
|
33788
34209
|
}
|
|
33789
34210
|
if (surface.hoistGrep) {
|
|
33790
|
-
pi.registerTool({
|
|
34211
|
+
pi.registerTool(withPathAliasPreparation({
|
|
33791
34212
|
name: "grep",
|
|
33792
34213
|
label: "grep",
|
|
33793
34214
|
description: "Search for a regex pattern across files. Uses AFT's trigram index inside the project root for fast repeated queries, and falls back to ripgrep for paths outside the project root.",
|
|
@@ -33809,8 +34230,6 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33809
34230
|
}
|
|
33810
34231
|
if (params.include)
|
|
33811
34232
|
req.include = params.include;
|
|
33812
|
-
if (params.caseSensitive !== undefined)
|
|
33813
|
-
req.caseSensitive = params.caseSensitive;
|
|
33814
34233
|
const response = await callToolCall(bridge, "grep", req, extCtx);
|
|
33815
34234
|
if (response.success === false) {
|
|
33816
34235
|
throw new Error(response.text || response.message || "grep failed");
|
|
@@ -33821,7 +34240,7 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33821
34240
|
const text = appendSkippedSearchPaths(response.text ?? "", pathSplit?.missing ?? []);
|
|
33822
34241
|
return textResult(text, response);
|
|
33823
34242
|
}
|
|
33824
|
-
});
|
|
34243
|
+
}));
|
|
33825
34244
|
}
|
|
33826
34245
|
}
|
|
33827
34246
|
function buildMutationResult(response) {
|
|
@@ -34165,7 +34584,7 @@ var SearchParams = Type3.Object({
|
|
|
34165
34584
|
lang: AstLang,
|
|
34166
34585
|
paths: Type3.Optional(Type3.Array(Type3.String(), { description: "Paths to search (default: ['.'])" })),
|
|
34167
34586
|
globs: Type3.Optional(Type3.Array(Type3.String(), { description: "Include/exclude globs (prefix `!` to exclude)" })),
|
|
34168
|
-
contextLines:
|
|
34587
|
+
contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER, "Number of context lines around each match")
|
|
34169
34588
|
});
|
|
34170
34589
|
var ReplaceParams = Type3.Object({
|
|
34171
34590
|
pattern: Type3.String({ description: "AST pattern with meta-variables" }),
|
|
@@ -35056,7 +35475,7 @@ function formatWaitSummary(waited, details) {
|
|
|
35056
35475
|
return `Waited ${waited.elapsed_ms}ms; timeout reached without match.`;
|
|
35057
35476
|
}
|
|
35058
35477
|
if (waited.reason === "unavailable") {
|
|
35059
|
-
return `Waited ${waited.elapsed_ms}ms; the bridge
|
|
35478
|
+
return `Waited ${waited.elapsed_ms}ms; the bridge was busy, so task state is unknown. Do not poll; let the task's completion notification wake the session, or use one bash_status snapshot on the next normal tool call.`;
|
|
35060
35479
|
}
|
|
35061
35480
|
const exit = typeof details.exit_code === "number" ? `, exit ${details.exit_code}` : "";
|
|
35062
35481
|
return `Waited ${waited.elapsed_ms}ms; task exited (${details.status}${exit}).`;
|
|
@@ -35197,7 +35616,7 @@ function renderConflictToolResult(result, theme, context) {
|
|
|
35197
35616
|
return renderConflictResult(collectTextContent(result), theme, context);
|
|
35198
35617
|
}
|
|
35199
35618
|
function registerConflictsTool(pi, ctx) {
|
|
35200
|
-
pi.registerTool({
|
|
35619
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35201
35620
|
name: "aft_conflicts",
|
|
35202
35621
|
label: "conflicts",
|
|
35203
35622
|
description: "Show all git merge conflicts across the repository — returns line-numbered conflict regions with context for every conflicted file in a single call.",
|
|
@@ -35224,7 +35643,7 @@ function registerConflictsTool(pi, ctx) {
|
|
|
35224
35643
|
renderResult(result, _options, theme, context) {
|
|
35225
35644
|
return renderConflictToolResult(result, theme, context);
|
|
35226
35645
|
}
|
|
35227
|
-
});
|
|
35646
|
+
}));
|
|
35228
35647
|
}
|
|
35229
35648
|
|
|
35230
35649
|
// src/tools/fs.ts
|
|
@@ -35239,7 +35658,7 @@ var DeleteParams = Type6.Object({
|
|
|
35239
35658
|
}))
|
|
35240
35659
|
});
|
|
35241
35660
|
var MoveParams = Type6.Object({
|
|
35242
|
-
|
|
35661
|
+
path: Type6.String({
|
|
35243
35662
|
description: "Source file path to move (absolute or relative to project root)"
|
|
35244
35663
|
}),
|
|
35245
35664
|
destination: Type6.String({
|
|
@@ -35263,7 +35682,7 @@ function renderFsCall(toolName, args, theme, context) {
|
|
|
35263
35682
|
return renderToolCall("delete", summary, theme, context);
|
|
35264
35683
|
}
|
|
35265
35684
|
const moveArgs = args;
|
|
35266
|
-
return renderToolCall("move", `${accentPath(theme, moveArgs.
|
|
35685
|
+
return renderToolCall("move", `${accentPath(theme, moveArgs.path)} ${theme.fg("muted", "→")} ${accentPath(theme, moveArgs.destination)}`, theme, context);
|
|
35267
35686
|
}
|
|
35268
35687
|
function renderFsResult(toolName, args, result, theme, context) {
|
|
35269
35688
|
if (context.isError) {
|
|
@@ -35289,14 +35708,14 @@ function renderFsResult(toolName, args, result, theme, context) {
|
|
|
35289
35708
|
}
|
|
35290
35709
|
const moveArgs = args;
|
|
35291
35710
|
return renderSections([
|
|
35292
|
-
`${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath3(moveArgs.
|
|
35711
|
+
`${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath3(moveArgs.path ?? ""))}`,
|
|
35293
35712
|
`${theme.fg("muted", "to")} ${theme.fg("accent", shortenPath3(moveArgs.destination))}`
|
|
35294
35713
|
], context);
|
|
35295
35714
|
}
|
|
35296
35715
|
function registerFsTools(pi, ctx, surface) {
|
|
35297
35716
|
const backupsDisabled = ctx.config.backup?.enabled === false;
|
|
35298
35717
|
if (surface.delete) {
|
|
35299
|
-
pi.registerTool({
|
|
35718
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35300
35719
|
name: "aft_delete",
|
|
35301
35720
|
label: "delete",
|
|
35302
35721
|
description: "Delete one or more files (or directories). " + (backupsDisabled ? "Backup capture is disabled by user config, so this tool does not create undo snapshots. " : "Each file is backed up before deletion — use `aft_safety undo` to recover any of them. For directories, every file inside is individually backed up before removal. ") + "Directory deletion requires recursive: true. " + "Returns { success, complete, deleted, skipped_files }: partial success is allowed; files that fail are reported in skipped_files.",
|
|
@@ -35340,16 +35759,16 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
35340
35759
|
renderResult(result, _options, theme, context) {
|
|
35341
35760
|
return renderFsResult("aft_delete", context.args, result, theme, context);
|
|
35342
35761
|
}
|
|
35343
|
-
});
|
|
35762
|
+
}));
|
|
35344
35763
|
}
|
|
35345
35764
|
if (surface.move) {
|
|
35346
|
-
pi.registerTool({
|
|
35765
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35347
35766
|
name: "aft_move",
|
|
35348
35767
|
label: "move",
|
|
35349
35768
|
description: "Move or rename a file. " + (backupsDisabled ? "Backup capture is disabled by user config. " : "Creates an undo backup before moving. ") + "Creates parent directories for the destination automatically. This operates on files at the OS level — to relocate a code symbol between files, use `aft_refactor` with op='move' instead.",
|
|
35350
35769
|
parameters: MoveParams,
|
|
35351
35770
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
35352
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.
|
|
35771
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
35353
35772
|
const destination = await resolvePathArg(extCtx.cwd, params.destination);
|
|
35354
35773
|
const checked = new Set([filePath, destination]);
|
|
35355
35774
|
for (const file2 of checked) {
|
|
@@ -35359,7 +35778,7 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
35359
35778
|
}
|
|
35360
35779
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
35361
35780
|
const response = await callToolCall(bridge, "move", {
|
|
35362
|
-
filePath: params.
|
|
35781
|
+
filePath: params.path,
|
|
35363
35782
|
destination: params.destination
|
|
35364
35783
|
}, extCtx);
|
|
35365
35784
|
if (response.success === false) {
|
|
@@ -35373,7 +35792,7 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
35373
35792
|
renderResult(result, _options, theme, context) {
|
|
35374
35793
|
return renderFsResult("aft_move", context.args, result, theme, context);
|
|
35375
35794
|
}
|
|
35376
|
-
});
|
|
35795
|
+
}));
|
|
35377
35796
|
}
|
|
35378
35797
|
}
|
|
35379
35798
|
|
|
@@ -35382,7 +35801,9 @@ import { StringEnum as StringEnum2 } from "@earendil-works/pi-ai";
|
|
|
35382
35801
|
import { Type as Type7 } from "typebox";
|
|
35383
35802
|
var ImportParams = Type7.Object({
|
|
35384
35803
|
op: StringEnum2(["add", "remove", "organize"], { description: "Import operation" }),
|
|
35385
|
-
|
|
35804
|
+
path: Type7.String({
|
|
35805
|
+
description: "Path to the file (absolute or relative to project root)"
|
|
35806
|
+
}),
|
|
35386
35807
|
module: Type7.Optional(Type7.String({ description: "Module path (required for add/remove), e.g. 'react', './utils'" })),
|
|
35387
35808
|
names: Type7.Optional(Type7.Array(Type7.String(), {
|
|
35388
35809
|
description: "Named imports to add, using native named-import text with per-name `as` aliasing where supported, e.g. ['useState'], Solidity ['ERC20', 'IERC20 as IToken']"
|
|
@@ -35412,7 +35833,7 @@ function buildImportSections(args, payload, theme) {
|
|
|
35412
35833
|
const groups = asRecords2(response.groups);
|
|
35413
35834
|
const groupText = groups.length > 0 ? groups.map((group) => `${asString2(group.name) ?? "unknown"}: ${asNumber2(group.count) ?? 0}`).join(" · ") : "No imports found";
|
|
35414
35835
|
return [
|
|
35415
|
-
`${theme.fg("success", "organized")} ${theme.fg("accent", asString2(response.file) ?? args.
|
|
35836
|
+
`${theme.fg("success", "organized")} ${theme.fg("accent", asString2(response.file) ?? args.path ?? "")}`,
|
|
35416
35837
|
`${theme.fg("muted", "groups")} ${groupText}`,
|
|
35417
35838
|
`${theme.fg("muted", "duplicates removed")} ${asNumber2(response.removed_duplicates) ?? 0}`
|
|
35418
35839
|
];
|
|
@@ -35422,7 +35843,7 @@ function buildImportSections(args, payload, theme) {
|
|
|
35422
35843
|
const status = response.already_present === true ? theme.fg("warning", "already present") : theme.fg("success", "added");
|
|
35423
35844
|
return [
|
|
35424
35845
|
`${status} ${theme.fg("accent", moduleName2)}`,
|
|
35425
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.
|
|
35846
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.path ?? "")}`,
|
|
35426
35847
|
`${theme.fg("muted", "group")} ${asString2(response.group) ?? "—"}`
|
|
35427
35848
|
];
|
|
35428
35849
|
}
|
|
@@ -35431,14 +35852,14 @@ function buildImportSections(args, payload, theme) {
|
|
|
35431
35852
|
const removeStatus = didRemove ? `${theme.fg("success", "removed")} ${theme.fg("accent", moduleName)}` : `${theme.fg("warning", "not present")} ${theme.fg("accent", moduleName)}`;
|
|
35432
35853
|
return [
|
|
35433
35854
|
removeStatus,
|
|
35434
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.
|
|
35855
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.path ?? "")}`,
|
|
35435
35856
|
args.removeName ? `${theme.fg("muted", "name")} ${args.removeName}` : `${theme.fg("muted", "scope")} entire import`
|
|
35436
35857
|
];
|
|
35437
35858
|
}
|
|
35438
35859
|
function renderImportCall(args, theme, context) {
|
|
35439
35860
|
const summary = [
|
|
35440
35861
|
theme.fg("accent", args.op),
|
|
35441
|
-
accentPath(theme, args.
|
|
35862
|
+
accentPath(theme, args.path ?? ""),
|
|
35442
35863
|
args.module ? theme.fg("toolOutput", args.module) : undefined
|
|
35443
35864
|
].filter(Boolean).join(" ");
|
|
35444
35865
|
return renderToolCall("import", summary, theme, context);
|
|
@@ -35451,7 +35872,7 @@ function renderImportResult(result, args, theme, context) {
|
|
|
35451
35872
|
}
|
|
35452
35873
|
function registerImportTools(pi, ctx) {
|
|
35453
35874
|
const organizeRecovery = ctx.config.backup?.enabled === false ? "Backup capture is disabled by user config; review broad cleanup changes before proceeding." : "Use aft_safety checkpoint/undo before broad cleanup.";
|
|
35454
|
-
pi.registerTool({
|
|
35875
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35455
35876
|
name: "aft_import",
|
|
35456
35877
|
label: "import",
|
|
35457
35878
|
description: `Language-aware import management. Supports TS, JS, TSX, Python, Rust, Go, Solidity, Java, C#, PHP, Kotlin, Scala, Swift, Ruby, Lua, C, C++, Perl, Vue. Ops: \`add\`, \`remove\`, \`organize\`. ${organizeRecovery}`,
|
|
@@ -35460,7 +35881,7 @@ function registerImportTools(pi, ctx) {
|
|
|
35460
35881
|
if ((params.op === "add" || params.op === "remove") && isEmptyParam(params.module)) {
|
|
35461
35882
|
throw new Error(`op='${params.op}' requires 'module'`);
|
|
35462
35883
|
}
|
|
35463
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.
|
|
35884
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
35464
35885
|
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
35465
35886
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
35466
35887
|
});
|
|
@@ -35498,7 +35919,7 @@ function registerImportTools(pi, ctx) {
|
|
|
35498
35919
|
renderResult(result, _options, theme, context) {
|
|
35499
35920
|
return renderImportResult(result, context.args, theme, context);
|
|
35500
35921
|
}
|
|
35501
|
-
});
|
|
35922
|
+
}));
|
|
35502
35923
|
}
|
|
35503
35924
|
|
|
35504
35925
|
// src/tools/inspect.ts
|
|
@@ -35574,7 +35995,7 @@ function diagnosticsSummaryPart(summary) {
|
|
|
35574
35995
|
const counts = `${errors3 ?? 0} errors/${warnings ?? 0} warnings/${info ?? 0} info/${hints ?? 0} hints`;
|
|
35575
35996
|
const status = asString2(section.status);
|
|
35576
35997
|
if (status === "pending") {
|
|
35577
|
-
return hasCounts ? `diagnostics ${counts} so far — still pending (servers: ${diagnosticsServerSummary(section)})` : `diagnostics pending (servers: ${diagnosticsServerSummary(section)})`;
|
|
35998
|
+
return hasCounts ? `diagnostics ${counts} so far — still pending (servers: ${diagnosticsServerSummary(section)}); wait for the LSP update and use the next normal aft_inspect, not repeated polling` : `diagnostics pending (servers: ${diagnosticsServerSummary(section)}); wait for the LSP update and use the next normal aft_inspect, not repeated polling`;
|
|
35578
35999
|
}
|
|
35579
36000
|
if (status === "incomplete") {
|
|
35580
36001
|
return hasCounts ? `diagnostics ${counts} (incomplete — servers: ${diagnosticsServerSummary(section)})` : `diagnostics unavailable (status incomplete; servers: ${diagnosticsServerSummary(section)})`;
|
|
@@ -35727,7 +36148,7 @@ function buildInspectSections(payload, theme) {
|
|
|
35727
36148
|
].filter((part) => Boolean(part));
|
|
35728
36149
|
const sections = [theme.fg("accent", parts.join(" · "))];
|
|
35729
36150
|
if (stale > 0 || pending > 0) {
|
|
35730
|
-
sections.push(theme.fg("warning", `scanner state: ${stale} stale · ${pending} pending`));
|
|
36151
|
+
sections.push(theme.fg("warning", `scanner state: ${stale} stale · ${pending} pending. Treat stale_categories/pending_categories as stale or incomplete cache state. AFT schedules a Tier-2 refresh after its next idle or inspect-triggered background run; use one later normal aft_inspect after that refresh, not a polling loop`));
|
|
35731
36152
|
}
|
|
35732
36153
|
const topPreview = tier2TopPreview(summary, theme);
|
|
35733
36154
|
if (topPreview)
|
|
@@ -35803,7 +36224,7 @@ function navigateParamsSchema() {
|
|
|
35803
36224
|
op: StringEnum3(["call_tree", "callers", "trace_to", "trace_to_symbol", "impact", "trace_data"], {
|
|
35804
36225
|
description: "Navigation operation"
|
|
35805
36226
|
}),
|
|
35806
|
-
|
|
36227
|
+
path: Type9.String({
|
|
35807
36228
|
description: "Source file containing the symbol (absolute or relative to project root)"
|
|
35808
36229
|
}),
|
|
35809
36230
|
symbol: Type9.String({ description: "Name of the symbol to analyze" }),
|
|
@@ -35812,7 +36233,7 @@ function navigateParamsSchema() {
|
|
|
35812
36233
|
toSymbol: Type9.Optional(Type9.String({
|
|
35813
36234
|
description: "Target symbol for trace_to_symbol; the returned path ends here"
|
|
35814
36235
|
})),
|
|
35815
|
-
|
|
36236
|
+
toPath: Type9.Optional(Type9.String({
|
|
35816
36237
|
description: "Optional target file for trace_to_symbol; required when toSymbol exists in multiple files"
|
|
35817
36238
|
})),
|
|
35818
36239
|
includeTests: Type9.Optional(Type9.Boolean({
|
|
@@ -35834,7 +36255,7 @@ function buildNavigateSections(args, payload, theme) {
|
|
|
35834
36255
|
function renderNavigateCall(args, theme, context) {
|
|
35835
36256
|
const summary = [
|
|
35836
36257
|
theme.fg("accent", args.op),
|
|
35837
|
-
accentPath(theme, args.
|
|
36258
|
+
accentPath(theme, args.path),
|
|
35838
36259
|
theme.fg("toolOutput", args.symbol),
|
|
35839
36260
|
args.toSymbol ? theme.fg("toolOutput", `→ ${args.toSymbol}`) : undefined
|
|
35840
36261
|
].filter(Boolean).join(" ");
|
|
@@ -35846,14 +36267,14 @@ function renderNavigateResult(result, args, theme, context) {
|
|
|
35846
36267
|
return renderSections(buildNavigateSections(args, extractStructuredPayload(result), theme), context);
|
|
35847
36268
|
}
|
|
35848
36269
|
function registerNavigateTool(pi, ctx) {
|
|
35849
|
-
pi.registerTool({
|
|
36270
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35850
36271
|
name: "aft_callgraph",
|
|
35851
36272
|
label: "callgraph",
|
|
35852
|
-
description: "Answer code-relationship questions from a real call graph — instead of grep + read chains. Reach for this whenever the question is about how symbols connect. Use aft_zoom with `callgraph:true` for one-level forward calls-out while reading source; use aft_callgraph only for reverse callers or multi-level traces so you do not double-fetch the same relationships. All ops require both `
|
|
36273
|
+
description: "Answer code-relationship questions from a real call graph — instead of grep + read chains. Reach for this whenever the question is about how symbols connect. Use aft_zoom with `callgraph:true` for one-level forward calls-out while reading source; use aft_callgraph only for reverse callers or multi-level traces so you do not double-fetch the same relationships. All ops require both `path` and `symbol`. Use `callers` for call sites (before renaming/signature changes), `impact` for blast radius (what breaks if a symbol changes), `call_tree` for what a function calls, `trace_to` for how execution reaches a symbol from entry points, `trace_to_symbol` for the shortest path from one symbol to another (requires `toSymbol`; if ambiguous, the error returns candidate files — retry with `toPath`), `trace_data` to follow a value across assignments/params. Markers: ~ = edge resolved by name only (may point at the wrong same-named symbol); [unresolved] = callee not resolved to a definition, so the location shown is the call site. Unmarked edges are resolved exactly. By default, unresolved external/stdlib leaf calls in call_tree are collapsed into one summary per parent; pass includeUnresolved=true to show every unresolved edge individually.",
|
|
35853
36274
|
parameters: navigateParamsSchema(),
|
|
35854
36275
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
35855
|
-
if (isEmptyParam(params.
|
|
35856
|
-
throw new Error(`op='${params.op}' requires a \`
|
|
36276
|
+
if (isEmptyParam(params.path)) {
|
|
36277
|
+
throw new Error(`op='${params.op}' requires a \`path\``);
|
|
35857
36278
|
}
|
|
35858
36279
|
if (isEmptyParam(params.symbol)) {
|
|
35859
36280
|
throw new Error(`op='${params.op}' requires a \`symbol\``);
|
|
@@ -35864,8 +36285,8 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35864
36285
|
if (params.op === "trace_to_symbol" && isEmptyParam(params.toSymbol)) {
|
|
35865
36286
|
throw new Error("op='trace_to_symbol' requires a `toSymbol`");
|
|
35866
36287
|
}
|
|
35867
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.
|
|
35868
|
-
const toFile = !isEmptyParam(params.
|
|
36288
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
36289
|
+
const toFile = !isEmptyParam(params.toPath) ? await resolvePathArg(extCtx.cwd, params.toPath) : undefined;
|
|
35869
36290
|
const checked = new Set;
|
|
35870
36291
|
for (const target of [filePath, ...toFile !== undefined ? [toFile] : []]) {
|
|
35871
36292
|
if (checked.has(target))
|
|
@@ -35878,7 +36299,7 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35878
36299
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
35879
36300
|
const rawArgs = {
|
|
35880
36301
|
op: params.op,
|
|
35881
|
-
filePath: params.
|
|
36302
|
+
filePath: params.path,
|
|
35882
36303
|
symbol: params.symbol
|
|
35883
36304
|
};
|
|
35884
36305
|
const depth = coerceOptionalInt(params.depth, "depth", 1, Number.MAX_SAFE_INTEGER);
|
|
@@ -35888,8 +36309,8 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35888
36309
|
rawArgs.expression = params.expression;
|
|
35889
36310
|
if (!isEmptyParam(params.toSymbol))
|
|
35890
36311
|
rawArgs.toSymbol = params.toSymbol;
|
|
35891
|
-
if (!isEmptyParam(params.
|
|
35892
|
-
rawArgs.toFile = params.
|
|
36312
|
+
if (!isEmptyParam(params.toPath))
|
|
36313
|
+
rawArgs.toFile = params.toPath;
|
|
35893
36314
|
if (!isEmptyParam(params.includeTests))
|
|
35894
36315
|
rawArgs.includeTests = coerceBoolean(params.includeTests);
|
|
35895
36316
|
if (!isEmptyParam(params.includeUnresolved))
|
|
@@ -35914,7 +36335,7 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35914
36335
|
renderResult(result, _options, theme, context) {
|
|
35915
36336
|
return renderNavigateResult(result, context.args, theme, context);
|
|
35916
36337
|
}
|
|
35917
|
-
});
|
|
36338
|
+
}));
|
|
35918
36339
|
}
|
|
35919
36340
|
|
|
35920
36341
|
// src/tools/reading.ts
|
|
@@ -35931,21 +36352,21 @@ var OutlineParams = Type10.Object({
|
|
|
35931
36352
|
}))
|
|
35932
36353
|
});
|
|
35933
36354
|
var ZoomTarget = Type10.Object({
|
|
35934
|
-
|
|
36355
|
+
path: Type10.String({ description: "Path to file (absolute or project-relative)" }),
|
|
35935
36356
|
symbol: Type10.String({ description: "Symbol name in that file" })
|
|
35936
36357
|
});
|
|
35937
36358
|
var ZoomParams = Type10.Object({
|
|
35938
|
-
|
|
36359
|
+
path: Type10.Optional(Type10.String({ description: "Path to file (absolute or project-relative)" })),
|
|
35939
36360
|
url: Type10.Optional(Type10.String({
|
|
35940
36361
|
description: "HTTP/HTTPS URL of an HTML or Markdown document to fetch and zoom into"
|
|
35941
36362
|
})),
|
|
35942
36363
|
symbols: Type10.Optional(Type10.Union([Type10.String(), Type10.Array(Type10.String())], {
|
|
35943
|
-
description: "Symbol name for code, or heading text for Markdown/HTML. Pass a string
|
|
36364
|
+
description: "Symbol name for code, or heading text for Markdown/HTML. Pass a string or array for batched lookups in the same file/URL."
|
|
35944
36365
|
})),
|
|
35945
36366
|
targets: Type10.Optional(Type10.Union([ZoomTarget, Type10.Array(ZoomTarget)], {
|
|
35946
|
-
description: "Cross-file batch: `{
|
|
36367
|
+
description: "Cross-file batch: `{ path, symbol }` or an array of them. Mutually exclusive with path/url/symbols."
|
|
35947
36368
|
})),
|
|
35948
|
-
contextLines:
|
|
36369
|
+
contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER, "Lines of context before/after (default: 3)"),
|
|
35949
36370
|
callgraph: Type10.Optional(Type10.Boolean({
|
|
35950
36371
|
description: "Include call-graph annotations (calls-out / called-by within the same file). Default false; off keeps zoom output minimal."
|
|
35951
36372
|
}))
|
|
@@ -35966,7 +36387,7 @@ async function assertReadPathPermissions(extCtx, ctx, paths) {
|
|
|
35966
36387
|
}
|
|
35967
36388
|
}
|
|
35968
36389
|
function zoomTargetLabel(args) {
|
|
35969
|
-
return args.
|
|
36390
|
+
return args.path ?? args.url ?? "(no target)";
|
|
35970
36391
|
}
|
|
35971
36392
|
function zoomExtraCallSites(call) {
|
|
35972
36393
|
const extraCount = call.extra_count;
|
|
@@ -36087,7 +36508,7 @@ function renderZoomResult(result, args, theme, context) {
|
|
|
36087
36508
|
}
|
|
36088
36509
|
function registerReadingTools(pi, ctx, surface) {
|
|
36089
36510
|
if (surface.outline) {
|
|
36090
|
-
pi.registerTool({
|
|
36511
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36091
36512
|
name: "aft_outline",
|
|
36092
36513
|
label: "outline",
|
|
36093
36514
|
description: `Structural outline of source code, documentation files, or remote URLs. For code, returns symbols (functions, classes, types) with line ranges. For Markdown and HTML, returns heading hierarchy. Use this to explore structure before reading specific sections with aft_zoom. Set \`files: true\` with a directory target for a flat indexed file tree with language, symbol count, and byte metadata.
|
|
@@ -36130,7 +36551,17 @@ Pass a single \`target\`:
|
|
|
36130
36551
|
if (response.success === false) {
|
|
36131
36552
|
throw new Error(response.text || response.message || "outline failed");
|
|
36132
36553
|
}
|
|
36133
|
-
|
|
36554
|
+
let text = typeof response.text === "string" ? response.text : "";
|
|
36555
|
+
const trimmed = text.trim();
|
|
36556
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
36557
|
+
try {
|
|
36558
|
+
const parsed = JSON.parse(text);
|
|
36559
|
+
if (typeof parsed.text === "string") {
|
|
36560
|
+
text = parsed.text;
|
|
36561
|
+
}
|
|
36562
|
+
} catch {}
|
|
36563
|
+
}
|
|
36564
|
+
return textResult(text, response);
|
|
36134
36565
|
},
|
|
36135
36566
|
renderCall(args, theme, context) {
|
|
36136
36567
|
return renderOutlineCall(args, theme, context);
|
|
@@ -36138,13 +36569,13 @@ Pass a single \`target\`:
|
|
|
36138
36569
|
renderResult(result, _options, theme, context) {
|
|
36139
36570
|
return renderOutlineResult(result, theme, context);
|
|
36140
36571
|
}
|
|
36141
|
-
});
|
|
36572
|
+
}));
|
|
36142
36573
|
}
|
|
36143
36574
|
if (surface.zoom) {
|
|
36144
|
-
pi.registerTool({
|
|
36575
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36145
36576
|
name: "aft_zoom",
|
|
36146
36577
|
label: "zoom",
|
|
36147
|
-
description: "Inspect code symbols or documentation sections. For code, returns the full source of a symbol. Pass `callgraph: true` to also include call-graph annotations (calls-out / called-by within the same file). For Markdown and HTML, returns the section content under the given heading.\n\nUse exactly ONE mode: `{
|
|
36578
|
+
description: "Inspect code symbols or documentation sections. For code, returns the full source of a symbol. Pass `callgraph: true` to also include call-graph annotations (calls-out / called-by within the same file). For Markdown and HTML, returns the section content under the given heading.\n\nUse exactly ONE mode: `{ path, symbols }`, `{ url, symbols }`, or `{ targets }`. `symbols` can be a string or array (one or many lookups in the same file/URL). Use `targets` for cross-file batches: `{ path, symbol }` or an array of them.",
|
|
36148
36579
|
parameters: ZoomParams,
|
|
36149
36580
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
36150
36581
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
@@ -36154,7 +36585,7 @@ Pass a single \`target\`:
|
|
|
36154
36585
|
const entryEmpty = (entry) => {
|
|
36155
36586
|
if (!entry || typeof entry !== "object")
|
|
36156
36587
|
return true;
|
|
36157
|
-
const fp = entry.
|
|
36588
|
+
const fp = entry.path;
|
|
36158
36589
|
const sym = entry.symbol;
|
|
36159
36590
|
const fpEmpty = typeof fp !== "string" || fp.length === 0;
|
|
36160
36591
|
const symEmpty = typeof sym !== "string" || sym.length === 0;
|
|
@@ -36164,31 +36595,33 @@ Pass a single \`target\`:
|
|
|
36164
36595
|
return !t.every(entryEmpty);
|
|
36165
36596
|
return !entryEmpty(t);
|
|
36166
36597
|
};
|
|
36167
|
-
const
|
|
36598
|
+
const hasPath2 = !isEmptyParam(params.path);
|
|
36168
36599
|
const hasUrl = !isEmptyParam(params.url);
|
|
36169
36600
|
const hasTargets = hasTargetsProvided(params.targets);
|
|
36170
36601
|
const hasSymbols = !isEmptyParam(params.symbols);
|
|
36171
36602
|
const wantCallgraph = coerceBoolean(params.callgraph);
|
|
36172
36603
|
const contextLines = coerceOptionalInt(params.contextLines, "contextLines", 1, Number.MAX_SAFE_INTEGER);
|
|
36173
36604
|
if (hasTargets) {
|
|
36174
|
-
if (
|
|
36175
|
-
throw new Error("'targets' is mutually exclusive with '
|
|
36605
|
+
if (hasPath2 || hasUrl || hasSymbols) {
|
|
36606
|
+
throw new Error("'targets' is mutually exclusive with 'path', 'url', and 'symbols'");
|
|
36176
36607
|
}
|
|
36177
36608
|
const targets = Array.isArray(params.targets) ? params.targets : [params.targets];
|
|
36178
36609
|
if (targets.length === 0) {
|
|
36179
36610
|
throw new Error("'targets' must be a non-empty object or array");
|
|
36180
36611
|
}
|
|
36181
36612
|
for (const [i, entry] of targets.entries()) {
|
|
36182
|
-
if (!entry || typeof entry.
|
|
36183
|
-
throw new Error(`targets[${i}].
|
|
36613
|
+
if (!entry || typeof entry.path !== "string" || entry.path.length === 0) {
|
|
36614
|
+
throw new Error(`targets[${i}].path must be a non-empty string`);
|
|
36184
36615
|
}
|
|
36185
36616
|
if (typeof entry.symbol !== "string" || entry.symbol.length === 0) {
|
|
36186
36617
|
throw new Error(`targets[${i}].symbol must be a non-empty string`);
|
|
36187
36618
|
}
|
|
36188
36619
|
}
|
|
36189
|
-
const resolvedTargets = await Promise.all(targets.map((t) => resolvePathArg(extCtx.cwd, t.
|
|
36620
|
+
const resolvedTargets = await Promise.all(targets.map((t) => resolvePathArg(extCtx.cwd, t.path)));
|
|
36190
36621
|
await assertReadPathPermissions(extCtx, ctx, resolvedTargets);
|
|
36191
|
-
const rawArgs2 = {
|
|
36622
|
+
const rawArgs2 = {
|
|
36623
|
+
targets: targets.map((target) => ({ filePath: target.path, symbol: target.symbol }))
|
|
36624
|
+
};
|
|
36192
36625
|
if (contextLines !== undefined)
|
|
36193
36626
|
rawArgs2.contextLines = contextLines;
|
|
36194
36627
|
if (wantCallgraph)
|
|
@@ -36199,17 +36632,17 @@ Pass a single \`target\`:
|
|
|
36199
36632
|
}
|
|
36200
36633
|
return textResult(response2.text, response2);
|
|
36201
36634
|
}
|
|
36202
|
-
if (!
|
|
36203
|
-
throw new Error("Provide exactly one of '
|
|
36635
|
+
if (!hasPath2 && !hasUrl) {
|
|
36636
|
+
throw new Error("Provide exactly one of 'path', 'url', or 'targets'");
|
|
36204
36637
|
}
|
|
36205
|
-
if (
|
|
36206
|
-
throw new Error("Provide exactly ONE of '
|
|
36638
|
+
if (hasPath2 && hasUrl) {
|
|
36639
|
+
throw new Error("Provide exactly ONE of 'path' or 'url' — not both");
|
|
36207
36640
|
}
|
|
36208
36641
|
if (!hasUrl) {
|
|
36209
|
-
const file2 = await resolvePathArg(extCtx.cwd, params.
|
|
36642
|
+
const file2 = await resolvePathArg(extCtx.cwd, params.path);
|
|
36210
36643
|
await assertReadPathPermissions(extCtx, ctx, file2);
|
|
36211
36644
|
}
|
|
36212
|
-
const rawArgs = hasUrl ? { url: params.url } : { filePath: params.
|
|
36645
|
+
const rawArgs = hasUrl ? { url: params.url } : { filePath: params.path };
|
|
36213
36646
|
if (hasSymbols)
|
|
36214
36647
|
rawArgs.symbols = params.symbols;
|
|
36215
36648
|
if (contextLines !== undefined)
|
|
@@ -36228,7 +36661,7 @@ Pass a single \`target\`:
|
|
|
36228
36661
|
renderResult(result, _options, theme, context) {
|
|
36229
36662
|
return renderZoomResult(result, context.args, theme, context);
|
|
36230
36663
|
}
|
|
36231
|
-
});
|
|
36664
|
+
}));
|
|
36232
36665
|
}
|
|
36233
36666
|
}
|
|
36234
36667
|
|
|
@@ -36237,7 +36670,7 @@ import { StringEnum as StringEnum4 } from "@earendil-works/pi-ai";
|
|
|
36237
36670
|
import { Type as Type11 } from "typebox";
|
|
36238
36671
|
var RefactorParams = Type11.Object({
|
|
36239
36672
|
op: StringEnum4(["move", "extract", "inline"], { description: "Refactoring operation" }),
|
|
36240
|
-
|
|
36673
|
+
path: Type11.String({
|
|
36241
36674
|
description: "Source file (absolute or relative to project root)"
|
|
36242
36675
|
}),
|
|
36243
36676
|
symbol: Type11.Optional(Type11.String({ description: "Symbol name (for move, inline)" })),
|
|
@@ -36265,14 +36698,14 @@ function buildRefactorSections(args, payload, theme) {
|
|
|
36265
36698
|
if (args.op === "extract") {
|
|
36266
36699
|
return [
|
|
36267
36700
|
`${theme.fg("success", "extracted")} ${theme.fg("toolOutput", asString2(response.name) ?? args.name ?? "(function)")}`,
|
|
36268
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.
|
|
36701
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? ""))}`,
|
|
36269
36702
|
`${theme.fg("muted", "params")} ${Array.isArray(response.parameters) ? response.parameters.join(", ") || "none" : "none"}`,
|
|
36270
36703
|
`${theme.fg("muted", "return type")} ${asString2(response.return_type) ?? "unknown"}`
|
|
36271
36704
|
];
|
|
36272
36705
|
}
|
|
36273
36706
|
return [
|
|
36274
36707
|
`${theme.fg("success", "inlined")} ${theme.fg("toolOutput", asString2(response.symbol) ?? args.symbol ?? "(symbol)")}`,
|
|
36275
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.
|
|
36708
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? ""))}`,
|
|
36276
36709
|
`${theme.fg("muted", "context")} ${asString2(response.call_context) ?? "unknown"}`,
|
|
36277
36710
|
`${theme.fg("muted", "substitutions")} ${asNumber2(response.substitutions) ?? 0}`
|
|
36278
36711
|
];
|
|
@@ -36280,7 +36713,7 @@ function buildRefactorSections(args, payload, theme) {
|
|
|
36280
36713
|
function renderRefactorCall(args, theme, context) {
|
|
36281
36714
|
const summary = [
|
|
36282
36715
|
theme.fg("accent", args.op),
|
|
36283
|
-
accentPath(theme, args.
|
|
36716
|
+
accentPath(theme, args.path),
|
|
36284
36717
|
args.symbol ? theme.fg("toolOutput", args.symbol) : undefined
|
|
36285
36718
|
].filter(Boolean).join(" ");
|
|
36286
36719
|
return renderToolCall("refactor", summary, theme, context);
|
|
@@ -36291,7 +36724,7 @@ function renderRefactorResult(result, args, theme, context) {
|
|
|
36291
36724
|
return renderSections(buildRefactorSections(args, extractStructuredPayload(result), theme), context);
|
|
36292
36725
|
}
|
|
36293
36726
|
function registerRefactorTool(pi, ctx) {
|
|
36294
|
-
pi.registerTool({
|
|
36727
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36295
36728
|
name: "aft_refactor",
|
|
36296
36729
|
label: "refactor",
|
|
36297
36730
|
description: "Workspace-wide refactoring that updates imports and references across files. `move` relocates a top-level symbol (not nested functions or class methods) to another file, rewriting imports workspace-wide; a checkpoint is created first. To move/rename a whole file, use aft_move. `extract` pulls a line range into a new function (TS/JS/TSX, Python). `inline` replaces a call with the function's body.",
|
|
@@ -36318,7 +36751,7 @@ function registerRefactorTool(pi, ctx) {
|
|
|
36318
36751
|
if (params.op === "inline" && callSiteLine === undefined) {
|
|
36319
36752
|
throw new Error("'callSiteLine' is required for 'inline' op");
|
|
36320
36753
|
}
|
|
36321
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.
|
|
36754
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
36322
36755
|
const destination = !isEmptyParam(params.destination) ? await resolvePathArg(extCtx.cwd, params.destination) : undefined;
|
|
36323
36756
|
const permissionTargets = params.op === "move" && destination !== undefined ? [filePath, destination] : [filePath];
|
|
36324
36757
|
const checked = new Set;
|
|
@@ -36358,7 +36791,7 @@ function registerRefactorTool(pi, ctx) {
|
|
|
36358
36791
|
renderResult(result, _options, theme, context) {
|
|
36359
36792
|
return renderRefactorResult(result, context.args, theme, context);
|
|
36360
36793
|
}
|
|
36361
|
-
});
|
|
36794
|
+
}));
|
|
36362
36795
|
}
|
|
36363
36796
|
|
|
36364
36797
|
// src/tools/safety.ts
|
|
@@ -36371,7 +36804,7 @@ var SafetyParams = Type12.Object({
|
|
|
36371
36804
|
op: StringEnum5(["undo", "history", "checkpoint", "restore", "list"], {
|
|
36372
36805
|
description: "Safety operation"
|
|
36373
36806
|
}),
|
|
36374
|
-
|
|
36807
|
+
path: Type12.Optional(Type12.String({
|
|
36375
36808
|
description: "File path (required for history, optional for undo). Absolute or relative to project root."
|
|
36376
36809
|
})),
|
|
36377
36810
|
name: Type12.Optional(Type12.String({ description: "Checkpoint name (required for checkpoint, restore)" })),
|
|
@@ -36391,14 +36824,14 @@ function buildSafetySections(args, payload, theme) {
|
|
|
36391
36824
|
];
|
|
36392
36825
|
}
|
|
36393
36826
|
return [
|
|
36394
|
-
`${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath3(asString2(response.path) ?? args.
|
|
36827
|
+
`${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath3(asString2(response.path) ?? args.path ?? "(file)"))}`,
|
|
36395
36828
|
`${theme.fg("muted", "backup")} ${asString2(response.backup_id) ?? "—"}`
|
|
36396
36829
|
];
|
|
36397
36830
|
}
|
|
36398
36831
|
if (args.op === "history") {
|
|
36399
36832
|
const entries = asRecords2(response.entries);
|
|
36400
36833
|
const sections2 = [
|
|
36401
|
-
theme.fg("accent", shortenPath3(asString2(response.file) ?? args.
|
|
36834
|
+
theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? "(file)"))
|
|
36402
36835
|
];
|
|
36403
36836
|
if (entries.length === 0) {
|
|
36404
36837
|
sections2.push(theme.fg("muted", "No history entries."));
|
|
@@ -36448,7 +36881,7 @@ ${skipped.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(file)"
|
|
|
36448
36881
|
return sections;
|
|
36449
36882
|
}
|
|
36450
36883
|
function renderSafetyCall(args, theme, context) {
|
|
36451
|
-
const target = args.
|
|
36884
|
+
const target = args.path ?? args.name;
|
|
36452
36885
|
const summary = [theme.fg("accent", args.op), target ? accentPath(theme, target) : undefined].filter(Boolean).join(" ");
|
|
36453
36886
|
return renderToolCall("safety", summary, theme, context);
|
|
36454
36887
|
}
|
|
@@ -36458,19 +36891,19 @@ function renderSafetyResult(result, args, theme, context) {
|
|
|
36458
36891
|
return renderSections(buildSafetySections(args, extractStructuredPayload(result), theme), context);
|
|
36459
36892
|
}
|
|
36460
36893
|
function registerSafetyTool(pi, ctx) {
|
|
36461
|
-
pi.registerTool({
|
|
36894
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36462
36895
|
name: "aft_safety",
|
|
36463
36896
|
label: "safety",
|
|
36464
|
-
description: "File safety and recovery operations. Ops: `undo` (omit
|
|
36897
|
+
description: "File safety and recovery operations. Ops: `undo` (omit path to undo the entire last tool call; pass path to pop latest snapshot for one file — irreversible), `history` (list snapshots for a file), `checkpoint` (save named snapshot), `restore` (restore named checkpoint), `list` (list checkpoints). Per-file undo stack is capped at 20.",
|
|
36465
36898
|
parameters: SafetyParams,
|
|
36466
36899
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
36467
|
-
if (params.op === "history" && !params.
|
|
36468
|
-
throw new Error(`op='${params.op}' requires '
|
|
36900
|
+
if (params.op === "history" && !params.path) {
|
|
36901
|
+
throw new Error(`op='${params.op}' requires 'path'`);
|
|
36469
36902
|
}
|
|
36470
36903
|
if ((params.op === "checkpoint" || params.op === "restore") && !params.name) {
|
|
36471
36904
|
throw new Error(`op='${params.op}' requires 'name'`);
|
|
36472
36905
|
}
|
|
36473
|
-
const filePath = params.
|
|
36906
|
+
const filePath = params.path ? await resolvePathArg(extCtx.cwd, params.path) : undefined;
|
|
36474
36907
|
const fileInputs = coerceStringArray(params.files);
|
|
36475
36908
|
const files = fileInputs.length > 0 ? await Promise.all(fileInputs.map((file2) => resolvePathArg(extCtx.cwd, file2))) : undefined;
|
|
36476
36909
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
@@ -36527,7 +36960,7 @@ function registerSafetyTool(pi, ctx) {
|
|
|
36527
36960
|
renderResult(result, _options, theme, context) {
|
|
36528
36961
|
return renderSafetyResult(result, context.args, theme, context);
|
|
36529
36962
|
}
|
|
36530
|
-
});
|
|
36963
|
+
}));
|
|
36531
36964
|
}
|
|
36532
36965
|
|
|
36533
36966
|
// src/tools/semantic.ts
|
|
@@ -36554,14 +36987,6 @@ var SearchParams2 = Type13.Object({
|
|
|
36554
36987
|
default: 10,
|
|
36555
36988
|
description: "Maximum number of results (default: 10, max: 100)"
|
|
36556
36989
|
})),
|
|
36557
|
-
hint: Type13.Optional(Type13.Union([
|
|
36558
|
-
Type13.Literal("regex"),
|
|
36559
|
-
Type13.Literal("literal"),
|
|
36560
|
-
Type13.Literal("semantic"),
|
|
36561
|
-
Type13.Literal("auto")
|
|
36562
|
-
], {
|
|
36563
|
-
description: "Optional routing hint. Defaults to 'auto'."
|
|
36564
|
-
})),
|
|
36565
36990
|
includeTests: Type13.Optional(Type13.Boolean({
|
|
36566
36991
|
default: false,
|
|
36567
36992
|
description: "Include test files (*.test.*, *_test.rs, __tests__/, …) plus test-support, fixture, mock, snapshot, and corpus files. Defaults to false."
|
|
@@ -36653,13 +37078,11 @@ function renderSemanticResult(result, args, theme, context) {
|
|
|
36653
37078
|
return renderSections(buildSemanticSections(args, extractStructuredPayload(result), theme), context);
|
|
36654
37079
|
}
|
|
36655
37080
|
function registerSemanticTool(pi, ctx) {
|
|
36656
|
-
pi.registerTool({
|
|
37081
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36657
37082
|
name: "aft_search",
|
|
36658
37083
|
label: "search",
|
|
36659
37084
|
description: [
|
|
36660
|
-
"Search code with one tool: concepts, identifiers, error strings, regex, literals, and filenames are auto-routed to the right engine and returned ranked. For conceptual 'how does X work' queries, phrase a full natural-language sentence — the semantic lane is NL-aware and matches intent against docstrings and comments ('how does the ORM build and execute a query', 'where is rate limiting handled'), not just keywords. Exact names, strings, and regex stay terse ('^export', 'Cargo.lock')."
|
|
36661
|
-
"",
|
|
36662
|
-
"Set hint to 'regex', 'literal', or 'semantic' to force a lane."
|
|
37085
|
+
"Search code with one tool: concepts, identifiers, error strings, regex, literals, and filenames are auto-routed to the right engine and returned ranked. For conceptual 'how does X work' queries, phrase a full natural-language sentence — the semantic lane is NL-aware and matches intent against docstrings and comments ('how does the ORM build and execute a query', 'where is rate limiting handled'), not just keywords. Exact names, strings, and regex stay terse ('^export', 'Cargo.lock')."
|
|
36663
37086
|
].join(`
|
|
36664
37087
|
`),
|
|
36665
37088
|
parameters: SearchParams2,
|
|
@@ -36671,8 +37094,6 @@ function registerSemanticTool(pi, ctx) {
|
|
|
36671
37094
|
const req = { query: params.query };
|
|
36672
37095
|
if (params.topK !== undefined)
|
|
36673
37096
|
req.topK = params.topK;
|
|
36674
|
-
if (params.hint !== undefined)
|
|
36675
|
-
req.hint = params.hint;
|
|
36676
37097
|
if (params.includeTests !== undefined)
|
|
36677
37098
|
req.includeTests = params.includeTests;
|
|
36678
37099
|
if (params.path !== undefined)
|
|
@@ -36689,7 +37110,98 @@ function registerSemanticTool(pi, ctx) {
|
|
|
36689
37110
|
renderResult(result, _options, theme, context) {
|
|
36690
37111
|
return renderSemanticResult(result, context.args, theme, context);
|
|
36691
37112
|
}
|
|
36692
|
-
});
|
|
37113
|
+
}));
|
|
37114
|
+
}
|
|
37115
|
+
|
|
37116
|
+
// src/tool-registration.ts
|
|
37117
|
+
var ALL_ONLY_TOOLS = new Set(["aft_callgraph", "aft_delete", "aft_move", "aft_refactor"]);
|
|
37118
|
+
function resolvePiToolSurface(config2) {
|
|
37119
|
+
const surface = config2.tool_surface ?? "recommended";
|
|
37120
|
+
const disabled = new Set(config2.disabled_tools ?? []);
|
|
37121
|
+
const ok = (name) => !disabled.has(name);
|
|
37122
|
+
const allOnly = (name) => ALL_ONLY_TOOLS.has(name) && ok(name);
|
|
37123
|
+
const restrictToProjectRoot = config2.restrict_to_project_root ?? false;
|
|
37124
|
+
if (surface === "minimal") {
|
|
37125
|
+
return {
|
|
37126
|
+
hoistBash: ok("bash"),
|
|
37127
|
+
hoistRead: false,
|
|
37128
|
+
hoistWrite: false,
|
|
37129
|
+
hoistEdit: false,
|
|
37130
|
+
hoistGrep: false,
|
|
37131
|
+
restrictToProjectRoot,
|
|
37132
|
+
outline: ok("aft_outline"),
|
|
37133
|
+
zoom: ok("aft_zoom"),
|
|
37134
|
+
semantic: false,
|
|
37135
|
+
inspect: false,
|
|
37136
|
+
navigate: false,
|
|
37137
|
+
conflicts: false,
|
|
37138
|
+
importTool: false,
|
|
37139
|
+
safety: ok("aft_safety"),
|
|
37140
|
+
delete: false,
|
|
37141
|
+
move: false,
|
|
37142
|
+
astSearch: false,
|
|
37143
|
+
astReplace: false,
|
|
37144
|
+
refactor: false
|
|
37145
|
+
};
|
|
37146
|
+
}
|
|
37147
|
+
const base = {
|
|
37148
|
+
hoistBash: ok("bash"),
|
|
37149
|
+
hoistRead: ok("read"),
|
|
37150
|
+
hoistWrite: ok("write"),
|
|
37151
|
+
hoistEdit: ok("edit"),
|
|
37152
|
+
hoistGrep: ok("grep") && config2.search_index === true,
|
|
37153
|
+
restrictToProjectRoot,
|
|
37154
|
+
outline: ok("aft_outline"),
|
|
37155
|
+
zoom: ok("aft_zoom"),
|
|
37156
|
+
semantic: ok("aft_search") && config2.semantic_search === true,
|
|
37157
|
+
inspect: ok("aft_inspect") && config2.inspect?.enabled !== false,
|
|
37158
|
+
navigate: false,
|
|
37159
|
+
conflicts: ok("aft_conflicts"),
|
|
37160
|
+
importTool: ok("aft_import"),
|
|
37161
|
+
safety: ok("aft_safety"),
|
|
37162
|
+
delete: false,
|
|
37163
|
+
move: false,
|
|
37164
|
+
astSearch: ok("ast_grep_search"),
|
|
37165
|
+
astReplace: ok("ast_grep_replace"),
|
|
37166
|
+
refactor: false
|
|
37167
|
+
};
|
|
37168
|
+
if (surface === "all") {
|
|
37169
|
+
return {
|
|
37170
|
+
...base,
|
|
37171
|
+
navigate: allOnly("aft_callgraph"),
|
|
37172
|
+
delete: allOnly("aft_delete"),
|
|
37173
|
+
move: allOnly("aft_move"),
|
|
37174
|
+
refactor: allOnly("aft_refactor")
|
|
37175
|
+
};
|
|
37176
|
+
}
|
|
37177
|
+
return base;
|
|
37178
|
+
}
|
|
37179
|
+
function registerPiToolSurface(pi, ctx, surface) {
|
|
37180
|
+
if (surface.hoistBash && resolveBashConfig(ctx.config).enabled) {
|
|
37181
|
+
registerBashTool(pi, ctx, surface.semantic);
|
|
37182
|
+
}
|
|
37183
|
+
registerHoistedTools(pi, ctx, surface);
|
|
37184
|
+
if (surface.outline || surface.zoom) {
|
|
37185
|
+
registerReadingTools(pi, ctx, surface);
|
|
37186
|
+
}
|
|
37187
|
+
if (surface.semantic)
|
|
37188
|
+
registerSemanticTool(pi, ctx);
|
|
37189
|
+
if (surface.inspect)
|
|
37190
|
+
registerInspectTool(pi, ctx);
|
|
37191
|
+
if (surface.navigate)
|
|
37192
|
+
registerNavigateTool(pi, ctx);
|
|
37193
|
+
if (surface.conflicts)
|
|
37194
|
+
registerConflictsTool(pi, ctx);
|
|
37195
|
+
if (surface.importTool)
|
|
37196
|
+
registerImportTools(pi, ctx);
|
|
37197
|
+
if (surface.safety && ctx.config.backup?.enabled !== false)
|
|
37198
|
+
registerSafetyTool(pi, ctx);
|
|
37199
|
+
if (surface.astSearch || surface.astReplace)
|
|
37200
|
+
registerAstTools(pi, ctx, surface);
|
|
37201
|
+
if (surface.delete || surface.move)
|
|
37202
|
+
registerFsTools(pi, ctx, surface);
|
|
37203
|
+
if (surface.refactor)
|
|
37204
|
+
registerRefactorTool(pi, ctx);
|
|
36693
37205
|
}
|
|
36694
37206
|
|
|
36695
37207
|
// src/workflow-hints.ts
|
|
@@ -36720,18 +37232,18 @@ function buildWorkflowHints(opts) {
|
|
|
36720
37232
|
}
|
|
36721
37233
|
if (hasOutline && hasZoom && (hasGrep || hasSearch)) {
|
|
36722
37234
|
const searchName = hasSearch ? "aft_search" : grepName;
|
|
36723
|
-
const locate = hasSearch ?
|
|
37235
|
+
const locate = hasSearch ? "`aft_search` is the primary code-search tool: one call auto-routes concepts, identifiers, regex, error strings, and literals." : `\`${grepName}\` (the tool — indexed and ranked) locates code.`;
|
|
36724
37236
|
const readName = opts.hoistBuiltins ? "read" : "aft_read";
|
|
36725
37237
|
sections.push([
|
|
36726
37238
|
`**Code exploration**: ${locate} Then \`aft_outline\` for structure → \`aft_zoom\` for symbol(s). DO NOT run \`grep\`/\`rg\`/\`find\`/\`sed\`/\`cat\` through \`bash\` to locate or read code — the bash path is unindexed, unranked, serial, and routinely surfaces the wrong hit. Keep \`bash\` for shell facts (git state, file metadata, processes). Reflex translations:`,
|
|
36727
37239
|
`- \`grep -rn "handleAuth" src/\` in bash → \`${searchName}({ query: "handleAuth" })\``,
|
|
36728
37240
|
`- \`find . -name "*.ts" | xargs grep watcher\` in bash → \`${searchName}({ query: "watcher invalidation" })\` (concepts work too)`,
|
|
36729
|
-
`- \`sed -n '100,160p' app.ts\` / \`cat app.ts\` in bash → \`${readName}({
|
|
37241
|
+
`- \`sed -n '100,160p' app.ts\` / \`cat app.ts\` in bash → \`${readName}({ path: "app.ts", startLine: 100, endLine: 160 })\``
|
|
36730
37242
|
].join(`
|
|
36731
37243
|
`));
|
|
36732
37244
|
}
|
|
36733
37245
|
if (hasInspect) {
|
|
36734
|
-
sections.push("**Codebase health & diagnostics**: AFT does not surface compile/type errors automatically after edits — pull them with `aft_inspect`. Run it after a batch of edits and before you run tests or commit, when starting in unfamiliar code, or before a refactor/review. One call summarizes diagnostics (compile/type errors), TODOs, metrics, dead code, unused exports, and duplicates; pass `sections` for focused drill-down and `scope` to actively pull diagnostics for a specific file or directory. Its diagnostics are a fast checkpoint, not the authority — a clean `tsc` / `cargo check` / `pyright` run is the real gate. Treat
|
|
37246
|
+
sections.push("**Codebase health & diagnostics**: AFT does not surface compile/type errors automatically after edits — pull them with `aft_inspect`. Run it after a batch of edits and before you run tests or commit, when starting in unfamiliar code, or before a refactor/review. One call summarizes diagnostics (compile/type errors), TODOs, metrics, dead code, unused exports, and duplicates; pass `sections` for focused drill-down and `scope` to actively pull diagnostics for a specific file or directory. Its diagnostics are a fast checkpoint, not the authority — a clean `tsc` / `cargo check` / `pyright` run is the real gate. Treat stale_categories/pending_categories as stale or incomplete cache state. AFT schedules a Tier-2 refresh after its next idle or inspect-triggered background run; use one later normal aft_inspect after that refresh, not a polling loop.");
|
|
36735
37247
|
sections.push("**AFT status bar**: tool results may end with a one-line health bar `[AFT E<errors> W<warnings> | D<dead-code> U<unused-exports> C<clone/dup-groups> | T<todos>]` — an IDE-style glance that appears when a count changes. `E`/`W` are live LSP diagnostics for files touched this session (your universal compile-error signal across every language with an LSP). A `~` before `D` means the dead-code/unused/dup counts predate your latest edit — run `aft_inspect` for current numbers and detail. When `E>0`, you likely just introduced errors; investigate before moving on.");
|
|
36736
37248
|
}
|
|
36737
37249
|
if (hasNavigate) {
|
|
@@ -36851,14 +37363,13 @@ var PLUGIN_VERSION = (() => {
|
|
|
36851
37363
|
return "0.0.0";
|
|
36852
37364
|
}
|
|
36853
37365
|
})();
|
|
36854
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
37366
|
+
var ANNOUNCEMENT_VERSION = "0.49.0";
|
|
36855
37367
|
var ANNOUNCEMENT_FEATURES = [
|
|
36856
|
-
"
|
|
36857
|
-
"
|
|
36858
|
-
"
|
|
37368
|
+
"One unified tool surface: every tool now takes `path` (old spellings keep working), `edit` has one calling shape, and `occurrence` is 1-based. Fewer ways for models to get a call wrong; prompt caches re-warm once after this upgrade.",
|
|
37369
|
+
"aft_search now routes queries itself — the hint parameter is gone, and searches that find no exact match are re-ranked by their terms instead of returning nothing.",
|
|
37370
|
+
"Corporate and custom certificate authorities now work: HTTPS requests verify through the OS trust store (Keychain, Windows certificate store, SSL_CERT_FILE on Linux)."
|
|
36859
37371
|
];
|
|
36860
37372
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
|
|
36861
|
-
var ALL_ONLY_TOOLS = new Set(["aft_callgraph", "aft_delete", "aft_move", "aft_refactor"]);
|
|
36862
37373
|
var pendingEagerWarnings = new Map;
|
|
36863
37374
|
function isConfigureWarning(value) {
|
|
36864
37375
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -36939,67 +37450,6 @@ async function handleConfigureWarningsForSession(context) {
|
|
|
36939
37450
|
projectRoot: context.projectRoot
|
|
36940
37451
|
}, combinedWarnings);
|
|
36941
37452
|
}
|
|
36942
|
-
function resolveToolSurface(config2) {
|
|
36943
|
-
const surface = config2.tool_surface ?? "recommended";
|
|
36944
|
-
const disabled = new Set(config2.disabled_tools ?? []);
|
|
36945
|
-
const ok = (name) => !disabled.has(name);
|
|
36946
|
-
const allOnly = (name) => ALL_ONLY_TOOLS.has(name) && ok(name);
|
|
36947
|
-
const restrictToProjectRoot = config2.restrict_to_project_root ?? false;
|
|
36948
|
-
if (surface === "minimal") {
|
|
36949
|
-
return {
|
|
36950
|
-
hoistBash: ok("bash"),
|
|
36951
|
-
hoistRead: false,
|
|
36952
|
-
hoistWrite: false,
|
|
36953
|
-
hoistEdit: false,
|
|
36954
|
-
hoistGrep: false,
|
|
36955
|
-
restrictToProjectRoot,
|
|
36956
|
-
outline: ok("aft_outline"),
|
|
36957
|
-
zoom: ok("aft_zoom"),
|
|
36958
|
-
semantic: false,
|
|
36959
|
-
inspect: false,
|
|
36960
|
-
navigate: false,
|
|
36961
|
-
conflicts: false,
|
|
36962
|
-
importTool: false,
|
|
36963
|
-
safety: ok("aft_safety"),
|
|
36964
|
-
delete: false,
|
|
36965
|
-
move: false,
|
|
36966
|
-
astSearch: false,
|
|
36967
|
-
astReplace: false,
|
|
36968
|
-
refactor: false
|
|
36969
|
-
};
|
|
36970
|
-
}
|
|
36971
|
-
const base = {
|
|
36972
|
-
hoistBash: ok("bash"),
|
|
36973
|
-
hoistRead: ok("read"),
|
|
36974
|
-
hoistWrite: ok("write"),
|
|
36975
|
-
hoistEdit: ok("edit"),
|
|
36976
|
-
hoistGrep: ok("grep") && config2.search_index === true,
|
|
36977
|
-
restrictToProjectRoot,
|
|
36978
|
-
outline: ok("aft_outline"),
|
|
36979
|
-
zoom: ok("aft_zoom"),
|
|
36980
|
-
semantic: ok("aft_search") && config2.semantic_search === true,
|
|
36981
|
-
inspect: ok("aft_inspect") && config2.inspect?.enabled !== false,
|
|
36982
|
-
navigate: false,
|
|
36983
|
-
conflicts: ok("aft_conflicts"),
|
|
36984
|
-
importTool: ok("aft_import"),
|
|
36985
|
-
safety: ok("aft_safety"),
|
|
36986
|
-
delete: false,
|
|
36987
|
-
move: false,
|
|
36988
|
-
astSearch: ok("ast_grep_search"),
|
|
36989
|
-
astReplace: ok("ast_grep_replace"),
|
|
36990
|
-
refactor: false
|
|
36991
|
-
};
|
|
36992
|
-
if (surface === "all") {
|
|
36993
|
-
return {
|
|
36994
|
-
...base,
|
|
36995
|
-
navigate: allOnly("aft_callgraph"),
|
|
36996
|
-
delete: allOnly("aft_delete"),
|
|
36997
|
-
move: allOnly("aft_move"),
|
|
36998
|
-
refactor: allOnly("aft_refactor")
|
|
36999
|
-
};
|
|
37000
|
-
}
|
|
37001
|
-
return base;
|
|
37002
|
-
}
|
|
37003
37453
|
async function src_default(pi) {
|
|
37004
37454
|
const deliverConfigMigrationWarnings = (messages) => {
|
|
37005
37455
|
for (const message of messages) {
|
|
@@ -37210,7 +37660,7 @@ ${lines}
|
|
|
37210
37660
|
});
|
|
37211
37661
|
}
|
|
37212
37662
|
pool.setConfigureOverride("harness", "pi");
|
|
37213
|
-
pool.setConfigureOverride("aft_search_registered",
|
|
37663
|
+
pool.setConfigureOverride("aft_search_registered", resolvePiToolSurface(config2).semantic);
|
|
37214
37664
|
const ctx = { pool, config: config2, storageDir };
|
|
37215
37665
|
if (onnxRuntimePromise) {
|
|
37216
37666
|
onnxRuntimePromise.then((ortDylibDir) => {
|
|
@@ -37249,41 +37699,8 @@ ${lines}
|
|
|
37249
37699
|
if (ANNOUNCEMENT_VERSION && ANNOUNCEMENT_FEATURES.length > 0) {
|
|
37250
37700
|
sendFeatureAnnouncement(ANNOUNCEMENT_VERSION, ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER, storageDir);
|
|
37251
37701
|
}
|
|
37252
|
-
const surface =
|
|
37253
|
-
|
|
37254
|
-
registerBashTool(pi, ctx, surface.semantic);
|
|
37255
|
-
}
|
|
37256
|
-
registerHoistedTools(pi, ctx, surface);
|
|
37257
|
-
if (surface.outline || surface.zoom) {
|
|
37258
|
-
registerReadingTools(pi, ctx, surface);
|
|
37259
|
-
}
|
|
37260
|
-
if (surface.semantic) {
|
|
37261
|
-
registerSemanticTool(pi, ctx);
|
|
37262
|
-
}
|
|
37263
|
-
if (surface.inspect) {
|
|
37264
|
-
registerInspectTool(pi, ctx);
|
|
37265
|
-
}
|
|
37266
|
-
if (surface.navigate) {
|
|
37267
|
-
registerNavigateTool(pi, ctx);
|
|
37268
|
-
}
|
|
37269
|
-
if (surface.conflicts) {
|
|
37270
|
-
registerConflictsTool(pi, ctx);
|
|
37271
|
-
}
|
|
37272
|
-
if (surface.importTool) {
|
|
37273
|
-
registerImportTools(pi, ctx);
|
|
37274
|
-
}
|
|
37275
|
-
if (surface.safety && config2.backup?.enabled !== false) {
|
|
37276
|
-
registerSafetyTool(pi, ctx);
|
|
37277
|
-
}
|
|
37278
|
-
if (surface.astSearch || surface.astReplace) {
|
|
37279
|
-
registerAstTools(pi, ctx, surface);
|
|
37280
|
-
}
|
|
37281
|
-
if (surface.delete || surface.move) {
|
|
37282
|
-
registerFsTools(pi, ctx, surface);
|
|
37283
|
-
}
|
|
37284
|
-
if (surface.refactor) {
|
|
37285
|
-
registerRefactorTool(pi, ctx);
|
|
37286
|
-
}
|
|
37702
|
+
const surface = resolvePiToolSurface(config2);
|
|
37703
|
+
registerPiToolSurface(pi, ctx, surface);
|
|
37287
37704
|
registerWorkflowHints(pi, config2, surface);
|
|
37288
37705
|
registerStatusCommand(pi, ctx);
|
|
37289
37706
|
pi.on("tool_result", async (event, extCtx) => {
|
|
@@ -37349,7 +37766,7 @@ ${lines}
|
|
|
37349
37766
|
var __test__2 = {
|
|
37350
37767
|
enqueueConfigParseWarnings,
|
|
37351
37768
|
bridgeDirectoryFromCallback,
|
|
37352
|
-
resolveToolSurface,
|
|
37769
|
+
resolveToolSurface: resolvePiToolSurface,
|
|
37353
37770
|
handleConfigureWarningsForSession,
|
|
37354
37771
|
shouldPrepareOnnxRuntime,
|
|
37355
37772
|
createVersionMismatchHandler
|