@remixhq/claude-plugin 0.1.17 → 0.1.18
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 +9 -4
- package/dist/hook-post-collab.cjs +18 -62
- package/dist/hook-post-collab.cjs.map +1 -1
- package/dist/hook-pre-git.cjs +19 -13
- package/dist/hook-pre-git.cjs.map +1 -1
- package/dist/hook-stop-collab.cjs +1343 -1674
- package/dist/hook-stop-collab.cjs.map +1 -1
- package/dist/hook-user-prompt.cjs +33174 -153
- 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 +2682 -1498
- package/dist/mcp-server.cjs.map +1 -1
- package/package.json +3 -3
- package/skills/identity-and-scope-routing/SKILL.md +1 -0
- package/skills/review-merge-request/SKILL.md +5 -3
- package/skills/safe-collab-workflow/SKILL.md +8 -5
- package/skills/submit-change-step/SKILL.md +15 -16
package/agents/remix-collab.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: remix-collab
|
|
3
|
-
description: Specialized Claude agent for Remix repository collaboration, merge request review, sync, reconcile, and
|
|
3
|
+
description: Specialized Claude agent for Remix repository collaboration, merge request review, sync, reconcile, and queued finalize-turn workflows.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
You are the Remix collaboration specialist.
|
|
@@ -32,19 +32,24 @@ Operating rules:
|
|
|
32
32
|
9. Clearly explain local mutation risk before using tools that can modify the local repo.
|
|
33
33
|
10. In a bound repo, Remix MCP tools are the required workflow layer for ordinary collaboration work.
|
|
34
34
|
11. In a bound repo, exactly one final `remix_collab_finalize_turn` is required before the final user-facing response.
|
|
35
|
-
12.
|
|
36
|
-
13.
|
|
37
|
-
14.
|
|
35
|
+
12. `remix_collab_finalize_turn` is queued only. A remote change step does not exist yet until the finalize queue drains successfully.
|
|
36
|
+
13. The final recording call must use the exact user prompt and your final assistant response.
|
|
37
|
+
14. If `remix_collab_status` reports `await_finalize`, use `remix_collab_drain_finalize_queue`, then re-check status before merge-related or recovery flows that depend on a remote recorded turn.
|
|
38
|
+
15. Do not finish the turn before recording. Do not make additional repo mutations after the final turn-recording call unless you intend to record again.
|
|
39
|
+
16. Do not duplicate core business logic in reasoning. Use the MCP tools to inspect and execute the workflow.
|
|
38
40
|
|
|
39
41
|
When appropriate:
|
|
40
42
|
|
|
41
43
|
- use `remix_collab_init` to bind the current repo
|
|
42
44
|
- use `remix_collab_remix` to start from an existing app lineage
|
|
43
45
|
- use `remix_collab_checkout` to continue work on an existing app id without creating a fork
|
|
46
|
+
- when the user asks for an app's dashboard URL and an `appId` is known, answer with `https://dashboard.remix.one/apps/<appId>` without requiring an additional tool call
|
|
44
47
|
- when helping the user choose an app, prefer scoped or membership-oriented discovery first: use `organizationId` / `projectId` when known, or use `ownership` with `accessScope="explicit_member"` when the user means “apps I can work on”
|
|
45
48
|
- reserve `accessScope="all_readable"` for explicit public or broad readable discovery, not as the default work-oriented app picker
|
|
46
49
|
- use memory summary/search/timeline tools before repo inspection when historical context or reasoning is needed
|
|
47
50
|
- use the explicit change-step diff tool only after you already know which `changeStepId` matters
|
|
48
51
|
- use `remix_collab_finalize_turn` as the final turn recorder
|
|
52
|
+
- treat `remix_collab_finalize_turn` as local capture plus queueing, not immediate remote materialization
|
|
53
|
+
- use `remix_collab_drain_finalize_queue` and `remix_collab_status` when the workflow is blocked on `await_finalize`
|
|
49
54
|
- use `remix_collab_review_queue` for reviewable merge requests, `remix_collab_my_merge_requests` for authored requests, and `remix_collab_list_app_merge_requests` for app-scoped MR flows with required `queue` set to `app_reviewable`, `app_outgoing`, or `app_related_visible`
|
|
50
55
|
- use merge request tools for review and approval flows
|
|
@@ -685,6 +685,12 @@ function normalizeStringArray(value) {
|
|
|
685
685
|
)
|
|
686
686
|
);
|
|
687
687
|
}
|
|
688
|
+
function normalizeManualRecordingScope(value) {
|
|
689
|
+
if (value === "full_turn") {
|
|
690
|
+
return "full_turn";
|
|
691
|
+
}
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
688
694
|
function normalizeTouchedRepo(value, repoRoot) {
|
|
689
695
|
if (!value || typeof value !== "object") return null;
|
|
690
696
|
const parsed = value;
|
|
@@ -703,7 +709,7 @@ function normalizeTouchedRepo(value, repoRoot) {
|
|
|
703
709
|
manuallyRecorded: Boolean(parsed.manuallyRecorded),
|
|
704
710
|
manuallyRecordedAt: normalizeString(parsed.manuallyRecordedAt),
|
|
705
711
|
manuallyRecordedByTool: normalizeString(parsed.manuallyRecordedByTool),
|
|
706
|
-
manualRecordingScope: parsed.manualRecordingScope
|
|
712
|
+
manualRecordingScope: normalizeManualRecordingScope(parsed.manualRecordingScope),
|
|
707
713
|
manualRemoteChangeRecordedAt: normalizeString(parsed.manualRemoteChangeRecordedAt),
|
|
708
714
|
stopAttempted: Boolean(parsed.stopAttempted),
|
|
709
715
|
stopRecorded: Boolean(parsed.stopRecorded),
|
|
@@ -855,7 +861,7 @@ async function markPendingTurnConsultedMemory(sessionId) {
|
|
|
855
861
|
// package.json
|
|
856
862
|
var package_default = {
|
|
857
863
|
name: "@remixhq/claude-plugin",
|
|
858
|
-
version: "0.1.
|
|
864
|
+
version: "0.1.18",
|
|
859
865
|
description: "Claude Code plugin for Remix collaboration workflows",
|
|
860
866
|
homepage: "https://github.com/RemixDotOne/remix-claude-plugin",
|
|
861
867
|
license: "MIT",
|
|
@@ -886,8 +892,8 @@ var package_default = {
|
|
|
886
892
|
prepack: "npm run build"
|
|
887
893
|
},
|
|
888
894
|
dependencies: {
|
|
889
|
-
"@remixhq/core": "^0.1.
|
|
890
|
-
"@remixhq/mcp": "^0.1.
|
|
895
|
+
"@remixhq/core": "^0.1.13",
|
|
896
|
+
"@remixhq/mcp": "^0.1.13"
|
|
891
897
|
},
|
|
892
898
|
devDependencies: {
|
|
893
899
|
"@types/node": "^25.4.0",
|
|
@@ -7751,7 +7757,7 @@ var {
|
|
|
7751
7757
|
getCancelSignal: getCancelSignal2
|
|
7752
7758
|
} = getIpcExport();
|
|
7753
7759
|
|
|
7754
|
-
// node_modules/@remixhq/core/dist/chunk-
|
|
7760
|
+
// node_modules/@remixhq/core/dist/chunk-WT6VRLXU.js
|
|
7755
7761
|
async function runGit(args, cwd) {
|
|
7756
7762
|
const res = await execa("git", args, { cwd, stderr: "ignore" });
|
|
7757
7763
|
return String(res.stdout || "").trim();
|
|
@@ -7765,7 +7771,7 @@ async function getCurrentBranch(cwd) {
|
|
|
7765
7771
|
}
|
|
7766
7772
|
}
|
|
7767
7773
|
|
|
7768
|
-
// node_modules/@remixhq/core/dist/chunk-
|
|
7774
|
+
// node_modules/@remixhq/core/dist/chunk-YCFLOHJV.js
|
|
7769
7775
|
var import_promises14 = __toESM(require("fs/promises"), 1);
|
|
7770
7776
|
var import_path = __toESM(require("path"), 1);
|
|
7771
7777
|
var import_promises15 = __toESM(require("fs/promises"), 1);
|
|
@@ -8028,11 +8034,6 @@ function extractToolInput(payload) {
|
|
|
8028
8034
|
function extractToolResponse(payload) {
|
|
8029
8035
|
return getNestedRecord(payload.tool_response) ?? getNestedRecord(payload.toolResponse);
|
|
8030
8036
|
}
|
|
8031
|
-
function extractToolStructuredData(payload) {
|
|
8032
|
-
const toolResponse = extractToolResponse(payload);
|
|
8033
|
-
const structuredContent = getNestedRecord(toolResponse?.structuredContent) ?? getNestedRecord(payload.structuredContent);
|
|
8034
|
-
return getNestedRecord(toolResponse?.data) ?? getNestedRecord(structuredContent?.data) ?? structuredContent;
|
|
8035
|
-
}
|
|
8036
8037
|
function extractToolName(payload) {
|
|
8037
8038
|
return extractString(payload, ["tool_name", "toolName"]);
|
|
8038
8039
|
}
|
|
@@ -8046,26 +8047,6 @@ function normalizeHookToolName(toolName) {
|
|
|
8046
8047
|
}
|
|
8047
8048
|
return trimmed;
|
|
8048
8049
|
}
|
|
8049
|
-
function extractAssistantResponse(payload) {
|
|
8050
|
-
const candidateKeys = [
|
|
8051
|
-
"last_assistant_message",
|
|
8052
|
-
"lastAssistantMessage",
|
|
8053
|
-
"assistant_response",
|
|
8054
|
-
"assistantResponse",
|
|
8055
|
-
"assistant_message",
|
|
8056
|
-
"assistantMessage",
|
|
8057
|
-
"response",
|
|
8058
|
-
"message"
|
|
8059
|
-
];
|
|
8060
|
-
return extractString(payload, candidateKeys) ?? extractString(extractToolResponse(payload) ?? {}, candidateKeys) ?? extractString(extractToolStructuredData(payload) ?? {}, candidateKeys) ?? extractString(extractToolInput(payload), candidateKeys);
|
|
8061
|
-
}
|
|
8062
|
-
function extractFinalizeTurnMode(payload) {
|
|
8063
|
-
const mode = extractString(extractToolStructuredData(payload) ?? {}, ["mode"]) ?? extractString(extractToolResponse(payload) ?? {}, ["mode"]) ?? extractString(payload, ["mode"]);
|
|
8064
|
-
if (mode === "changed_turn" || mode === "no_diff_turn") {
|
|
8065
|
-
return mode;
|
|
8066
|
-
}
|
|
8067
|
-
return null;
|
|
8068
|
-
}
|
|
8069
8050
|
function extractString(input, keys) {
|
|
8070
8051
|
for (const key of keys) {
|
|
8071
8052
|
const value = input[key];
|
|
@@ -8117,9 +8098,6 @@ function didToolSucceed(payload) {
|
|
|
8117
8098
|
const hookEventName = extractString(payload, ["hook_event_name", "hookEventName"]);
|
|
8118
8099
|
return hookEventName === "PostToolUse";
|
|
8119
8100
|
}
|
|
8120
|
-
function isRemoteChangeRecordedButLocalSyncFailed(payload) {
|
|
8121
|
-
return extractToolErrorMessage(payload) === "Change step succeeded remotely, but automatic local sync failed.";
|
|
8122
|
-
}
|
|
8123
8101
|
function collectStringPathValue(value) {
|
|
8124
8102
|
if (typeof value === "string" && value.trim()) return [value.trim()];
|
|
8125
8103
|
if (Array.isArray(value)) {
|
|
@@ -8184,7 +8162,7 @@ async function resolveBoundRepoFromToolCwd(payload) {
|
|
|
8184
8162
|
|
|
8185
8163
|
// src/hook-post-collab.ts
|
|
8186
8164
|
function isRepoMutationToolName(toolName) {
|
|
8187
|
-
return /remix_collab_(
|
|
8165
|
+
return /remix_collab_(sync_apply|approve_and_sync_target|sync_upstream|reconcile_apply)$/i.test(toolName);
|
|
8188
8166
|
}
|
|
8189
8167
|
function isMemoryToolName(toolName) {
|
|
8190
8168
|
return /remix_collab_memory_(summary|search|timeline|change_step_diff)$/i.test(toolName);
|
|
@@ -8198,31 +8176,12 @@ function isStructuredLocalReadToolName(toolName) {
|
|
|
8198
8176
|
function isShellToolName(toolName) {
|
|
8199
8177
|
return /^Bash$/i.test(toolName);
|
|
8200
8178
|
}
|
|
8201
|
-
function
|
|
8202
|
-
return /remix_collab_(add|add_change_step)$/i.test(toolName);
|
|
8203
|
-
}
|
|
8204
|
-
function hasManualFullTurnPayload(payload) {
|
|
8205
|
-
const toolInput = extractToolInput(payload);
|
|
8206
|
-
return Boolean(extractString(toolInput, ["prompt"]) && extractAssistantResponse(payload));
|
|
8207
|
-
}
|
|
8208
|
-
function getManualRecordingScope(payload, toolName) {
|
|
8179
|
+
function getManualRecordingScope(toolName) {
|
|
8209
8180
|
if (/remix_collab_finalize_turn$/i.test(toolName)) {
|
|
8210
8181
|
return "full_turn";
|
|
8211
8182
|
}
|
|
8212
|
-
if (/remix_collab_(add|add_change_step)$/i.test(toolName)) {
|
|
8213
|
-
return hasManualFullTurnPayload(payload) ? "full_turn" : "change_step";
|
|
8214
|
-
}
|
|
8215
|
-
if (/remix_collab_(record_turn|record_no_diff_turn)$/i.test(toolName)) {
|
|
8216
|
-
return "full_turn";
|
|
8217
|
-
}
|
|
8218
8183
|
return null;
|
|
8219
8184
|
}
|
|
8220
|
-
function didFinalizeTurnRecordRemoteChange(payload, toolName) {
|
|
8221
|
-
if (!/remix_collab_finalize_turn$/i.test(toolName)) {
|
|
8222
|
-
return false;
|
|
8223
|
-
}
|
|
8224
|
-
return extractFinalizeTurnMode(payload) === "changed_turn";
|
|
8225
|
-
}
|
|
8226
8185
|
function isLikelyMutatingShellCommand(command) {
|
|
8227
8186
|
const normalized = command.trim().toLowerCase();
|
|
8228
8187
|
if (!normalized) return false;
|
|
@@ -8288,7 +8247,6 @@ async function runHookPostCollab(payload) {
|
|
|
8288
8247
|
return;
|
|
8289
8248
|
}
|
|
8290
8249
|
const toolSucceeded = didToolSucceed(payload);
|
|
8291
|
-
const remoteChangeRecordedButSyncFailed = (isRemoteChangeRecordingToolName(toolName) || /remix_collab_finalize_turn$/i.test(toolName)) && isRemoteChangeRecordedButLocalSyncFailed(payload);
|
|
8292
8250
|
await appendHookDiagnosticsEvent({
|
|
8293
8251
|
hook: "PostToolUse",
|
|
8294
8252
|
sessionId,
|
|
@@ -8297,7 +8255,6 @@ async function runHookPostCollab(payload) {
|
|
|
8297
8255
|
toolName,
|
|
8298
8256
|
fields: {
|
|
8299
8257
|
toolSucceeded,
|
|
8300
|
-
remoteChangeRecordedButSyncFailed,
|
|
8301
8258
|
isMemoryTool: isMemoryToolName(toolName),
|
|
8302
8259
|
isRepoMutationTool: isRepoMutationToolName(toolName),
|
|
8303
8260
|
isShellTool: isShellToolName(toolName),
|
|
@@ -8305,7 +8262,7 @@ async function runHookPostCollab(payload) {
|
|
|
8305
8262
|
isStructuredLocalReadTool: isStructuredLocalReadToolName(toolName)
|
|
8306
8263
|
}
|
|
8307
8264
|
});
|
|
8308
|
-
if (!toolSucceeded
|
|
8265
|
+
if (!toolSucceeded) {
|
|
8309
8266
|
await appendHookDiagnosticsEvent({
|
|
8310
8267
|
hook: "PostToolUse",
|
|
8311
8268
|
sessionId,
|
|
@@ -8326,7 +8283,7 @@ async function runHookPostCollab(payload) {
|
|
|
8326
8283
|
toolName
|
|
8327
8284
|
});
|
|
8328
8285
|
}
|
|
8329
|
-
const manualRecordingScope = getManualRecordingScope(
|
|
8286
|
+
const manualRecordingScope = getManualRecordingScope(toolName);
|
|
8330
8287
|
if (isRepoMutationToolName(toolName) || manualRecordingScope) {
|
|
8331
8288
|
const targetRepo = await resolveBoundRepoFromToolCwd(payload);
|
|
8332
8289
|
if (targetRepo) {
|
|
@@ -8345,7 +8302,7 @@ async function runHookPostCollab(payload) {
|
|
|
8345
8302
|
await markTouchedRepoManuallyRecorded(sessionId, targetRepo.repoRoot, {
|
|
8346
8303
|
toolName,
|
|
8347
8304
|
scope: manualRecordingScope,
|
|
8348
|
-
remoteChangeRecorded:
|
|
8305
|
+
remoteChangeRecorded: false
|
|
8349
8306
|
});
|
|
8350
8307
|
}
|
|
8351
8308
|
await appendHookDiagnosticsEvent({
|
|
@@ -8357,8 +8314,7 @@ async function runHookPostCollab(payload) {
|
|
|
8357
8314
|
repoRoot: targetRepo.repoRoot,
|
|
8358
8315
|
fields: {
|
|
8359
8316
|
hasObservedWrite: isRepoMutationToolName(toolName),
|
|
8360
|
-
manualRecordingScope
|
|
8361
|
-
remoteChangeRecordedButSyncFailed
|
|
8317
|
+
manualRecordingScope
|
|
8362
8318
|
}
|
|
8363
8319
|
});
|
|
8364
8320
|
} else {
|