@buildautomaton/cli 0.1.46 → 0.1.48

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
@@ -4175,8 +4175,8 @@ var init_parseUtil = __esm({
4175
4175
  init_errors();
4176
4176
  init_en();
4177
4177
  makeIssue = (params) => {
4178
- const { data, path: path44, errorMaps, issueData } = params;
4179
- const fullPath = [...path44, ...issueData.path || []];
4178
+ const { data, path: path46, errorMaps, issueData } = params;
4179
+ const fullPath = [...path46, ...issueData.path || []];
4180
4180
  const fullIssue = {
4181
4181
  ...issueData,
4182
4182
  path: fullPath
@@ -4484,11 +4484,11 @@ var init_types = __esm({
4484
4484
  init_parseUtil();
4485
4485
  init_util();
4486
4486
  ParseInputLazyPath = class {
4487
- constructor(parent, value, path44, key) {
4487
+ constructor(parent, value, path46, key) {
4488
4488
  this._cachedPath = [];
4489
4489
  this.parent = parent;
4490
4490
  this.data = value;
4491
- this._path = path44;
4491
+ this._path = path46;
4492
4492
  this._key = key;
4493
4493
  }
4494
4494
  get path() {
@@ -8103,15 +8103,15 @@ function assignProp(target, prop, value) {
8103
8103
  configurable: true
8104
8104
  });
8105
8105
  }
8106
- function getElementAtPath(obj, path44) {
8107
- if (!path44)
8106
+ function getElementAtPath(obj, path46) {
8107
+ if (!path46)
8108
8108
  return obj;
8109
- return path44.reduce((acc, key) => acc?.[key], obj);
8109
+ return path46.reduce((acc, key) => acc?.[key], obj);
8110
8110
  }
8111
8111
  function promiseAllObject(promisesObj) {
8112
8112
  const keys = Object.keys(promisesObj);
8113
- const promises5 = keys.map((key) => promisesObj[key]);
8114
- return Promise.all(promises5).then((results) => {
8113
+ const promises6 = keys.map((key) => promisesObj[key]);
8114
+ return Promise.all(promises6).then((results) => {
8115
8115
  const resolvedObj = {};
8116
8116
  for (let i = 0; i < keys.length; i++) {
8117
8117
  resolvedObj[keys[i]] = results[i];
@@ -8355,11 +8355,11 @@ function aborted(x, startIndex = 0) {
8355
8355
  }
8356
8356
  return false;
8357
8357
  }
8358
- function prefixIssues(path44, issues) {
8358
+ function prefixIssues(path46, issues) {
8359
8359
  return issues.map((iss) => {
8360
8360
  var _a2;
8361
8361
  (_a2 = iss).path ?? (_a2.path = []);
8362
- iss.path.unshift(path44);
8362
+ iss.path.unshift(path46);
8363
8363
  return iss;
8364
8364
  });
8365
8365
  }
@@ -8548,7 +8548,7 @@ function treeifyError(error40, _mapper) {
8548
8548
  return issue2.message;
8549
8549
  };
8550
8550
  const result = { errors: [] };
8551
- const processError = (error41, path44 = []) => {
8551
+ const processError = (error41, path46 = []) => {
8552
8552
  var _a2, _b;
8553
8553
  for (const issue2 of error41.issues) {
8554
8554
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -8558,7 +8558,7 @@ function treeifyError(error40, _mapper) {
8558
8558
  } else if (issue2.code === "invalid_element") {
8559
8559
  processError({ issues: issue2.issues }, issue2.path);
8560
8560
  } else {
8561
- const fullpath = [...path44, ...issue2.path];
8561
+ const fullpath = [...path46, ...issue2.path];
8562
8562
  if (fullpath.length === 0) {
8563
8563
  result.errors.push(mapper(issue2));
8564
8564
  continue;
@@ -8588,9 +8588,9 @@ function treeifyError(error40, _mapper) {
8588
8588
  processError(error40);
8589
8589
  return result;
8590
8590
  }
8591
- function toDotPath(path44) {
8591
+ function toDotPath(path46) {
8592
8592
  const segs = [];
8593
- for (const seg of path44) {
8593
+ for (const seg of path46) {
8594
8594
  if (typeof seg === "number")
8595
8595
  segs.push(`[${seg}]`);
8596
8596
  else if (typeof seg === "symbol")
@@ -22073,10 +22073,10 @@ var require_src2 = __commonJS({
22073
22073
  var fs_1 = __require("fs");
22074
22074
  var debug_1 = __importDefault(require_src());
22075
22075
  var log2 = debug_1.default("@kwsites/file-exists");
22076
- function check2(path44, isFile, isDirectory) {
22077
- log2(`checking %s`, path44);
22076
+ function check2(path46, isFile, isDirectory) {
22077
+ log2(`checking %s`, path46);
22078
22078
  try {
22079
- const stat2 = fs_1.statSync(path44);
22079
+ const stat2 = fs_1.statSync(path46);
22080
22080
  if (stat2.isFile() && isFile) {
22081
22081
  log2(`[OK] path represents a file`);
22082
22082
  return true;
@@ -22096,8 +22096,8 @@ var require_src2 = __commonJS({
22096
22096
  throw e;
22097
22097
  }
22098
22098
  }
22099
- function exists2(path44, type = exports.READABLE) {
22100
- return check2(path44, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
22099
+ function exists2(path46, type = exports.READABLE) {
22100
+ return check2(path46, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
22101
22101
  }
22102
22102
  exports.exists = exists2;
22103
22103
  exports.FILE = 1;
@@ -22940,9 +22940,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
22940
22940
  const rawPath = typeof o.path === "string" ? o.path.trim() : "";
22941
22941
  const summary = typeof o.summary === "string" ? o.summary.trim() : "";
22942
22942
  if (!rawPath || !summary) continue;
22943
- const path44 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
22944
- if (!path44) continue;
22945
- rows.push({ path: path44, summary: clampSummaryToAtMostTwoLines(summary) });
22943
+ const path46 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
22944
+ if (!path46) continue;
22945
+ rows.push({ path: path46, summary: clampSummaryToAtMostTwoLines(summary) });
22946
22946
  }
22947
22947
  return rows;
22948
22948
  }
@@ -24241,8 +24241,8 @@ function randomSecret() {
24241
24241
  }
24242
24242
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
24243
24243
  }
24244
- async function requestPreviewApi(port, secret, method, path44, body) {
24245
- const url2 = `http://127.0.0.1:${port}${path44}`;
24244
+ async function requestPreviewApi(port, secret, method, path46, body) {
24245
+ const url2 = `http://127.0.0.1:${port}${path46}`;
24246
24246
  const headers = {
24247
24247
  [PREVIEW_SECRET_HEADER]: secret,
24248
24248
  "Content-Type": "application/json"
@@ -24254,7 +24254,7 @@ async function requestPreviewApi(port, secret, method, path44, body) {
24254
24254
  });
24255
24255
  const data = await res.json().catch(() => ({}));
24256
24256
  if (!res.ok) {
24257
- throw new Error(data?.error ?? `Preview API ${method} ${path44}: ${res.status}`);
24257
+ throw new Error(data?.error ?? `Preview API ${method} ${path46}: ${res.status}`);
24258
24258
  }
24259
24259
  return data;
24260
24260
  }
@@ -24469,7 +24469,7 @@ function installBridgeProcessResilience() {
24469
24469
  }
24470
24470
 
24471
24471
  // src/cli-version.ts
24472
- var CLI_VERSION = "0.1.46".length > 0 ? "0.1.46" : "0.0.0-dev";
24472
+ var CLI_VERSION = "0.1.48".length > 0 ? "0.1.48" : "0.0.0-dev";
24473
24473
 
24474
24474
  // src/connection/heartbeat/constants.ts
24475
24475
  var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
@@ -27995,8 +27995,8 @@ function pathspec(...paths) {
27995
27995
  cache.set(key, paths);
27996
27996
  return key;
27997
27997
  }
27998
- function isPathSpec(path44) {
27999
- return path44 instanceof String && cache.has(path44);
27998
+ function isPathSpec(path46) {
27999
+ return path46 instanceof String && cache.has(path46);
28000
28000
  }
28001
28001
  function toPaths(pathSpec) {
28002
28002
  return cache.get(pathSpec) || [];
@@ -28085,8 +28085,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
28085
28085
  function forEachLineWithContent(input, callback) {
28086
28086
  return toLinesWithContent(input, true).map((line) => callback(line));
28087
28087
  }
28088
- function folderExists(path44) {
28089
- return (0, import_file_exists.exists)(path44, import_file_exists.FOLDER);
28088
+ function folderExists(path46) {
28089
+ return (0, import_file_exists.exists)(path46, import_file_exists.FOLDER);
28090
28090
  }
28091
28091
  function append(target, item) {
28092
28092
  if (Array.isArray(target)) {
@@ -28490,8 +28490,8 @@ function checkIsRepoRootTask() {
28490
28490
  commands,
28491
28491
  format: "utf-8",
28492
28492
  onError,
28493
- parser(path44) {
28494
- return /^\.(git)?$/.test(path44.trim());
28493
+ parser(path46) {
28494
+ return /^\.(git)?$/.test(path46.trim());
28495
28495
  }
28496
28496
  };
28497
28497
  }
@@ -28925,11 +28925,11 @@ function parseGrep(grep) {
28925
28925
  const paths = /* @__PURE__ */ new Set();
28926
28926
  const results = {};
28927
28927
  forEachLineWithContent(grep, (input) => {
28928
- const [path44, line, preview] = input.split(NULL);
28929
- paths.add(path44);
28930
- (results[path44] = results[path44] || []).push({
28928
+ const [path46, line, preview] = input.split(NULL);
28929
+ paths.add(path46);
28930
+ (results[path46] = results[path46] || []).push({
28931
28931
  line: asNumber(line),
28932
- path: path44,
28932
+ path: path46,
28933
28933
  preview
28934
28934
  });
28935
28935
  });
@@ -29694,14 +29694,14 @@ var init_hash_object = __esm2({
29694
29694
  init_task();
29695
29695
  }
29696
29696
  });
29697
- function parseInit(bare, path44, text) {
29697
+ function parseInit(bare, path46, text) {
29698
29698
  const response = String(text).trim();
29699
29699
  let result;
29700
29700
  if (result = initResponseRegex.exec(response)) {
29701
- return new InitSummary(bare, path44, false, result[1]);
29701
+ return new InitSummary(bare, path46, false, result[1]);
29702
29702
  }
29703
29703
  if (result = reInitResponseRegex.exec(response)) {
29704
- return new InitSummary(bare, path44, true, result[1]);
29704
+ return new InitSummary(bare, path46, true, result[1]);
29705
29705
  }
29706
29706
  let gitDir = "";
29707
29707
  const tokens = response.split(" ");
@@ -29712,7 +29712,7 @@ function parseInit(bare, path44, text) {
29712
29712
  break;
29713
29713
  }
29714
29714
  }
29715
- return new InitSummary(bare, path44, /^re/i.test(response), gitDir);
29715
+ return new InitSummary(bare, path46, /^re/i.test(response), gitDir);
29716
29716
  }
29717
29717
  var InitSummary;
29718
29718
  var initResponseRegex;
@@ -29721,9 +29721,9 @@ var init_InitSummary = __esm2({
29721
29721
  "src/lib/responses/InitSummary.ts"() {
29722
29722
  "use strict";
29723
29723
  InitSummary = class {
29724
- constructor(bare, path44, existing, gitDir) {
29724
+ constructor(bare, path46, existing, gitDir) {
29725
29725
  this.bare = bare;
29726
- this.path = path44;
29726
+ this.path = path46;
29727
29727
  this.existing = existing;
29728
29728
  this.gitDir = gitDir;
29729
29729
  }
@@ -29735,7 +29735,7 @@ var init_InitSummary = __esm2({
29735
29735
  function hasBareCommand(command) {
29736
29736
  return command.includes(bareCommand);
29737
29737
  }
29738
- function initTask(bare = false, path44, customArgs) {
29738
+ function initTask(bare = false, path46, customArgs) {
29739
29739
  const commands = ["init", ...customArgs];
29740
29740
  if (bare && !hasBareCommand(commands)) {
29741
29741
  commands.splice(1, 0, bareCommand);
@@ -29744,7 +29744,7 @@ function initTask(bare = false, path44, customArgs) {
29744
29744
  commands,
29745
29745
  format: "utf-8",
29746
29746
  parser(text) {
29747
- return parseInit(commands.includes("--bare"), path44, text);
29747
+ return parseInit(commands.includes("--bare"), path46, text);
29748
29748
  }
29749
29749
  };
29750
29750
  }
@@ -30560,12 +30560,12 @@ var init_FileStatusSummary = __esm2({
30560
30560
  "use strict";
30561
30561
  fromPathRegex = /^(.+)\0(.+)$/;
30562
30562
  FileStatusSummary = class {
30563
- constructor(path44, index, working_dir) {
30564
- this.path = path44;
30563
+ constructor(path46, index, working_dir) {
30564
+ this.path = path46;
30565
30565
  this.index = index;
30566
30566
  this.working_dir = working_dir;
30567
30567
  if (index === "R" || working_dir === "R") {
30568
- const detail = fromPathRegex.exec(path44) || [null, path44, path44];
30568
+ const detail = fromPathRegex.exec(path46) || [null, path46, path46];
30569
30569
  this.from = detail[2] || "";
30570
30570
  this.path = detail[1] || "";
30571
30571
  }
@@ -30596,14 +30596,14 @@ function splitLine(result, lineStr) {
30596
30596
  default:
30597
30597
  return;
30598
30598
  }
30599
- function data(index, workingDir, path44) {
30599
+ function data(index, workingDir, path46) {
30600
30600
  const raw = `${index}${workingDir}`;
30601
30601
  const handler = parsers6.get(raw);
30602
30602
  if (handler) {
30603
- handler(result, path44);
30603
+ handler(result, path46);
30604
30604
  }
30605
30605
  if (raw !== "##" && raw !== "!!") {
30606
- result.files.push(new FileStatusSummary(path44, index, workingDir));
30606
+ result.files.push(new FileStatusSummary(path46, index, workingDir));
30607
30607
  }
30608
30608
  }
30609
30609
  }
@@ -30912,9 +30912,9 @@ var init_simple_git_api = __esm2({
30912
30912
  next
30913
30913
  );
30914
30914
  }
30915
- hashObject(path44, write) {
30915
+ hashObject(path46, write) {
30916
30916
  return this._runTask(
30917
- hashObjectTask(path44, write === true),
30917
+ hashObjectTask(path46, write === true),
30918
30918
  trailingFunctionArgument(arguments)
30919
30919
  );
30920
30920
  }
@@ -31267,8 +31267,8 @@ var init_branch = __esm2({
31267
31267
  }
31268
31268
  });
31269
31269
  function toPath(input) {
31270
- const path44 = input.trim().replace(/^["']|["']$/g, "");
31271
- return path44 && normalize3(path44);
31270
+ const path46 = input.trim().replace(/^["']|["']$/g, "");
31271
+ return path46 && normalize3(path46);
31272
31272
  }
31273
31273
  var parseCheckIgnore;
31274
31274
  var init_CheckIgnore = __esm2({
@@ -31582,8 +31582,8 @@ __export2(sub_module_exports, {
31582
31582
  subModuleTask: () => subModuleTask,
31583
31583
  updateSubModuleTask: () => updateSubModuleTask
31584
31584
  });
31585
- function addSubModuleTask(repo, path44) {
31586
- return subModuleTask(["add", repo, path44]);
31585
+ function addSubModuleTask(repo, path46) {
31586
+ return subModuleTask(["add", repo, path46]);
31587
31587
  }
31588
31588
  function initSubModuleTask(customArgs) {
31589
31589
  return subModuleTask(["init", ...customArgs]);
@@ -31916,8 +31916,8 @@ var require_git = __commonJS2({
31916
31916
  }
31917
31917
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
31918
31918
  };
31919
- Git2.prototype.submoduleAdd = function(repo, path44, then) {
31920
- return this._runTask(addSubModuleTask2(repo, path44), trailingFunctionArgument2(arguments));
31919
+ Git2.prototype.submoduleAdd = function(repo, path46, then) {
31920
+ return this._runTask(addSubModuleTask2(repo, path46), trailingFunctionArgument2(arguments));
31921
31921
  };
31922
31922
  Git2.prototype.submoduleUpdate = function(args, then) {
31923
31923
  return this._runTask(
@@ -32880,9 +32880,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
32880
32880
  // src/agents/acp/put-summarize-change-summaries.ts
32881
32881
  async function putEncryptedChangeSummaryRows(params) {
32882
32882
  const base = params.apiBaseUrl.replace(/\/+$/, "");
32883
- const entries = params.rows.map(({ path: path44, summary }) => {
32883
+ const entries = params.rows.map(({ path: path46, summary }) => {
32884
32884
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
32885
- return { path: path44, summary: JSON.stringify(enc) };
32885
+ return { path: path46, summary: JSON.stringify(enc) };
32886
32886
  });
32887
32887
  const res = await fetch(
32888
32888
  `${base}/internal/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -33121,54 +33121,167 @@ function buildForkedSessionAgentPrompt(userPrompt, childSessionId, parentSession
33121
33121
  ].join("\n");
33122
33122
  }
33123
33123
 
33124
- // src/mcp/tools/session-history/fetch-parent-session-cloud.ts
33124
+ // src/mcp/tools/session-history/cloud/internal-api.ts
33125
33125
  function internalApiBase(cloudApiBaseUrl) {
33126
33126
  return cloudApiBaseUrl.replace(/\/+$/, "");
33127
33127
  }
33128
- async function fetchCloudSessionMeta(params) {
33128
+ async function fetchInternalApiJson(params) {
33129
33129
  const token = params.getCloudAccessToken();
33130
33130
  if (!token) return { ok: false, error: "Missing cloud access token." };
33131
- const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.sessionId)}/meta`;
33131
+ const url2 = `${internalApiBase(params.cloudApiBaseUrl)}${params.path}`;
33132
33132
  const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
33133
33133
  if (!res.ok) {
33134
33134
  const t = await res.text().catch(() => "");
33135
- return { ok: false, error: `Session meta fetch failed (${res.status}): ${t.slice(0, 200)}` };
33135
+ return { ok: false, error: `${params.errorLabel} (${res.status}): ${t.slice(0, 200)}` };
33136
33136
  }
33137
33137
  const data = await res.json().catch(() => null);
33138
- if (!data) return { ok: false, error: "Invalid session meta response." };
33138
+ if (!data) return { ok: false, error: `Invalid ${params.errorLabel.toLowerCase()} response.` };
33139
33139
  return { ok: true, data };
33140
33140
  }
33141
- async function fetchCloudParentSessionPrompts(params) {
33142
- const token = params.getCloudAccessToken();
33143
- if (!token) return { ok: false, error: "Missing cloud access token." };
33144
- const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/prompts`;
33145
- const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
33146
- if (!res.ok) {
33147
- const t = await res.text().catch(() => "");
33148
- return { ok: false, error: `Parent prompts fetch failed (${res.status}): ${t.slice(0, 200)}` };
33141
+
33142
+ // src/mcp/tools/session-history/cloud/fetch-session-meta.ts
33143
+ async function fetchCloudSessionMeta(params) {
33144
+ return fetchInternalApiJson({
33145
+ ...params,
33146
+ path: `/internal/sessions/${encodeURIComponent(params.sessionId)}/meta`,
33147
+ errorLabel: "Session meta fetch failed"
33148
+ });
33149
+ }
33150
+
33151
+ // src/lib/e2ee/decrypt-stored-e2ee-content.ts
33152
+ function parseJsonObject(raw) {
33153
+ try {
33154
+ const parsed = JSON.parse(raw);
33155
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
33156
+ return parsed;
33157
+ }
33158
+ } catch {
33159
+ return null;
33149
33160
  }
33150
- const data = await res.json().catch(() => null);
33151
- if (!data) return { ok: false, error: "Invalid parent prompts response." };
33152
- return { ok: true, data };
33161
+ return null;
33153
33162
  }
33154
- async function fetchCloudParentTurnTranscript(params) {
33155
- const token = params.getCloudAccessToken();
33156
- if (!token) return { ok: false, error: "Missing cloud access token." };
33157
- const url2 = `${internalApiBase(params.cloudApiBaseUrl)}/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/turns/${encodeURIComponent(params.turnId)}/transcript`;
33158
- const res = await fetch(url2, { headers: { Authorization: `Bearer ${token}` } });
33159
- if (!res.ok) {
33160
- const t = await res.text().catch(() => "");
33161
- return { ok: false, error: `Transcript fetch failed (${res.status}): ${t.slice(0, 200)}` };
33163
+ function storedStringHasE2eeEnvelope(raw) {
33164
+ const record2 = parseJsonObject(raw);
33165
+ return record2 != null && isE2eeEnvelope(record2.ee);
33166
+ }
33167
+ function e2eeKeyIdFromStoredString(raw) {
33168
+ const record2 = parseJsonObject(raw);
33169
+ if (record2 != null && isE2eeEnvelope(record2.ee)) return record2.ee.k;
33170
+ return null;
33171
+ }
33172
+ function decryptStoredE2eeRecord(record2, e2ee) {
33173
+ if (!isE2eeEnvelope(record2.ee)) return record2;
33174
+ if (!e2ee) {
33175
+ throw new Error(`E2EE_REQUIRED:${record2.ee.k}`);
33176
+ }
33177
+ if (record2.ee.k !== e2ee.keyId) {
33178
+ throw new Error(`E2EE_KEY_MISMATCH:encrypted=${record2.ee.k}:cli=${e2ee.keyId}`);
33179
+ }
33180
+ return e2ee.decryptMessage(record2);
33181
+ }
33182
+ function formatCliE2eeDecryptError(error40, e2ee) {
33183
+ const message = error40 instanceof Error ? error40.message : String(error40);
33184
+ const required2 = /^E2EE_REQUIRED:(.+)$/.exec(message.trim());
33185
+ if (required2) {
33186
+ const keyId = required2[1]?.trim() || "unknown";
33187
+ return `Parent session content is encrypted (key ${keyId}) but this bridge was not started with E2EE certificates. Restart the CLI with your E2EE certificate to read fork parent history.`;
33188
+ }
33189
+ const mismatch = /^E2EE_KEY_MISMATCH:encrypted=(.+):cli=(.*)$/.exec(message.trim());
33190
+ if (mismatch) {
33191
+ const encryptedKeyId = mismatch[1]?.trim() || "unknown";
33192
+ const cliKeyId = mismatch[2]?.trim() || e2ee?.keyId || "none";
33193
+ return `Parent session content is encrypted with key ${encryptedKeyId}, but this bridge certificate key is ${cliKeyId}. Use the same E2EE certificate that was used for the parent session.`;
33194
+ }
33195
+ if (message.includes("E2EE key mismatch")) {
33196
+ return `Parent session content could not be decrypted: ${message}`;
33197
+ }
33198
+ return `Parent session content could not be decrypted: ${message}`;
33199
+ }
33200
+ function decryptCliE2eeTranscriptPayload(payload, e2ee) {
33201
+ const record2 = parseJsonObject(payload);
33202
+ if (!record2 || !isE2eeEnvelope(record2.ee)) return payload;
33203
+ const decrypted = decryptStoredE2eeRecord(record2, e2ee);
33204
+ if (typeof decrypted.payload === "string") return decrypted.payload;
33205
+ return JSON.stringify(decrypted.payload ?? decrypted);
33206
+ }
33207
+ function decryptCliE2eeMessageField(payload, field, e2ee) {
33208
+ if (payload == null) return null;
33209
+ const record2 = parseJsonObject(payload);
33210
+ if (!record2 || !isE2eeEnvelope(record2.ee)) return payload;
33211
+ const decrypted = decryptStoredE2eeRecord(record2, e2ee);
33212
+ const value = decrypted[field];
33213
+ if (value == null) return null;
33214
+ return typeof value === "string" ? value : JSON.stringify(value);
33215
+ }
33216
+
33217
+ // src/mcp/tools/session-history/cloud/decrypt-parent-prompt-turns.ts
33218
+ function assertE2eeRuntimeForEnvelope(raw, e2ee) {
33219
+ if (storedStringHasE2eeEnvelope(raw) && !e2ee) {
33220
+ throw new Error(`E2EE_REQUIRED:${e2eeKeyIdFromStoredString(raw) ?? "unknown"}`);
33221
+ }
33222
+ }
33223
+ function decryptParentPromptTurns(turns, e2ee) {
33224
+ return turns.map((turn) => {
33225
+ const promptRaw = typeof turn.promptText === "string" ? turn.promptText : null;
33226
+ const titleRaw = typeof turn.title === "string" ? turn.title : null;
33227
+ if (promptRaw) assertE2eeRuntimeForEnvelope(promptRaw, e2ee);
33228
+ if (titleRaw) assertE2eeRuntimeForEnvelope(titleRaw, e2ee);
33229
+ return {
33230
+ ...turn,
33231
+ promptText: decryptCliE2eeMessageField(promptRaw, "prompt", e2ee),
33232
+ title: decryptCliE2eeMessageField(titleRaw, "title", e2ee)
33233
+ };
33234
+ });
33235
+ }
33236
+
33237
+ // src/mcp/tools/session-history/cloud/fetch-parent-prompts.ts
33238
+ async function fetchCloudParentSessionPrompts(params) {
33239
+ const fetched = await fetchInternalApiJson({
33240
+ ...params,
33241
+ path: `/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/prompts`,
33242
+ errorLabel: "Parent prompts fetch failed"
33243
+ });
33244
+ if (!fetched.ok) return fetched;
33245
+ const turns = Array.isArray(fetched.data.turns) ? fetched.data.turns : [];
33246
+ try {
33247
+ const decryptedTurns = decryptParentPromptTurns(turns, params.e2ee);
33248
+ return { ok: true, data: { ...fetched.data, turns: decryptedTurns } };
33249
+ } catch (error40) {
33250
+ return { ok: false, error: formatCliE2eeDecryptError(error40, params.e2ee) };
33162
33251
  }
33163
- const data = await res.json().catch(() => null);
33164
- const rows = Array.isArray(data?.transcript) ? data.transcript : [];
33252
+ }
33253
+
33254
+ // src/mcp/tools/session-history/cloud/format-parent-transcript-text.ts
33255
+ function formatParentTranscriptText(rows, e2ee) {
33165
33256
  const lines = rows.map((row) => {
33166
33257
  const kind = typeof row.kind === "string" ? row.kind : "event";
33167
- const payload = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload ?? "");
33258
+ const rawPayload = typeof row.payload === "string" ? row.payload : JSON.stringify(row.payload ?? "");
33259
+ if (storedStringHasE2eeEnvelope(rawPayload) && !e2ee) {
33260
+ throw new Error(`E2EE_REQUIRED:${e2eeKeyIdFromStoredString(rawPayload) ?? "unknown"}`);
33261
+ }
33262
+ const payload = decryptCliE2eeTranscriptPayload(rawPayload, e2ee);
33168
33263
  return `[${kind}] ${payload}`;
33169
33264
  });
33170
- return { ok: true, text: lines.join("\n") };
33265
+ return lines.join("\n");
33266
+ }
33267
+
33268
+ // src/mcp/tools/session-history/cloud/fetch-parent-transcript.ts
33269
+ async function fetchCloudParentTurnTranscript(params) {
33270
+ const fetched = await fetchInternalApiJson({
33271
+ ...params,
33272
+ path: `/internal/sessions/${encodeURIComponent(params.childSessionId)}/parent/turns/${encodeURIComponent(params.turnId)}/transcript`,
33273
+ errorLabel: "Transcript fetch failed"
33274
+ });
33275
+ if (!fetched.ok) return fetched;
33276
+ const rows = Array.isArray(fetched.data.transcript) ? fetched.data.transcript : [];
33277
+ try {
33278
+ return { ok: true, text: formatParentTranscriptText(rows, params.e2ee) };
33279
+ } catch (error40) {
33280
+ return { ok: false, error: formatCliE2eeDecryptError(error40, params.e2ee) };
33281
+ }
33171
33282
  }
33283
+
33284
+ // src/mcp/tools/session-history/cloud/resolve-parent-session-id.ts
33172
33285
  async function resolveParentSessionIdForChild(params) {
33173
33286
  const meta = await fetchCloudSessionMeta({
33174
33287
  sessionId: params.childSessionId,
@@ -33502,7 +33615,7 @@ async function createAcpManager(options) {
33502
33615
  // src/git/changes/types.ts
33503
33616
  var MAX_PATCH_CHARS = 35e4;
33504
33617
 
33505
- // src/git/changes/lib/repo-format.ts
33618
+ // src/git/changes/paths/repo-format.ts
33506
33619
  function posixJoinDirFile(dir, file2) {
33507
33620
  const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
33508
33621
  const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
@@ -33557,7 +33670,7 @@ function formatRemoteDisplayLabel(remoteUrl) {
33557
33670
  }
33558
33671
 
33559
33672
  // src/git/changes/get-working-tree-change-repo-details.ts
33560
- import * as path20 from "node:path";
33673
+ import * as path22 from "node:path";
33561
33674
 
33562
33675
  // src/git/commits/resolve-remote-tracking.ts
33563
33676
  async function tryConfigGet(g, key) {
@@ -33727,9 +33840,232 @@ async function listRecentCommits(repoDir, limitInput) {
33727
33840
  }
33728
33841
  }
33729
33842
 
33730
- // src/git/changes/lib/parse-git-status.ts
33731
- function parseNameStatusLines(lines) {
33732
- const m = /* @__PURE__ */ new Map();
33843
+ // src/git/changes/parse/normalize-git-diff-path.ts
33844
+ function normalizeGitDiffPath(path46) {
33845
+ return path46.replace(/\\/g, "/").replace(/\/ +/g, "/").trim();
33846
+ }
33847
+
33848
+ // src/git/changes/parse/expand-git-rename-numstat-path.ts
33849
+ function expandGitRenameNumstatPath(rawPath) {
33850
+ const open2 = rawPath.indexOf("{");
33851
+ const arrow = rawPath.indexOf("=>");
33852
+ const close = rawPath.lastIndexOf("}");
33853
+ if (open2 === -1 || arrow === -1 || close === -1 || open2 > arrow || arrow > close) {
33854
+ return null;
33855
+ }
33856
+ const prefix = rawPath.slice(0, open2);
33857
+ const suffix = rawPath.slice(close + 1);
33858
+ const oldPart = rawPath.slice(open2 + 1, arrow).trim();
33859
+ const newPart = rawPath.slice(arrow + 2, close).trim();
33860
+ return {
33861
+ oldPath: normalizeGitDiffPath(`${prefix}${oldPart}${suffix}`),
33862
+ newPath: normalizeGitDiffPath(`${prefix}${newPart}${suffix}`)
33863
+ };
33864
+ }
33865
+ function isGitRenameNumstatPath(rawPath) {
33866
+ return expandGitRenameNumstatPath(rawPath) != null;
33867
+ }
33868
+
33869
+ // src/git/changes/rows/build-changed-file-row.ts
33870
+ function buildChangedFileRow(options) {
33871
+ const pathInRepo = normalizeGitDiffPath(options.pathInRepo);
33872
+ const relLauncher = posixJoinDirFile(options.repoRelPath, pathInRepo);
33873
+ const additions = options.numEntry?.additions ?? 0;
33874
+ const deletions = options.numEntry?.deletions ?? 0;
33875
+ let change = options.nameEntry?.change ?? "modified";
33876
+ let movedFromPathRelLauncher;
33877
+ let movedFromPathInRepo;
33878
+ const oldPathInRepo = options.nameEntry?.oldPathInRepo ?? options.numEntry?.oldPathInRepo;
33879
+ if (change === "moved" && oldPathInRepo) {
33880
+ movedFromPathInRepo = normalizeGitDiffPath(oldPathInRepo);
33881
+ movedFromPathRelLauncher = posixJoinDirFile(options.repoRelPath, movedFromPathInRepo);
33882
+ } else if (oldPathInRepo && change === "modified") {
33883
+ change = "moved";
33884
+ movedFromPathInRepo = normalizeGitDiffPath(oldPathInRepo);
33885
+ movedFromPathRelLauncher = posixJoinDirFile(options.repoRelPath, movedFromPathInRepo);
33886
+ }
33887
+ if (options.untracked && !options.nameEntry) {
33888
+ change = "added";
33889
+ } else if (!options.nameEntry && options.numEntry && change !== "moved") {
33890
+ if (additions > 0 && deletions === 0) change = "added";
33891
+ else if (deletions > 0 && additions === 0) change = "removed";
33892
+ else change = "modified";
33893
+ }
33894
+ return {
33895
+ pathRelLauncher: relLauncher,
33896
+ additions,
33897
+ deletions,
33898
+ change,
33899
+ ...movedFromPathRelLauncher ? { movedFromPathRelLauncher } : {},
33900
+ ...movedFromPathInRepo ? { movedFromPathInRepo } : {}
33901
+ };
33902
+ }
33903
+
33904
+ // src/git/changes/listing/build-changed-file-rows.ts
33905
+ async function buildChangedFileRowsFromPaths(options) {
33906
+ const rows = [];
33907
+ await forEachWithGitYield([...options.paths], async (pathInRepo) => {
33908
+ if (isGitRenameNumstatPath(pathInRepo)) return;
33909
+ if (options.movedSourcePaths.has(pathInRepo)) return;
33910
+ const nameEntry = options.nameByPath.get(pathInRepo);
33911
+ const numEntry = options.numByPath.get(pathInRepo);
33912
+ const row = buildChangedFileRow({
33913
+ pathInRepo,
33914
+ repoRelPath: options.repoRelPath,
33915
+ nameEntry,
33916
+ numEntry,
33917
+ untracked: options.untrackedSet?.has(pathInRepo)
33918
+ });
33919
+ if (options.applyUntrackedStats) {
33920
+ await options.applyUntrackedStats(row, pathInRepo);
33921
+ }
33922
+ rows.push(row);
33923
+ });
33924
+ return rows;
33925
+ }
33926
+
33927
+ // src/git/changes/patch/patch-truncate.ts
33928
+ function truncatePatch(s) {
33929
+ if (s.length <= MAX_PATCH_CHARS) return s;
33930
+ return `${s.slice(0, MAX_PATCH_CHARS)}
33931
+
33932
+ \u2026 (diff truncated)`;
33933
+ }
33934
+
33935
+ // src/git/changes/patch/unified-diff-for-file.ts
33936
+ function patchTextFromGitDiffOutput(raw) {
33937
+ if (raw.trim() === "") return void 0;
33938
+ return normalizePatchContent(raw);
33939
+ }
33940
+ async function unifiedDiffForFile(repoCwd, pathInRepo, change, movedFromPathInRepo) {
33941
+ const g = cliSimpleGit(repoCwd);
33942
+ const renameArgs = ["-M", "--find-renames"];
33943
+ const args = change === "moved" && movedFromPathInRepo ? ["diff", "--no-color", ...renameArgs, "HEAD", "--", movedFromPathInRepo, pathInRepo] : change === "removed" ? ["diff", "--no-color", ...renameArgs, "HEAD", "--", pathInRepo] : ["diff", "--no-color", ...renameArgs, "HEAD", "--", pathInRepo];
33944
+ const raw = await g.raw([...args]).catch(() => "");
33945
+ const patch = patchTextFromGitDiffOutput(String(raw));
33946
+ return patch ? truncatePatch(patch) : void 0;
33947
+ }
33948
+ async function unifiedDiffForFileInRange(repoCwd, range, pathInRepo, change, movedFromPathInRepo) {
33949
+ const g = cliSimpleGit(repoCwd);
33950
+ const renameArgs = ["-M", "--find-renames"];
33951
+ const args = change === "moved" && movedFromPathInRepo ? ["diff", "--no-color", ...renameArgs, "-U20000", range, "--", movedFromPathInRepo, pathInRepo] : ["diff", "--no-color", ...renameArgs, "-U20000", range, "--", pathInRepo];
33952
+ const raw = await g.raw([...args]).catch(() => "");
33953
+ const patch = patchTextFromGitDiffOutput(String(raw));
33954
+ return patch ? truncatePatch(patch) : void 0;
33955
+ }
33956
+
33957
+ // src/git/changes/total-lines/resolve-changed-file-total-lines.ts
33958
+ import * as fs16 from "node:fs";
33959
+ import * as path19 from "node:path";
33960
+
33961
+ // src/git/changes/lines/count-lines.ts
33962
+ import { createReadStream } from "node:fs";
33963
+ import * as readline2 from "node:readline";
33964
+ function countLinesInText(text) {
33965
+ return splitTextIntoDiffLines(text).length;
33966
+ }
33967
+ async function countTextFileLines(filePath) {
33968
+ let bytes = 0;
33969
+ const maxBytes = 512e3;
33970
+ let lines = 0;
33971
+ const stream = createReadStream(filePath, { encoding: "utf8" });
33972
+ const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
33973
+ for await (const _line of rl) {
33974
+ lines += 1;
33975
+ bytes += Buffer.byteLength(String(_line), "utf8") + 1;
33976
+ if (bytes > maxBytes) {
33977
+ rl.close();
33978
+ stream.destroy();
33979
+ return lines;
33980
+ }
33981
+ }
33982
+ return lines;
33983
+ }
33984
+
33985
+ // src/git/changes/total-lines/resolve-changed-file-total-lines.ts
33986
+ var MAX_BYTES = 512e3;
33987
+ async function countLinesInGitRef(repoGitCwd, ref, pathInRepo) {
33988
+ const spec = `${ref}:${pathInRepo}`;
33989
+ const g = cliSimpleGit(repoGitCwd);
33990
+ try {
33991
+ await g.raw(["cat-file", "-e", spec]);
33992
+ } catch {
33993
+ return null;
33994
+ }
33995
+ try {
33996
+ const raw = String(await g.raw(["show", spec]));
33997
+ const capped = raw.length > MAX_BYTES ? raw.slice(0, MAX_BYTES) : raw;
33998
+ return countLinesInText(capped);
33999
+ } catch {
34000
+ return null;
34001
+ }
34002
+ }
34003
+ async function resolveWorkingTreeFileTotalLines(options) {
34004
+ if (options.isBinary) return null;
34005
+ const filePath = path19.join(options.repoGitCwd, options.pathInRepo);
34006
+ if (options.change === "removed") {
34007
+ return countLinesInGitRef(options.repoGitCwd, "HEAD", options.pathInRepo);
34008
+ }
34009
+ if (options.change === "moved" || options.change === "modified") {
34010
+ try {
34011
+ const st = await fs16.promises.stat(filePath);
34012
+ if (!st.isFile()) return null;
34013
+ return await countTextFileLines(filePath);
34014
+ } catch {
34015
+ return null;
34016
+ }
34017
+ }
34018
+ return null;
34019
+ }
34020
+ async function resolveCommitFileTotalLines(options) {
34021
+ if (options.isBinary) return null;
34022
+ if (options.change === "removed") {
34023
+ return countLinesInGitRef(options.repoGitCwd, options.parentSha, options.pathInRepo);
34024
+ }
34025
+ if (options.change === "moved" || options.change === "modified") {
34026
+ return countLinesInGitRef(options.repoGitCwd, options.commitSha, options.pathInRepo);
34027
+ }
34028
+ return null;
34029
+ }
34030
+
34031
+ // src/git/changes/listing/path-in-repo-from-launcher.ts
34032
+ function pathInRepoFromLauncher(pathRelLauncher, repoRelPath) {
34033
+ const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
34034
+ if (normRel === ".") return pathRelLauncher;
34035
+ if (pathRelLauncher.startsWith(`${normRel}/`)) {
34036
+ return pathRelLauncher.slice(normRel.length + 1);
34037
+ }
34038
+ return pathRelLauncher;
34039
+ }
34040
+ function normalizeRepoRelPath(repoRelPath) {
34041
+ return repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
34042
+ }
34043
+
34044
+ // src/git/changes/listing/enrich-commit-file-rows.ts
34045
+ async function enrichCommitFileRows(options) {
34046
+ await forEachWithGitYield(options.rows, async (row) => {
34047
+ const pathInRepo = pathInRepoFromLauncher(row.pathRelLauncher, options.repoRelPath);
34048
+ row.patchContent = await unifiedDiffForFileInRange(
34049
+ options.repoGitCwd,
34050
+ options.range,
34051
+ pathInRepo,
34052
+ row.change,
34053
+ row.movedFromPathInRepo
34054
+ );
34055
+ row.totalLines = await resolveCommitFileTotalLines({
34056
+ repoGitCwd: options.repoGitCwd,
34057
+ pathInRepo,
34058
+ change: row.change,
34059
+ commitSha: options.commitSha,
34060
+ parentSha: options.parentSha,
34061
+ isBinary: options.binaryByPath.has(pathInRepo)
34062
+ });
34063
+ });
34064
+ }
34065
+
34066
+ // src/git/changes/parse/parse-name-status-entries.ts
34067
+ function parseNameStatusEntries(lines) {
34068
+ const entries = [];
33733
34069
  for (const line of lines) {
33734
34070
  if (!line.trim()) continue;
33735
34071
  const tabParts = line.split(" ");
@@ -33737,58 +34073,143 @@ function parseNameStatusLines(lines) {
33737
34073
  const status = tabParts[0].trim();
33738
34074
  const code = status[0];
33739
34075
  if (code === "A") {
33740
- m.set(tabParts[tabParts.length - 1], "added");
34076
+ entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "added" });
33741
34077
  } else if (code === "D") {
33742
- m.set(tabParts[tabParts.length - 1], "removed");
34078
+ entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "removed" });
33743
34079
  } else if (code === "R" || code === "C") {
33744
- if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
34080
+ if (tabParts.length >= 3) {
34081
+ entries.push({
34082
+ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]),
34083
+ oldPathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 2]),
34084
+ change: "moved"
34085
+ });
34086
+ }
33745
34087
  } else if (code === "M" || code === "U" || code === "T") {
33746
- m.set(tabParts[tabParts.length - 1], "modified");
34088
+ entries.push({ pathInRepo: normalizeGitDiffPath(tabParts[tabParts.length - 1]), change: "modified" });
33747
34089
  }
33748
34090
  }
34091
+ return entries;
34092
+ }
34093
+ function parseNameStatusLines(lines) {
34094
+ const m = /* @__PURE__ */ new Map();
34095
+ for (const entry of parseNameStatusEntries(lines)) {
34096
+ m.set(entry.pathInRepo, entry.change);
34097
+ }
33749
34098
  return m;
33750
34099
  }
33751
- function parseNumstatFirstLine(line) {
34100
+
34101
+ // src/git/changes/parse/parse-numstat-entries.ts
34102
+ function parseNumstatCounts(parts) {
34103
+ const [a, d] = parts;
34104
+ return {
34105
+ additions: a === "-" ? 0 : parseInt(String(a), 10) || 0,
34106
+ deletions: d === "-" ? 0 : parseInt(String(d), 10) || 0
34107
+ };
34108
+ }
34109
+ function parseNumstatLine(line) {
33752
34110
  const parts = line.split(" ");
33753
34111
  if (parts.length < 3) return null;
33754
- const [a, d] = parts;
33755
- const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
33756
- const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
33757
- return { additions, deletions };
34112
+ const rawPath = parts[parts.length - 1];
34113
+ const { additions, deletions } = parseNumstatCounts(parts);
34114
+ const expanded = expandGitRenameNumstatPath(rawPath);
34115
+ const pathInRepo = normalizeGitDiffPath(expanded?.newPath ?? rawPath);
34116
+ return {
34117
+ pathInRepo,
34118
+ additions,
34119
+ deletions,
34120
+ ...expanded ? { oldPathInRepo: expanded.oldPath } : {}
34121
+ };
33758
34122
  }
33759
- function parseNumstat(lines) {
34123
+ function parseNumstatEntries(lines) {
33760
34124
  const m = /* @__PURE__ */ new Map();
33761
34125
  for (const line of lines) {
33762
34126
  if (!line.trim()) continue;
33763
34127
  const parts = line.split(" ");
33764
34128
  if (parts.length < 3) continue;
33765
- const [a, d, p] = parts;
33766
- const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
33767
- const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
33768
- m.set(p, { additions, deletions });
34129
+ const rawPath = parts[parts.length - 1];
34130
+ const expanded = expandGitRenameNumstatPath(rawPath);
34131
+ const entry = parseNumstatLine(line);
34132
+ if (!entry) continue;
34133
+ const existing = m.get(entry.pathInRepo);
34134
+ if (!existing || expanded) {
34135
+ m.set(entry.pathInRepo, entry);
34136
+ }
33769
34137
  }
33770
34138
  return m;
33771
34139
  }
34140
+ function parseNumstat(lines) {
34141
+ const m = /* @__PURE__ */ new Map();
34142
+ for (const [path46, entry] of parseNumstatEntries(lines)) {
34143
+ m.set(path46, { additions: entry.additions, deletions: entry.deletions });
34144
+ }
34145
+ return m;
34146
+ }
34147
+
34148
+ // src/git/changes/parse/numstat-from-git-no-index.ts
33772
34149
  async function numstatFromGitNoIndex(g, pathInRepo) {
33773
34150
  const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
33774
34151
  try {
33775
34152
  const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
33776
34153
  const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
33777
- return parseNumstatFirstLine(first2);
34154
+ const parsed = parseNumstatLine(first2);
34155
+ if (!parsed) return null;
34156
+ return { additions: parsed.additions, deletions: parsed.deletions };
33778
34157
  } catch {
33779
34158
  return null;
33780
34159
  }
33781
34160
  }
33782
34161
 
33783
- // src/git/changes/lib/patch-truncate.ts
33784
- function truncatePatch(s) {
33785
- if (s.length <= MAX_PATCH_CHARS) return s;
33786
- return `${s.slice(0, MAX_PATCH_CHARS)}
34162
+ // src/git/changes/rows/collect-moved-source-paths.ts
34163
+ function collectMovedSourcePaths(nameEntries, numByPath) {
34164
+ const sources = /* @__PURE__ */ new Set();
34165
+ for (const entry of nameEntries) {
34166
+ if (entry.change === "moved" && entry.oldPathInRepo) sources.add(entry.oldPathInRepo);
34167
+ }
34168
+ for (const entry of numByPath.values()) {
34169
+ if (entry.oldPathInRepo) sources.add(entry.oldPathInRepo);
34170
+ }
34171
+ return sources;
34172
+ }
33787
34173
 
33788
- \u2026 (diff truncated)`;
34174
+ // src/git/changes/numstat/is-binary-numstat-line.ts
34175
+ function isBinaryNumstatLine(line) {
34176
+ const parts = line.split(" ");
34177
+ if (parts.length < 3) return false;
34178
+ return parts[0] === "-" || parts[1] === "-";
34179
+ }
34180
+
34181
+ // src/git/changes/listing/collect-binary-paths-from-numstat.ts
34182
+ function collectBinaryPathsFromNumstat(numstatRaw) {
34183
+ const binaryByPath = /* @__PURE__ */ new Set();
34184
+ for (const line of numstatRaw.split("\n")) {
34185
+ if (!line.trim()) continue;
34186
+ const parts = line.split(" ");
34187
+ if (parts.length < 3) continue;
34188
+ const rawPath = parts[parts.length - 1];
34189
+ const expanded = expandGitRenameNumstatPath(rawPath);
34190
+ if (isBinaryNumstatLine(line)) binaryByPath.add(expanded?.newPath ?? rawPath);
34191
+ }
34192
+ return binaryByPath;
33789
34193
  }
33790
34194
 
33791
- // src/git/commits/list-changed-files-for-commit.ts
34195
+ // src/git/changes/listing/parse-diff-output.ts
34196
+ function parseDiffOutput(nameStatusRaw, numstatRaw) {
34197
+ const nameEntries = parseNameStatusEntries(nameStatusRaw.split("\n"));
34198
+ const nameByPath = new Map(nameEntries.map((entry) => [entry.pathInRepo, entry]));
34199
+ const numByPath = parseNumstatEntries(numstatRaw.split("\n"));
34200
+ const movedSourcePaths = collectMovedSourcePaths(nameEntries, numByPath);
34201
+ const binaryByPath = collectBinaryPathsFromNumstat(numstatRaw);
34202
+ return { nameEntries, nameByPath, numByPath, movedSourcePaths, binaryByPath };
34203
+ }
34204
+ function collectChangedPaths(options) {
34205
+ const paths = /* @__PURE__ */ new Set();
34206
+ for (const entry of options.nameEntries) paths.add(entry.pathInRepo);
34207
+ for (const pathInRepo of options.numByPath.keys()) paths.add(pathInRepo);
34208
+ for (const pathInRepo of options.untracked ?? []) paths.add(pathInRepo);
34209
+ return paths;
34210
+ }
34211
+
34212
+ // src/git/changes/listing/parent-for-commit-diff.ts
33792
34213
  var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
33793
34214
  async function parentForCommitDiff(g, sha) {
33794
34215
  try {
@@ -33801,76 +34222,112 @@ async function parentForCommitDiff(g, sha) {
33801
34222
  }
33802
34223
  }
33803
34224
  }
34225
+
34226
+ // src/git/changes/listing/rename-diff-args.ts
34227
+ var RENAME_DIFF_ARGS = ["-M", "--find-renames"];
34228
+
34229
+ // src/git/changes/rows/pick-preferred-changed-file-row.ts
34230
+ function rowRank(row) {
34231
+ if (row.change === "moved") return 3;
34232
+ if (row.change === "modified") return 2;
34233
+ if (row.change === "added") return 1;
34234
+ return 0;
34235
+ }
34236
+ function pickPreferredChangedFileRow(a, b) {
34237
+ if (rowRank(a) !== rowRank(b)) return rowRank(a) > rowRank(b) ? a : b;
34238
+ const aHasPatch = Boolean(a.patchContent?.trim());
34239
+ const bHasPatch = Boolean(b.patchContent?.trim());
34240
+ if (aHasPatch !== bHasPatch) return aHasPatch ? a : b;
34241
+ const aHasMoveMeta = Boolean(a.movedFromPathInRepo || a.movedFromPathRelLauncher);
34242
+ const bHasMoveMeta = Boolean(b.movedFromPathInRepo || b.movedFromPathRelLauncher);
34243
+ if (aHasMoveMeta !== bHasMoveMeta) return aHasMoveMeta ? a : b;
34244
+ return a;
34245
+ }
34246
+
34247
+ // src/git/changes/rows/dedupe-changed-file-rows.ts
34248
+ function dedupeChangedFileRows(rows) {
34249
+ const byPath = /* @__PURE__ */ new Map();
34250
+ for (const row of rows) {
34251
+ const key = normalizeGitDiffPath(row.pathRelLauncher);
34252
+ const normalizedRow = key === row.pathRelLauncher ? row : { ...row, pathRelLauncher: key };
34253
+ const existing = byPath.get(key);
34254
+ if (!existing) {
34255
+ byPath.set(key, normalizedRow);
34256
+ continue;
34257
+ }
34258
+ const preferRow = pickPreferredChangedFileRow(existing, normalizedRow);
34259
+ byPath.set(key, { ...preferRow, pathRelLauncher: key });
34260
+ }
34261
+ return [...byPath.values()];
34262
+ }
34263
+
34264
+ // src/git/changes/listing/list-changed-files-for-commit.ts
33804
34265
  async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
33805
34266
  const g = cliSimpleGit(repoGitCwd);
33806
34267
  const parent = await parentForCommitDiff(g, commitSha);
33807
34268
  const range = `${parent}..${commitSha}`;
33808
34269
  const [nameStatusRaw, numstatRaw] = await Promise.all([
33809
- g.raw(["diff", "--name-status", range]).catch(() => ""),
33810
- g.raw(["diff", "--numstat", range]).catch(() => "")
34270
+ g.raw(["diff", ...RENAME_DIFF_ARGS, "--name-status", range]).catch(() => ""),
34271
+ g.raw(["diff", ...RENAME_DIFF_ARGS, "--numstat", range]).catch(() => "")
33811
34272
  ]);
33812
- const kindByPath = parseNameStatusLines(String(nameStatusRaw).split("\n"));
33813
- const numByPath = parseNumstat(String(numstatRaw).split("\n"));
33814
- const paths = new Set([...kindByPath.keys(), ...numByPath.keys()].filter(Boolean));
33815
- const rows = [];
33816
- const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
33817
- await forEachWithGitYield([...paths], async (pathInRepo) => {
33818
- const relLauncher = posixJoinDirFile(normRel, pathInRepo.replace(/\\/g, "/"));
33819
- const nums = numByPath.get(pathInRepo);
33820
- let additions = nums?.additions ?? 0;
33821
- let deletions = nums?.deletions ?? 0;
33822
- let change = kindByPath.get(pathInRepo) ?? "modified";
33823
- if (!kindByPath.has(pathInRepo) && nums) {
33824
- if (additions > 0 && deletions === 0) change = "added";
33825
- else if (deletions > 0 && additions === 0) change = "removed";
33826
- else change = "modified";
33827
- }
33828
- rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
33829
- });
33830
- await forEachWithGitYield(rows, async (row) => {
33831
- let pathInRepo;
33832
- if (normRel === ".") {
33833
- pathInRepo = row.pathRelLauncher;
33834
- } else if (row.pathRelLauncher.startsWith(`${normRel}/`)) {
33835
- pathInRepo = row.pathRelLauncher.slice(normRel.length + 1);
33836
- } else {
33837
- pathInRepo = row.pathRelLauncher;
33838
- }
33839
- const raw = await g.raw(["diff", "-U20000", range, "--", pathInRepo]).catch(() => "");
33840
- const t = String(raw).trim();
33841
- row.patchContent = t ? truncatePatch(t) : void 0;
34273
+ const parsed = parseDiffOutput(String(nameStatusRaw), String(numstatRaw));
34274
+ const normRel = normalizeRepoRelPath(repoRelPath);
34275
+ const paths = collectChangedPaths({
34276
+ nameEntries: parsed.nameEntries,
34277
+ numByPath: parsed.numByPath
33842
34278
  });
33843
- rows.sort((a, b) => a.pathRelLauncher.localeCompare(b.pathRelLauncher));
33844
- return rows;
33845
- }
33846
-
33847
- // src/git/changes/list-changed-files-for-repo.ts
34279
+ let rows = await buildChangedFileRowsFromPaths({
34280
+ paths,
34281
+ repoRelPath: normRel,
34282
+ nameByPath: parsed.nameByPath,
34283
+ numByPath: parsed.numByPath,
34284
+ movedSourcePaths: parsed.movedSourcePaths
34285
+ });
34286
+ rows = dedupeChangedFileRows(rows);
34287
+ await enrichCommitFileRows({
34288
+ rows,
34289
+ repoGitCwd,
34290
+ repoRelPath: normRel,
34291
+ range,
34292
+ commitSha,
34293
+ parentSha: parent,
34294
+ binaryByPath: parsed.binaryByPath
34295
+ });
34296
+ const finalRows = dedupeChangedFileRows(rows);
34297
+ finalRows.sort((a, b) => a.pathRelLauncher.localeCompare(b.pathRelLauncher));
34298
+ return finalRows;
34299
+ }
34300
+
34301
+ // src/git/changes/listing/apply-untracked-file-stats.ts
33848
34302
  import * as fs17 from "node:fs";
33849
- import * as path19 from "node:path";
33850
-
33851
- // src/git/changes/lib/count-lines.ts
33852
- import { createReadStream } from "node:fs";
33853
- import * as readline2 from "node:readline";
33854
- async function countTextFileLines(filePath) {
33855
- let bytes = 0;
33856
- const maxBytes = 512e3;
33857
- let lines = 0;
33858
- const stream = createReadStream(filePath, { encoding: "utf8" });
33859
- const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
33860
- for await (const _line of rl) {
33861
- lines += 1;
33862
- bytes += Buffer.byteLength(String(_line), "utf8") + 1;
33863
- if (bytes > maxBytes) {
33864
- rl.close();
33865
- stream.destroy();
33866
- return lines;
34303
+ import * as path20 from "node:path";
34304
+ function createUntrackedStatsApplier(options) {
34305
+ return async (row, pathInRepo) => {
34306
+ if (!options.untrackedSet.has(pathInRepo)) return;
34307
+ if (options.nameByPath.has(pathInRepo)) return;
34308
+ if (row.change === "moved") return;
34309
+ const repoFilePath = path20.join(options.repoGitCwd, pathInRepo);
34310
+ const fromGit = await numstatFromGitNoIndex(options.g, pathInRepo);
34311
+ if (fromGit) {
34312
+ row.additions = fromGit.additions;
34313
+ row.deletions = fromGit.deletions;
34314
+ return;
33867
34315
  }
33868
- }
33869
- return lines;
34316
+ try {
34317
+ const st = await fs17.promises.stat(repoFilePath);
34318
+ row.additions = st.isFile() ? await countTextFileLines(repoFilePath) : 0;
34319
+ } catch {
34320
+ row.additions = 0;
34321
+ }
34322
+ row.deletions = 0;
34323
+ };
33870
34324
  }
33871
34325
 
33872
- // src/git/changes/hydrate-patch.ts
33873
- import * as fs16 from "node:fs";
34326
+ // src/git/changes/listing/enrich-working-tree-file-rows.ts
34327
+ import * as path21 from "node:path";
34328
+
34329
+ // src/git/changes/patch/hydrate-patch.ts
34330
+ import * as fs18 from "node:fs";
33874
34331
  var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
33875
34332
  var MAX_HYDRATE_LINES_PER_GAP = 8e3;
33876
34333
  var MAX_HYDRATE_LINES_PER_FILE = 8e4;
@@ -33885,7 +34342,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
33885
34342
  }
33886
34343
  async function readWorktreeFileLines(filePath) {
33887
34344
  try {
33888
- const raw = await fs16.promises.readFile(filePath, "utf8");
34345
+ const raw = await fs18.promises.readFile(filePath, "utf8");
33889
34346
  return raw.split(/\r?\n/);
33890
34347
  } catch {
33891
34348
  return null;
@@ -33985,82 +34442,70 @@ async function hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, p
33985
34442
  return truncatePatch(out.join("\n"));
33986
34443
  }
33987
34444
 
33988
- // src/git/changes/unified-diff-for-file.ts
33989
- function patchTextFromGitDiffOutput(raw) {
33990
- if (raw.trim() === "") return void 0;
33991
- return normalizePatchContent(raw);
33992
- }
33993
- async function unifiedDiffForFile(repoCwd, pathInRepo, change) {
33994
- const g = cliSimpleGit(repoCwd);
33995
- const args = change === "added" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : change === "removed" ? ["diff", "--no-color", "HEAD", "--", pathInRepo] : ["diff", "--no-color", "HEAD", "--", pathInRepo];
33996
- const raw = await g.raw([...args]).catch(() => "");
33997
- const patch = patchTextFromGitDiffOutput(String(raw));
33998
- return patch ? truncatePatch(patch) : void 0;
34445
+ // src/git/changes/listing/enrich-working-tree-file-rows.ts
34446
+ async function enrichWorkingTreeFileRows(options) {
34447
+ await forEachWithGitYield(options.rows, async (row) => {
34448
+ const pathInRepo = pathInRepoFromLauncher(row.pathRelLauncher, options.repoRelPath);
34449
+ const filePath = path21.join(options.repoGitCwd, pathInRepo);
34450
+ const hydrateKind = row.change === "moved" ? "modified" : row.change;
34451
+ let patch = await unifiedDiffForFile(options.repoGitCwd, pathInRepo, row.change, row.movedFromPathInRepo);
34452
+ if (patch) {
34453
+ patch = await hydrateUnifiedPatchWithFileContext(
34454
+ patch,
34455
+ filePath,
34456
+ options.repoGitCwd,
34457
+ pathInRepo,
34458
+ hydrateKind
34459
+ );
34460
+ }
34461
+ row.patchContent = patch;
34462
+ row.totalLines = await resolveWorkingTreeFileTotalLines({
34463
+ repoGitCwd: options.repoGitCwd,
34464
+ pathInRepo,
34465
+ change: row.change,
34466
+ isBinary: options.binaryByPath.has(pathInRepo)
34467
+ });
34468
+ });
33999
34469
  }
34000
34470
 
34001
- // src/git/changes/list-changed-files-for-repo.ts
34471
+ // src/git/changes/listing/list-changed-files-for-repo.ts
34002
34472
  async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
34003
34473
  const g = cliSimpleGit(repoGitCwd);
34004
34474
  const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
34005
- g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
34006
- g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
34475
+ g.raw(["diff", ...RENAME_DIFF_ARGS, "--name-status", "HEAD"]).catch(() => ""),
34476
+ g.raw(["diff", ...RENAME_DIFF_ARGS, "HEAD", "--numstat"]).catch(() => ""),
34007
34477
  g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
34008
34478
  ]);
34009
- const kindByPath = parseNameStatusLines(String(nameStatusRaw).split("\n"));
34010
- const numByPath = parseNumstat(String(numstatRaw).split("\n"));
34011
- const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
34479
+ const parsed = parseDiffOutput(String(nameStatusRaw), String(numstatRaw));
34012
34480
  const untracked = String(untrackedRaw).split("\n").map((s) => s.trim()).filter(Boolean);
34013
- for (const p of untracked) paths.add(p);
34014
- const rows = [];
34015
- await forEachWithGitYield([...paths], async (pathInRepo) => {
34016
- const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
34017
- const repoFilePath = path19.join(repoGitCwd, pathInRepo);
34018
- const nums = numByPath.get(pathInRepo);
34019
- let additions = nums?.additions ?? 0;
34020
- let deletions = nums?.deletions ?? 0;
34021
- let change = kindByPath.get(pathInRepo) ?? "modified";
34022
- if (untracked.includes(pathInRepo) && !kindByPath.has(pathInRepo)) {
34023
- change = "added";
34024
- const fromGit = await numstatFromGitNoIndex(g, pathInRepo);
34025
- if (fromGit) {
34026
- additions = fromGit.additions;
34027
- deletions = fromGit.deletions;
34028
- } else {
34029
- try {
34030
- const st = await fs17.promises.stat(repoFilePath);
34031
- if (st.isFile()) additions = await countTextFileLines(repoFilePath);
34032
- else additions = 0;
34033
- } catch {
34034
- additions = 0;
34035
- }
34036
- deletions = 0;
34037
- }
34038
- }
34039
- if (!kindByPath.has(pathInRepo) && nums) {
34040
- if (additions > 0 && deletions === 0) change = "added";
34041
- else if (deletions > 0 && additions === 0) change = "removed";
34042
- else change = "modified";
34043
- }
34044
- rows.push({ pathRelLauncher: relLauncher, additions, deletions, change });
34481
+ const untrackedSet = new Set(untracked);
34482
+ const paths = collectChangedPaths({
34483
+ nameEntries: parsed.nameEntries,
34484
+ numByPath: parsed.numByPath,
34485
+ untracked
34045
34486
  });
34046
- const normRel = repoRelPath === "." || repoRelPath === "" ? "." : repoRelPath;
34047
- await forEachWithGitYield(rows, async (row) => {
34048
- let pathInRepo;
34049
- if (normRel === ".") {
34050
- pathInRepo = row.pathRelLauncher;
34051
- } else if (row.pathRelLauncher.startsWith(`${normRel}/`)) {
34052
- pathInRepo = row.pathRelLauncher.slice(normRel.length + 1);
34053
- } else {
34054
- pathInRepo = row.pathRelLauncher;
34055
- }
34056
- const filePath = path19.join(repoGitCwd, pathInRepo);
34057
- let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
34058
- if (patch) {
34059
- patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
34060
- }
34061
- row.patchContent = patch;
34487
+ let rows = await buildChangedFileRowsFromPaths({
34488
+ paths,
34489
+ repoRelPath,
34490
+ nameByPath: parsed.nameByPath,
34491
+ numByPath: parsed.numByPath,
34492
+ movedSourcePaths: parsed.movedSourcePaths,
34493
+ untrackedSet,
34494
+ applyUntrackedStats: createUntrackedStatsApplier({
34495
+ g,
34496
+ repoGitCwd,
34497
+ untrackedSet,
34498
+ nameByPath: parsed.nameByPath
34499
+ })
34062
34500
  });
34063
- return rows;
34501
+ rows = dedupeChangedFileRows(rows);
34502
+ await enrichWorkingTreeFileRows({
34503
+ rows,
34504
+ repoGitCwd,
34505
+ repoRelPath,
34506
+ binaryByPath: parsed.binaryByPath
34507
+ });
34508
+ return dedupeChangedFileRows(rows);
34064
34509
  }
34065
34510
 
34066
34511
  // src/git/changes/get-working-tree-change-repo-details.ts
@@ -34069,8 +34514,8 @@ function normRepoRel(p) {
34069
34514
  return x === "" ? "." : x;
34070
34515
  }
34071
34516
  async function getWorkingTreeChangeRepoDetails(options) {
34072
- const bridgeRoot = path20.resolve(getBridgeRoot());
34073
- const sessionWtRoot = options.sessionWorktreeRootPath ? path20.resolve(options.sessionWorktreeRootPath) : null;
34517
+ const bridgeRoot = path22.resolve(getBridgeRoot());
34518
+ const sessionWtRoot = options.sessionWorktreeRootPath ? path22.resolve(options.sessionWorktreeRootPath) : null;
34074
34519
  const legacyNested = options.legacyRepoNestedSessionLayout === true;
34075
34520
  const out = [];
34076
34521
  const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
@@ -34085,7 +34530,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
34085
34530
  for (let i = 0; i < options.commitTargetPaths.length; i++) {
34086
34531
  if (i > 0) await yieldToEventLoop2();
34087
34532
  const target = options.commitTargetPaths[i];
34088
- const t = path20.resolve(target);
34533
+ const t = path22.resolve(target);
34089
34534
  if (!await isGitRepoDirectory(t)) continue;
34090
34535
  const g = cliSimpleGit(t);
34091
34536
  let branch = "HEAD";
@@ -34098,8 +34543,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
34098
34543
  const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
34099
34544
  let repoRelPath;
34100
34545
  if (sessionWtRoot) {
34101
- const anchor = legacyNested ? path20.dirname(t) : t;
34102
- const relNorm = path20.relative(sessionWtRoot, anchor);
34546
+ const anchor = legacyNested ? path22.dirname(t) : t;
34547
+ const relNorm = path22.relative(sessionWtRoot, anchor);
34103
34548
  repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
34104
34549
  } else {
34105
34550
  let top = t;
@@ -34108,8 +34553,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
34108
34553
  } catch {
34109
34554
  top = t;
34110
34555
  }
34111
- const rel = path20.relative(bridgeRoot, path20.resolve(top)).replace(/\\/g, "/") || ".";
34112
- repoRelPath = rel.startsWith("..") ? path20.basename(path20.resolve(top)) : rel;
34556
+ const rel = path22.relative(bridgeRoot, path22.resolve(top)).replace(/\\/g, "/") || ".";
34557
+ repoRelPath = rel.startsWith("..") ? path22.basename(path22.resolve(top)) : rel;
34113
34558
  }
34114
34559
  const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
34115
34560
  if (filter && norm !== filter) continue;
@@ -34141,7 +34586,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
34141
34586
  return out;
34142
34587
  }
34143
34588
 
34144
- // src/git/changes/lib/working-tree-changed-path-count.ts
34589
+ // src/git/changes/status/working-tree-changed-path-count.ts
34145
34590
  function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
34146
34591
  const kindByPath = parseNameStatusLines(nameStatusLines);
34147
34592
  const numByPath = parseNumstat(numstatLines);
@@ -34237,23 +34682,23 @@ async function commitSessionWorktrees(options) {
34237
34682
  }
34238
34683
 
34239
34684
  // src/worktrees/remove-session-worktrees.ts
34240
- import * as fs20 from "node:fs";
34685
+ import * as fs21 from "node:fs";
34241
34686
 
34242
34687
  // src/git/worktrees/worktree-remove.ts
34243
- import * as fs19 from "node:fs";
34688
+ import * as fs20 from "node:fs";
34244
34689
 
34245
34690
  // src/git/worktrees/resolve-main-repo-from-git-file.ts
34246
- import * as fs18 from "node:fs";
34247
- import * as path21 from "node:path";
34691
+ import * as fs19 from "node:fs";
34692
+ import * as path23 from "node:path";
34248
34693
  function resolveMainRepoFromWorktreeGitFile(wt) {
34249
- const gitDirFile = path21.join(wt, ".git");
34250
- if (!fs18.existsSync(gitDirFile) || !fs18.statSync(gitDirFile).isFile()) return "";
34251
- const first2 = fs18.readFileSync(gitDirFile, "utf8").trim();
34694
+ const gitDirFile = path23.join(wt, ".git");
34695
+ if (!fs19.existsSync(gitDirFile) || !fs19.statSync(gitDirFile).isFile()) return "";
34696
+ const first2 = fs19.readFileSync(gitDirFile, "utf8").trim();
34252
34697
  const m = first2.match(/^gitdir:\s*(.+)$/im);
34253
34698
  if (!m) return "";
34254
- const gitWorktreePath = path21.resolve(wt, m[1].trim());
34255
- const gitDir = path21.dirname(path21.dirname(gitWorktreePath));
34256
- return path21.dirname(gitDir);
34699
+ const gitWorktreePath = path23.resolve(wt, m[1].trim());
34700
+ const gitDir = path23.dirname(path23.dirname(gitWorktreePath));
34701
+ return path23.dirname(gitDir);
34257
34702
  }
34258
34703
 
34259
34704
  // src/git/worktrees/worktree-remove.ts
@@ -34262,7 +34707,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
34262
34707
  if (mainRepo) {
34263
34708
  await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
34264
34709
  } else {
34265
- fs19.rmSync(worktreePath, { recursive: true, force: true });
34710
+ fs20.rmSync(worktreePath, { recursive: true, force: true });
34266
34711
  }
34267
34712
  }
34268
34713
 
@@ -34275,7 +34720,7 @@ async function removeSessionWorktrees(paths, log2) {
34275
34720
  } catch (e) {
34276
34721
  log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
34277
34722
  try {
34278
- fs20.rmSync(wt, { recursive: true, force: true });
34723
+ fs21.rmSync(wt, { recursive: true, force: true });
34279
34724
  } catch {
34280
34725
  }
34281
34726
  }
@@ -34304,12 +34749,12 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
34304
34749
  }
34305
34750
 
34306
34751
  // src/worktrees/worktree-layout-file.ts
34307
- import * as fs21 from "node:fs";
34308
- import * as path22 from "node:path";
34752
+ import * as fs22 from "node:fs";
34753
+ import * as path24 from "node:path";
34309
34754
  import os7 from "node:os";
34310
34755
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
34311
34756
  function defaultWorktreeLayoutPath() {
34312
- return path22.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
34757
+ return path24.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
34313
34758
  }
34314
34759
  function normalizeLoadedLayout(raw) {
34315
34760
  if (raw && typeof raw === "object" && "launcherCwds" in raw) {
@@ -34321,8 +34766,8 @@ function normalizeLoadedLayout(raw) {
34321
34766
  function loadWorktreeLayout() {
34322
34767
  try {
34323
34768
  const p = defaultWorktreeLayoutPath();
34324
- if (!fs21.existsSync(p)) return { launcherCwds: [] };
34325
- const raw = JSON.parse(fs21.readFileSync(p, "utf8"));
34769
+ if (!fs22.existsSync(p)) return { launcherCwds: [] };
34770
+ const raw = JSON.parse(fs22.readFileSync(p, "utf8"));
34326
34771
  return normalizeLoadedLayout(raw);
34327
34772
  } catch {
34328
34773
  return { launcherCwds: [] };
@@ -34330,24 +34775,24 @@ function loadWorktreeLayout() {
34330
34775
  }
34331
34776
  function saveWorktreeLayout(layout) {
34332
34777
  try {
34333
- const dir = path22.dirname(defaultWorktreeLayoutPath());
34334
- fs21.mkdirSync(dir, { recursive: true });
34335
- fs21.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
34778
+ const dir = path24.dirname(defaultWorktreeLayoutPath());
34779
+ fs22.mkdirSync(dir, { recursive: true });
34780
+ fs22.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
34336
34781
  } catch {
34337
34782
  }
34338
34783
  }
34339
34784
  function baseNameSafe(pathString) {
34340
- return path22.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
34785
+ return path24.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
34341
34786
  }
34342
34787
  function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
34343
- const norm = path22.resolve(bridgeRootPath2);
34344
- const existing = layout.launcherCwds.find((e) => path22.resolve(e.absolutePath) === norm);
34788
+ const norm = path24.resolve(bridgeRootPath2);
34789
+ const existing = layout.launcherCwds.find((e) => path24.resolve(e.absolutePath) === norm);
34345
34790
  return existing?.dirName;
34346
34791
  }
34347
34792
  function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
34348
34793
  const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
34349
34794
  if (existing) return existing;
34350
- const norm = path22.resolve(bridgeRootPath2);
34795
+ const norm = path24.resolve(bridgeRootPath2);
34351
34796
  const base = baseNameSafe(norm);
34352
34797
  const used = new Set(layout.launcherCwds.map((e) => e.dirName));
34353
34798
  let name = base;
@@ -34362,11 +34807,11 @@ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
34362
34807
  }
34363
34808
 
34364
34809
  // src/worktrees/discover-session-worktree-on-disk.ts
34365
- import * as fs22 from "node:fs";
34366
- import * as path23 from "node:path";
34810
+ import * as fs23 from "node:fs";
34811
+ import * as path25 from "node:path";
34367
34812
  function isGitDir(dirPath) {
34368
34813
  try {
34369
- return fs22.existsSync(path23.join(dirPath, ".git"));
34814
+ return fs23.existsSync(path25.join(dirPath, ".git"));
34370
34815
  } catch {
34371
34816
  return false;
34372
34817
  }
@@ -34375,23 +34820,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
34375
34820
  const out = [];
34376
34821
  const walk = (dir) => {
34377
34822
  if (isGitDir(dir)) {
34378
- out.push(path23.resolve(dir));
34823
+ out.push(path25.resolve(dir));
34379
34824
  return;
34380
34825
  }
34381
34826
  let entries;
34382
34827
  try {
34383
- entries = fs22.readdirSync(dir, { withFileTypes: true });
34828
+ entries = fs23.readdirSync(dir, { withFileTypes: true });
34384
34829
  } catch {
34385
34830
  return;
34386
34831
  }
34387
34832
  for (const e of entries) {
34388
34833
  if (e.name.startsWith(".")) continue;
34389
- const full = path23.join(dir, e.name);
34834
+ const full = path25.join(dir, e.name);
34390
34835
  if (!e.isDirectory()) continue;
34391
34836
  walk(full);
34392
34837
  }
34393
34838
  };
34394
- walk(path23.resolve(rootPath));
34839
+ walk(path25.resolve(rootPath));
34395
34840
  return [...new Set(out)];
34396
34841
  }
34397
34842
  function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
@@ -34400,16 +34845,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
34400
34845
  if (depth > maxDepth) return;
34401
34846
  let entries;
34402
34847
  try {
34403
- entries = fs22.readdirSync(dir, { withFileTypes: true });
34848
+ entries = fs23.readdirSync(dir, { withFileTypes: true });
34404
34849
  } catch {
34405
34850
  return;
34406
34851
  }
34407
34852
  for (const e of entries) {
34408
34853
  if (e.name.startsWith(".")) continue;
34409
- const full = path23.join(dir, e.name);
34854
+ const full = path25.join(dir, e.name);
34410
34855
  if (!e.isDirectory()) continue;
34411
34856
  if (e.name === sessionId) {
34412
- if (isGitDir(full)) out.push(path23.resolve(full));
34857
+ if (isGitDir(full)) out.push(path25.resolve(full));
34413
34858
  } else {
34414
34859
  walk(full, depth + 1);
34415
34860
  }
@@ -34421,14 +34866,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
34421
34866
  function tryBindingFromSessionDirectory(sessionDir) {
34422
34867
  let st;
34423
34868
  try {
34424
- st = fs22.statSync(sessionDir);
34869
+ st = fs23.statSync(sessionDir);
34425
34870
  } catch {
34426
34871
  return null;
34427
34872
  }
34428
34873
  if (!st.isDirectory()) return null;
34429
34874
  const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
34430
34875
  if (worktreePaths.length === 0) return null;
34431
- const abs = path23.resolve(sessionDir);
34876
+ const abs = path25.resolve(sessionDir);
34432
34877
  return {
34433
34878
  sessionParentPath: abs,
34434
34879
  workingTreeRelRoot: abs,
@@ -34438,20 +34883,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
34438
34883
  function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
34439
34884
  const sid = sessionId.trim();
34440
34885
  if (!sid) return null;
34441
- const hintR = path23.resolve(checkoutPath);
34886
+ const hintR = path25.resolve(checkoutPath);
34442
34887
  let best = null;
34443
- let cur = path23.dirname(hintR);
34888
+ let cur = path25.dirname(hintR);
34444
34889
  for (let i = 0; i < 40; i++) {
34445
34890
  const paths = collectWorktreeRootsNamed(cur, sid, 24);
34446
- if (paths.some((p) => path23.resolve(p) === hintR)) {
34447
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path23.resolve(paths[0]);
34891
+ if (paths.some((p) => path25.resolve(p) === hintR)) {
34892
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path25.resolve(paths[0]);
34448
34893
  best = {
34449
- sessionParentPath: path23.resolve(isolated),
34450
- workingTreeRelRoot: path23.resolve(cur),
34451
- repoCheckoutPaths: paths.map((p) => path23.resolve(p))
34894
+ sessionParentPath: path25.resolve(isolated),
34895
+ workingTreeRelRoot: path25.resolve(cur),
34896
+ repoCheckoutPaths: paths.map((p) => path25.resolve(p))
34452
34897
  };
34453
34898
  }
34454
- const next = path23.dirname(cur);
34899
+ const next = path25.dirname(cur);
34455
34900
  if (next === cur) break;
34456
34901
  cur = next;
34457
34902
  }
@@ -34459,33 +34904,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
34459
34904
  }
34460
34905
  function discoverSessionWorktreeOnDisk(options) {
34461
34906
  const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
34462
- if (!sessionId.trim() || !fs22.existsSync(worktreesRootPath)) return null;
34907
+ if (!sessionId.trim() || !fs23.existsSync(worktreesRootPath)) return null;
34463
34908
  const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
34464
34909
  const keys = [];
34465
34910
  if (preferredKey) keys.push(preferredKey);
34466
34911
  try {
34467
- for (const name of fs22.readdirSync(worktreesRootPath)) {
34912
+ for (const name of fs23.readdirSync(worktreesRootPath)) {
34468
34913
  if (name.startsWith(".")) continue;
34469
- const p = path23.join(worktreesRootPath, name);
34470
- if (!fs22.statSync(p).isDirectory()) continue;
34914
+ const p = path25.join(worktreesRootPath, name);
34915
+ if (!fs23.statSync(p).isDirectory()) continue;
34471
34916
  if (name !== preferredKey) keys.push(name);
34472
34917
  }
34473
34918
  } catch {
34474
34919
  return null;
34475
34920
  }
34476
34921
  for (const key of keys) {
34477
- const layoutRoot = path23.join(worktreesRootPath, key);
34478
- if (!fs22.existsSync(layoutRoot) || !fs22.statSync(layoutRoot).isDirectory()) continue;
34479
- const sessionDir = path23.join(layoutRoot, sessionId);
34922
+ const layoutRoot = path25.join(worktreesRootPath, key);
34923
+ if (!fs23.existsSync(layoutRoot) || !fs23.statSync(layoutRoot).isDirectory()) continue;
34924
+ const sessionDir = path25.join(layoutRoot, sessionId);
34480
34925
  const nested = tryBindingFromSessionDirectory(sessionDir);
34481
34926
  if (nested) return nested;
34482
34927
  const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
34483
34928
  if (legacyPaths.length > 0) {
34484
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path23.resolve(legacyPaths[0]);
34929
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
34485
34930
  return {
34486
- sessionParentPath: path23.resolve(isolated),
34487
- workingTreeRelRoot: path23.resolve(layoutRoot),
34488
- repoCheckoutPaths: legacyPaths.map((p) => path23.resolve(p))
34931
+ sessionParentPath: path25.resolve(isolated),
34932
+ workingTreeRelRoot: path25.resolve(layoutRoot),
34933
+ repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
34489
34934
  };
34490
34935
  }
34491
34936
  }
@@ -34494,12 +34939,12 @@ function discoverSessionWorktreeOnDisk(options) {
34494
34939
  function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
34495
34940
  const sid = sessionId.trim();
34496
34941
  if (!sid) return null;
34497
- const hint = path23.resolve(sessionWorktreeRootPathOrHint);
34498
- const underHint = tryBindingFromSessionDirectory(path23.join(hint, sid));
34942
+ const hint = path25.resolve(sessionWorktreeRootPathOrHint);
34943
+ const underHint = tryBindingFromSessionDirectory(path25.join(hint, sid));
34499
34944
  if (underHint) return underHint;
34500
34945
  const direct = tryBindingFromSessionDirectory(hint);
34501
34946
  if (direct) {
34502
- if (path23.basename(hint) === sid && isGitDir(hint)) {
34947
+ if (path25.basename(hint) === sid && isGitDir(hint)) {
34503
34948
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
34504
34949
  if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
34505
34950
  return legacyFromCheckout;
@@ -34507,24 +34952,24 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
34507
34952
  }
34508
34953
  return direct;
34509
34954
  }
34510
- if (path23.basename(hint) === sid && isGitDir(hint)) {
34955
+ if (path25.basename(hint) === sid && isGitDir(hint)) {
34511
34956
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
34512
34957
  if (legacyFromCheckout) return legacyFromCheckout;
34513
34958
  }
34514
34959
  let st;
34515
34960
  try {
34516
- st = fs22.statSync(hint);
34961
+ st = fs23.statSync(hint);
34517
34962
  } catch {
34518
34963
  return null;
34519
34964
  }
34520
34965
  if (!st.isDirectory()) return null;
34521
34966
  const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
34522
34967
  if (legacyPaths.length === 0) return null;
34523
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path23.resolve(legacyPaths[0]);
34968
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
34524
34969
  return {
34525
- sessionParentPath: path23.resolve(isolated),
34970
+ sessionParentPath: path25.resolve(isolated),
34526
34971
  workingTreeRelRoot: hint,
34527
- repoCheckoutPaths: legacyPaths.map((p) => path23.resolve(p))
34972
+ repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
34528
34973
  };
34529
34974
  }
34530
34975
 
@@ -34581,8 +35026,8 @@ function parseSessionParent(v) {
34581
35026
  }
34582
35027
 
34583
35028
  // src/worktrees/prepare-new-session-worktrees.ts
34584
- import * as fs23 from "node:fs";
34585
- import * as path24 from "node:path";
35029
+ import * as fs24 from "node:fs";
35030
+ import * as path26 from "node:path";
34586
35031
 
34587
35032
  // src/git/worktrees/worktree-add.ts
34588
35033
  async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef = "HEAD") {
@@ -34592,7 +35037,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef
34592
35037
  }
34593
35038
 
34594
35039
  // src/worktrees/prepare-new-session-worktrees.ts
34595
- function normalizeRepoRelPath(rel) {
35040
+ function normalizeRepoRelPath2(rel) {
34596
35041
  return rel === "" ? "." : rel.replace(/\\/g, "/");
34597
35042
  }
34598
35043
  function resolveBaseRefForRepo(relNorm, baseBranches) {
@@ -34604,10 +35049,10 @@ function resolveBaseRefForRepo(relNorm, baseBranches) {
34604
35049
  }
34605
35050
  async function prepareNewSessionWorktrees(options) {
34606
35051
  const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
34607
- const bridgeResolved = path24.resolve(bridgeRoot);
35052
+ const bridgeResolved = path26.resolve(bridgeRoot);
34608
35053
  const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
34609
- const bridgeKeyDir = path24.join(worktreesRootPath, cwdKey);
34610
- const sessionDir = path24.join(bridgeKeyDir, sessionId);
35054
+ const bridgeKeyDir = path26.join(worktreesRootPath, cwdKey);
35055
+ const sessionDir = path26.join(bridgeKeyDir, sessionId);
34611
35056
  const repos = await discoverGitReposUnderRoot(bridgeResolved);
34612
35057
  if (repos.length === 0) {
34613
35058
  log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
@@ -34615,14 +35060,14 @@ async function prepareNewSessionWorktrees(options) {
34615
35060
  }
34616
35061
  const branch = `session-${sessionId}`;
34617
35062
  const worktreePaths = [];
34618
- fs23.mkdirSync(sessionDir, { recursive: true });
35063
+ fs24.mkdirSync(sessionDir, { recursive: true });
34619
35064
  for (const repo of repos) {
34620
- let rel = path24.relative(bridgeResolved, repo.absolutePath);
34621
- if (rel.startsWith("..") || path24.isAbsolute(rel)) continue;
34622
- const relNorm = normalizeRepoRelPath(rel === "" ? "." : rel);
34623
- const wtPath = relNorm === "." ? sessionDir : path24.join(sessionDir, relNorm);
35065
+ let rel = path26.relative(bridgeResolved, repo.absolutePath);
35066
+ if (rel.startsWith("..") || path26.isAbsolute(rel)) continue;
35067
+ const relNorm = normalizeRepoRelPath2(rel === "" ? "." : rel);
35068
+ const wtPath = relNorm === "." ? sessionDir : path26.join(sessionDir, relNorm);
34624
35069
  if (relNorm !== ".") {
34625
- fs23.mkdirSync(path24.dirname(wtPath), { recursive: true });
35070
+ fs24.mkdirSync(path26.dirname(wtPath), { recursive: true });
34626
35071
  }
34627
35072
  const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
34628
35073
  try {
@@ -34675,9 +35120,9 @@ function resolveExistingSessionParentPath(sessionId, cache2, discover) {
34675
35120
  }
34676
35121
 
34677
35122
  // src/worktrees/manager/resolve-explicit-session-parent-path.ts
34678
- import * as path25 from "node:path";
35123
+ import * as path27 from "node:path";
34679
35124
  function resolveExplicitSessionParentPath(params) {
34680
- const resolved = path25.resolve(params.parentPathRaw);
35125
+ const resolved = path27.resolve(params.parentPathRaw);
34681
35126
  if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
34682
35127
  return resolved;
34683
35128
  }
@@ -34693,7 +35138,7 @@ function resolveExplicitSessionParentPath(params) {
34693
35138
  for (let i = 0; i < 16; i++) {
34694
35139
  const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
34695
35140
  if (tryRoot) return rememberAndReturn(tryRoot);
34696
- const next = path25.dirname(cur);
35141
+ const next = path27.dirname(cur);
34697
35142
  if (next === cur) break;
34698
35143
  cur = next;
34699
35144
  }
@@ -34745,16 +35190,16 @@ async function resolveSessionParentPathForPrompt(params) {
34745
35190
  }
34746
35191
 
34747
35192
  // src/worktrees/manager/session-worktree-cache.ts
34748
- import * as path26 from "node:path";
35193
+ import * as path28 from "node:path";
34749
35194
  var SessionWorktreeCache = class {
34750
35195
  sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
34751
35196
  sessionParentPathBySession = /* @__PURE__ */ new Map();
34752
35197
  sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
34753
35198
  remember(sessionId, binding) {
34754
- const paths = binding.repoCheckoutPaths.map((p) => path26.resolve(p));
35199
+ const paths = binding.repoCheckoutPaths.map((p) => path28.resolve(p));
34755
35200
  this.sessionRepoCheckoutPaths.set(sessionId, paths);
34756
- this.sessionParentPathBySession.set(sessionId, path26.resolve(binding.sessionParentPath));
34757
- this.sessionWorkingTreeRelRootBySession.set(sessionId, path26.resolve(binding.workingTreeRelRoot));
35201
+ this.sessionParentPathBySession.set(sessionId, path28.resolve(binding.sessionParentPath));
35202
+ this.sessionWorkingTreeRelRootBySession.set(sessionId, path28.resolve(binding.workingTreeRelRoot));
34758
35203
  }
34759
35204
  clearSession(sessionId) {
34760
35205
  const paths = this.sessionRepoCheckoutPaths.get(sessionId);
@@ -34784,7 +35229,7 @@ var SessionWorktreeCache = class {
34784
35229
  const parent = this.sessionParentPathBySession.get(sessionId);
34785
35230
  const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
34786
35231
  if (!parent || !relRoot) return false;
34787
- return path26.resolve(parent) !== path26.resolve(relRoot);
35232
+ return path28.resolve(parent) !== path28.resolve(relRoot);
34788
35233
  }
34789
35234
  };
34790
35235
 
@@ -34888,29 +35333,29 @@ var SessionWorktreeManager = class {
34888
35333
  };
34889
35334
 
34890
35335
  // src/worktrees/manager/default-worktrees-root-path.ts
34891
- import * as path27 from "node:path";
35336
+ import * as path29 from "node:path";
34892
35337
  import os8 from "node:os";
34893
35338
  function defaultWorktreesRootPath() {
34894
- return path27.join(os8.homedir(), ".buildautomaton", "worktrees");
35339
+ return path29.join(os8.homedir(), ".buildautomaton", "worktrees");
34895
35340
  }
34896
35341
 
34897
35342
  // src/files/watch-file-index.ts
34898
35343
  import { watch } from "node:fs";
34899
- import path32 from "node:path";
35344
+ import path34 from "node:path";
34900
35345
 
34901
35346
  // src/files/index/paths.ts
34902
- import path28 from "node:path";
35347
+ import path30 from "node:path";
34903
35348
  import crypto2 from "node:crypto";
34904
35349
  function getCwdHashForFileIndex(resolvedCwd) {
34905
- return crypto2.createHash("sha256").update(path28.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
35350
+ return crypto2.createHash("sha256").update(path30.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
34906
35351
  }
34907
35352
 
34908
35353
  // src/files/index/build-file-index.ts
34909
- import path30 from "node:path";
35354
+ import path32 from "node:path";
34910
35355
 
34911
35356
  // src/files/index/walk-workspace-tree.ts
34912
- import fs24 from "node:fs";
34913
- import path29 from "node:path";
35357
+ import fs25 from "node:fs";
35358
+ import path31 from "node:path";
34914
35359
  var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
34915
35360
  "node_modules",
34916
35361
  "bower_components",
@@ -34927,7 +35372,7 @@ function shouldSkipWorkspaceWalkEntry(name) {
34927
35372
  async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
34928
35373
  let names;
34929
35374
  try {
34930
- names = await fs24.promises.readdir(dir);
35375
+ names = await fs25.promises.readdir(dir);
34931
35376
  } catch {
34932
35377
  return;
34933
35378
  }
@@ -34939,14 +35384,14 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
34939
35384
  if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
34940
35385
  }
34941
35386
  state.n++;
34942
- const full = path29.join(dir, name);
35387
+ const full = path31.join(dir, name);
34943
35388
  let stat2;
34944
35389
  try {
34945
- stat2 = await fs24.promises.stat(full);
35390
+ stat2 = await fs25.promises.stat(full);
34946
35391
  } catch {
34947
35392
  continue;
34948
35393
  }
34949
- const relative6 = path29.relative(baseDir, full).replace(/\\/g, "/");
35394
+ const relative6 = path31.relative(baseDir, full).replace(/\\/g, "/");
34950
35395
  if (stat2.isDirectory()) {
34951
35396
  await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
34952
35397
  } else if (stat2.isFile()) {
@@ -34959,7 +35404,7 @@ function createWalkYieldState() {
34959
35404
  }
34960
35405
 
34961
35406
  // src/files/index/file-index-sqlite-lock.ts
34962
- import fs25 from "node:fs";
35407
+ import fs26 from "node:fs";
34963
35408
  function isSqliteCorruptError(e) {
34964
35409
  const msg = e instanceof Error ? e.message : String(e);
34965
35410
  return msg.includes("malformed") || msg.includes("database disk image is malformed") || msg.includes("corrupt");
@@ -34973,7 +35418,7 @@ function withFileIndexSqliteLock(fn) {
34973
35418
  if (!isSqliteCorruptError(e)) throw e;
34974
35419
  closeAllCliSqliteConnections();
34975
35420
  try {
34976
- fs25.unlinkSync(getCliSqlitePath());
35421
+ fs26.unlinkSync(getCliSqlitePath());
34977
35422
  } catch {
34978
35423
  }
34979
35424
  chain = Promise.resolve();
@@ -35048,7 +35493,7 @@ async function collectWorkspacePathsAsync(resolved) {
35048
35493
  }
35049
35494
  async function buildFileIndexAsync(cwd) {
35050
35495
  return withFileIndexSqliteLock(async () => {
35051
- const resolved = path30.resolve(cwd);
35496
+ const resolved = path32.resolve(cwd);
35052
35497
  await yieldToEventLoop();
35053
35498
  assertNotShutdown();
35054
35499
  const paths = await collectWorkspacePathsAsync(resolved);
@@ -35060,7 +35505,7 @@ async function buildFileIndexAsync(cwd) {
35060
35505
  }
35061
35506
 
35062
35507
  // src/files/index/ensure-file-index.ts
35063
- import path31 from "node:path";
35508
+ import path33 from "node:path";
35064
35509
 
35065
35510
  // src/files/index/search-file-index.ts
35066
35511
  function escapeLikePattern(fragment) {
@@ -35112,7 +35557,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
35112
35557
 
35113
35558
  // src/files/index/ensure-file-index.ts
35114
35559
  async function ensureFileIndexAsync(cwd) {
35115
- const resolved = path31.resolve(cwd);
35560
+ const resolved = path33.resolve(cwd);
35116
35561
  if (await bridgeFileIndexIsPopulated(resolved)) {
35117
35562
  return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
35118
35563
  }
@@ -35156,7 +35601,7 @@ function createFsWatcher(resolved, schedule) {
35156
35601
  }
35157
35602
  }
35158
35603
  function startFileIndexWatcher(cwd = getBridgeRoot()) {
35159
- const resolved = path32.resolve(cwd);
35604
+ const resolved = path34.resolve(cwd);
35160
35605
  void buildFileIndexAsync(resolved).catch((e) => {
35161
35606
  if (e instanceof CliSqliteInterrupted) return;
35162
35607
  console.error("[file-index] Initial index build failed:", e);
@@ -35186,7 +35631,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
35186
35631
  }
35187
35632
 
35188
35633
  // src/connection/create-bridge-connection.ts
35189
- import * as path43 from "node:path";
35634
+ import * as path45 from "node:path";
35190
35635
 
35191
35636
  // src/dev-servers/manager/dev-server-manager.ts
35192
35637
  import { rm as rm2 } from "node:fs/promises";
@@ -35230,7 +35675,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
35230
35675
  }
35231
35676
 
35232
35677
  // src/dev-servers/process/wire-dev-server-child-process.ts
35233
- import fs26 from "node:fs";
35678
+ import fs27 from "node:fs";
35234
35679
 
35235
35680
  // src/dev-servers/manager/forward-pipe.ts
35236
35681
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -35266,7 +35711,7 @@ function wireDevServerChildProcess(d) {
35266
35711
  d.setPollInterval(void 0);
35267
35712
  return;
35268
35713
  }
35269
- fs26.readFile(d.mergedLogPath, (err, buf) => {
35714
+ fs27.readFile(d.mergedLogPath, (err, buf) => {
35270
35715
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
35271
35716
  if (buf.length <= d.mergedReadPos.value) return;
35272
35717
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -35304,7 +35749,7 @@ ${errTail}` : ""}`);
35304
35749
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
35305
35750
  };
35306
35751
  if (mergedPath) {
35307
- fs26.readFile(mergedPath, (err, buf) => {
35752
+ fs27.readFile(mergedPath, (err, buf) => {
35308
35753
  if (!err && buf.length > d.mergedReadPos.value) {
35309
35754
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
35310
35755
  if (chunk.length > 0) {
@@ -35406,13 +35851,13 @@ function parseDevServerDefs(servers) {
35406
35851
  }
35407
35852
 
35408
35853
  // src/dev-servers/manager/shell-spawn/utils.ts
35409
- import fs27 from "node:fs";
35854
+ import fs28 from "node:fs";
35410
35855
  function isSpawnEbadf(e) {
35411
35856
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
35412
35857
  }
35413
35858
  function rmDirQuiet(dir) {
35414
35859
  try {
35415
- fs27.rmSync(dir, { recursive: true, force: true });
35860
+ fs28.rmSync(dir, { recursive: true, force: true });
35416
35861
  } catch {
35417
35862
  }
35418
35863
  }
@@ -35420,7 +35865,7 @@ var cachedDevNullReadFd;
35420
35865
  function devNullReadFd() {
35421
35866
  if (cachedDevNullReadFd === void 0) {
35422
35867
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
35423
- cachedDevNullReadFd = fs27.openSync(devPath, "r");
35868
+ cachedDevNullReadFd = fs28.openSync(devPath, "r");
35424
35869
  }
35425
35870
  return cachedDevNullReadFd;
35426
35871
  }
@@ -35494,15 +35939,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
35494
35939
 
35495
35940
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
35496
35941
  import { spawn as spawn8 } from "node:child_process";
35497
- import fs28 from "node:fs";
35942
+ import fs29 from "node:fs";
35498
35943
  import { tmpdir } from "node:os";
35499
- import path33 from "node:path";
35944
+ import path35 from "node:path";
35500
35945
  function trySpawnMergedLogFile(command, env, cwd, signal) {
35501
- const tmpRoot = fs28.mkdtempSync(path33.join(tmpdir(), "ba-devsrv-log-"));
35502
- const logPath = path33.join(tmpRoot, "combined.log");
35946
+ const tmpRoot = fs29.mkdtempSync(path35.join(tmpdir(), "ba-devsrv-log-"));
35947
+ const logPath = path35.join(tmpRoot, "combined.log");
35503
35948
  let logFd;
35504
35949
  try {
35505
- logFd = fs28.openSync(logPath, "a");
35950
+ logFd = fs29.openSync(logPath, "a");
35506
35951
  } catch {
35507
35952
  rmDirQuiet(tmpRoot);
35508
35953
  return null;
@@ -35521,7 +35966,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
35521
35966
  } else {
35522
35967
  proc = spawn8("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
35523
35968
  }
35524
- fs28.closeSync(logFd);
35969
+ fs29.closeSync(logFd);
35525
35970
  return {
35526
35971
  proc,
35527
35972
  pipedStdoutStderr: true,
@@ -35530,7 +35975,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
35530
35975
  };
35531
35976
  } catch (e) {
35532
35977
  try {
35533
- fs28.closeSync(logFd);
35978
+ fs29.closeSync(logFd);
35534
35979
  } catch {
35535
35980
  }
35536
35981
  rmDirQuiet(tmpRoot);
@@ -35541,22 +35986,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
35541
35986
 
35542
35987
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
35543
35988
  import { spawn as spawn9 } from "node:child_process";
35544
- import fs29 from "node:fs";
35989
+ import fs30 from "node:fs";
35545
35990
  import { tmpdir as tmpdir2 } from "node:os";
35546
- import path34 from "node:path";
35991
+ import path36 from "node:path";
35547
35992
  function shSingleQuote(s) {
35548
35993
  return `'${s.replace(/'/g, `'\\''`)}'`;
35549
35994
  }
35550
35995
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
35551
- const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir2(), "ba-devsrv-sh-"));
35552
- const logPath = path34.join(tmpRoot, "combined.log");
35553
- const innerPath = path34.join(tmpRoot, "_cmd.sh");
35554
- const runnerPath = path34.join(tmpRoot, "_run.sh");
35996
+ const tmpRoot = fs30.mkdtempSync(path36.join(tmpdir2(), "ba-devsrv-sh-"));
35997
+ const logPath = path36.join(tmpRoot, "combined.log");
35998
+ const innerPath = path36.join(tmpRoot, "_cmd.sh");
35999
+ const runnerPath = path36.join(tmpRoot, "_run.sh");
35555
36000
  try {
35556
- fs29.writeFileSync(innerPath, `#!/bin/sh
36001
+ fs30.writeFileSync(innerPath, `#!/bin/sh
35557
36002
  ${command}
35558
36003
  `);
35559
- fs29.writeFileSync(
36004
+ fs30.writeFileSync(
35560
36005
  runnerPath,
35561
36006
  `#!/bin/sh
35562
36007
  cd ${shSingleQuote(cwd)}
@@ -35582,13 +36027,13 @@ cd ${shSingleQuote(cwd)}
35582
36027
  }
35583
36028
  }
35584
36029
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
35585
- const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir2(), "ba-devsrv-sh-"));
35586
- const logPath = path34.join(tmpRoot, "combined.log");
35587
- const runnerPath = path34.join(tmpRoot, "_run.bat");
36030
+ const tmpRoot = fs30.mkdtempSync(path36.join(tmpdir2(), "ba-devsrv-sh-"));
36031
+ const logPath = path36.join(tmpRoot, "combined.log");
36032
+ const runnerPath = path36.join(tmpRoot, "_run.bat");
35588
36033
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
35589
36034
  const com = process.env.ComSpec || "cmd.exe";
35590
36035
  try {
35591
- fs29.writeFileSync(
36036
+ fs30.writeFileSync(
35592
36037
  runnerPath,
35593
36038
  `@ECHO OFF\r
35594
36039
  CD /D ${q(cwd)}\r
@@ -36357,30 +36802,30 @@ function createOnBridgeIdentified(opts) {
36357
36802
  }
36358
36803
 
36359
36804
  // src/skills/discover-local-agent-skills.ts
36360
- import fs30 from "node:fs";
36361
- import path35 from "node:path";
36805
+ import fs31 from "node:fs";
36806
+ import path37 from "node:path";
36362
36807
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
36363
36808
  function discoverLocalSkills(cwd) {
36364
36809
  const out = [];
36365
36810
  const seenKeys = /* @__PURE__ */ new Set();
36366
36811
  for (const rel of SKILL_DISCOVERY_ROOTS) {
36367
- const base = path35.join(cwd, rel);
36368
- if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
36812
+ const base = path37.join(cwd, rel);
36813
+ if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
36369
36814
  let entries = [];
36370
36815
  try {
36371
- entries = fs30.readdirSync(base);
36816
+ entries = fs31.readdirSync(base);
36372
36817
  } catch {
36373
36818
  continue;
36374
36819
  }
36375
36820
  for (const name of entries) {
36376
- const dir = path35.join(base, name);
36821
+ const dir = path37.join(base, name);
36377
36822
  try {
36378
- if (!fs30.statSync(dir).isDirectory()) continue;
36823
+ if (!fs31.statSync(dir).isDirectory()) continue;
36379
36824
  } catch {
36380
36825
  continue;
36381
36826
  }
36382
- const skillMd = path35.join(dir, "SKILL.md");
36383
- if (!fs30.existsSync(skillMd)) continue;
36827
+ const skillMd = path37.join(dir, "SKILL.md");
36828
+ if (!fs31.existsSync(skillMd)) continue;
36384
36829
  const key = `${rel}/${name}`;
36385
36830
  if (seenKeys.has(key)) continue;
36386
36831
  seenKeys.add(key);
@@ -36392,23 +36837,23 @@ function discoverLocalSkills(cwd) {
36392
36837
  function discoverSkillLayoutRoots(cwd) {
36393
36838
  const roots = [];
36394
36839
  for (const rel of SKILL_DISCOVERY_ROOTS) {
36395
- const base = path35.join(cwd, rel);
36396
- if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
36840
+ const base = path37.join(cwd, rel);
36841
+ if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
36397
36842
  let entries = [];
36398
36843
  try {
36399
- entries = fs30.readdirSync(base);
36844
+ entries = fs31.readdirSync(base);
36400
36845
  } catch {
36401
36846
  continue;
36402
36847
  }
36403
36848
  const skills2 = [];
36404
36849
  for (const name of entries) {
36405
- const dir = path35.join(base, name);
36850
+ const dir = path37.join(base, name);
36406
36851
  try {
36407
- if (!fs30.statSync(dir).isDirectory()) continue;
36852
+ if (!fs31.statSync(dir).isDirectory()) continue;
36408
36853
  } catch {
36409
36854
  continue;
36410
36855
  }
36411
- if (!fs30.existsSync(path35.join(dir, "SKILL.md"))) continue;
36856
+ if (!fs31.existsSync(path37.join(dir, "SKILL.md"))) continue;
36412
36857
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
36413
36858
  skills2.push({ name, relPath });
36414
36859
  }
@@ -36764,7 +37209,7 @@ function syncRunningTurnQueueKeys(turns, queueKey) {
36764
37209
  }
36765
37210
 
36766
37211
  // src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
36767
- import fs31 from "node:fs";
37212
+ import fs32 from "node:fs";
36768
37213
  async function runLocalRevertBeforeQueuedPrompt(next, deps) {
36769
37214
  if (next.serverState !== "requeued_with_revert") return true;
36770
37215
  const sid = next.sessionId;
@@ -36773,7 +37218,7 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
36773
37218
  const agentBase = deps.sessionWorktreeManager.getSessionWorktreeRootForSession(sid) ?? getBridgeRoot();
36774
37219
  const file2 = snapshotFilePath(agentBase, tid);
36775
37220
  try {
36776
- await fs31.promises.access(file2, fs31.constants.F_OK);
37221
+ await fs32.promises.access(file2, fs32.constants.F_OK);
36777
37222
  } catch {
36778
37223
  deps.log(
36779
37224
  `[Queue] requeued_with_revert: no pre-turn snapshot for ${tid.slice(0, 8)}\u2026; continuing without revert.`
@@ -37061,9 +37506,9 @@ function parseChangeSummarySnapshots(raw) {
37061
37506
  for (const item of raw) {
37062
37507
  if (!item || typeof item !== "object") continue;
37063
37508
  const o = item;
37064
- const path44 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
37065
- if (!path44) continue;
37066
- const row = { path: path44 };
37509
+ const path46 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
37510
+ if (!path46) continue;
37511
+ const row = { path: path46 };
37067
37512
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
37068
37513
  if (typeof o.oldText === "string") row.oldText = o.oldText;
37069
37514
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -37277,14 +37722,14 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
37277
37722
  };
37278
37723
 
37279
37724
  // src/files/list-dir/index.ts
37280
- import fs33 from "node:fs";
37725
+ import fs34 from "node:fs";
37281
37726
 
37282
37727
  // src/files/ensure-under-cwd.ts
37283
- import path36 from "node:path";
37728
+ import path38 from "node:path";
37284
37729
  function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
37285
- const normalized = path36.normalize(relativePath).replace(/^(\.\/)+/, "");
37286
- const resolved = path36.resolve(cwd, normalized);
37287
- if (!resolved.startsWith(cwd + path36.sep) && resolved !== cwd) {
37730
+ const normalized = path38.normalize(relativePath).replace(/^(\.\/)+/, "");
37731
+ const resolved = path38.resolve(cwd, normalized);
37732
+ if (!resolved.startsWith(cwd + path38.sep) && resolved !== cwd) {
37288
37733
  return null;
37289
37734
  }
37290
37735
  return resolved;
@@ -37294,15 +37739,15 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
37294
37739
  var LIST_DIR_YIELD_EVERY = 256;
37295
37740
 
37296
37741
  // src/files/list-dir/map-dir-entry.ts
37297
- import path37 from "node:path";
37298
- import fs32 from "node:fs";
37742
+ import path39 from "node:path";
37743
+ import fs33 from "node:fs";
37299
37744
  async function mapDirEntry(d, relativePath, resolved) {
37300
- const entryPath = path37.join(relativePath || ".", d.name).replace(/\\/g, "/");
37301
- const fullPath = path37.join(resolved, d.name);
37745
+ const entryPath = path39.join(relativePath || ".", d.name).replace(/\\/g, "/");
37746
+ const fullPath = path39.join(resolved, d.name);
37302
37747
  let isDir = d.isDirectory();
37303
37748
  if (d.isSymbolicLink()) {
37304
37749
  try {
37305
- const targetStat = await fs32.promises.stat(fullPath);
37750
+ const targetStat = await fs33.promises.stat(fullPath);
37306
37751
  isDir = targetStat.isDirectory();
37307
37752
  } catch {
37308
37753
  isDir = false;
@@ -37332,7 +37777,7 @@ async function listDirAsync(relativePath, sessionParentPath = getBridgeRoot()) {
37332
37777
  return { error: "Path is outside working directory" };
37333
37778
  }
37334
37779
  try {
37335
- const names = await fs33.promises.readdir(resolved, { withFileTypes: true });
37780
+ const names = await fs34.promises.readdir(resolved, { withFileTypes: true });
37336
37781
  const entries = [];
37337
37782
  for (let i = 0; i < names.length; i++) {
37338
37783
  if (i > 0 && i % LIST_DIR_YIELD_EVERY === 0) {
@@ -37352,18 +37797,18 @@ var LINE_CHUNK_SIZE = 64 * 1024;
37352
37797
  var READ_RANGE_YIELD_EVERY_BYTES = 256 * 1024;
37353
37798
 
37354
37799
  // src/files/read-file/resolve-file-path.ts
37355
- import fs34 from "node:fs";
37800
+ import fs35 from "node:fs";
37356
37801
  async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeRoot()) {
37357
37802
  const resolved = ensureUnderCwd(relativePath, sessionParentPath);
37358
37803
  if (!resolved) return { error: "Path is outside working directory" };
37359
37804
  let real;
37360
37805
  try {
37361
- real = await fs34.promises.realpath(resolved);
37806
+ real = await fs35.promises.realpath(resolved);
37362
37807
  } catch {
37363
37808
  real = resolved;
37364
37809
  }
37365
37810
  try {
37366
- const stat2 = await fs34.promises.stat(real);
37811
+ const stat2 = await fs35.promises.stat(real);
37367
37812
  if (!stat2.isFile()) return { error: "Not a file" };
37368
37813
  return real;
37369
37814
  } catch (err) {
@@ -37372,11 +37817,11 @@ async function resolveFilePathAsync(relativePath, sessionParentPath = getBridgeR
37372
37817
  }
37373
37818
 
37374
37819
  // src/files/read-file/read-file-range-async.ts
37375
- import fs35 from "node:fs";
37820
+ import fs36 from "node:fs";
37376
37821
  import { StringDecoder } from "node:string_decoder";
37377
37822
  async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
37378
- const fileSize = (await fs35.promises.stat(filePath)).size;
37379
- const fd = await fs35.promises.open(filePath, "r");
37823
+ const fileSize = (await fs36.promises.stat(filePath)).size;
37824
+ const fd = await fs36.promises.open(filePath, "r");
37380
37825
  const bufSize = 64 * 1024;
37381
37826
  const buf = Buffer.alloc(bufSize);
37382
37827
  const decoder = new StringDecoder("utf8");
@@ -37538,11 +37983,11 @@ async function readFileRangeAsync(filePath, startLine, endLine, lineOffsetIn, li
37538
37983
  }
37539
37984
 
37540
37985
  // src/files/read-file/read-file-buffer-full-async.ts
37541
- import fs36 from "node:fs";
37986
+ import fs37 from "node:fs";
37542
37987
  var READ_CHUNK_BYTES = 256 * 1024;
37543
37988
  async function readFileBufferFullAsync(filePath) {
37544
- const stat2 = await fs36.promises.stat(filePath);
37545
- const fd = await fs36.promises.open(filePath, "r");
37989
+ const stat2 = await fs37.promises.stat(filePath);
37990
+ const fd = await fs37.promises.open(filePath, "r");
37546
37991
  const chunks = [];
37547
37992
  let position = 0;
37548
37993
  let bytesSinceYield = 0;
@@ -37649,13 +38094,13 @@ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
37649
38094
  }
37650
38095
 
37651
38096
  // src/files/handle-file-browser-search.ts
37652
- import path38 from "node:path";
38097
+ import path40 from "node:path";
37653
38098
  var SEARCH_LIMIT = 100;
37654
38099
  function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
37655
38100
  void (async () => {
37656
38101
  await yieldToEventLoop();
37657
38102
  const q = typeof msg.q === "string" ? msg.q : "";
37658
- const sessionParentPath = path38.resolve(
38103
+ const sessionParentPath = path40.resolve(
37659
38104
  sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
37660
38105
  );
37661
38106
  if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
@@ -37774,8 +38219,8 @@ function handleSkillLayoutRequest(msg, deps) {
37774
38219
  }
37775
38220
 
37776
38221
  // src/skills/install-remote-skills.ts
37777
- import fs37 from "node:fs";
37778
- import path39 from "node:path";
38222
+ import fs38 from "node:fs";
38223
+ import path41 from "node:path";
37779
38224
  function installRemoteSkills(cwd, targetDir, items) {
37780
38225
  const installed2 = [];
37781
38226
  if (!Array.isArray(items)) {
@@ -37786,15 +38231,15 @@ function installRemoteSkills(cwd, targetDir, items) {
37786
38231
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
37787
38232
  continue;
37788
38233
  }
37789
- const skillDir = path39.join(cwd, targetDir, item.skillName);
38234
+ const skillDir = path41.join(cwd, targetDir, item.skillName);
37790
38235
  for (const f of item.files) {
37791
38236
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
37792
- const dest = path39.join(skillDir, f.path);
37793
- fs37.mkdirSync(path39.dirname(dest), { recursive: true });
38237
+ const dest = path41.join(skillDir, f.path);
38238
+ fs38.mkdirSync(path41.dirname(dest), { recursive: true });
37794
38239
  if (f.text !== void 0) {
37795
- fs37.writeFileSync(dest, f.text, "utf8");
38240
+ fs38.writeFileSync(dest, f.text, "utf8");
37796
38241
  } else if (f.base64) {
37797
- fs37.writeFileSync(dest, Buffer.from(f.base64, "base64"));
38242
+ fs38.writeFileSync(dest, Buffer.from(f.base64, "base64"));
37798
38243
  }
37799
38244
  }
37800
38245
  installed2.push({
@@ -37952,7 +38397,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
37952
38397
  };
37953
38398
 
37954
38399
  // src/routing/handlers/revert-turn-snapshot.ts
37955
- import * as fs38 from "node:fs";
38400
+ import * as fs39 from "node:fs";
37956
38401
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
37957
38402
  const id = typeof msg.id === "string" ? msg.id : "";
37958
38403
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -37965,7 +38410,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
37965
38410
  const agentBase = sessionWorktreeManager.getSessionWorktreeRootForSession(sessionId) ?? getBridgeRoot();
37966
38411
  const file2 = snapshotFilePath(agentBase, turnId);
37967
38412
  try {
37968
- await fs38.promises.access(file2, fs38.constants.F_OK);
38413
+ await fs39.promises.access(file2, fs39.constants.F_OK);
37969
38414
  } catch {
37970
38415
  sendWsMessage(s, {
37971
38416
  type: "revert_turn_snapshot_result",
@@ -38007,7 +38452,7 @@ var handleDevServersConfig = (msg, deps) => {
38007
38452
  };
38008
38453
 
38009
38454
  // src/git/bridge-git-context.ts
38010
- import * as path40 from "node:path";
38455
+ import * as path42 from "node:path";
38011
38456
 
38012
38457
  // src/git/branches/get-current-branch.ts
38013
38458
  async function getCurrentBranch(repoPath) {
@@ -38057,12 +38502,12 @@ async function listRepoBranchRefs(repoPath) {
38057
38502
  // src/git/bridge-git-context.ts
38058
38503
  function folderNameForRelPath(relPath, bridgeRoot) {
38059
38504
  if (relPath === "." || relPath === "") {
38060
- return path40.basename(path40.resolve(bridgeRoot)) || "repo";
38505
+ return path42.basename(path42.resolve(bridgeRoot)) || "repo";
38061
38506
  }
38062
- return path40.basename(relPath) || relPath;
38507
+ return path42.basename(relPath) || relPath;
38063
38508
  }
38064
38509
  async function discoverGitReposForBridgeContext(bridgeRoot) {
38065
- const root = path40.resolve(bridgeRoot);
38510
+ const root = path42.resolve(bridgeRoot);
38066
38511
  if (await isGitRepoDirectory(root)) {
38067
38512
  const remoteUrl = await getRemoteOriginUrl(root);
38068
38513
  return [{ absolutePath: root, remoteUrl }];
@@ -38070,17 +38515,17 @@ async function discoverGitReposForBridgeContext(bridgeRoot) {
38070
38515
  const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
38071
38516
  const byPath = /* @__PURE__ */ new Map();
38072
38517
  for (const repo of [...deep, ...shallow]) {
38073
- byPath.set(path40.resolve(repo.absolutePath), repo);
38518
+ byPath.set(path42.resolve(repo.absolutePath), repo);
38074
38519
  }
38075
38520
  return [...byPath.values()];
38076
38521
  }
38077
38522
  async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
38078
- const bridgeResolved = path40.resolve(bridgeRoot);
38523
+ const bridgeResolved = path42.resolve(bridgeRoot);
38079
38524
  const repos = await discoverGitReposForBridgeContext(bridgeResolved);
38080
38525
  const rows = [];
38081
38526
  for (const repo of repos) {
38082
- let rel = path40.relative(bridgeResolved, repo.absolutePath);
38083
- if (rel.startsWith("..") || path40.isAbsolute(rel)) continue;
38527
+ let rel = path42.relative(bridgeResolved, repo.absolutePath);
38528
+ if (rel.startsWith("..") || path42.isAbsolute(rel)) continue;
38084
38529
  const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
38085
38530
  const currentBranch = await getCurrentBranch(repo.absolutePath);
38086
38531
  const remoteUrl = repo.remoteUrl.trim() || null;
@@ -38095,11 +38540,11 @@ async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
38095
38540
  return rows;
38096
38541
  }
38097
38542
  async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
38098
- const bridgeResolved = path40.resolve(bridgeRoot);
38543
+ const bridgeResolved = path42.resolve(bridgeRoot);
38099
38544
  const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
38100
- const repoPath = rel === "" ? bridgeResolved : path40.join(bridgeResolved, rel);
38101
- const resolved = path40.resolve(repoPath);
38102
- if (!resolved.startsWith(bridgeResolved + path40.sep) && resolved !== bridgeResolved) {
38545
+ const repoPath = rel === "" ? bridgeResolved : path42.join(bridgeResolved, rel);
38546
+ const resolved = path42.resolve(repoPath);
38547
+ if (!resolved.startsWith(bridgeResolved + path42.sep) && resolved !== bridgeResolved) {
38103
38548
  return [];
38104
38549
  }
38105
38550
  return listRepoBranchRefs(resolved);
@@ -38683,10 +39128,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
38683
39128
  }
38684
39129
 
38685
39130
  // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
38686
- import * as path42 from "node:path";
39131
+ import * as path44 from "node:path";
38687
39132
 
38688
39133
  // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
38689
- import * as path41 from "node:path";
39134
+ import * as path43 from "node:path";
38690
39135
  async function probeOneAgentTypeForCapabilities(params) {
38691
39136
  const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true, shouldContinue } = params;
38692
39137
  const canContinue = () => shouldContinue?.() !== false;
@@ -38724,7 +39169,7 @@ async function probeOneAgentTypeForCapabilities(params) {
38724
39169
  if (!canContinue()) return false;
38725
39170
  handle = await resolved.createClient({
38726
39171
  command: resolved.command,
38727
- cwd: path41.resolve(cwd),
39172
+ cwd: path43.resolve(cwd),
38728
39173
  backendAgentType: agentType,
38729
39174
  sessionMode: "agent",
38730
39175
  persistedAcpSessionId: null,
@@ -38802,7 +39247,7 @@ async function warmupAgentCapabilitiesOnConnect(params) {
38802
39247
  const { workspaceId, log: log2, getWs } = params;
38803
39248
  const isCurrent = beginAgentCapabilityWarmupRun();
38804
39249
  if (!isCurrent()) return;
38805
- const cwd = path42.resolve(getBridgeRoot());
39250
+ const cwd = path44.resolve(getBridgeRoot());
38806
39251
  async function sendBatchFromCache() {
38807
39252
  if (!isCurrent()) return;
38808
39253
  const socket = getWs();
@@ -38868,9 +39313,74 @@ async function warmupAgentCapabilitiesOnConnect(params) {
38868
39313
  })();
38869
39314
  }
38870
39315
 
38871
- // src/mcp/tools/session-history/fork-session-mcp-tools.ts
39316
+ // src/mcp/tools/session-history/fork/mcp-text-result.ts
39317
+ function mcpTextResult(text, isError = false) {
39318
+ return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
39319
+ }
39320
+
39321
+ // src/mcp/tools/session-history/fork/call-get-parent-session-turn-transcript.ts
39322
+ async function callGetParentSessionTurnTranscriptTool(ctx, cloud, childSessionId, args) {
39323
+ const turnId = typeof args.turnId === "string" ? args.turnId.trim() : "";
39324
+ if (!turnId) return mcpTextResult("turnId is required.", true);
39325
+ const transcript = await fetchCloudParentTurnTranscript({
39326
+ childSessionId,
39327
+ turnId,
39328
+ cloudApiBaseUrl: cloud.cloudApiBaseUrl,
39329
+ getCloudAccessToken: cloud.getCloudAccessToken,
39330
+ e2ee: ctx.e2ee
39331
+ });
39332
+ if (!transcript.ok) return mcpTextResult(transcript.error, true);
39333
+ return mcpTextResult(transcript.text);
39334
+ }
39335
+
39336
+ // src/mcp/tools/session-history/fork/call-list-parent-session-prompts.ts
39337
+ async function callListParentSessionPromptsTool(ctx, cloud, childSessionId, parentSessionId) {
39338
+ const detail = await fetchCloudParentSessionPrompts({
39339
+ childSessionId,
39340
+ cloudApiBaseUrl: cloud.cloudApiBaseUrl,
39341
+ getCloudAccessToken: cloud.getCloudAccessToken,
39342
+ e2ee: ctx.e2ee
39343
+ });
39344
+ if (!detail.ok) return mcpTextResult(detail.error, true);
39345
+ const turns = Array.isArray(detail.data.turns) ? detail.data.turns : [];
39346
+ const payload = turns.map((t) => ({
39347
+ turnId: typeof t.turnId === "string" ? t.turnId : "",
39348
+ title: typeof t.title === "string" ? t.title : null,
39349
+ status: typeof t.status === "string" ? t.status : null,
39350
+ promptText: typeof t.promptText === "string" ? t.promptText : null
39351
+ }));
39352
+ return mcpTextResult(JSON.stringify({ parentSessionId, turns: payload }, null, 2));
39353
+ }
39354
+
39355
+ // src/mcp/tools/session-history/fork/tool-names.ts
38872
39356
  var LIST_PARENT_SESSION_PROMPTS_TOOL = "list_parent_session_prompts";
38873
39357
  var GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL = "get_parent_session_turn_transcript";
39358
+
39359
+ // src/mcp/tools/session-history/fork/call-fork-session-mcp-tool.ts
39360
+ async function callForkSessionMcpTool(ctx, cloud, name, args) {
39361
+ const childSessionId = ctx.sessionId.trim();
39362
+ if (!childSessionId) {
39363
+ return mcpTextResult("sessionId is required.", true);
39364
+ }
39365
+ const parentResolved = await resolveParentSessionIdForChild({
39366
+ childSessionId,
39367
+ cloudApiBaseUrl: cloud.cloudApiBaseUrl,
39368
+ getCloudAccessToken: cloud.getCloudAccessToken
39369
+ });
39370
+ if (!parentResolved.ok) {
39371
+ return mcpTextResult(parentResolved.error, true);
39372
+ }
39373
+ const parentSessionId = parentResolved.parentSessionId;
39374
+ if (name === LIST_PARENT_SESSION_PROMPTS_TOOL) {
39375
+ return callListParentSessionPromptsTool(ctx, cloud, childSessionId, parentSessionId);
39376
+ }
39377
+ if (name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL) {
39378
+ return callGetParentSessionTurnTranscriptTool(ctx, cloud, childSessionId, args);
39379
+ }
39380
+ return mcpTextResult(`Unknown fork session tool: ${name}`, true);
39381
+ }
39382
+
39383
+ // src/mcp/tools/session-history/fork/tool-definitions.ts
38874
39384
  var SESSION_ID_SCHEMA = {
38875
39385
  type: "string",
38876
39386
  description: "Id of this forked (child) session \u2014 not the parent session id. Required on every tool call."
@@ -38900,57 +39410,11 @@ var FORK_SESSION_MCP_TOOL_DEFINITIONS = [
38900
39410
  }
38901
39411
  }
38902
39412
  ];
38903
- function textResult(text, isError = false) {
38904
- return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
38905
- }
39413
+
39414
+ // src/mcp/tools/session-history/fork/is-fork-session-mcp-tool-name.ts
38906
39415
  function isForkSessionMcpToolName(name) {
38907
39416
  return name === LIST_PARENT_SESSION_PROMPTS_TOOL || name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL;
38908
39417
  }
38909
- async function callForkSessionMcpTool(ctx, cloud, name, args) {
38910
- const childSessionId = ctx.sessionId.trim();
38911
- if (!childSessionId) {
38912
- return textResult("sessionId is required.", true);
38913
- }
38914
- const parentResolved = await resolveParentSessionIdForChild({
38915
- childSessionId,
38916
- cloudApiBaseUrl: cloud.cloudApiBaseUrl,
38917
- getCloudAccessToken: cloud.getCloudAccessToken
38918
- });
38919
- if (!parentResolved.ok) {
38920
- return textResult(parentResolved.error, true);
38921
- }
38922
- const parentSessionId = parentResolved.parentSessionId;
38923
- if (name === LIST_PARENT_SESSION_PROMPTS_TOOL) {
38924
- const detail = await fetchCloudParentSessionPrompts({
38925
- childSessionId,
38926
- cloudApiBaseUrl: cloud.cloudApiBaseUrl,
38927
- getCloudAccessToken: cloud.getCloudAccessToken
38928
- });
38929
- if (!detail.ok) return textResult(detail.error, true);
38930
- const turns = Array.isArray(detail.data.turns) ? detail.data.turns : [];
38931
- const payload = turns.map((t) => ({
38932
- turnId: typeof t.turnId === "string" ? t.turnId : "",
38933
- title: typeof t.title === "string" ? t.title : null,
38934
- status: typeof t.status === "string" ? t.status : null,
38935
- promptText: typeof t.promptText === "string" ? t.promptText : null
38936
- }));
38937
- return textResult(JSON.stringify({ parentSessionId, turns: payload }, null, 2));
38938
- }
38939
- if (name === GET_PARENT_SESSION_TURN_TRANSCRIPT_TOOL) {
38940
- const turnId = typeof args.turnId === "string" ? args.turnId.trim() : "";
38941
- if (!turnId) return textResult("turnId is required.", true);
38942
- const transcript = await fetchCloudParentTurnTranscript({
38943
- childSessionId,
38944
- turnId,
38945
- cloudApiBaseUrl: cloud.cloudApiBaseUrl,
38946
- getCloudAccessToken: cloud.getCloudAccessToken,
38947
- e2ee: ctx.e2ee
38948
- });
38949
- if (!transcript.ok) return textResult(transcript.error, true);
38950
- return textResult(transcript.text);
38951
- }
38952
- return textResult(`Unknown fork session tool: ${name}`, true);
38953
- }
38954
39418
 
38955
39419
  // src/mcp/bridge-mcp-tools.ts
38956
39420
  function requireCloud(shared) {
@@ -39207,8 +39671,8 @@ async function createBridgeConnection(options) {
39207
39671
  getCloudAccessToken: () => tokens.accessToken
39208
39672
  };
39209
39673
  const identifyReportedPaths = {
39210
- bridgeRootPath: path43.resolve(getBridgeRoot()),
39211
- worktreesRootPath: path43.resolve(worktreesRootPath)
39674
+ bridgeRootPath: path45.resolve(getBridgeRoot()),
39675
+ worktreesRootPath: path45.resolve(worktreesRootPath)
39212
39676
  };
39213
39677
  const { connect } = createMainBridgeWebSocketLifecycle({
39214
39678
  state,