@remixhq/claude-plugin 0.1.15 → 0.1.16

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,273 @@ 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-4L3ZBZUQ.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
+ };
7822
+ }
7823
+ function normalizeBranchName(value) {
7824
+ const normalized = String(value ?? "").trim();
7825
+ return normalized || null;
7826
+ }
7827
+ function normalizeProjectId(value) {
7828
+ const normalized = String(value ?? "").trim();
7829
+ return normalized || null;
7830
+ }
7831
+ function normalizeBranchBinding(value) {
7832
+ if (!value?.currentAppId || !value?.upstreamAppId) return null;
7833
+ return {
7834
+ projectId: normalizeProjectId(value.projectId),
7835
+ currentAppId: value.currentAppId,
7836
+ upstreamAppId: value.upstreamAppId,
7837
+ threadId: value.threadId ?? null,
7838
+ laneId: value.laneId ?? null,
7839
+ bindingMode: value.bindingMode === "legacy" ? "legacy" : "lane"
7840
+ };
7841
+ }
7842
+ function buildResolvedBinding(params) {
7843
+ if (!params.binding) return null;
7844
+ return {
7845
+ schemaVersion: 3,
7846
+ projectId: params.binding.projectId ?? params.fallbackProjectId,
7847
+ currentAppId: params.binding.currentAppId,
7848
+ upstreamAppId: params.binding.upstreamAppId,
7849
+ threadId: params.binding.threadId,
7850
+ repoFingerprint: params.repoFingerprint,
7851
+ remoteUrl: params.remoteUrl,
7852
+ defaultBranch: params.defaultBranch,
7853
+ laneId: params.binding.laneId,
7854
+ branchName: params.branchName,
7855
+ bindingMode: params.binding.bindingMode
7856
+ };
7857
+ }
7858
+ function deriveFallbackProjectId(params) {
7859
+ const candidates = [
7860
+ params.currentBranch ? params.branchBindings[params.currentBranch]?.projectId ?? null : null,
7861
+ params.defaultBranch ? params.branchBindings[params.defaultBranch]?.projectId ?? null : null,
7862
+ ...Object.values(params.branchBindings).map((binding) => binding.projectId),
7863
+ params.legacyProjectId
7864
+ ];
7865
+ for (const candidate of candidates) {
7866
+ if (candidate) return candidate;
7867
+ }
7868
+ return null;
7869
+ }
7870
+ async function readCollabBindingState(repoRoot, options) {
7871
+ try {
7872
+ const persist = options?.persist === true;
7873
+ const filePath = getCollabBindingPath(repoRoot);
7874
+ const raw = await import_promises13.default.readFile(filePath, "utf8");
7875
+ const parsed = JSON.parse(raw);
7876
+ if (!parsed || typeof parsed !== "object") return null;
7877
+ const currentBranch = normalizeBranchName(await getCurrentBranch(repoRoot).catch(() => null));
7878
+ if (parsed.schemaVersion === 1) {
7879
+ if (!parsed.currentAppId || !parsed.upstreamAppId) return null;
7880
+ const projectId = normalizeProjectId(parsed.projectId);
7881
+ const preferredBranch = normalizeBranchName(parsed.preferredBranch ?? parsed.defaultBranch ?? null);
7882
+ const branchKey = preferredBranch ?? currentBranch ?? null;
7883
+ const branchBindings2 = branchKey ? {
7884
+ [branchKey]: {
7885
+ projectId,
7886
+ currentAppId: parsed.currentAppId,
7887
+ upstreamAppId: parsed.upstreamAppId,
7888
+ threadId: parsed.threadId ?? null,
7889
+ laneId: null,
7890
+ bindingMode: "legacy"
7891
+ }
7892
+ } : {};
7893
+ const migratedFile = buildBindingFileV3({
7894
+ repoFingerprint: parsed.repoFingerprint ?? null,
7895
+ remoteUrl: parsed.remoteUrl ?? null,
7896
+ defaultBranch: parsed.defaultBranch ?? null,
7897
+ branchBindings: branchBindings2
7898
+ });
7899
+ if (persist) {
7900
+ try {
7901
+ await writeJsonAtomic(filePath, migratedFile);
7902
+ } catch {
7903
+ }
7904
+ }
7905
+ return {
7906
+ schemaVersion: 3,
7907
+ projectId,
7908
+ repoFingerprint: migratedFile.repoFingerprint,
7909
+ remoteUrl: migratedFile.remoteUrl,
7910
+ defaultBranch: migratedFile.defaultBranch,
7911
+ currentBranch,
7912
+ branchBindings: migratedFile.branchBindings,
7913
+ binding: buildResolvedBinding({
7914
+ fallbackProjectId: projectId,
7915
+ repoFingerprint: migratedFile.repoFingerprint,
7916
+ remoteUrl: migratedFile.remoteUrl,
7917
+ defaultBranch: migratedFile.defaultBranch,
7918
+ branchName: branchKey,
7919
+ binding: branchKey ? migratedFile.branchBindings[branchKey] : null
7920
+ })
7921
+ };
7922
+ }
7923
+ if (parsed.schemaVersion !== 2 && parsed.schemaVersion !== 3) return null;
7924
+ const file = parsed;
7925
+ let shouldPersistNormalizedBranchBindings = false;
7926
+ const legacyProjectId = normalizeProjectId(file.projectId);
7927
+ const branchBindings = Object.fromEntries(
7928
+ Object.entries(file.branchBindings ?? {}).map(([branchName, branchBinding]) => {
7929
+ const normalized = normalizeBranchBinding(branchBinding);
7930
+ const rawProjectId = branchBinding && typeof branchBinding === "object" && "projectId" in branchBinding ? normalizeProjectId(branchBinding.projectId) : null;
7931
+ const rawBindingMode = branchBinding && typeof branchBinding === "object" && "bindingMode" in branchBinding ? branchBinding.bindingMode : null;
7932
+ const hasLegacyPreferredBranch = Boolean(branchBinding) && typeof branchBinding === "object" && "preferredBranch" in branchBinding;
7933
+ let normalizedWithProject = normalized;
7934
+ if (normalizedWithProject && !normalizedWithProject.projectId && legacyProjectId) {
7935
+ normalizedWithProject = {
7936
+ ...normalizedWithProject,
7937
+ projectId: legacyProjectId
7938
+ };
7939
+ }
7940
+ if (normalizedWithProject && (rawBindingMode !== normalizedWithProject.bindingMode || hasLegacyPreferredBranch || rawProjectId !== normalizedWithProject.projectId)) {
7941
+ shouldPersistNormalizedBranchBindings = true;
7942
+ }
7943
+ return [branchName, normalizedWithProject];
7944
+ }).filter((entry) => Boolean(entry[1]))
7945
+ );
7946
+ const legacyExplicitBinding = normalizeBranchBinding(file.explicitBinding ?? null);
7947
+ const legacyExplicitBranch = currentBranch ?? normalizeBranchName(file.defaultBranch);
7948
+ if (legacyExplicitBinding && legacyExplicitBranch) {
7949
+ branchBindings[legacyExplicitBranch] = {
7950
+ ...legacyExplicitBinding,
7951
+ projectId: legacyExplicitBinding.projectId ?? branchBindings[legacyExplicitBranch]?.projectId ?? legacyProjectId,
7952
+ bindingMode: "lane"
7953
+ };
7954
+ shouldPersistNormalizedBranchBindings = true;
7955
+ }
7956
+ if (persist && ("explicitBinding" in file || shouldPersistNormalizedBranchBindings || parsed.schemaVersion === 2)) {
7957
+ try {
7958
+ await writeJsonAtomic(
7959
+ filePath,
7960
+ buildBindingFileV3({
7961
+ repoFingerprint: file.repoFingerprint ?? null,
7962
+ remoteUrl: file.remoteUrl ?? null,
7963
+ defaultBranch: file.defaultBranch ?? null,
7964
+ branchBindings
7965
+ })
7966
+ );
7967
+ } catch {
7968
+ }
7969
+ }
7970
+ const resolvedBranch = currentBranch ?? normalizeBranchName(file.defaultBranch);
7971
+ const fallbackProjectId = deriveFallbackProjectId({
7972
+ branchBindings,
7973
+ currentBranch: resolvedBranch,
7974
+ defaultBranch: normalizeBranchName(file.defaultBranch),
7975
+ legacyProjectId
7976
+ });
7977
+ return {
7978
+ schemaVersion: parsed.schemaVersion,
7979
+ projectId: fallbackProjectId,
7980
+ repoFingerprint: file.repoFingerprint ?? null,
7981
+ remoteUrl: file.remoteUrl ?? null,
7982
+ defaultBranch: file.defaultBranch ?? null,
7983
+ currentBranch,
7984
+ branchBindings,
7985
+ binding: buildResolvedBinding({
7986
+ fallbackProjectId,
7987
+ repoFingerprint: file.repoFingerprint ?? null,
7988
+ remoteUrl: file.remoteUrl ?? null,
7989
+ defaultBranch: file.defaultBranch ?? null,
7990
+ branchName: resolvedBranch,
7991
+ binding: resolvedBranch ? branchBindings[resolvedBranch] ?? null : null
7992
+ })
7993
+ };
7994
+ } catch {
7995
+ return null;
7996
+ }
7997
+ }
7998
+ async function readCollabBinding(repoRoot) {
7999
+ const state = await readCollabBindingState(repoRoot);
8000
+ return state?.binding ?? null;
8001
+ }
8002
+ async function writeCollabBinding(repoRoot, binding) {
8003
+ const filePath = getCollabBindingPath(repoRoot);
8004
+ const currentBranch = normalizeBranchName(await getCurrentBranch(repoRoot).catch(() => null));
8005
+ const branchName = normalizeBranchName(binding.branchName) ?? currentBranch ?? binding.defaultBranch ?? "main";
8006
+ const existing = await readCollabBindingState(repoRoot, { persist: true });
8007
+ const branchBindings = { ...existing?.branchBindings ?? {} };
8008
+ branchBindings[branchName] = {
8009
+ projectId: normalizeProjectId(binding.projectId) ?? branchBindings[branchName]?.projectId ?? existing?.projectId ?? null,
8010
+ currentAppId: binding.currentAppId,
8011
+ upstreamAppId: binding.upstreamAppId,
8012
+ threadId: binding.threadId ?? null,
8013
+ laneId: binding.laneId ?? null,
8014
+ bindingMode: binding.bindingMode ?? "lane"
8015
+ };
8016
+ await writeJsonAtomic(
8017
+ filePath,
8018
+ buildBindingFileV3({
8019
+ repoFingerprint: binding.repoFingerprint ?? null,
8020
+ remoteUrl: binding.remoteUrl ?? null,
8021
+ defaultBranch: binding.defaultBranch ?? null,
8022
+ branchBindings
8023
+ })
8024
+ );
8025
+ return filePath;
8026
+ }
8027
+
8028
+ // node_modules/@remixhq/core/dist/collab.js
7830
8029
  var import_promises15 = __toESM(require("fs/promises"), 1);
