@buildautomaton/cli 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21966,12 +21966,12 @@ var require_src2 = __commonJS({
21966
21966
  function check2(path24, isFile, isDirectory) {
21967
21967
  log2(`checking %s`, path24);
21968
21968
  try {
21969
- const stat = fs_1.statSync(path24);
21970
- if (stat.isFile() && isFile) {
21969
+ const stat2 = fs_1.statSync(path24);
21970
+ if (stat2.isFile() && isFile) {
21971
21971
  log2(`[OK] path represents a file`);
21972
21972
  return true;
21973
21973
  }
21974
- if (stat.isDirectory() && isDirectory) {
21974
+ if (stat2.isDirectory() && isDirectory) {
21975
21975
  log2(`[OK] path represents a directory`);
21976
21976
  return true;
21977
21977
  }
@@ -22061,14 +22061,30 @@ var import_websocket = __toESM(require_websocket(), 1);
22061
22061
  var import_websocket_server = __toESM(require_websocket_server(), 1);
22062
22062
  var wrapper_default = import_websocket.default;
22063
22063
 
22064
+ // src/net/apply-cli-outbound-network-prefs.ts
22065
+ import dns from "node:dns";
22066
+ var applied = false;
22067
+ function applyCliOutboundNetworkPreferences() {
22068
+ if (applied) return;
22069
+ applied = true;
22070
+ try {
22071
+ dns.setDefaultResultOrder("ipv4first");
22072
+ } catch {
22073
+ }
22074
+ }
22075
+
22064
22076
  // src/bridge/connection/create-ws-bridge.ts
22065
22077
  var BRIDGE_AUTH_ERROR_HEADER = "x-bridge-auth-error";
22066
22078
  var BRIDGE_AUTH_ERROR_TOKEN_INVALID = "token_invalid";
22067
22079
  function createWsBridge(options) {
22068
22080
  const { url: url2, onMessage, onOpen, onClose, onError: onError2, onAuthInvalid, clientPingIntervalMs } = options;
22069
- const wsOptions = {};
22081
+ applyCliOutboundNetworkPreferences();
22082
+ const wsOptions = {
22083
+ perMessageDeflate: false,
22084
+ family: 4
22085
+ };
22070
22086
  if (url2.startsWith("wss://")) {
22071
- wsOptions.agent = new https.Agent({ rejectUnauthorized: false });
22087
+ wsOptions.agent = new https.Agent({ rejectUnauthorized: false, family: 4 });
22072
22088
  }
22073
22089
  const ws = new wrapper_default(url2, wsOptions);
22074
22090
  let clientPingTimer = null;
@@ -22132,10 +22148,58 @@ function sendWsMessage(ws, payload) {
22132
22148
  }
22133
22149
  }
22134
22150
 
22135
- // src/acp/clients/acp-client.ts
22151
+ // src/acp/clients/sdk-stdio-acp-client.ts
22136
22152
  import { spawn } from "node:child_process";
22153
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
22154
+ import { dirname } from "node:path";
22137
22155
  import { Readable, Writable } from "node:stream";
22138
22156
 
22157
+ // src/files/diff/unified-diff.ts
22158
+ function computeLineDiff(oldText, newText) {
22159
+ const oldLines = oldText.split("\n");
22160
+ const newLines = newText.split("\n");
22161
+ const m = oldLines.length;
22162
+ const n = newLines.length;
22163
+ const dp = Array(m + 1);
22164
+ for (let i2 = 0; i2 <= m; i2++) dp[i2] = Array(n + 1).fill(0);
22165
+ for (let i2 = 1; i2 <= m; i2++) {
22166
+ for (let j2 = 1; j2 <= n; j2++) {
22167
+ if (oldLines[i2 - 1] === newLines[j2 - 1]) {
22168
+ dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
22169
+ } else {
22170
+ dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
22171
+ }
22172
+ }
22173
+ }
22174
+ const result = [];
22175
+ let i = m;
22176
+ let j = n;
22177
+ while (i > 0 || j > 0) {
22178
+ if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
22179
+ result.unshift({ type: "context", line: oldLines[i - 1] });
22180
+ i--;
22181
+ j--;
22182
+ } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
22183
+ result.unshift({ type: "add", line: newLines[j - 1] });
22184
+ j--;
22185
+ } else {
22186
+ result.unshift({ type: "remove", line: oldLines[i - 1] });
22187
+ i--;
22188
+ }
22189
+ }
22190
+ return result;
22191
+ }
22192
+ function editSnippetToUnifiedDiff(filePath, oldText, newText) {
22193
+ const lines = computeLineDiff(oldText, newText);
22194
+ const out = [`--- ${filePath}`, `+++ ${filePath}`];
22195
+ for (const d of lines) {
22196
+ if (d.type === "add") out.push(`+${d.line}`);
22197
+ else if (d.type === "remove") out.push(`-${d.line}`);
22198
+ else out.push(` ${d.line}`);
22199
+ }
22200
+ return out.join("\n");
22201
+ }
22202
+
22139
22203
  // src/files/cwd/bridge-workspace-directory.ts
22140
22204
  import * as path from "node:path";
22141
22205
  var bridgeWorkspaceDirectory = null;
@@ -22146,7 +22210,25 @@ function getBridgeWorkspaceDirectory() {
22146
22210
  return bridgeWorkspaceDirectory;
22147
22211
  }
22148
22212
 
22149
- // src/acp/clients/acp-client.ts
22213
+ // src/acp/safe-fs-path.ts
22214
+ import * as path2 from "node:path";
22215
+ function resolveSafePathUnderCwd(cwd, filePath) {
22216
+ const trimmed2 = filePath.trim();
22217
+ if (!trimmed2) return null;
22218
+ const normalizedCwd = path2.resolve(cwd);
22219
+ const resolved = path2.isAbsolute(trimmed2) ? path2.normalize(trimmed2) : path2.resolve(normalizedCwd, trimmed2);
22220
+ const rel = path2.relative(normalizedCwd, resolved);
22221
+ if (rel.startsWith("..") || path2.isAbsolute(rel)) return null;
22222
+ return resolved;
22223
+ }
22224
+ function toDisplayPathRelativeToCwd(cwd, absolutePath) {
22225
+ const normalizedCwd = path2.resolve(cwd);
22226
+ const rel = path2.relative(normalizedCwd, path2.resolve(absolutePath));
22227
+ if (!rel || rel === "") return path2.basename(absolutePath);
22228
+ return rel.split(path2.sep).join("/");
22229
+ }
22230
+
22231
+ // src/acp/clients/sdk-stdio-acp-client.ts
22150
22232
  function formatSpawnError(err, command) {
22151
22233
  if (err.code === "ENOENT") {
22152
22234
  return `Command "${command}" not found. Install the agent (e.g. Cursor CLI) or add it to PATH.`;
@@ -22161,9 +22243,26 @@ function toErrorMessage(err) {
22161
22243
  if (err != null && typeof err === "object") return JSON.stringify(err);
22162
22244
  return String(err);
22163
22245
  }
22164
- async function createAcpClient(options) {
22165
- const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
22166
- const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate } = options;
22246
+ function sliceFileContentRange(content, line, limit) {
22247
+ if (line == null && limit == null) return content;
22248
+ const lines = content.split("\n");
22249
+ const start = line != null && line > 0 ? line - 1 : 0;
22250
+ const end = limit != null && limit > 0 ? start + limit : lines.length;
22251
+ return lines.slice(start, end).join("\n");
22252
+ }
22253
+ function bridgePayloadFromSdkSessionNotification(params) {
22254
+ return { sessionId: params.sessionId, ...params.update };
22255
+ }
22256
+ async function createSdkStdioAcpClient(options) {
22257
+ const { ClientSideConnection: ClientSideConnection2, ndJsonStream: ndJsonStream2, PROTOCOL_VERSION: PROTOCOL_VERSION2 } = await Promise.resolve().then(() => (init_acp(), acp_exports));
22258
+ const {
22259
+ command,
22260
+ cwd = getBridgeWorkspaceDirectory(),
22261
+ onSessionUpdate,
22262
+ onFileChange,
22263
+ killSubprocessAfterCancelMs,
22264
+ onAgentSubprocessExit
22265
+ } = options;
22167
22266
  const isWindows = process.platform === "win32";
22168
22267
  const child = spawn(command[0], command.slice(1), {
22169
22268
  cwd,
@@ -22171,6 +22270,9 @@ async function createAcpClient(options) {
22171
22270
  env: process.env,
22172
22271
  shell: isWindows
22173
22272
  });
22273
+ child.once("close", (code, signal) => {
22274
+ onAgentSubprocessExit?.({ code, signal });
22275
+ });
22174
22276
  return new Promise((resolve14, reject) => {
22175
22277
  child.on("error", (err) => {
22176
22278
  child.kill();
@@ -22182,11 +22284,43 @@ async function createAcpClient(options) {
22182
22284
  const readable = Readable.toWeb(child.stdout);
22183
22285
  const stream = ndJsonStream2(writable, readable);
22184
22286
  const client = (_agent) => ({
22185
- async requestPermission(_params) {
22186
- return { outcome: "approved" };
22287
+ async requestPermission(params) {
22288
+ const opt = params?.options?.[0];
22289
+ if (opt && typeof opt.optionId === "string") {
22290
+ return { outcome: { outcome: "selected", optionId: opt.optionId } };
22291
+ }
22292
+ return { outcome: { outcome: "cancelled" } };
22293
+ },
22294
+ async readTextFile(params) {
22295
+ const abs = resolveSafePathUnderCwd(cwd, params.path);
22296
+ if (!abs) throw new Error("Invalid or disallowed path");
22297
+ try {
22298
+ let content = readFileSync(abs, "utf8");
22299
+ content = sliceFileContentRange(content, params.line, params.limit);
22300
+ return { content };
22301
+ } catch (e) {
22302
+ if (e.code === "ENOENT") return { content: "" };
22303
+ throw e;
22304
+ }
22305
+ },
22306
+ async writeTextFile(params) {
22307
+ const abs = resolveSafePathUnderCwd(cwd, params.path);
22308
+ if (!abs) throw new Error("Invalid or disallowed path");
22309
+ let oldText = "";
22310
+ try {
22311
+ oldText = readFileSync(abs, "utf8");
22312
+ } catch (e) {
22313
+ if (e.code !== "ENOENT") throw e;
22314
+ }
22315
+ mkdirSync(dirname(abs), { recursive: true });
22316
+ writeFileSync(abs, params.content, "utf8");
22317
+ const displayPath = toDisplayPathRelativeToCwd(cwd, abs);
22318
+ const patchContent = editSnippetToUnifiedDiff(displayPath, oldText, params.content);
22319
+ onFileChange?.({ path: displayPath, oldText, newText: params.content, patchContent });
22320
+ return {};
22187
22321
  },
22188
22322
  async sessionUpdate(params) {
22189
- onSessionUpdate?.(params);
22323
+ onSessionUpdate?.(bridgePayloadFromSdkSessionNotification(params));
22190
22324
  }
22191
22325
  });
22192
22326
  const connection = new ClientSideConnection2(client, stream);
@@ -22194,11 +22328,13 @@ async function createAcpClient(options) {
22194
22328
  child.kill();
22195
22329
  });
22196
22330
  await connection.initialize({
22197
- protocolVersion: "0.1.0",
22198
- capabilities: {},
22331
+ protocolVersion: PROTOCOL_VERSION2,
22332
+ clientCapabilities: {
22333
+ fs: { readTextFile: true, writeTextFile: true }
22334
+ },
22199
22335
  clientInfo: { name: "buildautomaton-cli", version: "0.1.0" }
22200
22336
  });
22201
- const newSessionRes = await connection.newSession({ workingDirectory: cwd });
22337
+ const newSessionRes = await connection.newSession({ cwd, mcpServers: [] });
22202
22338
  const sessionId = newSessionRes.sessionId;
22203
22339
  resolve14({
22204
22340
  sessionId,
@@ -22206,7 +22342,7 @@ async function createAcpClient(options) {
22206
22342
  try {
22207
22343
  const response = await connection.prompt({
22208
22344
  sessionId,
22209
- prompt: { type: "text", text: prompt }
22345
+ prompt: [{ type: "text", text: prompt }]
22210
22346
  });
22211
22347
  const r = response;
22212
22348
  const cancelled = (r?.stopReason ?? "").toLowerCase() === "cancelled";
@@ -22224,9 +22360,17 @@ async function createAcpClient(options) {
22224
22360
  }
22225
22361
  },
22226
22362
  async cancel() {
22227
- const conn = connection;
22228
- if (typeof conn.cancel === "function") {
22229
- await conn.cancel({ sessionId });
22363
+ try {
22364
+ await connection.cancel({ sessionId });
22365
+ } catch {
22366
+ }
22367
+ if (killSubprocessAfterCancelMs != null && killSubprocessAfterCancelMs >= 0) {
22368
+ const t = setTimeout(() => {
22369
+ if (child.exitCode == null && child.signalCode == null) {
22370
+ child.kill("SIGTERM");
22371
+ }
22372
+ }, killSubprocessAfterCancelMs);
22373
+ t.unref?.();
22230
22374
  }
22231
22375
  },
22232
22376
  resolveRequest() {
@@ -22540,11 +22684,11 @@ function logImmediate(line) {
22540
22684
 
22541
22685
  // src/config.ts
22542
22686
  import fs from "node:fs";
22543
- import path2 from "node:path";
22687
+ import path3 from "node:path";
22544
22688
  import os from "node:os";
22545
22689
  function getConfigPath() {
22546
- const dir = path2.join(os.homedir(), ".buildautomaton");
22547
- return path2.join(dir, "config.json");
22690
+ const dir = path3.join(os.homedir(), ".buildautomaton");
22691
+ return path3.join(dir, "config.json");
22548
22692
  }
22549
22693
  function normalizeApiUrl(url2) {
22550
22694
  return url2.replace(/\/$/, "");
@@ -22560,7 +22704,7 @@ function readRawConfig() {
22560
22704
  }
22561
22705
  function writeConfigForApi(apiUrl, auth) {
22562
22706
  const p = getConfigPath();
22563
- const dir = path2.dirname(p);
22707
+ const dir = path3.dirname(p);
22564
22708
  const key = normalizeApiUrl(apiUrl);
22565
22709
  const prev = readRawConfig() ?? {};
22566
22710
  const servers = { ...prev.servers ?? {}, [key]: { ...auth } };
@@ -22755,7 +22899,7 @@ function beginMainBridgeDeferredDisconnect(state, code, reason, log2, willReconn
22755
22899
  });
22756
22900
  }
22757
22901
  function clearMainBridgeReconnectQuietOnOpen(state, log2) {
22758
- clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "[Bridge service] Reconnected.");
22902
+ clearReconnectQuietOnSuccessfulConnection(state.mainQuiet, log2, "Bridge connection restored.");
22759
22903
  }
22760
22904
  function scheduleMainBridgeReconnect(state, connect, log2) {
22761
22905
  if (state.closedByUser || state.currentWs != null) return;
@@ -22792,7 +22936,7 @@ function clearFirehoseReconnectQuietOnOpen(ctx, log2) {
22792
22936
  clearReconnectQuietOnSuccessfulConnection(
22793
22937
  ctx.firehoseQuiet,
22794
22938
  log2,
22795
- `${PROXY_AND_LOG_SERVICE_LABEL} Reconnected.`
22939
+ "Preview tunnel restored (local HTTP proxy and dev logs)."
22796
22940
  );
22797
22941
  }
22798
22942
 
@@ -22943,7 +23087,7 @@ function buildBridgeUrl(apiUrl, workspaceId, authToken) {
22943
23087
 
22944
23088
  // src/git/discover-repos.ts
22945
23089
  import * as fs2 from "node:fs";
22946
- import * as path3 from "node:path";
23090
+ import * as path4 from "node:path";
22947
23091
 
22948
23092
  // ../../node_modules/.pnpm/simple-git@3.32.3/node_modules/simple-git/dist/esm/index.js
22949
23093
  var import_file_exists = __toESM(require_dist(), 1);
@@ -22952,7 +23096,7 @@ var import_promise_deferred = __toESM(require_dist2(), 1);
22952
23096
  var import_promise_deferred2 = __toESM(require_dist2(), 1);
22953
23097
  import { Buffer as Buffer2 } from "node:buffer";
22954
23098
  import { spawn as spawn3 } from "child_process";
22955
- import { normalize } from "node:path";
23099
+ import { normalize as normalize2 } from "node:path";
22956
23100
  import { EventEmitter } from "node:events";
22957
23101
  var __defProp2 = Object.defineProperty;
22958
23102
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
@@ -26255,7 +26399,7 @@ var init_branch = __esm2({
26255
26399
  });
26256
26400
  function toPath(input) {
26257
26401
  const path24 = input.trim().replace(/^["']|["']$/g, "");
26258
- return path24 && normalize(path24);
26402
+ return path24 && normalize2(path24);
26259
26403
  }
26260
26404
  var parseCheckIgnore;
26261
26405
  var init_CheckIgnore = __esm2({
@@ -27530,7 +27674,7 @@ async function isGitRepoDirectory(dirPath) {
27530
27674
  // src/git/discover-repos.ts
27531
27675
  async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
27532
27676
  const result = [];
27533
- const cwdResolved = path3.resolve(cwd);
27677
+ const cwdResolved = path4.resolve(cwd);
27534
27678
  if (await isGitRepoDirectory(cwdResolved)) {
27535
27679
  const remoteUrl = await getRemoteOriginUrl(cwdResolved);
27536
27680
  result.push({ absolutePath: cwdResolved, remoteUrl });
@@ -27543,7 +27687,7 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
27543
27687
  }
27544
27688
  for (const ent of entries) {
27545
27689
  if (!ent.isDirectory()) continue;
27546
- const childPath = path3.join(cwdResolved, ent.name);
27690
+ const childPath = path4.join(cwdResolved, ent.name);
27547
27691
  if (await isGitRepoDirectory(childPath)) {
27548
27692
  const remoteUrl = await getRemoteOriginUrl(childPath);
27549
27693
  result.push({ absolutePath: childPath, remoteUrl });
@@ -27552,11 +27696,11 @@ async function discoverGitRepos(cwd = getBridgeWorkspaceDirectory()) {
27552
27696
  return result;
27553
27697
  }
27554
27698
  async function discoverGitReposUnderRoot(rootAbs) {
27555
- const root = path3.resolve(rootAbs);
27699
+ const root = path4.resolve(rootAbs);
27556
27700
  const roots = [];
27557
27701
  async function walk(dir) {
27558
27702
  if (await isGitRepoDirectory(dir)) {
27559
- roots.push(path3.resolve(dir));
27703
+ roots.push(path4.resolve(dir));
27560
27704
  return;
27561
27705
  }
27562
27706
  let entries;
@@ -27567,7 +27711,7 @@ async function discoverGitReposUnderRoot(rootAbs) {
27567
27711
  }
27568
27712
  for (const ent of entries) {
27569
27713
  if (!ent.isDirectory() || ent.name === ".git") continue;
27570
- await walk(path3.join(dir, ent.name));
27714
+ await walk(path4.join(dir, ent.name));
27571
27715
  }
27572
27716
  }
27573
27717
  await walk(root);
@@ -27603,34 +27747,31 @@ function reportGitRepos(getWs, log2) {
27603
27747
 
27604
27748
  // src/bridge/connection/close-bridge-connection.ts
27605
27749
  async function closeBridgeConnection(state, acpManager, devServerManager, log2) {
27606
- log2?.("Shutting down\u2026");
27750
+ const say = log2 ?? logImmediate;
27751
+ say("Cleaning up connections\u2026");
27607
27752
  await new Promise((resolve14) => setImmediate(resolve14));
27608
- if (devServerManager) {
27609
- log2?.("Requesting dev server processes to stop\u2026");
27610
- await devServerManager.shutdownAllGraceful();
27611
- }
27612
27753
  state.closedByUser = true;
27613
27754
  clearReconnectQuietTimer(state.mainQuiet);
27614
27755
  clearReconnectQuietTimer(state.firehoseQuiet);
27615
27756
  if (state.reconnectTimeout != null) {
27616
- log2?.("Cancelling reconnect timer\u2026");
27757
+ say("Cancelling bridge reconnect timer\u2026");
27617
27758
  clearTimeout(state.reconnectTimeout);
27618
27759
  state.reconnectTimeout = null;
27619
27760
  }
27620
27761
  if (state.firehoseReconnectTimeout != null) {
27621
- log2?.("[Proxy and log service] Cancelling reconnect timer\u2026");
27762
+ say("Cancelling preview tunnel reconnect timer\u2026");
27622
27763
  clearTimeout(state.firehoseReconnectTimeout);
27623
27764
  state.firehoseReconnectTimeout = null;
27624
27765
  }
27625
27766
  if (state.firehoseHandle) {
27626
- log2?.("[Proxy and log service] Closing connection (CLI shutdown)\u2026");
27767
+ say("Closing preview tunnel (local HTTP proxy and dev logs)\u2026");
27627
27768
  state.firehoseHandle.close();
27628
27769
  state.firehoseHandle = null;
27629
27770
  }
27630
- log2?.("Disconnecting local agents (ACP)\u2026");
27771
+ say("Disconnecting local agent\u2026");
27631
27772
  acpManager.disconnect();
27632
27773
  if (state.currentWs) {
27633
- log2?.("[Bridge service] Closing connection (CLI shutdown)\u2026");
27774
+ say("Closing bridge connection to the cloud\u2026");
27634
27775
  state.currentWs.removeAllListeners();
27635
27776
  const wsState = state.currentWs.readyState;
27636
27777
  if (wsState === 1 || wsState === 2) {
@@ -27643,22 +27784,27 @@ async function closeBridgeConnection(state, acpManager, devServerManager, log2)
27643
27784
  }
27644
27785
  state.currentWs = null;
27645
27786
  }
27787
+ if (devServerManager) {
27788
+ say("Stopping local dev server processes\u2026");
27789
+ await devServerManager.shutdownAllGraceful();
27790
+ }
27791
+ say("Shutdown complete.");
27646
27792
  }
27647
27793
 
27648
27794
  // src/git/session-git-queue.ts
27649
27795
  import { execFile as execFile2 } from "node:child_process";
27796
+ import { readFile, stat } from "node:fs/promises";
27650
27797
  import { promisify as promisify2 } from "node:util";
27651
- import * as fs4 from "node:fs";
27652
- import * as path5 from "node:path";
27798
+ import * as path6 from "node:path";
27653
27799
 
27654
27800
  // src/git/pre-turn-snapshot.ts
27655
27801
  import * as fs3 from "node:fs";
27656
- import * as path4 from "node:path";
27802
+ import * as path5 from "node:path";
27657
27803
  import { execFile } from "node:child_process";
27658
27804
  import { promisify } from "node:util";
27659
27805
  var execFileAsync = promisify(execFile);
27660
27806
  function snapshotsDirForCwd(agentCwd) {
27661
- return path4.join(agentCwd, ".buildautomaton", "snapshots");
27807
+ return path5.join(agentCwd, ".buildautomaton", "snapshots");
27662
27808
  }
27663
27809
  async function gitStashCreate(repoRoot, log2) {
27664
27810
  try {
@@ -27687,7 +27833,7 @@ async function gitRun(repoRoot, args, log2, label) {
27687
27833
  async function resolveSnapshotRepoRoots(options) {
27688
27834
  const { worktreePaths, fallbackCwd, log: log2 } = options;
27689
27835
  if (worktreePaths?.length) {
27690
- const uniq = [...new Set(worktreePaths.map((p) => path4.resolve(p)))];
27836
+ const uniq = [...new Set(worktreePaths.map((p) => path5.resolve(p)))];
27691
27837
  return uniq;
27692
27838
  }
27693
27839
  try {
@@ -27719,7 +27865,7 @@ async function capturePreTurnSnapshot(options) {
27719
27865
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
27720
27866
  repos
27721
27867
  };
27722
- const filePath = path4.join(dir, `${runId}.json`);
27868
+ const filePath = path5.join(dir, `${runId}.json`);
27723
27869
  try {
27724
27870
  fs3.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8");
27725
27871
  } catch (e) {
@@ -27757,17 +27903,17 @@ async function applyPreTurnSnapshot(filePath, log2) {
27757
27903
  return { ok: true };
27758
27904
  }
27759
27905
  function snapshotFilePath(agentCwd, runId) {
27760
- return path4.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
27906
+ return path5.join(snapshotsDirForCwd(agentCwd), `${runId}.json`);
27761
27907
  }
27762
27908
 
27763
27909
  // src/git/session-git-queue.ts
27764
27910
  var execFileAsync2 = promisify2(execFile2);
27765
27911
  var MAX_FULL_FILE_TEXT_BYTES = 512 * 1024;
27766
- function readWorkspaceFileAsUtf8(absPath) {
27912
+ async function readWorkspaceFileAsUtf8(absPath) {
27767
27913
  try {
27768
- const st = fs4.statSync(absPath);
27914
+ const st = await stat(absPath);
27769
27915
  if (!st.isFile() || st.size > MAX_FULL_FILE_TEXT_BYTES) return void 0;
27770
- return fs4.readFileSync(absPath, "utf8");
27916
+ return await readFile(absPath, "utf8");
27771
27917
  } catch {
27772
27918
  return void 0;
27773
27919
  }
@@ -27777,7 +27923,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
27777
27923
  const filePath = snapshotFilePath(agentCwd, runId);
27778
27924
  let data;
27779
27925
  try {
27780
- const raw = fs4.readFileSync(filePath, "utf8");
27926
+ const raw = await readFile(filePath, "utf8");
27781
27927
  data = JSON.parse(raw);
27782
27928
  } catch (e) {
27783
27929
  log2(
@@ -27806,7 +27952,7 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
27806
27952
  continue;
27807
27953
  }
27808
27954
  const lines = namesRaw.split("\n").map((l) => l.trim()).filter(Boolean);
27809
- const slug = path5.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
27955
+ const slug = path6.basename(repo.path).replace(/[^\w.-]+/g, "_") || "repo";
27810
27956
  for (const rel of lines) {
27811
27957
  if (rel.includes("..")) continue;
27812
27958
  try {
@@ -27820,8 +27966,8 @@ async function collectTurnGitDiffFromPreTurnSnapshot(options) {
27820
27966
  );
27821
27967
  if (!patchContent.trim()) continue;
27822
27968
  const displayPath = multiRepo ? `${slug}/${rel}` : rel;
27823
- const absFile = path5.join(repo.path, rel);
27824
- const newText = readWorkspaceFileAsUtf8(absFile);
27969
+ const absFile = path6.join(repo.path, rel);
27970
+ const newText = await readWorkspaceFileAsUtf8(absFile);
27825
27971
  sendSessionUpdate({
27826
27972
  type: "session_file_change",
27827
27973
  sessionId,
@@ -27876,7 +28022,6 @@ async function sendPromptToAgent(options) {
27876
28022
  } catch (err) {
27877
28023
  const errMsg = err instanceof Error ? err.message : String(err);
27878
28024
  log2(`[Agent] Send failed: ${errMsg}`);
27879
- if (err instanceof Error && err.stack) log2(`[Agent] ${err.stack}`);
27880
28025
  sendResult({
27881
28026
  type: "prompt_result",
27882
28027
  id: promptId,
@@ -27889,7 +28034,7 @@ async function sendPromptToAgent(options) {
27889
28034
  }
27890
28035
 
27891
28036
  // src/acp/ensure-acp-client.ts
27892
- import * as fs5 from "node:fs";
28037
+ import * as fs4 from "node:fs";
27893
28038
  import * as path9 from "node:path";
27894
28039
 
27895
28040
  // src/error-message.ts
@@ -27908,81 +28053,38 @@ function isCodexAcpCommand(command) {
27908
28053
  const i = command.indexOf("@zed-industries/codex-acp");
27909
28054
  return i >= 0 && (i === 0 || command[i - 1] === "npx" || command[i - 1] === "bunx");
27910
28055
  }
28056
+ function buildCodexAcpSpawnCommand(base, _sessionMode) {
28057
+ return [...base];
28058
+ }
27911
28059
  async function createCodexAcpClient(options) {
27912
- const command = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
27913
- return createAcpClient({ ...options, command });
28060
+ const base = options.command?.length && options.command.some((a) => a.includes("codex-acp")) ? options.command : [...DEFAULT_CODEX_ACP_COMMAND];
28061
+ const command = buildCodexAcpSpawnCommand(base, options.sessionMode);
28062
+ return createSdkStdioAcpClient({ ...options, command });
28063
+ }
28064
+
28065
+ // src/acp/clients/claude-code-acp-client.ts
28066
+ function buildClaudeCodeAcpSpawnCommand(base, sessionMode) {
28067
+ if (!sessionMode) return [...base];
28068
+ const m = sessionMode.trim();
28069
+ if (m === "plan") return [...base, "--permission-mode", "plan"];
28070
+ return [...base];
28071
+ }
28072
+ async function createClaudeCodeAcpClient(options) {
28073
+ const command = buildClaudeCodeAcpSpawnCommand(options.command, options.sessionMode);
28074
+ return createSdkStdioAcpClient({
28075
+ ...options,
28076
+ command,
28077
+ /** Claude-based agents sometimes ignore `session/cancel`; unblocks stop / stuck prompt. */
28078
+ killSubprocessAfterCancelMs: options.killSubprocessAfterCancelMs ?? 1e3
28079
+ });
27914
28080
  }
27915
28081
 
27916
28082
  // src/acp/clients/cursor-acp-client.ts
27917
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
27918
- import { dirname } from "node:path";
28083
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "node:fs";
28084
+ import { dirname as dirname2 } from "node:path";
27919
28085
  import { spawn as spawn4 } from "node:child_process";
27920
28086
  import * as readline from "node:readline";
27921
28087
 
27922
- // src/acp/safe-fs-path.ts
27923
- import * as path6 from "node:path";
27924
- function resolveSafePathUnderCwd(cwd, filePath) {
27925
- const trimmed2 = filePath.trim();
27926
- if (!trimmed2) return null;
27927
- const normalizedCwd = path6.resolve(cwd);
27928
- const resolved = path6.isAbsolute(trimmed2) ? path6.normalize(trimmed2) : path6.resolve(normalizedCwd, trimmed2);
27929
- const rel = path6.relative(normalizedCwd, resolved);
27930
- if (rel.startsWith("..") || path6.isAbsolute(rel)) return null;
27931
- return resolved;
27932
- }
27933
- function toDisplayPathRelativeToCwd(cwd, absolutePath) {
27934
- const normalizedCwd = path6.resolve(cwd);
27935
- const rel = path6.relative(normalizedCwd, path6.resolve(absolutePath));
27936
- if (!rel || rel === "") return path6.basename(absolutePath);
27937
- return rel.split(path6.sep).join("/");
27938
- }
27939
-
27940
- // src/files/diff/unified-diff.ts
27941
- function computeLineDiff(oldText, newText) {
27942
- const oldLines = oldText.split("\n");
27943
- const newLines = newText.split("\n");
27944
- const m = oldLines.length;
27945
- const n = newLines.length;
27946
- const dp = Array(m + 1);
27947
- for (let i2 = 0; i2 <= m; i2++) dp[i2] = Array(n + 1).fill(0);
27948
- for (let i2 = 1; i2 <= m; i2++) {
27949
- for (let j2 = 1; j2 <= n; j2++) {
27950
- if (oldLines[i2 - 1] === newLines[j2 - 1]) {
27951
- dp[i2][j2] = dp[i2 - 1][j2 - 1] + 1;
27952
- } else {
27953
- dp[i2][j2] = Math.max(dp[i2 - 1][j2], dp[i2][j2 - 1]);
27954
- }
27955
- }
27956
- }
27957
- const result = [];
27958
- let i = m;
27959
- let j = n;
27960
- while (i > 0 || j > 0) {
27961
- if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
27962
- result.unshift({ type: "context", line: oldLines[i - 1] });
27963
- i--;
27964
- j--;
27965
- } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
27966
- result.unshift({ type: "add", line: newLines[j - 1] });
27967
- j--;
27968
- } else {
27969
- result.unshift({ type: "remove", line: oldLines[i - 1] });
27970
- i--;
27971
- }
27972
- }
27973
- return result;
27974
- }
27975
- function editSnippetToUnifiedDiff(filePath, oldText, newText) {
27976
- const lines = computeLineDiff(oldText, newText);
27977
- const out = [`--- ${filePath}`, `+++ ${filePath}`];
27978
- for (const d of lines) {
27979
- if (d.type === "add") out.push(`+${d.line}`);
27980
- else if (d.type === "remove") out.push(`-${d.line}`);
27981
- else out.push(` ${d.line}`);
27982
- }
27983
- return out.join("\n");
27984
- }
27985
-
27986
28088
  // src/acp/format-session-update-kind-for-log.ts
27987
28089
  var SESSION_UPDATE_KIND_LABELS = {
27988
28090
  tool_call: "Tool call",
@@ -28020,8 +28122,15 @@ function sliceLinesByRange(content, line, limit) {
28020
28122
  const end = limit != null && limit > 0 ? start + limit : lines.length;
28021
28123
  return lines.slice(start, end).join("\n");
28022
28124
  }
28125
+ function buildCursorAcpSpawnCommand(base, sessionMode) {
28126
+ if (!sessionMode) return [...base];
28127
+ const m = sessionMode.trim();
28128
+ if (m !== "ask" && m !== "plan") return [...base];
28129
+ return [...base, "--mode", m];
28130
+ }
28023
28131
  async function createCursorAcpClient(options) {
28024
- const { command, cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
28132
+ const command = buildCursorAcpSpawnCommand(options.command, options.sessionMode);
28133
+ const { cwd = getBridgeWorkspaceDirectory(), onSessionUpdate, onRequest, onFileChange } = options;
28025
28134
  const dbgFs = process.env.BUILDAMATON_DEBUG_ACP_FS === "1";
28026
28135
  const isWindows = process.platform === "win32";
28027
28136
  const child = spawn4(command[0], command.slice(1), {
@@ -28153,8 +28262,8 @@ async function createCursorAcpClient(options) {
28153
28262
  }
28154
28263
  }
28155
28264
  try {
28156
- mkdirSync2(dirname(abs), { recursive: true });
28157
- writeFileSync2(abs, newText, "utf8");
28265
+ mkdirSync3(dirname2(abs), { recursive: true });
28266
+ writeFileSync3(abs, newText, "utf8");
28158
28267
  } catch (e) {
28159
28268
  respondJsonRpcError(id, -32e3, e instanceof Error ? e.message : String(e));
28160
28269
  return;
@@ -28252,8 +28361,20 @@ async function createCursorAcpClient(options) {
28252
28361
  var AGENT_TYPE_DEFAULT_COMMANDS = {
28253
28362
  "cursor-cli": ["agent", "acp"],
28254
28363
  "codex-acp": [...DEFAULT_CODEX_ACP_COMMAND],
28255
- "claude-code": ["npx", "--yes", "@anthropic-ai/claude-code"]
28364
+ /** ACP stdio agent; `@anthropic-ai/claude-code` is the interactive CLI and does not speak ACP on stdout. */
28365
+ "claude-code": ["npx", "--yes", "@agentclientprotocol/claude-agent-acp"]
28256
28366
  };
28367
+ var AGENT_TYPE_DISPLAY_NAMES = {
28368
+ "cursor-cli": "Cursor",
28369
+ "codex-acp": "Codex",
28370
+ "claude-code": "Claude Code"
28371
+ };
28372
+ function getAgentTypeDisplayName(agentType) {
28373
+ if (agentType == null || agentType === "") return "Unknown agent";
28374
+ const known = AGENT_TYPE_DISPLAY_NAMES[agentType];
28375
+ if (known) return known;
28376
+ return agentType.split(/[-_]/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(" ");
28377
+ }
28257
28378
  function useCursorAcp(agentType, command) {
28258
28379
  if (agentType === "cursor-cli") return true;
28259
28380
  return command[0] === "agent" && command[1] === "acp";
@@ -28266,14 +28387,33 @@ function resolveAgentCommand(preferredAgentType) {
28266
28387
  if (!preferredAgentType) return null;
28267
28388
  const command = AGENT_TYPE_DEFAULT_COMMANDS[preferredAgentType];
28268
28389
  if (!command?.length) return null;
28269
- const createClient = useCursorAcp(preferredAgentType, command) ? createCursorAcpClient : useCodexAcp(preferredAgentType, command) ? createCodexAcpClient : createAcpClient;
28270
- const label = preferredAgentType;
28271
- return { command, label, createClient };
28390
+ if (useCursorAcp(preferredAgentType, command)) {
28391
+ return {
28392
+ command,
28393
+ label: preferredAgentType,
28394
+ createClient: createCursorAcpClient,
28395
+ spawnCommandForSession: (sessionMode) => buildCursorAcpSpawnCommand(command, sessionMode)
28396
+ };
28397
+ }
28398
+ if (useCodexAcp(preferredAgentType, command)) {
28399
+ return {
28400
+ command,
28401
+ label: preferredAgentType,
28402
+ createClient: createCodexAcpClient,
28403
+ spawnCommandForSession: (sessionMode) => buildCodexAcpSpawnCommand(command, sessionMode)
28404
+ };
28405
+ }
28406
+ return {
28407
+ command,
28408
+ label: preferredAgentType,
28409
+ createClient: createClaudeCodeAcpClient,
28410
+ spawnCommandForSession: (sessionMode) => buildClaudeCodeAcpSpawnCommand(command, sessionMode)
28411
+ };
28272
28412
  }
28273
28413
 
28274
28414
  // src/acp/session-file-change-path-kind.ts
28275
28415
  import { execFileSync as execFileSync3 } from "node:child_process";
28276
- import { existsSync, statSync as statSync2 } from "node:fs";
28416
+ import { existsSync, statSync } from "node:fs";
28277
28417
 
28278
28418
  // src/git/get-git-repo-root-sync.ts
28279
28419
  import { execFileSync } from "node:child_process";
@@ -28382,7 +28522,7 @@ function getSessionFileChangeDirectoryFlags(cwd, displayPath) {
28382
28522
  const abs = tryWorkspaceDisplayToAbs(cwd, displayPath);
28383
28523
  if (abs && existsSync(abs)) {
28384
28524
  try {
28385
- if (statSync2(abs).isDirectory()) {
28525
+ if (statSync(abs).isDirectory()) {
28386
28526
  return { isDirectory: true, directoryRemoved: false };
28387
28527
  }
28388
28528
  return { isDirectory: false, directoryRemoved: false };
@@ -28928,7 +29068,8 @@ async function ensureAcpClient(options) {
28928
29068
  state.lastAcpStartError = "No agent type: ensure the app sends agentType on prompts or agent_config for this bridge.";
28929
29069
  return null;
28930
29070
  }
28931
- const agentKey = `${resolved.label}::${resolved.command.join("\0")}`;
29071
+ const fullCmd = resolved.spawnCommandForSession(mode);
29072
+ const agentKey = `${resolved.label}::${fullCmd.join("\0")}`;
28932
29073
  if (state.acpHandle && state.acpAgentKey !== agentKey) {
28933
29074
  try {
28934
29075
  state.acpHandle.disconnect();
@@ -28942,7 +29083,7 @@ async function ensureAcpClient(options) {
28942
29083
  if (!state.acpStartPromise) {
28943
29084
  let statOk = false;
28944
29085
  try {
28945
- const st = fs5.statSync(targetCwd);
29086
+ const st = fs4.statSync(targetCwd);
28946
29087
  statOk = st.isDirectory();
28947
29088
  if (!statOk) {
28948
29089
  state.lastAcpStartError = `Agent cwd is not a directory: ${targetCwd}`;
@@ -28955,8 +29096,6 @@ async function ensureAcpClient(options) {
28955
29096
  if (!statOk) {
28956
29097
  return null;
28957
29098
  }
28958
- const modeFlag = mode && ["ask", "plan"].includes(mode) ? ["--mode", mode] : [];
28959
- const fullCmd = [...resolved.command, ...modeFlag];
28960
29099
  const hooks = buildAcpSessionBridgeHooks({
28961
29100
  routing,
28962
29101
  getSendSessionUpdate: () => sendSessionUpdate,
@@ -28964,8 +29103,15 @@ async function ensureAcpClient(options) {
28964
29103
  log: log2
28965
29104
  });
28966
29105
  state.acpStartPromise = resolved.createClient({
28967
- command: fullCmd,
29106
+ command: resolved.command,
29107
+ sessionMode: mode,
28968
29108
  cwd: targetCwd,
29109
+ onAgentSubprocessExit: () => {
29110
+ state.acpHandle = null;
29111
+ state.acpStartPromise = null;
29112
+ state.acpAgentKey = null;
29113
+ state.lastAcpStartError = "Agent subprocess exited";
29114
+ },
28969
29115
  ...hooks
28970
29116
  }).then((h) => {
28971
29117
  state.lastAcpStartError = null;
@@ -29003,6 +29149,14 @@ async function createAcpManager(options) {
29003
29149
  backendFallbackAgentType = agentType;
29004
29150
  }
29005
29151
  }
29152
+ function logPromptReceivedFromBridge(opts) {
29153
+ const { agentType, mode } = opts;
29154
+ const preferredForPrompt = agentType ?? backendFallbackAgentType ?? null;
29155
+ const modeLabel = typeof mode === "string" && mode.trim() !== "" ? mode.trim() : "not set";
29156
+ log2(
29157
+ `[Agent] Prompt received (${getAgentTypeDisplayName(preferredForPrompt)}, mode: ${modeLabel})`
29158
+ );
29159
+ }
29006
29160
  function handlePrompt(opts) {
29007
29161
  const {
29008
29162
  promptText,
@@ -29083,6 +29237,7 @@ async function createAcpManager(options) {
29083
29237
  if (promptRouting.runId !== runId) return false;
29084
29238
  const handle = state.acpHandle;
29085
29239
  if (handle?.cancel) {
29240
+ log2("[Agent] Stop requested");
29086
29241
  try {
29087
29242
  await handle.cancel();
29088
29243
  return true;
@@ -29091,6 +29246,7 @@ async function createAcpManager(options) {
29091
29246
  return false;
29092
29247
  }
29093
29248
  }
29249
+ log2("[Agent] Stop requested (agent still starting)");
29094
29250
  pendingCancelRunId = runId;
29095
29251
  return true;
29096
29252
  }
@@ -29103,7 +29259,14 @@ async function createAcpManager(options) {
29103
29259
  state.acpStartPromise = null;
29104
29260
  state.acpAgentKey = null;
29105
29261
  }
29106
- return { setPreferredAgentType, handlePrompt, cancelRun, resolveRequest, disconnect };
29262
+ return {
29263
+ setPreferredAgentType,
29264
+ logPromptReceivedFromBridge,
29265
+ handlePrompt,
29266
+ cancelRun,
29267
+ resolveRequest,
29268
+ disconnect
29269
+ };
29107
29270
  }
29108
29271
 
29109
29272
  // src/bridge/routing/handlers/auth-token.ts
@@ -29238,6 +29401,8 @@ function handleBridgePrompt(msg, deps) {
29238
29401
  const sessionWorktreesEnabled = msg.sessionWorktreesEnabled === true;
29239
29402
  const agentType = typeof msg.agentType === "string" && msg.agentType.trim() ? msg.agentType.trim() : void 0;
29240
29403
  const runId = typeof msg.runId === "string" ? msg.runId : void 0;
29404
+ const mode = typeof msg.mode === "string" && msg.mode.trim() ? msg.mode.trim() : void 0;
29405
+ acpManager.logPromptReceivedFromBridge({ agentType, mode });
29241
29406
  const sendResult = (result) => {
29242
29407
  const s = getWs();
29243
29408
  if (s) sendWsMessage(s, result);
@@ -29300,7 +29465,7 @@ function handleBridgePrompt(msg, deps) {
29300
29465
  promptId: msg.id,
29301
29466
  sessionId,
29302
29467
  runId,
29303
- mode: msg.mode,
29468
+ mode,
29304
29469
  agentType,
29305
29470
  cwd: effectiveCwd,
29306
29471
  sendResult,
@@ -29370,7 +29535,7 @@ var handleSkillCallMessage = (msg, { getWs, log: log2 }) => {
29370
29535
  };
29371
29536
 
29372
29537
  // src/files/list-dir.ts
29373
- import fs6 from "node:fs";
29538
+ import fs5 from "node:fs";
29374
29539
  import path13 from "node:path";
29375
29540
 
29376
29541
  // src/files/ensure-under-cwd.ts
@@ -29391,14 +29556,14 @@ function listDir(relativePath) {
29391
29556
  return { error: "Path is outside working directory" };
29392
29557
  }
29393
29558
  try {
29394
- const names = fs6.readdirSync(resolved, { withFileTypes: true });
29559
+ const names = fs5.readdirSync(resolved, { withFileTypes: true });
29395
29560
  const entries = names.filter((d) => !d.name.startsWith(".")).map((d) => {
29396
29561
  const entryPath = path13.join(relativePath || ".", d.name).replace(/\\/g, "/");
29397
29562
  const fullPath = path13.join(resolved, d.name);
29398
29563
  let isDir = d.isDirectory();
29399
29564
  if (d.isSymbolicLink()) {
29400
29565
  try {
29401
- const targetStat = fs6.statSync(fullPath);
29566
+ const targetStat = fs5.statSync(fullPath);
29402
29567
  isDir = targetStat.isDirectory();
29403
29568
  } catch {
29404
29569
  isDir = false;
@@ -29422,25 +29587,25 @@ function listDir(relativePath) {
29422
29587
  }
29423
29588
 
29424
29589
  // src/files/read-file.ts
29425
- import fs7 from "node:fs";
29590
+ import fs6 from "node:fs";
29426
29591
  import { StringDecoder } from "node:string_decoder";
29427
29592
  function resolveFilePath(relativePath) {
29428
29593
  const resolved = ensureUnderCwd(relativePath, getBridgeWorkspaceDirectory());
29429
29594
  if (!resolved) return { error: "Path is outside working directory" };
29430
29595
  let real;
29431
29596
  try {
29432
- real = fs7.realpathSync(resolved);
29597
+ real = fs6.realpathSync(resolved);
29433
29598
  } catch {
29434
29599
  real = resolved;
29435
29600
  }
29436
- const stat = fs7.statSync(real);
29437
- if (!stat.isFile()) return { error: "Not a file" };
29601
+ const stat2 = fs6.statSync(real);
29602
+ if (!stat2.isFile()) return { error: "Not a file" };
29438
29603
  return real;
29439
29604
  }
29440
29605
  var LINE_CHUNK_SIZE = 64 * 1024;
29441
29606
  function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize = LINE_CHUNK_SIZE) {
29442
- const fileSize = fs7.statSync(filePath).size;
29443
- const fd = fs7.openSync(filePath, "r");
29607
+ const fileSize = fs6.statSync(filePath).size;
29608
+ const fd = fs6.openSync(filePath, "r");
29444
29609
  const bufSize = 64 * 1024;
29445
29610
  const buf = Buffer.alloc(bufSize);
29446
29611
  const decoder = new StringDecoder("utf8");
@@ -29453,7 +29618,7 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
29453
29618
  let line0Accum = "";
29454
29619
  try {
29455
29620
  let bytesRead;
29456
- while (!done && (bytesRead = fs7.readSync(fd, buf, 0, bufSize, null)) > 0) {
29621
+ while (!done && (bytesRead = fs6.readSync(fd, buf, 0, bufSize, null)) > 0) {
29457
29622
  const text = partial2 + decoder.write(buf.subarray(0, bytesRead));
29458
29623
  partial2 = "";
29459
29624
  let lineStart = 0;
@@ -29588,10 +29753,10 @@ function readFileRange(filePath, startLine, endLine, lineOffsetIn, lineChunkSize
29588
29753
  }
29589
29754
  return { content: resultLines.join("\n"), size: fileSize };
29590
29755
  } finally {
29591
- fs7.closeSync(fd);
29756
+ fs6.closeSync(fd);
29592
29757
  }
29593
29758
  }
29594
- function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
29759
+ function readFile2(relativePath, startLine, endLine, lineOffset, lineChunkSize = LINE_CHUNK_SIZE) {
29595
29760
  try {
29596
29761
  const result = resolveFilePath(relativePath);
29597
29762
  if (typeof result === "object") return result;
@@ -29599,17 +29764,17 @@ function readFile(relativePath, startLine, endLine, lineOffset, lineChunkSize =
29599
29764
  if (hasRange) {
29600
29765
  return readFileRange(result, startLine, endLine, lineOffset, lineChunkSize);
29601
29766
  }
29602
- const stat = fs7.statSync(result);
29603
- const raw = fs7.readFileSync(result, "utf8");
29767
+ const stat2 = fs6.statSync(result);
29768
+ const raw = fs6.readFileSync(result, "utf8");
29604
29769
  const lines = raw.split(/\r?\n/);
29605
- return { content: raw, totalLines: lines.length, size: stat.size };
29770
+ return { content: raw, totalLines: lines.length, size: stat2.size };
29606
29771
  } catch (err) {
29607
29772
  return { error: err instanceof Error ? err.message : String(err) };
29608
29773
  }
29609
29774
  }
29610
29775
 
29611
29776
  // src/files/file-index.ts
29612
- import fs8 from "node:fs";
29777
+ import fs7 from "node:fs";
29613
29778
  import path14 from "node:path";
29614
29779
  import os2 from "node:os";
29615
29780
  import crypto2 from "node:crypto";
@@ -29656,23 +29821,23 @@ function binarySearch(arr, x) {
29656
29821
  function walkDir(dir, baseDir, out) {
29657
29822
  let names;
29658
29823
  try {
29659
- names = fs8.readdirSync(dir);
29824
+ names = fs7.readdirSync(dir);
29660
29825
  } catch {
29661
29826
  return;
29662
29827
  }
29663
29828
  for (const name of names) {
29664
29829
  if (name.startsWith(".")) continue;
29665
29830
  const full = path14.join(dir, name);
29666
- let stat;
29831
+ let stat2;
29667
29832
  try {
29668
- stat = fs8.statSync(full);
29833
+ stat2 = fs7.statSync(full);
29669
29834
  } catch {
29670
29835
  continue;
29671
29836
  }
29672
29837
  const relative4 = path14.relative(baseDir, full).replace(/\\/g, "/");
29673
- if (stat.isDirectory()) {
29838
+ if (stat2.isDirectory()) {
29674
29839
  walkDir(full, baseDir, out);
29675
- } else if (stat.isFile()) {
29840
+ } else if (stat2.isFile()) {
29676
29841
  out.push(relative4);
29677
29842
  }
29678
29843
  }
@@ -29696,8 +29861,8 @@ function buildFileIndex(cwd) {
29696
29861
  const data = { version: INDEX_VERSION, paths, trigramIndex };
29697
29862
  const indexPath = getIndexPath(resolved);
29698
29863
  try {
29699
- if (!fs8.existsSync(INDEX_DIR)) fs8.mkdirSync(INDEX_DIR, { recursive: true });
29700
- fs8.writeFileSync(indexPath, JSON.stringify(data), "utf8");
29864
+ if (!fs7.existsSync(INDEX_DIR)) fs7.mkdirSync(INDEX_DIR, { recursive: true });
29865
+ fs7.writeFileSync(indexPath, JSON.stringify(data), "utf8");
29701
29866
  } catch (e) {
29702
29867
  console.error("[file-index] Failed to write index:", e);
29703
29868
  }
@@ -29707,7 +29872,7 @@ function loadFileIndex(cwd) {
29707
29872
  const resolved = path14.resolve(cwd);
29708
29873
  const indexPath = getIndexPath(resolved);
29709
29874
  try {
29710
- const raw = fs8.readFileSync(indexPath, "utf8");
29875
+ const raw = fs7.readFileSync(indexPath, "utf8");
29711
29876
  const parsed = JSON.parse(raw);
29712
29877
  if (parsed !== null && typeof parsed === "object" && Array.isArray(parsed.paths)) {
29713
29878
  const obj = parsed;
@@ -29811,7 +29976,7 @@ function handleFileBrowserRequest(msg, socket) {
29811
29976
  const endLine = typeof msg.endLine === "number" ? msg.endLine : void 0;
29812
29977
  const lineOffset = typeof msg.lineOffset === "number" ? msg.lineOffset : void 0;
29813
29978
  const lineChunkSize = typeof msg.lineChunkSize === "number" ? msg.lineChunkSize : void 0;
29814
- const result = readFile(reqPath, startLine, endLine, lineOffset, lineChunkSize);
29979
+ const result = readFile2(reqPath, startLine, endLine, lineOffset, lineChunkSize);
29815
29980
  if ("error" in result) {
29816
29981
  sendWsMessage(socket, { type: "file_browser_response", id: msg.id, error: result.error });
29817
29982
  } else {
@@ -29846,7 +30011,7 @@ function handleFileBrowserSearchMessage(msg, { getWs }) {
29846
30011
  }
29847
30012
 
29848
30013
  // src/skills/discover-local-agent-skills.ts
29849
- import fs9 from "node:fs";
30014
+ import fs8 from "node:fs";
29850
30015
  import path15 from "node:path";
29851
30016
  var SKILL_DISCOVERY_ROOTS = [".agents/skills", ".claude/skills", ".cursor/skills", "skills"];
29852
30017
  function discoverLocalSkills(cwd) {
@@ -29854,22 +30019,22 @@ function discoverLocalSkills(cwd) {
29854
30019
  const seenKeys = /* @__PURE__ */ new Set();
29855
30020
  for (const rel of SKILL_DISCOVERY_ROOTS) {
29856
30021
  const base = path15.join(cwd, rel);
29857
- if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
30022
+ if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
29858
30023
  let entries = [];
29859
30024
  try {
29860
- entries = fs9.readdirSync(base);
30025
+ entries = fs8.readdirSync(base);
29861
30026
  } catch {
29862
30027
  continue;
29863
30028
  }
29864
30029
  for (const name of entries) {
29865
30030
  const dir = path15.join(base, name);
29866
30031
  try {
29867
- if (!fs9.statSync(dir).isDirectory()) continue;
30032
+ if (!fs8.statSync(dir).isDirectory()) continue;
29868
30033
  } catch {
29869
30034
  continue;
29870
30035
  }
29871
30036
  const skillMd = path15.join(dir, "SKILL.md");
29872
- if (!fs9.existsSync(skillMd)) continue;
30037
+ if (!fs8.existsSync(skillMd)) continue;
29873
30038
  const key = `${rel}/${name}`;
29874
30039
  if (seenKeys.has(key)) continue;
29875
30040
  seenKeys.add(key);
@@ -29882,10 +30047,10 @@ function discoverSkillLayoutRoots(cwd) {
29882
30047
  const roots = [];
29883
30048
  for (const rel of SKILL_DISCOVERY_ROOTS) {
29884
30049
  const base = path15.join(cwd, rel);
29885
- if (!fs9.existsSync(base) || !fs9.statSync(base).isDirectory()) continue;
30050
+ if (!fs8.existsSync(base) || !fs8.statSync(base).isDirectory()) continue;
29886
30051
  let entries = [];
29887
30052
  try {
29888
- entries = fs9.readdirSync(base);
30053
+ entries = fs8.readdirSync(base);
29889
30054
  } catch {
29890
30055
  continue;
29891
30056
  }
@@ -29893,11 +30058,11 @@ function discoverSkillLayoutRoots(cwd) {
29893
30058
  for (const name of entries) {
29894
30059
  const dir = path15.join(base, name);
29895
30060
  try {
29896
- if (!fs9.statSync(dir).isDirectory()) continue;
30061
+ if (!fs8.statSync(dir).isDirectory()) continue;
29897
30062
  } catch {
29898
30063
  continue;
29899
30064
  }
29900
- if (!fs9.existsSync(path15.join(dir, "SKILL.md"))) continue;
30065
+ if (!fs8.existsSync(path15.join(dir, "SKILL.md"))) continue;
29901
30066
  const relPath = `${rel}/${name}`.replace(/\\/g, "/");
29902
30067
  skills2.push({ name, relPath });
29903
30068
  }
@@ -29917,7 +30082,7 @@ function handleSkillLayoutRequest(msg, deps) {
29917
30082
  }
29918
30083
 
29919
30084
  // src/skills/install-remote-skills.ts
29920
- import fs10 from "node:fs";
30085
+ import fs9 from "node:fs";
29921
30086
  import path16 from "node:path";
29922
30087
  function installRemoteSkills(cwd, targetDir, items) {
29923
30088
  const installed = [];
@@ -29933,11 +30098,11 @@ function installRemoteSkills(cwd, targetDir, items) {
29933
30098
  for (const f of item.files) {
29934
30099
  if (typeof f.path !== "string" || !f.text && !f.base64) continue;
29935
30100
  const dest = path16.join(skillDir, f.path);
29936
- fs10.mkdirSync(path16.dirname(dest), { recursive: true });
30101
+ fs9.mkdirSync(path16.dirname(dest), { recursive: true });
29937
30102
  if (f.text !== void 0) {
29938
- fs10.writeFileSync(dest, f.text, "utf8");
30103
+ fs9.writeFileSync(dest, f.text, "utf8");
29939
30104
  } else if (f.base64) {
29940
- fs10.writeFileSync(dest, Buffer.from(f.base64, "base64"));
30105
+ fs9.writeFileSync(dest, Buffer.from(f.base64, "base64"));
29941
30106
  }
29942
30107
  }
29943
30108
  installed.push({
@@ -30031,7 +30196,7 @@ var handleSessionDiscardedMessage = (msg, deps) => {
30031
30196
  };
30032
30197
 
30033
30198
  // src/bridge/routing/handlers/revert-turn-snapshot.ts
30034
- import * as fs11 from "node:fs";
30199
+ import * as fs10 from "node:fs";
30035
30200
  var handleRevertTurnSnapshotMessage = (msg, deps) => {
30036
30201
  const id = typeof msg.id === "string" ? msg.id : "";
30037
30202
  const sessionId = typeof msg.sessionId === "string" ? msg.sessionId : "";
@@ -30043,7 +30208,7 @@ var handleRevertTurnSnapshotMessage = (msg, deps) => {
30043
30208
  if (!s) return;
30044
30209
  const agentBase = sessionWorktreeManager.getAgentCwdForSession(sessionId) ?? getBridgeWorkspaceDirectory();
30045
30210
  const file2 = snapshotFilePath(agentBase, turnId);
30046
- if (!fs11.existsSync(file2)) {
30211
+ if (!fs10.existsSync(file2)) {
30047
30212
  sendWsMessage(s, {
30048
30213
  type: "revert_turn_snapshot_result",
30049
30214
  id,
@@ -30144,25 +30309,12 @@ function dispatchBridgeMessage(msg, deps) {
30144
30309
  }
30145
30310
 
30146
30311
  // src/bridge/routing/handle-bridge-message.ts
30147
- var DEFERRED_INBOUND_TYPES = /* @__PURE__ */ new Set([
30148
- "server_control",
30149
- "prompt",
30150
- "install_skills",
30151
- "refresh_local_skills",
30152
- "dev_servers_config"
30153
- ]);
30154
30312
  function handleBridgeMessage(data, deps) {
30155
30313
  const msg = data;
30156
- const socket = deps.getWs();
30157
- if (!socket) return;
30158
- const type = msg.type;
30159
- if (typeof type === "string" && DEFERRED_INBOUND_TYPES.has(type)) {
30160
- setImmediate(() => {
30161
- dispatchBridgeMessage(msg, deps);
30162
- });
30163
- return;
30164
- }
30165
- dispatchBridgeMessage(msg, deps);
30314
+ if (!deps.getWs()) return;
30315
+ setImmediate(() => {
30316
+ dispatchBridgeMessage(msg, deps);
30317
+ });
30166
30318
  }
30167
30319
 
30168
30320
  // src/worktrees/session-worktree-manager.ts
@@ -30170,7 +30322,7 @@ import * as path20 from "node:path";
30170
30322
  import os4 from "node:os";
30171
30323
 
30172
30324
  // src/worktrees/prepare-new-session-worktrees.ts
30173
- import * as fs13 from "node:fs";
30325
+ import * as fs12 from "node:fs";
30174
30326
  import * as path18 from "node:path";
30175
30327
 
30176
30328
  // src/git/worktree-add.ts
@@ -30180,7 +30332,7 @@ async function gitWorktreeAddBranch(mainRepoPath, worktreePath, branch) {
30180
30332
  }
30181
30333
 
30182
30334
  // src/worktrees/worktree-layout-file.ts
30183
- import * as fs12 from "node:fs";
30335
+ import * as fs11 from "node:fs";
30184
30336
  import * as path17 from "node:path";
30185
30337
  import os3 from "node:os";
30186
30338
  var LAYOUT_FILENAME = "worktree-launcher-layout.json";
@@ -30197,8 +30349,8 @@ function normalizeLoadedLayout(raw) {
30197
30349
  function loadWorktreeLayout() {
30198
30350
  try {
30199
30351
  const p = defaultWorktreeLayoutPath();
30200
- if (!fs12.existsSync(p)) return { launcherCwds: [] };
30201
- const raw = JSON.parse(fs12.readFileSync(p, "utf8"));
30352
+ if (!fs11.existsSync(p)) return { launcherCwds: [] };
30353
+ const raw = JSON.parse(fs11.readFileSync(p, "utf8"));
30202
30354
  return normalizeLoadedLayout(raw);
30203
30355
  } catch {
30204
30356
  return { launcherCwds: [] };
@@ -30207,8 +30359,8 @@ function loadWorktreeLayout() {
30207
30359
  function saveWorktreeLayout(layout) {
30208
30360
  try {
30209
30361
  const dir = path17.dirname(defaultWorktreeLayoutPath());
30210
- fs12.mkdirSync(dir, { recursive: true });
30211
- fs12.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
30362
+ fs11.mkdirSync(dir, { recursive: true });
30363
+ fs11.writeFileSync(defaultWorktreeLayoutPath(), JSON.stringify(layout, null, 2), "utf8");
30212
30364
  } catch {
30213
30365
  }
30214
30366
  }
@@ -30245,13 +30397,13 @@ async function prepareNewSessionWorktrees(options) {
30245
30397
  }
30246
30398
  const branch = `session-${sessionId}`;
30247
30399
  const worktreePaths = [];
30248
- fs13.mkdirSync(agentMirrorRoot, { recursive: true });
30400
+ fs12.mkdirSync(agentMirrorRoot, { recursive: true });
30249
30401
  for (const repo of repos) {
30250
30402
  let rel = path18.relative(launcherResolved, repo.absolutePath);
30251
30403
  if (rel.startsWith("..") || path18.isAbsolute(rel)) continue;
30252
30404
  const relNorm = rel === "" ? "." : rel;
30253
30405
  const wtPath = path18.join(agentMirrorRoot, relNorm, sessionId);
30254
- fs13.mkdirSync(path18.dirname(wtPath), { recursive: true });
30406
+ fs12.mkdirSync(path18.dirname(wtPath), { recursive: true });
30255
30407
  try {
30256
30408
  await gitWorktreeAddBranch(repo.absolutePath, wtPath, branch);
30257
30409
  log2(`[worktrees] Added worktree ${wtPath} (branch ${branch}).`);
@@ -30288,18 +30440,18 @@ async function renameSessionWorktreeBranches(paths, newBranch, log2) {
30288
30440
  }
30289
30441
 
30290
30442
  // src/worktrees/remove-session-worktrees.ts
30291
- import * as fs16 from "node:fs";
30443
+ import * as fs15 from "node:fs";
30292
30444
 
30293
30445
  // src/git/worktree-remove.ts
30294
- import * as fs15 from "node:fs";
30446
+ import * as fs14 from "node:fs";
30295
30447
 
30296
30448
  // src/git/resolve-main-repo-from-git-file.ts
30297
- import * as fs14 from "node:fs";
30449
+ import * as fs13 from "node:fs";
30298
30450
  import * as path19 from "node:path";
30299
30451
  function resolveMainRepoFromWorktreeGitFile(wt) {
30300
30452
  const gitDirFile = path19.join(wt, ".git");
30301
- if (!fs14.existsSync(gitDirFile) || !fs14.statSync(gitDirFile).isFile()) return "";
30302
- const first2 = fs14.readFileSync(gitDirFile, "utf8").trim();
30453
+ if (!fs13.existsSync(gitDirFile) || !fs13.statSync(gitDirFile).isFile()) return "";
30454
+ const first2 = fs13.readFileSync(gitDirFile, "utf8").trim();
30303
30455
  const m = first2.match(/^gitdir:\s*(.+)$/im);
30304
30456
  if (!m) return "";
30305
30457
  const gitWorktreePath = path19.resolve(wt, m[1].trim());
@@ -30313,7 +30465,7 @@ async function gitWorktreeRemoveForce(worktreePath) {
30313
30465
  if (mainRepo) {
30314
30466
  await simpleGit(mainRepo).raw(["worktree", "remove", "--force", worktreePath]);
30315
30467
  } else {
30316
- fs15.rmSync(worktreePath, { recursive: true, force: true });
30468
+ fs14.rmSync(worktreePath, { recursive: true, force: true });
30317
30469
  }
30318
30470
  }
30319
30471
 
@@ -30326,7 +30478,7 @@ async function removeSessionWorktrees(paths, log2) {
30326
30478
  } catch (e) {
30327
30479
  log2(`[worktrees] Remove failed for ${wt}: ${e instanceof Error ? e.message : String(e)}`);
30328
30480
  try {
30329
- fs16.rmSync(wt, { recursive: true, force: true });
30481
+ fs15.rmSync(wt, { recursive: true, force: true });
30330
30482
  } catch {
30331
30483
  }
30332
30484
  }
@@ -30598,7 +30750,7 @@ function forceKillChild(proc, log2, shortId, graceMs) {
30598
30750
  }
30599
30751
 
30600
30752
  // src/dev-servers/process/wire-dev-server-child-process.ts
30601
- import fs17 from "node:fs";
30753
+ import fs16 from "node:fs";
30602
30754
 
30603
30755
  // src/dev-servers/manager/forward-pipe.ts
30604
30756
  function forwardChildPipe(childReadable, terminal, onData) {
@@ -30634,7 +30786,7 @@ function wireDevServerChildProcess(d) {
30634
30786
  d.setPollInterval(void 0);
30635
30787
  return;
30636
30788
  }
30637
- fs17.readFile(d.mergedLogPath, (err, buf) => {
30789
+ fs16.readFile(d.mergedLogPath, (err, buf) => {
30638
30790
  if (err || (d.getSpawnGeneration() ?? 0) !== d.scheduledGen) return;
30639
30791
  if (buf.length <= d.mergedReadPos.value) return;
30640
30792
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
@@ -30672,7 +30824,7 @@ ${errTail}` : ""}`);
30672
30824
  d.sendStatus(code === 0 || code == null ? "stopped" : "error", detail, tails);
30673
30825
  };
30674
30826
  if (mergedPath) {
30675
- fs17.readFile(mergedPath, (err, buf) => {
30827
+ fs16.readFile(mergedPath, (err, buf) => {
30676
30828
  if (!err && buf.length > d.mergedReadPos.value) {
30677
30829
  const chunk = Buffer.from(buf.subarray(d.mergedReadPos.value));
30678
30830
  if (chunk.length > 0) {
@@ -30774,13 +30926,13 @@ function parseDevServerDefs(servers) {
30774
30926
  }
30775
30927
 
30776
30928
  // src/dev-servers/manager/shell-spawn/utils.ts
30777
- import fs18 from "node:fs";
30929
+ import fs17 from "node:fs";
30778
30930
  function isSpawnEbadf(e) {
30779
30931
  return typeof e === "object" && e !== null && "code" in e && e.code === "EBADF";
30780
30932
  }
30781
30933
  function rmDirQuiet(dir) {
30782
30934
  try {
30783
- fs18.rmSync(dir, { recursive: true, force: true });
30935
+ fs17.rmSync(dir, { recursive: true, force: true });
30784
30936
  } catch {
30785
30937
  }
30786
30938
  }
@@ -30788,7 +30940,7 @@ var cachedDevNullReadFd;
30788
30940
  function devNullReadFd() {
30789
30941
  if (cachedDevNullReadFd === void 0) {
30790
30942
  const devPath = process.platform === "win32" ? "nul" : "/dev/null";
30791
- cachedDevNullReadFd = fs18.openSync(devPath, "r");
30943
+ cachedDevNullReadFd = fs17.openSync(devPath, "r");
30792
30944
  }
30793
30945
  return cachedDevNullReadFd;
30794
30946
  }
@@ -30862,15 +31014,15 @@ function trySpawnShellTruePiped(command, env, cwd, devNullFd, signal) {
30862
31014
 
30863
31015
  // src/dev-servers/manager/shell-spawn/try-spawn-merged-log-file.ts
30864
31016
  import { spawn as spawn7 } from "node:child_process";
30865
- import fs19 from "node:fs";
31017
+ import fs18 from "node:fs";
30866
31018
  import { tmpdir } from "node:os";
30867
31019
  import path22 from "node:path";
30868
31020
  function trySpawnMergedLogFile(command, env, cwd, signal) {
30869
- const tmpRoot = fs19.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
31021
+ const tmpRoot = fs18.mkdtempSync(path22.join(tmpdir(), "ba-devsrv-log-"));
30870
31022
  const logPath = path22.join(tmpRoot, "combined.log");
30871
31023
  let logFd;
30872
31024
  try {
30873
- logFd = fs19.openSync(logPath, "a");
31025
+ logFd = fs18.openSync(logPath, "a");
30874
31026
  } catch {
30875
31027
  rmDirQuiet(tmpRoot);
30876
31028
  return null;
@@ -30889,7 +31041,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
30889
31041
  } else {
30890
31042
  proc = spawn7("/bin/sh", ["-c", command], { env, cwd, stdio, ...signal ? { signal } : {} });
30891
31043
  }
30892
- fs19.closeSync(logFd);
31044
+ fs18.closeSync(logFd);
30893
31045
  return {
30894
31046
  proc,
30895
31047
  pipedStdoutStderr: true,
@@ -30898,7 +31050,7 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
30898
31050
  };
30899
31051
  } catch (e) {
30900
31052
  try {
30901
- fs19.closeSync(logFd);
31053
+ fs18.closeSync(logFd);
30902
31054
  } catch {
30903
31055
  }
30904
31056
  rmDirQuiet(tmpRoot);
@@ -30909,22 +31061,22 @@ function trySpawnMergedLogFile(command, env, cwd, signal) {
30909
31061
 
30910
31062
  // src/dev-servers/manager/shell-spawn/try-spawn-shell-script-log-redirect.ts
30911
31063
  import { spawn as spawn8 } from "node:child_process";
30912
- import fs20 from "node:fs";
31064
+ import fs19 from "node:fs";
30913
31065
  import { tmpdir as tmpdir2 } from "node:os";
30914
31066
  import path23 from "node:path";
30915
31067
  function shSingleQuote(s) {
30916
31068
  return `'${s.replace(/'/g, `'\\''`)}'`;
30917
31069
  }
30918
31070
  function trySpawnShellScriptLogRedirectUnix(command, env, cwd, signal) {
30919
- const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
31071
+ const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30920
31072
  const logPath = path23.join(tmpRoot, "combined.log");
30921
31073
  const innerPath = path23.join(tmpRoot, "_cmd.sh");
30922
31074
  const runnerPath = path23.join(tmpRoot, "_run.sh");
30923
31075
  try {
30924
- fs20.writeFileSync(innerPath, `#!/bin/sh
31076
+ fs19.writeFileSync(innerPath, `#!/bin/sh
30925
31077
  ${command}
30926
31078
  `);
30927
- fs20.writeFileSync(
31079
+ fs19.writeFileSync(
30928
31080
  runnerPath,
30929
31081
  `#!/bin/sh
30930
31082
  cd ${shSingleQuote(cwd)}
@@ -30950,13 +31102,13 @@ cd ${shSingleQuote(cwd)}
30950
31102
  }
30951
31103
  }
30952
31104
  function trySpawnShellScriptLogRedirectWin(command, env, cwd, signal) {
30953
- const tmpRoot = fs20.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
31105
+ const tmpRoot = fs19.mkdtempSync(path23.join(tmpdir2(), "ba-devsrv-sh-"));
30954
31106
  const logPath = path23.join(tmpRoot, "combined.log");
30955
31107
  const runnerPath = path23.join(tmpRoot, "_run.bat");
30956
31108
  const q = (p) => `"${p.replace(/"/g, '""')}"`;
30957
31109
  const com = process.env.ComSpec || "cmd.exe";
30958
31110
  try {
30959
- fs20.writeFileSync(
31111
+ fs19.writeFileSync(
30960
31112
  runnerPath,
30961
31113
  `@ECHO OFF\r
30962
31114
  CD /D ${q(cwd)}\r
@@ -31310,7 +31462,9 @@ var DevServerManager = class {
31310
31462
  async shutdownAllGraceful() {
31311
31463
  const pairs = [...this.processes.entries()];
31312
31464
  if (pairs.length === 0) return;
31313
- this.log(`[dev-server] Stopping ${pairs.length} dev server process(es) (bridge shutting down)\u2026`);
31465
+ this.log(
31466
+ `[dev-server] Stopping ${pairs.length} local dev server process${pairs.length === 1 ? "" : "es"}\u2026`
31467
+ );
31314
31468
  await Promise.all(pairs.map(([serverId, proc]) => this.gracefulTerminateOrUnknown(serverId, proc)));
31315
31469
  }
31316
31470
  async gracefulTerminateOrUnknown(serverId, proc) {
@@ -31477,9 +31631,10 @@ var FIREHOSE_CLIENT_PING_MS = 25e3;
31477
31631
  function connectFirehose(options) {
31478
31632
  const { firehoseServerUrl, workspaceId, bridgeName, proxyPorts, log: log2, devServerManager, onOpen, onClose } = options;
31479
31633
  const wsUrl = buildFirehoseCliWsUrl(firehoseServerUrl);
31480
- const wsOptions = { perMessageDeflate: false };
31634
+ applyCliOutboundNetworkPreferences();
31635
+ const wsOptions = { perMessageDeflate: false, family: 4 };
31481
31636
  if (wsUrl.startsWith("wss://")) {
31482
- wsOptions.agent = new https2.Agent({ rejectUnauthorized: false });
31637
+ wsOptions.agent = new https2.Agent({ rejectUnauthorized: false, family: 4 });
31483
31638
  }
31484
31639
  const ws = new wrapper_default(wsUrl, wsOptions);
31485
31640
  let clientPingTimer = null;
@@ -31515,16 +31670,14 @@ function connectFirehose(options) {
31515
31670
  sendWsMessage(ws, { type: "identify", workspaceId, bridgeName, proxyPorts });
31516
31671
  });
31517
31672
  ws.on("message", (raw) => {
31518
- setImmediate(() => {
31519
- if (Buffer.isBuffer(raw) && tryConsumeBinaryProxyBody(raw, deps)) {
31520
- return;
31521
- }
31522
- try {
31523
- const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
31524
- dispatchFirehoseJsonMessage(JSON.parse(text), deps);
31525
- } catch {
31526
- }
31527
- });
31673
+ if (Buffer.isBuffer(raw) && tryConsumeBinaryProxyBody(raw, deps)) {
31674
+ return;
31675
+ }
31676
+ try {
31677
+ const text = Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw);
31678
+ dispatchFirehoseJsonMessage(JSON.parse(text), deps);
31679
+ } catch {
31680
+ }
31528
31681
  });
31529
31682
  ws.on("close", (code, reason) => {
31530
31683
  clearClientPing();
@@ -31570,6 +31723,9 @@ function createOnBridgeIdentified(opts) {
31570
31723
  function attachFirehose(params) {
31571
31724
  state.lastFirehoseParams = params;
31572
31725
  clearFirehoseReconnectTimer();
31726
+ if (state.firehoseReconnectAttempt === 0) {
31727
+ logFn("Connecting to preview tunnel (local HTTP proxy and dev logs)\u2026");
31728
+ }
31573
31729
  state.firehoseGeneration += 1;
31574
31730
  const myGen = state.firehoseGeneration;
31575
31731
  if (state.firehoseHandle) {
@@ -31588,8 +31744,8 @@ function createOnBridgeIdentified(opts) {
31588
31744
  clearFirehoseReconnectQuietOnOpen({ firehoseQuiet: state.firehoseQuiet }, logFn);
31589
31745
  const logOpenAsFirehoseReconnect = state.firehoseReconnectAttempt > 0;
31590
31746
  state.firehoseReconnectAttempt = 0;
31591
- if (logOpenAsFirehoseReconnect) {
31592
- logFn("[Proxy and log service] reconnected.");
31747
+ if (!logOpenAsFirehoseReconnect) {
31748
+ logFn("Connected to preview tunnel (local HTTP proxy and dev logs).");
31593
31749
  }
31594
31750
  },
31595
31751
  onClose: (code, reason) => {
@@ -31678,6 +31834,7 @@ var CHECKS = {
31678
31834
  return false;
31679
31835
  }
31680
31836
  },
31837
+ /** Bridge spawns `@agentclientprotocol/claude-agent-acp` via npx; detection is “Claude toolchain likely present”. */
31681
31838
  "claude-code": async () => {
31682
31839
  try {
31683
31840
  await execFileAsync4("which", ["claude"], { timeout: 4e3 });
@@ -31787,8 +31944,8 @@ async function createBridgeConnection(options) {
31787
31944
  clearMainBridgeReconnectQuietOnOpen(state, logFn);
31788
31945
  state.reconnectAttempt = 0;
31789
31946
  state.logBridgeOpenAsReconnect = false;
31790
- if (logOpenAsPostRefreshReconnect) {
31791
- logFn("[Bridge service] reconnected.");
31947
+ if (!logOpenAsPostRefreshReconnect) {
31948
+ logFn("Connected to bridge service.");
31792
31949
  }
31793
31950
  const socket = getWs();
31794
31951
  if (socket) {
@@ -31827,6 +31984,9 @@ async function createBridgeConnection(options) {
31827
31984
  clearTimeout(state.reconnectTimeout);
31828
31985
  state.reconnectTimeout = null;
31829
31986
  }
31987
+ if (state.reconnectAttempt === 0) {
31988
+ logFn("Connecting to bridge service\u2026");
31989
+ }
31830
31990
  const prev = state.currentWs;
31831
31991
  if (prev) {
31832
31992
  prev.removeAllListeners();
@@ -31899,15 +32059,17 @@ async function runBridge(options) {
31899
32059
  onAuth: (_auth) => {
31900
32060
  }
31901
32061
  });
31902
- const onSignal2 = (signal) => {
31903
- logImmediate(`Received ${signal}; shutting down\u2026`);
32062
+ const onSignal2 = (kind) => {
32063
+ logImmediate(
32064
+ kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
32065
+ );
31904
32066
  setImmediate(() => {
31905
32067
  handle2.close();
31906
32068
  process.exit(0);
31907
32069
  });
31908
32070
  };
31909
- const onSigInt2 = () => onSignal2("SIGINT");
31910
- const onSigTerm2 = () => onSignal2("SIGTERM");
32071
+ const onSigInt2 = () => onSignal2("interrupt");
32072
+ const onSigTerm2 = () => onSignal2("stop");
31911
32073
  process.on("SIGINT", onSigInt2);
31912
32074
  process.on("SIGTERM", onSigTerm2);
31913
32075
  const auth = await handle2.authPromise;
@@ -31956,22 +32118,24 @@ async function runBridge(options) {
31956
32118
  });
31957
32119
  }
31958
32120
  });
31959
- const onSignal = (signal) => {
31960
- logImmediate(`Received ${signal}; shutting down\u2026`);
32121
+ const onSignal = (kind) => {
32122
+ logImmediate(
32123
+ kind === "interrupt" ? "Keyboard interrupt (Ctrl+C) \u2014 stopping\u2026" : "Stop requested \u2014 shutting down\u2026"
32124
+ );
31961
32125
  setImmediate(() => {
31962
32126
  void handle.close().then(() => {
31963
32127
  process.exit(0);
31964
32128
  });
31965
32129
  });
31966
32130
  };
31967
- const onSigInt = () => onSignal("SIGINT");
31968
- const onSigTerm = () => onSignal("SIGTERM");
32131
+ const onSigInt = () => onSignal("interrupt");
32132
+ const onSigTerm = () => onSignal("stop");
31969
32133
  process.on("SIGINT", onSigInt);
31970
32134
  process.on("SIGTERM", onSigTerm);
31971
32135
  }
31972
32136
  export {
31973
32137
  callSkill,
31974
- createAcpClient,
32138
+ createSdkStdioAcpClient as createAcpClient,
31975
32139
  createBridgeConnection,
31976
32140
  createWsBridge,
31977
32141
  getSkill,