@cortexkit/aft-pi 0.22.1 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bg-notifications.d.ts +0 -1
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/commands/aft-status.d.ts +5 -4
- package/dist/commands/aft-status.d.ts.map +1 -1
- package/dist/dialogs/status-dialog.d.ts +19 -0
- package/dist/dialogs/status-dialog.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +454 -58
- package/dist/shared/status.d.ts +10 -0
- package/dist/shared/status.d.ts.map +1 -1
- package/dist/tools/_shared.d.ts +1 -1
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/ast.d.ts +17 -17
- package/dist/tools/ast.d.ts.map +1 -1
- package/dist/tools/bash.d.ts +8 -8
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/conflicts.d.ts +4 -4
- package/dist/tools/conflicts.d.ts.map +1 -1
- package/dist/tools/fs.d.ts +9 -9
- package/dist/tools/fs.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/imports.d.ts +13 -13
- package/dist/tools/imports.d.ts.map +1 -1
- package/dist/tools/lsp.d.ts +9 -9
- package/dist/tools/lsp.d.ts.map +1 -1
- package/dist/tools/navigate.d.ts +10 -10
- package/dist/tools/navigate.d.ts.map +1 -1
- package/dist/tools/reading.d.ts +14 -14
- package/dist/tools/reading.d.ts.map +1 -1
- package/dist/tools/refactor.d.ts +14 -14
- package/dist/tools/refactor.d.ts.map +1 -1
- package/dist/tools/render-helpers.d.ts +2 -2
- package/dist/tools/render-helpers.d.ts.map +1 -1
- package/dist/tools/safety.d.ts +9 -9
- package/dist/tools/safety.d.ts.map +1 -1
- package/dist/tools/semantic.d.ts +7 -7
- package/dist/tools/semantic.d.ts.map +1 -1
- package/dist/tools/structure.d.ts +18 -18
- package/dist/tools/structure.d.ts.map +1 -1
- package/dist/workflow-hints.d.ts +1 -1
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -30623,6 +30623,8 @@ class BinaryBridge {
|
|
|
30623
30623
|
onConfigureWarnings;
|
|
30624
30624
|
onBashCompletion;
|
|
30625
30625
|
onBashLongRunning;
|
|
30626
|
+
cachedStatus = null;
|
|
30627
|
+
statusListeners = new Set;
|
|
30626
30628
|
configureWarningClients = new Map;
|
|
30627
30629
|
restartResetTimer = null;
|
|
30628
30630
|
errorPrefix;
|
|
@@ -30671,6 +30673,21 @@ class BinaryBridge {
|
|
|
30671
30673
|
hasPendingRequests() {
|
|
30672
30674
|
return this.pending.size > 0;
|
|
30673
30675
|
}
|
|
30676
|
+
getCachedStatus() {
|
|
30677
|
+
return this.cachedStatus;
|
|
30678
|
+
}
|
|
30679
|
+
subscribeStatus(listener) {
|
|
30680
|
+
this.statusListeners.add(listener);
|
|
30681
|
+
if (this.cachedStatus !== null) {
|
|
30682
|
+
this.deliverStatusSnapshot(listener, this.cachedStatus);
|
|
30683
|
+
}
|
|
30684
|
+
return () => {
|
|
30685
|
+
this.statusListeners.delete(listener);
|
|
30686
|
+
};
|
|
30687
|
+
}
|
|
30688
|
+
cacheStatusSnapshot(snapshot) {
|
|
30689
|
+
this.cachedStatus = snapshot;
|
|
30690
|
+
}
|
|
30674
30691
|
async send(command, params = {}, options) {
|
|
30675
30692
|
if (this._shuttingDown) {
|
|
30676
30693
|
throw new Error(`${this.errorPrefix} Bridge is shutting down, cannot send "${command}"`);
|
|
@@ -30807,6 +30824,23 @@ class BinaryBridge {
|
|
|
30807
30824
|
}
|
|
30808
30825
|
}
|
|
30809
30826
|
}
|
|
30827
|
+
handleStatusChangedFrame(frame) {
|
|
30828
|
+
const snapshot = frame.snapshot;
|
|
30829
|
+
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot))
|
|
30830
|
+
return;
|
|
30831
|
+
this.cachedStatus = snapshot;
|
|
30832
|
+
log("Received status_changed push frame; cached AFT status snapshot");
|
|
30833
|
+
for (const listener of this.statusListeners) {
|
|
30834
|
+
this.deliverStatusSnapshot(listener, this.cachedStatus);
|
|
30835
|
+
}
|
|
30836
|
+
}
|
|
30837
|
+
deliverStatusSnapshot(listener, snapshot) {
|
|
30838
|
+
try {
|
|
30839
|
+
listener(snapshot);
|
|
30840
|
+
} catch (err) {
|
|
30841
|
+
warn(`status listener threw: ${err instanceof Error ? err.message : String(err)}`);
|
|
30842
|
+
}
|
|
30843
|
+
}
|
|
30810
30844
|
async shutdown() {
|
|
30811
30845
|
this._shuttingDown = true;
|
|
30812
30846
|
this.clearRestartResetTimer();
|
|
@@ -31011,6 +31045,10 @@ class BinaryBridge {
|
|
|
31011
31045
|
});
|
|
31012
31046
|
continue;
|
|
31013
31047
|
}
|
|
31048
|
+
if (response.type === "status_changed") {
|
|
31049
|
+
this.handleStatusChangedFrame(response);
|
|
31050
|
+
continue;
|
|
31051
|
+
}
|
|
31014
31052
|
const id = response.id;
|
|
31015
31053
|
if (id && this.pending.has(id)) {
|
|
31016
31054
|
const entry = this.pending.get(id);
|
|
@@ -31972,9 +32010,9 @@ import { chmodSync as chmodSync3, copyFileSync as copyFileSync2, existsSync as e
|
|
|
31972
32010
|
import { createRequire as createRequire2 } from "node:module";
|
|
31973
32011
|
import { homedir as homedir4 } from "node:os";
|
|
31974
32012
|
import { join as join4 } from "node:path";
|
|
31975
|
-
function
|
|
32013
|
+
function readBinaryVersion(binaryPath) {
|
|
31976
32014
|
try {
|
|
31977
|
-
const result = spawnSync(
|
|
32015
|
+
const result = spawnSync(binaryPath, ["--version"], {
|
|
31978
32016
|
encoding: "utf-8",
|
|
31979
32017
|
stdio: ["pipe", "pipe", "pipe"],
|
|
31980
32018
|
timeout: 5000
|
|
@@ -31982,7 +32020,16 @@ function copyToVersionedCache(npmBinaryPath) {
|
|
|
31982
32020
|
const rawVersion = result.stdout?.trim();
|
|
31983
32021
|
if (!rawVersion)
|
|
31984
32022
|
return null;
|
|
31985
|
-
|
|
32023
|
+
return rawVersion.replace(/^aft\s+/, "");
|
|
32024
|
+
} catch {
|
|
32025
|
+
return null;
|
|
32026
|
+
}
|
|
32027
|
+
}
|
|
32028
|
+
function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
32029
|
+
try {
|
|
32030
|
+
const version = knownVersion ?? readBinaryVersion(npmBinaryPath);
|
|
32031
|
+
if (!version)
|
|
32032
|
+
return null;
|
|
31986
32033
|
const tag = version.startsWith("v") ? version : `v${version}`;
|
|
31987
32034
|
const cacheDir = getCacheDir();
|
|
31988
32035
|
const versionedDir = join4(cacheDir, tag);
|
|
@@ -32037,8 +32084,13 @@ function findBinarySync(expectedVersion) {
|
|
|
32037
32084
|
const req = createRequire2(import.meta.url);
|
|
32038
32085
|
const resolved = req.resolve(packageBin);
|
|
32039
32086
|
if (existsSync3(resolved)) {
|
|
32040
|
-
const
|
|
32041
|
-
|
|
32087
|
+
const npmVersion = readBinaryVersion(resolved);
|
|
32088
|
+
if (pluginVersion && npmVersion && npmVersion !== pluginVersion) {
|
|
32089
|
+
warn(`npm platform package binary v${npmVersion} does not match plugin v${pluginVersion}; skipping (continuing to PATH lookup)`);
|
|
32090
|
+
} else {
|
|
32091
|
+
const copied = copyToVersionedCache(resolved, npmVersion ?? undefined);
|
|
32092
|
+
return copied ?? resolved;
|
|
32093
|
+
}
|
|
32042
32094
|
}
|
|
32043
32095
|
} catch {}
|
|
32044
32096
|
try {
|
|
@@ -32736,8 +32788,6 @@ async function handleTurnEndBgCompletions(drainContext) {
|
|
|
32736
32788
|
}
|
|
32737
32789
|
async function triggerWakeIfPending(drainContext, skipDrain) {
|
|
32738
32790
|
const state = stateFor(drainContext.sessionID);
|
|
32739
|
-
if (drainContext.isActive?.())
|
|
32740
|
-
return;
|
|
32741
32791
|
if (!skipDrain && state.outstandingTaskIds.size > 0) {
|
|
32742
32792
|
await drainCompletions(drainContext);
|
|
32743
32793
|
}
|
|
@@ -32781,9 +32831,8 @@ ${formatLongRunningReminder(longRunning)}`;
|
|
|
32781
32831
|
async function drainCompletions({ ctx, directory, sessionID }) {
|
|
32782
32832
|
try {
|
|
32783
32833
|
const bridge = ctx.pool.getActiveBridgeForRoot(directory) ?? ctx.pool.getBridge(directory);
|
|
32784
|
-
|
|
32785
|
-
|
|
32786
|
-
const response = await bridge.send("bash_drain_completions", { session_id: sessionID });
|
|
32834
|
+
const params = sessionID ? { session_id: sessionID } : {};
|
|
32835
|
+
const response = await bridge.send("bash_drain_completions", params);
|
|
32787
32836
|
if (response.success === false) {
|
|
32788
32837
|
sessionWarn2(sessionID ?? "", `${LOG_PREFIX} drain failed: ${String(response.message ?? "unknown error")}`);
|
|
32789
32838
|
return;
|
|
@@ -32941,6 +32990,74 @@ function shorten(value, limit) {
|
|
|
32941
32990
|
return value.length <= limit ? value : `${value.slice(0, limit - 1)}…`;
|
|
32942
32991
|
}
|
|
32943
32992
|
|
|
32993
|
+
// src/dialogs/status-dialog.ts
|
|
32994
|
+
import {
|
|
32995
|
+
matchesKey,
|
|
32996
|
+
truncateToWidth,
|
|
32997
|
+
visibleWidth
|
|
32998
|
+
} from "@earendil-works/pi-tui";
|
|
32999
|
+
// package.json
|
|
33000
|
+
var package_default = {
|
|
33001
|
+
name: "@cortexkit/aft-pi",
|
|
33002
|
+
version: "0.24.0",
|
|
33003
|
+
type: "module",
|
|
33004
|
+
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
33005
|
+
main: "dist/index.js",
|
|
33006
|
+
types: "dist/index.d.ts",
|
|
33007
|
+
license: "MIT",
|
|
33008
|
+
repository: {
|
|
33009
|
+
type: "git",
|
|
33010
|
+
url: "https://github.com/cortexkit/aft"
|
|
33011
|
+
},
|
|
33012
|
+
files: [
|
|
33013
|
+
"dist",
|
|
33014
|
+
"README.md"
|
|
33015
|
+
],
|
|
33016
|
+
scripts: {
|
|
33017
|
+
build: "bun build src/index.ts --outdir dist --target node --format esm --external @earendil-works/pi-coding-agent --external @earendil-works/pi-ai --external @earendil-works/pi-tui --external typebox --external diff && tsc --emitDeclarationOnly",
|
|
33018
|
+
typecheck: "tsc --noEmit",
|
|
33019
|
+
test: "bun test src/__tests__/",
|
|
33020
|
+
lint: "biome check src",
|
|
33021
|
+
prepublishOnly: "bun run build"
|
|
33022
|
+
},
|
|
33023
|
+
dependencies: {
|
|
33024
|
+
"@cortexkit/aft-bridge": "0.24.0",
|
|
33025
|
+
typebox: "^1.1.24",
|
|
33026
|
+
"comment-json": "^5.0.0",
|
|
33027
|
+
diff: "^8.0.4",
|
|
33028
|
+
zod: "^4.1.8"
|
|
33029
|
+
},
|
|
33030
|
+
optionalDependencies: {
|
|
33031
|
+
"@cortexkit/aft-darwin-arm64": "0.24.0",
|
|
33032
|
+
"@cortexkit/aft-darwin-x64": "0.24.0",
|
|
33033
|
+
"@cortexkit/aft-linux-arm64": "0.24.0",
|
|
33034
|
+
"@cortexkit/aft-linux-x64": "0.24.0",
|
|
33035
|
+
"@cortexkit/aft-win32-x64": "0.24.0"
|
|
33036
|
+
},
|
|
33037
|
+
devDependencies: {
|
|
33038
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
33039
|
+
"@earendil-works/pi-ai": "*",
|
|
33040
|
+
"@earendil-works/pi-tui": "*",
|
|
33041
|
+
"@types/node": "^22.0.0",
|
|
33042
|
+
typescript: "^5.8.0"
|
|
33043
|
+
},
|
|
33044
|
+
peerDependencies: {
|
|
33045
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
33046
|
+
"@earendil-works/pi-tui": "*"
|
|
33047
|
+
},
|
|
33048
|
+
exports: {
|
|
33049
|
+
".": {
|
|
33050
|
+
types: "./dist/index.d.ts",
|
|
33051
|
+
import: "./dist/index.js"
|
|
33052
|
+
}
|
|
33053
|
+
},
|
|
33054
|
+
pi: {
|
|
33055
|
+
extensions: [
|
|
33056
|
+
"./dist/index.js"
|
|
33057
|
+
]
|
|
33058
|
+
}
|
|
33059
|
+
};
|
|
33060
|
+
|
|
32944
33061
|
// src/shared/status.ts
|
|
32945
33062
|
function asRecord(value) {
|
|
32946
33063
|
return typeof value === "object" && value !== null ? value : {};
|
|
@@ -32989,9 +33106,12 @@ function coerceAftStatus(response) {
|
|
|
32989
33106
|
};
|
|
32990
33107
|
const disk = asRecord(response.disk);
|
|
32991
33108
|
const symbolCache = asRecord(response.symbol_cache);
|
|
33109
|
+
const session = asRecord(response.session);
|
|
32992
33110
|
return {
|
|
32993
33111
|
version: readString(response.version, "unknown"),
|
|
32994
33112
|
project_root: readNullableString(response.project_root),
|
|
33113
|
+
canonical_root: readNullableString(response.canonical_root),
|
|
33114
|
+
cache_role: readString(response.cache_role, "not_initialized"),
|
|
32995
33115
|
features: {
|
|
32996
33116
|
format_on_edit: readBoolean(features.format_on_edit),
|
|
32997
33117
|
validate_on_edit: readString(features.validate_on_edit, "off"),
|
|
@@ -33026,13 +33146,21 @@ function coerceAftStatus(response) {
|
|
|
33026
33146
|
local_entries: readNumber(symbolCache.local_entries),
|
|
33027
33147
|
warm_entries: readNumber(symbolCache.warm_entries)
|
|
33028
33148
|
},
|
|
33029
|
-
storage_dir: readNullableString(response.storage_dir)
|
|
33149
|
+
storage_dir: readNullableString(response.storage_dir),
|
|
33150
|
+
checkpoints_total: readNumber(response.checkpoints_total),
|
|
33151
|
+
session: {
|
|
33152
|
+
id: readString(session.id, "__default__"),
|
|
33153
|
+
tracked_files: readNumber(session.tracked_files),
|
|
33154
|
+
checkpoints: readNumber(session.checkpoints)
|
|
33155
|
+
}
|
|
33030
33156
|
};
|
|
33031
33157
|
}
|
|
33032
33158
|
function formatStatusDialogMessage(status) {
|
|
33033
33159
|
const lines = [
|
|
33034
33160
|
`AFT version: ${status.version}`,
|
|
33035
33161
|
`Project root: ${status.project_root ?? "(not configured)"}`,
|
|
33162
|
+
`Canonical root: ${status.canonical_root ?? "(not configured)"}`,
|
|
33163
|
+
`Cache role: ${status.cache_role}`,
|
|
33036
33164
|
"",
|
|
33037
33165
|
"Enabled features",
|
|
33038
33166
|
`- format_on_edit: ${formatFlag(status.features.format_on_edit)}`,
|
|
@@ -33125,26 +33253,261 @@ function textResult(text, details) {
|
|
|
33125
33253
|
};
|
|
33126
33254
|
}
|
|
33127
33255
|
|
|
33256
|
+
// src/dialogs/status-dialog.ts
|
|
33257
|
+
var REFRESH_INTERVAL_MS = 1500;
|
|
33258
|
+
var OVERLAY_WIDTH = 84;
|
|
33259
|
+
async function showAftStatusDialog(pi, extCtx, pluginCtx) {
|
|
33260
|
+
await extCtx.ui.custom((tui, theme, _keybindings, done) => new AftStatusDialogComponent({
|
|
33261
|
+
pi,
|
|
33262
|
+
extCtx,
|
|
33263
|
+
pluginCtx,
|
|
33264
|
+
theme,
|
|
33265
|
+
tui,
|
|
33266
|
+
done
|
|
33267
|
+
}), {
|
|
33268
|
+
overlay: true,
|
|
33269
|
+
overlayOptions: { anchor: "center", width: OVERLAY_WIDTH }
|
|
33270
|
+
});
|
|
33271
|
+
}
|
|
33272
|
+
|
|
33273
|
+
class AftStatusDialogComponent {
|
|
33274
|
+
props;
|
|
33275
|
+
snapshot = null;
|
|
33276
|
+
errorMessage = null;
|
|
33277
|
+
refreshTimer = null;
|
|
33278
|
+
closed = false;
|
|
33279
|
+
constructor(props) {
|
|
33280
|
+
this.props = props;
|
|
33281
|
+
this.fetchOnce();
|
|
33282
|
+
this.refreshTimer = setInterval(() => {
|
|
33283
|
+
if (this.closed)
|
|
33284
|
+
return;
|
|
33285
|
+
this.fetchOnce();
|
|
33286
|
+
}, REFRESH_INTERVAL_MS);
|
|
33287
|
+
}
|
|
33288
|
+
async fetchOnce() {
|
|
33289
|
+
try {
|
|
33290
|
+
const bridge = bridgeFor(this.props.pluginCtx, this.props.extCtx.cwd);
|
|
33291
|
+
const cached = bridge.getCachedStatus();
|
|
33292
|
+
const response = cached ? { success: true, ...cached } : await callBridge(bridge, "status", {}, this.props.extCtx);
|
|
33293
|
+
if (!cached) {
|
|
33294
|
+
bridge.cacheStatusSnapshot(response);
|
|
33295
|
+
}
|
|
33296
|
+
if (this.closed)
|
|
33297
|
+
return;
|
|
33298
|
+
this.snapshot = coerceAftStatus(response);
|
|
33299
|
+
this.errorMessage = null;
|
|
33300
|
+
this.props.tui.requestRender();
|
|
33301
|
+
} catch (err) {
|
|
33302
|
+
if (this.closed)
|
|
33303
|
+
return;
|
|
33304
|
+
this.errorMessage = err instanceof Error ? err.message : String(err);
|
|
33305
|
+
this.props.tui.requestRender();
|
|
33306
|
+
}
|
|
33307
|
+
}
|
|
33308
|
+
handleInput(data) {
|
|
33309
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c") || matchesKey(data, "return")) {
|
|
33310
|
+
this.close();
|
|
33311
|
+
}
|
|
33312
|
+
}
|
|
33313
|
+
close() {
|
|
33314
|
+
if (this.closed)
|
|
33315
|
+
return;
|
|
33316
|
+
this.closed = true;
|
|
33317
|
+
if (this.refreshTimer) {
|
|
33318
|
+
clearInterval(this.refreshTimer);
|
|
33319
|
+
this.refreshTimer = null;
|
|
33320
|
+
}
|
|
33321
|
+
this.props.done(undefined);
|
|
33322
|
+
}
|
|
33323
|
+
invalidate() {}
|
|
33324
|
+
render(width) {
|
|
33325
|
+
const innerWidth = Math.max(40, width - 4);
|
|
33326
|
+
const inner = renderInner(this.snapshot, this.errorMessage, this.props.theme, innerWidth);
|
|
33327
|
+
return drawBorder(inner, width, this.props.theme);
|
|
33328
|
+
}
|
|
33329
|
+
dispose() {
|
|
33330
|
+
if (this.refreshTimer) {
|
|
33331
|
+
clearInterval(this.refreshTimer);
|
|
33332
|
+
this.refreshTimer = null;
|
|
33333
|
+
}
|
|
33334
|
+
}
|
|
33335
|
+
}
|
|
33336
|
+
function renderInner(s, error3, theme, innerWidth) {
|
|
33337
|
+
const lines = [];
|
|
33338
|
+
lines.push(`${theme.fg("accent", theme.bold("⚡ AFT Status"))} ${theme.fg("muted", `v${s?.version ?? package_default.version}`)}`);
|
|
33339
|
+
lines.push("");
|
|
33340
|
+
if (error3 && !s) {
|
|
33341
|
+
lines.push(theme.fg("warning", error3));
|
|
33342
|
+
lines.push("");
|
|
33343
|
+
lines.push(theme.fg("muted", "Press Escape to close"));
|
|
33344
|
+
return lines;
|
|
33345
|
+
}
|
|
33346
|
+
if (!s) {
|
|
33347
|
+
lines.push(theme.fg("muted", "Connecting to AFT…"));
|
|
33348
|
+
return lines;
|
|
33349
|
+
}
|
|
33350
|
+
lines.push(rowFull("Project root", s.project_root ?? "(not configured)", theme, innerWidth));
|
|
33351
|
+
lines.push(rowFull("Canonical root", s.canonical_root ?? "(not configured)", theme, innerWidth));
|
|
33352
|
+
const cacheTone = s.cache_role === "main" ? "accent" : s.cache_role === "worktree" ? "warning" : "muted";
|
|
33353
|
+
lines.push(rowFull("Cache role", theme.fg(cacheTone, s.cache_role), theme, innerWidth));
|
|
33354
|
+
lines.push("");
|
|
33355
|
+
const colWidth = Math.floor((innerWidth - 2) / 2);
|
|
33356
|
+
const left = [];
|
|
33357
|
+
const right = [];
|
|
33358
|
+
left.push(theme.fg("muted", "Search index"));
|
|
33359
|
+
left.push(kv("status", colorStatus(s.search_index.status, theme), theme));
|
|
33360
|
+
left.push(kv("files", formatCountShort(s.search_index.files), theme));
|
|
33361
|
+
left.push(kv("trigrams", formatCountShort(s.search_index.trigrams), theme));
|
|
33362
|
+
left.push(kv("disk", formatBytes(s.disk.trigram_disk_bytes), theme));
|
|
33363
|
+
right.push(theme.fg("muted", "Semantic index"));
|
|
33364
|
+
right.push(kv("status", colorStatus(s.semantic_index.status, theme), theme));
|
|
33365
|
+
right.push(kv("entries", formatCountShort(s.semantic_index.entries), theme));
|
|
33366
|
+
if (s.semantic_index.backend)
|
|
33367
|
+
right.push(kv("backend", s.semantic_index.backend, theme));
|
|
33368
|
+
if (s.semantic_index.model)
|
|
33369
|
+
right.push(kv("model", s.semantic_index.model, theme));
|
|
33370
|
+
if (s.semantic_index.dimension != null) {
|
|
33371
|
+
right.push(kv("dimension", String(s.semantic_index.dimension), theme));
|
|
33372
|
+
}
|
|
33373
|
+
right.push(kv("disk", formatBytes(s.disk.semantic_disk_bytes), theme));
|
|
33374
|
+
for (const line of renderColumns(left, right, colWidth))
|
|
33375
|
+
lines.push(line);
|
|
33376
|
+
lines.push("");
|
|
33377
|
+
const left2 = [];
|
|
33378
|
+
const right2 = [];
|
|
33379
|
+
left2.push(theme.fg("muted", "Runtime"));
|
|
33380
|
+
left2.push(kv("LSP servers", String(s.lsp_servers), theme));
|
|
33381
|
+
left2.push(kv("symbol cache", `${formatCountShort(s.symbol_cache.local_entries)} local · ${formatCountShort(s.symbol_cache.warm_entries)} warm`, theme));
|
|
33382
|
+
right2.push(theme.fg("muted", "Current session"));
|
|
33383
|
+
right2.push(kv("tracked files", String(s.session.tracked_files), theme));
|
|
33384
|
+
right2.push(kv("checkpoints", String(s.session.checkpoints), theme));
|
|
33385
|
+
right2.push(kv("all-session", String(s.checkpoints_total), theme));
|
|
33386
|
+
for (const line of renderColumns(left2, right2, colWidth))
|
|
33387
|
+
lines.push(line);
|
|
33388
|
+
lines.push("");
|
|
33389
|
+
lines.push(theme.fg("muted", "Features"));
|
|
33390
|
+
lines.push(` ${featureBadge("format_on_edit", s.features.format_on_edit, theme)} ${featureBadge("search_index", s.features.search_index, theme)} ${featureBadge("semantic_search", s.features.semantic_search, theme)}`);
|
|
33391
|
+
if (s.semantic_index.stage) {
|
|
33392
|
+
lines.push("");
|
|
33393
|
+
lines.push(theme.fg("muted", "Semantic build progress"));
|
|
33394
|
+
lines.push(kv("stage", s.semantic_index.stage, theme));
|
|
33395
|
+
if (s.semantic_index.files != null) {
|
|
33396
|
+
lines.push(kv("files seen", formatCountShort(s.semantic_index.files), theme));
|
|
33397
|
+
}
|
|
33398
|
+
if (s.semantic_index.entries_done != null || s.semantic_index.entries_total != null) {
|
|
33399
|
+
lines.push(kv("progress", `${formatCountShort(s.semantic_index.entries_done ?? null)} / ${formatCountShort(s.semantic_index.entries_total ?? null)}`, theme));
|
|
33400
|
+
}
|
|
33401
|
+
}
|
|
33402
|
+
if (s.semantic_index.error) {
|
|
33403
|
+
lines.push("");
|
|
33404
|
+
lines.push(theme.fg("error", `⚠ ${s.semantic_index.error}`));
|
|
33405
|
+
}
|
|
33406
|
+
if (error3) {
|
|
33407
|
+
lines.push("");
|
|
33408
|
+
lines.push(theme.fg("warning", `⚠ ${error3}`));
|
|
33409
|
+
}
|
|
33410
|
+
lines.push("");
|
|
33411
|
+
lines.push(theme.fg("muted", "Press Escape to close"));
|
|
33412
|
+
return lines;
|
|
33413
|
+
}
|
|
33414
|
+
function colorStatus(status, theme) {
|
|
33415
|
+
switch (status) {
|
|
33416
|
+
case "ready":
|
|
33417
|
+
try {
|
|
33418
|
+
return theme.fg("success", status);
|
|
33419
|
+
} catch {
|
|
33420
|
+
return theme.fg("accent", status);
|
|
33421
|
+
}
|
|
33422
|
+
case "loading":
|
|
33423
|
+
case "building":
|
|
33424
|
+
return theme.fg("warning", status);
|
|
33425
|
+
case "failed":
|
|
33426
|
+
case "error":
|
|
33427
|
+
return theme.fg("error", status);
|
|
33428
|
+
case "disabled":
|
|
33429
|
+
return theme.fg("muted", status);
|
|
33430
|
+
default:
|
|
33431
|
+
return status;
|
|
33432
|
+
}
|
|
33433
|
+
}
|
|
33434
|
+
function featureBadge(name, enabled, theme) {
|
|
33435
|
+
const indicator = enabled ? theme.fg("accent", "●") : theme.fg("muted", "○");
|
|
33436
|
+
const label = enabled ? name : theme.fg("muted", name);
|
|
33437
|
+
return `${indicator} ${label}`;
|
|
33438
|
+
}
|
|
33439
|
+
function kv(label, value, theme) {
|
|
33440
|
+
return ` ${theme.fg("muted", `${label}:`)} ${value}`;
|
|
33441
|
+
}
|
|
33442
|
+
function rowFull(label, value, theme, innerWidth) {
|
|
33443
|
+
const labelText = `${label}: `;
|
|
33444
|
+
const remaining = Math.max(10, innerWidth - visibleWidth(labelText));
|
|
33445
|
+
const truncated = truncateToWidth(value, remaining, "…");
|
|
33446
|
+
return `${theme.fg("muted", labelText)}${truncated}`;
|
|
33447
|
+
}
|
|
33448
|
+
function renderColumns(left, right, colWidth) {
|
|
33449
|
+
const rows = Math.max(left.length, right.length);
|
|
33450
|
+
const out = [];
|
|
33451
|
+
for (let i = 0;i < rows; i++) {
|
|
33452
|
+
const l = left[i] ?? "";
|
|
33453
|
+
const r = right[i] ?? "";
|
|
33454
|
+
const visible = visibleWidth(l);
|
|
33455
|
+
const pad = " ".repeat(Math.max(0, colWidth - visible));
|
|
33456
|
+
out.push(`${l}${pad} ${r}`);
|
|
33457
|
+
}
|
|
33458
|
+
return out;
|
|
33459
|
+
}
|
|
33460
|
+
function drawBorder(inner, width, theme) {
|
|
33461
|
+
const innerWidth = Math.max(40, width - 4);
|
|
33462
|
+
const border = (s) => theme.fg("borderMuted", s);
|
|
33463
|
+
const top = border(`╭${"─".repeat(innerWidth + 2)}╮`);
|
|
33464
|
+
const bottom = border(`╰${"─".repeat(innerWidth + 2)}╯`);
|
|
33465
|
+
const side = border("│");
|
|
33466
|
+
const out = [];
|
|
33467
|
+
out.push(top);
|
|
33468
|
+
for (const raw of inner) {
|
|
33469
|
+
const line = truncateToWidth(raw, innerWidth, "…");
|
|
33470
|
+
const visible = visibleWidth(line);
|
|
33471
|
+
const pad = " ".repeat(Math.max(0, innerWidth - visible));
|
|
33472
|
+
out.push(`${side} ${line}${pad} ${side}`);
|
|
33473
|
+
}
|
|
33474
|
+
out.push(bottom);
|
|
33475
|
+
return out;
|
|
33476
|
+
}
|
|
33477
|
+
function formatCountShort(value) {
|
|
33478
|
+
if (value == null || !Number.isFinite(value))
|
|
33479
|
+
return "—";
|
|
33480
|
+
if (value >= 1e6)
|
|
33481
|
+
return `${(value / 1e6).toFixed(1)}M`;
|
|
33482
|
+
if (value >= 1000)
|
|
33483
|
+
return `${Math.round(value / 1000)}K`;
|
|
33484
|
+
return String(value);
|
|
33485
|
+
}
|
|
33486
|
+
|
|
33128
33487
|
// src/commands/aft-status.ts
|
|
33129
33488
|
function registerStatusCommand(pi, ctx) {
|
|
33130
33489
|
pi.registerCommand("aft-status", {
|
|
33131
33490
|
description: "Show AFT plugin status (search/semantic indexes, LSP, storage)",
|
|
33132
33491
|
handler: async (_args, extCtx) => {
|
|
33133
33492
|
try {
|
|
33493
|
+
if (extCtx.hasUI) {
|
|
33494
|
+
await showAftStatusDialog(pi, extCtx, ctx);
|
|
33495
|
+
return;
|
|
33496
|
+
}
|
|
33134
33497
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33135
|
-
const
|
|
33498
|
+
const cached = bridge.getCachedStatus();
|
|
33499
|
+
const response = cached ? { success: true, ...cached } : await callBridge(bridge, "status", {}, extCtx);
|
|
33500
|
+
if (!cached) {
|
|
33501
|
+
bridge.cacheStatusSnapshot(response);
|
|
33502
|
+
}
|
|
33136
33503
|
const snapshot = coerceAftStatus(response);
|
|
33137
33504
|
const text = formatStatusDialogMessage(snapshot);
|
|
33138
|
-
|
|
33139
|
-
await extCtx.ui.input("AFT Status", text);
|
|
33140
|
-
} else {
|
|
33141
|
-
extCtx.ui.notify(text, "info");
|
|
33142
|
-
}
|
|
33505
|
+
extCtx.ui.notify(text, "info");
|
|
33143
33506
|
} catch (err) {
|
|
33144
33507
|
const message = `AFT status failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
33145
|
-
|
|
33508
|
+
try {
|
|
33146
33509
|
extCtx.ui.notify(message, "error");
|
|
33147
|
-
}
|
|
33510
|
+
} catch {
|
|
33148
33511
|
console.error(`[aft-plugin] ${message}`);
|
|
33149
33512
|
}
|
|
33150
33513
|
}
|
|
@@ -48890,13 +49253,13 @@ function registerShutdownCleanup(fn) {
|
|
|
48890
49253
|
}
|
|
48891
49254
|
|
|
48892
49255
|
// src/tools/ast.ts
|
|
48893
|
-
import { StringEnum } from "@
|
|
48894
|
-
import { Type } from "
|
|
49256
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
49257
|
+
import { Type } from "typebox";
|
|
48895
49258
|
|
|
48896
49259
|
// src/tools/render-helpers.ts
|
|
48897
49260
|
import { homedir as homedir7 } from "node:os";
|
|
48898
|
-
import { renderDiff } from "@
|
|
48899
|
-
import { Container, Spacer, Text } from "@
|
|
49261
|
+
import { renderDiff } from "@earendil-works/pi-coding-agent";
|
|
49262
|
+
import { Container, Spacer, Text } from "@earendil-works/pi-tui";
|
|
48900
49263
|
function reuseText(last) {
|
|
48901
49264
|
return last instanceof Text ? last : new Text("", 0, 0);
|
|
48902
49265
|
}
|
|
@@ -49290,8 +49653,8 @@ function registerAstTools(pi, ctx, surface) {
|
|
|
49290
49653
|
}
|
|
49291
49654
|
|
|
49292
49655
|
// src/tools/bash.ts
|
|
49293
|
-
import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@
|
|
49294
|
-
import { Type as Type2 } from "
|
|
49656
|
+
import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
|
|
49657
|
+
import { Type as Type2 } from "typebox";
|
|
49295
49658
|
var FOREGROUND_WAIT_WINDOW_MS = 5000;
|
|
49296
49659
|
var FOREGROUND_POLL_INTERVAL_MS = 100;
|
|
49297
49660
|
var BASH_TRANSPORT_TIMEOUT_MS = 30000;
|
|
@@ -49625,7 +49988,7 @@ function shortenCommand(command) {
|
|
|
49625
49988
|
}
|
|
49626
49989
|
|
|
49627
49990
|
// src/tools/conflicts.ts
|
|
49628
|
-
import { Type as Type3 } from "
|
|
49991
|
+
import { Type as Type3 } from "typebox";
|
|
49629
49992
|
var ConflictsParams = Type3.Object({});
|
|
49630
49993
|
function renderConflictCall(theme, context) {
|
|
49631
49994
|
return renderToolCall("conflicts", undefined, theme, context);
|
|
@@ -49674,7 +50037,7 @@ function registerConflictsTool(pi, ctx) {
|
|
|
49674
50037
|
}
|
|
49675
50038
|
|
|
49676
50039
|
// src/tools/fs.ts
|
|
49677
|
-
import { Type as Type4 } from "
|
|
50040
|
+
import { Type as Type4 } from "typebox";
|
|
49678
50041
|
var DeleteParams = Type4.Object({
|
|
49679
50042
|
files: Type4.Array(Type4.String(), {
|
|
49680
50043
|
description: "Paths to delete (one or more). Single-file callers pass a single-element array.",
|
|
@@ -49796,12 +50159,12 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
49796
50159
|
// src/tools/hoisted.ts
|
|
49797
50160
|
import { stat } from "node:fs/promises";
|
|
49798
50161
|
import { homedir as homedir8 } from "node:os";
|
|
49799
|
-
import { resolve as resolve3 } from "node:path";
|
|
50162
|
+
import { isAbsolute, relative as relative3, resolve as resolve3 } from "node:path";
|
|
49800
50163
|
import {
|
|
49801
50164
|
renderDiff as renderDiff2
|
|
49802
|
-
} from "@
|
|
49803
|
-
import { Container as Container3, Spacer as Spacer3, Text as Text3 } from "@
|
|
49804
|
-
import { Type as Type5 } from "
|
|
50165
|
+
} from "@earendil-works/pi-coding-agent";
|
|
50166
|
+
import { Container as Container3, Spacer as Spacer3, Text as Text3 } from "@earendil-works/pi-tui";
|
|
50167
|
+
import { Type as Type5 } from "typebox";
|
|
49805
50168
|
|
|
49806
50169
|
// src/tools/diff-format.ts
|
|
49807
50170
|
import { diffLines } from "diff";
|
|
@@ -49906,6 +50269,21 @@ function formatDiffForPi(oldContent, newContent, contextLines = DEFAULT_CONTEXT_
|
|
|
49906
50269
|
}
|
|
49907
50270
|
|
|
49908
50271
|
// src/tools/hoisted.ts
|
|
50272
|
+
function containsPath(parent, child) {
|
|
50273
|
+
const rel = relative3(parent, child);
|
|
50274
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
50275
|
+
}
|
|
50276
|
+
async function assertExternalDirectoryPermission(extCtx, target, action = "modify") {
|
|
50277
|
+
if (!target)
|
|
50278
|
+
return;
|
|
50279
|
+
const absoluteTarget = isAbsolute(target) ? target : resolve3(extCtx.cwd, target);
|
|
50280
|
+
if (containsPath(extCtx.cwd, absoluteTarget))
|
|
50281
|
+
return;
|
|
50282
|
+
const confirmed = await extCtx.ui?.confirm?.("Allow external directory access?", `AFT wants to ${action} outside the project: ${absoluteTarget}`);
|
|
50283
|
+
if (confirmed === true)
|
|
50284
|
+
return;
|
|
50285
|
+
throw new Error("Permission denied: external directory access was cancelled.");
|
|
50286
|
+
}
|
|
49909
50287
|
var ReadParams = Type5.Object({
|
|
49910
50288
|
path: Type5.String({ description: "Path to the file to read (relative or absolute)" }),
|
|
49911
50289
|
offset: Type5.Optional(Type5.Number({ description: "Line number to start reading from (1-indexed)" })),
|
|
@@ -49977,6 +50355,7 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
49977
50355
|
promptGuidelines: ["Use write only for new files or complete rewrites."],
|
|
49978
50356
|
parameters: WriteParams,
|
|
49979
50357
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
50358
|
+
await assertExternalDirectoryPermission(extCtx, params.filePath);
|
|
49980
50359
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
49981
50360
|
const response = await callBridge(bridge, "write", {
|
|
49982
50361
|
file: params.filePath,
|
|
@@ -50007,6 +50386,7 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
50007
50386
|
],
|
|
50008
50387
|
parameters: EditParams,
|
|
50009
50388
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
50389
|
+
await assertExternalDirectoryPermission(extCtx, params.filePath);
|
|
50010
50390
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
50011
50391
|
if (typeof params.appendContent === "string") {
|
|
50012
50392
|
const req2 = {
|
|
@@ -50052,8 +50432,10 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
50052
50432
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
50053
50433
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
50054
50434
|
const req = { pattern: params.pattern };
|
|
50055
|
-
if (params.path)
|
|
50435
|
+
if (params.path) {
|
|
50436
|
+
await assertExternalDirectoryPermission(extCtx, params.path, "search");
|
|
50056
50437
|
req.path = await resolvePathArg(extCtx.cwd, params.path);
|
|
50438
|
+
}
|
|
50057
50439
|
if (params.include)
|
|
50058
50440
|
req.include = splitIncludeGlobs(params.include);
|
|
50059
50441
|
if (params.caseSensitive !== undefined)
|
|
@@ -50265,8 +50647,8 @@ function formatReadFooter(agentSpecifiedRange, data) {
|
|
|
50265
50647
|
}
|
|
50266
50648
|
|
|
50267
50649
|
// src/tools/imports.ts
|
|
50268
|
-
import { StringEnum as StringEnum2 } from "@
|
|
50269
|
-
import { Type as Type6 } from "
|
|
50650
|
+
import { StringEnum as StringEnum2 } from "@earendil-works/pi-ai";
|
|
50651
|
+
import { Type as Type6 } from "typebox";
|
|
50270
50652
|
var ImportParams = Type6.Object({
|
|
50271
50653
|
op: StringEnum2(["add", "remove", "organize"], { description: "Import operation" }),
|
|
50272
50654
|
filePath: Type6.String({ description: "Path to the file" }),
|
|
@@ -50363,8 +50745,8 @@ function registerImportTools(pi, ctx) {
|
|
|
50363
50745
|
}
|
|
50364
50746
|
|
|
50365
50747
|
// src/tools/lsp.ts
|
|
50366
|
-
import { StringEnum as StringEnum3 } from "@
|
|
50367
|
-
import { Type as Type7 } from "
|
|
50748
|
+
import { StringEnum as StringEnum3 } from "@earendil-works/pi-ai";
|
|
50749
|
+
import { Type as Type7 } from "typebox";
|
|
50368
50750
|
var LspDiagnosticsParams = Type7.Object({
|
|
50369
50751
|
filePath: Type7.Optional(Type7.String({ description: "File to get diagnostics for (mutually exclusive with directory)" })),
|
|
50370
50752
|
directory: Type7.Optional(Type7.String({
|
|
@@ -50457,8 +50839,8 @@ function registerLspTools(pi, ctx) {
|
|
|
50457
50839
|
}
|
|
50458
50840
|
|
|
50459
50841
|
// src/tools/navigate.ts
|
|
50460
|
-
import { StringEnum as StringEnum4 } from "@
|
|
50461
|
-
import { Type as Type8 } from "
|
|
50842
|
+
import { StringEnum as StringEnum4 } from "@earendil-works/pi-ai";
|
|
50843
|
+
import { Type as Type8 } from "typebox";
|
|
50462
50844
|
function navigateParamsSchema() {
|
|
50463
50845
|
return Type8.Object({
|
|
50464
50846
|
op: StringEnum4(["call_tree", "callers", "trace_to", "impact", "trace_data"], {
|
|
@@ -50620,7 +51002,7 @@ function registerNavigateTool(pi, ctx) {
|
|
|
50620
51002
|
// src/tools/reading.ts
|
|
50621
51003
|
import { stat as stat2 } from "node:fs/promises";
|
|
50622
51004
|
import { resolve as resolve4 } from "node:path";
|
|
50623
|
-
import { Type as Type9 } from "
|
|
51005
|
+
import { Type as Type9 } from "typebox";
|
|
50624
51006
|
var OutlineParams = Type9.Object({
|
|
50625
51007
|
target: Type9.Union([Type9.String(), Type9.Array(Type9.String())], {
|
|
50626
51008
|
description: "What to outline: a file path, directory path, URL (http:// or https://), or array of file paths. The mode is auto-detected: URLs by `http://`/`https://` prefix, directories by stat, arrays as multi-file. Directory walks cap at 200 files."
|
|
@@ -50880,8 +51262,8 @@ ${lines}`;
|
|
|
50880
51262
|
}
|
|
50881
51263
|
|
|
50882
51264
|
// src/tools/refactor.ts
|
|
50883
|
-
import { StringEnum as StringEnum5 } from "@
|
|
50884
|
-
import { Type as Type10 } from "
|
|
51265
|
+
import { StringEnum as StringEnum5 } from "@earendil-works/pi-ai";
|
|
51266
|
+
import { Type as Type10 } from "typebox";
|
|
50885
51267
|
var RefactorParams = Type10.Object({
|
|
50886
51268
|
op: StringEnum5(["move", "extract", "inline"], { description: "Refactoring operation" }),
|
|
50887
51269
|
filePath: Type10.String({ description: "Source file" }),
|
|
@@ -50977,8 +51359,8 @@ function registerRefactorTool(pi, ctx) {
|
|
|
50977
51359
|
}
|
|
50978
51360
|
|
|
50979
51361
|
// src/tools/safety.ts
|
|
50980
|
-
import { StringEnum as StringEnum6 } from "@
|
|
50981
|
-
import { Type as Type11 } from "
|
|
51362
|
+
import { StringEnum as StringEnum6 } from "@earendil-works/pi-ai";
|
|
51363
|
+
import { Type as Type11 } from "typebox";
|
|
50982
51364
|
var SafetyParams = Type11.Object({
|
|
50983
51365
|
op: StringEnum6(["undo", "history", "checkpoint", "restore", "list"], {
|
|
50984
51366
|
description: "Safety operation"
|
|
@@ -51110,7 +51492,7 @@ function registerSafetyTool(pi, ctx) {
|
|
|
51110
51492
|
}
|
|
51111
51493
|
|
|
51112
51494
|
// src/tools/semantic.ts
|
|
51113
|
-
import { Type as Type12 } from "
|
|
51495
|
+
import { Type as Type12 } from "typebox";
|
|
51114
51496
|
var SearchParams2 = Type12.Object({
|
|
51115
51497
|
query: Type12.String({
|
|
51116
51498
|
description: "Concept or capability to find, phrased as a programmer would describe the code. Examples: 'fuzzy match with whitespace tolerance', 'undo backup before edit', 'retry failed network request'."
|
|
@@ -51139,6 +51521,16 @@ function buildSemanticSections(args, payload, theme) {
|
|
|
51139
51521
|
const lines = [theme.fg("accent", shortenPath(file2))];
|
|
51140
51522
|
fileResults.forEach((result) => {
|
|
51141
51523
|
const score = asNumber(result.score);
|
|
51524
|
+
const source = asString(result.source);
|
|
51525
|
+
if (source === "lexical") {
|
|
51526
|
+
lines.push(` ↳ ${theme.fg("muted", `[lexical match${score !== undefined ? ` — score ${score.toFixed(3)}` : ""}]`)}`);
|
|
51527
|
+
const snippet2 = asString(result.snippet);
|
|
51528
|
+
if (snippet2) {
|
|
51529
|
+
lines.push(...snippet2.split(`
|
|
51530
|
+
`).map((line) => ` ${line}`));
|
|
51531
|
+
}
|
|
51532
|
+
return;
|
|
51533
|
+
}
|
|
51142
51534
|
const startLine = asNumber(result.start_line);
|
|
51143
51535
|
const endLine = asNumber(result.end_line);
|
|
51144
51536
|
const range = startLine !== undefined ? `${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : "?";
|
|
@@ -51169,21 +51561,21 @@ function registerSemanticTool(pi, ctx) {
|
|
|
51169
51561
|
name: "aft_search",
|
|
51170
51562
|
label: "semantic search",
|
|
51171
51563
|
description: [
|
|
51172
|
-
"Find symbols by concept
|
|
51564
|
+
"Find symbols by concept using hybrid semantic + lexical search. Returns ranked code matches with similarity scores and provenance tags.",
|
|
51173
51565
|
"",
|
|
51174
51566
|
"When to reach for it:",
|
|
51175
51567
|
"- Exploring an unfamiliar area: 'where is rate limiting handled', 'how does auth flow work'",
|
|
51176
51568
|
"- Concept doesn't appear as a literal string: 'retry logic', 'cache invalidation', 'graceful shutdown'",
|
|
51569
|
+
"- Filename-shaped concepts: 'the bridge spawn helper', 'the session detection module'",
|
|
51177
51570
|
"- After 2+ grep attempts that came back empty or noisy",
|
|
51178
51571
|
"- You know roughly what the function does but not what it's named",
|
|
51179
51572
|
"",
|
|
51180
51573
|
"When NOT to use:",
|
|
51181
|
-
"- You have a specific symbol name → use grep",
|
|
51182
51574
|
"- You have an error message or stack trace → use grep",
|
|
51183
51575
|
"- You want the file/module structure → use aft_outline",
|
|
51184
51576
|
"- You're following a call chain → use aft_navigate",
|
|
51185
51577
|
"",
|
|
51186
|
-
"
|
|
51578
|
+
"Each result tags `source` as one of: 'semantic' (embedding match only), 'lexical' (trigram exact-token match the embedding lane missed), or 'hybrid' (both lanes agreed — strongest signal)."
|
|
51187
51579
|
].join(`
|
|
51188
51580
|
`),
|
|
51189
51581
|
parameters: SearchParams2,
|
|
@@ -51205,8 +51597,8 @@ function registerSemanticTool(pi, ctx) {
|
|
|
51205
51597
|
}
|
|
51206
51598
|
|
|
51207
51599
|
// src/tools/structure.ts
|
|
51208
|
-
import { StringEnum as StringEnum7 } from "@
|
|
51209
|
-
import { Type as Type13 } from "
|
|
51600
|
+
import { StringEnum as StringEnum7 } from "@earendil-works/pi-ai";
|
|
51601
|
+
import { Type as Type13 } from "typebox";
|
|
51210
51602
|
var TransformParams = Type13.Object({
|
|
51211
51603
|
op: StringEnum7(["add_member", "add_derive", "wrap_try_catch", "add_decorator", "add_struct_tags"], { description: "Transformation operation" }),
|
|
51212
51604
|
filePath: Type13.String({ description: "Path to the source file" }),
|
|
@@ -51344,8 +51736,8 @@ function buildWorkflowHints(opts) {
|
|
|
51344
51736
|
sections.push(`**Web/URL access**: \`aft_outline({ url })\` first for structure, then \`aft_zoom({ url, symbol: "<heading>" })\` for the specific section.`);
|
|
51345
51737
|
}
|
|
51346
51738
|
if (hasOutline && hasZoom && (hasGrep || hasSearch)) {
|
|
51347
|
-
const locator = hasGrep
|
|
51348
|
-
sections.push(`**Code exploration**: ${locator} to locate → \`aft_outline\` for structure → \`aft_zoom\` for symbol(s).`);
|
|
51739
|
+
const locator = hasGrep ? `\`${grepName}\`` : "`aft_search`";
|
|
51740
|
+
sections.push(hasGrep && hasSearch ? `**Code exploration**: For exact identifiers (\`useState\`, function names, env vars), error messages, or path-shaped queries → \`${grepName}\` first. For broad concepts ('where is X handled', 'how does Y work') → \`aft_search\`. Then use \`aft_outline\` for structure → \`aft_zoom\` for symbol(s).` : `**Code exploration**: ${locator} to locate → \`aft_outline\` for structure → \`aft_zoom\` for symbol(s).`);
|
|
51349
51741
|
}
|
|
51350
51742
|
if (hasNavigate) {
|
|
51351
51743
|
sections.push([
|
|
@@ -51546,7 +51938,7 @@ async function src_default(pi) {
|
|
|
51546
51938
|
log2(`AFT extension loading (plugin v${PLUGIN_VERSION})`);
|
|
51547
51939
|
let binaryPath;
|
|
51548
51940
|
try {
|
|
51549
|
-
binaryPath = await findBinary();
|
|
51941
|
+
binaryPath = await findBinary(PLUGIN_VERSION);
|
|
51550
51942
|
} catch (err) {
|
|
51551
51943
|
warn2(`Failed to resolve AFT binary: ${err instanceof Error ? err.message : String(err)}. ` + "Tools will not be registered.");
|
|
51552
51944
|
return;
|
|
@@ -51662,22 +52054,20 @@ ${lines}
|
|
|
51662
52054
|
pluginVersion: PLUGIN_VERSION
|
|
51663
52055
|
});
|
|
51664
52056
|
},
|
|
51665
|
-
onBashCompletion: (completion
|
|
52057
|
+
onBashCompletion: (completion) => {
|
|
51666
52058
|
handlePushedBgCompletion({
|
|
51667
52059
|
ctx,
|
|
51668
52060
|
directory: process.cwd(),
|
|
51669
52061
|
sessionID: completion.session_id,
|
|
51670
|
-
runtime: pi
|
|
51671
|
-
isActive: () => bridge.hasPendingRequests()
|
|
52062
|
+
runtime: pi
|
|
51672
52063
|
}, completion);
|
|
51673
52064
|
},
|
|
51674
|
-
onBashLongRunning: (reminder
|
|
52065
|
+
onBashLongRunning: (reminder) => {
|
|
51675
52066
|
handlePushedBgLongRunning({
|
|
51676
52067
|
ctx,
|
|
51677
52068
|
directory: process.cwd(),
|
|
51678
52069
|
sessionID: reminder.session_id,
|
|
51679
|
-
runtime: pi
|
|
51680
|
-
isActive: () => bridge.hasPendingRequests()
|
|
52070
|
+
runtime: pi
|
|
51681
52071
|
}, reminder);
|
|
51682
52072
|
}
|
|
51683
52073
|
};
|
|
@@ -51702,6 +52092,12 @@ ${lines}
|
|
|
51702
52092
|
log2(`Eager configure skipped: cwd=${cwd} is the user home directory. ` + `The first real tool call will warm the correct project bridge.`);
|
|
51703
52093
|
return;
|
|
51704
52094
|
}
|
|
52095
|
+
if (onnxRuntimePromise) {
|
|
52096
|
+
await Promise.race([
|
|
52097
|
+
onnxRuntimePromise,
|
|
52098
|
+
new Promise((resolve5) => setTimeout(() => resolve5(null), 60000))
|
|
52099
|
+
]);
|
|
52100
|
+
}
|
|
51705
52101
|
const bridge = pool.getBridge(cwd);
|
|
51706
52102
|
await bridge.send("status", {});
|
|
51707
52103
|
} catch (err) {
|