@buildautomaton/cli 0.1.38 → 0.1.39

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
@@ -4065,8 +4065,8 @@ var init_parseUtil = __esm({
4065
4065
  init_errors();
4066
4066
  init_en();
4067
4067
  makeIssue = (params) => {
4068
- const { data, path: path41, errorMaps, issueData } = params;
4069
- const fullPath = [...path41, ...issueData.path || []];
4068
+ const { data, path: path44, errorMaps, issueData } = params;
4069
+ const fullPath = [...path44, ...issueData.path || []];
4070
4070
  const fullIssue = {
4071
4071
  ...issueData,
4072
4072
  path: fullPath
@@ -4374,11 +4374,11 @@ var init_types = __esm({
4374
4374
  init_parseUtil();
4375
4375
  init_util();
4376
4376
  ParseInputLazyPath = class {
4377
- constructor(parent, value, path41, key) {
4377
+ constructor(parent, value, path44, key) {
4378
4378
  this._cachedPath = [];
4379
4379
  this.parent = parent;
4380
4380
  this.data = value;
4381
- this._path = path41;
4381
+ this._path = path44;
4382
4382
  this._key = key;
4383
4383
  }
4384
4384
  get path() {
@@ -7993,10 +7993,10 @@ function assignProp(target, prop, value) {
7993
7993
  configurable: true
7994
7994
  });
7995
7995
  }
7996
- function getElementAtPath(obj, path41) {
7997
- if (!path41)
7996
+ function getElementAtPath(obj, path44) {
7997
+ if (!path44)
7998
7998
  return obj;
7999
- return path41.reduce((acc, key) => acc?.[key], obj);
7999
+ return path44.reduce((acc, key) => acc?.[key], obj);
8000
8000
  }
8001
8001
  function promiseAllObject(promisesObj) {
8002
8002
  const keys = Object.keys(promisesObj);
@@ -8245,11 +8245,11 @@ function aborted(x, startIndex = 0) {
8245
8245
  }
8246
8246
  return false;
8247
8247
  }
8248
- function prefixIssues(path41, issues) {
8248
+ function prefixIssues(path44, issues) {
8249
8249
  return issues.map((iss) => {
8250
8250
  var _a2;
8251
8251
  (_a2 = iss).path ?? (_a2.path = []);
8252
- iss.path.unshift(path41);
8252
+ iss.path.unshift(path44);
8253
8253
  return iss;
8254
8254
  });
8255
8255
  }
@@ -8438,7 +8438,7 @@ function treeifyError(error40, _mapper) {
8438
8438
  return issue2.message;
8439
8439
  };
8440
8440
  const result = { errors: [] };
8441
- const processError = (error41, path41 = []) => {
8441
+ const processError = (error41, path44 = []) => {
8442
8442
  var _a2, _b;
8443
8443
  for (const issue2 of error41.issues) {
8444
8444
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -8448,7 +8448,7 @@ function treeifyError(error40, _mapper) {
8448
8448
  } else if (issue2.code === "invalid_element") {
8449
8449
  processError({ issues: issue2.issues }, issue2.path);
8450
8450
  } else {
8451
- const fullpath = [...path41, ...issue2.path];
8451
+ const fullpath = [...path44, ...issue2.path];
8452
8452
  if (fullpath.length === 0) {
8453
8453
  result.errors.push(mapper(issue2));
8454
8454
  continue;
@@ -8478,9 +8478,9 @@ function treeifyError(error40, _mapper) {
8478
8478
  processError(error40);
8479
8479
  return result;
8480
8480
  }
8481
- function toDotPath(path41) {
8481
+ function toDotPath(path44) {
8482
8482
  const segs = [];
8483
- for (const seg of path41) {
8483
+ for (const seg of path44) {
8484
8484
  if (typeof seg === "number")
8485
8485
  segs.push(`[${seg}]`);
8486
8486
  else if (typeof seg === "symbol")
@@ -20943,8 +20943,8 @@ var init_acp = __esm({
20943
20943
  this.#requestHandler = requestHandler;
20944
20944
  this.#notificationHandler = notificationHandler;
20945
20945
  this.#stream = stream;
20946
- this.#closedPromise = new Promise((resolve18) => {
20947
- this.#abortController.signal.addEventListener("abort", () => resolve18());
20946
+ this.#closedPromise = new Promise((resolve20) => {
20947
+ this.#abortController.signal.addEventListener("abort", () => resolve20());
20948
20948
  });
20949
20949
  this.#receive();
20950
20950
  }
@@ -21093,8 +21093,8 @@ var init_acp = __esm({
21093
21093
  }
21094
21094
  async sendRequest(method, params) {
21095
21095
  const id = this.#nextRequestId++;
21096
- const responsePromise = new Promise((resolve18, reject) => {
21097
- this.#pendingResponses.set(id, { resolve: resolve18, reject });
21096
+ const responsePromise = new Promise((resolve20, reject) => {
21097
+ this.#pendingResponses.set(id, { resolve: resolve20, reject });
21098
21098
  });
21099
21099
  await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
21100
21100
  return responsePromise;
@@ -21963,10 +21963,10 @@ var require_src2 = __commonJS({
21963
21963
  var fs_1 = __require("fs");
21964
21964
  var debug_1 = __importDefault(require_src());
21965
21965
  var log2 = debug_1.default("@kwsites/file-exists");
21966
- function check2(path41, isFile, isDirectory) {
21967
- log2(`checking %s`, path41);
21966
+ function check2(path44, isFile, isDirectory) {
21967
+ log2(`checking %s`, path44);
21968
21968
  try {
21969
- const stat2 = fs_1.statSync(path41);
21969
+ const stat2 = fs_1.statSync(path44);
21970
21970
  if (stat2.isFile() && isFile) {
21971
21971
  log2(`[OK] path represents a file`);
21972
21972
  return true;
@@ -21986,8 +21986,8 @@ var require_src2 = __commonJS({
21986
21986
  throw e;
21987
21987
  }
21988
21988
  }
21989
- function exists2(path41, type = exports.READABLE) {
21990
- return check2(path41, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
21989
+ function exists2(path44, type = exports.READABLE) {
21990
+ return check2(path44, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
21991
21991
  }
21992
21992
  exports.exists = exists2;
21993
21993
  exports.FILE = 1;
@@ -22794,9 +22794,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
22794
22794
  const rawPath = typeof o.path === "string" ? o.path.trim() : "";
22795
22795
  const summary = typeof o.summary === "string" ? o.summary.trim() : "";
22796
22796
  if (!rawPath || !summary) continue;
22797
- const path41 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
22798
- if (!path41) continue;
22799
- rows.push({ path: path41, summary: clampSummaryToAtMostTwoLines(summary) });
22797
+ const path44 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
22798
+ if (!path44) continue;
22799
+ rows.push({ path: path44, summary: clampSummaryToAtMostTwoLines(summary) });
22800
22800
  }
22801
22801
  return rows;
22802
22802
  }
@@ -23442,11 +23442,11 @@ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText,
23442
23442
  // src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
23443
23443
  function awaitSdkStdioPermissionRequestHandshake(params) {
23444
23444
  const { requestId, paramsRecord, pending, onRequest } = params;
23445
- return new Promise((resolve18) => {
23446
- pending.set(requestId, { resolve: resolve18, params: paramsRecord });
23445
+ return new Promise((resolve20) => {
23446
+ pending.set(requestId, { resolve: resolve20, params: paramsRecord });
23447
23447
  if (onRequest == null) {
23448
23448
  pending.delete(requestId);
23449
- resolve18({ outcome: { outcome: "denied" } });
23449
+ resolve20({ outcome: { outcome: "denied" } });
23450
23450
  return;
23451
23451
  }
23452
23452
  try {
@@ -23512,7 +23512,7 @@ async function createSdkStdioAcpClient(options) {
23512
23512
  child.once("close", (code, signal) => {
23513
23513
  onAgentSubprocessExit?.({ code, signal });
23514
23514
  });
23515
- return new Promise((resolve18, reject) => {
23515
+ return new Promise((resolve20, reject) => {
23516
23516
  let initSettled = false;
23517
23517
  const settleReject = (err) => {
23518
23518
  if (initSettled) return;
@@ -23526,7 +23526,7 @@ async function createSdkStdioAcpClient(options) {
23526
23526
  const settleResolve = (handle) => {
23527
23527
  if (initSettled) return;
23528
23528
  initSettled = true;
23529
- resolve18(handle);
23529
+ resolve20(handle);
23530
23530
  };
23531
23531
  child.on("error", (err) => {
23532
23532
  settleReject(new Error(formatSpawnError(err, command[0])));
@@ -23746,7 +23746,7 @@ async function proxyToLocal(request) {
23746
23746
  };
23747
23747
  const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
23748
23748
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
23749
- const once = await new Promise((resolve18) => {
23749
+ const once = await new Promise((resolve20) => {
23750
23750
  const req = mod.request(opts, (res) => {
23751
23751
  const chunks = [];
23752
23752
  res.on("data", (c) => chunks.push(c));
@@ -23757,7 +23757,7 @@ async function proxyToLocal(request) {
23757
23757
  if (typeof v === "string") headers[k] = v;
23758
23758
  else if (Array.isArray(v) && v[0]) headers[k] = v[0];
23759
23759
  }
23760
- resolve18({
23760
+ resolve20({
23761
23761
  id: request.id,
23762
23762
  statusCode: res.statusCode ?? 0,
23763
23763
  headers,
@@ -23766,7 +23766,7 @@ async function proxyToLocal(request) {
23766
23766
  });
23767
23767
  });
23768
23768
  req.on("error", (err) => {
23769
- resolve18({
23769
+ resolve20({
23770
23770
  id: request.id,
23771
23771
  statusCode: 0,
23772
23772
  headers: {},
@@ -23848,8 +23848,8 @@ function randomSecret() {
23848
23848
  }
23849
23849
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
23850
23850
  }
23851
- async function requestPreviewApi(port, secret, method, path41, body) {
23852
- const url2 = `http://127.0.0.1:${port}${path41}`;
23851
+ async function requestPreviewApi(port, secret, method, path44, body) {
23852
+ const url2 = `http://127.0.0.1:${port}${path44}`;
23853
23853
  const headers = {
23854
23854
  [PREVIEW_SECRET_HEADER]: secret,
23855
23855
  "Content-Type": "application/json"
@@ -23861,7 +23861,7 @@ async function requestPreviewApi(port, secret, method, path41, body) {
23861
23861
  });
23862
23862
  const data = await res.json().catch(() => ({}));
23863
23863
  if (!res.ok) {
23864
- throw new Error(data?.error ?? `Preview API ${method} ${path41}: ${res.status}`);
23864
+ throw new Error(data?.error ?? `Preview API ${method} ${path44}: ${res.status}`);
23865
23865
  }
23866
23866
  return data;
23867
23867
  }
@@ -24076,7 +24076,7 @@ function installBridgeProcessResilience() {
24076
24076
  }
24077
24077
 
24078
24078
  // src/cli-version.ts
24079
- var CLI_VERSION = "0.1.38".length > 0 ? "0.1.38" : "0.0.0-dev";
24079
+ var CLI_VERSION = "0.1.39".length > 0 ? "0.1.39" : "0.0.0-dev";
24080
24080
 
24081
24081
  // src/connection/heartbeat/constants.ts
24082
24082
  var BRIDGE_APP_HEARTBEAT_INTERVAL_MS = 1e4;
@@ -24517,14 +24517,14 @@ var baseOpen = async (options) => {
24517
24517
  }
24518
24518
  const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
24519
24519
  if (options.wait) {
24520
- return new Promise((resolve18, reject) => {
24520
+ return new Promise((resolve20, reject) => {
24521
24521
  subprocess.once("error", reject);
24522
24522
  subprocess.once("close", (exitCode) => {
24523
24523
  if (!options.allowNonzeroExitCode && exitCode > 0) {
24524
24524
  reject(new Error(`Exited with code ${exitCode}`));
24525
24525
  return;
24526
24526
  }
24527
- resolve18(subprocess);
24527
+ resolve20(subprocess);
24528
24528
  });
24529
24529
  });
24530
24530
  }
@@ -25013,8 +25013,8 @@ function runPendingAuth(options) {
25013
25013
  let hasOpenedBrowser = false;
25014
25014
  let resolved = false;
25015
25015
  let resolveAuth;
25016
- const authPromise = new Promise((resolve18) => {
25017
- resolveAuth = resolve18;
25016
+ const authPromise = new Promise((resolve20) => {
25017
+ resolveAuth = resolve20;
25018
25018
  });
25019
25019
  let reconnectAttempt = 0;
25020
25020
  const signInQuiet = createEmptyReconnectQuietSlot();
@@ -25165,7 +25165,7 @@ import sqliteWasm from "node-sqlite3-wasm";
25165
25165
 
25166
25166
  // src/runtime/yield-to-event-loop.ts
25167
25167
  function yieldToEventLoop() {
25168
- return new Promise((resolve18) => setImmediate(resolve18));
25168
+ return new Promise((resolve20) => setImmediate(resolve20));
25169
25169
  }
25170
25170
 
25171
25171
  // src/sqlite/cli-sqlite-paths.ts
@@ -25730,8 +25730,8 @@ function pathspec(...paths) {
25730
25730
  cache.set(key, paths);
25731
25731
  return key;
25732
25732
  }
25733
- function isPathSpec(path41) {
25734
- return path41 instanceof String && cache.has(path41);
25733
+ function isPathSpec(path44) {
25734
+ return path44 instanceof String && cache.has(path44);
25735
25735
  }
25736
25736
  function toPaths(pathSpec) {
25737
25737
  return cache.get(pathSpec) || [];
@@ -25820,8 +25820,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
25820
25820
  function forEachLineWithContent(input, callback) {
25821
25821
  return toLinesWithContent(input, true).map((line) => callback(line));
25822
25822
  }
25823
- function folderExists(path41) {
25824
- return (0, import_file_exists.exists)(path41, import_file_exists.FOLDER);
25823
+ function folderExists(path44) {
25824
+ return (0, import_file_exists.exists)(path44, import_file_exists.FOLDER);
25825
25825
  }
25826
25826
  function append(target, item) {
25827
25827
  if (Array.isArray(target)) {
@@ -26225,8 +26225,8 @@ function checkIsRepoRootTask() {
26225
26225
  commands,
26226
26226
  format: "utf-8",
26227
26227
  onError,
26228
- parser(path41) {
26229
- return /^\.(git)?$/.test(path41.trim());
26228
+ parser(path44) {
26229
+ return /^\.(git)?$/.test(path44.trim());
26230
26230
  }
26231
26231
  };
26232
26232
  }
@@ -26660,11 +26660,11 @@ function parseGrep(grep) {
26660
26660
  const paths = /* @__PURE__ */ new Set();
26661
26661
  const results = {};
26662
26662
  forEachLineWithContent(grep, (input) => {
26663
- const [path41, line, preview] = input.split(NULL);
26664
- paths.add(path41);
26665
- (results[path41] = results[path41] || []).push({
26663
+ const [path44, line, preview] = input.split(NULL);
26664
+ paths.add(path44);
26665
+ (results[path44] = results[path44] || []).push({
26666
26666
  line: asNumber(line),
26667
- path: path41,
26667
+ path: path44,
26668
26668
  preview
26669
26669
  });
26670
26670
  });
@@ -27429,14 +27429,14 @@ var init_hash_object = __esm2({
27429
27429
  init_task();
27430
27430
  }
27431
27431
  });
27432
- function parseInit(bare, path41, text) {
27432
+ function parseInit(bare, path44, text) {
27433
27433
  const response = String(text).trim();
27434
27434
  let result;
27435
27435
  if (result = initResponseRegex.exec(response)) {
27436
- return new InitSummary(bare, path41, false, result[1]);
27436
+ return new InitSummary(bare, path44, false, result[1]);
27437
27437
  }
27438
27438
  if (result = reInitResponseRegex.exec(response)) {
27439
- return new InitSummary(bare, path41, true, result[1]);
27439
+ return new InitSummary(bare, path44, true, result[1]);
27440
27440
  }
27441
27441
  let gitDir = "";
27442
27442
  const tokens = response.split(" ");
@@ -27447,7 +27447,7 @@ function parseInit(bare, path41, text) {
27447
27447
  break;
27448
27448
  }
27449
27449
  }
27450
- return new InitSummary(bare, path41, /^re/i.test(response), gitDir);
27450
+ return new InitSummary(bare, path44, /^re/i.test(response), gitDir);
27451
27451
  }
27452
27452
  var InitSummary;
27453
27453
  var initResponseRegex;
@@ -27456,9 +27456,9 @@ var init_InitSummary = __esm2({
27456
27456
  "src/lib/responses/InitSummary.ts"() {
27457
27457
  "use strict";
27458
27458
  InitSummary = class {
27459
- constructor(bare, path41, existing, gitDir) {
27459
+ constructor(bare, path44, existing, gitDir) {
27460
27460
  this.bare = bare;
27461
- this.path = path41;
27461
+ this.path = path44;
27462
27462
  this.existing = existing;
27463
27463
  this.gitDir = gitDir;
27464
27464
  }
@@ -27470,7 +27470,7 @@ var init_InitSummary = __esm2({
27470
27470
  function hasBareCommand(command) {
27471
27471
  return command.includes(bareCommand);
27472
27472
  }
27473
- function initTask(bare = false, path41, customArgs) {
27473
+ function initTask(bare = false, path44, customArgs) {
27474
27474
  const commands = ["init", ...customArgs];
27475
27475
  if (bare && !hasBareCommand(commands)) {
27476
27476
  commands.splice(1, 0, bareCommand);
@@ -27479,7 +27479,7 @@ function initTask(bare = false, path41, customArgs) {
27479
27479
  commands,
27480
27480
  format: "utf-8",
27481
27481
  parser(text) {
27482
- return parseInit(commands.includes("--bare"), path41, text);
27482
+ return parseInit(commands.includes("--bare"), path44, text);
27483
27483
  }
27484
27484
  };
27485
27485
  }
@@ -28295,12 +28295,12 @@ var init_FileStatusSummary = __esm2({
28295
28295
  "use strict";
28296
28296
  fromPathRegex = /^(.+)\0(.+)$/;
28297
28297
  FileStatusSummary = class {
28298
- constructor(path41, index, working_dir) {
28299
- this.path = path41;
28298
+ constructor(path44, index, working_dir) {
28299
+ this.path = path44;
28300
28300
  this.index = index;
28301
28301
  this.working_dir = working_dir;
28302
28302
  if (index === "R" || working_dir === "R") {
28303
- const detail = fromPathRegex.exec(path41) || [null, path41, path41];
28303
+ const detail = fromPathRegex.exec(path44) || [null, path44, path44];
28304
28304
  this.from = detail[2] || "";
28305
28305
  this.path = detail[1] || "";
28306
28306
  }
@@ -28331,14 +28331,14 @@ function splitLine(result, lineStr) {
28331
28331
  default:
28332
28332
  return;
28333
28333
  }
28334
- function data(index, workingDir, path41) {
28334
+ function data(index, workingDir, path44) {
28335
28335
  const raw = `${index}${workingDir}`;
28336
28336
  const handler = parsers6.get(raw);
28337
28337
  if (handler) {
28338
- handler(result, path41);
28338
+ handler(result, path44);
28339
28339
  }
28340
28340
  if (raw !== "##" && raw !== "!!") {
28341
- result.files.push(new FileStatusSummary(path41, index, workingDir));
28341
+ result.files.push(new FileStatusSummary(path44, index, workingDir));
28342
28342
  }
28343
28343
  }
28344
28344
  }
@@ -28647,9 +28647,9 @@ var init_simple_git_api = __esm2({
28647
28647
  next
28648
28648
  );
28649
28649
  }
28650
- hashObject(path41, write) {
28650
+ hashObject(path44, write) {
28651
28651
  return this._runTask(
28652
- hashObjectTask(path41, write === true),
28652
+ hashObjectTask(path44, write === true),
28653
28653
  trailingFunctionArgument(arguments)
28654
28654
  );
28655
28655
  }
@@ -29002,8 +29002,8 @@ var init_branch = __esm2({
29002
29002
  }
29003
29003
  });
29004
29004
  function toPath(input) {
29005
- const path41 = input.trim().replace(/^["']|["']$/g, "");
29006
- return path41 && normalize2(path41);
29005
+ const path44 = input.trim().replace(/^["']|["']$/g, "");
29006
+ return path44 && normalize2(path44);
29007
29007
  }
29008
29008
  var parseCheckIgnore;
29009
29009
  var init_CheckIgnore = __esm2({
@@ -29317,8 +29317,8 @@ __export2(sub_module_exports, {
29317
29317
  subModuleTask: () => subModuleTask,
29318
29318
  updateSubModuleTask: () => updateSubModuleTask
29319
29319
  });
29320
- function addSubModuleTask(repo, path41) {
29321
- return subModuleTask(["add", repo, path41]);
29320
+ function addSubModuleTask(repo, path44) {
29321
+ return subModuleTask(["add", repo, path44]);
29322
29322
  }
29323
29323
  function initSubModuleTask(customArgs) {
29324
29324
  return subModuleTask(["init", ...customArgs]);
@@ -29651,8 +29651,8 @@ var require_git = __commonJS2({
29651
29651
  }
29652
29652
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
29653
29653
  };
29654
- Git2.prototype.submoduleAdd = function(repo, path41, then) {
29655
- return this._runTask(addSubModuleTask2(repo, path41), trailingFunctionArgument2(arguments));
29654
+ Git2.prototype.submoduleAdd = function(repo, path44, then) {
29655
+ return this._runTask(addSubModuleTask2(repo, path44), trailingFunctionArgument2(arguments));
29656
29656
  };
29657
29657
  Git2.prototype.submoduleUpdate = function(args, then) {
29658
29658
  return this._runTask(
@@ -30289,8 +30289,8 @@ async function runGitTask(fn) {
30289
30289
  }
30290
30290
  async function yieldToEventLoop2() {
30291
30291
  throwIfGitShutdownRequested();
30292
- await new Promise((resolve18) => {
30293
- setImmediate(resolve18);
30292
+ await new Promise((resolve20) => {
30293
+ setImmediate(resolve20);
30294
30294
  });
30295
30295
  throwIfGitShutdownRequested();
30296
30296
  }
@@ -30615,9 +30615,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
30615
30615
  // src/agents/acp/put-summarize-change-summaries.ts
30616
30616
  async function putEncryptedChangeSummaryRows(params) {
30617
30617
  const base = params.apiBaseUrl.replace(/\/+$/, "");
30618
- const entries = params.rows.map(({ path: path41, summary }) => {
30618
+ const entries = params.rows.map(({ path: path44, summary }) => {
30619
30619
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
30620
- return { path: path41, summary: JSON.stringify(enc) };
30620
+ return { path: path44, summary: JSON.stringify(enc) };
30621
30621
  });
30622
30622
  const res = await fetch(
30623
30623
  `${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -31171,7 +31171,7 @@ async function createCursorAcpClient(options) {
31171
31171
  logDebug,
31172
31172
  getStderrText: () => stderrCapture.getText()
31173
31173
  };
31174
- return new Promise((resolve18, reject) => {
31174
+ return new Promise((resolve20, reject) => {
31175
31175
  child.on("error", (err) => {
31176
31176
  child.kill();
31177
31177
  reject(new Error(formatSpawnError2(err, command[0])));
@@ -31347,7 +31347,7 @@ async function createCursorAcpClient(options) {
31347
31347
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
31348
31348
  });
31349
31349
  const sessionId = established.sessionId;
31350
- resolve18({
31350
+ resolve20({
31351
31351
  sessionId,
31352
31352
  async sendPrompt(prompt, options2) {
31353
31353
  const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
@@ -32593,266 +32593,65 @@ async function createAcpManager(options) {
32593
32593
  };
32594
32594
  }
32595
32595
 
32596
- // src/worktrees/session-worktree-manager.ts
32597
- import * as path25 from "node:path";
32598
- import os8 from "node:os";
32599
-
32600
- // src/worktrees/prepare-new-session-worktrees.ts
32601
- import * as fs17 from "node:fs";
32602
- import * as path20 from "node:path";
32603
-
32604
- // src/git/worktrees/worktree-add.ts
32605
- async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
32606
- const mainGit = cliSimpleGit(mainRepoPath);
32607
- await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
32608
- }
32609
-
32610
- // src/worktrees/worktree-layout-file.ts
32611
- import * as fs16 from "node:fs";
32612
- import * as path19 from "node:path";
32613
- import os7 from "node:os";
32614
- var LAYOUT_FILENAME = "worktree-launcher-layout.json";
32615
- function defaultWorktreeLayoutPath() {
32616
- return path19.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
32617
- }
32618
- function normalizeLoadedLayout(raw) {
32619
- if (raw && typeof raw === "object" && "launcherCwds" in raw) {
32620
- const j = raw;
32621
- if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
32622
- }
32623
- return { launcherCwds: [] };
32624
- }
32625
- function loadWorktreeLayout() {
32626
- try {
32627
- const p = defaultWorktreeLayoutPath();
32628
- if (!fs16.existsSync(p)) return { launcherCwds: [] };
32629
- const raw = JSON.parse(fs16.readFileSync(p, "utf8"));
32630
- return normalizeLoadedLayout(raw);
32631
- } catch {
32632
- return { launcherCwds: [] };
32633
- }
32634
- }
32635
- function saveWorktreeLayout(layout) {
32636
- try {
32637
- const dir = path19.dirname(defaultWorktreeLayoutPath());
32638
- fs16.mkdirSync(dir, { recursive: true });
32639
- fs16.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
32640
- } catch {
32641
- }
32642
- }
32643
- function baseNameSafe(pathString) {
32644
- return path19.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
32645
- }
32646
- function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
32647
- const norm = path19.resolve(bridgeRootPath2);
32648
- const existing = layout.launcherCwds.find((e) => path19.resolve(e.absolutePath) === norm);
32649
- return existing?.dirName;
32650
- }
32651
- function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
32652
- const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
32653
- if (existing) return existing;
32654
- const norm = path19.resolve(bridgeRootPath2);
32655
- const base = baseNameSafe(norm);
32656
- const used = new Set(layout.launcherCwds.map((e) => e.dirName));
32657
- let name = base;
32658
- let n = 2;
32659
- while (used.has(name)) {
32660
- name = `${base}-${n}`;
32661
- n += 1;
32662
- }
32663
- layout.launcherCwds.push({ absolutePath: norm, dirName: name });
32664
- saveWorktreeLayout(layout);
32665
- return name;
32666
- }
32667
-
32668
- // src/worktrees/prepare-new-session-worktrees.ts
32669
- async function prepareNewSessionWorktrees(options) {
32670
- const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
32671
- const bridgeResolved = path20.resolve(bridgeRoot);
32672
- const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
32673
- const bridgeKeyDir = path20.join(worktreesRootPath, cwdKey);
32674
- const sessionDir = path20.join(bridgeKeyDir, sessionId);
32675
- const repos = await discoverGitReposUnderRoot(bridgeResolved);
32676
- if (repos.length === 0) {
32677
- log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
32678
- return null;
32679
- }
32680
- const branch = `session-${sessionId}`;
32681
- const worktreePaths = [];
32682
- fs17.mkdirSync(sessionDir, { recursive: true });
32683
- for (const repo of repos) {
32684
- let rel = path20.relative(bridgeResolved, repo.absolutePath);
32685
- if (rel.startsWith("..") || path20.isAbsolute(rel)) continue;
32686
- const relNorm = rel === "" ? "." : rel;
32687
- const wtPath = relNorm === "." ? sessionDir : path20.join(sessionDir, relNorm);
32688
- if (relNorm !== ".") {
32689
- fs17.mkdirSync(path20.dirname(wtPath), { recursive: true });
32690
- }
32691
- try {
32692
- await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
32693
- log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
32694
- worktreePaths.push(wtPath);
32695
- } catch (e) {
32696
- log2(
32697
- `[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
32698
- );
32699
- }
32700
- }
32701
- if (worktreePaths.length === 0) return null;
32702
- return {
32703
- worktreePaths,
32704
- sessionParentPath: sessionDir,
32705
- workingTreeRelRoot: sessionDir
32706
- };
32707
- }
32708
-
32709
- // src/git/branches/rename-branch.ts
32710
- async function gitRenameCurrentBranch(repoDir, newName) {
32711
- const g = cliSimpleGit(repoDir);
32712
- await g.raw(["branch", "-m", newName]);
32713
- }
32714
-
32715
- // src/worktrees/rename-session-worktree-branches.ts
32716
- async function renameSessionWorktreeBranches(paths, newBranch, log2) {
32717
- const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
32718
- for (const wt of paths) {
32719
- try {
32720
- await gitRenameCurrentBranch(wt, safe);
32721
- log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
32722
- } catch (e) {
32723
- log2(
32724
- `[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
32725
- );
32726
- }
32727
- }
32728
- }
32729
-
32730
- // src/worktrees/remove-session-worktrees.ts
32731
- import * as fs20 from "node:fs";
32732
-
32733
- // src/git/worktrees/worktree-remove.ts
32734
- import * as fs19 from "node:fs";
32735
-
32736
- // src/git/worktrees/resolve-main-repo-from-git-file.ts
32737
- import * as fs18 from "node:fs";
32738
- import * as path21 from "node:path";
32739
- function resolveMainRepoFromWorktreeGitFile(wt) {
32740
- const gitDirFile = path21.join(wt, ".git");
32741
- if (!fs18.existsSync(gitDirFile) || !fs18.statSync(gitDirFile).isFile()) return "";
32742
- const first2 = fs18.readFileSync(gitDirFile, "utf8").trim();
32743
- const m = first2.match(/^gitdir:\s*(.+)$/im);
32744
- if (!m) return "";
32745
- const gitWorktreePath = path21.resolve(wt, m[1].trim());
32746
- const gitDir = path21.dirname(path21.dirname(gitWorktreePath));
32747
- return path21.dirname(gitDir);
32748
- }
32596
+ // src/git/changes/types.ts
32597
+ var MAX_PATCH_CHARS = 35e4;
32749
32598
 
32750
- // src/git/worktrees/worktree-remove.ts
32751
- async function gitWorktreeRemoveForce(worktreePath) {
32752
- const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
32753
- if (mainRepo) {
32754
- await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
32755
- } else {
32756
- fs19.rmSync(worktreePath, { recursive: true, force: true });
32757
- }
32599
+ // src/git/changes/lib/repo-format.ts
32600
+ function posixJoinDirFile(dir, file2) {
32601
+ const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
32602
+ const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
32603
+ return d ? `${d}/${f}` : f;
32758
32604
  }
32759
-
32760
- // src/worktrees/remove-session-worktrees.ts
32761
- async function removeSessionWorktrees(paths, log2) {
32762
- for (const wt of paths) {
32605
+ function formatRepoShortTitle(remoteUrl, repoRelPath) {
32606
+ const u = remoteUrl.trim();
32607
+ if (u) {
32763
32608
  try {
32764
- await gitWorktreeRemoveForce(wt);
32765
- log2(`[worktrees] Removed worktree ${wt}`);
32766
- } catch (e) {
32767
- log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
32768
- try {
32769
- fs20.rmSync(wt, { recursive: true, force: true });
32770
- } catch {
32609
+ if (u.startsWith("git@")) {
32610
+ const colon = u.indexOf(":");
32611
+ if (colon > 0) {
32612
+ const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
32613
+ if (pathPart.includes("/")) return pathPart;
32614
+ }
32615
+ } else {
32616
+ const parsed = new URL(u);
32617
+ const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
32618
+ const parts = p.split("/").filter(Boolean);
32619
+ if (parts.length >= 2) {
32620
+ return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
32621
+ }
32622
+ if (parts.length === 1) return parts[0];
32771
32623
  }
32624
+ } catch {
32772
32625
  }
32773
32626
  }
32774
- }
32775
-
32776
- // src/git/changes/lib/parse-git-status.ts
32777
- function parseNameStatusLines(lines) {
32778
- const m = /* @__PURE__ */ new Map();
32779
- for (const line of lines) {
32780
- if (!line.trim()) continue;
32781
- const tabParts = line.split(" ");
32782
- if (tabParts.length < 2) continue;
32783
- const status = tabParts[0].trim();
32784
- const code = status[0];
32785
- if (code === "A") {
32786
- m.set(tabParts[tabParts.length - 1], "added");
32787
- } else if (code === "D") {
32788
- m.set(tabParts[tabParts.length - 1], "removed");
32789
- } else if (code === "R" || code === "C") {
32790
- if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
32791
- } else if (code === "M" || code === "U" || code === "T") {
32792
- m.set(tabParts[tabParts.length - 1], "modified");
32793
- }
32794
- }
32795
- return m;
32796
- }
32797
- function parseNumstatFirstLine(line) {
32798
- const parts = line.split(" ");
32799
- if (parts.length < 3) return null;
32800
- const [a, d] = parts;
32801
- const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
32802
- const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
32803
- return { additions, deletions };
32804
- }
32805
- function parseNumstat(lines) {
32806
- const m = /* @__PURE__ */ new Map();
32807
- for (const line of lines) {
32808
- if (!line.trim()) continue;
32809
- const parts = line.split(" ");
32810
- if (parts.length < 3) continue;
32811
- const [a, d, p] = parts;
32812
- const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
32813
- const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
32814
- m.set(p, { additions, deletions });
32627
+ if (repoRelPath && repoRelPath !== ".") {
32628
+ const segments = repoRelPath.split("/").filter(Boolean);
32629
+ const last2 = segments[segments.length - 1];
32630
+ if (last2) return last2;
32815
32631
  }
32816
- return m;
32632
+ return "Repository";
32817
32633
  }
32818
- async function numstatFromGitNoIndex(g, pathInRepo) {
32819
- const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
32634
+ function formatRemoteDisplayLabel(remoteUrl) {
32635
+ const u = remoteUrl.trim();
32636
+ if (!u) return "";
32637
+ let hostPath = u;
32820
32638
  try {
32821
- const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
32822
- const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
32823
- return parseNumstatFirstLine(first2);
32639
+ if (u.startsWith("git@")) {
32640
+ const rest = u.slice("git@".length);
32641
+ const slash = rest.indexOf(":");
32642
+ if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
32643
+ } else {
32644
+ const parsed = new URL(u);
32645
+ hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
32646
+ }
32824
32647
  } catch {
32825
- return null;
32826
- }
32827
- }
32828
-
32829
- // src/git/changes/lib/working-tree-changed-path-count.ts
32830
- function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
32831
- const kindByPath = parseNameStatusLines(nameStatusLines);
32832
- const numByPath = parseNumstat(numstatLines);
32833
- const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
32834
- for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
32835
- paths.add(p);
32648
+ hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
32836
32649
  }
32837
- return paths.size;
32650
+ return `origin \xB7 ${hostPath}`;
32838
32651
  }
32839
32652
 
32840
- // src/git/changes/count-working-tree-changed-files.ts
32841
- async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
32842
- return runGitTask(async () => {
32843
- const g = cliSimpleGit(repoGitCwd);
32844
- const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
32845
- g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
32846
- g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
32847
- g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
32848
- ]);
32849
- return workingTreeChangedPathCount(
32850
- String(nameStatusRaw).split("\n"),
32851
- String(numstatRaw).split("\n"),
32852
- String(untrackedRaw).split("\n")
32853
- );
32854
- });
32855
- }
32653
+ // src/git/changes/get-working-tree-change-repo-details.ts
32654
+ import * as path20 from "node:path";
32856
32655
 
32857
32656
  // src/git/commits/resolve-remote-tracking.ts
32858
32657
  async function tryConfigGet(g, key) {
@@ -32952,96 +32751,6 @@ async function commitsAheadOfRemoteTracking(repoDir) {
32952
32751
  }
32953
32752
  }
32954
32753
 
32955
- // src/git/status/working-tree-status.ts
32956
- async function getRepoWorkingTreeStatus(repoDir) {
32957
- return runGitTask(async () => {
32958
- const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
32959
- const hasUncommittedChanges = uncommittedFileCount > 0;
32960
- const ahead = await commitsAheadOfRemoteTracking(repoDir);
32961
- return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
32962
- });
32963
- }
32964
- async function aggregateSessionPathsWorkingTreeStatus(paths) {
32965
- let hasUncommittedChanges = false;
32966
- let hasUnpushedCommits = false;
32967
- let uncommittedFileCount = 0;
32968
- await forEachWithGitYield(paths, async (p) => {
32969
- const s = await getRepoWorkingTreeStatus(p);
32970
- uncommittedFileCount += s.uncommittedFileCount;
32971
- if (s.hasUncommittedChanges) hasUncommittedChanges = true;
32972
- if (s.hasUnpushedCommits) hasUnpushedCommits = true;
32973
- });
32974
- return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
32975
- }
32976
- async function pushAheadOfUpstreamForPaths(paths) {
32977
- await forEachWithGitYield(paths, async (p) => {
32978
- const g = cliSimpleGit(p);
32979
- const ahead = await commitsAheadOfRemoteTracking(p);
32980
- if (ahead <= 0) return;
32981
- await g.push();
32982
- });
32983
- }
32984
-
32985
- // src/git/changes/types.ts
32986
- var MAX_PATCH_CHARS = 35e4;
32987
-
32988
- // src/git/changes/lib/repo-format.ts
32989
- function posixJoinDirFile(dir, file2) {
32990
- const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
32991
- const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
32992
- return d ? `${d}/${f}` : f;
32993
- }
32994
- function formatRepoShortTitle(remoteUrl, repoRelPath) {
32995
- const u = remoteUrl.trim();
32996
- if (u) {
32997
- try {
32998
- if (u.startsWith("git@")) {
32999
- const colon = u.indexOf(":");
33000
- if (colon > 0) {
33001
- const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
33002
- if (pathPart.includes("/")) return pathPart;
33003
- }
33004
- } else {
33005
- const parsed = new URL(u);
33006
- const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
33007
- const parts = p.split("/").filter(Boolean);
33008
- if (parts.length >= 2) {
33009
- return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
33010
- }
33011
- if (parts.length === 1) return parts[0];
33012
- }
33013
- } catch {
33014
- }
33015
- }
33016
- if (repoRelPath && repoRelPath !== ".") {
33017
- const segments = repoRelPath.split("/").filter(Boolean);
33018
- const last2 = segments[segments.length - 1];
33019
- if (last2) return last2;
33020
- }
33021
- return "Repository";
33022
- }
33023
- function formatRemoteDisplayLabel(remoteUrl) {
33024
- const u = remoteUrl.trim();
33025
- if (!u) return "";
33026
- let hostPath = u;
33027
- try {
33028
- if (u.startsWith("git@")) {
33029
- const rest = u.slice("git@".length);
33030
- const slash = rest.indexOf(":");
33031
- if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
33032
- } else {
33033
- const parsed = new URL(u);
33034
- hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
33035
- }
33036
- } catch {
33037
- hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
33038
- }
33039
- return `origin \xB7 ${hostPath}`;
33040
- }
33041
-
33042
- // src/git/changes/get-working-tree-change-repo-details.ts
33043
- import * as path23 from "node:path";
33044
-
33045
32754
  // src/git/commits/lib/parse-log-lines.ts
33046
32755
  function parseLogShaDateSubjectLines(raw) {
33047
32756
  const out = [];
@@ -33112,6 +32821,59 @@ async function listRecentCommits(repoDir, limitInput) {
33112
32821
  }
33113
32822
  }
33114
32823
 
32824
+ // src/git/changes/lib/parse-git-status.ts
32825
+ function parseNameStatusLines(lines) {
32826
+ const m = /* @__PURE__ */ new Map();
32827
+ for (const line of lines) {
32828
+ if (!line.trim()) continue;
32829
+ const tabParts = line.split(" ");
32830
+ if (tabParts.length < 2) continue;
32831
+ const status = tabParts[0].trim();
32832
+ const code = status[0];
32833
+ if (code === "A") {
32834
+ m.set(tabParts[tabParts.length - 1], "added");
32835
+ } else if (code === "D") {
32836
+ m.set(tabParts[tabParts.length - 1], "removed");
32837
+ } else if (code === "R" || code === "C") {
32838
+ if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
32839
+ } else if (code === "M" || code === "U" || code === "T") {
32840
+ m.set(tabParts[tabParts.length - 1], "modified");
32841
+ }
32842
+ }
32843
+ return m;
32844
+ }
32845
+ function parseNumstatFirstLine(line) {
32846
+ const parts = line.split(" ");
32847
+ if (parts.length < 3) return null;
32848
+ const [a, d] = parts;
32849
+ const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
32850
+ const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
32851
+ return { additions, deletions };
32852
+ }
32853
+ function parseNumstat(lines) {
32854
+ const m = /* @__PURE__ */ new Map();
32855
+ for (const line of lines) {
32856
+ if (!line.trim()) continue;
32857
+ const parts = line.split(" ");
32858
+ if (parts.length < 3) continue;
32859
+ const [a, d, p] = parts;
32860
+ const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
32861
+ const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
32862
+ m.set(p, { additions, deletions });
32863
+ }
32864
+ return m;
32865
+ }
32866
+ async function numstatFromGitNoIndex(g, pathInRepo) {
32867
+ const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
32868
+ try {
32869
+ const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
32870
+ const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
32871
+ return parseNumstatFirstLine(first2);
32872
+ } catch {
32873
+ return null;
32874
+ }
32875
+ }
32876
+
33115
32877
  // src/git/changes/lib/patch-truncate.ts
33116
32878
  function truncatePatch(s) {
33117
32879
  if (s.length <= MAX_PATCH_CHARS) return s;
@@ -33177,8 +32939,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
33177
32939
  }
33178
32940
 
33179
32941
  // src/git/changes/list-changed-files-for-repo.ts
33180
- import * as fs22 from "node:fs";
33181
- import * as path22 from "node:path";
32942
+ import * as fs17 from "node:fs";
32943
+ import * as path19 from "node:path";
33182
32944
 
33183
32945
  // src/git/changes/lib/count-lines.ts
33184
32946
  import { createReadStream } from "node:fs";
@@ -33202,7 +32964,7 @@ async function countTextFileLines(filePath) {
33202
32964
  }
33203
32965
 
33204
32966
  // src/git/changes/hydrate-patch.ts
33205
- import * as fs21 from "node:fs";
32967
+ import * as fs16 from "node:fs";
33206
32968
  var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
33207
32969
  var MAX_HYDRATE_LINES_PER_GAP = 8e3;
33208
32970
  var MAX_HYDRATE_LINES_PER_FILE = 8e4;
@@ -33217,7 +32979,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
33217
32979
  }
33218
32980
  async function readWorktreeFileLines(filePath) {
33219
32981
  try {
33220
- const raw = await fs21.promises.readFile(filePath, "utf8");
32982
+ const raw = await fs16.promises.readFile(filePath, "utf8");
33221
32983
  return raw.split(/\r?\n/);
33222
32984
  } catch {
33223
32985
  return null;
@@ -33342,7 +33104,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
33342
33104
  const rows = [];
33343
33105
  await forEachWithGitYield([...paths], async (pathInRepo) => {
33344
33106
  const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
33345
- const repoFilePath = path22.join(repoGitCwd, pathInRepo);
33107
+ const repoFilePath = path19.join(repoGitCwd, pathInRepo);
33346
33108
  const nums = numByPath.get(pathInRepo);
33347
33109
  let additions = nums?.additions ?? 0;
33348
33110
  let deletions = nums?.deletions ?? 0;
@@ -33355,7 +33117,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
33355
33117
  deletions = fromGit.deletions;
33356
33118
  } else {
33357
33119
  try {
33358
- const st = await fs22.promises.stat(repoFilePath);
33120
+ const st = await fs17.promises.stat(repoFilePath);
33359
33121
  if (st.isFile()) additions = await countTextFileLines(repoFilePath);
33360
33122
  else additions = 0;
33361
33123
  } catch {
@@ -33381,7 +33143,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
33381
33143
  } else {
33382
33144
  pathInRepo = row.pathRelLauncher;
33383
33145
  }
33384
- const filePath = path22.join(repoGitCwd, pathInRepo);
33146
+ const filePath = path19.join(repoGitCwd, pathInRepo);
33385
33147
  let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
33386
33148
  if (patch) {
33387
33149
  patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
@@ -33397,8 +33159,8 @@ function normRepoRel(p) {
33397
33159
  return x === "" ? "." : x;
33398
33160
  }
33399
33161
  async function getWorkingTreeChangeRepoDetails(options) {
33400
- const bridgeRoot = path23.resolve(getBridgeRoot());
33401
- const sessionWtRoot = options.sessionWorktreeRootPath ? path23.resolve(options.sessionWorktreeRootPath) : null;
33162
+ const bridgeRoot = path20.resolve(getBridgeRoot());
33163
+ const sessionWtRoot = options.sessionWorktreeRootPath ? path20.resolve(options.sessionWorktreeRootPath) : null;
33402
33164
  const legacyNested = options.legacyRepoNestedSessionLayout === true;
33403
33165
  const out = [];
33404
33166
  const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
@@ -33413,7 +33175,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
33413
33175
  for (let i = 0; i < options.commitTargetPaths.length; i++) {
33414
33176
  if (i > 0) await yieldToEventLoop2();
33415
33177
  const target = options.commitTargetPaths[i];
33416
- const t = path23.resolve(target);
33178
+ const t = path20.resolve(target);
33417
33179
  if (!await isGitRepoDirectory(t)) continue;
33418
33180
  const g = cliSimpleGit(t);
33419
33181
  let branch = "HEAD";
@@ -33426,8 +33188,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
33426
33188
  const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
33427
33189
  let repoRelPath;
33428
33190
  if (sessionWtRoot) {
33429
- const anchor = legacyNested ? path23.dirname(t) : t;
33430
- const relNorm = path23.relative(sessionWtRoot, anchor);
33191
+ const anchor = legacyNested ? path20.dirname(t) : t;
33192
+ const relNorm = path20.relative(sessionWtRoot, anchor);
33431
33193
  repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
33432
33194
  } else {
33433
33195
  let top = t;
@@ -33436,8 +33198,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
33436
33198
  } catch {
33437
33199
  top = t;
33438
33200
  }
33439
- const rel = path23.relative(bridgeRoot, path23.resolve(top)).replace(/\\/g, "/") || ".";
33440
- repoRelPath = rel.startsWith("..") ? path23.basename(path23.resolve(top)) : rel;
33201
+ const rel = path20.relative(bridgeRoot, path20.resolve(top)).replace(/\\/g, "/") || ".";
33202
+ repoRelPath = rel.startsWith("..") ? path20.basename(path20.resolve(top)) : rel;
33441
33203
  }
33442
33204
  const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
33443
33205
  if (filter && norm !== filter) continue;
@@ -33469,6 +33231,64 @@ async function getWorkingTreeChangeRepoDetails(options) {
33469
33231
  return out;
33470
33232
  }
33471
33233
 
33234
+ // src/git/changes/lib/working-tree-changed-path-count.ts
33235
+ function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
33236
+ const kindByPath = parseNameStatusLines(nameStatusLines);
33237
+ const numByPath = parseNumstat(numstatLines);
33238
+ const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
33239
+ for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
33240
+ paths.add(p);
33241
+ }
33242
+ return paths.size;
33243
+ }
33244
+
33245
+ // src/git/changes/count-working-tree-changed-files.ts
33246
+ async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
33247
+ return runGitTask(async () => {
33248
+ const g = cliSimpleGit(repoGitCwd);
33249
+ const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
33250
+ g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
33251
+ g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
33252
+ g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
33253
+ ]);
33254
+ return workingTreeChangedPathCount(
33255
+ String(nameStatusRaw).split("\n"),
33256
+ String(numstatRaw).split("\n"),
33257
+ String(untrackedRaw).split("\n")
33258
+ );
33259
+ });
33260
+ }
33261
+
33262
+ // src/git/status/working-tree-status.ts
33263
+ async function getRepoWorkingTreeStatus(repoDir) {
33264
+ return runGitTask(async () => {
33265
+ const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
33266
+ const hasUncommittedChanges = uncommittedFileCount > 0;
33267
+ const ahead = await commitsAheadOfRemoteTracking(repoDir);
33268
+ return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
33269
+ });
33270
+ }
33271
+ async function aggregateSessionPathsWorkingTreeStatus(paths) {
33272
+ let hasUncommittedChanges = false;
33273
+ let hasUnpushedCommits = false;
33274
+ let uncommittedFileCount = 0;
33275
+ await forEachWithGitYield(paths, async (p) => {
33276
+ const s = await getRepoWorkingTreeStatus(p);
33277
+ uncommittedFileCount += s.uncommittedFileCount;
33278
+ if (s.hasUncommittedChanges) hasUncommittedChanges = true;
33279
+ if (s.hasUnpushedCommits) hasUnpushedCommits = true;
33280
+ });
33281
+ return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
33282
+ }
33283
+ async function pushAheadOfUpstreamForPaths(paths) {
33284
+ await forEachWithGitYield(paths, async (p) => {
33285
+ const g = cliSimpleGit(p);
33286
+ const ahead = await commitsAheadOfRemoteTracking(p);
33287
+ if (ahead <= 0) return;
33288
+ await g.push();
33289
+ });
33290
+ }
33291
+
33472
33292
  // src/git/branches/commit-and-push.ts
33473
33293
  async function gitCommitAllIfDirty(repoDir, message, options) {
33474
33294
  const g = cliSimpleGit(repoDir);
@@ -33506,12 +33326,137 @@ async function commitSessionWorktrees(options) {
33506
33326
  }
33507
33327
  }
33508
33328
 
33329
+ // src/worktrees/remove-session-worktrees.ts
33330
+ import * as fs20 from "node:fs";
33331
+
33332
+ // src/git/worktrees/worktree-remove.ts
33333
+ import * as fs19 from "node:fs";
33334
+
33335
+ // src/git/worktrees/resolve-main-repo-from-git-file.ts
33336
+ import * as fs18 from "node:fs";
33337
+ import * as path21 from "node:path";
33338
+ function resolveMainRepoFromWorktreeGitFile(wt) {
33339
+ const gitDirFile = path21.join(wt, ".git");
33340
+ if (!fs18.existsSync(gitDirFile) || !fs18.statSync(gitDirFile).isFile()) return "";
33341
+ const first2 = fs18.readFileSync(gitDirFile, "utf8").trim();
33342
+ const m = first2.match(/^gitdir:\s*(.+)$/im);
33343
+ if (!m) return "";
33344
+ const gitWorktreePath = path21.resolve(wt, m[1].trim());
33345
+ const gitDir = path21.dirname(path21.dirname(gitWorktreePath));
33346
+ return path21.dirname(gitDir);
33347
+ }
33348
+
33349
+ // src/git/worktrees/worktree-remove.ts
33350
+ async function gitWorktreeRemoveForce(worktreePath) {
33351
+ const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
33352
+ if (mainRepo) {
33353
+ await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
33354
+ } else {
33355
+ fs19.rmSync(worktreePath, { recursive: true, force: true });
33356
+ }
33357
+ }
33358
+
33359
+ // src/worktrees/remove-session-worktrees.ts
33360
+ async function removeSessionWorktrees(paths, log2) {
33361
+ for (const wt of paths) {
33362
+ try {
33363
+ await gitWorktreeRemoveForce(wt);
33364
+ log2(`[worktrees] Removed worktree ${wt}`);
33365
+ } catch (e) {
33366
+ log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
33367
+ try {
33368
+ fs20.rmSync(wt, { recursive: true, force: true });
33369
+ } catch {
33370
+ }
33371
+ }
33372
+ }
33373
+ }
33374
+
33375
+ // src/git/branches/rename-branch.ts
33376
+ async function gitRenameCurrentBranch(repoDir, newName) {
33377
+ const g = cliSimpleGit(repoDir);
33378
+ await g.raw(["branch", "-m", newName]);
33379
+ }
33380
+
33381
+ // src/worktrees/rename-session-worktree-branches.ts
33382
+ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
33383
+ const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
33384
+ for (const wt of paths) {
33385
+ try {
33386
+ await gitRenameCurrentBranch(wt, safe);
33387
+ log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
33388
+ } catch (e) {
33389
+ log2(
33390
+ `[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
33391
+ );
33392
+ }
33393
+ }
33394
+ }
33395
+
33396
+ // src/worktrees/worktree-layout-file.ts
33397
+ import * as fs21 from "node:fs";
33398
+ import * as path22 from "node:path";
33399
+ import os7 from "node:os";
33400
+ var LAYOUT_FILENAME = "worktree-launcher-layout.json";
33401
+ function defaultWorktreeLayoutPath() {
33402
+ return path22.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
33403
+ }
33404
+ function normalizeLoadedLayout(raw) {
33405
+ if (raw && typeof raw === "object" && "launcherCwds" in raw) {
33406
+ const j = raw;
33407
+ if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
33408
+ }
33409
+ return { launcherCwds: [] };
33410
+ }
33411
+ function loadWorktreeLayout() {
33412
+ try {
33413
+ const p = defaultWorktreeLayoutPath();
33414
+ if (!fs21.existsSync(p)) return { launcherCwds: [] };
33415
+ const raw = JSON.parse(fs21.readFileSync(p, "utf8"));
33416
+ return normalizeLoadedLayout(raw);
33417
+ } catch {
33418
+ return { launcherCwds: [] };
33419
+ }
33420
+ }
33421
+ function saveWorktreeLayout(layout) {
33422
+ try {
33423
+ const dir = path22.dirname(defaultWorktreeLayoutPath());
33424
+ fs21.mkdirSync(dir, { recursive: true });
33425
+ fs21.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
33426
+ } catch {
33427
+ }
33428
+ }
33429
+ function baseNameSafe(pathString) {
33430
+ return path22.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
33431
+ }
33432
+ function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
33433
+ const norm = path22.resolve(bridgeRootPath2);
33434
+ const existing = layout.launcherCwds.find((e) => path22.resolve(e.absolutePath) === norm);
33435
+ return existing?.dirName;
33436
+ }
33437
+ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
33438
+ const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
33439
+ if (existing) return existing;
33440
+ const norm = path22.resolve(bridgeRootPath2);
33441
+ const base = baseNameSafe(norm);
33442
+ const used = new Set(layout.launcherCwds.map((e) => e.dirName));
33443
+ let name = base;
33444
+ let n = 2;
33445
+ while (used.has(name)) {
33446
+ name = `${base}-${n}`;
33447
+ n += 1;
33448
+ }
33449
+ layout.launcherCwds.push({ absolutePath: norm, dirName: name });
33450
+ saveWorktreeLayout(layout);
33451
+ return name;
33452
+ }
33453
+
33509
33454
  // src/worktrees/discover-session-worktree-on-disk.ts
33510
- import * as fs23 from "node:fs";
33511
- import * as path24 from "node:path";
33455
+ import * as fs22 from "node:fs";
33456
+ import * as path23 from "node:path";
33512
33457
  function isGitDir(dirPath) {
33513
33458
  try {
33514
- return fs23.existsSync(path24.join(dirPath, ".git"));
33459
+ return fs22.existsSync(path23.join(dirPath, ".git"));
33515
33460
  } catch {
33516
33461
  return false;
33517
33462
  }
@@ -33520,23 +33465,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
33520
33465
  const out = [];
33521
33466
  const walk = (dir) => {
33522
33467
  if (isGitDir(dir)) {
33523
- out.push(path24.resolve(dir));
33468
+ out.push(path23.resolve(dir));
33524
33469
  return;
33525
33470
  }
33526
33471
  let entries;
33527
33472
  try {
33528
- entries = fs23.readdirSync(dir, { withFileTypes: true });
33473
+ entries = fs22.readdirSync(dir, { withFileTypes: true });
33529
33474
  } catch {
33530
33475
  return;
33531
33476
  }
33532
33477
  for (const e of entries) {
33533
33478
  if (e.name.startsWith(".")) continue;
33534
- const full = path24.join(dir, e.name);
33479
+ const full = path23.join(dir, e.name);
33535
33480
  if (!e.isDirectory()) continue;
33536
33481
  walk(full);
33537
33482
  }
33538
33483
  };
33539
- walk(path24.resolve(rootPath));
33484
+ walk(path23.resolve(rootPath));
33540
33485
  return [...new Set(out)];
33541
33486
  }
33542
33487
  function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
@@ -33545,16 +33490,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
33545
33490
  if (depth > maxDepth) return;
33546
33491
  let entries;
33547
33492
  try {
33548
- entries = fs23.readdirSync(dir, { withFileTypes: true });
33493
+ entries = fs22.readdirSync(dir, { withFileTypes: true });
33549
33494
  } catch {
33550
33495
  return;
33551
33496
  }
33552
33497
  for (const e of entries) {
33553
33498
  if (e.name.startsWith(".")) continue;
33554
- const full = path24.join(dir, e.name);
33499
+ const full = path23.join(dir, e.name);
33555
33500
  if (!e.isDirectory()) continue;
33556
33501
  if (e.name === sessionId) {
33557
- if (isGitDir(full)) out.push(path24.resolve(full));
33502
+ if (isGitDir(full)) out.push(path23.resolve(full));
33558
33503
  } else {
33559
33504
  walk(full, depth + 1);
33560
33505
  }
@@ -33566,14 +33511,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
33566
33511
  function tryBindingFromSessionDirectory(sessionDir) {
33567
33512
  let st;
33568
33513
  try {
33569
- st = fs23.statSync(sessionDir);
33514
+ st = fs22.statSync(sessionDir);
33570
33515
  } catch {
33571
33516
  return null;
33572
33517
  }
33573
33518
  if (!st.isDirectory()) return null;
33574
33519
  const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
33575
33520
  if (worktreePaths.length === 0) return null;
33576
- const abs = path24.resolve(sessionDir);
33521
+ const abs = path23.resolve(sessionDir);
33577
33522
  return {
33578
33523
  sessionParentPath: abs,
33579
33524
  workingTreeRelRoot: abs,
@@ -33583,20 +33528,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
33583
33528
  function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
33584
33529
  const sid = sessionId.trim();
33585
33530
  if (!sid) return null;
33586
- const hintR = path24.resolve(checkoutPath);
33531
+ const hintR = path23.resolve(checkoutPath);
33587
33532
  let best = null;
33588
- let cur = path24.dirname(hintR);
33533
+ let cur = path23.dirname(hintR);
33589
33534
  for (let i = 0; i < 40; i++) {
33590
33535
  const paths = collectWorktreeRootsNamed(cur, sid, 24);
33591
- if (paths.some((p) => path24.resolve(p) === hintR)) {
33592
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path24.resolve(paths[0]);
33536
+ if (paths.some((p) => path23.resolve(p) === hintR)) {
33537
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path23.resolve(paths[0]);
33593
33538
  best = {
33594
- sessionParentPath: path24.resolve(isolated),
33595
- workingTreeRelRoot: path24.resolve(cur),
33596
- repoCheckoutPaths: paths.map((p) => path24.resolve(p))
33539
+ sessionParentPath: path23.resolve(isolated),
33540
+ workingTreeRelRoot: path23.resolve(cur),
33541
+ repoCheckoutPaths: paths.map((p) => path23.resolve(p))
33597
33542
  };
33598
33543
  }
33599
- const next = path24.dirname(cur);
33544
+ const next = path23.dirname(cur);
33600
33545
  if (next === cur) break;
33601
33546
  cur = next;
33602
33547
  }
@@ -33604,33 +33549,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
33604
33549
  }
33605
33550
  function discoverSessionWorktreeOnDisk(options) {
33606
33551
  const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
33607
- if (!sessionId.trim() || !fs23.existsSync(worktreesRootPath)) return null;
33552
+ if (!sessionId.trim() || !fs22.existsSync(worktreesRootPath)) return null;
33608
33553
  const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
33609
33554
  const keys = [];
33610
33555
  if (preferredKey) keys.push(preferredKey);
33611
33556
  try {
33612
- for (const name of fs23.readdirSync(worktreesRootPath)) {
33557
+ for (const name of fs22.readdirSync(worktreesRootPath)) {
33613
33558
  if (name.startsWith(".")) continue;
33614
- const p = path24.join(worktreesRootPath, name);
33615
- if (!fs23.statSync(p).isDirectory()) continue;
33559
+ const p = path23.join(worktreesRootPath, name);
33560
+ if (!fs22.statSync(p).isDirectory()) continue;
33616
33561
  if (name !== preferredKey) keys.push(name);
33617
33562
  }
33618
33563
  } catch {
33619
33564
  return null;
33620
33565
  }
33621
33566
  for (const key of keys) {
33622
- const layoutRoot = path24.join(worktreesRootPath, key);
33623
- if (!fs23.existsSync(layoutRoot) || !fs23.statSync(layoutRoot).isDirectory()) continue;
33624
- const sessionDir = path24.join(layoutRoot, sessionId);
33567
+ const layoutRoot = path23.join(worktreesRootPath, key);
33568
+ if (!fs22.existsSync(layoutRoot) || !fs22.statSync(layoutRoot).isDirectory()) continue;
33569
+ const sessionDir = path23.join(layoutRoot, sessionId);
33625
33570
  const nested = tryBindingFromSessionDirectory(sessionDir);
33626
33571
  if (nested) return nested;
33627
33572
  const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
33628
33573
  if (legacyPaths.length > 0) {
33629
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
33574
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path23.resolve(legacyPaths[0]);
33630
33575
  return {
33631
- sessionParentPath: path24.resolve(isolated),
33632
- workingTreeRelRoot: path24.resolve(layoutRoot),
33633
- repoCheckoutPaths: legacyPaths.map((p) => path24.resolve(p))
33576
+ sessionParentPath: path23.resolve(isolated),
33577
+ workingTreeRelRoot: path23.resolve(layoutRoot),
33578
+ repoCheckoutPaths: legacyPaths.map((p) => path23.resolve(p))
33634
33579
  };
33635
33580
  }
33636
33581
  }
@@ -33639,12 +33584,12 @@ function discoverSessionWorktreeOnDisk(options) {
33639
33584
  function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
33640
33585
  const sid = sessionId.trim();
33641
33586
  if (!sid) return null;
33642
- const hint = path24.resolve(sessionWorktreeRootPathOrHint);
33643
- const underHint = tryBindingFromSessionDirectory(path24.join(hint, sid));
33587
+ const hint = path23.resolve(sessionWorktreeRootPathOrHint);
33588
+ const underHint = tryBindingFromSessionDirectory(path23.join(hint, sid));
33644
33589
  if (underHint) return underHint;
33645
33590
  const direct = tryBindingFromSessionDirectory(hint);
33646
33591
  if (direct) {
33647
- if (path24.basename(hint) === sid && isGitDir(hint)) {
33592
+ if (path23.basename(hint) === sid && isGitDir(hint)) {
33648
33593
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
33649
33594
  if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
33650
33595
  return legacyFromCheckout;
@@ -33652,216 +33597,349 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
33652
33597
  }
33653
33598
  return direct;
33654
33599
  }
33655
- if (path24.basename(hint) === sid && isGitDir(hint)) {
33600
+ if (path23.basename(hint) === sid && isGitDir(hint)) {
33656
33601
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
33657
33602
  if (legacyFromCheckout) return legacyFromCheckout;
33658
33603
  }
33659
33604
  let st;
33660
33605
  try {
33661
- st = fs23.statSync(hint);
33606
+ st = fs22.statSync(hint);
33662
33607
  } catch {
33663
33608
  return null;
33664
33609
  }
33665
33610
  if (!st.isDirectory()) return null;
33666
33611
  const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
33667
33612
  if (legacyPaths.length === 0) return null;
33668
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
33613
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path23.resolve(legacyPaths[0]);
33669
33614
  return {
33670
- sessionParentPath: path24.resolve(isolated),
33615
+ sessionParentPath: path23.resolve(isolated),
33671
33616
  workingTreeRelRoot: hint,
33672
- repoCheckoutPaths: legacyPaths.map((p) => path24.resolve(p))
33617
+ repoCheckoutPaths: legacyPaths.map((p) => path23.resolve(p))
33673
33618
  };
33674
33619
  }
33675
33620
 
33676
- // src/worktrees/session-worktree-manager.ts
33621
+ // src/worktrees/manager/discover-session-binding.ts
33622
+ function discoverSessionBinding(params) {
33623
+ return discoverSessionWorktreeOnDisk({
33624
+ sessionId: params.sessionId,
33625
+ worktreesRootPath: params.worktreesRootPath,
33626
+ layout: params.layout,
33627
+ bridgeRoot: getBridgeRoot()
33628
+ });
33629
+ }
33630
+
33631
+ // src/worktrees/manager/resolve-isolated-session-parent-path.ts
33632
+ function resolveIsolatedSessionParentPath(sessionId, cache2, ensureRepoCheckoutPaths) {
33633
+ if (!sessionId) return null;
33634
+ const sid = sessionId.trim();
33635
+ const cached2 = cache2.getSessionParentPath(sid);
33636
+ if (cached2) return cached2;
33637
+ const paths = ensureRepoCheckoutPaths(sid);
33638
+ if (!paths?.length) return null;
33639
+ return resolveIsolatedSessionParentPathFromCheckouts(paths);
33640
+ }
33641
+ function ensureRepoCheckoutPathsForSession(sessionId, cache2, discover) {
33642
+ if (!sessionId?.trim()) return void 0;
33643
+ const sid = sessionId.trim();
33644
+ const cached2 = cache2.getRepoCheckoutPaths(sid);
33645
+ if (cached2?.length) return cached2;
33646
+ const disc = discover(sid);
33647
+ if (disc?.repoCheckoutPaths.length) {
33648
+ cache2.remember(sid, disc);
33649
+ return [...disc.repoCheckoutPaths];
33650
+ }
33651
+ return void 0;
33652
+ }
33653
+
33654
+ // src/worktrees/manager/resolve-commit-targets.ts
33655
+ function resolveCommitTargets(sessionId, cache2, discover) {
33656
+ const paths = cache2.getRepoCheckoutPathsRef(sessionId);
33657
+ if (paths?.length) return paths;
33658
+ const disc = discover(sessionId);
33659
+ if (disc?.repoCheckoutPaths.length) {
33660
+ cache2.remember(sessionId, disc);
33661
+ return disc.repoCheckoutPaths;
33662
+ }
33663
+ return [getBridgeRoot()];
33664
+ }
33665
+
33666
+ // src/worktrees/manager/parse-session-parent.ts
33677
33667
  function parseSessionParent(v) {
33678
33668
  if (v === "bridge_root" || v === "worktrees_root") return v;
33679
33669
  if (v === "session_worktrees_root") return "worktrees_root";
33680
33670
  return null;
33681
33671
  }
33682
- var SessionWorktreeManager = class {
33683
- worktreesRootPath;
33684
- log;
33672
+
33673
+ // src/worktrees/prepare-new-session-worktrees.ts
33674
+ import * as fs23 from "node:fs";
33675
+ import * as path24 from "node:path";
33676
+
33677
+ // src/git/worktrees/worktree-add.ts
33678
+ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef = "HEAD") {
33679
+ const mainGit = cliSimpleGit(mainRepoPath);
33680
+ const base = baseRef.trim() || "HEAD";
33681
+ await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, base]);
33682
+ }
33683
+
33684
+ // src/worktrees/prepare-new-session-worktrees.ts
33685
+ function normalizeRepoRelPath(rel) {
33686
+ return rel === "" ? "." : rel.replace(/\\/g, "/");
33687
+ }
33688
+ function resolveBaseRefForRepo(relNorm, baseBranches) {
33689
+ if (!baseBranches) return "HEAD";
33690
+ const direct = baseBranches[relNorm]?.trim();
33691
+ if (direct) return direct;
33692
+ if (relNorm !== "." && baseBranches["."]?.trim()) return baseBranches["."].trim();
33693
+ return "HEAD";
33694
+ }
33695
+ async function prepareNewSessionWorktrees(options) {
33696
+ const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
33697
+ const bridgeResolved = path24.resolve(bridgeRoot);
33698
+ const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
33699
+ const bridgeKeyDir = path24.join(worktreesRootPath, cwdKey);
33700
+ const sessionDir = path24.join(bridgeKeyDir, sessionId);
33701
+ const repos = await discoverGitReposUnderRoot(bridgeResolved);
33702
+ if (repos.length === 0) {
33703
+ log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
33704
+ return null;
33705
+ }
33706
+ const branch = `session-${sessionId}`;
33707
+ const worktreePaths = [];
33708
+ fs23.mkdirSync(sessionDir, { recursive: true });
33709
+ for (const repo of repos) {
33710
+ let rel = path24.relative(bridgeResolved, repo.absolutePath);
33711
+ if (rel.startsWith("..") || path24.isAbsolute(rel)) continue;
33712
+ const relNorm = normalizeRepoRelPath(rel === "" ? "." : rel);
33713
+ const wtPath = relNorm === "." ? sessionDir : path24.join(sessionDir, relNorm);
33714
+ if (relNorm !== ".") {
33715
+ fs23.mkdirSync(path24.dirname(wtPath), { recursive: true });
33716
+ }
33717
+ const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
33718
+ try {
33719
+ await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch, baseRef);
33720
+ log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}, base ${baseRef}).`);
33721
+ worktreePaths.push(wtPath);
33722
+ } catch (e) {
33723
+ log2(
33724
+ `[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
33725
+ );
33726
+ }
33727
+ }
33728
+ if (worktreePaths.length === 0) return null;
33729
+ return {
33730
+ worktreePaths,
33731
+ sessionParentPath: sessionDir,
33732
+ workingTreeRelRoot: sessionDir
33733
+ };
33734
+ }
33735
+
33736
+ // src/worktrees/manager/prepare-and-remember-session-worktrees.ts
33737
+ async function prepareAndRememberSessionWorktrees(params) {
33738
+ const prep = await prepareNewSessionWorktrees({
33739
+ worktreesRootPath: params.worktreesRootPath,
33740
+ bridgeRoot: getBridgeRoot(),
33741
+ sessionId: params.sessionId,
33742
+ layout: params.layout,
33743
+ log: params.log,
33744
+ ...params.worktreeBaseBranches && Object.keys(params.worktreeBaseBranches).length > 0 ? { worktreeBaseBranches: params.worktreeBaseBranches } : {}
33745
+ });
33746
+ if (!prep) return void 0;
33747
+ params.cache.remember(params.sessionId, {
33748
+ sessionParentPath: prep.sessionParentPath,
33749
+ workingTreeRelRoot: prep.workingTreeRelRoot,
33750
+ repoCheckoutPaths: prep.worktreePaths
33751
+ });
33752
+ return params.cache.getSessionParentPath(params.sessionId);
33753
+ }
33754
+
33755
+ // src/worktrees/manager/resolve-existing-session-parent-path.ts
33756
+ function resolveExistingSessionParentPath(sessionId, cache2, discover) {
33757
+ const cached2 = cache2.getSessionParentPath(sessionId);
33758
+ if (cached2) return cached2;
33759
+ const disc = discover();
33760
+ if (disc) {
33761
+ cache2.remember(sessionId, disc);
33762
+ return cache2.getSessionParentPath(sessionId);
33763
+ }
33764
+ return void 0;
33765
+ }
33766
+
33767
+ // src/worktrees/manager/resolve-explicit-session-parent-path.ts
33768
+ import * as path25 from "node:path";
33769
+ function resolveExplicitSessionParentPath(params) {
33770
+ const resolved = path25.resolve(params.parentPathRaw);
33771
+ if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
33772
+ return resolved;
33773
+ }
33774
+ const rememberAndReturn = (binding) => {
33775
+ params.cache.remember(params.sessionId, binding);
33776
+ return params.cache.getSessionParentPath(params.sessionId) ?? resolved;
33777
+ };
33778
+ const diskFirst = params.discover();
33779
+ if (diskFirst) return rememberAndReturn(diskFirst);
33780
+ const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, params.sessionId);
33781
+ if (fromRoot) return rememberAndReturn(fromRoot);
33782
+ let cur = resolved;
33783
+ for (let i = 0; i < 16; i++) {
33784
+ const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
33785
+ if (tryRoot) return rememberAndReturn(tryRoot);
33786
+ const next = path25.dirname(cur);
33787
+ if (next === cur) break;
33788
+ cur = next;
33789
+ }
33790
+ return resolved;
33791
+ }
33792
+
33793
+ // src/worktrees/manager/resolve-session-parent-path-for-prompt.ts
33794
+ async function resolveSessionParentPathForPrompt(params) {
33795
+ const { sessionId, cache: cache2, worktreesRootPath, layout, log: log2, discover, opts } = params;
33796
+ if (!sessionId) return void 0;
33797
+ const sid = sessionId.trim();
33798
+ const parentPathRaw = opts.sessionParentPath?.trim();
33799
+ if (parentPathRaw) {
33800
+ return resolveExplicitSessionParentPath({
33801
+ sessionId: sid,
33802
+ sessionParent: opts.sessionParent,
33803
+ parentPathRaw,
33804
+ cache: cache2,
33805
+ discover: () => discover(sid)
33806
+ });
33807
+ }
33808
+ const parentKind = parseSessionParent(opts.sessionParent);
33809
+ if (parentKind === "bridge_root") {
33810
+ return void 0;
33811
+ }
33812
+ if (parentKind === "worktrees_root") {
33813
+ if (!opts.isNewSession) {
33814
+ return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
33815
+ }
33816
+ return prepareAndRememberSessionWorktrees({
33817
+ cache: cache2,
33818
+ sessionId: sid,
33819
+ worktreesRootPath,
33820
+ layout,
33821
+ log: log2,
33822
+ ...opts.worktreeBaseBranches ? { worktreeBaseBranches: opts.worktreeBaseBranches } : {}
33823
+ });
33824
+ }
33825
+ if (!opts.isNewSession) {
33826
+ return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
33827
+ }
33828
+ return prepareAndRememberSessionWorktrees({
33829
+ cache: cache2,
33830
+ sessionId: sid,
33831
+ worktreesRootPath,
33832
+ layout,
33833
+ log: log2
33834
+ });
33835
+ }
33836
+
33837
+ // src/worktrees/manager/session-worktree-cache.ts
33838
+ import * as path26 from "node:path";
33839
+ var SessionWorktreeCache = class {
33685
33840
  sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
33686
33841
  sessionParentPathBySession = /* @__PURE__ */ new Map();
33687
33842
  sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
33688
- layout;
33689
- constructor(options) {
33690
- this.worktreesRootPath = options.worktreesRootPath;
33691
- this.log = options.log;
33692
- this.layout = loadWorktreeLayout();
33693
- }
33694
- rememberSessionWorktrees(sessionId, binding) {
33695
- const paths = binding.repoCheckoutPaths.map((p) => path25.resolve(p));
33843
+ remember(sessionId, binding) {
33844
+ const paths = binding.repoCheckoutPaths.map((p) => path26.resolve(p));
33696
33845
  this.sessionRepoCheckoutPaths.set(sessionId, paths);
33697
- this.sessionParentPathBySession.set(sessionId, path25.resolve(binding.sessionParentPath));
33698
- this.sessionWorkingTreeRelRootBySession.set(sessionId, path25.resolve(binding.workingTreeRelRoot));
33846
+ this.sessionParentPathBySession.set(sessionId, path26.resolve(binding.sessionParentPath));
33847
+ this.sessionWorkingTreeRelRootBySession.set(sessionId, path26.resolve(binding.workingTreeRelRoot));
33848
+ }
33849
+ clearSession(sessionId) {
33850
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
33851
+ this.sessionRepoCheckoutPaths.delete(sessionId);
33852
+ this.sessionParentPathBySession.delete(sessionId);
33853
+ this.sessionWorkingTreeRelRootBySession.delete(sessionId);
33854
+ return paths;
33699
33855
  }
33700
- sessionParentPathAfterRemember(sessionId) {
33856
+ getSessionParentPath(sessionId) {
33701
33857
  return this.sessionParentPathBySession.get(sessionId);
33702
33858
  }
33703
- tryDiscoverFromDisk(sessionId) {
33704
- return discoverSessionWorktreeOnDisk({
33705
- sessionId,
33706
- worktreesRootPath: this.worktreesRootPath,
33707
- layout: this.layout,
33708
- bridgeRoot: getBridgeRoot()
33709
- });
33859
+ getWorkingTreeRelRoot(sessionId) {
33860
+ return this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
33861
+ }
33862
+ hasSession(sessionId) {
33863
+ return this.sessionParentPathBySession.has(sessionId);
33864
+ }
33865
+ getRepoCheckoutPaths(sessionId) {
33866
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
33867
+ return paths?.length ? [...paths] : void 0;
33868
+ }
33869
+ getRepoCheckoutPathsRef(sessionId) {
33870
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
33871
+ return paths?.length ? paths : void 0;
33710
33872
  }
33711
33873
  isLegacyNestedLayout(sessionId) {
33712
33874
  const parent = this.sessionParentPathBySession.get(sessionId);
33713
33875
  const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
33714
33876
  if (!parent || !relRoot) return false;
33715
- return path25.resolve(parent) !== path25.resolve(relRoot);
33877
+ return path26.resolve(parent) !== path26.resolve(relRoot);
33878
+ }
33879
+ };
33880
+
33881
+ // src/worktrees/manager/manager.ts
33882
+ var SessionWorktreeManager = class {
33883
+ worktreesRootPath;
33884
+ log;
33885
+ cache = new SessionWorktreeCache();
33886
+ layout;
33887
+ constructor(options) {
33888
+ this.worktreesRootPath = options.worktreesRootPath;
33889
+ this.log = options.log;
33890
+ this.layout = loadWorktreeLayout();
33891
+ }
33892
+ discover(sessionId) {
33893
+ return discoverSessionBinding({
33894
+ sessionId,
33895
+ worktreesRootPath: this.worktreesRootPath,
33896
+ layout: this.layout
33897
+ });
33716
33898
  }
33717
- /**
33718
- * Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
33719
- */
33720
33899
  getIsolatedSessionParentPathForSession(sessionId) {
33721
- if (!sessionId) return null;
33722
- const sid = sessionId.trim();
33723
- const cached2 = this.sessionParentPathBySession.get(sid);
33724
- if (cached2) return path25.resolve(cached2);
33725
- const paths = this.ensureRepoCheckoutPathsForSession(sid) ?? this.getRepoCheckoutPathsForSession(sid);
33726
- if (!paths?.length) return null;
33727
- return resolveIsolatedSessionParentPathFromCheckouts(paths);
33728
- }
33729
- /**
33730
- * Resolved **session parent path** for the agent: session directory in worktrees mode,
33731
- * or `undefined` meaning use {@link getBridgeRoot}.
33732
- */
33733
- async resolveSessionParentPathForPrompt(sessionId, opts) {
33734
- if (!sessionId) return void 0;
33735
- const sid = sessionId.trim();
33736
- const parentPathRaw = opts.sessionParentPath?.trim();
33737
- if (parentPathRaw) {
33738
- const resolved = path25.resolve(parentPathRaw);
33739
- if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
33740
- const diskFirst = this.tryDiscoverFromDisk(sid);
33741
- if (diskFirst) {
33742
- this.rememberSessionWorktrees(sid, diskFirst);
33743
- return this.sessionParentPathAfterRemember(sid);
33744
- }
33745
- const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, sid);
33746
- if (fromRoot) {
33747
- this.rememberSessionWorktrees(sid, fromRoot);
33748
- return this.sessionParentPathAfterRemember(sid);
33749
- }
33750
- let cur = resolved;
33751
- for (let i = 0; i < 16; i++) {
33752
- const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, sid);
33753
- if (tryRoot) {
33754
- this.rememberSessionWorktrees(sid, tryRoot);
33755
- return this.sessionParentPathAfterRemember(sid);
33756
- }
33757
- const next = path25.dirname(cur);
33758
- if (next === cur) break;
33759
- cur = next;
33760
- }
33761
- }
33762
- return resolved;
33763
- }
33764
- const parentKind = parseSessionParent(opts.sessionParent);
33765
- if (parentKind === "bridge_root") {
33766
- return void 0;
33767
- }
33768
- if (parentKind === "worktrees_root") {
33769
- if (!opts.isNewSession) {
33770
- const cached2 = this.sessionParentPathAfterRemember(sid);
33771
- if (cached2) return cached2;
33772
- const disc = this.tryDiscoverFromDisk(sid);
33773
- if (disc) {
33774
- this.rememberSessionWorktrees(sid, disc);
33775
- return this.sessionParentPathAfterRemember(sid);
33776
- }
33777
- return void 0;
33778
- }
33779
- const prep2 = await prepareNewSessionWorktrees({
33780
- worktreesRootPath: this.worktreesRootPath,
33781
- bridgeRoot: getBridgeRoot(),
33782
- sessionId: sid,
33783
- layout: this.layout,
33784
- log: this.log
33785
- });
33786
- if (!prep2) return void 0;
33787
- this.rememberSessionWorktrees(sid, {
33788
- sessionParentPath: prep2.sessionParentPath,
33789
- workingTreeRelRoot: prep2.workingTreeRelRoot,
33790
- repoCheckoutPaths: prep2.worktreePaths
33791
- });
33792
- return this.sessionParentPathAfterRemember(sid);
33793
- }
33794
- if (!opts.isNewSession) {
33795
- const cached2 = this.sessionParentPathAfterRemember(sid);
33796
- if (cached2) return cached2;
33797
- const disc = this.tryDiscoverFromDisk(sid);
33798
- if (disc) {
33799
- this.rememberSessionWorktrees(sid, disc);
33800
- return this.sessionParentPathAfterRemember(sid);
33801
- }
33802
- return void 0;
33803
- }
33804
- const prep = await prepareNewSessionWorktrees({
33900
+ return resolveIsolatedSessionParentPath(
33901
+ sessionId,
33902
+ this.cache,
33903
+ (sid) => this.ensureRepoCheckoutPathsForSession(sid)
33904
+ );
33905
+ }
33906
+ resolveSessionParentPathForPrompt(sessionId, opts) {
33907
+ return resolveSessionParentPathForPrompt({
33908
+ sessionId,
33909
+ cache: this.cache,
33805
33910
  worktreesRootPath: this.worktreesRootPath,
33806
- bridgeRoot: getBridgeRoot(),
33807
- sessionId: sid,
33808
33911
  layout: this.layout,
33809
- log: this.log
33810
- });
33811
- if (!prep) return void 0;
33812
- this.rememberSessionWorktrees(sid, {
33813
- sessionParentPath: prep.sessionParentPath,
33814
- workingTreeRelRoot: prep.workingTreeRelRoot,
33815
- repoCheckoutPaths: prep.worktreePaths
33912
+ log: this.log,
33913
+ discover: (sid) => this.discover(sid),
33914
+ opts
33816
33915
  });
33817
- return this.sessionParentPathAfterRemember(sid);
33818
33916
  }
33819
33917
  async renameSessionBranch(sessionId, newBranch) {
33820
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
33918
+ const paths = this.cache.getRepoCheckoutPathsRef(sessionId);
33821
33919
  if (!paths?.length) return;
33822
33920
  await renameSessionWorktreeBranches(paths, newBranch, this.log);
33823
33921
  }
33824
- /** True when this session uses an isolated worktree layout (not the bridge root). */
33825
33922
  usesWorktreeSession(sessionId) {
33826
33923
  if (!sessionId) return false;
33827
- return this.sessionParentPathBySession.has(sessionId);
33924
+ return this.cache.hasSession(sessionId);
33828
33925
  }
33829
- /** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
33830
33926
  getRepoCheckoutPathsForSession(sessionId) {
33831
33927
  if (!sessionId) return void 0;
33832
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
33833
- return paths?.length ? [...paths] : void 0;
33928
+ return this.cache.getRepoCheckoutPaths(sessionId);
33834
33929
  }
33835
- /**
33836
- * Same paths as {@link getRepoCheckoutPathsForSession}, but loads from disk into memory when the CLI
33837
- * restarted or maps were not yet populated (avoids discovering every repo under the worktrees root).
33838
- */
33839
33930
  ensureRepoCheckoutPathsForSession(sessionId) {
33840
- if (!sessionId?.trim()) return void 0;
33841
- const sid = sessionId.trim();
33842
- const cached2 = this.sessionRepoCheckoutPaths.get(sid);
33843
- if (cached2?.length) return [...cached2];
33844
- const disc = this.tryDiscoverFromDisk(sid);
33845
- if (disc?.repoCheckoutPaths.length) {
33846
- this.rememberSessionWorktrees(sid, disc);
33847
- return [...disc.repoCheckoutPaths];
33848
- }
33849
- return void 0;
33931
+ return ensureRepoCheckoutPathsForSession(sessionId, this.cache, (sid) => this.discover(sid));
33850
33932
  }
33851
- /** Session parent directory when in worktrees mode; null otherwise (same as {@link getIsolatedSessionParentPathForSession} path). */
33852
33933
  getSessionWorktreeRootForSession(sessionId) {
33853
33934
  return this.getIsolatedSessionParentPathForSession(sessionId);
33854
33935
  }
33855
33936
  async removeSessionWorktrees(sessionId) {
33856
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
33857
- this.sessionRepoCheckoutPaths.delete(sessionId);
33858
- this.sessionParentPathBySession.delete(sessionId);
33859
- this.sessionWorkingTreeRelRootBySession.delete(sessionId);
33937
+ const paths = this.cache.clearSession(sessionId);
33860
33938
  if (!paths?.length) return;
33861
33939
  await removeSessionWorktrees(paths, this.log);
33862
33940
  }
33863
33941
  async commitSession(params) {
33864
- const paths = this.sessionRepoCheckoutPaths.get(params.sessionId);
33942
+ const paths = this.cache.getRepoCheckoutPathsRef(params.sessionId);
33865
33943
  const targets = paths?.length ? paths : [getBridgeRoot()];
33866
33944
  return commitSessionWorktrees({
33867
33945
  paths: targets,
@@ -33870,28 +33948,17 @@ var SessionWorktreeManager = class {
33870
33948
  push: params.push
33871
33949
  });
33872
33950
  }
33873
- resolveCommitTargets(sessionId) {
33874
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
33875
- if (paths?.length) return paths;
33876
- const disc = this.tryDiscoverFromDisk(sessionId);
33877
- if (disc?.repoCheckoutPaths.length) {
33878
- this.rememberSessionWorktrees(sessionId, disc);
33879
- return disc.repoCheckoutPaths;
33880
- }
33881
- return [getBridgeRoot()];
33882
- }
33883
33951
  async getSessionWorkingTreeStatus(sessionId) {
33884
- return aggregateSessionPathsWorkingTreeStatus(this.resolveCommitTargets(sessionId));
33952
+ return aggregateSessionPathsWorkingTreeStatus(
33953
+ resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
33954
+ );
33885
33955
  }
33886
- /** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
33887
33956
  async getSessionWorkingTreeChangeDetails(sessionId, opts) {
33888
- const targets = this.resolveCommitTargets(sessionId);
33889
- const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
33890
- const legacyNested = this.isLegacyNestedLayout(sessionId);
33957
+ const targets = resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid));
33891
33958
  return getWorkingTreeChangeRepoDetails({
33892
33959
  commitTargetPaths: targets,
33893
- sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
33894
- legacyRepoNestedSessionLayout: legacyNested,
33960
+ sessionWorktreeRootPath: this.cache.getWorkingTreeRelRoot(sessionId),
33961
+ legacyRepoNestedSessionLayout: this.cache.isLegacyNestedLayout(sessionId),
33895
33962
  repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
33896
33963
  basis: opts?.basis,
33897
33964
  recentCommitsLimit: opts?.recentCommitsLimit
@@ -33899,7 +33966,9 @@ var SessionWorktreeManager = class {
33899
33966
  }
33900
33967
  async pushSessionUpstream(sessionId) {
33901
33968
  try {
33902
- await pushAheadOfUpstreamForPaths(this.resolveCommitTargets(sessionId));
33969
+ await pushAheadOfUpstreamForPaths(
33970
+ resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
33971
+ );
33903
33972
  return { ok: true };
33904
33973
  } catch (e) {
33905
33974
  const err = e instanceof Error ? e.message : String(e);
@@ -33907,27 +33976,31 @@ var SessionWorktreeManager = class {
33907
33976
  }
33908
33977
  }
33909
33978
  };
33979
+
33980
+ // src/worktrees/manager/default-worktrees-root-path.ts
33981
+ import * as path27 from "node:path";
33982
+ import os8 from "node:os";
33910
33983
  function defaultWorktreesRootPath() {
33911
- return path25.join(os8.homedir(), ".buildautomaton", "worktrees");
33984
+ return path27.join(os8.homedir(), ".buildautomaton", "worktrees");
33912
33985
  }
33913
33986
 
33914
33987
  // src/files/watch-file-index.ts
33915
33988
  import { watch } from "node:fs";
33916
- import path30 from "node:path";
33989
+ import path32 from "node:path";
33917
33990
 
33918
33991
  // src/files/index/paths.ts
33919
- import path26 from "node:path";
33992
+ import path28 from "node:path";
33920
33993
  import crypto2 from "node:crypto";
33921
33994
  function getCwdHashForFileIndex(resolvedCwd) {
33922
- return crypto2.createHash("sha256").update(path26.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
33995
+ return crypto2.createHash("sha256").update(path28.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
33923
33996
  }
33924
33997
 
33925
33998
  // src/files/index/build-file-index.ts
33926
- import path28 from "node:path";
33999
+ import path30 from "node:path";
33927
34000
 
33928
34001
  // src/files/index/walk-workspace-tree.ts
33929
34002
  import fs24 from "node:fs";
33930
- import path27 from "node:path";
34003
+ import path29 from "node:path";
33931
34004
  var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
33932
34005
  "node_modules",
33933
34006
  "bower_components",
@@ -33956,18 +34029,18 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
33956
34029
  if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
33957
34030
  }
33958
34031
  state.n++;
33959
- const full = path27.join(dir, name);
34032
+ const full = path29.join(dir, name);
33960
34033
  let stat2;
33961
34034
  try {
33962
34035
  stat2 = await fs24.promises.stat(full);
33963
34036
  } catch {
33964
34037
  continue;
33965
34038
  }
33966
- const relative5 = path27.relative(baseDir, full).replace(/\\/g, "/");
34039
+ const relative6 = path29.relative(baseDir, full).replace(/\\/g, "/");
33967
34040
  if (stat2.isDirectory()) {
33968
34041
  await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
33969
34042
  } else if (stat2.isFile()) {
33970
- onFile(relative5);
34043
+ onFile(relative6);
33971
34044
  }
33972
34045
  }
33973
34046
  }
@@ -34065,7 +34138,7 @@ async function collectWorkspacePathsAsync(resolved) {
34065
34138
  }
34066
34139
  async function buildFileIndexAsync(cwd) {
34067
34140
  return withFileIndexSqliteLock(async () => {
34068
- const resolved = path28.resolve(cwd);
34141
+ const resolved = path30.resolve(cwd);
34069
34142
  await yieldToEventLoop();
34070
34143
  assertNotShutdown();
34071
34144
  const paths = await collectWorkspacePathsAsync(resolved);
@@ -34077,7 +34150,7 @@ async function buildFileIndexAsync(cwd) {
34077
34150
  }
34078
34151
 
34079
34152
  // src/files/index/ensure-file-index.ts
34080
- import path29 from "node:path";
34153
+ import path31 from "node:path";
34081
34154
 
34082
34155
  // src/files/index/search-file-index.ts
34083
34156
  function escapeLikePattern(fragment) {
@@ -34129,7 +34202,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
34129
34202
 
34130
34203
  // src/files/index/ensure-file-index.ts
34131
34204
  async function ensureFileIndexAsync(cwd) {
34132
- const resolved = path29.resolve(cwd);
34205
+ const resolved = path31.resolve(cwd);
34133
34206
  if (await bridgeFileIndexIsPopulated(resolved)) {
34134
34207
  return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
34135
34208
  }
@@ -34173,7 +34246,7 @@ function createFsWatcher(resolved, schedule) {
34173
34246
  }
34174
34247
  }
34175
34248
  function startFileIndexWatcher(cwd = getBridgeRoot()) {
34176
- const resolved = path30.resolve(cwd);
34249
+ const resolved = path32.resolve(cwd);
34177
34250
  void buildFileIndexAsync(resolved).catch((e) => {
34178
34251
  if (e instanceof CliSqliteInterrupted) return;
34179
34252
  console.error("[file-index] Initial index build failed:", e);
@@ -34203,7 +34276,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
34203
34276
  }
34204
34277
 
34205
34278
  // src/connection/create-bridge-connection.ts
34206
- import * as path40 from "node:path";
34279
+ import * as path43 from "node:path";
34207
34280
 
34208
34281
  // src/dev-servers/manager/dev-server-manager.ts
34209
34282
  import { rm as rm2 } from "node:fs/promises";
@@ -34225,15 +34298,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
34225
34298
 
34226
34299
  // src/dev-servers/process/terminate-child-process.ts
34227
34300
  async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
34228
- const exited = new Promise((resolve18) => {
34229
- proc.once("exit", () => resolve18());
34301
+ const exited = new Promise((resolve20) => {
34302
+ proc.once("exit", () => resolve20());
34230
34303
  });
34231
34304
  log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
34232
34305
  try {
34233
34306
  proc.kill("SIGTERM");
34234
34307
  } catch {
34235
34308
  }
34236
- await Promise.race([exited, new Promise((resolve18) => setTimeout(resolve18, graceMs))]);
34309
+ await Promise.race([exited, new Promise((resolve20) => setTimeout(resolve20, graceMs))]);
34237
34310
  }
34238
34311
  function forceKillChild(proc, log2, shortId, graceMs) {
34239
34312
  log2(
@@ -34513,10 +34586,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
34513
34586
  import { spawn as spawn8 } from "node:child_process";
34514
34587
  import fs28 from "node:fs";
34515
34588
  import { tmpdir } from "node:os";
34516
- import path31 from "node:path";
34589
+ import path33 from "node:path";
34517
34590
  function trySpawnMergedLogFile(command, env, cwd, signal) {
34518
- const tmpRoot = fs28.mkdtempSync(path31.join(tmpdir(), "ba-devsrv-log-"));
34519
- const logPath = path31.join(tmpRoot, "combined.log");
34591
+ const tmpRoot = fs28.mkdtempSync(path33.join(tmpdir(), "ba-devsrv-log-"));
34592
+ const logPath = path33.join(tmpRoot, "combined.log");
34520
34593
  let logFd;
34521
34594
  try {
34522
34595
  logFd = fs28.openSync(logPath, "a");
@@ -34560,15 +34633,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
34560
34633
  import { spawn as spawn9 } from "node:child_process";
34561
34634
  import fs29 from "node:fs";
34562
34635
  import { tmpdir as tmpdir2 } from "node:os";
34563
- import path32 from "node:path";
34636
+ import path34 from "node:path";
34564
34637
  function shSingleQuote(s) {
34565
34638
  return `'${s.replace(/'/g, `'\\''`)}'`;
34566
34639
  }
34567
34640
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
34568
- const tmpRoot = fs29.mkdtempSync(path32.join(tmpdir2(), "ba-devsrv-sh-"));
34569
- const logPath = path32.join(tmpRoot, "combined.log");
34570
- const innerPath = path32.join(tmpRoot, "_cmd.sh");
34571
- const runnerPath = path32.join(tmpRoot, "_run.sh");
34641
+ const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir2(), "ba-devsrv-sh-"));
34642
+ const logPath = path34.join(tmpRoot, "combined.log");
34643
+ const innerPath = path34.join(tmpRoot, "_cmd.sh");
34644
+ const runnerPath = path34.join(tmpRoot, "_run.sh");
34572
34645
  try {
34573
34646
  fs29.writeFileSync(innerPath, `#!/bin/sh
34574
34647
  ${command}
@@ -34599,9 +34672,9 @@ cd ${shSingleQuote(cwd)}
34599
34672
  }
34600
34673
  }
34601
34674
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
34602
- const tmpRoot = fs29.mkdtempSync(path32.join(tmpdir2(), "ba-devsrv-sh-"));
34603
- const logPath = path32.join(tmpRoot, "combined.log");
34604
- const runnerPath = path32.join(tmpRoot, "_run.bat");
34675
+ const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir2(), "ba-devsrv-sh-"));
34676
+ const logPath = path34.join(tmpRoot, "combined.log");
34677
+ const runnerPath = path34.join(tmpRoot, "_run.bat");
34605
34678
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
34606
34679
  const com = process.env.ComSpec || "cmd.exe";
34607
34680
  try {
@@ -35375,13 +35448,13 @@ function createOnBridgeIdentified(opts) {
35375
35448
 
35376
35449
  // src/skills/discover-local-agent-skills.ts
35377
35450
  import fs30 from "node:fs";
35378
- import path33 from "node:path";
35451
+ import path35 from "node:path";
35379
35452
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
35380
35453
  function discoverLocalSkills(cwd) {
35381
35454
  const out = [];
35382
35455
  const seenKeys = /* @__PURE__ */ new Set();
35383
35456
  for (const rel of SKILL_DISCOVERY_ROOTS) {
35384
- const base = path33.join(cwd, rel);
35457
+ const base = path35.join(cwd, rel);
35385
35458
  if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
35386
35459
  let entries = [];
35387
35460
  try {
@@ -35390,13 +35463,13 @@ function discoverLocalSkills(cwd) {
35390
35463
  continue;
35391
35464
  }
35392
35465
  for (const name of entries) {
35393
- const dir = path33.join(base, name);
35466
+ const dir = path35.join(base, name);
35394
35467
  try {
35395
35468
  if (!fs30.statSync(dir).isDirectory()) continue;
35396
35469
  } catch {
35397
35470
  continue;
35398
35471
  }
35399
- const skillMd = path33.join(dir, "SKILL.md");
35472
+ const skillMd = path35.join(dir, "SKILL.md");
35400
35473
  if (!fs30.existsSync(skillMd)) continue;
35401
35474
  const key = `${rel}/${name}`;
35402
35475
  if (seenKeys.has(key)) continue;
@@ -35409,7 +35482,7 @@ function discoverLocalSkills(cwd) {
35409
35482
  function discoverSkillLayoutRoots(cwd) {
35410
35483
  const roots = [];
35411
35484
  for (const rel of SKILL_DISCOVERY_ROOTS) {
35412
- const base = path33.join(cwd, rel);
35485
+ const base = path35.join(cwd, rel);
35413
35486
  if (!fs30.existsSync(base) || !fs30.statSync(base).isDirectory()) continue;
35414
35487
  let entries = [];
35415
35488
  try {
@@ -35419,13 +35492,13 @@ function discoverSkillLayoutRoots(cwd) {
35419
35492
  }
35420
35493
  const skills2 = [];
35421
35494
  for (const name of entries) {
35422
- const dir = path33.join(base, name);
35495
+ const dir = path35.join(base, name);
35423
35496
  try {
35424
35497
  if (!fs30.statSync(dir).isDirectory()) continue;
35425
35498
  } catch {
35426
35499
  continue;
35427
35500
  }
35428
- if (!fs30.existsSync(path33.join(dir, "SKILL.md"))) continue;
35501
+ if (!fs30.existsSync(path35.join(dir, "SKILL.md"))) continue;
35429
35502
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
35430
35503
  skills2.push({ name, relPath });
35431
35504
  }
@@ -35548,7 +35621,9 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
35548
35621
  "file_browser_search",
35549
35622
  "skill_layout_request",
35550
35623
  "install_skills",
35551
- "refresh_local_skills"
35624
+ "refresh_local_skills",
35625
+ "bridge_git_context_request",
35626
+ "list_repo_branches_request"
35552
35627
  ];
35553
35628
  var API_TO_BRIDGE_TYPE_SET = new Set(API_TO_BRIDGE_MESSAGE_TYPES);
35554
35629
  function parseApiToBridgeMessage(data, log2) {
@@ -35632,9 +35707,6 @@ var handleAgentConfigMessage = (msg, deps) => {
35632
35707
  handleBridgeAgentConfig(msg, deps);
35633
35708
  };
35634
35709
 
35635
- // src/prompt-turn-queue/runner.ts
35636
- import fs31 from "node:fs";
35637
-
35638
35710
  // src/prompt-turn-queue/client-report.ts
35639
35711
  function sendPromptQueueClientReport(ws, queues) {
35640
35712
  if (!ws) return false;
@@ -35704,8 +35776,36 @@ async function mergeServerQueueSnapshot(queueKey, serverTurns) {
35704
35776
  return { queueKey, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), turns };
35705
35777
  }
35706
35778
 
35707
- // src/prompt-turn-queue/runner.ts
35708
- var runIdToQueueKey = /* @__PURE__ */ new Map();
35779
+ // src/prompt-turn-queue/runner/dispatch-local-prompt.ts
35780
+ function dispatchLocalPrompt(next, deps) {
35781
+ const pl = next.payload;
35782
+ const rawParent = pl["sessionParent"];
35783
+ const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
35784
+ const rawParentPath = pl["sessionParentPath"];
35785
+ const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
35786
+ const rawBaseBranches = pl["worktreeBaseBranches"];
35787
+ const worktreeBaseBranches = rawBaseBranches != null && typeof rawBaseBranches === "object" && !Array.isArray(rawBaseBranches) ? rawBaseBranches : void 0;
35788
+ const msg = {
35789
+ type: "prompt",
35790
+ sessionId: next.sessionId,
35791
+ runId: next.turnId,
35792
+ prompt: pl.prompt,
35793
+ mode: typeof pl.mode === "string" ? pl.mode : "agent",
35794
+ isNewSession: pl.isNewSession === true,
35795
+ ...sessionParent ? { sessionParent } : {},
35796
+ ...sessionParentPath ? { sessionParentPath } : {},
35797
+ ...worktreeBaseBranches && Object.keys(worktreeBaseBranches).length > 0 ? { worktreeBaseBranches } : {},
35798
+ ...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
35799
+ ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
35800
+ ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
35801
+ ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
35802
+ ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
35803
+ ...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
35804
+ };
35805
+ handleBridgePrompt(msg, deps);
35806
+ }
35807
+
35808
+ // src/prompt-turn-queue/runner/queue-selection.ts
35709
35809
  function isRunnableServerState(s) {
35710
35810
  return s === "queued" || s === "requeued" || s === "requeued_with_revert";
35711
35811
  }
@@ -35724,6 +35824,28 @@ function pickNextRunnableTurn(turns) {
35724
35824
  function hasRunningTurn(turns) {
35725
35825
  return turns.some((t) => t.lastClientState === "running");
35726
35826
  }
35827
+
35828
+ // src/prompt-turn-queue/runner/run-id-queue-key-map.ts
35829
+ var runIdToQueueKey = /* @__PURE__ */ new Map();
35830
+ function getRunIdQueueKey(runId) {
35831
+ return runIdToQueueKey.get(runId);
35832
+ }
35833
+ function setRunIdQueueKey(runId, queueKey) {
35834
+ runIdToQueueKey.set(runId, queueKey);
35835
+ }
35836
+ function deleteRunIdQueueKey(runId) {
35837
+ const queueKey = runIdToQueueKey.get(runId);
35838
+ runIdToQueueKey.delete(runId);
35839
+ return queueKey;
35840
+ }
35841
+ function syncRunningTurnQueueKeys(turns, queueKey) {
35842
+ for (const running of turns.filter((t) => t.lastClientState === "running")) {
35843
+ runIdToQueueKey.set(running.turnId, queueKey);
35844
+ }
35845
+ }
35846
+
35847
+ // src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
35848
+ import fs31 from "node:fs";
35727
35849
  async function runLocalRevertBeforeQueuedPrompt(next, deps) {
35728
35850
  if (next.serverState !== "requeued_with_revert") return true;
35729
35851
  const sid = next.sessionId;
@@ -35745,30 +35867,23 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
35745
35867
  }
35746
35868
  return res.ok;
35747
35869
  }
35748
- function dispatchLocalPrompt(next, deps) {
35749
- const pl = next.payload;
35750
- const rawParent = pl["sessionParent"];
35751
- const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
35752
- const rawParentPath = pl["sessionParentPath"];
35753
- const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
35754
- const msg = {
35755
- type: "prompt",
35756
- sessionId: next.sessionId,
35757
- runId: next.turnId,
35758
- prompt: pl.prompt,
35759
- mode: typeof pl.mode === "string" ? pl.mode : "agent",
35760
- isNewSession: pl.isNewSession === true,
35761
- ...sessionParent ? { sessionParent } : {},
35762
- ...sessionParentPath ? { sessionParentPath } : {},
35763
- ...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
35764
- ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
35765
- ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
35766
- ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
35767
- ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
35768
- ...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
35769
- };
35770
- handleBridgePrompt(msg, deps);
35870
+
35871
+ // src/prompt-turn-queue/runner/finalize-prompt-turn-on-bridge.ts
35872
+ async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
35873
+ if (!runId) return false;
35874
+ const queueKey = deleteRunIdQueueKey(runId);
35875
+ if (!queueKey) return false;
35876
+ const f = await readPersistedQueue(queueKey);
35877
+ if (!f) return false;
35878
+ const t = f.turns.find((x) => x.turnId === runId);
35879
+ if (!t) return false;
35880
+ t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
35881
+ await writePersistedQueue(f);
35882
+ sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
35883
+ return true;
35771
35884
  }
35885
+
35886
+ // src/prompt-turn-queue/runner/apply-prompt-queue-state-from-server.ts
35772
35887
  async function applyPromptQueueStateFromServer(msg, deps) {
35773
35888
  const raw = msg.queues;
35774
35889
  if (!raw || typeof raw !== "object") return;
@@ -35782,9 +35897,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
35782
35897
  if (!Array.isArray(serverTurns)) continue;
35783
35898
  const file2 = await readPersistedQueue(queueKey);
35784
35899
  if (!file2) continue;
35785
- for (const running of file2.turns.filter((t) => t.lastClientState === "running")) {
35786
- runIdToQueueKey.set(running.turnId, queueKey);
35787
- }
35900
+ syncRunningTurnQueueKeys(file2.turns, queueKey);
35788
35901
  }
35789
35902
  for (const [queueKey, serverTurns] of Object.entries(raw)) {
35790
35903
  if (!Array.isArray(serverTurns)) continue;
@@ -35829,7 +35942,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
35829
35942
  }
35830
35943
  next.lastClientState = "running";
35831
35944
  await writePersistedQueue(file2);
35832
- runIdToQueueKey.set(next.turnId, queueKey);
35945
+ setRunIdQueueKey(next.turnId, queueKey);
35833
35946
  startedThisTick.add(next.turnId);
35834
35947
  report[queueKey] = [{ turnId: next.turnId, clientState: "running" }];
35835
35948
  }
@@ -35842,24 +35955,10 @@ async function applyPromptQueueStateFromServer(msg, deps) {
35842
35955
  if (!file2) continue;
35843
35956
  const running = file2.turns.find((t) => t.lastClientState === "running");
35844
35957
  if (!running || !startedThisTick.has(running.turnId)) continue;
35845
- if (runIdToQueueKey.get(running.turnId) !== queueKey) continue;
35958
+ if (getRunIdQueueKey(running.turnId) !== queueKey) continue;
35846
35959
  dispatchLocalPrompt(running, deps);
35847
35960
  }
35848
35961
  }
35849
- async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
35850
- if (!runId) return false;
35851
- const queueKey = runIdToQueueKey.get(runId);
35852
- runIdToQueueKey.delete(runId);
35853
- if (!queueKey) return false;
35854
- const f = await readPersistedQueue(queueKey);
35855
- if (!f) return false;
35856
- const t = f.turns.find((x) => x.turnId === runId);
35857
- if (!t) return false;
35858
- t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
35859
- await writePersistedQueue(f);
35860
- sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
35861
- return true;
35862
- }
35863
35962
 
35864
35963
  // src/agents/acp/from-bridge/bridge-prompt-wiring.ts
35865
35964
  function createBridgePromptSenders(deps, getWs) {
@@ -35906,6 +36005,91 @@ function createBridgePromptSenders(deps, getWs) {
35906
36005
  return { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate };
35907
36006
  }
35908
36007
 
36008
+ // src/agents/acp/from-bridge/handle-bridge-prompt/parse-bridge-attachments.ts
36009
+ function parseBridgeAttachments(msg) {
36010
+ const raw = msg.attachments;
36011
+ if (!Array.isArray(raw)) return [];
36012
+ const out = [];
36013
+ for (const x of raw) {
36014
+ if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
36015
+ const o = x;
36016
+ const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
36017
+ if (!id) continue;
36018
+ const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
36019
+ out.push({ attachmentId: id, mimeType: mt });
36020
+ }
36021
+ return out;
36022
+ }
36023
+
36024
+ // src/agents/acp/from-bridge/handle-bridge-prompt/parse-worktree-base-branches.ts
36025
+ function parseWorktreeBaseBranches(msg) {
36026
+ const raw = msg.worktreeBaseBranches;
36027
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
36028
+ const out = {};
36029
+ for (const [k, v] of Object.entries(raw)) {
36030
+ if (typeof k !== "string" || typeof v !== "string") continue;
36031
+ const rel = k.trim();
36032
+ const branch = v.trim();
36033
+ if (rel && branch) out[rel] = branch;
36034
+ }
36035
+ return Object.keys(out).length > 0 ? out : void 0;
36036
+ }
36037
+
36038
+ // src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
36039
+ function decryptChangeSummaryFileInput(row, e2ee) {
36040
+ if (!e2ee) return row;
36041
+ for (const field of ["path", "patchContent", "oldText", "newText"]) {
36042
+ const raw = row[field];
36043
+ if (typeof raw !== "string" || raw.trim() === "") continue;
36044
+ let o;
36045
+ try {
36046
+ o = JSON.parse(raw);
36047
+ } catch {
36048
+ continue;
36049
+ }
36050
+ if (!isE2eeEnvelope(o.ee)) continue;
36051
+ try {
36052
+ const d = e2ee.decryptMessage(o);
36053
+ const out = {
36054
+ path: typeof d.path === "string" ? d.path : row.path
36055
+ };
36056
+ if (d.directoryRemoved === true) out.directoryRemoved = true;
36057
+ else if (row.directoryRemoved === true) out.directoryRemoved = true;
36058
+ if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
36059
+ else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
36060
+ if (typeof d.oldText === "string") out.oldText = d.oldText;
36061
+ else if (typeof row.oldText === "string") out.oldText = row.oldText;
36062
+ if (typeof d.newText === "string") out.newText = d.newText;
36063
+ else if (typeof row.newText === "string") out.newText = row.newText;
36064
+ return out;
36065
+ } catch {
36066
+ return row;
36067
+ }
36068
+ }
36069
+ return row;
36070
+ }
36071
+
36072
+ // src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
36073
+ function hasSummarizePayload(f) {
36074
+ return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
36075
+ }
36076
+ function resolveChangeSummaryPromptForAgent(params) {
36077
+ const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
36078
+ const snaps = params.sessionChangeSummaryFileSnapshots;
36079
+ if (!isBuiltin || !snaps || snaps.length === 0) {
36080
+ return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
36081
+ }
36082
+ const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
36083
+ const withPayload = decrypted.filter(hasSummarizePayload);
36084
+ if (withPayload.length === 0) {
36085
+ return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
36086
+ }
36087
+ return {
36088
+ promptText: buildSessionChangeSummaryPrompt(withPayload),
36089
+ sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
36090
+ };
36091
+ }
36092
+
35909
36093
  // src/agents/acp/from-bridge/bridge-prompt-preamble.ts
35910
36094
  import { execFile as execFile8 } from "node:child_process";
35911
36095
  import { promisify as promisify8 } from "node:util";
@@ -35958,9 +36142,9 @@ function parseChangeSummarySnapshots(raw) {
35958
36142
  for (const item of raw) {
35959
36143
  if (!item || typeof item !== "object") continue;
35960
36144
  const o = item;
35961
- const path41 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
35962
- if (!path41) continue;
35963
- const row = { path: path41 };
36145
+ const path44 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
36146
+ if (!path44) continue;
36147
+ const row = { path: path44 };
35964
36148
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
35965
36149
  if (typeof o.oldText === "string") row.oldText = o.oldText;
35966
36150
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -35977,76 +36161,73 @@ function parseFollowUpFieldsFromPromptMessage(msg) {
35977
36161
  return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
35978
36162
  }
35979
36163
 
35980
- // src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
35981
- function decryptChangeSummaryFileInput(row, e2ee) {
35982
- if (!e2ee) return row;
35983
- for (const field of ["path", "patchContent", "oldText", "newText"]) {
35984
- const raw = row[field];
35985
- if (typeof raw !== "string" || raw.trim() === "") continue;
35986
- let o;
35987
- try {
35988
- o = JSON.parse(raw);
35989
- } catch {
35990
- continue;
35991
- }
35992
- if (!isE2eeEnvelope(o.ee)) continue;
35993
- try {
35994
- const d = e2ee.decryptMessage(o);
35995
- const out = {
35996
- path: typeof d.path === "string" ? d.path : row.path
35997
- };
35998
- if (d.directoryRemoved === true) out.directoryRemoved = true;
35999
- else if (row.directoryRemoved === true) out.directoryRemoved = true;
36000
- if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
36001
- else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
36002
- if (typeof d.oldText === "string") out.oldText = d.oldText;
36003
- else if (typeof row.oldText === "string") out.oldText = row.oldText;
36004
- if (typeof d.newText === "string") out.newText = d.newText;
36005
- else if (typeof row.newText === "string") out.newText = row.newText;
36006
- return out;
36007
- } catch {
36008
- return row;
36009
- }
36010
- }
36011
- return row;
36012
- }
36013
-
36014
- // src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
36015
- function hasSummarizePayload(f) {
36016
- return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
36017
- }
36018
- function resolveChangeSummaryPromptForAgent(params) {
36019
- const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
36020
- const snaps = params.sessionChangeSummaryFileSnapshots;
36021
- if (!isBuiltin || !snaps || snaps.length === 0) {
36022
- return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
36023
- }
36024
- const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
36025
- const withPayload = decrypted.filter(hasSummarizePayload);
36026
- if (withPayload.length === 0) {
36027
- return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
36164
+ // src/agents/acp/from-bridge/handle-bridge-prompt/run-preamble-and-prompt.ts
36165
+ async function runPreambleAndPrompt(params) {
36166
+ const {
36167
+ deps,
36168
+ msg,
36169
+ getWs,
36170
+ log: log2,
36171
+ sessionWorktreeManager,
36172
+ sessionId,
36173
+ runId,
36174
+ promptText,
36175
+ attachments,
36176
+ mode,
36177
+ agentType,
36178
+ agentId,
36179
+ agentConfig,
36180
+ resolvedCwd,
36181
+ senders: { sendResult: sendResult2, sendSessionUpdate }
36182
+ } = params;
36183
+ const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
36184
+ await runBridgePromptPreamble({
36185
+ getWs,
36186
+ log: log2,
36187
+ sessionWorktreeManager,
36188
+ sessionId,
36189
+ runId,
36190
+ effectiveCwd
36191
+ });
36192
+ const {
36193
+ followUpCatalogPromptId,
36194
+ sessionChangeSummaryFilePaths: pathsFromBridge,
36195
+ sessionChangeSummaryFileSnapshots
36196
+ } = parseFollowUpFieldsFromPromptMessage(msg);
36197
+ const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
36198
+ followUpCatalogPromptId,
36199
+ sessionChangeSummaryFileSnapshots,
36200
+ bridgePromptText: promptText,
36201
+ e2ee: deps.e2ee
36202
+ });
36203
+ if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
36204
+ deps.log(
36205
+ "[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
36206
+ );
36028
36207
  }
36029
- return {
36030
- promptText: buildSessionChangeSummaryPrompt(withPayload),
36031
- sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
36032
- };
36208
+ const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
36209
+ deps.acpManager.handlePrompt({
36210
+ promptText: resolvedPromptText,
36211
+ promptId: msg.id,
36212
+ sessionId,
36213
+ runId,
36214
+ mode,
36215
+ agentType,
36216
+ agentId,
36217
+ agentConfig,
36218
+ sessionParentPath: effectiveCwd,
36219
+ sendResult: sendResult2,
36220
+ sendSessionUpdate,
36221
+ followUpCatalogPromptId,
36222
+ sessionChangeSummaryFilePaths: pathsForUpload,
36223
+ cloudApiBaseUrl: deps.cloudApiBaseUrl,
36224
+ getCloudAccessToken: deps.getCloudAccessToken,
36225
+ e2ee: deps.e2ee,
36226
+ ...attachments.length > 0 ? { attachments } : {}
36227
+ });
36033
36228
  }
36034
36229
 
36035
- // src/agents/acp/from-bridge/handle-bridge-prompt.ts
36036
- function parseBridgeAttachments(msg) {
36037
- const raw = msg.attachments;
36038
- if (!Array.isArray(raw)) return [];
36039
- const out = [];
36040
- for (const x of raw) {
36041
- if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
36042
- const o = x;
36043
- const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
36044
- if (!id) continue;
36045
- const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
36046
- out.push({ attachmentId: id, mimeType: mt });
36047
- }
36048
- return out;
36049
- }
36230
+ // src/agents/acp/from-bridge/handle-bridge-prompt/handle-bridge-prompt.ts
36050
36231
  function handleBridgePrompt(msg, deps) {
36051
36232
  const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
36052
36233
  const rawPrompt = msg.prompt;
@@ -36055,12 +36236,12 @@ function handleBridgePrompt(msg, deps) {
36055
36236
  const sessionId = msg.sessionId;
36056
36237
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
36057
36238
  const promptId = typeof msg.id === "string" ? msg.id : void 0;
36058
- const { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate } = createBridgePromptSenders(deps, getWs);
36239
+ const senders = createBridgePromptSenders(deps, getWs);
36059
36240
  if (!promptText.trim() && attachments.length === 0) {
36060
36241
  log2(
36061
36242
  `[Bridge service] Prompt ignored: empty or missing prompt text (session ${typeof msg.sessionId === "string" ? msg.sessionId.slice(0, 8) : "\u2014"}\u2026, run ${typeof msg.runId === "string" ? msg.runId.slice(0, 8) : "\u2014"}\u2026).`
36062
36243
  );
36063
- sendBridgeMessage(
36244
+ senders.sendBridgeMessage(
36064
36245
  {
36065
36246
  type: "prompt_result",
36066
36247
  ...promptId ? { id: promptId } : {},
@@ -36082,57 +36263,50 @@ function handleBridgePrompt(msg, deps) {
36082
36263
  const agentId = typeof rawAgentId === "string" && rawAgentId.trim() !== "" ? rawAgentId.trim() : null;
36083
36264
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
36084
36265
  const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
36266
+ const worktreeBaseBranches = parseWorktreeBaseBranches(msg);
36085
36267
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
36086
- async function preambleAndPrompt(resolvedCwd) {
36087
- const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
36088
- await runBridgePromptPreamble({
36268
+ void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, {
36269
+ isNewSession,
36270
+ sessionParent,
36271
+ sessionParentPath,
36272
+ ...worktreeBaseBranches ? { worktreeBaseBranches } : {}
36273
+ }).then(
36274
+ (cwd) => runPreambleAndPrompt({
36275
+ deps,
36276
+ msg,
36089
36277
  getWs,
36090
36278
  log: log2,
36091
36279
  sessionWorktreeManager,
36092
36280
  sessionId,
36093
36281
  runId,
36094
- effectiveCwd
36095
- });
36096
- const {
36097
- followUpCatalogPromptId,
36098
- sessionChangeSummaryFilePaths: pathsFromBridge,
36099
- sessionChangeSummaryFileSnapshots
36100
- } = parseFollowUpFieldsFromPromptMessage(msg);
36101
- const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
36102
- followUpCatalogPromptId,
36103
- sessionChangeSummaryFileSnapshots,
36104
- bridgePromptText: promptText,
36105
- e2ee: deps.e2ee
36106
- });
36107
- if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
36108
- deps.log(
36109
- "[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
36110
- );
36111
- }
36112
- const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
36113
- acpManager.handlePrompt({
36114
- promptText: resolvedPromptText,
36115
- promptId: msg.id,
36282
+ promptText,
36283
+ attachments,
36284
+ mode,
36285
+ agentType,
36286
+ agentId,
36287
+ agentConfig,
36288
+ resolvedCwd: cwd,
36289
+ senders
36290
+ })
36291
+ ).catch((err) => {
36292
+ log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
36293
+ void runPreambleAndPrompt({
36294
+ deps,
36295
+ msg,
36296
+ getWs,
36297
+ log: log2,
36298
+ sessionWorktreeManager,
36116
36299
  sessionId,
36117
36300
  runId,
36301
+ promptText,
36302
+ attachments,
36118
36303
  mode,
36119
36304
  agentType,
36120
36305
  agentId,
36121
36306
  agentConfig,
36122
- sessionParentPath: effectiveCwd,
36123
- sendResult: sendResult2,
36124
- sendSessionUpdate,
36125
- followUpCatalogPromptId,
36126
- sessionChangeSummaryFilePaths: pathsForUpload,
36127
- cloudApiBaseUrl: deps.cloudApiBaseUrl,
36128
- getCloudAccessToken: deps.getCloudAccessToken,
36129
- e2ee: deps.e2ee,
36130
- ...attachments.length > 0 ? { attachments } : {}
36307
+ resolvedCwd: void 0,
36308
+ senders
36131
36309
  });
36132
- }
36133
- void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
36134
- log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
36135
- void preambleAndPrompt(void 0);
36136
36310
  });
36137
36311
  }
36138
36312
 
@@ -36187,11 +36361,11 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
36187
36361
  import fs33 from "node:fs";
36188
36362
 
36189
36363
  // src/files/ensure-under-cwd.ts
36190
- import path34 from "node:path";
36364
+ import path36 from "node:path";
36191
36365
  function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
36192
- const normalized = path34.normalize(relativePath).replace(/^(\.\/)+/, "");
36193
- const resolved = path34.resolve(cwd, normalized);
36194
- if (!resolved.startsWith(cwd + path34.sep) && resolved !== cwd) {
36366
+ const normalized = path36.normalize(relativePath).replace(/^(\.\/)+/, "");
36367
+ const resolved = path36.resolve(cwd, normalized);
36368
+ if (!resolved.startsWith(cwd + path36.sep) && resolved !== cwd) {
36195
36369
  return null;
36196
36370
  }
36197
36371
  return resolved;
@@ -36201,11 +36375,11 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
36201
36375
  var LIST_DIR_YIELD_EVERY = 256;
36202
36376
 
36203
36377
  // src/files/list-dir/map-dir-entry.ts
36204
- import path35 from "node:path";
36378
+ import path37 from "node:path";
36205
36379
  import fs32 from "node:fs";
36206
36380
  async function mapDirEntry(d, relativePath, resolved) {
36207
- const entryPath = path35.join(relativePath || ".", d.name).replace(/\\/g, "/");
36208
- const fullPath = path35.join(resolved, d.name);
36381
+ const entryPath = path37.join(relativePath || ".", d.name).replace(/\\/g, "/");
36382
+ const fullPath = path37.join(resolved, d.name);
36209
36383
  let isDir = d.isDirectory();
36210
36384
  if (d.isSymbolicLink()) {
36211
36385
  try {
@@ -36556,13 +36730,13 @@ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
36556
36730
  }
36557
36731
 
36558
36732
  // src/files/handle-file-browser-search.ts
36559
- import path36 from "node:path";
36733
+ import path38 from "node:path";
36560
36734
  var SEARCH_LIMIT = 100;
36561
36735
  function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
36562
36736
  void (async () => {
36563
36737
  await yieldToEventLoop();
36564
36738
  const q = typeof msg.q === "string" ? msg.q : "";
36565
- const sessionParentPath = path36.resolve(
36739
+ const sessionParentPath = path38.resolve(
36566
36740
  sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
36567
36741
  );
36568
36742
  if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
@@ -36682,7 +36856,7 @@ function handleSkillLayoutRequest(msg, deps) {
36682
36856
 
36683
36857
  // src/skills/install-remote-skills.ts
36684
36858
  import fs37 from "node:fs";
36685
- import path37 from "node:path";
36859
+ import path39 from "node:path";
36686
36860
  function installRemoteSkills(cwd, targetDir, items) {
36687
36861
  const installed2 = [];
36688
36862
  if (!Array.isArray(items)) {
@@ -36693,11 +36867,11 @@ function installRemoteSkills(cwd, targetDir, items) {
36693
36867
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
36694
36868
  continue;
36695
36869
  }
36696
- const skillDir = path37.join(cwd, targetDir, item.skillName);
36870
+ const skillDir = path39.join(cwd, targetDir, item.skillName);
36697
36871
  for (const f of item.files) {
36698
36872
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
36699
- const dest = path37.join(skillDir, f.path);
36700
- fs37.mkdirSync(path37.dirname(dest), { recursive: true });
36873
+ const dest = path39.join(skillDir, f.path);
36874
+ fs37.mkdirSync(path39.dirname(dest), { recursive: true });
36701
36875
  if (f.text !== void 0) {
36702
36876
  fs37.writeFileSync(dest, f.text, "utf8");
36703
36877
  } else if (f.base64) {
@@ -36913,6 +37087,148 @@ var handleDevServersConfig = (msg, deps) => {
36913
37087
  deps.devServerManager?.applyConfig(devServers ?? []);
36914
37088
  };
36915
37089
 
37090
+ // src/git/bridge-git-context.ts
37091
+ import * as path40 from "node:path";
37092
+
37093
+ // src/git/branches/get-current-branch.ts
37094
+ async function getCurrentBranch(repoPath) {
37095
+ try {
37096
+ const git = cliSimpleGit(repoPath);
37097
+ const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
37098
+ const trimmed2 = typeof branch === "string" ? branch.trim() : "";
37099
+ if (!trimmed2 || trimmed2 === "HEAD") return null;
37100
+ return trimmed2;
37101
+ } catch {
37102
+ return null;
37103
+ }
37104
+ }
37105
+
37106
+ // src/git/branches/list-repo-branch-refs.ts
37107
+ function normalizeBranchRef(raw) {
37108
+ const trimmed2 = raw.trim();
37109
+ if (!trimmed2 || trimmed2 === "HEAD") return null;
37110
+ if (trimmed2.startsWith("refs/heads/")) return trimmed2.slice("refs/heads/".length);
37111
+ if (trimmed2.startsWith("refs/remotes/")) return trimmed2.slice("refs/remotes/".length);
37112
+ return trimmed2;
37113
+ }
37114
+ async function listRepoBranchRefs(repoPath) {
37115
+ try {
37116
+ const git = cliSimpleGit(repoPath);
37117
+ const out = await git.raw([
37118
+ "for-each-ref",
37119
+ "--sort=-committerdate",
37120
+ "--format=%(refname:short)",
37121
+ "refs/heads",
37122
+ "refs/remotes"
37123
+ ]);
37124
+ const lines = out.split("\n").map((line) => normalizeBranchRef(line)).filter((line) => Boolean(line));
37125
+ const seen = /* @__PURE__ */ new Set();
37126
+ const ordered = [];
37127
+ for (const name of lines) {
37128
+ if (seen.has(name)) continue;
37129
+ seen.add(name);
37130
+ ordered.push(name);
37131
+ }
37132
+ return ordered;
37133
+ } catch {
37134
+ return [];
37135
+ }
37136
+ }
37137
+
37138
+ // src/git/bridge-git-context.ts
37139
+ function folderNameForRelPath(relPath, bridgeRoot) {
37140
+ if (relPath === "." || relPath === "") {
37141
+ return path40.basename(path40.resolve(bridgeRoot)) || "repo";
37142
+ }
37143
+ return path40.basename(relPath) || relPath;
37144
+ }
37145
+ async function discoverGitReposForBridgeContext(bridgeRoot) {
37146
+ const root = path40.resolve(bridgeRoot);
37147
+ if (await isGitRepoDirectory(root)) {
37148
+ const remoteUrl = await getRemoteOriginUrl(root);
37149
+ return [{ absolutePath: root, remoteUrl }];
37150
+ }
37151
+ const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
37152
+ const byPath = /* @__PURE__ */ new Map();
37153
+ for (const repo of [...deep, ...shallow]) {
37154
+ byPath.set(path40.resolve(repo.absolutePath), repo);
37155
+ }
37156
+ return [...byPath.values()];
37157
+ }
37158
+ async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
37159
+ const bridgeResolved = path40.resolve(bridgeRoot);
37160
+ const repos = await discoverGitReposForBridgeContext(bridgeResolved);
37161
+ const rows = [];
37162
+ for (const repo of repos) {
37163
+ let rel = path40.relative(bridgeResolved, repo.absolutePath);
37164
+ if (rel.startsWith("..") || path40.isAbsolute(rel)) continue;
37165
+ const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
37166
+ const currentBranch = await getCurrentBranch(repo.absolutePath);
37167
+ const remoteUrl = repo.remoteUrl.trim() || null;
37168
+ rows.push({
37169
+ relPath,
37170
+ folderName: folderNameForRelPath(relPath, bridgeResolved),
37171
+ currentBranch,
37172
+ remoteUrl
37173
+ });
37174
+ }
37175
+ rows.sort((a, b) => a.relPath.localeCompare(b.relPath));
37176
+ return rows;
37177
+ }
37178
+ async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
37179
+ const bridgeResolved = path40.resolve(bridgeRoot);
37180
+ const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
37181
+ const repoPath = rel === "" ? bridgeResolved : path40.join(bridgeResolved, rel);
37182
+ const resolved = path40.resolve(repoPath);
37183
+ if (!resolved.startsWith(bridgeResolved + path40.sep) && resolved !== bridgeResolved) {
37184
+ return [];
37185
+ }
37186
+ return listRepoBranchRefs(resolved);
37187
+ }
37188
+
37189
+ // src/routing/handlers/bridge-git-context-messages.ts
37190
+ function handleBridgeGitContextRequestMessage(msg, getWs) {
37191
+ void (async () => {
37192
+ const socket = getWs();
37193
+ if (!socket) return;
37194
+ try {
37195
+ const repos = await getBridgeGitContext();
37196
+ sendWsMessage(socket, { type: "bridge_git_context_response", id: msg.id, repos });
37197
+ } catch (e) {
37198
+ sendWsMessage(socket, {
37199
+ type: "bridge_git_context_response",
37200
+ id: msg.id,
37201
+ error: e instanceof Error ? e.message : String(e)
37202
+ });
37203
+ }
37204
+ })();
37205
+ }
37206
+ function handleListRepoBranchesRequestMessage(msg, getWs) {
37207
+ void (async () => {
37208
+ const socket = getWs();
37209
+ if (!socket) return;
37210
+ const repoRelPath = typeof msg.repoRelPath === "string" ? msg.repoRelPath.trim() : "";
37211
+ if (!repoRelPath) {
37212
+ sendWsMessage(socket, {
37213
+ type: "list_repo_branches_response",
37214
+ id: msg.id,
37215
+ error: "repoRelPath required"
37216
+ });
37217
+ return;
37218
+ }
37219
+ try {
37220
+ const branches = await listRepoBranchesForBridge(repoRelPath);
37221
+ sendWsMessage(socket, { type: "list_repo_branches_response", id: msg.id, branches });
37222
+ } catch (e) {
37223
+ sendWsMessage(socket, {
37224
+ type: "list_repo_branches_response",
37225
+ id: msg.id,
37226
+ error: e instanceof Error ? e.message : String(e)
37227
+ });
37228
+ }
37229
+ })();
37230
+ }
37231
+
36916
37232
  // src/routing/dispatch-bridge-message.ts
36917
37233
  function dispatchBridgeMessage(msg, deps) {
36918
37234
  switch (msg.type) {
@@ -36976,6 +37292,12 @@ function dispatchBridgeMessage(msg, deps) {
36976
37292
  case "refresh_local_skills":
36977
37293
  handleRefreshLocalSkills(msg, deps);
36978
37294
  break;
37295
+ case "bridge_git_context_request":
37296
+ handleBridgeGitContextRequestMessage(msg, deps.getWs);
37297
+ break;
37298
+ case "list_repo_branches_request":
37299
+ handleListRepoBranchesRequestMessage(msg, deps.getWs);
37300
+ break;
36979
37301
  }
36980
37302
  }
36981
37303
 
@@ -37426,10 +37748,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
37426
37748
  }
37427
37749
 
37428
37750
  // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
37429
- import * as path39 from "node:path";
37751
+ import * as path42 from "node:path";
37430
37752
 
37431
37753
  // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
37432
- import * as path38 from "node:path";
37754
+ import * as path41 from "node:path";
37433
37755
  async function probeOneAgentTypeForCapabilities(params) {
37434
37756
  const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
37435
37757
  if (isCliImmediateShutdownRequested()) return false;
@@ -37469,7 +37791,7 @@ async function probeOneAgentTypeForCapabilities(params) {
37469
37791
  if (isCliImmediateShutdownRequested()) return false;
37470
37792
  handle = await resolved.createClient({
37471
37793
  command: resolved.command,
37472
- cwd: path38.resolve(cwd),
37794
+ cwd: path41.resolve(cwd),
37473
37795
  backendAgentType: agentType,
37474
37796
  sessionMode: "agent",
37475
37797
  persistedAcpSessionId: null,
@@ -37543,7 +37865,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
37543
37865
  async function warmupAgentCapabilitiesOnConnect(params) {
37544
37866
  const { workspaceId, log: log2, getWs } = params;
37545
37867
  if (isCliImmediateShutdownRequested()) return;
37546
- const cwd = path39.resolve(getBridgeRoot());
37868
+ const cwd = path42.resolve(getBridgeRoot());
37547
37869
  async function sendBatchFromCache() {
37548
37870
  const socket = getWs();
37549
37871
  if (!socket || socket.readyState !== wrapper_default.OPEN) return;
@@ -37710,8 +38032,8 @@ async function createBridgeConnection(options) {
37710
38032
  getCloudAccessToken: () => tokens.accessToken
37711
38033
  };
37712
38034
  const identifyReportedPaths = {
37713
- bridgeRootPath: path40.resolve(getBridgeRoot()),
37714
- worktreesRootPath: path40.resolve(worktreesRootPath)
38035
+ bridgeRootPath: path43.resolve(getBridgeRoot()),
38036
+ worktreesRootPath: path43.resolve(worktreesRootPath)
37715
38037
  };
37716
38038
  const { connect } = createMainBridgeWebSocketLifecycle({
37717
38039
  state,