@buildautomaton/cli 0.1.38 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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.38".length > 0 ? "0.1.38" : "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 }));
@@ -35553,266 +35553,65 @@ async function createAcpManager(options) {
35553
35553
  };
35554
35554
  }
35555
35555
 
35556
- // src/worktrees/session-worktree-manager.ts
35557
- import * as path26 from "node:path";
35558
- import os8 from "node:os";
35559
-
35560
- // src/worktrees/prepare-new-session-worktrees.ts
35561
- import * as fs18 from "node:fs";
35562
- import * as path21 from "node:path";
35563
-
35564
- // src/git/worktrees/worktree-add.ts
35565
- async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
35566
- const mainGit = cliSimpleGit(mainRepoPath);
35567
- await mainGit.raw(["worktree", "add", "-b", branch, worktreePath, "HEAD"]);
35568
- }
35569
-
35570
- // src/worktrees/worktree-layout-file.ts
35571
- import * as fs17 from "node:fs";
35572
- import * as path20 from "node:path";
35573
- import os7 from "node:os";
35574
- var LAYOUT_FILENAME = "worktree-launcher-layout.json";
35575
- function defaultWorktreeLayoutPath() {
35576
- return path20.join(os7.homedir(), ".buildautomaton", LAYOUT_FILENAME);
35577
- }
35578
- function normalizeLoadedLayout(raw) {
35579
- if (raw && typeof raw === "object" && "launcherCwds" in raw) {
35580
- const j = raw;
35581
- if (Array.isArray(j.launcherCwds)) return { launcherCwds: j.launcherCwds };
35582
- }
35583
- return { launcherCwds: [] };
35584
- }
35585
- function loadWorktreeLayout() {
35586
- try {
35587
- const p = defaultWorktreeLayoutPath();
35588
- if (!fs17.existsSync(p)) return { launcherCwds: [] };
35589
- const raw = JSON.parse(fs17.readFileSync(p, "utf8"));
35590
- return normalizeLoadedLayout(raw);
35591
- } catch {
35592
- return { launcherCwds: [] };
35593
- }
35594
- }
35595
- function saveWorktreeLayout(layout) {
35596
- try {
35597
- const dir = path20.dirname(defaultWorktreeLayoutPath());
35598
- fs17.mkdirSync(dir, { recursive: true });
35599
- fs17.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
35600
- } catch {
35601
- }
35602
- }
35603
- function baseNameSafe(pathString) {
35604
- return path20.basename(pathString).replace(/[^a-zA-Z0-9._-]+/g, "-") || "cwd";
35605
- }
35606
- function getLauncherDirNameIfPresent(layout, bridgeRootPath2) {
35607
- const norm = path20.resolve(bridgeRootPath2);
35608
- const existing = layout.launcherCwds.find((e) => path20.resolve(e.absolutePath) === norm);
35609
- return existing?.dirName;
35610
- }
35611
- function allocateDirNameForLauncherCwd(layout, bridgeRootPath2) {
35612
- const existing = getLauncherDirNameIfPresent(layout, bridgeRootPath2);
35613
- if (existing) return existing;
35614
- const norm = path20.resolve(bridgeRootPath2);
35615
- const base = baseNameSafe(norm);
35616
- const used = new Set(layout.launcherCwds.map((e) => e.dirName));
35617
- let name = base;
35618
- let n = 2;
35619
- while (used.has(name)) {
35620
- name = `${base}-${n}`;
35621
- n += 1;
35622
- }
35623
- layout.launcherCwds.push({ absolutePath: norm, dirName: name });
35624
- saveWorktreeLayout(layout);
35625
- return name;
35626
- }
35627
-
35628
- // src/worktrees/prepare-new-session-worktrees.ts
35629
- async function prepareNewSessionWorktrees(options) {
35630
- const { worktreesRootPath, bridgeRoot, sessionId, layout, log: log2 } = options;
35631
- const bridgeResolved = path21.resolve(bridgeRoot);
35632
- const cwdKey = allocateDirNameForLauncherCwd(layout, bridgeResolved);
35633
- const bridgeKeyDir = path21.join(worktreesRootPath, cwdKey);
35634
- const sessionDir = path21.join(bridgeKeyDir, sessionId);
35635
- const repos = await discoverGitReposUnderRoot(bridgeResolved);
35636
- if (repos.length === 0) {
35637
- log2("[worktrees] No Git repositories under bridge root; skipping worktree creation.");
35638
- return null;
35639
- }
35640
- const branch = `session-${sessionId}`;
35641
- const worktreePaths = [];
35642
- fs18.mkdirSync(sessionDir, { recursive: true });
35643
- for (const repo of repos) {
35644
- let rel = path21.relative(bridgeResolved, repo.absolutePath);
35645
- if (rel.startsWith("..") || path21.isAbsolute(rel)) continue;
35646
- const relNorm = rel === "" ? "." : rel;
35647
- const wtPath = relNorm === "." ? sessionDir : path21.join(sessionDir, relNorm);
35648
- if (relNorm !== ".") {
35649
- fs18.mkdirSync(path21.dirname(wtPath), { recursive: true });
35650
- }
35651
- try {
35652
- await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
35653
- log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
35654
- worktreePaths.push(wtPath);
35655
- } catch (e) {
35656
- log2(
35657
- `[worktrees] Worktree add failed for ${repo.absolutePath}: ${e instanceof Error ? e.message : String(e)}`
35658
- );
35659
- }
35660
- }
35661
- if (worktreePaths.length === 0) return null;
35662
- return {
35663
- worktreePaths,
35664
- sessionParentPath: sessionDir,
35665
- workingTreeRelRoot: sessionDir
35666
- };
35667
- }
35668
-
35669
- // src/git/branches/rename-branch.ts
35670
- async function gitRenameCurrentBranch(repoDir, newName) {
35671
- const g = cliSimpleGit(repoDir);
35672
- await g.raw(["branch", "-m", newName]);
35673
- }
35674
-
35675
- // src/worktrees/rename-session-worktree-branches.ts
35676
- async function renameSessionWorktreeBranches(paths, newBranch, log2) {
35677
- const safe = newBranch.replace(/[^a-zA-Z0-9/_-]+/g, "-").slice(0, 80) || "session-branch";
35678
- for (const wt of paths) {
35679
- try {
35680
- await gitRenameCurrentBranch(wt, safe);
35681
- log2(`[worktrees] Renamed branch in ${wt} \u2192 ${safe}`);
35682
- } catch (e) {
35683
- log2(
35684
- `[worktrees] Branch rename failed in ${wt}: ${e instanceof Error ? e.message : String(e)}`
35685
- );
35686
- }
35687
- }
35688
- }
35689
-
35690
- // src/worktrees/remove-session-worktrees.ts
35691
- import * as fs21 from "node:fs";
35692
-
35693
- // src/git/worktrees/worktree-remove.ts
35694
- import * as fs20 from "node:fs";
35695
-
35696
- // src/git/worktrees/resolve-main-repo-from-git-file.ts
35697
- import * as fs19 from "node:fs";
35698
- import * as path22 from "node:path";
35699
- function resolveMainRepoFromWorktreeGitFile(wt) {
35700
- const gitDirFile = path22.join(wt, ".git");
35701
- if (!fs19.existsSync(gitDirFile) || !fs19.statSync(gitDirFile).isFile()) return "";
35702
- const first2 = fs19.readFileSync(gitDirFile, "utf8").trim();
35703
- const m = first2.match(/^gitdir:\s*(.+)$/im);
35704
- if (!m) return "";
35705
- const gitWorktreePath = path22.resolve(wt, m[1].trim());
35706
- const gitDir = path22.dirname(path22.dirname(gitWorktreePath));
35707
- return path22.dirname(gitDir);
35708
- }
35556
+ // src/git/changes/types.ts
35557
+ var MAX_PATCH_CHARS = 35e4;
35709
35558
 
35710
- // src/git/worktrees/worktree-remove.ts
35711
- async function gitWorktreeRemoveForce(worktreePath) {
35712
- const mainRepo = resolveMainRepoFromWorktreeGitFile(worktreePath);
35713
- if (mainRepo) {
35714
- await cliSimpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
35715
- } else {
35716
- fs20.rmSync(worktreePath, { recursive: true, force: true });
35717
- }
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;
35718
35564
  }
35719
-
35720
- // src/worktrees/remove-session-worktrees.ts
35721
- async function removeSessionWorktrees(paths, log2) {
35722
- for (const wt of paths) {
35565
+ function formatRepoShortTitle(remoteUrl, repoRelPath) {
35566
+ const u = remoteUrl.trim();
35567
+ if (u) {
35723
35568
  try {
35724
- await gitWorktreeRemoveForce(wt);
35725
- log2(`[worktrees] Removed worktree ${wt}`);
35726
- } catch (e) {
35727
- log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
35728
- try {
35729
- fs21.rmSync(wt, { recursive: true, force: true });
35730
- } 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];
35731
35583
  }
35584
+ } catch {
35732
35585
  }
35733
35586
  }
35734
- }
35735
-
35736
- // src/git/changes/lib/parse-git-status.ts
35737
- function parseNameStatusLines(lines) {
35738
- const m = /* @__PURE__ */ new Map();
35739
- for (const line of lines) {
35740
- if (!line.trim()) continue;
35741
- const tabParts = line.split(" ");
35742
- if (tabParts.length < 2) continue;
35743
- const status = tabParts[0].trim();
35744
- const code = status[0];
35745
- if (code === "A") {
35746
- m.set(tabParts[tabParts.length - 1], "added");
35747
- } else if (code === "D") {
35748
- m.set(tabParts[tabParts.length - 1], "removed");
35749
- } else if (code === "R" || code === "C") {
35750
- if (tabParts.length >= 3) m.set(tabParts[tabParts.length - 1], "modified");
35751
- } else if (code === "M" || code === "U" || code === "T") {
35752
- m.set(tabParts[tabParts.length - 1], "modified");
35753
- }
35754
- }
35755
- return m;
35756
- }
35757
- function parseNumstatFirstLine(line) {
35758
- const parts = line.split(" ");
35759
- if (parts.length < 3) return null;
35760
- const [a, d] = parts;
35761
- const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
35762
- const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
35763
- return { additions, deletions };
35764
- }
35765
- function parseNumstat(lines) {
35766
- const m = /* @__PURE__ */ new Map();
35767
- for (const line of lines) {
35768
- if (!line.trim()) continue;
35769
- const parts = line.split(" ");
35770
- if (parts.length < 3) continue;
35771
- const [a, d, p] = parts;
35772
- const additions = a === "-" ? 0 : parseInt(String(a), 10) || 0;
35773
- const deletions = d === "-" ? 0 : parseInt(String(d), 10) || 0;
35774
- 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;
35775
35591
  }
35776
- return m;
35592
+ return "Repository";
35777
35593
  }
