@remixhq/claude-plugin 0.1.15 → 0.1.17

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.
@@ -539,6 +539,15 @@ __export(hook_stop_collab_exports, {
539
539
  });
540
540
  module.exports = __toCommonJS(hook_stop_collab_exports);
541
541
 
542
+ // node_modules/@remixhq/core/dist/chunk-GC2MOT3U.js
543
+ var REMIX_ERROR_CODES = {
544
+ REPO_LOCK_HELD: "REPO_LOCK_HELD",
545
+ REPO_LOCK_TIMEOUT: "REPO_LOCK_TIMEOUT",
546
+ REPO_LOCK_STALE_RECOVERED: "REPO_LOCK_STALE_RECOVERED",
547
+ REPO_STATE_CHANGED_DURING_OPERATION: "REPO_STATE_CHANGED_DURING_OPERATION",
548
+ PREFERRED_BRANCH_MISMATCH: "PREFERRED_BRANCH_MISMATCH"
549
+ };
550
+
542
551
  // node_modules/@remixhq/core/dist/chunk-YZ34ICNN.js
543
552
  var RemixError = class extends Error {
544
553
  code;
@@ -553,48 +562,11 @@ var RemixError = class extends Error {
553
562
  }
554
563
  };
555
564
 
556
- // node_modules/@remixhq/core/dist/chunk-GEHSFPCD.js
557
- var import_promises = __toESM(require("fs/promises"), 1);
558
- var import_path = __toESM(require("path"), 1);
559
- function getCollabBindingPath(repoRoot) {
560
- return import_path.default.join(repoRoot, ".remix", "config.json");
561
- }
562
- async function readCollabBinding(repoRoot) {
563
- try {
564
- const raw = await import_promises.default.readFile(getCollabBindingPath(repoRoot), "utf8");
565
- const parsed = JSON.parse(raw);
566
- if (parsed?.schemaVersion !== 1) return null;
567
- if (!parsed.projectId || !parsed.currentAppId || !parsed.upstreamAppId) return null;
568
- return {
569
- schemaVersion: 1,
570
- projectId: parsed.projectId,
571
- currentAppId: parsed.currentAppId,
572
- upstreamAppId: parsed.upstreamAppId,
573
- threadId: parsed.threadId ?? null,
574
- repoFingerprint: parsed.repoFingerprint ?? null,
575
- remoteUrl: parsed.remoteUrl ?? null,
576
- defaultBranch: parsed.defaultBranch ?? null,
577
- preferredBranch: parsed.preferredBranch ?? parsed.defaultBranch ?? null
578
- };
579
- } catch {
580
- return null;
581
- }
582
- }
583
-
584
- // node_modules/@remixhq/core/dist/chunk-GC2MOT3U.js
585
- var REMIX_ERROR_CODES = {
586
- REPO_LOCK_HELD: "REPO_LOCK_HELD",
587
- REPO_LOCK_TIMEOUT: "REPO_LOCK_TIMEOUT",
588
- REPO_LOCK_STALE_RECOVERED: "REPO_LOCK_STALE_RECOVERED",
589
- REPO_STATE_CHANGED_DURING_OPERATION: "REPO_STATE_CHANGED_DURING_OPERATION",
590
- PREFERRED_BRANCH_MISMATCH: "PREFERRED_BRANCH_MISMATCH"
591
- };
592
-
593
- // node_modules/@remixhq/core/dist/chunk-J3J4PBQ7.js
594
- var import_promises13 = __toESM(require("fs/promises"), 1);
565
+ // node_modules/@remixhq/core/dist/chunk-RREREIGW.js
566
+ var import_promises12 = __toESM(require("fs/promises"), 1);
595
567
  var import_crypto = require("crypto");
596
568
  var import_os = __toESM(require("os"), 1);
597
- var import_path2 = __toESM(require("path"), 1);
569
+ var import_path = __toESM(require("path"), 1);
598
570
 
599
571
  // node_modules/is-plain-obj/index.js
