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