7831
- var import_os2 = __toESM(require("os"), 1);
7832
8030
  var import_path4 = __toESM(require("path"), 1);
8031
+ var import_crypto2 = require("crypto");
7833
8032
  var import_promises16 = __toESM(require("fs/promises"), 1);
7834
- var import_os3 = __toESM(require("os"), 1);
8033
+ var import_os2 = __toESM(require("os"), 1);
7835
8034
  var import_path5 = __toESM(require("path"), 1);
8035
+ var import_promises17 = __toESM(require("fs/promises"), 1);
8036
+ var import_os3 = __toESM(require("os"), 1);
8037
+ var import_path6 = __toESM(require("path"), 1);
7836
8038
  function describeBranch(value) {
7837
8039
  const normalized = String(value ?? "").trim();
7838
8040
  return normalized || "(detached)";
7839
8041
  }
7840
- function isPreferredBranchMatch(currentBranch, preferredBranch) {
8042
+ function isBoundBranchMatch(currentBranch, branchName) {
7841
8043
  const current = String(currentBranch ?? "").trim();
7842
- const preferred = String(preferredBranch ?? "").trim();
7843
- if (!preferred || !current) return true;
7844
- return current === preferred;
8044
+ const expected = String(branchName ?? "").trim();
8045
+ if (!expected || !current) return true;
8046
+ return current === expected;
7845
8047
  }
7846
- function buildPreferredBranchMismatchHint(params) {
8048
+ function buildBranchMismatchHint(params) {
7847
8049
  const overrideFlag = params.overrideFlag?.trim() || "--allow-branch-mismatch";
7848
8050
  return [
7849
8051
  `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.`
8052
+ `Bound branch: ${describeBranch(params.branchName)}`,
8053
+ `Switch to ${describeBranch(params.branchName)} or rerun with ${overrideFlag} if this is intentional.`
7852
8054
  ].join("\n");
7853
8055
  }
7854
- function assertPreferredBranchMatch(params) {
8056
+ function assertBoundBranchMatch(params) {
7855
8057
  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}.`, {
8058
+ if (isBoundBranchMatch(params.currentBranch, params.branchName)) return;
8059
+ throw new RemixError(`Current branch does not match this checkout's bound Remix branch while running ${params.operation}.`, {
7858
8060
  code: REMIX_ERROR_CODES.PREFERRED_BRANCH_MISMATCH,
7859
8061
  exitCode: 2,
7860
- hint: buildPreferredBranchMismatchHint({
8062
+ hint: buildBranchMismatchHint({
7861
8063
  currentBranch: params.currentBranch,
7862
- preferredBranch: params.preferredBranch,
8064
+ branchName: params.branchName,
7863
8065
  overrideFlag: params.overrideFlag
7864
8066
  })
7865
8067
  });
@@ -7949,6 +8151,214 @@ async function pollChangeStepReplay(api, appId, replayId) {
7949
8151
  }
7950
8152
  throw new RemixError("Timed out waiting for AI-assisted replay.", { exitCode: 1 });
7951
8153
  }
8154
+ function normalizeBranchName2(value) {
8155
+ const normalized = String(value ?? "").trim();
8156
+ return normalized || null;
8157
+ }
8158
+ function buildBindingFromLane(state, lane) {
8159
+ if (!lane.currentAppId || !lane.upstreamAppId) return null;
8160
+ return {
8161
+ schemaVersion: 3,
8162
+ projectId: lane.projectId ?? state.projectId,
8163
+ currentAppId: lane.currentAppId,
8164
+ upstreamAppId: lane.upstreamAppId,
8165
+ threadId: lane.threadId ?? null,
8166
+ repoFingerprint: lane.repoFingerprint ?? state.repoFingerprint ?? null,
8167
+ remoteUrl: lane.remoteUrl ?? state.remoteUrl ?? null,
8168
+ defaultBranch: lane.defaultBranch ?? state.defaultBranch ?? null,
8169
+ laneId: lane.laneId ?? null,
8170
+ branchName: lane.branchName ?? state.currentBranch ?? null,
8171
+ bindingMode: "lane"
8172
+ };
8173
+ }
8174
+ function shouldPersistRemoteLaneMetadata(localBinding, lane) {
8175
+ return Boolean(
8176
+ !localBinding.laneId && lane.laneId || !localBinding.threadId && lane.threadId || !localBinding.repoFingerprint && lane.repoFingerprint || !localBinding.remoteUrl && lane.remoteUrl || !localBinding.defaultBranch && lane.defaultBranch || !localBinding.branchName && lane.branchName
8177
+ );
8178
+ }
8179
+ function shouldRequireRemoteLaneForCurrentBranch(params) {
8180
+ if (!params.currentBranch) return false;
8181
+ const defaultBranch = normalizeBranchName2(params.defaultBranch);
8182
+ if (params.currentBranch === defaultBranch) return false;
8183
+ return !params.binding.laneId || params.binding.currentAppId === params.binding.upstreamAppId;
8184
+ }
8185
+ async function persistResolvedLane(repoRoot, binding) {
8186
+ await writeCollabBinding(repoRoot, {
8187
+ projectId: binding.projectId,
8188
+ currentAppId: binding.currentAppId,
8189
+ upstreamAppId: binding.upstreamAppId,
8190
+ threadId: binding.threadId,
8191
+ repoFingerprint: binding.repoFingerprint,
8192
+ remoteUrl: binding.remoteUrl,
8193
+ defaultBranch: binding.defaultBranch,
8194
+ laneId: binding.laneId,
8195
+ branchName: binding.branchName,
8196
+ bindingMode: binding.bindingMode
8197
+ });
8198
+ return readCollabBinding(repoRoot);
8199
+ }
8200
+ async function resolveActiveLaneBinding(params) {
8201
+ const state = await readCollabBindingState(params.repoRoot);
8202
+ if (!state) {
8203
+ return { status: "not_bound", currentBranch: null };
8204
+ }
8205
+ const currentBranch = normalizeBranchName2(state.currentBranch);
8206
+ const localBinding = state.binding;
8207
+ if (localBinding) {
8208
+ const requireRemoteLane = shouldRequireRemoteLaneForCurrentBranch({
8209
+ binding: localBinding,
8210
+ currentBranch,
8211
+ defaultBranch: state.defaultBranch
8212
+ });
8213
+ if (!params.api || !currentBranch) {
8214
+ return {
8215
+ status: "resolved",
8216
+ source: "local",
8217
+ binding: localBinding,
8218
+ currentBranch
8219
+ };
8220
+ }
8221
+ const laneResp2 = await params.api.resolveProjectLaneBinding({
8222
+ projectId: localBinding.projectId ?? state.projectId ?? void 0,
8223
+ repoFingerprint: state.repoFingerprint ?? void 0,
8224
+ remoteUrl: state.remoteUrl ?? void 0,
8225
+ defaultBranch: state.defaultBranch ?? void 0,
8226
+ branchName: currentBranch
8227
+ });
8228
+ const lane2 = unwrapResponseObject(laneResp2, "project lane binding");
8229
+ if (lane2.status === "resolved") {
8230
+ const resolvedBranch = normalizeBranchName2(lane2.branchName);
8231
+ const resolvedProjectId = lane2.projectId ?? state.projectId;
8232
+ const branchConflict = Boolean(resolvedBranch && localBinding.branchName && resolvedBranch !== localBinding.branchName);
8233
+ const appConflict = Boolean(lane2.currentAppId && lane2.currentAppId !== localBinding.currentAppId);
8234
+ const upstreamConflict = Boolean(lane2.upstreamAppId && lane2.upstreamAppId !== localBinding.upstreamAppId);
8235
+ const projectConflict = Boolean(resolvedProjectId && localBinding.projectId && resolvedProjectId !== localBinding.projectId);
8236
+ if (branchConflict || appConflict || upstreamConflict || projectConflict) {
8237
+ if (requireRemoteLane) {
8238
+ const binding = buildBindingFromLane(state, lane2);
8239
+ if (binding) {
8240
+ return {
8241
+ status: "resolved",
8242
+ source: "remote",
8243
+ binding,
8244
+ currentBranch
8245
+ };
8246
+ }
8247
+ }
8248
+ return {
8249
+ status: "binding_conflict",
8250
+ binding: localBinding,
8251
+ resolvedLane: lane2,
8252
+ currentBranch
8253
+ };
8254
+ }
8255
+ if (shouldPersistRemoteLaneMetadata(localBinding, lane2)) {
8256
+ const binding = buildBindingFromLane(state, lane2);
8257
+ if (binding) {
8258
+ return {
8259
+ status: "resolved",
8260
+ source: "remote",
8261
+ binding,
8262
+ currentBranch
8263
+ };
8264
+ }
8265
+ }
8266
+ }
8267
+ if (requireRemoteLane) {
8268
+ return {
8269
+ status: "missing_branch_binding",
8270
+ currentBranch,
8271
+ projectId: state.projectId,
8272
+ repoFingerprint: state.repoFingerprint,
8273
+ remoteUrl: state.remoteUrl,
8274
+ defaultBranch: state.defaultBranch,
8275
+ upstreamAppId: localBinding.upstreamAppId ?? null,
8276
+ threadId: localBinding.threadId ?? null
8277
+ };
8278
+ }
8279
+ return {
8280
+ status: "resolved",
8281
+ source: "local",
8282
+ binding: localBinding,
8283
+ currentBranch
8284
+ };
8285
+ }
8286
+ if (!params.api || !currentBranch) {
8287
+ return {
8288
+ status: "missing_branch_binding",
8289
+ currentBranch,
8290
+ projectId: state.projectId,
8291
+ repoFingerprint: state.repoFingerprint,
8292
+ remoteUrl: state.remoteUrl,
8293
+ defaultBranch: state.defaultBranch,
8294
+ upstreamAppId: null,
8295
+ threadId: null
8296
+ };
8297
+ }
8298
+ const laneResp = await params.api.resolveProjectLaneBinding({
8299
+ projectId: state.projectId ?? void 0,
8300
+ repoFingerprint: state.repoFingerprint ?? void 0,
8301
+ remoteUrl: state.remoteUrl ?? void 0,
8302
+ defaultBranch: state.defaultBranch ?? void 0,
8303
+ branchName: currentBranch
8304
+ });
8305
+ const lane = unwrapResponseObject(laneResp, "project lane binding");
8306
+ if (lane.status === "resolved") {
8307
+ const binding = buildBindingFromLane(state, lane);
8308
+ if (binding) {
8309
+ return {
8310
+ status: "resolved",
8311
+ source: "remote",
8312
+ binding,
8313
+ currentBranch
8314
+ };
8315
+ }
8316
+ }
8317
+ if (lane.status === "binding_not_found") {
8318
+ return { status: "not_bound", currentBranch };
8319
+ }
8320
+ return {
8321
+ status: "missing_branch_binding",
8322
+ currentBranch,
8323
+ projectId: lane.projectId ?? state.projectId,
8324
+ repoFingerprint: lane.repoFingerprint ?? state.repoFingerprint,
8325
+ remoteUrl: lane.remoteUrl ?? state.remoteUrl,
8326
+ defaultBranch: lane.defaultBranch ?? state.defaultBranch,
8327
+ upstreamAppId: lane.upstreamAppId ?? null,
8328
+ threadId: lane.threadId ?? null
8329
+ };
8330
+ }
8331
+ async function ensureActiveLaneBinding(params) {
8332
+ const resolved = await resolveActiveLaneBinding({
8333
+ repoRoot: params.repoRoot,
8334
+ api: params.api
8335
+ });
8336
+ if (resolved.status === "resolved") {
8337
+ if (resolved.source === "local") {
8338
+ return resolved.binding;
8339
+ }
8340
+ return persistResolvedLane(params.repoRoot, resolved.binding);
8341
+ }
8342
+ if (resolved.status === "binding_conflict") {
8343
+ throw new RemixError("Current branch binding conflicts with the server-resolved Remix lane.", {
8344
+ exitCode: 2,
8345
+ hint: `Local app ${resolved.binding.currentAppId}; server app ${resolved.resolvedLane.currentAppId ?? "(unknown)"}. Repair the branch binding before running ${params.operation ?? "this command"}.`
8346
+ });
8347
+ }
8348
+ if (resolved.status === "not_bound") {
8349
+ return null;
8350
+ }
8351
+ if (!resolved.currentBranch) {
8352
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
8353
+ exitCode: 2,
8354
+ hint: `Switch to a named branch before running ${params.operation ?? "this command"}.`
8355
+ });
8356
+ }
8357
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
8358
+ exitCode: 2,
8359
+ hint: `Run \`remix collab init\` on branch ${resolved.currentBranch} before running ${params.operation ?? "this command"}.`
8360
+ });
8361
+ }
7952
8362
  async function collabRecordingPreflight(params) {
7953
8363
  let repoRoot;
7954
8364
  try {
@@ -7960,7 +8370,7 @@ async function collabRecordingPreflight(params) {
7960
8370
  repoRoot: null,
7961
8371
  appId: null,
7962
8372
  currentBranch: null,
7963
- preferredBranch: null,
8373
+ branchName: null,
7964
8374
  headCommitHash: null,
7965
8375
  worktreeClean: false,
7966
8376
  syncStatus: null,
@@ -7972,14 +8382,14 @@ async function collabRecordingPreflight(params) {
7972
8382
  hint: message
7973
8383
  };
7974
8384
  }
7975
- const binding = await readCollabBinding(repoRoot);
7976
- if (!binding) {
8385
+ const bindingResolution = await resolveActiveLaneBinding({ repoRoot, api: params.api });
8386
+ if (bindingResolution.status === "not_bound") {
7977
8387
  return {
7978
8388
  status: "not_bound",
7979
8389
  repoRoot,
7980
8390
  appId: null,
7981
8391
  currentBranch: null,
7982
- preferredBranch: null,
8392
+ branchName: null,
7983
8393
  headCommitHash: null,
7984
8394
  worktreeClean: false,
7985
8395
  syncStatus: null,
@@ -7991,19 +8401,56 @@ async function collabRecordingPreflight(params) {
7991
8401
  hint: "Run `remix collab init` first."
7992
8402
  };
7993
8403
  }
8404
+ if (bindingResolution.status === "missing_branch_binding") {
8405
+ return {
8406
+ status: "branch_binding_missing",
8407
+ repoRoot,
8408
+ appId: null,
8409
+ currentBranch: bindingResolution.currentBranch,
8410
+ branchName: bindingResolution.currentBranch,
8411
+ headCommitHash: null,
8412
+ worktreeClean: false,
8413
+ syncStatus: null,
8414
+ syncTargetCommitHash: null,
8415
+ syncTargetCommitId: null,
8416
+ reconcileTargetHeadCommitHash: null,
8417
+ reconcileTargetHeadCommitId: null,
8418
+ warnings: [],
8419
+ hint: `Current branch ${bindingResolution.currentBranch ?? "(detached)"} is not yet bound to a Remix lane.`
8420
+ };
8421
+ }
8422
+ if (bindingResolution.status === "binding_conflict") {
8423
+ return {
8424
+ status: "metadata_conflict",
8425
+ repoRoot,
8426
+ appId: bindingResolution.binding.currentAppId,
8427
+ currentBranch: bindingResolution.currentBranch,
8428
+ branchName: bindingResolution.binding.branchName,
8429
+ headCommitHash: null,
8430
+ worktreeClean: false,
8431
+ syncStatus: null,
8432
+ syncTargetCommitHash: null,
8433
+ syncTargetCommitId: null,
8434
+ reconcileTargetHeadCommitHash: null,
8435
+ reconcileTargetHeadCommitId: null,
8436
+ warnings: [],
8437
+ 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.`
8438
+ };
8439
+ }
8440
+ const binding = bindingResolution.binding;
7994
8441
  const [currentBranch, headCommitHash, worktreeStatus] = await Promise.all([
7995
8442
  getCurrentBranch(repoRoot),
7996
8443
  getHeadCommitHash(repoRoot),
7997
8444
  getWorktreeStatus(repoRoot)
7998
8445
  ]);
7999
- const preferredBranch = binding.preferredBranch ?? null;
8446
+ const branchName = binding.branchName ?? null;
8000
8447
  if (!headCommitHash) {
8001
8448
  return {
8002
8449
  status: "missing_head",
8003
8450
  repoRoot,
8004
8451
  appId: binding.currentAppId,
8005
8452
  currentBranch,
8006
- preferredBranch,
8453
+ branchName,
8007
8454
  headCommitHash: null,
8008
8455
  worktreeClean: worktreeStatus.isClean,
8009
8456
  syncStatus: null,
@@ -8015,13 +8462,13 @@ async function collabRecordingPreflight(params) {
8015
8462
  hint: "Failed to resolve local HEAD commit."
8016
8463
  };
8017
8464
  }
8018
- if (!params.allowBranchMismatch && !isPreferredBranchMatch(currentBranch, preferredBranch)) {
8465
+ if (!params.allowBranchMismatch && !isBoundBranchMatch(currentBranch, branchName)) {
8019
8466
  return {
8020
8467
  status: "branch_mismatch",
8021
8468
  repoRoot,
8022
8469
  appId: binding.currentAppId,
8023
8470
  currentBranch,
8024
- preferredBranch,
8471
+ branchName,
8025
8472
  headCommitHash,
8026
8473
  worktreeClean: worktreeStatus.isClean,
8027
8474
  syncStatus: null,
@@ -8030,9 +8477,9 @@ async function collabRecordingPreflight(params) {
8030
8477
  reconcileTargetHeadCommitHash: null,
8031
8478
  reconcileTargetHeadCommitId: null,
8032
8479
  warnings: [],
8033
- hint: buildPreferredBranchMismatchHint({
8480
+ hint: buildBranchMismatchHint({
8034
8481
  currentBranch,
8035
- preferredBranch
8482
+ branchName
8036
8483
  })
8037
8484
  };
8038
8485
  }
@@ -8050,7 +8497,7 @@ async function collabRecordingPreflight(params) {
8050
8497
  repoRoot,
8051
8498
  appId: binding.currentAppId,
8052
8499
  currentBranch,
8053
- preferredBranch,
8500
+ branchName,
8054
8501
  headCommitHash,
8055
8502
  worktreeClean: worktreeStatus.isClean,
8056
8503
  syncStatus: sync.status,
@@ -8068,7 +8515,7 @@ async function collabRecordingPreflight(params) {
8068
8515
  repoRoot,
8069
8516
  appId: binding.currentAppId,
8070
8517
  currentBranch,
8071
- preferredBranch,
8518
+ branchName,
8072
8519
  headCommitHash,
8073
8520
  worktreeClean: worktreeStatus.isClean,
8074
8521
  syncStatus: sync.status,
@@ -8093,7 +8540,7 @@ async function collabRecordingPreflight(params) {
8093
8540
  repoRoot,
8094
8541
  appId: binding.currentAppId,
8095
8542
  currentBranch,
8096
- preferredBranch,
8543
+ branchName,
8097
8544
  headCommitHash,
8098
8545
  worktreeClean: worktreeStatus.isClean,
8099
8546
  syncStatus: sync.status,
@@ -8111,7 +8558,7 @@ async function collabRecordingPreflight(params) {
8111
8558
  repoRoot,
8112
8559
  appId: binding.currentAppId,
8113
8560
  currentBranch,
8114
- preferredBranch,
8561
+ branchName,
8115
8562
  headCommitHash,
8116
8563
  worktreeClean: worktreeStatus.isClean,
8117
8564
  syncStatus: sync.status,
@@ -8128,7 +8575,7 @@ async function collabRecordingPreflight(params) {
8128
8575
  repoRoot,
8129
8576
  appId: binding.currentAppId,
8130
8577
  currentBranch,
8131
- preferredBranch,
8578
+ branchName,
8132
8579
  headCommitHash,
8133
8580
  worktreeClean: worktreeStatus.isClean,
8134
8581
  syncStatus: sync.status,
@@ -8162,12 +8609,12 @@ function createOwner(params) {
8162
8609
  };
8163
8610
  }
8164
8611
  async function writeOwnerMetadata(ownerPath, owner) {
8165
- await import_promises15.default.writeFile(ownerPath, `${JSON.stringify(owner, null, 2)}
8612
+ await import_promises16.default.writeFile(ownerPath, `${JSON.stringify(owner, null, 2)}
8166
8613
  `, "utf8");
8167
8614
  }
8168
8615
  async function readOwnerMetadata(ownerPath) {
8169
8616
  try {
8170
- const raw = await import_promises15.default.readFile(ownerPath, "utf8");
8617
+ const raw = await import_promises16.default.readFile(ownerPath, "utf8");
8171
8618
  const parsed = JSON.parse(raw);
8172
8619
  if (!parsed || typeof parsed !== "object") return null;
8173
8620
  if (!parsed.operation || !parsed.repoRoot || typeof parsed.pid !== "number" || !parsed.startedAt || !parsed.heartbeatAt) {
@@ -8204,23 +8651,23 @@ async function getLastKnownUpdateMs(lockDir, ownerPath, owner) {
8204
8651
  if (Number.isFinite(heartbeatMs)) return heartbeatMs;
8205
8652
  const startedMs = owner ? Date.parse(owner.startedAt) : Number.NaN;
8206
8653
  if (Number.isFinite(startedMs)) return startedMs;
8207
- const stat = await import_promises15.default.stat(ownerPath).catch(() => null);
8654
+ const stat = await import_promises16.default.stat(ownerPath).catch(() => null);
8208
8655
  if (stat) return stat.mtimeMs;
8209
- const dirStat = await import_promises15.default.stat(lockDir).catch(() => null);
8656
+ const dirStat = await import_promises16.default.stat(lockDir).catch(() => null);
8210
8657
  if (dirStat) return dirStat.mtimeMs;
8211
8658
  return 0;
8212
8659
  }
8213
8660
  async function ensureLockDir(lockDir) {
8214
- await import_promises15.default.mkdir(import_path4.default.dirname(lockDir), { recursive: true });
8661
+ await import_promises16.default.mkdir(import_path5.default.dirname(lockDir), { recursive: true });
8215
8662
  }
8216
8663
  async function tryAcquireLock(lockDir, ownerPath, owner) {
8217
8664
  try {
8218
8665
  await ensureLockDir(lockDir);
8219
- await import_promises15.default.mkdir(lockDir);
8666
+ await import_promises16.default.mkdir(lockDir);
8220
8667
  try {
8221
8668
  await writeOwnerMetadata(ownerPath, owner);
8222
8669
  } catch (error) {
8223
- await import_promises15.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8670
+ await import_promises16.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8224
8671
  throw error;
8225
8672
  }
8226
8673
  return true;
@@ -8269,7 +8716,7 @@ async function acquirePhysicalLock(lockDir, ownerPath, owner, options) {
8269
8716
  const alive = await isProcessAlive(currentOwner2);
8270
8717
  if (ageMs >= options.staleMs && alive !== true) {
8271
8718
  notices.push(buildStaleRecoveryNotice(currentOwner2));
8272
- await import_promises15.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8719
+ await import_promises16.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8273
8720
  continue;
8274
8721
  }
8275
8722
  await sleep2(RETRY_DELAY_MS);
@@ -8294,7 +8741,7 @@ function startHeartbeat(lockDir, ownerPath, owner, heartbeatMs) {
8294
8741
  };
8295
8742
  owner.heartbeatAt = nextOwner.heartbeatAt;
8296
8743
  void writeOwnerMetadata(ownerPath, nextOwner).catch(() => void 0);
8297
- void import_promises15.default.utimes(lockDir, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date()).catch(() => void 0);
8744
+ void import_promises16.default.utimes(lockDir, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date()).catch(() => void 0);
8298
8745
  }, heartbeatMs);
8299
8746
  }
8300
8747
  async function releaseReentrantLock(lockDir) {
@@ -8304,12 +8751,12 @@ async function releaseReentrantLock(lockDir) {
8304
8751
  if (held.count > 0) return;
8305
8752
  clearInterval(held.heartbeatTimer);
8306
8753
  heldLocks.delete(lockDir);
8307
- await import_promises15.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8754
+ await import_promises16.default.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
8308
8755
  }
8309
8756
  async function withRepoMutationLock(options, fn) {
8310
8757
  const repoRoot = await findGitRoot(options.cwd);
8311
8758
  const gitCommonDir = await getGitCommonDir(repoRoot);
8312
- const lockDir = import_path4.default.join(gitCommonDir, "remix", "locks", "repo-mutation.lock");
8759
+ const lockDir = import_path5.default.join(gitCommonDir, "remix", "locks", "repo-mutation.lock");
8313
8760
  const owner = createOwner({
8314
8761
  operation: options.operation,
8315
8762
  repoRoot,
@@ -8321,11 +8768,11 @@ async function withRepoMutationLock(options, fn) {
8321
8768
  const existing = heldLocks.get(lockDir);
8322
8769
  let notices = [];
8323
8770
  if (!existing) {
8324
- notices = await acquirePhysicalLock(lockDir, import_path4.default.join(lockDir, "owner.json"), owner, {
8771
+ notices = await acquirePhysicalLock(lockDir, import_path5.default.join(lockDir, "owner.json"), owner, {
8325
8772
  acquireTimeoutMs,
8326
8773
  staleMs
8327
8774
  });
8328
- const ownerPath = import_path4.default.join(lockDir, "owner.json");
8775
+ const ownerPath = import_path5.default.join(lockDir, "owner.json");
8329
8776
  heldLocks.set(lockDir, {
8330
8777
  count: 1,
8331
8778
  lockDir,
@@ -8350,7 +8797,11 @@ async function withRepoMutationLock(options, fn) {
8350
8797
  }
8351
8798
  async function collabSync(params) {
8352
8799
  const repoRoot = await findGitRoot(params.cwd);
8353
- const binding = await readCollabBinding(repoRoot);
8800
+ const binding = await ensureActiveLaneBinding({
8801
+ repoRoot,
8802
+ api: params.api,
8803
+ operation: "`remix collab sync`"
8804
+ });
8354
8805
  if (!binding) {
8355
8806
  throw new RemixError("Repository is not bound to Remix.", {
8356
8807
  exitCode: 2,
@@ -8359,9 +8810,9 @@ async function collabSync(params) {
8359
8810
  }
8360
8811
  await ensureCleanWorktree(repoRoot);
8361
8812
  const branch = await requireCurrentBranch(repoRoot);
8362
- assertPreferredBranchMatch({
8813
+ assertBoundBranchMatch({
8363
8814
  currentBranch: branch,
8364
- preferredBranch: binding.preferredBranch,
8815
+ branchName: binding.branchName,
8365
8816
  allowBranchMismatch: params.allowBranchMismatch,
8366
8817
  operation: "`remix collab sync`"
8367
8818
  });
@@ -8438,16 +8889,16 @@ async function collabSync(params) {
8438
8889
  });
8439
8890
  await ensureCleanWorktree(lockedRepoRoot);
8440
8891
  const lockedBranch = await requireCurrentBranch(lockedRepoRoot);
8441
- assertPreferredBranchMatch({
8892
+ assertBoundBranchMatch({
8442
8893
  currentBranch: lockedBranch,
8443
- preferredBranch: binding.preferredBranch,
8894
+ branchName: binding.branchName,
8444
8895
  allowBranchMismatch: params.allowBranchMismatch,
8445
8896
  operation: "`remix collab sync`"
8446
8897
  });
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");
8898
+ const tempDir = await import_promises17.default.mkdtemp(import_path6.default.join(import_os3.default.tmpdir(), "remix-sync-"));
8899
+ const bundlePath = import_path6.default.join(tempDir, "sync-local.bundle");
8449
8900
  try {
8450
- await import_promises16.default.writeFile(bundlePath, Buffer.from(bundleBase64, "base64"));
8901
+ await import_promises17.default.writeFile(bundlePath, Buffer.from(bundleBase64, "base64"));
8451
8902
  await importGitBundle(lockedRepoRoot, bundlePath, bundleRef);
8452
8903
  await ensureCommitExists(lockedRepoRoot, sync.targetCommitHash);
8453
8904
  const localCommitHash = await fastForwardToCommit(lockedRepoRoot, sync.targetCommitHash);
@@ -8459,7 +8910,7 @@ async function collabSync(params) {
8459
8910
  ...warnings.length > 0 ? { warnings } : {}
8460
8911
  };
8461
8912
  } finally {
8462
- await import_promises16.default.rm(tempDir, { recursive: true, force: true });
8913
+ await import_promises17.default.rm(tempDir, { recursive: true, force: true });
8463
8914
  }
8464
8915
  }
8465
8916
  );
@@ -8471,6 +8922,12 @@ function assertSupportedRecordingPreflight(preflight) {
8471
8922
  hint: preflight.hint
8472
8923
  });
8473
8924
  }
8925
+ if (preflight.status === "branch_binding_missing") {
8926
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
8927
+ exitCode: 2,
8928
+ hint: preflight.hint
8929
+ });
8930
+ }
8474
8931
  if (preflight.status === "not_git_repo") {
8475
8932
  throw new RemixError(preflight.hint || "Not inside a git repository.", {
8476
8933
  exitCode: 2,
@@ -8484,9 +8941,9 @@ function assertSupportedRecordingPreflight(preflight) {
8484
8941
  });
8485
8942
  }
8486
8943
  if (preflight.status === "branch_mismatch") {
8487
- assertPreferredBranchMatch({
8944
+ assertBoundBranchMatch({
8488
8945
  currentBranch: preflight.currentBranch,
8489
- preferredBranch: preflight.preferredBranch,
8946
+ branchName: preflight.branchName,
8490
8947
  allowBranchMismatch: false,
8491
8948
  operation: "`remix collab add`"
8492
8949
  });
@@ -8506,7 +8963,11 @@ function assertSupportedRecordingPreflight(preflight) {
8506
8963
  }
8507
8964
  async function collabAdd(params) {
8508
8965
  const repoRoot = await findGitRoot(params.cwd);
8509
- const binding = await readCollabBinding(repoRoot);
8966
+ const binding = await ensureActiveLaneBinding({
8967
+ repoRoot,
8968
+ api: params.api,
8969
+ operation: "`remix collab add`"
8970
+ });
8510
8971
  if (!binding) {
8511
8972
  throw new RemixError("Repository is not bound to Remix.", {
8512
8973
  exitCode: 2,
@@ -8527,9 +8988,9 @@ async function collabAdd(params) {
8527
8988
  });
8528
8989
  assertSupportedRecordingPreflight(preflight);
8529
8990
  const branch = preflight.currentBranch;
8530
- assertPreferredBranchMatch({
8991
+ assertBoundBranchMatch({
8531
8992
  currentBranch: branch,
8532
- preferredBranch: binding.preferredBranch,
8993
+ branchName: binding.branchName,
8533
8994
  allowBranchMismatch: params.allowBranchMismatch,
8534
8995
  operation: "`remix collab add`"
8535
8996
  });
@@ -8609,7 +9070,7 @@ async function collabAdd(params) {
8609
9070
  const replayResp = await params.api.startChangeStepReplay(binding.currentAppId, {
8610
9071
  prompt,
8611
9072
  assistantResponse: assistantResponse ?? void 0,
8612
- diff: await import_promises14.default.readFile(preserved.preservedDiffPath, "utf8"),
9073
+ diff: await import_promises15.default.readFile(preserved.preservedDiffPath, "utf8"),
8613
9074
  baseCommitHash: preserved.baseHeadCommitHash,
8614
9075
  targetHeadCommitHash: headCommitHash,
8615
9076
  expectedPaths: preserved.stagePlan.expectedPaths,
@@ -8702,6 +9163,7 @@ async function collabAdd(params) {
8702
9163
  });
8703
9164
  const resp = await params.api.createChangeStep(binding.currentAppId, {
8704
9165
  threadId: binding.threadId ?? void 0,
9166
+ collabLaneId: binding.laneId ?? void 0,
8705
9167
  prompt,
8706
9168
  assistantResponse: assistantResponse ?? void 0,
8707
9169
  diff,
@@ -8742,7 +9204,7 @@ async function collabAdd(params) {
8742
9204
  dryRun: false,
8743
9205
  allowBranchMismatch: params.allowBranchMismatch
8744
9206
  });
8745
- await import_promises14.default.rm(import_path3.default.dirname(backupPath), { recursive: true, force: true }).catch(() => void 0);
9207
+ await import_promises15.default.rm(import_path4.default.dirname(backupPath), { recursive: true, force: true }).catch(() => void 0);
8746
9208
  } catch (err) {
8747
9209
  const detail = formatCliErrorDetail(err);
8748
9210
  const hint = [
@@ -8775,6 +9237,12 @@ function assertSupportedRecordingPreflight2(preflight) {
8775
9237
  hint: preflight.hint
8776
9238
  });
8777
9239
  }
9240
+ if (preflight.status === "branch_binding_missing") {
9241
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
9242
+ exitCode: 2,
9243
+ hint: preflight.hint
9244
+ });
9245
+ }
8778
9246
  if (preflight.status === "not_git_repo") {
8779
9247
  throw new RemixError(preflight.hint || "Not inside a git repository.", {
8780
9248
  exitCode: 2,
@@ -8788,9 +9256,9 @@ function assertSupportedRecordingPreflight2(preflight) {
8788
9256
  });
8789
9257
  }
8790
9258
  if (preflight.status === "branch_mismatch") {
8791
- assertPreferredBranchMatch({
9259
+ assertBoundBranchMatch({
8792
9260
  currentBranch: preflight.currentBranch,
8793
- preferredBranch: preflight.preferredBranch,
9261
+ branchName: preflight.branchName,
8794
9262
  allowBranchMismatch: false,
8795
9263
  operation: "`remix collab record-turn`"
8796
9264
  });
@@ -8810,7 +9278,11 @@ function assertSupportedRecordingPreflight2(preflight) {
8810
9278
  }
8811
9279
  async function collabRecordTurn(params) {
8812
9280
  const repoRoot = await findGitRoot(params.cwd);
8813
- const binding = await readCollabBinding(repoRoot);
9281
+ const binding = await ensureActiveLaneBinding({
9282
+ repoRoot,
9283
+ api: params.api,
9284
+ operation: "`remix collab record-turn`"
9285
+ });
8814
9286
  if (!binding) {
8815
9287
  throw new RemixError("Repository is not bound to Remix.", {
8816
9288
  exitCode: 2,
@@ -8842,9 +9314,9 @@ async function collabRecordTurn(params) {
8842
9314
  });
8843
9315
  }
8844
9316
  const branch = await getCurrentBranch(repoRoot);
8845
- assertPreferredBranchMatch({
9317
+ assertBoundBranchMatch({
8846
9318
  currentBranch: branch,
8847
- preferredBranch: binding.preferredBranch,
9319
+ branchName: binding.branchName,
8848
9320
  allowBranchMismatch: params.allowBranchMismatch,
8849
9321
  operation: "`remix collab record-turn`"
8850
9322
  });
@@ -8858,6 +9330,7 @@ async function collabRecordTurn(params) {
8858
9330
  });
8859
9331
  const resp = await params.api.createCollabTurn(binding.currentAppId, {
8860
9332
  threadId: binding.threadId ?? void 0,
9333
+ collabLaneId: binding.laneId ?? void 0,
8861
9334
  prompt,
8862
9335
  assistantResponse,
8863
9336
  actor: params.actor,
@@ -8870,10 +9343,11 @@ async function collabRecordTurn(params) {
8870
9343
  },
8871
9344
  idempotencyKey
8872
9345
  });
8873
- return unwrapResponseObject(resp, "collab turn");
9346
+ const turn = unwrapResponseObject(resp, "collab turn");
9347
+ return turn;
8874
9348
  }
8875
9349
 
8876
- // node_modules/@remixhq/core/dist/chunk-B5S3PUIR.js
9350
+ // node_modules/@remixhq/core/dist/chunk-BNKPTE2U.js
8877
9351
  async function readJsonSafe(res) {
8878
9352
  const ct = res.headers.get("content-type") ?? "";
8879
9353
  if (!ct.toLowerCase().includes("application/json")) return null;
@@ -8971,8 +9445,20 @@ function createApiClient(config, opts) {
8971
9445
  const qs = new URLSearchParams();
8972
9446
  if (params.repoFingerprint) qs.set("repoFingerprint", params.repoFingerprint);
8973
9447
  if (params.remoteUrl) qs.set("remoteUrl", params.remoteUrl);
9448
+ if (params.branchName) qs.set("branchName", params.branchName);
8974
9449
  return request(`/v1/projects/bindings/resolve?${qs.toString()}`, { method: "GET" });
8975
9450
  },
9451
+ resolveProjectLaneBinding: (params) => {
9452
+ const qs = new URLSearchParams();
9453
+ if (params.projectId) qs.set("projectId", params.projectId);
9454
+ if (params.repoFingerprint) qs.set("repoFingerprint", params.repoFingerprint);
9455
+ if (params.remoteUrl) qs.set("remoteUrl", params.remoteUrl);
9456
+ if (params.defaultBranch) qs.set("defaultBranch", params.defaultBranch);
9457
+ qs.set("branchName", params.branchName);
9458
+ return request(`/v1/projects/bindings/resolve-lane?${qs.toString()}`, { method: "GET" });
9459
+ },
9460
+ ensureProjectLaneBinding: (payload) => request("/v1/projects/bindings/ensure-lane", { method: "POST", body: JSON.stringify(payload) }),
9461
+ bootstrapFreshProjectLane: (payload) => request("/v1/projects/bindings/bootstrap-fresh-lane", { method: "POST", body: JSON.stringify(payload) }),
8976
9462
  autoEnableDeveloper: () => request("/v1/developer/auto-enable", { method: "POST" }),
8977
9463
  listClientApps: (params) => {
8978
9464
  const qs = params?.orgId ? `?orgId=${encodeURIComponent(params.orgId)}` : "";
@@ -9036,6 +9522,7 @@ function createApiClient(config, opts) {
9036
9522
  if (params?.offset !== void 0) qs.set("offset", String(params.offset));
9037
9523
  if (params?.changeStepId) qs.set("changeStepId", params.changeStepId);
9038
9524
  if (params?.threadId) qs.set("threadId", params.threadId);
9525
+ if (params?.collabLaneId) qs.set("collabLaneId", params.collabLaneId);
9039
9526
  if (params?.createdAfter) qs.set("createdAfter", params.createdAfter);
9040
9527
  if (params?.createdBefore) qs.set("createdBefore", params.createdBefore);
9041
9528
  const suffix = qs.toString() ? `?${qs.toString()}` : "";
@@ -13296,9 +13783,9 @@ var coerce = {
13296
13783
  var NEVER = INVALID;
13297
13784
 
13298
13785
  // node_modules/@remixhq/core/dist/chunk-EVWDYCBL.js
13299
- var import_promises17 = __toESM(require("fs/promises"), 1);
13786
+ var import_promises18 = __toESM(require("fs/promises"), 1);
13300
13787
  var import_os4 = __toESM(require("os"), 1);
13301
- var import_path6 = __toESM(require("path"), 1);
13788
+ var import_path7 = __toESM(require("path"), 1);
13302
13789
 
13303
13790
  // node_modules/tslib/tslib.es6.mjs
13304
13791
  function __rest(s, e) {
@@ -32957,7 +33444,7 @@ var storedSessionSchema = external_exports.object({
32957
33444
  function xdgConfigHome() {
32958
33445
  const value = process.env.XDG_CONFIG_HOME;
32959
33446
  if (typeof value === "string" && value.trim()) return value;
32960
- return import_path6.default.join(import_os4.default.homedir(), ".config");
33447
+ return import_path7.default.join(import_os4.default.homedir(), ".config");
32961
33448
  }
32962
33449
  async function maybeLoadKeytar() {
32963
33450
  try {
@@ -32975,31 +33462,31 @@ async function maybeLoadKeytar() {
32975
33462
  return null;
32976
33463
  }
32977
33464
  async function ensurePathPermissions(filePath) {
32978
- const dir = import_path6.default.dirname(filePath);
32979
- await import_promises17.default.mkdir(dir, { recursive: true });
33465
+ const dir = import_path7.default.dirname(filePath);
33466
+ await import_promises18.default.mkdir(dir, { recursive: true });
32980
33467
  try {
32981
- await import_promises17.default.chmod(dir, 448);
33468
+ await import_promises18.default.chmod(dir, 448);
32982
33469
  } catch {
32983
33470
  }
32984
33471
  try {
32985
- await import_promises17.default.chmod(filePath, 384);
33472
+ await import_promises18.default.chmod(filePath, 384);
32986
33473
  } catch {
32987
33474
  }
32988
33475
  }
32989
- async function writeJsonAtomic(filePath, value) {
32990
- await import_promises17.default.mkdir(import_path6.default.dirname(filePath), { recursive: true });
33476
+ async function writeJsonAtomic2(filePath, value) {
33477
+ await import_promises18.default.mkdir(import_path7.default.dirname(filePath), { recursive: true });
32991
33478
  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);
33479
+ await import_promises18.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
33480
+ await import_promises18.default.rename(tmpPath, filePath);
32994
33481
  }
32995
33482
  async function writeSessionFileFallback(filePath, session) {
32996
- await writeJsonAtomic(filePath, session);
33483
+ await writeJsonAtomic2(filePath, session);
32997
33484
  await ensurePathPermissions(filePath);
32998
33485
  }
32999
33486
  function createLocalSessionStore(params) {
33000
33487
  const service = params?.service?.trim() || "remix-cli";
33001
33488
  const account = params?.account?.trim() || "default";
33002
- const filePath = params?.filePath?.trim() || import_path6.default.join(xdgConfigHome(), "remix", "session.json");
33489
+ const filePath = params?.filePath?.trim() || import_path7.default.join(xdgConfigHome(), "remix", "session.json");
33003
33490
  return {
33004
33491
  async getSession() {
33005
33492
  const keytar = await maybeLoadKeytar();
@@ -33013,7 +33500,7 @@ function createLocalSessionStore(params) {
33013
33500
  return null;
33014
33501
  }
33015
33502
  }
33016
- const raw = await import_promises17.default.readFile(filePath, "utf8").catch(() => null);
33503
+ const raw = await import_promises18.default.readFile(filePath, "utf8").catch(() => null);
33017
33504
  if (!raw) return null;
33018
33505
  try {
33019
33506
  const parsed = storedSessionSchema.safeParse(JSON.parse(raw));
@@ -33200,12 +33687,12 @@ async function createHookCollabApiClient() {
33200
33687
 
33201
33688
  // src/hook-diagnostics.ts
33202
33689
  var import_node_crypto2 = require("crypto");
33203
- var import_promises19 = __toESM(require("fs/promises"), 1);
33690
+ var import_promises20 = __toESM(require("fs/promises"), 1);
33204
33691
  var import_node_os5 = __toESM(require("os"), 1);
33205
33692
  var import_node_path7 = __toESM(require("path"), 1);
33206
33693
 
33207
33694
  // src/hook-state.ts
33208
- var import_promises18 = __toESM(require("fs/promises"), 1);
33695
+ var import_promises19 = __toESM(require("fs/promises"), 1);
33209
33696
  var import_node_os4 = __toESM(require("os"), 1);
33210
33697
  var import_node_path6 = __toESM(require("path"), 1);
33211
33698
  var import_node_crypto = require("crypto");
@@ -33222,11 +33709,11 @@ function stateLockPath(sessionId) {
33222
33709
  function stateLockMetaPath(sessionId) {
33223
33710
  return import_node_path6.default.join(stateLockPath(sessionId), "owner.json");
33224
33711
  }
33225
- async function writeJsonAtomic2(filePath, value) {
33226
- await import_promises18.default.mkdir(import_node_path6.default.dirname(filePath), { recursive: true });
33712
+ async function writeJsonAtomic3(filePath, value) {
33713
+ await import_promises19.default.mkdir(import_node_path6.default.dirname(filePath), { recursive: true });
33227
33714
  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);
33715
+ await import_promises19.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
33716
+ await import_promises19.default.rename(tmpPath, filePath);
33230
33717
  }
33231
33718
  var STATE_LOCK_WAIT_MS = 2e3;
33232
33719
  var STATE_LOCK_POLL_MS = 25;
@@ -33236,7 +33723,7 @@ async function sleep4(ms) {
33236
33723
  await new Promise((resolve) => setTimeout(resolve, ms));
33237
33724
  }
33238
33725
  async function readStateLockMetadata(sessionId) {
33239
- const raw = await import_promises18.default.readFile(stateLockMetaPath(sessionId), "utf8").catch(() => null);
33726
+ const raw = await import_promises19.default.readFile(stateLockMetaPath(sessionId), "utf8").catch(() => null);
33240
33727
  if (!raw) return null;
33241
33728
  try {
33242
33729
  const parsed = JSON.parse(raw);
@@ -33254,20 +33741,20 @@ async function readStateLockMetadata(sessionId) {
33254
33741
  }
33255
33742
  }
33256
33743
  async function writeStateLockMetadata(sessionId, metadata) {
33257
- await writeJsonAtomic2(stateLockMetaPath(sessionId), metadata);
33744
+ await writeJsonAtomic3(stateLockMetaPath(sessionId), metadata);
33258
33745
  }
33259
33746
  async function tryRemoveStaleStateLock(sessionId) {
33260
33747
  const lockPath = stateLockPath(sessionId);
33261
33748
  const metadata = await readStateLockMetadata(sessionId);
33262
33749
  const staleByHeartbeat = metadata && Date.now() - new Date(metadata.heartbeatAt).getTime() > STATE_LOCK_STALE_MS;
33263
33750
  if (staleByHeartbeat) {
33264
- await import_promises18.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33751
+ await import_promises19.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33265
33752
  return true;
33266
33753
  }
33267
33754
  if (!metadata) {
33268
- const lockStat = await import_promises18.default.stat(lockPath).catch(() => null);
33755
+ const lockStat = await import_promises19.default.stat(lockPath).catch(() => null);
33269
33756
  if (lockStat && Date.now() - lockStat.mtimeMs > STATE_LOCK_STALE_MS) {
33270
- await import_promises18.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33757
+ await import_promises19.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33271
33758
  return true;
33272
33759
  }
33273
33760
  }
@@ -33276,10 +33763,10 @@ async function tryRemoveStaleStateLock(sessionId) {
33276
33763
  async function acquireStateLock(sessionId) {
33277
33764
  const lockPath = stateLockPath(sessionId);
33278
33765
  const deadline = Date.now() + STATE_LOCK_WAIT_MS;
33279
- await import_promises18.default.mkdir(stateRoot(), { recursive: true });
33766
+ await import_promises19.default.mkdir(stateRoot(), { recursive: true });
33280
33767
  while (true) {
33281
33768
  try {
33282
- await import_promises18.default.mkdir(lockPath);
33769
+ await import_promises19.default.mkdir(lockPath);
33283
33770
  const ownerId = (0, import_node_crypto.randomUUID)();
33284
33771
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
33285
33772
  const metadata = {
@@ -33304,7 +33791,7 @@ async function acquireStateLock(sessionId) {
33304
33791
  clearInterval(heartbeat);
33305
33792
  const currentMetadata = await readStateLockMetadata(sessionId);
33306
33793
  if (currentMetadata?.ownerId === ownerId) {
33307
- await import_promises18.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33794
+ await import_promises19.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
33308
33795
  }
33309
33796
  };
33310
33797
  } catch (error) {
@@ -33416,7 +33903,7 @@ async function updatePendingTurnState(sessionId, updater) {
33416
33903
  });
33417
33904
  }
33418
33905
  async function loadPendingTurnState(sessionId) {
33419
- const raw = await import_promises18.default.readFile(statePath(sessionId), "utf8").catch(() => null);
33906
+ const raw = await import_promises19.default.readFile(statePath(sessionId), "utf8").catch(() => null);
33420
33907
  if (!raw) return null;
33421
33908
  try {
33422
33909
  const parsed = JSON.parse(raw);
@@ -33442,7 +33929,7 @@ async function loadPendingTurnState(sessionId) {
33442
33929
  }
33443
33930
  }
33444
33931
  async function savePendingTurnState(state) {
33445
- await writeJsonAtomic2(statePath(state.sessionId), state);
33932
+ await writeJsonAtomic3(statePath(state.sessionId), state);
33446
33933
  }
33447
33934
  async function upsertTouchedRepo(sessionId, params) {
33448
33935
  const normalizedRepoRoot = params.repoRoot.trim();
@@ -33518,14 +34005,14 @@ async function listTouchedRepos(sessionId) {
33518
34005
  }
33519
34006
  async function clearPendingTurnState(sessionId) {
33520
34007
  await withStateLock(sessionId, async () => {
33521
- await import_promises18.default.rm(statePath(sessionId), { force: true }).catch(() => void 0);
34008
+ await import_promises19.default.rm(statePath(sessionId), { force: true }).catch(() => void 0);
33522
34009
  });
33523
34010
  }
33524
34011
 
33525
34012
  // package.json
33526
34013
  var package_default = {
33527
34014
  name: "@remixhq/claude-plugin",
33528
- version: "0.1.15",
34015
+ version: "0.1.16",
33529
34016
  description: "Claude Code plugin for Remix collaboration workflows",
33530
34017
  homepage: "https://github.com/RemixDotOne/remix-claude-plugin",
33531
34018
  license: "MIT",
@@ -33556,8 +34043,8 @@ var package_default = {
33556
34043
  prepack: "npm run build"
33557
34044
  },
33558
34045
  dependencies: {
33559
- "@remixhq/core": "^0.1.10",
33560
- "@remixhq/mcp": "^0.1.10"
34046
+ "@remixhq/core": "^0.1.11",
34047
+ "@remixhq/mcp": "^0.1.11"
33561
34048
  },
33562
34049
  devDependencies: {
33563
34050
  "@types/node": "^25.4.0",
@@ -33608,13 +34095,13 @@ function normalizeFields(fields) {
33608
34095
  return Object.fromEntries(normalizedEntries);
33609
34096
  }
33610
34097
  async function rotateLogIfNeeded(logPath) {
33611
- const stat = await import_promises19.default.stat(logPath).catch(() => null);
34098
+ const stat = await import_promises20.default.stat(logPath).catch(() => null);
33612
34099
  if (!stat || stat.size < MAX_LOG_BYTES) {
33613
34100
  return;
33614
34101
  }
33615
34102
  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);
34103
+ await import_promises20.default.rm(rotatedPath, { force: true }).catch(() => void 0);
34104
+ await import_promises20.default.rename(logPath, rotatedPath).catch(() => void 0);
33618
34105
  }
33619
34106
  function summarizeText(value) {
33620
34107
  if (typeof value !== "string" || !value.trim()) {
@@ -33634,7 +34121,7 @@ function summarizeText(value) {
33634
34121
  async function appendHookDiagnosticsEvent(params) {
33635
34122
  try {
33636
34123
  const logPath = getHookDiagnosticsLogPath();
33637
- await import_promises19.default.mkdir(import_node_path7.default.dirname(logPath), { recursive: true });
34124
+ await import_promises20.default.mkdir(import_node_path7.default.dirname(logPath), { recursive: true });
33638
34125
  await rotateLogIfNeeded(logPath);
33639
34126
  const event = {
33640
34127
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -33651,14 +34138,14 @@ async function appendHookDiagnosticsEvent(params) {
33651
34138
  message: params.message?.trim() || null,
33652
34139
  fields: normalizeFields(params.fields)
33653
34140
  };
33654
- await import_promises19.default.appendFile(logPath, `${JSON.stringify(event)}
34141
+ await import_promises20.default.appendFile(logPath, `${JSON.stringify(event)}
33655
34142
  `, "utf8");
33656
34143
  } catch {
33657
34144
  }
33658
34145
  }
33659
34146
 
33660
34147
  // src/hook-utils.ts
33661
- var import_promises20 = __toESM(require("fs/promises"), 1);
34148
+ var import_promises21 = __toESM(require("fs/promises"), 1);
33662
34149
  var import_node_path8 = __toESM(require("path"), 1);
33663
34150
  async function readJsonStdin() {
33664
34151
  const chunks = [];
@@ -33722,13 +34209,13 @@ function extractBoolean(input, keys) {
33722
34209
  async function findBoundRepo(startPath) {
33723
34210
  if (!startPath) return null;
33724
34211
  let current = import_node_path8.default.resolve(startPath);
33725
- let stats = await import_promises20.default.stat(current).catch(() => null);
34212
+ let stats = await import_promises21.default.stat(current).catch(() => null);
33726
34213
  if (stats?.isFile()) {
33727
34214
  current = import_node_path8.default.dirname(current);
33728
34215
  }
33729
34216
  while (true) {
33730
34217
  const bindingPath = import_node_path8.default.join(current, ".remix", "config.json");
33731
- const bindingStats = await import_promises20.default.stat(bindingPath).catch(() => null);
34218
+ const bindingStats = await import_promises21.default.stat(bindingPath).catch(() => null);
33732
34219
  if (bindingStats?.isFile()) return current;
33733
34220
  const parent = import_node_path8.default.dirname(current);
33734
34221
  if (parent === current) return null;
@@ -33767,6 +34254,12 @@ function getErrorDetails(error) {
33767
34254
  return { message, hint: null };
33768
34255
  }
33769
34256
  function getRecordingBlockedMessage(status, repoRoot) {
34257
+ if (status.status === "branch_binding_missing") {
34258
+ return {
34259
+ message: "Fallback Remix turn recording was blocked because the current branch does not have a Remix lane binding yet.",
34260
+ hint: status.hint || `Run \`remix_collab_status\` for ${repoRoot}, then initialize or provision the current branch lane before recording work.`
34261
+ };
34262
+ }
33770
34263
  switch (status.status) {
33771
34264
  case "not_git_repo":
33772
34265
  return {
@@ -33785,8 +34278,8 @@ function getRecordingBlockedMessage(status, repoRoot) {
33785
34278
  };
33786
34279
  case "branch_mismatch":
33787
34280
  return {
33788
- message: "Fallback Remix turn recording was blocked by the checkout's preferred-branch policy.",
33789
- hint: status.hint || `Repo root: ${repoRoot}`
34281
+ message: "Fallback Remix turn recording was blocked because the current checkout branch does not match the branch expected by the bound Remix lane.",
34282
+ 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
34283
  };
33791
34284
  case "metadata_conflict":
33792
34285
  return {