600
572
  function isPlainObject(value) {
@@ -1526,7 +1498,7 @@ var npmRunPathEnv = ({ env = import_node_process5.default.env, ...options } = {}
1526
1498
  };
1527
1499
 
1528
1500
  // node_modules/execa/lib/terminate/kill.js
1529
- var import_promises2 = require("timers/promises");
1501
+ var import_promises = require("timers/promises");
1530
1502
 
1531
1503
  // node_modules/execa/lib/return/final-error.js
1532
1504
  var getFinalError = (originalError, message, isSync) => {
@@ -2033,7 +2005,7 @@ var killOnTimeout = async ({ kill, forceKillAfterDelay, context, controllerSigna
2033
2005
  return;
2034
2006
  }
2035
2007
  try {
2036
- await (0, import_promises2.setTimeout)(forceKillAfterDelay, void 0, { signal: controllerSignal });
2008
+ await (0, import_promises.setTimeout)(forceKillAfterDelay, void 0, { signal: controllerSignal });
2037
2009
  if (kill("SIGKILL")) {
2038
2010
  context.isForcefullyTerminated ??= true;
2039
2011
  }
@@ -2064,7 +2036,7 @@ var terminateOnCancel = async (subprocess, cancelSignal, context, { signal }) =>
2064
2036
  };
2065
2037
 
2066
2038
  // node_modules/execa/lib/ipc/graceful.js
2067
- var import_promises4 = require("timers/promises");
2039
+ var import_promises3 = require("timers/promises");
2068
2040
 
2069
2041
  // node_modules/execa/lib/ipc/send.js
2070
2042
  var import_node_util5 = require("util");
@@ -2251,7 +2223,7 @@ var import_node_events4 = require("events");
2251
2223
 
2252
2224
  // node_modules/execa/lib/ipc/incoming.js
2253
2225
  var import_node_events3 = require("events");
2254
- var import_promises3 = require("timers/promises");
2226
+ var import_promises2 = require("timers/promises");
2255
2227
 
2256
2228
  // node_modules/execa/lib/ipc/reference.js
2257
2229
  var addReference = (channel, reference) => {
@@ -2298,7 +2270,7 @@ var onMessage = async ({ anyProcess, channel, isSubprocess, ipcEmitter }, wrappe
2298
2270
  }
2299
2271
  while (incomingMessages.length > 0) {
2300
2272
  await waitForOutgoingMessages(anyProcess, ipcEmitter, wrappedMessage);
2301
- await import_promises3.scheduler.yield();
2273
+ await import_promises2.scheduler.yield();
2302
2274
  const message = await handleStrictRequest({
2303
2275
  wrappedMessage: incomingMessages[0],
2304
2276
  anyProcess,
@@ -2578,7 +2550,7 @@ var startIpc = async ({ anyProcess, channel, isSubprocess, ipc }) => {
2578
2550
  return;
2579
2551
  }
2580
2552
  getIpcEmitter(anyProcess, channel, isSubprocess);
2581
- await import_promises4.scheduler.yield();
2553
+ await import_promises3.scheduler.yield();
2582
2554
  };
2583
2555
  var cancelListening = false;
2584
2556
  var handleAbort = (wrappedMessage) => {
@@ -2651,7 +2623,7 @@ var getReason = ({ reason }) => {
2651
2623
  };
2652
2624
 
2653
2625
  // node_modules/execa/lib/terminate/timeout.js
2654
- var import_promises5 = require("timers/promises");
2626
+ var import_promises4 = require("timers/promises");
2655
2627
  var validateTimeout = ({ timeout }) => {
2656
2628
  if (timeout !== void 0 && (!Number.isFinite(timeout) || timeout < 0)) {
2657
2629
  throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`);
@@ -2659,7 +2631,7 @@ var validateTimeout = ({ timeout }) => {
2659
2631
  };
2660
2632
  var throwOnTimeout = (subprocess, timeout, context, controller) => timeout === 0 || timeout === void 0 ? [] : [killAfterTimeout(subprocess, timeout, context, controller)];
2661
2633
  var killAfterTimeout = async (subprocess, timeout, context, { signal }) => {
2662
- await (0, import_promises5.setTimeout)(timeout, void 0, { signal });
2634
+ await (0, import_promises4.setTimeout)(timeout, void 0, { signal });
2663
2635
  context.terminationReason ??= "timeout";
2664
2636
  subprocess.kill();
2665
2637
  throw new DiscardedError();
@@ -2916,7 +2888,7 @@ var CR_BINARY = CR.codePointAt(0);
2916
2888
 
2917
2889
  // node_modules/get-stream/source/index.js
2918
2890
  var import_node_events6 = require("events");
2919
- var import_promises6 = require("stream/promises");
2891
+ var import_promises5 = require("stream/promises");
2920
2892
 
2921
2893
  // node_modules/is-stream/index.js
2922
2894
  function isStream(stream, { checkOpen = true } = {}) {
@@ -3282,7 +3254,7 @@ var stringMethods = {
3282
3254
  };
3283
3255
 
3284
3256
  // node_modules/get-stream/source/index.js
3285
- Object.assign(nodeImports, { on: import_node_events6.on, finished: import_promises6.finished });
3257
+ Object.assign(nodeImports, { on: import_node_events6.on, finished: import_promises5.finished });
3286
3258
 
3287
3259
  // node_modules/execa/lib/io/max-buffer.js
3288
3260
  var handleMaxBuffer = ({ error, stream, readableObjectMode, lines, encoding, fdNumber }) => {
@@ -5474,7 +5446,7 @@ var addPropertiesAsync = {
5474
5446
  // node_modules/@sindresorhus/merge-streams/index.js
5475
5447
  var import_node_events10 = require("events");
5476
5448
  var import_node_stream4 = require("stream");
5477
- var import_promises7 = require("stream/promises");
5449
+ var import_promises6 = require("stream/promises");
5478
5450
  function mergeStreams(streams) {
5479
5451
  if (!Array.isArray(streams)) {
5480
5452
  throw new TypeError(`Expected an array, got \`${typeof streams}\`.`);
@@ -5557,7 +5529,7 @@ var onMergedStreamFinished = async (passThroughStream, streams, unpipeEvent) =>
5557
5529
  };
5558
5530
  var onMergedStreamEnd = async (passThroughStream, { signal }) => {
5559
5531
  try {
5560
- await (0, import_promises7.finished)(passThroughStream, { signal, cleanup: true });
5532
+ await (0, import_promises6.finished)(passThroughStream, { signal, cleanup: true });
5561
5533
  } catch (error) {
5562
5534
  errorOrAbortStream(passThroughStream, error);
5563
5535
  throw error;
@@ -5624,7 +5596,7 @@ var afterMergedStreamFinished = async (onFinished, stream, { signal }) => {
5624
5596
  };
5625
5597
  var onInputStreamEnd = async ({ passThroughStream, stream, streams, ended, aborted: aborted2, controller: { signal } }) => {
5626
5598
  try {
5627
- await (0, import_promises7.finished)(stream, {
5599
+ await (0, import_promises6.finished)(stream, {
5628
5600
  signal,
5629
5601
  cleanup: true,
5630
5602
  readable: true,
@@ -5689,7 +5661,7 @@ var PASSTHROUGH_LISTENERS_COUNT = 2;
5689
5661
  var PASSTHROUGH_LISTENERS_PER_STREAM = 1;
5690
5662
 
5691
5663
  // node_modules/execa/lib/io/pipeline.js
5692
- var import_promises8 = require("stream/promises");
5664
+ var import_promises7 = require("stream/promises");
5693
5665
  var pipeStreams = (source, destination) => {
5694
5666
  source.pipe(destination);
5695
5667
  onSourceFinish(source, destination);
@@ -5700,7 +5672,7 @@ var onSourceFinish = async (source, destination) => {
5700
5672
  return;
5701
5673
  }
5702
5674
  try {
5703
- await (0, import_promises8.finished)(source, { cleanup: true, readable: true, writable: false });
5675
+ await (0, import_promises7.finished)(source, { cleanup: true, readable: true, writable: false });
5704
5676
  } catch {
5705
5677
  }
5706
5678
  endDestinationStream(destination);
@@ -5715,7 +5687,7 @@ var onDestinationFinish = async (source, destination) => {
5715
5687
  return;
5716
5688
  }
5717
5689
  try {
5718
- await (0, import_promises8.finished)(destination, { cleanup: true, readable: false, writable: true });
5690
+ await (0, import_promises7.finished)(destination, { cleanup: true, readable: false, writable: true });
5719
5691
  } catch {
5720
5692
  }
5721
5693
  abortSourceStream(source);
@@ -6186,7 +6158,7 @@ var waitForBothSubprocesses = async (subprocessPromises) => {
6186
6158
  };
6187
6159
 
6188
6160
  // node_modules/execa/lib/pipe/streaming.js
6189
- var import_promises9 = require("stream/promises");
6161
+ var import_promises8 = require("stream/promises");
6190
6162
  var pipeSubprocessStream = (sourceStream, destinationStream, maxListenersController) => {
6191
6163
  const mergedStream = MERGED_STREAMS.has(destinationStream) ? pipeMoreSubprocessStream(sourceStream, destinationStream) : pipeFirstSubprocessStream(sourceStream, destinationStream);
6192
6164
  incrementMaxListeners(sourceStream, SOURCE_LISTENERS_PER_PIPE, maxListenersController.signal);
@@ -6207,7 +6179,7 @@ var pipeMoreSubprocessStream = (sourceStream, destinationStream) => {
6207
6179
  };
6208
6180
  var cleanupMergedStreamsMap = async (destinationStream) => {
6209
6181
  try {
6210
- await (0, import_promises9.finished)(destinationStream, { cleanup: true, readable: false, writable: true });
6182
+ await (0, import_promises8.finished)(destinationStream, { cleanup: true, readable: false, writable: true });
6211
6183
  } catch {
6212
6184
  }
6213
6185
  MERGED_STREAMS.delete(destinationStream);
@@ -6291,7 +6263,7 @@ var handlePipePromise = async ({
6291
6263
  var getSubprocessPromises = (sourcePromise, destination) => Promise.allSettled([sourcePromise, destination]);
6292
6264
 
6293
6265
  // node_modules/execa/lib/io/contents.js
6294
- var import_promises10 = require("timers/promises");
6266
+ var import_promises9 = require("timers/promises");
6295
6267
 
6296
6268
  // node_modules/execa/lib/io/iterate.js
6297
6269
  var import_node_events12 = require("events");
@@ -6443,7 +6415,7 @@ var logOutputAsync = async ({ stream, onStreamEnd, fdNumber, encoding, allMixed,
6443
6415
  await logLines(linesIterable, stream, fdNumber, verboseInfo);
6444
6416
  };
6445
6417
  var resumeStream = async (stream) => {
6446
- await (0, import_promises10.setImmediate)();
6418
+ await (0, import_promises9.setImmediate)();
6447
6419
  if (stream.readableFlowing === null) {
6448
6420
  stream.resume();
6449
6421
  }
@@ -6478,14 +6450,14 @@ var getBufferedData = async (streamPromise) => {
6478
6450
  var handleBufferedData = ({ bufferedData }) => isArrayBuffer(bufferedData) ? new Uint8Array(bufferedData) : bufferedData;
6479
6451
 
6480
6452
  // node_modules/execa/lib/resolve/wait-stream.js
6481
- var import_promises11 = require("stream/promises");
6453
+ var import_promises10 = require("stream/promises");
6482
6454
  var waitForStream = async (stream, fdNumber, streamInfo, { isSameDirection, stopOnExit = false } = {}) => {
6483
6455
  const state = handleStdinDestroy(stream, streamInfo);
6484
6456
  const abortController = new AbortController();
6485
6457
  try {
6486
6458
  await Promise.race([
6487
6459
  ...stopOnExit ? [streamInfo.exitPromise] : [],
6488
- (0, import_promises11.finished)(stream, { cleanup: true, signal: abortController.signal })
6460
+ (0, import_promises10.finished)(stream, { cleanup: true, signal: abortController.signal })
6489
6461
  ]);
6490
6462
  } catch (error) {
6491
6463
  if (!state.stdinCleanedUp) {
@@ -6799,7 +6771,7 @@ var import_node_stream6 = require("stream");
6799
6771
  var import_node_util9 = require("util");
6800
6772
 
6801
6773
  // node_modules/execa/lib/convert/shared.js
6802
- var import_promises12 = require("stream/promises");
6774
+ var import_promises11 = require("stream/promises");
6803
6775
  var safeWaitForSubprocessStdin = async (subprocessStdin) => {
6804
6776
  if (subprocessStdin === void 0) {
6805
6777
  return;
@@ -6819,10 +6791,10 @@ var safeWaitForSubprocessStdout = async (subprocessStdout) => {
6819
6791
  }
6820
6792
  };
6821
6793
  var waitForSubprocessStdin = async (subprocessStdin) => {
6822
- await (0, import_promises12.finished)(subprocessStdin, { cleanup: true, readable: false, writable: true });
6794
+ await (0, import_promises11.finished)(subprocessStdin, { cleanup: true, readable: false, writable: true });
6823
6795
  };
6824
6796
  var waitForSubprocessStdout = async (subprocessStdout) => {
6825
- await (0, import_promises12.finished)(subprocessStdout, { cleanup: true, readable: true, writable: false });
6797
+ await (0, import_promises11.finished)(subprocessStdout, { cleanup: true, readable: true, writable: false });
6826
6798
  };
6827
6799
  var waitForSubprocess = async (subprocess, error) => {
6828
6800
  await subprocess;
@@ -7371,7 +7343,7 @@ var {
7371
7343
  getCancelSignal: getCancelSignal2
7372
7344
  } = getIpcExport();
7373
7345
 
7374
- // node_modules/@remixhq/core/dist/chunk-J3J4PBQ7.js
7346
+ // node_modules/@remixhq/core/dist/chunk-RREREIGW.js
7375
7347
  async function runGit(args, cwd) {
7376
7348
  const res = await execa("git", args, { cwd, stderr: "ignore" });
7377
7349
  return String(res.stdout || "").trim();
@@ -7393,8 +7365,8 @@ async function runGitDetailed(args, cwd) {
7393
7365
  };
7394
7366
  }
7395
7367
  function normalizeRepoRelativePath(value) {
7396
- const normalized = import_path2.default.posix.normalize(value.replace(/\\/g, "/").trim());
7397
- if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../") || import_path2.default.posix.isAbsolute(normalized)) {
7368
+ const normalized = import_path.default.posix.normalize(value.replace(/\\/g, "/").trim());
7369
+ if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../") || import_path.default.posix.isAbsolute(normalized)) {
7398
7370
  throw new RemixError("Git returned an invalid repository-relative path.", {
7399
7371
  exitCode: 1,
7400
7372
  hint: `Path: ${value}`
@@ -7403,11 +7375,11 @@ function normalizeRepoRelativePath(value) {
7403
7375
  return normalized;
7404
7376
  }
7405
7377
  function resolveRepoRelativePath(repoRoot, relativePath) {
7406
- return import_path2.default.resolve(repoRoot, ...relativePath.split("/"));
7378
+ return import_path.default.resolve(repoRoot, ...relativePath.split("/"));
7407
7379
  }
7408
7380
  function isWithinRepoRoot(repoRoot, targetPath) {
7409
- const relative = import_path2.default.relative(repoRoot, targetPath);
7410
- return relative === "" || !relative.startsWith("..") && !import_path2.default.isAbsolute(relative);
7381
+ const relative = import_path.default.relative(repoRoot, targetPath);
7382
+ return relative === "" || !relative.startsWith("..") && !import_path.default.isAbsolute(relative);
7411
7383
  }
7412
7384
  function sha256Hex(value) {
7413
7385
  return (0, import_crypto.createHash)("sha256").update(value).digest("hex");
@@ -7417,7 +7389,7 @@ function resolveGitReportedPath(cwd, reportedPath) {
7417
7389
  if (!value) {
7418
7390
  throw new RemixError("Git returned an empty internal path.", { exitCode: 1 });
7419
7391
  }
7420
- return import_path2.default.isAbsolute(value) ? value : import_path2.default.resolve(cwd, value);
7392
+ return import_path.default.isAbsolute(value) ? value : import_path.default.resolve(cwd, value);
7421
7393
  }
7422
7394
  function parseGitNameStatusZ(stdout) {
7423
7395
  const tokens = stdout.split("\0");
@@ -7488,12 +7460,12 @@ function classifyGitApplyFailure(detail) {
7488
7460
  return "unknown";
7489
7461
  }
7490
7462
  async function pruneEmptyParentDirectories(repoRoot, filePath) {
7491
- let current = import_path2.default.dirname(filePath);
7463
+ let current = import_path.default.dirname(filePath);
7492
7464
  while (current !== repoRoot && isWithinRepoRoot(repoRoot, current)) {
7493
- const entries = await import_promises13.default.readdir(current).catch(() => null);
7465
+ const entries = await import_promises12.default.readdir(current).catch(() => null);
7494
7466
  if (!entries || entries.length > 0) return;
7495
- await import_promises13.default.rmdir(current).catch(() => void 0);
7496
- current = import_path2.default.dirname(current);
7467
+ await import_promises12.default.rmdir(current).catch(() => void 0);
7468
+ current = import_path.default.dirname(current);
7497
7469
  }
7498
7470
  }
7499
7471
  async function findGitRoot(startDir) {
@@ -7548,8 +7520,8 @@ async function getWorkspaceSnapshot(cwd) {
7548
7520
  throw new RemixError("Failed to resolve local HEAD commit.", { exitCode: 1 });
7549
7521
  }
7550
7522
  const includedUntrackedPaths = Array.from(new Set((await listUntrackedFiles(cwd)).map((entry) => normalizeRepoRelativePath(entry))));
7551
- const tempDir = await import_promises13.default.mkdtemp(import_path2.default.join(import_os.default.tmpdir(), "remix-index-"));
7552
- const tempIndexPath = import_path2.default.join(tempDir, "index");
7523
+ const tempDir = await import_promises12.default.mkdtemp(import_path.default.join(import_os.default.tmpdir(), "remix-index-"));
7524
+ const tempIndexPath = import_path.default.join(tempDir, "index");
7553
7525
  const env = { ...process.env, GIT_INDEX_FILE: tempIndexPath };
7554
7526
  try {
7555
7527
  try {
@@ -7572,14 +7544,14 @@ async function getWorkspaceSnapshot(cwd) {
7572
7544
  });
7573
7545
  }
7574
7546
  } finally {
7575
- await import_promises13.default.rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
7547
+ await import_promises12.default.rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
7576
7548
  }
7577
7549
  }
7578
7550
  async function writeTempUnifiedDiffBackup(diff, prefix = "remix-add-backup") {
7579
7551
  const safePrefix = prefix.replace(/[^a-zA-Z0-9._-]+/g, "-") || "remix-add-backup";
7580
- const tmpDir = await import_promises13.default.mkdtemp(import_path2.default.join(import_os.default.tmpdir(), `${safePrefix}-`));
7581
- const backupPath = import_path2.default.join(tmpDir, "submitted.diff");
7582
- await import_promises13.default.writeFile(backupPath, diff, "utf8");
7552
+ const tmpDir = await import_promises12.default.mkdtemp(import_path.default.join(import_os.default.tmpdir(), `${safePrefix}-`));
7553
+ const backupPath = import_path.default.join(tmpDir, "submitted.diff");
7554
+ await import_promises12.default.writeFile(backupPath, diff, "utf8");
7583
7555
  return { backupPath, diffSha256: sha256Hex(diff) };
7584
7556
  }
7585
7557
  async function validateUnifiedDiff(cwd, diff) {
@@ -7596,7 +7568,7 @@ async function validateUnifiedDiff(cwd, diff) {
7596
7568
  detail
7597
7569
  };
7598
7570
  } finally {
7599
- await import_promises13.default.rm(import_path2.default.dirname(backupPath), { recursive: true, force: true }).catch(() => void 0);
7571
+ await import_promises12.default.rm(import_path.default.dirname(backupPath), { recursive: true, force: true }).catch(() => void 0);
7600
7572
  }
7601
7573
  }
7602
7574
  async function preserveWorkspaceChanges(cwd, prefix = "remix-add-preserve") {
@@ -7616,7 +7588,7 @@ async function preserveWorkspaceChanges(cwd, prefix = "remix-add-preserve") {
7616
7588
  }
7617
7589
  async function reapplyPreservedWorkspaceChanges(cwd, preserved, operation = "`remix collab add`") {
7618
7590
  const patchFilePath = preserved.preservedDiffPath;
7619
- const tempPatchHash = await import_promises13.default.readFile(patchFilePath, "utf8").then((content) => sha256Hex(content)).catch(() => null);
7591
+ const tempPatchHash = await import_promises12.default.readFile(patchFilePath, "utf8").then((content) => sha256Hex(content)).catch(() => null);
7620
7592
  if (!tempPatchHash) {
7621
7593
  return { status: "failed", detail: "Preserved diff artifact is missing." };
7622
7594
  }
@@ -7748,7 +7720,7 @@ async function discardCapturedUntrackedChanges(repoRoot, capturedPaths) {
7748
7720
  hint: `Path: ${relativePath}`
7749
7721
  });
7750
7722
  }
7751
- await import_promises13.default.rm(absolutePath, { recursive: true, force: true }).catch((err) => {
7723
+ await import_promises12.default.rm(absolutePath, { recursive: true, force: true }).catch((err) => {
7752
7724
  throw new RemixError("Failed to remove a captured untracked path before syncing.", {
7753
7725
  exitCode: 1,
7754
7726
  hint: err instanceof Error ? `${relativePath}
@@ -7823,43 +7795,319 @@ function summarizeUnifiedDiff(diff) {
7823
7795
  return { changedFilesCount, insertions, deletions };
7824
7796
  }
7825
7797
 
7826
- // node_modules/@remixhq/core/dist/collab.js
7798
+ // node_modules/@remixhq/core/dist/chunk-IXWQWFYT.js
7799
+ var import_promises13 = __toESM(require("fs/promises"), 1);
7800
+ var import_path2 = __toESM(require("path"), 1);
7827
7801
  var import_promises14 = __toESM(require("fs/promises"), 1);
7828
7802
  var import_path3 = __toESM(require("path"), 1);
7829
- var import_crypto2 = require("crypto");
7803
+ async function writeJsonAtomic(filePath, value) {
7804
+ const dir = import_path3.default.dirname(filePath);
7805
+ await import_promises14.default.mkdir(dir, { recursive: true });
7806
+ const tmp = `${filePath}.tmp-${Date.now()}`;
7807
+ await import_promises14.default.writeFile(tmp, `${JSON.stringify(value, null, 2)}
7808
+ `, "utf8");
7809
+ await import_promises14.default.rename(tmp, filePath);
7810
+ }
7811
+ function getCollabBindingPath(repoRoot) {
7812
+ return import_path2.default.join(repoRoot, ".remix", "config.json");
7813
+ }
7814
+ function buildBindingFileV3(params) {
7815
+ return {
7816
+ schemaVersion: 3,
7817
+ repoFingerprint: params.repoFingerprint,
7818
+ remoteUrl: params.remoteUrl,
7819
+ defaultBranch: params.defaultBranch,
7820
+ branchBindings: params.branchBindings,
7821
+ ...params.explicitRootBinding ? { explicitRootBinding: params.explicitRootBinding } : {}
7822
+ };
7823
+ }
7824
+ function normalizeBranchName(value) {
7825
+ const normalized = String(value ?? "").trim();
7826
+ return normalized || null;
7827
+ }
7828
+ function normalizeProjectId(value) {
7829
+ const normalized = String(value ?? "").trim();
7830
+ return normalized || null;
7831
+ }
7832
+ function normalizeBranchBinding(value) {
7833
+ if (!value?.currentAppId || !value?.upstreamAppId) return null;
7834
+ return {
7835
+ projectId: normalizeProjectId(value.projectId),
7836
+ currentAppId: value.currentAppId,
7837
+ upstreamAppId: value.upstreamAppId,
7838
+ threadId: value.threadId ?? null,
7839
+ laneId: value.laneId ?? null,
7840
+ bindingMode: value.bindingMode === "legacy" ? "legacy" : value.bindingMode === "explicit_root" ? "explicit_root" : "lane"
7841
+ };
7842
+ }
7843
+ function buildResolvedBinding(params) {
7844
+ if (!params.binding) return null;
7845
+ return {
7846
+ schemaVersion: 3,
7847
+ projectId: params.binding.projectId ?? params.fallbackProjectId,
7848
+ currentAppId: params.binding.currentAppId,
7849
+ upstreamAppId: params.binding.upstreamAppId,
7850
+ threadId: params.binding.threadId,
7851
+ repoFingerprint: params.repoFingerprint,
7852
+ remoteUrl: params.remoteUrl,
7853
+ defaultBranch: params.defaultBranch,
7854
+ laneId: params.binding.laneId,
7855
+ branchName: params.branchName,
7856
+ bindingMode: params.binding.bindingMode
7857
+ };
7858
+ }
7859
+ function deriveFallbackProjectId(params) {
7860
+ const candidates = [
7861
+ params.currentBranch ? params.branchBindings[params.currentBranch]?.projectId ?? null : null,
7862
+ params.defaultBranch ? params.branchBindings[params.defaultBranch]?.projectId ?? null : null,
7863
+ ...Object.values(params.branchBindings).map((binding) => binding.projectId),
7864
+ params.legacyProjectId
7865
+ ];
7866
+ for (const candidate of candidates) {
7867
+ if (candidate) return candidate;
7868
+ }
7869
+ return null;
7870
+ }
7871
+ async function readCollabBindingState(repoRoot, options) {
7872
+ try {
7873
+ const persist = options?.persist === true;
7874
+ const filePath = getCollabBindingPath(repoRoot);
7875
+ const raw = await import_promises13.default.readFile(filePath, "utf8");
7876
+ const parsed = JSON.parse(raw);
7877
+ if (!parsed || typeof parsed !== "object") return null;
7878
+ const currentBranch = normalizeBranchName(await getCurrentBranch(repoRoot).catch(() => null));
7879
+ if (parsed.schemaVersion === 1) {
7880
+ if (!parsed.currentAppId || !parsed.upstreamAppId) return null;
7881
+ const projectId = normalizeProjectId(parsed.projectId);
7882
+ const preferredBranch = normalizeBranchName(parsed.preferredBranch ?? parsed.defaultBranch ?? null);
7883
+ const branchKey = preferredBranch ?? currentBranch ?? null;
7884
+ const branchBindings2 = branchKey ? {
7885
+ [branchKey]: {
7886
+ projectId,
7887
+ currentAppId: parsed.currentAppId,
7888
+ upstreamAppId: parsed.upstreamAppId,
7889
+ threadId: parsed.threadId ?? null,
7890
+ laneId: null,
7891
+ bindingMode: "legacy"
7892
+ }
7893
+ } : {};
7894
+ const migratedFile = buildBindingFileV3({
7895
+ repoFingerprint: parsed.repoFingerprint ?? null,
7896
+ remoteUrl: parsed.remoteUrl ?? null,
7897
+ defaultBranch: parsed.defaultBranch ?? null,
7898
+ branchBindings: branchBindings2
7899
+ });
7900
+ if (persist) {
7901
+ try {
7902
+ await writeJsonAtomic(filePath, migratedFile);
7903
+ } catch {
7904
+ }
7905
+ }
7906
+ return {
7907
+ schemaVersion: 3,
7908
+ projectId,
7909
+ repoFingerprint: migratedFile.repoFingerprint,
7910
+ remoteUrl: migratedFile.remoteUrl,
7911
+ defaultBranch: migratedFile.defaultBranch,
7912
+ currentBranch,
7913
+ branchBindings: migratedFile.branchBindings,
7914
+ explicitRootBinding: null,
7915
+ binding: buildResolvedBinding({
7916
+ fallbackProjectId: projectId,
7917
+ repoFingerprint: migratedFile.repoFingerprint,
7918
+ remoteUrl: migratedFile.remoteUrl,
7919
+ defaultBranch: migratedFile.defaultBranch,
7920
+ branchName: branchKey,
7921
+ binding: branchKey ? migratedFile.branchBindings[branchKey] : null
7922
+ })
7923
+ };
7924
+ }
7925
+ if (parsed.schemaVersion !== 2 && parsed.schemaVersion !== 3) return null;
7926
+ const file = parsed;
7927
+ let shouldPersistNormalizedBranchBindings = false;
7928
+ const legacyProjectId = normalizeProjectId(file.projectId);
7929
+ const branchBindings = Object.fromEntries(
7930
+ Object.entries(file.branchBindings ?? {}).map(([branchName, branchBinding]) => {
7931
+ const normalized = normalizeBranchBinding(branchBinding);
7932
+ const rawProjectId = branchBinding && typeof branchBinding === "object" && "projectId" in branchBinding ? normalizeProjectId(branchBinding.projectId) : null;
7933
+ const rawBindingMode = branchBinding && typeof branchBinding === "object" && "bindingMode" in branchBinding ? branchBinding.bindingMode : null;
7934
+ const hasLegacyPreferredBranch = Boolean(branchBinding) && typeof branchBinding === "object" && "preferredBranch" in branchBinding;
7935
+ let normalizedWithProject = normalized;
7936
+ if (normalizedWithProject && !normalizedWithProject.projectId && legacyProjectId) {
7937
+ normalizedWithProject = {
7938
+ ...normalizedWithProject,
7939
+ projectId: legacyProjectId
7940
+ };
7941
+ }
7942
+ if (normalizedWithProject && (rawBindingMode !== normalizedWithProject.bindingMode || hasLegacyPreferredBranch || rawProjectId !== normalizedWithProject.projectId)) {
7943
+ shouldPersistNormalizedBranchBindings = true;
7944
+ }
7945
+ return [branchName, normalizedWithProject];
7946
+ }).filter((entry) => Boolean(entry[1]))
7947
+ );
7948
+ const legacyExplicitBinding = normalizeBranchBinding(file.explicitBinding ?? null);
7949
+ const legacyExplicitBranch = currentBranch ?? normalizeBranchName(file.defaultBranch);
7950
+ if (legacyExplicitBinding && legacyExplicitBranch) {
7951
+ branchBindings[legacyExplicitBranch] = {
7952
+ ...legacyExplicitBinding,
7953
+ projectId: legacyExplicitBinding.projectId ?? branchBindings[legacyExplicitBranch]?.projectId ?? legacyProjectId,
7954
+ bindingMode: "lane"
7955
+ };
7956
+ shouldPersistNormalizedBranchBindings = true;
7957
+ }
7958
+ let explicitRootBinding = normalizeBranchBinding(file.explicitRootBinding ?? null);
7959
+ if (explicitRootBinding && !explicitRootBinding.projectId && legacyProjectId) {
7960
+ explicitRootBinding = {
7961
+ ...explicitRootBinding,
7962
+ projectId: legacyProjectId
7963
+ };
7964
+ shouldPersistNormalizedBranchBindings = true;
7965
+ }
7966
+ if (explicitRootBinding && explicitRootBinding.bindingMode !== "explicit_root") {
7967
+ explicitRootBinding = {
7968
+ ...explicitRootBinding,
7969
+ bindingMode: "explicit_root"
7970
+ };
7971
+ shouldPersistNormalizedBranchBindings = true;
7972
+ }
7973
+ if (persist && ("explicitBinding" in file || "explicitRootBinding" in file || shouldPersistNormalizedBranchBindings || parsed.schemaVersion === 2)) {
7974
+ try {
7975
+ await writeJsonAtomic(
7976
+ filePath,
7977
+ buildBindingFileV3({
7978
+ repoFingerprint: file.repoFingerprint ?? null,
7979
+ remoteUrl: file.remoteUrl ?? null,
7980
+ defaultBranch: file.defaultBranch ?? null,
7981
+ branchBindings,
7982
+ explicitRootBinding
7983
+ })
7984
+ );
7985
+ } catch {
7986
+ }
7987
+ }
7988
+ const resolvedBranch = currentBranch ?? normalizeBranchName(file.defaultBranch);
7989
+ const fallbackProjectId = deriveFallbackProjectId({
7990
+ branchBindings,
7991
+ currentBranch: resolvedBranch,
7992
+ defaultBranch: normalizeBranchName(file.defaultBranch),
7993
+ legacyProjectId: explicitRootBinding?.projectId ?? legacyProjectId
7994
+ });
7995
+ const resolvedBinding = buildResolvedBinding({
7996
+ fallbackProjectId,
7997
+ repoFingerprint: file.repoFingerprint ?? null,
7998
+ remoteUrl: file.remoteUrl ?? null,
7999
+ defaultBranch: file.defaultBranch ?? null,
8000
+ branchName: resolvedBranch,
8001
+ binding: resolvedBranch ? branchBindings[resolvedBranch] ?? null : null
8002
+ }) ?? (resolvedBranch && resolvedBranch === normalizeBranchName(file.defaultBranch) && explicitRootBinding ? buildResolvedBinding({
8003
+ fallbackProjectId,
8004
+ repoFingerprint: file.repoFingerprint ?? null,
8005
+ remoteUrl: file.remoteUrl ?? null,
8006
+ defaultBranch: file.defaultBranch ?? null,
8007
+ branchName: normalizeBranchName(file.defaultBranch),
8008
+ binding: explicitRootBinding
8009
+ }) : null);
8010
+ return {
8011
+ schemaVersion: parsed.schemaVersion,
8012
+ projectId: fallbackProjectId,
8013
+ repoFingerprint: file.repoFingerprint ?? null,
8014
+ remoteUrl: file.remoteUrl ?? null,
8015
+ defaultBranch: file.defaultBranch ?? null,
8016
+ currentBranch,
8017
+ branchBindings,
8018
+ explicitRootBinding: buildResolvedBinding({
8019
+ fallbackProjectId,
8020
+ repoFingerprint: file.repoFingerprint ?? null,
8021
+ remoteUrl: file.remoteUrl ?? null,
8022
+ defaultBranch: file.defaultBranch ?? null,
8023
+ branchName: normalizeBranchName(file.defaultBranch),
8024
+ binding: explicitRootBinding
8025
+ }),
8026
+ binding: resolvedBinding
8027
+ };
8028
+ } catch {
8029
+ return null;
8030
+ }
8031
+ }
8032
+ async function readCollabBinding(repoRoot) {
8033
+ const state = await readCollabBindingState(repoRoot);
8034
+ return state?.binding ?? null;
8035
+ }
8036
+ async function writeCollabBinding(repoRoot, binding) {
8037
+ const filePath = getCollabBindingPath(repoRoot);
8038
+ const currentBranch = normalizeBranchName(await getCurrentBranch(repoRoot).catch(() => null));
8039
+ const branchName = normalizeBranchName(binding.branchName) ?? currentBranch ?? binding.defaultBranch ?? "main";
8040
+ const existing = await readCollabBindingState(repoRoot, { persist: true });
8041
+ const branchBindings = { ...existing?.branchBindings ?? {} };
8042
+ branchBindings[branchName] = {
8043
+ projectId: normalizeProjectId(binding.projectId) ?? branchBindings[branchName]?.projectId ?? existing?.projectId ?? null,
8044
+ currentAppId: binding.currentAppId,
8045
+ upstreamAppId: binding.upstreamAppId,
8046
+ threadId: binding.threadId ?? null,
8047
+ laneId: binding.laneId ?? null,
8048
+ bindingMode: binding.bindingMode ?? "lane"
8049
+ };
8050
+ const explicitRootBinding = binding.bindingMode === "explicit_root" ? {
8051
+ ...branchBindings[branchName],
8052
+ bindingMode: "explicit_root"
8053
+ } : existing?.explicitRootBinding ? {
8054
+ projectId: existing.explicitRootBinding.projectId,
8055
+ currentAppId: existing.explicitRootBinding.currentAppId,
8056
+ upstreamAppId: existing.explicitRootBinding.upstreamAppId,
8057
+ threadId: existing.explicitRootBinding.threadId,
8058
+ laneId: existing.explicitRootBinding.laneId,
8059
+ bindingMode: "explicit_root"
8060
+ } : null;
8061
+ await writeJsonAtomic(
8062
+ filePath,
8063
+ buildBindingFileV3({
8064
+ repoFingerprint: binding.repoFingerprint ?? null,
8065
+ remoteUrl: binding.remoteUrl ?? null,
8066
+ defaultBranch: binding.defaultBranch ?? null,
8067
+ branchBindings,
8068
+ explicitRootBinding
8069
+ })
8070
+ );
8071
+ return filePath;
8072
+ }
8073
+
8074
+ // node_modules/@remixhq/core/dist/collab.js
7830
8075
  var import_promises15 = __toESM(require("fs/promises"), 1);
7831
- var import_os2 = __toESM(require("os"), 1);
7832
8076
  var import_path4 = __toESM(require("path"), 1);
8077
+ var import_crypto2 = require("crypto");
7833
8078
  var import_promises16 = __toESM(require("fs/promises"), 1);
7834
- var import_os3 = __toESM(require("os"), 1);
8079
+ var import_os2 = __toESM(require("os"), 1);
7835
8080
  var import_path5 = __toESM(require("path"), 1);
8081
+ var import_promises17 = __toESM(require("fs/promises"), 1);
8082
+ var import_os3 = __toESM(require("os"), 1);
8083
+ var import_path6 = __toESM(require("path"), 1);
7836
8084
  function describeBranch(value) {
7837
8085
  const normalized = String(value ?? "").trim();
7838
8086
  return normalized || "(detached)";
7839
8087
  }
7840
- function isPreferredBranchMatch(currentBranch, preferredBranch) {
8088
+ function isBoundBranchMatch(currentBranch, branchName) {
7841
8089
  const current = String(currentBranch ?? "").trim();
7842
- const preferred = String(preferredBranch ?? "").trim();
7843
- if (!preferred || !current) return true;
7844
- return current === preferred;
8090
+ const expected = String(branchName ?? "").trim();
8091
+ if (!expected || !current) return true;
8092
+ return current === expected;
7845
8093
  }
7846
- function buildPreferredBranchMismatchHint(params) {
8094
+ function buildBranchMismatchHint(params) {
7847
8095
  const overrideFlag = params.overrideFlag?.trim() || "--allow-branch-mismatch";
7848
8096
  return [
7849
8097
  `Current branch: ${describeBranch(params.currentBranch)}`,
7850
- `Preferred branch: ${describeBranch(params.preferredBranch)}`,
7851
- `Switch to ${describeBranch(params.preferredBranch)} or rerun with ${overrideFlag} if this is intentional.`
8098
+ `Bound branch: ${describeBranch(params.branchName)}`,
8099
+ `Switch to ${describeBranch(params.branchName)} or rerun with ${overrideFlag} if this is intentional.`
7852
8100
  ].join("\n");
7853
8101
  }
7854
- function assertPreferredBranchMatch(params) {
8102
+ function assertBoundBranchMatch(params) {
7855
8103
  if (params.allowBranchMismatch) return;
7856
- if (isPreferredBranchMatch(params.currentBranch, params.preferredBranch)) return;
7857
- throw new RemixError(`Current branch does not match this checkout's Remix preferred branch while running ${params.operation}.`, {
8104
+ if (isBoundBranchMatch(params.currentBranch, params.branchName)) return;
8105
+ throw new RemixError(`Current branch does not match this checkout's bound Remix branch while running ${params.operation}.`, {
7858
8106
  code: REMIX_ERROR_CODES.PREFERRED_BRANCH_MISMATCH,
7859
8107
  exitCode: 2,
7860
- hint: buildPreferredBranchMismatchHint({
8108
+ hint: buildBranchMismatchHint({
7861
8109
  currentBranch: params.currentBranch,
7862
- preferredBranch: params.preferredBranch,
8110
+ branchName: params.branchName,
7863
8111
  overrideFlag: params.overrideFlag
7864
8112
  })
7865
8113
  });
@@ -7949,6 +8197,237 @@ async function pollChangeStepReplay(api, appId, replayId) {
7949
8197
  }
7950
8198
  throw new RemixError("Timed out waiting for AI-assisted replay.", { exitCode: 1 });
7951
8199
  }
8200
+ function normalizeBranchName2(value) {
8201
+ const normalized = String(value ?? "").trim();
8202
+ return normalized || null;
8203
+ }
8204
+ function buildBindingFromLane(state, lane) {
8205
+ if (!lane.currentAppId || !lane.upstreamAppId) return null;
8206
+ const resolvedBranch = normalizeBranchName2(lane.branchName) ?? state.currentBranch ?? null;
8207
+ const resolvedDefaultBranch = normalizeBranchName2(lane.defaultBranch) ?? normalizeBranchName2(state.defaultBranch);
8208
+ const explicitRootProjectId = state.explicitRootBinding?.projectId ?? null;
8209
+ const bindingMode = explicitRootProjectId && lane.projectId === explicitRootProjectId && resolvedBranch && resolvedBranch === resolvedDefaultBranch ? "explicit_root" : "lane";
8210
+ return {
8211
+ schemaVersion: 3,
8212
+ projectId: lane.projectId ?? state.projectId,
8213
+ currentAppId: lane.currentAppId,
8214
+ upstreamAppId: lane.upstreamAppId,
8215
+ threadId: lane.threadId ?? null,
8216
+ repoFingerprint: lane.repoFingerprint ?? state.repoFingerprint ?? null,
8217
+ remoteUrl: lane.remoteUrl ?? state.remoteUrl ?? null,
8218
+ defaultBranch: lane.defaultBranch ?? state.defaultBranch ?? null,
8219
+ laneId: lane.laneId ?? null,
8220
+ branchName: resolvedBranch,
8221
+ bindingMode
8222
+ };
8223
+ }
8224
+ function shouldPersistRemoteLaneMetadata(localBinding, lane) {
8225
+ return Boolean(
8226
+ !localBinding.laneId && lane.laneId || !localBinding.threadId && lane.threadId || !localBinding.repoFingerprint && lane.repoFingerprint || !localBinding.remoteUrl && lane.remoteUrl || !localBinding.defaultBranch && lane.defaultBranch || !localBinding.branchName && lane.branchName
8227
+ );
8228
+ }
8229
+ function shouldRequireRemoteLaneForCurrentBranch(params) {
8230
+ if (!params.currentBranch) return false;
8231
+ const defaultBranch = normalizeBranchName2(params.defaultBranch);
8232
+ if (params.currentBranch === defaultBranch) return false;
8233
+ return !params.binding.laneId || params.binding.currentAppId === params.binding.upstreamAppId;
8234
+ }
8235
+ async function persistResolvedLane(repoRoot, binding) {
8236
+ await writeCollabBinding(repoRoot, {
8237
+ projectId: binding.projectId,
8238
+ currentAppId: binding.currentAppId,
8239
+ upstreamAppId: binding.upstreamAppId,
8240
+ threadId: binding.threadId,
8241
+ repoFingerprint: binding.repoFingerprint,
8242
+ remoteUrl: binding.remoteUrl,
8243
+ defaultBranch: binding.defaultBranch,
8244
+ laneId: binding.laneId,
8245
+ branchName: binding.branchName,
8246
+ bindingMode: binding.bindingMode
8247
+ });
8248
+ return readCollabBinding(repoRoot);
8249
+ }
8250
+ function buildAmbiguousResolution(params) {
8251
+ return {
8252
+ status: "ambiguous_family_selection",
8253
+ currentBranch: params.currentBranch,
8254
+ projectIds: Array.isArray(params.lane.projectIds) ? params.lane.projectIds.filter((value) => typeof value === "string" && value.trim().length > 0) : [],
8255
+ repoFingerprint: params.lane.repoFingerprint ?? params.state.repoFingerprint,
8256
+ remoteUrl: params.lane.remoteUrl ?? params.state.remoteUrl,
8257
+ defaultBranch: params.lane.defaultBranch ?? params.state.defaultBranch
8258
+ };
8259
+ }
8260
+ async function resolveActiveLaneBinding(params) {
8261
+ const state = await readCollabBindingState(params.repoRoot);
8262
+ if (!state) {
8263
+ return { status: "not_bound", currentBranch: null };
8264
+ }
8265
+ const currentBranch = normalizeBranchName2(state.currentBranch);
8266
+ const localBinding = state.binding;
8267
+ if (localBinding) {
8268
+ const requireRemoteLane = shouldRequireRemoteLaneForCurrentBranch({
8269
+ binding: localBinding,
8270
+ currentBranch,
8271
+ defaultBranch: state.defaultBranch
8272
+ });
8273
+ if (!params.api || !currentBranch) {
8274
+ return {
8275
+ status: "resolved",
8276
+ source: "local",
8277
+ binding: localBinding,
8278
+ currentBranch
8279
+ };
8280
+ }
8281
+ const laneResp2 = await params.api.resolveProjectLaneBinding({
8282
+ projectId: state.explicitRootBinding?.projectId ?? (requireRemoteLane ? void 0 : localBinding.projectId ?? state.projectId ?? void 0),
8283
+ repoFingerprint: state.repoFingerprint ?? void 0,
8284
+ remoteUrl: state.remoteUrl ?? void 0,
8285
+ defaultBranch: state.defaultBranch ?? void 0,
8286
+ branchName: currentBranch
8287
+ });
8288
+ const lane2 = unwrapResponseObject(laneResp2, "project lane binding");
8289
+ if (lane2.status === "ambiguous_family_selection") {
8290
+ return buildAmbiguousResolution({ state, currentBranch, lane: lane2 });
8291
+ }
8292
+ if (lane2.status === "resolved") {
8293
+ const resolvedBranch = normalizeBranchName2(lane2.branchName);
8294
+ const resolvedProjectId = lane2.projectId ?? state.projectId;
8295
+ const branchConflict = Boolean(resolvedBranch && localBinding.branchName && resolvedBranch !== localBinding.branchName);
8296
+ const appConflict = Boolean(lane2.currentAppId && lane2.currentAppId !== localBinding.currentAppId);
8297
+ const upstreamConflict = Boolean(lane2.upstreamAppId && lane2.upstreamAppId !== localBinding.upstreamAppId);
8298
+ const projectConflict = Boolean(resolvedProjectId && localBinding.projectId && resolvedProjectId !== localBinding.projectId);
8299
+ if (branchConflict || appConflict || upstreamConflict || projectConflict) {
8300
+ if (requireRemoteLane) {
8301
+ const binding = buildBindingFromLane(state, lane2);
8302
+ if (binding) {
8303
+ return {
8304
+ status: "resolved",
8305
+ source: "remote",
8306
+ binding,
8307
+ currentBranch
8308
+ };
8309
+ }
8310
+ }
8311
+ return {
8312
+ status: "binding_conflict",
8313
+ binding: localBinding,
8314
+ resolvedLane: lane2,
8315
+ currentBranch
8316
+ };
8317
+ }
8318
+ if (shouldPersistRemoteLaneMetadata(localBinding, lane2)) {
8319
+ const binding = buildBindingFromLane(state, lane2);
8320
+ if (binding) {
8321
+ return {
8322
+ status: "resolved",
8323
+ source: "remote",
8324
+ binding,
8325
+ currentBranch
8326
+ };
8327
+ }
8328
+ }
8329
+ }
8330
+ if (requireRemoteLane) {
8331
+ return {
8332
+ status: "missing_branch_binding",
8333
+ currentBranch,
8334
+ projectId: lane2.projectId ?? state.projectId,
8335
+ repoFingerprint: lane2.repoFingerprint ?? state.repoFingerprint,
8336
+ remoteUrl: lane2.remoteUrl ?? state.remoteUrl,
8337
+ defaultBranch: lane2.defaultBranch ?? state.defaultBranch,
8338
+ upstreamAppId: lane2.upstreamAppId ?? localBinding.upstreamAppId ?? null,
8339
+ threadId: lane2.threadId ?? localBinding.threadId ?? null
8340
+ };
8341
+ }
8342
+ return {
8343
+ status: "resolved",
8344
+ source: "local",
8345
+ binding: localBinding,
8346
+ currentBranch
8347
+ };
8348
+ }
8349
+ if (!params.api || !currentBranch) {
8350
+ return {
8351
+ status: "missing_branch_binding",
8352
+ currentBranch,
8353
+ projectId: state.projectId,
8354
+ repoFingerprint: state.repoFingerprint,
8355
+ remoteUrl: state.remoteUrl,
8356
+ defaultBranch: state.defaultBranch,
8357
+ upstreamAppId: null,
8358
+ threadId: null
8359
+ };
8360
+ }
8361
+ const laneResp = await params.api.resolveProjectLaneBinding({
8362
+ projectId: state.explicitRootBinding?.projectId ?? state.projectId ?? void 0,
8363
+ repoFingerprint: state.repoFingerprint ?? void 0,
8364
+ remoteUrl: state.remoteUrl ?? void 0,
8365
+ defaultBranch: state.defaultBranch ?? void 0,
8366
+ branchName: currentBranch
8367
+ });
8368
+ const lane = unwrapResponseObject(laneResp, "project lane binding");
8369
+ if (lane.status === "ambiguous_family_selection") {
8370
+ return buildAmbiguousResolution({ state, currentBranch, lane });
8371
+ }
8372
+ if (lane.status === "resolved") {
8373
+ const binding = buildBindingFromLane(state, lane);
8374
+ if (binding) {
8375
+ return {
8376
+ status: "resolved",
8377
+ source: "remote",
8378
+ binding,
8379
+ currentBranch
8380
+ };
8381
+ }
8382
+ }
8383
+ return {
8384
+ status: "missing_branch_binding",
8385
+ currentBranch,
8386
+ projectId: lane.projectId ?? state.explicitRootBinding?.projectId ?? state.projectId,
8387
+ repoFingerprint: lane.repoFingerprint ?? state.repoFingerprint,
8388
+ remoteUrl: lane.remoteUrl ?? state.remoteUrl,
8389
+ defaultBranch: lane.defaultBranch ?? state.defaultBranch,
8390
+ upstreamAppId: lane.upstreamAppId ?? state.explicitRootBinding?.upstreamAppId ?? null,
8391
+ threadId: lane.threadId ?? state.explicitRootBinding?.threadId ?? null
8392
+ };
8393
+ }
8394
+ async function ensureActiveLaneBinding(params) {
8395
+ const resolved = await resolveActiveLaneBinding({
8396
+ repoRoot: params.repoRoot,
8397
+ api: params.api
8398
+ });
8399
+ if (resolved.status === "resolved") {
8400
+ if (resolved.source === "local") {
8401
+ return resolved.binding;
8402
+ }
8403
+ return persistResolvedLane(params.repoRoot, resolved.binding);
8404
+ }
8405
+ if (resolved.status === "binding_conflict") {
8406
+ throw new RemixError("Current branch binding conflicts with the server-resolved Remix lane.", {
8407
+ exitCode: 2,
8408
+ hint: `Local app ${resolved.binding.currentAppId}; server app ${resolved.resolvedLane.currentAppId ?? "(unknown)"}. Repair the branch binding before running ${params.operation ?? "this command"}.`
8409
+ });
8410
+ }
8411
+ if (resolved.status === "ambiguous_family_selection") {
8412
+ throw new RemixError("Multiple canonical Remix families match this repository.", {
8413
+ exitCode: 2,
8414
+ hint: "This checkout is not specific enough to choose a single family for the current branch. Continue from a checkout already bound to the intended family, or run `remix collab init --force-new` to create a new canonical family."
8415
+ });
8416
+ }
8417
+ if (resolved.status === "not_bound") {
8418
+ return null;
8419
+ }
8420
+ if (!resolved.currentBranch) {
8421
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
8422
+ exitCode: 2,
8423
+ hint: `Switch to a named branch before running ${params.operation ?? "this command"}.`
8424
+ });
8425
+ }
8426
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
8427
+ exitCode: 2,
8428
+ hint: `Run \`remix collab init\` on branch ${resolved.currentBranch} before running ${params.operation ?? "this command"}.`
8429
+ });
8430
+ }
7952
8431
  async function collabRecordingPreflight(params) {
7953
8432
  let repoRoot;
7954
8433
  try {
@@ -7960,7 +8439,7 @@ async function collabRecordingPreflight(params) {
7960
8439
  repoRoot: null,
7961
8440
  appId: null,
7962
8441
  currentBranch: null,
7963
- preferredBranch: null,
8442
+ branchName: null,
7964
8443
  headCommitHash: null,
7965
8444
  worktreeClean: false,
7966
8445
  syncStatus: null,
@@ -7972,14 +8451,14 @@ async function collabRecordingPreflight(params) {
7972
8451
  hint: message
7973
8452
  };
7974
8453
  }
7975
- const binding = await readCollabBinding(repoRoot);
7976
- if (!binding) {
8454
+ const bindingResolution = await resolveActiveLaneBinding({ repoRoot, api: params.api });
8455
+ if (bindingResolution.status === "not_bound") {
7977
8456
  return {
7978
8457
  status: "not_bound",
7979
8458
  repoRoot,
7980
8459
  appId: null,
7981
8460
  currentBranch: null,
7982
- preferredBranch: null,
8461
+ branchName: null,
7983
8462
  headCommitHash: null,
7984
8463
  worktreeClean: false,
7985
8464
  syncStatus: null,
@@ -7991,19 +8470,74 @@ async function collabRecordingPreflight(params) {
7991
8470
  hint: "Run `remix collab init` first."
7992
8471
  };
7993
8472
  }
8473
+ if (bindingResolution.status === "missing_branch_binding") {
8474
+ return {
8475
+ status: "branch_binding_missing",
8476
+ repoRoot,
8477
+ appId: null,
8478
+ currentBranch: bindingResolution.currentBranch,
8479
+ branchName: bindingResolution.currentBranch,
8480
+ headCommitHash: null,
8481
+ worktreeClean: false,
8482
+ syncStatus: null,
8483
+ syncTargetCommitHash: null,
8484
+ syncTargetCommitId: null,
8485
+ reconcileTargetHeadCommitHash: null,
8486
+ reconcileTargetHeadCommitId: null,
8487
+ warnings: [],
8488
+ hint: `Current branch ${bindingResolution.currentBranch ?? "(detached)"} is not yet bound to a Remix lane.`
8489
+ };
8490
+ }
8491
+ if (bindingResolution.status === "ambiguous_family_selection") {
8492
+ return {
8493
+ status: "family_ambiguous",
8494
+ repoRoot,
8495
+ appId: null,
8496
+ currentBranch: bindingResolution.currentBranch,
8497
+ branchName: bindingResolution.currentBranch,
8498
+ headCommitHash: null,
8499
+ worktreeClean: false,
8500
+ syncStatus: null,
8501
+ syncTargetCommitHash: null,
8502
+ syncTargetCommitId: null,
8503
+ reconcileTargetHeadCommitHash: null,
8504
+ reconcileTargetHeadCommitId: null,
8505
+ warnings: [],
8506
+ hint: "Multiple canonical Remix families match this repository. Continue from a checkout already bound to the intended family, or run `remix collab init --force-new` to create a new canonical family."
8507
+ };
8508
+ }
8509
+ if (bindingResolution.status === "binding_conflict") {
8510
+ return {
8511
+ status: "metadata_conflict",
8512
+ repoRoot,
8513
+ appId: bindingResolution.binding.currentAppId,
8514
+ currentBranch: bindingResolution.currentBranch,
8515
+ branchName: bindingResolution.binding.branchName,
8516
+ headCommitHash: null,
8517
+ worktreeClean: false,
8518
+ syncStatus: null,
8519
+ syncTargetCommitHash: null,
8520
+ syncTargetCommitId: null,
8521
+ reconcileTargetHeadCommitHash: null,
8522
+ reconcileTargetHeadCommitId: null,
8523
+ warnings: [],
8524
+ hint: `Local binding for ${bindingResolution.currentBranch ?? "(detached)"} points to app ${bindingResolution.binding.currentAppId}, but the server resolved lane ${bindingResolution.resolvedLane.laneId ?? "(unknown)"} / app ${bindingResolution.resolvedLane.currentAppId ?? "(unknown)"}. Repair the branch binding before recording work.`
8525
+ };
8526
+ }
8527
+ const binding = bindingResolution.binding;
7994
8528
  const [currentBranch, headCommitHash, worktreeStatus] = await Promise.all([
7995
8529
  getCurrentBranch(repoRoot),
7996
8530
  getHeadCommitHash(repoRoot),
7997
8531
  getWorktreeStatus(repoRoot)
7998
8532
  ]);
7999
- const preferredBranch = binding.preferredBranch ?? null;
8533
+ const branchName = binding.branchName ?? null;
8000
8534
  if (!headCommitHash) {
8001
8535
  return {
8002
8536
  status: "missing_head",
8003
8537
  repoRoot,
8004
8538
  appId: binding.currentAppId,
8005
8539
  currentBranch,
8006
- preferredBranch,
8540
+ branchName,
8007
8541
  headCommitHash: null,
8008
8542
  worktreeClean: worktreeStatus.isClean,
8009
8543
  syncStatus: null,
@@ -8015,13 +8549,13 @@ async function collabRecordingPreflight(params) {
8015
8549
  hint: "Failed to resolve local HEAD commit."
8016
8550
  };
8017
8551
  }
8018
- if (!params.allowBranchMismatch && !isPreferredBranchMatch(currentBranch, preferredBranch)) {
8552
+ if (!params.allowBranchMismatch && !isBoundBranchMatch(currentBranch, branchName)) {
8019
8553
  return {
8020
8554
  status: "branch_mismatch",
8021
8555
  repoRoot,
8022
8556
  appId: binding.currentAppId,
8023
8557
  currentBranch,
8024
- preferredBranch,
8558
+ branchName,
8025
8559
  headCommitHash,
8026
8560
  worktreeClean: worktreeStatus.isClean,
8027
8561
  syncStatus: null,
@@ -8030,9 +8564,9 @@ async function collabRecordingPreflight(params) {
8030
8564
  reconcileTargetHeadCommitHash: null,
8031
8565
  reconcileTargetHeadCommitId: null,
8032
8566
  warnings: [],
8033
- hint: buildPreferredBranchMismatchHint({
8567
+ hint: buildBranchMismatchHint({
8034
8568
  currentBranch,
8035
- preferredBranch
8569
+ branchName
8036
8570
  })
8037
8571
  };
8038
8572
  }
@@ -8050,7 +8584,7 @@ async function collabRecordingPreflight(params) {
8050
8584
  repoRoot,
8051
8585
  appId: binding.currentAppId,
8052
8586
  currentBranch,
8053
- preferredBranch,
8587
+ branchName,
8054
8588
  headCommitHash,
8055
8589
  worktreeClean: worktreeStatus.isClean,
8056
8590
  syncStatus: sync.status,
@@ -8068,7 +8602,7 @@ async function collabRecordingPreflight(params) {
8068
8602
  repoRoot,
8069
8603
  appId: binding.currentAppId,
8070
8604
  currentBranch,
8071
- preferredBranch,
8605
+ branchName,
8072
8606
  headCommitHash,
8073
8607
  worktreeClean: worktreeStatus.isClean,
8074
8608
  syncStatus: sync.status,
@@ -8093,7 +8627,7 @@ async function collabRecordingPreflight(params) {
8093
8627
  repoRoot,
8094
8628
  appId: binding.currentAppId,
8095
8629
  currentBranch,
8096
- preferredBranch,
8630
+ branchName,
8097
8631
  headCommitHash,
8098
8632
  worktreeClean: worktreeStatus.isClean,
8099
8633
  syncStatus: sync.status,
@@ -8111,7 +8645,7 @@ async function collabRecordingPreflight(params) {
8111
8645
  repoRoot,
8112
8646
  appId: binding.currentAppId,
8113
8647
  currentBranch,
8114
- preferredBranch,
8648
+ branchName,
8115
8649
  headCommitHash,
8116
8650
  worktreeClean: worktreeStatus.isClean,
8117
8651
  syncStatus: sync.status,
@@ -8128,7 +8662,7 @@ async function collabRecordingPreflight(params) {
8128
8662
  repoRoot,
8129
8663
  appId: binding.currentAppId,
8130
8664
  currentBranch,
8131
- preferredBranch,
8665
+ branchName,
8132
8666
  headCommitHash,
8133
8667
  worktreeClean: worktreeStatus.isClean,
8134
8668
  syncStatus: sync.status,
@@ -8162,12 +8696,12 @@ function createOwner(params) {
8162
8696
  };
8163
8697
  }
8164
8698
  async function writeOwnerMetadata(ownerPath, owner) {
8165
- await import_promises15.default.writeFile(ownerPath, `${JSON.stringify(owner, null, 2)}
8699
+ await import_promises16.default.writeFile(ownerPath, `${JSON.stringify(owner, null, 2)}
8166
8700
  `, "utf8");
8167
8701
  }
8168
8702
  async function readOwnerMetadata(ownerPath) {
8169
8703
  try {
8170
- const raw = await import_promises15.default.readFile(ownerPath, "utf8");
8704
+ const raw = await import_promises16.default.readFile(ownerPath, "utf8");
8171
8705
  const parsed = JSON.parse(raw);
8172
8706
  if (!parsed || typeof parsed !== "object") return null;
8173
8707
  if (!parsed.operation || !parsed.repoRoot || typeof parsed.pid !== "number" || !parsed.startedAt || !parsed.heartbeatAt) {
@@ -8204,23 +8738,23 @@ async function getLastKnownUpdateMs(lockDir, ownerPath, owner) {
8204
8738
  if (Number.isFinite(heartbeatMs)) return heartbeatMs;
8205
8739
  const startedMs = owner ? Date.parse(owner.startedAt) : Number.NaN;
8206
8740
  if (Number.isFinite(startedMs)) return startedMs;
8207
- const stat = await import_promises15.default.stat(ownerPath).catch(() => null);
8741
+ const stat = await import_promises16.default.stat(ownerPath).catch(() => null);
8208
8742
  if (stat) return stat.mtimeMs;
8209
- const dirStat = await import_promises15.default.stat(lockDir).catch(() => null);
8743
+ const dirStat = await import_promises16.default.stat(lockDir).catch(() => null);
8210
8744
  if (dirStat) return dirStat.mtimeMs;
8211
8745
  return 0;
8212
8746
  }
8213
8747
  async function ensureLockDir(lockDir) {
8214
- await import_promises15.default.mkdir(import_path4.default.dirname(lockDir), { recursive: true });
8748
+ await import_promises16.default.mkdir(import_path5.default.dirname(lockDir), { recursive: true });
8215
8749
  }
8216
8750
  async function tryAcquireLock(lockDir, ownerPath, owner) {
8217
8751
  try {
8218
8752
  await ensureLockDir(lockDir);
8219
- await import_promises15.default.mkdir(lockDir);
8753
+ await import_promises16.default.mkdir(lockDir);
8220
8754
  try {
8221
8755
  await writeOwnerMetadata(ownerPath, owner);
8222
8756
  } catch (error) {
8223
- await import_promises15.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8757
+ await import_promises16.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8224
8758
  throw error;
8225
8759
  }
8226
8760
  return true;
@@ -8269,7 +8803,7 @@ async function acquirePhysicalLock(lockDir, ownerPath, owner, options) {
8269
8803
  const alive = await isProcessAlive(currentOwner2);
8270
8804
  if (ageMs >= options.staleMs && alive !== true) {
8271
8805
  notices.push(buildStaleRecoveryNotice(currentOwner2));
8272
- await import_promises15.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8806
+ await import_promises16.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8273
8807
  continue;
8274
8808
  }
8275
8809
  await sleep2(RETRY_DELAY_MS);
@@ -8294,7 +8828,7 @@ function startHeartbeat(lockDir, ownerPath, owner, heartbeatMs) {
8294
8828
  };
8295
8829
  owner.heartbeatAt = nextOwner.heartbeatAt;
8296
8830
  void writeOwnerMetadata(ownerPath, nextOwner).catch(() => void 0);
8297
- void import_promises15.default.utimes(lockDir, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date()).catch(() => void 0);
8831
+ void import_promises16.default.utimes(lockDir, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date()).catch(() => void 0);
8298
8832
  }, heartbeatMs);
8299
8833
  }
8300
8834
  async function releaseReentrantLock(lockDir) {
@@ -8304,12 +8838,12 @@ async function releaseReentrantLock(lockDir) {
8304
8838
  if (held.count > 0) return;
8305
8839
  clearInterval(held.heartbeatTimer);
8306
8840
  heldLocks.delete(lockDir);
8307
- await import_promises15.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8841
+ await import_promises16.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8308
8842
  }
8309
8843
  async function withRepoMutationLock(options, fn) {
8310
8844
  const repoRoot = await findGitRoot(options.cwd);
8311
8845
  const gitCommonDir = await getGitCommonDir(repoRoot);
8312
- const lockDir = import_path4.default.join(gitCommonDir, "remix", "locks", "repo-mutation.lock");
8846
+ const lockDir = import_path5.default.join(gitCommonDir, "remix", "locks", "repo-mutation.lock");
8313
8847
  const owner = createOwner({
8314
8848
  operation: options.operation,
8315
8849
  repoRoot,
@@ -8321,11 +8855,11 @@ async function withRepoMutationLock(options, fn) {
8321
8855
  const existing = heldLocks.get(lockDir);
8322
8856
  let notices = [];
8323
8857
  if (!existing) {
8324
- notices = await acquirePhysicalLock(lockDir, import_path4.default.join(lockDir, "owner.json"), owner, {
8858
+ notices = await acquirePhysicalLock(lockDir, import_path5.default.join(lockDir, "owner.json"), owner, {
8325
8859
  acquireTimeoutMs,
8326
8860
  staleMs
8327
8861
  });
8328
- const ownerPath = import_path4.default.join(lockDir, "owner.json");
8862
+ const ownerPath = import_path5.default.join(lockDir, "owner.json");
8329
8863
  heldLocks.set(lockDir, {
8330
8864
  count: 1,
8331
8865
  lockDir,
@@ -8350,7 +8884,11 @@ async function withRepoMutationLock(options, fn) {
8350
8884
  }
8351
8885
  async function collabSync(params) {
8352
8886
  const repoRoot = await findGitRoot(params.cwd);
8353
- const binding = await readCollabBinding(repoRoot);
8887
+ const binding = await ensureActiveLaneBinding({
8888
+ repoRoot,
8889
+ api: params.api,
8890
+ operation: "`remix collab sync`"
8891
+ });
8354
8892
  if (!binding) {
8355
8893
  throw new RemixError("Repository is not bound to Remix.", {
8356
8894
  exitCode: 2,
@@ -8359,9 +8897,9 @@ async function collabSync(params) {
8359
8897
  }
8360
8898
  await ensureCleanWorktree(repoRoot);
8361
8899
  const branch = await requireCurrentBranch(repoRoot);
8362
- assertPreferredBranchMatch({
8900
+ assertBoundBranchMatch({
8363
8901
  currentBranch: branch,
8364
- preferredBranch: binding.preferredBranch,
8902
+ branchName: binding.branchName,
8365
8903
  allowBranchMismatch: params.allowBranchMismatch,
8366
8904
  operation: "`remix collab sync`"
8367
8905
  });
@@ -8438,16 +8976,16 @@ async function collabSync(params) {
8438
8976
  });
8439
8977
  await ensureCleanWorktree(lockedRepoRoot);
8440
8978
  const lockedBranch = await requireCurrentBranch(lockedRepoRoot);
8441
- assertPreferredBranchMatch({
8979
+ assertBoundBranchMatch({
8442
8980
  currentBranch: lockedBranch,
8443
- preferredBranch: binding.preferredBranch,
8981
+ branchName: binding.branchName,
8444
8982
  allowBranchMismatch: params.allowBranchMismatch,
8445
8983
  operation: "`remix collab sync`"
8446
8984
  });
8447
- const tempDir = await import_promises16.default.mkdtemp(import_path5.default.join(import_os3.default.tmpdir(), "remix-sync-"));
8448
- const bundlePath = import_path5.default.join(tempDir, "sync-local.bundle");
8985
+ const tempDir = await import_promises17.default.mkdtemp(import_path6.default.join(import_os3.default.tmpdir(), "remix-sync-"));
8986
+ const bundlePath = import_path6.default.join(tempDir, "sync-local.bundle");
8449
8987
  try {
8450
- await import_promises16.default.writeFile(bundlePath, Buffer.from(bundleBase64, "base64"));
8988
+ await import_promises17.default.writeFile(bundlePath, Buffer.from(bundleBase64, "base64"));
8451
8989
  await importGitBundle(lockedRepoRoot, bundlePath, bundleRef);
8452
8990
  await ensureCommitExists(lockedRepoRoot, sync.targetCommitHash);
8453
8991
  const localCommitHash = await fastForwardToCommit(lockedRepoRoot, sync.targetCommitHash);
@@ -8459,7 +8997,7 @@ async function collabSync(params) {
8459
8997
  ...warnings.length > 0 ? { warnings } : {}
8460
8998
  };
8461
8999
  } finally {
8462
- await import_promises16.default.rm(tempDir, { recursive: true, force: true });
9000
+ await import_promises17.default.rm(tempDir, { recursive: true, force: true });
8463
9001
  }
8464
9002
  }
8465
9003
  );
@@ -8471,6 +9009,18 @@ function assertSupportedRecordingPreflight(preflight) {
8471
9009
  hint: preflight.hint
8472
9010
  });
8473
9011
  }
9012
+ if (preflight.status === "branch_binding_missing") {
9013
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
9014
+ exitCode: 2,
9015
+ hint: preflight.hint
9016
+ });
9017
+ }
9018
+ if (preflight.status === "family_ambiguous") {
9019
+ throw new RemixError("Multiple canonical Remix families match this repository.", {
9020
+ exitCode: 2,
9021
+ hint: preflight.hint
9022
+ });
9023
+ }
8474
9024
  if (preflight.status === "not_git_repo") {
8475
9025
  throw new RemixError(preflight.hint || "Not inside a git repository.", {
8476
9026
  exitCode: 2,
@@ -8484,9 +9034,9 @@ function assertSupportedRecordingPreflight(preflight) {
8484
9034
  });
8485
9035
  }
8486
9036
  if (preflight.status === "branch_mismatch") {
8487
- assertPreferredBranchMatch({
9037
+ assertBoundBranchMatch({
8488
9038
  currentBranch: preflight.currentBranch,
8489
- preferredBranch: preflight.preferredBranch,
9039
+ branchName: preflight.branchName,
8490
9040
  allowBranchMismatch: false,
8491
9041
  operation: "`remix collab add`"
8492
9042
  });
@@ -8506,7 +9056,11 @@ function assertSupportedRecordingPreflight(preflight) {
8506
9056
  }
8507
9057
  async function collabAdd(params) {
8508
9058
  const repoRoot = await findGitRoot(params.cwd);
8509
- const binding = await readCollabBinding(repoRoot);
9059
+ const binding = await ensureActiveLaneBinding({
9060
+ repoRoot,
9061
+ api: params.api,
9062
+ operation: "`remix collab add`"
9063
+ });
8510
9064
  if (!binding) {
8511
9065
  throw new RemixError("Repository is not bound to Remix.", {
8512
9066
  exitCode: 2,
@@ -8527,9 +9081,9 @@ async function collabAdd(params) {
8527
9081
  });
8528
9082
  assertSupportedRecordingPreflight(preflight);
8529
9083
  const branch = preflight.currentBranch;
8530
- assertPreferredBranchMatch({
9084
+ assertBoundBranchMatch({
8531
9085
  currentBranch: branch,
8532
- preferredBranch: binding.preferredBranch,
9086
+ branchName: binding.branchName,
8533
9087
  allowBranchMismatch: params.allowBranchMismatch,
8534
9088
  operation: "`remix collab add`"
8535
9089
  });
@@ -8609,7 +9163,7 @@ async function collabAdd(params) {
8609
9163
  const replayResp = await params.api.startChangeStepReplay(binding.currentAppId, {
8610
9164
  prompt,
8611
9165
  assistantResponse: assistantResponse ?? void 0,
8612
- diff: await import_promises14.default.readFile(preserved.preservedDiffPath, "utf8"),
9166
+ diff: await import_promises15.default.readFile(preserved.preservedDiffPath, "utf8"),
8613
9167
  baseCommitHash: preserved.baseHeadCommitHash,
8614
9168
  targetHeadCommitHash: headCommitHash,
8615
9169
  expectedPaths: preserved.stagePlan.expectedPaths,
@@ -8702,6 +9256,7 @@ async function collabAdd(params) {
8702
9256
  });
8703
9257
  const resp = await params.api.createChangeStep(binding.currentAppId, {
8704
9258
  threadId: binding.threadId ?? void 0,
9259
+ collabLaneId: binding.laneId ?? void 0,
8705
9260
  prompt,
8706
9261
  assistantResponse: assistantResponse ?? void 0,
8707
9262
  diff,
@@ -8742,7 +9297,7 @@ async function collabAdd(params) {
8742
9297
  dryRun: false,
8743
9298
  allowBranchMismatch: params.allowBranchMismatch
8744
9299
  });
8745
- await import_promises14.default.rm(import_path3.default.dirname(backupPath), { recursive: true, force: true }).catch(() => void 0);
9300
+ await import_promises15.default.rm(import_path4.default.dirname(backupPath), { recursive: true, force: true }).catch(() => void 0);
8746
9301
  } catch (err) {
8747
9302
  const detail = formatCliErrorDetail(err);
8748
9303
  const hint = [
@@ -8775,6 +9330,18 @@ function assertSupportedRecordingPreflight2(preflight) {
8775
9330
  hint: preflight.hint
8776
9331
  });
8777
9332
  }
9333
+ if (preflight.status === "branch_binding_missing") {
9334
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
9335
+ exitCode: 2,
9336
+ hint: preflight.hint
9337
+ });
9338
+ }
9339
+ if (preflight.status === "family_ambiguous") {
9340
+ throw new RemixError("Multiple canonical Remix families match this repository.", {
9341
+ exitCode: 2,
9342
+ hint: preflight.hint
9343
+ });
9344
+ }
8778
9345
  if (preflight.status === "not_git_repo") {
8779
9346
  throw new RemixError(preflight.hint || "Not inside a git repository.", {
8780
9347
  exitCode: 2,
@@ -8788,9 +9355,9 @@ function assertSupportedRecordingPreflight2(preflight) {
8788
9355
  });
8789
9356
  }
8790
9357
  if (preflight.status === "branch_mismatch") {
8791
- assertPreferredBranchMatch({
9358
+ assertBoundBranchMatch({
8792
9359
  currentBranch: preflight.currentBranch,
8793
- preferredBranch: preflight.preferredBranch,
9360
+ branchName: preflight.branchName,
8794
9361
  allowBranchMismatch: false,
8795
9362
  operation: "`remix collab record-turn`"
8796
9363
  });
@@ -8810,7 +9377,11 @@ function assertSupportedRecordingPreflight2(preflight) {
8810
9377
  }
8811
9378
  async function collabRecordTurn(params) {
8812
9379
  const repoRoot = await findGitRoot(params.cwd);
8813
- const binding = await readCollabBinding(repoRoot);
9380
+ const binding = await ensureActiveLaneBinding({
9381
+ repoRoot,
9382
+ api: params.api,
9383
+ operation: "`remix collab record-turn`"
9384
+ });
8814
9385
  if (!binding) {
8815
9386
  throw new RemixError("Repository is not bound to Remix.", {
8816
9387
  exitCode: 2,
@@ -8842,9 +9413,9 @@ async function collabRecordTurn(params) {
8842
9413
  });
8843
9414
  }
8844
9415
  const branch = await getCurrentBranch(repoRoot);
8845
- assertPreferredBranchMatch({
9416
+ assertBoundBranchMatch({
8846
9417
  currentBranch: branch,
8847
- preferredBranch: binding.preferredBranch,
9418
+ branchName: binding.branchName,
8848
9419
  allowBranchMismatch: params.allowBranchMismatch,
8849
9420
  operation: "`remix collab record-turn`"
8850
9421
  });
@@ -8858,6 +9429,7 @@ async function collabRecordTurn(params) {
8858
9429
  });
8859
9430
  const resp = await params.api.createCollabTurn(binding.currentAppId, {
8860
9431
  threadId: binding.threadId ?? void 0,
9432
+ collabLaneId: binding.laneId ?? void 0,
8861
9433
  prompt,
8862
9434
  assistantResponse,
8863
9435
  actor: params.actor,
@@ -8870,10 +9442,11 @@ async function collabRecordTurn(params) {
8870
9442
  },
8871
9443
  idempotencyKey
8872
9444
  });
8873
- return unwrapResponseObject(resp, "collab turn");
9445
+ const turn = unwrapResponseObject(resp, "collab turn");
9446
+ return turn;
8874
9447
  }
8875
9448
 
8876
- // node_modules/@remixhq/core/dist/chunk-B5S3PUIR.js
9449
+ // node_modules/@remixhq/core/dist/chunk-BNKPTE2U.js
8877
9450
  async function readJsonSafe(res) {
8878
9451
  const ct = res.headers.get("content-type") ?? "";
8879
9452
  if (!ct.toLowerCase().includes("application/json")) return null;
@@ -8971,8 +9544,20 @@ function createApiClient(config, opts) {
8971
9544
  const qs = new URLSearchParams();
8972
9545
  if (params.repoFingerprint) qs.set("repoFingerprint", params.repoFingerprint);
8973
9546
  if (params.remoteUrl) qs.set("remoteUrl", params.remoteUrl);
9547
+ if (params.branchName) qs.set("branchName", params.branchName);
8974
9548
  return request(`/v1/projects/bindings/resolve?${qs.toString()}`, { method: "GET" });
8975
9549
  },
9550
+ resolveProjectLaneBinding: (params) => {
9551
+ const qs = new URLSearchParams();
9552
+ if (params.projectId) qs.set("projectId", params.projectId);
9553
+ if (params.repoFingerprint) qs.set("repoFingerprint", params.repoFingerprint);
9554
+ if (params.remoteUrl) qs.set("remoteUrl", params.remoteUrl);
9555
+ if (params.defaultBranch) qs.set("defaultBranch", params.defaultBranch);
9556
+ qs.set("branchName", params.branchName);
9557
+ return request(`/v1/projects/bindings/resolve-lane?${qs.toString()}`, { method: "GET" });
9558
+ },
9559
+ ensureProjectLaneBinding: (payload) => request("/v1/projects/bindings/ensure-lane", { method: "POST", body: JSON.stringify(payload) }),
9560
+ bootstrapFreshProjectLane: (payload) => request("/v1/projects/bindings/bootstrap-fresh-lane", { method: "POST", body: JSON.stringify(payload) }),
8976
9561
  autoEnableDeveloper: () => request("/v1/developer/auto-enable", { method: "POST" }),
8977
9562
  listClientApps: (params) => {
8978
9563
  const qs = params?.orgId ? `?orgId=${encodeURIComponent(params.orgId)}` : "";
@@ -9036,6 +9621,7 @@ function createApiClient(config, opts) {
9036
9621
  if (params?.offset !== void 0) qs.set("offset", String(params.offset));
9037
9622
  if (params?.changeStepId) qs.set("changeStepId", params.changeStepId);
9038
9623
  if (params?.threadId) qs.set("threadId", params.threadId);
9624
+ if (params?.collabLaneId) qs.set("collabLaneId", params.collabLaneId);
9039
9625
  if (params?.createdAfter) qs.set("createdAfter", params.createdAfter);
9040
9626
  if (params?.createdBefore) qs.set("createdBefore", params.createdBefore);
9041
9627
  const suffix = qs.toString() ? `?${qs.toString()}` : "";
@@ -13296,9 +13882,9 @@ var coerce = {
13296
13882
  var NEVER = INVALID;
13297
13883
 
13298
13884
  // node_modules/@remixhq/core/dist/chunk-EVWDYCBL.js
13299
- var import_promises17 = __toESM(require("fs/promises"), 1);
13885
+ var import_promises18 = __toESM(require("fs/promises"), 1);
13300
13886
  var import_os4 = __toESM(require("os"), 1);
13301
- var import_path6 = __toESM(require("path"), 1);
13887
+ var import_path7 = __toESM(require("path"), 1);
13302
13888
 
13303
13889
  // node_modules/tslib/tslib.es6.mjs
13304
13890
  function __rest(s, e) {
@@ -32957,7 +33543,7 @@ var storedSessionSchema = external_exports.object({
32957
33543
  function xdgConfigHome() {
32958
33544
  const value = process.env.XDG_CONFIG_HOME;
32959
33545
  if (typeof value === "string" && value.trim()) return value;
32960
- return import_path6.default.join(import_os4.default.homedir(), ".config");
33546
+ return import_path7.default.join(import_os4.default.homedir(), ".config");
32961
33547
  }
32962
33548
  async function maybeLoadKeytar() {
32963
33549
  try {
@@ -32975,31 +33561,31 @@ async function maybeLoadKeytar() {
32975
33561
  return null;
32976
33562
  }
32977
33563
  async function ensurePathPermissions(filePath) {
32978
- const dir = import_path6.default.dirname(filePath);
32979
- await import_promises17.default.mkdir(dir, { recursive: true });
33564
+ const dir = import_path7.default.dirname(filePath);
33565
+ await import_promises18.default.mkdir(dir, { recursive: true });
32980
33566
  try {
32981
- await import_promises17.default.chmod(dir, 448);
33567
+ await import_promises18.default.chmod(dir, 448);
32982
33568
  } catch {
32983
33569
  }
32984
33570
  try {
32985
- await import_promises17.default.chmod(filePath, 384);
33571
+ await import_promises18.default.chmod(filePath, 384);
32986
33572
  } catch {
32987
33573
  }
32988
33574
  }
32989
- async function writeJsonAtomic(filePath, value) {
32990
- await import_promises17.default.mkdir(import_path6.default.dirname(filePath), { recursive: true });
33575
+ async function writeJsonAtomic2(filePath, value) {
33576
+ await import_promises18.default.mkdir(import_path7.default.dirname(filePath), { recursive: true });
32991
33577
  const tmpPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
32992
- await import_promises17.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
32993
- await import_promises17.default.rename(tmpPath, filePath);
33578
+ await import_promises18.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
33579
+ await import_promises18.default.rename(tmpPath, filePath);
32994
33580
  }
32995
33581
  async function writeSessionFileFallback(filePath, session) {
32996
- await writeJsonAtomic(filePath, session);
33582
+ await writeJsonAtomic2(filePath, session);
32997
33583
  await ensurePathPermissions(filePath);
32998
33584
  }
32999
33585
  function createLocalSessionStore(params) {
33000
33586
  const service = params?.service?.trim() || "remix-cli";
33001
33587
  const account = params?.account?.trim() || "default";
33002
- const filePath = params?.filePath?.trim() || import_path6.default.join(xdgConfigHome(), "remix", "session.json");
33588
+ const filePath = params?.filePath?.trim() || import_path7.default.join(xdgConfigHome(), "remix", "session.json");
33003
33589
  return {
33004
33590
  async getSession() {
33005
33591
  const keytar = await maybeLoadKeytar();
@@ -33013,7 +33599,7 @@ function createLocalSessionStore(params) {
33013
33599
  return null;
33014
33600
  }
33015
33601
  }
33016
- const raw = await import_promises17.default.readFile(filePath, "utf8").catch(() => null);
33602
+ const raw = await import_promises18.default.readFile(filePath, "utf8").catch(() => null);
33017
33603
  if (!raw) return null;
33018
33604
  try {
33019
33605
  const parsed = storedSessionSchema.safeParse(JSON.parse(raw));
@@ -33200,12 +33786,12 @@ async function createHookCollabApiClient() {
33200
33786
 
33201
33787
  // src/hook-diagnostics.ts
33202
33788
  var import_node_crypto2 = require("crypto");
33203
- var import_promises19 = __toESM(require("fs/promises"), 1);
33789
+ var import_promises20 = __toESM(require("fs/promises"), 1);
33204
33790
  var import_node_os5 = __toESM(require("os"), 1);
33205
33791
  var import_node_path7 = __toESM(require("path"), 1);
33206
33792
 
33207
33793
  // src/hook-state.ts
33208
- var import_promises18 = __toESM(require("fs/promises"), 1);
33794
+ var import_promises19 = __toESM(require("fs/promises"), 1);
33209
33795
  var import_node_os4 = __toESM(require("os"), 1);
33210
33796
  var import_node_path6 = __toESM(require("path"), 1);
33211
33797
  var import_node_crypto = require("crypto");
@@ -33222,11 +33808,11 @@ function stateLockPath(sessionId) {
33222
33808
  function stateLockMetaPath(sessionId) {
33223
33809
  return import_node_path6.default.join(stateLockPath(sessionId), "owner.json");
33224
33810
  }
33225
- async function writeJsonAtomic2(filePath, value) {
33226
- await import_promises18.default.mkdir(import_node_path6.default.dirname(filePath), { recursive: true });
33811
+ async function writeJsonAtomic3(filePath, value) {
33812
+ await import_promises19.default.mkdir(import_node_path6.default.dirname(filePath), { recursive: true });
33227
33813
  const tmpPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
33228
- await import_promises18.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
33229
- await import_promises18.default.rename(tmpPath, filePath);
33814
+ await import_promises19.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
33815
+ await import_promises19.default.rename(tmpPath, filePath);
33230
33816
  }
33231
33817
  var STATE_LOCK_WAIT_MS = 2e3;
33232
33818
  var STATE_LOCK_POLL_MS = 25;
@@ -33236,7 +33822,7 @@ async function sleep4(ms) {
33236
33822
  await new Promise((resolve) => setTimeout(resolve, ms));
33237
33823
  }
33238
33824
  async function readStateLockMetadata(sessionId) {
33239
- const raw = await import_promises18.default.readFile(stateLockMetaPath(sessionId), "utf8").catch(() => null);
33825
+ const raw = await import_promises19.default.readFile(stateLockMetaPath(sessionId), "utf8").catch(() => null);
33240
33826
  if (!raw) return null;
33241
33827
  try {
33242
33828
  const parsed = JSON.parse(raw);
@@ -33254,20 +33840,20 @@ async function readStateLockMetadata(sessionId) {
33254
33840
  }
33255
33841
  }
33256
33842
  async function writeStateLockMetadata(sessionId, metadata) {
33257
- await writeJsonAtomic2(stateLockMetaPath(sessionId), metadata);
33843
+ await writeJsonAtomic3(stateLockMetaPath(sessionId), metadata);
33258
33844
  }
33259
33845
  async function tryRemoveStaleStateLock(sessionId) {
33260
33846
  const lockPath = stateLockPath(sessionId);
33261
33847
  const metadata = await readStateLockMetadata(sessionId);
33262
33848
  const staleByHeartbeat = metadata && Date.now() - new Date(metadata.heartbeatAt).getTime() > STATE_LOCK_STALE_MS;
33263
33849
  if (staleByHeartbeat) {
33264
- await import_promises18.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33850
+ await import_promises19.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33265
33851
  return true;
33266
33852
  }
33267
33853
  if (!metadata) {
33268
- const lockStat = await import_promises18.default.stat(lockPath).catch(() => null);
33854
+ const lockStat = await import_promises19.default.stat(lockPath).catch(() => null);
33269
33855
  if (lockStat && Date.now() - lockStat.mtimeMs > STATE_LOCK_STALE_MS) {
33270
- await import_promises18.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33856
+ await import_promises19.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33271
33857
  return true;
33272
33858
  }
33273
33859
  }
@@ -33276,10 +33862,10 @@ async function tryRemoveStaleStateLock(sessionId) {
33276
33862
  async function acquireStateLock(sessionId) {
33277
33863
  const lockPath = stateLockPath(sessionId);
33278
33864
  const deadline = Date.now() + STATE_LOCK_WAIT_MS;
33279
- await import_promises18.default.mkdir(stateRoot(), { recursive: true });
33865
+ await import_promises19.default.mkdir(stateRoot(), { recursive: true });
33280
33866
  while (true) {
33281
33867
  try {
33282
- await import_promises18.default.mkdir(lockPath);
33868
+ await import_promises19.default.mkdir(lockPath);
33283
33869
  const ownerId = (0, import_node_crypto.randomUUID)();
33284
33870
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
33285
33871
  const metadata = {
@@ -33304,7 +33890,7 @@ async function acquireStateLock(sessionId) {
33304
33890
  clearInterval(heartbeat);
33305
33891
  const currentMetadata = await readStateLockMetadata(sessionId);
33306
33892
  if (currentMetadata?.ownerId === ownerId) {
33307
- await import_promises18.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33893
+ await import_promises19.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33308
33894
  }
33309
33895
  };
33310
33896
  } catch (error) {
@@ -33416,7 +34002,7 @@ async function updatePendingTurnState(sessionId, updater) {
33416
34002
  });
33417
34003
  }
33418
34004
  async function loadPendingTurnState(sessionId) {
33419
- const raw = await import_promises18.default.readFile(statePath(sessionId), "utf8").catch(() => null);
34005
+ const raw = await import_promises19.default.readFile(statePath(sessionId), "utf8").catch(() => null);
33420
34006
  if (!raw) return null;
33421
34007
  try {
33422
34008
  const parsed = JSON.parse(raw);
@@ -33442,7 +34028,7 @@ async function loadPendingTurnState(sessionId) {
33442
34028
  }
33443
34029
  }
33444
34030
  async function savePendingTurnState(state) {
33445
- await writeJsonAtomic2(statePath(state.sessionId), state);
34031
+ await writeJsonAtomic3(statePath(state.sessionId), state);
33446
34032
  }
33447
34033
  async function upsertTouchedRepo(sessionId, params) {
33448
34034
  const normalizedRepoRoot = params.repoRoot.trim();
@@ -33518,14 +34104,14 @@ async function listTouchedRepos(sessionId) {
33518
34104
  }
33519
34105
  async function clearPendingTurnState(sessionId) {
33520
34106
  await withStateLock(sessionId, async () => {
33521
- await import_promises18.default.rm(statePath(sessionId), { force: true }).catch(() => void 0);
34107
+ await import_promises19.default.rm(statePath(sessionId), { force: true }).catch(() => void 0);
33522
34108
  });
33523
34109
  }
33524
34110
 
33525
34111
  // package.json
33526
34112
  var package_default = {
33527
34113
  name: "@remixhq/claude-plugin",
33528
- version: "0.1.15",
34114
+ version: "0.1.17",
33529
34115
  description: "Claude Code plugin for Remix collaboration workflows",
33530
34116
  homepage: "https://github.com/RemixDotOne/remix-claude-plugin",
33531
34117
  license: "MIT",
@@ -33556,8 +34142,8 @@ var package_default = {
33556
34142
  prepack: "npm run build"
33557
34143
  },
33558
34144
  dependencies: {
33559
- "@remixhq/core": "^0.1.10",
33560
- "@remixhq/mcp": "^0.1.10"
34145
+ "@remixhq/core": "^0.1.12",
34146
+ "@remixhq/mcp": "^0.1.12"
33561
34147
  },
33562
34148
  devDependencies: {
33563
34149
  "@types/node": "^25.4.0",
@@ -33608,13 +34194,13 @@ function normalizeFields(fields) {
33608
34194
  return Object.fromEntries(normalizedEntries);
33609
34195
  }
33610
34196
  async function rotateLogIfNeeded(logPath) {
33611
- const stat = await import_promises19.default.stat(logPath).catch(() => null);
34197
+ const stat = await import_promises20.default.stat(logPath).catch(() => null);
33612
34198
  if (!stat || stat.size < MAX_LOG_BYTES) {
33613
34199
  return;
33614
34200
  }
33615
34201
  const rotatedPath = `${logPath}.1`;
33616
- await import_promises19.default.rm(rotatedPath, { force: true }).catch(() => void 0);
33617
- await import_promises19.default.rename(logPath, rotatedPath).catch(() => void 0);
34202
+ await import_promises20.default.rm(rotatedPath, { force: true }).catch(() => void 0);
34203
+ await import_promises20.default.rename(logPath, rotatedPath).catch(() => void 0);
33618
34204
  }
33619
34205
  function summarizeText(value) {
33620
34206
  if (typeof value !== "string" || !value.trim()) {
@@ -33634,7 +34220,7 @@ function summarizeText(value) {
33634
34220
  async function appendHookDiagnosticsEvent(params) {
33635
34221
  try {
33636
34222
  const logPath = getHookDiagnosticsLogPath();
33637
- await import_promises19.default.mkdir(import_node_path7.default.dirname(logPath), { recursive: true });
34223
+ await import_promises20.default.mkdir(import_node_path7.default.dirname(logPath), { recursive: true });
33638
34224
  await rotateLogIfNeeded(logPath);
33639
34225
  const event = {
33640
34226
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -33651,14 +34237,14 @@ async function appendHookDiagnosticsEvent(params) {
33651
34237
  message: params.message?.trim() || null,
33652
34238
  fields: normalizeFields(params.fields)
33653
34239
  };
33654
- await import_promises19.default.appendFile(logPath, `${JSON.stringify(event)}
34240
+ await import_promises20.default.appendFile(logPath, `${JSON.stringify(event)}
33655
34241
  `, "utf8");
33656
34242
  } catch {
33657
34243
  }
33658
34244
  }
33659
34245
 
33660
34246
  // src/hook-utils.ts
33661
- var import_promises20 = __toESM(require("fs/promises"), 1);
34247
+ var import_promises21 = __toESM(require("fs/promises"), 1);
33662
34248
  var import_node_path8 = __toESM(require("path"), 1);
33663
34249
  async function readJsonStdin() {
33664
34250
  const chunks = [];
@@ -33722,13 +34308,13 @@ function extractBoolean(input, keys) {
33722
34308
  async function findBoundRepo(startPath) {
33723
34309
  if (!startPath) return null;
33724
34310
  let current = import_node_path8.default.resolve(startPath);
33725
- let stats = await import_promises20.default.stat(current).catch(() => null);
34311
+ let stats = await import_promises21.default.stat(current).catch(() => null);
33726
34312
  if (stats?.isFile()) {
33727
34313
  current = import_node_path8.default.dirname(current);
33728
34314
  }
33729
34315
  while (true) {
33730
34316
  const bindingPath = import_node_path8.default.join(current, ".remix", "config.json");
33731
- const bindingStats = await import_promises20.default.stat(bindingPath).catch(() => null);
34317
+ const bindingStats = await import_promises21.default.stat(bindingPath).catch(() => null);
33732
34318
  if (bindingStats?.isFile()) return current;
33733
34319
  const parent = import_node_path8.default.dirname(current);
33734
34320
  if (parent === current) return null;
@@ -33767,6 +34353,18 @@ function getErrorDetails(error) {
33767
34353
  return { message, hint: null };
33768
34354
  }
33769
34355
  function getRecordingBlockedMessage(status, repoRoot) {
34356
+ if (status.status === "branch_binding_missing") {
34357
+ return {
34358
+ message: "Fallback Remix turn recording was blocked because the current branch does not have a Remix lane binding yet.",
34359
+ hint: status.hint || `Run \`remix_collab_status\` for ${repoRoot}, then initialize or provision the current branch lane before recording work.`
34360
+ };
34361
+ }
34362
+ if (status.status === "family_ambiguous") {
34363
+ return {
34364
+ message: "Fallback Remix turn recording was blocked because multiple canonical Remix families match this repository and the current checkout does not identify which family to use.",
34365
+ hint: status.hint || `Continue from a checkout already bound to the intended family, or run \`remix_collab_init\` with forceNew=true for ${repoRoot} to create a new canonical family.`
34366
+ };
34367
+ }
33770
34368
  switch (status.status) {
33771
34369
  case "not_git_repo":
33772
34370
  return {
@@ -33785,8 +34383,8 @@ function getRecordingBlockedMessage(status, repoRoot) {
33785
34383
  };
33786
34384
  case "branch_mismatch":
33787
34385
  return {
33788
- message: "Fallback Remix turn recording was blocked by the checkout's preferred-branch policy.",
33789
- hint: status.hint || `Repo root: ${repoRoot}`
34386
+ message: "Fallback Remix turn recording was blocked because the current checkout branch does not match the branch expected by the bound Remix lane.",
34387
+ hint: status.hint || `Run \`remix_collab_status\` for ${repoRoot}, then switch back to the expected branch or refresh this branch's binding before recording or syncing.`
33790
34388
  };
33791
34389
  case "metadata_conflict":
33792
34390
  return {