@cortexkit/aft-pi 0.39.2 → 0.39.4
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 +21 -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 +639 -444
- package/dist/notifications.d.ts +5 -1
- package/dist/notifications.d.ts.map +1 -1
- package/dist/tools/ast.d.ts.map +1 -1
- package/dist/tools/bash.d.ts +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts +11 -5
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/inspect.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
|
@@ -12177,6 +12177,21 @@ class BridgeReplacedDuringVersionCheck extends Error {
|
|
|
12177
12177
|
}
|
|
12178
12178
|
}
|
|
12179
12179
|
|
|
12180
|
+
class BridgeTransportTimeoutError extends Error {
|
|
12181
|
+
command;
|
|
12182
|
+
timeoutMs;
|
|
12183
|
+
code = "transport_timeout";
|
|
12184
|
+
constructor(command, timeoutMs, message) {
|
|
12185
|
+
super(message);
|
|
12186
|
+
this.command = command;
|
|
12187
|
+
this.timeoutMs = timeoutMs;
|
|
12188
|
+
this.name = "BridgeTransportTimeoutError";
|
|
12189
|
+
}
|
|
12190
|
+
}
|
|
12191
|
+
function isBridgeTransportTimeout(err) {
|
|
12192
|
+
return err instanceof Error && err.code === "transport_timeout";
|
|
12193
|
+
}
|
|
12194
|
+
|
|
12180
12195
|
class BinaryBridge {
|
|
12181
12196
|
static RESTART_RESET_MS = 5 * 60 * 1000;
|
|
12182
12197
|
static STDERR_TAIL_MAX = 20;
|
|
@@ -12404,7 +12419,7 @@ class BinaryBridge {
|
|
|
12404
12419
|
} else {
|
|
12405
12420
|
this.warnVia(timeoutMsg2);
|
|
12406
12421
|
}
|
|
12407
|
-
entry.reject(new
|
|
12422
|
+
entry.reject(new BridgeTransportTimeoutError(command, effectiveTimeoutMs, `${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
12408
12423
|
return;
|
|
12409
12424
|
}
|
|
12410
12425
|
const childActiveSinceRequest = this.lastChildActivityAt > requestSentAt;
|
|
@@ -12940,6 +12955,220 @@ class BinaryBridge {
|
|
|
12940
12955
|
}
|
|
12941
12956
|
}
|
|
12942
12957
|
}
|
|
12958
|
+
// ../aft-bridge/dist/callgraph-format.js
|
|
12959
|
+
import { homedir as homedir3 } from "node:os";
|
|
12960
|
+
var PLAIN_CALLGRAPH_THEME = {
|
|
12961
|
+
fg: (_role, text) => text
|
|
12962
|
+
};
|
|
12963
|
+
function asRecord(value) {
|
|
12964
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
12965
|
+
return;
|
|
12966
|
+
return value;
|
|
12967
|
+
}
|
|
12968
|
+
function asRecords(value) {
|
|
12969
|
+
return Array.isArray(value) ? value.map(asRecord).filter(Boolean) : [];
|
|
12970
|
+
}
|
|
12971
|
+
function asString(value) {
|
|
12972
|
+
return typeof value === "string" ? value : undefined;
|
|
12973
|
+
}
|
|
12974
|
+
function asNumber(value) {
|
|
12975
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
12976
|
+
}
|
|
12977
|
+
function asBoolean(value) {
|
|
12978
|
+
return typeof value === "boolean" ? value : undefined;
|
|
12979
|
+
}
|
|
12980
|
+
function shortenPath(path2) {
|
|
12981
|
+
const home = homedir3();
|
|
12982
|
+
if (path2.startsWith(home))
|
|
12983
|
+
return `~${path2.slice(home.length)}`;
|
|
12984
|
+
return path2;
|
|
12985
|
+
}
|
|
12986
|
+
function joinNonEmpty(parts, separator = " · ") {
|
|
12987
|
+
return parts.filter((part) => Boolean(part && part.length > 0)).join(separator);
|
|
12988
|
+
}
|
|
12989
|
+
function treeLine(depth, text) {
|
|
12990
|
+
return `${" ".repeat(depth)}${depth === 0 ? "" : "↳ "}${text}`;
|
|
12991
|
+
}
|
|
12992
|
+
function nameMatchEdgeMarker(record, theme) {
|
|
12993
|
+
return asString(record.resolved_by) === "name_match" ? ` ${theme.fg("warning", "~")}` : "";
|
|
12994
|
+
}
|
|
12995
|
+
function renderCallTreeNode(node, depth, lines, theme) {
|
|
12996
|
+
const name = asString(node.name) ?? "(unknown)";
|
|
12997
|
+
const file = shortenPath(asString(node.file) ?? "(unknown file)");
|
|
12998
|
+
const line = asNumber(node.line);
|
|
12999
|
+
const unresolved = node.resolved === false ? ` ${theme.fg("warning", "[unresolved]")}` : "";
|
|
13000
|
+
const nameMatch = nameMatchEdgeMarker(node, theme);
|
|
13001
|
+
const location = line !== undefined ? `[${file}:${line}]` : `[${file}]`;
|
|
13002
|
+
lines.push(treeLine(depth, `${name} ${location}${unresolved}${nameMatch}`));
|
|
13003
|
+
asRecords(node.children).forEach((child) => {
|
|
13004
|
+
renderCallTreeNode(child, depth + 1, lines, theme);
|
|
13005
|
+
});
|
|
13006
|
+
}
|
|
13007
|
+
function depthWarning(response, theme, depthField = "depth_limited", truncatedField = "truncated") {
|
|
13008
|
+
const limited = asBoolean(response[depthField]);
|
|
13009
|
+
const truncated = asNumber(response[truncatedField]) ?? 0;
|
|
13010
|
+
if (!limited && truncated === 0)
|
|
13011
|
+
return "";
|
|
13012
|
+
const detail = truncated > 0 ? `, ${truncated} truncated` : "";
|
|
13013
|
+
return theme.fg("warning", `(depth limited${detail})`);
|
|
13014
|
+
}
|
|
13015
|
+
function renderTracePath(path2, index, lines, theme) {
|
|
13016
|
+
lines.push(`Path ${index + 1}`);
|
|
13017
|
+
asRecords(path2.hops).forEach((hop, hopIndex) => {
|
|
13018
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
13019
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
13020
|
+
const line = asNumber(hop.line);
|
|
13021
|
+
const entry = hop.is_entry_point === true ? " [entry]" : "";
|
|
13022
|
+
const nameMatch = nameMatchEdgeMarker(hop, theme);
|
|
13023
|
+
lines.push(treeLine(hopIndex + 1, `${symbol}${entry} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}${nameMatch}`));
|
|
13024
|
+
});
|
|
13025
|
+
}
|
|
13026
|
+
function renderCallersGroupLines(group, theme) {
|
|
13027
|
+
const file = shortenPath(asString(group.file) ?? "(unknown file)");
|
|
13028
|
+
const lines = [theme.fg("accent", file)];
|
|
13029
|
+
const callers = asRecords(group.callers);
|
|
13030
|
+
const bySymbolProvenance = new Map;
|
|
13031
|
+
for (const caller of callers) {
|
|
13032
|
+
const symbol = asString(caller.symbol) ?? "(unknown)";
|
|
13033
|
+
const provenanceKey = asString(caller.resolved_by) === "name_match" ? `${symbol}\x00name_match` : `${symbol}\x00exact`;
|
|
13034
|
+
const line = asNumber(caller.line);
|
|
13035
|
+
const bucket = bySymbolProvenance.get(provenanceKey) ?? [];
|
|
13036
|
+
if (line !== undefined)
|
|
13037
|
+
bucket.push(line);
|
|
13038
|
+
bySymbolProvenance.set(provenanceKey, bucket);
|
|
13039
|
+
}
|
|
13040
|
+
const keys = [...bySymbolProvenance.keys()].sort((a, b) => a.localeCompare(b));
|
|
13041
|
+
for (const key of keys) {
|
|
13042
|
+
const symbol = key.split("\x00")[0] ?? "(unknown)";
|
|
13043
|
+
const isNameMatch = key.endsWith("\x00name_match");
|
|
13044
|
+
const lineNums = (bySymbolProvenance.get(key) ?? []).sort((a, b) => a - b);
|
|
13045
|
+
const linePart = lineNums.length > 0 ? lineNums.map(String).join(", ") : "?";
|
|
13046
|
+
const marker = isNameMatch ? ` ${theme.fg("warning", "~")}` : "";
|
|
13047
|
+
lines.push(` ↳ ${symbol}:${linePart}${marker}`);
|
|
13048
|
+
}
|
|
13049
|
+
return lines;
|
|
13050
|
+
}
|
|
13051
|
+
function formatCallgraphSections(op, response, theme = PLAIN_CALLGRAPH_THEME) {
|
|
13052
|
+
const record = asRecord(response);
|
|
13053
|
+
if (!record)
|
|
13054
|
+
return [theme.fg("muted", "No navigation result.")];
|
|
13055
|
+
if (op === "call_tree") {
|
|
13056
|
+
const lines = [];
|
|
13057
|
+
renderCallTreeNode(record, 0, lines, theme);
|
|
13058
|
+
const warning = depthWarning(record, theme);
|
|
13059
|
+
if (warning)
|
|
13060
|
+
lines.push(warning);
|
|
13061
|
+
return lines.length > 0 ? lines : [theme.fg("muted", "No call tree available.")];
|
|
13062
|
+
}
|
|
13063
|
+
if (op === "callers") {
|
|
13064
|
+
const groups = asRecords(record.callers);
|
|
13065
|
+
const warning = depthWarning(record, theme);
|
|
13066
|
+
const total = asNumber(record.total_callers) ?? 0;
|
|
13067
|
+
const sections2 = [
|
|
13068
|
+
joinNonEmpty([
|
|
13069
|
+
theme.fg("success", `${total} caller${total === 1 ? "" : "s"}`),
|
|
13070
|
+
theme.fg("muted", `${groups.length} file group${groups.length === 1 ? "" : "s"}`),
|
|
13071
|
+
warning
|
|
13072
|
+
])
|
|
13073
|
+
];
|
|
13074
|
+
groups.forEach((group) => {
|
|
13075
|
+
sections2.push(renderCallersGroupLines(group, theme).join(`
|
|
13076
|
+
`));
|
|
13077
|
+
});
|
|
13078
|
+
return sections2;
|
|
13079
|
+
}
|
|
13080
|
+
if (op === "trace_to_symbol") {
|
|
13081
|
+
const path2 = asRecords(record.path);
|
|
13082
|
+
const complete = asBoolean(record.complete);
|
|
13083
|
+
const reason = asString(record.reason);
|
|
13084
|
+
if (path2.length === 0) {
|
|
13085
|
+
const prefix = complete === false ? theme.fg("warning", "No complete path") : theme.fg("muted", "No path");
|
|
13086
|
+
return [`${prefix}${reason ? ` (${reason})` : ""}`];
|
|
13087
|
+
}
|
|
13088
|
+
const lines = [theme.fg("success", `${path2.length} hop${path2.length === 1 ? "" : "s"}`)];
|
|
13089
|
+
path2.forEach((hop, index) => {
|
|
13090
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
13091
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
13092
|
+
const line = asNumber(hop.line);
|
|
13093
|
+
const nameMatch = nameMatchEdgeMarker(hop, theme);
|
|
13094
|
+
lines.push(treeLine(index + 1, `${symbol} ${line !== undefined ? `[${file}:${line}]` : `[${file}]`}${nameMatch}`));
|
|
13095
|
+
});
|
|
13096
|
+
return lines;
|
|
13097
|
+
}
|
|
13098
|
+
if (op === "trace_to") {
|
|
13099
|
+
const paths = asRecords(record.paths);
|
|
13100
|
+
const warning = depthWarning(record, theme, "max_depth_reached", "truncated_paths");
|
|
13101
|
+
const totalPaths = asNumber(record.total_paths) ?? paths.length;
|
|
13102
|
+
const entryPoints = asNumber(record.entry_points_found) ?? 0;
|
|
13103
|
+
const sections2 = [
|
|
13104
|
+
joinNonEmpty([
|
|
13105
|
+
theme.fg("success", `${totalPaths} path${totalPaths === 1 ? "" : "s"}`),
|
|
13106
|
+
theme.fg("muted", `${entryPoints} entry point${entryPoints === 1 ? "" : "s"}`),
|
|
13107
|
+
warning
|
|
13108
|
+
])
|
|
13109
|
+
];
|
|
13110
|
+
if (paths.length === 0)
|
|
13111
|
+
sections2.push(theme.fg("muted", "No entry paths found."));
|
|
13112
|
+
paths.forEach((path2, index) => {
|
|
13113
|
+
const lines = [];
|
|
13114
|
+
renderTracePath(path2, index, lines, theme);
|
|
13115
|
+
sections2.push(lines.join(`
|
|
13116
|
+
`));
|
|
13117
|
+
});
|
|
13118
|
+
return sections2;
|
|
13119
|
+
}
|
|
13120
|
+
if (op === "impact") {
|
|
13121
|
+
const callers = asRecords(record.callers);
|
|
13122
|
+
const warning = depthWarning(record, theme);
|
|
13123
|
+
const totalAffected = asNumber(record.total_affected) ?? callers.length;
|
|
13124
|
+
const affectedFiles = asNumber(record.affected_files) ?? 0;
|
|
13125
|
+
const sections2 = [
|
|
13126
|
+
joinNonEmpty([
|
|
13127
|
+
theme.fg("warning", `${totalAffected} affected call site${totalAffected === 1 ? "" : "s"}`),
|
|
13128
|
+
theme.fg("muted", `${affectedFiles} file${affectedFiles === 1 ? "" : "s"}`),
|
|
13129
|
+
warning
|
|
13130
|
+
])
|
|
13131
|
+
];
|
|
13132
|
+
if (callers.length === 0)
|
|
13133
|
+
sections2.push(theme.fg("muted", "No impacted callers found."));
|
|
13134
|
+
callers.forEach((caller) => {
|
|
13135
|
+
const file = shortenPath(asString(caller.caller_file) ?? "(unknown file)");
|
|
13136
|
+
const symbol = asString(caller.caller_symbol) ?? "(unknown)";
|
|
13137
|
+
const line = asNumber(caller.line) ?? 0;
|
|
13138
|
+
const entry = caller.is_entry_point === true ? ` ${theme.fg("warning", "[entry]")}` : "";
|
|
13139
|
+
const nameMatch = nameMatchEdgeMarker(caller, theme);
|
|
13140
|
+
const expression = asString(caller.call_expression);
|
|
13141
|
+
const params = Array.isArray(caller.parameters) ? caller.parameters.map(String).join(", ") : "";
|
|
13142
|
+
sections2.push([
|
|
13143
|
+
`${theme.fg("accent", file)}:${line}`,
|
|
13144
|
+
` ↳ ${symbol}${entry}${nameMatch}`,
|
|
13145
|
+
expression ? ` ${theme.fg("muted", expression)}` : undefined,
|
|
13146
|
+
params ? ` ${theme.fg("muted", `params: ${params}`)}` : undefined
|
|
13147
|
+
].filter(Boolean).join(`
|
|
13148
|
+
`));
|
|
13149
|
+
});
|
|
13150
|
+
return sections2;
|
|
13151
|
+
}
|
|
13152
|
+
const hops = asRecords(record.hops);
|
|
13153
|
+
const sections = [
|
|
13154
|
+
joinNonEmpty([
|
|
13155
|
+
theme.fg("success", `${hops.length} hop${hops.length === 1 ? "" : "s"}`),
|
|
13156
|
+
asBoolean(record.depth_limited) ? theme.fg("warning", "(depth limited)") : undefined
|
|
13157
|
+
])
|
|
13158
|
+
];
|
|
13159
|
+
if (hops.length === 0)
|
|
13160
|
+
sections.push(theme.fg("muted", "No data-flow hops found."));
|
|
13161
|
+
hops.forEach((hop, index) => {
|
|
13162
|
+
const file = shortenPath(asString(hop.file) ?? "(unknown file)");
|
|
13163
|
+
const symbol = asString(hop.symbol) ?? "(unknown)";
|
|
13164
|
+
const variable = asString(hop.variable) ?? "(unknown)";
|
|
13165
|
+
const line = asNumber(hop.line) ?? 0;
|
|
13166
|
+
const approximate = hop.approximate === true ? ` ${theme.fg("warning", "[approx]")}` : "";
|
|
13167
|
+
const nameMatch = nameMatchEdgeMarker(hop, theme);
|
|
13168
|
+
sections.push(treeLine(index, `${variable} ${theme.fg("muted", `${asString(hop.flow_type) ?? "flow"}`)} ${symbol} [${file}:${line}]${approximate}${nameMatch}`));
|
|
13169
|
+
});
|
|
13170
|
+
return sections;
|
|
13171
|
+
}
|
|
12943
13172
|
// ../aft-bridge/dist/coerce.js
|
|
12944
13173
|
function coerceStringArray(value) {
|
|
12945
13174
|
if (Array.isArray(value)) {
|
|
@@ -12990,7 +13219,7 @@ function isEmptyParam(value) {
|
|
|
12990
13219
|
import { spawnSync } from "node:child_process";
|
|
12991
13220
|
import { createHash, randomUUID } from "node:crypto";
|
|
12992
13221
|
import { chmodSync, closeSync, copyFileSync, createWriteStream, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
12993
|
-
import { homedir as
|
|
13222
|
+
import { homedir as homedir4 } from "node:os";
|
|
12994
13223
|
import { join as join3 } from "node:path";
|
|
12995
13224
|
import { Readable } from "node:stream";
|
|
12996
13225
|
import { pipeline } from "node:stream/promises";
|
|
@@ -13048,10 +13277,10 @@ function isExpectedCachedBinary(binaryPath, tag) {
|
|
|
13048
13277
|
function getCacheDir() {
|
|
13049
13278
|
if (process.platform === "win32") {
|
|
13050
13279
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
13051
|
-
const base2 = localAppData || join3(
|
|
13280
|
+
const base2 = localAppData || join3(homedir4(), "AppData", "Local");
|
|
13052
13281
|
return join3(base2, "aft", "bin");
|
|
13053
13282
|
}
|
|
13054
|
-
const base = process.env.XDG_CACHE_HOME || join3(
|
|
13283
|
+
const base = process.env.XDG_CACHE_HOME || join3(homedir4(), ".cache");
|
|
13055
13284
|
return join3(base, "aft", "bin");
|
|
13056
13285
|
}
|
|
13057
13286
|
function getBinaryName() {
|
|
@@ -13293,7 +13522,7 @@ function formatEditSummary(data) {
|
|
|
13293
13522
|
if (data.created === true) {
|
|
13294
13523
|
let s2 = `Created file (${counts}).`;
|
|
13295
13524
|
if (data.formatted)
|
|
13296
|
-
s2 +=
|
|
13525
|
+
s2 += formatAutoFormattedSuffix(data);
|
|
13297
13526
|
return s2;
|
|
13298
13527
|
}
|
|
13299
13528
|
let detail = counts;
|
|
@@ -13304,9 +13533,21 @@ function formatEditSummary(data) {
|
|
|
13304
13533
|
}
|
|
13305
13534
|
let s = `Edited (${detail}).`;
|
|
13306
13535
|
if (data.formatted)
|
|
13307
|
-
s +=
|
|
13536
|
+
s += formatAutoFormattedSuffix(data);
|
|
13308
13537
|
return s;
|
|
13309
13538
|
}
|
|
13539
|
+
function formatAutoFormattedSuffix(data) {
|
|
13540
|
+
const reflowText = data.reformatted?.text;
|
|
13541
|
+
if (typeof reflowText === "string" && reflowText.length > 0) {
|
|
13542
|
+
return `
|
|
13543
|
+
Auto-formatted — the formatter reflowed your edit. On disk now:
|
|
13544
|
+
${reflowText}`;
|
|
13545
|
+
}
|
|
13546
|
+
if (data.reformatted?.extensive === true) {
|
|
13547
|
+
return " Auto-formatted — extensive reflow; re-read the file before your next anchored edit.";
|
|
13548
|
+
}
|
|
13549
|
+
return " Auto-formatted.";
|
|
13550
|
+
}
|
|
13310
13551
|
// ../aft-bridge/dist/format.js
|
|
13311
13552
|
function compressionSavingsPercent(original, compressed) {
|
|
13312
13553
|
if (original <= 0)
|
|
@@ -13331,7 +13572,7 @@ function stripJsoncSymbols(value) {
|
|
|
13331
13572
|
// ../aft-bridge/dist/migration.js
|
|
13332
13573
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
13333
13574
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
13334
|
-
import { homedir as
|
|
13575
|
+
import { homedir as homedir6, tmpdir } from "node:os";
|
|
13335
13576
|
import { dirname as dirname2, join as join6 } from "node:path";
|
|
13336
13577
|
|
|
13337
13578
|
// ../aft-bridge/dist/paths.js
|
|
@@ -13388,7 +13629,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
13388
13629
|
import { execSync } from "node:child_process";
|
|
13389
13630
|
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";
|
|
13390
13631
|
import { createRequire as createRequire2 } from "node:module";
|
|
13391
|
-
import { homedir as
|
|
13632
|
+
import { homedir as homedir5 } from "node:os";
|
|
13392
13633
|
import { join as join5 } from "node:path";
|
|
13393
13634
|
var ensureBinaryForResolver = ensureBinary;
|
|
13394
13635
|
function copyToVersionedCache(npmBinaryPath, knownVersion) {
|
|
@@ -13430,7 +13671,7 @@ function normalizeBareVersion(version) {
|
|
|
13430
13671
|
return version.startsWith("v") ? version.slice(1) : version;
|
|
13431
13672
|
}
|
|
13432
13673
|
function homeDirFromEnv(env) {
|
|
13433
|
-
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) ||
|
|
13674
|
+
return (process.platform === "win32" ? env.USERPROFILE || env.HOME : env.HOME) || homedir5();
|
|
13434
13675
|
}
|
|
13435
13676
|
function cacheDirFromEnv(env) {
|
|
13436
13677
|
if (process.platform === "win32") {
|
|
@@ -13612,8 +13853,8 @@ function dataHome() {
|
|
|
13612
13853
|
}
|
|
13613
13854
|
function homeDir() {
|
|
13614
13855
|
if (process.platform === "win32")
|
|
13615
|
-
return process.env.USERPROFILE || process.env.HOME ||
|
|
13616
|
-
return process.env.HOME ||
|
|
13856
|
+
return process.env.USERPROFILE || process.env.HOME || homedir6();
|
|
13857
|
+
return process.env.HOME || homedir6();
|
|
13617
13858
|
}
|
|
13618
13859
|
function resolveLegacyStorageRoot(harness) {
|
|
13619
13860
|
if (harness === "pi")
|
|
@@ -13703,13 +13944,13 @@ async function ensureStorageMigrated(opts) {
|
|
|
13703
13944
|
}
|
|
13704
13945
|
// ../aft-bridge/dist/npm-resolver.js
|
|
13705
13946
|
import { readdirSync, statSync as statSync2 } from "node:fs";
|
|
13706
|
-
import { homedir as
|
|
13947
|
+
import { homedir as homedir7 } from "node:os";
|
|
13707
13948
|
import { delimiter, dirname as dirname3, isAbsolute as isAbsolute2, join as join7 } from "node:path";
|
|
13708
13949
|
function defaultDeps() {
|
|
13709
13950
|
return {
|
|
13710
13951
|
platform: process.platform,
|
|
13711
13952
|
env: process.env,
|
|
13712
|
-
home:
|
|
13953
|
+
home: homedir7(),
|
|
13713
13954
|
execPath: process.execPath
|
|
13714
13955
|
};
|
|
13715
13956
|
}
|
|
@@ -15044,13 +15285,13 @@ function tokenizeStage(stage) {
|
|
|
15044
15285
|
}
|
|
15045
15286
|
// ../aft-bridge/dist/pool.js
|
|
15046
15287
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
15047
|
-
import { homedir as
|
|
15288
|
+
import { homedir as homedir8 } from "node:os";
|
|
15048
15289
|
var DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
15049
15290
|
var DEFAULT_MAX_POOL_SIZE = 8;
|
|
15050
15291
|
var CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
15051
15292
|
function canonicalHomeDir() {
|
|
15052
15293
|
try {
|
|
15053
|
-
const home =
|
|
15294
|
+
const home = homedir8();
|
|
15054
15295
|
if (!home)
|
|
15055
15296
|
return null;
|
|
15056
15297
|
try {
|
|
@@ -16073,7 +16314,7 @@ import {
|
|
|
16073
16314
|
// package.json
|
|
16074
16315
|
var package_default = {
|
|
16075
16316
|
name: "@cortexkit/aft-pi",
|
|
16076
|
-
version: "0.39.
|
|
16317
|
+
version: "0.39.4",
|
|
16077
16318
|
type: "module",
|
|
16078
16319
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
16079
16320
|
main: "dist/index.js",
|
|
@@ -16096,7 +16337,7 @@ var package_default = {
|
|
|
16096
16337
|
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
|
|
16097
16338
|
},
|
|
16098
16339
|
dependencies: {
|
|
16099
|
-
"@cortexkit/aft-bridge": "0.39.
|
|
16340
|
+
"@cortexkit/aft-bridge": "0.39.4",
|
|
16100
16341
|
"@xterm/headless": "^5.5.0",
|
|
16101
16342
|
"comment-json": "^5.0.0",
|
|
16102
16343
|
diff: "^8.0.4",
|
|
@@ -16104,12 +16345,12 @@ var package_default = {
|
|
|
16104
16345
|
zod: "^4.1.8"
|
|
16105
16346
|
},
|
|
16106
16347
|
optionalDependencies: {
|
|
16107
|
-
"@cortexkit/aft-darwin-arm64": "0.39.
|
|
16108
|
-
"@cortexkit/aft-darwin-x64": "0.39.
|
|
16109
|
-
"@cortexkit/aft-linux-arm64": "0.39.
|
|
16110
|
-
"@cortexkit/aft-linux-x64": "0.39.
|
|
16111
|
-
"@cortexkit/aft-win32-arm64": "0.39.
|
|
16112
|
-
"@cortexkit/aft-win32-x64": "0.39.
|
|
16348
|
+
"@cortexkit/aft-darwin-arm64": "0.39.4",
|
|
16349
|
+
"@cortexkit/aft-darwin-x64": "0.39.4",
|
|
16350
|
+
"@cortexkit/aft-linux-arm64": "0.39.4",
|
|
16351
|
+
"@cortexkit/aft-linux-x64": "0.39.4",
|
|
16352
|
+
"@cortexkit/aft-win32-arm64": "0.39.4",
|
|
16353
|
+
"@cortexkit/aft-win32-x64": "0.39.4"
|
|
16113
16354
|
},
|
|
16114
16355
|
devDependencies: {
|
|
16115
16356
|
"@earendil-works/pi-coding-agent": "*",
|
|
@@ -16136,7 +16377,7 @@ var package_default = {
|
|
|
16136
16377
|
};
|
|
16137
16378
|
|
|
16138
16379
|
// src/shared/status.ts
|
|
16139
|
-
function
|
|
16380
|
+
function asRecord2(value) {
|
|
16140
16381
|
return typeof value === "object" && value !== null ? value : {};
|
|
16141
16382
|
}
|
|
16142
16383
|
function readString(value, fallback = "") {
|
|
@@ -16155,7 +16396,7 @@ function readOptionalNumber(value) {
|
|
|
16155
16396
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
16156
16397
|
}
|
|
16157
16398
|
function readCompressionAggregate(value) {
|
|
16158
|
-
const aggregate =
|
|
16399
|
+
const aggregate = asRecord2(value);
|
|
16159
16400
|
return {
|
|
16160
16401
|
events: readNumber(aggregate.events),
|
|
16161
16402
|
original_tokens: readNumber(aggregate.original_tokens),
|
|
@@ -16166,7 +16407,7 @@ function readCompressionAggregate(value) {
|
|
|
16166
16407
|
function readCompression(value) {
|
|
16167
16408
|
if (typeof value !== "object" || value === null)
|
|
16168
16409
|
return;
|
|
16169
|
-
const compression =
|
|
16410
|
+
const compression = asRecord2(value);
|
|
16170
16411
|
return {
|
|
16171
16412
|
project: readCompressionAggregate(compression.project),
|
|
16172
16413
|
session: readCompressionAggregate(compression.session)
|
|
@@ -16175,7 +16416,7 @@ function readCompression(value) {
|
|
|
16175
16416
|
function readStatusBar(value) {
|
|
16176
16417
|
if (typeof value !== "object" || value === null)
|
|
16177
16418
|
return;
|
|
16178
|
-
const bar =
|
|
16419
|
+
const bar = asRecord2(value);
|
|
16179
16420
|
return {
|
|
16180
16421
|
errors: readNumber(bar.errors),
|
|
16181
16422
|
warnings: readNumber(bar.warnings),
|
|
@@ -16219,16 +16460,16 @@ function formatBytes(bytes) {
|
|
|
16219
16460
|
return `${value.toFixed(decimals)} ${units[unitIndex]}`;
|
|
16220
16461
|
}
|
|
16221
16462
|
function coerceAftStatus(response) {
|
|
16222
|
-
const features =
|
|
16223
|
-
const searchIndex =
|
|
16224
|
-
const semanticIndex =
|
|
16463
|
+
const features = asRecord2(response.features);
|
|
16464
|
+
const searchIndex = asRecord2(response.search_index);
|
|
16465
|
+
const semanticIndex = asRecord2(response.semantic_index);
|
|
16225
16466
|
const semanticConfig = {
|
|
16226
|
-
...
|
|
16227
|
-
...
|
|
16467
|
+
...asRecord2(response.semantic),
|
|
16468
|
+
...asRecord2(response.semantic_config)
|
|
16228
16469
|
};
|
|
16229
|
-
const disk =
|
|
16230
|
-
const symbolCache =
|
|
16231
|
-
const session =
|
|
16470
|
+
const disk = asRecord2(response.disk);
|
|
16471
|
+
const symbolCache = asRecord2(response.symbol_cache);
|
|
16472
|
+
const session = asRecord2(response.session);
|
|
16232
16473
|
return {
|
|
16233
16474
|
version: readString(response.version, "unknown"),
|
|
16234
16475
|
project_root: readNullableString(response.project_root),
|
|
@@ -16684,7 +16925,7 @@ function registerStatusCommand(pi, ctx) {
|
|
|
16684
16925
|
|
|
16685
16926
|
// src/config.ts
|
|
16686
16927
|
import { existsSync as existsSync6, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
16687
|
-
import { homedir as
|
|
16928
|
+
import { homedir as homedir9 } from "node:os";
|
|
16688
16929
|
import { join as join10 } from "node:path";
|
|
16689
16930
|
var import_comment_json = __toESM(require_src2(), 1);
|
|
16690
16931
|
|
|
@@ -30381,6 +30622,8 @@ var AftConfigSchema = exports_external.object({
|
|
|
30381
30622
|
restrict_to_project_root: exports_external.boolean().optional(),
|
|
30382
30623
|
search_index: exports_external.boolean().optional(),
|
|
30383
30624
|
semantic_search: exports_external.boolean().optional(),
|
|
30625
|
+
callgraph_store: exports_external.boolean().optional(),
|
|
30626
|
+
callgraph_chunk_size: exports_external.number().optional(),
|
|
30384
30627
|
inspect: InspectConfigSchema.optional(),
|
|
30385
30628
|
bash: BashConfigSchema.optional(),
|
|
30386
30629
|
experimental: ExperimentalConfigSchema.optional(),
|
|
@@ -30441,6 +30684,37 @@ function resolveLspConfigForConfigure(config2) {
|
|
|
30441
30684
|
}
|
|
30442
30685
|
return overrides;
|
|
30443
30686
|
}
|
|
30687
|
+
function resolveProjectOverridesForConfigure(config2) {
|
|
30688
|
+
const overrides = {};
|
|
30689
|
+
if (config2.format_on_edit !== undefined)
|
|
30690
|
+
overrides.format_on_edit = config2.format_on_edit;
|
|
30691
|
+
if (config2.formatter_timeout_secs !== undefined)
|
|
30692
|
+
overrides.formatter_timeout_secs = config2.formatter_timeout_secs;
|
|
30693
|
+
if (config2.validate_on_edit !== undefined)
|
|
30694
|
+
overrides.validate_on_edit = config2.validate_on_edit;
|
|
30695
|
+
if (config2.formatter !== undefined)
|
|
30696
|
+
overrides.formatter = config2.formatter;
|
|
30697
|
+
if (config2.checker !== undefined)
|
|
30698
|
+
overrides.checker = config2.checker;
|
|
30699
|
+
overrides.restrict_to_project_root = config2.restrict_to_project_root ?? false;
|
|
30700
|
+
if (config2.search_index !== undefined)
|
|
30701
|
+
overrides.search_index = config2.search_index;
|
|
30702
|
+
if (config2.semantic_search !== undefined)
|
|
30703
|
+
overrides.semantic_search = config2.semantic_search;
|
|
30704
|
+
if (config2.callgraph_store !== undefined)
|
|
30705
|
+
overrides.callgraph_store = config2.callgraph_store;
|
|
30706
|
+
if (config2.callgraph_chunk_size !== undefined)
|
|
30707
|
+
overrides.callgraph_chunk_size = config2.callgraph_chunk_size;
|
|
30708
|
+
Object.assign(overrides, resolveExperimentalConfigForConfigure(config2));
|
|
30709
|
+
Object.assign(overrides, resolveLspConfigForConfigure(config2));
|
|
30710
|
+
if (config2.semantic !== undefined)
|
|
30711
|
+
overrides.semantic = config2.semantic;
|
|
30712
|
+
if (config2.inspect !== undefined)
|
|
30713
|
+
overrides.inspect = config2.inspect;
|
|
30714
|
+
if (config2.max_callgraph_files !== undefined)
|
|
30715
|
+
overrides.max_callgraph_files = config2.max_callgraph_files;
|
|
30716
|
+
return overrides;
|
|
30717
|
+
}
|
|
30444
30718
|
function resolveExperimentalConfigForConfigure(config2) {
|
|
30445
30719
|
const overrides = {};
|
|
30446
30720
|
const bash = resolveBashConfig(config2);
|
|
@@ -30617,6 +30891,17 @@ function detectConfigFile(basePath) {
|
|
|
30617
30891
|
return { format: "json", path: jsonPath };
|
|
30618
30892
|
return { format: "none", path: jsonPath };
|
|
30619
30893
|
}
|
|
30894
|
+
var configLoadErrors = [];
|
|
30895
|
+
function getConfigLoadErrors() {
|
|
30896
|
+
return configLoadErrors;
|
|
30897
|
+
}
|
|
30898
|
+
function formatConfigParseFailureMessage(configPath, errorMessage) {
|
|
30899
|
+
return `AFT config at ${configPath} failed to parse and was ignored (running on defaults): ${errorMessage}. ` + "Fix the syntax or run `npx @cortexkit/aft doctor`.";
|
|
30900
|
+
}
|
|
30901
|
+
function recordConfigParseFailure(configPath, errorMessage) {
|
|
30902
|
+
configLoadErrors.push({ path: configPath, message: errorMessage });
|
|
30903
|
+
warn2(formatConfigParseFailureMessage(configPath, errorMessage));
|
|
30904
|
+
}
|
|
30620
30905
|
function loadConfigFromPath(configPath) {
|
|
30621
30906
|
try {
|
|
30622
30907
|
if (!existsSync6(configPath))
|
|
@@ -30624,7 +30909,7 @@ function loadConfigFromPath(configPath) {
|
|
|
30624
30909
|
const content = readFileSync4(configPath, "utf-8");
|
|
30625
30910
|
const rawConfig = import_comment_json.parse(content);
|
|
30626
30911
|
if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) {
|
|
30627
|
-
|
|
30912
|
+
recordConfigParseFailure(configPath, "root must be an object");
|
|
30628
30913
|
return null;
|
|
30629
30914
|
}
|
|
30630
30915
|
migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
|
|
@@ -30640,6 +30925,7 @@ function loadConfigFromPath(configPath) {
|
|
|
30640
30925
|
} catch (err) {
|
|
30641
30926
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
30642
30927
|
error2(`Error loading config from ${configPath}: ${errorMsg}`);
|
|
30928
|
+
recordConfigParseFailure(configPath, errorMsg);
|
|
30643
30929
|
return null;
|
|
30644
30930
|
}
|
|
30645
30931
|
}
|
|
@@ -30775,6 +31061,8 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
|
|
|
30775
31061
|
"configure_warnings_delivery",
|
|
30776
31062
|
"search_index",
|
|
30777
31063
|
"semantic_search",
|
|
31064
|
+
"callgraph_store",
|
|
31065
|
+
"callgraph_chunk_size",
|
|
30778
31066
|
"inspect",
|
|
30779
31067
|
"experimental",
|
|
30780
31068
|
"bash"
|
|
@@ -30836,9 +31124,10 @@ function resolveBridgePoolTransportOptions(config2) {
|
|
|
30836
31124
|
};
|
|
30837
31125
|
}
|
|
30838
31126
|
function getGlobalPiDir() {
|
|
30839
|
-
return join10(
|
|
31127
|
+
return join10(homedir9(), ".pi", "agent");
|
|
30840
31128
|
}
|
|
30841
31129
|
function loadAftConfig(projectDirectory) {
|
|
31130
|
+
configLoadErrors = [];
|
|
30842
31131
|
const userBasePath = join10(getGlobalPiDir(), "aft");
|
|
30843
31132
|
migrateAftConfigFile(`${userBasePath}.jsonc`);
|
|
30844
31133
|
migrateAftConfigFile(`${userBasePath}.json`);
|
|
@@ -30893,7 +31182,7 @@ import {
|
|
|
30893
31182
|
unlinkSync as unlinkSync5,
|
|
30894
31183
|
writeFileSync as writeFileSync4
|
|
30895
31184
|
} from "node:fs";
|
|
30896
|
-
import { homedir as
|
|
31185
|
+
import { homedir as homedir10 } from "node:os";
|
|
30897
31186
|
import { join as join11 } from "node:path";
|
|
30898
31187
|
function aftCacheBase() {
|
|
30899
31188
|
const override = process.env.AFT_CACHE_DIR;
|
|
@@ -30901,10 +31190,10 @@ function aftCacheBase() {
|
|
|
30901
31190
|
return override;
|
|
30902
31191
|
if (process.platform === "win32") {
|
|
30903
31192
|
const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
|
|
30904
|
-
const base2 = localAppData || join11(
|
|
31193
|
+
const base2 = localAppData || join11(homedir10(), "AppData", "Local");
|
|
30905
31194
|
return join11(base2, "aft");
|
|
30906
31195
|
}
|
|
30907
|
-
const base = process.env.XDG_CACHE_HOME || join11(
|
|
31196
|
+
const base = process.env.XDG_CACHE_HOME || join11(homedir10(), ".cache");
|
|
30908
31197
|
return join11(base, "aft");
|
|
30909
31198
|
}
|
|
30910
31199
|
function lspCacheRoot() {
|
|
@@ -32617,6 +32906,8 @@ function warningTitle(warning) {
|
|
|
32617
32906
|
return "Checker is not installed";
|
|
32618
32907
|
case "lsp_binary_missing":
|
|
32619
32908
|
return "LSP binary is missing";
|
|
32909
|
+
case "config_parse_failed":
|
|
32910
|
+
return "Config failed to parse";
|
|
32620
32911
|
}
|
|
32621
32912
|
}
|
|
32622
32913
|
function formatConfigureWarning(warning) {
|
|
@@ -32862,7 +33153,7 @@ import { Type as Type3 } from "typebox";
|
|
|
32862
33153
|
|
|
32863
33154
|
// src/tools/hoisted.ts
|
|
32864
33155
|
import { stat } from "node:fs/promises";
|
|
32865
|
-
import { homedir as
|
|
33156
|
+
import { homedir as homedir11 } from "node:os";
|
|
32866
33157
|
import { isAbsolute as isAbsolute4, relative as relative4, resolve as resolve4, sep } from "node:path";
|
|
32867
33158
|
import {
|
|
32868
33159
|
renderDiff
|
|
@@ -32984,9 +33275,9 @@ function expandTilde2(path3) {
|
|
|
32984
33275
|
if (!path3 || !path3.startsWith("~"))
|
|
32985
33276
|
return path3;
|
|
32986
33277
|
if (path3 === "~")
|
|
32987
|
-
return
|
|
33278
|
+
return homedir11();
|
|
32988
33279
|
if (path3.startsWith(`~${sep}`) || path3.startsWith("~/")) {
|
|
32989
|
-
return resolve4(
|
|
33280
|
+
return resolve4(homedir11(), path3.slice(2));
|
|
32990
33281
|
}
|
|
32991
33282
|
return path3;
|
|
32992
33283
|
}
|
|
@@ -33044,14 +33335,7 @@ function appendSkippedSearchPaths(text, missing) {
|
|
|
33044
33335
|
|
|
33045
33336
|
${note}` : note;
|
|
33046
33337
|
}
|
|
33047
|
-
function
|
|
33048
|
-
const raw = process.env.AFT_PI_EXTERNAL_PROMPT_TIMEOUT_MS;
|
|
33049
|
-
if (raw === undefined)
|
|
33050
|
-
return 30000;
|
|
33051
|
-
const parsed = Number.parseInt(raw, 10);
|
|
33052
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : 30000;
|
|
33053
|
-
}
|
|
33054
|
-
async function assertExternalDirectoryPermission(extCtx, target, action = "modify", options = {}) {
|
|
33338
|
+
async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
|
|
33055
33339
|
if (!target)
|
|
33056
33340
|
return;
|
|
33057
33341
|
const expanded = expandTilde2(target);
|
|
@@ -33060,30 +33344,7 @@ async function assertExternalDirectoryPermission(extCtx, target, action = "modif
|
|
|
33060
33344
|
return;
|
|
33061
33345
|
if (options.restrictToProjectRoot === false)
|
|
33062
33346
|
return;
|
|
33063
|
-
|
|
33064
|
-
if (extCtx.hasUI === false || !confirmFn) {
|
|
33065
|
-
throw new Error(`Permission denied: cannot prompt for ${action} outside the project (${absoluteTarget}).`);
|
|
33066
|
-
}
|
|
33067
|
-
const timeoutMs = externalDirectoryPromptTimeoutMs();
|
|
33068
|
-
let timer;
|
|
33069
|
-
const timeoutPromise = new Promise((resolve5) => {
|
|
33070
|
-
timer = setTimeout(() => resolve5("timeout"), timeoutMs);
|
|
33071
|
-
});
|
|
33072
|
-
try {
|
|
33073
|
-
const result = await Promise.race([
|
|
33074
|
-
confirmFn("Allow external directory access?", `AFT wants to ${action} outside the project: ${absoluteTarget}`),
|
|
33075
|
-
timeoutPromise
|
|
33076
|
-
]);
|
|
33077
|
-
if (result === true)
|
|
33078
|
-
return;
|
|
33079
|
-
if (result === "timeout") {
|
|
33080
|
-
throw new Error(`Permission denied: external directory prompt timed out after ${timeoutMs}ms.`);
|
|
33081
|
-
}
|
|
33082
|
-
throw new Error("Permission denied: external directory access was cancelled.");
|
|
33083
|
-
} finally {
|
|
33084
|
-
if (timer !== undefined)
|
|
33085
|
-
clearTimeout(timer);
|
|
33086
|
-
}
|
|
33347
|
+
throw new Error(`Blocked: '${absoluteTarget}' is outside the project root and restrict_to_project_root is ` + "enabled (AFT full isolation). Not overridable per-call; set restrict_to_project_root: false " + "in aft.jsonc to allow external paths.");
|
|
33087
33348
|
}
|
|
33088
33349
|
var ReadParams = Type2.Object({
|
|
33089
33350
|
path: Type2.String({
|
|
@@ -33132,7 +33393,7 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
33132
33393
|
const offset = coerceOptionalInt(params.offset, "offset", 1, Number.MAX_SAFE_INTEGER);
|
|
33133
33394
|
const limit = coerceOptionalInt(params.limit, "limit", 1, Number.MAX_SAFE_INTEGER);
|
|
33134
33395
|
const filePath = await resolvePathArg(extCtx.cwd, params.path);
|
|
33135
|
-
await assertExternalDirectoryPermission(extCtx, filePath,
|
|
33396
|
+
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
33136
33397
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
33137
33398
|
});
|
|
33138
33399
|
const req = {
|
|
@@ -33170,7 +33431,7 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
33170
33431
|
parameters: WriteParams,
|
|
33171
33432
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
33172
33433
|
const filePath = await resolvePathArg(extCtx.cwd, params.filePath);
|
|
33173
|
-
await assertExternalDirectoryPermission(extCtx, filePath,
|
|
33434
|
+
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
33174
33435
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
33175
33436
|
});
|
|
33176
33437
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
@@ -33204,7 +33465,7 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
33204
33465
|
parameters: EditParams,
|
|
33205
33466
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
33206
33467
|
const filePath = await resolvePathArg(extCtx.cwd, params.filePath);
|
|
33207
|
-
await assertExternalDirectoryPermission(extCtx, filePath,
|
|
33468
|
+
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
33208
33469
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
33209
33470
|
});
|
|
33210
33471
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
@@ -33257,7 +33518,7 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
33257
33518
|
if (params.path) {
|
|
33258
33519
|
pathSplit = await splitSearchPathArg(extCtx.cwd, params.path);
|
|
33259
33520
|
for (const target of pathSplit.paths) {
|
|
33260
|
-
await assertExternalDirectoryPermission(extCtx, absoluteSearchPath(extCtx.cwd, target),
|
|
33521
|
+
await assertExternalDirectoryPermission(extCtx, absoluteSearchPath(extCtx.cwd, target), {
|
|
33261
33522
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
33262
33523
|
});
|
|
33263
33524
|
}
|
|
@@ -33380,7 +33641,7 @@ function reuseContainer(last) {
|
|
|
33380
33641
|
}
|
|
33381
33642
|
function renderMutationCall(toolName, filePath, theme, context) {
|
|
33382
33643
|
const text = reuseText(context.lastComponent);
|
|
33383
|
-
const pathDisplay = filePath ? theme.fg("accent",
|
|
33644
|
+
const pathDisplay = filePath ? theme.fg("accent", shortenPath2(filePath)) : theme.fg("toolOutput", "...");
|
|
33384
33645
|
text.setText(`${theme.fg("toolTitle", theme.bold(toolName))} ${pathDisplay}`);
|
|
33385
33646
|
return text;
|
|
33386
33647
|
}
|
|
@@ -33416,8 +33677,8 @@ ${summary}${suffix}`);
|
|
|
33416
33677
|
container.addChild(new Text(renderDiff(diff), 1, 0));
|
|
33417
33678
|
return container;
|
|
33418
33679
|
}
|
|
33419
|
-
function
|
|
33420
|
-
const home =
|
|
33680
|
+
function shortenPath2(path3) {
|
|
33681
|
+
const home = homedir11();
|
|
33421
33682
|
if (path3.startsWith(home))
|
|
33422
33683
|
return `~${path3.slice(home.length)}`;
|
|
33423
33684
|
return path3;
|
|
@@ -33467,7 +33728,7 @@ function formatReadFooter2(agentSpecifiedRange, data) {
|
|
|
33467
33728
|
}
|
|
33468
33729
|
|
|
33469
33730
|
// src/tools/render-helpers.ts
|
|
33470
|
-
import { homedir as
|
|
33731
|
+
import { homedir as homedir12 } from "node:os";
|
|
33471
33732
|
import { renderDiff as renderDiff2 } from "@earendil-works/pi-coding-agent";
|
|
33472
33733
|
import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
|
|
33473
33734
|
function reuseText2(last) {
|
|
@@ -33476,8 +33737,8 @@ function reuseText2(last) {
|
|
|
33476
33737
|
function reuseContainer2(last) {
|
|
33477
33738
|
return last instanceof Container2 ? last : new Container2;
|
|
33478
33739
|
}
|
|
33479
|
-
function
|
|
33480
|
-
const home =
|
|
33740
|
+
function shortenPath3(path3) {
|
|
33741
|
+
const home = homedir12();
|
|
33481
33742
|
if (path3.startsWith(home))
|
|
33482
33743
|
return `~${path3.slice(home.length)}`;
|
|
33483
33744
|
return path3;
|
|
@@ -33491,7 +33752,7 @@ function renderToolCall(toolName, summary, theme, context) {
|
|
|
33491
33752
|
function accentPath(theme, path3) {
|
|
33492
33753
|
if (!path3)
|
|
33493
33754
|
return theme.fg("toolOutput", "...");
|
|
33494
|
-
return theme.fg("accent",
|
|
33755
|
+
return theme.fg("accent", shortenPath3(path3));
|
|
33495
33756
|
}
|
|
33496
33757
|
function collectTextContent(result) {
|
|
33497
33758
|
return result.content.filter((part) => part.type === "text").map((part) => part.text ?? "").join(`
|
|
@@ -33530,23 +33791,20 @@ function renderSections(sections, context) {
|
|
|
33530
33791
|
});
|
|
33531
33792
|
return container;
|
|
33532
33793
|
}
|
|
33533
|
-
function
|
|
33794
|
+
function asRecord3(value) {
|
|
33534
33795
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
33535
33796
|
return;
|
|
33536
33797
|
return value;
|
|
33537
33798
|
}
|
|
33538
|
-
function
|
|
33539
|
-
return Array.isArray(value) ? value.map(
|
|
33799
|
+
function asRecords2(value) {
|
|
33800
|
+
return Array.isArray(value) ? value.map(asRecord3).filter(Boolean) : [];
|
|
33540
33801
|
}
|
|
33541
|
-
function
|
|
33802
|
+
function asString2(value) {
|
|
33542
33803
|
return typeof value === "string" ? value : undefined;
|
|
33543
33804
|
}
|
|
33544
|
-
function
|
|
33805
|
+
function asNumber2(value) {
|
|
33545
33806
|
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
33546
33807
|
}
|
|
33547
|
-
function asBoolean(value) {
|
|
33548
|
-
return typeof value === "boolean" ? value : undefined;
|
|
33549
|
-
}
|
|
33550
33808
|
function formatValue(value) {
|
|
33551
33809
|
if (Array.isArray(value))
|
|
33552
33810
|
return value.map(formatValue).join(", ");
|
|
@@ -33659,7 +33917,7 @@ var ReplaceParams = Type3.Object({
|
|
|
33659
33917
|
dryRun: Type3.Optional(Type3.Boolean({ description: "Preview without applying (default: false)" }))
|
|
33660
33918
|
});
|
|
33661
33919
|
function appendHintSection(response, sections, theme) {
|
|
33662
|
-
const hint =
|
|
33920
|
+
const hint = asString2(response.hint);
|
|
33663
33921
|
if (hint && hint.length > 0) {
|
|
33664
33922
|
sections.push(theme.fg("warning", hint));
|
|
33665
33923
|
}
|
|
@@ -33668,8 +33926,8 @@ function appendScopeSections(response, sections, theme) {
|
|
|
33668
33926
|
if (response.no_files_matched_scope === true) {
|
|
33669
33927
|
sections.push(theme.fg("warning", "No files matched the scope (paths/globs resolved to zero files)"));
|
|
33670
33928
|
}
|
|
33671
|
-
const warnings =
|
|
33672
|
-
const warningStrings = Array.isArray(response.scope_warnings) ? response.scope_warnings.filter((w) => typeof w === "string") : warnings.map((w) =>
|
|
33929
|
+
const warnings = asRecords2(response.scope_warnings);
|
|
33930
|
+
const warningStrings = Array.isArray(response.scope_warnings) ? response.scope_warnings.filter((w) => typeof w === "string") : warnings.map((w) => asString2(w.warning) ?? "").filter(Boolean);
|
|
33673
33931
|
if (warningStrings.length > 0) {
|
|
33674
33932
|
sections.push(`${theme.fg("muted", "Scope warnings:")}
|
|
33675
33933
|
${warningStrings.map((w) => ` ${w}`).join(`
|
|
@@ -33681,7 +33939,7 @@ async function resolveAstPaths(extCtx, paths) {
|
|
|
33681
33939
|
return;
|
|
33682
33940
|
return Promise.all(paths.filter((path3) => typeof path3 === "string").map((path3) => resolvePathArg(extCtx.cwd, path3)));
|
|
33683
33941
|
}
|
|
33684
|
-
async function assertAstPathsPermission(extCtx, paths,
|
|
33942
|
+
async function assertAstPathsPermission(extCtx, paths, restrictToProjectRoot) {
|
|
33685
33943
|
if (paths === undefined || paths.length === 0)
|
|
33686
33944
|
return;
|
|
33687
33945
|
const checked = new Set;
|
|
@@ -33689,17 +33947,17 @@ async function assertAstPathsPermission(extCtx, paths, action, restrictToProject
|
|
|
33689
33947
|
if (checked.has(path3))
|
|
33690
33948
|
continue;
|
|
33691
33949
|
checked.add(path3);
|
|
33692
|
-
await assertExternalDirectoryPermission(extCtx, path3,
|
|
33950
|
+
await assertExternalDirectoryPermission(extCtx, path3, { restrictToProjectRoot });
|
|
33693
33951
|
}
|
|
33694
33952
|
}
|
|
33695
33953
|
function buildAstSearchSections(payload, theme) {
|
|
33696
|
-
const response =
|
|
33954
|
+
const response = asRecord3(payload);
|
|
33697
33955
|
if (!response)
|
|
33698
33956
|
return [theme.fg("muted", "No AST search results.")];
|
|
33699
|
-
const matches =
|
|
33700
|
-
const totalMatches =
|
|
33701
|
-
const filesWithMatches =
|
|
33702
|
-
const filesSearched =
|
|
33957
|
+
const matches = asRecords2(response.matches);
|
|
33958
|
+
const totalMatches = asNumber2(response.total_matches) ?? matches.length;
|
|
33959
|
+
const filesWithMatches = asNumber2(response.files_with_matches) ?? groupByFile(matches, (match) => asString2(match.file)).size;
|
|
33960
|
+
const filesSearched = asNumber2(response.files_searched);
|
|
33703
33961
|
const header = [
|
|
33704
33962
|
theme.fg("success", `${totalMatches} match${totalMatches === 1 ? "" : "es"}`),
|
|
33705
33963
|
theme.fg("accent", `${filesWithMatches} file${filesWithMatches === 1 ? "" : "s"}`),
|
|
@@ -33711,26 +33969,26 @@ function buildAstSearchSections(payload, theme) {
|
|
|
33711
33969
|
appendHintSection(response, sections2, theme);
|
|
33712
33970
|
return sections2;
|
|
33713
33971
|
}
|
|
33714
|
-
const grouped = groupByFile(matches, (match) =>
|
|
33972
|
+
const grouped = groupByFile(matches, (match) => asString2(match.file));
|
|
33715
33973
|
const sections = [header];
|
|
33716
33974
|
for (const [file2, fileMatches] of grouped.entries()) {
|
|
33717
|
-
const lines = [theme.fg("accent",
|
|
33975
|
+
const lines = [theme.fg("accent", shortenPath3(file2))];
|
|
33718
33976
|
fileMatches.forEach((match, index) => {
|
|
33719
|
-
const line =
|
|
33720
|
-
const column =
|
|
33721
|
-
const snippet =
|
|
33977
|
+
const line = asNumber2(match.line) ?? 0;
|
|
33978
|
+
const column = asNumber2(match.column) ?? 0;
|
|
33979
|
+
const snippet = asString2(match.text)?.trim() || "(empty match)";
|
|
33722
33980
|
lines.push(` ${index + 1}. ${theme.fg("muted", `${line}:${column}`)} ${snippet}`);
|
|
33723
|
-
const metaVars =
|
|
33981
|
+
const metaVars = asRecord3(match.meta_variables);
|
|
33724
33982
|
if (metaVars && Object.keys(metaVars).length > 0) {
|
|
33725
33983
|
Object.entries(metaVars).forEach(([name, value]) => {
|
|
33726
33984
|
lines.push(` ${theme.fg("muted", `${name} =`)} ${formatValue(value)}`);
|
|
33727
33985
|
});
|
|
33728
33986
|
}
|
|
33729
|
-
const context =
|
|
33987
|
+
const context = asRecords2(match.context);
|
|
33730
33988
|
context.forEach((ctxLine) => {
|
|
33731
|
-
const ctxNumber =
|
|
33989
|
+
const ctxNumber = asNumber2(ctxLine.line) ?? 0;
|
|
33732
33990
|
const prefix = ctxLine.is_match === true ? theme.fg("accent", ">") : theme.fg("muted", "|");
|
|
33733
|
-
lines.push(` ${prefix} ${ctxNumber}: ${
|
|
33991
|
+
lines.push(` ${prefix} ${ctxNumber}: ${asString2(ctxLine.text) ?? ""}`);
|
|
33734
33992
|
});
|
|
33735
33993
|
});
|
|
33736
33994
|
sections.push(lines.join(`
|
|
@@ -33739,13 +33997,13 @@ function buildAstSearchSections(payload, theme) {
|
|
|
33739
33997
|
return sections;
|
|
33740
33998
|
}
|
|
33741
33999
|
function buildAstReplaceSections(payload, theme) {
|
|
33742
|
-
const response =
|
|
34000
|
+
const response = asRecord3(payload);
|
|
33743
34001
|
if (!response)
|
|
33744
34002
|
return [theme.fg("muted", "No AST replace results.")];
|
|
33745
|
-
const files =
|
|
33746
|
-
const totalReplacements =
|
|
33747
|
-
const totalFiles =
|
|
33748
|
-
const filesWithMatches =
|
|
34003
|
+
const files = asRecords2(response.files);
|
|
34004
|
+
const totalReplacements = asNumber2(response.total_replacements) ?? 0;
|
|
34005
|
+
const totalFiles = asNumber2(response.total_files) ?? files.length;
|
|
34006
|
+
const filesWithMatches = asNumber2(response.files_with_matches);
|
|
33749
34007
|
const dryRun = response.dry_run === true;
|
|
33750
34008
|
const headerParts = [
|
|
33751
34009
|
dryRun ? theme.fg("warning", "[dry run]") : theme.fg("success", "[applied]"),
|
|
@@ -33761,10 +34019,10 @@ function buildAstReplaceSections(payload, theme) {
|
|
|
33761
34019
|
return sections;
|
|
33762
34020
|
}
|
|
33763
34021
|
files.forEach((fileResult) => {
|
|
33764
|
-
const file2 =
|
|
33765
|
-
const replacements =
|
|
33766
|
-
const error50 =
|
|
33767
|
-
const diff =
|
|
34022
|
+
const file2 = shortenPath3(asString2(fileResult.file) ?? "(unknown file)");
|
|
34023
|
+
const replacements = asNumber2(fileResult.replacements) ?? 0;
|
|
34024
|
+
const error50 = asString2(fileResult.error);
|
|
34025
|
+
const diff = asString2(fileResult.diff);
|
|
33768
34026
|
const lines = [
|
|
33769
34027
|
`${theme.fg("accent", file2)} ${theme.fg("muted", `(${replacements} replacement${replacements === 1 ? "" : "s"})`)}`
|
|
33770
34028
|
];
|
|
@@ -33774,7 +34032,7 @@ function buildAstReplaceSections(payload, theme) {
|
|
|
33774
34032
|
const rendered = renderUnifiedDiff(diff);
|
|
33775
34033
|
lines.push(rendered || theme.fg("muted", "No diff available."));
|
|
33776
34034
|
} else {
|
|
33777
|
-
const backupId =
|
|
34035
|
+
const backupId = asString2(fileResult.backup_id);
|
|
33778
34036
|
lines.push(backupId ? `${theme.fg("success", "saved")} ${theme.fg("muted", backupId)}` : theme.fg("success", "saved"));
|
|
33779
34037
|
}
|
|
33780
34038
|
sections.push(lines.join(`
|
|
@@ -33808,7 +34066,7 @@ function registerAstTools(pi, ctx, surface) {
|
|
|
33808
34066
|
parameters: SearchParams,
|
|
33809
34067
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
33810
34068
|
const paths = await resolveAstPaths(extCtx, params.paths);
|
|
33811
|
-
await assertAstPathsPermission(extCtx, paths,
|
|
34069
|
+
await assertAstPathsPermission(extCtx, paths, ctx.config.restrict_to_project_root ?? false);
|
|
33812
34070
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33813
34071
|
const req = {
|
|
33814
34072
|
pattern: params.pattern,
|
|
@@ -33839,7 +34097,7 @@ function registerAstTools(pi, ctx, surface) {
|
|
|
33839
34097
|
parameters: ReplaceParams,
|
|
33840
34098
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
33841
34099
|
const paths = await resolveAstPaths(extCtx, params.paths);
|
|
33842
|
-
await assertAstPathsPermission(extCtx, paths,
|
|
34100
|
+
await assertAstPathsPermission(extCtx, paths, ctx.config.restrict_to_project_root ?? false);
|
|
33843
34101
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33844
34102
|
const req = {
|
|
33845
34103
|
pattern: params.pattern,
|
|
@@ -33871,7 +34129,7 @@ import { Type as Type4 } from "typebox";
|
|
|
33871
34129
|
var FOREGROUND_POLL_INTERVAL_MS = 100;
|
|
33872
34130
|
var BASH_WAIT_POLL_INTERVAL_MS = 100;
|
|
33873
34131
|
var DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS = 30000;
|
|
33874
|
-
var MAX_BASH_STATUS_WAIT_TIMEOUT_MS =
|
|
34132
|
+
var MAX_BASH_STATUS_WAIT_TIMEOUT_MS = 30 * 60 * 1000;
|
|
33875
34133
|
var REGEX_WAIT_SCAN_WINDOW_BYTES = 64 * 1024;
|
|
33876
34134
|
function resolveForegroundWaitMs(configured) {
|
|
33877
34135
|
const override = process.env.AFT_TEST_FOREGROUND_WAIT_MS;
|
|
@@ -33883,7 +34141,8 @@ function resolveForegroundWaitMs(configured) {
|
|
|
33883
34141
|
return configured;
|
|
33884
34142
|
}
|
|
33885
34143
|
var BASH_TRANSPORT_TIMEOUT_MS = 30000;
|
|
33886
|
-
var
|
|
34144
|
+
var DEFAULT_HARD_TIMEOUT_MS = 30 * 60 * 1000;
|
|
34145
|
+
var BashBaseParams = {
|
|
33887
34146
|
command: Type4.String({
|
|
33888
34147
|
description: "Shell command to execute. Supports pipes, redirections, and shell syntax."
|
|
33889
34148
|
}),
|
|
@@ -33893,19 +34152,38 @@ var BashParams = Type4.Object({
|
|
|
33893
34152
|
})),
|
|
33894
34153
|
description: Type4.Optional(Type4.String({
|
|
33895
34154
|
description: "Human-readable description shown in UI logs. Helps users understand what the command does without reading shell syntax."
|
|
33896
|
-
}))
|
|
34155
|
+
}))
|
|
34156
|
+
};
|
|
34157
|
+
var BashBackgroundFlagParam = {
|
|
33897
34158
|
background: Type4.Optional(Type4.Boolean({
|
|
33898
|
-
description: "Spawn command in background and return immediately with a task_id. Use
|
|
33899
|
-
}))
|
|
34159
|
+
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."
|
|
34160
|
+
}))
|
|
34161
|
+
};
|
|
34162
|
+
var BashCompressionParam = {
|
|
33900
34163
|
compressed: Type4.Optional(Type4.Boolean({
|
|
33901
34164
|
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."
|
|
33902
|
-
}))
|
|
34165
|
+
}))
|
|
34166
|
+
};
|
|
34167
|
+
var BashPtyParams = {
|
|
33903
34168
|
pty: Type4.Optional(Type4.Boolean({
|
|
33904
34169
|
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.'
|
|
33905
34170
|
})),
|
|
33906
34171
|
ptyRows: optionalInt(1, 60),
|
|
33907
34172
|
ptyCols: optionalInt(1, 140)
|
|
34173
|
+
};
|
|
34174
|
+
var BashParams = Type4.Object({
|
|
34175
|
+
...BashBaseParams,
|
|
34176
|
+
...BashBackgroundFlagParam,
|
|
34177
|
+
...BashCompressionParam,
|
|
34178
|
+
...BashPtyParams
|
|
34179
|
+
});
|
|
34180
|
+
var BashForegroundOnlyParams = Type4.Object({
|
|
34181
|
+
...BashBaseParams,
|
|
34182
|
+
...BashCompressionParam
|
|
33908
34183
|
});
|
|
34184
|
+
function bashParamsForConfig(backgroundEnabled) {
|
|
34185
|
+
return backgroundEnabled ? BashParams : BashForegroundOnlyParams;
|
|
34186
|
+
}
|
|
33909
34187
|
var BashTaskParams = Type4.Object({
|
|
33910
34188
|
task_id: Type4.String({
|
|
33911
34189
|
description: "Background bash task id returned by bash({ background: true })."
|
|
@@ -33946,6 +34224,9 @@ var BashWriteParams = Type4.Object({
|
|
|
33946
34224
|
description: "Either a string of verbatim bytes (e.g. 'print(1)\\n') OR an array mixing strings " + "and { key: '<name>' } objects for atomic text+key sequences. " + "Example: [ 'iHello', { key: 'esc' }, ':wq', { key: 'enter' } ]. " + "Allowed key names: enter, return, tab, space, backspace, esc, escape, up, down, " + "left, right, home, end, page-up, page-down, delete, insert, f1..f12, ctrl-a..ctrl-z."
|
|
33947
34225
|
})
|
|
33948
34226
|
});
|
|
34227
|
+
function unavailableSnapshot() {
|
|
34228
|
+
return { status: "unknown" };
|
|
34229
|
+
}
|
|
33949
34230
|
async function callBashBridge(bridge, command, params = {}, extCtx, options) {
|
|
33950
34231
|
return await callBridge(bridge, command, params, extCtx, {
|
|
33951
34232
|
transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS,
|
|
@@ -33979,28 +34260,30 @@ function registerBashTool(pi, ctx, aftSearchRegistered = false) {
|
|
|
33979
34260
|
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";
|
|
33980
34261
|
const bashCfg = resolveBashConfig(ctx.config);
|
|
33981
34262
|
const compressionSentence = bashCfg.compress ? " Output is compressed by default; pass `compressed: false` for raw output." : "";
|
|
33982
|
-
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
|
|
34263
|
+
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).";
|
|
33983
34264
|
pi.registerTool({
|
|
33984
34265
|
name: "bash",
|
|
33985
34266
|
label: "bash",
|
|
33986
34267
|
description: `Execute shell commands.${compressionSentence}${tasksSentence}
|
|
33987
34268
|
|
|
33988
34269
|
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}.`,
|
|
33989
|
-
promptSnippet: "Run shell commands (timeout in milliseconds; supports workdir, background tasks, compressed output, PTY mode)",
|
|
34270
|
+
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)",
|
|
33990
34271
|
promptGuidelines: [
|
|
33991
34272
|
`DO NOT use bash for code search or exploration — ${searchSteer}.`,
|
|
33992
34273
|
"Set compressed: false when you need ANSI color codes in the output."
|
|
33993
34274
|
],
|
|
33994
|
-
parameters:
|
|
34275
|
+
parameters: bashParamsForConfig(bashCfg.background),
|
|
33995
34276
|
async execute(_toolCallId, params, _signal, onUpdate, extCtx) {
|
|
33996
34277
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33997
34278
|
const bashCfg2 = resolveBashConfig(ctx.config);
|
|
33998
34279
|
const foregroundWaitMs = resolveForegroundWaitMs(bashCfg2.foreground_wait_window_ms);
|
|
34280
|
+
const backgroundDisabled = !bashCfg2.background;
|
|
33999
34281
|
const timeout = coerceOptionalInt(params.timeout, "timeout", 1, Number.MAX_SAFE_INTEGER);
|
|
34000
|
-
const ptyRows = coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
|
|
34001
|
-
const ptyCols = coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
|
|
34002
|
-
const
|
|
34003
|
-
const
|
|
34282
|
+
const ptyRows = backgroundDisabled ? undefined : coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
|
|
34283
|
+
const ptyCols = backgroundDisabled ? undefined : coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
|
|
34284
|
+
const requestedPty = !backgroundDisabled && params.pty === true;
|
|
34285
|
+
const effectiveBackground = !backgroundDisabled && (params.background === true || requestedPty);
|
|
34286
|
+
const effectiveTimeout = effectiveBackground || backgroundDisabled ? timeout : resolveBashKillTimeout(timeout, foregroundWaitMs);
|
|
34004
34287
|
let spawnContext = {
|
|
34005
34288
|
command: params.command,
|
|
34006
34289
|
cwd: params.workdir
|
|
@@ -34025,7 +34308,7 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
34025
34308
|
background: effectiveBackground,
|
|
34026
34309
|
notify_on_completion: effectiveBackground,
|
|
34027
34310
|
compressed: params.compressed,
|
|
34028
|
-
pty:
|
|
34311
|
+
pty: requestedPty,
|
|
34029
34312
|
pty_rows: ptyRows,
|
|
34030
34313
|
pty_cols: ptyCols
|
|
34031
34314
|
}, extCtx, {
|
|
@@ -34047,9 +34330,9 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
34047
34330
|
if (response.status === "running" && taskId) {
|
|
34048
34331
|
if (effectiveBackground) {
|
|
34049
34332
|
trackBgTask(resolveSessionId(extCtx), taskId);
|
|
34050
|
-
return bashResult(appendPipeStripNote(formatBackgroundLaunch(taskId,
|
|
34333
|
+
return bashResult(appendPipeStripNote(formatBackgroundLaunch(taskId, requestedPty), pipeStrip.note), { task_id: taskId });
|
|
34051
34334
|
}
|
|
34052
|
-
const waitTimeoutMs = effectiveTimeout !== undefined ? Math.min(effectiveTimeout, foregroundWaitMs) : foregroundWaitMs;
|
|
34335
|
+
const waitTimeoutMs = backgroundDisabled ? effectiveTimeout ?? DEFAULT_HARD_TIMEOUT_MS : effectiveTimeout !== undefined ? Math.min(effectiveTimeout, foregroundWaitMs) : foregroundWaitMs;
|
|
34053
34336
|
const startedAt = Date.now();
|
|
34054
34337
|
while (true) {
|
|
34055
34338
|
const status = await callBashBridge(bridge, "bash_status", { task_id: taskId }, extCtx);
|
|
@@ -34066,6 +34349,10 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
34066
34349
|
});
|
|
34067
34350
|
}
|
|
34068
34351
|
if (Date.now() - startedAt >= waitTimeoutMs) {
|
|
34352
|
+
if (backgroundDisabled) {
|
|
34353
|
+
await sleep(FOREGROUND_POLL_INTERVAL_MS);
|
|
34354
|
+
continue;
|
|
34355
|
+
}
|
|
34069
34356
|
const promoted = await callBashBridge(bridge, "bash_promote", { task_id: taskId }, extCtx);
|
|
34070
34357
|
if (promoted.success === false) {
|
|
34071
34358
|
throw new Error(promoted.message ?? "bash_promote failed");
|
|
@@ -34093,10 +34380,12 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
|
|
|
34093
34380
|
return renderBashResult(result, theme, context);
|
|
34094
34381
|
}
|
|
34095
34382
|
});
|
|
34096
|
-
|
|
34097
|
-
|
|
34098
|
-
|
|
34099
|
-
|
|
34383
|
+
if (bashCfg.background) {
|
|
34384
|
+
pi.registerTool(createBashStatusTool(ctx));
|
|
34385
|
+
pi.registerTool(createBashWatchTool(ctx));
|
|
34386
|
+
pi.registerTool(createBashWriteTool(ctx));
|
|
34387
|
+
pi.registerTool(createBashKillTool(ctx));
|
|
34388
|
+
}
|
|
34100
34389
|
}
|
|
34101
34390
|
function formatBackgroundLaunch(taskId, isPty) {
|
|
34102
34391
|
if (isPty) {
|
|
@@ -34115,7 +34404,7 @@ function createBashStatusTool(ctx) {
|
|
|
34115
34404
|
return {
|
|
34116
34405
|
name: "bash_status",
|
|
34117
34406
|
label: "bash_status",
|
|
34118
|
-
description: "Read-only snapshot of a background bash task. Returns immediately. Never waits.
|
|
34407
|
+
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.",
|
|
34119
34408
|
promptSnippet: "Inspect a background bash task by task_id",
|
|
34120
34409
|
parameters: BashStatusParams,
|
|
34121
34410
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
@@ -34130,7 +34419,7 @@ function createBashWatchTool(ctx) {
|
|
|
34130
34419
|
return {
|
|
34131
34420
|
name: "bash_watch",
|
|
34132
34421
|
label: "bash_watch",
|
|
34133
|
-
description: "Watch a background bash task.
|
|
34422
|
+
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.",
|
|
34134
34423
|
promptSnippet: "Wait for or watch a background bash task",
|
|
34135
34424
|
parameters: BashWatchParams,
|
|
34136
34425
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
@@ -34284,9 +34573,31 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
|
|
|
34284
34573
|
if (waitForExit)
|
|
34285
34574
|
markTaskWaiting(sessionId, taskId);
|
|
34286
34575
|
let sawTerminal = false;
|
|
34576
|
+
let lastData;
|
|
34287
34577
|
try {
|
|
34288
34578
|
while (true) {
|
|
34289
|
-
|
|
34579
|
+
let data;
|
|
34580
|
+
try {
|
|
34581
|
+
data = await bashStatusSnapshot(bridge, extCtx, taskId, outputMode, bridgeOptions);
|
|
34582
|
+
} catch (err) {
|
|
34583
|
+
if (!isBridgeTransportTimeout(err))
|
|
34584
|
+
throw err;
|
|
34585
|
+
if (isSyncWatchAborted(sessionId)) {
|
|
34586
|
+
return withWaited(lastData ?? unavailableSnapshot(), {
|
|
34587
|
+
reason: "user_message",
|
|
34588
|
+
elapsed_ms: Date.now() - startedAt
|
|
34589
|
+
});
|
|
34590
|
+
}
|
|
34591
|
+
if (Date.now() >= deadline) {
|
|
34592
|
+
return withWaited(lastData ?? unavailableSnapshot(), {
|
|
34593
|
+
reason: "unavailable",
|
|
34594
|
+
elapsed_ms: Date.now() - startedAt
|
|
34595
|
+
});
|
|
34596
|
+
}
|
|
34597
|
+
await sleep(Math.min(BASH_WAIT_POLL_INTERVAL_MS, Math.max(0, deadline - Date.now())));
|
|
34598
|
+
continue;
|
|
34599
|
+
}
|
|
34600
|
+
lastData = data;
|
|
34290
34601
|
const terminal = isTerminalStatus(data.status);
|
|
34291
34602
|
if (waitFor) {
|
|
34292
34603
|
const scan = await readNewTaskOutput(extCtx, taskId, data, spillCursor);
|
|
@@ -34478,6 +34789,9 @@ function formatWaitSummary(waited, details) {
|
|
|
34478
34789
|
if (waited.reason === "timeout") {
|
|
34479
34790
|
return `Waited ${waited.elapsed_ms}ms; timeout reached without match.`;
|
|
34480
34791
|
}
|
|
34792
|
+
if (waited.reason === "unavailable") {
|
|
34793
|
+
return `Waited ${waited.elapsed_ms}ms; the bridge stayed busy and status couldn't be read. The task may still be running — check with bash_status({ taskId }).`;
|
|
34794
|
+
}
|
|
34481
34795
|
const exit = typeof details.exit_code === "number" ? `, exit ${details.exit_code}` : "";
|
|
34482
34796
|
return `Waited ${waited.elapsed_ms}ms; task exited (${details.status}${exit}).`;
|
|
34483
34797
|
}
|
|
@@ -34647,7 +34961,7 @@ function registerConflictsTool(pi, ctx) {
|
|
|
34647
34961
|
const reqParams = {};
|
|
34648
34962
|
const path3 = params?.path;
|
|
34649
34963
|
if (typeof path3 === "string" && path3.trim() !== "") {
|
|
34650
|
-
await assertExternalDirectoryPermission(extCtx, path3,
|
|
34964
|
+
await assertExternalDirectoryPermission(extCtx, path3, {
|
|
34651
34965
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
34652
34966
|
});
|
|
34653
34967
|
reqParams.path = await resolvePathArg(extCtx.cwd, path3);
|
|
@@ -34703,10 +35017,10 @@ function renderFsResult(toolName, args, result, theme, context) {
|
|
|
34703
35017
|
const skipped = data.skipped_files ?? [];
|
|
34704
35018
|
const lines = [];
|
|
34705
35019
|
for (const entry of deletedPaths) {
|
|
34706
|
-
lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent",
|
|
35020
|
+
lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent", shortenPath3(entry))}`);
|
|
34707
35021
|
}
|
|
34708
35022
|
for (const entry of skipped) {
|
|
34709
|
-
lines.push(`${theme.fg("error", "✗ skipped")} ${theme.fg("accent",
|
|
35023
|
+
lines.push(`${theme.fg("error", "✗ skipped")} ${theme.fg("accent", shortenPath3(entry.file))} ${theme.fg("muted", `(${entry.reason})`)}`);
|
|
34710
35024
|
}
|
|
34711
35025
|
if (lines.length === 0) {
|
|
34712
35026
|
lines.push(theme.fg("muted", "(no files deleted)"));
|
|
@@ -34716,8 +35030,8 @@ function renderFsResult(toolName, args, result, theme, context) {
|
|
|
34716
35030
|
}
|
|
34717
35031
|
const moveArgs = args;
|
|
34718
35032
|
return renderSections([
|
|
34719
|
-
`${theme.fg("success", "✓ moved")} ${theme.fg("accent",
|
|
34720
|
-
`${theme.fg("muted", "to")} ${theme.fg("accent",
|
|
35033
|
+
`${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath3(moveArgs.filePath))}`,
|
|
35034
|
+
`${theme.fg("muted", "to")} ${theme.fg("accent", shortenPath3(moveArgs.destination))}`
|
|
34721
35035
|
], context);
|
|
34722
35036
|
}
|
|
34723
35037
|
function registerFsTools(pi, ctx, surface) {
|
|
@@ -34738,7 +35052,7 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
34738
35052
|
if (checked.has(file2))
|
|
34739
35053
|
continue;
|
|
34740
35054
|
checked.add(file2);
|
|
34741
|
-
await assertExternalDirectoryPermission(extCtx, file2,
|
|
35055
|
+
await assertExternalDirectoryPermission(extCtx, file2, {
|
|
34742
35056
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
34743
35057
|
});
|
|
34744
35058
|
}
|
|
@@ -34782,7 +35096,7 @@ function registerFsTools(pi, ctx, surface) {
|
|
|
34782
35096
|
const destination = await resolvePathArg(extCtx.cwd, params.destination);
|
|
34783
35097
|
const checked = new Set([filePath, destination]);
|
|
34784
35098
|
for (const file2 of checked) {
|
|
34785
|
-
await assertExternalDirectoryPermission(extCtx, file2,
|
|
35099
|
+
await assertExternalDirectoryPermission(extCtx, file2, {
|
|
34786
35100
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
34787
35101
|
});
|
|
34788
35102
|
}
|
|
@@ -34831,33 +35145,33 @@ var ImportParams = Type7.Object({
|
|
|
34831
35145
|
}))
|
|
34832
35146
|
});
|
|
34833
35147
|
function buildImportSections(args, payload, theme) {
|
|
34834
|
-
const response =
|
|
35148
|
+
const response = asRecord3(payload);
|
|
34835
35149
|
if (!response)
|
|
34836
35150
|
return [theme.fg("muted", "No import result.")];
|
|
34837
35151
|
if (args.op === "organize") {
|
|
34838
|
-
const groups =
|
|
34839
|
-
const groupText = groups.length > 0 ? groups.map((group) => `${
|
|
35152
|
+
const groups = asRecords2(response.groups);
|
|
35153
|
+
const groupText = groups.length > 0 ? groups.map((group) => `${asString2(group.name) ?? "unknown"}: ${asNumber2(group.count) ?? 0}`).join(" · ") : "No imports found";
|
|
34840
35154
|
return [
|
|
34841
|
-
`${theme.fg("success", "organized")} ${theme.fg("accent",
|
|
35155
|
+
`${theme.fg("success", "organized")} ${theme.fg("accent", asString2(response.file) ?? args.filePath)}`,
|
|
34842
35156
|
`${theme.fg("muted", "groups")} ${groupText}`,
|
|
34843
|
-
`${theme.fg("muted", "duplicates removed")} ${
|
|
35157
|
+
`${theme.fg("muted", "duplicates removed")} ${asNumber2(response.removed_duplicates) ?? 0}`
|
|
34844
35158
|
];
|
|
34845
35159
|
}
|
|
34846
35160
|
if (args.op === "add") {
|
|
34847
|
-
const moduleName2 =
|
|
35161
|
+
const moduleName2 = asString2(response.module) ?? args.module ?? "(module)";
|
|
34848
35162
|
const status = response.already_present === true ? theme.fg("warning", "already present") : theme.fg("success", "added");
|
|
34849
35163
|
return [
|
|
34850
35164
|
`${status} ${theme.fg("accent", moduleName2)}`,
|
|
34851
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent",
|
|
34852
|
-
`${theme.fg("muted", "group")} ${
|
|
35165
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.filePath)}`,
|
|
35166
|
+
`${theme.fg("muted", "group")} ${asString2(response.group) ?? "—"}`
|
|
34853
35167
|
];
|
|
34854
35168
|
}
|
|
34855
|
-
const moduleName =
|
|
35169
|
+
const moduleName = asString2(response.module) ?? args.module ?? "(module)";
|
|
34856
35170
|
const didRemove = response.removed !== false;
|
|
34857
35171
|
const removeStatus = didRemove ? `${theme.fg("success", "removed")} ${theme.fg("accent", moduleName)}` : `${theme.fg("warning", "not present")} ${theme.fg("accent", moduleName)}`;
|
|
34858
35172
|
return [
|
|
34859
35173
|
removeStatus,
|
|
34860
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent",
|
|
35174
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", asString2(response.file) ?? args.filePath)}`,
|
|
34861
35175
|
args.removeName ? `${theme.fg("muted", "name")} ${args.removeName}` : `${theme.fg("muted", "scope")} entire import`
|
|
34862
35176
|
];
|
|
34863
35177
|
}
|
|
@@ -34886,7 +35200,7 @@ function registerImportTools(pi, ctx) {
|
|
|
34886
35200
|
throw new Error(`op='${params.op}' requires 'module'`);
|
|
34887
35201
|
}
|
|
34888
35202
|
const filePath = await resolvePathArg(extCtx.cwd, params.filePath);
|
|
34889
|
-
await assertExternalDirectoryPermission(extCtx, filePath,
|
|
35203
|
+
await assertExternalDirectoryPermission(extCtx, filePath, {
|
|
34890
35204
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
34891
35205
|
});
|
|
34892
35206
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
@@ -34962,7 +35276,7 @@ async function resolveAndGateScope(extCtx, ctx, scope) {
|
|
|
34962
35276
|
if (checked.has(target))
|
|
34963
35277
|
continue;
|
|
34964
35278
|
checked.add(target);
|
|
34965
|
-
await assertExternalDirectoryPermission(extCtx, target,
|
|
35279
|
+
await assertExternalDirectoryPermission(extCtx, target, {
|
|
34966
35280
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
34967
35281
|
});
|
|
34968
35282
|
}
|
|
@@ -34990,16 +35304,16 @@ function diagnosticsServerSummary(section) {
|
|
|
34990
35304
|
return parts.length > 0 ? parts.join("; ") : "none reported";
|
|
34991
35305
|
}
|
|
34992
35306
|
function diagnosticsSummaryPart(summary) {
|
|
34993
|
-
const section =
|
|
35307
|
+
const section = asRecord3(summary?.diagnostics);
|
|
34994
35308
|
if (!section)
|
|
34995
35309
|
return;
|
|
34996
|
-
const errors3 =
|
|
34997
|
-
const warnings =
|
|
34998
|
-
const info =
|
|
34999
|
-
const hints =
|
|
35310
|
+
const errors3 = asNumber2(section.errors);
|
|
35311
|
+
const warnings = asNumber2(section.warnings);
|
|
35312
|
+
const info = asNumber2(section.info);
|
|
35313
|
+
const hints = asNumber2(section.hints);
|
|
35000
35314
|
const hasCounts = [errors3, warnings, info, hints].some((value) => value !== undefined);
|
|
35001
35315
|
const counts = `${errors3 ?? 0} errors/${warnings ?? 0} warnings/${info ?? 0} info/${hints ?? 0} hints`;
|
|
35002
|
-
const status =
|
|
35316
|
+
const status = asString2(section.status);
|
|
35003
35317
|
if (status === "pending") {
|
|
35004
35318
|
return hasCounts ? `diagnostics ${counts} so far — still pending (servers: ${diagnosticsServerSummary(section)})` : `diagnostics pending (servers: ${diagnosticsServerSummary(section)})`;
|
|
35005
35319
|
}
|
|
@@ -35012,9 +35326,9 @@ function diagnosticsSummaryPart(summary) {
|
|
|
35012
35326
|
return;
|
|
35013
35327
|
}
|
|
35014
35328
|
function diagnosticLocation(diagnostic) {
|
|
35015
|
-
const file2 =
|
|
35016
|
-
const line =
|
|
35017
|
-
const column =
|
|
35329
|
+
const file2 = asString2(diagnostic.file) ?? "(unknown file)";
|
|
35330
|
+
const line = asNumber2(diagnostic.line);
|
|
35331
|
+
const column = asNumber2(diagnostic.column);
|
|
35018
35332
|
if (line === undefined)
|
|
35019
35333
|
return file2;
|
|
35020
35334
|
if (column === undefined)
|
|
@@ -35022,14 +35336,14 @@ function diagnosticLocation(diagnostic) {
|
|
|
35022
35336
|
return `${file2}:${line}:${column}`;
|
|
35023
35337
|
}
|
|
35024
35338
|
function diagnosticsDetailSection(details) {
|
|
35025
|
-
const diagnostics =
|
|
35339
|
+
const diagnostics = asRecords2(details?.diagnostics);
|
|
35026
35340
|
if (diagnostics.length === 0)
|
|
35027
35341
|
return;
|
|
35028
35342
|
const lines = ["diagnostics"];
|
|
35029
35343
|
for (const diagnostic of diagnostics) {
|
|
35030
|
-
const severity =
|
|
35031
|
-
const message =
|
|
35032
|
-
const source =
|
|
35344
|
+
const severity = asString2(diagnostic.severity) ?? "information";
|
|
35345
|
+
const message = asString2(diagnostic.message) ?? "(no message)";
|
|
35346
|
+
const source = asString2(diagnostic.source);
|
|
35033
35347
|
const suffix = source ? ` [${source}]` : "";
|
|
35034
35348
|
lines.push(`- ${diagnosticLocation(diagnostic)} ${severity} ${message}${suffix}`);
|
|
35035
35349
|
}
|
|
@@ -35037,15 +35351,15 @@ function diagnosticsDetailSection(details) {
|
|
|
35037
35351
|
`);
|
|
35038
35352
|
}
|
|
35039
35353
|
function countFrom(summary, key) {
|
|
35040
|
-
const section =
|
|
35041
|
-
return
|
|
35354
|
+
const section = asRecord3(summary?.[key]);
|
|
35355
|
+
return asNumber2(section?.count);
|
|
35042
35356
|
}
|
|
35043
35357
|
function tier2SummaryPart(summary, key, label) {
|
|
35044
|
-
const section =
|
|
35045
|
-
const count =
|
|
35358
|
+
const section = asRecord3(summary?.[key]);
|
|
35359
|
+
const count = asNumber2(section?.count);
|
|
35046
35360
|
if (count !== undefined)
|
|
35047
35361
|
return `${label} ${count}`;
|
|
35048
|
-
const status =
|
|
35362
|
+
const status = asString2(section?.status);
|
|
35049
35363
|
return `${label} ${status ?? "unavailable"}`;
|
|
35050
35364
|
}
|
|
35051
35365
|
function shortDupOccurrence(entry) {
|
|
@@ -35054,12 +35368,12 @@ function shortDupOccurrence(entry) {
|
|
|
35054
35368
|
}
|
|
35055
35369
|
function tier2TopPreview(summary, theme) {
|
|
35056
35370
|
const lines = [];
|
|
35057
|
-
const dup =
|
|
35371
|
+
const dup = asRecord3(summary?.duplicates);
|
|
35058
35372
|
const dupTop = Array.isArray(dup?.top) ? dup.top : [];
|
|
35059
35373
|
for (const group of dupTop) {
|
|
35060
|
-
const record2 =
|
|
35374
|
+
const record2 = asRecord3(group);
|
|
35061
35375
|
const files = Array.isArray(record2?.files) ? record2.files : [];
|
|
35062
|
-
const cost =
|
|
35376
|
+
const cost = asNumber2(record2?.cost);
|
|
35063
35377
|
if (files.length < 2)
|
|
35064
35378
|
continue;
|
|
35065
35379
|
const a = shortDupOccurrence(String(files[0]));
|
|
@@ -35070,12 +35384,12 @@ function tier2TopPreview(summary, theme) {
|
|
|
35070
35384
|
["dead_code", "dead"],
|
|
35071
35385
|
["unused_exports", "unused"]
|
|
35072
35386
|
]) {
|
|
35073
|
-
const section =
|
|
35387
|
+
const section = asRecord3(summary?.[key]);
|
|
35074
35388
|
const top = Array.isArray(section?.top) ? section.top : [];
|
|
35075
35389
|
for (const item of top) {
|
|
35076
|
-
const record2 =
|
|
35077
|
-
const file2 =
|
|
35078
|
-
const symbol2 =
|
|
35390
|
+
const record2 = asRecord3(item);
|
|
35391
|
+
const file2 = asString2(record2?.file);
|
|
35392
|
+
const symbol2 = asString2(record2?.symbol);
|
|
35079
35393
|
if (!file2 || !symbol2)
|
|
35080
35394
|
continue;
|
|
35081
35395
|
lines.push(` ${label} ${symbol2} (${file2.split("/").pop()})`);
|
|
@@ -35088,7 +35402,7 @@ ${lines.join(`
|
|
|
35088
35402
|
`)}`;
|
|
35089
35403
|
}
|
|
35090
35404
|
function tier2RefreshCategories(response) {
|
|
35091
|
-
const scannerState =
|
|
35405
|
+
const scannerState = asRecord3(response.scanner_state);
|
|
35092
35406
|
const categories = new Set;
|
|
35093
35407
|
for (const key of ["pending_categories", "stale_categories"]) {
|
|
35094
35408
|
const values = scannerState?.[key];
|
|
@@ -35135,18 +35449,18 @@ function runPendingTier2Categories(bridge, categories, extCtx) {
|
|
|
35135
35449
|
});
|
|
35136
35450
|
}
|
|
35137
35451
|
function buildInspectSections(payload, theme) {
|
|
35138
|
-
const response =
|
|
35452
|
+
const response = asRecord3(payload);
|
|
35139
35453
|
if (!response)
|
|
35140
35454
|
return [theme.fg("muted", "No inspect snapshot available.")];
|
|
35141
|
-
const summary =
|
|
35142
|
-
const metrics =
|
|
35143
|
-
const scannerState =
|
|
35455
|
+
const summary = asRecord3(response.summary);
|
|
35456
|
+
const metrics = asRecord3(summary?.metrics);
|
|
35457
|
+
const scannerState = asRecord3(response.scanner_state);
|
|
35144
35458
|
const stale = Array.isArray(scannerState?.stale_categories) ? scannerState.stale_categories.length : 0;
|
|
35145
35459
|
const pending = Array.isArray(scannerState?.pending_categories) ? scannerState.pending_categories.length : 0;
|
|
35146
35460
|
const parts = [
|
|
35147
35461
|
`todos ${countFrom(summary, "todos") ?? 0}`,
|
|
35148
35462
|
diagnosticsSummaryPart(summary),
|
|
35149
|
-
`metrics ${
|
|
35463
|
+
`metrics ${asNumber2(metrics?.files) ?? 0} files/${asNumber2(metrics?.symbols) ?? 0} symbols`,
|
|
35150
35464
|
tier2SummaryPart(summary, "dead_code", "dead code"),
|
|
35151
35465
|
tier2SummaryPart(summary, "unused_exports", "unused exports"),
|
|
35152
35466
|
tier2SummaryPart(summary, "duplicates", "duplicates")
|
|
@@ -35158,7 +35472,7 @@ function buildInspectSections(payload, theme) {
|
|
|
35158
35472
|
const topPreview = tier2TopPreview(summary, theme);
|
|
35159
35473
|
if (topPreview)
|
|
35160
35474
|
sections.push(topPreview);
|
|
35161
|
-
const details =
|
|
35475
|
+
const details = asRecord3(response.details);
|
|
35162
35476
|
if (details) {
|
|
35163
35477
|
const names = Object.keys(details);
|
|
35164
35478
|
sections.push(names.length > 0 ? `details: ${names.join(", ")}` : theme.fg("muted", "No drill-down details returned."));
|
|
@@ -35166,7 +35480,7 @@ function buildInspectSections(payload, theme) {
|
|
|
35166
35480
|
if (diagnosticsDetails)
|
|
35167
35481
|
sections.push(diagnosticsDetails);
|
|
35168
35482
|
}
|
|
35169
|
-
const text =
|
|
35483
|
+
const text = asString2(response.text);
|
|
35170
35484
|
if (text)
|
|
35171
35485
|
sections.push(text);
|
|
35172
35486
|
return sections;
|
|
@@ -35199,7 +35513,10 @@ function registerInspectTool(pi, ctx) {
|
|
|
35199
35513
|
runPendingTier2Categories(bridge, tier2RefreshCategories(response), extCtx);
|
|
35200
35514
|
const body = response.text;
|
|
35201
35515
|
if (typeof body === "string") {
|
|
35202
|
-
const
|
|
35516
|
+
const diagnosticsSummary = diagnosticsSummaryPart(asRecord3(response.summary));
|
|
35517
|
+
const diagnosticsDetail = diagnosticsDetailSection(asRecord3(response.details));
|
|
35518
|
+
const diagnostics = [diagnosticsSummary, diagnosticsDetail].filter(Boolean).join(`
|
|
35519
|
+
`);
|
|
35203
35520
|
const text = diagnostics ? body ? `${body}
|
|
35204
35521
|
|
|
35205
35522
|
${diagnostics}` : diagnostics : body;
|
|
@@ -35239,138 +35556,11 @@ function navigateParamsSchema() {
|
|
|
35239
35556
|
}))
|
|
35240
35557
|
});
|
|
35241
35558
|
}
|
|
35242
|
-
function treeLine(depth, text) {
|
|
35243
|
-
return `${" ".repeat(depth)}${depth === 0 ? "" : "↳ "}${text}`;
|
|
35244
|
-
}
|
|
35245
|
-
function renderCallTreeNode(node, depth, lines) {
|
|
35246
|
-
const name = asString(node.name) ?? "(unknown)";
|
|
35247
|
-
const file2 = shortenPath2(asString(node.file) ?? "(unknown file)");
|
|
35248
|
-
const line = asNumber(node.line);
|
|
35249
|
-
lines.push(treeLine(depth, `${name} ${line !== undefined ? `[${file2}:${line}]` : `[${file2}]`}`));
|
|
35250
|
-
asRecords(node.children).forEach((child) => {
|
|
35251
|
-
renderCallTreeNode(child, depth + 1, lines);
|
|
35252
|
-
});
|
|
35253
|
-
}
|
|
35254
|
-
function depthWarning(response, theme, depthField = "depth_limited", truncatedField = "truncated") {
|
|
35255
|
-
const limited = asBoolean(response[depthField]);
|
|
35256
|
-
const truncated = asNumber(response[truncatedField]) ?? 0;
|
|
35257
|
-
if (!limited && truncated === 0)
|
|
35258
|
-
return "";
|
|
35259
|
-
const detail = truncated > 0 ? `, ${truncated} truncated` : "";
|
|
35260
|
-
return theme.fg("warning", `(depth limited${detail})`);
|
|
35261
|
-
}
|
|
35262
|
-
function renderTracePath(path3, index, lines) {
|
|
35263
|
-
lines.push(`Path ${index + 1}`);
|
|
35264
|
-
asRecords(path3.hops).forEach((hop, hopIndex) => {
|
|
35265
|
-
const symbol2 = asString(hop.symbol) ?? "(unknown)";
|
|
35266
|
-
const file2 = shortenPath2(asString(hop.file) ?? "(unknown file)");
|
|
35267
|
-
const line = asNumber(hop.line);
|
|
35268
|
-
const entry = hop.is_entry_point === true ? " [entry]" : "";
|
|
35269
|
-
lines.push(treeLine(hopIndex + 1, `${symbol2}${entry} ${line !== undefined ? `[${file2}:${line}]` : `[${file2}]`}`));
|
|
35270
|
-
});
|
|
35271
|
-
}
|
|
35272
35559
|
function buildNavigateSections(args, payload, theme) {
|
|
35273
|
-
const
|
|
35274
|
-
|
|
35275
|
-
|
|
35276
|
-
|
|
35277
|
-
const lines = [];
|
|
35278
|
-
renderCallTreeNode(response, 0, lines);
|
|
35279
|
-
const warning = depthWarning(response, theme);
|
|
35280
|
-
if (warning)
|
|
35281
|
-
lines.push(warning);
|
|
35282
|
-
return lines.length > 0 ? lines : [theme.fg("muted", "No call tree available.")];
|
|
35283
|
-
}
|
|
35284
|
-
if (args.op === "callers") {
|
|
35285
|
-
const groups = asRecords(response.callers);
|
|
35286
|
-
const warning = depthWarning(response, theme);
|
|
35287
|
-
const sections2 = [
|
|
35288
|
-
`${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()
|
|
35289
|
-
];
|
|
35290
|
-
groups.forEach((group) => {
|
|
35291
|
-
const file2 = shortenPath2(asString(group.file) ?? "(unknown file)");
|
|
35292
|
-
const lines = [theme.fg("accent", file2)];
|
|
35293
|
-
asRecords(group.callers).forEach((caller) => {
|
|
35294
|
-
lines.push(` ↳ ${asString(caller.symbol) ?? "(unknown)"} ${theme.fg("muted", `line ${asNumber(caller.line) ?? "?"}`)}`);
|
|
35295
|
-
});
|
|
35296
|
-
sections2.push(lines.join(`
|
|
35297
|
-
`));
|
|
35298
|
-
});
|
|
35299
|
-
return sections2;
|
|
35300
|
-
}
|
|
35301
|
-
if (args.op === "trace_to_symbol") {
|
|
35302
|
-
const path3 = asRecords(response.path);
|
|
35303
|
-
const complete = asBoolean(response.complete);
|
|
35304
|
-
const reason = asString(response.reason);
|
|
35305
|
-
if (path3.length === 0) {
|
|
35306
|
-
const prefix = complete === false ? theme.fg("warning", "No complete path") : theme.fg("muted", "No path");
|
|
35307
|
-
return [`${prefix}${reason ? ` (${reason})` : ""}`];
|
|
35308
|
-
}
|
|
35309
|
-
const lines = [theme.fg("success", `${path3.length} hop${path3.length === 1 ? "" : "s"}`)];
|
|
35310
|
-
path3.forEach((hop, index) => {
|
|
35311
|
-
const symbol2 = asString(hop.symbol) ?? "(unknown)";
|
|
35312
|
-
const file2 = shortenPath2(asString(hop.file) ?? "(unknown file)");
|
|
35313
|
-
const line = asNumber(hop.line);
|
|
35314
|
-
lines.push(treeLine(index + 1, `${symbol2} ${line !== undefined ? `[${file2}:${line}]` : `[${file2}]`}`));
|
|
35315
|
-
});
|
|
35316
|
-
return lines;
|
|
35317
|
-
}
|
|
35318
|
-
if (args.op === "trace_to") {
|
|
35319
|
-
const paths = asRecords(response.paths);
|
|
35320
|
-
const warning = depthWarning(response, theme, "max_depth_reached", "truncated_paths");
|
|
35321
|
-
const sections2 = [
|
|
35322
|
-
`${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()
|
|
35323
|
-
];
|
|
35324
|
-
if (paths.length === 0)
|
|
35325
|
-
sections2.push(theme.fg("muted", "No entry paths found."));
|
|
35326
|
-
paths.forEach((path3, index) => {
|
|
35327
|
-
const lines = [];
|
|
35328
|
-
renderTracePath(path3, index, lines);
|
|
35329
|
-
sections2.push(lines.join(`
|
|
35330
|
-
`));
|
|
35331
|
-
});
|
|
35332
|
-
return sections2;
|
|
35333
|
-
}
|
|
35334
|
-
if (args.op === "impact") {
|
|
35335
|
-
const callers = asRecords(response.callers);
|
|
35336
|
-
const warning = depthWarning(response, theme);
|
|
35337
|
-
const sections2 = [
|
|
35338
|
-
`${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()
|
|
35339
|
-
];
|
|
35340
|
-
if (callers.length === 0)
|
|
35341
|
-
sections2.push(theme.fg("muted", "No impacted callers found."));
|
|
35342
|
-
callers.forEach((caller) => {
|
|
35343
|
-
const file2 = shortenPath2(asString(caller.caller_file) ?? "(unknown file)");
|
|
35344
|
-
const symbol2 = asString(caller.caller_symbol) ?? "(unknown)";
|
|
35345
|
-
const line = asNumber(caller.line) ?? 0;
|
|
35346
|
-
const entry = caller.is_entry_point === true ? ` ${theme.fg("warning", "[entry]")}` : "";
|
|
35347
|
-
const expression = asString(caller.call_expression);
|
|
35348
|
-
const params = Array.isArray(caller.parameters) ? caller.parameters.map(String).join(", ") : "";
|
|
35349
|
-
sections2.push([
|
|
35350
|
-
`${theme.fg("accent", file2)}:${line}`,
|
|
35351
|
-
` ↳ ${symbol2}${entry}`,
|
|
35352
|
-
expression ? ` ${theme.fg("muted", expression)}` : undefined,
|
|
35353
|
-
params ? ` ${theme.fg("muted", `params: ${params}`)}` : undefined
|
|
35354
|
-
].filter(Boolean).join(`
|
|
35355
|
-
`));
|
|
35356
|
-
});
|
|
35357
|
-
return sections2;
|
|
35358
|
-
}
|
|
35359
|
-
const hops = asRecords(response.hops);
|
|
35360
|
-
const sections = [
|
|
35361
|
-
`${theme.fg("success", `${hops.length} hop${hops.length === 1 ? "" : "s"}`)} ${asBoolean(response.depth_limited) ? theme.fg("warning", "(depth limited)") : ""}`.trim()
|
|
35362
|
-
];
|
|
35363
|
-
if (hops.length === 0)
|
|
35364
|
-
sections.push(theme.fg("muted", "No data-flow hops found."));
|
|
35365
|
-
hops.forEach((hop, index) => {
|
|
35366
|
-
const file2 = shortenPath2(asString(hop.file) ?? "(unknown file)");
|
|
35367
|
-
const symbol2 = asString(hop.symbol) ?? "(unknown)";
|
|
35368
|
-
const variable = asString(hop.variable) ?? "(unknown)";
|
|
35369
|
-
const line = asNumber(hop.line) ?? 0;
|
|
35370
|
-
const approximate = hop.approximate === true ? ` ${theme.fg("warning", "[approx]")}` : "";
|
|
35371
|
-
sections.push(treeLine(index, `${variable} ${theme.fg("muted", `${asString(hop.flow_type) ?? "flow"}`)} ${symbol2} [${file2}:${line}]${approximate}`));
|
|
35372
|
-
});
|
|
35373
|
-
return sections;
|
|
35560
|
+
const themeAdapter = {
|
|
35561
|
+
fg: (role, s) => theme.fg(role, s)
|
|
35562
|
+
};
|
|
35563
|
+
return formatCallgraphSections(args.op, payload, themeAdapter);
|
|
35374
35564
|
}
|
|
35375
35565
|
function renderNavigateCall(args, theme, context) {
|
|
35376
35566
|
const summary = [
|
|
@@ -35390,7 +35580,7 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35390
35580
|
pi.registerTool({
|
|
35391
35581
|
name: "aft_callgraph",
|
|
35392
35582
|
label: "callgraph",
|
|
35393
|
-
description: "Answer code-relationship questions from a real call graph — instead of grep + read chains. Reach for this whenever the question is about how symbols connect. All ops require both `filePath` and `symbol`. Use `callers` for call sites (before renaming/signature changes), `impact` for blast radius (what breaks if a symbol changes), `call_tree` for what a function calls, `trace_to` for how execution reaches a symbol from entry points, `trace_to_symbol` for the shortest path from one symbol to another (requires `toSymbol`; if ambiguous, the error returns candidate files — retry with `toFile`), `trace_data` to follow a value across assignments/params.",
|
|
35583
|
+
description: "Answer code-relationship questions from a real call graph — instead of grep + read chains. Reach for this whenever the question is about how symbols connect. All ops require both `filePath` and `symbol`. Use `callers` for call sites (before renaming/signature changes), `impact` for blast radius (what breaks if a symbol changes), `call_tree` for what a function calls, `trace_to` for how execution reaches a symbol from entry points, `trace_to_symbol` for the shortest path from one symbol to another (requires `toSymbol`; if ambiguous, the error returns candidate files — retry with `toFile`), `trace_data` to follow a value across assignments/params. Markers: ~ = edge resolved by name only (may point at the wrong same-named symbol); [unresolved] = callee not resolved to a definition, so the location shown is the call site. Unmarked edges are resolved exactly.",
|
|
35394
35584
|
parameters: navigateParamsSchema(),
|
|
35395
35585
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
35396
35586
|
if (isEmptyParam(params.filePath)) {
|
|
@@ -35412,7 +35602,7 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35412
35602
|
if (checked.has(target))
|
|
35413
35603
|
continue;
|
|
35414
35604
|
checked.add(target);
|
|
35415
|
-
await assertExternalDirectoryPermission(extCtx, target,
|
|
35605
|
+
await assertExternalDirectoryPermission(extCtx, target, {
|
|
35416
35606
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
35417
35607
|
});
|
|
35418
35608
|
}
|
|
@@ -35433,7 +35623,8 @@ function registerNavigateTool(pi, ctx) {
|
|
|
35433
35623
|
req.toFile = toFile;
|
|
35434
35624
|
try {
|
|
35435
35625
|
const response = await callBridge(bridge, params.op, req, extCtx);
|
|
35436
|
-
return textResult(
|
|
35626
|
+
return textResult(formatCallgraphSections(params.op, response, PLAIN_CALLGRAPH_THEME).join(`
|
|
35627
|
+
`), response);
|
|
35437
35628
|
} catch (error50) {
|
|
35438
35629
|
if (error50 instanceof BridgeError && CALLGRAPH_SOFT_CODES.has(error50.code)) {
|
|
35439
35630
|
return textResult(error50.message);
|
|
@@ -35491,7 +35682,7 @@ async function assertReadPathPermissions(extCtx, ctx, paths) {
|
|
|
35491
35682
|
if (!target || checked.has(target))
|
|
35492
35683
|
continue;
|
|
35493
35684
|
checked.add(target);
|
|
35494
|
-
await assertExternalDirectoryPermission(extCtx, target,
|
|
35685
|
+
await assertExternalDirectoryPermission(extCtx, target, {
|
|
35495
35686
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
35496
35687
|
});
|
|
35497
35688
|
}
|
|
@@ -35511,25 +35702,25 @@ function buildOutlineSections(text, theme) {
|
|
|
35511
35702
|
`)];
|
|
35512
35703
|
}
|
|
35513
35704
|
function buildZoomSections(args, payload, theme) {
|
|
35514
|
-
const batch =
|
|
35705
|
+
const batch = asRecord3(payload);
|
|
35515
35706
|
const batchItems = Array.isArray(batch?.symbols) ? batch.symbols : Array.isArray(batch?.entries) ? batch.entries : null;
|
|
35516
35707
|
if (batchItems) {
|
|
35517
35708
|
const header = batch?.complete === false ? [theme.fg("warning", "Incomplete zoom results")] : [];
|
|
35518
35709
|
return [
|
|
35519
35710
|
...header,
|
|
35520
35711
|
...batchItems.map((item) => {
|
|
35521
|
-
const record2 =
|
|
35712
|
+
const record2 = asRecord3(item);
|
|
35522
35713
|
if (!record2)
|
|
35523
35714
|
return theme.fg("muted", "No zoom result available.");
|
|
35524
|
-
const name =
|
|
35525
|
-
const itemTargetLabel =
|
|
35715
|
+
const name = asString2(record2.name) ?? "(unknown symbol)";
|
|
35716
|
+
const itemTargetLabel = asString2(record2.targetLabel) ?? zoomTargetLabel(args);
|
|
35526
35717
|
if (record2.success === false) {
|
|
35527
|
-
const location = record2.targetLabel ? ` in ${
|
|
35528
|
-
return theme.fg("error", `Symbol "${name}" not found${location}: ${
|
|
35718
|
+
const location = record2.targetLabel ? ` in ${shortenPath3(itemTargetLabel)}` : "";
|
|
35719
|
+
return theme.fg("error", `Symbol "${name}" not found${location}: ${asString2(record2.error) ?? "zoom failed"}`);
|
|
35529
35720
|
}
|
|
35530
|
-
const content =
|
|
35721
|
+
const content = asString2(record2.content);
|
|
35531
35722
|
return [
|
|
35532
|
-
`${theme.fg("accent", name)} ${theme.fg("muted",
|
|
35723
|
+
`${theme.fg("accent", name)} ${theme.fg("muted", shortenPath3(itemTargetLabel))}`,
|
|
35533
35724
|
content
|
|
35534
35725
|
].filter(Boolean).join(`
|
|
35535
35726
|
`);
|
|
@@ -35540,32 +35731,32 @@ function buildZoomSections(args, payload, theme) {
|
|
|
35540
35731
|
if (items.length === 0)
|
|
35541
35732
|
return [theme.fg("muted", "No zoom result available.")];
|
|
35542
35733
|
return items.map((item) => {
|
|
35543
|
-
const record2 =
|
|
35734
|
+
const record2 = asRecord3(item);
|
|
35544
35735
|
if (!record2)
|
|
35545
35736
|
return theme.fg("muted", "No zoom result available.");
|
|
35546
|
-
const name =
|
|
35547
|
-
const kind =
|
|
35548
|
-
const range =
|
|
35737
|
+
const name = asString2(record2.name) ?? "(unknown symbol)";
|
|
35738
|
+
const kind = asString2(record2.kind) ?? "symbol";
|
|
35739
|
+
const range = asRecord3(record2.range);
|
|
35549
35740
|
const startLine = range && typeof range.start_line === "number" ? range.start_line : undefined;
|
|
35550
35741
|
const endLine = range && typeof range.end_line === "number" ? range.end_line : undefined;
|
|
35551
35742
|
const targetLabel = zoomTargetLabel(args);
|
|
35552
|
-
const location = startLine !== undefined ? `${
|
|
35743
|
+
const location = startLine !== undefined ? `${shortenPath3(targetLabel)}:${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : shortenPath3(targetLabel);
|
|
35553
35744
|
const lines = [`${theme.fg("accent", name)} ${theme.fg("muted", `[${kind}] ${location}`)}`];
|
|
35554
|
-
const content =
|
|
35745
|
+
const content = asString2(record2.content);
|
|
35555
35746
|
if (content) {
|
|
35556
35747
|
lines.push(content.split(`
|
|
35557
35748
|
`).map((line) => ` ${line}`).join(`
|
|
35558
35749
|
`));
|
|
35559
35750
|
}
|
|
35560
|
-
const annotations =
|
|
35561
|
-
const callsOut = annotations ?
|
|
35562
|
-
const calledBy = annotations ?
|
|
35751
|
+
const annotations = asRecord3(record2.annotations);
|
|
35752
|
+
const callsOut = annotations ? asRecords2(annotations.calls_out) : [];
|
|
35753
|
+
const calledBy = annotations ? asRecords2(annotations.called_by) : [];
|
|
35563
35754
|
if (callsOut.length > 0) {
|
|
35564
|
-
lines.push(`${theme.fg("muted", "calls out")}`, callsOut.map((call) => ` ↳ ${
|
|
35755
|
+
lines.push(`${theme.fg("muted", "calls out")}`, callsOut.map((call) => ` ↳ ${asString2(call.name) ?? "(unknown)"}${typeof call.line === "number" ? `:${call.line}` : ""}`).join(`
|
|
35565
35756
|
`));
|
|
35566
35757
|
}
|
|
35567
35758
|
if (calledBy.length > 0) {
|
|
35568
|
-
lines.push(`${theme.fg("muted", "called by")}`, calledBy.map((call) => ` ↳ ${
|
|
35759
|
+
lines.push(`${theme.fg("muted", "called by")}`, calledBy.map((call) => ` ↳ ${asString2(call.name) ?? "(unknown)"}${typeof call.line === "number" ? `:${call.line}` : ""}`).join(`
|
|
35569
35760
|
`));
|
|
35570
35761
|
}
|
|
35571
35762
|
return lines.join(`
|
|
@@ -35904,32 +36095,32 @@ var RefactorParams = Type11.Object({
|
|
|
35904
36095
|
callSiteLine: optionalInt(1, Number.MAX_SAFE_INTEGER)
|
|
35905
36096
|
});
|
|
35906
36097
|
function buildRefactorSections(args, payload, theme) {
|
|
35907
|
-
const response =
|
|
36098
|
+
const response = asRecord3(payload);
|
|
35908
36099
|
if (!response)
|
|
35909
36100
|
return [theme.fg("muted", "No refactor result.")];
|
|
35910
36101
|
if (args.op === "move") {
|
|
35911
|
-
const results =
|
|
36102
|
+
const results = asRecords2(response.results);
|
|
35912
36103
|
return [
|
|
35913
36104
|
`${theme.fg("success", "moved symbol")} ${theme.fg("toolOutput", args.symbol ?? "(symbol)")}`,
|
|
35914
|
-
`${theme.fg("muted", "files modified")} ${
|
|
35915
|
-
`${theme.fg("muted", "consumers updated")} ${
|
|
35916
|
-
results.length > 0 ? results.map((entry) => ` ↳ ${
|
|
36105
|
+
`${theme.fg("muted", "files modified")} ${asNumber2(response.files_modified) ?? results.length}`,
|
|
36106
|
+
`${theme.fg("muted", "consumers updated")} ${asNumber2(response.consumers_updated) ?? 0}`,
|
|
36107
|
+
results.length > 0 ? results.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(unknown file)")}`).join(`
|
|
35917
36108
|
`) : theme.fg("muted", "No files reported.")
|
|
35918
36109
|
];
|
|
35919
36110
|
}
|
|
35920
36111
|
if (args.op === "extract") {
|
|
35921
36112
|
return [
|
|
35922
|
-
`${theme.fg("success", "extracted")} ${theme.fg("toolOutput",
|
|
35923
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent",
|
|
36113
|
+
`${theme.fg("success", "extracted")} ${theme.fg("toolOutput", asString2(response.name) ?? args.name ?? "(function)")}`,
|
|
36114
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.filePath))}`,
|
|
35924
36115
|
`${theme.fg("muted", "params")} ${Array.isArray(response.parameters) ? response.parameters.join(", ") || "none" : "none"}`,
|
|
35925
|
-
`${theme.fg("muted", "return type")} ${
|
|
36116
|
+
`${theme.fg("muted", "return type")} ${asString2(response.return_type) ?? "unknown"}`
|
|
35926
36117
|
];
|
|
35927
36118
|
}
|
|
35928
36119
|
return [
|
|
35929
|
-
`${theme.fg("success", "inlined")} ${theme.fg("toolOutput",
|
|
35930
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent",
|
|
35931
|
-
`${theme.fg("muted", "context")} ${
|
|
35932
|
-
`${theme.fg("muted", "substitutions")} ${
|
|
36120
|
+
`${theme.fg("success", "inlined")} ${theme.fg("toolOutput", asString2(response.symbol) ?? args.symbol ?? "(symbol)")}`,
|
|
36121
|
+
`${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.filePath))}`,
|
|
36122
|
+
`${theme.fg("muted", "context")} ${asString2(response.call_context) ?? "unknown"}`,
|
|
36123
|
+
`${theme.fg("muted", "substitutions")} ${asNumber2(response.substitutions) ?? 0}`
|
|
35933
36124
|
];
|
|
35934
36125
|
}
|
|
35935
36126
|
function renderRefactorCall(args, theme, context) {
|
|
@@ -35986,7 +36177,7 @@ function registerRefactorTool(pi, ctx) {
|
|
|
35986
36177
|
if (checked.has(target))
|
|
35987
36178
|
continue;
|
|
35988
36179
|
checked.add(target);
|
|
35989
|
-
await assertExternalDirectoryPermission(extCtx, target,
|
|
36180
|
+
await assertExternalDirectoryPermission(extCtx, target, {
|
|
35990
36181
|
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
35991
36182
|
});
|
|
35992
36183
|
}
|
|
@@ -36038,34 +36229,34 @@ var SafetyParams = Type12.Object({
|
|
|
36038
36229
|
}))
|
|
36039
36230
|
});
|
|
36040
36231
|
function buildSafetySections(args, payload, theme) {
|
|
36041
|
-
const response =
|
|
36232
|
+
const response = asRecord3(payload);
|
|
36042
36233
|
if (!response)
|
|
36043
36234
|
return [theme.fg("muted", "No safety result.")];
|
|
36044
36235
|
if (args.op === "undo") {
|
|
36045
36236
|
if (response.operation === true) {
|
|
36046
36237
|
return [
|
|
36047
|
-
`${theme.fg("success", "restored operation")} ${theme.fg("accent",
|
|
36048
|
-
`${theme.fg("muted", "files")} ${
|
|
36238
|
+
`${theme.fg("success", "restored operation")} ${theme.fg("accent", asString2(response.op_id) ?? "(operation)")}`,
|
|
36239
|
+
`${theme.fg("muted", "files")} ${asNumber2(response.restored_count) ?? asRecords2(response.restored).length}`
|
|
36049
36240
|
];
|
|
36050
36241
|
}
|
|
36051
36242
|
return [
|
|
36052
|
-
`${theme.fg("success", "restored")} ${theme.fg("accent",
|
|
36053
|
-
`${theme.fg("muted", "backup")} ${
|
|
36243
|
+
`${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath3(asString2(response.path) ?? args.filePath ?? "(file)"))}`,
|
|
36244
|
+
`${theme.fg("muted", "backup")} ${asString2(response.backup_id) ?? "—"}`
|
|
36054
36245
|
];
|
|
36055
36246
|
}
|
|
36056
36247
|
if (args.op === "history") {
|
|
36057
|
-
const entries =
|
|
36248
|
+
const entries = asRecords2(response.entries);
|
|
36058
36249
|
const sections2 = [
|
|
36059
|
-
theme.fg("accent",
|
|
36250
|
+
theme.fg("accent", shortenPath3(asString2(response.file) ?? args.filePath ?? "(file)"))
|
|
36060
36251
|
];
|
|
36061
36252
|
if (entries.length === 0) {
|
|
36062
36253
|
sections2.push(theme.fg("muted", "No history entries."));
|
|
36063
36254
|
return sections2;
|
|
36064
36255
|
}
|
|
36065
36256
|
sections2.push(entries.map((entry, index) => {
|
|
36066
|
-
const backupId =
|
|
36257
|
+
const backupId = asString2(entry.backup_id) ?? `entry-${index + 1}`;
|
|
36067
36258
|
const timestamp = formatTimestamp(entry.timestamp) ?? "unknown time";
|
|
36068
|
-
const description =
|
|
36259
|
+
const description = asString2(entry.description) ?? "";
|
|
36069
36260
|
return `${index + 1}. ${backupId} ${theme.fg("muted", timestamp)}${description ? `
|
|
36070
36261
|
${description}` : ""}`;
|
|
36071
36262
|
}).join(`
|
|
@@ -36073,22 +36264,22 @@ function buildSafetySections(args, payload, theme) {
|
|
|
36073
36264
|
return sections2;
|
|
36074
36265
|
}
|
|
36075
36266
|
if (args.op === "checkpoint") {
|
|
36076
|
-
const skipped =
|
|
36267
|
+
const skipped = asRecords2(response.skipped);
|
|
36077
36268
|
return [
|
|
36078
|
-
`${theme.fg("success", "checkpoint created")} ${theme.fg("accent",
|
|
36079
|
-
`${theme.fg("muted", "files")} ${
|
|
36269
|
+
`${theme.fg("success", "checkpoint created")} ${theme.fg("accent", asString2(response.name) ?? args.name ?? "(checkpoint)")}`,
|
|
36270
|
+
`${theme.fg("muted", "files")} ${asNumber2(response.file_count) ?? 0}`,
|
|
36080
36271
|
skipped.length > 0 ? `${theme.fg("warning", "skipped")}
|
|
36081
|
-
${skipped.map((entry) => ` ↳ ${
|
|
36272
|
+
${skipped.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(file)")}: ${asString2(entry.error) ?? "unknown error"}`).join(`
|
|
36082
36273
|
`)}` : theme.fg("muted", "No skipped files.")
|
|
36083
36274
|
];
|
|
36084
36275
|
}
|
|
36085
36276
|
if (args.op === "restore") {
|
|
36086
36277
|
return [
|
|
36087
|
-
`${theme.fg("success", "checkpoint restored")} ${theme.fg("accent",
|
|
36088
|
-
`${theme.fg("muted", "files")} ${
|
|
36278
|
+
`${theme.fg("success", "checkpoint restored")} ${theme.fg("accent", asString2(response.name) ?? args.name ?? "(checkpoint)")}`,
|
|
36279
|
+
`${theme.fg("muted", "files")} ${asNumber2(response.file_count) ?? 0}`
|
|
36089
36280
|
];
|
|
36090
36281
|
}
|
|
36091
|
-
const checkpoints =
|
|
36282
|
+
const checkpoints = asRecords2(response.checkpoints);
|
|
36092
36283
|
const sections = [
|
|
36093
36284
|
theme.fg("accent", `${checkpoints.length} checkpoint${checkpoints.length === 1 ? "" : "s"}`)
|
|
36094
36285
|
];
|
|
@@ -36097,8 +36288,8 @@ ${skipped.map((entry) => ` ↳ ${shortenPath2(asString(entry.file) ?? "(file)")
|
|
|
36097
36288
|
return sections;
|
|
36098
36289
|
}
|
|
36099
36290
|
sections.push(checkpoints.map((checkpoint, index) => {
|
|
36100
|
-
const name =
|
|
36101
|
-
const count =
|
|
36291
|
+
const name = asString2(checkpoint.name) ?? `checkpoint-${index + 1}`;
|
|
36292
|
+
const count = asNumber2(checkpoint.file_count) ?? 0;
|
|
36102
36293
|
const created = formatTimestamp(checkpoint.created_at) ?? "unknown time";
|
|
36103
36294
|
return `${index + 1}. ${name} ${theme.fg("muted", `${count} file${count === 1 ? "" : "s"} · ${created}`)}`;
|
|
36104
36295
|
}).join(`
|
|
@@ -36139,7 +36330,7 @@ function registerSafetyTool(pi, ctx) {
|
|
|
36139
36330
|
previewReq.file = filePath;
|
|
36140
36331
|
const preview = await callBridge(bridge, "undo_preview", previewReq, extCtx);
|
|
36141
36332
|
for (const file2 of new Set(responsePaths(preview))) {
|
|
36142
|
-
await assertExternalDirectoryPermission(extCtx, file2,
|
|
36333
|
+
await assertExternalDirectoryPermission(extCtx, file2, {
|
|
36143
36334
|
restrictToProjectRoot
|
|
36144
36335
|
});
|
|
36145
36336
|
}
|
|
@@ -36152,7 +36343,7 @@ function registerSafetyTool(pi, ctx) {
|
|
|
36152
36343
|
if (checked.has(file2))
|
|
36153
36344
|
continue;
|
|
36154
36345
|
checked.add(file2);
|
|
36155
|
-
await assertExternalDirectoryPermission(extCtx, file2,
|
|
36346
|
+
await assertExternalDirectoryPermission(extCtx, file2, {
|
|
36156
36347
|
restrictToProjectRoot
|
|
36157
36348
|
});
|
|
36158
36349
|
}
|
|
@@ -36161,7 +36352,7 @@ function registerSafetyTool(pi, ctx) {
|
|
|
36161
36352
|
if (params.op === "restore" && params.name) {
|
|
36162
36353
|
const preview = await callBridge(bridge, "checkpoint_paths", { name: params.name }, extCtx);
|
|
36163
36354
|
for (const file2 of new Set(responsePaths(preview))) {
|
|
36164
|
-
await assertExternalDirectoryPermission(extCtx, file2,
|
|
36355
|
+
await assertExternalDirectoryPermission(extCtx, file2, {
|
|
36165
36356
|
restrictToProjectRoot
|
|
36166
36357
|
});
|
|
36167
36358
|
}
|
|
@@ -36242,13 +36433,13 @@ var SearchParams2 = Type13.Object({
|
|
|
36242
36433
|
}))
|
|
36243
36434
|
});
|
|
36244
36435
|
function buildSemanticSections(args, payload, theme) {
|
|
36245
|
-
const response =
|
|
36436
|
+
const response = asRecord3(payload);
|
|
36246
36437
|
if (!response)
|
|
36247
36438
|
return [theme.fg("muted", "No search result.")];
|
|
36248
|
-
const status =
|
|
36249
|
-
const semanticStatus =
|
|
36250
|
-
const interpretedAs =
|
|
36251
|
-
const queryKind =
|
|
36439
|
+
const status = asString2(response.status) ?? "unknown";
|
|
36440
|
+
const semanticStatus = asString2(response.semantic_status) ?? status;
|
|
36441
|
+
const interpretedAs = asString2(response.interpreted_as) ?? "unknown";
|
|
36442
|
+
const queryKind = asString2(response.query_kind);
|
|
36252
36443
|
const sections = [
|
|
36253
36444
|
`${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}`)}`
|
|
36254
36445
|
];
|
|
@@ -36260,34 +36451,34 @@ function buildSemanticSections(args, payload, theme) {
|
|
|
36260
36451
|
const honestyNote = semanticHonestyNote(response, theme);
|
|
36261
36452
|
if (honestyNote)
|
|
36262
36453
|
sections.push(honestyNote);
|
|
36263
|
-
const results =
|
|
36454
|
+
const results = asRecords2(response.results);
|
|
36264
36455
|
if (status !== "ready" && results.length === 0) {
|
|
36265
|
-
sections.push(
|
|
36456
|
+
sections.push(asString2(response.text) ?? theme.fg("muted", "Semantic index is not ready."));
|
|
36266
36457
|
return sections;
|
|
36267
36458
|
}
|
|
36268
36459
|
if (results.length === 0) {
|
|
36269
36460
|
sections.push(theme.fg("muted", "No matches found."));
|
|
36270
36461
|
return sections;
|
|
36271
36462
|
}
|
|
36272
|
-
const grouped = groupByFile(results, (result) =>
|
|
36463
|
+
const grouped = groupByFile(results, (result) => asString2(result.file));
|
|
36273
36464
|
for (const [file2, fileResults] of grouped.entries()) {
|
|
36274
|
-
const lines = [theme.fg("accent",
|
|
36465
|
+
const lines = [theme.fg("accent", shortenPath3(file2))];
|
|
36275
36466
|
fileResults.forEach((result) => {
|
|
36276
|
-
if (
|
|
36277
|
-
const line =
|
|
36278
|
-
const column =
|
|
36279
|
-
const lineText =
|
|
36467
|
+
if (asString2(result.kind) === "GrepLine") {
|
|
36468
|
+
const line = asNumber2(result.line);
|
|
36469
|
+
const column = asNumber2(result.column);
|
|
36470
|
+
const lineText = asString2(result.line_text) ?? "";
|
|
36280
36471
|
const location2 = line !== undefined ? `${line}${column !== undefined ? `:${column}` : ""}` : "?";
|
|
36281
36472
|
lines.push(` ↳ ${theme.fg("muted", `line ${location2}`)} ${lineText}`);
|
|
36282
36473
|
return;
|
|
36283
36474
|
}
|
|
36284
|
-
const score =
|
|
36285
|
-
const source =
|
|
36286
|
-
const kind =
|
|
36287
|
-
const location =
|
|
36475
|
+
const score = asNumber2(result.score);
|
|
36476
|
+
const source = asString2(result.source);
|
|
36477
|
+
const kind = asString2(result.kind) ?? "symbol";
|
|
36478
|
+
const location = asString2(result.location);
|
|
36288
36479
|
if (source === "lexical") {
|
|
36289
36480
|
lines.push(` ↳ ${theme.fg("muted", `[lexical match${score !== undefined ? ` — score ${score.toFixed(3)}` : ""}]`)}`);
|
|
36290
|
-
const snippet2 =
|
|
36481
|
+
const snippet2 = asString2(result.snippet);
|
|
36291
36482
|
if (snippet2) {
|
|
36292
36483
|
lines.push(...snippet2.split(`
|
|
36293
36484
|
`).map((line) => ` ${line}`));
|
|
@@ -36295,16 +36486,16 @@ function buildSemanticSections(args, payload, theme) {
|
|
|
36295
36486
|
return;
|
|
36296
36487
|
}
|
|
36297
36488
|
if (kind === "file_summary" || location === "[file summary]") {
|
|
36298
|
-
const summary =
|
|
36489
|
+
const summary = asString2(result.snippet) ?? asString2(result.name) ?? "(no summary)";
|
|
36299
36490
|
lines.push(` ↳ ${summary} ${theme.fg("muted", `[file summary${score !== undefined ? ` score ${score.toFixed(3)}` : ""}]`)}`);
|
|
36300
36491
|
return;
|
|
36301
36492
|
}
|
|
36302
|
-
const startLine =
|
|
36303
|
-
const endLine =
|
|
36493
|
+
const startLine = asNumber2(result.start_line);
|
|
36494
|
+
const endLine = asNumber2(result.end_line);
|
|
36304
36495
|
const range = startLine !== undefined ? `${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : "?";
|
|
36305
|
-
const name =
|
|
36496
|
+
const name = asString2(result.name) ?? "(unknown)";
|
|
36306
36497
|
lines.push(` ↳ ${name} ${theme.fg("muted", `[${kind}] lines ${range}${score !== undefined ? ` score ${score.toFixed(3)}` : ""}`)}`);
|
|
36307
|
-
const snippet =
|
|
36498
|
+
const snippet = asString2(result.snippet);
|
|
36308
36499
|
if (snippet) {
|
|
36309
36500
|
lines.push(...snippet.split(`
|
|
36310
36501
|
`).map((line) => ` ${line}`));
|
|
@@ -36416,11 +36607,11 @@ function buildWorkflowHints(opts) {
|
|
|
36416
36607
|
}
|
|
36417
36608
|
if (hasBgBash) {
|
|
36418
36609
|
sections.push([
|
|
36419
|
-
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`task_id\` immediately
|
|
36420
|
-
"1.
|
|
36421
|
-
"2.
|
|
36422
|
-
|
|
36423
|
-
|
|
36610
|
+
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`task_id\` immediately.`,
|
|
36611
|
+
"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).",
|
|
36612
|
+
"2. Useful parallel work available → end your turn; the completion reminder delivers the result. (Or spawn a subagent for the side work.)",
|
|
36613
|
+
"3. Want to react to a specific early output line → async `bash_watch` (background:true + pattern).",
|
|
36614
|
+
"Never loop `bash_status` to wait — it's a one-shot inspector, not a wait primitive."
|
|
36424
36615
|
].join(`
|
|
36425
36616
|
`));
|
|
36426
36617
|
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: "..." })\`.`);
|
|
@@ -36535,7 +36726,7 @@ function isConfigureWarning(value) {
|
|
|
36535
36726
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
36536
36727
|
return false;
|
|
36537
36728
|
const warning = value;
|
|
36538
|
-
return (warning.kind === "formatter_not_installed" || warning.kind === "checker_not_installed" || warning.kind === "lsp_binary_missing") && typeof warning.hint === "string";
|
|
36729
|
+
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";
|
|
36539
36730
|
}
|
|
36540
36731
|
function coerceConfigureWarnings(warnings) {
|
|
36541
36732
|
return warnings.filter(isConfigureWarning);
|
|
@@ -36545,6 +36736,18 @@ function drainPendingEagerWarnings(projectRoot) {
|
|
|
36545
36736
|
pendingEagerWarnings.delete(projectRoot);
|
|
36546
36737
|
return pending;
|
|
36547
36738
|
}
|
|
36739
|
+
function enqueueConfigParseWarnings(projectRoot, errors3) {
|
|
36740
|
+
if (!projectRoot || errors3.length === 0)
|
|
36741
|
+
return;
|
|
36742
|
+
const pending = pendingEagerWarnings.get(projectRoot) ?? [];
|
|
36743
|
+
for (const entry of errors3) {
|
|
36744
|
+
const hint = formatConfigParseFailureMessage(entry.path, entry.message);
|
|
36745
|
+
if (!pending.some((item) => item.kind === "config_parse_failed" && item.hint === hint)) {
|
|
36746
|
+
pending.push({ kind: "config_parse_failed", hint });
|
|
36747
|
+
}
|
|
36748
|
+
}
|
|
36749
|
+
pendingEagerWarnings.set(projectRoot, pending);
|
|
36750
|
+
}
|
|
36548
36751
|
function shouldPrepareOnnxRuntime(config2) {
|
|
36549
36752
|
const isFastembedSemanticBackend = (config2.semantic?.backend ?? "fastembed") === "fastembed";
|
|
36550
36753
|
return config2.semantic_search === true && isFastembedSemanticBackend;
|
|
@@ -36653,6 +36856,7 @@ async function src_default(pi) {
|
|
|
36653
36856
|
}
|
|
36654
36857
|
await ensureStorageMigrated({ harness: "pi", binaryPath, logger: bridgeLogger });
|
|
36655
36858
|
const config2 = loadAftConfig(process.cwd());
|
|
36859
|
+
enqueueConfigParseWarnings(process.cwd(), getConfigLoadErrors());
|
|
36656
36860
|
const storageDir = resolveCortexKitStorageRoot();
|
|
36657
36861
|
let onnxRuntimePromise = null;
|
|
36658
36862
|
if (shouldPrepareOnnxRuntime(config2)) {
|
|
@@ -36661,30 +36865,7 @@ async function src_default(pi) {
|
|
|
36661
36865
|
return null;
|
|
36662
36866
|
});
|
|
36663
36867
|
}
|
|
36664
|
-
const configOverrides =
|
|
36665
|
-
if (config2.format_on_edit !== undefined)
|
|
36666
|
-
configOverrides.format_on_edit = config2.format_on_edit;
|
|
36667
|
-
if (config2.formatter_timeout_secs !== undefined)
|
|
36668
|
-
configOverrides.formatter_timeout_secs = config2.formatter_timeout_secs;
|
|
36669
|
-
if (config2.validate_on_edit !== undefined)
|
|
36670
|
-
configOverrides.validate_on_edit = config2.validate_on_edit;
|
|
36671
|
-
if (config2.formatter !== undefined)
|
|
36672
|
-
configOverrides.formatter = config2.formatter;
|
|
36673
|
-
if (config2.checker !== undefined)
|
|
36674
|
-
configOverrides.checker = config2.checker;
|
|
36675
|
-
configOverrides.restrict_to_project_root = config2.restrict_to_project_root ?? false;
|
|
36676
|
-
if (config2.search_index !== undefined)
|
|
36677
|
-
configOverrides.search_index = config2.search_index;
|
|
36678
|
-
if (config2.semantic_search !== undefined)
|
|
36679
|
-
configOverrides.semantic_search = config2.semantic_search;
|
|
36680
|
-
Object.assign(configOverrides, resolveExperimentalConfigForConfigure(config2));
|
|
36681
|
-
Object.assign(configOverrides, resolveLspConfigForConfigure(config2));
|
|
36682
|
-
if (config2.semantic !== undefined)
|
|
36683
|
-
configOverrides.semantic = config2.semantic;
|
|
36684
|
-
if (config2.inspect !== undefined)
|
|
36685
|
-
configOverrides.inspect = config2.inspect;
|
|
36686
|
-
if (config2.max_callgraph_files !== undefined)
|
|
36687
|
-
configOverrides.max_callgraph_files = config2.max_callgraph_files;
|
|
36868
|
+
const configOverrides = resolveProjectOverridesForConfigure(config2);
|
|
36688
36869
|
if (config2.url_fetch_allow_private !== undefined)
|
|
36689
36870
|
configOverrides.url_fetch_allow_private = config2.url_fetch_allow_private;
|
|
36690
36871
|
configOverrides.storage_dir = storageDir;
|
|
@@ -36892,12 +37073,25 @@ ${lines}
|
|
|
36892
37073
|
return { content, details: event.details, isError: event.isError };
|
|
36893
37074
|
});
|
|
36894
37075
|
pi.on("turn_end", async (_event, extCtx) => {
|
|
37076
|
+
const sessionID = resolveSessionId(extCtx);
|
|
36895
37077
|
await handleTurnEndBgCompletions({
|
|
36896
37078
|
ctx,
|
|
36897
37079
|
directory: extCtx.cwd,
|
|
36898
|
-
sessionID
|
|
37080
|
+
sessionID,
|
|
36899
37081
|
runtime: pi
|
|
36900
37082
|
});
|
|
37083
|
+
const bridge = pool.getActiveBridgeForRoot(extCtx.cwd);
|
|
37084
|
+
if (bridge && sessionID) {
|
|
37085
|
+
handleConfigureWarningsForSession({
|
|
37086
|
+
projectRoot: extCtx.cwd,
|
|
37087
|
+
sessionId: sessionID,
|
|
37088
|
+
client: pi,
|
|
37089
|
+
bridge,
|
|
37090
|
+
warnings: [],
|
|
37091
|
+
storageDir,
|
|
37092
|
+
pluginVersion: PLUGIN_VERSION
|
|
37093
|
+
});
|
|
37094
|
+
}
|
|
36901
37095
|
});
|
|
36902
37096
|
pi.on("input", (_event, extCtx) => {
|
|
36903
37097
|
signalSyncWatchAbort(resolveSessionId(extCtx));
|
|
@@ -36926,6 +37120,7 @@ ${lines}
|
|
|
36926
37120
|
log2(`AFT extension ready (surface=${config2.tool_surface ?? "recommended"})`);
|
|
36927
37121
|
}
|
|
36928
37122
|
var __test__2 = {
|
|
37123
|
+
enqueueConfigParseWarnings,
|
|
36929
37124
|
bridgeDirectoryFromCallback,
|
|
36930
37125
|
resolveToolSurface,
|
|
36931
37126
|
handleConfigureWarningsForSession,
|