@cortexkit/aft-pi 0.48.1 → 0.49.1
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 +818 -313
- 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
|
@@ -7952,6 +7952,7 @@ function sleep(ms) {
|
|
|
7952
7952
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
7953
7953
|
}
|
|
7954
7954
|
// ../aft-bridge/dist/bash-hints.js
|
|
7955
|
+
import * as fs from "node:fs";
|
|
7955
7956
|
import * as os from "node:os";
|
|
7956
7957
|
import * as path from "node:path";
|
|
7957
7958
|
var CONFLICT_HINT = `
|
|
@@ -7960,6 +7961,7 @@ var CONFLICT_HINT = `
|
|
|
7960
7961
|
var GREP_SEARCH_AFT_SEARCH_HINT = "DO NOT search code by running grep/rg in bash — it is unindexed, unranked, and serial. Use the `aft_search` tool instead (it auto-routes concepts, identifiers, regex, and literals).";
|
|
7961
7962
|
var GREP_SEARCH_GREP_HINT = "DO NOT search code by running grep/rg in bash — it is unindexed, unranked, and serial. Use the `grep` tool instead (indexed and ranked).";
|
|
7962
7963
|
var GREP_SEARCH_HINT_PREFIX = "DO NOT search code by running grep/rg in bash —";
|
|
7964
|
+
var GREP_SEARCH_FRESHNESS_WINDOW_MS = 60000;
|
|
7963
7965
|
function maybeAppendConflictsHint(output) {
|
|
7964
7966
|
if (!output.includes("Automatic merge failed; fix conflicts"))
|
|
7965
7967
|
return output;
|
|
@@ -8031,12 +8033,19 @@ function shouldSuppressGrepSearchHint(command, projectRoot) {
|
|
|
8031
8033
|
const operands = collectPathOperands(firstStage, firstToken.end);
|
|
8032
8034
|
if (operands.length === 0)
|
|
8033
8035
|
return false;
|
|
8036
|
+
let sawInProjectOperand = false;
|
|
8034
8037
|
for (const operand of operands) {
|
|
8035
8038
|
if (isDynamicPathOperand(operand))
|
|
8036
8039
|
continue;
|
|
8037
|
-
|
|
8038
|
-
|
|
8040
|
+
const resolvedOperand = resolvePathOperand(effectiveCwd, operand);
|
|
8041
|
+
if (!isPathInsideProject(resolvedRoot, effectiveCwd, operand))
|
|
8042
|
+
continue;
|
|
8043
|
+
sawInProjectOperand = true;
|
|
8044
|
+
if (shouldSuppressResolvedPath(resolvedOperand))
|
|
8045
|
+
return true;
|
|
8039
8046
|
}
|
|
8047
|
+
if (sawInProjectOperand)
|
|
8048
|
+
return false;
|
|
8040
8049
|
}
|
|
8041
8050
|
return sawCodeSearchStatement;
|
|
8042
8051
|
}
|
|
@@ -8135,6 +8144,17 @@ function isPathInsideProject(resolvedRoot, baseCwd, operand) {
|
|
|
8135
8144
|
const rel = path.relative(resolvedRoot, resolved);
|
|
8136
8145
|
return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
8137
8146
|
}
|
|
8147
|
+
function shouldSuppressResolvedPath(resolved) {
|
|
8148
|
+
try {
|
|
8149
|
+
const stats = fs.statSync(resolved);
|
|
8150
|
+
if (stats.isFile())
|
|
8151
|
+
return true;
|
|
8152
|
+
const ageMs = Date.now() - stats.mtimeMs;
|
|
8153
|
+
return ageMs >= 0 && ageMs < GREP_SEARCH_FRESHNESS_WINDOW_MS;
|
|
8154
|
+
} catch {
|
|
8155
|
+
return false;
|
|
8156
|
+
}
|
|
8157
|
+
}
|
|
8138
8158
|
function splitTopLevelStatements(command) {
|
|
8139
8159
|
const statements = [];
|
|
8140
8160
|
let start = 0;
|
|
@@ -9623,14 +9643,6 @@ function coerceStringArray(value) {
|
|
|
9623
9643
|
}
|
|
9624
9644
|
return [];
|
|
9625
9645
|
}
|
|
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
9646
|
function coerceTargetParam(value) {
|
|
9635
9647
|
if (Array.isArray(value)) {
|
|
9636
9648
|
return value.filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
@@ -9744,7 +9756,7 @@ function formatDroppedKeyWarnings(dropped) {
|
|
|
9744
9756
|
// ../aft-bridge/dist/downloader.js
|
|
9745
9757
|
import { spawnSync } from "node:child_process";
|
|
9746
9758
|
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
9747
|
-
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, mkdirSync, openSync, readFileSync as readFileSync3, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
9759
|
+
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync as existsSync2, mkdirSync, openSync, readFileSync as readFileSync3, renameSync, rmSync, statSync as statSync2, unlinkSync, writeSync } from "node:fs";
|
|
9748
9760
|
import { homedir as homedir4 } from "node:os";
|
|
9749
9761
|
import { join as join3 } from "node:path";
|
|
9750
9762
|
import { Readable } from "node:stream";
|
|
@@ -9777,7 +9789,7 @@ function readBinaryVersion(binaryPath) {
|
|
|
9777
9789
|
const result = spawnSync(binaryPath, ["--version"], {
|
|
9778
9790
|
encoding: "utf-8",
|
|
9779
9791
|
stdio: ["pipe", "pipe", "pipe"],
|
|
9780
|
-
timeout:
|
|
9792
|
+
timeout: 60000
|
|
9781
9793
|
});
|
|
9782
9794
|
const stdoutVersion = result.stdout?.trim();
|
|
9783
9795
|
const stderrVersion = result.stderr?.trim();
|
|
@@ -9982,7 +9994,7 @@ async function acquireDownloadLock(lockPath) {
|
|
|
9982
9994
|
if (code !== "EEXIST")
|
|
9983
9995
|
throw err;
|
|
9984
9996
|
try {
|
|
9985
|
-
const ageMs = Date.now() -
|
|
9997
|
+
const ageMs = Date.now() - statSync2(lockPath).mtimeMs;
|
|
9986
9998
|
if (ageMs > DOWNLOAD_LOCK_STALE_MS) {
|
|
9987
9999
|
rmSync(lockPath, { force: true });
|
|
9988
10000
|
continue;
|
|
@@ -10185,6 +10197,23 @@ ${reflowText}`;
|
|
|
10185
10197
|
}
|
|
10186
10198
|
return " Auto-formatted.";
|
|
10187
10199
|
}
|
|
10200
|
+
// ../aft-bridge/dist/error-contract.js
|
|
10201
|
+
class AftToolError extends Error {
|
|
10202
|
+
code;
|
|
10203
|
+
response;
|
|
10204
|
+
constructor(message, code, response) {
|
|
10205
|
+
const cause = { code, message, response };
|
|
10206
|
+
super(message, { cause });
|
|
10207
|
+
this.name = "AftToolError";
|
|
10208
|
+
this.code = code;
|
|
10209
|
+
this.response = response;
|
|
10210
|
+
}
|
|
10211
|
+
}
|
|
10212
|
+
function toolErrorFromResponse(command, response) {
|
|
10213
|
+
const code = typeof response.code === "string" && response.code.length > 0 ? response.code : "unknown_error";
|
|
10214
|
+
const message = typeof response.message === "string" && response.message.length > 0 ? response.message : `${command} failed`;
|
|
10215
|
+
return new AftToolError(message, code, response);
|
|
10216
|
+
}
|
|
10188
10217
|
// ../aft-bridge/dist/format.js
|
|
10189
10218
|
function compressionSavingsPercent(original, compressed) {
|
|
10190
10219
|
if (original <= 0)
|
|
@@ -10208,7 +10237,7 @@ function stripJsoncSymbols(value) {
|
|
|
10208
10237
|
}
|
|
10209
10238
|
// ../aft-bridge/dist/migration.js
|
|
10210
10239
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
10211
|
-
import { closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync2, statSync as
|
|
10240
|
+
import { closeSync as closeSync3, existsSync as existsSync5, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
10212
10241
|
import { homedir as homedir8, tmpdir } from "node:os";
|
|
10213
10242
|
import { basename, dirname as dirname3, join as join7, resolve as resolve4 } from "node:path";
|
|
10214
10243
|
|
|
@@ -10708,7 +10737,7 @@ function acquireConfigMigrationLock(lockDir) {
|
|
|
10708
10737
|
if (code !== "EEXIST")
|
|
10709
10738
|
throw err;
|
|
10710
10739
|
try {
|
|
10711
|
-
const ageMs = Date.now() -
|
|
10740
|
+
const ageMs = Date.now() - statSync3(lockDir).mtimeMs;
|
|
10712
10741
|
if (ageMs > 60000) {
|
|
10713
10742
|
rmSync2(lockDir, { recursive: true, force: true });
|
|
10714
10743
|
continue;
|
|
@@ -10994,7 +11023,7 @@ async function ensureStorageMigrated(opts) {
|
|
|
10994
11023
|
throw new Error(`AFT storage migration failed (${detail}). ` + `Harness: ${opts.harness}. Legacy: ${legacyRoot}. Target: ${newRoot}. ` + `See log: ${logPath}. ` + `Plugin load aborted to prevent legacy/new state divergence.` + (stderrTail ? ` Stderr tail: ${stderrTail}` : "") + (stdoutTail ? ` Stdout tail: ${stdoutTail}` : ""));
|
|
10995
11024
|
}
|
|
10996
11025
|
// ../aft-bridge/dist/npm-resolver.js
|
|
10997
|
-
import { readdirSync, statSync as
|
|
11026
|
+
import { readdirSync, statSync as statSync4 } from "node:fs";
|
|
10998
11027
|
import { homedir as homedir9 } from "node:os";
|
|
10999
11028
|
import { delimiter, dirname as dirname4, isAbsolute as isAbsolute3, join as join8 } from "node:path";
|
|
11000
11029
|
function defaultDeps() {
|
|
@@ -11010,7 +11039,7 @@ function npmBinaryName(platform) {
|
|
|
11010
11039
|
}
|
|
11011
11040
|
function isFile(p) {
|
|
11012
11041
|
try {
|
|
11013
|
-
return
|
|
11042
|
+
return statSync4(p).isFile();
|
|
11014
11043
|
} catch {
|
|
11015
11044
|
return false;
|
|
11016
11045
|
}
|
|
@@ -11110,7 +11139,7 @@ function npmSpawnEnv(resolved, baseEnv = process.env) {
|
|
|
11110
11139
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
11111
11140
|
import { execFileSync } from "node:child_process";
|
|
11112
11141
|
import { createHash as createHash3 } from "node:crypto";
|
|
11113
|
-
import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync2, readFileSync as readFileSync6, readlinkSync, realpathSync, rmSync as rmSync3, statSync as
|
|
11142
|
+
import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync2, readFileSync as readFileSync6, readlinkSync, realpathSync, rmSync as rmSync3, statSync as statSync5, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
11114
11143
|
import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute4, join as join9, relative as relative2, resolve as resolve5, win32 } from "node:path";
|
|
11115
11144
|
import { Readable as Readable2 } from "node:stream";
|
|
11116
11145
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
@@ -11240,7 +11269,7 @@ function cleanupAbandonedStagingDirs(onnxBaseDir) {
|
|
|
11240
11269
|
abandoned = true;
|
|
11241
11270
|
} else {
|
|
11242
11271
|
try {
|
|
11243
|
-
const ageMs = Date.now() -
|
|
11272
|
+
const ageMs = Date.now() - statSync5(stagingDir).mtimeMs;
|
|
11244
11273
|
abandoned = ageMs > STALE_LOCK_MS;
|
|
11245
11274
|
} catch {
|
|
11246
11275
|
abandoned = true;
|
|
@@ -11673,7 +11702,7 @@ function writeOnnxInstalledMeta(installDir, version, sha256, archiveSha256) {
|
|
|
11673
11702
|
function readOnnxInstalledMeta(installDir) {
|
|
11674
11703
|
const path2 = join9(installDir, ONNX_INSTALLED_META_FILE);
|
|
11675
11704
|
try {
|
|
11676
|
-
if (!
|
|
11705
|
+
if (!statSync5(path2).isFile())
|
|
11677
11706
|
return null;
|
|
11678
11707
|
const raw = readFileSync6(path2, "utf8");
|
|
11679
11708
|
const parsed = JSON.parse(raw);
|
|
@@ -11724,7 +11753,7 @@ ${new Date().toISOString()}
|
|
|
11724
11753
|
const parsed = Number.parseInt(firstLine, 10);
|
|
11725
11754
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
11726
11755
|
owningPid = parsed;
|
|
11727
|
-
lockMtimeMs =
|
|
11756
|
+
lockMtimeMs = statSync5(lockPath).mtimeMs;
|
|
11728
11757
|
} catch {
|
|
11729
11758
|
return tryClaim();
|
|
11730
11759
|
}
|
|
@@ -11807,6 +11836,488 @@ function isProcessAlive(pid) {
|
|
|
11807
11836
|
return true;
|
|
11808
11837
|
}
|
|
11809
11838
|
}
|
|
11839
|
+
// ../aft-bridge/dist/path-aliases.js
|
|
11840
|
+
class InvalidRequestError extends AftToolError {
|
|
11841
|
+
constructor(message) {
|
|
11842
|
+
super(message, "invalid_request", {
|
|
11843
|
+
success: false,
|
|
11844
|
+
code: "invalid_request",
|
|
11845
|
+
message
|
|
11846
|
+
});
|
|
11847
|
+
this.name = "InvalidRequestError";
|
|
11848
|
+
}
|
|
11849
|
+
}
|
|
11850
|
+
function isWellFormedUnicodeString(value) {
|
|
11851
|
+
for (let index = 0;index < value.length; index++) {
|
|
11852
|
+
const codeUnit = value.charCodeAt(index);
|
|
11853
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
11854
|
+
const next = value.charCodeAt(index + 1);
|
|
11855
|
+
if (next < 56320 || next > 57343 || Number.isNaN(next))
|
|
11856
|
+
return false;
|
|
11857
|
+
index++;
|
|
11858
|
+
} else if (codeUnit >= 56320 && codeUnit <= 57343) {
|
|
11859
|
+
return false;
|
|
11860
|
+
}
|
|
11861
|
+
}
|
|
11862
|
+
return true;
|
|
11863
|
+
}
|
|
11864
|
+
function hasOwn(record, key) {
|
|
11865
|
+
return Object.hasOwn(record, key);
|
|
11866
|
+
}
|
|
11867
|
+
function invalidPathValue(property) {
|
|
11868
|
+
throw new InvalidRequestError(`'${property}' must be a non-empty well-formed Unicode string`);
|
|
11869
|
+
}
|
|
11870
|
+
function pathValue(record, property) {
|
|
11871
|
+
const value = record[property];
|
|
11872
|
+
if (typeof value !== "string" || value.length === 0 || !isWellFormedUnicodeString(value)) {
|
|
11873
|
+
invalidPathValue(property);
|
|
11874
|
+
}
|
|
11875
|
+
return value;
|
|
11876
|
+
}
|
|
11877
|
+
function normalizeAliasPair(record, canonical, legacy, required) {
|
|
11878
|
+
const hasCanonical = hasOwn(record, canonical);
|
|
11879
|
+
const hasLegacy = hasOwn(record, legacy);
|
|
11880
|
+
if (!hasCanonical && !hasLegacy) {
|
|
11881
|
+
if (required) {
|
|
11882
|
+
throw new InvalidRequestError(`'${canonical}' is required`);
|
|
11883
|
+
}
|
|
11884
|
+
return;
|
|
11885
|
+
}
|
|
11886
|
+
if (hasCanonical && hasLegacy) {
|
|
11887
|
+
let canonicalValue;
|
|
11888
|
+
let legacyValue;
|
|
11889
|
+
try {
|
|
11890
|
+
canonicalValue = pathValue(record, canonical);
|
|
11891
|
+
legacyValue = pathValue(record, legacy);
|
|
11892
|
+
} catch {
|
|
11893
|
+
throw new InvalidRequestError(`Invalid request: '${canonical}' and '${legacy}' must both be non-empty well-formed Unicode strings`);
|
|
11894
|
+
}
|
|
11895
|
+
if (canonicalValue !== legacyValue) {
|
|
11896
|
+
throw new InvalidRequestError(`Invalid request: '${canonical}' and '${legacy}' must contain equal decoded strings`);
|
|
11897
|
+
}
|
|
11898
|
+
delete record[legacy];
|
|
11899
|
+
return;
|
|
11900
|
+
}
|
|
11901
|
+
if (hasCanonical) {
|
|
11902
|
+
pathValue(record, canonical);
|
|
11903
|
+
return;
|
|
11904
|
+
}
|
|
11905
|
+
record[canonical] = pathValue(record, legacy);
|
|
11906
|
+
delete record[legacy];
|
|
11907
|
+
}
|
|
11908
|
+
function validateOptionalCanonicalPath(record, property) {
|
|
11909
|
+
if (hasOwn(record, property))
|
|
11910
|
+
pathValue(record, property);
|
|
11911
|
+
}
|
|
11912
|
+
function normalizeZoomTargets(record) {
|
|
11913
|
+
if (!hasOwn(record, "targets"))
|
|
11914
|
+
return;
|
|
11915
|
+
const targets = record.targets;
|
|
11916
|
+
const normalizeTarget = (target, index) => {
|
|
11917
|
+
if (!target || typeof target !== "object" || Array.isArray(target)) {
|
|
11918
|
+
throw new InvalidRequestError(`'targets[${index}].path' must be a non-empty string`);
|
|
11919
|
+
}
|
|
11920
|
+
const source = target;
|
|
11921
|
+
const emptyTarget = source.symbol === "" && (hasOwn(source, "path") && source.path === "" || hasOwn(source, "filePath") && source.filePath === "");
|
|
11922
|
+
if (emptyTarget)
|
|
11923
|
+
return { ...source };
|
|
11924
|
+
const normalized = { ...source };
|
|
11925
|
+
try {
|
|
11926
|
+
normalizeAliasPair(normalized, "path", "filePath", true);
|
|
11927
|
+
} catch (error2) {
|
|
11928
|
+
if (error2 instanceof InvalidRequestError) {
|
|
11929
|
+
throw new InvalidRequestError(error2.message.replace("'filePath'", `'targets[${index}].filePath'`).replace("'path'", `'targets[${index}].path'`));
|
|
11930
|
+
}
|
|
11931
|
+
throw error2;
|
|
11932
|
+
}
|
|
11933
|
+
return normalized;
|
|
11934
|
+
};
|
|
11935
|
+
if (Array.isArray(targets)) {
|
|
11936
|
+
if (targets.length === 0)
|
|
11937
|
+
return;
|
|
11938
|
+
record.targets = targets.map(normalizeTarget);
|
|
11939
|
+
return;
|
|
11940
|
+
}
|
|
11941
|
+
if (targets && typeof targets === "object") {
|
|
11942
|
+
record.targets = normalizeTarget(targets, 0);
|
|
11943
|
+
}
|
|
11944
|
+
}
|
|
11945
|
+
function bareToolName(toolName) {
|
|
11946
|
+
const bare = toolName.startsWith("aft_") ? toolName.slice(4) : toolName;
|
|
11947
|
+
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") {
|
|
11948
|
+
return bare;
|
|
11949
|
+
}
|
|
11950
|
+
return;
|
|
11951
|
+
}
|
|
11952
|
+
function prepareCanonicalPathArguments(toolName, rawArguments) {
|
|
11953
|
+
if (!rawArguments || typeof rawArguments !== "object" || Array.isArray(rawArguments)) {
|
|
11954
|
+
throw new InvalidRequestError("tool arguments must be an object");
|
|
11955
|
+
}
|
|
11956
|
+
const tool = bareToolName(toolName);
|
|
11957
|
+
const record = { ...rawArguments };
|
|
11958
|
+
if (!tool)
|
|
11959
|
+
return record;
|
|
11960
|
+
switch (tool) {
|
|
11961
|
+
case "read":
|
|
11962
|
+
case "write":
|
|
11963
|
+
case "edit":
|
|
11964
|
+
case "move":
|
|
11965
|
+
case "import":
|
|
11966
|
+
case "refactor":
|
|
11967
|
+
normalizeAliasPair(record, "path", "filePath", true);
|
|
11968
|
+
break;
|
|
11969
|
+
case "zoom":
|
|
11970
|
+
normalizeAliasPair(record, "path", "filePath", false);
|
|
11971
|
+
normalizeZoomTargets(record);
|
|
11972
|
+
break;
|
|
11973
|
+
case "callgraph":
|
|
11974
|
+
normalizeAliasPair(record, "path", "filePath", true);
|
|
11975
|
+
normalizeAliasPair(record, "toPath", "toFile", false);
|
|
11976
|
+
break;
|
|
11977
|
+
case "safety":
|
|
11978
|
+
normalizeAliasPair(record, "path", "filePath", false);
|
|
11979
|
+
break;
|
|
11980
|
+
case "grep":
|
|
11981
|
+
case "search":
|
|
11982
|
+
case "conflicts":
|
|
11983
|
+
validateOptionalCanonicalPath(record, "path");
|
|
11984
|
+
break;
|
|
11985
|
+
}
|
|
11986
|
+
return record;
|
|
11987
|
+
}
|
|
11988
|
+
var EDIT_ROOT_COMPATIBILITY_KEYS = new Set([
|
|
11989
|
+
"oldString",
|
|
11990
|
+
"newString",
|
|
11991
|
+
"replaceAll",
|
|
11992
|
+
"occurrence"
|
|
11993
|
+
]);
|
|
11994
|
+
var EDIT_ROOT_CANONICAL_KEYS = new Set(["path", "appendContent", "edits", "symbol", "content"]);
|
|
11995
|
+
var EDIT_ITEM_KEYS = new Set([
|
|
11996
|
+
"oldString",
|
|
11997
|
+
"newString",
|
|
11998
|
+
"replaceAll",
|
|
11999
|
+
"occurrence",
|
|
12000
|
+
"startLine",
|
|
12001
|
+
"endLine",
|
|
12002
|
+
"content"
|
|
12003
|
+
]);
|
|
12004
|
+
var ASCII_WHITESPACE = /^[\t\n\v\f\r ]+$/;
|
|
12005
|
+
var ASCII_TRIM = /^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g;
|
|
12006
|
+
var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
|
|
12007
|
+
function prepareCanonicalEditArguments(toolName, rawArguments) {
|
|
12008
|
+
if (!rawArguments || typeof rawArguments !== "object" || Array.isArray(rawArguments)) {
|
|
12009
|
+
throw new InvalidRequestError("tool arguments must be an object");
|
|
12010
|
+
}
|
|
12011
|
+
const raw = rawArguments;
|
|
12012
|
+
const record = copyOwnProperties(raw);
|
|
12013
|
+
normalizeEditPathAlias(record);
|
|
12014
|
+
const suppliedLineFields = ["startLine", "endLine"].filter((key) => hasOwn(record, key));
|
|
12015
|
+
if (suppliedLineFields.length > 0) {
|
|
12016
|
+
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 }].");
|
|
12017
|
+
}
|
|
12018
|
+
const isOpenCodeRetiredBoundary = toolName === "aft_edit";
|
|
12019
|
+
const retiredFields = ["file", "mode"].filter((key) => hasOwn(record, key));
|
|
12020
|
+
if (retiredFields.length > 0 && isOpenCodeRetiredBoundary) {
|
|
12021
|
+
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`.");
|
|
12022
|
+
}
|
|
12023
|
+
const unknownRootKeys = Object.getOwnPropertyNames(record).filter((key) => !EDIT_ROOT_CANONICAL_KEYS.has(key) && !EDIT_ROOT_COMPATIBILITY_KEYS.has(key) && key !== "filePath").sort();
|
|
12024
|
+
if (unknownRootKeys.length > 0) {
|
|
12025
|
+
throw new InvalidRequestError(formatUnknownKeys(unknownRootKeys));
|
|
12026
|
+
}
|
|
12027
|
+
const modes = editModesPresent(record);
|
|
12028
|
+
if (hasOrphanedSymbolContent(record)) {
|
|
12029
|
+
throw new InvalidRequestError("edit: 'content' requires a non-empty string 'symbol' when symbol mode is selected");
|
|
12030
|
+
}
|
|
12031
|
+
if (modes.length > 1) {
|
|
12032
|
+
throw new InvalidRequestError(`edit: conflicting modes: ${modes.join(", ")}. ` + OMIT_OPTIONAL_FIELDS_STEERING);
|
|
12033
|
+
}
|
|
12034
|
+
if (modes.length === 0) {
|
|
12035
|
+
throw new InvalidRequestError("edit: exactly one of `appendContent`, `edits`, or `symbol` plus `content` is required. " + OMIT_OPTIONAL_FIELDS_STEERING);
|
|
12036
|
+
}
|
|
12037
|
+
const mode = modes[0];
|
|
12038
|
+
if (mode === "appendContent") {
|
|
12039
|
+
if (typeof record.appendContent !== "string") {
|
|
12040
|
+
throw new InvalidRequestError("edit: 'appendContent' must be a string");
|
|
12041
|
+
}
|
|
12042
|
+
} else if (mode === "edits") {
|
|
12043
|
+
const parsedEdits = parseEditArray(record.edits);
|
|
12044
|
+
record.edits = parsedEdits.map((item, index) => normalizeEditItem(item, index));
|
|
12045
|
+
} else if (mode === "symbol/content") {
|
|
12046
|
+
if (!hasOwn(record, "symbol") || typeof record.symbol !== "string") {
|
|
12047
|
+
throw new InvalidRequestError("edit: 'symbol' must be a string when symbol mode is selected");
|
|
12048
|
+
}
|
|
12049
|
+
if (!hasOwn(record, "content") || typeof record.content !== "string") {
|
|
12050
|
+
throw new InvalidRequestError("edit: symbol mode requires both 'symbol' and 'content' string properties");
|
|
12051
|
+
}
|
|
12052
|
+
} else {
|
|
12053
|
+
const item = {};
|
|
12054
|
+
for (const key of EDIT_ROOT_COMPATIBILITY_KEYS) {
|
|
12055
|
+
if (hasOwn(record, key))
|
|
12056
|
+
item[key] = record[key];
|
|
12057
|
+
}
|
|
12058
|
+
record.edits = [normalizeEditItem(item, 0)];
|
|
12059
|
+
for (const key of EDIT_ROOT_COMPATIBILITY_KEYS)
|
|
12060
|
+
delete record[key];
|
|
12061
|
+
}
|
|
12062
|
+
validateEditPath(record);
|
|
12063
|
+
return record;
|
|
12064
|
+
}
|
|
12065
|
+
function normalizeEditPathAlias(record) {
|
|
12066
|
+
const hasCanonical = hasOwn(record, "path");
|
|
12067
|
+
const hasLegacy = hasOwn(record, "filePath");
|
|
12068
|
+
if (!hasCanonical && !hasLegacy)
|
|
12069
|
+
return;
|
|
12070
|
+
if (hasCanonical && hasLegacy) {
|
|
12071
|
+
let canonical;
|
|
12072
|
+
let legacy;
|
|
12073
|
+
try {
|
|
12074
|
+
canonical = pathValue(record, "path");
|
|
12075
|
+
legacy = pathValue(record, "filePath");
|
|
12076
|
+
} catch {
|
|
12077
|
+
throw new InvalidRequestError("Invalid request: 'path' and 'filePath' must both be non-empty well-formed Unicode strings");
|
|
12078
|
+
}
|
|
12079
|
+
if (canonical !== legacy) {
|
|
12080
|
+
throw new InvalidRequestError("Invalid request: 'path' and 'filePath' must contain equal decoded strings");
|
|
12081
|
+
}
|
|
12082
|
+
delete record.filePath;
|
|
12083
|
+
return;
|
|
12084
|
+
}
|
|
12085
|
+
if (!hasCanonical) {
|
|
12086
|
+
record.path = pathValue(record, "filePath");
|
|
12087
|
+
delete record.filePath;
|
|
12088
|
+
}
|
|
12089
|
+
}
|
|
12090
|
+
function validateEditPath(record) {
|
|
12091
|
+
if (!hasOwn(record, "path")) {
|
|
12092
|
+
throw new InvalidRequestError("'path' is required");
|
|
12093
|
+
}
|
|
12094
|
+
pathValue(record, "path");
|
|
12095
|
+
}
|
|
12096
|
+
function formatUnknownKeys(keys) {
|
|
12097
|
+
return `Unrecognized keys: ${keys.map((key) => `"${key}"`).join(", ")}`;
|
|
12098
|
+
}
|
|
12099
|
+
function editModesPresent(record) {
|
|
12100
|
+
const hasAppendContent = isNonEmptyString(record.appendContent);
|
|
12101
|
+
if (!hasAppendContent)
|
|
12102
|
+
delete record.appendContent;
|
|
12103
|
+
const hasEdits = normalizeEditArraySentinels(record);
|
|
12104
|
+
if (!hasEdits)
|
|
12105
|
+
delete record.edits;
|
|
12106
|
+
const hasSymbol = isNonEmptyString(record.symbol);
|
|
12107
|
+
if (!hasSymbol) {
|
|
12108
|
+
delete record.symbol;
|
|
12109
|
+
if (record.content === null || record.content === "")
|
|
12110
|
+
delete record.content;
|
|
12111
|
+
} else if (record.content === null) {
|
|
12112
|
+
delete record.content;
|
|
12113
|
+
}
|
|
12114
|
+
const hasSingleEdit = isNonEmptyString(record.oldString);
|
|
12115
|
+
if (!hasSingleEdit) {
|
|
12116
|
+
for (const key of EDIT_ROOT_COMPATIBILITY_KEYS)
|
|
12117
|
+
delete record[key];
|
|
12118
|
+
} else {
|
|
12119
|
+
for (const key of ["newString", "replaceAll", "occurrence"]) {
|
|
12120
|
+
if (record[key] === null)
|
|
12121
|
+
delete record[key];
|
|
12122
|
+
}
|
|
12123
|
+
}
|
|
12124
|
+
const modes = [];
|
|
12125
|
+
if (hasAppendContent)
|
|
12126
|
+
modes.push("appendContent");
|
|
12127
|
+
if (hasEdits)
|
|
12128
|
+
modes.push("edits");
|
|
12129
|
+
if (hasSymbol)
|
|
12130
|
+
modes.push("symbol/content");
|
|
12131
|
+
if (hasSingleEdit)
|
|
12132
|
+
modes.push("oldString/newString");
|
|
12133
|
+
return modes;
|
|
12134
|
+
}
|
|
12135
|
+
function isNonEmptyString(value) {
|
|
12136
|
+
return typeof value === "string" && value.length > 0;
|
|
12137
|
+
}
|
|
12138
|
+
var OMIT_OPTIONAL_FIELDS_STEERING = "Omit unused optional fields entirely; do not send empty strings or empty arrays for them.";
|
|
12139
|
+
function isEditSentinelItem(item) {
|
|
12140
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
12141
|
+
return false;
|
|
12142
|
+
const record = item;
|
|
12143
|
+
if (record.oldString !== "")
|
|
12144
|
+
return false;
|
|
12145
|
+
const newStringEmpty = !hasOwn(record, "newString") || record.newString === "" || record.newString === null;
|
|
12146
|
+
if (!newStringEmpty)
|
|
12147
|
+
return false;
|
|
12148
|
+
return !hasOwn(record, "content") || record.content === "" || record.content === null;
|
|
12149
|
+
}
|
|
12150
|
+
function normalizeEditArraySentinels(record) {
|
|
12151
|
+
const value = record.edits;
|
|
12152
|
+
if (Array.isArray(value)) {
|
|
12153
|
+
const survivors = value.filter((item) => !isEditSentinelItem(item));
|
|
12154
|
+
if (survivors.length === 0)
|
|
12155
|
+
return false;
|
|
12156
|
+
record.edits = survivors;
|
|
12157
|
+
return true;
|
|
12158
|
+
}
|
|
12159
|
+
if (typeof value !== "string" || value.length === 0)
|
|
12160
|
+
return false;
|
|
12161
|
+
try {
|
|
12162
|
+
const parsed = JSON.parse(value);
|
|
12163
|
+
if (!Array.isArray(parsed))
|
|
12164
|
+
return true;
|
|
12165
|
+
const survivors = parsed.filter((item) => !isEditSentinelItem(item));
|
|
12166
|
+
if (survivors.length === 0)
|
|
12167
|
+
return false;
|
|
12168
|
+
record.edits = survivors;
|
|
12169
|
+
return true;
|
|
12170
|
+
} catch {
|
|
12171
|
+
return true;
|
|
12172
|
+
}
|
|
12173
|
+
}
|
|
12174
|
+
function hasOrphanedSymbolContent(record) {
|
|
12175
|
+
return isNonEmptyString(record.content) && !isNonEmptyString(record.symbol);
|
|
12176
|
+
}
|
|
12177
|
+
function parseEditArray(value) {
|
|
12178
|
+
if (typeof value === "string") {
|
|
12179
|
+
let parsed;
|
|
12180
|
+
try {
|
|
12181
|
+
parsed = JSON.parse(value);
|
|
12182
|
+
} catch {
|
|
12183
|
+
throw new InvalidRequestError("edit: 'edits' must contain valid JSON representing an array");
|
|
12184
|
+
}
|
|
12185
|
+
if (!Array.isArray(parsed)) {
|
|
12186
|
+
throw new InvalidRequestError("edit: 'edits' JSON must have an array root");
|
|
12187
|
+
}
|
|
12188
|
+
if (parsed.length === 0) {
|
|
12189
|
+
throw new InvalidRequestError("edit: 'edits' array must not be empty");
|
|
12190
|
+
}
|
|
12191
|
+
return parsed;
|
|
12192
|
+
}
|
|
12193
|
+
if (!Array.isArray(value)) {
|
|
12194
|
+
throw new InvalidRequestError("edit: 'edits' must be a non-empty array");
|
|
12195
|
+
}
|
|
12196
|
+
if (value.length === 0) {
|
|
12197
|
+
throw new InvalidRequestError("edit: 'edits' array must not be empty");
|
|
12198
|
+
}
|
|
12199
|
+
return value;
|
|
12200
|
+
}
|
|
12201
|
+
function normalizeEditItem(value, index) {
|
|
12202
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
12203
|
+
throw new InvalidRequestError(`edit: edits[${index}] must be an object`);
|
|
12204
|
+
}
|
|
12205
|
+
const source = value;
|
|
12206
|
+
const item = copyOwnProperties(source);
|
|
12207
|
+
normalizeItemAlias(item, "oldString", "oldText");
|
|
12208
|
+
normalizeItemAlias(item, "newString", "newText");
|
|
12209
|
+
const hasFindField = ["oldString", "newString", "replaceAll", "occurrence"].some((key) => hasOwn(item, key));
|
|
12210
|
+
const hasRangeField = ["startLine", "endLine", "content"].some((key) => hasOwn(item, key));
|
|
12211
|
+
if (hasFindField && hasRangeField) {
|
|
12212
|
+
throw new InvalidRequestError(`edit: edits[${index}] mixes find/replace and line-range fields`);
|
|
12213
|
+
}
|
|
12214
|
+
if (hasFindField) {
|
|
12215
|
+
if (!hasOwn(item, "oldString") || typeof item.oldString !== "string") {
|
|
12216
|
+
throw new InvalidRequestError(`edit: edits[${index}] requires string 'oldString'`);
|
|
12217
|
+
}
|
|
12218
|
+
if (hasOwn(item, "newString") && typeof item.newString !== "string") {
|
|
12219
|
+
throw new InvalidRequestError(`edit: edits[${index}].newString must be a string`);
|
|
12220
|
+
}
|
|
12221
|
+
coerceEditScalars(item, index);
|
|
12222
|
+
validateEditItemKeys(item, index);
|
|
12223
|
+
return item;
|
|
12224
|
+
}
|
|
12225
|
+
if (hasRangeField) {
|
|
12226
|
+
for (const key of ["startLine", "endLine"]) {
|
|
12227
|
+
const value2 = item[key];
|
|
12228
|
+
if (typeof value2 === "string" && /^[0-9]+$/.test(value2.trim())) {
|
|
12229
|
+
item[key] = Number(value2.trim());
|
|
12230
|
+
}
|
|
12231
|
+
if (!hasOwn(item, key) || !isPositiveSafeInteger(item[key])) {
|
|
12232
|
+
throw new InvalidRequestError(`edit: edits[${index}].${key} must be a positive integer`);
|
|
12233
|
+
}
|
|
12234
|
+
}
|
|
12235
|
+
if (item.startLine > item.endLine) {
|
|
12236
|
+
throw new InvalidRequestError(`edit: edits[${index}] requires startLine <= endLine`);
|
|
12237
|
+
}
|
|
12238
|
+
if (!hasOwn(item, "content") || typeof item.content !== "string") {
|
|
12239
|
+
throw new InvalidRequestError(`edit: edits[${index}] requires string 'content'`);
|
|
12240
|
+
}
|
|
12241
|
+
validateEditItemKeys(item, index);
|
|
12242
|
+
return item;
|
|
12243
|
+
}
|
|
12244
|
+
throw new InvalidRequestError(`edit: edits[${index}] must be a find/replace or line-range item`);
|
|
12245
|
+
}
|
|
12246
|
+
function normalizeItemAlias(item, canonical, legacy) {
|
|
12247
|
+
if (hasOwn(item, legacy)) {
|
|
12248
|
+
if (!hasOwn(item, canonical))
|
|
12249
|
+
item[canonical] = item[legacy];
|
|
12250
|
+
delete item[legacy];
|
|
12251
|
+
}
|
|
12252
|
+
}
|
|
12253
|
+
function validateEditItemKeys(item, index) {
|
|
12254
|
+
const unknown = Object.getOwnPropertyNames(item).filter((key) => !EDIT_ITEM_KEYS.has(key)).sort();
|
|
12255
|
+
if (unknown.length > 0) {
|
|
12256
|
+
throw new InvalidRequestError(`edit: edits[${index}] contains ${formatUnknownKeys(unknown)}`);
|
|
12257
|
+
}
|
|
12258
|
+
}
|
|
12259
|
+
function coerceEditScalars(item, index) {
|
|
12260
|
+
if (hasOwn(item, "replaceAll") && hasOwn(item, "occurrence")) {
|
|
12261
|
+
throw new InvalidRequestError(`edit: edits[${index}] cannot contain both 'replaceAll' and 'occurrence'`);
|
|
12262
|
+
}
|
|
12263
|
+
if (hasOwn(item, "replaceAll"))
|
|
12264
|
+
item.replaceAll = coerceEditBoolean(item.replaceAll, index);
|
|
12265
|
+
if (hasOwn(item, "occurrence")) {
|
|
12266
|
+
const occurrence = coerceEditOccurrence(item.occurrence, index);
|
|
12267
|
+
if (occurrence === undefined)
|
|
12268
|
+
delete item.occurrence;
|
|
12269
|
+
else
|
|
12270
|
+
item.occurrence = occurrence;
|
|
12271
|
+
}
|
|
12272
|
+
}
|
|
12273
|
+
function coerceEditBoolean(value, index) {
|
|
12274
|
+
if (typeof value === "boolean")
|
|
12275
|
+
return value;
|
|
12276
|
+
if (typeof value === "number" && Number.isFinite(value) && (value === 0 || value === 1)) {
|
|
12277
|
+
return value === 1;
|
|
12278
|
+
}
|
|
12279
|
+
if (typeof value === "string") {
|
|
12280
|
+
if (value === "1")
|
|
12281
|
+
return true;
|
|
12282
|
+
if (value === "0")
|
|
12283
|
+
return false;
|
|
12284
|
+
if (/^(?:true|false)$/i.test(value))
|
|
12285
|
+
return value.toLowerCase() === "true";
|
|
12286
|
+
}
|
|
12287
|
+
throw new InvalidRequestError(`edit: edits[${index}].replaceAll must be a boolean, true/false string, or 0/1`);
|
|
12288
|
+
}
|
|
12289
|
+
function coerceEditOccurrence(value, index) {
|
|
12290
|
+
if (value === null)
|
|
12291
|
+
return;
|
|
12292
|
+
if (typeof value === "string") {
|
|
12293
|
+
const trimmed = value.replace(ASCII_TRIM, "");
|
|
12294
|
+
if (trimmed.length === 0 || ASCII_WHITESPACE.test(trimmed))
|
|
12295
|
+
return;
|
|
12296
|
+
if (!/^[+]?[0-9]+$/.test(trimmed)) {
|
|
12297
|
+
throw new InvalidRequestError(`edit: edits[${index}].occurrence must be a positive integer`);
|
|
12298
|
+
}
|
|
12299
|
+
try {
|
|
12300
|
+
const parsed = BigInt(trimmed);
|
|
12301
|
+
if (parsed < 1n || parsed > BigInt(MAX_SAFE_INTEGER))
|
|
12302
|
+
throw new Error("out of range");
|
|
12303
|
+
return Number(parsed);
|
|
12304
|
+
} catch {
|
|
12305
|
+
throw new InvalidRequestError(`edit: edits[${index}].occurrence must be a positive integer`);
|
|
12306
|
+
}
|
|
12307
|
+
}
|
|
12308
|
+
if (typeof value === "number" && isPositiveSafeInteger(value))
|
|
12309
|
+
return value;
|
|
12310
|
+
throw new InvalidRequestError(`edit: edits[${index}].occurrence must be a positive integer`);
|
|
12311
|
+
}
|
|
12312
|
+
function isPositiveSafeInteger(value) {
|
|
12313
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
12314
|
+
}
|
|
12315
|
+
function copyOwnProperties(source) {
|
|
12316
|
+
const copy = Object.create(null);
|
|
12317
|
+
for (const key of Object.getOwnPropertyNames(source))
|
|
12318
|
+
copy[key] = source[key];
|
|
12319
|
+
return copy;
|
|
12320
|
+
}
|
|
11810
12321
|
// ../aft-bridge/dist/pool.js
|
|
11811
12322
|
import { homedir as homedir10 } from "node:os";
|
|
11812
12323
|
|
|
@@ -12261,7 +12772,7 @@ class RevivableProjectTransport {
|
|
|
12261
12772
|
}
|
|
12262
12773
|
}
|
|
12263
12774
|
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/client.js
|
|
12264
|
-
import { promises as
|
|
12775
|
+
import { promises as fs3 } from "node:fs";
|
|
12265
12776
|
import { debuglog } from "node:util";
|
|
12266
12777
|
|
|
12267
12778
|
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/auth.js
|
|
@@ -12329,7 +12840,7 @@ async function authenticateClient(sock, conn, deadlineMs) {
|
|
|
12329
12840
|
}
|
|
12330
12841
|
|
|
12331
12842
|
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/connection-file.js
|
|
12332
|
-
import { promises as
|
|
12843
|
+
import { promises as fs2 } from "node:fs";
|
|
12333
12844
|
|
|
12334
12845
|
// ../../node_modules/.bun/@cortexkit+subc-client@0.5.0/node_modules/@cortexkit/subc-client/dist/envelope.js
|
|
12335
12846
|
var PROTOCOL_VERSION = 2;
|
|
@@ -12497,7 +13008,7 @@ function validate(info) {
|
|
|
12497
13008
|
async function verifyOwnerOnly(path2) {
|
|
12498
13009
|
if (process.platform === "win32")
|
|
12499
13010
|
return;
|
|
12500
|
-
const stat2 = await
|
|
13011
|
+
const stat2 = await fs2.stat(path2);
|
|
12501
13012
|
const mode = stat2.mode & 511;
|
|
12502
13013
|
if ((mode & 63) !== 0) {
|
|
12503
13014
|
throw new ConnectionFileError(`connection file ${path2} has insecure permissions 0o${mode.toString(8)}; expected owner-only 0600`);
|
|
@@ -12505,7 +13016,7 @@ async function verifyOwnerOnly(path2) {
|
|
|
12505
13016
|
}
|
|
12506
13017
|
async function readConnectionFile(path2) {
|
|
12507
13018
|
await verifyOwnerOnly(path2);
|
|
12508
|
-
const raw = await
|
|
13019
|
+
const raw = await fs2.readFile(path2, "utf8");
|
|
12509
13020
|
let parsed;
|
|
12510
13021
|
try {
|
|
12511
13022
|
parsed = JSON.parse(raw);
|
|
@@ -13568,7 +14079,7 @@ function isRetryableRouteOpenCode(code) {
|
|
|
13568
14079
|
}
|
|
13569
14080
|
async function connectionFileExists(path2) {
|
|
13570
14081
|
try {
|
|
13571
|
-
await
|
|
14082
|
+
await fs3.access(path2);
|
|
13572
14083
|
return true;
|
|
13573
14084
|
} catch {
|
|
13574
14085
|
return false;
|
|
@@ -15795,7 +16306,7 @@ import {
|
|
|
15795
16306
|
// package.json
|
|
15796
16307
|
var package_default = {
|
|
15797
16308
|
name: "@cortexkit/aft-pi",
|
|
15798
|
-
version: "0.
|
|
16309
|
+
version: "0.49.1",
|
|
15799
16310
|
type: "module",
|
|
15800
16311
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
15801
16312
|
main: "dist/index.js",
|
|
@@ -15818,19 +16329,19 @@ var package_default = {
|
|
|
15818
16329
|
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
|
|
15819
16330
|
},
|
|
15820
16331
|
dependencies: {
|
|
15821
|
-
"@cortexkit/aft-bridge": "0.
|
|
16332
|
+
"@cortexkit/aft-bridge": "0.49.1",
|
|
15822
16333
|
"comment-json": "^5.0.0",
|
|
15823
16334
|
diff: "^8.0.4",
|
|
15824
16335
|
typebox: "1.1.38",
|
|
15825
16336
|
zod: "^4.4.3"
|
|
15826
16337
|
},
|
|
15827
16338
|
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.
|
|
16339
|
+
"@cortexkit/aft-darwin-arm64": "0.49.1",
|
|
16340
|
+
"@cortexkit/aft-darwin-x64": "0.49.1",
|
|
16341
|
+
"@cortexkit/aft-linux-arm64": "0.49.1",
|
|
16342
|
+
"@cortexkit/aft-linux-x64": "0.49.1",
|
|
16343
|
+
"@cortexkit/aft-win32-arm64": "0.49.1",
|
|
16344
|
+
"@cortexkit/aft-win32-x64": "0.49.1"
|
|
15834
16345
|
},
|
|
15835
16346
|
devDependencies: {
|
|
15836
16347
|
"@earendil-works/pi-coding-agent": "^0.80.2",
|
|
@@ -16072,6 +16583,20 @@ import { Type } from "typebox";
|
|
|
16072
16583
|
var optionalInt = (min, max, description = "(integer)") => Type.Optional(Type.Union([Type.Integer({ minimum: min, maximum: max }), Type.String()], {
|
|
16073
16584
|
description
|
|
16074
16585
|
}));
|
|
16586
|
+
function withPathAliasPreparation(tool) {
|
|
16587
|
+
const existing = tool.prepareArguments;
|
|
16588
|
+
const prepare = (args) => {
|
|
16589
|
+
const prepared = tool.name === "edit" ? prepareCanonicalEditArguments(tool.name, args) : prepareCanonicalPathArguments(tool.name, args);
|
|
16590
|
+
return existing ? existing(prepared) : prepared;
|
|
16591
|
+
};
|
|
16592
|
+
return {
|
|
16593
|
+
...tool,
|
|
16594
|
+
prepareArguments: prepare,
|
|
16595
|
+
execute(toolCallId, params, signal, onUpdate, context) {
|
|
16596
|
+
return tool.execute(toolCallId, prepare(params), signal, onUpdate, context);
|
|
16597
|
+
}
|
|
16598
|
+
};
|
|
16599
|
+
}
|
|
16075
16600
|
function bridgeFor(ctx, cwd) {
|
|
16076
16601
|
if (!existsSync7(cwd)) {
|
|
16077
16602
|
throw new Error(`project directory no longer exists: ${cwd} (stale restored session?)`);
|
|
@@ -31354,7 +31879,7 @@ import {
|
|
|
31354
31879
|
readFileSync as readFileSync10,
|
|
31355
31880
|
renameSync as renameSync6,
|
|
31356
31881
|
rmSync as rmSync4,
|
|
31357
|
-
statSync as
|
|
31882
|
+
statSync as statSync7,
|
|
31358
31883
|
writeFileSync as writeFileSync6
|
|
31359
31884
|
} from "node:fs";
|
|
31360
31885
|
import { join as join13 } from "node:path";
|
|
@@ -31365,7 +31890,7 @@ import {
|
|
|
31365
31890
|
mkdirSync as mkdirSync6,
|
|
31366
31891
|
openSync as openSync5,
|
|
31367
31892
|
readFileSync as readFileSync8,
|
|
31368
|
-
statSync as
|
|
31893
|
+
statSync as statSync6,
|
|
31369
31894
|
unlinkSync as unlinkSync6,
|
|
31370
31895
|
writeFileSync as writeFileSync5
|
|
31371
31896
|
} from "node:fs";
|
|
@@ -31398,7 +31923,7 @@ function lspBinDir(npmPackage) {
|
|
|
31398
31923
|
function isInstalled(npmPackage, binary) {
|
|
31399
31924
|
for (const candidate of lspBinaryCandidates(binary)) {
|
|
31400
31925
|
try {
|
|
31401
|
-
if (
|
|
31926
|
+
if (statSync6(join11(lspBinDir(npmPackage), candidate)).isFile())
|
|
31402
31927
|
return true;
|
|
31403
31928
|
} catch {}
|
|
31404
31929
|
}
|
|
@@ -31426,7 +31951,7 @@ function writeInstalledMetaIn(installDir, version2, sha256) {
|
|
|
31426
31951
|
function readInstalledMetaIn(installDir) {
|
|
31427
31952
|
const path2 = join11(installDir, INSTALLED_META_FILE);
|
|
31428
31953
|
try {
|
|
31429
|
-
if (!
|
|
31954
|
+
if (!statSync6(path2).isFile())
|
|
31430
31955
|
return null;
|
|
31431
31956
|
const raw = readFileSync8(path2, "utf8");
|
|
31432
31957
|
const parsed = JSON.parse(raw);
|
|
@@ -31483,7 +32008,7 @@ ${new Date().toISOString()}
|
|
|
31483
32008
|
const parsed = Number.parseInt(firstLine, 10);
|
|
31484
32009
|
if (Number.isFinite(parsed) && parsed > 0)
|
|
31485
32010
|
owningPid = parsed;
|
|
31486
|
-
lockMtimeMs =
|
|
32011
|
+
lockMtimeMs = statSync6(lock).mtimeMs;
|
|
31487
32012
|
} catch {
|
|
31488
32013
|
return tryClaim();
|
|
31489
32014
|
}
|
|
@@ -32037,7 +32562,7 @@ async function ensureServerInstalled(spec, config2, fetchImpl, signal) {
|
|
|
32037
32562
|
return null;
|
|
32038
32563
|
});
|
|
32039
32564
|
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.`);
|
|
32565
|
+
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
32566
|
return {
|
|
32042
32567
|
started: false,
|
|
32043
32568
|
reason: `TOFU sha256 mismatch on ${spec.npm}@${version2} — see plugin log`
|
|
@@ -32088,7 +32613,7 @@ function hashInstalledBinary(spec) {
|
|
|
32088
32613
|
let pathToHash = null;
|
|
32089
32614
|
for (const p of candidates) {
|
|
32090
32615
|
try {
|
|
32091
|
-
if (
|
|
32616
|
+
if (statSync7(p).isFile()) {
|
|
32092
32617
|
pathToHash = p;
|
|
32093
32618
|
break;
|
|
32094
32619
|
}
|
|
@@ -32114,7 +32639,7 @@ function installedBinaryPath(spec) {
|
|
|
32114
32639
|
] : [lspBinaryPath(spec.npm, spec.binary)];
|
|
32115
32640
|
for (const candidate of candidates) {
|
|
32116
32641
|
try {
|
|
32117
|
-
if (
|
|
32642
|
+
if (statSync7(candidate).isFile())
|
|
32118
32643
|
return candidate;
|
|
32119
32644
|
} catch {}
|
|
32120
32645
|
}
|
|
@@ -32225,7 +32750,7 @@ import {
|
|
|
32225
32750
|
realpathSync as realpathSync3,
|
|
32226
32751
|
renameSync as renameSync7,
|
|
32227
32752
|
rmSync as rmSync5,
|
|
32228
|
-
statSync as
|
|
32753
|
+
statSync as statSync8,
|
|
32229
32754
|
unlinkSync as unlinkSync7,
|
|
32230
32755
|
writeFileSync as writeFileSync7
|
|
32231
32756
|
} from "node:fs";
|
|
@@ -32338,7 +32863,7 @@ function ghBinaryPath(spec, platform) {
|
|
|
32338
32863
|
function isGithubInstalled(spec, platform) {
|
|
32339
32864
|
for (const candidate of ghBinaryCandidates(spec, platform)) {
|
|
32340
32865
|
try {
|
|
32341
|
-
if (
|
|
32866
|
+
if (statSync8(join14(ghBinDir(spec), candidate)).isFile())
|
|
32342
32867
|
return true;
|
|
32343
32868
|
} catch {}
|
|
32344
32869
|
}
|
|
@@ -32352,7 +32877,7 @@ function ghBinaryCandidates(spec, platform) {
|
|
|
32352
32877
|
function readGithubInstalledMetaIn(installDir) {
|
|
32353
32878
|
try {
|
|
32354
32879
|
const path2 = join14(installDir, INSTALLED_META_FILE2);
|
|
32355
|
-
if (!
|
|
32880
|
+
if (!statSync8(path2).isFile())
|
|
32356
32881
|
return null;
|
|
32357
32882
|
const parsed = JSON.parse(readFileSync11(path2, "utf8"));
|
|
32358
32883
|
if (typeof parsed.version !== "string" || parsed.version.length === 0)
|
|
@@ -32730,7 +33255,7 @@ function validateCachedGithubInstall(spec, platform) {
|
|
|
32730
33255
|
const meta3 = readGithubInstalledMetaIn(packageDir);
|
|
32731
33256
|
const binaryPath = ghBinaryCandidates(spec, platform).map((candidate) => join14(ghBinDir(spec), candidate)).find((candidate) => {
|
|
32732
33257
|
try {
|
|
32733
|
-
return
|
|
33258
|
+
return statSync8(candidate).isFile();
|
|
32734
33259
|
} catch {
|
|
32735
33260
|
return false;
|
|
32736
33261
|
}
|
|
@@ -33517,29 +34042,25 @@ async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
|
|
|
33517
34042
|
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
34043
|
}
|
|
33519
34044
|
var ReadParams = Type2.Object({
|
|
33520
|
-
path: Type2.
|
|
34045
|
+
path: Type2.String({
|
|
33521
34046
|
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")
|
|
34047
|
+
}),
|
|
34048
|
+
startLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line to start reading from"),
|
|
34049
|
+
endLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line to stop reading at (inclusive)"),
|
|
34050
|
+
limit: optionalInt(1, Number.MAX_SAFE_INTEGER, "Maximum number of lines to return"),
|
|
34051
|
+
offset: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based line number to start reading from (use with limit)")
|
|
33528
34052
|
});
|
|
33529
34053
|
var WriteParams = Type2.Object({
|
|
33530
|
-
|
|
34054
|
+
path: Type2.String({
|
|
33531
34055
|
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
|
-
})),
|
|
34056
|
+
}),
|
|
33536
34057
|
content: Type2.String({ description: "Full file contents to write" })
|
|
33537
34058
|
});
|
|
33538
34059
|
var BatchEditParams = Type2.Object({
|
|
33539
34060
|
oldString: Type2.Optional(Type2.String({ description: "Text to find for a batch find/replace edit" })),
|
|
33540
34061
|
newString: Type2.Optional(Type2.String({ description: "Replacement text for a batch find/replace edit" })),
|
|
33541
34062
|
replaceAll: Type2.Optional(Type2.Boolean({ description: "Replace every occurrence for this batch item" })),
|
|
33542
|
-
occurrence: optionalInt(
|
|
34063
|
+
occurrence: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based occurrence for this batch item (1 = first match)"),
|
|
33543
34064
|
startLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based start line for a batch line-range edit"),
|
|
33544
34065
|
endLine: optionalInt(1, Number.MAX_SAFE_INTEGER, "1-based end line for a batch line-range edit"),
|
|
33545
34066
|
content: Type2.Optional(Type2.String({
|
|
@@ -33547,21 +34068,19 @@ var BatchEditParams = Type2.Object({
|
|
|
33547
34068
|
}))
|
|
33548
34069
|
});
|
|
33549
34070
|
var EditParams = Type2.Object({
|
|
33550
|
-
|
|
34071
|
+
path: Type2.String({
|
|
33551
34072
|
description: "Path to the file to edit (absolute or relative to project root)"
|
|
34073
|
+
}),
|
|
34074
|
+
symbol: Type2.Optional(Type2.String({ description: "Named symbol to replace (function, class, type)" })),
|
|
34075
|
+
content: Type2.Optional(Type2.String({
|
|
34076
|
+
description: "Replacement content for symbol mode. For whole-file writes, use the `write` tool."
|
|
33552
34077
|
})),
|
|
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
34078
|
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,
|
|
34079
|
+
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
34080
|
})),
|
|
33563
34081
|
edits: Type2.Optional(Type2.Array(BatchEditParams, {
|
|
33564
|
-
|
|
34082
|
+
minItems: 1,
|
|
34083
|
+
description: "Batch edits — non-empty array of { oldString, newString }, { oldString, newString, replaceAll: true }, or { startLine, endLine, content } objects applied atomically."
|
|
33565
34084
|
}))
|
|
33566
34085
|
});
|
|
33567
34086
|
var GrepParams = Type2.Object({
|
|
@@ -33569,16 +34088,15 @@ var GrepParams = Type2.Object({
|
|
|
33569
34088
|
path: Type2.Optional(Type2.String({
|
|
33570
34089
|
description: "Path scope (file or directory; absolute or relative to project root)"
|
|
33571
34090
|
})),
|
|
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" }))
|
|
34091
|
+
include: Type2.Optional(Type2.String({ description: "Glob filter for included files (e.g. '*.ts,*.tsx')" }))
|
|
33574
34092
|
});
|
|
33575
34093
|
function readPathArg(args) {
|
|
33576
|
-
return
|
|
34094
|
+
return typeof args.path === "string" ? args.path : undefined;
|
|
33577
34095
|
}
|
|
33578
34096
|
function mutationFilePathArg(args) {
|
|
33579
|
-
return
|
|
34097
|
+
return typeof args.path === "string" ? args.path : undefined;
|
|
33580
34098
|
}
|
|
33581
|
-
function
|
|
34099
|
+
function hasOwn2(record2, key) {
|
|
33582
34100
|
return Object.hasOwn(record2, key);
|
|
33583
34101
|
}
|
|
33584
34102
|
function validateBatchEdit(edit, index) {
|
|
@@ -33589,7 +34107,7 @@ function validateBatchEdit(edit, index) {
|
|
|
33589
34107
|
if (typeof record2.oldString === "string") {
|
|
33590
34108
|
return;
|
|
33591
34109
|
}
|
|
33592
|
-
if (
|
|
34110
|
+
if (hasOwn2(record2, "startLine")) {
|
|
33593
34111
|
if (typeof record2.startLine !== "number" || !Number.isInteger(record2.startLine) || record2.startLine < 0) {
|
|
33594
34112
|
throw new Error(`batch: edit[${index}] 'startLine' must be a positive integer (1-based)`);
|
|
33595
34113
|
}
|
|
@@ -33628,7 +34146,7 @@ function renderReadCall(args, theme, context) {
|
|
|
33628
34146
|
}
|
|
33629
34147
|
function registerHoistedTools(pi, ctx, surface) {
|
|
33630
34148
|
if (surface.hoistRead) {
|
|
33631
|
-
pi.registerTool({
|
|
34149
|
+
pi.registerTool(withPathAliasPreparation({
|
|
33632
34150
|
name: "read",
|
|
33633
34151
|
label: "read",
|
|
33634
34152
|
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 +34159,8 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
33641
34159
|
if (typeof pathArg !== "string") {
|
|
33642
34160
|
throw new Error("read: missing required parameter `path`");
|
|
33643
34161
|
}
|
|
34162
|
+
const startLine = coerceOptionalInt(params.startLine, "startLine", 1, Number.MAX_SAFE_INTEGER);
|
|
34163
|
+
const endLine = coerceOptionalInt(params.endLine, "endLine", 1, Number.MAX_SAFE_INTEGER);
|
|
33644
34164
|
const offset = coerceOptionalInt(params.offset, "offset", 1, Number.MAX_SAFE_INTEGER);
|
|
33645
34165
|
const limit = coerceOptionalInt(params.limit, "limit", 1, Number.MAX_SAFE_INTEGER);
|
|
33646
34166
|
const filePath = await resolvePathArg(extCtx.cwd, pathArg);
|
|
@@ -33649,6 +34169,10 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
33649
34169
|
serverValidatedRead: true
|
|
33650
34170
|
});
|
|
33651
34171
|
const rawArgs = { filePath: pathArg };
|
|
34172
|
+
if (startLine !== undefined)
|
|
34173
|
+
rawArgs.startLine = startLine;
|
|
34174
|
+
if (endLine !== undefined)
|
|
34175
|
+
rawArgs.endLine = endLine;
|
|
33652
34176
|
if (offset !== undefined)
|
|
33653
34177
|
rawArgs.offset = offset;
|
|
33654
34178
|
if (limit !== undefined)
|
|
@@ -33684,21 +34208,21 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33684
34208
|
renderCall(args, theme, context) {
|
|
33685
34209
|
return renderReadCall(args, theme, context);
|
|
33686
34210
|
}
|
|
33687
|
-
});
|
|
34211
|
+
}));
|
|
33688
34212
|
}
|
|
33689
34213
|
if (surface.hoistWrite) {
|
|
33690
34214
|
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({
|
|
34215
|
+
pi.registerTool(withPathAliasPreparation({
|
|
33692
34216
|
name: "write",
|
|
33693
34217
|
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
|
|
34218
|
+
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.`,
|
|
34219
|
+
promptSnippet: "Create or overwrite files (uses path; auto-formats)",
|
|
33696
34220
|
promptGuidelines: ["Use write only for new files or complete rewrites."],
|
|
33697
34221
|
parameters: WriteParams,
|
|
33698
34222
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
33699
34223
|
const filePathArg = mutationFilePathArg(params);
|
|
33700
34224
|
if (typeof filePathArg !== "string") {
|
|
33701
|
-
throw new Error("write: missing required parameter `
|
|
34225
|
+
throw new Error("write: missing required parameter `path`");
|
|
33702
34226
|
}
|
|
33703
34227
|
const filePath = await resolvePathArg(extCtx.cwd, filePathArg);
|
|
33704
34228
|
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
@@ -33711,7 +34235,7 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33711
34235
|
};
|
|
33712
34236
|
const response = await callToolCall(bridge, "write", rawArgs, extCtx);
|
|
33713
34237
|
if (response.success === false) {
|
|
33714
|
-
throw
|
|
34238
|
+
throw toolErrorFromResponse("write", response);
|
|
33715
34239
|
}
|
|
33716
34240
|
return buildMutationResult(response);
|
|
33717
34241
|
},
|
|
@@ -33721,60 +34245,45 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33721
34245
|
renderResult(result, _options, theme, context) {
|
|
33722
34246
|
return renderMutationResult(result, theme, context);
|
|
33723
34247
|
}
|
|
33724
|
-
});
|
|
34248
|
+
}));
|
|
33725
34249
|
}
|
|
33726
34250
|
if (surface.hoistEdit) {
|
|
33727
|
-
pi.registerTool({
|
|
34251
|
+
pi.registerTool(withPathAliasPreparation({
|
|
33728
34252
|
name: "edit",
|
|
33729
34253
|
label: "edit",
|
|
33730
|
-
description: "Edit part of a file via `appendContent`, batch `edits[]`, or `
|
|
33731
|
-
promptSnippet: "Partial file edits via appendContent, edits[], or
|
|
34254
|
+
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).",
|
|
34255
|
+
promptSnippet: "Partial file edits via appendContent, edits[], or symbol plus content (exactly one mode per call).",
|
|
33732
34256
|
promptGuidelines: [
|
|
33733
34257
|
"Prefer edit over write when changing part of an existing file.",
|
|
33734
34258
|
"Use appendContent when adding text to the end of a file.",
|
|
33735
34259
|
"Use edits[] for multiple atomic changes in one file.",
|
|
33736
|
-
"Include enough surrounding context in
|
|
34260
|
+
"Include enough surrounding context in an edits[] find/replace item to make the match unique, or set replaceAll/occurrence explicitly."
|
|
33737
34261
|
],
|
|
33738
34262
|
parameters: EditParams,
|
|
33739
34263
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
33740
34264
|
const argsRecord = params;
|
|
33741
34265
|
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 `
|
|
34266
|
+
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
34267
|
}
|
|
33744
34268
|
const filePathArg = mutationFilePathArg(params);
|
|
33745
34269
|
if (typeof filePathArg !== "string") {
|
|
33746
|
-
throw new Error("edit: missing required parameter `
|
|
34270
|
+
throw new Error("edit: missing required parameter `path`");
|
|
33747
34271
|
}
|
|
33748
|
-
if (params.
|
|
34272
|
+
if (params.edits !== undefined)
|
|
33749
34273
|
validateBatchEdits(params.edits);
|
|
33750
34274
|
const filePath = await resolvePathArg(extCtx.cwd, filePathArg);
|
|
33751
34275
|
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
33752
34276
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
33753
34277
|
});
|
|
33754
34278
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33755
|
-
const rawArgs = {
|
|
33756
|
-
for (const key of ["appendContent", "
|
|
34279
|
+
const rawArgs = { path: filePathArg };
|
|
34280
|
+
for (const key of ["appendContent", "edits", "symbol", "content"]) {
|
|
33757
34281
|
if (argsRecord[key] !== undefined)
|
|
33758
34282
|
rawArgs[key] = argsRecord[key];
|
|
33759
34283
|
}
|
|
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
34284
|
const response = await callToolCall(bridge, "edit", rawArgs, extCtx);
|
|
33776
34285
|
if (response.success === false) {
|
|
33777
|
-
throw
|
|
34286
|
+
throw toolErrorFromResponse("edit", response);
|
|
33778
34287
|
}
|
|
33779
34288
|
return buildMutationResult(response);
|
|
33780
34289
|
},
|
|
@@ -33784,10 +34293,10 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33784
34293
|
renderResult(result, _options, theme, context) {
|
|
33785
34294
|
return renderMutationResult(result, theme, context);
|
|
33786
34295
|
}
|
|
33787
|
-
});
|
|
34296
|
+
}));
|
|
33788
34297
|
}
|
|
33789
34298
|
if (surface.hoistGrep) {
|
|
33790
|
-
pi.registerTool({
|
|
34299
|
+
pi.registerTool(withPathAliasPreparation({
|
|
33791
34300
|
name: "grep",
|
|
33792
34301
|
label: "grep",
|
|
33793
34302
|
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 +34318,6 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33809
34318
|
}
|
|
33810
34319
|
if (params.include)
|
|
33811
34320
|
req.include = params.include;
|
|
33812
|
-
if (params.caseSensitive !== undefined)
|
|
33813
|
-
req.caseSensitive = params.caseSensitive;
|
|
33814
34321
|
const response = await callToolCall(bridge, "grep", req, extCtx);
|
|
33815
34322
|
if (response.success === false) {
|
|
33816
34323
|
throw new Error(response.text || response.message || "grep failed");
|
|
@@ -33821,7 +34328,7 @@ PDFs aren't supported on the Pi harness yet.`, response);
|
|
|
33821
34328
|
const text = appendSkippedSearchPaths(response.text ?? "", pathSplit?.missing ?? []);
|
|
33822
34329
|
return textResult(text, response);
|
|
33823
34330
|
}
|
|
33824
|
-
});
|
|
34331
|
+
}));
|
|
33825
34332
|
}
|
|
33826
34333
|
}
|
|
33827
34334
|
function buildMutationResult(response) {
|
|
@@ -34165,7 +34672,7 @@ var SearchParams = Type3.Object({
|
|
|
34165
34672
|
lang: AstLang,
|
|
34166
34673
|
paths: Type3.Optional(Type3.Array(Type3.String(), { description: "Paths to search (default: ['.'])" })),
|
|
34167
34674
|
globs: Type3.Optional(Type3.Array(Type3.String(), { description: "Include/exclude globs (prefix `!` to exclude)" })),
|
|
34168
|
-
contextLines:
|
|
34675
|
+
contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER, "Number of context lines around each match")
|
|
34169
34676
|
});
|
|
34170
34677
|
var ReplaceParams = Type3.Object({
|
|
34171
34678
|
pattern: Type3.String({ description: "AST pattern with meta-variables" }),
|
|
@@ -35056,7 +35563,7 @@ function formatWaitSummary(waited, details) {
|
|
|
35056
35563
|
return `Waited ${waited.elapsed_ms}ms; timeout reached without match.`;
|
|
35057
35564
|
}
|
|
35058
35565
|
if (waited.reason === "unavailable") {
|
|
35059
|
-
return `Waited ${waited.elapsed_ms}ms; the bridge
|
|
35566
|
+
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
35567
|
}
|
|
35061
35568
|
const exit = typeof details.exit_code === "number" ? `, exit ${details.exit_code}` : "";
|
|
35062
35569
|
return `Waited ${waited.elapsed_ms}ms; task exited (${details.status}${exit}).`;
|
|
@@ -35197,7 +35704,7 @@ function renderConflictToolResult(result, theme, context) {
|
|
|
35197
35704
|
return renderConflictResult(collectTextContent(result), theme, context);
|
|
35198
35705
|
}
|
|
35199
35706
|
function registerConflictsTool(pi, ctx) {
|
|
35200
|
-
pi.registerTool({
|
|
35707
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35201
35708
|
name: "aft_conflicts",
|
|
35202
35709
|
label: "conflicts",
|
|
35203
35710
|
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 +35731,7 @@ function registerConflictsTool(pi, ctx) {
|
|
|
35224
35731
|
renderResult(result, _options, theme, context) {
|
|
35225
35732
|
return renderConflictToolResult(result, theme, context);
|
|
35226
35733
|
}
|
|
35227
|
-
});
|
|
35734
|
+
}));
|
|
35228
35735
|
}
|
|
35229
35736
|
|
|
35230
35737
|
// src/tools/fs.ts
|
|
@@ -35239,7 +35746,7 @@ var DeleteParams = Type6.Object({
|
|
|
35239
35746
|
}))
|
|
35240
35747
|
});
|
|
35241
35748
|
var MoveParams = Type6.Object({
|
|
35242
|
-
|
|
35749
|
+
path: Type6.String({
|
|
35243
35750
|
description: "Source file path to move (absolute or relative to project root)"
|
|
35244
35751
|
}),
|
|
35245
35752
|
destination: Type6.String({
|
|
@@ -35263,7 +35770,7 @@ function renderFsCall(toolName, args, theme, context) {
|
|
|
35263
35770
|
return renderToolCall("delete", summary, theme, context);
|
|
35264
35771
|
}
|
|
35265
35772
|
const moveArgs = args;
|
|
35266
|
-
return renderToolCall("move", `${accentPath(theme, moveArgs.
|
|
35773
|
+
return renderToolCall("move", `${accentPath(theme, moveArgs.path)} ${theme.fg("muted", "→")} ${accentPath(theme, moveArgs.destination)}`, theme, context);
|
|
35267
35774
|
}
|
|
35268
35775
|
function renderFsResult(toolName, args, result, theme, context) {
|
|
35269
35776
|
if (context.isError) {
|
|
@@ -35289,14 +35796,14 @@ function renderFsResult(toolName, args, result, theme, context) {
|
|
|
35289
35796
|
}
|
|
35290
35797
|
const moveArgs = args;
|
|
35291
35798
|
return renderSections([
|
|
35292
|
-
`${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath3(moveArgs.
|
|
35799
|
+
`${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath3(moveArgs.path ?? ""))}`,
|
|
35293
35800
|
`${theme.fg("muted", "to")} ${theme.fg("accent", shortenPath3(moveArgs.destination))}`
|
|
35294
35801
|
], context);
|
|
35295
35802
|
}
|
|
35296
35803
|
function registerFsTools(pi, ctx, surface) {
|
|
35297
35804
|
const backupsDisabled = ctx.config.backup?.enabled === false;
|
|
35298
35805
|
if (surface.delete) {
|
|
35299
|
-
pi.registerTool({
|
|
35806
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35300
35807
|
name: "aft_delete",
|
|
35301
35808
|
label: "delete",
|
|
35302
35809
|
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 +35847,16 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
35340
35847
|
renderResult(result, _options, theme, context) {
|
|
35341
35848
|
return renderFsResult("aft_delete", context.args, result, theme, context);
|
|
35342
35849
|
}
|
|
35343
|
-
});
|
|
35850
|
+
}));
|
|
35344
35851
|
}
|
|
35345
35852
|
if (surface.move) {
|
|
35346
|
-
pi.registerTool({
|
|
35853
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35347
35854
|
name: "aft_move",
|
|
35348
35855
|
label: "move",
|
|
35349
35856
|
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
35857
|
parameters: MoveParams,
|
|
35351
35858
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
35352
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.
|
|
35859
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
35353
35860
|
const destination = await resolvePathArg(extCtx.cwd, params.destination);
|
|
35354
35861
|
const checked = new Set([filePath, destination]);
|
|
35355
35862
|
for (const file2 of checked) {
|
|
@@ -35359,7 +35866,7 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
35359
35866
|
}
|
|
35360
35867
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
35361
35868
|
const response = await callToolCall(bridge, "move", {
|
|
35362
|
-
filePath: params.
|
|
35869
|
+
filePath: params.path,
|
|
35363
35870
|
destination: params.destination
|
|
35364
35871
|
}, extCtx);
|
|
35365
35872
|
if (response.success === false) {
|
|
@@ -35373,7 +35880,7 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
35373
35880
|
renderResult(result, _options, theme, context) {
|
|
35374
35881
|
return renderFsResult("aft_move", context.args, result, theme, context);
|
|
35375
35882
|
}
|
|
35376
|
-
});
|
|
35883
|
+
}));
|
|
35377
35884
|
}
|
|
35378
35885
|
}
|
|
35379
35886
|
|
|
@@ -35382,7 +35889,9 @@ import { StringEnum as StringEnum2 } from "@earendil-works/pi-ai";
|
|
|
35382
35889
|
import { Type as Type7 } from "typebox";
|
|
35383
35890
|
var ImportParams = Type7.Object({
|
|
35384
35891
|
op: StringEnum2(["add", "remove", "organize"], { description: "Import operation" }),
|
|
35385
|
-
|
|
35892
|
+
path: Type7.String({
|
|
35893
|
+
description: "Path to the file (absolute or relative to project root)"
|
|
35894
|
+
}),
|
|
35386
35895
|
module: Type7.Optional(Type7.String({ description: "Module path (required for add/remove), e.g. 'react', './utils'" })),
|
|
35387
35896
|
names: Type7.Optional(Type7.Array(Type7.String(), {
|
|
35388
35897
|
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 +35921,7 @@ function buildImportSections(args, payload, theme) {
|
|
|
35412
35921
|
const groups = asRecords2(response.groups);
|
|
35413
35922
|
const groupText = groups.length > 0 ? groups.map((group) => `${asString2(group.name) ?? "unknown"}: ${asNumber2(group.count) ?? 0}`).join(" · ") : "No imports found";
|
|
35414
35923
|
return [
|
|
35415
|
-
`${theme.fg("success", "organized")} ${theme.fg("accent", asString2(response.file) ?? args.
|
|
35924
|
+
`${theme.fg("success", "organized")} ${theme.fg("accent", asString2(response.file) ?? args.path ?? "")}`,
|
|
35416
35925
|
`${theme.fg("muted", "groups")} ${groupText}`,
|
|
35417
35926
|
`${theme.fg("muted", "duplicates removed")} ${asNumber2(response.removed_duplicates) ?? 0}`
|
|
35418
35927
|
];
|
|
@@ -35422,7 +35931,7 @@ function buildImportSections(args, payload, theme) {
|
|
|
35422
35931
|
const status = response.already_present === true ? theme.fg("warning", "already present") : theme.fg("success", "added");
|
|
35423
35932
|
return [
|
|
35424
35933
|
`${status} ${theme.fg("accent", moduleName2)}`,
|
|
35425
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.
|
|
35934
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.path ?? "")}`,
|
|
35426
35935
|
`${theme.fg("muted", "group")} ${asString2(response.group) ?? "—"}`
|
|
35427
35936
|
];
|
|
35428
35937
|
}
|
|
@@ -35431,14 +35940,14 @@ function buildImportSections(args, payload, theme) {
|
|
|
35431
35940
|
const removeStatus = didRemove ? `${theme.fg("success", "removed")} ${theme.fg("accent", moduleName)}` : `${theme.fg("warning", "not present")} ${theme.fg("accent", moduleName)}`;
|
|
35432
35941
|
return [
|
|
35433
35942
|
removeStatus,
|
|
35434
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.
|
|
35943
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.path ?? "")}`,
|
|
35435
35944
|
args.removeName ? `${theme.fg("muted", "name")} ${args.removeName}` : `${theme.fg("muted", "scope")} entire import`
|
|
35436
35945
|
];
|
|
35437
35946
|
}
|
|
35438
35947
|
function renderImportCall(args, theme, context) {
|
|
35439
35948
|
const summary = [
|
|
35440
35949
|
theme.fg("accent", args.op),
|
|
35441
|
-
accentPath(theme, args.
|
|
35950
|
+
accentPath(theme, args.path ?? ""),
|
|
35442
35951
|
args.module ? theme.fg("toolOutput", args.module) : undefined
|
|
35443
35952
|
].filter(Boolean).join(" ");
|
|
35444
35953
|
return renderToolCall("import", summary, theme, context);
|
|
@@ -35451,7 +35960,7 @@ function renderImportResult(result, args, theme, context) {
|
|
|
35451
35960
|
}
|
|
35452
35961
|
function registerImportTools(pi, ctx) {
|
|
35453
35962
|
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({
|
|
35963
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35455
35964
|
name: "aft_import",
|
|
35456
35965
|
label: "import",
|
|
35457
35966
|
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 +35969,7 @@ function registerImportTools(pi, ctx) {
|
|
|
35460
35969
|
if ((params.op === "add" || params.op === "remove") && isEmptyParam(params.module)) {
|
|
35461
35970
|
throw new Error(`op='${params.op}' requires 'module'`);
|
|
35462
35971
|
}
|
|
35463
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.
|
|
35972
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
35464
35973
|
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
35465
35974
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
35466
35975
|
});
|
|
@@ -35498,7 +36007,7 @@ function registerImportTools(pi, ctx) {
|
|
|
35498
36007
|
renderResult(result, _options, theme, context) {
|
|
35499
36008
|
return renderImportResult(result, context.args, theme, context);
|
|
35500
36009
|
}
|
|
35501
|
-
});
|
|
36010
|
+
}));
|
|
35502
36011
|
}
|
|
35503
36012
|
|
|
35504
36013
|
// src/tools/inspect.ts
|
|
@@ -35574,7 +36083,7 @@ function diagnosticsSummaryPart(summary) {
|
|
|
35574
36083
|
const counts = `${errors3 ?? 0} errors/${warnings ?? 0} warnings/${info ?? 0} info/${hints ?? 0} hints`;
|
|
35575
36084
|
const status = asString2(section.status);
|
|
35576
36085
|
if (status === "pending") {
|
|
35577
|
-
return hasCounts ? `diagnostics ${counts} so far — still pending (servers: ${diagnosticsServerSummary(section)})` : `diagnostics pending (servers: ${diagnosticsServerSummary(section)})`;
|
|
36086
|
+
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
36087
|
}
|
|
35579
36088
|
if (status === "incomplete") {
|
|
35580
36089
|
return hasCounts ? `diagnostics ${counts} (incomplete — servers: ${diagnosticsServerSummary(section)})` : `diagnostics unavailable (status incomplete; servers: ${diagnosticsServerSummary(section)})`;
|
|
@@ -35727,7 +36236,7 @@ function buildInspectSections(payload, theme) {
|
|
|
35727
36236
|
].filter((part) => Boolean(part));
|
|
35728
36237
|
const sections = [theme.fg("accent", parts.join(" · "))];
|
|
35729
36238
|
if (stale > 0 || pending > 0) {
|
|
35730
|
-
sections.push(theme.fg("warning", `scanner state: ${stale} stale · ${pending} pending`));
|
|
36239
|
+
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
36240
|
}
|
|
35732
36241
|
const topPreview = tier2TopPreview(summary, theme);
|
|
35733
36242
|
if (topPreview)
|
|
@@ -35803,7 +36312,7 @@ function navigateParamsSchema() {
|
|
|
35803
36312
|
op: StringEnum3(["call_tree", "callers", "trace_to", "trace_to_symbol", "impact", "trace_data"], {
|
|
35804
36313
|
description: "Navigation operation"
|
|
35805
36314
|
}),
|
|
35806
|
-
|
|
36315
|
+
path: Type9.String({
|
|
35807
36316
|
description: "Source file containing the symbol (absolute or relative to project root)"
|
|
35808
36317
|
}),
|
|
35809
36318
|
symbol: Type9.String({ description: "Name of the symbol to analyze" }),
|
|
@@ -35812,7 +36321,7 @@ function navigateParamsSchema() {
|
|
|
35812
36321
|
toSymbol: Type9.Optional(Type9.String({
|
|
35813
36322
|
description: "Target symbol for trace_to_symbol; the returned path ends here"
|
|
35814
36323
|
})),
|
|
35815
|
-
|
|
36324
|
+
toPath: Type9.Optional(Type9.String({
|
|
35816
36325
|
description: "Optional target file for trace_to_symbol; required when toSymbol exists in multiple files"
|
|
35817
36326
|
})),
|
|
35818
36327
|
includeTests: Type9.Optional(Type9.Boolean({
|
|
@@ -35834,7 +36343,7 @@ function buildNavigateSections(args, payload, theme) {
|
|
|
35834
36343
|
function renderNavigateCall(args, theme, context) {
|
|
35835
36344
|
const summary = [
|
|
35836
36345
|
theme.fg("accent", args.op),
|
|
35837
|
-
accentPath(theme, args.
|
|
36346
|
+
accentPath(theme, args.path),
|
|
35838
36347
|
theme.fg("toolOutput", args.symbol),
|
|
35839
36348
|
args.toSymbol ? theme.fg("toolOutput", `→ ${args.toSymbol}`) : undefined
|
|
35840
36349
|
].filter(Boolean).join(" ");
|
|
@@ -35846,14 +36355,14 @@ function renderNavigateResult(result, args, theme, context) {
|
|
|
35846
36355
|
return renderSections(buildNavigateSections(args, extractStructuredPayload(result), theme), context);
|
|
35847
36356
|
}
|
|
35848
36357
|
function registerNavigateTool(pi, ctx) {
|
|
35849
|
-
pi.registerTool({
|
|
36358
|
+
pi.registerTool(withPathAliasPreparation({
|
|
35850
36359
|
name: "aft_callgraph",
|
|
35851
36360
|
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 `
|
|
36361
|
+
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
36362
|
parameters: navigateParamsSchema(),
|
|
35854
36363
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
35855
|
-
if (isEmptyParam(params.
|
|
35856
|
-
throw new Error(`op='${params.op}' requires a \`
|
|
36364
|
+
if (isEmptyParam(params.path)) {
|
|
36365
|
+
throw new Error(`op='${params.op}' requires a \`path\``);
|
|
35857
36366
|
}
|
|
35858
36367
|
if (isEmptyParam(params.symbol)) {
|
|
35859
36368
|
throw new Error(`op='${params.op}' requires a \`symbol\``);
|
|
@@ -35864,8 +36373,8 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35864
36373
|
if (params.op === "trace_to_symbol" && isEmptyParam(params.toSymbol)) {
|
|
35865
36374
|
throw new Error("op='trace_to_symbol' requires a `toSymbol`");
|
|
35866
36375
|
}
|
|
35867
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.
|
|
35868
|
-
const toFile = !isEmptyParam(params.
|
|
36376
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
36377
|
+
const toFile = !isEmptyParam(params.toPath) ? await resolvePathArg(extCtx.cwd, params.toPath) : undefined;
|
|
35869
36378
|
const checked = new Set;
|
|
35870
36379
|
for (const target of [filePath, ...toFile !== undefined ? [toFile] : []]) {
|
|
35871
36380
|
if (checked.has(target))
|
|
@@ -35878,7 +36387,7 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35878
36387
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
35879
36388
|
const rawArgs = {
|
|
35880
36389
|
op: params.op,
|
|
35881
|
-
filePath: params.
|
|
36390
|
+
filePath: params.path,
|
|
35882
36391
|
symbol: params.symbol
|
|
35883
36392
|
};
|
|
35884
36393
|
const depth = coerceOptionalInt(params.depth, "depth", 1, Number.MAX_SAFE_INTEGER);
|
|
@@ -35888,8 +36397,8 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35888
36397
|
rawArgs.expression = params.expression;
|
|
35889
36398
|
if (!isEmptyParam(params.toSymbol))
|
|
35890
36399
|
rawArgs.toSymbol = params.toSymbol;
|
|
35891
|
-
if (!isEmptyParam(params.
|
|
35892
|
-
rawArgs.toFile = params.
|
|
36400
|
+
if (!isEmptyParam(params.toPath))
|
|
36401
|
+
rawArgs.toFile = params.toPath;
|
|
35893
36402
|
if (!isEmptyParam(params.includeTests))
|
|
35894
36403
|
rawArgs.includeTests = coerceBoolean(params.includeTests);
|
|
35895
36404
|
if (!isEmptyParam(params.includeUnresolved))
|
|
@@ -35914,7 +36423,7 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35914
36423
|
renderResult(result, _options, theme, context) {
|
|
35915
36424
|
return renderNavigateResult(result, context.args, theme, context);
|
|
35916
36425
|
}
|
|
35917
|
-
});
|
|
36426
|
+
}));
|
|
35918
36427
|
}
|
|
35919
36428
|
|
|
35920
36429
|
// src/tools/reading.ts
|
|
@@ -35931,21 +36440,21 @@ var OutlineParams = Type10.Object({
|
|
|
35931
36440
|
}))
|
|
35932
36441
|
});
|
|
35933
36442
|
var ZoomTarget = Type10.Object({
|
|
35934
|
-
|
|
36443
|
+
path: Type10.String({ description: "Path to file (absolute or project-relative)" }),
|
|
35935
36444
|
symbol: Type10.String({ description: "Symbol name in that file" })
|
|
35936
36445
|
});
|
|
35937
36446
|
var ZoomParams = Type10.Object({
|
|
35938
|
-
|
|
36447
|
+
path: Type10.Optional(Type10.String({ description: "Path to file (absolute or project-relative)" })),
|
|
35939
36448
|
url: Type10.Optional(Type10.String({
|
|
35940
36449
|
description: "HTTP/HTTPS URL of an HTML or Markdown document to fetch and zoom into"
|
|
35941
36450
|
})),
|
|
35942
36451
|
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
|
|
36452
|
+
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
36453
|
})),
|
|
35945
36454
|
targets: Type10.Optional(Type10.Union([ZoomTarget, Type10.Array(ZoomTarget)], {
|
|
35946
|
-
description: "Cross-file batch: `{
|
|
36455
|
+
description: "Cross-file batch: `{ path, symbol }` or an array of them. Mutually exclusive with path/url/symbols."
|
|
35947
36456
|
})),
|
|
35948
|
-
contextLines:
|
|
36457
|
+
contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER, "Lines of context before/after (default: 3)"),
|
|
35949
36458
|
callgraph: Type10.Optional(Type10.Boolean({
|
|
35950
36459
|
description: "Include call-graph annotations (calls-out / called-by within the same file). Default false; off keeps zoom output minimal."
|
|
35951
36460
|
}))
|
|
@@ -35966,7 +36475,7 @@ async function assertReadPathPermissions(extCtx, ctx, paths) {
|
|
|
35966
36475
|
}
|
|
35967
36476
|
}
|
|
35968
36477
|
function zoomTargetLabel(args) {
|
|
35969
|
-
return args.
|
|
36478
|
+
return args.path ?? args.url ?? "(no target)";
|
|
35970
36479
|
}
|
|
35971
36480
|
function zoomExtraCallSites(call) {
|
|
35972
36481
|
const extraCount = call.extra_count;
|
|
@@ -36087,7 +36596,7 @@ function renderZoomResult(result, args, theme, context) {
|
|
|
36087
36596
|
}
|
|
36088
36597
|
function registerReadingTools(pi, ctx, surface) {
|
|
36089
36598
|
if (surface.outline) {
|
|
36090
|
-
pi.registerTool({
|
|
36599
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36091
36600
|
name: "aft_outline",
|
|
36092
36601
|
label: "outline",
|
|
36093
36602
|
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 +36639,17 @@ Pass a single \`target\`:
|
|
|
36130
36639
|
if (response.success === false) {
|
|
36131
36640
|
throw new Error(response.text || response.message || "outline failed");
|
|
36132
36641
|
}
|
|
36133
|
-
|
|
36642
|
+
let text = typeof response.text === "string" ? response.text : "";
|
|
36643
|
+
const trimmed = text.trim();
|
|
36644
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
36645
|
+
try {
|
|
36646
|
+
const parsed = JSON.parse(text);
|
|
36647
|
+
if (typeof parsed.text === "string") {
|
|
36648
|
+
text = parsed.text;
|
|
36649
|
+
}
|
|
36650
|
+
} catch {}
|
|
36651
|
+
}
|
|
36652
|
+
return textResult(text, response);
|
|
36134
36653
|
},
|
|
36135
36654
|
renderCall(args, theme, context) {
|
|
36136
36655
|
return renderOutlineCall(args, theme, context);
|
|
@@ -36138,13 +36657,13 @@ Pass a single \`target\`:
|
|
|
36138
36657
|
renderResult(result, _options, theme, context) {
|
|
36139
36658
|
return renderOutlineResult(result, theme, context);
|
|
36140
36659
|
}
|
|
36141
|
-
});
|
|
36660
|
+
}));
|
|
36142
36661
|
}
|
|
36143
36662
|
if (surface.zoom) {
|
|
36144
|
-
pi.registerTool({
|
|
36663
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36145
36664
|
name: "aft_zoom",
|
|
36146
36665
|
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: `{
|
|
36666
|
+
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
36667
|
parameters: ZoomParams,
|
|
36149
36668
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
36150
36669
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
@@ -36154,7 +36673,7 @@ Pass a single \`target\`:
|
|
|
36154
36673
|
const entryEmpty = (entry) => {
|
|
36155
36674
|
if (!entry || typeof entry !== "object")
|
|
36156
36675
|
return true;
|
|
36157
|
-
const fp = entry.
|
|
36676
|
+
const fp = entry.path;
|
|
36158
36677
|
const sym = entry.symbol;
|
|
36159
36678
|
const fpEmpty = typeof fp !== "string" || fp.length === 0;
|
|
36160
36679
|
const symEmpty = typeof sym !== "string" || sym.length === 0;
|
|
@@ -36164,31 +36683,33 @@ Pass a single \`target\`:
|
|
|
36164
36683
|
return !t.every(entryEmpty);
|
|
36165
36684
|
return !entryEmpty(t);
|
|
36166
36685
|
};
|
|
36167
|
-
const
|
|
36686
|
+
const hasPath2 = !isEmptyParam(params.path);
|
|
36168
36687
|
const hasUrl = !isEmptyParam(params.url);
|
|
36169
36688
|
const hasTargets = hasTargetsProvided(params.targets);
|
|
36170
36689
|
const hasSymbols = !isEmptyParam(params.symbols);
|
|
36171
36690
|
const wantCallgraph = coerceBoolean(params.callgraph);
|
|
36172
36691
|
const contextLines = coerceOptionalInt(params.contextLines, "contextLines", 1, Number.MAX_SAFE_INTEGER);
|
|
36173
36692
|
if (hasTargets) {
|
|
36174
|
-
if (
|
|
36175
|
-
throw new Error("'targets' is mutually exclusive with '
|
|
36693
|
+
if (hasPath2 || hasUrl || hasSymbols) {
|
|
36694
|
+
throw new Error("'targets' is mutually exclusive with 'path', 'url', and 'symbols'");
|
|
36176
36695
|
}
|
|
36177
36696
|
const targets = Array.isArray(params.targets) ? params.targets : [params.targets];
|
|
36178
36697
|
if (targets.length === 0) {
|
|
36179
36698
|
throw new Error("'targets' must be a non-empty object or array");
|
|
36180
36699
|
}
|
|
36181
36700
|
for (const [i, entry] of targets.entries()) {
|
|
36182
|
-
if (!entry || typeof entry.
|
|
36183
|
-
throw new Error(`targets[${i}].
|
|
36701
|
+
if (!entry || typeof entry.path !== "string" || entry.path.length === 0) {
|
|
36702
|
+
throw new Error(`targets[${i}].path must be a non-empty string`);
|
|
36184
36703
|
}
|
|
36185
36704
|
if (typeof entry.symbol !== "string" || entry.symbol.length === 0) {
|
|
36186
36705
|
throw new Error(`targets[${i}].symbol must be a non-empty string`);
|
|
36187
36706
|
}
|
|
36188
36707
|
}
|
|
36189
|
-
const resolvedTargets = await Promise.all(targets.map((t) => resolvePathArg(extCtx.cwd, t.
|
|
36708
|
+
const resolvedTargets = await Promise.all(targets.map((t) => resolvePathArg(extCtx.cwd, t.path)));
|
|
36190
36709
|
await assertReadPathPermissions(extCtx, ctx, resolvedTargets);
|
|
36191
|
-
const rawArgs2 = {
|
|
36710
|
+
const rawArgs2 = {
|
|
36711
|
+
targets: targets.map((target) => ({ filePath: target.path, symbol: target.symbol }))
|
|
36712
|
+
};
|
|
36192
36713
|
if (contextLines !== undefined)
|
|
36193
36714
|
rawArgs2.contextLines = contextLines;
|
|
36194
36715
|
if (wantCallgraph)
|
|
@@ -36199,17 +36720,17 @@ Pass a single \`target\`:
|
|
|
36199
36720
|
}
|
|
36200
36721
|
return textResult(response2.text, response2);
|
|
36201
36722
|
}
|
|
36202
|
-
if (!
|
|
36203
|
-
throw new Error("Provide exactly one of '
|
|
36723
|
+
if (!hasPath2 && !hasUrl) {
|
|
36724
|
+
throw new Error("Provide exactly one of 'path', 'url', or 'targets'");
|
|
36204
36725
|
}
|
|
36205
|
-
if (
|
|
36206
|
-
throw new Error("Provide exactly ONE of '
|
|
36726
|
+
if (hasPath2 && hasUrl) {
|
|
36727
|
+
throw new Error("Provide exactly ONE of 'path' or 'url' — not both");
|
|
36207
36728
|
}
|
|
36208
36729
|
if (!hasUrl) {
|
|
36209
|
-
const file2 = await resolvePathArg(extCtx.cwd, params.
|
|
36730
|
+
const file2 = await resolvePathArg(extCtx.cwd, params.path);
|
|
36210
36731
|
await assertReadPathPermissions(extCtx, ctx, file2);
|
|
36211
36732
|
}
|
|
36212
|
-
const rawArgs = hasUrl ? { url: params.url } : { filePath: params.
|
|
36733
|
+
const rawArgs = hasUrl ? { url: params.url } : { filePath: params.path };
|
|
36213
36734
|
if (hasSymbols)
|
|
36214
36735
|
rawArgs.symbols = params.symbols;
|
|
36215
36736
|
if (contextLines !== undefined)
|
|
@@ -36228,7 +36749,7 @@ Pass a single \`target\`:
|
|
|
36228
36749
|
renderResult(result, _options, theme, context) {
|
|
36229
36750
|
return renderZoomResult(result, context.args, theme, context);
|
|
36230
36751
|
}
|
|
36231
|
-
});
|
|
36752
|
+
}));
|
|
36232
36753
|
}
|
|
36233
36754
|
}
|
|
36234
36755
|
|
|
@@ -36237,7 +36758,7 @@ import { StringEnum as StringEnum4 } from "@earendil-works/pi-ai";
|
|
|
36237
36758
|
import { Type as Type11 } from "typebox";
|
|
36238
36759
|
var RefactorParams = Type11.Object({
|
|
36239
36760
|
op: StringEnum4(["move", "extract", "inline"], { description: "Refactoring operation" }),
|
|
36240
|
-
|
|
36761
|
+
path: Type11.String({
|
|
36241
36762
|
description: "Source file (absolute or relative to project root)"
|
|
36242
36763
|
}),
|
|
36243
36764
|
symbol: Type11.Optional(Type11.String({ description: "Symbol name (for move, inline)" })),
|
|
@@ -36265,14 +36786,14 @@ function buildRefactorSections(args, payload, theme) {
|
|
|
36265
36786
|
if (args.op === "extract") {
|
|
36266
36787
|
return [
|
|
36267
36788
|
`${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.
|
|
36789
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? ""))}`,
|
|
36269
36790
|
`${theme.fg("muted", "params")} ${Array.isArray(response.parameters) ? response.parameters.join(", ") || "none" : "none"}`,
|
|
36270
36791
|
`${theme.fg("muted", "return type")} ${asString2(response.return_type) ?? "unknown"}`
|
|
36271
36792
|
];
|
|
36272
36793
|
}
|
|
36273
36794
|
return [
|
|
36274
36795
|
`${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.
|
|
36796
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? ""))}`,
|
|
36276
36797
|
`${theme.fg("muted", "context")} ${asString2(response.call_context) ?? "unknown"}`,
|
|
36277
36798
|
`${theme.fg("muted", "substitutions")} ${asNumber2(response.substitutions) ?? 0}`
|
|
36278
36799
|
];
|
|
@@ -36280,7 +36801,7 @@ function buildRefactorSections(args, payload, theme) {
|
|
|
36280
36801
|
function renderRefactorCall(args, theme, context) {
|
|
36281
36802
|
const summary = [
|
|
36282
36803
|
theme.fg("accent", args.op),
|
|
36283
|
-
accentPath(theme, args.
|
|
36804
|
+
accentPath(theme, args.path),
|
|
36284
36805
|
args.symbol ? theme.fg("toolOutput", args.symbol) : undefined
|
|
36285
36806
|
].filter(Boolean).join(" ");
|
|
36286
36807
|
return renderToolCall("refactor", summary, theme, context);
|
|
@@ -36291,7 +36812,7 @@ function renderRefactorResult(result, args, theme, context) {
|
|
|
36291
36812
|
return renderSections(buildRefactorSections(args, extractStructuredPayload(result), theme), context);
|
|
36292
36813
|
}
|
|
36293
36814
|
function registerRefactorTool(pi, ctx) {
|
|
36294
|
-
pi.registerTool({
|
|
36815
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36295
36816
|
name: "aft_refactor",
|
|
36296
36817
|
label: "refactor",
|
|
36297
36818
|
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 +36839,7 @@ function registerRefactorTool(pi, ctx) {
|
|
|
36318
36839
|
if (params.op === "inline" && callSiteLine === undefined) {
|
|
36319
36840
|
throw new Error("'callSiteLine' is required for 'inline' op");
|
|
36320
36841
|
}
|
|
36321
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.
|
|
36842
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
36322
36843
|
const destination = !isEmptyParam(params.destination) ? await resolvePathArg(extCtx.cwd, params.destination) : undefined;
|
|
36323
36844
|
const permissionTargets = params.op === "move" && destination !== undefined ? [filePath, destination] : [filePath];
|
|
36324
36845
|
const checked = new Set;
|
|
@@ -36358,7 +36879,7 @@ function registerRefactorTool(pi, ctx) {
|
|
|
36358
36879
|
renderResult(result, _options, theme, context) {
|
|
36359
36880
|
return renderRefactorResult(result, context.args, theme, context);
|
|
36360
36881
|
}
|
|
36361
|
-
});
|
|
36882
|
+
}));
|
|
36362
36883
|
}
|
|
36363
36884
|
|
|
36364
36885
|
// src/tools/safety.ts
|
|
@@ -36371,7 +36892,7 @@ var SafetyParams = Type12.Object({
|
|
|
36371
36892
|
op: StringEnum5(["undo", "history", "checkpoint", "restore", "list"], {
|
|
36372
36893
|
description: "Safety operation"
|
|
36373
36894
|
}),
|
|
36374
|
-
|
|
36895
|
+
path: Type12.Optional(Type12.String({
|
|
36375
36896
|
description: "File path (required for history, optional for undo). Absolute or relative to project root."
|
|
36376
36897
|
})),
|
|
36377
36898
|
name: Type12.Optional(Type12.String({ description: "Checkpoint name (required for checkpoint, restore)" })),
|
|
@@ -36391,14 +36912,14 @@ function buildSafetySections(args, payload, theme) {
|
|
|
36391
36912
|
];
|
|
36392
36913
|
}
|
|
36393
36914
|
return [
|
|
36394
|
-
`${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath3(asString2(response.path) ?? args.
|
|
36915
|
+
`${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath3(asString2(response.path) ?? args.path ?? "(file)"))}`,
|
|
36395
36916
|
`${theme.fg("muted", "backup")} ${asString2(response.backup_id) ?? "—"}`
|
|
36396
36917
|
];
|
|
36397
36918
|
}
|
|
36398
36919
|
if (args.op === "history") {
|
|
36399
36920
|
const entries = asRecords2(response.entries);
|
|
36400
36921
|
const sections2 = [
|
|
36401
|
-
theme.fg("accent", shortenPath3(asString2(response.file) ?? args.
|
|
36922
|
+
theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? "(file)"))
|
|
36402
36923
|
];
|
|
36403
36924
|
if (entries.length === 0) {
|
|
36404
36925
|
sections2.push(theme.fg("muted", "No history entries."));
|
|
@@ -36448,7 +36969,7 @@ ${skipped.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(file)"
|
|
|
36448
36969
|
return sections;
|
|
36449
36970
|
}
|
|
36450
36971
|
function renderSafetyCall(args, theme, context) {
|
|
36451
|
-
const target = args.
|
|
36972
|
+
const target = args.path ?? args.name;
|
|
36452
36973
|
const summary = [theme.fg("accent", args.op), target ? accentPath(theme, target) : undefined].filter(Boolean).join(" ");
|
|
36453
36974
|
return renderToolCall("safety", summary, theme, context);
|
|
36454
36975
|
}
|
|
@@ -36458,19 +36979,19 @@ function renderSafetyResult(result, args, theme, context) {
|
|
|
36458
36979
|
return renderSections(buildSafetySections(args, extractStructuredPayload(result), theme), context);
|
|
36459
36980
|
}
|
|
36460
36981
|
function registerSafetyTool(pi, ctx) {
|
|
36461
|
-
pi.registerTool({
|
|
36982
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36462
36983
|
name: "aft_safety",
|
|
36463
36984
|
label: "safety",
|
|
36464
|
-
description: "File safety and recovery operations. Ops: `undo` (omit
|
|
36985
|
+
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
36986
|
parameters: SafetyParams,
|
|
36466
36987
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
36467
|
-
if (params.op === "history" && !params.
|
|
36468
|
-
throw new Error(`op='${params.op}' requires '
|
|
36988
|
+
if (params.op === "history" && !params.path) {
|
|
36989
|
+
throw new Error(`op='${params.op}' requires 'path'`);
|
|
36469
36990
|
}
|
|
36470
36991
|
if ((params.op === "checkpoint" || params.op === "restore") && !params.name) {
|
|
36471
36992
|
throw new Error(`op='${params.op}' requires 'name'`);
|
|
36472
36993
|
}
|
|
36473
|
-
const filePath = params.
|
|
36994
|
+
const filePath = params.path ? await resolvePathArg(extCtx.cwd, params.path) : undefined;
|
|
36474
36995
|
const fileInputs = coerceStringArray(params.files);
|
|
36475
36996
|
const files = fileInputs.length > 0 ? await Promise.all(fileInputs.map((file2) => resolvePathArg(extCtx.cwd, file2))) : undefined;
|
|
36476
36997
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
@@ -36527,7 +37048,7 @@ function registerSafetyTool(pi, ctx) {
|
|
|
36527
37048
|
renderResult(result, _options, theme, context) {
|
|
36528
37049
|
return renderSafetyResult(result, context.args, theme, context);
|
|
36529
37050
|
}
|
|
36530
|
-
});
|
|
37051
|
+
}));
|
|
36531
37052
|
}
|
|
36532
37053
|
|
|
36533
37054
|
// src/tools/semantic.ts
|
|
@@ -36554,14 +37075,6 @@ var SearchParams2 = Type13.Object({
|
|
|
36554
37075
|
default: 10,
|
|
36555
37076
|
description: "Maximum number of results (default: 10, max: 100)"
|
|
36556
37077
|
})),
|
|
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
37078
|
includeTests: Type13.Optional(Type13.Boolean({
|
|
36566
37079
|
default: false,
|
|
36567
37080
|
description: "Include test files (*.test.*, *_test.rs, __tests__/, …) plus test-support, fixture, mock, snapshot, and corpus files. Defaults to false."
|
|
@@ -36653,13 +37166,11 @@ function renderSemanticResult(result, args, theme, context) {
|
|
|
36653
37166
|
return renderSections(buildSemanticSections(args, extractStructuredPayload(result), theme), context);
|
|
36654
37167
|
}
|
|
36655
37168
|
function registerSemanticTool(pi, ctx) {
|
|
36656
|
-
pi.registerTool({
|
|
37169
|
+
pi.registerTool(withPathAliasPreparation({
|
|
36657
37170
|
name: "aft_search",
|
|
36658
37171
|
label: "search",
|
|
36659
37172
|
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."
|
|
37173
|
+
"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
37174
|
].join(`
|
|
36664
37175
|
`),
|
|
36665
37176
|
parameters: SearchParams2,
|
|
@@ -36671,8 +37182,6 @@ function registerSemanticTool(pi, ctx) {
|
|
|
36671
37182
|
const req = { query: params.query };
|
|
36672
37183
|
if (params.topK !== undefined)
|
|
36673
37184
|
req.topK = params.topK;
|
|
36674
|
-
if (params.hint !== undefined)
|
|
36675
|
-
req.hint = params.hint;
|
|
36676
37185
|
if (params.includeTests !== undefined)
|
|
36677
37186
|
req.includeTests = params.includeTests;
|
|
36678
37187
|
if (params.path !== undefined)
|
|
@@ -36689,7 +37198,98 @@ function registerSemanticTool(pi, ctx) {
|
|
|
36689
37198
|
renderResult(result, _options, theme, context) {
|
|
36690
37199
|
return renderSemanticResult(result, context.args, theme, context);
|
|
36691
37200
|
}
|
|
36692
|
-
});
|
|
37201
|
+
}));
|
|
37202
|
+
}
|
|
37203
|
+
|
|
37204
|
+
// src/tool-registration.ts
|
|
37205
|
+
var ALL_ONLY_TOOLS = new Set(["aft_callgraph", "aft_delete", "aft_move", "aft_refactor"]);
|
|
37206
|
+
function resolvePiToolSurface(config2) {
|
|
37207
|
+
const surface = config2.tool_surface ?? "recommended";
|
|
37208
|
+
const disabled = new Set(config2.disabled_tools ?? []);
|
|
37209
|
+
const ok = (name) => !disabled.has(name);
|
|
37210
|
+
const allOnly = (name) => ALL_ONLY_TOOLS.has(name) && ok(name);
|
|
37211
|
+
const restrictToProjectRoot = config2.restrict_to_project_root ?? false;
|
|
37212
|
+
if (surface === "minimal") {
|
|
37213
|
+
return {
|
|
37214
|
+
hoistBash: ok("bash"),
|
|
37215
|
+
hoistRead: false,
|
|
37216
|
+
hoistWrite: false,
|
|
37217
|
+
hoistEdit: false,
|
|
37218
|
+
hoistGrep: false,
|
|
37219
|
+
restrictToProjectRoot,
|
|
37220
|
+
outline: ok("aft_outline"),
|
|
37221
|
+
zoom: ok("aft_zoom"),
|
|
37222
|
+
semantic: false,
|
|
37223
|
+
inspect: false,
|
|
37224
|
+
navigate: false,
|
|
37225
|
+
conflicts: false,
|
|
37226
|
+
importTool: false,
|
|
37227
|
+
safety: ok("aft_safety"),
|
|
37228
|
+
delete: false,
|
|
37229
|
+
move: false,
|
|
37230
|
+
astSearch: false,
|
|
37231
|
+
astReplace: false,
|
|
37232
|
+
refactor: false
|
|
37233
|
+
};
|
|
37234
|
+
}
|
|
37235
|
+
const base = {
|
|
37236
|
+
hoistBash: ok("bash"),
|
|
37237
|
+
hoistRead: ok("read"),
|
|
37238
|
+
hoistWrite: ok("write"),
|
|
37239
|
+
hoistEdit: ok("edit"),
|
|
37240
|
+
hoistGrep: ok("grep") && config2.search_index === true,
|
|
37241
|
+
restrictToProjectRoot,
|
|
37242
|
+
outline: ok("aft_outline"),
|
|
37243
|
+
zoom: ok("aft_zoom"),
|
|
37244
|
+
semantic: ok("aft_search") && config2.semantic_search === true,
|
|
37245
|
+
inspect: ok("aft_inspect") && config2.inspect?.enabled !== false,
|
|
37246
|
+
navigate: false,
|
|
37247
|
+
conflicts: ok("aft_conflicts"),
|
|
37248
|
+
importTool: ok("aft_import"),
|
|
37249
|
+
safety: ok("aft_safety"),
|
|
37250
|
+
delete: false,
|
|
37251
|
+
move: false,
|
|
37252
|
+
astSearch: ok("ast_grep_search"),
|
|
37253
|
+
astReplace: ok("ast_grep_replace"),
|
|
37254
|
+
refactor: false
|
|
37255
|
+
};
|
|
37256
|
+
if (surface === "all") {
|
|
37257
|
+
return {
|
|
37258
|
+
...base,
|
|
37259
|
+
navigate: allOnly("aft_callgraph"),
|
|
37260
|
+
delete: allOnly("aft_delete"),
|
|
37261
|
+
move: allOnly("aft_move"),
|
|
37262
|
+
refactor: allOnly("aft_refactor")
|
|
37263
|
+
};
|
|
37264
|
+
}
|
|
37265
|
+
return base;
|
|
37266
|
+
}
|
|
37267
|
+
function registerPiToolSurface(pi, ctx, surface) {
|
|
37268
|
+
if (surface.hoistBash && resolveBashConfig(ctx.config).enabled) {
|
|
37269
|
+
registerBashTool(pi, ctx, surface.semantic);
|
|
37270
|
+
}
|
|
37271
|
+
registerHoistedTools(pi, ctx, surface);
|
|
37272
|
+
if (surface.outline || surface.zoom) {
|
|
37273
|
+
registerReadingTools(pi, ctx, surface);
|
|
37274
|
+
}
|
|
37275
|
+
if (surface.semantic)
|
|
37276
|
+
registerSemanticTool(pi, ctx);
|
|
37277
|
+
if (surface.inspect)
|
|
37278
|
+
registerInspectTool(pi, ctx);
|
|
37279
|
+
if (surface.navigate)
|
|
37280
|
+
registerNavigateTool(pi, ctx);
|
|
37281
|
+
if (surface.conflicts)
|
|
37282
|
+
registerConflictsTool(pi, ctx);
|
|
37283
|
+
if (surface.importTool)
|
|
37284
|
+
registerImportTools(pi, ctx);
|
|
37285
|
+
if (surface.safety && ctx.config.backup?.enabled !== false)
|
|
37286
|
+
registerSafetyTool(pi, ctx);
|
|
37287
|
+
if (surface.astSearch || surface.astReplace)
|
|
37288
|
+
registerAstTools(pi, ctx, surface);
|
|
37289
|
+
if (surface.delete || surface.move)
|
|
37290
|
+
registerFsTools(pi, ctx, surface);
|
|
37291
|
+
if (surface.refactor)
|
|
37292
|
+
registerRefactorTool(pi, ctx);
|
|
36693
37293
|
}
|
|
36694
37294
|
|
|
36695
37295
|
// src/workflow-hints.ts
|
|
@@ -36720,18 +37320,18 @@ function buildWorkflowHints(opts) {
|
|
|
36720
37320
|
}
|
|
36721
37321
|
if (hasOutline && hasZoom && (hasGrep || hasSearch)) {
|
|
36722
37322
|
const searchName = hasSearch ? "aft_search" : grepName;
|
|
36723
|
-
const locate = hasSearch ?
|
|
37323
|
+
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
37324
|
const readName = opts.hoistBuiltins ? "read" : "aft_read";
|
|
36725
37325
|
sections.push([
|
|
36726
37326
|
`**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
37327
|
`- \`grep -rn "handleAuth" src/\` in bash → \`${searchName}({ query: "handleAuth" })\``,
|
|
36728
37328
|
`- \`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}({
|
|
37329
|
+
`- \`sed -n '100,160p' app.ts\` / \`cat app.ts\` in bash → \`${readName}({ path: "app.ts", startLine: 100, endLine: 160 })\``
|
|
36730
37330
|
].join(`
|
|
36731
37331
|
`));
|
|
36732
37332
|
}
|
|
36733
37333
|
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
|
|
37334
|
+
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
37335
|
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
37336
|
}
|
|
36737
37337
|
if (hasNavigate) {
|
|
@@ -36851,14 +37451,13 @@ var PLUGIN_VERSION = (() => {
|
|
|
36851
37451
|
return "0.0.0";
|
|
36852
37452
|
}
|
|
36853
37453
|
})();
|
|
36854
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
37454
|
+
var ANNOUNCEMENT_VERSION = "0.49.1";
|
|
36855
37455
|
var ANNOUNCEMENT_FEATURES = [
|
|
36856
|
-
"
|
|
36857
|
-
"
|
|
36858
|
-
"
|
|
37456
|
+
"Windows: language servers spawn again under extended-length paths, config edits reach running servers, and dead-code/diagnostics results no longer silently drop — one canonical path form across the LSP and inspect surface.",
|
|
37457
|
+
"Faster answers under load: completed bash commands respond immediately instead of waiting behind background maintenance (thanks @hheei), and chained commands like `cargo test && ...` keep every command's output.",
|
|
37458
|
+
"Permission rules with absolute paths outside the project root now match (thanks @iceteaSA), and slow embedding providers get a clear timeout message naming the setting that raises the budget."
|
|
36859
37459
|
];
|
|
36860
37460
|
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
37461
|
var pendingEagerWarnings = new Map;
|
|
36863
37462
|
function isConfigureWarning(value) {
|
|
36864
37463
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -36939,67 +37538,6 @@ async function handleConfigureWarningsForSession(context) {
|
|
|
36939
37538
|
projectRoot: context.projectRoot
|
|
36940
37539
|
}, combinedWarnings);
|
|
36941
37540
|
}
|
|
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
37541
|
async function src_default(pi) {
|
|
37004
37542
|
const deliverConfigMigrationWarnings = (messages) => {
|
|
37005
37543
|
for (const message of messages) {
|
|
@@ -37210,7 +37748,7 @@ ${lines}
|
|
|
37210
37748
|
});
|
|
37211
37749
|
}
|
|
37212
37750
|
pool.setConfigureOverride("harness", "pi");
|
|
37213
|
-
pool.setConfigureOverride("aft_search_registered",
|
|
37751
|
+
pool.setConfigureOverride("aft_search_registered", resolvePiToolSurface(config2).semantic);
|
|
37214
37752
|
const ctx = { pool, config: config2, storageDir };
|
|
37215
37753
|
if (onnxRuntimePromise) {
|
|
37216
37754
|
onnxRuntimePromise.then((ortDylibDir) => {
|
|
@@ -37249,41 +37787,8 @@ ${lines}
|
|
|
37249
37787
|
if (ANNOUNCEMENT_VERSION && ANNOUNCEMENT_FEATURES.length > 0) {
|
|
37250
37788
|
sendFeatureAnnouncement(ANNOUNCEMENT_VERSION, ANNOUNCEMENT_FEATURES, ANNOUNCEMENT_FOOTER, storageDir);
|
|
37251
37789
|
}
|
|
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
|
-
}
|
|
37790
|
+
const surface = resolvePiToolSurface(config2);
|
|
37791
|
+
registerPiToolSurface(pi, ctx, surface);
|
|
37287
37792
|
registerWorkflowHints(pi, config2, surface);
|
|
37288
37793
|
registerStatusCommand(pi, ctx);
|
|
37289
37794
|
pi.on("tool_result", async (event, extCtx) => {
|
|
@@ -37349,7 +37854,7 @@ ${lines}
|
|
|
37349
37854
|
var __test__2 = {
|
|
37350
37855
|
enqueueConfigParseWarnings,
|
|
37351
37856
|
bridgeDirectoryFromCallback,
|
|
37352
|
-
resolveToolSurface,
|
|
37857
|
+
resolveToolSurface: resolvePiToolSurface,
|
|
37353
37858
|
handleConfigureWarningsForSession,
|
|
37354
37859
|
shouldPrepareOnnxRuntime,
|
|
37355
37860
|
createVersionMismatchHandler
|