@cortexkit/aft-pi 0.52.1 → 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
  }
@@ -12945,8 +12966,8 @@ class SubcTransportPool {
12945
12966
  this.connectFn = options.connect ?? ((opts) => SubcClient.connect(opts));
12946
12967
  this.onBgEventsNudge = options.onBgEventsNudge;
12947
12968
  this.onBgEventsNudgeRef = options.onBgEventsNudgeRef;
12948
- this.bgBackoffSleep = options.bgBackoffSleep ?? ((ms) => new Promise((resolve4) => setTimeout(resolve4, ms)));
12949
- 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)));
12950
12971
  this.bgDispatchProbeIntervalMs = options.bgDispatchProbeIntervalMs ?? BG_DISPATCH_PROBE_INTERVAL_MS;
12951
12972
  const lifecycle = options.lifecycle;
12952
12973
  const demandCheck = options.lifecycleDemandCheck ?? options.demandCheck ?? lifecycle?.demandCheck;
@@ -13862,12 +13883,12 @@ function stripJsoncSymbols(value) {
13862
13883
  import { spawnSync as spawnSync2 } from "node:child_process";
13863
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";
13864
13885
  import { homedir as homedir8, tmpdir } from "node:os";
13865
- 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";
13866
13887
 
13867
13888
  // ../aft-bridge/dist/paths.js
13868
13889
  import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync } from "node:fs";
13869
13890
  import { homedir as homedir6 } from "node:os";
