@remixhq/claude-plugin 0.1.23 → 0.1.24
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/dist/hook-post-collab.cjs +3 -3
- package/dist/hook-post-collab.cjs.map +1 -1
- package/dist/hook-stop-collab.cjs +111 -45
- package/dist/hook-stop-collab.cjs.map +1 -1
- package/dist/hook-user-prompt.cjs +147 -52
- 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 +171 -57
- package/dist/mcp-server.cjs.map +1 -1
- package/package.json +3 -3
|
@@ -8827,6 +8827,15 @@ function shouldRequireRemoteLaneForCurrentBranch(params) {
|
|
|
8827
8827
|
if (params.currentBranch === defaultBranch) return false;
|
|
8828
8828
|
return !params.binding.laneId || params.binding.currentAppId === params.binding.upstreamAppId;
|
|
8829
8829
|
}
|
|
8830
|
+
function resolveLaneLookupProjectId(params) {
|
|
8831
|
+
const currentBranch = normalizeBranchName2(params.currentBranch);
|
|
8832
|
+
const defaultBranch = normalizeBranchName2(params.defaultBranch);
|
|
8833
|
+
const localProjectId = params.localBinding.projectId ?? null;
|
|
8834
|
+
if (currentBranch && currentBranch !== defaultBranch && localProjectId) {
|
|
8835
|
+
return localProjectId;
|
|
8836
|
+
}
|
|
8837
|
+
return params.explicitRootProjectId ?? (params.requireRemoteLane ? void 0 : localProjectId ?? params.fallbackProjectId ?? void 0);
|
|
8838
|
+
}
|
|
8830
8839
|
async function persistResolvedLane(repoRoot, binding) {
|
|
8831
8840
|
await writeCollabBinding(repoRoot, {
|
|
8832
8841
|
projectId: binding.projectId,
|
|
@@ -8905,7 +8914,14 @@ async function resolveActiveLaneBindingUncached(params, state) {
|
|
|
8905
8914
|
};
|
|
8906
8915
|
}
|
|
8907
8916
|
const laneResp2 = await params.api.resolveProjectLaneBinding({
|
|
8908
|
-
projectId:
|
|
8917
|
+
projectId: resolveLaneLookupProjectId({
|
|
8918
|
+
explicitRootProjectId: state.explicitRootBinding?.projectId,
|
|
8919
|
+
localBinding,
|
|
8920
|
+
currentBranch,
|
|
8921
|
+
defaultBranch: state.defaultBranch,
|
|
8922
|
+
requireRemoteLane,
|
|
8923
|
+
fallbackProjectId: state.projectId
|
|
8924
|
+
}),
|
|
8909
8925
|
repoFingerprint: state.repoFingerprint ?? void 0,
|
|
8910
8926
|
remoteUrl: state.remoteUrl ?? void 0,
|
|
8911
8927
|
defaultBranch: state.defaultBranch ?? void 0,
|
|
@@ -9756,6 +9772,59 @@ function buildWorkspaceMetadata(params) {
|
|
|
9756
9772
|
}
|
|
9757
9773
|
return metadata;
|
|
9758
9774
|
}
|
|
9775
|
+
async function findExistingChangeStepByIdempotency(params) {
|
|
9776
|
+
const idempotencyKey = params.idempotencyKey?.trim();
|
|
9777
|
+
if (!idempotencyKey) return null;
|
|
9778
|
+
const resp = await params.api.listChangeSteps(params.appId, { limit: 1, idempotencyKey });
|
|
9779
|
+
const responseObject = unwrapResponseObject(
|
|
9780
|
+
resp,
|
|
9781
|
+
"change step list"
|
|
9782
|
+
);
|
|
9783
|
+
const steps = Array.isArray(responseObject) ? responseObject : Array.isArray(responseObject.items) ? responseObject.items : [];
|
|
9784
|
+
return steps.find((step) => step.idempotencyKey === idempotencyKey) ?? null;
|
|
9785
|
+
}
|
|
9786
|
+
async function writeBaselineFromSucceededChangeStep(params) {
|
|
9787
|
+
const nextServerHeadHash = typeof params.changeStep.headCommitHash === "string" ? params.changeStep.headCommitHash.trim() : "";
|
|
9788
|
+
if (!nextServerHeadHash) {
|
|
9789
|
+
throw buildFinalizeCliError({
|
|
9790
|
+
message: "Backend returned a succeeded change step without a head commit hash.",
|
|
9791
|
+
exitCode: 1,
|
|
9792
|
+
hint: "This is a backend invariant violation; retry will not help. Run `remix collab status` before trying again.",
|
|
9793
|
+
disposition: "terminal",
|
|
9794
|
+
reason: "missing_head_commit_hash"
|
|
9795
|
+
});
|
|
9796
|
+
}
|
|
9797
|
+
let nextServerRevisionId = typeof params.changeStep.resultRevisionId === "string" ? params.changeStep.resultRevisionId.trim() : "";
|
|
9798
|
+
let nextServerTreeHash = null;
|
|
9799
|
+
if (!nextServerRevisionId) {
|
|
9800
|
+
const freshHeadResp = await params.api.getAppHead(params.job.currentAppId);
|
|
9801
|
+
const freshHead = unwrapResponseObject(freshHeadResp, "app head");
|
|
9802
|
+
if (freshHead.headCommitHash !== nextServerHeadHash || !freshHead.headRevisionId) {
|
|
9803
|
+
throw buildFinalizeCliError({
|
|
9804
|
+
message: "Backend returned a succeeded change step without a matching result revision.",
|
|
9805
|
+
exitCode: 1,
|
|
9806
|
+
hint: "The local baseline was not advanced because the post-step revision could not be verified. Restart the backend/CLI and retry after checking `remix collab status`.",
|
|
9807
|
+
disposition: "terminal",
|
|
9808
|
+
reason: "missing_result_revision_id"
|
|
9809
|
+
});
|
|
9810
|
+
}
|
|
9811
|
+
nextServerRevisionId = freshHead.headRevisionId;
|
|
9812
|
+
nextServerTreeHash = freshHead.treeHash ?? null;
|
|
9813
|
+
}
|
|
9814
|
+
await writeLocalBaseline({
|
|
9815
|
+
repoRoot: params.job.repoRoot,
|
|
9816
|
+
repoFingerprint: params.job.repoFingerprint,
|
|
9817
|
+
laneId: params.job.laneId,
|
|
9818
|
+
currentAppId: params.job.currentAppId,
|
|
9819
|
+
branchName: params.job.branchName,
|
|
9820
|
+
lastSnapshotId: params.snapshot.id,
|
|
9821
|
+
lastSnapshotHash: params.snapshot.snapshotHash,
|
|
9822
|
+
lastServerRevisionId: nextServerRevisionId,
|
|
9823
|
+
lastServerTreeHash: nextServerTreeHash,
|
|
9824
|
+
lastServerHeadHash: nextServerHeadHash,
|
|
9825
|
+
lastSeenLocalCommitHash: params.snapshot.localCommitHash
|
|
9826
|
+
});
|
|
9827
|
+
}
|
|
9759
9828
|
async function harvestPreTurnEvents(repoRoot, fromCommit, toCommit) {
|
|
9760
9829
|
if (!toCommit) return null;
|
|
9761
9830
|
try {
|
|
@@ -9941,6 +10010,34 @@ async function processClaimedPendingFinalizeJobInner(params) {
|
|
|
9941
10010
|
});
|
|
9942
10011
|
}
|
|
9943
10012
|
const replayNeeded = appHead.headCommitHash !== submissionBaseHeadHash || baselineDrifted;
|
|
10013
|
+
if (replayNeeded) {
|
|
10014
|
+
const existingChangeStep = await findExistingChangeStepByIdempotency({
|
|
10015
|
+
api: params.api,
|
|
10016
|
+
appId: job.currentAppId,
|
|
10017
|
+
idempotencyKey: job.idempotencyKey
|
|
10018
|
+
});
|
|
10019
|
+
if (existingChangeStep) {
|
|
10020
|
+
const changeStep2 = existingChangeStep.status === "succeeded" ? existingChangeStep : await pollChangeStep(params.api, job.currentAppId, existingChangeStep.id);
|
|
10021
|
+
invalidateAppHeadCache(job.currentAppId);
|
|
10022
|
+
invalidateAppDeltaCacheForApp(job.currentAppId);
|
|
10023
|
+
await writeBaselineFromSucceededChangeStep({ api: params.api, job, snapshot, changeStep: changeStep2 });
|
|
10024
|
+
await updatePendingFinalizeJob(job.id, {
|
|
10025
|
+
status: "completed",
|
|
10026
|
+
metadata: { changeStepId: String(changeStep2.id ?? "") }
|
|
10027
|
+
});
|
|
10028
|
+
return {
|
|
10029
|
+
mode: "changed_turn",
|
|
10030
|
+
idempotencyKey: job.idempotencyKey ?? "",
|
|
10031
|
+
queued: false,
|
|
10032
|
+
jobId: job.id,
|
|
10033
|
+
repoState,
|
|
10034
|
+
changeStep: changeStep2,
|
|
10035
|
+
collabTurn: null,
|
|
10036
|
+
autoSync: null,
|
|
10037
|
+
warnings: []
|
|
10038
|
+
};
|
|
10039
|
+
}
|
|
10040
|
+
}
|
|
9944
10041
|
if (replayNeeded) {
|
|
9945
10042
|
try {
|
|
9946
10043
|
const replayResp = await params.api.startChangeStepReplay(job.currentAppId, {
|
|
@@ -10038,46 +10135,7 @@ async function processClaimedPendingFinalizeJobInner(params) {
|
|
|
10038
10135
|
const changeStep = await pollChangeStep(params.api, job.currentAppId, String(createdStep.id));
|
|
10039
10136
|
invalidateAppHeadCache(job.currentAppId);
|
|
10040
10137
|
invalidateAppDeltaCacheForApp(job.currentAppId);
|
|
10041
|
-
|
|
10042
|
-
if (!nextServerHeadHash) {
|
|
10043
|
-
throw buildFinalizeCliError({
|
|
10044
|
-
message: "Backend returned a succeeded change step without a head commit hash.",
|
|
10045
|
-
exitCode: 1,
|
|
10046
|
-
hint: "This is a backend invariant violation; retry will not help. Run `remix collab status` before trying again.",
|
|
10047
|
-
disposition: "terminal",
|
|
10048
|
-
reason: "missing_head_commit_hash"
|
|
10049
|
-
});
|
|
10050
|
-
}
|
|
10051
|
-
let nextServerRevisionId = typeof changeStep.resultRevisionId === "string" ? changeStep.resultRevisionId.trim() : "";
|
|
10052
|
-
let nextServerTreeHash = null;
|
|
10053
|
-
if (!nextServerRevisionId) {
|
|
10054
|
-
const freshHeadResp = await params.api.getAppHead(job.currentAppId);
|
|
10055
|
-
const freshHead = unwrapResponseObject(freshHeadResp, "app head");
|
|
10056
|
-
if (freshHead.headCommitHash !== nextServerHeadHash || !freshHead.headRevisionId) {
|
|
10057
|
-
throw buildFinalizeCliError({
|
|
10058
|
-
message: "Backend returned a succeeded change step without a matching result revision.",
|
|
10059
|
-
exitCode: 1,
|
|
10060
|
-
hint: "The local baseline was not advanced because the post-step revision could not be verified. Restart the backend/CLI and retry after checking `remix collab status`.",
|
|
10061
|
-
disposition: "terminal",
|
|
10062
|
-
reason: "missing_result_revision_id"
|
|
10063
|
-
});
|
|
10064
|
-
}
|
|
10065
|
-
nextServerRevisionId = freshHead.headRevisionId;
|
|
10066
|
-
nextServerTreeHash = freshHead.treeHash ?? null;
|
|
10067
|
-
}
|
|
10068
|
-
await writeLocalBaseline({
|
|
10069
|
-
repoRoot: job.repoRoot,
|
|
10070
|
-
repoFingerprint: job.repoFingerprint,
|
|
10071
|
-
laneId: job.laneId,
|
|
10072
|
-
currentAppId: job.currentAppId,
|
|
10073
|
-
branchName: job.branchName,
|
|
10074
|
-
lastSnapshotId: snapshot.id,
|
|
10075
|
-
lastSnapshotHash: snapshot.snapshotHash,
|
|
10076
|
-
lastServerRevisionId: nextServerRevisionId,
|
|
10077
|
-
lastServerTreeHash: nextServerTreeHash,
|
|
10078
|
-
lastServerHeadHash: nextServerHeadHash,
|
|
10079
|
-
lastSeenLocalCommitHash: snapshot.localCommitHash
|
|
10080
|
-
});
|
|
10138
|
+
await writeBaselineFromSucceededChangeStep({ api: params.api, job, snapshot, changeStep });
|
|
10081
10139
|
await updatePendingFinalizeJob(job.id, {
|
|
10082
10140
|
status: "completed",
|
|
10083
10141
|
metadata: { changeStepId: String(changeStep.id ?? "") }
|
|
@@ -10570,7 +10628,7 @@ async function createPendingTurnState(params) {
|
|
|
10570
10628
|
// package.json
|
|
10571
10629
|
var package_default = {
|
|
10572
10630
|
name: "@remixhq/claude-plugin",
|
|
10573
|
-
version: "0.1.
|
|
10631
|
+
version: "0.1.24",
|
|
10574
10632
|
description: "Claude Code plugin for Remix collaboration workflows",
|
|
10575
10633
|
homepage: "https://github.com/RemixDotOne/remix-claude-plugin",
|
|
10576
10634
|
license: "MIT",
|
|
@@ -10608,8 +10666,8 @@ var package_default = {
|
|
|
10608
10666
|
prepack: "npm run build"
|
|
10609
10667
|
},
|
|
10610
10668
|
dependencies: {
|
|
10611
|
-
"@remixhq/core": "^0.1.
|
|
10612
|
-
"@remixhq/mcp": "^0.1.
|
|
10669
|
+
"@remixhq/core": "^0.1.19",
|
|
10670
|
+
"@remixhq/mcp": "^0.1.19"
|
|
10613
10671
|
},
|
|
10614
10672
|
devDependencies: {
|
|
10615
10673
|
"@types/node": "^25.4.0",
|
|
@@ -10881,7 +10939,7 @@ function mergeOutcomeIntoMarker(existing, outcome) {
|
|
|
10881
10939
|
return existing;
|
|
10882
10940
|
}
|
|
10883
10941
|
|
|
10884
|
-
// node_modules/@remixhq/core/dist/chunk-
|
|
10942
|
+
// node_modules/@remixhq/core/dist/chunk-C2FOZ3O7.js
|
|
10885
10943
|
async function readJsonSafe(res) {
|
|
10886
10944
|
const ct = res.headers.get("content-type") ?? "";
|
|
10887
10945
|
if (!ct.toLowerCase().includes("application/json")) return null;
|
|
@@ -11075,6 +11133,14 @@ function createApiClient(config, opts) {
|
|
|
11075
11133
|
method: "POST",
|
|
11076
11134
|
body: JSON.stringify(payload)
|
|
11077
11135
|
}),
|
|
11136
|
+
listChangeSteps: (appId, params) => {
|
|
11137
|
+
const qs = new URLSearchParams();
|
|
11138
|
+
if (params?.limit !== void 0) qs.set("limit", String(params.limit));
|
|
11139
|
+
if (params?.offset !== void 0) qs.set("offset", String(params.offset));
|
|
11140
|
+
if (params?.idempotencyKey) qs.set("idempotencyKey", params.idempotencyKey);
|
|
11141
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
11142
|
+
return request(`/v1/apps/${encodeURIComponent(appId)}/change-steps${suffix}`, { method: "GET" });
|
|
11143
|
+
},
|
|
11078
11144
|
createCollabTurn: (appId, payload) => request(`/v1/apps/${encodeURIComponent(appId)}/collab-turns`, {
|
|
11079
11145
|
method: "POST",
|
|
11080
11146
|
body: JSON.stringify(payload)
|
|
@@ -36105,26 +36171,39 @@ function isPidAlive2(pid) {
|
|
|
36105
36171
|
return false;
|
|
36106
36172
|
}
|
|
36107
36173
|
}
|
|
36108
|
-
function
|
|
36174
|
+
function readSpawnLock(spawnLockPath) {
|
|
36109
36175
|
try {
|
|
36110
36176
|
const raw = (0, import_node_fs7.readFileSync)(spawnLockPath, "utf8").trim();
|
|
36111
36177
|
if (!raw) return null;
|
|
36178
|
+
if (raw.startsWith("{")) {
|
|
36179
|
+
const parsed = JSON.parse(raw);
|
|
36180
|
+
const pid2 = Number(parsed?.pid ?? 0);
|
|
36181
|
+
return {
|
|
36182
|
+
pid: Number.isFinite(pid2) && pid2 > 0 ? pid2 : null,
|
|
36183
|
+
branchName: typeof parsed?.branchName === "string" && parsed.branchName.trim() ? parsed.branchName : null
|
|
36184
|
+
};
|
|
36185
|
+
}
|
|
36112
36186
|
const pid = Number.parseInt(raw, 10);
|
|
36113
|
-
return
|
|
36187
|
+
return {
|
|
36188
|
+
pid: Number.isFinite(pid) && pid > 0 ? pid : null,
|
|
36189
|
+
branchName: null
|
|
36190
|
+
};
|
|
36114
36191
|
} catch {
|
|
36115
36192
|
return null;
|
|
36116
36193
|
}
|
|
36117
36194
|
}
|
|
36118
|
-
function maybeAutoSpawnBranchInit(repoRoot) {
|
|
36195
|
+
function maybeAutoSpawnBranchInit(repoRoot, branchName) {
|
|
36119
36196
|
const remixDir = import_node_path13.default.join(repoRoot, ".remix");
|
|
36120
36197
|
const spawnLockPath = import_node_path13.default.join(repoRoot, COLLAB_INIT_SPAWN_LOCK_REL);
|
|
36121
36198
|
const logPath = import_node_path13.default.join(repoRoot, COLLAB_INIT_LOG_REL);
|
|
36122
36199
|
try {
|
|
36123
36200
|
if ((0, import_node_fs7.existsSync)(spawnLockPath)) {
|
|
36124
|
-
const
|
|
36201
|
+
const lock = readSpawnLock(spawnLockPath);
|
|
36202
|
+
const lockPid = lock?.pid ?? null;
|
|
36125
36203
|
const lockAlive = lockPid !== null && isPidAlive2(lockPid);
|
|
36204
|
+
const sameBranch = !lock?.branchName || !branchName || lock.branchName === branchName;
|
|
36126
36205
|
const ageMs = Date.now() - (0, import_node_fs7.statSync)(spawnLockPath).mtimeMs;
|
|
36127
|
-
if (lockAlive && ageMs < COLLAB_INIT_SPAWN_LOCK_STALE_MS) {
|
|
36206
|
+
if (lockAlive && sameBranch && ageMs < COLLAB_INIT_SPAWN_LOCK_STALE_MS) {
|
|
36128
36207
|
return { spawned: false, reason: "spawn_lock_held" };
|
|
36129
36208
|
}
|
|
36130
36209
|
if (!lockAlive) {
|
|
@@ -36153,6 +36232,13 @@ function maybeAutoSpawnBranchInit(repoRoot) {
|
|
|
36153
36232
|
};
|
|
36154
36233
|
}
|
|
36155
36234
|
try {
|
|
36235
|
+
(0, import_node_fs7.appendFileSync)(
|
|
36236
|
+
logPath,
|
|
36237
|
+
`
|
|
36238
|
+
[${(/* @__PURE__ */ new Date()).toISOString()}] auto-spawning remix collab init for branch=${branchName ?? "(unknown)"} repo=${repoRoot}
|
|
36239
|
+
`,
|
|
36240
|
+
"utf8"
|
|
36241
|
+
);
|
|
36156
36242
|
const child = (0, import_node_child_process8.spawn)("remix", ["collab", "init"], {
|
|
36157
36243
|
cwd: repoRoot,
|
|
36158
36244
|
detached: true,
|
|
@@ -36161,7 +36247,16 @@ function maybeAutoSpawnBranchInit(repoRoot) {
|
|
|
36161
36247
|
});
|
|
36162
36248
|
child.unref();
|
|
36163
36249
|
try {
|
|
36164
|
-
|
|
36250
|
+
const lock = {
|
|
36251
|
+
schemaVersion: 1,
|
|
36252
|
+
pid: child.pid ?? null,
|
|
36253
|
+
branchName,
|
|
36254
|
+
repoRoot,
|
|
36255
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36256
|
+
command: "remix collab init"
|
|
36257
|
+
};
|
|
36258
|
+
(0, import_node_fs7.writeFileSync)(spawnLockPath, `${JSON.stringify(lock, null, 2)}
|
|
36259
|
+
`, "utf8");
|
|
36165
36260
|
(0, import_node_fs7.utimesSync)(spawnLockPath, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date());
|
|
36166
36261
|
} catch {
|
|
36167
36262
|
}
|
|
@@ -36380,7 +36475,7 @@ async function runHookUserPrompt(payload) {
|
|
|
36380
36475
|
}
|
|
36381
36476
|
if (isCurrentBranchUnbound) {
|
|
36382
36477
|
const currentBranch = bindingState?.currentBranch ?? null;
|
|
36383
|
-
const outcome = maybeAutoSpawnBranchInit(boundRepo);
|
|
36478
|
+
const outcome = maybeAutoSpawnBranchInit(boundRepo, currentBranch);
|
|
36384
36479
|
if (outcome.spawned) {
|
|
36385
36480
|
advisorySections.push(buildBranchInitContextMessage(currentBranch, boundRepo, outcome.logPath));
|
|
36386
36481
|
await appendHookDiagnosticsEvent({
|