@buildautomaton/cli 0.1.37 → 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/cli.js CHANGED
@@ -973,7 +973,7 @@ var require_command = __commonJS({
973
973
  "../../node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/command.js"(exports) {
974
974
  var EventEmitter2 = __require("node:events").EventEmitter;
975
975
  var childProcess2 = __require("node:child_process");
976
- var path43 = __require("node:path");
976
+ var path46 = __require("node:path");
977
977
  var fs41 = __require("node:fs");
978
978
  var process8 = __require("node:process");
979
979
  var { Argument: Argument2, humanReadableArgName } = require_argument();
@@ -1906,9 +1906,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
1906
1906
  let launchWithNode = false;
1907
1907
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1908
1908
  function findFile(baseDir, baseName) {
1909
- const localBin = path43.resolve(baseDir, baseName);
1909
+ const localBin = path46.resolve(baseDir, baseName);
1910
1910
  if (fs41.existsSync(localBin)) return localBin;
1911
- if (sourceExt.includes(path43.extname(baseName))) return void 0;
1911
+ if (sourceExt.includes(path46.extname(baseName))) return void 0;
1912
1912
  const foundExt = sourceExt.find(
1913
1913
  (ext) => fs41.existsSync(`${localBin}${ext}`)
1914
1914
  );
@@ -1926,17 +1926,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
1926
1926
  } catch (err) {
1927
1927
  resolvedScriptPath = this._scriptPath;
1928
1928
  }
1929
- executableDir = path43.resolve(
1930
- path43.dirname(resolvedScriptPath),
1929
+ executableDir = path46.resolve(
1930
+ path46.dirname(resolvedScriptPath),
1931
1931
  executableDir
1932
1932
  );
1933
1933
  }
1934
1934
  if (executableDir) {
1935
1935
  let localFile = findFile(executableDir, executableFile);
1936
1936
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
1937
- const legacyName = path43.basename(
1937
+ const legacyName = path46.basename(
1938
1938
  this._scriptPath,
1939
- path43.extname(this._scriptPath)
1939
+ path46.extname(this._scriptPath)
1940
1940
  );
1941
1941
  if (legacyName !== this._name) {
1942
1942
  localFile = findFile(
@@ -1947,7 +1947,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1947
1947
  }
1948
1948
  executableFile = localFile || executableFile;
1949
1949
  }
1950
- launchWithNode = sourceExt.includes(path43.extname(executableFile));
1950
+ launchWithNode = sourceExt.includes(path46.extname(executableFile));
1951
1951
  let proc;
1952
1952
  if (process8.platform !== "win32") {
1953
1953
  if (launchWithNode) {
@@ -2787,7 +2787,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2787
2787
  * @return {Command}
2788
2788
  */
2789
2789
  nameFromFilename(filename) {
2790
- this._name = path43.basename(filename, path43.extname(filename));
2790
+ this._name = path46.basename(filename, path46.extname(filename));
2791
2791
  return this;
2792
2792
  }
2793
2793
  /**
@@ -2801,9 +2801,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
2801
2801
  * @param {string} [path]
2802
2802
  * @return {(string|null|Command)}
2803
2803
  */
2804
- executableDir(path44) {
2805
- if (path44 === void 0) return this._executableDir;
2806
- this._executableDir = path44;
2804
+ executableDir(path47) {
2805
+ if (path47 === void 0) return this._executableDir;
2806
+ this._executableDir = path47;
2807
2807
  return this;
2808
2808
  }
2809
2809
  /**
@@ -7061,8 +7061,8 @@ var init_parseUtil = __esm({
7061
7061
  init_errors();
7062
7062
  init_en();
7063
7063
  makeIssue = (params) => {
7064
- const { data, path: path43, errorMaps, issueData } = params;
7065
- const fullPath = [...path43, ...issueData.path || []];
7064
+ const { data, path: path46, errorMaps, issueData } = params;
7065
+ const fullPath = [...path46, ...issueData.path || []];
7066
7066
  const fullIssue = {
7067
7067
  ...issueData,
7068
7068
  path: fullPath
@@ -7370,11 +7370,11 @@ var init_types = __esm({
7370
7370
  init_parseUtil();
7371
7371
  init_util();
7372
7372
  ParseInputLazyPath = class {
7373
- constructor(parent, value, path43, key) {
7373
+ constructor(parent, value, path46, key) {
7374
7374
  this._cachedPath = [];
7375
7375
  this.parent = parent;
7376
7376
  this.data = value;
7377
- this._path = path43;
7377
+ this._path = path46;
7378
7378
  this._key = key;
7379
7379
  }
7380
7380
  get path() {
@@ -11529,10 +11529,10 @@ var require_src2 = __commonJS({
11529
11529
  var fs_1 = __require("fs");
11530
11530
  var debug_1 = __importDefault(require_src());
11531
11531
  var log2 = debug_1.default("@kwsites/file-exists");
11532
- function check2(path43, isFile, isDirectory) {
11533
- log2(`checking %s`, path43);
11532
+ function check2(path46, isFile, isDirectory) {
11533
+ log2(`checking %s`, path46);
11534
11534
  try {
11535
- const stat3 = fs_1.statSync(path43);
11535
+ const stat3 = fs_1.statSync(path46);
11536
11536
  if (stat3.isFile() && isFile) {
11537
11537
  log2(`[OK] path represents a file`);
11538
11538
  return true;
@@ -11552,8 +11552,8 @@ var require_src2 = __commonJS({
11552
11552
  throw e;
11553
11553
  }
11554
11554
  }
11555
- function exists2(path43, type = exports.READABLE) {
11556
- return check2(path43, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
11555
+ function exists2(path46, type = exports.READABLE) {
11556
+ return check2(path46, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
11557
11557
  }
11558
11558
  exports.exists = exists2;
11559
11559
  exports.FILE = 1;
@@ -11850,10 +11850,10 @@ function assignProp(target, prop, value) {
11850
11850
  configurable: true
11851
11851
  });
11852
11852
  }
11853
- function getElementAtPath(obj, path43) {
11854
- if (!path43)
11853
+ function getElementAtPath(obj, path46) {
11854
+ if (!path46)
11855
11855
  return obj;
11856
- return path43.reduce((acc, key) => acc?.[key], obj);
11856
+ return path46.reduce((acc, key) => acc?.[key], obj);
11857
11857
  }
11858
11858
  function promiseAllObject(promisesObj) {
11859
11859
  const keys = Object.keys(promisesObj);
@@ -12102,11 +12102,11 @@ function aborted(x, startIndex = 0) {
12102
12102
  }
12103
12103
  return false;
12104
12104
  }
12105
- function prefixIssues(path43, issues) {
12105
+ function prefixIssues(path46, issues) {
12106
12106
  return issues.map((iss) => {
12107
12107
  var _a2;
12108
12108
  (_a2 = iss).path ?? (_a2.path = []);
12109
- iss.path.unshift(path43);
12109
+ iss.path.unshift(path46);
12110
12110
  return iss;
12111
12111
  });
12112
12112
  }
@@ -12295,7 +12295,7 @@ function treeifyError(error40, _mapper) {
12295
12295
  return issue2.message;
12296
12296
  };
12297
12297
  const result = { errors: [] };
12298
- const processError = (error41, path43 = []) => {
12298
+ const processError = (error41, path46 = []) => {
12299
12299
  var _a2, _b;
12300
12300
  for (const issue2 of error41.issues) {
12301
12301
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -12305,7 +12305,7 @@ function treeifyError(error40, _mapper) {
12305
12305
  } else if (issue2.code === "invalid_element") {
12306
12306
  processError({ issues: issue2.issues }, issue2.path);
12307
12307
  } else {
12308
- const fullpath = [...path43, ...issue2.path];
12308
+ const fullpath = [...path46, ...issue2.path];
12309
12309
  if (fullpath.length === 0) {
12310
12310
  result.errors.push(mapper(issue2));
12311
12311
  continue;
@@ -12335,9 +12335,9 @@ function treeifyError(error40, _mapper) {
12335
12335
  processError(error40);
12336
12336
  return result;
12337
12337
  }
12338
- function toDotPath(path43) {
12338
+ function toDotPath(path46) {
12339
12339
  const segs = [];
12340
- for (const seg of path43) {
12340
+ for (const seg of path46) {
12341
12341
  if (typeof seg === "number")
12342
12342
  segs.push(`[${seg}]`);
12343
12343
  else if (typeof seg === "symbol")
@@ -24800,8 +24800,8 @@ var init_acp = __esm({
24800
24800
  this.#requestHandler = requestHandler;
24801
24801
  this.#notificationHandler = notificationHandler;
24802
24802
  this.#stream = stream;
24803
- this.#closedPromise = new Promise((resolve20) => {
24804
- this.#abortController.signal.addEventListener("abort", () => resolve20());
24803
+ this.#closedPromise = new Promise((resolve22) => {
24804
+ this.#abortController.signal.addEventListener("abort", () => resolve22());
24805
24805
  });
24806
24806
  this.#receive();
24807
24807
  }
@@ -24950,8 +24950,8 @@ var init_acp = __esm({
24950
24950
  }
24951
24951
  async sendRequest(method, params) {
24952
24952
  const id = this.#nextRequestId++;
24953
- const responsePromise = new Promise((resolve20, reject) => {
24954
- this.#pendingResponses.set(id, { resolve: resolve20, reject });
24953
+ const responsePromise = new Promise((resolve22, reject) => {
24954
+ this.#pendingResponses.set(id, { resolve: resolve22, reject });
24955
24955
  });
24956
24956
  await this.#sendMessage({ jsonrpc: "2.0", id, method, params });
24957
24957
  return responsePromise;
@@ -25064,7 +25064,7 @@ var {
25064
25064
  } = import_index.default;
25065
25065
 
25066
25066
  // src/cli-version.ts
25067
- var CLI_VERSION = "0.1.37".length > 0 ? "0.1.37" : "0.0.0-dev";
25067
+ var CLI_VERSION = "0.1.39".length > 0 ? "0.1.39" : "0.0.0-dev";
25068
25068
 
25069
25069
  // src/cli/defaults.ts
25070
25070
  var DEFAULT_API_URL = process.env.BUILDAUTOMATON_API_URL ?? "https://api.buildautomaton.com";
@@ -25072,7 +25072,7 @@ var DEFAULT_FIREHOSE_URL = "https://buildautomaton-firehose.fly.dev";
25072
25072
 
25073
25073
  // src/cli/run-cli-action.ts
25074
25074
  import * as fs40 from "node:fs";
25075
- import * as path42 from "node:path";
25075
+ import * as path45 from "node:path";
25076
25076
 
25077
25077
  // src/cli-log-level.ts
25078
25078
  var verbosity = "info";
@@ -26079,14 +26079,14 @@ var baseOpen = async (options) => {
26079
26079
  }
26080
26080
  const subprocess = childProcess.spawn(command, cliArguments, childProcessOptions);
26081
26081
  if (options.wait) {
26082
- return new Promise((resolve20, reject) => {
26082
+ return new Promise((resolve22, reject) => {
26083
26083
  subprocess.once("error", reject);
26084
26084
  subprocess.once("close", (exitCode) => {
26085
26085
  if (!options.allowNonzeroExitCode && exitCode > 0) {
26086
26086
  reject(new Error(`Exited with code ${exitCode}`));
26087
26087
  return;
26088
26088
  }
26089
- resolve20(subprocess);
26089
+ resolve22(subprocess);
26090
26090
  });
26091
26091
  });
26092
26092
  }
@@ -26575,8 +26575,8 @@ function runPendingAuth(options) {
26575
26575
  let hasOpenedBrowser = false;
26576
26576
  let resolved = false;
26577
26577
  let resolveAuth;
26578
- const authPromise = new Promise((resolve20) => {
26579
- resolveAuth = resolve20;
26578
+ const authPromise = new Promise((resolve22) => {
26579
+ resolveAuth = resolve22;
26580
26580
  });
26581
26581
  let reconnectAttempt = 0;
26582
26582
  const signInQuiet = createEmptyReconnectQuietSlot();
@@ -26727,7 +26727,7 @@ import sqliteWasm from "node-sqlite3-wasm";
26727
26727
 
26728
26728
  // src/runtime/yield-to-event-loop.ts
26729
26729
  function yieldToEventLoop() {
26730
- return new Promise((resolve20) => setImmediate(resolve20));
26730
+ return new Promise((resolve22) => setImmediate(resolve22));
26731
26731
  }
26732
26732
 
26733
26733
  // src/sqlite/cli-sqlite-paths.ts
@@ -27570,9 +27570,9 @@ function parseChangeSummaryJson(raw, allowedPaths, options) {
27570
27570
  const rawPath = typeof o.path === "string" ? o.path.trim() : "";
27571
27571
  const summary = typeof o.summary === "string" ? o.summary.trim() : "";
27572
27572
  if (!rawPath || !summary) continue;
27573
- const path43 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
27574
- if (!path43) continue;
27575
- rows.push({ path: path43, summary: clampSummaryToAtMostTwoLines(summary) });
27573
+ const path46 = skip ? normalizeRepoRelativePath(rawPath) || rawPath : resolveChangeSummaryPathAgainstAllowed(rawPath, allowedPaths);
27574
+ if (!path46) continue;
27575
+ rows.push({ path: path46, summary: clampSummaryToAtMostTwoLines(summary) });
27576
27576
  }
27577
27577
  return rows;
27578
27578
  }
@@ -27866,8 +27866,8 @@ function pathspec(...paths) {
27866
27866
  cache.set(key, paths);
27867
27867
  return key;
27868
27868
  }
27869
- function isPathSpec(path43) {
27870
- return path43 instanceof String && cache.has(path43);
27869
+ function isPathSpec(path46) {
27870
+ return path46 instanceof String && cache.has(path46);
27871
27871
  }
27872
27872
  function toPaths(pathSpec) {
27873
27873
  return cache.get(pathSpec) || [];
@@ -27956,8 +27956,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
27956
27956
  function forEachLineWithContent(input, callback) {
27957
27957
  return toLinesWithContent(input, true).map((line) => callback(line));
27958
27958
  }
27959
- function folderExists(path43) {
27960
- return (0, import_file_exists.exists)(path43, import_file_exists.FOLDER);
27959
+ function folderExists(path46) {
27960
+ return (0, import_file_exists.exists)(path46, import_file_exists.FOLDER);
27961
27961
  }
27962
27962
  function append(target, item) {
27963
27963
  if (Array.isArray(target)) {
@@ -28361,8 +28361,8 @@ function checkIsRepoRootTask() {
28361
28361
  commands,
28362
28362
  format: "utf-8",
28363
28363
  onError,
28364
- parser(path43) {
28365
- return /^\.(git)?$/.test(path43.trim());
28364
+ parser(path46) {
28365
+ return /^\.(git)?$/.test(path46.trim());
28366
28366
  }
28367
28367
  };
28368
28368
  }
@@ -28796,11 +28796,11 @@ function parseGrep(grep) {
28796
28796
  const paths = /* @__PURE__ */ new Set();
28797
28797
  const results = {};
28798
28798
  forEachLineWithContent(grep, (input) => {
28799
- const [path43, line, preview] = input.split(NULL);
28800
- paths.add(path43);
28801
- (results[path43] = results[path43] || []).push({
28799
+ const [path46, line, preview] = input.split(NULL);
28800
+ paths.add(path46);
28801
+ (results[path46] = results[path46] || []).push({
28802
28802
  line: asNumber(line),
28803
- path: path43,
28803
+ path: path46,
28804
28804
  preview
28805
28805
  });
28806
28806
  });
@@ -29565,14 +29565,14 @@ var init_hash_object = __esm2({
29565
29565
  init_task();
29566
29566
  }
29567
29567
  });
29568
- function parseInit(bare, path43, text) {
29568
+ function parseInit(bare, path46, text) {
29569
29569
  const response = String(text).trim();
29570
29570
  let result;
29571
29571
  if (result = initResponseRegex.exec(response)) {
29572
- return new InitSummary(bare, path43, false, result[1]);
29572
+ return new InitSummary(bare, path46, false, result[1]);
29573
29573
  }
29574
29574
  if (result = reInitResponseRegex.exec(response)) {
29575
- return new InitSummary(bare, path43, true, result[1]);
29575
+ return new InitSummary(bare, path46, true, result[1]);
29576
29576
  }
29577
29577
  let gitDir = "";
29578
29578
  const tokens = response.split(" ");
@@ -29583,7 +29583,7 @@ function parseInit(bare, path43, text) {
29583
29583
  break;
29584
29584
  }
29585
29585
  }
29586
- return new InitSummary(bare, path43, /^re/i.test(response), gitDir);
29586
+ return new InitSummary(bare, path46, /^re/i.test(response), gitDir);
29587
29587
  }
29588
29588
  var InitSummary;
29589
29589
  var initResponseRegex;
@@ -29592,9 +29592,9 @@ var init_InitSummary = __esm2({
29592
29592
  "src/lib/responses/InitSummary.ts"() {
29593
29593
  "use strict";
29594
29594
  InitSummary = class {
29595
- constructor(bare, path43, existing, gitDir) {
29595
+ constructor(bare, path46, existing, gitDir) {
29596
29596
  this.bare = bare;
29597
- this.path = path43;
29597
+ this.path = path46;
29598
29598
  this.existing = existing;
29599
29599
  this.gitDir = gitDir;
29600
29600
  }
@@ -29606,7 +29606,7 @@ var init_InitSummary = __esm2({
29606
29606
  function hasBareCommand(command) {
29607
29607
  return command.includes(bareCommand);
29608
29608
  }
29609
- function initTask(bare = false, path43, customArgs) {
29609
+ function initTask(bare = false, path46, customArgs) {
29610
29610
  const commands = ["init", ...customArgs];
29611
29611
  if (bare && !hasBareCommand(commands)) {
29612
29612
  commands.splice(1, 0, bareCommand);
@@ -29615,7 +29615,7 @@ function initTask(bare = false, path43, customArgs) {
29615
29615
  commands,
29616
29616
  format: "utf-8",
29617
29617
  parser(text) {
29618
- return parseInit(commands.includes("--bare"), path43, text);
29618
+ return parseInit(commands.includes("--bare"), path46, text);
29619
29619
  }
29620
29620
  };
29621
29621
  }
@@ -30431,12 +30431,12 @@ var init_FileStatusSummary = __esm2({
30431
30431
  "use strict";
30432
30432
  fromPathRegex = /^(.+)\0(.+)$/;
30433
30433
  FileStatusSummary = class {
30434
- constructor(path43, index, working_dir) {
30435
- this.path = path43;
30434
+ constructor(path46, index, working_dir) {
30435
+ this.path = path46;
30436
30436
  this.index = index;
30437
30437
  this.working_dir = working_dir;
30438
30438
  if (index === "R" || working_dir === "R") {
30439
- const detail = fromPathRegex.exec(path43) || [null, path43, path43];
30439
+ const detail = fromPathRegex.exec(path46) || [null, path46, path46];
30440
30440
  this.from = detail[2] || "";
30441
30441
  this.path = detail[1] || "";
30442
30442
  }
@@ -30467,14 +30467,14 @@ function splitLine(result, lineStr) {
30467
30467
  default:
30468
30468
  return;
30469
30469
  }
30470
- function data(index, workingDir, path43) {
30470
+ function data(index, workingDir, path46) {
30471
30471
  const raw = `${index}${workingDir}`;
30472
30472
  const handler = parsers6.get(raw);
30473
30473
  if (handler) {
30474
- handler(result, path43);
30474
+ handler(result, path46);
30475
30475
  }
30476
30476
  if (raw !== "##" && raw !== "!!") {
30477
- result.files.push(new FileStatusSummary(path43, index, workingDir));
30477
+ result.files.push(new FileStatusSummary(path46, index, workingDir));
30478
30478
  }
30479
30479
  }
30480
30480
  }
@@ -30783,9 +30783,9 @@ var init_simple_git_api = __esm2({
30783
30783
  next
30784
30784
  );
30785
30785
  }
30786
- hashObject(path43, write) {
30786
+ hashObject(path46, write) {
30787
30787
  return this._runTask(
30788
- hashObjectTask(path43, write === true),
30788
+ hashObjectTask(path46, write === true),
30789
30789
  trailingFunctionArgument(arguments)
30790
30790
  );
30791
30791
  }
@@ -31138,8 +31138,8 @@ var init_branch = __esm2({
31138
31138
  }
31139
31139
  });
31140
31140
  function toPath(input) {
31141
- const path43 = input.trim().replace(/^["']|["']$/g, "");
31142
- return path43 && normalize(path43);
31141
+ const path46 = input.trim().replace(/^["']|["']$/g, "");
31142
+ return path46 && normalize(path46);
31143
31143
  }
31144
31144
  var parseCheckIgnore;
31145
31145
  var init_CheckIgnore = __esm2({
@@ -31453,8 +31453,8 @@ __export2(sub_module_exports, {
31453
31453
  subModuleTask: () => subModuleTask,
31454
31454
  updateSubModuleTask: () => updateSubModuleTask
31455
31455
  });
31456
- function addSubModuleTask(repo, path43) {
31457
- return subModuleTask(["add", repo, path43]);
31456
+ function addSubModuleTask(repo, path46) {
31457
+ return subModuleTask(["add", repo, path46]);
31458
31458
  }
31459
31459
  function initSubModuleTask(customArgs) {
31460
31460
  return subModuleTask(["init", ...customArgs]);
@@ -31787,8 +31787,8 @@ var require_git = __commonJS2({
31787
31787
  }
31788
31788
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
31789
31789
  };
31790
- Git2.prototype.submoduleAdd = function(repo, path43, then) {
31791
- return this._runTask(addSubModuleTask2(repo, path43), trailingFunctionArgument2(arguments));
31790
+ Git2.prototype.submoduleAdd = function(repo, path46, then) {
31791
+ return this._runTask(addSubModuleTask2(repo, path46), trailingFunctionArgument2(arguments));
31792
31792
  };
31793
31793
  Git2.prototype.submoduleUpdate = function(args, then) {
31794
31794
  return this._runTask(
@@ -32425,8 +32425,8 @@ async function runGitTask(fn) {
32425
32425
  }
32426
32426
  async function yieldToEventLoop2() {
32427
32427
  throwIfGitShutdownRequested();
32428
- await new Promise((resolve20) => {
32429
- setImmediate(resolve20);
32428
+ await new Promise((resolve22) => {
32429
+ setImmediate(resolve22);
32430
32430
  });
32431
32431
  throwIfGitShutdownRequested();
32432
32432
  }
@@ -32751,9 +32751,9 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
32751
32751
  // src/agents/acp/put-summarize-change-summaries.ts
32752
32752
  async function putEncryptedChangeSummaryRows(params) {
32753
32753
  const base = params.apiBaseUrl.replace(/\/+$/, "");
32754
- const entries = params.rows.map(({ path: path43, summary }) => {
32754
+ const entries = params.rows.map(({ path: path46, summary }) => {
32755
32755
  const enc = params.e2ee.encryptFields({ summary }, ["summary"]);
32756
- return { path: path43, summary: JSON.stringify(enc) };
32756
+ return { path: path46, summary: JSON.stringify(enc) };
32757
32757
  });
32758
32758
  const res = await fetch(
32759
32759
  `${base}/api/sessions/${encodeURIComponent(params.sessionId)}/follow-ups/summarize-changes`,
@@ -33756,11 +33756,11 @@ async function sendAcpPromptViaTransport(transport, ctx, sessionId, promptText,
33756
33756
  // src/agents/acp/clients/sdk/sdk-stdio-permission-request-handshake.ts
33757
33757
  function awaitSdkStdioPermissionRequestHandshake(params) {
33758
33758
  const { requestId, paramsRecord, pending, onRequest } = params;
33759
- return new Promise((resolve20) => {
33760
- pending.set(requestId, { resolve: resolve20, params: paramsRecord });
33759
+ return new Promise((resolve22) => {
33760
+ pending.set(requestId, { resolve: resolve22, params: paramsRecord });
33761
33761
  if (onRequest == null) {
33762
33762
  pending.delete(requestId);
33763
- resolve20({ outcome: { outcome: "denied" } });
33763
+ resolve22({ outcome: { outcome: "denied" } });
33764
33764
  return;
33765
33765
  }
33766
33766
  try {
@@ -33826,7 +33826,7 @@ async function createSdkStdioAcpClient(options) {
33826
33826
  child.once("close", (code, signal) => {
33827
33827
  onAgentSubprocessExit?.({ code, signal });
33828
33828
  });
33829
- return new Promise((resolve20, reject) => {
33829
+ return new Promise((resolve22, reject) => {
33830
33830
  let initSettled = false;
33831
33831
  const settleReject = (err) => {
33832
33832
  if (initSettled) return;
@@ -33840,7 +33840,7 @@ async function createSdkStdioAcpClient(options) {
33840
33840
  const settleResolve = (handle) => {
33841
33841
  if (initSettled) return;
33842
33842
  initSettled = true;
33843
- resolve20(handle);
33843
+ resolve22(handle);
33844
33844
  };
33845
33845
  child.on("error", (err) => {
33846
33846
  settleReject(new Error(formatSpawnError(err, command[0])));
@@ -34131,7 +34131,7 @@ async function createCursorAcpClient(options) {
34131
34131
  logDebug,
34132
34132
  getStderrText: () => stderrCapture.getText()
34133
34133
  };
34134
- return new Promise((resolve20, reject) => {
34134
+ return new Promise((resolve22, reject) => {
34135
34135
  child.on("error", (err) => {
34136
34136
  child.kill();
34137
34137
  reject(new Error(formatSpawnError2(err, command[0])));
@@ -34307,7 +34307,7 @@ async function createCursorAcpClient(options) {
34307
34307
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
34308
34308
  });
34309
34309
  const sessionId = established.sessionId;
34310
- resolve20({
34310
+ resolve22({
34311
34311
  sessionId,
34312
34312
  async sendPrompt(prompt, options2) {
34313
34313
  const imgs = options2?.images?.map((im) => ({ type: "image", mimeType: im.mimeType, data: im.dataBase64 }));
@@ -34599,7 +34599,7 @@ function createBridgeOnFileChange(opts) {
34599
34599
 
34600
34600
  // src/agents/acp/hooks/bridge-on-request.ts
34601
34601
  function createBridgeOnRequest(opts) {
34602
- const { routing, getSendRequest, log: log2, getAutoApproveAcpPermissions, resolveAcpPermissionRequest } = opts;
34602
+ const { routing, getSendRequest, log: log2, getAutoApproveAcpPermissions, resolveAcpPermissionRequest, nextTranscriptStreamSeq } = opts;
34603
34603
  return (request) => {
34604
34604
  const runId = routing.runId;
34605
34605
  const sessionId = routing.sessionId;
@@ -34628,6 +34628,7 @@ function createBridgeOnRequest(opts) {
34628
34628
  );
34629
34629
  }
34630
34630
  try {
34631
+ const transcriptStreamSeq = nextTranscriptStreamSeq?.(runId);
34631
34632
  sendReq({
34632
34633
  type: "session_update",
34633
34634
  ...sessionId ? { sessionId } : {},
@@ -34638,7 +34639,8 @@ function createBridgeOnRequest(opts) {
34638
34639
  requestId: request.requestId,
34639
34640
  method: request.method,
34640
34641
  params: request.params
34641
- }
34642
+ },
34643
+ ...transcriptStreamSeq != null ? { transcriptStreamSeq } : {}
34642
34644
  });
34643
34645
  } catch (err) {
34644
34646
  log2(
@@ -35039,7 +35041,7 @@ function sendSessionInfoTitleUpdate(params) {
35039
35041
 
35040
35042
  // src/agents/acp/hooks/bridge-on-session-update/create-bridge-on-session-update.ts
35041
35043
  function createBridgeOnSessionUpdate(opts) {
35042
- const { routing, getSendSessionUpdate, log: log2, sessionParentPath } = opts;
35044
+ const { routing, getSendSessionUpdate, log: log2, sessionParentPath, nextTranscriptStreamSeq } = opts;
35043
35045
  const pathTracker = new PathSnapshotTracker();
35044
35046
  return (params) => {
35045
35047
  const runId = routing.runId;
@@ -35117,12 +35119,14 @@ function createBridgeOnSessionUpdate(opts) {
35117
35119
  return;
35118
35120
  }
35119
35121
  try {
35122
+ const transcriptStreamSeq = nextTranscriptStreamSeq?.(runId);
35120
35123
  send({
35121
35124
  type: "session_update",
35122
35125
  ...sessionId ? { sessionId } : {},
35123
35126
  runId,
35124
35127
  kind: updateKind,
35125
- payload: params
35128
+ payload: params,
35129
+ ...transcriptStreamSeq != null ? { transcriptStreamSeq } : {}
35126
35130
  });
35127
35131
  } catch (err) {
35128
35132
  log2(
@@ -35135,10 +35139,17 @@ function createBridgeOnSessionUpdate(opts) {
35135
35139
 
35136
35140
  // src/agents/acp/hooks/build-acp-session-bridge-hooks.ts
35137
35141
  function buildAcpSessionBridgeHooks(opts) {
35142
+ const transcriptStreamSeqByRunId = /* @__PURE__ */ new Map();
35143
+ const nextTranscriptStreamSeq = (runId) => {
35144
+ const n = (transcriptStreamSeqByRunId.get(runId) ?? 0) + 1;
35145
+ transcriptStreamSeqByRunId.set(runId, n);
35146
+ return n;
35147
+ };
35148
+ const optsWithSeq = { ...opts, nextTranscriptStreamSeq };
35138
35149
  return {
35139
- onFileChange: createBridgeOnFileChange(opts),
35140
- onSessionUpdate: createBridgeOnSessionUpdate(opts),
35141
- onRequest: createBridgeOnRequest(opts)
35150
+ onFileChange: createBridgeOnFileChange(optsWithSeq),
35151
+ onSessionUpdate: createBridgeOnSessionUpdate(optsWithSeq),
35152
+ onRequest: createBridgeOnRequest(optsWithSeq)
35142
35153
  };
35143
35154
  }
35144
35155
 
@@ -35542,266 +35553,65 @@ async function createAcpManager(options) {
35542
35553
  };
35543
35554
  }
35544
35555
 
35545
- // src/worktrees/session-worktree-manager.ts
35546
- import * as path26 from "node:path";
35547
- import os8 from "node:os";
35548
-
35549
- // src/worktrees/prepare-new-session-worktrees.ts
35550
- import * as fs18 from "node:fs";
35551
- import * as path21 from "node:path";
35552
-
35553
- // src/git/worktrees/worktree-add.ts
35554
- async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
35555
- const mainGit = cliSimpleGit(mainRepoPath);
35556
- await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
35557
- }
35558
-
35559
- // src/worktrees/worktree-layout-file.ts
35560
- import * as fs17 from "node:fs";
35561
- import * as path20 from "node:path";
35562
- import os7 from "node:os";
35563
- var LAYOUT_FILENAME = "worktree-launcher-layout.json";
35564
- function defaultWorktreeLayoutPath() {
35565
- return path20.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
35566
- }
35567
- function normalizeLoadedLayout(raw) {
35568
- if (raw && typeof raw === "object" && "launcherCwds" in raw) {
35569
- const j = raw;
35570
- if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
35571
- }
35572
- return { launcherCwds: [] };
35573
- }
35574
- function loadWorktreeLayout() {
35575
- try {
35576
- const p = defaultWorktreeLayoutPath();
35577
- if (!fs17.existsSync(p)) return { launcherCwds: [] };
35578
- const raw = JSON.parse(fs17.readFileSync(p, "utf8"));
35579
- return normalizeLoadedLayout(raw);
35580
- } catch {
35581
- return { launcherCwds: [] };
35582
- }
35583
- }
35584
- function saveWorktreeLayout(layout) {
35585
- try {
35586
- const dir = path20.dirname(defaultWorktreeLayoutPath());
35587
- fs17.mkdirSync(dir, { recursive: true });
35588
- fs17.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
35589
- } catch {
35590
- }
35591
- }
35592
- function baseNameSafe(pathString) {
35593
- return path20.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
35594
- }
35595
- function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
35596
- const norm = path20.resolve(bridgeRootPath2);
35597
- const existing = layout.launcherCwds.find((e) => path20.resolve(e.absolutePath) === norm);
35598
- return existing?.dirName;
35599
- }
35600
- function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
35601
- const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
35602
- if (existing) return existing;
35603
- const norm = path20.resolve(bridgeRootPath2);
35604
- const base = baseNameSafe(norm);
35605
- const used = new Set(layout.launcherCwds.map((e) => e.dirName));
35606
- let name = base;
35607
- let n = 2;
35608
- while (used.has(name)) {
35609
- name = `${base}-${n}`;
35610
- n += 1;
35611
- }
35612
- layout.launcherCwds.push({ absolutePath: norm, dirName: name });
35613
- saveWorktreeLayout(layout);
35614
- return name;
35615
- }
35616
-
35617
- // src/worktrees/prepare-new-session-worktrees.ts
35618
- async function prepareNewSessionWorktrees(options) {
35619
- const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
35620
- const bridgeResolved = path21.resolve(bridgeRoot);
35621
- const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
35622
- const bridgeKeyDir = path21.join(worktreesRootPath, cwdKey);
35623
- const sessionDir = path21.join(bridgeKeyDir, sessionId);
35624
- const repos = await discoverGitReposUnderRoot(bridgeResolved);
35625
- if (repos.length === 0) {
35626
- log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
35627
- return null;
35628
- }
35629
- const branch = `session-${sessionId}`;
35630
- const worktreePaths = [];
35631
- fs18.mkdirSync(sessionDir, { recursive: true });
35632
- for (const repo of repos) {
35633
- let rel = path21.relative(bridgeResolved, repo.absolutePath);
35634
- if (rel.startsWith("..") || path21.isAbsolute(rel)) continue;
35635
- const relNorm = rel === "" ? "." : rel;
35636
- const wtPath = relNorm === "." ? sessionDir : path21.join(sessionDir, relNorm);
35637
- if (relNorm !== ".") {
35638
- fs18.mkdirSync(path21.dirname(wtPath), { recursive: true });
35639
- }
35640
- try {
35641
- await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
35642
- log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
35643
- worktreePaths.push(wtPath);
35644
- } catch (e) {
35645
- log2(
35646
- `[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
35647
- );
35648
- }
35649
- }
35650
- if (worktreePaths.length === 0) return null;
35651
- return {
35652
- worktreePaths,
35653
- sessionParentPath: sessionDir,
35654
- workingTreeRelRoot: sessionDir
35655
- };
35656
- }
35657
-
35658
- // src/git/branches/rename-branch.ts
35659
- async function gitRenameCurrentBranch(repoDir, newName) {
35660
- const g = cliSimpleGit(repoDir);
35661
- await g.raw(["branch", "-m", newName]);
35662
- }
35663
-
35664
- // src/worktrees/rename-session-worktree-branches.ts
35665
- async function renameSessionWorktreeBranches(paths, newBranch, log2) {
35666
- const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
35667
- for (const wt of paths) {
35668
- try {
35669
- await gitRenameCurrentBranch(wt, safe);
35670
- log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
35671
- } catch (e) {
35672
- log2(
35673
- `[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
35674
- );
35675
- }
35676
- }
35677
- }
35678
-
35679
- // src/worktrees/remove-session-worktrees.ts
35680
- import * as fs21 from "node:fs";
35681
-
35682
- // src/git/worktrees/worktree-remove.ts
35683
- import * as fs20 from "node:fs";
35684
-
35685
- // src/git/worktrees/resolve-main-repo-from-git-file.ts
35686
- import * as fs19 from "node:fs";
35687
- import * as path22 from "node:path";
35688
- function resolveMainRepoFromWorktreeGitFile(wt) {
35689
- const gitDirFile = path22.join(wt, ".git");
35690
- if (!fs19.existsSync(gitDirFile) || !fs19.statSync(gitDirFile).isFile()) return "";
35691
- const first2 = fs19.readFileSync(gitDirFile, "utf8").trim();
35692
- const m = first2.match(/^gitdir:\s*(.+)$/im);
35693
- if (!m) return "";
35694
- const gitWorktreePath = path22.resolve(wt, m[1].trim());
35695
- const gitDir = path22.dirname(path22.dirname(gitWorktreePath));
35696
- return path22.dirname(gitDir);
35697
- }
35556
+ // src/git/changes/types.ts
35557
+ var MAX_PATCH_CHARS = 35e4;
35698
35558
 
35699
- // src/git/worktrees/worktree-remove.ts
35700
- async function gitWorktreeRemoveForce(worktreePath) {
35701
- const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
35702
- if (mainRepo) {
35703
- await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
35704
- } else {
35705
- fs20.rmSync(worktreePath, { recursive: true, force: true });
35706
- }
35559
+ // src/git/changes/lib/repo-format.ts
35560
+ function posixJoinDirFile(dir, file2) {
35561
+ const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
35562
+ const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
35563
+ return d ? `${d}/${f}` : f;
35707
35564
  }
35708
-
35709
- // src/worktrees/remove-session-worktrees.ts
35710
- async function removeSessionWorktrees(paths, log2) {
35711
- for (const wt of paths) {
35565
+ function formatRepoShortTitle(remoteUrl, repoRelPath) {
35566
+ const u = remoteUrl.trim();
35567
+ if (u) {
35712
35568
  try {
35713
- await gitWorktreeRemoveForce(wt);
35714
- log2(`[worktrees] Removed worktree ${wt}`);
35715
- } catch (e) {
35716
- log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
35717
- try {
35718
- fs21.rmSync(wt, { recursive: true, force: true });
35719
- } catch {
35569
+ if (u.startsWith("git@")) {
35570
+ const colon = u.indexOf(":");
35571
+ if (colon > 0) {
35572
+ const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
35573
+ if (pathPart.includes("/")) return pathPart;
35574
+ }
35575
+ } else {
35576
+ const parsed = new URL(u);
35577
+ const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
35578
+ const parts = p.split("/").filter(Boolean);
35579
+ if (parts.length >= 2) {
35580
+ return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
35581
+ }
35582
+ if (parts.length === 1) return parts[0];
35720
35583
  }
35584
+ } catch {
35721
35585
  }
35722
35586
  }
35723
- }
35724
-
35725
- // src/git/changes/lib/parse-git-status.ts
35726
- function parseNameStatusLines(lines) {
35727
- const m = /* @__PURE__ */ new Map();
35728
- for (const line of lines) {
35729
- if (!line.trim()) continue;
35730
- const tabParts = line.split(" ");
35731
- if (tabParts.length < 2) continue;
35732
- const status = tabParts[0].trim();
35733
- const code = status[0];
35734
- if (code === "A") {
35735
- m.set(tabParts[tabParts.length - 1], "added");
35736
- } else if (code === "D") {
35737
- m.set(tabParts[tabParts.length - 1], "removed");
35738
- } else if (code === "R" || code === "C") {
35739
- if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
35740
- } else if (code === "M" || code === "U" || code === "T") {
35741
- m.set(tabParts[tabParts.length - 1], "modified");
35742
- }
35743
- }
35744
- return m;
35745
- }
35746
- function parseNumstatFirstLine(line) {
35747
- const parts = line.split(" ");
35748
- if (parts.length < 3) return null;
35749
- const [a, d] = parts;
35750
- const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
35751
- const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
35752
- return { additions, deletions };
35753
- }
35754
- function parseNumstat(lines) {
35755
- const m = /* @__PURE__ */ new Map();
35756
- for (const line of lines) {
35757
- if (!line.trim()) continue;
35758
- const parts = line.split(" ");
35759
- if (parts.length < 3) continue;
35760
- const [a, d, p] = parts;
35761
- const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
35762
- const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
35763
- m.set(p, { additions, deletions });
35587
+ if (repoRelPath && repoRelPath !== ".") {
35588
+ const segments = repoRelPath.split("/").filter(Boolean);
35589
+ const last2 = segments[segments.length - 1];
35590
+ if (last2) return last2;
35764
35591
  }
35765
- return m;
35592
+ return "Repository";
35766
35593
  }
35767
- async function numstatFromGitNoIndex(g, pathInRepo) {
35768
- const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
35594
+ function formatRemoteDisplayLabel(remoteUrl) {
35595
+ const u = remoteUrl.trim();
35596
+ if (!u) return "";
35597
+ let hostPath = u;
35769
35598
  try {
35770
- const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
35771
- const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
35772
- return parseNumstatFirstLine(first2);
35599
+ if (u.startsWith("git@")) {
35600
+ const rest = u.slice("git@".length);
35601
+ const slash = rest.indexOf(":");
35602
+ if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
35603
+ } else {
35604
+ const parsed = new URL(u);
35605
+ hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
35606
+ }
35773
35607
  } catch {
35774
- return null;
35775
- }
35776
- }
35777
-
35778
- // src/git/changes/lib/working-tree-changed-path-count.ts
35779
- function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
35780
- const kindByPath = parseNameStatusLines(nameStatusLines);
35781
- const numByPath = parseNumstat(numstatLines);
35782
- const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
35783
- for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
35784
- paths.add(p);
35608
+ hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
35785
35609
  }
35786
- return paths.size;
35610
+ return `origin \xB7 ${hostPath}`;
35787
35611
  }
35788
35612
 
35789
- // src/git/changes/count-working-tree-changed-files.ts
35790
- async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
35791
- return runGitTask(async () => {
35792
- const g = cliSimpleGit(repoGitCwd);
35793
- const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
35794
- g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
35795
- g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
35796
- g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
35797
- ]);
35798
- return workingTreeChangedPathCount(
35799
- String(nameStatusRaw).split("\n"),
35800
- String(numstatRaw).split("\n"),
35801
- String(untrackedRaw).split("\n")
35802
- );
35803
- });
35804
- }
35613
+ // src/git/changes/get-working-tree-change-repo-details.ts
35614
+ import * as path21 from "node:path";
35805
35615
 
35806
35616
  // src/git/commits/resolve-remote-tracking.ts
35807
35617
  async function tryConfigGet(g, key) {
@@ -35901,96 +35711,6 @@ async function commitsAheadOfRemoteTracking(repoDir) {
35901
35711
  }
35902
35712
  }
35903
35713
 
35904
- // src/git/status/working-tree-status.ts
35905
- async function getRepoWorkingTreeStatus(repoDir) {
35906
- return runGitTask(async () => {
35907
- const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
35908
- const hasUncommittedChanges = uncommittedFileCount > 0;
35909
- const ahead = await commitsAheadOfRemoteTracking(repoDir);
35910
- return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
35911
- });
35912
- }
35913
- async function aggregateSessionPathsWorkingTreeStatus(paths) {
35914
- let hasUncommittedChanges = false;
35915
- let hasUnpushedCommits = false;
35916
- let uncommittedFileCount = 0;
35917
- await forEachWithGitYield(paths, async (p) => {
35918
- const s = await getRepoWorkingTreeStatus(p);
35919
- uncommittedFileCount += s.uncommittedFileCount;
35920
- if (s.hasUncommittedChanges) hasUncommittedChanges = true;
35921
- if (s.hasUnpushedCommits) hasUnpushedCommits = true;
35922
- });
35923
- return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
35924
- }
35925
- async function pushAheadOfUpstreamForPaths(paths) {
35926
- await forEachWithGitYield(paths, async (p) => {
35927
- const g = cliSimpleGit(p);
35928
- const ahead = await commitsAheadOfRemoteTracking(p);
35929
- if (ahead <= 0) return;
35930
- await g.push();
35931
- });
35932
- }
35933
-
35934
- // src/git/changes/types.ts
35935
- var MAX_PATCH_CHARS = 35e4;
35936
-
35937
- // src/git/changes/lib/repo-format.ts
35938
- function posixJoinDirFile(dir, file2) {
35939
- const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
35940
- const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
35941
- return d ? `${d}/${f}` : f;
35942
- }
35943
- function formatRepoShortTitle(remoteUrl, repoRelPath) {
35944
- const u = remoteUrl.trim();
35945
- if (u) {
35946
- try {
35947
- if (u.startsWith("git@")) {
35948
- const colon = u.indexOf(":");
35949
- if (colon > 0) {
35950
- const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
35951
- if (pathPart.includes("/")) return pathPart;
35952
- }
35953
- } else {
35954
- const parsed = new URL(u);
35955
- const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
35956
- const parts = p.split("/").filter(Boolean);
35957
- if (parts.length >= 2) {
35958
- return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
35959
- }
35960
- if (parts.length === 1) return parts[0];
35961
- }
35962
- } catch {
35963
- }
35964
- }
35965
- if (repoRelPath && repoRelPath !== ".") {
35966
- const segments = repoRelPath.split("/").filter(Boolean);
35967
- const last2 = segments[segments.length - 1];
35968
- if (last2) return last2;
35969
- }
35970
- return "Repository";
35971
- }
35972
- function formatRemoteDisplayLabel(remoteUrl) {
35973
- const u = remoteUrl.trim();
35974
- if (!u) return "";
35975
- let hostPath = u;
35976
- try {
35977
- if (u.startsWith("git@")) {
35978
- const rest = u.slice("git@".length);
35979
- const slash = rest.indexOf(":");
35980
- if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
35981
- } else {
35982
- const parsed = new URL(u);
35983
- hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
35984
- }
35985
- } catch {
35986
- hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
35987
- }
35988
- return `origin \xB7 ${hostPath}`;
35989
- }
35990
-
35991
- // src/git/changes/get-working-tree-change-repo-details.ts
35992
- import * as path24 from "node:path";
35993
-
35994
35714
  // src/git/commits/lib/parse-log-lines.ts
35995
35715
  function parseLogShaDateSubjectLines(raw) {
35996
35716
  const out = [];
@@ -36061,6 +35781,59 @@ async function listRecentCommits(repoDir, limitInput) {
36061
35781
  }
36062
35782
  }
36063
35783
 
35784
+ // src/git/changes/lib/parse-git-status.ts
35785
+ function parseNameStatusLines(lines) {
35786
+ const m = /* @__PURE__ */ new Map();
35787
+ for (const line of lines) {
35788
+ if (!line.trim()) continue;
35789
+ const tabParts = line.split(" ");
35790
+ if (tabParts.length < 2) continue;
35791
+ const status = tabParts[0].trim();
35792
+ const code = status[0];
35793
+ if (code === "A") {
35794
+ m.set(tabParts[tabParts.length - 1], "added");
35795
+ } else if (code === "D") {
35796
+ m.set(tabParts[tabParts.length - 1], "removed");
35797
+ } else if (code === "R" || code === "C") {
35798
+ if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
35799
+ } else if (code === "M" || code === "U" || code === "T") {
35800
+ m.set(tabParts[tabParts.length - 1], "modified");
35801
+ }
35802
+ }
35803
+ return m;
35804
+ }
35805
+ function parseNumstatFirstLine(line) {
35806
+ const parts = line.split(" ");
35807
+ if (parts.length < 3) return null;
35808
+ const [a, d] = parts;
35809
+ const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
35810
+ const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
35811
+ return { additions, deletions };
35812
+ }
35813
+ function parseNumstat(lines) {
35814
+ const m = /* @__PURE__ */ new Map();
35815
+ for (const line of lines) {
35816
+ if (!line.trim()) continue;
35817
+ const parts = line.split(" ");
35818
+ if (parts.length < 3) continue;
35819
+ const [a, d, p] = parts;
35820
+ const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
35821
+ const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
35822
+ m.set(p, { additions, deletions });
35823
+ }
35824
+ return m;
35825
+ }
35826
+ async function numstatFromGitNoIndex(g, pathInRepo) {
35827
+ const devNull = process.platform === "win32" ? "NUL" : "/dev/null";
35828
+ try {
35829
+ const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
35830
+ const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
35831
+ return parseNumstatFirstLine(first2);
35832
+ } catch {
35833
+ return null;
35834
+ }
35835
+ }
35836
+
36064
35837
  // src/git/changes/lib/patch-truncate.ts
36065
35838
  function truncatePatch(s) {
36066
35839
  if (s.length <= MAX_PATCH_CHARS) return s;
@@ -36126,8 +35899,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
36126
35899
  }
36127
35900
 
36128
35901
  // src/git/changes/list-changed-files-for-repo.ts
36129
- import * as fs23 from "node:fs";
36130
- import * as path23 from "node:path";
35902
+ import * as fs18 from "node:fs";
35903
+ import * as path20 from "node:path";
36131
35904
 
36132
35905
  // src/git/changes/lib/count-lines.ts
36133
35906
  import { createReadStream } from "node:fs";
@@ -36151,7 +35924,7 @@ async function countTextFileLines(filePath) {
36151
35924
  }
36152
35925
 
36153
35926
  // src/git/changes/hydrate-patch.ts
36154
- import * as fs22 from "node:fs";
35927
+ import * as fs17 from "node:fs";
36155
35928
  var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
36156
35929
  var MAX_HYDRATE_LINES_PER_GAP = 8e3;
36157
35930
  var MAX_HYDRATE_LINES_PER_FILE = 8e4;
@@ -36166,7 +35939,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
36166
35939
  }
36167
35940
  async function readWorktreeFileLines(filePath) {
36168
35941
  try {
36169
- const raw = await fs22.promises.readFile(filePath, "utf8");
35942
+ const raw = await fs17.promises.readFile(filePath, "utf8");
36170
35943
  return raw.split(/\r?\n/);
36171
35944
  } catch {
36172
35945
  return null;
@@ -36291,7 +36064,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
36291
36064
  const rows = [];
36292
36065
  await forEachWithGitYield([...paths], async (pathInRepo) => {
36293
36066
  const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
36294
- const repoFilePath = path23.join(repoGitCwd, pathInRepo);
36067
+ const repoFilePath = path20.join(repoGitCwd, pathInRepo);
36295
36068
  const nums = numByPath.get(pathInRepo);
36296
36069
  let additions = nums?.additions ?? 0;
36297
36070
  let deletions = nums?.deletions ?? 0;
@@ -36304,7 +36077,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
36304
36077
  deletions = fromGit.deletions;
36305
36078
  } else {
36306
36079
  try {
36307
- const st = await fs23.promises.stat(repoFilePath);
36080
+ const st = await fs18.promises.stat(repoFilePath);
36308
36081
  if (st.isFile()) additions = await countTextFileLines(repoFilePath);
36309
36082
  else additions = 0;
36310
36083
  } catch {
@@ -36330,7 +36103,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
36330
36103
  } else {
36331
36104
  pathInRepo = row.pathRelLauncher;
36332
36105
  }
36333
- const filePath = path23.join(repoGitCwd, pathInRepo);
36106
+ const filePath = path20.join(repoGitCwd, pathInRepo);
36334
36107
  let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
36335
36108
  if (patch) {
36336
36109
  patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
@@ -36346,8 +36119,8 @@ function normRepoRel(p) {
36346
36119
  return x === "" ? "." : x;
36347
36120
  }
36348
36121
  async function getWorkingTreeChangeRepoDetails(options) {
36349
- const bridgeRoot = path24.resolve(getBridgeRoot());
36350
- const sessionWtRoot = options.sessionWorktreeRootPath ? path24.resolve(options.sessionWorktreeRootPath) : null;
36122
+ const bridgeRoot = path21.resolve(getBridgeRoot());
36123
+ const sessionWtRoot = options.sessionWorktreeRootPath ? path21.resolve(options.sessionWorktreeRootPath) : null;
36351
36124
  const legacyNested = options.legacyRepoNestedSessionLayout === true;
36352
36125
  const out = [];
36353
36126
  const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
@@ -36362,7 +36135,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
36362
36135
  for (let i = 0; i < options.commitTargetPaths.length; i++) {
36363
36136
  if (i > 0) await yieldToEventLoop2();
36364
36137
  const target = options.commitTargetPaths[i];
36365
- const t = path24.resolve(target);
36138
+ const t = path21.resolve(target);
36366
36139
  if (!await isGitRepoDirectory(t)) continue;
36367
36140
  const g = cliSimpleGit(t);
36368
36141
  let branch = "HEAD";
@@ -36375,8 +36148,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
36375
36148
  const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
36376
36149
  let repoRelPath;
36377
36150
  if (sessionWtRoot) {
36378
- const anchor = legacyNested ? path24.dirname(t) : t;
36379
- const relNorm = path24.relative(sessionWtRoot, anchor);
36151
+ const anchor = legacyNested ? path21.dirname(t) : t;
36152
+ const relNorm = path21.relative(sessionWtRoot, anchor);
36380
36153
  repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
36381
36154
  } else {
36382
36155
  let top = t;
@@ -36385,8 +36158,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
36385
36158
  } catch {
36386
36159
  top = t;
36387
36160
  }
36388
- const rel = path24.relative(bridgeRoot, path24.resolve(top)).replace(/\\/g, "/") || ".";
36389
- repoRelPath = rel.startsWith("..") ? path24.basename(path24.resolve(top)) : rel;
36161
+ const rel = path21.relative(bridgeRoot, path21.resolve(top)).replace(/\\/g, "/") || ".";
36162
+ repoRelPath = rel.startsWith("..") ? path21.basename(path21.resolve(top)) : rel;
36390
36163
  }
36391
36164
  const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
36392
36165
  if (filter && norm !== filter) continue;
@@ -36418,6 +36191,64 @@ async function getWorkingTreeChangeRepoDetails(options) {
36418
36191
  return out;
36419
36192
  }
36420
36193
 
36194
+ // src/git/changes/lib/working-tree-changed-path-count.ts
36195
+ function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
36196
+ const kindByPath = parseNameStatusLines(nameStatusLines);
36197
+ const numByPath = parseNumstat(numstatLines);
36198
+ const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
36199
+ for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
36200
+ paths.add(p);
36201
+ }
36202
+ return paths.size;
36203
+ }
36204
+
36205
+ // src/git/changes/count-working-tree-changed-files.ts
36206
+ async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
36207
+ return runGitTask(async () => {
36208
+ const g = cliSimpleGit(repoGitCwd);
36209
+ const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
36210
+ g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
36211
+ g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
36212
+ g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
36213
+ ]);
36214
+ return workingTreeChangedPathCount(
36215
+ String(nameStatusRaw).split("\n"),
36216
+ String(numstatRaw).split("\n"),
36217
+ String(untrackedRaw).split("\n")
36218
+ );
36219
+ });
36220
+ }
36221
+
36222
+ // src/git/status/working-tree-status.ts
36223
+ async function getRepoWorkingTreeStatus(repoDir) {
36224
+ return runGitTask(async () => {
36225
+ const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
36226
+ const hasUncommittedChanges = uncommittedFileCount > 0;
36227
+ const ahead = await commitsAheadOfRemoteTracking(repoDir);
36228
+ return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
36229
+ });
36230
+ }
36231
+ async function aggregateSessionPathsWorkingTreeStatus(paths) {
36232
+ let hasUncommittedChanges = false;
36233
+ let hasUnpushedCommits = false;
36234
+ let uncommittedFileCount = 0;
36235
+ await forEachWithGitYield(paths, async (p) => {
36236
+ const s = await getRepoWorkingTreeStatus(p);
36237
+ uncommittedFileCount += s.uncommittedFileCount;
36238
+ if (s.hasUncommittedChanges) hasUncommittedChanges = true;
36239
+ if (s.hasUnpushedCommits) hasUnpushedCommits = true;
36240
+ });
36241
+ return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
36242
+ }
36243
+ async function pushAheadOfUpstreamForPaths(paths) {
36244
+ await forEachWithGitYield(paths, async (p) => {
36245
+ const g = cliSimpleGit(p);
36246
+ const ahead = await commitsAheadOfRemoteTracking(p);
36247
+ if (ahead <= 0) return;
36248
+ await g.push();
36249
+ });
36250
+ }
36251
+
36421
36252
  // src/git/branches/commit-and-push.ts
36422
36253
  async function gitCommitAllIfDirty(repoDir, message, options) {
36423
36254
  const g = cliSimpleGit(repoDir);
@@ -36455,12 +36286,137 @@ async function commitSessionWorktrees(options) {
36455
36286
  }
36456
36287
  }
36457
36288
 
36289
+ // src/worktrees/remove-session-worktrees.ts
36290
+ import * as fs21 from "node:fs";
36291
+
36292
+ // src/git/worktrees/worktree-remove.ts
36293
+ import * as fs20 from "node:fs";
36294
+
36295
+ // src/git/worktrees/resolve-main-repo-from-git-file.ts
36296
+ import * as fs19 from "node:fs";
36297
+ import * as path22 from "node:path";
36298
+ function resolveMainRepoFromWorktreeGitFile(wt) {
36299
+ const gitDirFile = path22.join(wt, ".git");
36300
+ if (!fs19.existsSync(gitDirFile) || !fs19.statSync(gitDirFile).isFile()) return "";
36301
+ const first2 = fs19.readFileSync(gitDirFile, "utf8").trim();
36302
+ const m = first2.match(/^gitdir:\s*(.+)$/im);
36303
+ if (!m) return "";
36304
+ const gitWorktreePath = path22.resolve(wt, m[1].trim());
36305
+ const gitDir = path22.dirname(path22.dirname(gitWorktreePath));
36306
+ return path22.dirname(gitDir);
36307
+ }
36308
+
36309
+ // src/git/worktrees/worktree-remove.ts
36310
+ async function gitWorktreeRemoveForce(worktreePath) {
36311
+ const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
36312
+ if (mainRepo) {
36313
+ await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
36314
+ } else {
36315
+ fs20.rmSync(worktreePath, { recursive: true, force: true });
36316
+ }
36317
+ }
36318
+
36319
+ // src/worktrees/remove-session-worktrees.ts
36320
+ async function removeSessionWorktrees(paths, log2) {
36321
+ for (const wt of paths) {
36322
+ try {
36323
+ await gitWorktreeRemoveForce(wt);
36324
+ log2(`[worktrees] Removed worktree ${wt}`);
36325
+ } catch (e) {
36326
+ log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
36327
+ try {
36328
+ fs21.rmSync(wt, { recursive: true, force: true });
36329
+ } catch {
36330
+ }
36331
+ }
36332
+ }
36333
+ }
36334
+
36335
+ // src/git/branches/rename-branch.ts
36336
+ async function gitRenameCurrentBranch(repoDir, newName) {
36337
+ const g = cliSimpleGit(repoDir);
36338
+ await g.raw(["branch", "-m", newName]);
36339
+ }
36340
+
36341
+ // src/worktrees/rename-session-worktree-branches.ts
36342
+ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
36343
+ const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
36344
+ for (const wt of paths) {
36345
+ try {
36346
+ await gitRenameCurrentBranch(wt, safe);
36347
+ log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
36348
+ } catch (e) {
36349
+ log2(
36350
+ `[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
36351
+ );
36352
+ }
36353
+ }
36354
+ }
36355
+
36356
+ // src/worktrees/worktree-layout-file.ts
36357
+ import * as fs22 from "node:fs";
36358
+ import * as path23 from "node:path";
36359
+ import os7 from "node:os";
36360
+ var LAYOUT_FILENAME = "worktree-launcher-layout.json";
36361
+ function defaultWorktreeLayoutPath() {
36362
+ return path23.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
36363
+ }
36364
+ function normalizeLoadedLayout(raw) {
36365
+ if (raw && typeof raw === "object" && "launcherCwds" in raw) {
36366
+ const j = raw;
36367
+ if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
36368
+ }
36369
+ return { launcherCwds: [] };
36370
+ }
36371
+ function loadWorktreeLayout() {
36372
+ try {
36373
+ const p = defaultWorktreeLayoutPath();
36374
+ if (!fs22.existsSync(p)) return { launcherCwds: [] };
36375
+ const raw = JSON.parse(fs22.readFileSync(p, "utf8"));
36376
+ return normalizeLoadedLayout(raw);
36377
+ } catch {
36378
+ return { launcherCwds: [] };
36379
+ }
36380
+ }
36381
+ function saveWorktreeLayout(layout) {
36382
+ try {
36383
+ const dir = path23.dirname(defaultWorktreeLayoutPath());
36384
+ fs22.mkdirSync(dir, { recursive: true });
36385
+ fs22.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
36386
+ } catch {
36387
+ }
36388
+ }
36389
+ function baseNameSafe(pathString) {
36390
+ return path23.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
36391
+ }
36392
+ function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
36393
+ const norm = path23.resolve(bridgeRootPath2);
36394
+ const existing = layout.launcherCwds.find((e) => path23.resolve(e.absolutePath) === norm);
36395
+ return existing?.dirName;
36396
+ }
36397
+ function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
36398
+ const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
36399
+ if (existing) return existing;
36400
+ const norm = path23.resolve(bridgeRootPath2);
36401
+ const base = baseNameSafe(norm);
36402
+ const used = new Set(layout.launcherCwds.map((e) => e.dirName));
36403
+ let name = base;
36404
+ let n = 2;
36405
+ while (used.has(name)) {
36406
+ name = `${base}-${n}`;
36407
+ n += 1;
36408
+ }
36409
+ layout.launcherCwds.push({ absolutePath: norm, dirName: name });
36410
+ saveWorktreeLayout(layout);
36411
+ return name;
36412
+ }
36413
+
36458
36414
  // src/worktrees/discover-session-worktree-on-disk.ts
36459
- import * as fs24 from "node:fs";
36460
- import * as path25 from "node:path";
36415
+ import * as fs23 from "node:fs";
36416
+ import * as path24 from "node:path";
36461
36417
  function isGitDir(dirPath) {
36462
36418
  try {
36463
- return fs24.existsSync(path25.join(dirPath, ".git"));
36419
+ return fs23.existsSync(path24.join(dirPath, ".git"));
36464
36420
  } catch {
36465
36421
  return false;
36466
36422
  }
@@ -36469,23 +36425,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
36469
36425
  const out = [];
36470
36426
  const walk = (dir) => {
36471
36427
  if (isGitDir(dir)) {
36472
- out.push(path25.resolve(dir));
36428
+ out.push(path24.resolve(dir));
36473
36429
  return;
36474
36430
  }
36475
36431
  let entries;
36476
36432
  try {
36477
- entries = fs24.readdirSync(dir, { withFileTypes: true });
36433
+ entries = fs23.readdirSync(dir, { withFileTypes: true });
36478
36434
  } catch {
36479
36435
  return;
36480
36436
  }
36481
36437
  for (const e of entries) {
36482
36438
  if (e.name.startsWith(".")) continue;
36483
- const full = path25.join(dir, e.name);
36439
+ const full = path24.join(dir, e.name);
36484
36440
  if (!e.isDirectory()) continue;
36485
36441
  walk(full);
36486
36442
  }
36487
36443
  };
36488
- walk(path25.resolve(rootPath));
36444
+ walk(path24.resolve(rootPath));
36489
36445
  return [...new Set(out)];
36490
36446
  }
36491
36447
  function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
@@ -36494,16 +36450,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
36494
36450
  if (depth > maxDepth) return;
36495
36451
  let entries;
36496
36452
  try {
36497
- entries = fs24.readdirSync(dir, { withFileTypes: true });
36453
+ entries = fs23.readdirSync(dir, { withFileTypes: true });
36498
36454
  } catch {
36499
36455
  return;
36500
36456
  }
36501
36457
  for (const e of entries) {
36502
36458
  if (e.name.startsWith(".")) continue;
36503
- const full = path25.join(dir, e.name);
36459
+ const full = path24.join(dir, e.name);
36504
36460
  if (!e.isDirectory()) continue;
36505
36461
  if (e.name === sessionId) {
36506
- if (isGitDir(full)) out.push(path25.resolve(full));
36462
+ if (isGitDir(full)) out.push(path24.resolve(full));
36507
36463
  } else {
36508
36464
  walk(full, depth + 1);
36509
36465
  }
@@ -36515,14 +36471,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
36515
36471
  function tryBindingFromSessionDirectory(sessionDir) {
36516
36472
  let st;
36517
36473
  try {
36518
- st = fs24.statSync(sessionDir);
36474
+ st = fs23.statSync(sessionDir);
36519
36475
  } catch {
36520
36476
  return null;
36521
36477
  }
36522
36478
  if (!st.isDirectory()) return null;
36523
36479
  const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
36524
36480
  if (worktreePaths.length === 0) return null;
36525
- const abs = path25.resolve(sessionDir);
36481
+ const abs = path24.resolve(sessionDir);
36526
36482
  return {
36527
36483
  sessionParentPath: abs,
36528
36484
  workingTreeRelRoot: abs,
@@ -36532,20 +36488,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
36532
36488
  function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
36533
36489
  const sid = sessionId.trim();
36534
36490
  if (!sid) return null;
36535
- const hintR = path25.resolve(checkoutPath);
36491
+ const hintR = path24.resolve(checkoutPath);
36536
36492
  let best = null;
36537
- let cur = path25.dirname(hintR);
36493
+ let cur = path24.dirname(hintR);
36538
36494
  for (let i = 0; i < 40; i++) {
36539
36495
  const paths = collectWorktreeRootsNamed(cur, sid, 24);
36540
- if (paths.some((p) => path25.resolve(p) === hintR)) {
36541
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path25.resolve(paths[0]);
36496
+ if (paths.some((p) => path24.resolve(p) === hintR)) {
36497
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(paths) ?? path24.resolve(paths[0]);
36542
36498
  best = {
36543
- sessionParentPath: path25.resolve(isolated),
36544
- workingTreeRelRoot: path25.resolve(cur),
36545
- repoCheckoutPaths: paths.map((p) => path25.resolve(p))
36499
+ sessionParentPath: path24.resolve(isolated),
36500
+ workingTreeRelRoot: path24.resolve(cur),
36501
+ repoCheckoutPaths: paths.map((p) => path24.resolve(p))
36546
36502
  };
36547
36503
  }
36548
- const next = path25.dirname(cur);
36504
+ const next = path24.dirname(cur);
36549
36505
  if (next === cur) break;
36550
36506
  cur = next;
36551
36507
  }
@@ -36553,33 +36509,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
36553
36509
  }
36554
36510
  function discoverSessionWorktreeOnDisk(options) {
36555
36511
  const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
36556
- if (!sessionId.trim() || !fs24.existsSync(worktreesRootPath)) return null;
36512
+ if (!sessionId.trim() || !fs23.existsSync(worktreesRootPath)) return null;
36557
36513
  const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
36558
36514
  const keys = [];
36559
36515
  if (preferredKey) keys.push(preferredKey);
36560
36516
  try {
36561
- for (const name of fs24.readdirSync(worktreesRootPath)) {
36517
+ for (const name of fs23.readdirSync(worktreesRootPath)) {
36562
36518
  if (name.startsWith(".")) continue;
36563
- const p = path25.join(worktreesRootPath, name);
36564
- if (!fs24.statSync(p).isDirectory()) continue;
36519
+ const p = path24.join(worktreesRootPath, name);
36520
+ if (!fs23.statSync(p).isDirectory()) continue;
36565
36521
  if (name !== preferredKey) keys.push(name);
36566
36522
  }
36567
36523
  } catch {
36568
36524
  return null;
36569
36525
  }
36570
36526
  for (const key of keys) {
36571
- const layoutRoot = path25.join(worktreesRootPath, key);
36572
- if (!fs24.existsSync(layoutRoot) || !fs24.statSync(layoutRoot).isDirectory()) continue;
36573
- const sessionDir = path25.join(layoutRoot, sessionId);
36527
+ const layoutRoot = path24.join(worktreesRootPath, key);
36528
+ if (!fs23.existsSync(layoutRoot) || !fs23.statSync(layoutRoot).isDirectory()) continue;
36529
+ const sessionDir = path24.join(layoutRoot, sessionId);
36574
36530
  const nested = tryBindingFromSessionDirectory(sessionDir);
36575
36531
  if (nested) return nested;
36576
36532
  const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
36577
36533
  if (legacyPaths.length > 0) {
36578
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
36534
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
36579
36535
  return {
36580
- sessionParentPath: path25.resolve(isolated),
36581
- workingTreeRelRoot: path25.resolve(layoutRoot),
36582
- repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
36536
+ sessionParentPath: path24.resolve(isolated),
36537
+ workingTreeRelRoot: path24.resolve(layoutRoot),
36538
+ repoCheckoutPaths: legacyPaths.map((p) => path24.resolve(p))
36583
36539
  };
36584
36540
  }
36585
36541
  }
@@ -36588,12 +36544,12 @@ function discoverSessionWorktreeOnDisk(options) {
36588
36544
  function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
36589
36545
  const sid = sessionId.trim();
36590
36546
  if (!sid) return null;
36591
- const hint = path25.resolve(sessionWorktreeRootPathOrHint);
36592
- const underHint = tryBindingFromSessionDirectory(path25.join(hint, sid));
36547
+ const hint = path24.resolve(sessionWorktreeRootPathOrHint);
36548
+ const underHint = tryBindingFromSessionDirectory(path24.join(hint, sid));
36593
36549
  if (underHint) return underHint;
36594
36550
  const direct = tryBindingFromSessionDirectory(hint);
36595
36551
  if (direct) {
36596
- if (path25.basename(hint) === sid && isGitDir(hint)) {
36552
+ if (path24.basename(hint) === sid && isGitDir(hint)) {
36597
36553
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
36598
36554
  if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
36599
36555
  return legacyFromCheckout;
@@ -36601,216 +36557,349 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
36601
36557
  }
36602
36558
  return direct;
36603
36559
  }
36604
- if (path25.basename(hint) === sid && isGitDir(hint)) {
36560
+ if (path24.basename(hint) === sid && isGitDir(hint)) {
36605
36561
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
36606
36562
  if (legacyFromCheckout) return legacyFromCheckout;
36607
36563
  }
36608
36564
  let st;
36609
36565
  try {
36610
- st = fs24.statSync(hint);
36566
+ st = fs23.statSync(hint);
36611
36567
  } catch {
36612
36568
  return null;
36613
36569
  }
36614
36570
  if (!st.isDirectory()) return null;
36615
36571
  const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
36616
36572
  if (legacyPaths.length === 0) return null;
36617
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
36573
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
36618
36574
  return {
36619
- sessionParentPath: path25.resolve(isolated),
36575
+ sessionParentPath: path24.resolve(isolated),
36620
36576
  workingTreeRelRoot: hint,
36621
- repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
36577
+ repoCheckoutPaths: legacyPaths.map((p) => path24.resolve(p))
36622
36578
  };
36623
36579
  }
36624
36580
 
36625
- // src/worktrees/session-worktree-manager.ts
36581
+ // src/worktrees/manager/discover-session-binding.ts
36582
+ function discoverSessionBinding(params) {
36583
+ return discoverSessionWorktreeOnDisk({
36584
+ sessionId: params.sessionId,
36585
+ worktreesRootPath: params.worktreesRootPath,
36586
+ layout: params.layout,
36587
+ bridgeRoot: getBridgeRoot()
36588
+ });
36589
+ }
36590
+
36591
+ // src/worktrees/manager/resolve-isolated-session-parent-path.ts
36592
+ function resolveIsolatedSessionParentPath(sessionId, cache2, ensureRepoCheckoutPaths) {
36593
+ if (!sessionId) return null;
36594
+ const sid = sessionId.trim();
36595
+ const cached2 = cache2.getSessionParentPath(sid);
36596
+ if (cached2) return cached2;
36597
+ const paths = ensureRepoCheckoutPaths(sid);
36598
+ if (!paths?.length) return null;
36599
+ return resolveIsolatedSessionParentPathFromCheckouts(paths);
36600
+ }
36601
+ function ensureRepoCheckoutPathsForSession(sessionId, cache2, discover) {
36602
+ if (!sessionId?.trim()) return void 0;
36603
+ const sid = sessionId.trim();
36604
+ const cached2 = cache2.getRepoCheckoutPaths(sid);
36605
+ if (cached2?.length) return cached2;
36606
+ const disc = discover(sid);
36607
+ if (disc?.repoCheckoutPaths.length) {
36608
+ cache2.remember(sid, disc);
36609
+ return [...disc.repoCheckoutPaths];
36610
+ }
36611
+ return void 0;
36612
+ }
36613
+
36614
+ // src/worktrees/manager/resolve-commit-targets.ts
36615
+ function resolveCommitTargets(sessionId, cache2, discover) {
36616
+ const paths = cache2.getRepoCheckoutPathsRef(sessionId);
36617
+ if (paths?.length) return paths;
36618
+ const disc = discover(sessionId);
36619
+ if (disc?.repoCheckoutPaths.length) {
36620
+ cache2.remember(sessionId, disc);
36621
+ return disc.repoCheckoutPaths;
36622
+ }
36623
+ return [getBridgeRoot()];
36624
+ }
36625
+
36626
+ // src/worktrees/manager/parse-session-parent.ts
36626
36627
  function parseSessionParent(v) {
36627
36628
  if (v === "bridge_root" || v === "worktrees_root") return v;
36628
36629
  if (v === "session_worktrees_root") return "worktrees_root";
36629
36630
  return null;
36630
36631
  }
36631
- var SessionWorktreeManager = class {
36632
- worktreesRootPath;
36633
- log;
36632
+
36633
+ // src/worktrees/prepare-new-session-worktrees.ts
36634
+ import * as fs24 from "node:fs";
36635
+ import * as path25 from "node:path";
36636
+
36637
+ // src/git/worktrees/worktree-add.ts
36638
+ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch, baseRef = "HEAD") {
36639
+ const mainGit = cliSimpleGit(mainRepoPath);
36640
+ const base = baseRef.trim() || "HEAD";
36641
+ await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, base]);
36642
+ }
36643
+
36644
+ // src/worktrees/prepare-new-session-worktrees.ts
36645
+ function normalizeRepoRelPath(rel) {
36646
+ return rel === "" ? "." : rel.replace(/\\/g, "/");
36647
+ }
36648
+ function resolveBaseRefForRepo(relNorm, baseBranches) {
36649
+ if (!baseBranches) return "HEAD";
36650
+ const direct = baseBranches[relNorm]?.trim();
36651
+ if (direct) return direct;
36652
+ if (relNorm !== "." && baseBranches["."]?.trim()) return baseBranches["."].trim();
36653
+ return "HEAD";
36654
+ }
36655
+ async function prepareNewSessionWorktrees(options) {
36656
+ const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2, worktreeBaseBranches } = options;
36657
+ const bridgeResolved = path25.resolve(bridgeRoot);
36658
+ const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
36659
+ const bridgeKeyDir = path25.join(worktreesRootPath, cwdKey);
36660
+ const sessionDir = path25.join(bridgeKeyDir, sessionId);
36661
+ const repos = await discoverGitReposUnderRoot(bridgeResolved);
36662
+ if (repos.length === 0) {
36663
+ log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
36664
+ return null;
36665
+ }
36666
+ const branch = `session-${sessionId}`;
36667
+ const worktreePaths = [];
36668
+ fs24.mkdirSync(sessionDir, { recursive: true });
36669
+ for (const repo of repos) {
36670
+ let rel = path25.relative(bridgeResolved, repo.absolutePath);
36671
+ if (rel.startsWith("..") || path25.isAbsolute(rel)) continue;
36672
+ const relNorm = normalizeRepoRelPath(rel === "" ? "." : rel);
36673
+ const wtPath = relNorm === "." ? sessionDir : path25.join(sessionDir, relNorm);
36674
+ if (relNorm !== ".") {
36675
+ fs24.mkdirSync(path25.dirname(wtPath), { recursive: true });
36676
+ }
36677
+ const baseRef = resolveBaseRefForRepo(relNorm, worktreeBaseBranches);
36678
+ try {
36679
+ await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch, baseRef);
36680
+ log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}, base ${baseRef}).`);
36681
+ worktreePaths.push(wtPath);
36682
+ } catch (e) {
36683
+ log2(
36684
+ `[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
36685
+ );
36686
+ }
36687
+ }
36688
+ if (worktreePaths.length === 0) return null;
36689
+ return {
36690
+ worktreePaths,
36691
+ sessionParentPath: sessionDir,
36692
+ workingTreeRelRoot: sessionDir
36693
+ };
36694
+ }
36695
+
36696
+ // src/worktrees/manager/prepare-and-remember-session-worktrees.ts
36697
+ async function prepareAndRememberSessionWorktrees(params) {
36698
+ const prep = await prepareNewSessionWorktrees({
36699
+ worktreesRootPath: params.worktreesRootPath,
36700
+ bridgeRoot: getBridgeRoot(),
36701
+ sessionId: params.sessionId,
36702
+ layout: params.layout,
36703
+ log: params.log,
36704
+ ...params.worktreeBaseBranches && Object.keys(params.worktreeBaseBranches).length > 0 ? { worktreeBaseBranches: params.worktreeBaseBranches } : {}
36705
+ });
36706
+ if (!prep) return void 0;
36707
+ params.cache.remember(params.sessionId, {
36708
+ sessionParentPath: prep.sessionParentPath,
36709
+ workingTreeRelRoot: prep.workingTreeRelRoot,
36710
+ repoCheckoutPaths: prep.worktreePaths
36711
+ });
36712
+ return params.cache.getSessionParentPath(params.sessionId);
36713
+ }
36714
+
36715
+ // src/worktrees/manager/resolve-existing-session-parent-path.ts
36716
+ function resolveExistingSessionParentPath(sessionId, cache2, discover) {
36717
+ const cached2 = cache2.getSessionParentPath(sessionId);
36718
+ if (cached2) return cached2;
36719
+ const disc = discover();
36720
+ if (disc) {
36721
+ cache2.remember(sessionId, disc);
36722
+ return cache2.getSessionParentPath(sessionId);
36723
+ }
36724
+ return void 0;
36725
+ }
36726
+
36727
+ // src/worktrees/manager/resolve-explicit-session-parent-path.ts
36728
+ import * as path26 from "node:path";
36729
+ function resolveExplicitSessionParentPath(params) {
36730
+ const resolved = path26.resolve(params.parentPathRaw);
36731
+ if (parseSessionParent(params.sessionParent) !== "worktrees_root") {
36732
+ return resolved;
36733
+ }
36734
+ const rememberAndReturn = (binding) => {
36735
+ params.cache.remember(params.sessionId, binding);
36736
+ return params.cache.getSessionParentPath(params.sessionId) ?? resolved;
36737
+ };
36738
+ const diskFirst = params.discover();
36739
+ if (diskFirst) return rememberAndReturn(diskFirst);
36740
+ const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, params.sessionId);
36741
+ if (fromRoot) return rememberAndReturn(fromRoot);
36742
+ let cur = resolved;
36743
+ for (let i = 0; i < 16; i++) {
36744
+ const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, params.sessionId);
36745
+ if (tryRoot) return rememberAndReturn(tryRoot);
36746
+ const next = path26.dirname(cur);
36747
+ if (next === cur) break;
36748
+ cur = next;
36749
+ }
36750
+ return resolved;
36751
+ }
36752
+
36753
+ // src/worktrees/manager/resolve-session-parent-path-for-prompt.ts
36754
+ async function resolveSessionParentPathForPrompt(params) {
36755
+ const { sessionId, cache: cache2, worktreesRootPath, layout, log: log2, discover, opts } = params;
36756
+ if (!sessionId) return void 0;
36757
+ const sid = sessionId.trim();
36758
+ const parentPathRaw = opts.sessionParentPath?.trim();
36759
+ if (parentPathRaw) {
36760
+ return resolveExplicitSessionParentPath({
36761
+ sessionId: sid,
36762
+ sessionParent: opts.sessionParent,
36763
+ parentPathRaw,
36764
+ cache: cache2,
36765
+ discover: () => discover(sid)
36766
+ });
36767
+ }
36768
+ const parentKind = parseSessionParent(opts.sessionParent);
36769
+ if (parentKind === "bridge_root") {
36770
+ return void 0;
36771
+ }
36772
+ if (parentKind === "worktrees_root") {
36773
+ if (!opts.isNewSession) {
36774
+ return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
36775
+ }
36776
+ return prepareAndRememberSessionWorktrees({
36777
+ cache: cache2,
36778
+ sessionId: sid,
36779
+ worktreesRootPath,
36780
+ layout,
36781
+ log: log2,
36782
+ ...opts.worktreeBaseBranches ? { worktreeBaseBranches: opts.worktreeBaseBranches } : {}
36783
+ });
36784
+ }
36785
+ if (!opts.isNewSession) {
36786
+ return resolveExistingSessionParentPath(sid, cache2, () => discover(sid));
36787
+ }
36788
+ return prepareAndRememberSessionWorktrees({
36789
+ cache: cache2,
36790
+ sessionId: sid,
36791
+ worktreesRootPath,
36792
+ layout,
36793
+ log: log2
36794
+ });
36795
+ }
36796
+
36797
+ // src/worktrees/manager/session-worktree-cache.ts
36798
+ import * as path27 from "node:path";
36799
+ var SessionWorktreeCache = class {
36634
36800
  sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
36635
36801
  sessionParentPathBySession = /* @__PURE__ */ new Map();
36636
36802
  sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
36637
- layout;
36638
- constructor(options) {
36639
- this.worktreesRootPath = options.worktreesRootPath;
36640
- this.log = options.log;
36641
- this.layout = loadWorktreeLayout();
36642
- }
36643
- rememberSessionWorktrees(sessionId, binding) {
36644
- const paths = binding.repoCheckoutPaths.map((p) => path26.resolve(p));
36803
+ remember(sessionId, binding) {
36804
+ const paths = binding.repoCheckoutPaths.map((p) => path27.resolve(p));
36645
36805
  this.sessionRepoCheckoutPaths.set(sessionId, paths);
36646
- this.sessionParentPathBySession.set(sessionId, path26.resolve(binding.sessionParentPath));
36647
- this.sessionWorkingTreeRelRootBySession.set(sessionId, path26.resolve(binding.workingTreeRelRoot));
36806
+ this.sessionParentPathBySession.set(sessionId, path27.resolve(binding.sessionParentPath));
36807
+ this.sessionWorkingTreeRelRootBySession.set(sessionId, path27.resolve(binding.workingTreeRelRoot));
36808
+ }
36809
+ clearSession(sessionId) {
36810
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36811
+ this.sessionRepoCheckoutPaths.delete(sessionId);
36812
+ this.sessionParentPathBySession.delete(sessionId);
36813
+ this.sessionWorkingTreeRelRootBySession.delete(sessionId);
36814
+ return paths;
36648
36815
  }
36649
- sessionParentPathAfterRemember(sessionId) {
36816
+ getSessionParentPath(sessionId) {
36650
36817
  return this.sessionParentPathBySession.get(sessionId);
36651
36818
  }
36652
- tryDiscoverFromDisk(sessionId) {
36653
- return discoverSessionWorktreeOnDisk({
36654
- sessionId,
36655
- worktreesRootPath: this.worktreesRootPath,
36656
- layout: this.layout,
36657
- bridgeRoot: getBridgeRoot()
36658
- });
36819
+ getWorkingTreeRelRoot(sessionId) {
36820
+ return this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
36821
+ }
36822
+ hasSession(sessionId) {
36823
+ return this.sessionParentPathBySession.has(sessionId);
36824
+ }
36825
+ getRepoCheckoutPaths(sessionId) {
36826
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36827
+ return paths?.length ? [...paths] : void 0;
36828
+ }
36829
+ getRepoCheckoutPathsRef(sessionId) {
36830
+ const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36831
+ return paths?.length ? paths : void 0;
36659
36832
  }
36660
36833
  isLegacyNestedLayout(sessionId) {
36661
36834
  const parent = this.sessionParentPathBySession.get(sessionId);
36662
36835
  const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
36663
36836
  if (!parent || !relRoot) return false;
36664
- return path26.resolve(parent) !== path26.resolve(relRoot);
36837
+ return path27.resolve(parent) !== path27.resolve(relRoot);
36838
+ }
36839
+ };
36840
+
36841
+ // src/worktrees/manager/manager.ts
36842
+ var SessionWorktreeManager = class {
36843
+ worktreesRootPath;
36844
+ log;
36845
+ cache = new SessionWorktreeCache();
36846
+ layout;
36847
+ constructor(options) {
36848
+ this.worktreesRootPath = options.worktreesRootPath;
36849
+ this.log = options.log;
36850
+ this.layout = loadWorktreeLayout();
36851
+ }
36852
+ discover(sessionId) {
36853
+ return discoverSessionBinding({
36854
+ sessionId,
36855
+ worktreesRootPath: this.worktreesRootPath,
36856
+ layout: this.layout
36857
+ });
36665
36858
  }
36666
- /**
36667
- * Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
36668
- */
36669
36859
  getIsolatedSessionParentPathForSession(sessionId) {
36670
- if (!sessionId) return null;
36671
- const sid = sessionId.trim();
36672
- const cached2 = this.sessionParentPathBySession.get(sid);
36673
- if (cached2) return path26.resolve(cached2);
36674
- const paths = this.ensureRepoCheckoutPathsForSession(sid) ?? this.getRepoCheckoutPathsForSession(sid);
36675
- if (!paths?.length) return null;
36676
- return resolveIsolatedSessionParentPathFromCheckouts(paths);
36677
- }
36678
- /**
36679
- * Resolved **session parent path** for the agent: session directory in worktrees mode,
36680
- * or `undefined` meaning use {@link getBridgeRoot}.
36681
- */
36682
- async resolveSessionParentPathForPrompt(sessionId, opts) {
36683
- if (!sessionId) return void 0;
36684
- const sid = sessionId.trim();
36685
- const parentPathRaw = opts.sessionParentPath?.trim();
36686
- if (parentPathRaw) {
36687
- const resolved = path26.resolve(parentPathRaw);
36688
- if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
36689
- const diskFirst = this.tryDiscoverFromDisk(sid);
36690
- if (diskFirst) {
36691
- this.rememberSessionWorktrees(sid, diskFirst);
36692
- return this.sessionParentPathAfterRemember(sid);
36693
- }
36694
- const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, sid);
36695
- if (fromRoot) {
36696
- this.rememberSessionWorktrees(sid, fromRoot);
36697
- return this.sessionParentPathAfterRemember(sid);
36698
- }
36699
- let cur = resolved;
36700
- for (let i = 0; i < 16; i++) {
36701
- const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, sid);
36702
- if (tryRoot) {
36703
- this.rememberSessionWorktrees(sid, tryRoot);
36704
- return this.sessionParentPathAfterRemember(sid);
36705
- }
36706
- const next = path26.dirname(cur);
36707
- if (next === cur) break;
36708
- cur = next;
36709
- }
36710
- }
36711
- return resolved;
36712
- }
36713
- const parentKind = parseSessionParent(opts.sessionParent);
36714
- if (parentKind === "bridge_root") {
36715
- return void 0;
36716
- }
36717
- if (parentKind === "worktrees_root") {
36718
- if (!opts.isNewSession) {
36719
- const cached2 = this.sessionParentPathAfterRemember(sid);
36720
- if (cached2) return cached2;
36721
- const disc = this.tryDiscoverFromDisk(sid);
36722
- if (disc) {
36723
- this.rememberSessionWorktrees(sid, disc);
36724
- return this.sessionParentPathAfterRemember(sid);
36725
- }
36726
- return void 0;
36727
- }
36728
- const prep2 = await prepareNewSessionWorktrees({
36729
- worktreesRootPath: this.worktreesRootPath,
36730
- bridgeRoot: getBridgeRoot(),
36731
- sessionId: sid,
36732
- layout: this.layout,
36733
- log: this.log
36734
- });
36735
- if (!prep2) return void 0;
36736
- this.rememberSessionWorktrees(sid, {
36737
- sessionParentPath: prep2.sessionParentPath,
36738
- workingTreeRelRoot: prep2.workingTreeRelRoot,
36739
- repoCheckoutPaths: prep2.worktreePaths
36740
- });
36741
- return this.sessionParentPathAfterRemember(sid);
36742
- }
36743
- if (!opts.isNewSession) {
36744
- const cached2 = this.sessionParentPathAfterRemember(sid);
36745
- if (cached2) return cached2;
36746
- const disc = this.tryDiscoverFromDisk(sid);
36747
- if (disc) {
36748
- this.rememberSessionWorktrees(sid, disc);
36749
- return this.sessionParentPathAfterRemember(sid);
36750
- }
36751
- return void 0;
36752
- }
36753
- const prep = await prepareNewSessionWorktrees({
36860
+ return resolveIsolatedSessionParentPath(
36861
+ sessionId,
36862
+ this.cache,
36863
+ (sid) => this.ensureRepoCheckoutPathsForSession(sid)
36864
+ );
36865
+ }
36866
+ resolveSessionParentPathForPrompt(sessionId, opts) {
36867
+ return resolveSessionParentPathForPrompt({
36868
+ sessionId,
36869
+ cache: this.cache,
36754
36870
  worktreesRootPath: this.worktreesRootPath,
36755
- bridgeRoot: getBridgeRoot(),
36756
- sessionId: sid,
36757
36871
  layout: this.layout,
36758
- log: this.log
36759
- });
36760
- if (!prep) return void 0;
36761
- this.rememberSessionWorktrees(sid, {
36762
- sessionParentPath: prep.sessionParentPath,
36763
- workingTreeRelRoot: prep.workingTreeRelRoot,
36764
- repoCheckoutPaths: prep.worktreePaths
36872
+ log: this.log,
36873
+ discover: (sid) => this.discover(sid),
36874
+ opts
36765
36875
  });
36766
- return this.sessionParentPathAfterRemember(sid);
36767
36876
  }
36768
36877
  async renameSessionBranch(sessionId, newBranch) {
36769
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36878
+ const paths = this.cache.getRepoCheckoutPathsRef(sessionId);
36770
36879
  if (!paths?.length) return;
36771
36880
  await renameSessionWorktreeBranches(paths, newBranch, this.log);
36772
36881
  }
36773
- /** True when this session uses an isolated worktree layout (not the bridge root). */
36774
36882
  usesWorktreeSession(sessionId) {
36775
36883
  if (!sessionId) return false;
36776
- return this.sessionParentPathBySession.has(sessionId);
36884
+ return this.cache.hasSession(sessionId);
36777
36885
  }
36778
- /** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
36779
36886
  getRepoCheckoutPathsForSession(sessionId) {
36780
36887
  if (!sessionId) return void 0;
36781
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36782
- return paths?.length ? [...paths] : void 0;
36888
+ return this.cache.getRepoCheckoutPaths(sessionId);
36783
36889
  }
36784
- /**
36785
- * Same paths as {@link getRepoCheckoutPathsForSession}, but loads from disk into memory when the CLI
36786
- * restarted or maps were not yet populated (avoids discovering every repo under the worktrees root).
36787
- */
36788
36890
  ensureRepoCheckoutPathsForSession(sessionId) {
36789
- if (!sessionId?.trim()) return void 0;
36790
- const sid = sessionId.trim();
36791
- const cached2 = this.sessionRepoCheckoutPaths.get(sid);
36792
- if (cached2?.length) return [...cached2];
36793
- const disc = this.tryDiscoverFromDisk(sid);
36794
- if (disc?.repoCheckoutPaths.length) {
36795
- this.rememberSessionWorktrees(sid, disc);
36796
- return [...disc.repoCheckoutPaths];
36797
- }
36798
- return void 0;
36891
+ return ensureRepoCheckoutPathsForSession(sessionId, this.cache, (sid) => this.discover(sid));
36799
36892
  }
36800
- /** Session parent directory when in worktrees mode; null otherwise (same as {@link getIsolatedSessionParentPathForSession} path). */
36801
36893
  getSessionWorktreeRootForSession(sessionId) {
36802
36894
  return this.getIsolatedSessionParentPathForSession(sessionId);
36803
36895
  }
36804
36896
  async removeSessionWorktrees(sessionId) {
36805
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36806
- this.sessionRepoCheckoutPaths.delete(sessionId);
36807
- this.sessionParentPathBySession.delete(sessionId);
36808
- this.sessionWorkingTreeRelRootBySession.delete(sessionId);
36897
+ const paths = this.cache.clearSession(sessionId);
36809
36898
  if (!paths?.length) return;
36810
36899
  await removeSessionWorktrees(paths, this.log);
36811
36900
  }
36812
36901
  async commitSession(params) {
36813
- const paths = this.sessionRepoCheckoutPaths.get(params.sessionId);
36902
+ const paths = this.cache.getRepoCheckoutPathsRef(params.sessionId);
36814
36903
  const targets = paths?.length ? paths : [getBridgeRoot()];
36815
36904
  return commitSessionWorktrees({
36816
36905
  paths: targets,
@@ -36819,28 +36908,17 @@ var SessionWorktreeManager = class {
36819
36908
  push: params.push
36820
36909
  });
36821
36910
  }
36822
- resolveCommitTargets(sessionId) {
36823
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36824
- if (paths?.length) return paths;
36825
- const disc = this.tryDiscoverFromDisk(sessionId);
36826
- if (disc?.repoCheckoutPaths.length) {
36827
- this.rememberSessionWorktrees(sessionId, disc);
36828
- return disc.repoCheckoutPaths;
36829
- }
36830
- return [getBridgeRoot()];
36831
- }
36832
36911
  async getSessionWorkingTreeStatus(sessionId) {
36833
- return aggregateSessionPathsWorkingTreeStatus(this.resolveCommitTargets(sessionId));
36912
+ return aggregateSessionPathsWorkingTreeStatus(
36913
+ resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
36914
+ );
36834
36915
  }
36835
- /** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
36836
36916
  async getSessionWorkingTreeChangeDetails(sessionId, opts) {
36837
- const targets = this.resolveCommitTargets(sessionId);
36838
- const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
36839
- const legacyNested = this.isLegacyNestedLayout(sessionId);
36917
+ const targets = resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid));
36840
36918
  return getWorkingTreeChangeRepoDetails({
36841
36919
  commitTargetPaths: targets,
36842
- sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
36843
- legacyRepoNestedSessionLayout: legacyNested,
36920
+ sessionWorktreeRootPath: this.cache.getWorkingTreeRelRoot(sessionId),
36921
+ legacyRepoNestedSessionLayout: this.cache.isLegacyNestedLayout(sessionId),
36844
36922
  repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
36845
36923
  basis: opts?.basis,
36846
36924
  recentCommitsLimit: opts?.recentCommitsLimit
@@ -36848,7 +36926,9 @@ var SessionWorktreeManager = class {
36848
36926
  }
36849
36927
  async pushSessionUpstream(sessionId) {
36850
36928
  try {
36851
- await pushAheadOfUpstreamForPaths(this.resolveCommitTargets(sessionId));
36929
+ await pushAheadOfUpstreamForPaths(
36930
+ resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
36931
+ );
36852
36932
  return { ok: true };
36853
36933
  } catch (e) {
36854
36934
  const err = e instanceof Error ? e.message : String(e);
@@ -36856,27 +36936,31 @@ var SessionWorktreeManager = class {
36856
36936
  }
36857
36937
  }
36858
36938
  };
36939
+
36940
+ // src/worktrees/manager/default-worktrees-root-path.ts
36941
+ import * as path28 from "node:path";
36942
+ import os8 from "node:os";
36859
36943
  function defaultWorktreesRootPath() {
36860
- return path26.join(os8.homedir(), ".buildautomaton", "worktrees");
36944
+ return path28.join(os8.homedir(), ".buildautomaton", "worktrees");
36861
36945
  }
36862
36946
 
36863
36947
  // src/files/watch-file-index.ts
36864
36948
  import { watch } from "node:fs";
36865
- import path31 from "node:path";
36949
+ import path33 from "node:path";
36866
36950
 
36867
36951
  // src/files/index/paths.ts
36868
- import path27 from "node:path";
36952
+ import path29 from "node:path";
36869
36953
  import crypto2 from "node:crypto";
36870
36954
  function getCwdHashForFileIndex(resolvedCwd) {
36871
- return crypto2.createHash("sha256").update(path27.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
36955
+ return crypto2.createHash("sha256").update(path29.resolve(resolvedCwd)).digest("hex").slice(0, INDEX_HASH_LEN);
36872
36956
  }
36873
36957
 
36874
36958
  // src/files/index/build-file-index.ts
36875
- import path29 from "node:path";
36959
+ import path31 from "node:path";
36876
36960
 
36877
36961
  // src/files/index/walk-workspace-tree.ts
36878
36962
  import fs25 from "node:fs";
36879
- import path28 from "node:path";
36963
+ import path30 from "node:path";
36880
36964
  var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
36881
36965
  "node_modules",
36882
36966
  "bower_components",
@@ -36905,18 +36989,18 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
36905
36989
  if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
36906
36990
  }
36907
36991
  state.n++;
36908
- const full = path28.join(dir, name);
36992
+ const full = path30.join(dir, name);
36909
36993
  let stat3;
36910
36994
  try {
36911
36995
  stat3 = await fs25.promises.stat(full);
36912
36996
  } catch {
36913
36997
  continue;
36914
36998
  }
36915
- const relative5 = path28.relative(baseDir, full).replace(/\\/g, "/");
36999
+ const relative6 = path30.relative(baseDir, full).replace(/\\/g, "/");
36916
37000
  if (stat3.isDirectory()) {
36917
37001
  await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
36918
37002
  } else if (stat3.isFile()) {
36919
- onFile(relative5);
37003
+ onFile(relative6);
36920
37004
  }
36921
37005
  }
36922
37006
  }
@@ -37014,7 +37098,7 @@ async function collectWorkspacePathsAsync(resolved) {
37014
37098
  }
37015
37099
  async function buildFileIndexAsync(cwd) {
37016
37100
  return withFileIndexSqliteLock(async () => {
37017
- const resolved = path29.resolve(cwd);
37101
+ const resolved = path31.resolve(cwd);
37018
37102
  await yieldToEventLoop();
37019
37103
  assertNotShutdown();
37020
37104
  const paths = await collectWorkspacePathsAsync(resolved);
@@ -37026,7 +37110,7 @@ async function buildFileIndexAsync(cwd) {
37026
37110
  }
37027
37111
 
37028
37112
  // src/files/index/ensure-file-index.ts
37029
- import path30 from "node:path";
37113
+ import path32 from "node:path";
37030
37114
 
37031
37115
  // src/files/index/search-file-index.ts
37032
37116
  function escapeLikePattern(fragment) {
@@ -37078,7 +37162,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
37078
37162
 
37079
37163
  // src/files/index/ensure-file-index.ts
37080
37164
  async function ensureFileIndexAsync(cwd) {
37081
- const resolved = path30.resolve(cwd);
37165
+ const resolved = path32.resolve(cwd);
37082
37166
  if (await bridgeFileIndexIsPopulated(resolved)) {
37083
37167
  return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
37084
37168
  }
@@ -37122,7 +37206,7 @@ function createFsWatcher(resolved, schedule) {
37122
37206
  }
37123
37207
  }
37124
37208
  function startFileIndexWatcher(cwd = getBridgeRoot()) {
37125
- const resolved = path31.resolve(cwd);
37209
+ const resolved = path33.resolve(cwd);
37126
37210
  void buildFileIndexAsync(resolved).catch((e) => {
37127
37211
  if (e instanceof CliSqliteInterrupted) return;
37128
37212
  console.error("[file-index] Initial index build failed:", e);
@@ -37152,7 +37236,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
37152
37236
  }
37153
37237
 
37154
37238
  // src/connection/create-bridge-connection.ts
37155
- import * as path41 from "node:path";
37239
+ import * as path44 from "node:path";
37156
37240
 
37157
37241
  // src/dev-servers/manager/dev-server-manager.ts
37158
37242
  import { rm as rm2 } from "node:fs/promises";
@@ -37174,15 +37258,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
37174
37258
 
37175
37259
  // src/dev-servers/process/terminate-child-process.ts
37176
37260
  async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
37177
- const exited = new Promise((resolve20) => {
37178
- proc.once("exit", () => resolve20());
37261
+ const exited = new Promise((resolve22) => {
37262
+ proc.once("exit", () => resolve22());
37179
37263
  });
37180
37264
  log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
37181
37265
  try {
37182
37266
  proc.kill("SIGTERM");
37183
37267
  } catch {
37184
37268
  }
37185
- await Promise.race([exited, new Promise((resolve20) => setTimeout(resolve20, graceMs))]);
37269
+ await Promise.race([exited, new Promise((resolve22) => setTimeout(resolve22, graceMs))]);
37186
37270
  }
37187
37271
  function forceKillChild(proc, log2, shortId, graceMs) {
37188
37272
  log2(
@@ -37462,10 +37546,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
37462
37546
  import { spawn as spawn7 } from "node:child_process";
37463
37547
  import fs29 from "node:fs";
37464
37548
  import { tmpdir } from "node:os";
37465
- import path32 from "node:path";
37549
+ import path34 from "node:path";
37466
37550
  function trySpawnMergedLogFile(command, env, cwd, signal) {
37467
- const tmpRoot = fs29.mkdtempSync(path32.join(tmpdir(), "ba-devsrv-log-"));
37468
- const logPath = path32.join(tmpRoot, "combined.log");
37551
+ const tmpRoot = fs29.mkdtempSync(path34.join(tmpdir(), "ba-devsrv-log-"));
37552
+ const logPath = path34.join(tmpRoot, "combined.log");
37469
37553
  let logFd;
37470
37554
  try {
37471
37555
  logFd = fs29.openSync(logPath, "a");
@@ -37509,15 +37593,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
37509
37593
  import { spawn as spawn8 } from "node:child_process";
37510
37594
  import fs30 from "node:fs";
37511
37595
  import { tmpdir as tmpdir2 } from "node:os";
37512
- import path33 from "node:path";
37596
+ import path35 from "node:path";
37513
37597
  function shSingleQuote(s) {
37514
37598
  return `'${s.replace(/'/g, `'\\''`)}'`;
37515
37599
  }
37516
37600
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
37517
- const tmpRoot = fs30.mkdtempSync(path33.join(tmpdir2(), "ba-devsrv-sh-"));
37518
- const logPath = path33.join(tmpRoot, "combined.log");
37519
- const innerPath = path33.join(tmpRoot, "_cmd.sh");
37520
- const runnerPath = path33.join(tmpRoot, "_run.sh");
37601
+ const tmpRoot = fs30.mkdtempSync(path35.join(tmpdir2(), "ba-devsrv-sh-"));
37602
+ const logPath = path35.join(tmpRoot, "combined.log");
37603
+ const innerPath = path35.join(tmpRoot, "_cmd.sh");
37604
+ const runnerPath = path35.join(tmpRoot, "_run.sh");
37521
37605
  try {
37522
37606
  fs30.writeFileSync(innerPath, `#!/bin/sh
37523
37607
  ${command}
@@ -37548,9 +37632,9 @@ cd ${shSingleQuote(cwd)}
37548
37632
  }
37549
37633
  }
37550
37634
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
37551
- const tmpRoot = fs30.mkdtempSync(path33.join(tmpdir2(), "ba-devsrv-sh-"));
37552
- const logPath = path33.join(tmpRoot, "combined.log");
37553
- const runnerPath = path33.join(tmpRoot, "_run.bat");
37635
+ const tmpRoot = fs30.mkdtempSync(path35.join(tmpdir2(), "ba-devsrv-sh-"));
37636
+ const logPath = path35.join(tmpRoot, "combined.log");
37637
+ const runnerPath = path35.join(tmpRoot, "_run.bat");
37554
37638
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
37555
37639
  const com = process.env.ComSpec || "cmd.exe";
37556
37640
  try {
@@ -38063,7 +38147,7 @@ async function proxyToLocal(request) {
38063
38147
  };
38064
38148
  const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
38065
38149
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
38066
- const once = await new Promise((resolve20) => {
38150
+ const once = await new Promise((resolve22) => {
38067
38151
  const req = mod.request(opts, (res) => {
38068
38152
  const chunks = [];
38069
38153
  res.on("data", (c) => chunks.push(c));
@@ -38074,7 +38158,7 @@ async function proxyToLocal(request) {
38074
38158
  if (typeof v === "string") headers[k] = v;
38075
38159
  else if (Array.isArray(v) && v[0]) headers[k] = v[0];
38076
38160
  }
38077
- resolve20({
38161
+ resolve22({
38078
38162
  id: request.id,
38079
38163
  statusCode: res.statusCode ?? 0,
38080
38164
  headers,
@@ -38083,7 +38167,7 @@ async function proxyToLocal(request) {
38083
38167
  });
38084
38168
  });
38085
38169
  req.on("error", (err) => {
38086
- resolve20({
38170
+ resolve22({
38087
38171
  id: request.id,
38088
38172
  statusCode: 0,
38089
38173
  headers: {},
@@ -38492,13 +38576,13 @@ function createOnBridgeIdentified(opts) {
38492
38576
 
38493
38577
  // src/skills/discover-local-agent-skills.ts
38494
38578
  import fs31 from "node:fs";
38495
- import path34 from "node:path";
38579
+ import path36 from "node:path";
38496
38580
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
38497
38581
  function discoverLocalSkills(cwd) {
38498
38582
  const out = [];
38499
38583
  const seenKeys = /* @__PURE__ */ new Set();
38500
38584
  for (const rel of SKILL_DISCOVERY_ROOTS) {
38501
- const base = path34.join(cwd, rel);
38585
+ const base = path36.join(cwd, rel);
38502
38586
  if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
38503
38587
  let entries = [];
38504
38588
  try {
@@ -38507,13 +38591,13 @@ function discoverLocalSkills(cwd) {
38507
38591
  continue;
38508
38592
  }
38509
38593
  for (const name of entries) {
38510
- const dir = path34.join(base, name);
38594
+ const dir = path36.join(base, name);
38511
38595
  try {
38512
38596
  if (!fs31.statSync(dir).isDirectory()) continue;
38513
38597
  } catch {
38514
38598
  continue;
38515
38599
  }
38516
- const skillMd = path34.join(dir, "SKILL.md");
38600
+ const skillMd = path36.join(dir, "SKILL.md");
38517
38601
  if (!fs31.existsSync(skillMd)) continue;
38518
38602
  const key = `${rel}/${name}`;
38519
38603
  if (seenKeys.has(key)) continue;
@@ -38526,7 +38610,7 @@ function discoverLocalSkills(cwd) {
38526
38610
  function discoverSkillLayoutRoots(cwd) {
38527
38611
  const roots = [];
38528
38612
  for (const rel of SKILL_DISCOVERY_ROOTS) {
38529
- const base = path34.join(cwd, rel);
38613
+ const base = path36.join(cwd, rel);
38530
38614
  if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
38531
38615
  let entries = [];
38532
38616
  try {
@@ -38536,13 +38620,13 @@ function discoverSkillLayoutRoots(cwd) {
38536
38620
  }
38537
38621
  const skills2 = [];
38538
38622
  for (const name of entries) {
38539
- const dir = path34.join(base, name);
38623
+ const dir = path36.join(base, name);
38540
38624
  try {
38541
38625
  if (!fs31.statSync(dir).isDirectory()) continue;
38542
38626
  } catch {
38543
38627
  continue;
38544
38628
  }
38545
- if (!fs31.existsSync(path34.join(dir, "SKILL.md"))) continue;
38629
+ if (!fs31.existsSync(path36.join(dir, "SKILL.md"))) continue;
38546
38630
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
38547
38631
  skills2.push({ name, relPath });
38548
38632
  }
@@ -38665,7 +38749,9 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
38665
38749
  "file_browser_search",
38666
38750
  "skill_layout_request",
38667
38751
  "install_skills",
38668
- "refresh_local_skills"
38752
+ "refresh_local_skills",
38753
+ "bridge_git_context_request",
38754
+ "list_repo_branches_request"
38669
38755
  ];
38670
38756
  var API_TO_BRIDGE_TYPE_SET = new Set(API_TO_BRIDGE_MESSAGE_TYPES);
38671
38757
  function parseApiToBridgeMessage(data, log2) {
@@ -38749,9 +38835,6 @@ var handleAgentConfigMessage = (msg, deps) => {
38749
38835
  handleBridgeAgentConfig(msg, deps);
38750
38836
  };
38751
38837
 
38752
- // src/prompt-turn-queue/runner.ts
38753
- import fs32 from "node:fs";
38754
-
38755
38838
  // src/prompt-turn-queue/client-report.ts
38756
38839
  function sendPromptQueueClientReport(ws, queues) {
38757
38840
  if (!ws) return false;
@@ -38821,8 +38904,36 @@ async function mergeServerQueueSnapshot(queueKey, serverTurns) {
38821
38904
  return { queueKey, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), turns };
38822
38905
  }
38823
38906
 
38824
- // src/prompt-turn-queue/runner.ts
38825
- var runIdToQueueKey = /* @__PURE__ */ new Map();
38907
+ // src/prompt-turn-queue/runner/dispatch-local-prompt.ts
38908
+ function dispatchLocalPrompt(next, deps) {
38909
+ const pl = next.payload;
38910
+ const rawParent = pl["sessionParent"];
38911
+ const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
38912
+ const rawParentPath = pl["sessionParentPath"];
38913
+ const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
38914
+ const rawBaseBranches = pl["worktreeBaseBranches"];
38915
+ const worktreeBaseBranches = rawBaseBranches != null && typeof rawBaseBranches === "object" && !Array.isArray(rawBaseBranches) ? rawBaseBranches : void 0;
38916
+ const msg = {
38917
+ type: "prompt",
38918
+ sessionId: next.sessionId,
38919
+ runId: next.turnId,
38920
+ prompt: pl.prompt,
38921
+ mode: typeof pl.mode === "string" ? pl.mode : "agent",
38922
+ isNewSession: pl.isNewSession === true,
38923
+ ...sessionParent ? { sessionParent } : {},
38924
+ ...sessionParentPath ? { sessionParentPath } : {},
38925
+ ...worktreeBaseBranches && Object.keys(worktreeBaseBranches).length > 0 ? { worktreeBaseBranches } : {},
38926
+ ...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
38927
+ ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
38928
+ ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
38929
+ ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
38930
+ ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
38931
+ ...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
38932
+ };
38933
+ handleBridgePrompt(msg, deps);
38934
+ }
38935
+
38936
+ // src/prompt-turn-queue/runner/queue-selection.ts
38826
38937
  function isRunnableServerState(s) {
38827
38938
  return s === "queued" || s === "requeued" || s === "requeued_with_revert";
38828
38939
  }
@@ -38841,6 +38952,28 @@ function pickNextRunnableTurn(turns) {
38841
38952
  function hasRunningTurn(turns) {
38842
38953
  return turns.some((t) => t.lastClientState === "running");
38843
38954
  }
38955
+
38956
+ // src/prompt-turn-queue/runner/run-id-queue-key-map.ts
38957
+ var runIdToQueueKey = /* @__PURE__ */ new Map();
38958
+ function getRunIdQueueKey(runId) {
38959
+ return runIdToQueueKey.get(runId);
38960
+ }
38961
+ function setRunIdQueueKey(runId, queueKey) {
38962
+ runIdToQueueKey.set(runId, queueKey);
38963
+ }
38964
+ function deleteRunIdQueueKey(runId) {
38965
+ const queueKey = runIdToQueueKey.get(runId);
38966
+ runIdToQueueKey.delete(runId);
38967
+ return queueKey;
38968
+ }
38969
+ function syncRunningTurnQueueKeys(turns, queueKey) {
38970
+ for (const running of turns.filter((t) => t.lastClientState === "running")) {
38971
+ runIdToQueueKey.set(running.turnId, queueKey);
38972
+ }
38973
+ }
38974
+
38975
+ // src/prompt-turn-queue/runner/run-local-revert-before-queued-prompt.ts
38976
+ import fs32 from "node:fs";
38844
38977
  async function runLocalRevertBeforeQueuedPrompt(next, deps) {
38845
38978
  if (next.serverState !== "requeued_with_revert") return true;
38846
38979
  const sid = next.sessionId;
@@ -38862,30 +38995,23 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
38862
38995
  }
38863
38996
  return res.ok;
38864
38997
  }
38865
- function dispatchLocalPrompt(next, deps) {
38866
- const pl = next.payload;
38867
- const rawParent = pl["sessionParent"];
38868
- const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
38869
- const rawParentPath = pl["sessionParentPath"];
38870
- const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
38871
- const msg = {
38872
- type: "prompt",
38873
- sessionId: next.sessionId,
38874
- runId: next.turnId,
38875
- prompt: pl.prompt,
38876
- mode: typeof pl.mode === "string" ? pl.mode : "agent",
38877
- isNewSession: pl.isNewSession === true,
38878
- ...sessionParent ? { sessionParent } : {},
38879
- ...sessionParentPath ? { sessionParentPath } : {},
38880
- ...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
38881
- ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
38882
- ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
38883
- ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
38884
- ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
38885
- ...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
38886
- };
38887
- handleBridgePrompt(msg, deps);
38998
+
38999
+ // src/prompt-turn-queue/runner/finalize-prompt-turn-on-bridge.ts
39000
+ async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
39001
+ if (!runId) return false;
39002
+ const queueKey = deleteRunIdQueueKey(runId);
39003
+ if (!queueKey) return false;
39004
+ const f = await readPersistedQueue(queueKey);
39005
+ if (!f) return false;
39006
+ const t = f.turns.find((x) => x.turnId === runId);
39007
+ if (!t) return false;
39008
+ t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
39009
+ await writePersistedQueue(f);
39010
+ sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
39011
+ return true;
38888
39012
  }
39013
+
39014
+ // src/prompt-turn-queue/runner/apply-prompt-queue-state-from-server.ts
38889
39015
  async function applyPromptQueueStateFromServer(msg, deps) {
38890
39016
  const raw = msg.queues;
38891
39017
  if (!raw || typeof raw !== "object") return;
@@ -38899,9 +39025,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
38899
39025
  if (!Array.isArray(serverTurns)) continue;
38900
39026
  const file2 = await readPersistedQueue(queueKey);
38901
39027
  if (!file2) continue;
38902
- for (const running of file2.turns.filter((t) => t.lastClientState === "running")) {
38903
- runIdToQueueKey.set(running.turnId, queueKey);
38904
- }
39028
+ syncRunningTurnQueueKeys(file2.turns, queueKey);
38905
39029
  }
38906
39030
  for (const [queueKey, serverTurns] of Object.entries(raw)) {
38907
39031
  if (!Array.isArray(serverTurns)) continue;
@@ -38946,7 +39070,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
38946
39070
  }
38947
39071
  next.lastClientState = "running";
38948
39072
  await writePersistedQueue(file2);
38949
- runIdToQueueKey.set(next.turnId, queueKey);
39073
+ setRunIdQueueKey(next.turnId, queueKey);
38950
39074
  startedThisTick.add(next.turnId);
38951
39075
  report[queueKey] = [{ turnId: next.turnId, clientState: "running" }];
38952
39076
  }
@@ -38959,24 +39083,10 @@ async function applyPromptQueueStateFromServer(msg, deps) {
38959
39083
  if (!file2) continue;
38960
39084
  const running = file2.turns.find((t) => t.lastClientState === "running");
38961
39085
  if (!running || !startedThisTick.has(running.turnId)) continue;
38962
- if (runIdToQueueKey.get(running.turnId) !== queueKey) continue;
39086
+ if (getRunIdQueueKey(running.turnId) !== queueKey) continue;
38963
39087
  dispatchLocalPrompt(running, deps);
38964
39088
  }
38965
39089
  }
38966
- async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
38967
- if (!runId) return false;
38968
- const queueKey = runIdToQueueKey.get(runId);
38969
- runIdToQueueKey.delete(runId);
38970
- if (!queueKey) return false;
38971
- const f = await readPersistedQueue(queueKey);
38972
- if (!f) return false;
38973
- const t = f.turns.find((x) => x.turnId === runId);
38974
- if (!t) return false;
38975
- t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
38976
- await writePersistedQueue(f);
38977
- sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
38978
- return true;
38979
- }
38980
39090
 
38981
39091
  // src/agents/acp/from-bridge/bridge-prompt-wiring.ts
38982
39092
  function createBridgePromptSenders(deps, getWs) {
@@ -39023,6 +39133,91 @@ function createBridgePromptSenders(deps, getWs) {
39023
39133
  return { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate };
39024
39134
  }
39025
39135
 
39136
+ // src/agents/acp/from-bridge/handle-bridge-prompt/parse-bridge-attachments.ts
39137
+ function parseBridgeAttachments(msg) {
39138
+ const raw = msg.attachments;
39139
+ if (!Array.isArray(raw)) return [];
39140
+ const out = [];
39141
+ for (const x of raw) {
39142
+ if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
39143
+ const o = x;
39144
+ const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
39145
+ if (!id) continue;
39146
+ const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
39147
+ out.push({ attachmentId: id, mimeType: mt });
39148
+ }
39149
+ return out;
39150
+ }
39151
+
39152
+ // src/agents/acp/from-bridge/handle-bridge-prompt/parse-worktree-base-branches.ts
39153
+ function parseWorktreeBaseBranches(msg) {
39154
+ const raw = msg.worktreeBaseBranches;
39155
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
39156
+ const out = {};
39157
+ for (const [k, v] of Object.entries(raw)) {
39158
+ if (typeof k !== "string" || typeof v !== "string") continue;
39159
+ const rel = k.trim();
39160
+ const branch = v.trim();
39161
+ if (rel && branch) out[rel] = branch;
39162
+ }
39163
+ return Object.keys(out).length > 0 ? out : void 0;
39164
+ }
39165
+
39166
+ // src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
39167
+ function decryptChangeSummaryFileInput(row, e2ee) {
39168
+ if (!e2ee) return row;
39169
+ for (const field of ["path", "patchContent", "oldText", "newText"]) {
39170
+ const raw = row[field];
39171
+ if (typeof raw !== "string" || raw.trim() === "") continue;
39172
+ let o;
39173
+ try {
39174
+ o = JSON.parse(raw);
39175
+ } catch {
39176
+ continue;
39177
+ }
39178
+ if (!isE2eeEnvelope(o.ee)) continue;
39179
+ try {
39180
+ const d = e2ee.decryptMessage(o);
39181
+ const out = {
39182
+ path: typeof d.path === "string" ? d.path : row.path
39183
+ };
39184
+ if (d.directoryRemoved === true) out.directoryRemoved = true;
39185
+ else if (row.directoryRemoved === true) out.directoryRemoved = true;
39186
+ if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
39187
+ else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
39188
+ if (typeof d.oldText === "string") out.oldText = d.oldText;
39189
+ else if (typeof row.oldText === "string") out.oldText = row.oldText;
39190
+ if (typeof d.newText === "string") out.newText = d.newText;
39191
+ else if (typeof row.newText === "string") out.newText = row.newText;
39192
+ return out;
39193
+ } catch {
39194
+ return row;
39195
+ }
39196
+ }
39197
+ return row;
39198
+ }
39199
+
39200
+ // src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
39201
+ function hasSummarizePayload(f) {
39202
+ return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
39203
+ }
39204
+ function resolveChangeSummaryPromptForAgent(params) {
39205
+ const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
39206
+ const snaps = params.sessionChangeSummaryFileSnapshots;
39207
+ if (!isBuiltin || !snaps || snaps.length === 0) {
39208
+ return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
39209
+ }
39210
+ const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
39211
+ const withPayload = decrypted.filter(hasSummarizePayload);
39212
+ if (withPayload.length === 0) {
39213
+ return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
39214
+ }
39215
+ return {
39216
+ promptText: buildSessionChangeSummaryPrompt(withPayload),
39217
+ sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
39218
+ };
39219
+ }
39220
+
39026
39221
  // src/agents/acp/from-bridge/bridge-prompt-preamble.ts
39027
39222
  import { execFile as execFile8 } from "node:child_process";
39028
39223
  import { promisify as promisify8 } from "node:util";
@@ -39075,9 +39270,9 @@ function parseChangeSummarySnapshots(raw) {
39075
39270
  for (const item of raw) {
39076
39271
  if (!item || typeof item !== "object") continue;
39077
39272
  const o = item;
39078
- const path43 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
39079
- if (!path43) continue;
39080
- const row = { path: path43 };
39273
+ const path46 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
39274
+ if (!path46) continue;
39275
+ const row = { path: path46 };
39081
39276
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
39082
39277
  if (typeof o.oldText === "string") row.oldText = o.oldText;
39083
39278
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -39094,76 +39289,73 @@ function parseFollowUpFieldsFromPromptMessage(msg) {
39094
39289
  return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
39095
39290
  }
39096
39291
 
39097
- // src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
39098
- function decryptChangeSummaryFileInput(row, e2ee) {
39099
- if (!e2ee) return row;
39100
- for (const field of ["path", "patchContent", "oldText", "newText"]) {
39101
- const raw = row[field];
39102
- if (typeof raw !== "string" || raw.trim() === "") continue;
39103
- let o;
39104
- try {
39105
- o = JSON.parse(raw);
39106
- } catch {
39107
- continue;
39108
- }
39109
- if (!isE2eeEnvelope(o.ee)) continue;
39110
- try {
39111
- const d = e2ee.decryptMessage(o);
39112
- const out = {
39113
- path: typeof d.path === "string" ? d.path : row.path
39114
- };
39115
- if (d.directoryRemoved === true) out.directoryRemoved = true;
39116
- else if (row.directoryRemoved === true) out.directoryRemoved = true;
39117
- if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
39118
- else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
39119
- if (typeof d.oldText === "string") out.oldText = d.oldText;
39120
- else if (typeof row.oldText === "string") out.oldText = row.oldText;
39121
- if (typeof d.newText === "string") out.newText = d.newText;
39122
- else if (typeof row.newText === "string") out.newText = row.newText;
39123
- return out;
39124
- } catch {
39125
- return row;
39126
- }
39127
- }
39128
- return row;
39129
- }
39130
-
39131
- // src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
39132
- function hasSummarizePayload(f) {
39133
- return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
39134
- }
39135
- function resolveChangeSummaryPromptForAgent(params) {
39136
- const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
39137
- const snaps = params.sessionChangeSummaryFileSnapshots;
39138
- if (!isBuiltin || !snaps || snaps.length === 0) {
39139
- return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
39140
- }
39141
- const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
39142
- const withPayload = decrypted.filter(hasSummarizePayload);
39143
- if (withPayload.length === 0) {
39144
- return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
39292
+ // src/agents/acp/from-bridge/handle-bridge-prompt/run-preamble-and-prompt.ts
39293
+ async function runPreambleAndPrompt(params) {
39294
+ const {
39295
+ deps,
39296
+ msg,
39297
+ getWs,
39298
+ log: log2,
39299
+ sessionWorktreeManager,
39300
+ sessionId,
39301
+ runId,
39302
+ promptText,
39303
+ attachments,
39304
+ mode,
39305
+ agentType,
39306
+ agentId,
39307
+ agentConfig,
39308
+ resolvedCwd,
39309
+ senders: { sendResult: sendResult2, sendSessionUpdate }
39310
+ } = params;
39311
+ const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
39312
+ await runBridgePromptPreamble({
39313
+ getWs,
39314
+ log: log2,
39315
+ sessionWorktreeManager,
39316
+ sessionId,
39317
+ runId,
39318
+ effectiveCwd
39319
+ });
39320
+ const {
39321
+ followUpCatalogPromptId,
39322
+ sessionChangeSummaryFilePaths: pathsFromBridge,
39323
+ sessionChangeSummaryFileSnapshots
39324
+ } = parseFollowUpFieldsFromPromptMessage(msg);
39325
+ const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
39326
+ followUpCatalogPromptId,
39327
+ sessionChangeSummaryFileSnapshots,
39328
+ bridgePromptText: promptText,
39329
+ e2ee: deps.e2ee
39330
+ });
39331
+ if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
39332
+ deps.log(
39333
+ "[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
39334
+ );
39145
39335
  }
39146
- return {
39147
- promptText: buildSessionChangeSummaryPrompt(withPayload),
39148
- sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
39149
- };
39336
+ const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
39337
+ deps.acpManager.handlePrompt({
39338
+ promptText: resolvedPromptText,
39339
+ promptId: msg.id,
39340
+ sessionId,
39341
+ runId,
39342
+ mode,
39343
+ agentType,
39344
+ agentId,
39345
+ agentConfig,
39346
+ sessionParentPath: effectiveCwd,
39347
+ sendResult: sendResult2,
39348
+ sendSessionUpdate,
39349
+ followUpCatalogPromptId,
39350
+ sessionChangeSummaryFilePaths: pathsForUpload,
39351
+ cloudApiBaseUrl: deps.cloudApiBaseUrl,
39352
+ getCloudAccessToken: deps.getCloudAccessToken,
39353
+ e2ee: deps.e2ee,
39354
+ ...attachments.length > 0 ? { attachments } : {}
39355
+ });
39150
39356
  }
39151
39357
 
39152
- // src/agents/acp/from-bridge/handle-bridge-prompt.ts
39153
- function parseBridgeAttachments(msg) {
39154
- const raw = msg.attachments;
39155
- if (!Array.isArray(raw)) return [];
39156
- const out = [];
39157
- for (const x of raw) {
39158
- if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
39159
- const o = x;
39160
- const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
39161
- if (!id) continue;
39162
- const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
39163
- out.push({ attachmentId: id, mimeType: mt });
39164
- }
39165
- return out;
39166
- }
39358
+ // src/agents/acp/from-bridge/handle-bridge-prompt/handle-bridge-prompt.ts
39167
39359
  function handleBridgePrompt(msg, deps) {
39168
39360
  const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
39169
39361
  const rawPrompt = msg.prompt;
@@ -39172,12 +39364,12 @@ function handleBridgePrompt(msg, deps) {
39172
39364
  const sessionId = msg.sessionId;
39173
39365
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
39174
39366
  const promptId = typeof msg.id === "string" ? msg.id : void 0;
39175
- const { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate } = createBridgePromptSenders(deps, getWs);
39367
+ const senders = createBridgePromptSenders(deps, getWs);
39176
39368
  if (!promptText.trim() && attachments.length === 0) {
39177
39369
  log2(
39178
39370
  `[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).`
39179
39371
  );
39180
- sendBridgeMessage(
39372
+ senders.sendBridgeMessage(
39181
39373
  {
39182
39374
  type: "prompt_result",
39183
39375
  ...promptId ? { id: promptId } : {},
@@ -39199,57 +39391,50 @@ function handleBridgePrompt(msg, deps) {
39199
39391
  const agentId = typeof rawAgentId === "string" && rawAgentId.trim() !== "" ? rawAgentId.trim() : null;
39200
39392
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
39201
39393
  const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
39394
+ const worktreeBaseBranches = parseWorktreeBaseBranches(msg);
39202
39395
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
39203
- async function preambleAndPrompt(resolvedCwd) {
39204
- const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
39205
- await runBridgePromptPreamble({
39396
+ void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, {
39397
+ isNewSession,
39398
+ sessionParent,
39399
+ sessionParentPath,
39400
+ ...worktreeBaseBranches ? { worktreeBaseBranches } : {}
39401
+ }).then(
39402
+ (cwd) => runPreambleAndPrompt({
39403
+ deps,
39404
+ msg,
39206
39405
  getWs,
39207
39406
  log: log2,
39208
39407
  sessionWorktreeManager,
39209
39408
  sessionId,
39210
39409
  runId,
39211
- effectiveCwd
39212
- });
39213
- const {
39214
- followUpCatalogPromptId,
39215
- sessionChangeSummaryFilePaths: pathsFromBridge,
39216
- sessionChangeSummaryFileSnapshots
39217
- } = parseFollowUpFieldsFromPromptMessage(msg);
39218
- const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
39219
- followUpCatalogPromptId,
39220
- sessionChangeSummaryFileSnapshots,
39221
- bridgePromptText: promptText,
39222
- e2ee: deps.e2ee
39223
- });
39224
- if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
39225
- deps.log(
39226
- "[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
39227
- );
39228
- }
39229
- const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
39230
- acpManager.handlePrompt({
39231
- promptText: resolvedPromptText,
39232
- promptId: msg.id,
39410
+ promptText,
39411
+ attachments,
39412
+ mode,
39413
+ agentType,
39414
+ agentId,
39415
+ agentConfig,
39416
+ resolvedCwd: cwd,
39417
+ senders
39418
+ })
39419
+ ).catch((err) => {
39420
+ log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
39421
+ void runPreambleAndPrompt({
39422
+ deps,
39423
+ msg,
39424
+ getWs,
39425
+ log: log2,
39426
+ sessionWorktreeManager,
39233
39427
  sessionId,
39234
39428
  runId,
39429
+ promptText,
39430
+ attachments,
39235
39431
  mode,
39236
39432
  agentType,
39237
39433
  agentId,
39238
39434
  agentConfig,
39239
- sessionParentPath: effectiveCwd,
39240
- sendResult: sendResult2,
39241
- sendSessionUpdate,
39242
- followUpCatalogPromptId,
39243
- sessionChangeSummaryFilePaths: pathsForUpload,
39244
- cloudApiBaseUrl: deps.cloudApiBaseUrl,
39245
- getCloudAccessToken: deps.getCloudAccessToken,
39246
- e2ee: deps.e2ee,
39247
- ...attachments.length > 0 ? { attachments } : {}
39435
+ resolvedCwd: void 0,
39436
+ senders
39248
39437
  });
39249
- }
39250
- void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
39251
- log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
39252
- void preambleAndPrompt(void 0);
39253
39438
  });
39254
39439
  }
39255
39440
 
@@ -39297,8 +39482,8 @@ function randomSecret() {
39297
39482
  }
39298
39483
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
39299
39484
  }
39300
- async function requestPreviewApi(port, secret, method, path43, body) {
39301
- const url2 = `http://127.0.0.1:${port}${path43}`;
39485
+ async function requestPreviewApi(port, secret, method, path46, body) {
39486
+ const url2 = `http://127.0.0.1:${port}${path46}`;
39302
39487
  const headers = {
39303
39488
  [PREVIEW_SECRET_HEADER]: secret,
39304
39489
  "Content-Type": "application/json"
@@ -39310,7 +39495,7 @@ async function requestPreviewApi(port, secret, method, path43, body) {
39310
39495
  });
39311
39496
  const data = await res.json().catch(() => ({}));
39312
39497
  if (!res.ok) {
39313
- throw new Error(data?.error ?? `Preview API ${method} ${path43}: ${res.status}`);
39498
+ throw new Error(data?.error ?? `Preview API ${method} ${path46}: ${res.status}`);
39314
39499
  }
39315
39500
  return data;
39316
39501
  }
@@ -39478,11 +39663,11 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
39478
39663
  import fs34 from "node:fs";
39479
39664
 
39480
39665
  // src/files/ensure-under-cwd.ts
39481
- import path35 from "node:path";
39666
+ import path37 from "node:path";
39482
39667
  function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
39483
- const normalized = path35.normalize(relativePath).replace(/^(\.\/)+/, "");
39484
- const resolved = path35.resolve(cwd, normalized);
39485
- if (!resolved.startsWith(cwd + path35.sep) && resolved !== cwd) {
39668
+ const normalized = path37.normalize(relativePath).replace(/^(\.\/)+/, "");
39669
+ const resolved = path37.resolve(cwd, normalized);
39670
+ if (!resolved.startsWith(cwd + path37.sep) && resolved !== cwd) {
39486
39671
  return null;
39487
39672
  }
39488
39673
  return resolved;
@@ -39492,11 +39677,11 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
39492
39677
  var LIST_DIR_YIELD_EVERY = 256;
39493
39678
 
39494
39679
  // src/files/list-dir/map-dir-entry.ts
39495
- import path36 from "node:path";
39680
+ import path38 from "node:path";
39496
39681
  import fs33 from "node:fs";
39497
39682
  async function mapDirEntry(d, relativePath, resolved) {
39498
- const entryPath = path36.join(relativePath || ".", d.name).replace(/\\/g, "/");
39499
- const fullPath = path36.join(resolved, d.name);
39683
+ const entryPath = path38.join(relativePath || ".", d.name).replace(/\\/g, "/");
39684
+ const fullPath = path38.join(resolved, d.name);
39500
39685
  let isDir = d.isDirectory();
39501
39686
  if (d.isSymbolicLink()) {
39502
39687
  try {
@@ -39847,13 +40032,13 @@ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
39847
40032
  }
39848
40033
 
39849
40034
  // src/files/handle-file-browser-search.ts
39850
- import path37 from "node:path";
40035
+ import path39 from "node:path";
39851
40036
  var SEARCH_LIMIT = 100;
39852
40037
  function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
39853
40038
  void (async () => {
39854
40039
  await yieldToEventLoop();
39855
40040
  const q = typeof msg.q === "string" ? msg.q : "";
39856
- const sessionParentPath = path37.resolve(
40041
+ const sessionParentPath = path39.resolve(
39857
40042
  sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
39858
40043
  );
39859
40044
  if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
@@ -39973,7 +40158,7 @@ function handleSkillLayoutRequest(msg, deps) {
39973
40158
 
39974
40159
  // src/skills/install-remote-skills.ts
39975
40160
  import fs38 from "node:fs";
39976
- import path38 from "node:path";
40161
+ import path40 from "node:path";
39977
40162
  function installRemoteSkills(cwd, targetDir, items) {
39978
40163
  const installed2 = [];
39979
40164
  if (!Array.isArray(items)) {
@@ -39984,11 +40169,11 @@ function installRemoteSkills(cwd, targetDir, items) {
39984
40169
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
39985
40170
  continue;
39986
40171
  }
39987
- const skillDir = path38.join(cwd, targetDir, item.skillName);
40172
+ const skillDir = path40.join(cwd, targetDir, item.skillName);
39988
40173
  for (const f of item.files) {
39989
40174
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
39990
- const dest = path38.join(skillDir, f.path);
39991
- fs38.mkdirSync(path38.dirname(dest), { recursive: true });
40175
+ const dest = path40.join(skillDir, f.path);
40176
+ fs38.mkdirSync(path40.dirname(dest), { recursive: true });
39992
40177
  if (f.text !== void 0) {
39993
40178
  fs38.writeFileSync(dest, f.text, "utf8");
39994
40179
  } else if (f.base64) {
@@ -40204,6 +40389,148 @@ var handleDevServersConfig = (msg, deps) => {
40204
40389
  deps.devServerManager?.applyConfig(devServers ?? []);
40205
40390
  };
40206
40391
 
40392
+ // src/git/bridge-git-context.ts
40393
+ import * as path41 from "node:path";
40394
+
40395
+ // src/git/branches/get-current-branch.ts
40396
+ async function getCurrentBranch(repoPath) {
40397
+ try {
40398
+ const git = cliSimpleGit(repoPath);
40399
+ const branch = await git.revparse(["--abbrev-ref", "HEAD"]);
40400
+ const trimmed2 = typeof branch === "string" ? branch.trim() : "";
40401
+ if (!trimmed2 || trimmed2 === "HEAD") return null;
40402
+ return trimmed2;
40403
+ } catch {
40404
+ return null;
40405
+ }
40406
+ }
40407
+
40408
+ // src/git/branches/list-repo-branch-refs.ts
40409
+ function normalizeBranchRef(raw) {
40410
+ const trimmed2 = raw.trim();
40411
+ if (!trimmed2 || trimmed2 === "HEAD") return null;
40412
+ if (trimmed2.startsWith("refs/heads/")) return trimmed2.slice("refs/heads/".length);
40413
+ if (trimmed2.startsWith("refs/remotes/")) return trimmed2.slice("refs/remotes/".length);
40414
+ return trimmed2;
40415
+ }
40416
+ async function listRepoBranchRefs(repoPath) {
40417
+ try {
40418
+ const git = cliSimpleGit(repoPath);
40419
+ const out = await git.raw([
40420
+ "for-each-ref",
40421
+ "--sort=-committerdate",
40422
+ "--format=%(refname:short)",
40423
+ "refs/heads",
40424
+ "refs/remotes"
40425
+ ]);
40426
+ const lines = out.split("\n").map((line) => normalizeBranchRef(line)).filter((line) => Boolean(line));
40427
+ const seen = /* @__PURE__ */ new Set();
40428
+ const ordered = [];
40429
+ for (const name of lines) {
40430
+ if (seen.has(name)) continue;
40431
+ seen.add(name);
40432
+ ordered.push(name);
40433
+ }
40434
+ return ordered;
40435
+ } catch {
40436
+ return [];
40437
+ }
40438
+ }
40439
+
40440
+ // src/git/bridge-git-context.ts
40441
+ function folderNameForRelPath(relPath, bridgeRoot) {
40442
+ if (relPath === "." || relPath === "") {
40443
+ return path41.basename(path41.resolve(bridgeRoot)) || "repo";
40444
+ }
40445
+ return path41.basename(relPath) || relPath;
40446
+ }
40447
+ async function discoverGitReposForBridgeContext(bridgeRoot) {
40448
+ const root = path41.resolve(bridgeRoot);
40449
+ if (await isGitRepoDirectory(root)) {
40450
+ const remoteUrl = await getRemoteOriginUrl(root);
40451
+ return [{ absolutePath: root, remoteUrl }];
40452
+ }
40453
+ const [deep, shallow] = await Promise.all([discoverGitReposUnderRoot(root), discoverGitRepos(root)]);
40454
+ const byPath = /* @__PURE__ */ new Map();
40455
+ for (const repo of [...deep, ...shallow]) {
40456
+ byPath.set(path41.resolve(repo.absolutePath), repo);
40457
+ }
40458
+ return [...byPath.values()];
40459
+ }
40460
+ async function getBridgeGitContext(bridgeRoot = getBridgeRoot()) {
40461
+ const bridgeResolved = path41.resolve(bridgeRoot);
40462
+ const repos = await discoverGitReposForBridgeContext(bridgeResolved);
40463
+ const rows = [];
40464
+ for (const repo of repos) {
40465
+ let rel = path41.relative(bridgeResolved, repo.absolutePath);
40466
+ if (rel.startsWith("..") || path41.isAbsolute(rel)) continue;
40467
+ const relPath = rel === "" ? "." : rel.replace(/\\/g, "/");
40468
+ const currentBranch = await getCurrentBranch(repo.absolutePath);
40469
+ const remoteUrl = repo.remoteUrl.trim() || null;
40470
+ rows.push({
40471
+ relPath,
40472
+ folderName: folderNameForRelPath(relPath, bridgeResolved),
40473
+ currentBranch,
40474
+ remoteUrl
40475
+ });
40476
+ }
40477
+ rows.sort((a, b) => a.relPath.localeCompare(b.relPath));
40478
+ return rows;
40479
+ }
40480
+ async function listRepoBranchesForBridge(repoRelPath, bridgeRoot = getBridgeRoot()) {
40481
+ const bridgeResolved = path41.resolve(bridgeRoot);
40482
+ const rel = repoRelPath.trim() === "." ? "" : repoRelPath.trim().replace(/\\/g, "/");
40483
+ const repoPath = rel === "" ? bridgeResolved : path41.join(bridgeResolved, rel);
40484
+ const resolved = path41.resolve(repoPath);
40485
+ if (!resolved.startsWith(bridgeResolved + path41.sep) && resolved !== bridgeResolved) {
40486
+ return [];
40487
+ }
40488
+ return listRepoBranchRefs(resolved);
40489
+ }
40490
+
40491
+ // src/routing/handlers/bridge-git-context-messages.ts
40492
+ function handleBridgeGitContextRequestMessage(msg, getWs) {
40493
+ void (async () => {
40494
+ const socket = getWs();
40495
+ if (!socket) return;
40496
+ try {
40497
+ const repos = await getBridgeGitContext();
40498
+ sendWsMessage(socket, { type: "bridge_git_context_response", id: msg.id, repos });
40499
+ } catch (e) {
40500
+ sendWsMessage(socket, {
40501
+ type: "bridge_git_context_response",
40502
+ id: msg.id,
40503
+ error: e instanceof Error ? e.message : String(e)
40504
+ });
40505
+ }
40506
+ })();
40507
+ }
40508
+ function handleListRepoBranchesRequestMessage(msg, getWs) {
40509
+ void (async () => {
40510
+ const socket = getWs();
40511
+ if (!socket) return;
40512
+ const repoRelPath = typeof msg.repoRelPath === "string" ? msg.repoRelPath.trim() : "";
40513
+ if (!repoRelPath) {
40514
+ sendWsMessage(socket, {
40515
+ type: "list_repo_branches_response",
40516
+ id: msg.id,
40517
+ error: "repoRelPath required"
40518
+ });
40519
+ return;
40520
+ }
40521
+ try {
40522
+ const branches = await listRepoBranchesForBridge(repoRelPath);
40523
+ sendWsMessage(socket, { type: "list_repo_branches_response", id: msg.id, branches });
40524
+ } catch (e) {
40525
+ sendWsMessage(socket, {
40526
+ type: "list_repo_branches_response",
40527
+ id: msg.id,
40528
+ error: e instanceof Error ? e.message : String(e)
40529
+ });
40530
+ }
40531
+ })();
40532
+ }
40533
+
40207
40534
  // src/routing/dispatch-bridge-message.ts
40208
40535
  function dispatchBridgeMessage(msg, deps) {
40209
40536
  switch (msg.type) {
@@ -40267,6 +40594,12 @@ function dispatchBridgeMessage(msg, deps) {
40267
40594
  case "refresh_local_skills":
40268
40595
  handleRefreshLocalSkills(msg, deps);
40269
40596
  break;
40597
+ case "bridge_git_context_request":
40598
+ handleBridgeGitContextRequestMessage(msg, deps.getWs);
40599
+ break;
40600
+ case "list_repo_branches_request":
40601
+ handleListRepoBranchesRequestMessage(msg, deps.getWs);
40602
+ break;
40270
40603
  }
40271
40604
  }
40272
40605
 
@@ -40638,10 +40971,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
40638
40971
  }
40639
40972
 
40640
40973
  // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
40641
- import * as path40 from "node:path";
40974
+ import * as path43 from "node:path";
40642
40975
 
40643
40976
  // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
40644
- import * as path39 from "node:path";
40977
+ import * as path42 from "node:path";
40645
40978
  async function probeOneAgentTypeForCapabilities(params) {
40646
40979
  const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
40647
40980
  if (isCliImmediateShutdownRequested()) return false;
@@ -40681,7 +41014,7 @@ async function probeOneAgentTypeForCapabilities(params) {
40681
41014
  if (isCliImmediateShutdownRequested()) return false;
40682
41015
  handle = await resolved.createClient({
40683
41016
  command: resolved.command,
40684
- cwd: path39.resolve(cwd),
41017
+ cwd: path42.resolve(cwd),
40685
41018
  backendAgentType: agentType,
40686
41019
  sessionMode: "agent",
40687
41020
  persistedAcpSessionId: null,
@@ -40755,7 +41088,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
40755
41088
  async function warmupAgentCapabilitiesOnConnect(params) {
40756
41089
  const { workspaceId, log: log2, getWs } = params;
40757
41090
  if (isCliImmediateShutdownRequested()) return;
40758
- const cwd = path40.resolve(getBridgeRoot());
41091
+ const cwd = path43.resolve(getBridgeRoot());
40759
41092
  async function sendBatchFromCache() {
40760
41093
  const socket = getWs();
40761
41094
  if (!socket || socket.readyState !== wrapper_default.OPEN) return;
@@ -40922,8 +41255,8 @@ async function createBridgeConnection(options) {
40922
41255
  getCloudAccessToken: () => tokens.accessToken
40923
41256
  };
40924
41257
  const identifyReportedPaths = {
40925
- bridgeRootPath: path41.resolve(getBridgeRoot()),
40926
- worktreesRootPath: path41.resolve(worktreesRootPath)
41258
+ bridgeRootPath: path44.resolve(getBridgeRoot()),
41259
+ worktreesRootPath: path44.resolve(worktreesRootPath)
40927
41260
  };
40928
41261
  const { connect } = createMainBridgeWebSocketLifecycle({
40929
41262
  state,
@@ -41177,7 +41510,7 @@ async function runCliAction(program2, opts) {
41177
41510
  const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
41178
41511
  const bridgeRootOpt = (opts.bridgeRoot && typeof opts.bridgeRoot === "string" && opts.bridgeRoot.trim() ? opts.bridgeRoot.trim() : null) ?? (opts.cwd && typeof opts.cwd === "string" && opts.cwd.trim() ? opts.cwd.trim() : null);
41179
41512
  if (bridgeRootOpt) {
41180
- const resolvedBridgeRoot = path42.resolve(process.cwd(), bridgeRootOpt);
41513
+ const resolvedBridgeRoot = path45.resolve(process.cwd(), bridgeRootOpt);
41181
41514
  try {
41182
41515
  const st = fs40.statSync(resolvedBridgeRoot);
41183
41516
  if (!st.isDirectory()) {
@@ -41199,7 +41532,7 @@ async function runCliAction(program2, opts) {
41199
41532
  );
41200
41533
  let worktreesRootPath;
41201
41534
  if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
41202
- worktreesRootPath = path42.resolve(opts.worktreesRoot.trim());
41535
+ worktreesRootPath = path45.resolve(opts.worktreesRoot.trim());
41203
41536
  }
41204
41537
  const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
41205
41538
  if (e2eCertificates) {