@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.
@@ -3223,8 +3223,8 @@ var require_utils = __commonJS({
3223
3223
  }
3224
3224
  return ind;
3225
3225
  }
3226
- function removeDotSegments(path14) {
3227
- let input = path14;
3226
+ function removeDotSegments(path13) {
3227
+ let input = path13;
3228
3228
  const output = [];
3229
3229
  let nextSlash = -1;
3230
3230
  let len = 0;
@@ -3423,8 +3423,8 @@ var require_schemes = __commonJS({
3423
3423
  wsComponent.secure = void 0;
3424
3424
  }
3425
3425
  if (wsComponent.resourceName) {
3426
- const [path14, query] = wsComponent.resourceName.split("?");
3427
- wsComponent.path = path14 && path14 !== "/" ? path14 : void 0;
3426
+ const [path13, query] = wsComponent.resourceName.split("?");
3427
+ wsComponent.path = path13 && path13 !== "/" ? path13 : void 0;
3428
3428
  wsComponent.query = query;
3429
3429
  wsComponent.resourceName = void 0;
3430
3430
  }
@@ -6806,7 +6806,7 @@ var require_windows = __commonJS({
6806
6806
  module2.exports = isexe;
6807
6807
  isexe.sync = sync;
6808
6808
  var fs11 = require("fs");
6809
- function checkPathExt(path14, options) {
6809
+ function checkPathExt(path13, options) {
6810
6810
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
6811
6811
  if (!pathext) {
6812
6812
  return true;
@@ -6817,25 +6817,25 @@ var require_windows = __commonJS({
6817
6817
  }
6818
6818
  for (var i2 = 0; i2 < pathext.length; i2++) {
6819
6819
  var p = pathext[i2].toLowerCase();
6820
- if (p && path14.substr(-p.length).toLowerCase() === p) {
6820
+ if (p && path13.substr(-p.length).toLowerCase() === p) {
6821
6821
  return true;
6822
6822
  }
6823
6823
  }
6824
6824
  return false;
6825
6825
  }
6826
- function checkStat(stat, path14, options) {
6826
+ function checkStat(stat, path13, options) {
6827
6827
  if (!stat.isSymbolicLink() && !stat.isFile()) {
6828
6828
  return false;
6829
6829
  }
6830
- return checkPathExt(path14, options);
6830
+ return checkPathExt(path13, options);
6831
6831
  }
6832
- function isexe(path14, options, cb) {
6833
- fs11.stat(path14, function(er, stat) {
6834
- cb(er, er ? false : checkStat(stat, path14, options));
6832
+ function isexe(path13, options, cb) {
6833
+ fs11.stat(path13, function(er, stat) {
6834
+ cb(er, er ? false : checkStat(stat, path13, options));
6835
6835
  });
6836
6836
  }
6837
- function sync(path14, options) {
6838
- return checkStat(fs11.statSync(path14), path14, options);
6837
+ function sync(path13, options) {
6838
+ return checkStat(fs11.statSync(path13), path13, options);
6839
6839
  }
6840
6840
  }
6841
6841
  });
@@ -6847,13 +6847,13 @@ var require_mode = __commonJS({
6847
6847
  module2.exports = isexe;
6848
6848
  isexe.sync = sync;
6849
6849
  var fs11 = require("fs");
6850
- function isexe(path14, options, cb) {
6851
- fs11.stat(path14, function(er, stat) {
6850
+ function isexe(path13, options, cb) {
6851
+ fs11.stat(path13, function(er, stat) {
6852
6852
  cb(er, er ? false : checkStat(stat, options));
6853
6853
  });
6854
6854
  }
6855
- function sync(path14, options) {
6856
- return checkStat(fs11.statSync(path14), options);
6855
+ function sync(path13, options) {
6856
+ return checkStat(fs11.statSync(path13), options);
6857
6857
  }
6858
6858
  function checkStat(stat, options) {
6859
6859
  return stat.isFile() && checkMode(stat, options);
@@ -6887,7 +6887,7 @@ var require_isexe = __commonJS({
6887
6887
  }
6888
6888
  module2.exports = isexe;
6889
6889
  isexe.sync = sync;
6890
- function isexe(path14, options, cb) {
6890
+ function isexe(path13, options, cb) {
6891
6891
  if (typeof options === "function") {
6892
6892
  cb = options;
6893
6893
  options = {};
@@ -6897,7 +6897,7 @@ var require_isexe = __commonJS({
6897
6897
  throw new TypeError("callback not provided");
6898
6898
  }
6899
6899
  return new Promise(function(resolve, reject) {
6900
- isexe(path14, options || {}, function(er, is) {
6900
+ isexe(path13, options || {}, function(er, is) {
6901
6901
  if (er) {
6902
6902
  reject(er);
6903
6903
  } else {
@@ -6906,7 +6906,7 @@ var require_isexe = __commonJS({
6906
6906
  });
6907
6907
  });
6908
6908
  }
6909
- core(path14, options || {}, function(er, is) {
6909
+ core(path13, options || {}, function(er, is) {
6910
6910
  if (er) {
6911
6911
  if (er.code === "EACCES" || options && options.ignoreErrors) {
6912
6912
  er = null;
@@ -6916,9 +6916,9 @@ var require_isexe = __commonJS({
6916
6916
  cb(er, is);
6917
6917
  });
6918
6918
  }
6919
- function sync(path14, options) {
6919
+ function sync(path13, options) {
6920
6920
  try {
6921
- return core.sync(path14, options || {});
6921
+ return core.sync(path13, options || {});
6922
6922
  } catch (er) {
6923
6923
  if (options && options.ignoreErrors || er.code === "EACCES") {
6924
6924
  return false;
@@ -6935,7 +6935,7 @@ var require_which = __commonJS({
6935
6935
  "node_modules/which/which.js"(exports2, module2) {
6936
6936
  "use strict";
6937
6937
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
6938
- var path14 = require("path");
6938
+ var path13 = require("path");
6939
6939
  var COLON = isWindows ? ";" : ":";
6940
6940
  var isexe = require_isexe();
6941
6941
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -6973,7 +6973,7 @@ var require_which = __commonJS({
6973
6973
  return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
6974
6974
  const ppRaw = pathEnv[i2];
6975
6975
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
6976
- const pCmd = path14.join(pathPart, cmd);
6976
+ const pCmd = path13.join(pathPart, cmd);
6977
6977
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
6978
6978
  resolve(subStep(p, i2, 0));
6979
6979
  });
@@ -7000,7 +7000,7 @@ var require_which = __commonJS({
7000
7000
  for (let i2 = 0; i2 < pathEnv.length; i2++) {
7001
7001
  const ppRaw = pathEnv[i2];
7002
7002
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
7003
- const pCmd = path14.join(pathPart, cmd);
7003
+ const pCmd = path13.join(pathPart, cmd);
7004
7004
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
7005
7005
  for (let j = 0; j < pathExt.length; j++) {
7006
7006
  const cur = p + pathExt[j];
@@ -7048,7 +7048,7 @@ var require_path_key = __commonJS({
7048
7048
  var require_resolveCommand = __commonJS({
7049
7049
  "node_modules/cross-spawn/lib/util/resolveCommand.js"(exports2, module2) {
7050
7050
  "use strict";
7051
- var path14 = require("path");
7051
+ var path13 = require("path");
7052
7052
  var which = require_which();
7053
7053
  var getPathKey = require_path_key();
7054
7054
  function resolveCommandAttempt(parsed, withoutPathExt) {
@@ -7066,7 +7066,7 @@ var require_resolveCommand = __commonJS({
7066
7066
  try {
7067
7067
  resolved = which.sync(parsed.command, {
7068
7068
  path: env[getPathKey({ env })],
7069
- pathExt: withoutPathExt ? path14.delimiter : void 0
7069
+ pathExt: withoutPathExt ? path13.delimiter : void 0
7070
7070
  });
7071
7071
  } catch (e) {
7072
7072
  } finally {
@@ -7075,7 +7075,7 @@ var require_resolveCommand = __commonJS({
7075
7075
  }
7076
7076
  }
7077
7077
  if (resolved) {
7078
- resolved = path14.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
7078
+ resolved = path13.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
7079
7079
  }
7080
7080
  return resolved;
7081
7081
  }
@@ -7129,8 +7129,8 @@ var require_shebang_command = __commonJS({
7129
7129
  if (!match) {
7130
7130
  return null;
7131
7131
  }
7132
- const [path14, argument] = match[0].replace(/#! ?/, "").split(" ");
7133
- const binary = path14.split("/").pop();
7132
+ const [path13, argument] = match[0].replace(/#! ?/, "").split(" ");
7133
+ const binary = path13.split("/").pop();
7134
7134
  if (binary === "env") {
7135
7135
  return argument;
7136
7136
  }
@@ -7165,7 +7165,7 @@ var require_readShebang = __commonJS({
7165
7165
  var require_parse = __commonJS({
7166
7166
  "node_modules/cross-spawn/lib/parse.js"(exports2, module2) {
7167
7167
  "use strict";
7168
- var path14 = require("path");
7168
+ var path13 = require("path");
7169
7169
  var resolveCommand = require_resolveCommand();
7170
7170
  var escape2 = require_escape();
7171
7171
  var readShebang = require_readShebang();
@@ -7190,7 +7190,7 @@ var require_parse = __commonJS({
7190
7190
  const needsShell = !isExecutableRegExp.test(commandFile);
7191
7191
  if (parsed.options.forceShell || needsShell) {
7192
7192
  const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
7193
- parsed.command = path14.normalize(parsed.command);
7193
+ parsed.command = path13.normalize(parsed.command);
7194
7194
  parsed.command = escape2.command(parsed.command);
7195
7195
  parsed.args = parsed.args.map((arg) => escape2.argument(arg, needsDoubleEscapeMetaChars));
7196
7196
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
@@ -7498,10 +7498,10 @@ function assignProp(target, prop, value) {
7498
7498
  configurable: true
7499
7499
  });
7500
7500
  }
7501
- function getElementAtPath(obj, path14) {
7502
- if (!path14)
7501
+ function getElementAtPath(obj, path13) {
7502
+ if (!path13)
7503
7503
  return obj;
7504
- return path14.reduce((acc, key) => acc?.[key], obj);
7504
+ return path13.reduce((acc, key) => acc?.[key], obj);
7505
7505
  }
7506
7506
  function promiseAllObject(promisesObj) {
7507
7507
  const keys = Object.keys(promisesObj);
@@ -7821,11 +7821,11 @@ function aborted(x, startIndex = 0) {
7821
7821
  }
7822
7822
  return false;
7823
7823
  }
7824
- function prefixIssues(path14, issues) {
7824
+ function prefixIssues(path13, issues) {
7825
7825
  return issues.map((iss) => {
7826
7826
  var _a;
7827
7827
  (_a = iss).path ?? (_a.path = []);
7828
- iss.path.unshift(path14);
7828
+ iss.path.unshift(path13);
7829
7829
  return iss;
7830
7830
  });
7831
7831
  }
@@ -13886,8 +13886,8 @@ function getErrorMap() {
13886
13886
 
13887
13887
  // node_modules/zod/v3/helpers/parseUtil.js
13888
13888
  var makeIssue = (params) => {
13889
- const { data, path: path14, errorMaps, issueData } = params;
13890
- const fullPath = [...path14, ...issueData.path || []];
13889
+ const { data, path: path13, errorMaps, issueData } = params;
13890
+ const fullPath = [...path13, ...issueData.path || []];
13891
13891
  const fullIssue = {
13892
13892
  ...issueData,
13893
13893
  path: fullPath
@@ -14003,11 +14003,11 @@ var errorUtil;
14003
14003
 
14004
14004
  // node_modules/zod/v3/types.js
14005
14005
  var ParseInputLazyPath = class {
14006
- constructor(parent, value, path14, key) {
14006
+ constructor(parent, value, path13, key) {
14007
14007
  this._cachedPath = [];
14008
14008
  this.parent = parent;
14009
14009
  this.data = value;
14010
- this._path = path14;
14010
+ this._path = path13;
14011
14011
  this._key = key;
14012
14012
  }
14013
14013
  get path() {
@@ -21513,7 +21513,7 @@ var RemixError = class extends Error {
21513
21513
  }
21514
21514
  };
21515
21515
 
21516
- // node_modules/@remixhq/core/dist/chunk-B5S3PUIR.js
21516
+ // node_modules/@remixhq/core/dist/chunk-BNKPTE2U.js
21517
21517
  async function readJsonSafe(res) {
21518
21518
  const ct = res.headers.get("content-type") ?? "";
21519
21519
  if (!ct.toLowerCase().includes("application/json")) return null;
@@ -21527,7 +21527,7 @@ function createApiClient(config2, opts) {
21527
21527
  const apiKey = (opts?.apiKey ?? "").trim();
21528
21528
  const tokenProvider = opts?.tokenProvider;
21529
21529
  const CLIENT_KEY_HEADER = "x-comerge-api-key";
21530
- async function request(path14, init) {
21530
+ async function request(path13, init) {
21531
21531
  if (!tokenProvider) {
21532
21532
  throw new RemixError("API client is missing a token provider.", {
21533
21533
  exitCode: 1,
@@ -21535,7 +21535,7 @@ function createApiClient(config2, opts) {
21535
21535
  });
21536
21536
  }
21537
21537
  const auth = await tokenProvider();
21538
- const url = new URL(path14, config2.apiUrl).toString();
21538
+ const url = new URL(path13, config2.apiUrl).toString();
21539
21539
  const doFetch = async (bearer) => fetch(url, {
21540
21540
  ...init,
21541
21541
  headers: {
@@ -21559,7 +21559,7 @@ function createApiClient(config2, opts) {
21559
21559
  const json = await readJsonSafe(res);
21560
21560
  return json ?? null;
21561
21561
  }
21562
- async function requestBinary(path14, init) {
21562
+ async function requestBinary(path13, init) {
21563
21563
  if (!tokenProvider) {
21564
21564
  throw new RemixError("API client is missing a token provider.", {
21565
21565
  exitCode: 1,
@@ -21567,7 +21567,7 @@ function createApiClient(config2, opts) {
21567
21567
  });
21568
21568
  }
21569
21569
  const auth = await tokenProvider();
21570
- const url = new URL(path14, config2.apiUrl).toString();
21570
+ const url = new URL(path13, config2.apiUrl).toString();
21571
21571
  const doFetch = async (bearer) => fetch(url, {
21572
21572
  ...init,
21573
21573
  headers: {
@@ -21611,8 +21611,20 @@ function createApiClient(config2, opts) {
21611
21611
  const qs = new URLSearchParams();
21612
21612
  if (params.repoFingerprint) qs.set("repoFingerprint", params.repoFingerprint);
21613
21613
  if (params.remoteUrl) qs.set("remoteUrl", params.remoteUrl);
21614
+ if (params.branchName) qs.set("branchName", params.branchName);
21614
21615
  return request(`/v1/projects/bindings/resolve?${qs.toString()}`, { method: "GET" });
21615
21616
  },
21617
+ resolveProjectLaneBinding: (params) => {
21618
+ const qs = new URLSearchParams();
21619
+ if (params.projectId) qs.set("projectId", params.projectId);
21620
+ if (params.repoFingerprint) qs.set("repoFingerprint", params.repoFingerprint);
21621
+ if (params.remoteUrl) qs.set("remoteUrl", params.remoteUrl);
21622
+ if (params.defaultBranch) qs.set("defaultBranch", params.defaultBranch);
21623
+ qs.set("branchName", params.branchName);
21624
+ return request(`/v1/projects/bindings/resolve-lane?${qs.toString()}`, { method: "GET" });
21625
+ },
21626
+ ensureProjectLaneBinding: (payload) => request("/v1/projects/bindings/ensure-lane", { method: "POST", body: JSON.stringify(payload) }),
21627
+ bootstrapFreshProjectLane: (payload) => request("/v1/projects/bindings/bootstrap-fresh-lane", { method: "POST", body: JSON.stringify(payload) }),
21616
21628
  autoEnableDeveloper: () => request("/v1/developer/auto-enable", { method: "POST" }),
21617
21629
  listClientApps: (params) => {
21618
21630
  const qs = params?.orgId ? `?orgId=${encodeURIComponent(params.orgId)}` : "";
@@ -21676,6 +21688,7 @@ function createApiClient(config2, opts) {
21676
21688
  if (params?.offset !== void 0) qs.set("offset", String(params.offset));
21677
21689
  if (params?.changeStepId) qs.set("changeStepId", params.changeStepId);
21678
21690
  if (params?.threadId) qs.set("threadId", params.threadId);
21691
+ if (params?.collabLaneId) qs.set("collabLaneId", params.collabLaneId);
21679
21692
  if (params?.createdAfter) qs.set("createdAfter", params.createdAfter);
21680
21693
  if (params?.createdBefore) qs.set("createdBefore", params.createdBefore);
21681
21694
  const suffix = qs.toString() ? `?${qs.toString()}` : "";
@@ -30785,8 +30798,8 @@ var IcebergError = class extends Error {
30785
30798
  return this.status === 419;
30786
30799
  }
30787
30800
  };
30788
- function buildUrl(baseUrl, path14, query) {
30789
- const url = new URL(path14, baseUrl);
30801
+ function buildUrl(baseUrl, path13, query) {
30802
+ const url = new URL(path13, baseUrl);
30790
30803
  if (query) {
30791
30804
  for (const [key, value] of Object.entries(query)) {
30792
30805
  if (value !== void 0) {
@@ -30816,12 +30829,12 @@ function createFetchClient(options) {
30816
30829
  return {
30817
30830
  async request({
30818
30831
  method,
30819
- path: path14,
30832
+ path: path13,
30820
30833
  query,
30821
30834
  body,
30822
30835
  headers
30823
30836
  }) {
30824
- const url = buildUrl(options.baseUrl, path14, query);
30837
+ const url = buildUrl(options.baseUrl, path13, query);
30825
30838
  const authHeaders = await buildAuthHeaders(options.auth);
30826
30839
  const res = await fetchFn(url, {
30827
30840
  method,
@@ -31640,7 +31653,7 @@ var StorageFileApi = class extends BaseApiClient {
31640
31653
  * @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
31641
31654
  * @param fileBody The body of the file to be stored in the bucket.
31642
31655
  */
31643
- async uploadOrUpdate(method, path14, fileBody, fileOptions) {
31656
+ async uploadOrUpdate(method, path13, fileBody, fileOptions) {
31644
31657
  var _this = this;
31645
31658
  return _this.handleOperation(async () => {
31646
31659
  let body;
@@ -31664,7 +31677,7 @@ var StorageFileApi = class extends BaseApiClient {
31664
31677
  if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
31665
31678
  }
31666
31679
  if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) headers = _objectSpread22(_objectSpread22({}, headers), fileOptions.headers);
31667
- const cleanPath = _this._removeEmptyFolders(path14);
31680
+ const cleanPath = _this._removeEmptyFolders(path13);
31668
31681
  const _path = _this._getFinalPath(cleanPath);
31669
31682
  const data = await (method == "PUT" ? put : post)(_this.fetch, `${_this.url}/object/${_path}`, body, _objectSpread22({ headers }, (options === null || options === void 0 ? void 0 : options.duplex) ? { duplex: options.duplex } : {}));
31670
31683
  return {
@@ -31725,8 +31738,8 @@ var StorageFileApi = class extends BaseApiClient {
31725
31738
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
31726
31739
  * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Upload file using `ArrayBuffer` from base64 file data instead, see example below.
31727
31740
  */
31728
- async upload(path14, fileBody, fileOptions) {
31729
- return this.uploadOrUpdate("POST", path14, fileBody, fileOptions);
31741
+ async upload(path13, fileBody, fileOptions) {
31742
+ return this.uploadOrUpdate("POST", path13, fileBody, fileOptions);
31730
31743
  }
31731
31744
  /**
31732
31745
  * Upload a file with a token generated from `createSignedUploadUrl`.
@@ -31765,9 +31778,9 @@ var StorageFileApi = class extends BaseApiClient {
31765
31778
  * - `objects` table permissions: none
31766
31779
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
31767
31780
  */
31768
- async uploadToSignedUrl(path14, token, fileBody, fileOptions) {
31781
+ async uploadToSignedUrl(path13, token, fileBody, fileOptions) {
31769
31782
  var _this3 = this;
31770
- const cleanPath = _this3._removeEmptyFolders(path14);
31783
+ const cleanPath = _this3._removeEmptyFolders(path13);
31771
31784
  const _path = _this3._getFinalPath(cleanPath);
31772
31785
  const url = new URL(_this3.url + `/object/upload/sign/${_path}`);
31773
31786
  url.searchParams.set("token", token);
@@ -31829,10 +31842,10 @@ var StorageFileApi = class extends BaseApiClient {
31829
31842
  * - `objects` table permissions: `insert`
31830
31843
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
31831
31844
  */
31832
- async createSignedUploadUrl(path14, options) {
31845
+ async createSignedUploadUrl(path13, options) {
31833
31846
  var _this4 = this;
31834
31847
  return _this4.handleOperation(async () => {
31835
- let _path = _this4._getFinalPath(path14);
31848
+ let _path = _this4._getFinalPath(path13);
31836
31849
  const headers = _objectSpread22({}, _this4.headers);
31837
31850
  if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
31838
31851
  const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
@@ -31841,7 +31854,7 @@ var StorageFileApi = class extends BaseApiClient {
31841
31854
  if (!token) throw new StorageError("No token returned by API");
31842
31855
  return {
31843
31856
  signedUrl: url.toString(),
31844
- path: path14,
31857
+ path: path13,
31845
31858
  token
31846
31859
  };
31847
31860
  });
@@ -31897,8 +31910,8 @@ var StorageFileApi = class extends BaseApiClient {
31897
31910
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
31898
31911
  * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Update file using `ArrayBuffer` from base64 file data instead, see example below.
31899
31912
  */
31900
- async update(path14, fileBody, fileOptions) {
31901
- return this.uploadOrUpdate("PUT", path14, fileBody, fileOptions);
31913
+ async update(path13, fileBody, fileOptions) {
31914
+ return this.uploadOrUpdate("PUT", path13, fileBody, fileOptions);
31902
31915
  }
31903
31916
  /**
31904
31917
  * Moves an existing file to a new path in the same bucket.
@@ -32045,10 +32058,10 @@ var StorageFileApi = class extends BaseApiClient {
32045
32058
  * - `objects` table permissions: `select`
32046
32059
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
32047
32060
  */
32048
- async createSignedUrl(path14, expiresIn, options) {
32061
+ async createSignedUrl(path13, expiresIn, options) {
32049
32062
  var _this8 = this;
32050
32063
  return _this8.handleOperation(async () => {
32051
- let _path = _this8._getFinalPath(path14);
32064
+ let _path = _this8._getFinalPath(path13);
32052
32065
  const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
32053
32066
  let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
32054
32067
  const downloadQueryParam = (options === null || options === void 0 ? void 0 : options.download) ? `&download=${options.download === true ? "" : options.download}` : "";
@@ -32175,11 +32188,11 @@ var StorageFileApi = class extends BaseApiClient {
32175
32188
  * - `objects` table permissions: `select`
32176
32189
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
32177
32190
  */
32178
- download(path14, options, parameters) {
32191
+ download(path13, options, parameters) {
32179
32192
  const renderPath = typeof (options === null || options === void 0 ? void 0 : options.transform) !== "undefined" ? "render/image/authenticated" : "object";
32180
32193
  const transformationQuery = this.transformOptsToQueryString((options === null || options === void 0 ? void 0 : options.transform) || {});
32181
32194
  const queryString = transformationQuery ? `?${transformationQuery}` : "";
32182
- const _path = this._getFinalPath(path14);
32195
+ const _path = this._getFinalPath(path13);
32183
32196
  const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString}`, {
32184
32197
  headers: this.headers,
32185
32198
  noResolveJson: true
@@ -32209,9 +32222,9 @@ var StorageFileApi = class extends BaseApiClient {
32209
32222
  * }
32210
32223
  * ```
32211
32224
  */
32212
- async info(path14) {
32225
+ async info(path13) {
32213
32226
  var _this10 = this;
32214
- const _path = _this10._getFinalPath(path14);
32227
+ const _path = _this10._getFinalPath(path13);
32215
32228
  return _this10.handleOperation(async () => {
32216
32229
  return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
32217
32230
  });
@@ -32231,9 +32244,9 @@ var StorageFileApi = class extends BaseApiClient {
32231
32244
  * .exists('folder/avatar1.png')
32232
32245
  * ```
32233
32246
  */
32234
- async exists(path14) {
32247
+ async exists(path13) {
32235
32248
  var _this11 = this;
32236
- const _path = _this11._getFinalPath(path14);
32249
+ const _path = _this11._getFinalPath(path13);
32237
32250
  try {
32238
32251
  await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
32239
32252
  return {
@@ -32310,8 +32323,8 @@ var StorageFileApi = class extends BaseApiClient {
32310
32323
  * - `objects` table permissions: none
32311
32324
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
32312
32325
  */
32313
- getPublicUrl(path14, options) {
32314
- const _path = this._getFinalPath(path14);
32326
+ getPublicUrl(path13, options) {
32327
+ const _path = this._getFinalPath(path13);
32315
32328
  const _queryString = [];
32316
32329
  const downloadQueryParam = (options === null || options === void 0 ? void 0 : options.download) ? `download=${options.download === true ? "" : options.download}` : "";
32317
32330
  if (downloadQueryParam !== "") _queryString.push(downloadQueryParam);
@@ -32450,10 +32463,10 @@ var StorageFileApi = class extends BaseApiClient {
32450
32463
  * - `objects` table permissions: `select`
32451
32464
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
32452
32465
  */
32453
- async list(path14, options, parameters) {
32466
+ async list(path13, options, parameters) {
32454
32467
  var _this13 = this;
32455
32468
  return _this13.handleOperation(async () => {
32456
- const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path14 || "" });
32469
+ const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path13 || "" });
32457
32470
  return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
32458
32471
  });
32459
32472
  }
@@ -32517,11 +32530,11 @@ var StorageFileApi = class extends BaseApiClient {
32517
32530
  if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
32518
32531
  return btoa(data);
32519
32532
  }
32520
- _getFinalPath(path14) {
32521
- return `${this.bucketId}/${path14.replace(/^\/+/, "")}`;
32533
+ _getFinalPath(path13) {
32534
+ return `${this.bucketId}/${path13.replace(/^\/+/, "")}`;
32522
32535
  }
32523
- _removeEmptyFolders(path14) {
32524
- return path14.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
32536
+ _removeEmptyFolders(path13) {
32537
+ return path13.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
32525
32538
  }
32526
32539
  transformOptsToQueryString(transform2) {
32527
32540
  const params = [];
@@ -41792,89 +41805,11 @@ var REMIX_ERROR_CODES = {
41792
41805
  // node_modules/@remixhq/mcp/dist/index.js
41793
41806
  var import_path11 = __toESM(require("path"), 1);
41794
41807
 
41795
- // node_modules/@remixhq/core/dist/chunk-GEHSFPCD.js
41796
- var import_promises2 = __toESM(require("fs/promises"), 1);
41797
- var import_path2 = __toESM(require("path"), 1);
41798
- var import_promises3 = __toESM(require("fs/promises"), 1);
41799
- var import_path3 = __toESM(require("path"), 1);
41800
- async function reserveDirectory(targetDir) {
41801
- try {
41802
- await import_promises3.default.mkdir(targetDir);
41803
- return targetDir;
41804
- } catch (error2) {
41805
- if (error2?.code === "EEXIST") {
41806
- throw new RemixError("Output directory already exists.", {
41807
- exitCode: 2,
41808
- hint: `Choose an empty destination path: ${targetDir}`
41809
- });
41810
- }
41811
- throw error2;
41812
- }
41813
- }
41814
- async function reserveAvailableDirPath(preferredDir) {
41815
- const parent = import_path3.default.dirname(preferredDir);
41816
- const base = import_path3.default.basename(preferredDir);
41817
- for (let i2 = 1; i2 <= 1e3; i2 += 1) {
41818
- const candidate = i2 === 1 ? preferredDir : import_path3.default.join(parent, `${base}-${i2}`);
41819
- try {
41820
- await import_promises3.default.mkdir(candidate);
41821
- return candidate;
41822
- } catch (error2) {
41823
- if (error2?.code === "EEXIST") continue;
41824
- throw error2;
41825
- }
41826
- }
41827
- throw new RemixError("No available output directory name.", {
41828
- exitCode: 2,
41829
- hint: `Tried ${base} through ${base}-1000 under ${parent}.`
41830
- });
41831
- }
41832
- async function writeJsonAtomic2(filePath, value) {
41833
- const dir = import_path3.default.dirname(filePath);
41834
- await import_promises3.default.mkdir(dir, { recursive: true });
41835
- const tmp = `${filePath}.tmp-${Date.now()}`;
41836
- await import_promises3.default.writeFile(tmp, `${JSON.stringify(value, null, 2)}
41837
- `, "utf8");
41838
- await import_promises3.default.rename(tmp, filePath);
41839
- }
41840
- function getCollabBindingPath(repoRoot) {
41841
- return import_path2.default.join(repoRoot, ".remix", "config.json");
41842
- }
41843
- async function readCollabBinding(repoRoot) {
41844
- try {
41845
- const raw = await import_promises2.default.readFile(getCollabBindingPath(repoRoot), "utf8");
41846
- const parsed = JSON.parse(raw);
41847
- if (parsed?.schemaVersion !== 1) return null;
41848
- if (!parsed.projectId || !parsed.currentAppId || !parsed.upstreamAppId) return null;
41849
- return {
41850
- schemaVersion: 1,
41851
- projectId: parsed.projectId,
41852
- currentAppId: parsed.currentAppId,
41853
- upstreamAppId: parsed.upstreamAppId,
41854
- threadId: parsed.threadId ?? null,
41855
- repoFingerprint: parsed.repoFingerprint ?? null,
41856
- remoteUrl: parsed.remoteUrl ?? null,
41857
- defaultBranch: parsed.defaultBranch ?? null,
41858
- preferredBranch: parsed.preferredBranch ?? parsed.defaultBranch ?? null
41859
- };
41860
- } catch {
41861
- return null;
41862
- }
41863
- }
41864
- async function writeCollabBinding(repoRoot, binding) {
41865
- const filePath = getCollabBindingPath(repoRoot);
41866
- await writeJsonAtomic2(filePath, {
41867
- schemaVersion: 1,
41868
- ...binding
41869
- });
41870
- return filePath;
41871
- }
41872
-
41873
- // node_modules/@remixhq/core/dist/chunk-J3J4PBQ7.js
41874
- var import_promises15 = __toESM(require("fs/promises"), 1);
41808
+ // node_modules/@remixhq/core/dist/chunk-RREREIGW.js
41809
+ var import_promises13 = __toESM(require("fs/promises"), 1);
41875
41810
  var import_crypto = require("crypto");
41876
41811
  var import_os2 = __toESM(require("os"), 1);
41877
- var import_path4 = __toESM(require("path"), 1);
41812
+ var import_path2 = __toESM(require("path"), 1);
41878
41813
 
41879
41814
  // node_modules/is-plain-obj/index.js
41880
41815
  function isPlainObject4(value) {
@@ -42806,7 +42741,7 @@ var npmRunPathEnv = ({ env = import_node_process6.default.env, ...options } = {}
42806
42741
  };
42807
42742
 
42808
42743
  // node_modules/execa/lib/terminate/kill.js
42809
- var import_promises4 = require("timers/promises");
42744
+ var import_promises2 = require("timers/promises");
42810
42745
 
42811
42746
  // node_modules/execa/lib/return/final-error.js
42812
42747
  var getFinalError = (originalError, message, isSync) => {
@@ -43313,7 +43248,7 @@ var killOnTimeout = async ({ kill, forceKillAfterDelay, context, controllerSigna
43313
43248
  return;
43314
43249
  }
43315
43250
  try {
43316
- await (0, import_promises4.setTimeout)(forceKillAfterDelay, void 0, { signal: controllerSignal });
43251
+ await (0, import_promises2.setTimeout)(forceKillAfterDelay, void 0, { signal: controllerSignal });
43317
43252
  if (kill("SIGKILL")) {
43318
43253
  context.isForcefullyTerminated ??= true;
43319
43254
  }
@@ -43344,7 +43279,7 @@ var terminateOnCancel = async (subprocess, cancelSignal, context, { signal }) =>
43344
43279
  };
43345
43280
 
43346
43281
  // node_modules/execa/lib/ipc/graceful.js
43347
- var import_promises6 = require("timers/promises");
43282
+ var import_promises4 = require("timers/promises");
43348
43283
 
43349
43284
  // node_modules/execa/lib/ipc/send.js
43350
43285
  var import_node_util5 = require("util");
@@ -43531,7 +43466,7 @@ var import_node_events4 = require("events");
43531
43466
 
43532
43467
  // node_modules/execa/lib/ipc/incoming.js
43533
43468
  var import_node_events3 = require("events");
43534
- var import_promises5 = require("timers/promises");
43469
+ var import_promises3 = require("timers/promises");
43535
43470
 
43536
43471
  // node_modules/execa/lib/ipc/reference.js
43537
43472
  var addReference = (channel, reference) => {
@@ -43578,7 +43513,7 @@ var onMessage = async ({ anyProcess, channel, isSubprocess, ipcEmitter }, wrappe
43578
43513
  }
43579
43514
  while (incomingMessages.length > 0) {
43580
43515
  await waitForOutgoingMessages(anyProcess, ipcEmitter, wrappedMessage);
43581
- await import_promises5.scheduler.yield();
43516
+ await import_promises3.scheduler.yield();
43582
43517
  const message = await handleStrictRequest({
43583
43518
  wrappedMessage: incomingMessages[0],
43584
43519
  anyProcess,
@@ -43858,7 +43793,7 @@ var startIpc = async ({ anyProcess, channel, isSubprocess, ipc }) => {
43858
43793
  return;
43859
43794
  }
43860
43795
  getIpcEmitter(anyProcess, channel, isSubprocess);
43861
- await import_promises6.scheduler.yield();
43796
+ await import_promises4.scheduler.yield();
43862
43797
  };
43863
43798
  var cancelListening = false;
43864
43799
  var handleAbort = (wrappedMessage) => {
@@ -43931,7 +43866,7 @@ var getReason = ({ reason }) => {
43931
43866
  };
43932
43867
 
43933
43868
  // node_modules/execa/lib/terminate/timeout.js
43934
- var import_promises7 = require("timers/promises");
43869
+ var import_promises5 = require("timers/promises");
43935
43870
  var validateTimeout = ({ timeout }) => {
43936
43871
  if (timeout !== void 0 && (!Number.isFinite(timeout) || timeout < 0)) {
43937
43872
  throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`);
@@ -43939,7 +43874,7 @@ var validateTimeout = ({ timeout }) => {
43939
43874
  };
43940
43875
  var throwOnTimeout = (subprocess, timeout, context, controller) => timeout === 0 || timeout === void 0 ? [] : [killAfterTimeout(subprocess, timeout, context, controller)];
43941
43876
  var killAfterTimeout = async (subprocess, timeout, context, { signal }) => {
43942
- await (0, import_promises7.setTimeout)(timeout, void 0, { signal });
43877
+ await (0, import_promises5.setTimeout)(timeout, void 0, { signal });
43943
43878
  context.terminationReason ??= "timeout";
43944
43879
  subprocess.kill();
43945
43880
  throw new DiscardedError();
@@ -44196,7 +44131,7 @@ var CR_BINARY = CR.codePointAt(0);
44196
44131
 
44197
44132
  // node_modules/get-stream/source/index.js
44198
44133
  var import_node_events6 = require("events");
44199
- var import_promises8 = require("stream/promises");
44134
+ var import_promises6 = require("stream/promises");
44200
44135
 
44201
44136
  // node_modules/is-stream/index.js
44202
44137
  function isStream(stream, { checkOpen = true } = {}) {
@@ -44562,7 +44497,7 @@ var stringMethods = {
44562
44497
  };
44563
44498
 
44564
44499
  // node_modules/get-stream/source/index.js
44565
- Object.assign(nodeImports, { on: import_node_events6.on, finished: import_promises8.finished });
44500
+ Object.assign(nodeImports, { on: import_node_events6.on, finished: import_promises6.finished });
44566
44501
 
44567
44502
  // node_modules/execa/lib/io/max-buffer.js
44568
44503
  var handleMaxBuffer = ({ error: error2, stream, readableObjectMode, lines, encoding, fdNumber }) => {
@@ -46257,13 +46192,13 @@ var logOutputSync = ({ serializedResult, fdNumber, state, verboseInfo, encoding,
46257
46192
  }
46258
46193
  };
46259
46194
  var writeToFiles = (serializedResult, stdioItems, outputFiles) => {
46260
- for (const { path: path14, append } of stdioItems.filter(({ type }) => FILE_TYPES.has(type))) {
46261
- const pathString = typeof path14 === "string" ? path14 : path14.toString();
46195
+ for (const { path: path13, append } of stdioItems.filter(({ type }) => FILE_TYPES.has(type))) {
46196
+ const pathString = typeof path13 === "string" ? path13 : path13.toString();
46262
46197
  if (append || outputFiles.has(pathString)) {
46263
- (0, import_node_fs4.appendFileSync)(path14, serializedResult);
46198
+ (0, import_node_fs4.appendFileSync)(path13, serializedResult);
46264
46199
  } else {
46265
46200
  outputFiles.add(pathString);
46266
- (0, import_node_fs4.writeFileSync)(path14, serializedResult);
46201
+ (0, import_node_fs4.writeFileSync)(path13, serializedResult);
46267
46202
  }
46268
46203
  }
46269
46204
  };
@@ -46754,7 +46689,7 @@ var addPropertiesAsync = {
46754
46689
  // node_modules/@sindresorhus/merge-streams/index.js
46755
46690
  var import_node_events10 = require("events");
46756
46691
  var import_node_stream4 = require("stream");
46757
- var import_promises9 = require("stream/promises");
46692
+ var import_promises7 = require("stream/promises");
46758
46693
  function mergeStreams(streams) {
46759
46694
  if (!Array.isArray(streams)) {
46760
46695
  throw new TypeError(`Expected an array, got \`${typeof streams}\`.`);
@@ -46837,7 +46772,7 @@ var onMergedStreamFinished = async (passThroughStream, streams, unpipeEvent) =>
46837
46772
  };
46838
46773
  var onMergedStreamEnd = async (passThroughStream, { signal }) => {
46839
46774
  try {
46840
- await (0, import_promises9.finished)(passThroughStream, { signal, cleanup: true });
46775
+ await (0, import_promises7.finished)(passThroughStream, { signal, cleanup: true });
46841
46776
  } catch (error2) {
46842
46777
  errorOrAbortStream(passThroughStream, error2);
46843
46778
  throw error2;
@@ -46904,7 +46839,7 @@ var afterMergedStreamFinished = async (onFinished, stream, { signal }) => {
46904
46839
  };
46905
46840
  var onInputStreamEnd = async ({ passThroughStream, stream, streams, ended, aborted: aborted3, controller: { signal } }) => {
46906
46841
  try {
46907
- await (0, import_promises9.finished)(stream, {
46842
+ await (0, import_promises7.finished)(stream, {
46908
46843
  signal,
46909
46844
  cleanup: true,
46910
46845
  readable: true,
@@ -46969,7 +46904,7 @@ var PASSTHROUGH_LISTENERS_COUNT = 2;
46969
46904
  var PASSTHROUGH_LISTENERS_PER_STREAM = 1;
46970
46905
 
46971
46906
  // node_modules/execa/lib/io/pipeline.js
46972
- var import_promises10 = require("stream/promises");
46907
+ var import_promises8 = require("stream/promises");
46973
46908
  var pipeStreams = (source, destination) => {
46974
46909
  source.pipe(destination);
46975
46910
  onSourceFinish(source, destination);
@@ -46980,7 +46915,7 @@ var onSourceFinish = async (source, destination) => {
46980
46915
  return;
46981
46916
  }
46982
46917
  try {
46983
- await (0, import_promises10.finished)(source, { cleanup: true, readable: true, writable: false });
46918
+ await (0, import_promises8.finished)(source, { cleanup: true, readable: true, writable: false });
46984
46919
  } catch {
46985
46920
  }
46986
46921
  endDestinationStream(destination);
@@ -46995,7 +46930,7 @@ var onDestinationFinish = async (source, destination) => {
46995
46930
  return;
46996
46931
  }
46997
46932
  try {
46998
- await (0, import_promises10.finished)(destination, { cleanup: true, readable: false, writable: true });
46933
+ await (0, import_promises8.finished)(destination, { cleanup: true, readable: false, writable: true });
46999
46934
  } catch {
47000
46935
  }
47001
46936
  abortSourceStream(source);
@@ -47466,7 +47401,7 @@ var waitForBothSubprocesses = async (subprocessPromises) => {
47466
47401
  };
47467
47402
 
47468
47403
  // node_modules/execa/lib/pipe/streaming.js
47469
- var import_promises11 = require("stream/promises");
47404
+ var import_promises9 = require("stream/promises");
47470
47405
  var pipeSubprocessStream = (sourceStream, destinationStream, maxListenersController) => {
47471
47406
  const mergedStream = MERGED_STREAMS.has(destinationStream) ? pipeMoreSubprocessStream(sourceStream, destinationStream) : pipeFirstSubprocessStream(sourceStream, destinationStream);
47472
47407
  incrementMaxListeners(sourceStream, SOURCE_LISTENERS_PER_PIPE, maxListenersController.signal);
@@ -47487,7 +47422,7 @@ var pipeMoreSubprocessStream = (sourceStream, destinationStream) => {
47487
47422
  };
47488
47423
  var cleanupMergedStreamsMap = async (destinationStream) => {
47489
47424
  try {
47490
- await (0, import_promises11.finished)(destinationStream, { cleanup: true, readable: false, writable: true });
47425
+ await (0, import_promises9.finished)(destinationStream, { cleanup: true, readable: false, writable: true });
47491
47426
  } catch {
47492
47427
  }
47493
47428
  MERGED_STREAMS.delete(destinationStream);
@@ -47571,7 +47506,7 @@ var handlePipePromise = async ({
47571
47506
  var getSubprocessPromises = (sourcePromise, destination) => Promise.allSettled([sourcePromise, destination]);
47572
47507
 
47573
47508
  // node_modules/execa/lib/io/contents.js
47574
- var import_promises12 = require("timers/promises");
47509
+ var import_promises10 = require("timers/promises");
47575
47510
 
47576
47511
  // node_modules/execa/lib/io/iterate.js
47577
47512
  var import_node_events12 = require("events");
@@ -47723,7 +47658,7 @@ var logOutputAsync = async ({ stream, onStreamEnd, fdNumber, encoding, allMixed,
47723
47658
  await logLines(linesIterable, stream, fdNumber, verboseInfo);
47724
47659
  };
47725
47660
  var resumeStream = async (stream) => {
47726
- await (0, import_promises12.setImmediate)();
47661
+ await (0, import_promises10.setImmediate)();
47727
47662
  if (stream.readableFlowing === null) {
47728
47663
  stream.resume();
47729
47664
  }
@@ -47758,14 +47693,14 @@ var getBufferedData = async (streamPromise) => {
47758
47693
  var handleBufferedData = ({ bufferedData }) => isArrayBuffer(bufferedData) ? new Uint8Array(bufferedData) : bufferedData;
47759
47694
 
47760
47695
  // node_modules/execa/lib/resolve/wait-stream.js
47761
- var import_promises13 = require("stream/promises");
47696
+ var import_promises11 = require("stream/promises");
47762
47697
  var waitForStream = async (stream, fdNumber, streamInfo, { isSameDirection, stopOnExit = false } = {}) => {
47763
47698
  const state = handleStdinDestroy(stream, streamInfo);
47764
47699
  const abortController = new AbortController();
47765
47700
  try {
47766
47701
  await Promise.race([
47767
47702
  ...stopOnExit ? [streamInfo.exitPromise] : [],
47768
- (0, import_promises13.finished)(stream, { cleanup: true, signal: abortController.signal })
47703
+ (0, import_promises11.finished)(stream, { cleanup: true, signal: abortController.signal })
47769
47704
  ]);
47770
47705
  } catch (error2) {
47771
47706
  if (!state.stdinCleanedUp) {
@@ -48079,7 +48014,7 @@ var import_node_stream6 = require("stream");
48079
48014
  var import_node_util9 = require("util");
48080
48015
 
48081
48016
  // node_modules/execa/lib/convert/shared.js
48082
- var import_promises14 = require("stream/promises");
48017
+ var import_promises12 = require("stream/promises");
48083
48018
  var safeWaitForSubprocessStdin = async (subprocessStdin) => {
48084
48019
  if (subprocessStdin === void 0) {
48085
48020
  return;
@@ -48099,10 +48034,10 @@ var safeWaitForSubprocessStdout = async (subprocessStdout) => {
48099
48034
  }
48100
48035
  };
48101
48036
  var waitForSubprocessStdin = async (subprocessStdin) => {
48102
- await (0, import_promises14.finished)(subprocessStdin, { cleanup: true, readable: false, writable: true });
48037
+ await (0, import_promises12.finished)(subprocessStdin, { cleanup: true, readable: false, writable: true });
48103
48038
  };
48104
48039
  var waitForSubprocessStdout = async (subprocessStdout) => {
48105
- await (0, import_promises14.finished)(subprocessStdout, { cleanup: true, readable: true, writable: false });
48040
+ await (0, import_promises12.finished)(subprocessStdout, { cleanup: true, readable: true, writable: false });
48106
48041
  };
48107
48042
  var waitForSubprocess = async (subprocess, error2) => {
48108
48043
  await subprocess;
@@ -48651,7 +48586,7 @@ var {
48651
48586
  getCancelSignal: getCancelSignal2
48652
48587
  } = getIpcExport();
48653
48588
 
48654
- // node_modules/@remixhq/core/dist/chunk-J3J4PBQ7.js
48589
+ // node_modules/@remixhq/core/dist/chunk-RREREIGW.js
48655
48590
  var GIT_REMOTE_PROTOCOL_RE = /^(https?|ssh):\/\//i;
48656
48591
  var SCP_LIKE_GIT_REMOTE_RE = /^(?<user>[^@\s]+)@(?<host>[^:\s]+):(?<path>[^\\\s]+)$/;
48657
48592
  var CANONICAL_GIT_REMOTE_RE = /^(?<host>(?:localhost|[a-z0-9.-]+))\/(?<path>[^\\\s]+)$/i;
@@ -48709,8 +48644,8 @@ function sanitizeRefFragment(value) {
48709
48644
  return value.trim().replace(/[^A-Za-z0-9._/-]+/g, "-").replace(/\/{2,}/g, "/").replace(/^\/+|\/+$/g, "").replace(/^-+|-+$/g, "").slice(0, 120);
48710
48645
  }
48711
48646
  function normalizeRepoRelativePath(value) {
48712
- const normalized = import_path4.default.posix.normalize(value.replace(/\\/g, "/").trim());
48713
- if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../") || import_path4.default.posix.isAbsolute(normalized)) {
48647
+ const normalized = import_path2.default.posix.normalize(value.replace(/\\/g, "/").trim());
48648
+ if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../") || import_path2.default.posix.isAbsolute(normalized)) {
48714
48649
  throw new RemixError("Git returned an invalid repository-relative path.", {
48715
48650
  exitCode: 1,
48716
48651
  hint: `Path: ${value}`
@@ -48719,11 +48654,11 @@ function normalizeRepoRelativePath(value) {
48719
48654
  return normalized;
48720
48655
  }
48721
48656
  function resolveRepoRelativePath(repoRoot, relativePath) {
48722
- return import_path4.default.resolve(repoRoot, ...relativePath.split("/"));
48657
+ return import_path2.default.resolve(repoRoot, ...relativePath.split("/"));
48723
48658
  }
48724
48659
  function isWithinRepoRoot(repoRoot, targetPath) {
48725
- const relative = import_path4.default.relative(repoRoot, targetPath);
48726
- return relative === "" || !relative.startsWith("..") && !import_path4.default.isAbsolute(relative);
48660
+ const relative = import_path2.default.relative(repoRoot, targetPath);
48661
+ return relative === "" || !relative.startsWith("..") && !import_path2.default.isAbsolute(relative);
48727
48662
  }
48728
48663
  function sha256Hex(value) {
48729
48664
  return (0, import_crypto.createHash)("sha256").update(value).digest("hex");
@@ -48733,7 +48668,7 @@ function resolveGitReportedPath(cwd, reportedPath) {
48733
48668
  if (!value) {
48734
48669
  throw new RemixError("Git returned an empty internal path.", { exitCode: 1 });
48735
48670
  }
48736
- return import_path4.default.isAbsolute(value) ? value : import_path4.default.resolve(cwd, value);
48671
+ return import_path2.default.isAbsolute(value) ? value : import_path2.default.resolve(cwd, value);
48737
48672
  }
48738
48673
  function parseGitNameStatusZ(stdout) {
48739
48674
  const tokens = stdout.split("\0");
@@ -48804,12 +48739,12 @@ function classifyGitApplyFailure(detail) {
48804
48739
  return "unknown";
48805
48740
  }
48806
48741
  async function pruneEmptyParentDirectories(repoRoot, filePath) {
48807
- let current = import_path4.default.dirname(filePath);
48742
+ let current = import_path2.default.dirname(filePath);
48808
48743
  while (current !== repoRoot && isWithinRepoRoot(repoRoot, current)) {
48809
- const entries = await import_promises15.default.readdir(current).catch(() => null);
48744
+ const entries = await import_promises13.default.readdir(current).catch(() => null);
48810
48745
  if (!entries || entries.length > 0) return;
48811
- await import_promises15.default.rmdir(current).catch(() => void 0);
48812
- current = import_path4.default.dirname(current);
48746
+ await import_promises13.default.rmdir(current).catch(() => void 0);
48747
+ current = import_path2.default.dirname(current);
48813
48748
  }
48814
48749
  }
48815
48750
  async function findGitRoot(startDir) {
@@ -48911,8 +48846,8 @@ async function getWorkspaceSnapshot(cwd) {
48911
48846
  throw new RemixError("Failed to resolve local HEAD commit.", { exitCode: 1 });
48912
48847
  }
48913
48848
  const includedUntrackedPaths = Array.from(new Set((await listUntrackedFiles(cwd)).map((entry) => normalizeRepoRelativePath(entry))));
48914
- const tempDir = await import_promises15.default.mkdtemp(import_path4.default.join(import_os2.default.tmpdir(), "remix-index-"));
48915
- const tempIndexPath = import_path4.default.join(tempDir, "index");
48849
+ const tempDir = await import_promises13.default.mkdtemp(import_path2.default.join(import_os2.default.tmpdir(), "remix-index-"));
48850
+ const tempIndexPath = import_path2.default.join(tempDir, "index");
48916
48851
  const env = { ...process.env, GIT_INDEX_FILE: tempIndexPath };
48917
48852
  try {
48918
48853
  try {
@@ -48935,14 +48870,14 @@ async function getWorkspaceSnapshot(cwd) {
48935
48870
  });
48936
48871
  }
48937
48872
  } finally {
48938
- await import_promises15.default.rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
48873
+ await import_promises13.default.rm(tempDir, { recursive: true, force: true }).catch(() => void 0);
48939
48874
  }
48940
48875
  }
48941
48876
  async function writeTempUnifiedDiffBackup(diff, prefix = "remix-add-backup") {
48942
48877
  const safePrefix = prefix.replace(/[^a-zA-Z0-9._-]+/g, "-") || "remix-add-backup";
48943
- const tmpDir = await import_promises15.default.mkdtemp(import_path4.default.join(import_os2.default.tmpdir(), `${safePrefix}-`));
48944
- const backupPath = import_path4.default.join(tmpDir, "submitted.diff");
48945
- await import_promises15.default.writeFile(backupPath, diff, "utf8");
48878
+ const tmpDir = await import_promises13.default.mkdtemp(import_path2.default.join(import_os2.default.tmpdir(), `${safePrefix}-`));
48879
+ const backupPath = import_path2.default.join(tmpDir, "submitted.diff");
48880
+ await import_promises13.default.writeFile(backupPath, diff, "utf8");
48946
48881
  return { backupPath, diffSha256: sha256Hex(diff) };
48947
48882
  }
48948
48883
  async function validateUnifiedDiff(cwd, diff) {
@@ -48959,7 +48894,7 @@ async function validateUnifiedDiff(cwd, diff) {
48959
48894
  detail
48960
48895
  };
48961
48896
  } finally {
48962
- await import_promises15.default.rm(import_path4.default.dirname(backupPath), { recursive: true, force: true }).catch(() => void 0);
48897
+ await import_promises13.default.rm(import_path2.default.dirname(backupPath), { recursive: true, force: true }).catch(() => void 0);
48963
48898
  }
48964
48899
  }
48965
48900
  async function preserveWorkspaceChanges(cwd, prefix = "remix-add-preserve") {
@@ -48979,7 +48914,7 @@ async function preserveWorkspaceChanges(cwd, prefix = "remix-add-preserve") {
48979
48914
  }
48980
48915
  async function reapplyPreservedWorkspaceChanges(cwd, preserved, operation = "`remix collab add`") {
48981
48916
  const patchFilePath = preserved.preservedDiffPath;
48982
- const tempPatchHash = await import_promises15.default.readFile(patchFilePath, "utf8").then((content) => sha256Hex(content)).catch(() => null);
48917
+ const tempPatchHash = await import_promises13.default.readFile(patchFilePath, "utf8").then((content) => sha256Hex(content)).catch(() => null);
48983
48918
  if (!tempPatchHash) {
48984
48919
  return { status: "failed", detail: "Preserved diff artifact is missing." };
48985
48920
  }
@@ -49019,8 +48954,8 @@ async function createGitBundle(cwd, bundleName = "repository.bundle") {
49019
48954
  throw new RemixError("Failed to resolve local HEAD commit.", { exitCode: 1 });
49020
48955
  }
49021
48956
  const safeName = bundleName.replace(/[^a-zA-Z0-9._-]+/g, "_");
49022
- const tmpDir = await import_promises15.default.mkdtemp(import_path4.default.join(import_os2.default.tmpdir(), "remix-bundle-"));
49023
- const bundlePath = import_path4.default.join(tmpDir, safeName);
48957
+ const tmpDir = await import_promises13.default.mkdtemp(import_path2.default.join(import_os2.default.tmpdir(), "remix-bundle-"));
48958
+ const bundlePath = import_path2.default.join(tmpDir, safeName);
49024
48959
  const res = await runGitDetailed(["bundle", "create", bundlePath, "--all"], cwd);
49025
48960
  if (res.exitCode !== 0) {
49026
48961
  const detail = [res.stderr.trim(), res.stdout.trim()].filter(Boolean).join("\n\n");
@@ -49129,7 +49064,7 @@ async function discardCapturedUntrackedChanges(repoRoot, capturedPaths) {
49129
49064
  hint: `Path: ${relativePath}`
49130
49065
  });
49131
49066
  }
49132
- await import_promises15.default.rm(absolutePath, { recursive: true, force: true }).catch((err) => {
49067
+ await import_promises13.default.rm(absolutePath, { recursive: true, force: true }).catch((err) => {
49133
49068
  throw new RemixError("Failed to remove a captured untracked path before syncing.", {
49134
49069
  exitCode: 1,
49135
49070
  hint: err instanceof Error ? `${relativePath}
@@ -49171,7 +49106,7 @@ async function importGitBundle(cwd, bundlePath, bundleRef) {
49171
49106
  }
49172
49107
  }
49173
49108
  async function cloneGitBundleToDirectory(bundlePath, targetDir) {
49174
- const parentDir = import_path4.default.dirname(targetDir);
49109
+ const parentDir = import_path2.default.dirname(targetDir);
49175
49110
  const cloneRes = await runGitDetailed(["clone", bundlePath, targetDir], parentDir);
49176
49111
  if (cloneRes.exitCode !== 0) {
49177
49112
  const detail = [cloneRes.stderr.trim(), cloneRes.stdout.trim()].filter(Boolean).join("\n\n");
@@ -49191,10 +49126,10 @@ async function cloneGitBundleToDirectory(bundlePath, targetDir) {
49191
49126
  }
49192
49127
  async function ensureGitInfoExcludeEntries(cwd, entries) {
49193
49128
  const excludePath = await getGitPath(cwd, "info/exclude");
49194
- await import_promises15.default.mkdir(import_path4.default.dirname(excludePath), { recursive: true });
49129
+ await import_promises13.default.mkdir(import_path2.default.dirname(excludePath), { recursive: true });
49195
49130
  let current = "";
49196
49131
  try {
49197
- current = await import_promises15.default.readFile(excludePath, "utf8");
49132
+ current = await import_promises13.default.readFile(excludePath, "utf8");
49198
49133
  } catch {
49199
49134
  }
49200
49135
  const lines = new Set(current.split("\n").map((line) => line.trim()).filter(Boolean));
@@ -49206,7 +49141,7 @@ async function ensureGitInfoExcludeEntries(cwd, entries) {
49206
49141
  changed = true;
49207
49142
  }
49208
49143
  if (!changed) return;
49209
- await import_promises15.default.writeFile(excludePath, `${Array.from(lines).join("\n")}
49144
+ await import_promises13.default.writeFile(excludePath, `${Array.from(lines).join("\n")}
49210
49145
  `, "utf8");
49211
49146
  }
49212
49147
  async function ensureCommitExists(cwd, commitHash) {
@@ -49267,7 +49202,7 @@ async function hardResetToCommit(cwd, commitHash, operation = "`remix collab rec
49267
49202
  async function buildRepoFingerprint(params) {
49268
49203
  const remote = normalizeGitRemote(params.remoteUrl);
49269
49204
  const defaultBranch = params.defaultBranch?.trim().toLowerCase() || "";
49270
- const payload = remote ? { remote, defaultBranch } : { local: import_path4.default.resolve(params.gitRoot).toLowerCase(), defaultBranch };
49205
+ const payload = remote ? { remote, defaultBranch } : { local: import_path2.default.resolve(params.gitRoot).toLowerCase(), defaultBranch };
49271
49206
  return (0, import_crypto.createHash)("sha256").update(JSON.stringify(payload)).digest("hex");
49272
49207
  }
49273
49208
  function summarizeUnifiedDiff(diff) {
@@ -49283,6 +49218,268 @@ function summarizeUnifiedDiff(diff) {
49283
49218
  return { changedFilesCount, insertions, deletions };
49284
49219
  }
49285
49220
 
49221
+ // node_modules/@remixhq/core/dist/chunk-4L3ZBZUQ.js
49222
+ var import_promises14 = __toESM(require("fs/promises"), 1);
49223
+ var import_path3 = __toESM(require("path"), 1);
49224
+ var import_promises15 = __toESM(require("fs/promises"), 1);
49225
+ var import_path4 = __toESM(require("path"), 1);
49226
+ async function reserveDirectory(targetDir) {
49227
+ try {
49228
+ await import_promises15.default.mkdir(targetDir);
49229
+ return targetDir;
49230
+ } catch (error2) {
49231
+ if (error2?.code === "EEXIST") {
49232
+ throw new RemixError("Output directory already exists.", {
49233
+ exitCode: 2,
49234
+ hint: `Choose an empty destination path: ${targetDir}`
49235
+ });
49236
+ }
49237
+ throw error2;
49238
+ }
49239
+ }
49240
+ async function reserveAvailableDirPath(preferredDir) {
49241
+ const parent = import_path4.default.dirname(preferredDir);
49242
+ const base = import_path4.default.basename(preferredDir);
49243
+ for (let i2 = 1; i2 <= 1e3; i2 += 1) {
49244
+ const candidate = i2 === 1 ? preferredDir : import_path4.default.join(parent, `${base}-${i2}`);
49245
+ try {
49246
+ await import_promises15.default.mkdir(candidate);
49247
+ return candidate;
49248
+ } catch (error2) {
49249
+ if (error2?.code === "EEXIST") continue;
49250
+ throw error2;
49251
+ }
49252
+ }
49253
+ throw new RemixError("No available output directory name.", {
49254
+ exitCode: 2,
49255
+ hint: `Tried ${base} through ${base}-1000 under ${parent}.`
49256
+ });
49257
+ }
49258
+ async function writeJsonAtomic2(filePath, value) {
49259
+ const dir = import_path4.default.dirname(filePath);
49260
+ await import_promises15.default.mkdir(dir, { recursive: true });
49261
+ const tmp = `${filePath}.tmp-${Date.now()}`;
49262
+ await import_promises15.default.writeFile(tmp, `${JSON.stringify(value, null, 2)}
49263
+ `, "utf8");
49264
+ await import_promises15.default.rename(tmp, filePath);
49265
+ }
49266
+ function getCollabBindingPath(repoRoot) {
49267
+ return import_path3.default.join(repoRoot, ".remix", "config.json");
49268
+ }
49269
+ function buildBindingFileV3(params) {
49270
+ return {
49271
+ schemaVersion: 3,
49272
+ repoFingerprint: params.repoFingerprint,
49273
+ remoteUrl: params.remoteUrl,
49274
+ defaultBranch: params.defaultBranch,
49275
+ branchBindings: params.branchBindings
49276
+ };
49277
+ }
49278
+ function normalizeBranchName(value) {
49279
+ const normalized = String(value ?? "").trim();
49280
+ return normalized || null;
49281
+ }
49282
+ function normalizeProjectId(value) {
49283
+ const normalized = String(value ?? "").trim();
49284
+ return normalized || null;
49285
+ }
49286
+ function normalizeBranchBinding(value) {
49287
+ if (!value?.currentAppId || !value?.upstreamAppId) return null;
49288
+ return {
49289
+ projectId: normalizeProjectId(value.projectId),
49290
+ currentAppId: value.currentAppId,
49291
+ upstreamAppId: value.upstreamAppId,
49292
+ threadId: value.threadId ?? null,
49293
+ laneId: value.laneId ?? null,
49294
+ bindingMode: value.bindingMode === "legacy" ? "legacy" : "lane"
49295
+ };
49296
+ }
49297
+ function buildResolvedBinding(params) {
49298
+ if (!params.binding) return null;
49299
+ return {
49300
+ schemaVersion: 3,
49301
+ projectId: params.binding.projectId ?? params.fallbackProjectId,
49302
+ currentAppId: params.binding.currentAppId,
49303
+ upstreamAppId: params.binding.upstreamAppId,
49304
+ threadId: params.binding.threadId,
49305
+ repoFingerprint: params.repoFingerprint,
49306
+ remoteUrl: params.remoteUrl,
49307
+ defaultBranch: params.defaultBranch,
49308
+ laneId: params.binding.laneId,
49309
+ branchName: params.branchName,
49310
+ bindingMode: params.binding.bindingMode
49311
+ };
49312
+ }
49313
+ function deriveFallbackProjectId(params) {
49314
+ const candidates = [
49315
+ params.currentBranch ? params.branchBindings[params.currentBranch]?.projectId ?? null : null,
49316
+ params.defaultBranch ? params.branchBindings[params.defaultBranch]?.projectId ?? null : null,
49317
+ ...Object.values(params.branchBindings).map((binding) => binding.projectId),
49318
+ params.legacyProjectId
49319
+ ];
49320
+ for (const candidate of candidates) {
49321
+ if (candidate) return candidate;
49322
+ }
49323
+ return null;
49324
+ }
49325
+ async function readCollabBindingState(repoRoot, options) {
49326
+ try {
49327
+ const persist = options?.persist === true;
49328
+ const filePath = getCollabBindingPath(repoRoot);
49329
+ const raw = await import_promises14.default.readFile(filePath, "utf8");
49330
+ const parsed = JSON.parse(raw);
49331
+ if (!parsed || typeof parsed !== "object") return null;
49332
+ const currentBranch = normalizeBranchName(await getCurrentBranch(repoRoot).catch(() => null));
49333
+ if (parsed.schemaVersion === 1) {
49334
+ if (!parsed.currentAppId || !parsed.upstreamAppId) return null;
49335
+ const projectId = normalizeProjectId(parsed.projectId);
49336
+ const preferredBranch = normalizeBranchName(parsed.preferredBranch ?? parsed.defaultBranch ?? null);
49337
+ const branchKey = preferredBranch ?? currentBranch ?? null;
49338
+ const branchBindings2 = branchKey ? {
49339
+ [branchKey]: {
49340
+ projectId,
49341
+ currentAppId: parsed.currentAppId,
49342
+ upstreamAppId: parsed.upstreamAppId,
49343
+ threadId: parsed.threadId ?? null,
49344
+ laneId: null,
49345
+ bindingMode: "legacy"
49346
+ }
49347
+ } : {};
49348
+ const migratedFile = buildBindingFileV3({
49349
+ repoFingerprint: parsed.repoFingerprint ?? null,
49350
+ remoteUrl: parsed.remoteUrl ?? null,
49351
+ defaultBranch: parsed.defaultBranch ?? null,
49352
+ branchBindings: branchBindings2
49353
+ });
49354
+ if (persist) {
49355
+ try {
49356
+ await writeJsonAtomic2(filePath, migratedFile);
49357
+ } catch {
49358
+ }
49359
+ }
49360
+ return {
49361
+ schemaVersion: 3,
49362
+ projectId,
49363
+ repoFingerprint: migratedFile.repoFingerprint,
49364
+ remoteUrl: migratedFile.remoteUrl,
49365
+ defaultBranch: migratedFile.defaultBranch,
49366
+ currentBranch,
49367
+ branchBindings: migratedFile.branchBindings,
49368
+ binding: buildResolvedBinding({
49369
+ fallbackProjectId: projectId,
49370
+ repoFingerprint: migratedFile.repoFingerprint,
49371
+ remoteUrl: migratedFile.remoteUrl,
49372
+ defaultBranch: migratedFile.defaultBranch,
49373
+ branchName: branchKey,
49374
+ binding: branchKey ? migratedFile.branchBindings[branchKey] : null
49375
+ })
49376
+ };
49377
+ }
49378
+ if (parsed.schemaVersion !== 2 && parsed.schemaVersion !== 3) return null;
49379
+ const file = parsed;
49380
+ let shouldPersistNormalizedBranchBindings = false;
49381
+ const legacyProjectId = normalizeProjectId(file.projectId);
49382
+ const branchBindings = Object.fromEntries(
49383
+ Object.entries(file.branchBindings ?? {}).map(([branchName, branchBinding]) => {
49384
+ const normalized = normalizeBranchBinding(branchBinding);
49385
+ const rawProjectId = branchBinding && typeof branchBinding === "object" && "projectId" in branchBinding ? normalizeProjectId(branchBinding.projectId) : null;
49386
+ const rawBindingMode = branchBinding && typeof branchBinding === "object" && "bindingMode" in branchBinding ? branchBinding.bindingMode : null;
49387
+ const hasLegacyPreferredBranch = Boolean(branchBinding) && typeof branchBinding === "object" && "preferredBranch" in branchBinding;
49388
+ let normalizedWithProject = normalized;
49389
+ if (normalizedWithProject && !normalizedWithProject.projectId && legacyProjectId) {
49390
+ normalizedWithProject = {
49391
+ ...normalizedWithProject,
49392
+ projectId: legacyProjectId
49393
+ };
49394
+ }
49395
+ if (normalizedWithProject && (rawBindingMode !== normalizedWithProject.bindingMode || hasLegacyPreferredBranch || rawProjectId !== normalizedWithProject.projectId)) {
49396
+ shouldPersistNormalizedBranchBindings = true;
49397
+ }
49398
+ return [branchName, normalizedWithProject];
49399
+ }).filter((entry) => Boolean(entry[1]))
49400
+ );
49401
+ const legacyExplicitBinding = normalizeBranchBinding(file.explicitBinding ?? null);
49402
+ const legacyExplicitBranch = currentBranch ?? normalizeBranchName(file.defaultBranch);
49403
+ if (legacyExplicitBinding && legacyExplicitBranch) {
49404
+ branchBindings[legacyExplicitBranch] = {
49405
+ ...legacyExplicitBinding,
49406
+ projectId: legacyExplicitBinding.projectId ?? branchBindings[legacyExplicitBranch]?.projectId ?? legacyProjectId,
49407
+ bindingMode: "lane"
49408
+ };
49409
+ shouldPersistNormalizedBranchBindings = true;
49410
+ }
49411
+ if (persist && ("explicitBinding" in file || shouldPersistNormalizedBranchBindings || parsed.schemaVersion === 2)) {
49412
+ try {
49413
+ await writeJsonAtomic2(
49414
+ filePath,
49415
+ buildBindingFileV3({
49416
+ repoFingerprint: file.repoFingerprint ?? null,
49417
+ remoteUrl: file.remoteUrl ?? null,
49418
+ defaultBranch: file.defaultBranch ?? null,
49419
+ branchBindings
49420
+ })
49421
+ );
49422
+ } catch {
49423
+ }
49424
+ }
49425
+ const resolvedBranch = currentBranch ?? normalizeBranchName(file.defaultBranch);
49426
+ const fallbackProjectId = deriveFallbackProjectId({
49427
+ branchBindings,
49428
+ currentBranch: resolvedBranch,
49429
+ defaultBranch: normalizeBranchName(file.defaultBranch),
49430
+ legacyProjectId
49431
+ });
49432
+ return {
49433
+ schemaVersion: parsed.schemaVersion,
49434
+ projectId: fallbackProjectId,
49435
+ repoFingerprint: file.repoFingerprint ?? null,
49436
+ remoteUrl: file.remoteUrl ?? null,
49437
+ defaultBranch: file.defaultBranch ?? null,
49438
+ currentBranch,
49439
+ branchBindings,
49440
+ binding: buildResolvedBinding({
49441
+ fallbackProjectId,
49442
+ repoFingerprint: file.repoFingerprint ?? null,
49443
+ remoteUrl: file.remoteUrl ?? null,
49444
+ defaultBranch: file.defaultBranch ?? null,
49445
+ branchName: resolvedBranch,
49446
+ binding: resolvedBranch ? branchBindings[resolvedBranch] ?? null : null
49447
+ })
49448
+ };
49449
+ } catch {
49450
+ return null;
49451
+ }
49452
+ }
49453
+ async function readCollabBinding(repoRoot) {
49454
+ const state = await readCollabBindingState(repoRoot);
49455
+ return state?.binding ?? null;
49456
+ }
49457
+ async function writeCollabBinding(repoRoot, binding) {
49458
+ const filePath = getCollabBindingPath(repoRoot);
49459
+ const currentBranch = normalizeBranchName(await getCurrentBranch(repoRoot).catch(() => null));
49460
+ const branchName = normalizeBranchName(binding.branchName) ?? currentBranch ?? binding.defaultBranch ?? "main";
49461
+ const existing = await readCollabBindingState(repoRoot, { persist: true });
49462
+ const branchBindings = { ...existing?.branchBindings ?? {} };
49463
+ branchBindings[branchName] = {
49464
+ projectId: normalizeProjectId(binding.projectId) ?? branchBindings[branchName]?.projectId ?? existing?.projectId ?? null,
49465
+ currentAppId: binding.currentAppId,
49466
+ upstreamAppId: binding.upstreamAppId,
49467
+ threadId: binding.threadId ?? null,
49468
+ laneId: binding.laneId ?? null,
49469
+ bindingMode: binding.bindingMode ?? "lane"
49470
+ };
49471
+ await writeJsonAtomic2(
49472
+ filePath,
49473
+ buildBindingFileV3({
49474
+ repoFingerprint: binding.repoFingerprint ?? null,
49475
+ remoteUrl: binding.remoteUrl ?? null,
49476
+ defaultBranch: binding.defaultBranch ?? null,
49477
+ branchBindings
49478
+ })
49479
+ );
49480
+ return filePath;
49481
+ }
49482
+
49286
49483
  // node_modules/@remixhq/core/dist/collab.js
49287
49484
  var import_promises16 = __toESM(require("fs/promises"), 1);
49288
49485
  var import_path5 = __toESM(require("path"), 1);
@@ -49309,29 +49506,29 @@ function describeBranch(value) {
49309
49506
  const normalized = String(value ?? "").trim();
49310
49507
  return normalized || "(detached)";
49311
49508
  }
49312
- function isPreferredBranchMatch(currentBranch, preferredBranch) {
49509
+ function isBoundBranchMatch(currentBranch, branchName) {
49313
49510
  const current = String(currentBranch ?? "").trim();
49314
- const preferred = String(preferredBranch ?? "").trim();
49315
- if (!preferred || !current) return true;
49316
- return current === preferred;
49511
+ const expected = String(branchName ?? "").trim();
49512
+ if (!expected || !current) return true;
49513
+ return current === expected;
49317
49514
  }
49318
- function buildPreferredBranchMismatchHint(params) {
49515
+ function buildBranchMismatchHint(params) {
49319
49516
  const overrideFlag = params.overrideFlag?.trim() || "--allow-branch-mismatch";
49320
49517
  return [
49321
49518
  `Current branch: ${describeBranch(params.currentBranch)}`,
49322
- `Preferred branch: ${describeBranch(params.preferredBranch)}`,
49323
- `Switch to ${describeBranch(params.preferredBranch)} or rerun with ${overrideFlag} if this is intentional.`
49519
+ `Bound branch: ${describeBranch(params.branchName)}`,
49520
+ `Switch to ${describeBranch(params.branchName)} or rerun with ${overrideFlag} if this is intentional.`
49324
49521
  ].join("\n");
49325
49522
  }
49326
- function assertPreferredBranchMatch(params) {
49523
+ function assertBoundBranchMatch(params) {
49327
49524
  if (params.allowBranchMismatch) return;
49328
- if (isPreferredBranchMatch(params.currentBranch, params.preferredBranch)) return;
49329
- throw new RemixError(`Current branch does not match this checkout's Remix preferred branch while running ${params.operation}.`, {
49525
+ if (isBoundBranchMatch(params.currentBranch, params.branchName)) return;
49526
+ throw new RemixError(`Current branch does not match this checkout's bound Remix branch while running ${params.operation}.`, {
49330
49527
  code: REMIX_ERROR_CODES.PREFERRED_BRANCH_MISMATCH,
49331
49528
  exitCode: 2,
49332
- hint: buildPreferredBranchMismatchHint({
49529
+ hint: buildBranchMismatchHint({
49333
49530
  currentBranch: params.currentBranch,
49334
- preferredBranch: params.preferredBranch,
49531
+ branchName: params.branchName,
49335
49532
  overrideFlag: params.overrideFlag
49336
49533
  })
49337
49534
  });
@@ -49392,6 +49589,52 @@ function sanitizeCheckoutDirName(value) {
49392
49589
  function buildDashboardAppUrl(appId) {
49393
49590
  return `https://dashboard.remix.one/apps/${encodeURIComponent(appId)}`;
49394
49591
  }
49592
+ async function resolveProjectLaneIfAuthoritative(api, params) {
49593
+ const branchName = String(params.branchName ?? "").trim();
49594
+ if (!branchName) return null;
49595
+ if (!params.projectId && !params.repoFingerprint && !params.remoteUrl) return null;
49596
+ const readLane = async () => {
49597
+ const laneResp = await api.resolveProjectLaneBinding({
49598
+ projectId: params.projectId ?? void 0,
49599
+ repoFingerprint: params.repoFingerprint ?? void 0,
49600
+ remoteUrl: params.remoteUrl ?? void 0,
49601
+ defaultBranch: params.defaultBranch ?? void 0,
49602
+ branchName
49603
+ });
49604
+ return unwrapResponseObject(laneResp, "project lane binding");
49605
+ };
49606
+ let lane = await readLane();
49607
+ const shouldRepairResolvedLane = lane.status === "resolved" && Boolean(
49608
+ params.expectedUpstreamAppId && (!lane.upstreamAppId || lane.upstreamAppId !== params.expectedUpstreamAppId)
49609
+ );
49610
+ if (shouldRepairResolvedLane && params.createIfMissing && params.seedAppId) {
49611
+ const ensuredResp = await api.ensureProjectLaneBinding({
49612
+ projectId: params.projectId ?? void 0,
49613
+ repoFingerprint: params.repoFingerprint ?? void 0,
49614
+ remoteUrl: params.remoteUrl ?? void 0,
49615
+ defaultBranch: params.defaultBranch ?? void 0,
49616
+ branchName,
49617
+ seedAppId: params.seedAppId
49618
+ });
49619
+ lane = unwrapResponseObject(ensuredResp, "project lane binding");
49620
+ }
49621
+ if (lane.status !== "resolved" && params.createIfMissing && params.seedAppId) {
49622
+ const ensuredResp = await api.ensureProjectLaneBinding({
49623
+ projectId: params.projectId ?? void 0,
49624
+ repoFingerprint: params.repoFingerprint ?? void 0,
49625
+ remoteUrl: params.remoteUrl ?? void 0,
49626
+ defaultBranch: params.defaultBranch ?? void 0,
49627
+ branchName,
49628
+ seedAppId: params.seedAppId
49629
+ });
49630
+ lane = unwrapResponseObject(ensuredResp, "project lane binding");
49631
+ }
49632
+ if (lane.status !== "resolved") return null;
49633
+ if (params.projectId && lane.projectId && lane.projectId !== params.projectId) return null;
49634
+ if (params.currentAppId && lane.currentAppId && lane.currentAppId !== params.currentAppId) return null;
49635
+ if (params.expectedUpstreamAppId && lane.upstreamAppId && lane.upstreamAppId !== params.expectedUpstreamAppId) return null;
49636
+ return lane;
49637
+ }
49395
49638
  async function pollAppReady(api, appId) {
49396
49639
  const started = Date.now();
49397
49640
  let delay = 2e3;
@@ -49535,6 +49778,273 @@ async function pollMergeRequestCompletion(api, mrId, params) {
49535
49778
  }
49536
49779
  throw new RemixError("Timed out waiting for merge approval to complete.", { exitCode: 1 });
49537
49780
  }
49781
+ function normalizeBranchName2(value) {
49782
+ const normalized = String(value ?? "").trim();
49783
+ return normalized || null;
49784
+ }
49785
+ function buildBindingFromLane(state, lane) {
49786
+ if (!lane.currentAppId || !lane.upstreamAppId) return null;
49787
+ return {
49788
+ schemaVersion: 3,
49789
+ projectId: lane.projectId ?? state.projectId,
49790
+ currentAppId: lane.currentAppId,
49791
+ upstreamAppId: lane.upstreamAppId,
49792
+ threadId: lane.threadId ?? null,
49793
+ repoFingerprint: lane.repoFingerprint ?? state.repoFingerprint ?? null,
49794
+ remoteUrl: lane.remoteUrl ?? state.remoteUrl ?? null,
49795
+ defaultBranch: lane.defaultBranch ?? state.defaultBranch ?? null,
49796
+ laneId: lane.laneId ?? null,
49797
+ branchName: lane.branchName ?? state.currentBranch ?? null,
49798
+ bindingMode: "lane"
49799
+ };
49800
+ }
49801
+ function shouldPersistRemoteLaneMetadata(localBinding, lane) {
49802
+ return Boolean(
49803
+ !localBinding.laneId && lane.laneId || !localBinding.threadId && lane.threadId || !localBinding.repoFingerprint && lane.repoFingerprint || !localBinding.remoteUrl && lane.remoteUrl || !localBinding.defaultBranch && lane.defaultBranch || !localBinding.branchName && lane.branchName
49804
+ );
49805
+ }
49806
+ function shouldRequireRemoteLaneForCurrentBranch(params) {
49807
+ if (!params.currentBranch) return false;
49808
+ const defaultBranch = normalizeBranchName2(params.defaultBranch);
49809
+ if (params.currentBranch === defaultBranch) return false;
49810
+ return !params.binding.laneId || params.binding.currentAppId === params.binding.upstreamAppId;
49811
+ }
49812
+ async function persistResolvedLane(repoRoot, binding) {
49813
+ await writeCollabBinding(repoRoot, {
49814
+ projectId: binding.projectId,
49815
+ currentAppId: binding.currentAppId,
49816
+ upstreamAppId: binding.upstreamAppId,
49817
+ threadId: binding.threadId,
49818
+ repoFingerprint: binding.repoFingerprint,
49819
+ remoteUrl: binding.remoteUrl,
49820
+ defaultBranch: binding.defaultBranch,
49821
+ laneId: binding.laneId,
49822
+ branchName: binding.branchName,
49823
+ bindingMode: binding.bindingMode
49824
+ });
49825
+ return readCollabBinding(repoRoot);
49826
+ }
49827
+ async function resolveActiveLaneBinding(params) {
49828
+ const state = await readCollabBindingState(params.repoRoot);
49829
+ if (!state) {
49830
+ return { status: "not_bound", currentBranch: null };
49831
+ }
49832
+ const currentBranch = normalizeBranchName2(state.currentBranch);
49833
+ const localBinding = state.binding;
49834
+ if (localBinding) {
49835
+ const requireRemoteLane = shouldRequireRemoteLaneForCurrentBranch({
49836
+ binding: localBinding,
49837
+ currentBranch,
49838
+ defaultBranch: state.defaultBranch
49839
+ });
49840
+ if (!params.api || !currentBranch) {
49841
+ return {
49842
+ status: "resolved",
49843
+ source: "local",
49844
+ binding: localBinding,
49845
+ currentBranch
49846
+ };
49847
+ }
49848
+ const laneResp2 = await params.api.resolveProjectLaneBinding({
49849
+ projectId: localBinding.projectId ?? state.projectId ?? void 0,
49850
+ repoFingerprint: state.repoFingerprint ?? void 0,
49851
+ remoteUrl: state.remoteUrl ?? void 0,
49852
+ defaultBranch: state.defaultBranch ?? void 0,
49853
+ branchName: currentBranch
49854
+ });
49855
+ const lane2 = unwrapResponseObject(laneResp2, "project lane binding");
49856
+ if (lane2.status === "resolved") {
49857
+ const resolvedBranch = normalizeBranchName2(lane2.branchName);
49858
+ const resolvedProjectId = lane2.projectId ?? state.projectId;
49859
+ const branchConflict = Boolean(resolvedBranch && localBinding.branchName && resolvedBranch !== localBinding.branchName);
49860
+ const appConflict = Boolean(lane2.currentAppId && lane2.currentAppId !== localBinding.currentAppId);
49861
+ const upstreamConflict = Boolean(lane2.upstreamAppId && lane2.upstreamAppId !== localBinding.upstreamAppId);
49862
+ const projectConflict = Boolean(resolvedProjectId && localBinding.projectId && resolvedProjectId !== localBinding.projectId);
49863
+ if (branchConflict || appConflict || upstreamConflict || projectConflict) {
49864
+ if (requireRemoteLane) {
49865
+ const binding = buildBindingFromLane(state, lane2);
49866
+ if (binding) {
49867
+ return {
49868
+ status: "resolved",
49869
+ source: "remote",
49870
+ binding,
49871
+ currentBranch
49872
+ };
49873
+ }
49874
+ }
49875
+ return {
49876
+ status: "binding_conflict",
49877
+ binding: localBinding,
49878
+ resolvedLane: lane2,
49879
+ currentBranch
49880
+ };
49881
+ }
49882
+ if (shouldPersistRemoteLaneMetadata(localBinding, lane2)) {
49883
+ const binding = buildBindingFromLane(state, lane2);
49884
+ if (binding) {
49885
+ return {
49886
+ status: "resolved",
49887
+ source: "remote",
49888
+ binding,
49889
+ currentBranch
49890
+ };
49891
+ }
49892
+ }
49893
+ }
49894
+ if (requireRemoteLane) {
49895
+ return {
49896
+ status: "missing_branch_binding",
49897
+ currentBranch,
49898
+ projectId: state.projectId,
49899
+ repoFingerprint: state.repoFingerprint,
49900
+ remoteUrl: state.remoteUrl,
49901
+ defaultBranch: state.defaultBranch,
49902
+ upstreamAppId: localBinding.upstreamAppId ?? null,
49903
+ threadId: localBinding.threadId ?? null
49904
+ };
49905
+ }
49906
+ return {
49907
+ status: "resolved",
49908
+ source: "local",
49909
+ binding: localBinding,
49910
+ currentBranch
49911
+ };
49912
+ }
49913
+ if (!params.api || !currentBranch) {
49914
+ return {
49915
+ status: "missing_branch_binding",
49916
+ currentBranch,
49917
+ projectId: state.projectId,
49918
+ repoFingerprint: state.repoFingerprint,
49919
+ remoteUrl: state.remoteUrl,
49920
+ defaultBranch: state.defaultBranch,
49921
+ upstreamAppId: null,
49922
+ threadId: null
49923
+ };
49924
+ }
49925
+ const laneResp = await params.api.resolveProjectLaneBinding({
49926
+ projectId: state.projectId ?? void 0,
49927
+ repoFingerprint: state.repoFingerprint ?? void 0,
49928
+ remoteUrl: state.remoteUrl ?? void 0,
49929
+ defaultBranch: state.defaultBranch ?? void 0,
49930
+ branchName: currentBranch
49931
+ });
49932
+ const lane = unwrapResponseObject(laneResp, "project lane binding");
49933
+ if (lane.status === "resolved") {
49934
+ const binding = buildBindingFromLane(state, lane);
49935
+ if (binding) {
49936
+ return {
49937
+ status: "resolved",
49938
+ source: "remote",
49939
+ binding,
49940
+ currentBranch
49941
+ };
49942
+ }
49943
+ }
49944
+ if (lane.status === "binding_not_found") {
49945
+ return { status: "not_bound", currentBranch };
49946
+ }
49947
+ return {
49948
+ status: "missing_branch_binding",
49949
+ currentBranch,
49950
+ projectId: lane.projectId ?? state.projectId,
49951
+ repoFingerprint: lane.repoFingerprint ?? state.repoFingerprint,
49952
+ remoteUrl: lane.remoteUrl ?? state.remoteUrl,
49953
+ defaultBranch: lane.defaultBranch ?? state.defaultBranch,
49954
+ upstreamAppId: lane.upstreamAppId ?? null,
49955
+ threadId: lane.threadId ?? null
49956
+ };
49957
+ }
49958
+ async function ensureActiveLaneBinding(params) {
49959
+ const resolved = await resolveActiveLaneBinding({
49960
+ repoRoot: params.repoRoot,
49961
+ api: params.api
49962
+ });
49963
+ if (resolved.status === "resolved") {
49964
+ if (resolved.source === "local") {
49965
+ return resolved.binding;
49966
+ }
49967
+ return persistResolvedLane(params.repoRoot, resolved.binding);
49968
+ }
49969
+ if (resolved.status === "binding_conflict") {
49970
+ throw new RemixError("Current branch binding conflicts with the server-resolved Remix lane.", {
49971
+ exitCode: 2,
49972
+ hint: `Local app ${resolved.binding.currentAppId}; server app ${resolved.resolvedLane.currentAppId ?? "(unknown)"}. Repair the branch binding before running ${params.operation ?? "this command"}.`
49973
+ });
49974
+ }
49975
+ if (resolved.status === "not_bound") {
49976
+ return null;
49977
+ }
49978
+ if (!resolved.currentBranch) {
49979
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
49980
+ exitCode: 2,
49981
+ hint: `Switch to a named branch before running ${params.operation ?? "this command"}.`
49982
+ });
49983
+ }
49984
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
49985
+ exitCode: 2,
49986
+ hint: `Run \`remix collab init\` on branch ${resolved.currentBranch} before running ${params.operation ?? "this command"}.`
49987
+ });
49988
+ }
49989
+ async function provisionActiveLaneBinding(params) {
49990
+ const resolved = await resolveActiveLaneBinding(params);
49991
+ if (resolved.status === "resolved") {
49992
+ if (resolved.source === "local") {
49993
+ return { binding: resolved.binding, warnings: [] };
49994
+ }
49995
+ const persisted2 = await persistResolvedLane(params.repoRoot, resolved.binding);
49996
+ return { binding: persisted2, warnings: [] };
49997
+ }
49998
+ if (resolved.status === "binding_conflict") {
49999
+ throw new RemixError("Current branch binding conflicts with the server-resolved Remix lane.", {
50000
+ exitCode: 2,
50001
+ hint: `Local app ${resolved.binding.currentAppId}; server app ${resolved.resolvedLane.currentAppId ?? "(unknown)"}. Repair the branch binding before running ${params.operation}.`
50002
+ });
50003
+ }
50004
+ if (resolved.status === "not_bound") {
50005
+ return { binding: null, warnings: [] };
50006
+ }
50007
+ if (!resolved.currentBranch) {
50008
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
50009
+ exitCode: 2,
50010
+ hint: `Switch to a named branch before running ${params.operation}.`
50011
+ });
50012
+ }
50013
+ let lane;
50014
+ try {
50015
+ const laneResp = await params.api.ensureProjectLaneBinding({
50016
+ projectId: resolved.projectId ?? void 0,
50017
+ repoFingerprint: resolved.repoFingerprint ?? void 0,
50018
+ remoteUrl: resolved.remoteUrl ?? void 0,
50019
+ defaultBranch: resolved.defaultBranch ?? void 0,
50020
+ branchName: resolved.currentBranch
50021
+ });
50022
+ lane = unwrapResponseObject(laneResp, "project lane binding");
50023
+ } catch (error2) {
50024
+ throw new RemixError(`Failed to provision a Remix lane for branch ${resolved.currentBranch}.`, {
50025
+ exitCode: error2 instanceof RemixError ? error2.exitCode : 1,
50026
+ hint: formatCliErrorDetail(error2) || `Remix could not create or recover the branch lane required before ${params.operation}.`
50027
+ });
50028
+ }
50029
+ const state = await readCollabBindingState(params.repoRoot);
50030
+ if (!state) {
50031
+ return { binding: null, warnings: [] };
50032
+ }
50033
+ const binding = buildBindingFromLane(state, lane);
50034
+ if (!binding) {
50035
+ throw new RemixError(`Failed to provision a Remix lane for branch ${resolved.currentBranch}.`, {
50036
+ exitCode: 1,
50037
+ hint: "The server returned incomplete lane binding metadata."
50038
+ });
50039
+ }
50040
+ const persisted = await persistResolvedLane(params.repoRoot, binding);
50041
+ return {
50042
+ binding: persisted,
50043
+ warnings: [
50044
+ lane.created ? `Provisioned Remix lane for branch ${resolved.currentBranch}.` : `Recovered existing Remix lane binding for branch ${resolved.currentBranch}.`
50045
+ ]
50046
+ };
50047
+ }
49538
50048
  async function collabRecordingPreflight(params) {
49539
50049
  let repoRoot;
49540
50050
  try {
@@ -49546,7 +50056,7 @@ async function collabRecordingPreflight(params) {
49546
50056
  repoRoot: null,
49547
50057
  appId: null,
49548
50058
  currentBranch: null,
49549
- preferredBranch: null,
50059
+ branchName: null,
49550
50060
  headCommitHash: null,
49551
50061
  worktreeClean: false,
49552
50062
  syncStatus: null,
@@ -49558,14 +50068,14 @@ async function collabRecordingPreflight(params) {
49558
50068
  hint: message
49559
50069
  };
49560
50070
  }
49561
- const binding = await readCollabBinding(repoRoot);
49562
- if (!binding) {
50071
+ const bindingResolution = await resolveActiveLaneBinding({ repoRoot, api: params.api });
50072
+ if (bindingResolution.status === "not_bound") {
49563
50073
  return {
49564
50074
  status: "not_bound",
49565
50075
  repoRoot,
49566
50076
  appId: null,
49567
50077
  currentBranch: null,
49568
- preferredBranch: null,
50078
+ branchName: null,
49569
50079
  headCommitHash: null,
49570
50080
  worktreeClean: false,
49571
50081
  syncStatus: null,
@@ -49577,19 +50087,56 @@ async function collabRecordingPreflight(params) {
49577
50087
  hint: "Run `remix collab init` first."
49578
50088
  };
49579
50089
  }
50090
+ if (bindingResolution.status === "missing_branch_binding") {
50091
+ return {
50092
+ status: "branch_binding_missing",
50093
+ repoRoot,
50094
+ appId: null,
50095
+ currentBranch: bindingResolution.currentBranch,
50096
+ branchName: bindingResolution.currentBranch,
50097
+ headCommitHash: null,
50098
+ worktreeClean: false,
50099
+ syncStatus: null,
50100
+ syncTargetCommitHash: null,
50101
+ syncTargetCommitId: null,
50102
+ reconcileTargetHeadCommitHash: null,
50103
+ reconcileTargetHeadCommitId: null,
50104
+ warnings: [],
50105
+ hint: `Current branch ${bindingResolution.currentBranch ?? "(detached)"} is not yet bound to a Remix lane.`
50106
+ };
50107
+ }
50108
+ if (bindingResolution.status === "binding_conflict") {
50109
+ return {
50110
+ status: "metadata_conflict",
50111
+ repoRoot,
50112
+ appId: bindingResolution.binding.currentAppId,
50113
+ currentBranch: bindingResolution.currentBranch,
50114
+ branchName: bindingResolution.binding.branchName,
50115
+ headCommitHash: null,
50116
+ worktreeClean: false,
50117
+ syncStatus: null,
50118
+ syncTargetCommitHash: null,
50119
+ syncTargetCommitId: null,
50120
+ reconcileTargetHeadCommitHash: null,
50121
+ reconcileTargetHeadCommitId: null,
50122
+ warnings: [],
50123
+ 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.`
50124
+ };
50125
+ }
50126
+ const binding = bindingResolution.binding;
49580
50127
  const [currentBranch, headCommitHash, worktreeStatus] = await Promise.all([
49581
50128
  getCurrentBranch(repoRoot),
49582
50129
  getHeadCommitHash(repoRoot),
49583
50130
  getWorktreeStatus(repoRoot)
49584
50131
  ]);
49585
- const preferredBranch = binding.preferredBranch ?? null;
50132
+ const branchName = binding.branchName ?? null;
49586
50133
  if (!headCommitHash) {
49587
50134
  return {
49588
50135
  status: "missing_head",
49589
50136
  repoRoot,
49590
50137
  appId: binding.currentAppId,
49591
50138
  currentBranch,
49592
- preferredBranch,
50139
+ branchName,
49593
50140
  headCommitHash: null,
49594
50141
  worktreeClean: worktreeStatus.isClean,
49595
50142
  syncStatus: null,
@@ -49601,13 +50148,13 @@ async function collabRecordingPreflight(params) {
49601
50148
  hint: "Failed to resolve local HEAD commit."
49602
50149
  };
49603
50150
  }
49604
- if (!params.allowBranchMismatch && !isPreferredBranchMatch(currentBranch, preferredBranch)) {
50151
+ if (!params.allowBranchMismatch && !isBoundBranchMatch(currentBranch, branchName)) {
49605
50152
  return {
49606
50153
  status: "branch_mismatch",
49607
50154
  repoRoot,
49608
50155
  appId: binding.currentAppId,
49609
50156
  currentBranch,
49610
- preferredBranch,
50157
+ branchName,
49611
50158
  headCommitHash,
49612
50159
  worktreeClean: worktreeStatus.isClean,
49613
50160
  syncStatus: null,
@@ -49616,9 +50163,9 @@ async function collabRecordingPreflight(params) {
49616
50163
  reconcileTargetHeadCommitHash: null,
49617
50164
  reconcileTargetHeadCommitId: null,
49618
50165
  warnings: [],
49619
- hint: buildPreferredBranchMismatchHint({
50166
+ hint: buildBranchMismatchHint({
49620
50167
  currentBranch,
49621
- preferredBranch
50168
+ branchName
49622
50169
  })
49623
50170
  };
49624
50171
  }
@@ -49636,7 +50183,7 @@ async function collabRecordingPreflight(params) {
49636
50183
  repoRoot,
49637
50184
  appId: binding.currentAppId,
49638
50185
  currentBranch,
49639
- preferredBranch,
50186
+ branchName,
49640
50187
  headCommitHash,
49641
50188
  worktreeClean: worktreeStatus.isClean,
49642
50189
  syncStatus: sync.status,
@@ -49654,7 +50201,7 @@ async function collabRecordingPreflight(params) {
49654
50201
  repoRoot,
49655
50202
  appId: binding.currentAppId,
49656
50203
  currentBranch,
49657
- preferredBranch,
50204
+ branchName,
49658
50205
  headCommitHash,
49659
50206
  worktreeClean: worktreeStatus.isClean,
49660
50207
  syncStatus: sync.status,
@@ -49679,7 +50226,7 @@ async function collabRecordingPreflight(params) {
49679
50226
  repoRoot,
49680
50227
  appId: binding.currentAppId,
49681
50228
  currentBranch,
49682
- preferredBranch,
50229
+ branchName,
49683
50230
  headCommitHash,
49684
50231
  worktreeClean: worktreeStatus.isClean,
49685
50232
  syncStatus: sync.status,
@@ -49697,7 +50244,7 @@ async function collabRecordingPreflight(params) {
49697
50244
  repoRoot,
49698
50245
  appId: binding.currentAppId,
49699
50246
  currentBranch,
49700
- preferredBranch,
50247
+ branchName,
49701
50248
  headCommitHash,
49702
50249
  worktreeClean: worktreeStatus.isClean,
49703
50250
  syncStatus: sync.status,
@@ -49714,7 +50261,7 @@ async function collabRecordingPreflight(params) {
49714
50261
  repoRoot,
49715
50262
  appId: binding.currentAppId,
49716
50263
  currentBranch,
49717
- preferredBranch,
50264
+ branchName,
49718
50265
  headCommitHash,
49719
50266
  worktreeClean: worktreeStatus.isClean,
49720
50267
  syncStatus: sync.status,
@@ -49936,7 +50483,11 @@ async function withRepoMutationLock(options, fn) {
49936
50483
  }
49937
50484
  async function collabSync(params) {
49938
50485
  const repoRoot = await findGitRoot(params.cwd);
49939
- const binding = await readCollabBinding(repoRoot);
50486
+ const binding = await ensureActiveLaneBinding({
50487
+ repoRoot,
50488
+ api: params.api,
50489
+ operation: "`remix collab sync`"
50490
+ });
49940
50491
  if (!binding) {
49941
50492
  throw new RemixError("Repository is not bound to Remix.", {
49942
50493
  exitCode: 2,
@@ -49945,9 +50496,9 @@ async function collabSync(params) {
49945
50496
  }
49946
50497
  await ensureCleanWorktree(repoRoot);
49947
50498
  const branch = await requireCurrentBranch(repoRoot);
49948
- assertPreferredBranchMatch({
50499
+ assertBoundBranchMatch({
49949
50500
  currentBranch: branch,
49950
- preferredBranch: binding.preferredBranch,
50501
+ branchName: binding.branchName,
49951
50502
  allowBranchMismatch: params.allowBranchMismatch,
49952
50503
  operation: "`remix collab sync`"
49953
50504
  });
@@ -50024,9 +50575,9 @@ async function collabSync(params) {
50024
50575
  });
50025
50576
  await ensureCleanWorktree(lockedRepoRoot);
50026
50577
  const lockedBranch = await requireCurrentBranch(lockedRepoRoot);
50027
- assertPreferredBranchMatch({
50578
+ assertBoundBranchMatch({
50028
50579
  currentBranch: lockedBranch,
50029
- preferredBranch: binding.preferredBranch,
50580
+ branchName: binding.branchName,
50030
50581
  allowBranchMismatch: params.allowBranchMismatch,
50031
50582
  operation: "`remix collab sync`"
50032
50583
  });
@@ -50057,6 +50608,12 @@ function assertSupportedRecordingPreflight(preflight) {
50057
50608
  hint: preflight.hint
50058
50609
  });
50059
50610
  }
50611
+ if (preflight.status === "branch_binding_missing") {
50612
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
50613
+ exitCode: 2,
50614
+ hint: preflight.hint
50615
+ });
50616
+ }
50060
50617
  if (preflight.status === "not_git_repo") {
50061
50618
  throw new RemixError(preflight.hint || "Not inside a git repository.", {
50062
50619
  exitCode: 2,
@@ -50070,9 +50627,9 @@ function assertSupportedRecordingPreflight(preflight) {
50070
50627
  });
50071
50628
  }
50072
50629
  if (preflight.status === "branch_mismatch") {
50073
- assertPreferredBranchMatch({
50630
+ assertBoundBranchMatch({
50074
50631
  currentBranch: preflight.currentBranch,
50075
- preferredBranch: preflight.preferredBranch,
50632
+ branchName: preflight.branchName,
50076
50633
  allowBranchMismatch: false,
50077
50634
  operation: "`remix collab add`"
50078
50635
  });
@@ -50092,7 +50649,11 @@ function assertSupportedRecordingPreflight(preflight) {
50092
50649
  }
50093
50650
  async function collabAdd(params) {
50094
50651
  const repoRoot = await findGitRoot(params.cwd);
50095
- const binding = await readCollabBinding(repoRoot);
50652
+ const binding = await ensureActiveLaneBinding({
50653
+ repoRoot,
50654
+ api: params.api,
50655
+ operation: "`remix collab add`"
50656
+ });
50096
50657
  if (!binding) {
50097
50658
  throw new RemixError("Repository is not bound to Remix.", {
50098
50659
  exitCode: 2,
@@ -50113,9 +50674,9 @@ async function collabAdd(params) {
50113
50674
  });
50114
50675
  assertSupportedRecordingPreflight(preflight);
50115
50676
  const branch = preflight.currentBranch;
50116
- assertPreferredBranchMatch({
50677
+ assertBoundBranchMatch({
50117
50678
  currentBranch: branch,
50118
- preferredBranch: binding.preferredBranch,
50679
+ branchName: binding.branchName,
50119
50680
  allowBranchMismatch: params.allowBranchMismatch,
50120
50681
  operation: "`remix collab add`"
50121
50682
  });
@@ -50288,6 +50849,7 @@ async function collabAdd(params) {
50288
50849
  });
50289
50850
  const resp = await params.api.createChangeStep(binding.currentAppId, {
50290
50851
  threadId: binding.threadId ?? void 0,
50852
+ collabLaneId: binding.laneId ?? void 0,
50291
50853
  prompt,
50292
50854
  assistantResponse: assistantResponse ?? void 0,
50293
50855
  diff,
@@ -50361,6 +50923,12 @@ function assertSupportedRecordingPreflight2(preflight) {
50361
50923
  hint: preflight.hint
50362
50924
  });
50363
50925
  }
50926
+ if (preflight.status === "branch_binding_missing") {
50927
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
50928
+ exitCode: 2,
50929
+ hint: preflight.hint
50930
+ });
50931
+ }
50364
50932
  if (preflight.status === "not_git_repo") {
50365
50933
  throw new RemixError(preflight.hint || "Not inside a git repository.", {
50366
50934
  exitCode: 2,
@@ -50374,9 +50942,9 @@ function assertSupportedRecordingPreflight2(preflight) {
50374
50942
  });
50375
50943
  }
50376
50944
  if (preflight.status === "branch_mismatch") {
50377
- assertPreferredBranchMatch({
50945
+ assertBoundBranchMatch({
50378
50946
  currentBranch: preflight.currentBranch,
50379
- preferredBranch: preflight.preferredBranch,
50947
+ branchName: preflight.branchName,
50380
50948
  allowBranchMismatch: false,
50381
50949
  operation: "`remix collab record-turn`"
50382
50950
  });
@@ -50396,7 +50964,11 @@ function assertSupportedRecordingPreflight2(preflight) {
50396
50964
  }
50397
50965
  async function collabRecordTurn(params) {
50398
50966
  const repoRoot = await findGitRoot(params.cwd);
50399
- const binding = await readCollabBinding(repoRoot);
50967
+ const binding = await ensureActiveLaneBinding({
50968
+ repoRoot,
50969
+ api: params.api,
50970
+ operation: "`remix collab record-turn`"
50971
+ });
50400
50972
  if (!binding) {
50401
50973
  throw new RemixError("Repository is not bound to Remix.", {
50402
50974
  exitCode: 2,
@@ -50428,9 +51000,9 @@ async function collabRecordTurn(params) {
50428
51000
  });
50429
51001
  }
50430
51002
  const branch = await getCurrentBranch(repoRoot);
50431
- assertPreferredBranchMatch({
51003
+ assertBoundBranchMatch({
50432
51004
  currentBranch: branch,
50433
- preferredBranch: binding.preferredBranch,
51005
+ branchName: binding.branchName,
50434
51006
  allowBranchMismatch: params.allowBranchMismatch,
50435
51007
  operation: "`remix collab record-turn`"
50436
51008
  });
@@ -50444,6 +51016,7 @@ async function collabRecordTurn(params) {
50444
51016
  });
50445
51017
  const resp = await params.api.createCollabTurn(binding.currentAppId, {
50446
51018
  threadId: binding.threadId ?? void 0,
51019
+ collabLaneId: binding.laneId ?? void 0,
50447
51020
  prompt,
50448
51021
  assistantResponse,
50449
51022
  actor: params.actor,
@@ -50456,7 +51029,8 @@ async function collabRecordTurn(params) {
50456
51029
  },
50457
51030
  idempotencyKey
50458
51031
  });
50459
- return unwrapResponseObject(resp, "collab turn");
51032
+ const turn = unwrapResponseObject(resp, "collab turn");
51033
+ return turn;
50460
51034
  }
50461
51035
  function collectWarnings(value) {
50462
51036
  if (!Array.isArray(value)) return [];
@@ -50464,7 +51038,11 @@ function collectWarnings(value) {
50464
51038
  }
50465
51039
  async function collabFinalizeTurn(params) {
50466
51040
  const repoRoot = await findGitRoot(params.cwd);
50467
- const binding = await readCollabBinding(repoRoot);
51041
+ const binding = await ensureActiveLaneBinding({
51042
+ repoRoot,
51043
+ api: params.api,
51044
+ operation: "`remix collab finalize-turn`"
51045
+ });
50468
51046
  if (!binding) {
50469
51047
  throw new RemixError("Repository is not bound to Remix.", {
50470
51048
  exitCode: 2,
@@ -50570,7 +51148,11 @@ async function collabApprove(params) {
50570
51148
  operation: "collabApproveSyncTarget"
50571
51149
  },
50572
51150
  async ({ repoRoot, warnings }) => {
50573
- const binding = await readCollabBinding(repoRoot);
51151
+ const binding = await ensureActiveLaneBinding({
51152
+ repoRoot,
51153
+ api: params.api,
51154
+ operation: "`remix collab approve --sync-target-repo`"
51155
+ });
50574
51156
  if (!binding) {
50575
51157
  throw new RemixError("Repository is not bound to Remix.", {
50576
51158
  exitCode: 2,
@@ -50579,9 +51161,9 @@ async function collabApprove(params) {
50579
51161
  }
50580
51162
  await ensureCleanWorktree(repoRoot, "`remix collab approve --sync-target-repo`");
50581
51163
  const branch = await requireCurrentBranch(repoRoot);
50582
- assertPreferredBranchMatch({
51164
+ assertBoundBranchMatch({
50583
51165
  currentBranch: branch,
50584
- preferredBranch: binding.preferredBranch,
51166
+ branchName: binding.branchName,
50585
51167
  allowBranchMismatch: params.allowBranchMismatch,
50586
51168
  operation: "`remix collab approve --sync-target-repo`"
50587
51169
  });
@@ -50659,10 +51241,6 @@ function isSubpath(parentPath, candidatePath) {
50659
51241
  const relative = import_path8.default.relative(parentPath, candidatePath);
50660
51242
  return relative === "" || !relative.startsWith("..") && !import_path8.default.isAbsolute(relative);
50661
51243
  }
50662
- function buildPreferredCheckoutBranch(appId) {
50663
- const normalized = appId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
50664
- return `remix/remix/${normalized || "app"}`;
50665
- }
50666
51244
  async function resolveCheckoutDestination(params) {
50667
51245
  if (params.outputDir?.trim()) {
50668
51246
  const preferredRepoRoot = import_path8.default.resolve(params.outputDir.trim());
@@ -50727,7 +51305,9 @@ async function materializeAppCheckout(params) {
50727
51305
  const bundle = await params.api.downloadAppBundle(params.appId);
50728
51306
  await import_promises19.default.writeFile(bundlePath, bundle.data);
50729
51307
  await cloneGitBundleToDirectory(bundlePath, repoRoot);
50730
- await checkoutLocalBranch(repoRoot, buildPreferredCheckoutBranch(params.appId));
51308
+ if (params.expectedBranchName?.trim()) {
51309
+ await checkoutLocalBranch(repoRoot, params.expectedBranchName.trim());
51310
+ }
50731
51311
  await ensureGitInfoExcludeEntries(repoRoot, [".remix/"]);
50732
51312
  } catch (err) {
50733
51313
  await import_promises19.default.rm(repoRoot, { recursive: true, force: true }).catch(() => {
@@ -50736,15 +51316,15 @@ async function materializeAppCheckout(params) {
50736
51316
  } finally {
50737
51317
  await import_promises19.default.rm(bundleTempDir, { recursive: true, force: true });
50738
51318
  }
50739
- const remoteUrl = normalizeGitRemote(await getRemoteOriginUrl(repoRoot));
50740
- const defaultBranch = await getDefaultBranch(repoRoot) ?? await getCurrentBranch(repoRoot) ?? null;
50741
- const preferredBranch = await getCurrentBranch(repoRoot) ?? buildPreferredCheckoutBranch(params.appId);
51319
+ const branchName = await getCurrentBranch(repoRoot) ?? params.expectedBranchName?.trim() ?? null;
51320
+ const remoteUrl = normalizeGitRemote(params.expectedRemoteUrl ?? await getRemoteOriginUrl(repoRoot));
51321
+ const defaultBranch = params.expectedDefaultBranch?.trim() ?? await getDefaultBranch(repoRoot) ?? branchName ?? null;
50742
51322
  const repoFingerprint = remoteUrl ? await buildRepoFingerprint({ gitRoot: repoRoot, remoteUrl, defaultBranch }) : null;
50743
51323
  return {
50744
51324
  repoRoot,
50745
51325
  remoteUrl,
50746
51326
  defaultBranch,
50747
- preferredBranch,
51327
+ branchName,
50748
51328
  repoFingerprint
50749
51329
  };
50750
51330
  }
@@ -50757,29 +51337,55 @@ async function collabCheckout(params) {
50757
51337
  });
50758
51338
  }
50759
51339
  const app = await pollAppReady(params.api, appId);
51340
+ const collab = app.collab && typeof app.collab === "object" ? app.collab : null;
51341
+ const source = app.source && typeof app.source === "object" ? app.source : null;
51342
+ const authoritativeBranchName = typeof collab?.branchName === "string" && collab.branchName.trim() || typeof source?.branch === "string" && source.branch.trim() || typeof source?.defaultBranch === "string" && source.defaultBranch.trim() || null;
51343
+ const authoritativeRemoteUrl = typeof source?.remoteUrl === "string" && source.remoteUrl.trim() || null;
51344
+ const authoritativeDefaultBranch = typeof source?.defaultBranch === "string" && source.defaultBranch.trim() || null;
50760
51345
  const checkout = await materializeAppCheckout({
50761
51346
  api: params.api,
50762
51347
  cwd: params.cwd,
50763
51348
  appId: String(app.id),
50764
51349
  outputDir: params.outputDir ?? null,
50765
- defaultDirName: sanitizeCheckoutDirName(String(app.name || app.id))
51350
+ defaultDirName: sanitizeCheckoutDirName(String(app.name || app.id)),
51351
+ expectedBranchName: authoritativeBranchName,
51352
+ expectedRemoteUrl: authoritativeRemoteUrl,
51353
+ expectedDefaultBranch: authoritativeDefaultBranch
50766
51354
  });
50767
51355
  const upstreamAppId = String(app.forkedFromAppId ?? app.id);
50768
- const bindingPath = await writeCollabBinding(checkout.repoRoot, {
51356
+ const laneId = typeof collab?.laneId === "string" ? collab.laneId : null;
51357
+ const repoFingerprint = typeof source?.repoFingerprint === "string" ? source.repoFingerprint : checkout.repoFingerprint;
51358
+ const remoteUrl = typeof source?.remoteUrl === "string" ? source.remoteUrl : checkout.remoteUrl;
51359
+ const defaultBranch = typeof source?.defaultBranch === "string" ? source.defaultBranch : checkout.defaultBranch;
51360
+ const branchName = authoritativeBranchName ?? checkout.branchName;
51361
+ const authoritativeLane = await resolveProjectLaneIfAuthoritative(params.api, {
50769
51362
  projectId: String(app.projectId),
51363
+ repoFingerprint,
51364
+ remoteUrl,
51365
+ defaultBranch,
51366
+ branchName,
50770
51367
  currentAppId: String(app.id),
50771
- upstreamAppId,
50772
- threadId: app.threadId ? String(app.threadId) : null,
50773
- repoFingerprint: checkout.repoFingerprint,
50774
- remoteUrl: checkout.remoteUrl,
50775
- defaultBranch: checkout.defaultBranch,
50776
- preferredBranch: checkout.preferredBranch
51368
+ expectedUpstreamAppId: upstreamAppId,
51369
+ createIfMissing: true,
51370
+ seedAppId: String(app.id)
51371
+ });
51372
+ const bindingPath = await writeCollabBinding(checkout.repoRoot, {
51373
+ projectId: authoritativeLane?.projectId ?? String(app.projectId),
51374
+ currentAppId: authoritativeLane?.currentAppId ?? String(app.id),
51375
+ upstreamAppId: authoritativeLane?.upstreamAppId ?? upstreamAppId,
51376
+ threadId: authoritativeLane?.threadId ?? (app.threadId ? String(app.threadId) : null),
51377
+ repoFingerprint: authoritativeLane?.repoFingerprint ?? repoFingerprint,
51378
+ remoteUrl: authoritativeLane?.remoteUrl ?? remoteUrl,
51379
+ defaultBranch: authoritativeLane?.defaultBranch ?? defaultBranch,
51380
+ laneId: authoritativeLane?.laneId ?? laneId,
51381
+ branchName: authoritativeLane?.branchName ?? branchName,
51382
+ bindingMode: "lane"
50777
51383
  });
50778
51384
  return {
50779
- appId: String(app.id),
50780
- dashboardUrl: buildDashboardAppUrl(String(app.id)),
50781
- projectId: String(app.projectId),
50782
- upstreamAppId,
51385
+ appId: authoritativeLane?.currentAppId ?? String(app.id),
51386
+ dashboardUrl: buildDashboardAppUrl(authoritativeLane?.currentAppId ?? String(app.id)),
51387
+ projectId: authoritativeLane?.projectId ?? String(app.projectId),
51388
+ upstreamAppId: authoritativeLane?.upstreamAppId ?? upstreamAppId,
50783
51389
  bindingPath,
50784
51390
  repoRoot: checkout.repoRoot
50785
51391
  };
@@ -50799,14 +51405,26 @@ async function resolveQueueAppId(params) {
50799
51405
  });
50800
51406
  }
50801
51407
  const repoRoot = await findGitRoot(params.cwd);
50802
- const binding = await readCollabBinding(repoRoot);
50803
- if (!binding) {
51408
+ const bindingResolution = await resolveActiveLaneBinding({ repoRoot });
51409
+ if (bindingResolution.status === "not_bound") {
50804
51410
  throw new RemixError("Repository is not bound to Remix.", {
50805
51411
  exitCode: 2,
50806
51412
  hint: "Bind the repository first or pass `appId` explicitly for the app-scoped merge-request queue."
50807
51413
  });
50808
51414
  }
50809
- return binding.currentAppId;
51415
+ if (bindingResolution.status === "missing_branch_binding") {
51416
+ throw new RemixError("Current branch is not yet bound to a Remix lane.", {
51417
+ exitCode: 2,
51418
+ hint: `Switch back to a bound branch or create a lane by recording work on ${bindingResolution.currentBranch ?? "this branch"}.`
51419
+ });
51420
+ }
51421
+ if (bindingResolution.status === "binding_conflict") {
51422
+ throw new RemixError("Current branch binding conflicts with the server-resolved Remix lane.", {
51423
+ exitCode: 2,
51424
+ hint: `Local app ${bindingResolution.binding.currentAppId}; server app ${bindingResolution.resolvedLane.currentAppId ?? "(unknown)"}.`
51425
+ });
51426
+ }
51427
+ return bindingResolution.binding.currentAppId;
50810
51428
  }
50811
51429
  async function collabListMergeRequests(params) {
50812
51430
  const appId = await resolveQueueAppId({
@@ -50835,12 +51453,32 @@ async function collabListMergeRequests(params) {
50835
51453
  async function resolveScopeTarget(params) {
50836
51454
  if (params.targetId?.trim()) return params.targetId.trim();
50837
51455
  const repoRoot = await findGitRoot(params.cwd);
50838
- const binding = await readCollabBinding(repoRoot);
50839
- if (!binding) {
51456
+ const bindingResolution = await resolveActiveLaneBinding({ repoRoot, api: params.api });
51457
+ if (bindingResolution.status === "not_bound") {
50840
51458
  throw new RemixError("Repository is not bound to Remix and no explicit target id was provided.", { exitCode: 2 });
50841
51459
  }
50842
- if (params.scope === "project") return binding.projectId;
51460
+ if (bindingResolution.status === "missing_branch_binding") {
51461
+ throw new RemixError("Current branch is not yet bound to a Remix lane and no explicit target id was provided.", {
51462
+ exitCode: 2
51463
+ });
51464
+ }
51465
+ if (bindingResolution.status === "binding_conflict") {
51466
+ throw new RemixError("Current branch binding conflicts with the server-resolved Remix lane.", {
51467
+ exitCode: 2,
51468
+ hint: `Local app ${bindingResolution.binding.currentAppId}; server app ${bindingResolution.resolvedLane.currentAppId ?? "(unknown)"}.`
51469
+ });
51470
+ }
51471
+ const binding = bindingResolution.binding;
51472
+ if (params.scope === "project") {
51473
+ if (!binding.projectId) {
51474
+ throw new RemixError("Could not resolve the project for the current repository binding.", { exitCode: 2 });
51475
+ }
51476
+ return binding.projectId;
51477
+ }
50843
51478
  if (params.scope === "app") return binding.currentAppId;
51479
+ if (!binding.projectId) {
51480
+ throw new RemixError("Could not resolve the project for the current repository binding.", { exitCode: 2 });
51481
+ }
50844
51482
  const project = unwrapResponseObject(await params.api.getProject(binding.projectId), "project");
50845
51483
  const organizationId = typeof project.organizationId === "string" ? project.organizationId : null;
50846
51484
  if (!organizationId) {
@@ -50962,13 +51600,28 @@ async function collabInit(params) {
50962
51600
  const remoteUrl = normalizeGitRemote(await getRemoteOriginUrl(repoRoot));
50963
51601
  const currentBranch = await getCurrentBranch(repoRoot);
50964
51602
  const defaultBranch = await getDefaultBranch(repoRoot) ?? currentBranch;
50965
- const preferredBranch = currentBranch ?? defaultBranch ?? null;
51603
+ const branchName = currentBranch ?? defaultBranch ?? null;
50966
51604
  const repoFingerprint = await buildRepoFingerprint({ gitRoot: repoRoot, remoteUrl, defaultBranch });
50967
51605
  const repoSnapshot = await captureRepoSnapshot(repoRoot);
51606
+ if (params.forceNew) {
51607
+ const bindingResp = await params.api.resolveProjectBinding({
51608
+ repoFingerprint,
51609
+ remoteUrl: remoteUrl ?? void 0,
51610
+ branchName: branchName ?? void 0
51611
+ });
51612
+ const existing = bindingResp?.responseObject;
51613
+ if (existing?.projectId && existing?.appId) {
51614
+ throw new RemixError("`remix collab init --force-new` is not allowed for repositories already known to Remix.", {
51615
+ exitCode: 2,
51616
+ hint: "This repository already resolves to an existing Remix lineage. Run `remix collab init` without `--force-new`, or use an explicit checkout/remix flow instead of creating a duplicate imported app."
51617
+ });
51618
+ }
51619
+ }
50968
51620
  if (!params.forceNew) {
50969
51621
  const bindingResp = await params.api.resolveProjectBinding({
50970
51622
  repoFingerprint,
50971
- remoteUrl: remoteUrl ?? void 0
51623
+ remoteUrl: remoteUrl ?? void 0,
51624
+ branchName: branchName ?? void 0
50972
51625
  });
50973
51626
  const existing = bindingResp?.responseObject;
50974
51627
  if (existing?.projectId && existing?.appId) {
@@ -50976,25 +51629,55 @@ async function collabInit(params) {
50976
51629
  operation: "`remix collab init`",
50977
51630
  recoveryHint: "The repository changed while the local binding was being initialized. Review the local changes and rerun `remix collab init`."
50978
51631
  });
51632
+ const initialProjectId = String(existing.projectId);
51633
+ const initialCurrentAppId = String(existing.appId);
51634
+ const initialUpstreamAppId = String(existing.upstreamAppId ?? existing.appId);
51635
+ const initialThreadId = existing.threadId ? String(existing.threadId) : null;
50979
51636
  const bindingPath2 = await writeCollabBinding(repoRoot, {
50980
- projectId: String(existing.projectId),
50981
- currentAppId: String(existing.appId),
50982
- upstreamAppId: String(existing.upstreamAppId ?? existing.appId),
50983
- threadId: existing.threadId ? String(existing.threadId) : null,
51637
+ projectId: initialProjectId,
51638
+ currentAppId: initialCurrentAppId,
51639
+ upstreamAppId: initialUpstreamAppId,
51640
+ threadId: initialThreadId,
50984
51641
  repoFingerprint,
50985
51642
  remoteUrl,
50986
51643
  defaultBranch: defaultBranch ?? null,
50987
- preferredBranch
51644
+ laneId: null,
51645
+ branchName,
51646
+ bindingMode: "lane"
50988
51647
  });
51648
+ let boundProjectId2 = initialProjectId;
51649
+ let boundCurrentAppId2 = initialCurrentAppId;
51650
+ let boundUpstreamAppId2 = initialUpstreamAppId;
51651
+ let boundThreadId2 = initialThreadId;
51652
+ let finalWarnings = [...warnings];
51653
+ if (branchName) {
51654
+ const provisioned = await provisionActiveLaneBinding({
51655
+ repoRoot,
51656
+ api: params.api,
51657
+ operation: "`remix collab init`"
51658
+ });
51659
+ if (provisioned.binding) {
51660
+ boundProjectId2 = provisioned.binding.projectId ?? boundProjectId2;
51661
+ boundCurrentAppId2 = provisioned.binding.currentAppId;
51662
+ boundUpstreamAppId2 = provisioned.binding.upstreamAppId;
51663
+ boundThreadId2 = provisioned.binding.threadId;
51664
+ }
51665
+ finalWarnings = [...finalWarnings, ...provisioned.warnings];
51666
+ }
51667
+ if (boundCurrentAppId2) {
51668
+ const readyApp = await pollAppReady(params.api, boundCurrentAppId2);
51669
+ boundProjectId2 = String(readyApp.projectId ?? boundProjectId2);
51670
+ boundThreadId2 = readyApp.threadId ? String(readyApp.threadId) : boundThreadId2;
51671
+ }
50989
51672
  return {
50990
51673
  reused: true,
50991
- projectId: String(existing.projectId),
50992
- appId: String(existing.appId),
50993
- dashboardUrl: buildDashboardAppUrl(String(existing.appId)),
50994
- upstreamAppId: String(existing.upstreamAppId ?? existing.appId),
51674
+ projectId: boundProjectId2,
51675
+ appId: boundCurrentAppId2,
51676
+ dashboardUrl: buildDashboardAppUrl(boundCurrentAppId2),
51677
+ upstreamAppId: boundUpstreamAppId2,
50995
51678
  bindingPath: bindingPath2,
50996
51679
  repoRoot,
50997
- ...warnings.length > 0 ? { warnings } : {}
51680
+ ...finalWarnings.length > 0 ? { warnings: finalWarnings } : {}
50998
51681
  };
50999
51682
  }
51000
51683
  }
@@ -51021,6 +51704,7 @@ async function collabInit(params) {
51021
51704
  path: params.path?.trim() || void 0,
51022
51705
  platform: "generic",
51023
51706
  isPublic: false,
51707
+ branch: currentBranch ?? void 0,
51024
51708
  remoteUrl: remoteUrl ?? void 0,
51025
51709
  defaultBranch: defaultBranch ?? void 0,
51026
51710
  repoFingerprint,
@@ -51028,31 +51712,65 @@ async function collabInit(params) {
51028
51712
  });
51029
51713
  const imported = unwrapResponseObject(importResp, "import");
51030
51714
  const app = await pollAppReady(params.api, String(imported.appId));
51715
+ let boundProjectId = String(app.projectId);
51716
+ let boundCurrentAppId = String(app.id);
51717
+ let boundUpstreamAppId = String(app.id);
51718
+ let boundThreadId = app.threadId ? String(app.threadId) : null;
51719
+ let boundLaneId = null;
51720
+ if (branchName) {
51721
+ const laneResp = defaultBranch && branchName !== defaultBranch ? await params.api.bootstrapFreshProjectLane({
51722
+ projectId: boundProjectId,
51723
+ repoFingerprint,
51724
+ remoteUrl: remoteUrl ?? void 0,
51725
+ defaultBranch: defaultBranch ?? void 0,
51726
+ branchName,
51727
+ seedAppId: String(app.id)
51728
+ }) : await params.api.ensureProjectLaneBinding({
51729
+ projectId: boundProjectId,
51730
+ repoFingerprint,
51731
+ remoteUrl: remoteUrl ?? void 0,
51732
+ defaultBranch: defaultBranch ?? void 0,
51733
+ branchName,
51734
+ seedAppId: String(app.id)
51735
+ });
51736
+ const lane = unwrapResponseObject(laneResp, "project lane binding");
51737
+ boundProjectId = typeof lane.projectId === "string" && lane.projectId ? lane.projectId : boundProjectId;
51738
+ boundCurrentAppId = typeof lane.currentAppId === "string" && lane.currentAppId ? lane.currentAppId : boundCurrentAppId;
51739
+ boundUpstreamAppId = typeof lane.upstreamAppId === "string" && lane.upstreamAppId ? lane.upstreamAppId : boundUpstreamAppId;
51740
+ boundThreadId = typeof lane.threadId === "string" && lane.threadId ? lane.threadId : boundThreadId;
51741
+ boundLaneId = typeof lane.laneId === "string" && lane.laneId ? lane.laneId : null;
51742
+ }
51743
+ if (boundCurrentAppId) {
51744
+ const readyApp = await pollAppReady(params.api, boundCurrentAppId);
51745
+ boundProjectId = String(readyApp.projectId ?? boundProjectId);
51746
+ boundThreadId = readyApp.threadId ? String(readyApp.threadId) : boundThreadId;
51747
+ }
51031
51748
  await assertRepoSnapshotUnchanged(repoRoot, repoSnapshot, {
51032
51749
  operation: "`remix collab init`",
51033
51750
  recoveryHint: "The repository changed before the Remix binding was written. Review the local changes and rerun `remix collab init`."
51034
51751
  });
51035
51752
  const bindingPath = await writeCollabBinding(repoRoot, {
51036
- projectId: String(app.projectId),
51037
- currentAppId: String(app.id),
51038
- upstreamAppId: String(app.id),
51039
- threadId: app.threadId ? String(app.threadId) : null,
51753
+ projectId: boundProjectId,
51754
+ currentAppId: boundCurrentAppId,
51755
+ upstreamAppId: boundUpstreamAppId,
51756
+ threadId: boundThreadId,
51040
51757
  repoFingerprint,
51041
51758
  remoteUrl,
51042
51759
  defaultBranch: defaultBranch ?? null,
51043
- preferredBranch
51760
+ laneId: boundLaneId,
51761
+ branchName,
51762
+ bindingMode: "lane"
51044
51763
  });
51045
51764
  return {
51046
51765
  reused: false,
51047
- projectId: String(app.projectId),
51048
- appId: String(app.id),
51049
- dashboardUrl: buildDashboardAppUrl(String(app.id)),
51050
- upstreamAppId: String(app.id),
51766
+ projectId: boundProjectId,
51767
+ appId: boundCurrentAppId,
51768
+ dashboardUrl: buildDashboardAppUrl(boundCurrentAppId),
51769
+ upstreamAppId: boundUpstreamAppId,
51051
51770
  bindingPath,
51052
51771
  repoRoot,
51053
51772
  remoteUrl,
51054
51773
  defaultBranch,
51055
- preferredBranch,
51056
51774
  ...warnings.length > 0 ? { warnings } : {}
51057
51775
  };
51058
51776
  }
@@ -51098,7 +51816,11 @@ async function collabList(params) {
51098
51816
  }
51099
51817
  async function collabReconcile(params) {
51100
51818
  const repoRoot = await findGitRoot(params.cwd);
51101
- const binding = await readCollabBinding(repoRoot);
51819
+ const binding = await ensureActiveLaneBinding({
51820
+ repoRoot,
51821
+ api: params.api,
51822
+ operation: "`remix collab reconcile`"
51823
+ });
51102
51824
  if (!binding) {
51103
51825
  throw new RemixError("Repository is not bound to Remix.", {
51104
51826
  exitCode: 2,
@@ -51107,9 +51829,9 @@ async function collabReconcile(params) {
51107
51829
  }
51108
51830
  await ensureCleanWorktree(repoRoot, "`remix collab reconcile`");
51109
51831
  const branch = await requireCurrentBranch(repoRoot);
51110
- assertPreferredBranchMatch({
51832
+ assertBoundBranchMatch({
51111
51833
  currentBranch: branch,
51112
- preferredBranch: binding.preferredBranch,
51834
+ branchName: binding.branchName,
51113
51835
  allowBranchMismatch: params.allowBranchMismatch,
51114
51836
  operation: "`remix collab reconcile`"
51115
51837
  });
@@ -51218,9 +51940,9 @@ async function collabReconcile(params) {
51218
51940
  });
51219
51941
  await ensureCleanWorktree(lockedRepoRoot, "`remix collab reconcile`");
51220
51942
  const lockedBranch = await requireCurrentBranch(lockedRepoRoot);
51221
- assertPreferredBranchMatch({
51943
+ assertBoundBranchMatch({
51222
51944
  currentBranch: lockedBranch,
51223
- preferredBranch: binding.preferredBranch,
51945
+ branchName: binding.branchName,
51224
51946
  allowBranchMismatch: params.allowBranchMismatch,
51225
51947
  operation: "`remix collab reconcile`"
51226
51948
  });
@@ -51275,38 +51997,76 @@ async function collabRemix(params) {
51275
51997
  hint: "Pass the source app id to remix."
51276
51998
  });
51277
51999
  }
51278
- const forkResp = await params.api.forkApp(sourceAppId, { name: params.name?.trim() || void 0, platform: "generic" });
52000
+ const sourceApp = await pollAppReady(params.api, sourceAppId);
52001
+ const sourceCollab = sourceApp.collab && typeof sourceApp.collab === "object" ? sourceApp.collab : null;
52002
+ const sourceSource = sourceApp.source && typeof sourceApp.source === "object" ? sourceApp.source : null;
52003
+ const sourceBranchName = typeof sourceCollab?.branchName === "string" && sourceCollab.branchName.trim() || typeof sourceSource?.branch === "string" && sourceSource.branch.trim() || typeof sourceSource?.defaultBranch === "string" && sourceSource.defaultBranch.trim() || void 0;
52004
+ const forkResp = await params.api.forkApp(sourceAppId, {
52005
+ name: params.name?.trim() || void 0,
52006
+ platform: "generic",
52007
+ branchName: sourceBranchName
52008
+ });
51279
52009
  const forked = unwrapResponseObject(forkResp, "fork");
51280
52010
  const app = await pollAppReady(params.api, String(forked.id));
52011
+ const collab = app.collab && typeof app.collab === "object" ? app.collab : null;
52012
+ const source = app.source && typeof app.source === "object" ? app.source : null;
52013
+ const authoritativeBranchName = typeof collab?.branchName === "string" && collab.branchName.trim() || typeof source?.branch === "string" && source.branch.trim() || typeof source?.defaultBranch === "string" && source.defaultBranch.trim() || sourceBranchName || null;
52014
+ const authoritativeRemoteUrl = typeof source?.remoteUrl === "string" && source.remoteUrl.trim() || null;
52015
+ const authoritativeDefaultBranch = typeof source?.defaultBranch === "string" && source.defaultBranch.trim() || null;
51281
52016
  const checkout = await materializeAppCheckout({
51282
52017
  api: params.api,
51283
52018
  cwd: params.cwd,
51284
52019
  appId: String(app.id),
51285
52020
  outputDir: params.outputDir ?? null,
51286
- defaultDirName: sanitizeCheckoutDirName(String(params.name?.trim() || app.name || app.id))
51287
- });
51288
- const bindingPath = await writeCollabBinding(checkout.repoRoot, {
52021
+ defaultDirName: sanitizeCheckoutDirName(String(params.name?.trim() || app.name || app.id)),
52022
+ expectedBranchName: authoritativeBranchName,
52023
+ expectedRemoteUrl: authoritativeRemoteUrl,
52024
+ expectedDefaultBranch: authoritativeDefaultBranch
52025
+ });
52026
+ const laneId = typeof collab?.laneId === "string" ? collab.laneId : null;
52027
+ const repoFingerprint = typeof source?.repoFingerprint === "string" ? source.repoFingerprint : checkout.repoFingerprint;
52028
+ const remoteUrl = typeof source?.remoteUrl === "string" ? source.remoteUrl : checkout.remoteUrl;
52029
+ const defaultBranch = typeof source?.defaultBranch === "string" ? source.defaultBranch : checkout.defaultBranch;
52030
+ const branchName = authoritativeBranchName ?? checkout.branchName;
52031
+ const authoritativeLane = await resolveProjectLaneIfAuthoritative(params.api, {
51289
52032
  projectId: String(app.projectId),
52033
+ repoFingerprint,
52034
+ remoteUrl,
52035
+ defaultBranch,
52036
+ branchName,
51290
52037
  currentAppId: String(app.id),
51291
- upstreamAppId: String(app.forkedFromAppId ?? sourceAppId),
51292
- threadId: app.threadId ? String(app.threadId) : null,
51293
- repoFingerprint: checkout.repoFingerprint,
51294
- remoteUrl: checkout.remoteUrl,
51295
- defaultBranch: checkout.defaultBranch,
51296
- preferredBranch: checkout.preferredBranch
52038
+ expectedUpstreamAppId: String(app.forkedFromAppId ?? sourceAppId),
52039
+ createIfMissing: true,
52040
+ seedAppId: String(app.id)
52041
+ });
52042
+ const bindingPath = await writeCollabBinding(checkout.repoRoot, {
52043
+ projectId: authoritativeLane?.projectId ?? String(app.projectId),
52044
+ currentAppId: authoritativeLane?.currentAppId ?? String(app.id),
52045
+ upstreamAppId: authoritativeLane?.upstreamAppId ?? String(app.forkedFromAppId ?? sourceAppId),
52046
+ threadId: authoritativeLane?.threadId ?? (app.threadId ? String(app.threadId) : null),
52047
+ repoFingerprint: authoritativeLane?.repoFingerprint ?? repoFingerprint,
52048
+ remoteUrl: authoritativeLane?.remoteUrl ?? remoteUrl,
52049
+ defaultBranch: authoritativeLane?.defaultBranch ?? defaultBranch,
52050
+ laneId: authoritativeLane?.laneId ?? laneId,
52051
+ branchName: authoritativeLane?.branchName ?? branchName,
52052
+ bindingMode: "lane"
51297
52053
  });
51298
52054
  return {
51299
- appId: String(app.id),
51300
- dashboardUrl: buildDashboardAppUrl(String(app.id)),
51301
- projectId: String(app.projectId),
51302
- upstreamAppId: String(app.forkedFromAppId ?? sourceAppId),
52055
+ appId: authoritativeLane?.currentAppId ?? String(app.id),
52056
+ dashboardUrl: buildDashboardAppUrl(authoritativeLane?.currentAppId ?? String(app.id)),
52057
+ projectId: authoritativeLane?.projectId ?? String(app.projectId),
52058
+ upstreamAppId: authoritativeLane?.upstreamAppId ?? String(app.forkedFromAppId ?? sourceAppId),
51303
52059
  bindingPath,
51304
52060
  repoRoot: checkout.repoRoot
51305
52061
  };
51306
52062
  }
51307
52063
  async function collabRequestMerge(params) {
51308
52064
  const repoRoot = await findGitRoot(params.cwd);
51309
- const binding = await readCollabBinding(repoRoot);
52065
+ const binding = await ensureActiveLaneBinding({
52066
+ repoRoot,
52067
+ api: params.api,
52068
+ operation: "`remix collab request-merge`"
52069
+ });
51310
52070
  if (!binding) throw new RemixError("Repository is not bound to Remix.", { exitCode: 2 });
51311
52071
  const resp = await params.api.openMergeRequest(binding.currentAppId);
51312
52072
  return unwrapResponseObject(resp, "merge request");
@@ -51339,7 +52099,9 @@ function createBaseStatus() {
51339
52099
  repoFingerprint: null,
51340
52100
  remoteUrl: null,
51341
52101
  defaultBranch: null,
51342
- preferredBranch: null
52102
+ laneId: null,
52103
+ branchName: null,
52104
+ bindingMode: null
51343
52105
  },
51344
52106
  remote: {
51345
52107
  checked: false,
@@ -51403,12 +52165,13 @@ async function collabStatus(params) {
51403
52165
  }
51404
52166
  status.repo.isGitRepo = true;
51405
52167
  status.repo.repoRoot = repoRoot;
51406
- const [branch, headCommitHash, worktreeStatus, binding] = await Promise.all([
52168
+ const [branch, initialHeadCommitHash, worktreeStatus, bindingResolution] = await Promise.all([
51407
52169
  getCurrentBranch(repoRoot),
51408
52170
  getHeadCommitHash(repoRoot),
51409
52171
  getWorktreeStatus(repoRoot),
51410
- readCollabBinding(repoRoot)
52172
+ resolveActiveLaneBinding({ repoRoot, api: params.api ?? void 0 })
51411
52173
  ]);
52174
+ let headCommitHash = initialHeadCommitHash;
51412
52175
  status.repo.branch = branch;
51413
52176
  status.repo.branchMismatch = false;
51414
52177
  status.repo.headCommitHash = headCommitHash;
@@ -51422,13 +52185,36 @@ async function collabStatus(params) {
51422
52185
  if (!status.repo.worktree.isClean) addWarning(status, "Working tree has local changes.");
51423
52186
  if (!branch) addWarning(status, "Repository is in a detached HEAD state.");
51424
52187
  if (!headCommitHash) addWarning(status, "Failed to resolve local HEAD commit.");
51425
- if (!binding) {
52188
+ if (bindingResolution.status === "not_bound") {
51426
52189
  status.binding.path = null;
51427
52190
  addBlockedReason(status.sync, "not_bound");
51428
52191
  addBlockedReason(status.reconcile, "not_bound");
51429
52192
  status.recommendedAction = "init";
51430
52193
  return status;
51431
52194
  }
52195
+ if (bindingResolution.status === "missing_branch_binding") {
52196
+ status.binding = {
52197
+ isBound: true,
52198
+ path: getCollabBindingPath(repoRoot),
52199
+ projectId: bindingResolution.projectId,
52200
+ currentAppId: null,
52201
+ upstreamAppId: bindingResolution.upstreamAppId,
52202
+ isRemix: null,
52203
+ threadId: bindingResolution.threadId,
52204
+ repoFingerprint: bindingResolution.repoFingerprint,
52205
+ remoteUrl: bindingResolution.remoteUrl,
52206
+ defaultBranch: bindingResolution.defaultBranch,
52207
+ laneId: null,
52208
+ branchName: bindingResolution.currentBranch,
52209
+ bindingMode: "lane"
52210
+ };
52211
+ addBlockedReason(status.sync, "branch_binding_missing");
52212
+ addBlockedReason(status.reconcile, "branch_binding_missing");
52213
+ addWarning(status, `Current branch ${bindingResolution.currentBranch ?? "(detached)"} is not yet bound to a Remix lane.`);
52214
+ status.recommendedAction = "no_action";
52215
+ return status;
52216
+ }
52217
+ const binding = bindingResolution.binding;
51432
52218
  status.binding = {
51433
52219
  isBound: true,
51434
52220
  path: getCollabBindingPath(repoRoot),
@@ -51440,13 +52226,21 @@ async function collabStatus(params) {
51440
52226
  repoFingerprint: binding.repoFingerprint,
51441
52227
  remoteUrl: binding.remoteUrl,
51442
52228
  defaultBranch: binding.defaultBranch,
51443
- preferredBranch: binding.preferredBranch
52229
+ laneId: binding.laneId,
52230
+ branchName: binding.branchName,
52231
+ bindingMode: binding.bindingMode
51444
52232
  };
51445
- status.repo.branchMismatch = !isPreferredBranchMatch(branch, binding.preferredBranch);
52233
+ status.repo.branchMismatch = !isBoundBranchMatch(branch, binding.branchName);
51446
52234
  if (status.repo.branchMismatch) {
51447
52235
  addWarning(
51448
52236
  status,
51449
- `Current branch ${branch ?? "(detached)"} does not match preferred branch ${binding.preferredBranch ?? "(unset)"}.`
52237
+ `Current branch ${branch ?? "(detached)"} does not match bound branch ${binding.branchName ?? "(unset)"}.`
52238
+ );
52239
+ }
52240
+ if (bindingResolution.status === "binding_conflict") {
52241
+ addWarning(
52242
+ status,
52243
+ `Local binding app ${binding.currentAppId} conflicts with server-resolved app ${bindingResolution.resolvedLane.currentAppId ?? "(unknown)"} for branch ${bindingResolution.currentBranch ?? "(detached)"}.`
51450
52244
  );
51451
52245
  }
51452
52246
  if (!params.api) {
@@ -51494,8 +52288,14 @@ async function collabStatus(params) {
51494
52288
  const syncResp = syncResult.value;
51495
52289
  if (syncResp) {
51496
52290
  const sync = unwrapResponseObject(syncResp, "sync result");
52291
+ const latestHeadCommitHash = sync.targetCommitHash && sync.status !== "up_to_date" ? await getHeadCommitHash(repoRoot) : headCommitHash;
52292
+ if (latestHeadCommitHash) {
52293
+ headCommitHash = latestHeadCommitHash;
52294
+ status.repo.headCommitHash = latestHeadCommitHash;
52295
+ }
52296
+ const normalizedSyncStatus = headCommitHash && sync.targetCommitHash && headCommitHash === sync.targetCommitHash ? "up_to_date" : sync.status;
51497
52297
  status.sync.checked = true;
51498
- status.sync.status = sync.status;
52298
+ status.sync.status = normalizedSyncStatus;
51499
52299
  status.sync.warnings = sync.warnings;
51500
52300
  status.sync.targetCommitHash = sync.targetCommitHash;
51501
52301
  status.sync.targetCommitId = sync.targetCommitId;
@@ -51503,9 +52303,9 @@ async function collabStatus(params) {
51503
52303
  if (!status.repo.worktree.isClean) addBlockedReason(status.sync, "dirty_worktree");
51504
52304
  if (!branch) addBlockedReason(status.sync, "detached_head");
51505
52305
  if (status.repo.branchMismatch) addBlockedReason(status.sync, "branch_mismatch");
51506
- if (sync.status === "conflict_risk") addBlockedReason(status.sync, "metadata_conflict");
52306
+ if (normalizedSyncStatus === "conflict_risk") addBlockedReason(status.sync, "metadata_conflict");
51507
52307
  status.sync.canApply = status.sync.status === "ready_to_fast_forward" && status.repo.worktree.isClean && Boolean(branch) && !status.sync.blockedReasons.includes("metadata_conflict");
51508
- if (sync.status === "conflict_risk") {
52308
+ if (normalizedSyncStatus === "conflict_risk") {
51509
52309
  status.reconcile.checked = true;
51510
52310
  status.reconcile.status = "metadata_conflict";
51511
52311
  status.reconcile.warnings = sync.warnings;
@@ -51515,7 +52315,7 @@ async function collabStatus(params) {
51515
52315
  if (!status.repo.worktree.isClean) addBlockedReason(status.reconcile, "dirty_worktree");
51516
52316
  if (!branch) addBlockedReason(status.reconcile, "detached_head");
51517
52317
  if (status.repo.branchMismatch) addBlockedReason(status.reconcile, "branch_mismatch");
51518
- } else if (sync.status === "base_unknown") {
52318
+ } else if (normalizedSyncStatus === "base_unknown") {
51519
52319
  try {
51520
52320
  const preflightResp = await params.api.preflightAppReconcile(binding.currentAppId, {
51521
52321
  localHeadCommitHash: headCommitHash,
@@ -51570,7 +52370,11 @@ async function collabStatus(params) {
51570
52370
  }
51571
52371
  async function collabSyncUpstream(params) {
51572
52372
  const repoRoot = await findGitRoot(params.cwd);
51573
- const binding = await readCollabBinding(repoRoot);
52373
+ const binding = await ensureActiveLaneBinding({
52374
+ repoRoot,
52375
+ api: params.api,
52376
+ operation: "`remix collab sync-upstream`"
52377
+ });
51574
52378
  if (!binding) {
51575
52379
  throw new RemixError("Repository is not bound to Remix.", {
51576
52380
  exitCode: 2,
@@ -51828,7 +52632,7 @@ function normalizeByMessage(err) {
51828
52632
  category: "local_state"
51829
52633
  });
51830
52634
  }
51831
- if (code === ERROR_CODES.PREFERRED_BRANCH_MISMATCH || message.includes("preferred branch")) {
52635
+ if (code === ERROR_CODES.PREFERRED_BRANCH_MISMATCH || message.includes("preferred branch") || message.includes("bound branch")) {
51832
52636
  return makeNormalized({
51833
52637
  code: ERROR_CODES.PREFERRED_BRANCH_MISMATCH,
51834
52638
  message,
@@ -52367,7 +53171,7 @@ function getRiskLevel(status) {
52367
53171
  function getRecommendedNextActions(status) {
52368
53172
  if (status.repo.branchMismatch) {
52369
53173
  return [
52370
- `Switch to the preferred branch (${status.binding.preferredBranch ?? "configured in the binding"}) before using Remix mutation tools, or rerun with allowBranchMismatch=true if this deviation is intentional.`
53174
+ `Switch to the bound branch (${status.binding.branchName ?? "configured in the binding"}) before using Remix mutation tools, or rerun with allowBranchMismatch=true if this deviation is intentional.`
52371
53175
  ];
52372
53176
  }
52373
53177
  switch (status.recommendedAction) {
@@ -55329,7 +56133,7 @@ async function listPendingTurnStateSummaries() {
55329
56133
  // package.json
55330
56134
  var package_default = {
55331
56135
  name: "@remixhq/claude-plugin",
55332
- version: "0.1.15",
56136
+ version: "0.1.16",
55333
56137
  description: "Claude Code plugin for Remix collaboration workflows",
55334
56138
  homepage: "https://github.com/RemixDotOne/remix-claude-plugin",
55335
56139
  license: "MIT",
@@ -55360,8 +56164,8 @@ var package_default = {
55360
56164
  prepack: "npm run build"
55361
56165
  },
55362
56166
  dependencies: {
55363
- "@remixhq/core": "^0.1.10",
55364
- "@remixhq/mcp": "^0.1.10"
56167
+ "@remixhq/core": "^0.1.11",
56168
+ "@remixhq/mcp": "^0.1.11"
55365
56169
  },
55366
56170
  devDependencies: {
55367
56171
  "@types/node": "^25.4.0",