@cortexkit/aft-pi 0.52.0 → 0.52.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7948,7 +7948,7 @@ function error(message, meta) {
7948
7948
  }
7949
7949
  // ../aft-bridge/dist/bash-format.js
7950
7950
  function isTerminalStatus(status) {
7951
- return status === "completed" || status === "failed" || status === "killed" || status === "timed_out";
7951
+ return status === "completed" || status === "failed" || status === "killed" || status === "timed_out" || status === "fate_unknown";
7952
7952
  }
7953
7953
  function sleep(ms) {
7954
7954
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -8491,6 +8491,7 @@ var TERMINAL_BASH_STATUSES = new Set([
8491
8491
  "failed",
8492
8492
  "killed",
8493
8493
  "timed_out",
8494
+ "fate_unknown",
8494
8495
  "cancelled",
8495
8496
  "timeout"
8496
8497
  ]);
@@ -10359,34 +10360,54 @@ async function fetchLatestTag() {
10359
10360
  }
10360
10361
  // ../aft-bridge/dist/durable-log.js
10361
10362
  import { appendFile, mkdir, rename, rm, stat } from "node:fs/promises";
10363
+ import { dirname } from "node:path";
10364
+
10365
+ // ../aft-bridge/dist/storage-paths.js
10362
10366
  import { homedir as homedir5 } from "node:os";
10363
- import { dirname, join as join5 } from "node:path";
10364
- var DEFAULT_LOG_BYTES = 32 * 1024 * 1024;
10365
- var DEFAULT_LOG_GENERATIONS = 1;
10367
+ import { join as join5, resolve as resolve3 } from "node:path";
10366
10368
  function homeDir2() {
10367
10369
  if (process.platform === "win32")
10368
10370
  return process.env.USERPROFILE || process.env.HOME || homedir5();
10369
10371
  return process.env.HOME || homedir5();
10370
10372
  }
10371
10373
  function dataHome() {
10372
- if (process.env.XDG_DATA_HOME)
10373
- return process.env.XDG_DATA_HOME;
10374
+ const xdg = process.env.XDG_DATA_HOME;
10375
+ if (xdg)
10376
+ return xdg;
10374
10377
  if (process.platform === "win32") {
10375
10378
  return process.env.LOCALAPPDATA || process.env.APPDATA || join5(homeDir2(), "AppData", "Local");
10376
10379
  }
10377
10380
  return join5(homeDir2(), ".local", "share");
10378
10381
  }
10382
+ function resolveStoragePath(raw) {
10383
+ let expanded = raw;
10384
+ if (raw === "~") {
10385
+ expanded = homeDir2();
10386
+ } else if (raw.startsWith("~/") || raw.startsWith("~\\")) {
10387
+ expanded = join5(homeDir2(), raw.slice(2));
10388
+ }
10389
+ return resolve3(expanded);
10390
+ }
10391
+ function resolveCortexKitStorageRoot() {
10392
+ const override = process.env.AFT_STORAGE_DIR;
10393
+ if (override)
10394
+ return resolveStoragePath(override);
10395
+ return resolveStoragePath(join5(dataHome(), "cortexkit", "aft"));
10396
+ }
10379
10397
  function resolveAftStorageRoot(configuredRoot) {
10398
+ if (process.env.AFT_STORAGE_DIR)
10399
+ return resolveCortexKitStorageRoot();
10380
10400
  if (configuredRoot)
10381
10401
  return configuredRoot;
10382
- if (process.env.AFT_CACHE_DIR)
10383
- return join5(process.env.AFT_CACHE_DIR, "aft");
10384
- return join5(dataHome(), "cortexkit", "aft");
10402
+ return resolveCortexKitStorageRoot();
10385
10403
  }
10386
10404
  function resolveAftLogPath(filename, configuredRoot) {
10387
10405
  return join5(resolveAftStorageRoot(configuredRoot), "logs", filename);
10388
10406
  }
10389
10407
 
10408
+ // ../aft-bridge/dist/durable-log.js
10409
+ var DEFAULT_LOG_BYTES = 32 * 1024 * 1024;
10410
+ var DEFAULT_LOG_GENERATIONS = 1;
10390
10411
  class RotatingLogSink {
10391
10412
  path;
10392
10413
  maxBytes;
@@ -10758,7 +10779,7 @@ class SubcSocket {
10758
10779
  return this.sock.localPort ?? null;
10759
10780
  }
10760
10781
  static connect(host, port, deadlineMs) {
10761
- return new Promise((resolve3, reject) => {
10782
+ return new Promise((resolve4, reject) => {
10762
10783
  const sock = net.connect({ host, port });
10763
10784
  sock.setNoDelay(true);
10764
10785
  const timer = setTimeout(() => {
@@ -10767,7 +10788,7 @@ class SubcSocket {
10767
10788
  }, Math.max(0, deadlineMs - Date.now()));
10768
10789
  sock.once("connect", () => {
10769
10790
  clearTimeout(timer);
10770
- resolve3(new SubcSocket(sock));
10791
+ resolve4(new SubcSocket(sock));
10771
10792
  });
10772
10793
  sock.once("error", (err) => {
10773
10794
  clearTimeout(timer);
@@ -10799,7 +10820,7 @@ class SubcSocket {
10799
10820
  }
10800
10821
  if (n === 0)
10801
10822
  return Promise.resolve(new Uint8Array(0));
10802
- return new Promise((resolve3, reject) => {
10823
+ return new Promise((resolve4, reject) => {
10803
10824
  let timer = null;
10804
10825
  if (Number.isFinite(deadlineMs)) {
10805
10826
  const remaining = deadlineMs - Date.now();
@@ -10812,7 +10833,7 @@ class SubcSocket {
10812
10833
  reject(new SocketTimeoutError(`timed out waiting for ${n} bytes`));
10813
10834
  }, remaining);
10814
10835
  }
10815
- this.waiter = { need: n, resolve: resolve3, reject, timer };
10836
+ this.waiter = { need: n, resolve: resolve4, reject, timer };
10816
10837
  this.tryServe();
10817
10838
  });
10818
10839
  }
@@ -10848,7 +10869,7 @@ class SubcSocket {
10848
10869
  let queued = false;
10849
10870
  let settled = false;
10850
10871
  let timer = null;
10851
- const completed = new Promise((resolve3, reject) => {
10872
+ const completed = new Promise((resolve4, reject) => {
10852
10873
  const settle = (run) => {
10853
10874
  if (settled)
10854
10875
  return;
@@ -10872,7 +10893,7 @@ class SubcSocket {
10872
10893
  if (err) {
10873
10894
  reject(new SocketWriteQueuedError("subc socket reported a write error after bytes were handed to the socket", err instanceof Error ? err : new Error(String(err))));
10874
10895
  } else {
10875
- resolve3();
10896
+ resolve4();
10876
10897
  }
10877
10898
  });
10878
10899
  });
@@ -11295,15 +11316,15 @@ class SubcClient {
11295
11316
  const key = pendingKey(handle, corr);
11296
11317
  let subscriptionPending = null;
11297
11318
  let resolveClosed = null;
11298
- const closed = new Promise((resolve3, reject) => {
11319
+ const closed = new Promise((resolve4, reject) => {
11299
11320
  if (this.closedErr) {
11300
11321
  reject(this.closedErr);
11301
11322
  return;
11302
11323
  }
11303
- resolveClosed = resolve3;
11324
+ resolveClosed = resolve4;
11304
11325
  subscriptionPending = {
11305
11326
  handle,
11306
- resolve: () => resolve3(),
11327
+ resolve: () => resolve4(),
11307
11328
  reject,
11308
11329
  onProgress: onEvent,
11309
11330
  timer: null,
@@ -11392,11 +11413,11 @@ class SubcClient {
11392
11413
  const waiters = [];
11393
11414
  for (const pending of this.pending.values()) {
11394
11415
  if (pending.handle === handle && !pending.subscription) {
11395
- waiters.push(new Promise((resolve3) => {
11416
+ waiters.push(new Promise((resolve4) => {
11396
11417
  const previous = pending.onSettle;
11397
11418
  pending.onSettle = () => {
11398
11419
  previous?.();
11399
- resolve3();
11420
+ resolve4();
11400
11421
  };
11401
11422
  }));
11402
11423
  }
@@ -11452,11 +11473,11 @@ class SubcClient {
11452
11473
  const channel = handle?.channel ?? 0;
11453
11474
  const epoch = handle?.epoch ?? 0;
11454
11475
  const frame = buildFrame(FrameType.Request, buildFlags(false, priority, false, admission), channel, epoch, corr, body);
11455
- return new Promise((resolve3, reject) => {
11476
+ return new Promise((resolve4, reject) => {
11456
11477
  const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
11457
11478
  const pending = {
11458
11479
  handle,
11459
- resolve: resolve3,
11480
+ resolve: resolve4,
11460
11481
  reject,
11461
11482
  onProgress,
11462
11483
  timer: null,
@@ -11530,11 +11551,11 @@ class SubcClient {
11530
11551
  }
11531
11552
  return this.outcomeUnknownCallError("connection dropped before the managed call returned a response", error2);
11532
11553
  };
11533
- return new Promise((resolve3, reject) => {
11554
+ return new Promise((resolve4, reject) => {
11534
11555
  const ms = timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
11535
11556
  const pending = {
11536
11557
  handle,
11537
- resolve: resolve3,
11558
+ resolve: resolve4,
11538
11559
  reject,
11539
11560
  onProgress,
11540
11561
  timer: null,
@@ -11983,7 +12004,7 @@ function normalizeConnectOptions(opts) {
11983
12004
  identity: opts.identity,
11984
12005
  targetKind: opts.targetKind ?? DEFAULT_MANAGED_TARGET_KIND,
11985
12006
  reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
11986
- sleep: opts.sleep ?? ((ms) => new Promise((resolve3) => setTimeout(resolve3, ms))),
12007
+ sleep: opts.sleep ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms))),
11987
12008
  timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS,
11988
12009
  livenessProbeWindowMs: opts.livenessProbeWindowMs ?? LIVENESS_PROBE_WINDOW_MS,
11989
12010
  onControlPush: opts.onControlPush
@@ -12442,14 +12463,14 @@ function getLifecycleRegistry() {
12442
12463
 
12443
12464
  // ../aft-bridge/dist/project-identity.js
12444
12465
  import { realpathSync } from "node:fs";
12445
- import { resolve as resolve3 } from "node:path";
12466
+ import { resolve as resolve4 } from "node:path";
12446
12467
  function canonicalizeProjectRoot(dir) {
12447
12468
  const trimmed = dir.replace(/[/\\]+$/, "");
12448
12469
  let canonical;
12449
12470
  try {
12450
12471
  canonical = realpathSync(trimmed);
12451
12472
  } catch {
12452
- canonical = resolve3(trimmed);
12473
+ canonical = resolve4(trimmed);
12453
12474
  }
12454
12475
  return normalizeWindowsRoot(canonical);
12455
12476
  }
@@ -12482,6 +12503,8 @@ var AFT_MODULE_ID = "aft";
12482
12503
  var MAX_CONSECUTIVE_TRANSPORT_FAILURES = 3;
12483
12504
  var RECONNECT_RETRY_FLOOR_MS = 100;
12484
12505
  var RECONNECT_RETRY_CAP_MS = 2000;
12506
+ var ROUTE_OPEN_RELOAD_WAIT_CAP_MS = 15000;
12507
+ var ROUTE_OPEN_RELOAD_WAIT_EXHAUSTED_SUFFIX = " The AFT daemon module did not return within the 15s reload window.";
12485
12508
  var BG_STABLE_MS = 5000;
12486
12509
  function reconnectBackoffMs(attempt) {
12487
12510
  return Math.min(RECONNECT_RETRY_FLOOR_MS * 2 ** Math.min(attempt, 6), RECONNECT_RETRY_CAP_MS);
@@ -12790,6 +12813,15 @@ function isAbsentRootRouteError(err) {
12790
12813
  function absentRootError(root) {
12791
12814
  return new SubcError(`invalid route project root: project root does not exist: ${root}`, "config_divergence");
12792
12815
  }
12816
+ function reloadWindowExhaustedError(error2) {
12817
+ if (error2 instanceof Error) {
12818
+ try {
12819
+ error2.message += ROUTE_OPEN_RELOAD_WAIT_EXHAUSTED_SUFFIX;
12820
+ return error2;
12821
+ } catch {}
12822
+ }
12823
+ return new Error(`${String(error2)}${ROUTE_OPEN_RELOAD_WAIT_EXHAUSTED_SUFFIX}`);
12824
+ }
12793
12825
  function safeCloseRoute(client, route) {
12794
12826
  try {
12795
12827
  client.closeRouteChannel(route).catch(() => {
@@ -12914,6 +12946,7 @@ class SubcTransportPool {
12914
12946
  connecting = null;
12915
12947
  routeReopenRetryDelayMs = RECONNECT_RETRY_FLOOR_MS;
12916
12948
  routeReopenRetry = null;
12949
+ routeReopenRetryMs = null;
12917
12950
  sessions = new Map;
12918
12951
  rootIndex = new Map;
12919
12952
  dormantRoots = new Map;
@@ -12933,8 +12966,8 @@ class SubcTransportPool {
12933
12966
  this.connectFn = options.connect ?? ((opts) => SubcClient.connect(opts));
12934
12967
  this.onBgEventsNudge = options.onBgEventsNudge;
12935
12968
  this.onBgEventsNudgeRef = options.onBgEventsNudgeRef;
12936
- this.bgBackoffSleep = options.bgBackoffSleep ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms)));
12937
- this.routeRetrySleep = options.routeRetrySleep ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms)));
12969
+ this.bgBackoffSleep = options.bgBackoffSleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
12970
+ this.routeRetrySleep = options.routeRetrySleep ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
12938
12971
  this.bgDispatchProbeIntervalMs = options.bgDispatchProbeIntervalMs ?? BG_DISPATCH_PROBE_INTERVAL_MS;
12939
12972
  const lifecycle = options.lifecycle;
12940
12973
  const demandCheck = options.lifecycleDemandCheck ?? options.demandCheck ?? lifecycle?.demandCheck;
@@ -13387,6 +13420,23 @@ class SubcTransportPool {
13387
13420
  throw error2;
13388
13421
  }
13389
13422
  };
13423
+ const openRouteAfterReloadWindow = async () => {
13424
+ let reloadWaitedMs = 0;
13425
+ while (true) {
13426
+ try {
13427
+ return await openRoute();
13428
+ } catch (error2) {
13429
+ if (!isRouteOpenReloadWindowError(error2))
13430
+ throw error2;
13431
+ const { delayMs, wait } = this.waitForRouteReopenBackoff();
13432
+ if (reloadWaitedMs + delayMs > ROUTE_OPEN_RELOAD_WAIT_CAP_MS) {
13433
+ throw reloadWindowExhaustedError(error2);
13434
+ }
13435
+ reloadWaitedMs += delayMs;
13436
+ await wait;
13437
+ }
13438
+ }
13439
+ };
13390
13440
  const clearRouteEntry = (entry) => {
13391
13441
  if (record.routeEntry !== entry)
13392
13442
  return;
@@ -13423,7 +13473,7 @@ class SubcTransportPool {
13423
13473
  this.ensureBgSubscription(identity, record);
13424
13474
  return reply;
13425
13475
  };
13426
- let routeAndEntry = await openRoute();
13476
+ let routeAndEntry = await openRouteAfterReloadWindow();
13427
13477
  try {
13428
13478
  return await requestOnRoute(routeAndEntry.route);
13429
13479
  } catch (error2) {
@@ -13431,8 +13481,8 @@ class SubcTransportPool {
13431
13481
  throw this.annotateReapError(error2, record);
13432
13482
  if (isRouteProvenAbsentError(error2) && this.isCurrentSession(key, record) && this.client === client) {
13433
13483
  clearRouteEntry(routeAndEntry.entry);
13434
- await this.waitForRouteReopenBackoff();
13435
- routeAndEntry = await openRoute();
13484
+ await this.waitForRouteReopenBackoff().wait;
13485
+ routeAndEntry = await openRouteAfterReloadWindow();
13436
13486
  try {
13437
13487
  const reply = await requestOnRoute(routeAndEntry.route);
13438
13488
  this.resetRouteReopenBackoff();
@@ -13458,17 +13508,21 @@ class SubcTransportPool {
13458
13508
  }
13459
13509
  waitForRouteReopenBackoff() {
13460
13510
  const pending = this.routeReopenRetry;
13461
- if (pending)
13462
- return pending;
13463
- const delay = this.routeReopenRetryDelayMs;
13464
- this.routeReopenRetryDelayMs = Math.min(delay * 2, RECONNECT_RETRY_CAP_MS);
13511
+ const pendingDelay = this.routeReopenRetryMs;
13512
+ if (pending && pendingDelay !== null)
13513
+ return { delayMs: pendingDelay, wait: pending };
13514
+ const delayMs = this.routeReopenRetryDelayMs;
13515
+ this.routeReopenRetryDelayMs = Math.min(delayMs * 2, RECONNECT_RETRY_CAP_MS);
13465
13516
  let retry;
13466
- retry = Promise.resolve().then(() => this.routeRetrySleep(delay)).finally(() => {
13467
- if (this.routeReopenRetry === retry)
13517
+ retry = Promise.resolve().then(() => this.routeRetrySleep(delayMs)).finally(() => {
13518
+ if (this.routeReopenRetry === retry) {
13468
13519
  this.routeReopenRetry = null;
13520
+ this.routeReopenRetryMs = null;
13521
+ }
13469
13522
  });
13470
13523
  this.routeReopenRetry = retry;
13471
- return retry;
13524
+ this.routeReopenRetryMs = delayMs;
13525
+ return { delayMs, wait: retry };
13472
13526
  }
13473
13527
  resetRouteReopenBackoff() {
13474
13528
  this.routeReopenRetryDelayMs = RECONNECT_RETRY_FLOOR_MS;
@@ -13755,6 +13809,12 @@ function isRouteGoodbyeError(error2) {
13755
13809
  }
13756
13810
  return error2.code === "route_closed" && error2.message.includes("route closed by subc");
13757
13811
  }
13812
+ function isRouteOpenReloadWindowError(error2) {
13813
+ if (error2 === null || typeof error2 !== "object")
13814
+ return false;
13815
+ const code = error2.code;
13816
+ return code === "module_reloading" || code === "module_warming" || code === "target_unavailable";
13817
+ }
13758
13818
  function hasEngineResponse(error2) {
13759
13819
  const response = error2.response;
13760
13820
  if (response !== null && typeof response === "object")
@@ -13823,12 +13883,12 @@ function stripJsoncSymbols(value) {
13823
13883
  import { spawnSync as spawnSync2 } from "node:child_process";
13824
13884
  import { closeSync as closeSync3, existsSync as existsSync6, mkdirSync as mkdirSync4, openSync as openSync3, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync2, statSync as statSync4, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
13825
13885
  import { homedir as homedir8, tmpdir } from "node:os";
13826
- import { basename, dirname as dirname3, join as join8, resolve as resolve5 } from "node:path";
13886
+ import { basename, dirname as dirname3, join as join8, resolve as resolve6 } from "node:path";
13827
13887
 
13828
13888
  // ../aft-bridge/dist/paths.js
13829
13889
  import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync } from "node:fs";
13830
13890
  import { homedir as homedir6 } from "node:os";
13831
- import { dirname as dirname2, isAbsolute as isAbsolute2, join as join6, resolve as resolve4 } from "node:path";
13891
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join6, resolve as resolve5 } from "node:path";
13832
13892
  function homeDir3() {
13833
13893
  if (process.platform === "win32")
13834
13894
  return process.env.USERPROFILE || process.env.HOME || homedir6();
@@ -13843,7 +13903,7 @@ function configHome() {
13843
13903
  function legacyOpenCodeConfigDir() {
13844
13904
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
13845
13905
  if (envDir)
13846
- return resolve4(envDir);
13906
+ return resolve5(envDir);
13847
13907
  return join6(configHome(), "opencode");
13848
13908
  }
13849
13909
  function legacyPiAgentDir() {
@@ -14086,7 +14146,16 @@ function platformKey(platform = process.platform, arch = process.arch) {
14086
14146
  }
14087
14147
  return key;
14088
14148
  }
14149
+ function logBinaryResolution(resolution) {
14150
+ log(`Resolved binary from ${resolution.source}: ${resolution.path}`);
14151
+ }
14089
14152
  function findBinarySync(expectedVersion) {
14153
+ const resolution = findBinarySyncInner(expectedVersion);
14154
+ if (resolution)
14155
+ logBinaryResolution(resolution);
14156
+ return resolution?.path ?? null;
14157
+ }
14158
+ function findBinarySyncInner(expectedVersion) {
14090
14159
  const ext = process.platform === "win32" ? ".exe" : "";
14091
14160
  const env = { ...process.env };
14092
14161
  const pluginVersion = expectedVersion ?? (() => {
@@ -14100,8 +14169,9 @@ function findBinarySync(expectedVersion) {
14100
14169
  if (pluginVersion) {
14101
14170
  const tag = pluginVersion.startsWith("v") ? pluginVersion : `v${pluginVersion}`;
14102
14171
  const versionCached = cachedBinaryPathFromEnv(tag, env, ext);
14103
- if (versionCached && isExpectedCachedBinary2(versionCached, pluginVersion))
14104
- return versionCached;
14172
+ if (versionCached && isExpectedCachedBinary2(versionCached, pluginVersion)) {
14173
+ return { path: versionCached, source: "versioned cache" };
14174
+ }
14105
14175
  }
14106
14176
  try {
14107
14177
  const key = platformKey();
@@ -14116,7 +14186,7 @@ function findBinarySync(expectedVersion) {
14116
14186
  warn(`npm platform package binary v${npmVersion} does not match plugin v${pluginVersion}; skipping (continuing to PATH lookup)`);
14117
14187
  } else {
14118
14188
  const copied = copyToVersionedCache(resolved, npmVersion);
14119
- return copied ?? resolved;
14189
+ return { path: copied ?? resolved, source: "npm platform package" };
14120
14190
  }
14121
14191
  }
14122
14192
  } catch {}
@@ -14134,27 +14204,28 @@ function findBinarySync(expectedVersion) {
14134
14204
  }
14135
14205
  const usable = probeBinaryCandidate(candidate, "PATH", expectedVersion);
14136
14206
  if (usable)
14137
- return usable;
14207
+ return { path: usable, source: "PATH" };
14138
14208
  }
14139
14209
  } catch {}
14140
14210
  const cargoPath = join7(homeDirFromEnv(env), ".cargo", "bin", `aft${ext}`);
14141
14211
  if (existsSync5(cargoPath)) {
14142
14212
  const usable = probeBinaryCandidate(cargoPath, "cargo", expectedVersion);
14143
14213
  if (usable)
14144
- return usable;
14214
+ return { path: usable, source: "cargo" };
14145
14215
  }
14146
14216
  return null;
14147
14217
  }
14148
14218
  async function findBinary(expectedVersion) {
14149
14219
  const syncResult = findBinarySync(expectedVersion);
14150
14220
  if (syncResult) {
14151
- log(`Resolved binary: ${syncResult}`);
14152
14221
  return syncResult;
14153
14222
  }
14154
14223
  log("Binary not found locally, attempting auto-download...");
14155
14224
  const downloaded = await ensureBinaryForResolver(expectedVersion);
14156
- if (downloaded)
14225
+ if (downloaded) {
14226
+ logBinaryResolution({ path: downloaded, source: "auto-download" });
14157
14227
  return downloaded;
14228
+ }
14158
14229
  throw new Error([
14159
14230
  "Could not find the `aft` binary.",
14160
14231
  "",
@@ -14197,9 +14268,6 @@ function resolveLegacyStorageRoot(harness) {
14197
14268
  return join8(homeDir4(), ".pi", "agent", "aft");
14198
14269
  return join8(dataHome2(), "opencode", "storage", "plugin", "aft");
14199
14270
  }
14200
- function resolveCortexKitStorageRoot() {
14201
- return join8(dataHome2(), "cortexkit", "aft");
14202
- }
14203
14271
  function stripJsoncForParse(input) {
14204
14272
  let out = "";
14205
14273
  let inString = false;
@@ -14469,8 +14537,8 @@ function visibleConfigMigrationWarning(scope, targetPath, paths, reason) {
14469
14537
  }
14470
14538
  function migrateAftConfigFile(opts) {
14471
14539
  const warnings = [];
14472
- const resolvedTarget = resolve5(opts.targetPath);
14473
- const existingSources = opts.legacySources.filter((source) => existsSync6(source.path) && resolve5(source.path) !== resolvedTarget);
14540
+ const resolvedTarget = resolve6(opts.targetPath);
14541
+ const existingSources = opts.legacySources.filter((source) => existsSync6(source.path) && resolve6(source.path) !== resolvedTarget);
14474
14542
  const info = opts.logger?.info ?? opts.logger?.log;
14475
14543
  if (existingSources.length === 0) {
14476
14544
  return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
@@ -14716,7 +14784,7 @@ function npmSpawnEnv(resolved, baseEnv = process.env) {
14716
14784
  import { execFileSync } from "node:child_process";
14717
14785
  import { createHash as createHash3 } from "node:crypto";
14718
14786
  import { chmodSync as chmodSync3, closeSync as closeSync4, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync7, lstatSync, mkdirSync as mkdirSync5, openSync as openSync4, readdirSync as readdirSync3, readFileSync as readFileSync6, readlinkSync, realpathSync as realpathSync2, rmSync as rmSync3, statSync as statSync6, symlinkSync, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
14719
- import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute4, join as join10, relative as relative2, resolve as resolve6, win32 } from "node:path";
14787
+ import { basename as basename2, dirname as dirname5, isAbsolute as isAbsolute4, join as join10, relative as relative2, resolve as resolve7, win32 } from "node:path";
14720
14788
  import { Readable as Readable2 } from "node:stream";
14721
14789
  import { pipeline as pipeline2 } from "node:stream/promises";
14722
14790
  var ORT_VERSION = "1.24.4";
@@ -15021,7 +15089,7 @@ function findSystemOnnxRuntime(libName) {
15021
15089
  const normalizeCase = process.platform === "win32" || process.platform === "darwin";
15022
15090
  const seen = new Set;
15023
15091
  const uniquePaths = searchPaths.filter((p) => {
15024
- let key = resolve6(p).replace(/[/\\]+$/, "");
15092
+ let key = resolve7(p).replace(/[/\\]+$/, "");
15025
15093
  if (normalizeCase)
15026
15094
  key = key.toLowerCase();
15027
15095
  if (seen.has(key))
@@ -15105,7 +15173,7 @@ function validateExtractedTree(stagingRoot) {
15105
15173
  const lst = lstatSync(fullPath);
15106
15174
  if (lst.isSymbolicLink()) {
15107
15175
  const linkTarget = readlinkSync(fullPath);
15108
- const resolvedTarget = resolve6(dirname5(fullPath), linkTarget);
15176
+ const resolvedTarget = resolve7(dirname5(fullPath), linkTarget);
15109
15177
  const rel2 = relative2(realRoot, resolvedTarget);
15110
15178
  if (rel2.startsWith("..") || isAbsolute4(rel2) || win32.isAbsolute(rel2)) {
15111
15179
  throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
@@ -15223,7 +15291,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
15223
15291
  unlinkSync4(dst);
15224
15292
  } catch {}
15225
15293
  const dstForContainment = join10(targetRoot, link.name);
15226
- const resolvedTarget = resolve6(dirname5(dstForContainment), link.target);
15294
+ const resolvedTarget = resolve7(dirname5(dstForContainment), link.target);
15227
15295
  if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
15228
15296
  const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
15229
15297
  if (requiredLibs.has(link.name)) {
@@ -16290,7 +16358,7 @@ class RevivableTransportPool {
16290
16358
  const delay = Math.max(0, this.revivalRetryNotBefore - Date.now());
16291
16359
  if (delay > 0) {
16292
16360
  let scheduled;
16293
- scheduled = new Promise((resolve7) => setTimeout(resolve7, delay)).then(() => {
16361
+ scheduled = new Promise((resolve8) => setTimeout(resolve8, delay)).then(() => {
16294
16362
  if (this.revival === scheduled)
16295
16363
  this.revival = null;
16296
16364
  return this.ensureActivePool();
@@ -17393,7 +17461,7 @@ import {
17393
17461
  // package.json
17394
17462
  var package_default = {
17395
17463
  name: "@cortexkit/aft-pi",
17396
- version: "0.52.0",
17464
+ version: "0.52.2",
17397
17465
  type: "module",
17398
17466
  description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
17399
17467
  main: "dist/index.js",
@@ -17416,19 +17484,19 @@ var package_default = {
17416
17484
  "test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
17417
17485
  },
17418
17486
  dependencies: {
17419
- "@cortexkit/aft-bridge": "0.52.0",
17487
+ "@cortexkit/aft-bridge": "0.52.2",
17420
17488
  "comment-json": "^5.0.0",
17421
17489
  diff: "^8.0.4",
17422
17490
  typebox: "1.1.38",
17423
17491
  zod: "^4.4.3"
17424
17492
  },
17425
17493
  optionalDependencies: {
17426
- "@cortexkit/aft-darwin-arm64": "0.52.0",
17427
- "@cortexkit/aft-darwin-x64": "0.52.0",
17428
- "@cortexkit/aft-linux-arm64": "0.52.0",
17429
- "@cortexkit/aft-linux-x64": "0.52.0",
17430
- "@cortexkit/aft-win32-arm64": "0.52.0",
17431
- "@cortexkit/aft-win32-x64": "0.52.0"
17494
+ "@cortexkit/aft-darwin-arm64": "0.52.2",
17495
+ "@cortexkit/aft-darwin-x64": "0.52.2",
17496
+ "@cortexkit/aft-linux-arm64": "0.52.2",
17497
+ "@cortexkit/aft-linux-x64": "0.52.2",
17498
+ "@cortexkit/aft-win32-arm64": "0.52.2",
17499
+ "@cortexkit/aft-win32-x64": "0.52.2"
17432
17500
  },
17433
17501
  devDependencies: {
17434
17502
  "@earendil-works/pi-coding-agent": "^0.80.2",
@@ -18088,6 +18156,8 @@ function registerStatusCommand(pi, ctx) {
18088
18156
 
18089
18157
  // src/config.ts
18090
18158
  import { existsSync as existsSync10, readFileSync as readFileSync7, renameSync as renameSync5, unlinkSync as unlinkSync5, writeFileSync as writeFileSync4 } from "node:fs";
18159
+ import { homedir as homedir12 } from "node:os";
18160
+ import { isAbsolute as isAbsolute6 } from "node:path";
18091
18161
  var import_comment_json = __toESM(require_src2(), 1);
18092
18162
 
18093
18163
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
@@ -32451,6 +32521,32 @@ var CheckerEnum = exports_external.enum([
32451
32521
  "none"
32452
32522
  ]);
32453
32523
  var ConfigureWarningsDeliveryEnum = exports_external.enum(["toast", "log", "chat"]);
32524
+ var IndexKindSchema = exports_external.enum(["search", "semantic", "callgraph"]);
32525
+ function isSupportedAbsoluteIndexPath(path2) {
32526
+ if (path2 === "~" || path2.startsWith("~/") || path2.startsWith("~\\")) {
32527
+ return isAbsolute6(homedir12());
32528
+ }
32529
+ return isAbsolute6(path2);
32530
+ }
32531
+ var IndexRootSchema = exports_external.object({
32532
+ path: exports_external.string().refine(isSupportedAbsoluteIndexPath, {
32533
+ message: "index.roots path must be absolute after ~ expansion"
32534
+ }),
32535
+ indexes: exports_external.array(IndexKindSchema).min(1, "index.roots indexes must be non-empty")
32536
+ }).superRefine(({ indexes }, context) => {
32537
+ if (new Set(indexes).size !== indexes.length) {
32538
+ context.addIssue({
32539
+ code: "custom",
32540
+ message: "index.roots indexes must not contain duplicates"
32541
+ });
32542
+ }
32543
+ }).transform(({ path: path2, indexes }) => ({
32544
+ path: path2,
32545
+ indexes: ["search", "semantic", "callgraph"].filter((kind) => indexes.includes(kind) || kind === "search" && indexes.includes("semantic"))
32546
+ }));
32547
+ var IndexConfigSchema = exports_external.object({
32548
+ roots: exports_external.array(IndexRootSchema).optional()
32549
+ });
32454
32550
  var SemanticConfigSchema = exports_external.object({
32455
32551
  backend: exports_external.enum(["fastembed", "openai_compatible", "ollama"]).optional(),
32456
32552
  model: exports_external.string().trim().min(1).optional(),
@@ -32516,7 +32612,12 @@ var SubcConfigSchema = exports_external.object({
32516
32612
  connection_file: exports_external.string().optional()
32517
32613
  });
32518
32614
  var GhShimConfigSchema = exports_external.object({
32519
- enabled: exports_external.boolean().optional()
32615
+ enabled: exports_external.boolean().optional(),
32616
+ binary_path: exports_external.string().trim().refine(isAbsolute6, "gh_shim.binary_path must be absolute").optional()
32617
+ });
32618
+ var GitCoAuthorSchema = exports_external.string().trim().refine((value) => value === "off" || value === "auto" || /^[^<>\r\n]+\s+<[^<>\s@]+@[^<>\s@]+>$/.test(value), "git.co_author must be 'off', 'auto', or an explicit 'Name <email>' identity");
32619
+ var GitConfigSchema = exports_external.object({
32620
+ co_author: GitCoAuthorSchema.optional()
32520
32621
  });
32521
32622
  var InspectConfigSchema = exports_external.object({
32522
32623
  enabled: exports_external.boolean().optional(),
@@ -32551,6 +32652,7 @@ var AftConfigSchema = exports_external.preprocess((value) => stripHarnessSpecifi
32551
32652
  disabled_tools: exports_external.array(exports_external.string()).optional(),
32552
32653
  restrict_to_project_root: exports_external.boolean().optional(),
32553
32654
  search_index: exports_external.boolean().optional(),
32655
+ index: IndexConfigSchema.optional(),
32554
32656
  semantic_search: exports_external.boolean().optional(),
32555
32657
  callgraph_store: exports_external.boolean().optional(),
32556
32658
  callgraph_chunk_size: exports_external.number().optional(),
@@ -32565,7 +32667,8 @@ var AftConfigSchema = exports_external.preprocess((value) => stripHarnessSpecifi
32565
32667
  semantic: SemanticConfigSchema.optional(),
32566
32668
  bridge: BridgeConfigSchema.optional(),
32567
32669
  subc: SubcConfigSchema.optional(),
32568
- gh_shim: GhShimConfigSchema.optional()
32670
+ gh_shim: GhShimConfigSchema.optional(),
32671
+ git: GitConfigSchema.optional()
32569
32672
  }).strict());
32570
32673
  var CONFIG_MIGRATIONS = [
32571
32674
  { oldKey: "experimental_search_index", newPath: ["search_index"] },
@@ -32912,6 +33015,7 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
32912
33015
  "callgraph_chunk_size",
32913
33016
  "inspect",
32914
33017
  "worktree",
33018
+ "git",
32915
33019
  "experimental",
32916
33020
  "bash"
32917
33021
  ]);
@@ -32934,6 +33038,8 @@ function getStrippedTopLevelKeys(override) {
32934
33038
  stripped.push("bridge");
32935
33039
  if (override.backup !== undefined)
32936
33040
  stripped.push("backup");
33041
+ if (override.index?.roots !== undefined)
33042
+ stripped.push("index.roots");
32937
33043
  if (override.sandbox?.enabled === false)
32938
33044
  stripped.push("sandbox.enabled");
32939
33045
  if (override.sandbox?.write_allow !== undefined)
@@ -33640,18 +33746,18 @@ function ensureInstallAnchor(cwd) {
33640
33746
  }
33641
33747
  }
33642
33748
  function runInstall(spec, version2, cwd, signal) {
33643
- return new Promise((resolve7) => {
33749
+ return new Promise((resolve8) => {
33644
33750
  const target = `${spec.npm}@${version2}`;
33645
33751
  log2(`[lsp] installing ${target} to ${cwd}`);
33646
33752
  if (signal?.aborted) {
33647
33753
  warn2(`[lsp] install ${target} aborted before spawn`);
33648
- resolve7(false);
33754
+ resolve8(false);
33649
33755
  return;
33650
33756
  }
33651
33757
  const npm = resolveNpm();
33652
33758
  if (!npm) {
33653
33759
  warn2(`[lsp] npm not found on PATH or known locations; cannot install ${target}`);
33654
- resolve7(false);
33760
+ resolve8(false);
33655
33761
  return;
33656
33762
  }
33657
33763
  ensureInstallAnchor(cwd);
@@ -33674,7 +33780,7 @@ function runInstall(spec, version2, cwd, signal) {
33674
33780
  return;
33675
33781
  settled = true;
33676
33782
  cleanup();
33677
- resolve7(ok);
33783
+ resolve8(ok);
33678
33784
  };
33679
33785
  const onAbort = () => {
33680
33786
  warn2(`[lsp] install ${target} aborted during shutdown`);
@@ -33775,7 +33881,7 @@ function cachedPackageDir(npmPackage) {
33775
33881
  return lspBinDir(npmPackage).replace(/[\\/]node_modules[\\/]\.bin[\\/]?$/, "");
33776
33882
  }
33777
33883
  function hashInstalledBinary(spec) {
33778
- return new Promise((resolve7, reject) => {
33884
+ return new Promise((resolve8, reject) => {
33779
33885
  const candidates = process.platform === "win32" ? [
33780
33886
  lspBinaryPath(spec.npm, spec.binary),
33781
33887
  lspBinaryPath(spec.npm, `${spec.binary}.cmd`),
@@ -33799,7 +33905,7 @@ function hashInstalledBinary(spec) {
33799
33905
  const stream = createReadStream(pathToHash);
33800
33906
  stream.on("error", reject);
33801
33907
  stream.on("data", (chunk) => hash2.update(chunk));
33802
- stream.on("end", () => resolve7(hash2.digest("hex")));
33908
+ stream.on("end", () => resolve8(hash2.digest("hex")));
33803
33909
  });
33804
33910
  }
33805
33911
  function installedBinaryPath(spec) {
@@ -33926,7 +34032,7 @@ import {
33926
34032
  unlinkSync as unlinkSync7,
33927
34033
  writeFileSync as writeFileSync7
33928
34034
  } from "node:fs";
33929
- import { dirname as dirname6, join as join15, relative as relative3, resolve as resolve7 } from "node:path";
34035
+ import { dirname as dirname6, join as join15, relative as relative3, resolve as resolve8 } from "node:path";
33930
34036
  import { Readable as Readable3 } from "node:stream";
33931
34037
  import { pipeline as pipeline3 } from "node:stream/promises";
33932
34038
 
@@ -34083,12 +34189,12 @@ function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveS
34083
34189
  var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
34084
34190
  var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
34085
34191
  function sha256OfFile(path2) {
34086
- return new Promise((resolve8, reject) => {
34192
+ return new Promise((resolve9, reject) => {
34087
34193
  const hash2 = createHash5("sha256");
34088
34194
  const stream = createReadStream2(path2);
34089
34195
  stream.on("error", reject);
34090
34196
  stream.on("data", (chunk) => hash2.update(chunk));
34091
- stream.on("end", () => resolve8(hash2.digest("hex")));
34197
+ stream.on("end", () => resolve9(hash2.digest("hex")));
34092
34198
  });
34093
34199
  }
34094
34200
  function sha256OfFileSync2(path2) {
@@ -34345,7 +34451,7 @@ function validateExtraction(stagingRoot) {
34345
34451
  throw new Error(`failed to realpath ${full}: ${err}`);
34346
34452
  }
34347
34453
  const rel = relative3(realStagingRoot, realFull);
34348
- if (rel.startsWith("..") || resolve7(realStagingRoot, rel) !== realFull) {
34454
+ if (rel.startsWith("..") || resolve8(realStagingRoot, rel) !== realFull) {
34349
34455
  throw new Error(`archive entry escapes staging root: ${full} → ${realFull} (zip-slip defense)`);
34350
34456
  }
34351
34457
  if (lst.isDirectory()) {
@@ -34928,8 +35034,8 @@ function installProcessHandlers() {
34928
35034
  }
34929
35035
  signalShutdownStarted = true;
34930
35036
  process.exitCode = SIGNAL_EXIT_CODES[sig];
34931
- const timeout = new Promise((resolve8) => {
34932
- setTimeout(resolve8, SIGNAL_CLEANUP_TIMEOUT_MS);
35037
+ const timeout = new Promise((resolve9) => {
35038
+ setTimeout(resolve9, SIGNAL_CLEANUP_TIMEOUT_MS);
34933
35039
  });
34934
35040
  Promise.race([runCleanups(sig), timeout]).finally(() => {
34935
35041
  process.exit(SIGNAL_EXIT_CODES[sig]);
@@ -34971,12 +35077,12 @@ import { Type as Type3 } from "typebox";
34971
35077
 
34972
35078
  // src/tools/hoisted.ts
34973
35079
  import { stat as stat2 } from "node:fs/promises";
34974
- import { homedir as homedir12 } from "node:os";
34975
- import { isAbsolute as isAbsolute6, relative as relative4, resolve as resolve8, sep } from "node:path";
35080
+ import { homedir as homedir14 } from "node:os";
35081
+ import { isAbsolute as isAbsolute7, relative as relative4, resolve as resolve9, sep } from "node:path";
34976
35082
  import {
34977
- renderDiff
35083
+ renderDiff as renderDiff2
34978
35084
  } from "@earendil-works/pi-coding-agent";
34979
- import { Container, Spacer, Text } from "@earendil-works/pi-tui";
35085
+ import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
34980
35086
  import { Type as Type2 } from "typebox";
34981
35087
 
34982
35088
  // src/tools/diff-format.ts
@@ -35081,6 +35187,189 @@ function formatDiffForPi(oldContent, newContent, contextLines = DEFAULT_CONTEXT_
35081
35187
  `), firstChangedLine };
35082
35188
  }
35083
35189
 
35190
+ // src/tools/render-helpers.ts
35191
+ import { homedir as homedir13 } from "node:os";
35192
+ import { renderDiff } from "@earendil-works/pi-coding-agent";
35193
+ import { Container, Spacer, Text } from "@earendil-works/pi-tui";
35194
+ var NATIVE_COLLAPSED_RESULT_MAX_LINES = 5;
35195
+ function reuseText(last) {
35196
+ return last instanceof Text ? last : new Text("", 0, 0);
35197
+ }
35198
+ function reuseContainer(last) {
35199
+ return last instanceof Container ? last : new Container;
35200
+ }
35201
+ function shortenPath2(path2) {
35202
+ const home = homedir13();
35203
+ if (path2.startsWith(home))
35204
+ return `~${path2.slice(home.length)}`;
35205
+ return path2;
35206
+ }
35207
+ function renderToolCall(toolName, summary, theme, context) {
35208
+ const text = reuseText(context.lastComponent);
35209
+ const suffix = summary ? ` ${summary}` : "";
35210
+ text.setText(`${theme.fg("toolTitle", theme.bold(toolName))}${suffix}`);
35211
+ return text;
35212
+ }
35213
+ function accentPath(theme, path2) {
35214
+ if (!path2)
35215
+ return theme.fg("toolOutput", "...");
35216
+ return theme.fg("accent", shortenPath2(path2));
35217
+ }
35218
+ function collectTextContent(result) {
35219
+ return result.content.filter((part) => part.type === "text").map((part) => part.text ?? "").join(`
35220
+ `).trim();
35221
+ }
35222
+ function extractStructuredPayload(result) {
35223
+ if (result.details !== undefined)
35224
+ return result.details;
35225
+ const text = collectTextContent(result);
35226
+ if (!text)
35227
+ return;
35228
+ try {
35229
+ return JSON.parse(text);
35230
+ } catch {
35231
+ return;
35232
+ }
35233
+ }
35234
+ function renderErrorResult(result, fallback, theme, context) {
35235
+ const text = reuseText(context.lastComponent);
35236
+ const message = collectTextContent(result) || fallback;
35237
+ text.setText(`
35238
+ ${theme.fg("error", message)}`);
35239
+ return text;
35240
+ }
35241
+ function collapsibleResult({
35242
+ summary,
35243
+ full,
35244
+ expanded,
35245
+ context
35246
+ }) {
35247
+ if (expanded === true || full.render(200).length <= NATIVE_COLLAPSED_RESULT_MAX_LINES) {
35248
+ return full;
35249
+ }
35250
+ const text = reuseText(context.lastComponent);
35251
+ text.setText(`
35252
+ ${summary.replace(/[\r\n]+/g, " ").trim()}`);
35253
+ return text;
35254
+ }
35255
+ function renderSections(sections, context) {
35256
+ const container = reuseContainer(context.lastComponent);
35257
+ container.clear();
35258
+ const visibleSections = sections.filter((section) => section.trim().length > 0);
35259
+ if (visibleSections.length === 0)
35260
+ return container;
35261
+ container.addChild(new Spacer(1));
35262
+ visibleSections.forEach((section, index) => {
35263
+ if (index > 0)
35264
+ container.addChild(new Spacer(1));
35265
+ container.addChild(new Text(section, 0, 0));
35266
+ });
35267
+ return container;
35268
+ }
35269
+ function asRecord3(value) {
35270
+ if (!value || typeof value !== "object" || Array.isArray(value))
35271
+ return;
35272
+ return value;
35273
+ }
35274
+ function asRecords2(value) {
35275
+ return Array.isArray(value) ? value.map(asRecord3).filter(Boolean) : [];
35276
+ }
35277
+ function asString2(value) {
35278
+ return typeof value === "string" ? value : undefined;
35279
+ }
35280
+ function asNumber2(value) {
35281
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
35282
+ }
35283
+ function formatValue(value) {
35284
+ if (Array.isArray(value))
35285
+ return value.map(formatValue).join(", ");
35286
+ if (typeof value === "string")
35287
+ return value;
35288
+ if (typeof value === "number" || typeof value === "boolean")
35289
+ return String(value);
35290
+ if (value === null || value === undefined)
35291
+ return "—";
35292
+ try {
35293
+ return JSON.stringify(value);
35294
+ } catch {
35295
+ return String(value);
35296
+ }
35297
+ }
35298
+ function groupByFile(items, getFile) {
35299
+ const groups = new Map;
35300
+ items.forEach((item) => {
35301
+ const file2 = getFile(item) ?? "(unknown file)";
35302
+ const current = groups.get(file2) ?? [];
35303
+ current.push(item);
35304
+ groups.set(file2, current);
35305
+ });
35306
+ return groups;
35307
+ }
35308
+ function formatTimestamp(value) {
35309
+ if (typeof value === "string" && value.length > 0)
35310
+ return value;
35311
+ if (typeof value !== "number" || !Number.isFinite(value))
35312
+ return;
35313
+ const millis = value > 1000000000000 ? value : value * 1000;
35314
+ const date5 = new Date(millis);
35315
+ if (Number.isNaN(date5.getTime()))
35316
+ return String(value);
35317
+ return date5.toISOString().replace("T", " ").replace(".000Z", "Z");
35318
+ }
35319
+ function formatUnifiedDiffForPi(unifiedDiff) {
35320
+ if (!unifiedDiff.trim())
35321
+ return "";
35322
+ const entries = [];
35323
+ const hunkHeader = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
35324
+ let oldLine = 1;
35325
+ let newLine = 1;
35326
+ for (const line of unifiedDiff.split(`
35327
+ `)) {
35328
+ if (!line)
35329
+ continue;
35330
+ if (line.startsWith("--- ") || line.startsWith("+++ "))
35331
+ continue;
35332
+ if (line.startsWith("\"))
35333
+ continue;
35334
+ const headerMatch = line.match(hunkHeader);
35335
+ if (headerMatch) {
35336
+ oldLine = Number(headerMatch[1]);
35337
+ newLine = Number(headerMatch[2]);
35338
+ continue;
35339
+ }
35340
+ if (line.startsWith("+") && !line.startsWith("+++")) {
35341
+ entries.push({ prefix: "+", line: newLine, text: line.slice(1) });
35342
+ newLine += 1;
35343
+ continue;
35344
+ }
35345
+ if (line.startsWith("-") && !line.startsWith("---")) {
35346
+ entries.push({ prefix: "-", line: oldLine, text: line.slice(1) });
35347
+ oldLine += 1;
35348
+ continue;
35349
+ }
35350
+ if (line.startsWith(" ")) {
35351
+ entries.push({ prefix: " ", line: oldLine, text: line.slice(1) });
35352
+ oldLine += 1;
35353
+ newLine += 1;
35354
+ }
35355
+ }
35356
+ if (entries.length === 0)
35357
+ return "";
35358
+ const width = String(entries.reduce((max, entry) => Math.max(max, entry.line), 1)).length;
35359
+ return entries.map((entry) => `${entry.prefix}${String(entry.line).padStart(width, " ")} ${entry.text}`).join(`
35360
+ `);
35361
+ }
35362
+ function renderUnifiedDiff(unifiedDiff) {
35363
+ const piDiff = formatUnifiedDiffForPi(unifiedDiff);
35364
+ if (!piDiff)
35365
+ return "";
35366
+ try {
35367
+ return renderDiff(piDiff);
35368
+ } catch {
35369
+ return piDiff;
35370
+ }
35371
+ }
35372
+
35084
35373
  // src/tools/hoisted.ts
35085
35374
  function readAttachments(response) {
35086
35375
  return Array.isArray(response.attachments) ? response.attachments : [];
@@ -35113,22 +35402,22 @@ function modelSupportsImages(extCtx) {
35113
35402
  var NON_VISION_IMAGE_NOTE = "[Current model does not support images. The image will be omitted from this request.]";
35114
35403
  function containsPath(parent, child) {
35115
35404
  const rel = relative4(parent, child);
35116
- return rel === "" || !rel.startsWith("..") && !isAbsolute6(rel);
35405
+ return rel === "" || !rel.startsWith("..") && !isAbsolute7(rel);
35117
35406
  }
35118
35407
  function normalizePathInput(path2) {
35119
35408
  const decoded = decodeFileUrl(path2);
35120
35409
  if (!decoded || !decoded.startsWith("~"))
35121
35410
  return decoded;
35122
35411
  if (decoded === "~")
35123
- return homedir12();
35412
+ return homedir14();
35124
35413
  if (decoded.startsWith(`~${sep}`) || decoded.startsWith("~/")) {
35125
- return resolve8(homedir12(), decoded.slice(2));
35414
+ return resolve9(homedir14(), decoded.slice(2));
35126
35415
  }
35127
35416
  return decoded;
35128
35417
  }
35129
35418
  function absoluteSearchPath(cwd, target) {
35130
35419
  const expanded = normalizePathInput(target);
35131
- return isAbsolute6(expanded) ? expanded : resolve8(cwd, expanded);
35420
+ return isAbsolute7(expanded) ? expanded : resolve9(cwd, expanded);
35132
35421
  }
35133
35422
  async function searchPathExists(cwd, target) {
35134
35423
  try {
@@ -35184,7 +35473,7 @@ async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
35184
35473
  if (!target)
35185
35474
  return;
35186
35475
  const expanded = normalizePathInput(target);
35187
- const absoluteTarget = isAbsolute6(expanded) ? expanded : resolve8(extCtx.cwd, expanded);
35476
+ const absoluteTarget = isAbsolute7(expanded) ? expanded : resolve9(extCtx.cwd, expanded);
35188
35477
  if (containsPath(extCtx.cwd, absoluteTarget))
35189
35478
  return;
35190
35479
  if (options.restrictToProjectRoot === false)
@@ -35240,6 +35529,7 @@ var HASHLINE_EDIT_DESCRIPTION = [
35240
35529
  "",
35241
35530
  "Quick reference:",
35242
35531
  "- Header: `[path#TAG]`; TAG is exactly four hexadecimal digits from a current tagged read. Read every addressed row and gap boundary; REM and MV require a whole-file tagged read. Re-read after an edit before chaining: an edit-response tag can retain only changed context.",
35532
+ "- Same canonical path: multiple sections compose in patch order against pre-request coordinates.",
35243
35533
  "- Addresses: `0` (BOF), `N` (one line), `N.=M` (range; `N..=M`/`N..M` also work), `<N`/`>N` (gap before/after), `N*`/`<N*`/`>N*` (block), and `$`/`$-K` (EOF-relative). A plain `N` PUT replaces; use `<N` or `>N` to insert.",
35244
35534
  "- PUT text: `PUT <address>:` followed by one or more `+` body rows (`+` alone is blank). A final patch newline is allowed. PUT without `:` copies `@name` (or the anonymous register) and takes no body; names use `@` plus ASCII letters, digits, `_`, or `-`.",
35245
35535
  "- CUT: `CUT <address> [@name]`. REM: bare `REM` only, removing the whole file. MV: `MV <destination>` (one whitespace-free path, optional matching quotes), once and after any line operations. `*** Begin Patch`/`*** End Patch` is an optional envelope."
@@ -35309,9 +35599,9 @@ function validateBatchEdits(edits) {
35309
35599
  });
35310
35600
  }
35311
35601
  function renderReadCall(args, theme, context) {
35312
- const text = reuseText(context.lastComponent);
35602
+ const text = reuseText2(context.lastComponent);
35313
35603
  const filePath = args ? readPathArg(args) : undefined;
35314
- const pathDisplay = filePath ? theme.fg("accent", shortenPath2(filePath)) : theme.fg("toolOutput", "...");
35604
+ const pathDisplay = filePath ? theme.fg("accent", shortenPath3(filePath)) : theme.fg("toolOutput", "...");
35315
35605
  text.setText(`${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`);
35316
35606
  return text;
35317
35607
  }
@@ -35413,8 +35703,8 @@ PDFs aren't supported on the Pi harness yet.`, response);
35413
35703
  renderCall(args, theme, context) {
35414
35704
  return renderMutationCall("write", mutationFilePathArg(args ?? {}), theme, context);
35415
35705
  },
35416
- renderResult(result, _options, theme, context) {
35417
- return renderMutationResult(result, theme, context);
35706
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
35707
+ return renderMutationResult(result, theme, context, options);
35418
35708
  }
35419
35709
  }));
35420
35710
  }
@@ -35496,8 +35786,8 @@ PDFs aren't supported on the Pi harness yet.`, response);
35496
35786
  renderCall(args, theme, context) {
35497
35787
  return renderMutationCall("edit", mutationFilePathArg(args ?? {}), theme, context);
35498
35788
  },
35499
- renderResult(result, _options, theme, context) {
35500
- return renderMutationResult(result, theme, context);
35789
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
35790
+ return renderMutationResult(result, theme, context, options);
35501
35791
  }
35502
35792
  }));
35503
35793
  }
@@ -35637,23 +35927,23 @@ function formatDiagnosticsText(diagnostics) {
35637
35927
  return JSON.stringify(diagnostics, null, 2);
35638
35928
  }
35639
35929
  }
35640
- function reuseText(last) {
35641
- return last instanceof Text ? last : new Text("", 0, 0);
35930
+ function reuseText2(last) {
35931
+ return last instanceof Text2 ? last : new Text2("", 0, 0);
35642
35932
  }
35643
- function reuseContainer(last) {
35644
- return last instanceof Container ? last : new Container;
35933
+ function reuseContainer2(last) {
35934
+ return last instanceof Container2 ? last : new Container2;
35645
35935
  }
35646
35936
  function renderMutationCall(toolName, filePath, theme, context) {
35647
- const text = reuseText(context.lastComponent);
35648
- const pathDisplay = filePath ? theme.fg("accent", shortenPath2(filePath)) : theme.fg("toolOutput", "...");
35937
+ const text = reuseText2(context.lastComponent);
35938
+ const pathDisplay = filePath ? theme.fg("accent", shortenPath3(filePath)) : theme.fg("toolOutput", "...");
35649
35939
  text.setText(`${theme.fg("toolTitle", theme.bold(toolName))} ${pathDisplay}`);
35650
35940
  return text;
35651
35941
  }
35652
- function renderMutationResult(result, theme, context) {
35942
+ function renderMutationResult(result, theme, context, options = { expanded: true }) {
35653
35943
  if (context.isError) {
35654
35944
  const errorText = result.content.filter((c) => c.type === "text").map((c) => c.text ?? "").join(`
35655
35945
  `).trim();
35656
- const text = reuseText(context.lastComponent);
35946
+ const text = reuseText2(context.lastComponent);
35657
35947
  text.setText(`
35658
35948
  ${theme.fg("error", errorText || "edit failed")}`);
35659
35949
  return text;
@@ -35663,27 +35953,46 @@ ${theme.fg("error", errorText || "edit failed")}`);
35663
35953
  if (!diff) {
35664
35954
  const additions = details?.additions ?? 0;
35665
35955
  const deletions = details?.deletions ?? 0;
35666
- const text = reuseText(context.lastComponent);
35667
35956
  const countDetail = typeof details?.editsApplied === "number" && details.editsApplied > 1 ? `, ${details.editsApplied} edits` : typeof details?.replacements === "number" && details.replacements > 1 ? `, ${details.replacements} replacements` : "";
35668
- const summary = theme.fg("success", `+${additions}/-${deletions}${countDetail}`);
35957
+ const summary2 = theme.fg("success", `+${additions}/-${deletions}${countDetail}`);
35669
35958
  let suffix = "";
35670
35959
  if (details?.truncated) {
35671
35960
  suffix = ` ${theme.fg("muted", "(diff truncated)")}`;
35672
35961
  } else if (details?.noOp) {
35673
35962
  suffix = ` ${theme.fg("muted", "(no net change)")}`;
35674
35963
  }
35675
- text.setText(`
35676
- ${summary}${suffix}`);
35677
- return text;
35964
+ const full = reuseText2(context.lastComponent);
35965
+ full.setText(`
35966
+ ${summary2}${suffix}`);
35967
+ return collapsibleResult({
35968
+ summary: `${summary2}${suffix}`,
35969
+ full,
35970
+ expanded: options.expanded,
35971
+ context
35972
+ });
35678
35973
  }
35679
- const container = reuseContainer(context.lastComponent);
35974
+ const container = reuseContainer2(context.lastComponent);
35680
35975
  container.clear();
35681
- container.addChild(new Spacer(1));
35682
- container.addChild(new Text(renderDiff(diff), 1, 0));
35683
- return container;
35976
+ container.addChild(new Spacer2(1));
35977
+ let renderedDiff;
35978
+ try {
35979
+ renderedDiff = renderDiff2(diff);
35980
+ } catch {
35981
+ renderedDiff = diff;
35982
+ }
35983
+ container.addChild(new Text2(renderedDiff, 1, 0));
35984
+ const rawPath = context.args && typeof context.args === "object" && typeof context.args.path === "string" ? context.args.path : undefined;
35985
+ const summaryPath = rawPath ? ` ${shortenPath3(rawPath)}` : "";
35986
+ const summary = `edited${summaryPath} (+${details?.additions ?? 0}/-${details?.deletions ?? 0})`;
35987
+ return collapsibleResult({
35988
+ summary,
35989
+ full: container,
35990
+ expanded: options.expanded,
35991
+ context
35992
+ });
35684
35993
  }
35685
- function shortenPath2(path2) {
35686
- const home = homedir12();
35994
+ function shortenPath3(path2) {
35995
+ const home = homedir14();
35687
35996
  if (path2.startsWith(home))
35688
35997
  return `~${path2.slice(home.length)}`;
35689
35998
  return path2;
@@ -35699,174 +36008,6 @@ async function resolvePathArg(cwd, path2) {
35699
36008
  }
35700
36009
  }
35701
36010
 
35702
- // src/tools/render-helpers.ts
35703
- import { homedir as homedir13 } from "node:os";
35704
- import { renderDiff as renderDiff2 } from "@earendil-works/pi-coding-agent";
35705
- import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
35706
- function reuseText2(last) {
35707
- return last instanceof Text2 ? last : new Text2("", 0, 0);
35708
- }
35709
- function reuseContainer2(last) {
35710
- return last instanceof Container2 ? last : new Container2;
35711
- }
35712
- function shortenPath3(path2) {
35713
- const home = homedir13();
35714
- if (path2.startsWith(home))
35715
- return `~${path2.slice(home.length)}`;
35716
- return path2;
35717
- }
35718
- function renderToolCall(toolName, summary, theme, context) {
35719
- const text = reuseText2(context.lastComponent);
35720
- const suffix = summary ? ` ${summary}` : "";
35721
- text.setText(`${theme.fg("toolTitle", theme.bold(toolName))}${suffix}`);
35722
- return text;
35723
- }
35724
- function accentPath(theme, path2) {
35725
- if (!path2)
35726
- return theme.fg("toolOutput", "...");
35727
- return theme.fg("accent", shortenPath3(path2));
35728
- }
35729
- function collectTextContent(result) {
35730
- return result.content.filter((part) => part.type === "text").map((part) => part.text ?? "").join(`
35731
- `).trim();
35732
- }
35733
- function extractStructuredPayload(result) {
35734
- if (result.details !== undefined)
35735
- return result.details;
35736
- const text = collectTextContent(result);
35737
- if (!text)
35738
- return;
35739
- try {
35740
- return JSON.parse(text);
35741
- } catch {
35742
- return;
35743
- }
35744
- }
35745
- function renderErrorResult(result, fallback, theme, context) {
35746
- const text = reuseText2(context.lastComponent);
35747
- const message = collectTextContent(result) || fallback;
35748
- text.setText(`
35749
- ${theme.fg("error", message)}`);
35750
- return text;
35751
- }
35752
- function renderSections(sections, context) {
35753
- const container = reuseContainer2(context.lastComponent);
35754
- container.clear();
35755
- const visibleSections = sections.filter((section) => section.trim().length > 0);
35756
- if (visibleSections.length === 0)
35757
- return container;
35758
- container.addChild(new Spacer2(1));
35759
- visibleSections.forEach((section, index) => {
35760
- if (index > 0)
35761
- container.addChild(new Spacer2(1));
35762
- container.addChild(new Text2(section, 0, 0));
35763
- });
35764
- return container;
35765
- }
35766
- function asRecord3(value) {
35767
- if (!value || typeof value !== "object" || Array.isArray(value))
35768
- return;
35769
- return value;
35770
- }
35771
- function asRecords2(value) {
35772
- return Array.isArray(value) ? value.map(asRecord3).filter(Boolean) : [];
35773
- }
35774
- function asString2(value) {
35775
- return typeof value === "string" ? value : undefined;
35776
- }
35777
- function asNumber2(value) {
35778
- return typeof value === "number" && Number.isFinite(value) ? value : undefined;
35779
- }
35780
- function formatValue(value) {
35781
- if (Array.isArray(value))
35782
- return value.map(formatValue).join(", ");
35783
- if (typeof value === "string")
35784
- return value;
35785
- if (typeof value === "number" || typeof value === "boolean")
35786
- return String(value);
35787
- if (value === null || value === undefined)
35788
- return "—";
35789
- try {
35790
- return JSON.stringify(value);
35791
- } catch {
35792
- return String(value);
35793
- }
35794
- }
35795
- function groupByFile(items, getFile) {
35796
- const groups = new Map;
35797
- items.forEach((item) => {
35798
- const file2 = getFile(item) ?? "(unknown file)";
35799
- const current = groups.get(file2) ?? [];
35800
- current.push(item);
35801
- groups.set(file2, current);
35802
- });
35803
- return groups;
35804
- }
35805
- function formatTimestamp(value) {
35806
- if (typeof value === "string" && value.length > 0)
35807
- return value;
35808
- if (typeof value !== "number" || !Number.isFinite(value))
35809
- return;
35810
- const millis = value > 1000000000000 ? value : value * 1000;
35811
- const date5 = new Date(millis);
35812
- if (Number.isNaN(date5.getTime()))
35813
- return String(value);
35814
- return date5.toISOString().replace("T", " ").replace(".000Z", "Z");
35815
- }
35816
- function formatUnifiedDiffForPi(unifiedDiff) {
35817
- if (!unifiedDiff.trim())
35818
- return "";
35819
- const entries = [];
35820
- const hunkHeader = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
35821
- let oldLine = 1;
35822
- let newLine = 1;
35823
- for (const line of unifiedDiff.split(`
35824
- `)) {
35825
- if (!line)
35826
- continue;
35827
- if (line.startsWith("--- ") || line.startsWith("+++ "))
35828
- continue;
35829
- if (line.startsWith("\"))
35830
- continue;
35831
- const headerMatch = line.match(hunkHeader);
35832
- if (headerMatch) {
35833
- oldLine = Number(headerMatch[1]);
35834
- newLine = Number(headerMatch[2]);
35835
- continue;
35836
- }
35837
- if (line.startsWith("+") && !line.startsWith("+++")) {
35838
- entries.push({ prefix: "+", line: newLine, text: line.slice(1) });
35839
- newLine += 1;
35840
- continue;
35841
- }
35842
- if (line.startsWith("-") && !line.startsWith("---")) {
35843
- entries.push({ prefix: "-", line: oldLine, text: line.slice(1) });
35844
- oldLine += 1;
35845
- continue;
35846
- }
35847
- if (line.startsWith(" ")) {
35848
- entries.push({ prefix: " ", line: oldLine, text: line.slice(1) });
35849
- oldLine += 1;
35850
- newLine += 1;
35851
- }
35852
- }
35853
- if (entries.length === 0)
35854
- return "";
35855
- const width = String(entries.reduce((max, entry) => Math.max(max, entry.line), 1)).length;
35856
- return entries.map((entry) => `${entry.prefix}${String(entry.line).padStart(width, " ")} ${entry.text}`).join(`
35857
- `);
35858
- }
35859
- function renderUnifiedDiff(unifiedDiff) {
35860
- const piDiff = formatUnifiedDiffForPi(unifiedDiff);
35861
- if (!piDiff)
35862
- return "";
35863
- try {
35864
- return renderDiff2(piDiff);
35865
- } catch {
35866
- return piDiff;
35867
- }
35868
- }
35869
-
35870
36011
  // src/tools/ast.ts
35871
36012
  var AstLang = StringEnum(["typescript", "tsx", "javascript", "python", "rust", "go", "pascal", "r", "objc"], {
35872
36013
  description: "Target language"
@@ -35944,7 +36085,7 @@ function buildAstSearchSections(payload, theme) {
35944
36085
  const grouped = groupByFile(matches, (match) => asString2(match.file));
35945
36086
  const sections = [header];
35946
36087
  for (const [file2, fileMatches] of grouped.entries()) {
35947
- const lines = [theme.fg("accent", shortenPath3(file2))];
36088
+ const lines = [theme.fg("accent", shortenPath2(file2))];
35948
36089
  fileMatches.forEach((match, index) => {
35949
36090
  const line = asNumber2(match.line) ?? 0;
35950
36091
  const column = asNumber2(match.column) ?? 0;
@@ -35991,7 +36132,7 @@ function buildAstReplaceSections(payload, theme) {
35991
36132
  return sections;
35992
36133
  }
35993
36134
  files.forEach((fileResult) => {
35994
- const file2 = shortenPath3(asString2(fileResult.file) ?? "(unknown file)");
36135
+ const file2 = shortenPath2(asString2(fileResult.file) ?? "(unknown file)");
35995
36136
  const replacements = asNumber2(fileResult.replacements) ?? 0;
35996
36137
  const error53 = asString2(fileResult.error);
35997
36138
  const diff = asString2(fileResult.diff);
@@ -36017,17 +36158,27 @@ function renderAstCall(toolName, args, theme, context) {
36017
36158
  const summary = toolName === "ast_grep_replace" ? `${lang} ${theme.fg("toolOutput", `${args.pattern} → ${args.rewrite}`)}` : `${lang} ${theme.fg("toolOutput", args.pattern)}`;
36018
36159
  return renderToolCall(toolName === "ast_grep_replace" ? "ast replace" : "ast search", summary, theme, context);
36019
36160
  }
36020
- function renderAstResult(toolName, result, theme, context) {
36161
+ function renderAstResult(toolName, result, theme, context, options = { expanded: true }) {
36021
36162
  if (context.isError) {
36022
36163
  return renderErrorResult(result, `${toolName} failed`, theme, context);
36023
36164
  }
36024
36165
  const payload = extractStructuredPayload(result);
36025
36166
  if (!payload) {
36026
- const text = collectTextContent(result);
36027
- return renderSections([text || theme.fg("muted", "No result.")], context);
36167
+ const text = collectTextContent(result) || theme.fg("muted", "No result.");
36168
+ return collapsibleResult({
36169
+ summary: text,
36170
+ full: renderSections([text], context),
36171
+ expanded: options.expanded,
36172
+ context
36173
+ });
36028
36174
  }
36029
36175
  const sections = toolName === "ast_grep_replace" ? buildAstReplaceSections(payload, theme) : buildAstSearchSections(payload, theme);
36030
- return renderSections(sections, context);
36176
+ return collapsibleResult({
36177
+ summary: sections[0] ?? `${toolName} completed`,
36178
+ full: renderSections(sections, context),
36179
+ expanded: options.expanded,
36180
+ context
36181
+ });
36031
36182
  }
36032
36183
  function registerAstTools(pi, ctx, surface) {
36033
36184
  if (surface.astSearch) {
@@ -36059,8 +36210,8 @@ function registerAstTools(pi, ctx, surface) {
36059
36210
  renderCall(args, theme, context) {
36060
36211
  return renderAstCall("ast_grep_search", args, theme, context);
36061
36212
  },
36062
- renderResult(result, _options, theme, context) {
36063
- return renderAstResult("ast_grep_search", result, theme, context);
36213
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
36214
+ return renderAstResult("ast_grep_search", result, theme, context, options);
36064
36215
  }
36065
36216
  });
36066
36217
  }
@@ -36093,8 +36244,8 @@ function registerAstTools(pi, ctx, surface) {
36093
36244
  renderCall(args, theme, context) {
36094
36245
  return renderAstCall("ast_grep_replace", args, theme, context);
36095
36246
  },
36096
- renderResult(result, _options, theme, context) {
36097
- return renderAstResult("ast_grep_replace", result, theme, context);
36247
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
36248
+ return renderAstResult("ast_grep_replace", result, theme, context, options);
36098
36249
  }
36099
36250
  });
36100
36251
  }
@@ -36419,8 +36570,8 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
36419
36570
  renderCall(args, theme, context) {
36420
36571
  return renderBashCall(args?.command, args?.description, theme, context);
36421
36572
  },
36422
- renderResult(result, _options, theme, context) {
36423
- return renderBashResult(result, theme, context);
36573
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
36574
+ return renderBashResult(result, theme, context, options);
36424
36575
  }
36425
36576
  });
36426
36577
  if (bashCfg.background) {
@@ -36857,7 +37008,7 @@ function renderBashCall(command, description, theme, context) {
36857
37008
  text.setText(`${theme.fg("toolTitle", theme.bold("bash"))} ${theme.fg("accent", display)}`);
36858
37009
  return text;
36859
37010
  }
36860
- function renderBashResult(result, theme, context) {
37011
+ function renderBashResult(result, theme, context, options = { expanded: true }) {
36861
37012
  if (context.isError) {
36862
37013
  const errorText = result.content.filter((c) => c.type === "text").map((c) => c.text ?? "").join(`
36863
37014
  `).trim();
@@ -36904,7 +37055,14 @@ ${theme.fg("error", errorText || "bash failed")}`);
36904
37055
  const truncText = theme.fg("warning", "(output truncated)");
36905
37056
  container.addChild(new Text3(truncText, 1, 0));
36906
37057
  }
36907
- return container;
37058
+ const firstOutputLine = rawOutput.split(/\r?\n/, 1)[0] || "(no output)";
37059
+ const exitSummary = exitCode === undefined ? "completed" : `exit ${exitCode}`;
37060
+ return collapsibleResult({
37061
+ summary: `${exitSummary}: ${firstOutputLine}`,
37062
+ full: container,
37063
+ expanded: options.expanded,
37064
+ context
37065
+ });
36908
37066
  }
36909
37067
  function shortenCommand(command) {
36910
37068
  if (command.length <= 60)
@@ -36936,14 +37094,19 @@ function buildConflictSections(text) {
36936
37094
  sections.push(...rest.map((section) => section.trim()).filter(Boolean));
36937
37095
  return sections;
36938
37096
  }
36939
- function renderConflictResult(text, theme, context) {
37097
+ function renderConflictResult(text, theme, context, options = { expanded: true }) {
36940
37098
  const sections = buildConflictSections(text).map((section, index) => index === 0 ? theme.fg("warning", section) : section);
36941
- return renderSections(sections, context);
37099
+ return collapsibleResult({
37100
+ summary: sections[0] ?? "No merge conflicts found.",
37101
+ full: renderSections(sections, context),
37102
+ expanded: options.expanded,
37103
+ context
37104
+ });
36942
37105
  }
36943
- function renderConflictToolResult(result, theme, context) {
37106
+ function renderConflictToolResult(result, theme, context, options = { expanded: true }) {
36944
37107
  if (context.isError)
36945
37108
  return renderErrorResult(result, "conflicts failed", theme, context);
36946
- return renderConflictResult(collectTextContent(result), theme, context);
37109
+ return renderConflictResult(collectTextContent(result), theme, context, options);
36947
37110
  }
36948
37111
  function registerConflictsTool(pi, ctx) {
36949
37112
  pi.registerTool(withPathAliasPreparation({
@@ -36970,8 +37133,8 @@ function registerConflictsTool(pi, ctx) {
36970
37133
  renderCall(_args, theme, context) {
36971
37134
  return renderConflictCall(theme, context);
36972
37135
  },
36973
- renderResult(result, _options, theme, context) {
36974
- return renderConflictToolResult(result, theme, context);
37136
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
37137
+ return renderConflictToolResult(result, theme, context, options);
36975
37138
  }
36976
37139
  }));
36977
37140
  }
@@ -37014,7 +37177,7 @@ function renderFsCall(toolName, args, theme, context) {
37014
37177
  const moveArgs = args;
37015
37178
  return renderToolCall("move", `${accentPath(theme, moveArgs.path)} ${theme.fg("muted", "→")} ${accentPath(theme, moveArgs.destination)}`, theme, context);
37016
37179
  }
37017
- function renderFsResult(toolName, args, result, theme, context) {
37180
+ function renderFsResult(toolName, args, result, theme, context, options = { expanded: true }) {
37018
37181
  if (context.isError) {
37019
37182
  return renderErrorResult(result, `${toolName} failed`, theme, context);
37020
37183
  }
@@ -37025,22 +37188,33 @@ function renderFsResult(toolName, args, result, theme, context) {
37025
37188
  const skipped = data.skipped_files ?? [];
37026
37189
  const lines = [];
37027
37190
  for (const entry of deletedPaths) {
37028
- lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent", shortenPath3(entry))}`);
37191
+ lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent", shortenPath2(entry))}`);
37029
37192
  }
37030
37193
  for (const entry of skipped) {
37031
- lines.push(`${theme.fg("error", "✗ skipped")} ${theme.fg("accent", shortenPath3(entry.file))} ${theme.fg("muted", `(${entry.reason})`)}`);
37194
+ lines.push(`${theme.fg("error", "✗ skipped")} ${theme.fg("accent", shortenPath2(entry.file))} ${theme.fg("muted", `(${entry.reason})`)}`);
37032
37195
  }
37033
37196
  if (lines.length === 0) {
37034
37197
  lines.push(theme.fg("muted", "(no files deleted)"));
37035
37198
  }
37036
- return renderSections([lines.join(`
37037
- `)], context);
37199
+ return collapsibleResult({
37200
+ summary: `${deletedPaths.length} deleted, ${skipped.length} skipped`,
37201
+ full: renderSections([lines.join(`
37202
+ `)], context),
37203
+ expanded: options.expanded,
37204
+ context
37205
+ });
37038
37206
  }
37039
37207
  const moveArgs = args;
37040
- return renderSections([
37041
- `${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath3(moveArgs.path ?? ""))}`,
37042
- `${theme.fg("muted", "to")} ${theme.fg("accent", shortenPath3(moveArgs.destination))}`
37043
- ], context);
37208
+ const sections = [
37209
+ `${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath2(moveArgs.path ?? ""))}`,
37210
+ `${theme.fg("muted", "to")} ${theme.fg("accent", shortenPath2(moveArgs.destination))}`
37211
+ ];
37212
+ return collapsibleResult({
37213
+ summary: `moved ${shortenPath2(moveArgs.path ?? "")} to ${shortenPath2(moveArgs.destination)}`,
37214
+ full: renderSections(sections, context),
37215
+ expanded: options.expanded,
37216
+ context
37217
+ });
37044
37218
  }
37045
37219
  function registerFsTools(pi, ctx, surface) {
37046
37220
  const backupsDisabled = ctx.config.backup?.enabled === false;
@@ -37086,8 +37260,8 @@ function registerFsTools(pi, ctx, surface) {
37086
37260
  renderCall(args, theme, context) {
37087
37261
  return renderFsCall("aft_delete", args, theme, context);
37088
37262
  },
37089
- renderResult(result, _options, theme, context) {
37090
- return renderFsResult("aft_delete", context.args, result, theme, context);
37263
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
37264
+ return renderFsResult("aft_delete", context.args, result, theme, context, options);
37091
37265
  }
37092
37266
  }));
37093
37267
  }
@@ -37119,8 +37293,8 @@ function registerFsTools(pi, ctx, surface) {
37119
37293
  renderCall(args, theme, context) {
37120
37294
  return renderFsCall("aft_move", args, theme, context);
37121
37295
  },
37122
- renderResult(result, _options, theme, context) {
37123
- return renderFsResult("aft_move", context.args, result, theme, context);
37296
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
37297
+ return renderFsResult("aft_move", context.args, result, theme, context, options);
37124
37298
  }
37125
37299
  }));
37126
37300
  }
@@ -37194,11 +37368,17 @@ function renderImportCall(args, theme, context) {
37194
37368
  ].filter(Boolean).join(" ");
37195
37369
  return renderToolCall("import", summary, theme, context);
37196
37370
  }
37197
- function renderImportResult(result, args, theme, context) {
37371
+ function renderImportResult(result, args, theme, context, options = { expanded: true }) {
37198
37372
  if (context.isError)
37199
37373
  return renderErrorResult(result, "import failed", theme, context);
37200
37374
  const payload = extractStructuredPayload(result);
37201
- return renderSections(buildImportSections(args, payload, theme), context);
37375
+ const sections = buildImportSections(args, payload, theme);
37376
+ return collapsibleResult({
37377
+ summary: sections[0] ?? `${args.op} import completed`,
37378
+ full: renderSections(sections, context),
37379
+ expanded: options.expanded,
37380
+ context
37381
+ });
37202
37382
  }
37203
37383
  function registerImportTools(pi, ctx) {
37204
37384
  const organizeRecovery = ctx.config.backup?.enabled === false ? "Backup capture is disabled by user config; review broad cleanup changes before proceeding." : "Use aft_safety checkpoint/undo before broad cleanup.";
@@ -37246,8 +37426,8 @@ function registerImportTools(pi, ctx) {
37246
37426
  renderCall(args, theme, context) {
37247
37427
  return renderImportCall(args, theme, context);
37248
37428
  },
37249
- renderResult(result, _options, theme, context) {
37250
- return renderImportResult(result, context.args, theme, context);
37429
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
37430
+ return renderImportResult(result, context.args, theme, context, options);
37251
37431
  }
37252
37432
  }));
37253
37433
  }
@@ -37531,14 +37711,18 @@ function renderInspectCall(args, theme, context) {
37531
37711
  const summary = [sections, scope, args.topK ? `topK=${args.topK}` : undefined].filter(Boolean).join(" ");
37532
37712
  return renderToolCall("inspect", summary ? theme.fg("toolOutput", summary) : undefined, theme, context);
37533
37713
  }
37534
- function renderInspectResult(result, theme, context) {
37714
+ function renderInspectResult(result, theme, context, options = { expanded: true }) {
37535
37715
  const payload = extractStructuredPayload(result);
37536
37716
  const terminal = parseInspectTerminal(payload);
37537
- if (terminal)
37538
- return renderSections([renderInspectTerminal(terminal, asString2(asRecord3(payload)?.text))], context);
37539
37717
  if (context.isError)
37540
37718
  return renderErrorResult(result, "inspect failed", theme, context);
37541
- return renderSections(buildInspectSections(payload, theme), context);
37719
+ const sections = terminal ? [renderInspectTerminal(terminal, asString2(asRecord3(payload)?.text))] : buildInspectSections(payload, theme);
37720
+ return collapsibleResult({
37721
+ summary: sections[0] ?? "inspect completed",
37722
+ full: renderSections(sections, context),
37723
+ expanded: options.expanded,
37724
+ context
37725
+ });
37542
37726
  }
37543
37727
  function registerInspectTool(pi, ctx) {
37544
37728
  pi.registerTool({
@@ -37573,8 +37757,8 @@ function registerInspectTool(pi, ctx) {
37573
37757
  renderCall(args, theme, context) {
37574
37758
  return renderInspectCall(args, theme, context);
37575
37759
  },
37576
- renderResult(result, _options, theme, context) {
37577
- return renderInspectResult(result, theme, context);
37760
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
37761
+ return renderInspectResult(result, theme, context, options);
37578
37762
  }
37579
37763
  });
37580
37764
  }
@@ -37625,10 +37809,16 @@ function renderNavigateCall(args, theme, context) {
37625
37809
  ].filter(Boolean).join(" ");
37626
37810
  return renderToolCall("callgraph", summary, theme, context);
37627
37811
  }
37628
- function renderNavigateResult(result, args, theme, context) {
37812
+ function renderNavigateResult(result, args, theme, context, options = { expanded: true }) {
37629
37813
  if (context.isError)
37630
37814
  return renderErrorResult(result, "navigate failed", theme, context);
37631
- return renderSections(buildNavigateSections(args, extractStructuredPayload(result), theme), context);
37815
+ const sections = buildNavigateSections(args, extractStructuredPayload(result), theme);
37816
+ return collapsibleResult({
37817
+ summary: sections[0] ?? `${args.op} completed`,
37818
+ full: renderSections(sections, context),
37819
+ expanded: options.expanded,
37820
+ context
37821
+ });
37632
37822
  }
37633
37823
  function registerNavigateTool(pi, ctx) {
37634
37824
  pi.registerTool(withPathAliasPreparation({
@@ -37696,8 +37886,8 @@ function registerNavigateTool(pi, ctx) {
37696
37886
  renderCall(args, theme, context) {
37697
37887
  return renderNavigateCall(args, theme, context);
37698
37888
  },
37699
- renderResult(result, _options, theme, context) {
37700
- return renderNavigateResult(result, context.args, theme, context);
37889
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
37890
+ return renderNavigateResult(result, context.args, theme, context, options);
37701
37891
  }
37702
37892
  }));
37703
37893
  }
@@ -37774,7 +37964,7 @@ function renderZoomRecord(record2, targetLabel, theme) {
37774
37964
  const range = asRecord3(record2.range);
37775
37965
  const startLine = range && typeof range.start_line === "number" ? range.start_line : undefined;
37776
37966
  const endLine = range && typeof range.end_line === "number" ? range.end_line : undefined;
37777
- const location = startLine !== undefined ? `${shortenPath3(targetLabel)}:${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : shortenPath3(targetLabel);
37967
+ const location = startLine !== undefined ? `${shortenPath2(targetLabel)}:${startLine}${endLine && endLine !== startLine ? `-${endLine}` : ""}` : shortenPath2(targetLabel);
37778
37968
  const lines = [`${theme.fg("accent", name)} ${theme.fg("muted", `[${kind}] ${location}`)}`];
37779
37969
  const content = asString2(record2.content);
37780
37970
  if (content) {
@@ -37811,7 +38001,7 @@ function buildZoomSections(args, payload, theme) {
37811
38001
  const name = asString2(record2.name) ?? asString2(response.name) ?? "(unknown symbol)";
37812
38002
  const itemTargetLabel = asString2(record2.targetLabel) ?? zoomTargetLabel(args);
37813
38003
  if (response.success === false || record2.success === false) {
37814
- const location = record2.targetLabel ? ` in ${shortenPath3(itemTargetLabel)}` : "";
38004
+ const location = record2.targetLabel ? ` in ${shortenPath2(itemTargetLabel)}` : "";
37815
38005
  return theme.fg("error", `Symbol "${name}" not found${location}: ${asString2(response.message) ?? asString2(record2.error) ?? "zoom failed"}`);
37816
38006
  }
37817
38007
  if (record2.response) {
@@ -37820,7 +38010,7 @@ function buildZoomSections(args, payload, theme) {
37820
38010
  const content = asString2(record2.content);
37821
38011
  if (content) {
37822
38012
  return [
37823
- `${theme.fg("accent", name)} ${theme.fg("muted", shortenPath3(itemTargetLabel))}`,
38013
+ `${theme.fg("accent", name)} ${theme.fg("muted", shortenPath2(itemTargetLabel))}`,
37824
38014
  content
37825
38015
  ].filter(Boolean).join(`
37826
38016
  `);
@@ -37843,10 +38033,16 @@ function renderOutlineCall(args, theme, context) {
37843
38033
  const summary = Array.isArray(args.target) ? theme.fg("accent", `${args.target.length} ${args.files ? "directories" : "files"}`) : typeof args.target === "string" ? `${accentPath(theme, args.target)}${args.files ? " files" : ""}` : undefined;
37844
38034
  return renderToolCall("outline", summary, theme, context);
37845
38035
  }
37846
- function renderOutlineResult(result, theme, context) {
38036
+ function renderOutlineResult(result, theme, context, options = { expanded: true }) {
37847
38037
  if (context.isError)
37848
38038
  return renderErrorResult(result, "outline failed", theme, context);
37849
- return renderSections(buildOutlineSections(collectTextContent(result), theme), context);
38039
+ const sections = buildOutlineSections(collectTextContent(result), theme);
38040
+ return collapsibleResult({
38041
+ summary: sections[0] ?? "outline completed",
38042
+ full: renderSections(sections, context),
38043
+ expanded: options.expanded,
38044
+ context
38045
+ });
37850
38046
  }
37851
38047
  function renderZoomCall(args, theme, context) {
37852
38048
  const symbols = args.symbols;
@@ -37865,10 +38061,16 @@ function renderZoomCall(args, theme, context) {
37865
38061
  }
37866
38062
  return renderToolCall("zoom", `${accentPath(theme, zoomTargetLabel(args))} ${summary}`, theme, context);
37867
38063
  }
37868
- function renderZoomResult(result, args, theme, context) {
38064
+ function renderZoomResult(result, args, theme, context, options = { expanded: true }) {
37869
38065
  if (context.isError)
37870
38066
  return renderErrorResult(result, "zoom failed", theme, context);
37871
- return renderSections(buildZoomSections(args, extractStructuredPayload(result), theme), context);
38067
+ const sections = buildZoomSections(args, extractStructuredPayload(result), theme);
38068
+ return collapsibleResult({
38069
+ summary: sections[0] ?? "zoom completed",
38070
+ full: renderSections(sections, context),
38071
+ expanded: options.expanded,
38072
+ context
38073
+ });
37872
38074
  }
37873
38075
  function registerReadingTools(pi, ctx, surface) {
37874
38076
  if (surface.outline) {
@@ -37930,8 +38132,8 @@ Pass a single \`target\`:
37930
38132
  renderCall(args, theme, context) {
37931
38133
  return renderOutlineCall(args, theme, context);
37932
38134
  },
37933
- renderResult(result, _options, theme, context) {
37934
- return renderOutlineResult(result, theme, context);
38135
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
38136
+ return renderOutlineResult(result, theme, context, options);
37935
38137
  }
37936
38138
  }));
37937
38139
  }
@@ -38022,8 +38224,8 @@ Pass a single \`target\`:
38022
38224
  renderCall(args, theme, context) {
38023
38225
  return renderZoomCall(args, theme, context);
38024
38226
  },
38025
- renderResult(result, _options, theme, context) {
38026
- return renderZoomResult(result, context.args, theme, context);
38227
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
38228
+ return renderZoomResult(result, context.args, theme, context, options);
38027
38229
  }
38028
38230
  }));
38029
38231
  }
@@ -38055,21 +38257,21 @@ function buildRefactorSections(args, payload, theme) {
38055
38257
  `${theme.fg("success", "moved symbol")} ${theme.fg("toolOutput", args.symbol ?? "(symbol)")}`,
38056
38258
  `${theme.fg("muted", "files modified")} ${asNumber2(response.files_modified) ?? results.length}`,
38057
38259
  `${theme.fg("muted", "consumers updated")} ${asNumber2(response.consumers_updated) ?? 0}`,
38058
- results.length > 0 ? results.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(unknown file)")}`).join(`
38260
+ results.length > 0 ? results.map((entry) => ` ↳ ${shortenPath2(asString2(entry.file) ?? "(unknown file)")}`).join(`
38059
38261
  `) : theme.fg("muted", "No files reported.")
38060
38262
  ];
38061
38263
  }
38062
38264
  if (args.op === "extract") {
38063
38265
  return [
38064
38266
  `${theme.fg("success", "extracted")} ${theme.fg("toolOutput", asString2(response.name) ?? args.name ?? "(function)")}`,
38065
- `${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? ""))}`,
38267
+ `${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath2(asString2(response.file) ?? args.path ?? ""))}`,
38066
38268
  `${theme.fg("muted", "params")} ${Array.isArray(response.parameters) ? response.parameters.join(", ") || "none" : "none"}`,
38067
38269
  `${theme.fg("muted", "return type")} ${asString2(response.return_type) ?? "unknown"}`
38068
38270
  ];
38069
38271
  }
38070
38272
  return [
38071
38273
  `${theme.fg("success", "inlined")} ${theme.fg("toolOutput", asString2(response.symbol) ?? args.symbol ?? "(symbol)")}`,
38072
- `${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? ""))}`,
38274
+ `${theme.fg("muted", "file")} ${theme.fg("accent", shortenPath2(asString2(response.file) ?? args.path ?? ""))}`,
38073
38275
  `${theme.fg("muted", "context")} ${asString2(response.call_context) ?? "unknown"}`,
38074
38276
  `${theme.fg("muted", "substitutions")} ${asNumber2(response.substitutions) ?? 0}`
38075
38277
  ];
@@ -38082,10 +38284,16 @@ function renderRefactorCall(args, theme, context) {
38082
38284
  ].filter(Boolean).join(" ");
38083
38285
  return renderToolCall("refactor", summary, theme, context);
38084
38286
  }
38085
- function renderRefactorResult(result, args, theme, context) {
38287
+ function renderRefactorResult(result, args, theme, context, options = { expanded: true }) {
38086
38288
  if (context.isError)
38087
38289
  return renderErrorResult(result, "refactor failed", theme, context);
38088
- return renderSections(buildRefactorSections(args, extractStructuredPayload(result), theme), context);
38290
+ const sections = buildRefactorSections(args, extractStructuredPayload(result), theme);
38291
+ return collapsibleResult({
38292
+ summary: sections[0] ?? `${args.op} completed`,
38293
+ full: renderSections(sections, context),
38294
+ expanded: options.expanded,
38295
+ context
38296
+ });
38089
38297
  }
38090
38298
  function registerRefactorTool(pi, ctx) {
38091
38299
  pi.registerTool(withPathAliasPreparation({
@@ -38152,8 +38360,8 @@ function registerRefactorTool(pi, ctx) {
38152
38360
  renderCall(args, theme, context) {
38153
38361
  return renderRefactorCall(args, theme, context);
38154
38362
  },
38155
- renderResult(result, _options, theme, context) {
38156
- return renderRefactorResult(result, context.args, theme, context);
38363
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
38364
+ return renderRefactorResult(result, context.args, theme, context, options);
38157
38365
  }
38158
38366
  }));
38159
38367
  }
@@ -38188,14 +38396,14 @@ function buildSafetySections(args, payload, theme) {
38188
38396
  ];
38189
38397
  }
38190
38398
  return [
38191
- `${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath3(asString2(response.path) ?? args.path ?? "(file)"))}`,
38399
+ `${theme.fg("success", "restored")} ${theme.fg("accent", shortenPath2(asString2(response.path) ?? args.path ?? "(file)"))}`,
38192
38400
  `${theme.fg("muted", "backup")} ${asString2(response.backup_id) ?? "—"}`
38193
38401
  ];
38194
38402
  }
38195
38403
  if (args.op === "history") {
38196
38404
  const entries = asRecords2(response.entries);
38197
38405
  const sections2 = [
38198
- theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? "(file)"))
38406
+ theme.fg("accent", shortenPath2(asString2(response.file) ?? args.path ?? "(file)"))
38199
38407
  ];
38200
38408
  if (entries.length === 0) {
38201
38409
  sections2.push(theme.fg("muted", "No history entries."));
@@ -38217,7 +38425,7 @@ function buildSafetySections(args, payload, theme) {
38217
38425
  `${theme.fg("success", "checkpoint created")} ${theme.fg("accent", asString2(response.name) ?? args.name ?? "(checkpoint)")}`,
38218
38426
  `${theme.fg("muted", "files")} ${asNumber2(response.file_count) ?? 0}`,
38219
38427
  skipped.length > 0 ? `${theme.fg("warning", "skipped")}
38220
- ${skipped.map((entry) => ` ↳ ${shortenPath3(asString2(entry.file) ?? "(file)")}: ${asString2(entry.error) ?? "unknown error"}`).join(`
38428
+ ${skipped.map((entry) => ` ↳ ${shortenPath2(asString2(entry.file) ?? "(file)")}: ${asString2(entry.error) ?? "unknown error"}`).join(`
38221
38429
  `)}` : theme.fg("muted", "No skipped files.")
38222
38430
  ];
38223
38431
  }
@@ -38249,10 +38457,16 @@ function renderSafetyCall(args, theme, context) {
38249
38457
  const summary = [theme.fg("accent", args.op), target ? accentPath(theme, target) : undefined].filter(Boolean).join(" ");
38250
38458
  return renderToolCall("safety", summary, theme, context);
38251
38459
  }
38252
- function renderSafetyResult(result, args, theme, context) {
38460
+ function renderSafetyResult(result, args, theme, context, options = { expanded: true }) {
38253
38461
  if (context.isError)
38254
38462
  return renderErrorResult(result, "safety failed", theme, context);
38255
- return renderSections(buildSafetySections(args, extractStructuredPayload(result), theme), context);
38463
+ const sections = buildSafetySections(args, extractStructuredPayload(result), theme);
38464
+ return collapsibleResult({
38465
+ summary: `${args.op}: ${sections[0] ?? "completed"}`,
38466
+ full: renderSections(sections, context),
38467
+ expanded: options.expanded,
38468
+ context
38469
+ });
38256
38470
  }
38257
38471
  function registerSafetyTool(pi, ctx) {
38258
38472
  pi.registerTool(withPathAliasPreparation({
@@ -38321,8 +38535,8 @@ function registerSafetyTool(pi, ctx) {
38321
38535
  renderCall(args, theme, context) {
38322
38536
  return renderSafetyCall(args, theme, context);
38323
38537
  },
38324
- renderResult(result, _options, theme, context) {
38325
- return renderSafetyResult(result, context.args, theme, context);
38538
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
38539
+ return renderSafetyResult(result, context.args, theme, context, options);
38326
38540
  }
38327
38541
  }));
38328
38542
  }
@@ -38389,7 +38603,7 @@ function buildSemanticSections(args, payload, theme) {
38389
38603
  }
38390
38604
  const grouped = groupByFile(results, (result) => asString2(result.file));
38391
38605
  for (const [file2, fileResults] of grouped.entries()) {
38392
- const lines = [theme.fg("accent", shortenPath3(file2))];
38606
+ const lines = [theme.fg("accent", shortenPath2(file2))];
38393
38607
  fileResults.forEach((result) => {
38394
38608
  if (asString2(result.kind) === "GrepLine") {
38395
38609
  const line = asNumber2(result.line);
@@ -38436,10 +38650,16 @@ function buildSemanticSections(args, payload, theme) {
38436
38650
  function renderSemanticCall(args, theme, context) {
38437
38651
  return renderToolCall("search", theme.fg("toolOutput", args.query), theme, context);
38438
38652
  }
38439
- function renderSemanticResult(result, args, theme, context) {
38653
+ function renderSemanticResult(result, args, theme, context, options = { expanded: true }) {
38440
38654
  if (context.isError)
38441
38655
  return renderErrorResult(result, "search failed", theme, context);
38442
- return renderSections(buildSemanticSections(args, extractStructuredPayload(result), theme), context);
38656
+ const sections = buildSemanticSections(args, extractStructuredPayload(result), theme);
38657
+ return collapsibleResult({
38658
+ summary: sections[0] ?? "search completed",
38659
+ full: renderSections(sections, context),
38660
+ expanded: options.expanded,
38661
+ context
38662
+ });
38443
38663
  }
38444
38664
  function registerSemanticTool(pi, ctx) {
38445
38665
  pi.registerTool(withPathAliasPreparation({
@@ -38471,8 +38691,8 @@ function registerSemanticTool(pi, ctx) {
38471
38691
  renderCall(args, theme, context) {
38472
38692
  return renderSemanticCall(args, theme, context);
38473
38693
  },
38474
- renderResult(result, _options, theme, context) {
38475
- return renderSemanticResult(result, context.args, theme, context);
38694
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
38695
+ return renderSemanticResult(result, context.args, theme, context, options);
38476
38696
  }
38477
38697
  }));
38478
38698
  }
@@ -38727,7 +38947,7 @@ var PLUGIN_VERSION = (() => {
38727
38947
  return "0.0.0";
38728
38948
  }
38729
38949
  })();
38730
- var ANNOUNCEMENT_VERSION = "0.52.0";
38950
+ var ANNOUNCEMENT_VERSION = "0.52.2";
38731
38951
  var ANNOUNCEMENT_FEATURES = [
38732
38952
  "Callgraph stays consistent under load: edits arriving mid-rebuild can no longer be silently lost from navigation results.",
38733
38953
  "aft_zoom handles real-world .jsonc: files with comment banners above the opening brace now resolve, and not-found errors point at the segment that actually failed (thanks @iceteaSA).",
@@ -39078,7 +39298,7 @@ ${lines}
39078
39298
  if (onnxRuntimePromise) {
39079
39299
  await Promise.race([
39080
39300
  onnxRuntimePromise,
39081
- new Promise((resolve9) => setTimeout(() => resolve9(null), 60000))
39301
+ new Promise((resolve10) => setTimeout(() => resolve10(null), 60000))
39082
39302
  ]);
39083
39303
  }
39084
39304
  const bridge = pool.getBridge(cwd);