35778
- async function numstatFromGitNoIndex(g, pathInRepo) {
35779
- 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;
35780
35598
  try {
35781
- const out = await g.raw(["diff", "--numstat", "--no-index", "--", devNull, pathInRepo]);
35782
- const first2 = String(out).split("\n").find((l) => l.trim()) ?? "";
35783
- 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
+ }
35784
35607
  } catch {
35785
- return null;
35786
- }
35787
- }
35788
-
35789
- // src/git/changes/lib/working-tree-changed-path-count.ts
35790
- function workingTreeChangedPathCount(nameStatusLines, numstatLines, untrackedLines) {
35791
- const kindByPath = parseNameStatusLines(nameStatusLines);
35792
- const numByPath = parseNumstat(numstatLines);
35793
- const paths = /* @__PURE__ */ new Set([...kindByPath.keys(), ...numByPath.keys()]);
35794
- for (const p of untrackedLines.map((s) => s.trim()).filter(Boolean)) {
35795
- paths.add(p);
35608
+ hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
35796
35609
  }
35797
- return paths.size;
35610
+ return `origin \xB7 ${hostPath}`;
35798
35611
  }
35799
35612
 
35800
- // src/git/changes/count-working-tree-changed-files.ts
35801
- async function countWorkingTreeChangedFilesForRepo(repoGitCwd) {
35802
- return runGitTask(async () => {
35803
- const g = cliSimpleGit(repoGitCwd);
35804
- const [nameStatusRaw, numstatRaw, untrackedRaw] = await Promise.all([
35805
- g.raw(["diff", "--name-status", "HEAD"]).catch(() => ""),
35806
- g.raw(["diff", "HEAD", "--numstat"]).catch(() => ""),
35807
- g.raw(["ls-files", "--others", "--exclude-standard"]).catch(() => "")
35808
- ]);
35809
- return workingTreeChangedPathCount(
35810
- String(nameStatusRaw).split("\n"),
35811
- String(numstatRaw).split("\n"),
35812
- String(untrackedRaw).split("\n")
35813
- );
35814
- });
35815
- }
35613
+ // src/git/changes/get-working-tree-change-repo-details.ts
35614
+ import * as path21 from "node:path";
35816
35615
 
35817
35616
  // src/git/commits/resolve-remote-tracking.ts
35818
35617
  async function tryConfigGet(g, key) {
@@ -35912,96 +35711,6 @@ async function commitsAheadOfRemoteTracking(repoDir) {
35912
35711
  }
35913
35712
  }
35914
35713
 
