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