13870
- 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";
13871
13892
  function homeDir3() {
13872
13893
  if (process.platform === "win32")
13873
13894
  return process.env.USERPROFILE || process.env.HOME || homedir6();
@@ -13882,7 +13903,7 @@ function configHome() {
13882
13903
  function legacyOpenCodeConfigDir() {
13883
13904
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
13884
13905
  if (envDir)
13885
- return resolve4(envDir);
13906
+ return resolve5(envDir);
13886
13907
  return join6(configHome(), "opencode");
13887
13908
  }
13888
13909
  function legacyPiAgentDir() {
@@ -14247,9 +14268,6 @@ function resolveLegacyStorageRoot(harness) {
14247
14268
  return join8(homeDir4(), ".pi", "agent", "aft");
14248
14269
  return join8(dataHome2(), "opencode", "storage", "plugin", "aft");
14249
14270
  }
14250
- function resolveCortexKitStorageRoot() {
14251
- return join8(dataHome2(), "cortexkit", "aft");
14252
- }
14253
14271
  function stripJsoncForParse(input) {
14254
14272
  let out = "";
14255
14273
  let inString = false;
@@ -14519,8 +14537,8 @@ function visibleConfigMigrationWarning(scope, targetPath, paths, reason) {
14519
14537
  }
14520
14538
  function migrateAftConfigFile(opts) {
14521
14539
  const warnings = [];
14522
- const resolvedTarget = resolve5(opts.targetPath);
14523
- 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);
14524
14542
  const info = opts.logger?.info ?? opts.logger?.log;
14525
14543
  if (existingSources.length === 0) {
14526
14544
  return { migrated: false, conflict: false, targetPath: opts.targetPath, warnings };
@@ -14766,7 +14784,7 @@ function npmSpawnEnv(resolved, baseEnv = process.env) {
14766
14784
  import { execFileSync } from "node:child_process";
14767
14785
  import { createHash as createHash3 } from "node:crypto";
14768
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";
14769
- 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";
14770
14788
  import { Readable as Readable2 } from "node:stream";
14771
14789
  import { pipeline as pipeline2 } from "node:stream/promises";
14772
14790
  var ORT_VERSION = "1.24.4";
@@ -15071,7 +15089,7 @@ function findSystemOnnxRuntime(libName) {
15071
15089
  const normalizeCase = process.platform === "win32" || process.platform === "darwin";
15072
15090
  const seen = new Set;
15073
15091
  const uniquePaths = searchPaths.filter((p) => {
15074
- let key = resolve6(p).replace(/[/\\]+$/, "");
15092
+ let key = resolve7(p).replace(/[/\\]+$/, "");
15075
15093
  if (normalizeCase)
15076
15094
  key = key.toLowerCase();
15077
15095
  if (seen.has(key))
@@ -15155,7 +15173,7 @@ function validateExtractedTree(stagingRoot) {
15155
15173
  const lst = lstatSync(fullPath);
15156
15174
  if (lst.isSymbolicLink()) {
15157
15175
  const linkTarget = readlinkSync(fullPath);
15158
- const resolvedTarget = resolve6(dirname5(fullPath), linkTarget);
15176
+ const resolvedTarget = resolve7(dirname5(fullPath), linkTarget);
15159
15177
  const rel2 = relative2(realRoot, resolvedTarget);
15160
15178
  if (rel2.startsWith("..") || isAbsolute4(rel2) || win32.isAbsolute(rel2)) {
15161
15179
  throw new Error(`extracted symlink ${fullPath} points outside staging root: ${linkTarget}`);
@@ -15273,7 +15291,7 @@ function copyOnnxLibraries(info, extractedDir, targetDir, realFiles, symlinks, c
15273
15291
  unlinkSync4(dst);
15274
15292
  } catch {}
15275
15293
  const dstForContainment = join10(targetRoot, link.name);
15276
- const resolvedTarget = resolve6(dirname5(dstForContainment), link.target);
15294
+ const resolvedTarget = resolve7(dirname5(dstForContainment), link.target);
15277
15295
  if (!isPathInsideRoot(targetRoot, resolvedTarget)) {
15278
15296
  const message = `ONNX Runtime symlink ${link.name} points outside install dir: ${link.target}`;
15279
15297
  if (requiredLibs.has(link.name)) {
@@ -16340,7 +16358,7 @@ class RevivableTransportPool {
16340
16358
  const delay = Math.max(0, this.revivalRetryNotBefore - Date.now());
16341
16359
  if (delay > 0) {
16342
16360
  let scheduled;
16343
- scheduled = new Promise((resolve7) => setTimeout(resolve7, delay)).then(() => {
16361
+ scheduled = new Promise((resolve8) => setTimeout(resolve8, delay)).then(() => {
16344
16362
  if (this.revival === scheduled)
16345
16363
  this.revival = null;
16346
16364
  return this.ensureActivePool();
@@ -17443,7 +17461,7 @@ import {
17443
17461
  // package.json
17444
17462
  var package_default = {
17445
17463
  name: "@cortexkit/aft-pi",
17446
- version: "0.52.1",
17464
+ version: "0.52.2",
17447
17465
  type: "module",
17448
17466
  description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
17449
17467
  main: "dist/index.js",
@@ -17466,19 +17484,19 @@ var package_default = {
17466
17484
  "test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
17467
17485
  },
17468
17486
  dependencies: {
17469
- "@cortexkit/aft-bridge": "0.52.1",
17487
+ "@cortexkit/aft-bridge": "0.52.2",
17470
17488
  "comment-json": "^5.0.0",
17471
17489
  diff: "^8.0.4",
17472
17490
  typebox: "1.1.38",
17473
17491
  zod: "^4.4.3"
17474
17492
  },
17475
17493
  optionalDependencies: {
17476
- "@cortexkit/aft-darwin-arm64": "0.52.1",
17477
- "@cortexkit/aft-darwin-x64": "0.52.1",
17478
- "@cortexkit/aft-linux-arm64": "0.52.1",
17479
- "@cortexkit/aft-linux-x64": "0.52.1",
17480
- "@cortexkit/aft-win32-arm64": "0.52.1",
17481
- "@cortexkit/aft-win32-x64": "0.52.1"
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"
17482
17500
  },
17483
17501
  devDependencies: {
17484
17502
  "@earendil-works/pi-coding-agent": "^0.80.2",
@@ -18138,6 +18156,8 @@ function registerStatusCommand(pi, ctx) {
18138
18156
 
18139
18157
  // src/config.ts
18140
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";
18141
18161
  var import_comment_json = __toESM(require_src2(), 1);
18142
18162
 
18143
18163
  // ../../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
@@ -32501,6 +32521,32 @@ var CheckerEnum = exports_external.enum([
32501
32521
  "none"
32502
32522
  ]);
32503
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
+ });
32504
32550
  var SemanticConfigSchema = exports_external.object({
32505
32551
  backend: exports_external.enum(["fastembed", "openai_compatible", "ollama"]).optional(),
32506
32552
  model: exports_external.string().trim().min(1).optional(),
@@ -32566,7 +32612,12 @@ var SubcConfigSchema = exports_external.object({
32566
32612
  connection_file: exports_external.string().optional()
32567
32613
  });
32568
32614
  var GhShimConfigSchema = exports_external.object({
32569
- 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()
32570
32621
  });
32571
32622
  var InspectConfigSchema = exports_external.object({
32572
32623
  enabled: exports_external.boolean().optional(),
@@ -32601,6 +32652,7 @@ var AftConfigSchema = exports_external.preprocess((value) => stripHarnessSpecifi
32601
32652
  disabled_tools: exports_external.array(exports_external.string()).optional(),
32602
32653
  restrict_to_project_root: exports_external.boolean().optional(),
32603
32654
  search_index: exports_external.boolean().optional(),
32655
+ index: IndexConfigSchema.optional(),
32604
32656
  semantic_search: exports_external.boolean().optional(),
32605
32657
  callgraph_store: exports_external.boolean().optional(),
32606
32658
  callgraph_chunk_size: exports_external.number().optional(),
@@ -32615,7 +32667,8 @@ var AftConfigSchema = exports_external.preprocess((value) => stripHarnessSpecifi
32615
32667
  semantic: SemanticConfigSchema.optional(),
32616
32668
  bridge: BridgeConfigSchema.optional(),
32617
32669
  subc: SubcConfigSchema.optional(),
32618
- gh_shim: GhShimConfigSchema.optional()
32670
+ gh_shim: GhShimConfigSchema.optional(),
32671
+ git: GitConfigSchema.optional()
32619
32672
  }).strict());
32620
32673
  var CONFIG_MIGRATIONS = [
32621
32674
  { oldKey: "experimental_search_index", newPath: ["search_index"] },
@@ -32962,6 +33015,7 @@ var PROJECT_SAFE_TOP_LEVEL_FIELDS = new Set([
32962
33015
  "callgraph_chunk_size",
32963
33016
  "inspect",
32964
33017
  "worktree",
33018
+ "git",
32965
33019
  "experimental",
32966
33020
  "bash"
32967
33021
  ]);
@@ -32984,6 +33038,8 @@ function getStrippedTopLevelKeys(override) {
32984
33038
  stripped.push("bridge");
32985
33039
  if (override.backup !== undefined)
32986
33040
  stripped.push("backup");
33041
+ if (override.index?.roots !== undefined)
33042
+ stripped.push("index.roots");
32987
33043
  if (override.sandbox?.enabled === false)
32988
33044
  stripped.push("sandbox.enabled");
32989
33045
  if (override.sandbox?.write_allow !== undefined)
@@ -33690,18 +33746,18 @@ function ensureInstallAnchor(cwd) {
33690
33746
  }
33691
33747
  }
33692
33748
  function runInstall(spec, version2, cwd, signal) {
33693
- return new Promise((resolve7) => {
33749
+ return new Promise((resolve8) => {
33694
33750
  const target = `${spec.npm}@${version2}`;
33695
33751
  log2(`[lsp] installing ${target} to ${cwd}`);
33696
33752
  if (signal?.aborted) {
33697
33753
  warn2(`[lsp] install ${target} aborted before spawn`);
33698
- resolve7(false);
33754
+ resolve8(false);
33699
33755
  return;
33700
33756
  }
33701
33757
  const npm = resolveNpm();
33702
33758
  if (!npm) {
33703
33759
  warn2(`[lsp] npm not found on PATH or known locations; cannot install ${target}`);
33704
- resolve7(false);
33760
+ resolve8(false);
33705
33761
  return;
33706
33762
  }
33707
33763
  ensureInstallAnchor(cwd);
@@ -33724,7 +33780,7 @@ function runInstall(spec, version2, cwd, signal) {
33724
33780
  return;
33725
33781
  settled = true;
33726
33782
  cleanup();
33727
- resolve7(ok);
33783
+ resolve8(ok);
33728
33784
  };
33729
33785
  const onAbort = () => {
33730
33786
  warn2(`[lsp] install ${target} aborted during shutdown`);
@@ -33825,7 +33881,7 @@ function cachedPackageDir(npmPackage) {
33825
33881
  return lspBinDir(npmPackage).replace(/[\\/]node_modules[\\/]\.bin[\\/]?$/, "");
33826
33882
  }
33827
33883
  function hashInstalledBinary(spec) {
33828
- return new Promise((resolve7, reject) => {
33884
+ return new Promise((resolve8, reject) => {
33829
33885
  const candidates = process.platform === "win32" ? [
33830
33886
  lspBinaryPath(spec.npm, spec.binary),
33831
33887
  lspBinaryPath(spec.npm, `${spec.binary}.cmd`),
@@ -33849,7 +33905,7 @@ function hashInstalledBinary(spec) {
33849
33905
  const stream = createReadStream(pathToHash);
33850
33906
  stream.on("error", reject);
33851
33907
  stream.on("data", (chunk) => hash2.update(chunk));
33852
- stream.on("end", () => resolve7(hash2.digest("hex")));
33908
+ stream.on("end", () => resolve8(hash2.digest("hex")));
33853
33909
  });
33854
33910
  }
33855
33911
  function installedBinaryPath(spec) {
@@ -33976,7 +34032,7 @@ import {
33976
34032
  unlinkSync as unlinkSync7,
33977
34033
  writeFileSync as writeFileSync7
33978
34034
  } from "node:fs";
33979
- 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";
33980
34036
  import { Readable as Readable3 } from "node:stream";
33981
34037
  import { pipeline as pipeline3 } from "node:stream/promises";
33982
34038
 
@@ -34133,12 +34189,12 @@ function writeGithubInstalledMetaIn(installDir, version2, binarySha256, archiveS
34133
34189
  var MAX_DOWNLOAD_BYTES3 = 256 * 1024 * 1024;
34134
34190
  var MAX_EXTRACT_BYTES2 = 1024 * 1024 * 1024;
34135
34191
  function sha256OfFile(path2) {
34136
- return new Promise((resolve8, reject) => {
34192
+ return new Promise((resolve9, reject) => {
34137
34193
  const hash2 = createHash5("sha256");
34138
34194
  const stream = createReadStream2(path2);
34139
34195
  stream.on("error", reject);
34140
34196
  stream.on("data", (chunk) => hash2.update(chunk));
34141
- stream.on("end", () => resolve8(hash2.digest("hex")));
34197
+ stream.on("end", () => resolve9(hash2.digest("hex")));
34142
34198
  });
34143
34199
  }
34144
34200
  function sha256OfFileSync2(path2) {
@@ -34395,7 +34451,7 @@ function validateExtraction(stagingRoot) {
34395
34451
  throw new Error(`failed to realpath ${full}: ${err}`);
34396
34452
  }
34397
34453
  const rel = relative3(realStagingRoot, realFull);
34398
- if (rel.startsWith("..") || resolve7(realStagingRoot, rel) !== realFull) {
34454
+ if (rel.startsWith("..") || resolve8(realStagingRoot, rel) !== realFull) {
34399
34455
  throw new Error(`archive entry escapes staging root: ${full} → ${realFull} (zip-slip defense)`);
34400
34456
  }
34401
34457
  if (lst.isDirectory()) {
@@ -34978,8 +35034,8 @@ function installProcessHandlers() {
34978
35034
  }
34979
35035
  signalShutdownStarted = true;
34980
35036
  process.exitCode = SIGNAL_EXIT_CODES[sig];
34981
- const timeout = new Promise((resolve8) => {
34982
- setTimeout(resolve8, SIGNAL_CLEANUP_TIMEOUT_MS);
35037
+ const timeout = new Promise((resolve9) => {
35038
+ setTimeout(resolve9, SIGNAL_CLEANUP_TIMEOUT_MS);
34983
35039
  });
34984
35040
  Promise.race([runCleanups(sig), timeout]).finally(() => {
34985
35041
  process.exit(SIGNAL_EXIT_CODES[sig]);
@@ -35021,12 +35077,12 @@ import { Type as Type3 } from "typebox";
35021
35077
 
35022
35078
  // src/tools/hoisted.ts
35023
35079
  import { stat as stat2 } from "node:fs/promises";
35024
- import { homedir as homedir12 } from "node:os";
35025
- 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";
35026
35082
  import {
35027
- renderDiff
35083
+ renderDiff as renderDiff2
35028
35084
  } from "@earendil-works/pi-coding-agent";
35029
- 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";
35030
35086
  import { Type as Type2 } from "typebox";
35031
35087
 
35032
35088
  // src/tools/diff-format.ts
@@ -35131,6 +35187,189 @@ function formatDiffForPi(oldContent, newContent, contextLines = DEFAULT_CONTEXT_
35131
35187
  `), firstChangedLine };
35132
35188
  }
35133
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
+
35134
35373
  // src/tools/hoisted.ts
35135
35374
  function readAttachments(response) {
35136
35375
  return Array.isArray(response.attachments) ? response.attachments : [];
@@ -35163,22 +35402,22 @@ function modelSupportsImages(extCtx) {
35163
35402
  var NON_VISION_IMAGE_NOTE = "[Current model does not support images. The image will be omitted from this request.]";
35164
35403
  function containsPath(parent, child) {
35165
35404
  const rel = relative4(parent, child);
35166
- return rel === "" || !rel.startsWith("..") && !isAbsolute6(rel);
35405
+ return rel === "" || !rel.startsWith("..") && !isAbsolute7(rel);
35167
35406
  }
35168
35407
  function normalizePathInput(path2) {
35169
35408
  const decoded = decodeFileUrl(path2);
35170
35409
  if (!decoded || !decoded.startsWith("~"))
35171
35410
  return decoded;
35172
35411
  if (decoded === "~")
35173
- return homedir12();
35412
+ return homedir14();
35174
35413
  if (decoded.startsWith(`~${sep}`) || decoded.startsWith("~/")) {
35175
- return resolve8(homedir12(), decoded.slice(2));
35414
+ return resolve9(homedir14(), decoded.slice(2));
35176
35415
  }
35177
35416
  return decoded;
35178
35417
  }
35179
35418
  function absoluteSearchPath(cwd, target) {
35180
35419
  const expanded = normalizePathInput(target);
35181
- return isAbsolute6(expanded) ? expanded : resolve8(cwd, expanded);
35420
+ return isAbsolute7(expanded) ? expanded : resolve9(cwd, expanded);
35182
35421
  }
35183
35422
  async function searchPathExists(cwd, target) {
35184
35423
  try {
@@ -35234,7 +35473,7 @@ async function assertExternalDirectoryPermission(extCtx, target, options = {}) {
35234
35473
  if (!target)
35235
35474
  return;
35236
35475
  const expanded = normalizePathInput(target);
35237
- const absoluteTarget = isAbsolute6(expanded) ? expanded : resolve8(extCtx.cwd, expanded);
35476
+ const absoluteTarget = isAbsolute7(expanded) ? expanded : resolve9(extCtx.cwd, expanded);
35238
35477
  if (containsPath(extCtx.cwd, absoluteTarget))
35239
35478
  return;
35240
35479
  if (options.restrictToProjectRoot === false)
@@ -35360,9 +35599,9 @@ function validateBatchEdits(edits) {
35360
35599
  });
35361
35600
  }
35362
35601
  function renderReadCall(args, theme, context) {
35363
- const text = reuseText(context.lastComponent);
35602
+ const text = reuseText2(context.lastComponent);
35364
35603
  const filePath = args ? readPathArg(args) : undefined;
35365
- const pathDisplay = filePath ? theme.fg("accent", shortenPath2(filePath)) : theme.fg("toolOutput", "...");
35604
+ const pathDisplay = filePath ? theme.fg("accent", shortenPath3(filePath)) : theme.fg("toolOutput", "...");
35366
35605
  text.setText(`${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`);
35367
35606
  return text;
35368
35607
  }
@@ -35464,8 +35703,8 @@ PDFs aren't supported on the Pi harness yet.`, response);
35464
35703
  renderCall(args, theme, context) {
35465
35704
  return renderMutationCall("write", mutationFilePathArg(args ?? {}), theme, context);
35466
35705
  },
35467
- renderResult(result, _options, theme, context) {
35468
- return renderMutationResult(result, theme, context);
35706
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
35707
+ return renderMutationResult(result, theme, context, options);
35469
35708
  }
35470
35709
  }));
35471
35710
  }
@@ -35547,8 +35786,8 @@ PDFs aren't supported on the Pi harness yet.`, response);
35547
35786
  renderCall(args, theme, context) {
35548
35787
  return renderMutationCall("edit", mutationFilePathArg(args ?? {}), theme, context);
35549
35788
  },
35550
- renderResult(result, _options, theme, context) {
35551
- return renderMutationResult(result, theme, context);
35789
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
35790
+ return renderMutationResult(result, theme, context, options);
35552
35791
  }
35553
35792
  }));
35554
35793
  }
@@ -35688,23 +35927,23 @@ function formatDiagnosticsText(diagnostics) {
35688
35927
  return JSON.stringify(diagnostics, null, 2);
35689
35928
  }
35690
35929
  }
35691
- function reuseText(last) {
35692
- return last instanceof Text ? last : new Text("", 0, 0);
35930
+ function reuseText2(last) {
35931
+ return last instanceof Text2 ? last : new Text2("", 0, 0);
35693
35932
  }
35694
- function reuseContainer(last) {
35695
- return last instanceof Container ? last : new Container;
35933
+ function reuseContainer2(last) {
35934
+ return last instanceof Container2 ? last : new Container2;
35696
35935
  }
35697
35936
  function renderMutationCall(toolName, filePath, theme, context) {
35698
- const text = reuseText(context.lastComponent);
35699
- 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", "...");
35700
35939
  text.setText(`${theme.fg("toolTitle", theme.bold(toolName))} ${pathDisplay}`);
35701
35940
  return text;
35702
35941
  }
35703
- function renderMutationResult(result, theme, context) {
35942
+ function renderMutationResult(result, theme, context, options = { expanded: true }) {
35704
35943
  if (context.isError) {
35705
35944
  const errorText = result.content.filter((c) => c.type === "text").map((c) => c.text ?? "").join(`
35706
35945
  `).trim();
35707
- const text = reuseText(context.lastComponent);
35946
+ const text = reuseText2(context.lastComponent);
35708
35947
  text.setText(`
35709
35948
  ${theme.fg("error", errorText || "edit failed")}`);
35710
35949
  return text;
@@ -35714,27 +35953,46 @@ ${theme.fg("error", errorText || "edit failed")}`);
35714
35953
  if (!diff) {
35715
35954
  const additions = details?.additions ?? 0;
35716
35955
  const deletions = details?.deletions ?? 0;
35717
- const text = reuseText(context.lastComponent);
35718
35956
  const countDetail = typeof details?.editsApplied === "number" && details.editsApplied > 1 ? `, ${details.editsApplied} edits` : typeof details?.replacements === "number" && details.replacements > 1 ? `, ${details.replacements} replacements` : "";
35719
- const summary = theme.fg("success", `+${additions}/-${deletions}${countDetail}`);
35957
+ const summary2 = theme.fg("success", `+${additions}/-${deletions}${countDetail}`);
35720
35958
  let suffix = "";
35721
35959
  if (details?.truncated) {
35722
35960
  suffix = ` ${theme.fg("muted", "(diff truncated)")}`;
35723
35961
  } else if (details?.noOp) {
35724
35962
  suffix = ` ${theme.fg("muted", "(no net change)")}`;
35725
35963
  }
35726
- text.setText(`
35727
- ${summary}${suffix}`);
35728
- 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
+ });
35729
35973
  }
35730
- const container = reuseContainer(context.lastComponent);
35974
+ const container = reuseContainer2(context.lastComponent);
35731
35975
  container.clear();
35732
- container.addChild(new Spacer(1));
35733
- container.addChild(new Text(renderDiff(diff), 1, 0));
35734
- 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
+ });
35735
35993
  }
35736
- function shortenPath2(path2) {
35737
- const home = homedir12();
35994
+ function shortenPath3(path2) {
35995
+ const home = homedir14();
35738
35996
  if (path2.startsWith(home))
35739
35997
  return `~${path2.slice(home.length)}`;
35740
35998
  return path2;
@@ -35750,174 +36008,6 @@ async function resolvePathArg(cwd, path2) {
35750
36008
  }
35751
36009
  }
35752
36010
 
35753
- // src/tools/render-helpers.ts
35754
- import { homedir as homedir13 } from "node:os";
35755
- import { renderDiff as renderDiff2 } from "@earendil-works/pi-coding-agent";
35756
- import { Container as Container2, Spacer as Spacer2, Text as Text2 } from "@earendil-works/pi-tui";
35757
- function reuseText2(last) {
35758
- return last instanceof Text2 ? last : new Text2("", 0, 0);
35759
- }
35760
- function reuseContainer2(last) {
35761
- return last instanceof Container2 ? last : new Container2;
35762
- }
35763
- function shortenPath3(path2) {
35764
- const home = homedir13();
35765
- if (path2.startsWith(home))
35766
- return `~${path2.slice(home.length)}`;
35767
- return path2;
35768
- }
35769
- function renderToolCall(toolName, summary, theme, context) {
35770
- const text = reuseText2(context.lastComponent);
35771
- const suffix = summary ? ` ${summary}` : "";
35772
- text.setText(`${theme.fg("toolTitle", theme.bold(toolName))}${suffix}`);
35773
- return text;
35774
- }
35775
- function accentPath(theme, path2) {
35776
- if (!path2)
35777
- return theme.fg("toolOutput", "...");
35778
- return theme.fg("accent", shortenPath3(path2));
35779
- }
35780
- function collectTextContent(result) {
35781
- return result.content.filter((part) => part.type === "text").map((part) => part.text ?? "").join(`
35782
- `).trim();
35783
- }
35784
- function extractStructuredPayload(result) {
35785
- if (result.details !== undefined)
35786
- return result.details;
35787
- const text = collectTextContent(result);
35788
- if (!text)
35789
- return;
35790
- try {
35791
- return JSON.parse(text);
35792
- } catch {
35793
- return;
35794
- }
35795
- }
35796
- function renderErrorResult(result, fallback, theme, context) {
35797
- const text = reuseText2(context.lastComponent);
35798
- const message = collectTextContent(result) || fallback;
35799
- text.setText(`
35800
- ${theme.fg("error", message)}`);
35801
- return text;
35802
- }
35803
- function renderSections(sections, context) {
35804
- const container = reuseContainer2(context.lastComponent);
35805
- container.clear();
35806
- const visibleSections = sections.filter((section) => section.trim().length > 0);
35807
- if (visibleSections.length === 0)
35808
- return container;
35809
- container.addChild(new Spacer2(1));
35810
- visibleSections.forEach((section, index) => {
35811
- if (index > 0)
35812
- container.addChild(new Spacer2(1));
35813
- container.addChild(new Text2(section, 0, 0));
35814
- });
35815
- return container;
35816
- }
35817
- function asRecord3(value) {
35818
- if (!value || typeof value !== "object" || Array.isArray(value))
35819
- return;
35820
- return value;
35821
- }
35822
- function asRecords2(value) {
35823
- return Array.isArray(value) ? value.map(asRecord3).filter(Boolean) : [];
35824
- }
35825
- function asString2(value) {
35826
- return typeof value === "string" ? value : undefined;
35827
- }
35828
- function asNumber2(value) {
35829
- return typeof value === "number" && Number.isFinite(value) ? value : undefined;
35830
- }
35831
- function formatValue(value) {
35832
- if (Array.isArray(value))
35833
- return value.map(formatValue).join(", ");
35834
- if (typeof value === "string")
35835
- return value;
35836
- if (typeof value === "number" || typeof value === "boolean")
35837
- return String(value);
35838
- if (value === null || value === undefined)
35839
- return "—";
35840
- try {
35841
- return JSON.stringify(value);
35842
- } catch {
35843
- return String(value);
35844
- }
35845
- }
35846
- function groupByFile(items, getFile) {
35847
- const groups = new Map;
35848
- items.forEach((item) => {
35849
- const file2 = getFile(item) ?? "(unknown file)";
35850
- const current = groups.get(file2) ?? [];
35851
- current.push(item);
35852
- groups.set(file2, current);
35853
- });
35854
- return groups;
35855
- }
35856
- function formatTimestamp(value) {
35857
- if (typeof value === "string" && value.length > 0)
35858
- return value;
35859
- if (typeof value !== "number" || !Number.isFinite(value))
35860
- return;
35861
- const millis = value > 1000000000000 ? value : value * 1000;
35862
- const date5 = new Date(millis);
35863
- if (Number.isNaN(date5.getTime()))
35864
- return String(value);
35865
- return date5.toISOString().replace("T", " ").replace(".000Z", "Z");
35866
- }
35867
- function formatUnifiedDiffForPi(unifiedDiff) {
35868
- if (!unifiedDiff.trim())
35869
- return "";
35870
- const entries = [];
35871
- const hunkHeader = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
35872
- let oldLine = 1;
35873
- let newLine = 1;
35874
- for (const line of unifiedDiff.split(`
35875
- `)) {
35876
- if (!line)
35877
- continue;
35878
- if (line.startsWith("--- ") || line.startsWith("+++ "))
35879
- continue;
35880
- if (line.startsWith("\"))
35881
- continue;
35882
- const headerMatch = line.match(hunkHeader);
35883
- if (headerMatch) {
35884
- oldLine = Number(headerMatch[1]);
35885
- newLine = Number(headerMatch[2]);
35886
- continue;
35887
- }
35888
- if (line.startsWith("+") && !line.startsWith("+++")) {
35889
- entries.push({ prefix: "+", line: newLine, text: line.slice(1) });
35890
- newLine += 1;
35891
- continue;
35892
- }
35893
- if (line.startsWith("-") && !line.startsWith("---")) {
35894
- entries.push({ prefix: "-", line: oldLine, text: line.slice(1) });
35895
- oldLine += 1;
35896
- continue;
35897
- }
35898
- if (line.startsWith(" ")) {
35899
- entries.push({ prefix: " ", line: oldLine, text: line.slice(1) });
35900
- oldLine += 1;
35901
- newLine += 1;
35902
- }
35903
- }
35904
- if (entries.length === 0)
35905
- return "";
35906
- const width = String(entries.reduce((max, entry) => Math.max(max, entry.line), 1)).length;
35907
- return entries.map((entry) => `${entry.prefix}${String(entry.line).padStart(width, " ")} ${entry.text}`).join(`
35908
- `);
35909
- }
35910
- function renderUnifiedDiff(unifiedDiff) {
35911
- const piDiff = formatUnifiedDiffForPi(unifiedDiff);
35912
- if (!piDiff)
35913
- return "";
35914
- try {
35915
- return renderDiff2(piDiff);
35916
- } catch {
35917
- return piDiff;
35918
- }
35919
- }
35920
-
35921
36011
  // src/tools/ast.ts
35922
36012
  var AstLang = StringEnum(["typescript", "tsx", "javascript", "python", "rust", "go", "pascal", "r", "objc"], {
35923
36013
  description: "Target language"
@@ -35995,7 +36085,7 @@ function buildAstSearchSections(payload, theme) {
35995
36085
  const grouped = groupByFile(matches, (match) => asString2(match.file));
35996
36086
  const sections = [header];
35997
36087
  for (const [file2, fileMatches] of grouped.entries()) {
35998
- const lines = [theme.fg("accent", shortenPath3(file2))];
36088
+ const lines = [theme.fg("accent", shortenPath2(file2))];
35999
36089
  fileMatches.forEach((match, index) => {
36000
36090
  const line = asNumber2(match.line) ?? 0;
36001
36091
  const column = asNumber2(match.column) ?? 0;
@@ -36042,7 +36132,7 @@ function buildAstReplaceSections(payload, theme) {
36042
36132
  return sections;
36043
36133
  }
36044
36134
  files.forEach((fileResult) => {
36045
- const file2 = shortenPath3(asString2(fileResult.file) ?? "(unknown file)");
36135
+ const file2 = shortenPath2(asString2(fileResult.file) ?? "(unknown file)");
36046
36136
  const replacements = asNumber2(fileResult.replacements) ?? 0;
36047
36137
  const error53 = asString2(fileResult.error);
36048
36138
  const diff = asString2(fileResult.diff);
@@ -36068,17 +36158,27 @@ function renderAstCall(toolName, args, theme, context) {
36068
36158
  const summary = toolName === "ast_grep_replace" ? `${lang} ${theme.fg("toolOutput", `${args.pattern} → ${args.rewrite}`)}` : `${lang} ${theme.fg("toolOutput", args.pattern)}`;
36069
36159
  return renderToolCall(toolName === "ast_grep_replace" ? "ast replace" : "ast search", summary, theme, context);
36070
36160
  }
36071
- function renderAstResult(toolName, result, theme, context) {
36161
+ function renderAstResult(toolName, result, theme, context, options = { expanded: true }) {
36072
36162
  if (context.isError) {
36073
36163
  return renderErrorResult(result, `${toolName} failed`, theme, context);
36074
36164
  }
36075
36165
  const payload = extractStructuredPayload(result);
36076
36166
  if (!payload) {
36077
- const text = collectTextContent(result);
36078
- 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
+ });
36079
36174
  }
36080
36175
  const sections = toolName === "ast_grep_replace" ? buildAstReplaceSections(payload, theme) : buildAstSearchSections(payload, theme);
36081
- 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
+ });
36082
36182
  }
36083
36183
  function registerAstTools(pi, ctx, surface) {
36084
36184
  if (surface.astSearch) {
@@ -36110,8 +36210,8 @@ function registerAstTools(pi, ctx, surface) {
36110
36210
  renderCall(args, theme, context) {
36111
36211
  return renderAstCall("ast_grep_search", args, theme, context);
36112
36212
  },
36113
- renderResult(result, _options, theme, context) {
36114
- 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);
36115
36215
  }
36116
36216
  });
36117
36217
  }
@@ -36144,8 +36244,8 @@ function registerAstTools(pi, ctx, surface) {
36144
36244
  renderCall(args, theme, context) {
36145
36245
  return renderAstCall("ast_grep_replace", args, theme, context);
36146
36246
  },
36147
- renderResult(result, _options, theme, context) {
36148
- 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);
36149
36249
  }
36150
36250
  });
36151
36251
  }
@@ -36470,8 +36570,8 @@ DO NOT use bash for code search or code exploration. If you are about to run gre
36470
36570
  renderCall(args, theme, context) {
36471
36571
  return renderBashCall(args?.command, args?.description, theme, context);
36472
36572
  },
36473
- renderResult(result, _options, theme, context) {
36474
- return renderBashResult(result, theme, context);
36573
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
36574
+ return renderBashResult(result, theme, context, options);
36475
36575
  }
36476
36576
  });
36477
36577
  if (bashCfg.background) {
@@ -36908,7 +37008,7 @@ function renderBashCall(command, description, theme, context) {
36908
37008
  text.setText(`${theme.fg("toolTitle", theme.bold("bash"))} ${theme.fg("accent", display)}`);
36909
37009
  return text;
36910
37010
  }
36911
- function renderBashResult(result, theme, context) {
37011
+ function renderBashResult(result, theme, context, options = { expanded: true }) {
36912
37012
  if (context.isError) {
36913
37013
  const errorText = result.content.filter((c) => c.type === "text").map((c) => c.text ?? "").join(`
36914
37014
  `).trim();
@@ -36955,7 +37055,14 @@ ${theme.fg("error", errorText || "bash failed")}`);
36955
37055
  const truncText = theme.fg("warning", "(output truncated)");
36956
37056
  container.addChild(new Text3(truncText, 1, 0));
36957
37057
  }
36958
- 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
+ });
36959
37066
  }
36960
37067
  function shortenCommand(command) {
36961
37068
  if (command.length <= 60)
@@ -36987,14 +37094,19 @@ function buildConflictSections(text) {
36987
37094
  sections.push(...rest.map((section) => section.trim()).filter(Boolean));
36988
37095
  return sections;
36989
37096
  }
36990
- function renderConflictResult(text, theme, context) {
37097
+ function renderConflictResult(text, theme, context, options = { expanded: true }) {
36991
37098
  const sections = buildConflictSections(text).map((section, index) => index === 0 ? theme.fg("warning", section) : section);
36992
- 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
+ });
36993
37105
  }
36994
- function renderConflictToolResult(result, theme, context) {
37106
+ function renderConflictToolResult(result, theme, context, options = { expanded: true }) {
36995
37107
  if (context.isError)
36996
37108
  return renderErrorResult(result, "conflicts failed", theme, context);
36997
- return renderConflictResult(collectTextContent(result), theme, context);
37109
+ return renderConflictResult(collectTextContent(result), theme, context, options);
36998
37110
  }
36999
37111
  function registerConflictsTool(pi, ctx) {
37000
37112
  pi.registerTool(withPathAliasPreparation({
@@ -37021,8 +37133,8 @@ function registerConflictsTool(pi, ctx) {
37021
37133
  renderCall(_args, theme, context) {
37022
37134
  return renderConflictCall(theme, context);
37023
37135
  },
37024
- renderResult(result, _options, theme, context) {
37025
- return renderConflictToolResult(result, theme, context);
37136
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
37137
+ return renderConflictToolResult(result, theme, context, options);
37026
37138
  }
37027
37139
  }));
37028
37140
  }
@@ -37065,7 +37177,7 @@ function renderFsCall(toolName, args, theme, context) {
37065
37177
  const moveArgs = args;
37066
37178
  return renderToolCall("move", `${accentPath(theme, moveArgs.path)} ${theme.fg("muted", "→")} ${accentPath(theme, moveArgs.destination)}`, theme, context);
37067
37179
  }
37068
- function renderFsResult(toolName, args, result, theme, context) {
37180
+ function renderFsResult(toolName, args, result, theme, context, options = { expanded: true }) {
37069
37181
  if (context.isError) {
37070
37182
  return renderErrorResult(result, `${toolName} failed`, theme, context);
37071
37183
  }
@@ -37076,22 +37188,33 @@ function renderFsResult(toolName, args, result, theme, context) {
37076
37188
  const skipped = data.skipped_files ?? [];
37077
37189
  const lines = [];
37078
37190
  for (const entry of deletedPaths) {
37079
- lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent", shortenPath3(entry))}`);
37191
+ lines.push(`${theme.fg("success", "✓ deleted")} ${theme.fg("accent", shortenPath2(entry))}`);
37080
37192
  }
37081
37193
  for (const entry of skipped) {
37082
- 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})`)}`);
37083
37195
  }
37084
37196
  if (lines.length === 0) {
37085
37197
  lines.push(theme.fg("muted", "(no files deleted)"));
37086
37198
  }
37087
- return renderSections([lines.join(`
37088
- `)], 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
+ });
37089
37206
  }
37090
37207
  const moveArgs = args;
37091
- return renderSections([
37092
- `${theme.fg("success", "✓ moved")} ${theme.fg("accent", shortenPath3(moveArgs.path ?? ""))}`,
37093
- `${theme.fg("muted", "to")} ${theme.fg("accent", shortenPath3(moveArgs.destination))}`
37094
- ], 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
+ });
37095
37218
  }
37096
37219
  function registerFsTools(pi, ctx, surface) {
37097
37220
  const backupsDisabled = ctx.config.backup?.enabled === false;
@@ -37137,8 +37260,8 @@ function registerFsTools(pi, ctx, surface) {
37137
37260
  renderCall(args, theme, context) {
37138
37261
  return renderFsCall("aft_delete", args, theme, context);
37139
37262
  },
37140
- renderResult(result, _options, theme, context) {
37141
- 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);
37142
37265
  }
37143
37266
  }));
37144
37267
  }
@@ -37170,8 +37293,8 @@ function registerFsTools(pi, ctx, surface) {
37170
37293
  renderCall(args, theme, context) {
37171
37294
  return renderFsCall("aft_move", args, theme, context);
37172
37295
  },
37173
- renderResult(result, _options, theme, context) {
37174
- 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);
37175
37298
  }
37176
37299
  }));
37177
37300
  }
@@ -37245,11 +37368,17 @@ function renderImportCall(args, theme, context) {
37245
37368
  ].filter(Boolean).join(" ");
37246
37369
  return renderToolCall("import", summary, theme, context);
37247
37370
  }
37248
- function renderImportResult(result, args, theme, context) {
37371
+ function renderImportResult(result, args, theme, context, options = { expanded: true }) {
37249
37372
  if (context.isError)
37250
37373
  return renderErrorResult(result, "import failed", theme, context);
37251
37374
  const payload = extractStructuredPayload(result);
37252
- 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
+ });
37253
37382
  }
37254
37383
  function registerImportTools(pi, ctx) {
37255
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.";
@@ -37297,8 +37426,8 @@ function registerImportTools(pi, ctx) {
37297
37426
  renderCall(args, theme, context) {
37298
37427
  return renderImportCall(args, theme, context);
37299
37428
  },
37300
- renderResult(result, _options, theme, context) {
37301
- 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);
37302
37431
  }
37303
37432
  }));
37304
37433
  }
@@ -37582,14 +37711,18 @@ function renderInspectCall(args, theme, context) {
37582
37711
  const summary = [sections, scope, args.topK ? `topK=${args.topK}` : undefined].filter(Boolean).join(" ");
37583
37712
  return renderToolCall("inspect", summary ? theme.fg("toolOutput", summary) : undefined, theme, context);
37584
37713
  }
37585
- function renderInspectResult(result, theme, context) {
37714
+ function renderInspectResult(result, theme, context, options = { expanded: true }) {
37586
37715
  const payload = extractStructuredPayload(result);
37587
37716
  const terminal = parseInspectTerminal(payload);
37588
- if (terminal)
37589
- return renderSections([renderInspectTerminal(terminal, asString2(asRecord3(payload)?.text))], context);
37590
37717
  if (context.isError)
37591
37718
  return renderErrorResult(result, "inspect failed", theme, context);
37592
- 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
+ });
37593
37726
  }
37594
37727
  function registerInspectTool(pi, ctx) {
37595
37728
  pi.registerTool({
@@ -37624,8 +37757,8 @@ function registerInspectTool(pi, ctx) {
37624
37757
  renderCall(args, theme, context) {
37625
37758
  return renderInspectCall(args, theme, context);
37626
37759
  },
37627
- renderResult(result, _options, theme, context) {
37628
- return renderInspectResult(result, theme, context);
37760
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
37761
+ return renderInspectResult(result, theme, context, options);
37629
37762
  }
37630
37763
  });
37631
37764
  }
@@ -37676,10 +37809,16 @@ function renderNavigateCall(args, theme, context) {
37676
37809
  ].filter(Boolean).join(" ");
37677
37810
  return renderToolCall("callgraph", summary, theme, context);
37678
37811
  }
37679
- function renderNavigateResult(result, args, theme, context) {
37812
+ function renderNavigateResult(result, args, theme, context, options = { expanded: true }) {
37680
37813
  if (context.isError)
37681
37814
  return renderErrorResult(result, "navigate failed", theme, context);
37682
- 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
+ });
37683
37822
  }
37684
37823
  function registerNavigateTool(pi, ctx) {
37685
37824
  pi.registerTool(withPathAliasPreparation({
@@ -37747,8 +37886,8 @@ function registerNavigateTool(pi, ctx) {
37747
37886
  renderCall(args, theme, context) {
37748
37887
  return renderNavigateCall(args, theme, context);
37749
37888
  },
37750
- renderResult(result, _options, theme, context) {
37751
- 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);
37752
37891
  }
37753
37892
  }));
37754
37893
  }
@@ -37825,7 +37964,7 @@ function renderZoomRecord(record2, targetLabel, theme) {
37825
37964
  const range = asRecord3(record2.range);
37826
37965
  const startLine = range && typeof range.start_line === "number" ? range.start_line : undefined;
37827
37966
  const endLine = range && typeof range.end_line === "number" ? range.end_line : undefined;
37828
- 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);
37829
37968
  const lines = [`${theme.fg("accent", name)} ${theme.fg("muted", `[${kind}] ${location}`)}`];
37830
37969
  const content = asString2(record2.content);
37831
37970
  if (content) {
@@ -37862,7 +38001,7 @@ function buildZoomSections(args, payload, theme) {
37862
38001
  const name = asString2(record2.name) ?? asString2(response.name) ?? "(unknown symbol)";
37863
38002
  const itemTargetLabel = asString2(record2.targetLabel) ?? zoomTargetLabel(args);
37864
38003
  if (response.success === false || record2.success === false) {
37865
- const location = record2.targetLabel ? ` in ${shortenPath3(itemTargetLabel)}` : "";
38004
+ const location = record2.targetLabel ? ` in ${shortenPath2(itemTargetLabel)}` : "";
37866
38005
  return theme.fg("error", `Symbol "${name}" not found${location}: ${asString2(response.message) ?? asString2(record2.error) ?? "zoom failed"}`);
37867
38006
  }
37868
38007
  if (record2.response) {
@@ -37871,7 +38010,7 @@ function buildZoomSections(args, payload, theme) {
37871
38010
  const content = asString2(record2.content);
37872
38011
  if (content) {
37873
38012
  return [
37874
- `${theme.fg("accent", name)} ${theme.fg("muted", shortenPath3(itemTargetLabel))}`,
38013
+ `${theme.fg("accent", name)} ${theme.fg("muted", shortenPath2(itemTargetLabel))}`,
37875
38014
  content
37876
38015
  ].filter(Boolean).join(`
37877
38016
  `);
@@ -37894,10 +38033,16 @@ function renderOutlineCall(args, theme, context) {
37894
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;
37895
38034
  return renderToolCall("outline", summary, theme, context);
37896
38035
  }
37897
- function renderOutlineResult(result, theme, context) {
38036
+ function renderOutlineResult(result, theme, context, options = { expanded: true }) {
37898
38037
  if (context.isError)
37899
38038
  return renderErrorResult(result, "outline failed", theme, context);
37900
- 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
+ });
37901
38046
  }
37902
38047
  function renderZoomCall(args, theme, context) {
37903
38048
  const symbols = args.symbols;
@@ -37916,10 +38061,16 @@ function renderZoomCall(args, theme, context) {
37916
38061
  }
37917
38062
  return renderToolCall("zoom", `${accentPath(theme, zoomTargetLabel(args))} ${summary}`, theme, context);
37918
38063
  }
37919
- function renderZoomResult(result, args, theme, context) {
38064
+ function renderZoomResult(result, args, theme, context, options = { expanded: true }) {
37920
38065
  if (context.isError)
37921
38066
  return renderErrorResult(result, "zoom failed", theme, context);
37922
- 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
+ });
37923
38074
  }
37924
38075
  function registerReadingTools(pi, ctx, surface) {
37925
38076
  if (surface.outline) {
@@ -37981,8 +38132,8 @@ Pass a single \`target\`:
37981
38132
  renderCall(args, theme, context) {
37982
38133
  return renderOutlineCall(args, theme, context);
37983
38134
  },
37984
- renderResult(result, _options, theme, context) {
37985
- return renderOutlineResult(result, theme, context);
38135
+ renderResult(result, options = { expanded: false, isPartial: false }, theme, context) {
38136
+ return renderOutlineResult(result, theme, context, options);
37986
38137
  }
37987
38138
  }));
37988
38139
  }
@@ -38073,8 +38224,8 @@ Pass a single \`target\`:
38073
38224
  renderCall(args, theme, context) {
38074
38225
  return renderZoomCall(args, theme, context);
38075
38226
  },
38076
- renderResult(result, _options, theme, context) {
38077
- 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);
38078
38229
  }
38079
38230
  }));
38080
38231
  }
@@ -38106,21 +38257,21 @@ function buildRefactorSections(args, payload, theme) {
38106
38257
  `${theme.fg("success", "moved symbol")} ${theme.fg("toolOutput", args.symbol ?? "(symbol)")}`,
38107
38258
  `${theme.fg("muted", "files modified")} ${asNumber2(response.files_modified) ?? results.length}`,
38108
38259
  `${theme.fg("muted", "consumers updated")} ${asNumber2(response.consumers_updated) ?? 0}`,
38109
- 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(`
38110
38261
  `) : theme.fg("muted", "No files reported.")
38111
38262
  ];
38112
38263
  }
38113
38264
  if (args.op === "extract") {
38114
38265
  return [
38115
38266
  `${theme.fg("success", "extracted")} ${theme.fg("toolOutput", asString2(response.name) ?? args.name ?? "(function)")}`,
38116
- `${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 ?? ""))}`,
38117
38268
  `${theme.fg("muted", "params")} ${Array.isArray(response.parameters) ? response.parameters.join(", ") || "none" : "none"}`,
38118
38269
  `${theme.fg("muted", "return type")} ${asString2(response.return_type) ?? "unknown"}`
38119
38270
  ];
38120
38271
  }
38121
38272
  return [
38122
38273
  `${theme.fg("success", "inlined")} ${theme.fg("toolOutput", asString2(response.symbol) ?? args.symbol ?? "(symbol)")}`,
38123
- `${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 ?? ""))}`,
38124
38275
  `${theme.fg("muted", "context")} ${asString2(response.call_context) ?? "unknown"}`,
38125
38276
  `${theme.fg("muted", "substitutions")} ${asNumber2(response.substitutions) ?? 0}`
38126
38277
  ];
@@ -38133,10 +38284,16 @@ function renderRefactorCall(args, theme, context) {
38133
38284
  ].filter(Boolean).join(" ");
38134
38285
  return renderToolCall("refactor", summary, theme, context);
38135
38286
  }
38136
- function renderRefactorResult(result, args, theme, context) {
38287
+ function renderRefactorResult(result, args, theme, context, options = { expanded: true }) {
38137
38288
  if (context.isError)
38138
38289
  return renderErrorResult(result, "refactor failed", theme, context);
38139
- 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
+ });
38140
38297
  }
38141
38298
  function registerRefactorTool(pi, ctx) {
38142
38299
  pi.registerTool(withPathAliasPreparation({
@@ -38203,8 +38360,8 @@ function registerRefactorTool(pi, ctx) {
38203
38360
  renderCall(args, theme, context) {
38204
38361
  return renderRefactorCall(args, theme, context);
38205
38362
  },
38206
- renderResult(result, _options, theme, context) {
38207
- 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);
38208
38365
  }
38209
38366
  }));
38210
38367
  }
@@ -38239,14 +38396,14 @@ function buildSafetySections(args, payload, theme) {
38239
38396
  ];
38240
38397
  }
38241
38398
  return [
38242
- `${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)"))}`,
38243
38400
  `${theme.fg("muted", "backup")} ${asString2(response.backup_id) ?? "—"}`
38244
38401
  ];
38245
38402
  }
38246
38403
  if (args.op === "history") {
38247
38404
  const entries = asRecords2(response.entries);
38248
38405
  const sections2 = [
38249
- theme.fg("accent", shortenPath3(asString2(response.file) ?? args.path ?? "(file)"))
38406
+ theme.fg("accent", shortenPath2(asString2(response.file) ?? args.path ?? "(file)"))
38250
38407
  ];
38251
38408
  if (entries.length === 0) {
38252
38409
  sections2.push(theme.fg("muted", "No history entries."));
@@ -38268,7 +38425,7 @@ function buildSafetySections(args, payload, theme) {
38268
38425
  `${theme.fg("success", "checkpoint created")} ${theme.fg("accent", asString2(response.name) ?? args.name ?? "(checkpoint)")}`,
38269
38426
  `${theme.fg("muted", "files")} ${asNumber2(response.file_count) ?? 0}`,
38270
38427
  skipped.length > 0 ? `${theme.fg("warning", "skipped")}
38271
- ${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(`
38272
38429
  `)}` : theme.fg("muted", "No skipped files.")
38273
38430
  ];
38274
38431
  }
@@ -38300,10 +38457,16 @@ function renderSafetyCall(args, theme, context) {
38300
38457
  const summary = [theme.fg("accent", args.op), target ? accentPath(theme, target) : undefined].filter(Boolean).join(" ");
38301
38458
  return renderToolCall("safety", summary, theme, context);
38302
38459
  }
38303
- function renderSafetyResult(result, args, theme, context) {
38460
+ function renderSafetyResult(result, args, theme, context, options = { expanded: true }) {
38304
38461
  if (context.isError)
38305
38462
  return renderErrorResult(result, "safety failed", theme, context);
38306
- 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
+ });
38307
38470
  }
38308
38471
  function registerSafetyTool(pi, ctx) {
38309
38472
  pi.registerTool(withPathAliasPreparation({
@@ -38372,8 +38535,8 @@ function registerSafetyTool(pi, ctx) {
38372
38535
  renderCall(args, theme, context) {
38373
38536
  return renderSafetyCall(args, theme, context);
38374
38537
  },
38375
- renderResult(result, _options, theme, context) {
38376
- 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);
38377
38540
  }
38378
38541
  }));
38379
38542
  }
@@ -38440,7 +38603,7 @@ function buildSemanticSections(args, payload, theme) {
38440
38603
  }
38441
38604
  const grouped = groupByFile(results, (result) => asString2(result.file));
38442
38605
  for (const [file2, fileResults] of grouped.entries()) {
38443
- const lines = [theme.fg("accent", shortenPath3(file2))];
38606
+ const lines = [theme.fg("accent", shortenPath2(file2))];
38444
38607
  fileResults.forEach((result) => {
38445
38608
  if (asString2(result.kind) === "GrepLine") {
38446
38609
  const line = asNumber2(result.line);
@@ -38487,10 +38650,16 @@ function buildSemanticSections(args, payload, theme) {
38487
38650
  function renderSemanticCall(args, theme, context) {
38488
38651
  return renderToolCall("search", theme.fg("toolOutput", args.query), theme, context);
38489
38652
  }
38490
- function renderSemanticResult(result, args, theme, context) {
38653
+ function renderSemanticResult(result, args, theme, context, options = { expanded: true }) {
38491
38654
  if (context.isError)
38492
38655
  return renderErrorResult(result, "search failed", theme, context);
38493
- 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
+ });
38494
38663
  }
38495
38664
  function registerSemanticTool(pi, ctx) {
38496
38665
  pi.registerTool(withPathAliasPreparation({
@@ -38522,8 +38691,8 @@ function registerSemanticTool(pi, ctx) {
38522
38691
  renderCall(args, theme, context) {
38523
38692
  return renderSemanticCall(args, theme, context);
38524
38693
  },
38525
- renderResult(result, _options, theme, context) {
38526
- 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);
38527
38696
  }
38528
38697
  }));
38529
38698
  }
@@ -38778,7 +38947,7 @@ var PLUGIN_VERSION = (() => {
38778
38947
  return "0.0.0";
38779
38948
  }
38780
38949
  })();
38781
- var ANNOUNCEMENT_VERSION = "0.52.1";
38950
+ var ANNOUNCEMENT_VERSION = "0.52.2";
38782
38951
  var ANNOUNCEMENT_FEATURES = [
38783
38952
  "Callgraph stays consistent under load: edits arriving mid-rebuild can no longer be silently lost from navigation results.",
38784
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).",
@@ -39129,7 +39298,7 @@ ${lines}
39129
39298
  if (onnxRuntimePromise) {
39130
39299
  await Promise.race([
39131
39300
  onnxRuntimePromise,
39132
- new Promise((resolve9) => setTimeout(() => resolve9(null), 60000))
39301
+ new Promise((resolve10) => setTimeout(() => resolve10(null), 60000))
39133
39302
  ]);
39134
39303
  }
39135
39304
  const bridge = pool.getBridge(cwd);