35915
- // src/git/status/working-tree-status.ts
35916
- async function getRepoWorkingTreeStatus(repoDir) {
35917
- return runGitTask(async () => {
35918
- const uncommittedFileCount = await countWorkingTreeChangedFilesForRepo(repoDir);
35919
- const hasUncommittedChanges = uncommittedFileCount > 0;
35920
- const ahead = await commitsAheadOfRemoteTracking(repoDir);
35921
- return { hasUncommittedChanges, hasUnpushedCommits: ahead > 0, uncommittedFileCount };
35922
- });
35923
- }
35924
- async function aggregateSessionPathsWorkingTreeStatus(paths) {
35925
- let hasUncommittedChanges = false;
35926
- let hasUnpushedCommits = false;
35927
- let uncommittedFileCount = 0;
35928
- await forEachWithGitYield(paths, async (p) => {
35929
- const s = await getRepoWorkingTreeStatus(p);
35930
- uncommittedFileCount += s.uncommittedFileCount;
35931
- if (s.hasUncommittedChanges) hasUncommittedChanges = true;
35932
- if (s.hasUnpushedCommits) hasUnpushedCommits = true;
35933
- });
35934
- return { hasUncommittedChanges, hasUnpushedCommits, uncommittedFileCount };
35935
- }
35936
- async function pushAheadOfUpstreamForPaths(paths) {
35937
- await forEachWithGitYield(paths, async (p) => {
35938
- const g = cliSimpleGit(p);
35939
- const ahead = await commitsAheadOfRemoteTracking(p);
35940
- if (ahead <= 0) return;
35941
- await g.push();
35942
- });
35943
- }
35944
-
35945
- // src/git/changes/types.ts
35946
- var MAX_PATCH_CHARS = 35e4;
35947
-
35948
- // src/git/changes/lib/repo-format.ts
35949
- function posixJoinDirFile(dir, file2) {
35950
- const d = dir === "." || dir === "" ? "" : dir.replace(/\\/g, "/").replace(/\/+$/, "");
35951
- const f = file2.replace(/\\/g, "/").replace(/^\/+/, "");
35952
- return d ? `${d}/${f}` : f;
35953
- }
35954
- function formatRepoShortTitle(remoteUrl, repoRelPath) {
35955
- const u = remoteUrl.trim();
35956
- if (u) {
35957
- try {
35958
- if (u.startsWith("git@")) {
35959
- const colon = u.indexOf(":");
35960
- if (colon > 0) {
35961
- const pathPart = u.slice(colon + 1).replace(/\.git$/i, "").replace(/\/+$/, "");
35962
- if (pathPart.includes("/")) return pathPart;
35963
- }
35964
- } else {
35965
- const parsed = new URL(u);
35966
- const p = parsed.pathname.replace(/^\//, "").replace(/\.git$/i, "");
35967
- const parts = p.split("/").filter(Boolean);
35968
- if (parts.length >= 2) {
35969
- return `${parts[parts.length - 2]}/${parts[parts.length - 1]}`;
35970
- }
35971
- if (parts.length === 1) return parts[0];
35972
- }
35973
- } catch {
35974
- }
35975
- }
35976
- if (repoRelPath && repoRelPath !== ".") {
35977
- const segments = repoRelPath.split("/").filter(Boolean);
35978
- const last2 = segments[segments.length - 1];
35979
- if (last2) return last2;
35980
- }
35981
- return "Repository";
35982
- }
35983
- function formatRemoteDisplayLabel(remoteUrl) {
35984
- const u = remoteUrl.trim();
35985
- if (!u) return "";
35986
- let hostPath = u;
35987
- try {
35988
- if (u.startsWith("git@")) {
35989
- const rest = u.slice("git@".length);
35990
- const slash = rest.indexOf(":");
35991
- if (slash > 0) hostPath = `${rest.slice(0, slash)}/${rest.slice(slash + 1)}`;
35992
- } else {
35993
- const parsed = new URL(u);
35994
- hostPath = `${parsed.hostname}${parsed.pathname}`.replace(/\/\.git$/i, "").replace(/\.git$/i, "");
35995
- }
35996
- } catch {
35997
- hostPath = u.replace(/^https?:\/\//i, "").replace(/\.git$/i, "");
35998
- }
35999
- return `origin \xB7 ${hostPath}`;
36000
- }
36001
-
36002
- // src/git/changes/get-working-tree-change-repo-details.ts
36003
- import * as path24 from "node:path";
36004
-
36005
35714
  // src/git/commits/lib/parse-log-lines.ts
36006
35715
  function parseLogShaDateSubjectLines(raw) {
36007
35716
  const out = [];
@@ -36072,6 +35781,59 @@ async function listRecentCommits(repoDir, limitInput) {
36072
35781
  }
36073
35782
  }
36074
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
+
36075
35837
  // src/git/changes/lib/patch-truncate.ts
36076
35838
  function truncatePatch(s) {
36077
35839
  if (s.length <= MAX_PATCH_CHARS) return s;
@@ -36137,8 +35899,8 @@ async function listChangedFilesForCommit(repoGitCwd, repoRelPath, commitSha) {
36137
35899
  }
36138
35900
 
36139
35901
  // src/git/changes/list-changed-files-for-repo.ts
36140
- import * as fs23 from "node:fs";
36141
- import * as path23 from "node:path";
35902
+ import * as fs18 from "node:fs";
35903
+ import * as path20 from "node:path";
36142
35904
 
36143
35905
  // src/git/changes/lib/count-lines.ts
36144
35906
  import { createReadStream } from "node:fs";
@@ -36162,7 +35924,7 @@ async function countTextFileLines(filePath) {
36162
35924
  }
36163
35925
 
36164
35926
  // src/git/changes/hydrate-patch.ts
36165
- import * as fs22 from "node:fs";
35927
+ import * as fs17 from "node:fs";
36166
35928
  var UNIFIED_HUNK_HEADER_RE = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
36167
35929
  var MAX_HYDRATE_LINES_PER_GAP = 8e3;
36168
35930
  var MAX_HYDRATE_LINES_PER_FILE = 8e4;
@@ -36177,7 +35939,7 @@ async function readGitBlobLines(repoCwd, pathInRepo) {
36177
35939
  }
36178
35940
  async function readWorktreeFileLines(filePath) {
36179
35941
  try {
36180
- const raw = await fs22.promises.readFile(filePath, "utf8");
35942
+ const raw = await fs17.promises.readFile(filePath, "utf8");
36181
35943
  return raw.split(/\r?\n/);
36182
35944
  } catch {
36183
35945
  return null;
@@ -36302,7 +36064,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
36302
36064
  const rows = [];
36303
36065
  await forEachWithGitYield([...paths], async (pathInRepo) => {
36304
36066
  const relLauncher = posixJoinDirFile(repoRelPath, pathInRepo.replace(/\\/g, "/"));
36305
- const repoFilePath = path23.join(repoGitCwd, pathInRepo);
36067
+ const repoFilePath = path20.join(repoGitCwd, pathInRepo);
36306
36068
  const nums = numByPath.get(pathInRepo);
36307
36069
  let additions = nums?.additions ?? 0;
36308
36070
  let deletions = nums?.deletions ?? 0;
@@ -36315,7 +36077,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
36315
36077
  deletions = fromGit.deletions;
36316
36078
  } else {
36317
36079
  try {
36318
- const st = await fs23.promises.stat(repoFilePath);
36080
+ const st = await fs18.promises.stat(repoFilePath);
36319
36081
  if (st.isFile()) additions = await countTextFileLines(repoFilePath);
36320
36082
  else additions = 0;
36321
36083
  } catch {
@@ -36341,7 +36103,7 @@ async function listChangedFilesForRepo(repoGitCwd, repoRelPath) {
36341
36103
  } else {
36342
36104
  pathInRepo = row.pathRelLauncher;
36343
36105
  }
36344
- const filePath = path23.join(repoGitCwd, pathInRepo);
36106
+ const filePath = path20.join(repoGitCwd, pathInRepo);
36345
36107
  let patch = await unifiedDiffForFile(repoGitCwd, pathInRepo, row.change);
36346
36108
  if (patch) {
36347
36109
  patch = await hydrateUnifiedPatchWithFileContext(patch, filePath, repoGitCwd, pathInRepo, row.change);
@@ -36357,8 +36119,8 @@ function normRepoRel(p) {
36357
36119
  return x === "" ? "." : x;
36358
36120
  }
36359
36121
  async function getWorkingTreeChangeRepoDetails(options) {
36360
- const bridgeRoot = path24.resolve(getBridgeRoot());
36361
- 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;
36362
36124
  const legacyNested = options.legacyRepoNestedSessionLayout === true;
36363
36125
  const out = [];
36364
36126
  const filter = options.repoFilterRelPath != null ? normRepoRel(options.repoFilterRelPath) : null;
@@ -36373,7 +36135,7 @@ async function getWorkingTreeChangeRepoDetails(options) {
36373
36135
  for (let i = 0; i < options.commitTargetPaths.length; i++) {
36374
36136
  if (i > 0) await yieldToEventLoop2();
36375
36137
  const target = options.commitTargetPaths[i];
36376
- const t = path24.resolve(target);
36138
+ const t = path21.resolve(target);
36377
36139
  if (!await isGitRepoDirectory(t)) continue;
36378
36140
  const g = cliSimpleGit(t);
36379
36141
  let branch = "HEAD";
@@ -36386,8 +36148,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
36386
36148
  const remoteDisplay = formatRemoteDisplayLabel(remoteUrl);
36387
36149
  let repoRelPath;
36388
36150
  if (sessionWtRoot) {
36389
- const anchor = legacyNested ? path24.dirname(t) : t;
36390
- const relNorm = path24.relative(sessionWtRoot, anchor);
36151
+ const anchor = legacyNested ? path21.dirname(t) : t;
36152
+ const relNorm = path21.relative(sessionWtRoot, anchor);
36391
36153
  repoRelPath = relNorm === "" ? "." : relNorm.replace(/\\/g, "/");
36392
36154
  } else {
36393
36155
  let top = t;
@@ -36396,8 +36158,8 @@ async function getWorkingTreeChangeRepoDetails(options) {
36396
36158
  } catch {
36397
36159
  top = t;
36398
36160
  }
36399
- const rel = path24.relative(bridgeRoot, path24.resolve(top)).replace(/\\/g, "/") || ".";
36400
- 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;
36401
36163
  }
36402
36164
  const norm = normRepoRel(repoRelPath === "" ? "." : repoRelPath);
36403
36165
  if (filter && norm !== filter) continue;
@@ -36429,6 +36191,64 @@ async function getWorkingTreeChangeRepoDetails(options) {
36429
36191
  return out;
36430
36192
  }
36431
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
+
36432
36252
  // src/git/branches/commit-and-push.ts
36433
36253
  async function gitCommitAllIfDirty(repoDir, message, options) {
36434
36254
  const g = cliSimpleGit(repoDir);
@@ -36466,12 +36286,137 @@ async function commitSessionWorktrees(options) {
36466
36286
  }
36467
36287
  }
36468
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
+
36469
36414
  // src/worktrees/discover-session-worktree-on-disk.ts
36470
- import * as fs24 from "node:fs";
36471
- import * as path25 from "node:path";
36415
+ import * as fs23 from "node:fs";
36416
+ import * as path24 from "node:path";
36472
36417
  function isGitDir(dirPath) {
36473
36418
  try {
36474
- return fs24.existsSync(path25.join(dirPath, ".git"));
36419
+ return fs23.existsSync(path24.join(dirPath, ".git"));
36475
36420
  } catch {
36476
36421
  return false;
36477
36422
  }
@@ -36480,23 +36425,23 @@ function collectGitRepoRootsUnderDirectory(rootPath) {
36480
36425
  const out = [];
36481
36426
  const walk = (dir) => {
36482
36427
  if (isGitDir(dir)) {
36483
- out.push(path25.resolve(dir));
36428
+ out.push(path24.resolve(dir));
36484
36429
  return;
36485
36430
  }
36486
36431
  let entries;
36487
36432
  try {
36488
- entries = fs24.readdirSync(dir, { withFileTypes: true });
36433
+ entries = fs23.readdirSync(dir, { withFileTypes: true });
36489
36434
  } catch {
36490
36435
  return;
36491
36436
  }
36492
36437
  for (const e of entries) {
36493
36438
  if (e.name.startsWith(".")) continue;
36494
- const full = path25.join(dir, e.name);
36439
+ const full = path24.join(dir, e.name);
36495
36440
  if (!e.isDirectory()) continue;
36496
36441
  walk(full);
36497
36442
  }
36498
36443
  };
36499
- walk(path25.resolve(rootPath));
36444
+ walk(path24.resolve(rootPath));
36500
36445
  return [...new Set(out)];
36501
36446
  }
36502
36447
  function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
@@ -36505,16 +36450,16 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
36505
36450
  if (depth > maxDepth) return;
36506
36451
  let entries;
36507
36452
  try {
36508
- entries = fs24.readdirSync(dir, { withFileTypes: true });
36453
+ entries = fs23.readdirSync(dir, { withFileTypes: true });
36509
36454
  } catch {
36510
36455
  return;
36511
36456
  }
36512
36457
  for (const e of entries) {
36513
36458
  if (e.name.startsWith(".")) continue;
36514
- const full = path25.join(dir, e.name);
36459
+ const full = path24.join(dir, e.name);
36515
36460
  if (!e.isDirectory()) continue;
36516
36461
  if (e.name === sessionId) {
36517
- if (isGitDir(full)) out.push(path25.resolve(full));
36462
+ if (isGitDir(full)) out.push(path24.resolve(full));
36518
36463
  } else {
36519
36464
  walk(full, depth + 1);
36520
36465
  }
@@ -36526,14 +36471,14 @@ function collectWorktreeRootsNamed(root, sessionId, maxDepth) {
36526
36471
  function tryBindingFromSessionDirectory(sessionDir) {
36527
36472
  let st;
36528
36473
  try {
36529
- st = fs24.statSync(sessionDir);
36474
+ st = fs23.statSync(sessionDir);
36530
36475
  } catch {
36531
36476
  return null;
36532
36477
  }
36533
36478
  if (!st.isDirectory()) return null;
36534
36479
  const worktreePaths = collectGitRepoRootsUnderDirectory(sessionDir);
36535
36480
  if (worktreePaths.length === 0) return null;
36536
- const abs = path25.resolve(sessionDir);
36481
+ const abs = path24.resolve(sessionDir);
36537
36482
  return {
36538
36483
  sessionParentPath: abs,
36539
36484
  workingTreeRelRoot: abs,
@@ -36543,20 +36488,20 @@ function tryBindingFromSessionDirectory(sessionDir) {
36543
36488
  function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
36544
36489
  const sid = sessionId.trim();
36545
36490
  if (!sid) return null;
36546
- const hintR = path25.resolve(checkoutPath);
36491
+ const hintR = path24.resolve(checkoutPath);
36547
36492
  let best = null;
36548
- let cur = path25.dirname(hintR);
36493
+ let cur = path24.dirname(hintR);
36549
36494
  for (let i = 0; i < 40; i++) {
36550
36495
  const paths = collectWorktreeRootsNamed(cur, sid, 24);
36551
- if (paths.some((p) => path25.resolve(p) === hintR)) {
36552
- 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]);
36553
36498
  best = {
36554
- sessionParentPath: path25.resolve(isolated),
36555
- workingTreeRelRoot: path25.resolve(cur),
36556
- 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))
36557
36502
  };
36558
36503
  }
36559
- const next = path25.dirname(cur);
36504
+ const next = path24.dirname(cur);
36560
36505
  if (next === cur) break;
36561
36506
  cur = next;
36562
36507
  }
@@ -36564,33 +36509,33 @@ function discoverLegacyBindingAscendingFromCheckout(sessionId, checkoutPath) {
36564
36509
  }
36565
36510
  function discoverSessionWorktreeOnDisk(options) {
36566
36511
  const { sessionId, worktreesRootPath, layout, bridgeRoot } = options;
36567
- if (!sessionId.trim() || !fs24.existsSync(worktreesRootPath)) return null;
36512
+ if (!sessionId.trim() || !fs23.existsSync(worktreesRootPath)) return null;
36568
36513
  const preferredKey = getLauncherDirNameIfPresent(layout, bridgeRoot);
36569
36514
  const keys = [];
36570
36515
  if (preferredKey) keys.push(preferredKey);
36571
36516
  try {
36572
- for (const name of fs24.readdirSync(worktreesRootPath)) {
36517
+ for (const name of fs23.readdirSync(worktreesRootPath)) {
36573
36518
  if (name.startsWith(".")) continue;
36574
- const p = path25.join(worktreesRootPath, name);
36575
- if (!fs24.statSync(p).isDirectory()) continue;
36519
+ const p = path24.join(worktreesRootPath, name);
36520
+ if (!fs23.statSync(p).isDirectory()) continue;
36576
36521
  if (name !== preferredKey) keys.push(name);
36577
36522
  }
36578
36523
  } catch {
36579
36524
  return null;
36580
36525
  }
36581
36526
  for (const key of keys) {
36582
- const layoutRoot = path25.join(worktreesRootPath, key);
36583
- if (!fs24.existsSync(layoutRoot) || !fs24.statSync(layoutRoot).isDirectory()) continue;
36584
- 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);
36585
36530
  const nested = tryBindingFromSessionDirectory(sessionDir);
36586
36531
  if (nested) return nested;
36587
36532
  const legacyPaths = collectWorktreeRootsNamed(layoutRoot, sessionId, 24);
36588
36533
  if (legacyPaths.length > 0) {
36589
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
36534
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
36590
36535
  return {
36591
- sessionParentPath: path25.resolve(isolated),
36592
- workingTreeRelRoot: path25.resolve(layoutRoot),
36593
- 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))
36594
36539
  };
36595
36540
  }
36596
36541
  }
@@ -36599,12 +36544,12 @@ function discoverSessionWorktreeOnDisk(options) {
36599
36544
  function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPathOrHint, sessionId) {
36600
36545
  const sid = sessionId.trim();
36601
36546
  if (!sid) return null;
36602
- const hint = path25.resolve(sessionWorktreeRootPathOrHint);
36603
- const underHint = tryBindingFromSessionDirectory(path25.join(hint, sid));
36547
+ const hint = path24.resolve(sessionWorktreeRootPathOrHint);
36548
+ const underHint = tryBindingFromSessionDirectory(path24.join(hint, sid));
36604
36549
  if (underHint) return underHint;
36605
36550
  const direct = tryBindingFromSessionDirectory(hint);
36606
36551
  if (direct) {
36607
- if (path25.basename(hint) === sid && isGitDir(hint)) {
36552
+ if (path24.basename(hint) === sid && isGitDir(hint)) {
36608
36553
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
36609
36554
  if (legacyFromCheckout && legacyFromCheckout.repoCheckoutPaths.length > direct.repoCheckoutPaths.length) {
36610
36555
  return legacyFromCheckout;
@@ -36612,216 +36557,349 @@ function discoverSessionWorktreesUnderSessionWorktreeRoot(sessionWorktreeRootPat
36612
36557
  }
36613
36558
  return direct;
36614
36559
  }
36615
- if (path25.basename(hint) === sid && isGitDir(hint)) {
36560
+ if (path24.basename(hint) === sid && isGitDir(hint)) {
36616
36561
  const legacyFromCheckout = discoverLegacyBindingAscendingFromCheckout(sid, hint);
36617
36562
  if (legacyFromCheckout) return legacyFromCheckout;
36618
36563
  }
36619
36564
  let st;
36620
36565
  try {
36621
- st = fs24.statSync(hint);
36566
+ st = fs23.statSync(hint);
36622
36567
  } catch {
36623
36568
  return null;
36624
36569
  }
36625
36570
  if (!st.isDirectory()) return null;
36626
36571
  const legacyPaths = collectWorktreeRootsNamed(hint, sid, 24);
36627
36572
  if (legacyPaths.length === 0) return null;
36628
- const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path25.resolve(legacyPaths[0]);
36573
+ const isolated = resolveIsolatedSessionParentPathFromCheckouts(legacyPaths) ?? path24.resolve(legacyPaths[0]);
36629
36574
  return {
36630
- sessionParentPath: path25.resolve(isolated),
36575
+ sessionParentPath: path24.resolve(isolated),
36631
36576
  workingTreeRelRoot: hint,
36632
- repoCheckoutPaths: legacyPaths.map((p) => path25.resolve(p))
36577
+ repoCheckoutPaths: legacyPaths.map((p) => path24.resolve(p))
36633
36578
  };
36634
36579
  }
36635
36580
 
36636
- // 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
36637
36627
  function parseSessionParent(v) {
36638
36628
  if (v === "bridge_root" || v === "worktrees_root") return v;
36639
36629
  if (v === "session_worktrees_root") return "worktrees_root";
36640
36630
  return null;
36641
36631
  }
36642
- var SessionWorktreeManager = class {
36643
- worktreesRootPath;
36644
- 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 {
36645
36800
  sessionRepoCheckoutPaths = /* @__PURE__ */ new Map();
36646
36801
  sessionParentPathBySession = /* @__PURE__ */ new Map();
36647
36802
  sessionWorkingTreeRelRootBySession = /* @__PURE__ */ new Map();
36648
- layout;
36649
- constructor(options) {
36650
- this.worktreesRootPath = options.worktreesRootPath;
36651
- this.log = options.log;
36652
- this.layout = loadWorktreeLayout();
36653
- }
36654
- rememberSessionWorktrees(sessionId, binding) {
36655
- const paths = binding.repoCheckoutPaths.map((p) => path26.resolve(p));
36803
+ remember(sessionId, binding) {
36804
+ const paths = binding.repoCheckoutPaths.map((p) => path27.resolve(p));
36656
36805
  this.sessionRepoCheckoutPaths.set(sessionId, paths);
36657
- this.sessionParentPathBySession.set(sessionId, path26.resolve(binding.sessionParentPath));
36658
- 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;
36659
36815
  }
36660
- sessionParentPathAfterRemember(sessionId) {
36816
+ getSessionParentPath(sessionId) {
36661
36817
  return this.sessionParentPathBySession.get(sessionId);
36662
36818
  }
36663
- tryDiscoverFromDisk(sessionId) {
36664
- return discoverSessionWorktreeOnDisk({
36665
- sessionId,
36666
- worktreesRootPath: this.worktreesRootPath,
36667
- layout: this.layout,
36668
- bridgeRoot: getBridgeRoot()
36669
- });
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;
36670
36832
  }
36671
36833
  isLegacyNestedLayout(sessionId) {
36672
36834
  const parent = this.sessionParentPathBySession.get(sessionId);
36673
36835
  const relRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId);
36674
36836
  if (!parent || !relRoot) return false;
36675
- 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
+ });
36676
36858
  }
36677
- /**
36678
- * Session parent path for `worktrees_root`: the per-session directory (new layout) or primary checkout (legacy).
36679
- */
36680
36859
  getIsolatedSessionParentPathForSession(sessionId) {
36681
- if (!sessionId) return null;
36682
- const sid = sessionId.trim();
36683
- const cached2 = this.sessionParentPathBySession.get(sid);
36684
- if (cached2) return path26.resolve(cached2);
36685
- const paths = this.ensureRepoCheckoutPathsForSession(sid) ?? this.getRepoCheckoutPathsForSession(sid);
36686
- if (!paths?.length) return null;
36687
- return resolveIsolatedSessionParentPathFromCheckouts(paths);
36688
- }
36689
- /**
36690
- * Resolved **session parent path** for the agent: session directory in worktrees mode,
36691
- * or `undefined` meaning use {@link getBridgeRoot}.
36692
- */
36693
- async resolveSessionParentPathForPrompt(sessionId, opts) {
36694
- if (!sessionId) return void 0;
36695
- const sid = sessionId.trim();
36696
- const parentPathRaw = opts.sessionParentPath?.trim();
36697
- if (parentPathRaw) {
36698
- const resolved = path26.resolve(parentPathRaw);
36699
- if (sid && parseSessionParent(opts.sessionParent) === "worktrees_root") {
36700
- const diskFirst = this.tryDiscoverFromDisk(sid);
36701
- if (diskFirst) {
36702
- this.rememberSessionWorktrees(sid, diskFirst);
36703
- return this.sessionParentPathAfterRemember(sid);
36704
- }
36705
- const fromRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(resolved, sid);
36706
- if (fromRoot) {
36707
- this.rememberSessionWorktrees(sid, fromRoot);
36708
- return this.sessionParentPathAfterRemember(sid);
36709
- }
36710
- let cur = resolved;
36711
- for (let i = 0; i < 16; i++) {
36712
- const tryRoot = discoverSessionWorktreesUnderSessionWorktreeRoot(cur, sid);
36713
- if (tryRoot) {
36714
- this.rememberSessionWorktrees(sid, tryRoot);
36715
- return this.sessionParentPathAfterRemember(sid);
36716
- }
36717
- const next = path26.dirname(cur);
36718
- if (next === cur) break;
36719
- cur = next;
36720
- }
36721
- }
36722
- return resolved;
36723
- }
36724
- const parentKind = parseSessionParent(opts.sessionParent);
36725
- if (parentKind === "bridge_root") {
36726
- return void 0;
36727
- }
36728
- if (parentKind === "worktrees_root") {
36729
- if (!opts.isNewSession) {
36730
- const cached2 = this.sessionParentPathAfterRemember(sid);
36731
- if (cached2) return cached2;
36732
- const disc = this.tryDiscoverFromDisk(sid);
36733
- if (disc) {
36734
- this.rememberSessionWorktrees(sid, disc);
36735
- return this.sessionParentPathAfterRemember(sid);
36736
- }
36737
- return void 0;
36738
- }
36739
- const prep2 = await prepareNewSessionWorktrees({
36740
- worktreesRootPath: this.worktreesRootPath,
36741
- bridgeRoot: getBridgeRoot(),
36742
- sessionId: sid,
36743
- layout: this.layout,
36744
- log: this.log
36745
- });
36746
- if (!prep2) return void 0;
36747
- this.rememberSessionWorktrees(sid, {
36748
- sessionParentPath: prep2.sessionParentPath,
36749
- workingTreeRelRoot: prep2.workingTreeRelRoot,
36750
- repoCheckoutPaths: prep2.worktreePaths
36751
- });
36752
- return this.sessionParentPathAfterRemember(sid);
36753
- }
36754
- if (!opts.isNewSession) {
36755
- const cached2 = this.sessionParentPathAfterRemember(sid);
36756
- if (cached2) return cached2;
36757
- const disc = this.tryDiscoverFromDisk(sid);
36758
- if (disc) {
36759
- this.rememberSessionWorktrees(sid, disc);
36760
- return this.sessionParentPathAfterRemember(sid);
36761
- }
36762
- return void 0;
36763
- }
36764
- 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,
36765
36870
  worktreesRootPath: this.worktreesRootPath,
36766
- bridgeRoot: getBridgeRoot(),
36767
- sessionId: sid,
36768
36871
  layout: this.layout,
36769
- log: this.log
36770
- });
36771
- if (!prep) return void 0;
36772
- this.rememberSessionWorktrees(sid, {
36773
- sessionParentPath: prep.sessionParentPath,
36774
- workingTreeRelRoot: prep.workingTreeRelRoot,
36775
- repoCheckoutPaths: prep.worktreePaths
36872
+ log: this.log,
36873
+ discover: (sid) => this.discover(sid),
36874
+ opts
36776
36875
  });
36777
- return this.sessionParentPathAfterRemember(sid);
36778
36876
  }
36779
36877
  async renameSessionBranch(sessionId, newBranch) {
36780
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36878
+ const paths = this.cache.getRepoCheckoutPathsRef(sessionId);
36781
36879
  if (!paths?.length) return;
36782
36880
  await renameSessionWorktreeBranches(paths, newBranch, this.log);
36783
36881
  }
36784
- /** True when this session uses an isolated worktree layout (not the bridge root). */
36785
36882
  usesWorktreeSession(sessionId) {
36786
36883
  if (!sessionId) return false;
36787
- return this.sessionParentPathBySession.has(sessionId);
36884
+ return this.cache.hasSession(sessionId);
36788
36885
  }
36789
- /** Per-repo git checkout directories for this session (for snapshots, commits, change lists). */
36790
36886
  getRepoCheckoutPathsForSession(sessionId) {
36791
36887
  if (!sessionId) return void 0;
36792
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36793
- return paths?.length ? [...paths] : void 0;
36888
+ return this.cache.getRepoCheckoutPaths(sessionId);
36794
36889
  }
36795
- /**
36796
- * Same paths as {@link getRepoCheckoutPathsForSession}, but loads from disk into memory when the CLI
36797
- * restarted or maps were not yet populated (avoids discovering every repo under the worktrees root).
36798
- */
36799
36890
  ensureRepoCheckoutPathsForSession(sessionId) {
36800
- if (!sessionId?.trim()) return void 0;
36801
- const sid = sessionId.trim();
36802
- const cached2 = this.sessionRepoCheckoutPaths.get(sid);
36803
- if (cached2?.length) return [...cached2];
36804
- const disc = this.tryDiscoverFromDisk(sid);
36805
- if (disc?.repoCheckoutPaths.length) {
36806
- this.rememberSessionWorktrees(sid, disc);
36807
- return [...disc.repoCheckoutPaths];
36808
- }
36809
- return void 0;
36891
+ return ensureRepoCheckoutPathsForSession(sessionId, this.cache, (sid) => this.discover(sid));
36810
36892
  }
36811
- /** Session parent directory when in worktrees mode; null otherwise (same as {@link getIsolatedSessionParentPathForSession} path). */
36812
36893
  getSessionWorktreeRootForSession(sessionId) {
36813
36894
  return this.getIsolatedSessionParentPathForSession(sessionId);
36814
36895
  }
36815
36896
  async removeSessionWorktrees(sessionId) {
36816
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36817
- this.sessionRepoCheckoutPaths.delete(sessionId);
36818
- this.sessionParentPathBySession.delete(sessionId);
36819
- this.sessionWorkingTreeRelRootBySession.delete(sessionId);
36897
+ const paths = this.cache.clearSession(sessionId);
36820
36898
  if (!paths?.length) return;
36821
36899
  await removeSessionWorktrees(paths, this.log);
36822
36900
  }
36823
36901
  async commitSession(params) {
36824
- const paths = this.sessionRepoCheckoutPaths.get(params.sessionId);
36902
+ const paths = this.cache.getRepoCheckoutPathsRef(params.sessionId);
36825
36903
  const targets = paths?.length ? paths : [getBridgeRoot()];
36826
36904
  return commitSessionWorktrees({
36827
36905
  paths: targets,
@@ -36830,28 +36908,17 @@ var SessionWorktreeManager = class {
36830
36908
  push: params.push
36831
36909
  });
36832
36910
  }
36833
- resolveCommitTargets(sessionId) {
36834
- const paths = this.sessionRepoCheckoutPaths.get(sessionId);
36835
- if (paths?.length) return paths;
36836
- const disc = this.tryDiscoverFromDisk(sessionId);
36837
- if (disc?.repoCheckoutPaths.length) {
36838
- this.rememberSessionWorktrees(sessionId, disc);
36839
- return disc.repoCheckoutPaths;
36840
- }
36841
- return [getBridgeRoot()];
36842
- }
36843
36911
  async getSessionWorkingTreeStatus(sessionId) {
36844
- return aggregateSessionPathsWorkingTreeStatus(this.resolveCommitTargets(sessionId));
36912
+ return aggregateSessionPathsWorkingTreeStatus(
36913
+ resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
36914
+ );
36845
36915
  }
36846
- /** Per-repo changed files vs HEAD (or a single commit vs parent) for the same git roots used for commit/push. */
36847
36916
  async getSessionWorkingTreeChangeDetails(sessionId, opts) {
36848
- const targets = this.resolveCommitTargets(sessionId);
36849
- const sessionWorkingTreeRelRoot = this.sessionWorkingTreeRelRootBySession.get(sessionId) ?? null;
36850
- const legacyNested = this.isLegacyNestedLayout(sessionId);
36917
+ const targets = resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid));
36851
36918
  return getWorkingTreeChangeRepoDetails({
36852
36919
  commitTargetPaths: targets,
36853
- sessionWorktreeRootPath: sessionWorkingTreeRelRoot,
36854
- legacyRepoNestedSessionLayout: legacyNested,
36920
+ sessionWorktreeRootPath: this.cache.getWorkingTreeRelRoot(sessionId),
36921
+ legacyRepoNestedSessionLayout: this.cache.isLegacyNestedLayout(sessionId),
36855
36922
  repoFilterRelPath: opts?.repoRelPath?.trim() ? opts.repoRelPath.trim() : null,
36856
36923
  basis: opts?.basis,
36857
36924
  recentCommitsLimit: opts?.recentCommitsLimit
@@ -36859,7 +36926,9 @@ var SessionWorktreeManager = class {
36859
36926
  }
36860
36927
  async pushSessionUpstream(sessionId) {
36861
36928
  try {
36862
- await pushAheadOfUpstreamForPaths(this.resolveCommitTargets(sessionId));
36929
+ await pushAheadOfUpstreamForPaths(
36930
+ resolveCommitTargets(sessionId, this.cache, (sid) => this.discover(sid))
36931
+ );
36863
36932
  return { ok: true };
36864
36933
  } catch (e) {
36865
36934
  const err = e instanceof Error ? e.message : String(e);
@@ -36867,27 +36936,31 @@ var SessionWorktreeManager = class {
36867
36936
  }
36868
36937
  }
36869
36938
  };
36939
+
36940
+ // src/worktrees/manager/default-worktrees-root-path.ts
36941
+ import * as path28 from "node:path";
36942
+ import os8 from "node:os";
36870
36943
  function defaultWorktreesRootPath() {
36871
- return path26.join(os8.homedir(), ".buildautomaton", "worktrees");
36944
+ return path28.join(os8.homedir(), ".buildautomaton", "worktrees");
36872
36945
  }
36873
36946
 
36874
36947
  // src/files/watch-file-index.ts
36875
36948
  import { watch } from "node:fs";
36876
- import path31 from "node:path";
36949
+ import path33 from "node:path";
36877
36950
 
36878
36951
  // src/files/index/paths.ts
36879
- import path27 from "node:path";
36952
+ import path29 from "node:path";
36880
36953
  import crypto2 from "node:crypto";
36881
36954
  function getCwdHashForFileIndex(resolvedCwd) {
36882
- 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);
36883
36956
  }
36884
36957
 
36885
36958
  // src/files/index/build-file-index.ts
36886
- import path29 from "node:path";
36959
+ import path31 from "node:path";
36887
36960
 
36888
36961
  // src/files/index/walk-workspace-tree.ts
36889
36962
  import fs25 from "node:fs";
36890
- import path28 from "node:path";
36963
+ import path30 from "node:path";
36891
36964
  var DEPENDENCY_INSTALL_DIR_NAMES = /* @__PURE__ */ new Set([
36892
36965
  "node_modules",
36893
36966
  "bower_components",
@@ -36916,18 +36989,18 @@ async function walkWorkspaceTreeAsync(dir, baseDir, onFile, state) {
36916
36989
  if (isCliImmediateShutdownRequested()) throw new CliSqliteInterrupted();
36917
36990
  }
36918
36991
  state.n++;
36919
- const full = path28.join(dir, name);
36992
+ const full = path30.join(dir, name);
36920
36993
  let stat3;
36921
36994
  try {
36922
36995
  stat3 = await fs25.promises.stat(full);
36923
36996
  } catch {
36924
36997
  continue;
36925
36998
  }
36926
- const relative5 = path28.relative(baseDir, full).replace(/\\/g, "/");
36999
+ const relative6 = path30.relative(baseDir, full).replace(/\\/g, "/");
36927
37000
  if (stat3.isDirectory()) {
36928
37001
  await walkWorkspaceTreeAsync(full, baseDir, onFile, state);
36929
37002
  } else if (stat3.isFile()) {
36930
- onFile(relative5);
37003
+ onFile(relative6);
36931
37004
  }
36932
37005
  }
36933
37006
  }
@@ -37025,7 +37098,7 @@ async function collectWorkspacePathsAsync(resolved) {
37025
37098
  }
37026
37099
  async function buildFileIndexAsync(cwd) {
37027
37100
  return withFileIndexSqliteLock(async () => {
37028
- const resolved = path29.resolve(cwd);
37101
+ const resolved = path31.resolve(cwd);
37029
37102
  await yieldToEventLoop();
37030
37103
  assertNotShutdown();
37031
37104
  const paths = await collectWorkspacePathsAsync(resolved);
@@ -37037,7 +37110,7 @@ async function buildFileIndexAsync(cwd) {
37037
37110
  }
37038
37111
 
37039
37112
  // src/files/index/ensure-file-index.ts
37040
- import path30 from "node:path";
37113
+ import path32 from "node:path";
37041
37114
 
37042
37115
  // src/files/index/search-file-index.ts
37043
37116
  function escapeLikePattern(fragment) {
@@ -37089,7 +37162,7 @@ async function searchBridgeFilePathsAsync(resolvedCwd, query, limit = 100) {
37089
37162
 
37090
37163
  // src/files/index/ensure-file-index.ts
37091
37164
  async function ensureFileIndexAsync(cwd) {
37092
- const resolved = path30.resolve(cwd);
37165
+ const resolved = path32.resolve(cwd);
37093
37166
  if (await bridgeFileIndexIsPopulated(resolved)) {
37094
37167
  return { fromCache: true, pathCount: await bridgeFileIndexPathCount(resolved) };
37095
37168
  }
@@ -37133,7 +37206,7 @@ function createFsWatcher(resolved, schedule) {
37133
37206
  }
37134
37207
  }
37135
37208
  function startFileIndexWatcher(cwd = getBridgeRoot()) {
37136
- const resolved = path31.resolve(cwd);
37209
+ const resolved = path33.resolve(cwd);
37137
37210
  void buildFileIndexAsync(resolved).catch((e) => {
37138
37211
  if (e instanceof CliSqliteInterrupted) return;
37139
37212
  console.error("[file-index] Initial index build failed:", e);
@@ -37163,7 +37236,7 @@ function startFileIndexWatcher(cwd = getBridgeRoot()) {
37163
37236
  }
37164
37237
 
37165
37238
  // src/connection/create-bridge-connection.ts
37166
- import * as path41 from "node:path";
37239
+ import * as path44 from "node:path";
37167
37240
 
37168
37241
  // src/dev-servers/manager/dev-server-manager.ts
37169
37242
  import { rm as rm2 } from "node:fs/promises";
@@ -37185,15 +37258,15 @@ function sendDevServerStatus(getWs, serverId, status, options) {
37185
37258
 
37186
37259
  // src/dev-servers/process/terminate-child-process.ts
37187
37260
  async function sigtermAndWaitForExit(proc, graceMs, log2, shortId) {
37188
- const exited = new Promise((resolve20) => {
37189
- proc.once("exit", () => resolve20());
37261
+ const exited = new Promise((resolve22) => {
37262
+ proc.once("exit", () => resolve22());
37190
37263
  });
37191
37264
  log2(`[dev-server] Sending SIGTERM to ${shortId} (pid=${proc.pid ?? "?"}).`);
37192
37265
  try {
37193
37266
  proc.kill("SIGTERM");
37194
37267
  } catch {
37195
37268
  }
37196
- await Promise.race([exited, new Promise((resolve20) => setTimeout(resolve20, graceMs))]);
37269
+ await Promise.race([exited, new Promise((resolve22) => setTimeout(resolve22, graceMs))]);
37197
37270
  }
37198
37271
  function forceKillChild(proc, log2, shortId, graceMs) {
37199
37272
  log2(
@@ -37473,10 +37546,10 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
37473
37546
  import { spawn as spawn7 } from "node:child_process";
37474
37547
  import fs29 from "node:fs";
37475
37548
  import { tmpdir } from "node:os";
37476
- import path32 from "node:path";
37549
+ import path34 from "node:path";
37477
37550
  function trySpawnMergedLogFile(command, env, cwd, signal) {
37478
- const tmpRoot = fs29.mkdtempSync(path32.join(tmpdir(), "ba-devsrv-log-"));
37479
- 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");
37480
37553
  let logFd;
37481
37554
  try {
37482
37555
  logFd = fs29.openSync(logPath, "a");
@@ -37520,15 +37593,15 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
37520
37593
  import { spawn as spawn8 } from "node:child_process";
37521
37594
  import fs30 from "node:fs";
37522
37595
  import { tmpdir as tmpdir2 } from "node:os";
37523
- import path33 from "node:path";
37596
+ import path35 from "node:path";
37524
37597
  function shSingleQuote(s) {
37525
37598
  return `'${s.replace(/'/g, `'\\''`)}'`;
37526
37599
  }
37527
37600
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
37528
- const tmpRoot = fs30.mkdtempSync(path33.join(tmpdir2(), "ba-devsrv-sh-"));
37529
- const logPath = path33.join(tmpRoot, "combined.log");
37530
- const innerPath = path33.join(tmpRoot, "_cmd.sh");
37531
- 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");
37532
37605
  try {
37533
37606
  fs30.writeFileSync(innerPath, `#!/bin/sh
37534
37607
  ${command}
@@ -37559,9 +37632,9 @@ cd ${shSingleQuote(cwd)}
37559
37632
  }
37560
37633
  }
37561
37634
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
37562
- const tmpRoot = fs30.mkdtempSync(path33.join(tmpdir2(), "ba-devsrv-sh-"));
37563
- const logPath = path33.join(tmpRoot, "combined.log");
37564
- 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");
37565
37638
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
37566
37639
  const com = process.env.ComSpec || "cmd.exe";
37567
37640
  try {
@@ -38074,7 +38147,7 @@ async function proxyToLocal(request) {
38074
38147
  };
38075
38148
  const maxAttempts = isIdempotentProxyMethod(request.method) ? LOCAL_PREVIEW_FETCH_RETRY_DELAYS_MS.length + 1 : 1;
38076
38149
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
38077
- const once = await new Promise((resolve20) => {
38150
+ const once = await new Promise((resolve22) => {
38078
38151
  const req = mod.request(opts, (res) => {
38079
38152
  const chunks = [];
38080
38153
  res.on("data", (c) => chunks.push(c));
@@ -38085,7 +38158,7 @@ async function proxyToLocal(request) {
38085
38158
  if (typeof v === "string") headers[k] = v;
38086
38159
  else if (Array.isArray(v) && v[0]) headers[k] = v[0];
38087
38160
  }
38088
- resolve20({
38161
+ resolve22({
38089
38162
  id: request.id,
38090
38163
  statusCode: res.statusCode ?? 0,
38091
38164
  headers,
@@ -38094,7 +38167,7 @@ async function proxyToLocal(request) {
38094
38167
  });
38095
38168
  });
38096
38169
  req.on("error", (err) => {
38097
- resolve20({
38170
+ resolve22({
38098
38171
  id: request.id,
38099
38172
  statusCode: 0,
38100
38173
  headers: {},
@@ -38503,13 +38576,13 @@ function createOnBridgeIdentified(opts) {
38503
38576
 
38504
38577
  // src/skills/discover-local-agent-skills.ts
38505
38578
  import fs31 from "node:fs";
38506
- import path34 from "node:path";
38579
+ import path36 from "node:path";
38507
38580
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
38508
38581
  function discoverLocalSkills(cwd) {
38509
38582
  const out = [];
38510
38583
  const seenKeys = /* @__PURE__ */ new Set();
38511
38584
  for (const rel of SKILL_DISCOVERY_ROOTS) {
38512
- const base = path34.join(cwd, rel);
38585
+ const base = path36.join(cwd, rel);
38513
38586
  if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
38514
38587
  let entries = [];
38515
38588
  try {
@@ -38518,13 +38591,13 @@ function discoverLocalSkills(cwd) {
38518
38591
  continue;
38519
38592
  }
38520
38593
  for (const name of entries) {
38521
- const dir = path34.join(base, name);
38594
+ const dir = path36.join(base, name);
38522
38595
  try {
38523
38596
  if (!fs31.statSync(dir).isDirectory()) continue;
38524
38597
  } catch {
38525
38598
  continue;
38526
38599
  }
38527
- const skillMd = path34.join(dir, "SKILL.md");
38600
+ const skillMd = path36.join(dir, "SKILL.md");
38528
38601
  if (!fs31.existsSync(skillMd)) continue;
38529
38602
  const key = `${rel}/${name}`;
38530
38603
  if (seenKeys.has(key)) continue;
@@ -38537,7 +38610,7 @@ function discoverLocalSkills(cwd) {
38537
38610
  function discoverSkillLayoutRoots(cwd) {
38538
38611
  const roots = [];
38539
38612
  for (const rel of SKILL_DISCOVERY_ROOTS) {
38540
- const base = path34.join(cwd, rel);
38613
+ const base = path36.join(cwd, rel);
38541
38614
  if (!fs31.existsSync(base) || !fs31.statSync(base).isDirectory()) continue;
38542
38615
  let entries = [];
38543
38616
  try {
@@ -38547,13 +38620,13 @@ function discoverSkillLayoutRoots(cwd) {
38547
38620
  }
38548
38621
  const skills2 = [];
38549
38622
  for (const name of entries) {
38550
- const dir = path34.join(base, name);
38623
+ const dir = path36.join(base, name);
38551
38624
  try {
38552
38625
  if (!fs31.statSync(dir).isDirectory()) continue;
38553
38626
  } catch {
38554
38627
  continue;
38555
38628
  }
38556
- if (!fs31.existsSync(path34.join(dir, "SKILL.md"))) continue;
38629
+ if (!fs31.existsSync(path36.join(dir, "SKILL.md"))) continue;
38557
38630
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
38558
38631
  skills2.push({ name, relPath });
38559
38632
  }
@@ -38676,7 +38749,9 @@ var API_TO_BRIDGE_MESSAGE_TYPES = [
38676
38749
  "file_browser_search",
38677
38750
  "skill_layout_request",
38678
38751
  "install_skills",
38679
- "refresh_local_skills"
38752
+ "refresh_local_skills",
38753
+ "bridge_git_context_request",
38754
+ "list_repo_branches_request"
38680
38755
  ];
38681
38756
  var API_TO_BRIDGE_TYPE_SET = new Set(API_TO_BRIDGE_MESSAGE_TYPES);
38682
38757
  function parseApiToBridgeMessage(data, log2) {
@@ -38760,9 +38835,6 @@ var handleAgentConfigMessage = (msg, deps) => {
38760
38835
  handleBridgeAgentConfig(msg, deps);
38761
38836
  };
38762
38837
 
38763
- // src/prompt-turn-queue/runner.ts
38764
- import fs32 from "node:fs";
38765
-
38766
38838
  // src/prompt-turn-queue/client-report.ts
38767
38839
  function sendPromptQueueClientReport(ws, queues) {
38768
38840
  if (!ws) return false;
@@ -38832,8 +38904,36 @@ async function mergeServerQueueSnapshot(queueKey, serverTurns) {
38832
38904
  return { queueKey, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), turns };
38833
38905
  }
38834
38906
 
38835
- // src/prompt-turn-queue/runner.ts
38836
- 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
38837
38937
  function isRunnableServerState(s) {
38838
38938
  return s === "queued" || s === "requeued" || s === "requeued_with_revert";
38839
38939
  }
@@ -38852,6 +38952,28 @@ function pickNextRunnableTurn(turns) {
38852
38952
  function hasRunningTurn(turns) {
38853
38953
  return turns.some((t) => t.lastClientState === "running");
38854
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";
38855
38977
  async function runLocalRevertBeforeQueuedPrompt(next, deps) {
38856
38978
  if (next.serverState !== "requeued_with_revert") return true;
38857
38979
  const sid = next.sessionId;
@@ -38873,30 +38995,23 @@ async function runLocalRevertBeforeQueuedPrompt(next, deps) {
38873
38995
  }
38874
38996
  return res.ok;
38875
38997
  }
38876
- function dispatchLocalPrompt(next, deps) {
38877
- const pl = next.payload;
38878
- const rawParent = pl["sessionParent"];
38879
- const sessionParent = rawParent === "bridge_root" || rawParent === "worktrees_root" ? rawParent : void 0;
38880
- const rawParentPath = pl["sessionParentPath"];
38881
- const sessionParentPath = typeof rawParentPath === "string" && rawParentPath.trim() !== "" ? rawParentPath.trim() : void 0;
38882
- const msg = {
38883
- type: "prompt",
38884
- sessionId: next.sessionId,
38885
- runId: next.turnId,
38886
- prompt: pl.prompt,
38887
- mode: typeof pl.mode === "string" ? pl.mode : "agent",
38888
- isNewSession: pl.isNewSession === true,
38889
- ...sessionParent ? { sessionParent } : {},
38890
- ...sessionParentPath ? { sessionParentPath } : {},
38891
- ...typeof pl.followUpCatalogPromptId === "string" ? { followUpCatalogPromptId: pl.followUpCatalogPromptId } : {},
38892
- ...Array.isArray(pl.sessionChangeSummaryFilePaths) ? { sessionChangeSummaryFilePaths: pl.sessionChangeSummaryFilePaths } : {},
38893
- ...Array.isArray(pl.sessionChangeSummaryFileSnapshots) ? { sessionChangeSummaryFileSnapshots: pl.sessionChangeSummaryFileSnapshots } : {},
38894
- ...typeof pl.agentType === "string" && pl.agentType.trim() ? { agentType: pl.agentType.trim() } : {},
38895
- ...pl.agentConfig != null && typeof pl.agentConfig === "object" && !Array.isArray(pl.agentConfig) && Object.keys(pl.agentConfig).length > 0 ? { agentConfig: pl.agentConfig } : {},
38896
- ...Array.isArray(pl.attachments) && pl.attachments.length > 0 ? { attachments: pl.attachments } : {}
38897
- };
38898
- 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;
38899
39012
  }
39013
+
39014
+ // src/prompt-turn-queue/runner/apply-prompt-queue-state-from-server.ts
38900
39015
  async function applyPromptQueueStateFromServer(msg, deps) {
38901
39016
  const raw = msg.queues;
38902
39017
  if (!raw || typeof raw !== "object") return;
@@ -38910,9 +39025,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
38910
39025
  if (!Array.isArray(serverTurns)) continue;
38911
39026
  const file2 = await readPersistedQueue(queueKey);
38912
39027
  if (!file2) continue;
38913
- for (const running of file2.turns.filter((t) => t.lastClientState === "running")) {
38914
- runIdToQueueKey.set(running.turnId, queueKey);
38915
- }
39028
+ syncRunningTurnQueueKeys(file2.turns, queueKey);
38916
39029
  }
38917
39030
  for (const [queueKey, serverTurns] of Object.entries(raw)) {
38918
39031
  if (!Array.isArray(serverTurns)) continue;
@@ -38957,7 +39070,7 @@ async function applyPromptQueueStateFromServer(msg, deps) {
38957
39070
  }
38958
39071
  next.lastClientState = "running";
38959
39072
  await writePersistedQueue(file2);
38960
- runIdToQueueKey.set(next.turnId, queueKey);
39073
+ setRunIdQueueKey(next.turnId, queueKey);
38961
39074
  startedThisTick.add(next.turnId);
38962
39075
  report[queueKey] = [{ turnId: next.turnId, clientState: "running" }];
38963
39076
  }
@@ -38970,24 +39083,10 @@ async function applyPromptQueueStateFromServer(msg, deps) {
38970
39083
  if (!file2) continue;
38971
39084
  const running = file2.turns.find((t) => t.lastClientState === "running");
38972
39085
  if (!running || !startedThisTick.has(running.turnId)) continue;
38973
- if (runIdToQueueKey.get(running.turnId) !== queueKey) continue;
39086
+ if (getRunIdQueueKey(running.turnId) !== queueKey) continue;
38974
39087
  dispatchLocalPrompt(running, deps);
38975
39088
  }
38976
39089
  }
38977
- async function finalizePromptTurnOnBridge(getWs, runId, success2, opts) {
38978
- if (!runId) return false;
38979
- const queueKey = runIdToQueueKey.get(runId);
38980
- runIdToQueueKey.delete(runId);
38981
- if (!queueKey) return false;
38982
- const f = await readPersistedQueue(queueKey);
38983
- if (!f) return false;
38984
- const t = f.turns.find((x) => x.turnId === runId);
38985
- if (!t) return false;
38986
- t.lastClientState = opts?.terminalClientState ?? (success2 ? "stopped" : "failed");
38987
- await writePersistedQueue(f);
38988
- sendPromptQueueClientReport(getWs(), { [queueKey]: [{ turnId: runId, clientState: t.lastClientState }] });
38989
- return true;
38990
- }
38991
39090
 
38992
39091
  // src/agents/acp/from-bridge/bridge-prompt-wiring.ts
38993
39092
  function createBridgePromptSenders(deps, getWs) {
@@ -39034,6 +39133,91 @@ function createBridgePromptSenders(deps, getWs) {
39034
39133
  return { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate };
39035
39134
  }
39036
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
+
39037
39221
  // src/agents/acp/from-bridge/bridge-prompt-preamble.ts
39038
39222
  import { execFile as execFile8 } from "node:child_process";
39039
39223
  import { promisify as promisify8 } from "node:util";
@@ -39086,9 +39270,9 @@ function parseChangeSummarySnapshots(raw) {
39086
39270
  for (const item of raw) {
39087
39271
  if (!item || typeof item !== "object") continue;
39088
39272
  const o = item;
39089
- const path43 = typeof o.path === "string" && o.path.trim() !== "" ? o.path.trim() : "";
39090
- if (!path43) continue;
39091
- 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 };
39092
39276
  if (typeof o.patchContent === "string") row.patchContent = o.patchContent;
39093
39277
  if (typeof o.oldText === "string") row.oldText = o.oldText;
39094
39278
  if (typeof o.newText === "string") row.newText = o.newText;
@@ -39105,76 +39289,73 @@ function parseFollowUpFieldsFromPromptMessage(msg) {
39105
39289
  return { followUpCatalogPromptId, sessionChangeSummaryFilePaths, sessionChangeSummaryFileSnapshots };
39106
39290
  }
39107
39291
 
39108
- // src/agents/acp/change-summary/decrypt-change-summary-file-input.ts
39109
- function decryptChangeSummaryFileInput(row, e2ee) {
39110
- if (!e2ee) return row;
39111
- for (const field of ["path", "patchContent", "oldText", "newText"]) {
39112
- const raw = row[field];
39113
- if (typeof raw !== "string" || raw.trim() === "") continue;
39114
- let o;
39115
- try {
39116
- o = JSON.parse(raw);
39117
- } catch {
39118
- continue;
39119
- }
39120
- if (!isE2eeEnvelope(o.ee)) continue;
39121
- try {
39122
- const d = e2ee.decryptMessage(o);
39123
- const out = {
39124
- path: typeof d.path === "string" ? d.path : row.path
39125
- };
39126
- if (d.directoryRemoved === true) out.directoryRemoved = true;
39127
- else if (row.directoryRemoved === true) out.directoryRemoved = true;
39128
- if (typeof d.patchContent === "string") out.patchContent = d.patchContent;
39129
- else if (typeof row.patchContent === "string" && row.patchContent !== raw) out.patchContent = row.patchContent;
39130
- if (typeof d.oldText === "string") out.oldText = d.oldText;
39131
- else if (typeof row.oldText === "string") out.oldText = row.oldText;
39132
- if (typeof d.newText === "string") out.newText = d.newText;
39133
- else if (typeof row.newText === "string") out.newText = row.newText;
39134
- return out;
39135
- } catch {
39136
- return row;
39137
- }
39138
- }
39139
- return row;
39140
- }
39141
-
39142
- // src/agents/acp/change-summary/resolve-change-summary-prompt-for-agent.ts
39143
- function hasSummarizePayload(f) {
39144
- return f.directoryRemoved === true || f.patchContent != null && f.patchContent.trim() !== "" || f.oldText != null && f.oldText.trim() !== "" || f.newText != null && f.newText.trim() !== "";
39145
- }
39146
- function resolveChangeSummaryPromptForAgent(params) {
39147
- const isBuiltin = params.followUpCatalogPromptId === BUILTIN_SESSION_CHANGE_SUMMARY_FOLLOW_UP_CATALOG_PROMPT_ID;
39148
- const snaps = params.sessionChangeSummaryFileSnapshots;
39149
- if (!isBuiltin || !snaps || snaps.length === 0) {
39150
- return { promptText: params.bridgePromptText, sessionChangeSummaryFilePaths: void 0 };
39151
- }
39152
- const decrypted = dedupeSessionFileChangesByPath(snaps.map((row) => decryptChangeSummaryFileInput(row, params.e2ee)));
39153
- const withPayload = decrypted.filter(hasSummarizePayload);
39154
- if (withPayload.length === 0) {
39155
- 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
+ );
39156
39335
  }
39157
- return {
39158
- promptText: buildSessionChangeSummaryPrompt(withPayload),
39159
- sessionChangeSummaryFilePaths: withPayload.map((f) => f.path)
39160
- };
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
+ });
39161
39356
  }
39162
39357
 
39163
- // src/agents/acp/from-bridge/handle-bridge-prompt.ts
39164
- function parseBridgeAttachments(msg) {
39165
- const raw = msg.attachments;
39166
- if (!Array.isArray(raw)) return [];
39167
- const out = [];
39168
- for (const x of raw) {
39169
- if (x === null || typeof x !== "object" || Array.isArray(x)) continue;
39170
- const o = x;
39171
- const id = typeof o.attachmentId === "string" ? o.attachmentId.trim() : "";
39172
- if (!id) continue;
39173
- const mt = typeof o.mimeType === "string" && o.mimeType.trim() ? o.mimeType.trim() : "application/octet-stream";
39174
- out.push({ attachmentId: id, mimeType: mt });
39175
- }
39176
- return out;
39177
- }
39358
+ // src/agents/acp/from-bridge/handle-bridge-prompt/handle-bridge-prompt.ts
39178
39359
  function handleBridgePrompt(msg, deps) {
39179
39360
  const { getWs, log: log2, acpManager, sessionWorktreeManager } = deps;
39180
39361
  const rawPrompt = msg.prompt;
@@ -39183,12 +39364,12 @@ function handleBridgePrompt(msg, deps) {
39183
39364
  const sessionId = msg.sessionId;
39184
39365
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
39185
39366
  const promptId = typeof msg.id === "string" ? msg.id : void 0;
39186
- const { sendBridgeMessage, sendResult: sendResult2, sendSessionUpdate } = createBridgePromptSenders(deps, getWs);
39367
+ const senders = createBridgePromptSenders(deps, getWs);
39187
39368
  if (!promptText.trim() && attachments.length === 0) {
39188
39369
  log2(
39189
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).`
39190
39371
  );
39191
- sendBridgeMessage(
39372
+ senders.sendBridgeMessage(
39192
39373
  {
39193
39374
  type: "prompt_result",
39194
39375
  ...promptId ? { id: promptId } : {},
@@ -39210,57 +39391,50 @@ function handleBridgePrompt(msg, deps) {
39210
39391
  const agentId = typeof rawAgentId === "string" && rawAgentId.trim() !== "" ? rawAgentId.trim() : null;
39211
39392
  const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
39212
39393
  const agentConfig = msg.agentConfig != null && typeof msg.agentConfig === "object" && !Array.isArray(msg.agentConfig) ? msg.agentConfig : void 0;
39394
+ const worktreeBaseBranches = parseWorktreeBaseBranches(msg);
39213
39395
  acpManager.logPromptReceivedFromBridge({ agentType, mode });
39214
- async function preambleAndPrompt(resolvedCwd) {
39215
- const effectiveCwd = resolveSessionParentPathForAgentProcess(resolvedCwd);
39216
- 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,
39217
39405
  getWs,
39218
39406
  log: log2,
39219
39407
  sessionWorktreeManager,
39220
39408
  sessionId,
39221
39409
  runId,
39222
- effectiveCwd
39223
- });
39224
- const {
39225
- followUpCatalogPromptId,
39226
- sessionChangeSummaryFilePaths: pathsFromBridge,
39227
- sessionChangeSummaryFileSnapshots
39228
- } = parseFollowUpFieldsFromPromptMessage(msg);
39229
- const { promptText: resolvedPromptText, sessionChangeSummaryFilePaths } = resolveChangeSummaryPromptForAgent({
39230
- followUpCatalogPromptId,
39231
- sessionChangeSummaryFileSnapshots,
39232
- bridgePromptText: promptText,
39233
- e2ee: deps.e2ee
39234
- });
39235
- if (sessionChangeSummaryFileSnapshots && sessionChangeSummaryFileSnapshots.length > 0 && resolvedPromptText === promptText) {
39236
- deps.log(
39237
- "[Agent] Change-summary snapshots were present but the prompt was not rebuilt (decrypt failed or empty payloads); sending the bridge prompt as-is."
39238
- );
39239
- }
39240
- const pathsForUpload = sessionChangeSummaryFilePaths ?? pathsFromBridge;
39241
- acpManager.handlePrompt({
39242
- promptText: resolvedPromptText,
39243
- 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,
39244
39427
  sessionId,
39245
39428
  runId,
39429
+ promptText,
39430
+ attachments,
39246
39431
  mode,
39247
39432
  agentType,
39248
39433
  agentId,
39249
39434
  agentConfig,
39250
- sessionParentPath: effectiveCwd,
39251
- sendResult: sendResult2,
39252
- sendSessionUpdate,
39253
- followUpCatalogPromptId,
39254
- sessionChangeSummaryFilePaths: pathsForUpload,
39255
- cloudApiBaseUrl: deps.cloudApiBaseUrl,
39256
- getCloudAccessToken: deps.getCloudAccessToken,
39257
- e2ee: deps.e2ee,
39258
- ...attachments.length > 0 ? { attachments } : {}
39435
+ resolvedCwd: void 0,
39436
+ senders
39259
39437
  });
39260
- }
39261
- void sessionWorktreeManager.resolveSessionParentPathForPrompt(sessionId, { isNewSession, sessionParent, sessionParentPath }).then((cwd) => preambleAndPrompt(cwd)).catch((err) => {
39262
- log2(`[Agent] Session parent path resolve failed: ${err instanceof Error ? err.message : String(err)}`);
39263
- void preambleAndPrompt(void 0);
39264
39438
  });
39265
39439
  }
39266
39440
 
@@ -39308,8 +39482,8 @@ function randomSecret() {
39308
39482
  }
39309
39483
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
39310
39484
  }
39311
- async function requestPreviewApi(port, secret, method, path43, body) {
39312
- 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}`;
39313
39487
  const headers = {
39314
39488
  [PREVIEW_SECRET_HEADER]: secret,
39315
39489
  "Content-Type": "application/json"
@@ -39321,7 +39495,7 @@ async function requestPreviewApi(port, secret, method, path43, body) {
39321
39495
  });
39322
39496
  const data = await res.json().catch(() => ({}));
39323
39497
  if (!res.ok) {
39324
- throw new Error(data?.error ?? `Preview API ${method} ${path43}: ${res.status}`);
39498
+ throw new Error(data?.error ?? `Preview API ${method} ${path46}: ${res.status}`);
39325
39499
  }
39326
39500
  return data;
39327
39501
  }
@@ -39489,11 +39663,11 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
39489
39663
  import fs34 from "node:fs";
39490
39664
 
39491
39665
  // src/files/ensure-under-cwd.ts
39492
- import path35 from "node:path";
39666
+ import path37 from "node:path";
39493
39667
  function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
39494
- const normalized = path35.normalize(relativePath).replace(/^(\.\/)+/, "");
39495
- const resolved = path35.resolve(cwd, normalized);
39496
- 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) {
39497
39671
  return null;
39498
39672
  }
39499
39673
  return resolved;
@@ -39503,11 +39677,11 @@ function ensureUnderCwd(relativePath, cwd = getBridgeRoot()) {
39503
39677
  var LIST_DIR_YIELD_EVERY = 256;
39504
39678
 
39505
39679
  // src/files/list-dir/map-dir-entry.ts
39506
- import path36 from "node:path";
39680
+ import path38 from "node:path";
39507
39681
  import fs33 from "node:fs";
39508
39682
  async function mapDirEntry(d, relativePath, resolved) {
39509
- const entryPath = path36.join(relativePath || ".", d.name).replace(/\\/g, "/");
39510
- 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);
39511
39685
  let isDir = d.isDirectory();
39512
39686
  if (d.isSymbolicLink()) {
39513
39687
  try {
@@ -39858,13 +40032,13 @@ function resolveFileBrowserSessionParent(sessionWorktreeManager, sessionId) {
39858
40032
  }
39859
40033
 
39860
40034
  // src/files/handle-file-browser-search.ts
39861
- import path37 from "node:path";
40035
+ import path39 from "node:path";
39862
40036
  var SEARCH_LIMIT = 100;
39863
40037
  function handleFileBrowserSearch(msg, socket, e2ee, sessionWorktreeManager) {
39864
40038
  void (async () => {
39865
40039
  await yieldToEventLoop();
39866
40040
  const q = typeof msg.q === "string" ? msg.q : "";
39867
- const sessionParentPath = path37.resolve(
40041
+ const sessionParentPath = path39.resolve(
39868
40042
  sessionWorktreeManager != null ? resolveFileBrowserSessionParent(sessionWorktreeManager, msg.sessionId) : getBridgeRoot()
39869
40043
  );
39870
40044
  if (!await bridgeFileIndexIsPopulated(sessionParentPath)) {
@@ -39984,7 +40158,7 @@ function handleSkillLayoutRequest(msg, deps) {
39984
40158
 
39985
40159
  // src/skills/install-remote-skills.ts
39986
40160
  import fs38 from "node:fs";
39987
- import path38 from "node:path";
40161
+ import path40 from "node:path";
39988
40162
  function installRemoteSkills(cwd, targetDir, items) {
39989
40163
  const installed2 = [];
39990
40164
  if (!Array.isArray(items)) {
@@ -39995,11 +40169,11 @@ function installRemoteSkills(cwd, targetDir, items) {
39995
40169
  if (typeof item.sourceId !== "string" || typeof item.skillName !== "string" || typeof item.versionHash !== "string" || !Array.isArray(item.files)) {
39996
40170
  continue;
39997
40171
  }
39998
- const skillDir = path38.join(cwd, targetDir, item.skillName);
40172
+ const skillDir = path40.join(cwd, targetDir, item.skillName);
39999
40173
  for (const f of item.files) {
40000
40174
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
40001
- const dest = path38.join(skillDir, f.path);
40002
- fs38.mkdirSync(path38.dirname(dest), { recursive: true });
40175
+ const dest = path40.join(skillDir, f.path);
40176
+ fs38.mkdirSync(path40.dirname(dest), { recursive: true });
40003
40177
  if (f.text !== void 0) {
40004
40178
  fs38.writeFileSync(dest, f.text, "utf8");
40005
40179
  } else if (f.base64) {
@@ -40215,6 +40389,148 @@ var handleDevServersConfig = (msg, deps) => {
40215
40389
  deps.devServerManager?.applyConfig(devServers ?? []);
40216
40390
  };
40217
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
+
40218
40534
  // src/routing/dispatch-bridge-message.ts
40219
40535
  function dispatchBridgeMessage(msg, deps) {
40220
40536
  switch (msg.type) {
@@ -40278,6 +40594,12 @@ function dispatchBridgeMessage(msg, deps) {
40278
40594
  case "refresh_local_skills":
40279
40595
  handleRefreshLocalSkills(msg, deps);
40280
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;
40281
40603
  }
40282
40604
  }
40283
40605
 
@@ -40649,10 +40971,10 @@ function listCliAgentCapabilityCacheForWorkspace(db, workspaceId) {
40649
40971
  }
40650
40972
 
40651
40973
  // src/agents/capabilities/warmup-agent-capabilities-on-connect.ts
40652
- import * as path40 from "node:path";
40974
+ import * as path43 from "node:path";
40653
40975
 
40654
40976
  // src/agents/capabilities/probe-one-agent-type-for-capabilities.ts
40655
- import * as path39 from "node:path";
40977
+ import * as path42 from "node:path";
40656
40978
  async function probeOneAgentTypeForCapabilities(params) {
40657
40979
  const { agentType, cwd, workspaceId, log: log2, reportAgentCapabilities, bridgeReport = true } = params;
40658
40980
  if (isCliImmediateShutdownRequested()) return false;
@@ -40692,7 +41014,7 @@ async function probeOneAgentTypeForCapabilities(params) {
40692
41014
  if (isCliImmediateShutdownRequested()) return false;
40693
41015
  handle = await resolved.createClient({
40694
41016
  command: resolved.command,
40695
- cwd: path39.resolve(cwd),
41017
+ cwd: path42.resolve(cwd),
40696
41018
  backendAgentType: agentType,
40697
41019
  sessionMode: "agent",
40698
41020
  persistedAcpSessionId: null,
@@ -40766,7 +41088,7 @@ async function probeAgentCapabilitiesForDetectedTypes(params) {
40766
41088
  async function warmupAgentCapabilitiesOnConnect(params) {
40767
41089
  const { workspaceId, log: log2, getWs } = params;
40768
41090
  if (isCliImmediateShutdownRequested()) return;
40769
- const cwd = path40.resolve(getBridgeRoot());
41091
+ const cwd = path43.resolve(getBridgeRoot());
40770
41092
  async function sendBatchFromCache() {
40771
41093
  const socket = getWs();
40772
41094
  if (!socket || socket.readyState !== wrapper_default.OPEN) return;
@@ -40933,8 +41255,8 @@ async function createBridgeConnection(options) {
40933
41255
  getCloudAccessToken: () => tokens.accessToken
40934
41256
  };
40935
41257
  const identifyReportedPaths = {
40936
- bridgeRootPath: path41.resolve(getBridgeRoot()),
40937
- worktreesRootPath: path41.resolve(worktreesRootPath)
41258
+ bridgeRootPath: path44.resolve(getBridgeRoot()),
41259
+ worktreesRootPath: path44.resolve(worktreesRootPath)
40938
41260
  };
40939
41261
  const { connect } = createMainBridgeWebSocketLifecycle({
40940
41262
  state,
@@ -41188,7 +41510,7 @@ async function runCliAction(program2, opts) {
41188
41510
  const firehoseServerUrl = opts.firehoseUrl ?? opts.proxyUrl ?? process.env.BUILDAUTOMATON_FIREHOSE_URL ?? process.env.BUILDAUTOMATON_PROXY_URL ?? DEFAULT_FIREHOSE_URL;
41189
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);
41190
41512
  if (bridgeRootOpt) {
41191
- const resolvedBridgeRoot = path42.resolve(process.cwd(), bridgeRootOpt);
41513
+ const resolvedBridgeRoot = path45.resolve(process.cwd(), bridgeRootOpt);
41192
41514
  try {
41193
41515
  const st = fs40.statSync(resolvedBridgeRoot);
41194
41516
  if (!st.isDirectory()) {
@@ -41210,7 +41532,7 @@ async function runCliAction(program2, opts) {
41210
41532
  );
41211
41533
  let worktreesRootPath;
41212
41534
  if (opts.worktreesRoot && opts.worktreesRoot.trim()) {
41213
- worktreesRootPath = path42.resolve(opts.worktreesRoot.trim());
41535
+ worktreesRootPath = path45.resolve(opts.worktreesRoot.trim());
41214
41536
  }
41215
41537
  const e2eCertificates = opts.e2eeCertificatesDir?.trim() ? await loadOrCreateE2eCertificates(opts.e2eeCertificatesDir.trim()) : void 0;
41216
41538
  if (e2eCertificates) {