@cortexkit/aft-pi 0.39.1 → 0.39.3
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/config.d.ts +38 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +650 -401
- package/dist/notifications.d.ts +5 -1
- package/dist/notifications.d.ts.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/navigate.d.ts.map +1 -1
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -12020,6 +12020,11 @@ var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
|
|
|
12020
12020
|
function timeoutForCommand(command) {
|
|
12021
12021
|
return LONG_RUNNING_COMMAND_TIMEOUT_MS[command];
|
|
12022
12022
|
}
|
|
12023
|
+
var PASSIVE_COMMANDS = new Set(["status"]);
|
|
12024
|
+
var PASSIVE_COMMAND_TIMEOUT_MS = 5000;
|
|
12025
|
+
function isPassiveCommand(command) {
|
|
12026
|
+
return PASSIVE_COMMANDS.has(command);
|
|
12027
|
+
}
|
|
12023
12028
|
|
|
12024
12029
|
// ../aft-bridge/dist/status-bar.js
|
|
12025
12030
|
var STATUS_BAR_HEARTBEAT_CALLS = 15;
|
|
@@ -12092,6 +12097,11 @@ function bashTaskIdFrom(response) {
|
|
|
12092
12097
|
function tagStderrLine(line) {
|
|
12093
12098
|
return /^\[aft(-\w+)?\] /.test(line) ? line : `[aft] ${line}`;
|
|
12094
12099
|
}
|
|
12100
|
+
var BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE = "failed to parse processor information from /proc/cpuinfo";
|
|
12101
|
+
function shouldSurfaceStderrLine(line) {
|
|
12102
|
+
const normalized = line.trim();
|
|
12103
|
+
return !(normalized === `Error in cpuinfo: ${BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE}` || normalized === BENIGN_CPUINFO_PROC_CPUINFO_PARSE_FAILURE);
|
|
12104
|
+
}
|
|
12095
12105
|
function compareSemver(a, b) {
|
|
12096
12106
|
const [aMain, aPre] = a.split("-", 2);
|
|
12097
12107
|
const [bMain, bPre] = b.split("-", 2);
|
|
@@ -12183,6 +12193,7 @@ class BinaryBridge {
|
|
|
12183
12193
|
_restartCount = 0;
|
|
12184
12194
|
_shuttingDown = false;
|
|
12185
12195
|
timeoutMs;
|
|
12196
|
+
hangThreshold;
|
|
12186
12197
|
maxRestarts;
|
|
12187
12198
|
configured = false;
|
|
12188
12199
|
_configurePromise = null;
|
|
@@ -12207,6 +12218,7 @@ class BinaryBridge {
|
|
|
12207
12218
|
this.binaryPath = binaryPath;
|
|
12208
12219
|
this.cwd = cwd;
|
|
12209
12220
|
this.timeoutMs = options?.timeoutMs ?? DEFAULT_BRIDGE_TIMEOUT_MS;
|
|
12221
|
+
this.hangThreshold = options?.hangThreshold ?? BRIDGE_HANG_TIMEOUT_THRESHOLD;
|
|
12210
12222
|
this.maxRestarts = options?.maxRestarts ?? 3;
|
|
12211
12223
|
this.configOverrides = clampSemanticTimeout(configOverrides ?? {}, this.timeoutMs);
|
|
12212
12224
|
this.minVersion = options?.minVersion;
|
|
@@ -12324,7 +12336,9 @@ class BinaryBridge {
|
|
|
12324
12336
|
if (requestSessionId && options?.configureWarningClient !== undefined) {
|
|
12325
12337
|
this.configureWarningClients.set(requestSessionId, options.configureWarningClient);
|
|
12326
12338
|
}
|
|
12327
|
-
const
|
|
12339
|
+
const passive = isPassiveCommand(command);
|
|
12340
|
+
const resolvedTimeoutMs = options?.transportTimeoutMs ?? options?.timeoutMs ?? this.timeoutMs;
|
|
12341
|
+
const effectiveTimeoutMs = passive ? Math.min(resolvedTimeoutMs, PASSIVE_COMMAND_TIMEOUT_MS) : resolvedTimeoutMs;
|
|
12328
12342
|
const implicitTransportOptions = {
|
|
12329
12343
|
...options?.transportTimeoutMs !== undefined || options?.timeoutMs !== undefined ? { transportTimeoutMs: effectiveTimeoutMs } : {},
|
|
12330
12344
|
markConfiguredOnSuccess: false
|
|
@@ -12374,7 +12388,7 @@ class BinaryBridge {
|
|
|
12374
12388
|
}
|
|
12375
12389
|
const line = `${JSON.stringify(request)}
|
|
12376
12390
|
`;
|
|
12377
|
-
const keepBridgeOnTimeout = options?.keepBridgeOnTimeout === true;
|
|
12391
|
+
const keepBridgeOnTimeout = passive || options?.keepBridgeOnTimeout === true;
|
|
12378
12392
|
let requestSentAt = Date.now();
|
|
12379
12393
|
const response = await new Promise((resolve2, reject) => {
|
|
12380
12394
|
const timer = setTimeout(() => {
|
|
@@ -12396,7 +12410,7 @@ class BinaryBridge {
|
|
|
12396
12410
|
const childActiveSinceRequest = this.lastChildActivityAt > requestSentAt;
|
|
12397
12411
|
const consecutiveTimeouts = this.consecutiveRequestTimeouts + 1;
|
|
12398
12412
|
this.consecutiveRequestTimeouts = consecutiveTimeouts;
|
|
12399
|
-
const keepWarm = childActiveSinceRequest || consecutiveTimeouts <
|
|
12413
|
+
const keepWarm = childActiveSinceRequest || consecutiveTimeouts < this.hangThreshold;
|
|
12400
12414
|
const restartSuffix = keepWarm ? " — bridge kept warm" : " — restarting bridge";
|
|
12401
12415
|
const timeoutMsg = `Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms${restartSuffix}`;
|
|
12402
12416
|
if (requestSessionId) {
|
|
@@ -12691,7 +12705,7 @@ class BinaryBridge {
|
|
|
12691
12705
|
`)) !== -1) {
|
|
12692
12706
|
const line = this.stderrBuffer.slice(0, newlineIdx).replace(/\r$/, "");
|
|
12693
12707
|
this.stderrBuffer = this.stderrBuffer.slice(newlineIdx + 1);
|
|
12694
|
-
if (!line)
|
|
12708
|
+
if (!line || !shouldSurfaceStderrLine(line))
|
|
12695
12709
|
continue;
|
|
12696
12710
|
const tagged = tagStderrLine(line);
|
|
12697
12711
|
this.logVia(tagged);
|
|
@@ -12701,7 +12715,7 @@ class BinaryBridge {
|
|
|
12701
12715
|
flushStderrBuffer() {
|
|
12702
12716
|
const line = this.stderrBuffer.replace(/\r$/, "");
|
|
12703
12717
|
this.stderrBuffer = "";
|
|
12704
|
-
if (!line)
|
|
12718
|
+
if (!line || !shouldSurfaceStderrLine(line))
|
|
12705
12719
|
return;
|
|
12706
12720
|
const tagged = tagStderrLine(line);
|
|
12707
12721
|
this.logVia(tagged);
|
|
@@ -12926,6 +12940,208 @@ class BinaryBridge {
|
|
|
12926
12940
|
}
|
|
12927
12941
|
}
|
|
12928
12942
|
}
|
|
12943
|
+
// ../aft-bridge/dist/callgraph-format.js
|
|
12944
|
+
import { homedir as homedir3 } from "node:os";
|
|
12945
|
+
var PLAIN_CALLGRAPH_THEME = {
|
|
12946
|
+
fg: (_role, text) => text
|
|
12947
|
+
};
|
|
12948
|
+
function asRecord(value) {
|
|
12949
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
12950
|
+
return;
|
|
12951
|
+
return value;
|
|
12952
|
+
}
|
|
12953
|
+
function asRecords(value) {
|
|
12954
|
+
return Array.isArray(value) ? value.map(asRecord).filter(Boolean) : [];
|
|
12955
|
+
}
|
|
12956
|
+
function asString(value) {
|
|
12957
|
+
return typeof value === "string" ? value : undefined;
|
|
12958
|
+
}
|
|
12959
|
+
function asNumber(value) {
|
|
12960
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
12961
|
+
}
|
|
12962
|
+
function asBoolean(value) {
|
|
12963
|
+
return typeof value === "boolean" ? value : undefined;
|
|
12964
|
+
}
|
|
12965
|
+
function shortenPath(path2) {
|
|
12966
|
+
const home = homedir3();
|
|
12967
|
+
if (path2.startsWith(home))
|
|
12968
|
+
return `~${path2.slice(home.length)}`;
|
|
12969
|
+
return path2;
|
|
12970
|
+
}
|
|
12971
|
+
function joinNonEmpty(parts, separator = " · ") {
|
|
12972
|
+
return parts.filter((part) => Boolean(part && part.length > 0)).join(separator);
|
|
12973
|
+
}
|
|
12974
|
+
function treeLine(depth, text) {
|
|
12975
|
+
return `${" ".repeat(depth)}${depth === 0 ? "" : "↳ "}${text}`;
|
|
12976
|
+
}
|
|
12977
|
+
function renderCallTreeNode(node, depth, lines, theme) {
|
|
12978
|
+
const name = asString(node.name) ?? "(unknown)";
|
|
12979
|
+
const file = shortenPath(asString(node.file) ?? "(unknown file)");
|
|
12980
|
+
const line = asNumber(node.line);
|
|
12981
|
+
const unresolved = node.resolved === false ? ` ${theme.fg("warning", "[unresolved]")}` : "";
|
|
12982
|
+
const location = line !== undefined ? `[${file}:${line}]` : `[${file}]`;
|
|
12983
|
+
lines.push(treeLine(depth, `${name} ${location}${unresolved}`));
|
|
12984
|
+
asRecords(node.children).forEach((child) => {
|
|
12985
|
+
renderCallTreeNode(child, depth + 1, lines, theme);
|
|
12986
|
+
});
|
|
12987
|
+
}
|
|
12988
|
+
function depthWarning(response, theme, depthField = "depth_limited", truncatedField = "truncated") {
|
|
12989
|
+
const limited = asBoolean(response[depthField]);
|
|
12990
|
+
const truncated = asNumber(response[truncatedField]) ?? 0;
|
|
12991
|
+
if (!limited && truncated === 0)
|
|
12992
|
+
return "";
|
|
12993
|
+
const detail = truncated > 0 ? `, ${truncated} truncated` : "";
|
|
12994
|
+
return theme.fg("warning", `(depth limited${detail})`);
|
|
12995
|
+
}
|
|
12996
|
+
function renderTracePath(path2, index, lines) {
|
|
12997
|
+
lines.push(`Path ${index + 1}`);
|
|
12998
|
+
asRecords(path2.hops).forEach((hop, hopIndex) => {
|
|
12999
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
13000
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
13001
|
+
const line = asNumber(hop.line);
|
|
13002
|
+
const entry = hop.is_entry_point === true ? " [entry]" : "";
|
|
13003
|
+
lines.push(treeLine(hopIndex + 1, `${symbol}${entry} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}`));
|
|
13004
|
+
});
|
|
13005
|
+
}
|
|
13006
|
+
function renderCallersGroupLines(group, theme) {
|
|
13007
|
+
const file = shortenPath(asString(group.file) ?? "(unknown file)");
|
|
13008
|
+
const lines = [theme.fg("accent", file)];
|
|
13009
|
+
const callers = asRecords(group.callers);
|
|
13010
|
+
const bySymbol = new Map;
|
|
13011
|
+
for (const caller of callers) {
|
|
13012
|
+
const symbol = asString(caller.symbol) ?? "(unknown)";
|
|
13013
|
+
const line = asNumber(caller.line);
|
|
13014
|
+
const bucket = bySymbol.get(symbol) ?? [];
|
|
13015
|
+
if (line !== undefined)
|
|
13016
|
+
bucket.push(line);
|
|
13017
|
+
bySymbol.set(symbol, bucket);
|
|
13018
|
+
}
|
|
13019
|
+
const symbols = [...bySymbol.keys()].sort((a, b) => a.localeCompare(b));
|
|
13020
|
+
for (const symbol of symbols) {
|
|
13021
|
+
const lineNums = (bySymbol.get(symbol) ?? []).sort((a, b) => a - b);
|
|
13022
|
+
const linePart = lineNums.length > 0 ? lineNums.map(String).join(", ") : "?";
|
|
13023
|
+
lines.push(` ↳ ${symbol}:${linePart}`);
|
|
13024
|
+
}
|
|
13025
|
+
return lines;
|
|
13026
|
+
}
|
|
13027
|
+
function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME) {
|
|
13028
|
+
const record = asRecord(response);
|
|
13029
|
+
if (!record)
|
|
13030
|
+
return [theme.fg("muted", "No navigation result.")];
|
|
13031
|
+
if (op === "call_tree") {
|
|
13032
|
+
const lines = [];
|
|
13033
|
+
renderCallTreeNode(record, 0, lines, theme);
|
|
13034
|
+
const warning = depthWarning(record, theme);
|
|
13035
|
+
if (warning)
|
|
13036
|
+
lines.push(warning);
|
|
13037
|
+
return lines.length > 0 ? lines : [theme.fg("muted", "No call tree available.")];
|
|
13038
|
+
}
|
|
13039
|
+
if (op === "callers") {
|
|
13040
|
+
const groups = asRecords(record.callers);
|
|
13041
|
+
const warning = depthWarning(record, theme);
|
|
13042
|
+
const total = asNumber(record.total_callers) ?? 0;
|
|
13043
|
+
const sections2 = [
|
|
13044
|
+
joinNonEmpty([
|
|
13045
|
+
theme.fg("success", `${total} caller${total === 1 ? "" : "s"}`),
|
|
13046
|
+
theme.fg("muted", `${groups.length} file group${groups.length === 1 ? "" : "s"}`),
|
|
13047
|
+
warning
|
|
13048
|
+
])
|
|
13049
|
+
];
|
|
13050
|
+
groups.forEach((group) => {
|
|
13051
|
+
sections2.push(renderCallersGroupLines(group, theme).join(`
|
|
13052
|
+
`));
|
|
13053
|
+
});
|
|
13054
|
+
return sections2;
|
|
13055
|
+
}
|
|
13056
|
+
if (op === "trace_to_symbol") {
|
|
13057
|
+
const path2 = asRecords(record.path);
|
|
13058
|
+
const complete = asBoolean(record.complete);
|
|
13059
|
+
const reason = asString(record.reason);
|
|
13060
|
+
if (path2.length === 0) {
|
|
13061
|
+
const prefix = complete === false ? theme.fg("warning", "No complete path") : theme.fg("muted", "No path");
|
|
13062
|
+
return [`${prefix}${reason ? ` (${reason})` : ""}`];
|
|
13063
|
+
}
|
|
13064
|
+
const lines = [theme.fg("success", `${path2.length} hop${path2.length === 1 ? "" : "s"}`)];
|
|
13065
|
+
path2.forEach((hop, index) => {
|
|
13066
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
13067
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
13068
|
+
const line = asNumber(hop.line);
|
|
13069
|
+
lines.push(treeLine(index + 1, `${symbol} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}`));
|
|
13070
|
+
});
|
|
13071
|
+
return lines;
|
|
13072
|
+
}
|
|
13073
|
+
if (op === "trace_to") {
|
|
13074
|
+
const paths = asRecords(record.paths);
|
|
13075
|
+
const warning = depthWarning(record, theme, "max_depth_reached", "truncated_paths");
|
|
13076
|
+
const totalPaths = asNumber(record.total_paths) ?? paths.length;
|
|
13077
|
+
const entryPoints = asNumber(record.entry_points_found) ?? 0;
|
|
13078
|
+
const sections2 = [
|
|
13079
|
+
joinNonEmpty([
|
|
13080
|
+
theme.fg("success", `${totalPaths} path${totalPaths === 1 ? "" : "s"}`),
|
|
13081
|
+
theme.fg("muted", `${entryPoints} entry point${entryPoints === 1 ? "" : "s"}`),
|
|
13082
|
+
warning
|
|
13083
|
+
])
|
|
13084
|
+
];
|
|
13085
|
+
if (paths.length === 0)
|
|
13086
|
+
sections2.push(theme.fg("muted", "No entry paths found."));
|
|
13087
|
+
paths.forEach((path2, index) => {
|
|
13088
|
+
const lines = [];
|
|
13089
|
+
renderTracePath(path2, index, lines);
|
|
13090
|
+
sections2.push(lines.join(`
|
|
13091
|
+
`));
|
|
13092
|
+
});
|
|
13093
|
+
return sections2;
|
|
13094
|
+
}
|
|
13095
|
+
if (op === "impact") {
|
|
13096
|
+
const callers = asRecords(record.callers);
|
|
13097
|
+
const warning = depthWarning(record, theme);
|
|
13098
|
+
const totalAffected = asNumber(record.total_affected) ?? callers.length;
|
|
13099
|
+
const affectedFiles = asNumber(record.affected_files) ?? 0;
|
|
13100
|
+
const sections2 = [
|
|
13101
|
+
joinNonEmpty([
|
|
13102
|
+
theme.fg("warning", `${totalAffected} affected call site${totalAffected === 1 ? "" : "s"}`),
|
|
13103
|
+
theme.fg("muted", `${affectedFiles} file${affectedFiles === 1 ? "" : "s"}`),
|
|
13104
|
+
warning
|
|
13105
|
+
])
|
|
13106
|
+
];
|
|
13107
|
+
if (callers.length === 0)
|
|
13108
|
+
sections2.push(theme.fg("muted", "No impacted callers found."));
|
|
13109
|
+
callers.forEach((caller) => {
|
|
13110
|
+
const file = shortenPath(asString(caller.caller_file) ?? "(unknown file)");
|
|
13111
|
+
const symbol = asString(caller.caller_symbol) ?? "(unknown)";
|
|
13112
|
+
const line = asNumber(caller.line) ?? 0;
|
|
13113
|
+
const entry = caller.is_entry_point === true ? ` ${theme.fg("warning", "[entry]")}` : "";
|
|
13114
|
+
const expression = asString(caller.call_expression);
|
|
13115
|
+
const params = Array.isArray(caller.parameters) ? caller.parameters.map(String).join(", ") : "";
|
|
13116
|
+
sections2.push([
|
|
13117
|
+
`${theme.fg("accent", file)}:${line}`,
|
|
13118
|
+
` ↳ ${symbol}${entry}`,
|
|
13119
|
+
expression ? ` ${theme.fg("muted", expression)}` : undefined,
|
|
13120
|
+
params ? ` ${theme.fg("muted", `params: ${params}`)}` : undefined
|
|
13121
|
+
].filter(Boolean).join(`
|
|
13122
|
+
`));
|
|
13123
|
+
});
|
|
13124
|
+
return sections2;
|
|
13125
|
+
}
|
|
13126
|
+
const hops = asRecords(record.hops);
|
|
13127
|
+
const sections = [
|
|
13128
|
+
joinNonEmpty([
|
|
13129
|
+
theme.fg("success", `${hops.length} hop${hops.length === 1 ? "" : "s"}`),
|
|
13130
|
+
asBoolean(record.depth_limited) ? theme.fg("warning", "(depth limited)") : undefined
|
|
13131
|
+
])
|
|
13132
|
+
];
|
|
13133
|
+
if (hops.length === 0)
|
|
13134
|
+
sections.push(theme.fg("muted", "No data-flow hops found."));
|
|
13135
|
+
hops.forEach((hop, index) => {
|
|
13136
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
13137
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
13138
|
+
const variable = asString(hop.variable) ?? "(unknown)";
|
|
13139
|
+
const line = asNumber(hop.line) ?? 0;
|
|
13140
|
+
const approximate = hop.approximate === true ? ` ${theme.fg("warning", "[approx]")}` : "";
|
|
13141
|
+
sections.push(treeLine(index, `${variable} ${theme.fg("muted", `${asString(hop.flow_type) ?? "flow"}`)} ${symbol} [${file}:${line}]${approximate}`));
|
|
13142
|
+
});
|
|
13143
|
+
return sections;
|
|
13144
|
+
}
|
|
12929
13145
|
// ../aft-bridge/dist/coerce.js
|
|
12930
13146
|
function coerceStringArray(value) {
|
|
12931
13147
|
if (Array.isArray(value)) {
|
|
@@ -12976,7 +13192,7 @@ function isEmptyParam(value) {
|
|
|
12976
13192
|
import { spawnSync } from "node:child_process";
|
|
12977
13193
|
import { createHash, randomUUID } from "node:crypto";
|
|
12978
13194
|
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
12979
|
-
import { homedir as
|
|
13195
|
+
import { homedir as homedir4 } from "node:os";
|
|
12980
13196
|
import { join as join3 } from "node:path";
|
|
12981
13197
|
import { Readable } from "node:stream";
|
|
12982
13198
|
import { pipeline } from "node:stream/promises";
|
|
@@ -13034,10 +13250,10 @@ function isExpectedCachedBinary(binaryPath, tag) {
|
|
|
13034
13250
|
function getCacheDir() {
|
|
13035
13251
|
if (process.platform === "win32") {
|
|
13036
13252
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
13037
|
-
const base2 = localAppData || join3(
|
|
13253
|
+
const base2 = localAppData || join3(homedir4(), "AppData", "Local");
|
|
13038
13254
|
return join3(base2, "aft", "bin");
|
|
13039
13255
|
}
|
|
13040
|
-
const base = process.env.XDG_CACHE_HOME || join3(
|
|
13256
|
+
const base = process.env.XDG_CACHE_HOME || join3(homedir4(), ".cache");
|
|
13041
13257
|
return join3(base, "aft", "bin");
|
|
13042
13258
|
}
|
|
13043
13259
|
function getBinaryName() {
|
|
@@ -13317,7 +13533,7 @@ function stripJsoncSymbols(value) {
|
|
|
13317
13533
|
// ../aft-bridge/dist/migration.js
|
|
13318
13534
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
13319
13535
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
13320
|
-
import { homedir as
|
|
13536
|
+
import { homedir as homedir6, tmpdir } from "node:os";
|
|
13321
13537
|
import { dirname as dirname2, join as join6 } from "node:path";
|
|
13322
13538
|
|
|
13323
13539
|
// ../aft-bridge/dist/paths.js
|
|
@@ -13374,7 +13590,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
13374
13590
|
import { execSync } from "node:child_process";
|
|
13375
13591
|
import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, openSync as openSync2, readSync, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
13376
13592
|
import { createRequire as createRequire2 } from "node:module";
|
|
13377
|
-
import { homedir as
|
|
13593
|
+
import { homedir as homedir5 } from "node:os";
|
|
13378
13594
|
import { join as join5 } from "node:path";
|
|
13379
13595
|
var ensureBinaryForResolver = ensureBinary;
|
|
13380
13596
|
function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
@@ -13416,7 +13632,7 @@ function normalizeBareVersion(version) {
|
|
|
13416
13632
|
return version.startsWith("v") ? version.slice(1) : version;
|
|
13417
13633
|
}
|
|
13418
13634
|
function homeDirFromEnv(env) {
|
|
13419
|
-
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) ||
|
|
13635
|
+
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir5();
|
|
13420
13636
|
}
|
|
13421
13637
|
function cacheDirFromEnv(env) {
|
|
13422
13638
|
if (process.platform === "win32") {
|
|
@@ -13598,8 +13814,8 @@ function dataHome() {
|
|
|
13598
13814
|
}
|
|
13599
13815
|
function homeDir() {
|
|
13600
13816
|
if (process.platform === "win32")
|
|
13601
|
-
return process.env.USERPROFILE || process.env.HOME ||
|
|
13602
|
-
return process.env.HOME ||
|
|
13817
|
+
return process.env.USERPROFILE || process.env.HOME || homedir6();
|
|
13818
|
+
return process.env.HOME || homedir6();
|
|
13603
13819
|
}
|
|
13604
13820
|
function resolveLegacyStorageRoot(harness) {
|
|
13605
13821
|
if (harness === "pi")
|
|
@@ -13689,13 +13905,13 @@ async function ensureStorageMigrated(opts) {
|
|
|
13689
13905
|
}
|
|
13690
13906
|
// ../aft-bridge/dist/npm-resolver.js
|
|
13691
13907
|
import { readdirSync, statSync as statSync2 } from "node:fs";
|
|
13692
|
-
import { homedir as
|
|
13908
|
+
import { homedir as homedir7 } from "node:os";
|
|
13693
13909
|
import { delimiter, dirname as dirname3, isAbsolute as isAbsolute2, join as join7 } from "node:path";
|
|
13694
13910
|
function defaultDeps() {
|
|
13695
13911
|
return {
|
|
13696
13912
|
platform: process.platform,
|
|
13697
13913
|
env: process.env,
|
|
13698
|
-
home:
|
|
13914
|
+
home: homedir7(),
|
|
13699
13915
|
execPath: process.execPath
|
|
13700
13916
|
};
|
|
13701
13917
|
}
|
|
@@ -15030,13 +15246,13 @@ function tokenizeStage(stage) {
|
|
|
15030
15246
|
}
|
|
15031
15247
|
// ../aft-bridge/dist/pool.js
|
|
15032
15248
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
15033
|
-
import { homedir as
|
|
15249
|
+
import { homedir as homedir8 } from "node:os";
|
|
15034
15250
|
var DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
15035
15251
|
var DEFAULT_MAX_POOL_SIZE = 8;
|
|
15036
15252
|
var CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
15037
15253
|
function canonicalHomeDir() {
|
|
15038
15254
|
try {
|
|
15039
|
-
const home =
|
|
15255
|
+
const home = homedir8();
|
|
15040
15256
|
if (!home)
|
|
15041
15257
|
return null;
|
|
15042
15258
|
try {
|
|
@@ -15074,6 +15290,7 @@ class BridgePool {
|
|
|
15074
15290
|
this.projectConfigLoader = options.projectConfigLoader;
|
|
15075
15291
|
this.bridgeOptions = {
|
|
15076
15292
|
timeoutMs: options.timeoutMs,
|
|
15293
|
+
hangThreshold: options.hangThreshold,
|
|
15077
15294
|
maxRestarts: options.maxRestarts,
|
|
15078
15295
|
minVersion: options.minVersion,
|
|
15079
15296
|
onVersionMismatch: options.onVersionMismatch,
|
|
@@ -16058,7 +16275,7 @@ import {
|
|
|
16058
16275
|
// package.json
|
|
16059
16276
|
var package_default = {
|
|
16060
16277
|
name: "@cortexkit/aft-pi",
|
|
16061
|
-
version: "0.39.
|
|
16278
|
+
version: "0.39.3",
|
|
16062
16279
|
type: "module",
|
|
16063
16280
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
16064
16281
|
main: "dist/index.js",
|
|
@@ -16081,7 +16298,7 @@ var package_default = {
|
|
|
16081
16298
|
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
|
|
16082
16299
|
},
|
|
16083
16300
|
dependencies: {
|
|
16084
|
-
"@cortexkit/aft-bridge": "0.39.
|
|
16301
|
+
"@cortexkit/aft-bridge": "0.39.3",
|
|
16085
16302
|
"@xterm/headless": "^5.5.0",
|
|
16086
16303
|
"comment-json": "^5.0.0",
|
|
16087
16304
|
diff: "^8.0.4",
|
|
@@ -16089,12 +16306,12 @@ var package_default = {
|
|
|
16089
16306
|
zod: "^4.1.8"
|
|
16090
16307
|
},
|
|
16091
16308
|
optionalDependencies: {
|
|
16092
|
-
"@cortexkit/aft-darwin-arm64": "0.39.
|
|
16093
|
-
"@cortexkit/aft-darwin-x64": "0.39.
|
|
16094
|
-
"@cortexkit/aft-linux-arm64": "0.39.
|
|
16095
|
-
"@cortexkit/aft-linux-x64": "0.39.
|
|
16096
|
-
"@cortexkit/aft-win32-arm64": "0.39.
|
|
16097
|
-
"@cortexkit/aft-win32-x64": "0.39.
|
|
16309
|
+
"@cortexkit/aft-darwin-arm64": "0.39.3",
|
|
16310
|
+
"@cortexkit/aft-darwin-x64": "0.39.3",
|
|
16311
|
+
"@cortexkit/aft-linux-arm64": "0.39.3",
|
|
16312
|
+
"@cortexkit/aft-linux-x64": "0.39.3",
|
|
16313
|
+
"@cortexkit/aft-win32-arm64": "0.39.3",
|
|
16314
|
+
"@cortexkit/aft-win32-x64": "0.39.3"
|
|
16098
16315
|
},
|
|
16099
16316
|
devDependencies: {
|
|
16100
16317
|
"@earendil-works/pi-coding-agent": "*",
|
|
@@ -16121,7 +16338,7 @@ var package_default = {
|
|
|
16121
16338
|
};
|
|
16122
16339
|
|
|
16123
16340
|
// src/shared/status.ts
|
|
16124
|
-
function
|
|
16341
|
+
function asRecord2(value) {
|
|
16125
16342
|
return typeof value === "object" && value !== null ? value : {};
|
|
16126
16343
|
}
|
|
16127
16344
|
function readString(value, fallback = "") {
|
|
@@ -16140,7 +16357,7 @@ function readOptionalNumber(value) {
|
|
|
16140
16357
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
16141
16358
|
}
|
|
16142
16359
|
function readCompressionAggregate(value) {
|
|
16143
|
-
const aggregate =
|
|
16360
|
+
const aggregate = asRecord2(value);
|
|
16144
16361
|
return {
|
|
16145
16362
|
events: readNumber(aggregate.events),
|
|
16146
16363
|
original_tokens: readNumber(aggregate.original_tokens),
|
|
@@ -16151,7 +16368,7 @@ function readCompressionAggregate(value) {
|
|
|
16151
16368
|
function readCompression(value) {
|
|
16152
16369
|
if (typeof value !== "object" || value === null)
|
|
16153
16370
|
return;
|
|
16154
|
-
const compression =
|
|
16371
|
+
const compression = asRecord2(value);
|
|
16155
16372
|
return {
|
|
16156
16373
|
project: readCompressionAggregate(compression.project),
|
|
16157
16374
|
session: readCompressionAggregate(compression.session)
|
|
@@ -16160,7 +16377,7 @@ function readCompression(value) {
|
|
|
16160
16377
|
function readStatusBar(value) {
|
|
16161
16378
|
if (typeof value !== "object" || value === null)
|
|
16162
16379
|
return;
|
|
16163
|
-
const bar =
|
|
16380
|
+
const bar = asRecord2(value);
|
|
16164
16381
|
return {
|
|
16165
16382
|
errors: readNumber(bar.errors),
|
|
16166
16383
|
warnings: readNumber(bar.warnings),
|
|
@@ -16204,16 +16421,16 @@ function formatBytes(bytes) {
|
|
|
16204
16421
|
return `${value.toFixed(decimals)} ${units[unitIndex]}`;
|
|
16205
16422
|
}
|
|
16206
16423
|
function coerceAftStatus(response) {
|
|
16207
|
-
const features =
|
|
16208
|
-
const searchIndex =
|
|
16209
|
-
const semanticIndex =
|
|
16424
|
+
const features = asRecord2(response.features);
|
|
16425
|
+
const searchIndex = asRecord2(response.search_index);
|
|
16426
|
+
const semanticIndex = asRecord2(response.semantic_index);
|
|
16210
16427
|
const semanticConfig = {
|
|
16211
|
-
...
|
|
16212
|
-
...
|
|
16428
|
+
...asRecord2(response.semantic),
|
|
16429
|
+
...asRecord2(response.semantic_config)
|
|
16213
16430
|
};
|
|
16214
|
-
const disk =
|
|
16215
|
-
const symbolCache =
|
|
16216
|
-
const session =
|
|
16431
|
+
const disk = asRecord2(response.disk);
|
|
16432
|
+
const symbolCache = asRecord2(response.symbol_cache);
|
|
16433
|
+
const session = asRecord2(response.session);
|
|
16217
16434
|
return {
|
|
16218
16435
|
version: readString(response.version, "unknown"),
|
|
16219
16436
|
project_root: readNullableString(response.project_root),
|
|
@@ -16669,7 +16886,7 @@ function registerStatusCommand(pi, ctx) {
|
|
|
16669
16886
|
|
|
16670
16887
|
// src/config.ts
|
|
16671
16888
|
import { existsSync as existsSync6, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
16672
|
-
import { homedir as
|
|
16889
|
+
import { homedir as homedir9 } from "node:os";
|
|
16673
16890
|
import { join as join10 } from "node:path";
|
|
16674
16891
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
16675
16892
|
|
|
@@ -30331,6 +30548,10 @@ var BashFeaturesSchema = exports_external.object({
|
|
|
30331
30548
|
foreground_wait_window_ms: exports_external.number().int().positive().optional()
|
|
30332
30549
|
});
|
|
30333
30550
|
var BashConfigSchema = exports_external.union([exports_external.boolean(), BashFeaturesSchema]);
|
|
30551
|
+
var BridgeConfigSchema = exports_external.object({
|
|
30552
|
+
request_timeout_ms: exports_external.number().int().min(1000, { message: "bridge.request_timeout_ms must be at least 1000" }).optional(),
|
|
30553
|
+
hang_threshold: exports_external.number().int().min(1, { message: "bridge.hang_threshold must be at least 1" }).optional()
|
|
30554
|
+
});
|
|
30334
30555
|
var InspectConfigSchema = exports_external.object({
|
|
30335
30556
|
enabled: exports_external.boolean().optional(),
|
|
30336
30557
|
tier2_idle_minutes: exports_external.number().min(0).optional(),
|
|
@@ -30362,13 +30583,16 @@ var AftConfigSchema = exports_external.object({
|
|
|
30362
30583
|
restrict_to_project_root: exports_external.boolean().optional(),
|
|
30363
30584
|
search_index: exports_external.boolean().optional(),
|
|
30364
30585
|
semantic_search: exports_external.boolean().optional(),
|
|
30586
|
+
callgraph_store: exports_external.boolean().optional(),
|
|
30587
|
+
callgraph_chunk_size: exports_external.number().optional(),
|
|
30365
30588
|
inspect: InspectConfigSchema.optional(),
|
|
30366
30589
|
bash: BashConfigSchema.optional(),
|
|
30367
30590
|
experimental: ExperimentalConfigSchema.optional(),
|
|
30368
30591
|
lsp: LspConfigSchema.optional(),
|
|
30369
30592
|
url_fetch_allow_private: exports_external.boolean().optional(),
|
|
30370
30593
|
semantic: SemanticConfigSchema.optional(),
|
|
30371
|
-
max_callgraph_files: exports_external.number().int().positive().optional()
|
|
30594
|
+
max_callgraph_files: exports_external.number().int().positive().optional(),
|
|
30595
|
+
bridge: BridgeConfigSchema.optional()
|
|
30372
30596
|
}).strict();
|
|
30373
30597
|
function normalizeLspExtension(extension) {
|
|
30374
30598
|
return extension.trim().replace(/^\.+/, "");
|
|
@@ -30421,6 +30645,37 @@ function resolveLspConfigForConfigure(config2) {
|
|
|
30421
30645
|
}
|
|
30422
30646
|
return overrides;
|
|
30423
30647
|
}
|
|
30648
|
+
function resolveProjectOverridesForConfigure(config2) {
|
|
30649
|
+
const overrides = {};
|
|
30650
|
+
if (config2.format_on_edit !== undefined)
|
|
30651
|
+
overrides.format_on_edit = config2.format_on_edit;
|
|
30652
|
+
if (config2.formatter_timeout_secs !== undefined)
|
|
30653
|
+
overrides.formatter_timeout_secs = config2.formatter_timeout_secs;
|
|
30654
|
+
if (config2.validate_on_edit !== undefined)
|
|
30655
|
+
overrides.validate_on_edit = config2.validate_on_edit;
|
|
30656
|
+
if (config2.formatter !== undefined)
|
|
30657
|
+
overrides.formatter = config2.formatter;
|
|
30658
|
+
if (config2.checker !== undefined)
|
|
30659
|
+
overrides.checker = config2.checker;
|
|
30660
|
+
overrides.restrict_to_project_root = config2.restrict_to_project_root ?? false;
|
|
30661
|
+
if (config2.search_index !== undefined)
|
|
30662
|
+
overrides.search_index = config2.search_index;
|
|
30663
|
+
if (config2.semantic_search !== undefined)
|
|
30664
|
+
overrides.semantic_search = config2.semantic_search;
|
|
30665
|
+
if (config2.callgraph_store !== undefined)
|
|
30666
|
+
overrides.callgraph_store = config2.callgraph_store;
|
|
30667
|
+
if (config2.callgraph_chunk_size !== undefined)
|
|
30668
|
+
overrides.callgraph_chunk_size = config2.callgraph_chunk_size;
|
|
30669
|
+
Object.assign(overrides, resolveExperimentalConfigForConfigure(config2));
|
|
30670
|
+
Object.assign(overrides, resolveLspConfigForConfigure(config2));
|
|
30671
|
+
if (config2.semantic !== undefined)
|
|
30672
|
+
overrides.semantic = config2.semantic;
|
|
30673
|
+
if (config2.inspect !== undefined)
|
|
30674
|
+
overrides.inspect = config2.inspect;
|
|
30675
|
+
if (config2.max_callgraph_files !== undefined)
|
|
30676
|
+
overrides.max_callgraph_files = config2.max_callgraph_files;
|
|
30677
|
+
return overrides;
|
|
30678
|
+
}
|
|
30424
30679
|
function resolveExperimentalConfigForConfigure(config2) {
|
|
30425
30680
|
const overrides = {};
|
|
30426
30681
|
const bash = resolveBashConfig(config2);
|
|
@@ -30597,6 +30852,17 @@ function detectConfigFile(basePath) {
|
|
|
30597
30852
|
return { format: "json", path: jsonPath };
|
|
30598
30853
|
return { format: "none", path: jsonPath };
|
|
30599
30854
|
}
|
|
30855
|
+
var configLoadErrors = [];
|
|
30856
|
+
function getConfigLoadErrors() {
|
|
30857
|
+
return configLoadErrors;
|
|
30858
|
+
}
|
|
30859
|
+
function formatConfigParseFailureMessage(configPath, errorMessage) {
|
|
30860
|
+
return `AFT config at ${configPath} failed to parse and was ignored (running on defaults): ${errorMessage}. ` + "Fix the syntax or run `npx @cortexkit/aft doctor`.";
|
|
30861
|
+
}
|
|
30862
|
+
function recordConfigParseFailure(configPath, errorMessage) {
|
|
30863
|
+
configLoadErrors.push({ path: configPath, message: errorMessage });
|
|
30864
|
+
warn2(formatConfigParseFailureMessage(configPath, errorMessage));
|
|
30865
|
+
}
|
|
30600
30866
|
function loadConfigFromPath(configPath) {
|
|
30601
30867
|
try {
|
|
30602
30868
|
if (!existsSync6(configPath))
|
|
@@ -30604,7 +30870,7 @@ function loadConfigFromPath(configPath) {
|
|
|
30604
30870
|
const content = readFileSync4(configPath, "utf-8");
|
|
30605
30871
|
const rawConfig = import_comment_json.parse(content);
|
|
30606
30872
|
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
|
|
30607
|
-
|
|
30873
|
+
recordConfigParseFailure(configPath, "root must be an object");
|
|
30608
30874
|
return null;
|
|
30609
30875
|
}
|
|
30610
30876
|
migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
|
|
@@ -30620,6 +30886,7 @@ function loadConfigFromPath(configPath) {
|
|
|
30620
30886
|
} catch (err) {
|
|
30621
30887
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
30622
30888
|
error2(`Error loading config from ${configPath}: ${errorMsg}`);
|
|
30889
|
+
recordConfigParseFailure(configPath, errorMsg);
|
|
30623
30890
|
return null;
|
|
30624
30891
|
}
|
|
30625
30892
|
}
|
|
@@ -30755,6 +31022,8 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
|
|
|
30755
31022
|
"configure_warnings_delivery",
|
|
30756
31023
|
"search_index",
|
|
30757
31024
|
"semantic_search",
|
|
31025
|
+
"callgraph_store",
|
|
31026
|
+
"callgraph_chunk_size",
|
|
30758
31027
|
"inspect",
|
|
30759
31028
|
"experimental",
|
|
30760
31029
|
"bash"
|
|
@@ -30776,6 +31045,8 @@ function getStrippedTopLevelKeys(override) {
|
|
|
30776
31045
|
stripped.push("url_fetch_allow_private");
|
|
30777
31046
|
if (override.max_callgraph_files !== undefined)
|
|
30778
31047
|
stripped.push("max_callgraph_files");
|
|
31048
|
+
if (override.bridge !== undefined)
|
|
31049
|
+
stripped.push("bridge");
|
|
30779
31050
|
return stripped;
|
|
30780
31051
|
}
|
|
30781
31052
|
function mergeConfigs(base, override) {
|
|
@@ -30787,6 +31058,7 @@ function mergeConfigs(base, override) {
|
|
|
30787
31058
|
const experimental = mergeExperimentalConfig(base.experimental, override.experimental);
|
|
30788
31059
|
const bash = mergeBashConfig(base.bash, override.bash);
|
|
30789
31060
|
const inspect = mergeInspectConfig(base.inspect, override.inspect);
|
|
31061
|
+
const bridge = base.bridge;
|
|
30790
31062
|
const safeOverride = pickProjectSafeFields(override);
|
|
30791
31063
|
delete safeOverride.bash;
|
|
30792
31064
|
delete safeOverride.inspect;
|
|
@@ -30800,13 +31072,23 @@ function mergeConfigs(base, override) {
|
|
|
30800
31072
|
...inspect !== undefined ? { inspect } : {},
|
|
30801
31073
|
experimental,
|
|
30802
31074
|
semantic,
|
|
31075
|
+
...bridge !== undefined ? { bridge } : {},
|
|
30803
31076
|
...disabledTools.length > 0 ? { disabled_tools: [...new Set(disabledTools)] } : {}
|
|
30804
31077
|
};
|
|
30805
31078
|
}
|
|
31079
|
+
var DEFAULT_BRIDGE_REQUEST_TIMEOUT_MS = 30000;
|
|
31080
|
+
var DEFAULT_BRIDGE_HANG_THRESHOLD = 2;
|
|
31081
|
+
function resolveBridgePoolTransportOptions(config2) {
|
|
31082
|
+
return {
|
|
31083
|
+
timeoutMs: config2.bridge?.request_timeout_ms ?? DEFAULT_BRIDGE_REQUEST_TIMEOUT_MS,
|
|
31084
|
+
hangThreshold: config2.bridge?.hang_threshold ?? DEFAULT_BRIDGE_HANG_THRESHOLD
|
|
31085
|
+
};
|
|
31086
|
+
}
|
|
30806
31087
|
function getGlobalPiDir() {
|
|
30807
|
-
return join10(
|
|
31088
|
+
return join10(homedir9(), ".pi", "agent");
|
|
30808
31089
|
}
|
|
30809
31090
|
function loadAftConfig(projectDirectory) {
|
|
31091
|
+
configLoadErrors = [];
|
|
30810
31092
|
const userBasePath = join10(getGlobalPiDir(), "aft");
|
|
30811
31093
|
migrateAftConfigFile(`${userBasePath}.jsonc`);
|
|
30812
31094
|
migrateAftConfigFile(`${userBasePath}.json`);
|
|
@@ -30861,7 +31143,7 @@ import {
|
|
|
30861
31143
|
unlinkSync as unlinkSync5,
|
|
30862
31144
|
writeFileSync as writeFileSync4
|
|
30863
31145
|
} from "node:fs";
|
|
30864
|
-
import { homedir as
|
|
31146
|
+
import { homedir as homedir10 } from "node:os";
|
|
30865
31147
|
import { join as join11 } from "node:path";
|
|
30866
31148
|
function aftCacheBase() {
|
|
30867
31149
|
const override = process.env.AFT_CACHE_DIR;
|
|
@@ -30869,10 +31151,10 @@ function aftCacheBase() {
|
|
|
30869
31151
|
return override;
|
|
30870
31152
|
if (process.platform === "win32") {
|
|
30871
31153
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
30872
|
-
const base2 = localAppData || join11(
|
|
31154
|
+
const base2 = localAppData || join11(homedir10(), "AppData", "Local");
|
|
30873
31155
|
return join11(base2, "aft");
|
|
30874
31156
|
}
|
|
30875
|
-
const base = process.env.XDG_CACHE_HOME || join11(
|
|
31157
|
+
const base = process.env.XDG_CACHE_HOME || join11(homedir10(), ".cache");
|
|
30876
31158
|
return join11(base, "aft");
|
|
30877
31159
|
}
|
|
30878
31160
|
function lspCacheRoot() {
|
|
@@ -32585,6 +32867,8 @@ function warningTitle(warning) {
|
|
|
32585
32867
|
return "Checker is not installed";
|
|
32586
32868
|
case "lsp_binary_missing":
|
|
32587
32869
|
return "LSP binary is missing";
|
|
32870
|
+
case "config_parse_failed":
|
|
32871
|
+
return "Config failed to parse";
|
|
32588
32872
|
}
|
|
32589
32873
|
}
|
|
32590
32874
|
function formatConfigureWarning(warning) {
|
|
@@ -32830,7 +33114,7 @@ import { Type as Type3 } from "typebox";
|
|
|
32830
33114
|
|
|
32831
33115
|
// src/tools/hoisted.ts
|
|
32832
33116
|
import { stat } from "node:fs/promises";
|
|
32833
|
-
import { homedir as
|
|
33117
|
+
import { homedir as homedir11 } from "node:os";
|
|
32834
33118
|
import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve4, sep } from "node:path";
|
|
32835
33119
|
import {
|
|
32836
33120
|
renderDiff
|
|
@@ -32952,12 +33236,66 @@ function expandTilde2(path3) {
|
|
|
32952
33236
|
if (!path3 || !path3.startsWith("~"))
|
|
32953
33237
|
return path3;
|
|
32954
33238
|
if (path3 === "~")
|
|
32955
|
-
return
|
|
33239
|
+
return homedir11();
|
|
32956
33240
|
if (path3.startsWith(`~${sep}`) || path3.startsWith("~/")) {
|
|
32957
|
-
return resolve4(
|
|
33241
|
+
return resolve4(homedir11(), path3.slice(2));
|
|
32958
33242
|
}
|
|
32959
33243
|
return path3;
|
|
32960
33244
|
}
|
|
33245
|
+
function absoluteSearchPath(cwd, target) {
|
|
33246
|
+
const expanded = expandTilde2(target);
|
|
33247
|
+
return isAbsolute4(expanded) ? expanded : resolve4(cwd, expanded);
|
|
33248
|
+
}
|
|
33249
|
+
async function searchPathExists(cwd, target) {
|
|
33250
|
+
try {
|
|
33251
|
+
await stat(absoluteSearchPath(cwd, target));
|
|
33252
|
+
return true;
|
|
33253
|
+
} catch {
|
|
33254
|
+
return false;
|
|
33255
|
+
}
|
|
33256
|
+
}
|
|
33257
|
+
async function splitSearchPathArg(cwd, raw) {
|
|
33258
|
+
if (await searchPathExists(cwd, raw) || !/\s/.test(raw)) {
|
|
33259
|
+
return { paths: [raw], missing: [] };
|
|
33260
|
+
}
|
|
33261
|
+
const fragments = raw.trim().split(/\s+/).filter(Boolean);
|
|
33262
|
+
if (fragments.length < 2) {
|
|
33263
|
+
return { paths: [raw], missing: [] };
|
|
33264
|
+
}
|
|
33265
|
+
const existing = [];
|
|
33266
|
+
const missing = [];
|
|
33267
|
+
for (const fragment of fragments) {
|
|
33268
|
+
if (await searchPathExists(cwd, fragment)) {
|
|
33269
|
+
existing.push(fragment);
|
|
33270
|
+
} else {
|
|
33271
|
+
missing.push(fragment);
|
|
33272
|
+
}
|
|
33273
|
+
}
|
|
33274
|
+
if (existing.length === 0) {
|
|
33275
|
+
return { paths: [raw], missing: [] };
|
|
33276
|
+
}
|
|
33277
|
+
return { paths: existing, missing };
|
|
33278
|
+
}
|
|
33279
|
+
async function bridgeSearchPathArg(cwd, split) {
|
|
33280
|
+
if (split.paths.length === 1 && split.missing.length === 0) {
|
|
33281
|
+
return await resolvePathArg(cwd, split.paths[0]);
|
|
33282
|
+
}
|
|
33283
|
+
return split.paths.map((target) => absoluteSearchPath(cwd, target)).join(" ");
|
|
33284
|
+
}
|
|
33285
|
+
function formatSkippedSearchPaths(missing) {
|
|
33286
|
+
if (missing.length === 0)
|
|
33287
|
+
return;
|
|
33288
|
+
const noun = missing.length === 1 ? "path" : "paths";
|
|
33289
|
+
return `Skipped ${missing.length} ${noun} not found: ${missing.join(", ")}`;
|
|
33290
|
+
}
|
|
33291
|
+
function appendSkippedSearchPaths(text, missing) {
|
|
33292
|
+
const note = formatSkippedSearchPaths(missing);
|
|
33293
|
+
if (!note)
|
|
33294
|
+
return text;
|
|
33295
|
+
return text.length > 0 ? `${text}
|
|
33296
|
+
|
|
33297
|
+
${note}` : note;
|
|
33298
|
+
}
|
|
32961
33299
|
function externalDirectoryPromptTimeoutMs() {
|
|
32962
33300
|
const raw = process.env.AFT_PI_EXTERNAL_PROMPT_TIMEOUT_MS;
|
|
32963
33301
|
if (raw === undefined)
|
|
@@ -33167,19 +33505,26 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
33167
33505
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
33168
33506
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33169
33507
|
const req = { pattern: params.pattern };
|
|
33508
|
+
let pathSplit;
|
|
33170
33509
|
if (params.path) {
|
|
33171
|
-
await
|
|
33172
|
-
|
|
33173
|
-
|
|
33174
|
-
|
|
33510
|
+
pathSplit = await splitSearchPathArg(extCtx.cwd, params.path);
|
|
33511
|
+
for (const target of pathSplit.paths) {
|
|
33512
|
+
await assertExternalDirectoryPermission(extCtx, absoluteSearchPath(extCtx.cwd, target), "search", {
|
|
33513
|
+
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
33514
|
+
});
|
|
33515
|
+
}
|
|
33516
|
+
req.path = await bridgeSearchPathArg(extCtx.cwd, pathSplit);
|
|
33175
33517
|
}
|
|
33176
33518
|
if (params.include)
|
|
33177
33519
|
req.include = splitIncludeGlobs(params.include);
|
|
33178
33520
|
if (params.caseSensitive !== undefined)
|
|
33179
33521
|
req.case_sensitive = params.caseSensitive;
|
|
33180
33522
|
const response = await callBridge(bridge, "grep", req, extCtx);
|
|
33181
|
-
|
|
33182
|
-
|
|
33523
|
+
if (pathSplit && pathSplit.missing.length > 0) {
|
|
33524
|
+
response.complete = false;
|
|
33525
|
+
}
|
|
33526
|
+
const text = appendSkippedSearchPaths(response.text ?? "", pathSplit?.missing ?? []);
|
|
33527
|
+
return textResult(text, response);
|
|
33183
33528
|
}
|
|
33184
33529
|
});
|
|
33185
33530
|
}
|
|
@@ -33287,7 +33632,7 @@ function reuseContainer(last) {
|
|
|
33287
33632
|
}
|
|
33288
33633
|
function renderMutationCall(toolName, filePath, theme, context) {
|
|
33289
33634
|
const text = reuseText(context.lastComponent);
|
|
33290
|
-
const pathDisplay = filePath ? theme.fg("accent",
|
|
33635
|
+
const pathDisplay = filePath ? theme.fg("accent", shortenPath2(filePath)) : theme.fg("toolOutput", "...");
|
|
33291
33636
|
text.setText(`${theme.fg("toolTitle", theme.bold(toolName))} ${pathDisplay}`);
|
|
33292
33637
|
return text;
|
|
33293
33638
|
}
|
|
@@ -33323,15 +33668,15 @@ ${summary}${suffix}`);
|
|
|
33323
33668
|
container.addChild(new Text(renderDiff(diff), 1, 0));
|
|
33324
33669
|
return container;
|
|
33325
33670
|
}
|
|
33326
|
-
function
|
|
33327
|
-
const home =
|
|
33671
|
+
function shortenPath2(path3) {
|
|
33672
|
+
const home = homedir11();
|
|
33328
33673
|
if (path3.startsWith(home))
|
|
33329
33674
|
return `~${path3.slice(home.length)}`;
|
|
33330
33675
|
return path3;
|
|
33331
33676
|
}
|
|
33332
33677
|
async function resolvePathArg(cwd, path3) {
|
|
33333
33678
|
const expanded = expandTilde2(path3);
|
|
33334
|
-
const abs =
|
|
33679
|
+
const abs = absoluteSearchPath(cwd, path3);
|
|
33335
33680
|
try {
|
|
33336
33681
|
await stat(abs);
|
|
33337
33682
|
return abs;
|
|
@@ -33374,7 +33719,7 @@ function formatReadFooter2(agentSpecifiedRange, data) {
|
|
|
33374
33719
|
}
|
|
33375
33720
|
|
|
33376
33721
|
// src/tools/render-helpers.ts
|
|
33377
|
-
import { homedir as
|
|
33722
|
+
import { homedir as homedir12 } from "node:os";
|
|
33378
33723
|
import { renderDiff as renderDiff2 } from "@earendil-works/pi-coding-agent";
|
|
33379
33724
|
import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
|
|
33380
33725
|
function reuseText2(last) {
|
|
@@ -33383,8 +33728,8 @@ function reuseText2(last) {
|
|
|
33383
33728
|
function reuseContainer2(last) {
|
|
33384
33729
|
return last instanceof Container2 ? last : new Container2;
|
|
33385
33730
|
}
|
|
33386
|
-
function
|
|
33387
|
-
const home =
|
|
33731
|
+
function shortenPath3(path3) {
|
|
33732
|
+
const home = homedir12();
|
|
33388
33733
|
if (path3.startsWith(home))
|
|
33389
33734
|
return `~${path3.slice(home.length)}`;
|
|
33390
33735
|
return path3;
|
|
@@ -33398,7 +33743,7 @@ function renderToolCall(toolName, summary, theme, context) {
|
|
|
33398
33743
|
function accentPath(theme, path3) {
|
|
33399
33744
|
if (!path3)
|
|
33400
33745
|
return theme.fg("toolOutput", "...");
|
|
33401
|
-
return theme.fg("accent",
|
|
33746
|
+
return theme.fg("accent", shortenPath3(path3));
|
|
33402
33747
|
}
|
|
33403
33748
|
function collectTextContent(result) {
|
|
33404
33749
|
return result.content.filter((part) => part.type === "text").map((part) => part.text ?? "").join(`
|
|
@@ -33437,23 +33782,20 @@ function renderSections(sections, context) {
|
|
|
33437
33782
|
});
|
|
33438
33783
|
return container;
|
|
33439
33784
|
}
|
|
33440
|
-
function
|
|
33785
|
+
function asRecord3(value) {
|
|
33441
33786
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
33442
33787
|
return;
|
|
33443
33788
|
return value;
|
|
33444
33789
|
}
|
|
33445
|
-
function
|
|
33446
|
-
return Array.isArray(value) ? value.map(
|
|
33790
|
+
function asRecords2(value) {
|
|
33791
|
+
return Array.isArray(value) ? value.map(asRecord3).filter(Boolean) : [];
|
|
33447
33792
|
}
|
|
33448
|
-
function
|
|
33793
|
+
function asString2(value) {
|
|
33449
33794
|
return typeof value === "string" ? value : undefined;
|
|
33450
33795
|
}
|
|
33451
|
-
function
|
|
33796
|
+
function asNumber2(value) {
|
|
33452
33797
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
33453
33798
|
}
|
|
33454
|
-
function asBoolean(value) {
|
|
33455
|
-
return typeof value === "boolean" ? value : undefined;
|
|
33456
|
-
}
|
|
33457
33799
|
function formatValue(value) {
|
|
33458
33800
|
if (Array.isArray(value))
|
|
33459
33801
|
return value.map(formatValue).join(", ");
|
|
@@ -33566,7 +33908,7 @@ var ReplaceParams = Type3.Object({
|
|
|
33566
33908
|
dryRun: Type3.Optional(Type3.Boolean({ description: "Preview without applying (default: false)" }))
|
|
33567
33909
|
});
|
|
33568
33910
|
function appendHintSection(response, sections, theme) {
|
|
33569
|
-
const hint =
|
|
33911
|
+
const hint = asString2(response.hint);
|
|
33570
33912
|
if (hint && hint.length > 0) {
|
|
33571
33913
|
sections.push(theme.fg("warning", hint));
|
|
33572
33914
|
}
|
|
@@ -33575,8 +33917,8 @@ function appendScopeSections(response, sections, theme) {
|
|
|
33575
33917
|
if (response.no_files_matched_scope === true) {
|
|
33576
33918
|
sections.push(theme.fg("warning", "No files matched the scope (paths/globs resolved to zero files)"));
|
|
33577
33919
|
}
|
|
33578
|
-
const warnings =
|
|
33579
|
-
const warningStrings = Array.isArray(response.scope_warnings) ? response.scope_warnings.filter((w) => typeof w === "string") : warnings.map((w) =>
|
|
33920
|
+
const warnings = asRecords2(response.scope_warnings);
|
|
33921
|
+
const warningStrings = Array.isArray(response.scope_warnings) ? response.scope_warnings.filter((w) => typeof w === "string") : warnings.map((w) => asString2(w.warning) ?? "").filter(Boolean);
|
|
33580
33922
|
if (warningStrings.length > 0) {
|
|
33581
33923
|
sections.push(`${theme.fg("muted", "Scope warnings:")}
|
|
33582
33924
|
${warningStrings.map((w) => ` ${w}`).join(`
|
|
@@ -33600,13 +33942,13 @@ async function assertAstPathsPermission(extCtx, paths, action, restrictToProject
|
|
|
33600
33942
|
}
|
|
33601
33943
|
}
|
|
33602
33944
|
function buildAstSearchSections(payload, theme) {
|
|
33603
|
-
const response =
|
|
33945
|
+
const response = asRecord3(payload);
|
|
33604
33946
|
if (!response)
|
|
33605
33947
|
return [theme.fg("muted", "No AST search results.")];
|
|
33606
|
-
const matches =
|
|
33607
|
-
const totalMatches =
|
|
33608
|
-
const filesWithMatches =
|
|
33609
|
-
const filesSearched =
|
|
33948
|
+
const matches = asRecords2(response.matches);
|
|
33949
|
+
const totalMatches = asNumber2(response.total_matches) ?? matches.length;
|
|
33950
|
+
const filesWithMatches = asNumber2(response.files_with_matches) ?? groupByFile(matches, (match) => asString2(match.file)).size;
|
|
33951
|
+
const filesSearched = asNumber2(response.files_searched);
|
|
33610
33952
|
const header = [
|
|
33611
33953
|
theme.fg("success", `${totalMatches} match${totalMatches === 1 ? "" : "es"}`),
|
|
33612
33954
|
theme.fg("accent", `${filesWithMatches} file${filesWithMatches === 1 ? "" : "s"}`),
|
|
@@ -33618,26 +33960,26 @@ function buildAstSearchSections(payload, theme) {
|
|
|
33618
33960
|
appendHintSection(response, sections2, theme);
|
|
33619
33961
|
return sections2;
|
|
33620
33962
|
}
|
|
33621
|
-
const grouped = groupByFile(matches, (match) =>
|
|
33963
|
+
const grouped = groupByFile(matches, (match) => asString2(match.file));
|
|
33622
33964
|
const sections = [header];
|
|
33623
33965
|
for (const [file2, fileMatches] of grouped.entries()) {
|
|
33624
|
-
const lines = [theme.fg("accent",
|
|
33966
|
+
const lines = [theme.fg("accent", shortenPath3(file2))];
|
|
33625
33967
|
fileMatches.forEach((match, index) => {
|
|
33626
|
-
const line =
|
|
33627
|
-
const column =
|
|
33628
|
-
const snippet =
|
|
33968
|
+
const line = asNumber2(match.line) ?? 0;
|
|
33969
|
+
const column = asNumber2(match.column) ?? 0;
|
|
33970
|
+
const snippet = asString2(match.text)?.trim() || "(empty match)";
|
|
33629
33971
|
lines.push(` ${index + 1}. ${theme.fg("muted", `${line}:${column}`)} ${snippet}`);
|
|
33630
|
-
const metaVars =
|
|
33972
|
+
const metaVars = asRecord3(match.meta_variables);
|
|
33631
33973
|
if (metaVars && Object.keys(metaVars).length > 0) {
|
|
33632
33974
|
Object.entries(metaVars).forEach(([name, value]) => {
|
|
33633
33975
|
lines.push(` ${theme.fg("muted", `${name} =`)} ${formatValue(value)}`);
|
|
33634
33976
|
});
|
|
33635
33977
|
}
|
|
33636
|
-
const context =
|
|
33978
|
+
const context = asRecords2(match.context);
|
|
33637
33979
|
context.forEach((ctxLine) => {
|
|
33638
|
-
const ctxNumber =
|
|
33980
|
+
const ctxNumber = asNumber2(ctxLine.line) ?? 0;
|
|
33639
33981
|
const prefix = ctxLine.is_match === true ? theme.fg("accent", ">") : theme.fg("muted", "|");
|
|
33640
|
-
lines.push(` ${prefix} ${ctxNumber}: ${
|
|
33982
|
+
lines.push(` ${prefix} ${ctxNumber}: ${asString2(ctxLine.text) ?? ""}`);
|
|
33641
33983
|
});
|
|
33642
33984
|
});
|
|
33643
33985
|
sections.push(lines.join(`
|
|
@@ -33646,13 +33988,13 @@ function buildAstSearchSections(payload, theme) {
|
|
|
33646
33988
|
return sections;
|
|
33647
33989
|
}
|
|
33648
33990
|
function buildAstReplaceSections(payload, theme) {
|
|
33649
|
-
const response =
|
|
33991
|
+
const response = asRecord3(payload);
|
|
33650
33992
|
if (!response)
|
|
33651
33993
|
return [theme.fg("muted", "No AST replace results.")];
|
|
33652
|
-
const files =
|
|
33653
|
-
const totalReplacements =
|
|
33654
|
-
const totalFiles =
|
|
33655
|
-
const filesWithMatches =
|
|
33994
|
+
const files = asRecords2(response.files);
|
|
33995
|
+
const totalReplacements = asNumber2(response.total_replacements) ?? 0;
|
|
33996
|
+
const totalFiles = asNumber2(response.total_files) ?? files.length;
|
|
33997
|
+
const filesWithMatches = asNumber2(response.files_with_matches);
|
|
33656
33998
|
const dryRun = response.dry_run === true;
|
|
33657
33999
|
const headerParts = [
|
|
33658
34000
|
dryRun ? theme.fg("warning", "[dry run]") : theme.fg("success", "[applied]"),
|
|
@@ -33668,10 +34010,10 @@ function buildAstReplaceSections(payload, theme) {
|
|
|
33668
34010
|
return sections;
|
|
33669
34011
|
}
|
|
33670
34012
|
files.forEach((fileResult) => {
|
|
33671
|
-
const file2 =
|
|
33672
|
-
const replacements =
|
|
33673
|
-
const error50 =
|
|
33674
|
-
const diff =
|
|
34013
|
+
const file2 = shortenPath3(asString2(fileResult.file) ?? "(unknown file)");
|
|
34014
|
+
const replacements = asNumber2(fileResult.replacements) ?? 0;
|
|
34015
|
+
const error50 = asString2(fileResult.error);
|
|
34016
|
+
const diff = asString2(fileResult.diff);
|
|
33675
34017
|
const lines = [
|
|
33676
34018
|
`${theme.fg("accent", file2)} ${theme.fg("muted", `(${replacements} replacement${replacements === 1 ? "" : "s"})`)}`
|
|
33677
34019
|
];
|
|
@@ -33681,7 +34023,7 @@ function buildAstReplaceSections(payload, theme) {
|
|
|
33681
34023
|
const rendered = renderUnifiedDiff(diff);
|
|
33682
34024
|
lines.push(rendered || theme.fg("muted", "No diff available."));
|
|
33683
34025
|
} else {
|
|
33684
|
-
const backupId =
|
|
34026
|
+
const backupId = asString2(fileResult.backup_id);
|
|
33685
34027
|
lines.push(backupId ? `${theme.fg("success", "saved")} ${theme.fg("muted", backupId)}` : theme.fg("success", "saved"));
|
|
33686
34028
|
}
|
|
33687
34029
|
sections.push(lines.join(`
|
|
@@ -33778,7 +34120,7 @@ import { Type as Type4 } from "typebox";
|
|
|
33778
34120
|
var FOREGROUND_POLL_INTERVAL_MS = 100;
|
|
33779
34121
|
var BASH_WAIT_POLL_INTERVAL_MS = 100;
|
|
33780
34122
|
var DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS = 30000;
|
|
33781
|
-
var MAX_BASH_STATUS_WAIT_TIMEOUT_MS =
|
|
34123
|
+
var MAX_BASH_STATUS_WAIT_TIMEOUT_MS = 30 * 60 * 1000;
|
|
33782
34124
|
var REGEX_WAIT_SCAN_WINDOW_BYTES = 64 * 1024;
|
|
33783
34125
|
function resolveForegroundWaitMs(configured) {
|
|
33784
34126
|
const override = process.env.AFT_TEST_FOREGROUND_WAIT_MS;
|
|
@@ -33790,7 +34132,8 @@ function resolveForegroundWaitMs(configured) {
|
|
|
33790
34132
|
return configured;
|
|
33791
34133
|
}
|
|
33792
34134
|
var BASH_TRANSPORT_TIMEOUT_MS = 30000;
|
|
33793
|
-
var
|
|
34135
|
+
var DEFAULT_HARD_TIMEOUT_MS = 30 * 60 * 1000;
|
|
34136
|
+
var BashBaseParams = {
|
|
33794
34137
|
command: Type4.String({
|
|
33795
34138
|
description: "Shell command to execute. Supports pipes, redirections, and shell syntax."
|
|
33796
34139
|
}),
|
|
@@ -33800,19 +34143,38 @@ var BashParams = Type4.Object({
|
|
|
33800
34143
|
})),
|
|
33801
34144
|
description: Type4.Optional(Type4.String({
|
|
33802
34145
|
description: "Human-readable description shown in UI logs. Helps users understand what the command does without reading shell syntax."
|
|
33803
|
-
}))
|
|
34146
|
+
}))
|
|
34147
|
+
};
|
|
34148
|
+
var BashBackgroundFlagParam = {
|
|
33804
34149
|
background: Type4.Optional(Type4.Boolean({
|
|
33805
|
-
description: "Spawn command in background and return immediately with a task_id. Use
|
|
33806
|
-
}))
|
|
34150
|
+
description: "Spawn command in background and return immediately with a task_id. Use bash_watch to wait for completion or output patterns; bash_status for a one-shot snapshot only. Use bash_kill to terminate. Ideal for long-running tasks like builds or dev servers."
|
|
34151
|
+
}))
|
|
34152
|
+
};
|
|
34153
|
+
var BashCompressionParam = {
|
|
33807
34154
|
compressed: Type4.Optional(Type4.Boolean({
|
|
33808
34155
|
description: "Compress output by removing ANSI codes, carriage returns, and excessive blank lines. Default: true. Set to false for raw terminal output including color codes."
|
|
33809
|
-
}))
|
|
34156
|
+
}))
|
|
34157
|
+
};
|
|
34158
|
+
var BashPtyParams = {
|
|
33810
34159
|
pty: Type4.Optional(Type4.Boolean({
|
|
33811
34160
|
description: 'Spawn the command in a real PTY for interactive programs. Implies background: true automatically. Inspect with bash_status({ task_id, output_mode: "screen" }) and send input with bash_write.'
|
|
33812
34161
|
})),
|
|
33813
34162
|
ptyRows: optionalInt(1, 60),
|
|
33814
34163
|
ptyCols: optionalInt(1, 140)
|
|
34164
|
+
};
|
|
34165
|
+
var BashParams = Type4.Object({
|
|
34166
|
+
...BashBaseParams,
|
|
34167
|
+
...BashBackgroundFlagParam,
|
|
34168
|
+
...BashCompressionParam,
|
|
34169
|
+
...BashPtyParams
|
|
34170
|
+
});
|
|
34171
|
+
var BashForegroundOnlyParams = Type4.Object({
|
|
34172
|
+
...BashBaseParams,
|
|
34173
|
+
...BashCompressionParam
|
|
33815
34174
|
});
|
|
34175
|
+
function bashParamsForConfig(backgroundEnabled) {
|
|
34176
|
+
return backgroundEnabled ? BashParams : BashForegroundOnlyParams;
|
|
34177
|
+
}
|
|
33816
34178
|
var BashTaskParams = Type4.Object({
|
|
33817
34179
|
task_id: Type4.String({
|
|
33818
34180
|
description: "Background bash task id returned by bash({ background: true })."
|
|
@@ -33886,28 +34248,30 @@ function registerBashTool(pi, ctx, aftSearchRegistered = false) {
|
|
|
33886
34248
|
const searchSteer = aftSearchRegistered ? "use `aft_search` (concepts, identifiers, regex, literals), `read`, `aft_outline`, or `aft_zoom` instead" : "use the `grep` tool, `read`, `aft_outline`, or `aft_zoom` instead";
|
|
33887
34249
|
const bashCfg = resolveBashConfig(ctx.config);
|
|
33888
34250
|
const compressionSentence = bashCfg.compress ? " Output is compressed by default; pass `compressed: false` for raw output." : "";
|
|
33889
|
-
const tasksSentence = bashCfg.background ? ' Pass `background: true` to run in the background and get a task_id for `bash_status`/`bash_kill`. Pass `pty: true` for interactive programs (REPLs, TUIs) and drive them with `bash_status({ output_mode: "screen" })` plus `bash_write`.' : " Commands
|
|
34251
|
+
const tasksSentence = bashCfg.background ? ' Pass `background: true` to run in the background and get a task_id for `bash_status`/`bash_watch`/`bash_kill`. Pass `pty: true` for interactive programs (REPLs, TUIs) and drive them with `bash_status({ output_mode: "screen" })` plus `bash_write`. Use `bash_watch` to wait for exit or output patterns (sync blocks, async notifies). Do not loop `bash_status` to wait.' : " Commands run in the foreground to completion; `timeout` is the hard kill cap (default 30 minutes).";
|
|
33890
34252
|
pi.registerTool({
|
|
33891
34253
|
name: "bash",
|
|
33892
34254
|
label: "bash",
|
|
33893
34255
|
description: `Execute shell commands.${compressionSentence}${tasksSentence}
|
|
33894
34256
|
|
|
33895
34257
|
DO NOT use bash for code search or code exploration. If you are about to run grep, rg, sed, awk, find, or cat through bash to locate or read code: STOP — ${searchSteer}.`,
|
|
33896
|
-
promptSnippet: "Run shell commands (timeout in milliseconds; supports workdir, background tasks, compressed output, PTY mode)",
|
|
34258
|
+
promptSnippet: bashCfg.background ? "Run shell commands (timeout in milliseconds; supports workdir, background tasks, compressed output, PTY mode)" : "Run shell commands (timeout in milliseconds; supports workdir and compressed output)",
|
|
33897
34259
|
promptGuidelines: [
|
|
33898
34260
|
`DO NOT use bash for code search or exploration — ${searchSteer}.`,
|
|
33899
34261
|
"Set compressed: false when you need ANSI color codes in the output."
|
|
33900
34262
|
],
|
|
33901
|
-
parameters:
|
|
34263
|
+
parameters: bashParamsForConfig(bashCfg.background),
|
|
33902
34264
|
async execute(_toolCallId, params, _signal, onUpdate, extCtx) {
|
|
33903
34265
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33904
34266
|
const bashCfg2 = resolveBashConfig(ctx.config);
|
|
33905
34267
|
const foregroundWaitMs = resolveForegroundWaitMs(bashCfg2.foreground_wait_window_ms);
|
|
34268
|
+
const backgroundDisabled = !bashCfg2.background;
|
|
33906
34269
|
const timeout = coerceOptionalInt(params.timeout, "timeout", 1, Number.MAX_SAFE_INTEGER);
|
|
33907
|
-
const ptyRows = coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
|
|
33908
|
-
const ptyCols = coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
|
|
33909
|
-
const
|
|
33910
|
-
const
|
|
34270
|
+
const ptyRows = backgroundDisabled ? undefined : coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
|
|
34271
|
+
const ptyCols = backgroundDisabled ? undefined : coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
|
|
34272
|
+
const requestedPty = !backgroundDisabled && params.pty === true;
|
|
34273
|
+
const effectiveBackground = !backgroundDisabled && (params.background === true || requestedPty);
|
|
34274
|
+
const effectiveTimeout = effectiveBackground || backgroundDisabled ? timeout : resolveBashKillTimeout(timeout, foregroundWaitMs);
|
|
33911
34275
|
let spawnContext = {
|
|
33912
34276
|
command: params.command,
|
|
33913
34277
|
cwd: params.workdir
|
|
@@ -33932,7 +34296,7 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
33932
34296
|
background: effectiveBackground,
|
|
33933
34297
|
notify_on_completion: effectiveBackground,
|
|
33934
34298
|
compressed: params.compressed,
|
|
33935
|
-
pty:
|
|
34299
|
+
pty: requestedPty,
|
|
33936
34300
|
pty_rows: ptyRows,
|
|
33937
34301
|
pty_cols: ptyCols
|
|
33938
34302
|
}, extCtx, {
|
|
@@ -33954,9 +34318,9 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
33954
34318
|
if (response.status === "running" && taskId) {
|
|
33955
34319
|
if (effectiveBackground) {
|
|
33956
34320
|
trackBgTask(resolveSessionId(extCtx), taskId);
|
|
33957
|
-
return bashResult(appendPipeStripNote(formatBackgroundLaunch(taskId,
|
|
34321
|
+
return bashResult(appendPipeStripNote(formatBackgroundLaunch(taskId, requestedPty), pipeStrip.note), { task_id: taskId });
|
|
33958
34322
|
}
|
|
33959
|
-
const waitTimeoutMs = effectiveTimeout !== undefined ? Math.min(effectiveTimeout, foregroundWaitMs) : foregroundWaitMs;
|
|
34323
|
+
const waitTimeoutMs = backgroundDisabled ? effectiveTimeout ?? DEFAULT_HARD_TIMEOUT_MS : effectiveTimeout !== undefined ? Math.min(effectiveTimeout, foregroundWaitMs) : foregroundWaitMs;
|
|
33960
34324
|
const startedAt = Date.now();
|
|
33961
34325
|
while (true) {
|
|
33962
34326
|
const status = await callBashBridge(bridge, "bash_status", { task_id: taskId }, extCtx);
|
|
@@ -33973,6 +34337,10 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
33973
34337
|
});
|
|
33974
34338
|
}
|
|
33975
34339
|
if (Date.now() - startedAt >= waitTimeoutMs) {
|
|
34340
|
+
if (backgroundDisabled) {
|
|
34341
|
+
await sleep(FOREGROUND_POLL_INTERVAL_MS);
|
|
34342
|
+
continue;
|
|
34343
|
+
}
|
|
33976
34344
|
const promoted = await callBashBridge(bridge, "bash_promote", { task_id: taskId }, extCtx);
|
|
33977
34345
|
if (promoted.success === false) {
|
|
33978
34346
|
throw new Error(promoted.message ?? "bash_promote failed");
|
|
@@ -34000,10 +34368,12 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
34000
34368
|
return renderBashResult(result, theme, context);
|
|
34001
34369
|
}
|
|
34002
34370
|
});
|
|
34003
|
-
|
|
34004
|
-
|
|
34005
|
-
|
|
34006
|
-
|
|
34371
|
+
if (bashCfg.background) {
|
|
34372
|
+
pi.registerTool(createBashStatusTool(ctx));
|
|
34373
|
+
pi.registerTool(createBashWatchTool(ctx));
|
|
34374
|
+
pi.registerTool(createBashWriteTool(ctx));
|
|
34375
|
+
pi.registerTool(createBashKillTool(ctx));
|
|
34376
|
+
}
|
|
34007
34377
|
}
|
|
34008
34378
|
function formatBackgroundLaunch(taskId, isPty) {
|
|
34009
34379
|
if (isPty) {
|
|
@@ -34022,7 +34392,7 @@ function createBashStatusTool(ctx) {
|
|
|
34022
34392
|
return {
|
|
34023
34393
|
name: "bash_status",
|
|
34024
34394
|
label: "bash_status",
|
|
34025
|
-
description: "Read-only snapshot of a background bash task. Returns immediately. Never waits.
|
|
34395
|
+
description: "Read-only snapshot of a background bash task. Returns immediately. Never waits. One look to check on a task is fine — never loop it to wait for completion. To wait, use bash_watch.",
|
|
34026
34396
|
promptSnippet: "Inspect a background bash task by task_id",
|
|
34027
34397
|
parameters: BashStatusParams,
|
|
34028
34398
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
@@ -34037,7 +34407,7 @@ function createBashWatchTool(ctx) {
|
|
|
34037
34407
|
return {
|
|
34038
34408
|
name: "bash_watch",
|
|
34039
34409
|
label: "bash_watch",
|
|
34040
|
-
description: "Watch a background bash task.
|
|
34410
|
+
description: "Watch a background bash task. Sync (default) blocks until a pattern matches, the task exits, or timeout — use it when the result is the next thing you need, even for long builds/tests/installs (pass timeout_ms up to 30 min for those). The user can interrupt anytime; the wait auto-converts to an async notification. Async (background:true, requires pattern) registers a non-blocking notification and returns immediately — use when you have parallel work or want to end your turn. Never loop bash_status to wait.",
|
|
34041
34411
|
promptSnippet: "Wait for or watch a background bash task",
|
|
34042
34412
|
parameters: BashWatchParams,
|
|
34043
34413
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
@@ -34610,10 +34980,10 @@ function renderFsResult(toolName, args, result, theme, context) {
|
|
|
34610
34980
|
const skipped = data.skipped_files ?? [];
|
|
34611
34981
|
const lines = [];
|
|
34612
34982
|
for (const entry of deletedPaths) {
|
|
34613
|
-
lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent",
|
|
34983
|
+
lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent", shortenPath3(entry))}`);
|
|
34614
34984
|
}
|
|
34615
34985
|
for (const entry of skipped) {
|
|
34616
|
-
lines.push(`${theme.fg("error", "✗ skipped")} ${theme.fg("accent",
|
|
34986
|
+
lines.push(`${theme.fg("error", "✗ skipped")} ${theme.fg("accent", shortenPath3(entry.file))} ${theme.fg("muted", `(${entry.reason})`)}`);
|
|
34617
34987
|
}
|
|
34618
34988
|
if (lines.length === 0) {
|
|
34619
34989
|
lines.push(theme.fg("muted", "(no files deleted)"));
|
|
@@ -34623,8 +34993,8 @@ function renderFsResult(toolName, args, result, theme, context) {
|
|
|
34623
34993
|
}
|
|
34624
34994
|
const moveArgs = args;
|
|
34625
34995
|
return renderSections([
|
|
34626
|
-
`${theme.fg("success", "✓ moved")} ${theme.fg("accent",
|
|
34627
|
-
`${theme.fg("muted", "to")} ${theme.fg("accent",
|
|
34996
|
+
`${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath3(moveArgs.filePath))}`,
|
|
34997
|
+
`${theme.fg("muted", "to")} ${theme.fg("accent", shortenPath3(moveArgs.destination))}`
|
|
34628
34998
|
], context);
|
|
34629
34999
|
}
|
|
34630
35000
|
function registerFsTools(pi, ctx, surface) {
|
|
@@ -34738,33 +35108,33 @@ var ImportParams = Type7.Object({
|
|
|
34738
35108
|
}))
|
|
34739
35109
|
});
|
|
34740
35110
|
function buildImportSections(args, payload, theme) {
|
|
34741
|
-
const response =
|
|
35111
|
+
const response = asRecord3(payload);
|
|
34742
35112
|
if (!response)
|
|
34743
35113
|
return [theme.fg("muted", "No import result.")];
|
|
34744
35114
|
if (args.op === "organize") {
|
|
34745
|
-
const groups =
|
|
34746
|
-
const groupText = groups.length > 0 ? groups.map((group) => `${
|
|
35115
|
+
const groups = asRecords2(response.groups);
|
|
35116
|
+
const groupText = groups.length > 0 ? groups.map((group) => `${asString2(group.name) ?? "unknown"}: ${asNumber2(group.count) ?? 0}`).join(" · ") : "No imports found";
|
|
34747
35117
|
return [
|
|
34748
|
-
`${theme.fg("success", "organized")} ${theme.fg("accent",
|
|
35118
|
+
`${theme.fg("success", "organized")} ${theme.fg("accent", asString2(response.file) ?? args.filePath)}`,
|
|
34749
35119
|
`${theme.fg("muted", "groups")} ${groupText}`,
|
|
34750
|
-
`${theme.fg("muted", "duplicates removed")} ${
|
|
35120
|
+
`${theme.fg("muted", "duplicates removed")} ${asNumber2(response.removed_duplicates) ?? 0}`
|
|
34751
35121
|
];
|
|
34752
35122
|
}
|
|
34753
35123
|
if (args.op === "add") {
|
|
34754
|
-
const moduleName2 =
|
|
35124
|
+
const moduleName2 = asString2(response.module) ?? args.module ?? "(module)";
|
|
34755
35125
|
const status = response.already_present === true ? theme.fg("warning", "already present") : theme.fg("success", "added");
|
|
34756
35126
|
return [
|
|
34757
35127
|
`${status} ${theme.fg("accent", moduleName2)}`,
|
|
34758
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent",
|
|
34759
|
-
`${theme.fg("muted", "group")} ${
|
|
35128
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.filePath)}`,
|
|
35129
|
+
`${theme.fg("muted", "group")} ${asString2(response.group) ?? "—"}`
|
|
34760
35130
|
];
|
|
34761
35131
|
}
|
|
34762
|
-
const moduleName =
|
|
35132
|
+
const moduleName = asString2(response.module) ?? args.module ?? "(module)";
|
|
34763
35133
|
const didRemove = response.removed !== false;
|
|
34764
35134
|
const removeStatus = didRemove ? `${theme.fg("success", "removed")} ${theme.fg("accent", moduleName)}` : `${theme.fg("warning", "not present")} ${theme.fg("accent", moduleName)}`;
|
|
34765
35135
|
return [
|
|
34766
35136
|
removeStatus,
|
|
34767
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent",
|
|
35137
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.filePath)}`,
|
|
34768
35138
|
args.removeName ? `${theme.fg("muted", "name")} ${args.removeName}` : `${theme.fg("muted", "scope")} entire import`
|
|
34769
35139
|
];
|
|
34770
35140
|
}
|
|
@@ -34897,16 +35267,16 @@ function diagnosticsServerSummary(section) {
|
|
|
34897
35267
|
return parts.length > 0 ? parts.join("; ") : "none reported";
|
|
34898
35268
|
}
|
|
34899
35269
|
function diagnosticsSummaryPart(summary) {
|
|
34900
|
-
const section =
|
|
35270
|
+
const section = asRecord3(summary?.diagnostics);
|
|
34901
35271
|
if (!section)
|
|
34902
35272
|
return;
|
|
34903
|
-
const errors3 =
|
|
34904
|
-
const warnings =
|
|
34905
|
-
const info =
|
|
34906
|
-
const hints =
|
|
35273
|
+
const errors3 = asNumber2(section.errors);
|
|
35274
|
+
const warnings = asNumber2(section.warnings);
|
|
35275
|
+
const info = asNumber2(section.info);
|
|
35276
|
+
const hints = asNumber2(section.hints);
|
|
34907
35277
|
const hasCounts = [errors3, warnings, info, hints].some((value) => value !== undefined);
|
|
34908
35278
|
const counts = `${errors3 ?? 0} errors/${warnings ?? 0} warnings/${info ?? 0} info/${hints ?? 0} hints`;
|
|
34909
|
-
const status =
|
|
35279
|
+
const status = asString2(section.status);
|
|
34910
35280
|
if (status === "pending") {
|
|
34911
35281
|
return hasCounts ? `diagnostics ${counts} so far — still pending (servers: ${diagnosticsServerSummary(section)})` : `diagnostics pending (servers: ${diagnosticsServerSummary(section)})`;
|
|
34912
35282
|
}
|
|
@@ -34919,9 +35289,9 @@ function diagnosticsSummaryPart(summary) {
|
|
|
34919
35289
|
return;
|
|
34920
35290
|
}
|
|
34921
35291
|
function diagnosticLocation(diagnostic) {
|
|
34922
|
-
const file2 =
|
|
34923
|
-
const line =
|
|
34924
|
-
const column =
|
|
35292
|
+
const file2 = asString2(diagnostic.file) ?? "(unknown file)";
|
|
35293
|
+
const line = asNumber2(diagnostic.line);
|
|
35294
|
+
const column = asNumber2(diagnostic.column);
|
|
34925
35295
|
if (line === undefined)
|
|
34926
35296
|
return file2;
|
|
34927
35297
|
if (column === undefined)
|
|
@@ -34929,14 +35299,14 @@ function diagnosticLocation(diagnostic) {
|
|
|
34929
35299
|
return `${file2}:${line}:${column}`;
|
|
34930
35300
|
}
|
|
34931
35301
|
function diagnosticsDetailSection(details) {
|
|
34932
|
-
const diagnostics =
|
|
35302
|
+
const diagnostics = asRecords2(details?.diagnostics);
|
|
34933
35303
|
if (diagnostics.length === 0)
|
|
34934
35304
|
return;
|
|
34935
35305
|
const lines = ["diagnostics"];
|
|
34936
35306
|
for (const diagnostic of diagnostics) {
|
|
34937
|
-
const severity =
|
|
34938
|
-
const message =
|
|
34939
|
-
const source =
|
|
35307
|
+
const severity = asString2(diagnostic.severity) ?? "information";
|
|
35308
|
+
const message = asString2(diagnostic.message) ?? "(no message)";
|
|
35309
|
+
const source = asString2(diagnostic.source);
|
|
34940
35310
|
const suffix = source ? ` [${source}]` : "";
|
|
34941
35311
|
lines.push(`- ${diagnosticLocation(diagnostic)} ${severity} ${message}${suffix}`);
|
|
34942
35312
|
}
|
|
@@ -34944,15 +35314,15 @@ function diagnosticsDetailSection(details) {
|
|
|
34944
35314
|
`);
|
|
34945
35315
|
}
|
|
34946
35316
|
function countFrom(summary, key) {
|
|
34947
|
-
const section =
|
|
34948
|
-
return
|
|
35317
|
+
const section = asRecord3(summary?.[key]);
|
|
35318
|
+
return asNumber2(section?.count);
|
|
34949
35319
|
}
|
|
34950
35320
|
function tier2SummaryPart(summary, key, label) {
|
|
34951
|
-
const section =
|
|
34952
|
-
const count =
|
|
35321
|
+
const section = asRecord3(summary?.[key]);
|
|
35322
|
+
const count = asNumber2(section?.count);
|
|
34953
35323
|
if (count !== undefined)
|
|
34954
35324
|
return `${label} ${count}`;
|
|
34955
|
-
const status =
|
|
35325
|
+
const status = asString2(section?.status);
|
|
34956
35326
|
return `${label} ${status ?? "unavailable"}`;
|
|
34957
35327
|
}
|
|
34958
35328
|
function shortDupOccurrence(entry) {
|
|
@@ -34961,12 +35331,12 @@ function shortDupOccurrence(entry) {
|
|
|
34961
35331
|
}
|
|
34962
35332
|
function tier2TopPreview(summary, theme) {
|
|
34963
35333
|
const lines = [];
|
|
34964
|
-
const dup =
|
|
35334
|
+
const dup = asRecord3(summary?.duplicates);
|
|
34965
35335
|
const dupTop = Array.isArray(dup?.top) ? dup.top : [];
|
|
34966
35336
|
for (const group of dupTop) {
|
|
34967
|
-
const record2 =
|
|
35337
|
+
const record2 = asRecord3(group);
|
|
34968
35338
|
const files = Array.isArray(record2?.files) ? record2.files : [];
|
|
34969
|
-
const cost =
|
|
35339
|
+
const cost = asNumber2(record2?.cost);
|
|
34970
35340
|
if (files.length < 2)
|
|
34971
35341
|
continue;
|
|
34972
35342
|
const a = shortDupOccurrence(String(files[0]));
|
|
@@ -34977,12 +35347,12 @@ function tier2TopPreview(summary, theme) {
|
|
|
34977
35347
|
["dead_code", "dead"],
|
|
34978
35348
|
["unused_exports", "unused"]
|
|
34979
35349
|
]) {
|
|
34980
|
-
const section =
|
|
35350
|
+
const section = asRecord3(summary?.[key]);
|
|
34981
35351
|
const top = Array.isArray(section?.top) ? section.top : [];
|
|
34982
35352
|
for (const item of top) {
|
|
34983
|
-
const record2 =
|
|
34984
|
-
const file2 =
|
|
34985
|
-
const symbol2 =
|
|
35353
|
+
const record2 = asRecord3(item);
|
|
35354
|
+
const file2 = asString2(record2?.file);
|
|
35355
|
+
const symbol2 = asString2(record2?.symbol);
|
|
34986
35356
|
if (!file2 || !symbol2)
|
|
34987
35357
|
continue;
|
|
34988
35358
|
lines.push(` ${label} ${symbol2} (${file2.split("/").pop()})`);
|
|
@@ -34995,7 +35365,7 @@ ${lines.join(`
|
|
|
34995
35365
|
`)}`;
|
|
34996
35366
|
}
|
|
34997
35367
|
function tier2RefreshCategories(response) {
|
|
34998
|
-
const scannerState =
|
|
35368
|
+
const scannerState = asRecord3(response.scanner_state);
|
|
34999
35369
|
const categories = new Set;
|
|
35000
35370
|
for (const key of ["pending_categories", "stale_categories"]) {
|
|
35001
35371
|
const values = scannerState?.[key];
|
|
@@ -35042,18 +35412,18 @@ function runPendingTier2Categories(bridge, categories, extCtx) {
|
|
|
35042
35412
|
});
|
|
35043
35413
|
}
|
|
35044
35414
|
function buildInspectSections(payload, theme) {
|
|
35045
|
-
const response =
|
|
35415
|
+
const response = asRecord3(payload);
|
|
35046
35416
|
if (!response)
|
|
35047
35417
|
return [theme.fg("muted", "No inspect snapshot available.")];
|
|
35048
|
-
const summary =
|
|
35049
|
-
const metrics =
|
|
35050
|
-
const scannerState =
|
|
35418
|
+
const summary = asRecord3(response.summary);
|
|
35419
|
+
const metrics = asRecord3(summary?.metrics);
|
|
35420
|
+
const scannerState = asRecord3(response.scanner_state);
|
|
35051
35421
|
const stale = Array.isArray(scannerState?.stale_categories) ? scannerState.stale_categories.length : 0;
|
|
35052
35422
|
const pending = Array.isArray(scannerState?.pending_categories) ? scannerState.pending_categories.length : 0;
|
|
35053
35423
|
const parts = [
|
|
35054
35424
|
`todos ${countFrom(summary, "todos") ?? 0}`,
|
|
35055
35425
|
diagnosticsSummaryPart(summary),
|
|
35056
|
-
`metrics ${
|
|
35426
|
+
`metrics ${asNumber2(metrics?.files) ?? 0} files/${asNumber2(metrics?.symbols) ?? 0} symbols`,
|
|
35057
35427
|
tier2SummaryPart(summary, "dead_code", "dead code"),
|
|
35058
35428
|
tier2SummaryPart(summary, "unused_exports", "unused exports"),
|
|
35059
35429
|
tier2SummaryPart(summary, "duplicates", "duplicates")
|
|
@@ -35065,7 +35435,7 @@ function buildInspectSections(payload, theme) {
|
|
|
35065
35435
|
const topPreview = tier2TopPreview(summary, theme);
|
|
35066
35436
|
if (topPreview)
|
|
35067
35437
|
sections.push(topPreview);
|
|
35068
|
-
const details =
|
|
35438
|
+
const details = asRecord3(response.details);
|
|
35069
35439
|
if (details) {
|
|
35070
35440
|
const names = Object.keys(details);
|
|
35071
35441
|
sections.push(names.length > 0 ? `details: ${names.join(", ")}` : theme.fg("muted", "No drill-down details returned."));
|
|
@@ -35073,7 +35443,7 @@ function buildInspectSections(payload, theme) {
|
|
|
35073
35443
|
if (diagnosticsDetails)
|
|
35074
35444
|
sections.push(diagnosticsDetails);
|
|
35075
35445
|
}
|
|
35076
|
-
const text =
|
|
35446
|
+
const text = asString2(response.text);
|
|
35077
35447
|
if (text)
|
|
35078
35448
|
sections.push(text);
|
|
35079
35449
|
return sections;
|
|
@@ -35106,7 +35476,7 @@ function registerInspectTool(pi, ctx) {
|
|
|
35106
35476
|
runPendingTier2Categories(bridge, tier2RefreshCategories(response), extCtx);
|
|
35107
35477
|
const body = response.text;
|
|
35108
35478
|
if (typeof body === "string") {
|
|
35109
|
-
const diagnostics = diagnosticsSummaryPart(
|
|
35479
|
+
const diagnostics = diagnosticsSummaryPart(asRecord3(response.summary));
|
|
35110
35480
|
const text = diagnostics ? body ? `${body}
|
|
35111
35481
|
|
|
35112
35482
|
${diagnostics}` : diagnostics : body;
|
|
@@ -35146,138 +35516,11 @@ function navigateParamsSchema() {
|
|
|
35146
35516
|
}))
|
|
35147
35517
|
});
|
|
35148
35518
|
}
|
|
35149
|
-
function treeLine(depth, text) {
|
|
35150
|
-
return `${" ".repeat(depth)}${depth === 0 ? "" : "↳ "}${text}`;
|
|
35151
|
-
}
|
|
35152
|
-
function renderCallTreeNode(node, depth, lines) {
|
|
35153
|
-
const name = asString(node.name) ?? "(unknown)";
|
|
35154
|
-
const file2 = shortenPath2(asString(node.file) ?? "(unknown file)");
|
|
35155
|
-
const line = asNumber(node.line);
|
|
35156
|
-
lines.push(treeLine(depth, `${name} ${line !== undefined ? `[${file2}:${line}]` : `[${file2}]`}`));
|
|
35157
|
-
asRecords(node.children).forEach((child) => {
|
|
35158
|
-
renderCallTreeNode(child, depth + 1, lines);
|
|
35159
|
-
});
|
|
35160
|
-
}
|
|
35161
|
-
function depthWarning(response, theme, depthField = "depth_limited", truncatedField = "truncated") {
|
|
35162
|
-
const limited = asBoolean(response[depthField]);
|
|
35163
|
-
const truncated = asNumber(response[truncatedField]) ?? 0;
|
|
35164
|
-
if (!limited && truncated === 0)
|
|
35165
|
-
return "";
|
|
35166
|
-
const detail = truncated > 0 ? `, ${truncated} truncated` : "";
|
|
35167
|
-
return theme.fg("warning", `(depth limited${detail})`);
|
|
35168
|
-
}
|
|
35169
|
-
function renderTracePath(path3, index, lines) {
|
|
35170
|
-
lines.push(`Path ${index + 1}`);
|
|
35171
|
-
asRecords(path3.hops).forEach((hop, hopIndex) => {
|
|
35172
|
-
const symbol2 = asString(hop.symbol) ?? "(unknown)";
|
|
35173
|
-
const file2 = shortenPath2(asString(hop.file) ?? "(unknown file)");
|
|
35174
|
-
const line = asNumber(hop.line);
|
|
35175
|
-
const entry = hop.is_entry_point === true ? " [entry]" : "";
|
|
35176
|
-
lines.push(treeLine(hopIndex + 1, `${symbol2}${entry} ${line !== undefined ? `[${file2}:${line}]` : `[${file2}]`}`));
|
|
35177
|
-
});
|
|
35178
|
-
}
|
|
35179
35519
|
function buildNavigateSections(args, payload, theme) {
|
|
35180
|
-
const
|
|
35181
|
-
|
|
35182
|
-
|
|
35183
|
-
|
|
35184
|
-
const lines = [];
|
|
35185
|
-
renderCallTreeNode(response, 0, lines);
|
|
35186
|
-
const warning = depthWarning(response, theme);
|
|
35187
|
-
if (warning)
|
|
35188
|
-
lines.push(warning);
|
|
35189
|
-
return lines.length > 0 ? lines : [theme.fg("muted", "No call tree available.")];
|
|
35190
|
-
}
|
|
35191
|
-
if (args.op === "callers") {
|
|
35192
|
-
const groups = asRecords(response.callers);
|
|
35193
|
-
const warning = depthWarning(response, theme);
|
|
35194
|
-
const sections2 = [
|
|
35195
|
-
`${theme.fg("success", `${asNumber(response.total_callers) ?? 0} caller${(asNumber(response.total_callers) ?? 0) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${groups.length} file group${groups.length === 1 ? "" : "s"}`)} ${warning}`.trim()
|
|
35196
|
-
];
|
|
35197
|
-
groups.forEach((group) => {
|
|
35198
|
-
const file2 = shortenPath2(asString(group.file) ?? "(unknown file)");
|
|
35199
|
-
const lines = [theme.fg("accent", file2)];
|
|
35200
|
-
asRecords(group.callers).forEach((caller) => {
|
|
35201
|
-
lines.push(` ↳ ${asString(caller.symbol) ?? "(unknown)"} ${theme.fg("muted", `line ${asNumber(caller.line) ?? "?"}`)}`);
|
|
35202
|
-
});
|
|
35203
|
-
sections2.push(lines.join(`
|
|
35204
|
-
`));
|
|
35205
|
-
});
|
|
35206
|
-
return sections2;
|
|
35207
|
-
}
|
|
35208
|
-
if (args.op === "trace_to_symbol") {
|
|
35209
|
-
const path3 = asRecords(response.path);
|
|
35210
|
-
const complete = asBoolean(response.complete);
|
|
35211
|
-
const reason = asString(response.reason);
|
|
35212
|
-
if (path3.length === 0) {
|
|
35213
|
-
const prefix = complete === false ? theme.fg("warning", "No complete path") : theme.fg("muted", "No path");
|
|
35214
|
-
return [`${prefix}${reason ? ` (${reason})` : ""}`];
|
|
35215
|
-
}
|
|
35216
|
-
const lines = [theme.fg("success", `${path3.length} hop${path3.length === 1 ? "" : "s"}`)];
|
|
35217
|
-
path3.forEach((hop, index) => {
|
|
35218
|
-
const symbol2 = asString(hop.symbol) ?? "(unknown)";
|
|
35219
|
-
const file2 = shortenPath2(asString(hop.file) ?? "(unknown file)");
|
|
35220
|
-
const line = asNumber(hop.line);
|
|
35221
|
-
lines.push(treeLine(index + 1, `${symbol2} ${line !== undefined ? `[${file2}:${line}]` : `[${file2}]`}`));
|
|
35222
|
-
});
|
|
35223
|
-
return lines;
|
|
35224
|
-
}
|
|
35225
|
-
if (args.op === "trace_to") {
|
|
35226
|
-
const paths = asRecords(response.paths);
|
|
35227
|
-
const warning = depthWarning(response, theme, "max_depth_reached", "truncated_paths");
|
|
35228
|
-
const sections2 = [
|
|
35229
|
-
`${theme.fg("success", `${asNumber(response.total_paths) ?? paths.length} path${(asNumber(response.total_paths) ?? paths.length) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${asNumber(response.entry_points_found) ?? 0} entry point${(asNumber(response.entry_points_found) ?? 0) === 1 ? "" : "s"}`)} ${warning}`.trim()
|
|
35230
|
-
];
|
|
35231
|
-
if (paths.length === 0)
|
|
35232
|
-
sections2.push(theme.fg("muted", "No entry paths found."));
|
|
35233
|
-
paths.forEach((path3, index) => {
|
|
35234
|
-
const lines = [];
|
|
35235
|
-
renderTracePath(path3, index, lines);
|
|
35236
|
-
sections2.push(lines.join(`
|
|
35237
|
-
`));
|
|
35238
|
-
});
|
|
35239
|
-
return sections2;
|
|
35240
|
-
}
|
|
35241
|
-
if (args.op === "impact") {
|
|
35242
|
-
const callers = asRecords(response.callers);
|
|
35243
|
-
const warning = depthWarning(response, theme);
|
|
35244
|
-
const sections2 = [
|
|
35245
|
-
`${theme.fg("warning", `${asNumber(response.total_affected) ?? callers.length} affected call site${(asNumber(response.total_affected) ?? callers.length) === 1 ? "" : "s"}`)} ${theme.fg("muted", `${asNumber(response.affected_files) ?? 0} file${(asNumber(response.affected_files) ?? 0) === 1 ? "" : "s"}`)} ${warning}`.trim()
|
|
35246
|
-
];
|
|
35247
|
-
if (callers.length === 0)
|
|
35248
|
-
sections2.push(theme.fg("muted", "No impacted callers found."));
|
|
35249
|
-
callers.forEach((caller) => {
|
|
35250
|
-
const file2 = shortenPath2(asString(caller.caller_file) ?? "(unknown file)");
|
|
35251
|
-
const symbol2 = asString(caller.caller_symbol) ?? "(unknown)";
|
|
35252
|
-
const line = asNumber(caller.line) ?? 0;
|
|
35253
|
-
const entry = caller.is_entry_point === true ? ` ${theme.fg("warning", "[entry]")}` : "";
|
|
35254
|
-
const expression = asString(caller.call_expression);
|
|
35255
|
-
const params = Array.isArray(caller.parameters) ? caller.parameters.map(String).join(", ") : "";
|
|
35256
|
-
sections2.push([
|
|
35257
|
-
`${theme.fg("accent", file2)}:${line}`,
|
|
35258
|
-
` ↳ ${symbol2}${entry}`,
|
|
35259
|
-
expression ? ` ${theme.fg("muted", expression)}` : undefined,
|
|
35260
|
-
params ? ` ${theme.fg("muted", `params: ${params}`)}` : undefined
|
|
35261
|
-
].filter(Boolean).join(`
|
|
35262
|
-
`));
|
|
35263
|
-
});
|
|
35264
|
-
return sections2;
|
|
35265
|
-
}
|
|
35266
|
-
const hops = asRecords(response.hops);
|
|
35267
|
-
const sections = [
|
|
35268
|
-
`${theme.fg("success", `${hops.length} hop${hops.length === 1 ? "" : "s"}`)} ${asBoolean(response.depth_limited) ? theme.fg("warning", "(depth limited)") : ""}`.trim()
|
|
35269
|
-
];
|
|
35270
|
-
if (hops.length === 0)
|
|
35271
|
-
sections.push(theme.fg("muted", "No data-flow hops found."));
|
|
35272
|
-
hops.forEach((hop, index) => {
|
|
35273
|
-
const file2 = shortenPath2(asString(hop.file) ?? "(unknown file)");
|
|
35274
|
-
const symbol2 = asString(hop.symbol) ?? "(unknown)";
|
|
35275
|
-
const variable = asString(hop.variable) ?? "(unknown)";
|
|
35276
|
-
const line = asNumber(hop.line) ?? 0;
|
|
35277
|
-
const approximate = hop.approximate === true ? ` ${theme.fg("warning", "[approx]")}` : "";
|
|
35278
|
-
sections.push(treeLine(index, `${variable} ${theme.fg("muted", `${asString(hop.flow_type) ?? "flow"}`)} ${symbol2} [${file2}:${line}]${approximate}`));
|
|
35279
|
-
});
|
|
35280
|
-
return sections;
|
|
35520
|
+
const themeAdapter = {
|
|
35521
|
+
fg: (role, s) => theme.fg(role, s)
|
|
35522
|
+
};
|
|
35523
|
+
return formatCallgraphSections(args.op, payload, themeAdapter);
|
|
35281
35524
|
}
|
|
35282
35525
|
function renderNavigateCall(args, theme, context) {
|
|
35283
35526
|
const summary = [
|
|
@@ -35340,7 +35583,8 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35340
35583
|
req.toFile = toFile;
|
|
35341
35584
|
try {
|
|
35342
35585
|
const response = await callBridge(bridge, params.op, req, extCtx);
|
|
35343
|
-
return textResult(
|
|
35586
|
+
return textResult(formatCallgraphSections(params.op, response, PLAIN_CALLGRAPH_THEME).join(`
|
|
35587
|
+
`), response);
|
|
35344
35588
|
} catch (error50) {
|
|
35345
35589
|
if (error50 instanceof BridgeError && CALLGRAPH_SOFT_CODES.has(error50.code)) {
|
|
35346
35590
|
return textResult(error50.message);
|
|
@@ -35418,25 +35662,25 @@ function buildOutlineSections(text, theme) {
|
|
|
35418
35662
|
`)];
|
|
35419
35663
|
}
|
|
35420
35664
|
function buildZoomSections(args, payload, theme) {
|
|
35421
|
-
const batch =
|
|
35665
|
+
const batch = asRecord3(payload);
|
|
35422
35666
|
const batchItems = Array.isArray(batch?.symbols) ? batch.symbols : Array.isArray(batch?.entries) ? batch.entries : null;
|
|
35423
35667
|
if (batchItems) {
|
|
35424
35668
|
const header = batch?.complete === false ? [theme.fg("warning", "Incomplete zoom results")] : [];
|
|
35425
35669
|
return [
|
|
35426
35670
|
...header,
|
|
35427
35671
|
...batchItems.map((item) => {
|
|
35428
|
-
const record2 =
|
|
35672
|
+
const record2 = asRecord3(item);
|
|
35429
35673
|
if (!record2)
|
|
35430
35674
|
return theme.fg("muted", "No zoom result available.");
|
|
35431
|
-
const name =
|
|
35432
|
-
const itemTargetLabel =
|
|
35675
|
+
const name = asString2(record2.name) ?? "(unknown symbol)";
|
|
35676
|
+
const itemTargetLabel = asString2(record2.targetLabel) ?? zoomTargetLabel(args);
|
|
35433
35677
|
if (record2.success === false) {
|
|
35434
|
-
const location = record2.targetLabel ? ` in ${
|
|
35435
|
-
return theme.fg("error", `Symbol "${name}" not found${location}: ${
|
|
35678
|
+
const location = record2.targetLabel ? ` in ${shortenPath3(itemTargetLabel)}` : "";
|
|
35679
|
+
return theme.fg("error", `Symbol "${name}" not found${location}: ${asString2(record2.error) ?? "zoom failed"}`);
|
|
35436
35680
|
}
|
|
35437
|
-
const content =
|
|
35681
|
+
const content = asString2(record2.content);
|
|
35438
35682
|
return [
|
|
35439
|
-
`${theme.fg("accent", name)} ${theme.fg("muted",
|
|
35683
|
+
`${theme.fg("accent", name)} ${theme.fg("muted", shortenPath3(itemTargetLabel))}`,
|
|
35440
35684
|
content
|
|
35441
35685
|
].filter(Boolean).join(`
|
|
35442
35686
|
`);
|
|
@@ -35447,32 +35691,32 @@ function buildZoomSections(args, payload, theme) {
|
|
|
35447
35691
|
if (items.length === 0)
|
|
35448
35692
|
return [theme.fg("muted", "No zoom result available.")];
|
|
35449
35693
|
return items.map((item) => {
|
|
35450
|
-
const record2 =
|
|
35694
|
+
const record2 = asRecord3(item);
|
|
35451
35695
|
if (!record2)
|
|
35452
35696
|
return theme.fg("muted", "No zoom result available.");
|
|
35453
|
-
const name =
|
|
35454
|
-
const kind =
|
|
35455
|
-
const range =
|
|
35697
|
+
const name = asString2(record2.name) ?? "(unknown symbol)";
|
|
35698
|
+
const kind = asString2(record2.kind) ?? "symbol";
|
|
35699
|
+
const range = asRecord3(record2.range);
|
|
35456
35700
|
const startLine = range && typeof range.start_line === "number" ? range.start_line : undefined;
|
|
35457
35701
|
const endLine = range && typeof range.end_line === "number" ? range.end_line : undefined;
|
|
35458
35702
|
const targetLabel = zoomTargetLabel(args);
|
|
35459
|
-
const location = startLine !== undefined ? `${
|
|
35703
|
+
const location = startLine !== undefined ? `${shortenPath3(targetLabel)}:${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : shortenPath3(targetLabel);
|
|
35460
35704
|
const lines = [`${theme.fg("accent", name)} ${theme.fg("muted", `[${kind}] ${location}`)}`];
|
|
35461
|
-
const content =
|
|
35705
|
+
const content = asString2(record2.content);
|
|
35462
35706
|
if (content) {
|
|
35463
35707
|
lines.push(content.split(`
|
|
35464
35708
|
`).map((line) => ` ${line}`).join(`
|
|
35465
35709
|
`));
|
|
35466
35710
|
}
|
|
35467
|
-
const annotations =
|
|
35468
|
-
const callsOut = annotations ?
|
|
35469
|
-
const calledBy = annotations ?
|
|
35711
|
+
const annotations = asRecord3(record2.annotations);
|
|
35712
|
+
const callsOut = annotations ? asRecords2(annotations.calls_out) : [];
|
|
35713
|
+
const calledBy = annotations ? asRecords2(annotations.called_by) : [];
|
|
35470
35714
|
if (callsOut.length > 0) {
|
|
35471
|
-
lines.push(`${theme.fg("muted", "calls out")}`, callsOut.map((call) => ` ↳ ${
|
|
35715
|
+
lines.push(`${theme.fg("muted", "calls out")}`, callsOut.map((call) => ` ↳ ${asString2(call.name) ?? "(unknown)"}${typeof call.line === "number" ? `:${call.line}` : ""}`).join(`
|
|
35472
35716
|
`));
|
|
35473
35717
|
}
|
|
35474
35718
|
if (calledBy.length > 0) {
|
|
35475
|
-
lines.push(`${theme.fg("muted", "called by")}`, calledBy.map((call) => ` ↳ ${
|
|
35719
|
+
lines.push(`${theme.fg("muted", "called by")}`, calledBy.map((call) => ` ↳ ${asString2(call.name) ?? "(unknown)"}${typeof call.line === "number" ? `:${call.line}` : ""}`).join(`
|
|
35476
35720
|
`));
|
|
35477
35721
|
}
|
|
35478
35722
|
return lines.join(`
|
|
@@ -35811,32 +36055,32 @@ var RefactorParams = Type11.Object({
|
|
|
35811
36055
|
callSiteLine: optionalInt(1, Number.MAX_SAFE_INTEGER)
|
|
35812
36056
|
});
|
|
35813
36057
|
function buildRefactorSections(args, payload, theme) {
|
|
35814
|
-
const response =
|
|
36058
|
+
const response = asRecord3(payload);
|
|
35815
36059
|
if (!response)
|
|
35816
36060
|
return [theme.fg("muted", "No refactor result.")];
|
|
35817
36061
|
if (args.op === "move") {
|
|
35818
|
-
const results =
|
|
36062
|
+
const results = asRecords2(response.results);
|
|
35819
36063
|
return [
|
|
35820
36064
|
`${theme.fg("success", "moved symbol")} ${theme.fg("toolOutput", args.symbol ?? "(symbol)")}`,
|
|
35821
|
-
`${theme.fg("muted", "files modified")} ${
|
|
35822
|
-
`${theme.fg("muted", "consumers updated")} ${
|
|
35823
|
-
results.length > 0 ? results.map((entry) => ` ↳ ${
|
|
36065
|
+
`${theme.fg("muted", "files modified")} ${asNumber2(response.files_modified) ?? results.length}`,
|
|
36066
|
+
`${theme.fg("muted", "consumers updated")} ${asNumber2(response.consumers_updated) ?? 0}`,
|
|
36067
|
+
results.length > 0 ? results.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(unknown file)")}`).join(`
|
|
35824
36068
|
`) : theme.fg("muted", "No files reported.")
|
|
35825
36069
|
];
|
|
35826
36070
|
}
|
|
35827
36071
|
if (args.op === "extract") {
|
|
35828
36072
|
return [
|
|
35829
|
-
`${theme.fg("success", "extracted")} ${theme.fg("toolOutput",
|
|
35830
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent",
|
|
36073
|
+
`${theme.fg("success", "extracted")} ${theme.fg("toolOutput", asString2(response.name) ?? args.name ?? "(function)")}`,
|
|
36074
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.filePath))}`,
|
|
35831
36075
|
`${theme.fg("muted", "params")} ${Array.isArray(response.parameters) ? response.parameters.join(", ") || "none" : "none"}`,
|
|
35832
|
-
`${theme.fg("muted", "return type")} ${
|
|
36076
|
+
`${theme.fg("muted", "return type")} ${asString2(response.return_type) ?? "unknown"}`
|
|
35833
36077
|
];
|
|
35834
36078
|
}
|
|
35835
36079
|
return [
|
|
35836
|
-
`${theme.fg("success", "inlined")} ${theme.fg("toolOutput",
|
|
35837
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent",
|
|
35838
|
-
`${theme.fg("muted", "context")} ${
|
|
35839
|
-
`${theme.fg("muted", "substitutions")} ${
|
|
36080
|
+
`${theme.fg("success", "inlined")} ${theme.fg("toolOutput", asString2(response.symbol) ?? args.symbol ?? "(symbol)")}`,
|
|
36081
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.filePath))}`,
|
|
36082
|
+
`${theme.fg("muted", "context")} ${asString2(response.call_context) ?? "unknown"}`,
|
|
36083
|
+
`${theme.fg("muted", "substitutions")} ${asNumber2(response.substitutions) ?? 0}`
|
|
35840
36084
|
];
|
|
35841
36085
|
}
|
|
35842
36086
|
function renderRefactorCall(args, theme, context) {
|
|
@@ -35945,34 +36189,34 @@ var SafetyParams = Type12.Object({
|
|
|
35945
36189
|
}))
|
|
35946
36190
|
});
|
|
35947
36191
|
function buildSafetySections(args, payload, theme) {
|
|
35948
|
-
const response =
|
|
36192
|
+
const response = asRecord3(payload);
|
|
35949
36193
|
if (!response)
|
|
35950
36194
|
return [theme.fg("muted", "No safety result.")];
|
|
35951
36195
|
if (args.op === "undo") {
|
|
35952
36196
|
if (response.operation === true) {
|
|
35953
36197
|
return [
|
|
35954
|
-
`${theme.fg("success", "restored operation")} ${theme.fg("accent",
|
|
35955
|
-
`${theme.fg("muted", "files")} ${
|
|
36198
|
+
`${theme.fg("success", "restored operation")} ${theme.fg("accent", asString2(response.op_id) ?? "(operation)")}`,
|
|
36199
|
+
`${theme.fg("muted", "files")} ${asNumber2(response.restored_count) ?? asRecords2(response.restored).length}`
|
|
35956
36200
|
];
|
|
35957
36201
|
}
|
|
35958
36202
|
return [
|
|
35959
|
-
`${theme.fg("success", "restored")} ${theme.fg("accent",
|
|
35960
|
-
`${theme.fg("muted", "backup")} ${
|
|
36203
|
+
`${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath3(asString2(response.path) ?? args.filePath ?? "(file)"))}`,
|
|
36204
|
+
`${theme.fg("muted", "backup")} ${asString2(response.backup_id) ?? "—"}`
|
|
35961
36205
|
];
|
|
35962
36206
|
}
|
|
35963
36207
|
if (args.op === "history") {
|
|
35964
|
-
const entries =
|
|
36208
|
+
const entries = asRecords2(response.entries);
|
|
35965
36209
|
const sections2 = [
|
|
35966
|
-
theme.fg("accent",
|
|
36210
|
+
theme.fg("accent", shortenPath3(asString2(response.file) ?? args.filePath ?? "(file)"))
|
|
35967
36211
|
];
|
|
35968
36212
|
if (entries.length === 0) {
|
|
35969
36213
|
sections2.push(theme.fg("muted", "No history entries."));
|
|
35970
36214
|
return sections2;
|
|
35971
36215
|
}
|
|
35972
36216
|
sections2.push(entries.map((entry, index) => {
|
|
35973
|
-
const backupId =
|
|
36217
|
+
const backupId = asString2(entry.backup_id) ?? `entry-${index + 1}`;
|
|
35974
36218
|
const timestamp = formatTimestamp(entry.timestamp) ?? "unknown time";
|
|
35975
|
-
const description =
|
|
36219
|
+
const description = asString2(entry.description) ?? "";
|
|
35976
36220
|
return `${index + 1}. ${backupId} ${theme.fg("muted", timestamp)}${description ? `
|
|
35977
36221
|
${description}` : ""}`;
|
|
35978
36222
|
}).join(`
|
|
@@ -35980,22 +36224,22 @@ function buildSafetySections(args, payload, theme) {
|
|
|
35980
36224
|
return sections2;
|
|
35981
36225
|
}
|
|
35982
36226
|
if (args.op === "checkpoint") {
|
|
35983
|
-
const skipped =
|
|
36227
|
+
const skipped = asRecords2(response.skipped);
|
|
35984
36228
|
return [
|
|
35985
|
-
`${theme.fg("success", "checkpoint created")} ${theme.fg("accent",
|
|
35986
|
-
`${theme.fg("muted", "files")} ${
|
|
36229
|
+
`${theme.fg("success", "checkpoint created")} ${theme.fg("accent", asString2(response.name) ?? args.name ?? "(checkpoint)")}`,
|
|
36230
|
+
`${theme.fg("muted", "files")} ${asNumber2(response.file_count) ?? 0}`,
|
|
35987
36231
|
skipped.length > 0 ? `${theme.fg("warning", "skipped")}
|
|
35988
|
-
${skipped.map((entry) => ` ↳ ${
|
|
36232
|
+
${skipped.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(file)")}: ${asString2(entry.error) ?? "unknown error"}`).join(`
|
|
35989
36233
|
`)}` : theme.fg("muted", "No skipped files.")
|
|
35990
36234
|
];
|
|
35991
36235
|
}
|
|
35992
36236
|
if (args.op === "restore") {
|
|
35993
36237
|
return [
|
|
35994
|
-
`${theme.fg("success", "checkpoint restored")} ${theme.fg("accent",
|
|
35995
|
-
`${theme.fg("muted", "files")} ${
|
|
36238
|
+
`${theme.fg("success", "checkpoint restored")} ${theme.fg("accent", asString2(response.name) ?? args.name ?? "(checkpoint)")}`,
|
|
36239
|
+
`${theme.fg("muted", "files")} ${asNumber2(response.file_count) ?? 0}`
|
|
35996
36240
|
];
|
|
35997
36241
|
}
|
|
35998
|
-
const checkpoints =
|
|
36242
|
+
const checkpoints = asRecords2(response.checkpoints);
|
|
35999
36243
|
const sections = [
|
|
36000
36244
|
theme.fg("accent", `${checkpoints.length} checkpoint${checkpoints.length === 1 ? "" : "s"}`)
|
|
36001
36245
|
];
|
|
@@ -36004,8 +36248,8 @@ ${skipped.map((entry) => ` ↳ ${shortenPath2(asString(entry.file) ?? "(file)")
|
|
|
36004
36248
|
return sections;
|
|
36005
36249
|
}
|
|
36006
36250
|
sections.push(checkpoints.map((checkpoint, index) => {
|
|
36007
|
-
const name =
|
|
36008
|
-
const count =
|
|
36251
|
+
const name = asString2(checkpoint.name) ?? `checkpoint-${index + 1}`;
|
|
36252
|
+
const count = asNumber2(checkpoint.file_count) ?? 0;
|
|
36009
36253
|
const created = formatTimestamp(checkpoint.created_at) ?? "unknown time";
|
|
36010
36254
|
return `${index + 1}. ${name} ${theme.fg("muted", `${count} file${count === 1 ? "" : "s"} · ${created}`)}`;
|
|
36011
36255
|
}).join(`
|
|
@@ -36149,13 +36393,13 @@ var SearchParams2 = Type13.Object({
|
|
|
36149
36393
|
}))
|
|
36150
36394
|
});
|
|
36151
36395
|
function buildSemanticSections(args, payload, theme) {
|
|
36152
|
-
const response =
|
|
36396
|
+
const response = asRecord3(payload);
|
|
36153
36397
|
if (!response)
|
|
36154
36398
|
return [theme.fg("muted", "No search result.")];
|
|
36155
|
-
const status =
|
|
36156
|
-
const semanticStatus =
|
|
36157
|
-
const interpretedAs =
|
|
36158
|
-
const queryKind =
|
|
36399
|
+
const status = asString2(response.status) ?? "unknown";
|
|
36400
|
+
const semanticStatus = asString2(response.semantic_status) ?? status;
|
|
36401
|
+
const interpretedAs = asString2(response.interpreted_as) ?? "unknown";
|
|
36402
|
+
const queryKind = asString2(response.query_kind);
|
|
36159
36403
|
const sections = [
|
|
36160
36404
|
`${theme.fg(semanticStatus === "ready" ? "success" : "warning", `semantic: ${semanticStatus}`)} ${theme.fg("muted", `mode=${interpretedAs}${queryKind ? ` kind=${queryKind}` : ""} query=${JSON.stringify(args.query)} topK=${args.topK ?? 10}`)}`
|
|
36161
36405
|
];
|
|
@@ -36167,34 +36411,34 @@ function buildSemanticSections(args, payload, theme) {
|
|
|
36167
36411
|
const honestyNote = semanticHonestyNote(response, theme);
|
|
36168
36412
|
if (honestyNote)
|
|
36169
36413
|
sections.push(honestyNote);
|
|
36170
|
-
const results =
|
|
36414
|
+
const results = asRecords2(response.results);
|
|
36171
36415
|
if (status !== "ready" && results.length === 0) {
|
|
36172
|
-
sections.push(
|
|
36416
|
+
sections.push(asString2(response.text) ?? theme.fg("muted", "Semantic index is not ready."));
|
|
36173
36417
|
return sections;
|
|
36174
36418
|
}
|
|
36175
36419
|
if (results.length === 0) {
|
|
36176
36420
|
sections.push(theme.fg("muted", "No matches found."));
|
|
36177
36421
|
return sections;
|
|
36178
36422
|
}
|
|
36179
|
-
const grouped = groupByFile(results, (result) =>
|
|
36423
|
+
const grouped = groupByFile(results, (result) => asString2(result.file));
|
|
36180
36424
|
for (const [file2, fileResults] of grouped.entries()) {
|
|
36181
|
-
const lines = [theme.fg("accent",
|
|
36425
|
+
const lines = [theme.fg("accent", shortenPath3(file2))];
|
|
36182
36426
|
fileResults.forEach((result) => {
|
|
36183
|
-
if (
|
|
36184
|
-
const line =
|
|
36185
|
-
const column =
|
|
36186
|
-
const lineText =
|
|
36427
|
+
if (asString2(result.kind) === "GrepLine") {
|
|
36428
|
+
const line = asNumber2(result.line);
|
|
36429
|
+
const column = asNumber2(result.column);
|
|
36430
|
+
const lineText = asString2(result.line_text) ?? "";
|
|
36187
36431
|
const location2 = line !== undefined ? `${line}${column !== undefined ? `:${column}` : ""}` : "?";
|
|
36188
36432
|
lines.push(` ↳ ${theme.fg("muted", `line ${location2}`)} ${lineText}`);
|
|
36189
36433
|
return;
|
|
36190
36434
|
}
|
|
36191
|
-
const score =
|
|
36192
|
-
const source =
|
|
36193
|
-
const kind =
|
|
36194
|
-
const location =
|
|
36435
|
+
const score = asNumber2(result.score);
|
|
36436
|
+
const source = asString2(result.source);
|
|
36437
|
+
const kind = asString2(result.kind) ?? "symbol";
|
|
36438
|
+
const location = asString2(result.location);
|
|
36195
36439
|
if (source === "lexical") {
|
|
36196
36440
|
lines.push(` ↳ ${theme.fg("muted", `[lexical match${score !== undefined ? ` — score ${score.toFixed(3)}` : ""}]`)}`);
|
|
36197
|
-
const snippet2 =
|
|
36441
|
+
const snippet2 = asString2(result.snippet);
|
|
36198
36442
|
if (snippet2) {
|
|
36199
36443
|
lines.push(...snippet2.split(`
|
|
36200
36444
|
`).map((line) => ` ${line}`));
|
|
@@ -36202,16 +36446,16 @@ function buildSemanticSections(args, payload, theme) {
|
|
|
36202
36446
|
return;
|
|
36203
36447
|
}
|
|
36204
36448
|
if (kind === "file_summary" || location === "[file summary]") {
|
|
36205
|
-
const summary =
|
|
36449
|
+
const summary = asString2(result.snippet) ?? asString2(result.name) ?? "(no summary)";
|
|
36206
36450
|
lines.push(` ↳ ${summary} ${theme.fg("muted", `[file summary${score !== undefined ? ` score ${score.toFixed(3)}` : ""}]`)}`);
|
|
36207
36451
|
return;
|
|
36208
36452
|
}
|
|
36209
|
-
const startLine =
|
|
36210
|
-
const endLine =
|
|
36453
|
+
const startLine = asNumber2(result.start_line);
|
|
36454
|
+
const endLine = asNumber2(result.end_line);
|
|
36211
36455
|
const range = startLine !== undefined ? `${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : "?";
|
|
36212
|
-
const name =
|
|
36456
|
+
const name = asString2(result.name) ?? "(unknown)";
|
|
36213
36457
|
lines.push(` ↳ ${name} ${theme.fg("muted", `[${kind}] lines ${range}${score !== undefined ? ` score ${score.toFixed(3)}` : ""}`)}`);
|
|
36214
|
-
const snippet =
|
|
36458
|
+
const snippet = asString2(result.snippet);
|
|
36215
36459
|
if (snippet) {
|
|
36216
36460
|
lines.push(...snippet.split(`
|
|
36217
36461
|
`).map((line) => ` ${line}`));
|
|
@@ -36323,11 +36567,11 @@ function buildWorkflowHints(opts) {
|
|
|
36323
36567
|
}
|
|
36324
36568
|
if (hasBgBash) {
|
|
36325
36569
|
sections.push([
|
|
36326
|
-
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`task_id\` immediately
|
|
36327
|
-
"1.
|
|
36328
|
-
"2.
|
|
36329
|
-
|
|
36330
|
-
|
|
36570
|
+
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`task_id\` immediately.`,
|
|
36571
|
+
"1. Nothing else useful to do (a build/test/validation whose result is the next thing you need) → sync `bash_watch` to block until it exits (pass a longer timeout_ms for long commands; the user can interrupt).",
|
|
36572
|
+
"2. Useful parallel work available → end your turn; the completion reminder delivers the result. (Or spawn a subagent for the side work.)",
|
|
36573
|
+
"3. Want to react to a specific early output line → async `bash_watch` (background:true + pattern).",
|
|
36574
|
+
"Never loop `bash_status` to wait — it's a one-shot inspector, not a wait primitive."
|
|
36331
36575
|
].join(`
|
|
36332
36576
|
`));
|
|
36333
36577
|
sections.push(`**PTY / interactive commands**: PTY mode is for interactive REPLs and terminal apps (python, node, bash itself, vim). Start with \`${bashName}({ command: "python", pty: true, background: true })\`, read the screen with \`bash_status({ task_id, output_mode: "screen" })\`, and send input with \`bash_write({ task_id, input: "..." })\`.`);
|
|
@@ -36442,7 +36686,7 @@ function isConfigureWarning(value) {
|
|
|
36442
36686
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
36443
36687
|
return false;
|
|
36444
36688
|
const warning = value;
|
|
36445
|
-
return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing") && typeof warning.hint === "string";
|
|
36689
|
+
return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing" || warning.kind === "config_parse_failed") && typeof warning.hint === "string";
|
|
36446
36690
|
}
|
|
36447
36691
|
function coerceConfigureWarnings(warnings) {
|
|
36448
36692
|
return warnings.filter(isConfigureWarning);
|
|
@@ -36452,6 +36696,18 @@ function drainPendingEagerWarnings(projectRoot) {
|
|
|
36452
36696
|
pendingEagerWarnings.delete(projectRoot);
|
|
36453
36697
|
return pending;
|
|
36454
36698
|
}
|
|
36699
|
+
function enqueueConfigParseWarnings(projectRoot, errors3) {
|
|
36700
|
+
if (!projectRoot || errors3.length === 0)
|
|
36701
|
+
return;
|
|
36702
|
+
const pending = pendingEagerWarnings.get(projectRoot) ?? [];
|
|
36703
|
+
for (const entry of errors3) {
|
|
36704
|
+
const hint = formatConfigParseFailureMessage(entry.path, entry.message);
|
|
36705
|
+
if (!pending.some((item) => item.kind === "config_parse_failed" && item.hint === hint)) {
|
|
36706
|
+
pending.push({ kind: "config_parse_failed", hint });
|
|
36707
|
+
}
|
|
36708
|
+
}
|
|
36709
|
+
pendingEagerWarnings.set(projectRoot, pending);
|
|
36710
|
+
}
|
|
36455
36711
|
function shouldPrepareOnnxRuntime(config2) {
|
|
36456
36712
|
const isFastembedSemanticBackend = (config2.semantic?.backend ?? "fastembed") === "fastembed";
|
|
36457
36713
|
return config2.semantic_search === true && isFastembedSemanticBackend;
|
|
@@ -36560,6 +36816,7 @@ async function src_default(pi) {
|
|
|
36560
36816
|
}
|
|
36561
36817
|
await ensureStorageMigrated({ harness: "pi", binaryPath, logger: bridgeLogger });
|
|
36562
36818
|
const config2 = loadAftConfig(process.cwd());
|
|
36819
|
+
enqueueConfigParseWarnings(process.cwd(), getConfigLoadErrors());
|
|
36563
36820
|
const storageDir = resolveCortexKitStorageRoot();
|
|
36564
36821
|
let onnxRuntimePromise = null;
|
|
36565
36822
|
if (shouldPrepareOnnxRuntime(config2)) {
|
|
@@ -36568,30 +36825,7 @@ async function src_default(pi) {
|
|
|
36568
36825
|
return null;
|
|
36569
36826
|
});
|
|
36570
36827
|
}
|
|
36571
|
-
const configOverrides =
|
|
36572
|
-
if (config2.format_on_edit !== undefined)
|
|
36573
|
-
configOverrides.format_on_edit = config2.format_on_edit;
|
|
36574
|
-
if (config2.formatter_timeout_secs !== undefined)
|
|
36575
|
-
configOverrides.formatter_timeout_secs = config2.formatter_timeout_secs;
|
|
36576
|
-
if (config2.validate_on_edit !== undefined)
|
|
36577
|
-
configOverrides.validate_on_edit = config2.validate_on_edit;
|
|
36578
|
-
if (config2.formatter !== undefined)
|
|
36579
|
-
configOverrides.formatter = config2.formatter;
|
|
36580
|
-
if (config2.checker !== undefined)
|
|
36581
|
-
configOverrides.checker = config2.checker;
|
|
36582
|
-
configOverrides.restrict_to_project_root = config2.restrict_to_project_root ?? false;
|
|
36583
|
-
if (config2.search_index !== undefined)
|
|
36584
|
-
configOverrides.search_index = config2.search_index;
|
|
36585
|
-
if (config2.semantic_search !== undefined)
|
|
36586
|
-
configOverrides.semantic_search = config2.semantic_search;
|
|
36587
|
-
Object.assign(configOverrides, resolveExperimentalConfigForConfigure(config2));
|
|
36588
|
-
Object.assign(configOverrides, resolveLspConfigForConfigure(config2));
|
|
36589
|
-
if (config2.semantic !== undefined)
|
|
36590
|
-
configOverrides.semantic = config2.semantic;
|
|
36591
|
-
if (config2.inspect !== undefined)
|
|
36592
|
-
configOverrides.inspect = config2.inspect;
|
|
36593
|
-
if (config2.max_callgraph_files !== undefined)
|
|
36594
|
-
configOverrides.max_callgraph_files = config2.max_callgraph_files;
|
|
36828
|
+
const configOverrides = resolveProjectOverridesForConfigure(config2);
|
|
36595
36829
|
if (config2.url_fetch_allow_private !== undefined)
|
|
36596
36830
|
configOverrides.url_fetch_allow_private = config2.url_fetch_allow_private;
|
|
36597
36831
|
configOverrides.storage_dir = storageDir;
|
|
@@ -36658,6 +36892,7 @@ ${lines}
|
|
|
36658
36892
|
}
|
|
36659
36893
|
let pool;
|
|
36660
36894
|
const poolOptions = {
|
|
36895
|
+
...resolveBridgePoolTransportOptions(config2),
|
|
36661
36896
|
errorPrefix: "[aft-pi]",
|
|
36662
36897
|
minVersion: PLUGIN_VERSION,
|
|
36663
36898
|
onVersionMismatch: createVersionMismatchHandler(() => pool),
|
|
@@ -36798,12 +37033,25 @@ ${lines}
|
|
|
36798
37033
|
return { content, details: event.details, isError: event.isError };
|
|
36799
37034
|
});
|
|
36800
37035
|
pi.on("turn_end", async (_event, extCtx) => {
|
|
37036
|
+
const sessionID = resolveSessionId(extCtx);
|
|
36801
37037
|
await handleTurnEndBgCompletions({
|
|
36802
37038
|
ctx,
|
|
36803
37039
|
directory: extCtx.cwd,
|
|
36804
|
-
sessionID
|
|
37040
|
+
sessionID,
|
|
36805
37041
|
runtime: pi
|
|
36806
37042
|
});
|
|
37043
|
+
const bridge = pool.getActiveBridgeForRoot(extCtx.cwd);
|
|
37044
|
+
if (bridge && sessionID) {
|
|
37045
|
+
handleConfigureWarningsForSession({
|
|
37046
|
+
projectRoot: extCtx.cwd,
|
|
37047
|
+
sessionId: sessionID,
|
|
37048
|
+
client: pi,
|
|
37049
|
+
bridge,
|
|
37050
|
+
warnings: [],
|
|
37051
|
+
storageDir,
|
|
37052
|
+
pluginVersion: PLUGIN_VERSION
|
|
37053
|
+
});
|
|
37054
|
+
}
|
|
36807
37055
|
});
|
|
36808
37056
|
pi.on("input", (_event, extCtx) => {
|
|
36809
37057
|
signalSyncWatchAbort(resolveSessionId(extCtx));
|
|
@@ -36832,6 +37080,7 @@ ${lines}
|
|
|
36832
37080
|
log2(`AFT extension ready (surface=${config2.tool_surface ?? "recommended"})`);
|
|
36833
37081
|
}
|
|
36834
37082
|
var __test__2 = {
|
|
37083
|
+
enqueueConfigParseWarnings,
|
|
36835
37084
|
bridgeDirectoryFromCallback,
|
|
36836
37085
|
resolveToolSurface,
|
|
36837
37086
|
handleConfigureWarningsForSession,
|