@remixhq/mcp 0.1.11 → 0.1.13
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/dist/cli.js +246 -53
- package/dist/cli.js.map +1 -1
- package/dist/index.js +236 -49
- package/dist/index.js.map +1 -1
- package/dist/server.js +236 -49
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { createRequire } from "module";
|
|
5
|
-
|
|
6
|
-
// src/server.ts
|
|
7
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { drainPendingFinalizeQueue } from "@remixhq/core/collab";
|
|
9
6
|
|
|
10
7
|
// src/domain/apiClient.ts
|
|
11
8
|
import { createApiClient as createCoreApiClient, resolveConfig as resolveConfig2 } from "@remixhq/core";
|
|
@@ -42,6 +39,10 @@ async function createApiClient() {
|
|
|
42
39
|
});
|
|
43
40
|
}
|
|
44
41
|
|
|
42
|
+
// src/server.ts
|
|
43
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
44
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
45
|
+
|
|
45
46
|
// src/observability/logger.ts
|
|
46
47
|
function createLogger() {
|
|
47
48
|
return {
|
|
@@ -77,6 +78,7 @@ var ERROR_CODES = {
|
|
|
77
78
|
DIRTY_WORKTREE: "DIRTY_WORKTREE",
|
|
78
79
|
DETACHED_HEAD: "DETACHED_HEAD",
|
|
79
80
|
PREFERRED_BRANCH_MISMATCH: "PREFERRED_BRANCH_MISMATCH",
|
|
81
|
+
FAMILY_SELECTION_AMBIGUOUS: "FAMILY_SELECTION_AMBIGUOUS",
|
|
80
82
|
MISSING_HEAD: "MISSING_HEAD",
|
|
81
83
|
REPO_LOCK_HELD: "REPO_LOCK_HELD",
|
|
82
84
|
REPO_LOCK_TIMEOUT: "REPO_LOCK_TIMEOUT",
|
|
@@ -213,6 +215,14 @@ function normalizeByMessage(err) {
|
|
|
213
215
|
category: "local_state"
|
|
214
216
|
});
|
|
215
217
|
}
|
|
218
|
+
if (message.includes("Multiple canonical Remix families") || message.includes("family selection is ambiguous")) {
|
|
219
|
+
return makeNormalized({
|
|
220
|
+
code: ERROR_CODES.FAMILY_SELECTION_AMBIGUOUS,
|
|
221
|
+
message,
|
|
222
|
+
hint,
|
|
223
|
+
category: "local_state"
|
|
224
|
+
});
|
|
225
|
+
}
|
|
216
226
|
if (message.includes("Failed to resolve local HEAD")) {
|
|
217
227
|
return makeNormalized({
|
|
218
228
|
code: ERROR_CODES.MISSING_HEAD,
|
|
@@ -346,14 +356,6 @@ function assertConfirm(confirm, operation) {
|
|
|
346
356
|
if (confirm) return;
|
|
347
357
|
throw createPolicyError(`${operation} requires explicit confirmation.`, "Pass confirm=true to run this tool.");
|
|
348
358
|
}
|
|
349
|
-
function assertDiffWithinLimit(policy, diff) {
|
|
350
|
-
const sizeBytes = Buffer.byteLength(diff, "utf8");
|
|
351
|
-
if (sizeBytes <= policy.maxDiffBytes) return;
|
|
352
|
-
throw createPolicyError(
|
|
353
|
-
"Diff exceeds the configured maximum size for Remix MCP.",
|
|
354
|
-
`Configured limit=${policy.maxDiffBytes} bytes actual=${sizeBytes} bytes.`
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
359
|
|
|
358
360
|
// src/bootstrap/context.ts
|
|
359
361
|
function createServerContext(params) {
|
|
@@ -501,8 +503,6 @@ var finalizeTurnInputSchema = {
|
|
|
501
503
|
...commonRequestFieldsSchema,
|
|
502
504
|
prompt: z2.string().trim().min(1),
|
|
503
505
|
assistantResponse: z2.string().trim().min(1),
|
|
504
|
-
diffSource: z2.enum(["worktree", "external"]).optional(),
|
|
505
|
-
externalDiff: z2.string().optional(),
|
|
506
506
|
sync: z2.boolean().optional(),
|
|
507
507
|
allowBranchMismatch: z2.boolean().optional(),
|
|
508
508
|
idempotencyKey: z2.string().trim().min(1).optional()
|
|
@@ -510,11 +510,17 @@ var finalizeTurnInputSchema = {
|
|
|
510
510
|
var previewInputSchema = {
|
|
511
511
|
...commonRequestFieldsSchema
|
|
512
512
|
};
|
|
513
|
+
var drainFinalizeQueueInputSchema = {
|
|
514
|
+
...commonRequestFieldsSchema
|
|
515
|
+
};
|
|
513
516
|
var applyInputSchema = {
|
|
514
517
|
...commonRequestFieldsSchema,
|
|
515
518
|
confirm: z2.boolean(),
|
|
516
519
|
allowBranchMismatch: z2.boolean().optional()
|
|
517
520
|
};
|
|
521
|
+
var reAnchorInputSchema = {
|
|
522
|
+
...applyInputSchema
|
|
523
|
+
};
|
|
518
524
|
var requestMergeInputSchema = {
|
|
519
525
|
...commonRequestFieldsSchema
|
|
520
526
|
};
|
|
@@ -624,7 +630,10 @@ var initDataSchema = z2.object({
|
|
|
624
630
|
dashboardUrl: z2.string().url(),
|
|
625
631
|
upstreamAppId: z2.string(),
|
|
626
632
|
bindingPath: z2.string(),
|
|
627
|
-
repoRoot: z2.string()
|
|
633
|
+
repoRoot: z2.string(),
|
|
634
|
+
bindingMode: z2.enum(["legacy", "lane", "explicit_root"]).optional(),
|
|
635
|
+
createdCanonicalFamily: z2.boolean().optional(),
|
|
636
|
+
baselineStatus: z2.enum(["seeded", "existing", "requires_re_anchor", "requires_sync"]).optional()
|
|
628
637
|
});
|
|
629
638
|
var listDataSchema = z2.object({
|
|
630
639
|
apps: genericArraySchema,
|
|
@@ -647,19 +656,26 @@ var checkoutDataSchema = z2.object({
|
|
|
647
656
|
repoRoot: z2.string()
|
|
648
657
|
});
|
|
649
658
|
var addDataSchema = z2.object({
|
|
650
|
-
changeStep: genericRecordSchema
|
|
651
|
-
autoSync: genericRecordSchema
|
|
659
|
+
changeStep: genericRecordSchema
|
|
652
660
|
});
|
|
653
661
|
var recordTurnDataSchema = genericRecordSchema;
|
|
654
662
|
var finalizeTurnDataSchema = z2.object({
|
|
655
663
|
mode: z2.enum(["changed_turn", "no_diff_turn"]),
|
|
656
664
|
idempotencyKey: z2.string().min(1),
|
|
665
|
+
queued: z2.boolean(),
|
|
666
|
+
jobId: z2.string().nullable(),
|
|
667
|
+
repoState: z2.string().nullable(),
|
|
657
668
|
changeStep: genericRecordSchema.nullable(),
|
|
658
669
|
collabTurn: genericRecordSchema.nullable(),
|
|
659
670
|
autoSync: genericRecordSchema.nullable(),
|
|
660
671
|
warnings: z2.array(z2.string())
|
|
661
672
|
});
|
|
673
|
+
var drainFinalizeQueueDataSchema = z2.object({
|
|
674
|
+
processed: z2.number().int().nonnegative(),
|
|
675
|
+
results: z2.array(genericRecordSchema)
|
|
676
|
+
});
|
|
662
677
|
var syncDataSchema = genericRecordSchema;
|
|
678
|
+
var reAnchorDataSchema = genericRecordSchema;
|
|
663
679
|
var requestMergeDataSchema = genericRecordSchema;
|
|
664
680
|
var mergeRequestQueueDataSchema = z2.object({
|
|
665
681
|
queue: mergeRequestQueueSchema,
|
|
@@ -734,7 +750,9 @@ var checkoutSuccessSchema = makeSuccessSchema(checkoutDataSchema);
|
|
|
734
750
|
var addSuccessSchema = makeSuccessSchema(addDataSchema);
|
|
735
751
|
var recordTurnSuccessSchema = makeSuccessSchema(recordTurnDataSchema);
|
|
736
752
|
var finalizeTurnSuccessSchema = makeSuccessSchema(finalizeTurnDataSchema);
|
|
753
|
+
var drainFinalizeQueueSuccessSchema = makeSuccessSchema(drainFinalizeQueueDataSchema);
|
|
737
754
|
var syncSuccessSchema = makeSuccessSchema(syncDataSchema);
|
|
755
|
+
var reAnchorSuccessSchema = makeSuccessSchema(reAnchorDataSchema);
|
|
738
756
|
var requestMergeSuccessSchema = makeSuccessSchema(requestMergeDataSchema);
|
|
739
757
|
var mergeRequestQueueSuccessSchema = makeSuccessSchema(mergeRequestQueueDataSchema);
|
|
740
758
|
var viewMergeRequestSuccessSchema = makeSuccessSchema(viewMergeRequestDataSchema);
|
|
@@ -752,17 +770,18 @@ var accessDebugSuccessSchema = makeSuccessSchema(accessDebugDataSchema);
|
|
|
752
770
|
var updateMemberRoleSuccessSchema = makeSuccessSchema(updateMemberRoleDataSchema);
|
|
753
771
|
|
|
754
772
|
// src/domain/coreAdapter.ts
|
|
773
|
+
import { spawn } from "child_process";
|
|
755
774
|
import {
|
|
756
775
|
collabList as coreCollabList,
|
|
757
776
|
collabListMembers as coreCollabListMembers,
|
|
758
777
|
collabUpdateMemberRole as coreCollabUpdateMemberRole,
|
|
759
|
-
|
|
778
|
+
drainPendingFinalizeQueue as coreDrainPendingFinalizeQueue,
|
|
760
779
|
collabFinalizeTurn as coreCollabFinalizeTurn,
|
|
761
|
-
collabRecordTurn as coreCollabRecordTurn,
|
|
762
780
|
collabApprove as coreCollabApprove,
|
|
763
781
|
collabCheckout as coreCollabCheckout,
|
|
764
782
|
collabListMergeRequests as coreCollabListMergeRequests,
|
|
765
783
|
collabInit as coreCollabInit,
|
|
784
|
+
collabReAnchor as coreCollabReAnchor,
|
|
766
785
|
collabInvite as coreCollabInvite,
|
|
767
786
|
collabReconcile as coreCollabReconcile,
|
|
768
787
|
collabReject as coreCollabReject,
|
|
@@ -773,10 +792,13 @@ import {
|
|
|
773
792
|
collabSyncUpstream as coreCollabSyncUpstream,
|
|
774
793
|
collabView as coreCollabView
|
|
775
794
|
} from "@remixhq/core/collab";
|
|
776
|
-
import { findGitRoot
|
|
795
|
+
import { findGitRoot } from "@remixhq/core/repo";
|
|
777
796
|
function getRiskLevel(status) {
|
|
778
797
|
if (status.recommendedAction === "reconcile") return "high";
|
|
779
|
-
if (status.recommendedAction === "
|
|
798
|
+
if (status.recommendedAction === "choose_family" || status.recommendedAction === "await_finalize") return "medium";
|
|
799
|
+
if (status.recommendedAction === "pull" || status.recommendedAction === "re_anchor" || status.remote.incomingOpenMergeRequestCount) {
|
|
800
|
+
return "medium";
|
|
801
|
+
}
|
|
780
802
|
if (status.repo.branchMismatch || !status.repo.isGitRepo || !status.binding.isBound || !status.repo.worktree.isClean) return "medium";
|
|
781
803
|
return "low";
|
|
782
804
|
}
|
|
@@ -789,12 +811,22 @@ function getRecommendedNextActions(status) {
|
|
|
789
811
|
switch (status.recommendedAction) {
|
|
790
812
|
case "init":
|
|
791
813
|
return ["Run remix_collab_init to bind the repository to Remix before using any Remix collaboration mutation flow."];
|
|
792
|
-
case "
|
|
793
|
-
return ["Run remix_collab_sync_preview, then remix_collab_sync_apply if the preview is acceptable.
|
|
814
|
+
case "pull":
|
|
815
|
+
return ["Run remix_collab_sync_preview, then remix_collab_sync_apply if the preview is acceptable. This pulls the server delta into the local working tree without rewriting local git history."];
|
|
816
|
+
case "re_anchor":
|
|
817
|
+
return ["Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply to re-anchor the lane after manual Git/GitHub history movement."];
|
|
794
818
|
case "reconcile":
|
|
795
|
-
return ["Run remix_collab_reconcile_preview before attempting remix_collab_reconcile_apply. Reconcile
|
|
819
|
+
return ["Run remix_collab_reconcile_preview before attempting remix_collab_reconcile_apply. Reconcile now routes through the explicit Remix recovery/re-anchor flow for diverged state."];
|
|
820
|
+
case "await_finalize":
|
|
821
|
+
return [
|
|
822
|
+
"Run remix_collab_drain_finalize_queue before merge-related or recovery flows. finalize_turn is queued only until the local finalize queue is drained."
|
|
823
|
+
];
|
|
796
824
|
case "review_queue":
|
|
797
825
|
return ["Run remix_collab_review_queue to inspect reviewable merge requests instead of using local git merge flows."];
|
|
826
|
+
case "choose_family":
|
|
827
|
+
return [
|
|
828
|
+
"This checkout is ambiguous across multiple canonical Remix families. Continue from a checkout already bound to the intended family, or run remix_collab_init with forceNew=true to create and bind a new canonical family."
|
|
829
|
+
];
|
|
798
830
|
default:
|
|
799
831
|
return [];
|
|
800
832
|
}
|
|
@@ -822,6 +854,17 @@ function truncateText(value, maxChars) {
|
|
|
822
854
|
originalChars: value.length
|
|
823
855
|
};
|
|
824
856
|
}
|
|
857
|
+
function spawnFinalizeQueueDrainer() {
|
|
858
|
+
const entrypoint = process.argv[1];
|
|
859
|
+
if (!entrypoint) return false;
|
|
860
|
+
const child = spawn(process.execPath, [...process.execArgv, entrypoint, "--drain-finalize-queue"], {
|
|
861
|
+
detached: true,
|
|
862
|
+
stdio: "ignore",
|
|
863
|
+
env: process.env
|
|
864
|
+
});
|
|
865
|
+
child.unref();
|
|
866
|
+
return true;
|
|
867
|
+
}
|
|
825
868
|
async function getStatus(params) {
|
|
826
869
|
const api = params.includeRemote ? await createCollabApiClient() : null;
|
|
827
870
|
const status = await coreCollabStatus({
|
|
@@ -852,7 +895,11 @@ async function initCollab(params) {
|
|
|
852
895
|
return {
|
|
853
896
|
data: result,
|
|
854
897
|
warnings: collectResultWarnings(result),
|
|
855
|
-
recommendedNextActions:
|
|
898
|
+
recommendedNextActions: result.baselineStatus === "requires_re_anchor" ? [
|
|
899
|
+
"Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply to anchor this checkout before recording turns."
|
|
900
|
+
] : result.baselineStatus === "requires_sync" ? [
|
|
901
|
+
"Run remix_collab_sync_preview, then remix_collab_sync_apply to pull the server delta and create the first local baseline for this checkout."
|
|
902
|
+
] : ["Run remix_collab_status to inspect sync, reconcile, and merge-request readiness before mutating bound-repo state."],
|
|
856
903
|
logContext: {
|
|
857
904
|
repoRoot: result.repoRoot,
|
|
858
905
|
appId: result.appId
|
|
@@ -922,23 +969,44 @@ async function finalizeCollabTurn(params) {
|
|
|
922
969
|
cwd: params.cwd,
|
|
923
970
|
prompt: params.prompt,
|
|
924
971
|
assistantResponse: params.assistantResponse,
|
|
925
|
-
diff: params.externalDiff ?? null,
|
|
926
|
-
diffSource: params.diffSource,
|
|
927
972
|
sync: params.sync,
|
|
928
973
|
allowBranchMismatch: params.allowBranchMismatch ?? false,
|
|
929
974
|
idempotencyKey: params.idempotencyKey ?? null,
|
|
930
975
|
actor: params.agent
|
|
931
976
|
});
|
|
977
|
+
if (result.queued) {
|
|
978
|
+
if (!spawnFinalizeQueueDrainer()) {
|
|
979
|
+
await coreDrainPendingFinalizeQueue({ api });
|
|
980
|
+
}
|
|
981
|
+
}
|
|
932
982
|
return {
|
|
933
983
|
data: result,
|
|
934
984
|
warnings: result.warnings,
|
|
935
|
-
recommendedNextActions: [],
|
|
985
|
+
recommendedNextActions: result.queued ? ["Run remix_collab_drain_finalize_queue before merge-related flows if you need this queued turn recorded immediately."] : [],
|
|
936
986
|
logContext: {
|
|
937
987
|
repoRoot,
|
|
938
988
|
appId: result.changeStep?.appId ?? result.collabTurn?.appId ?? null
|
|
939
989
|
}
|
|
940
990
|
};
|
|
941
991
|
}
|
|
992
|
+
async function drainFinalizeQueue(params) {
|
|
993
|
+
const api = await createCollabApiClient();
|
|
994
|
+
const repoRoot = await findGitRoot(params.cwd);
|
|
995
|
+
const results = await coreDrainPendingFinalizeQueue({ api });
|
|
996
|
+
const warnings = results.flatMap((result) => collectResultWarnings(result));
|
|
997
|
+
return {
|
|
998
|
+
data: {
|
|
999
|
+
processed: results.length,
|
|
1000
|
+
results
|
|
1001
|
+
},
|
|
1002
|
+
warnings,
|
|
1003
|
+
recommendedNextActions: [],
|
|
1004
|
+
logContext: {
|
|
1005
|
+
repoRoot,
|
|
1006
|
+
appId: null
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
942
1010
|
async function syncCollab(params) {
|
|
943
1011
|
const api = await createCollabApiClient();
|
|
944
1012
|
const result = await coreCollabSync({
|
|
@@ -950,12 +1018,33 @@ async function syncCollab(params) {
|
|
|
950
1018
|
return {
|
|
951
1019
|
data: result,
|
|
952
1020
|
warnings: collectResultWarnings(result),
|
|
953
|
-
recommendedNextActions: params.dryRun ? ["Run remix_collab_sync_apply with confirm=true to apply this
|
|
1021
|
+
recommendedNextActions: params.dryRun ? result.status === "delta_ready" ? ["Run remix_collab_sync_apply with confirm=true to apply this server delta into the local working tree."] : result.status === "base_unknown" ? [
|
|
1022
|
+
"Direct pull is unavailable because Remix cannot diff from the last acknowledged server head.",
|
|
1023
|
+
"Run remix_collab_reconcile_preview next to inspect recovery options before applying any recovery flow."
|
|
1024
|
+
] : [] : [],
|
|
954
1025
|
logContext: {
|
|
955
1026
|
repoRoot: result.repoRoot
|
|
956
1027
|
}
|
|
957
1028
|
};
|
|
958
1029
|
}
|
|
1030
|
+
async function reAnchor(params) {
|
|
1031
|
+
const api = await createCollabApiClient();
|
|
1032
|
+
const result = await coreCollabReAnchor({
|
|
1033
|
+
api,
|
|
1034
|
+
cwd: params.cwd,
|
|
1035
|
+
dryRun: params.dryRun,
|
|
1036
|
+
allowBranchMismatch: params.allowBranchMismatch ?? false
|
|
1037
|
+
});
|
|
1038
|
+
return {
|
|
1039
|
+
data: result,
|
|
1040
|
+
warnings: collectWarnings(result.warnings),
|
|
1041
|
+
recommendedNextActions: params.dryRun ? ["Run remix_collab_re_anchor_apply with confirm=true to re-anchor this lane after manual Git/GitHub history movement."] : [],
|
|
1042
|
+
logContext: {
|
|
1043
|
+
repoRoot: result.repoRoot,
|
|
1044
|
+
appId: result.currentAppId
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
959
1048
|
async function requestMerge(params) {
|
|
960
1049
|
const api = await createCollabApiClient();
|
|
961
1050
|
const result = await coreCollabRequestMerge({
|
|
@@ -1129,8 +1218,8 @@ async function reconcile(params) {
|
|
|
1129
1218
|
return {
|
|
1130
1219
|
data: result,
|
|
1131
1220
|
warnings: collectWarnings(result.warnings),
|
|
1132
|
-
recommendedNextActions: params.dryRun ? ["Run remix_collab_reconcile_apply with confirm=true only if the preview is acceptable.
|
|
1133
|
-
risks: params.dryRun ? ["Reconcile
|
|
1221
|
+
recommendedNextActions: params.dryRun ? ["Run remix_collab_reconcile_apply with confirm=true only if the preview is acceptable. This is the explicit Remix recovery flow for diverged state."] : [],
|
|
1222
|
+
risks: params.dryRun ? ["Reconcile may upload local history to re-anchor the server lane before future recording continues."] : [],
|
|
1134
1223
|
logContext: {
|
|
1135
1224
|
repoRoot: result.repoRoot ?? null
|
|
1136
1225
|
}
|
|
@@ -1706,9 +1795,6 @@ function buildSuccessEnvelope(tool, requestId, result) {
|
|
|
1706
1795
|
};
|
|
1707
1796
|
}
|
|
1708
1797
|
function deriveErrorRisks(tool, normalized) {
|
|
1709
|
-
if (isFinalizeTurnLocalSyncFailure(tool, normalized)) {
|
|
1710
|
-
return ["The change step succeeded remotely, but the local repository may need manual recovery or a follow-up sync."];
|
|
1711
|
-
}
|
|
1712
1798
|
if (normalized.code === "DESTRUCTIVE_OPERATION_BLOCKED") {
|
|
1713
1799
|
return ["A policy guard blocked a potentially destructive or state-mutating operation."];
|
|
1714
1800
|
}
|
|
@@ -1720,16 +1806,9 @@ function deriveErrorRisks(tool, normalized) {
|
|
|
1720
1806
|
}
|
|
1721
1807
|
return [];
|
|
1722
1808
|
}
|
|
1723
|
-
function isFinalizeTurnLocalSyncFailure(tool, normalized) {
|
|
1724
|
-
return tool === "remix_collab_finalize_turn" && normalized.message === "Change step succeeded remotely, but automatic local sync failed.";
|
|
1725
|
-
}
|
|
1726
1809
|
function buildErrorEnvelope(tool, requestId, error) {
|
|
1727
1810
|
const normalized = normalizeToolError(error);
|
|
1728
|
-
const recommendedNextActions =
|
|
1729
|
-
"Run `remix_collab_status` to confirm the bound repo state before attempting recovery.",
|
|
1730
|
-
"Run `remix_collab_sync_preview` next, then `remix_collab_sync_apply` with `confirm=true` if the preview looks correct.",
|
|
1731
|
-
"Inspect `error.hint` for any preserved diff backup path before retrying local recovery, and do not rerun `remix_collab_finalize_turn` immediately."
|
|
1732
|
-
] : normalized.code === "AUTH_REQUIRED" ? ["Set COMERGE_ACCESS_TOKEN, then retry the tool call."] : normalized.code === "REPO_LOCK_TIMEOUT" ? ["Wait for the active Remix mutation to finish, then retry the tool call."] : normalized.code === "REPO_STATE_CHANGED_DURING_OPERATION" ? ["Review local repository changes, then rerun the tool once the worktree is stable."] : normalized.code === "PREFERRED_BRANCH_MISMATCH" ? ["Switch to the repository's preferred Remix branch, or rerun with allowBranchMismatch=true if intentional."] : [];
|
|
1811
|
+
const recommendedNextActions = normalized.code === "AUTH_REQUIRED" ? ["Set COMERGE_ACCESS_TOKEN, then retry the tool call."] : normalized.code === "REPO_LOCK_TIMEOUT" ? ["Wait for the active Remix mutation to finish, then retry the tool call."] : normalized.code === "REPO_STATE_CHANGED_DURING_OPERATION" ? ["Review local repository changes, then rerun the tool once the worktree is stable."] : normalized.code === "PREFERRED_BRANCH_MISMATCH" ? ["Switch to the repository's preferred Remix branch, or rerun with allowBranchMismatch=true if intentional."] : [];
|
|
1733
1812
|
return {
|
|
1734
1813
|
schemaVersion: SCHEMA_VERSION,
|
|
1735
1814
|
ok: false,
|
|
@@ -1875,7 +1954,7 @@ function registerCollabTools(server, context) {
|
|
|
1875
1954
|
});
|
|
1876
1955
|
registerTool(server, context, {
|
|
1877
1956
|
name: "remix_collab_finalize_turn",
|
|
1878
|
-
description: "Primary turn recorder for the current bound repository. Call this exactly once before the final response; it
|
|
1957
|
+
description: "Primary turn recorder for the current bound repository. Call this exactly once before the final response; it captures the current boundary locally and queues remote processing. Queued only: no remote change step exists yet until the finalize queue is drained.",
|
|
1879
1958
|
access: "local_write",
|
|
1880
1959
|
inputSchema: finalizeTurnInputSchema,
|
|
1881
1960
|
outputSchema: finalizeTurnSuccessSchema,
|
|
@@ -1883,15 +1962,10 @@ function registerCollabTools(server, context) {
|
|
|
1883
1962
|
run: async (args) => {
|
|
1884
1963
|
const input = z3.object(finalizeTurnInputSchema).parse(args);
|
|
1885
1964
|
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
1886
|
-
if ((input.diffSource ?? "worktree") === "external" || typeof input.externalDiff === "string") {
|
|
1887
|
-
assertDiffWithinLimit(context.policy, input.externalDiff ?? "");
|
|
1888
|
-
}
|
|
1889
1965
|
return finalizeCollabTurn({
|
|
1890
1966
|
cwd,
|
|
1891
1967
|
prompt: input.prompt,
|
|
1892
1968
|
assistantResponse: input.assistantResponse,
|
|
1893
|
-
diffSource: input.diffSource,
|
|
1894
|
-
externalDiff: input.externalDiff,
|
|
1895
1969
|
sync: input.sync,
|
|
1896
1970
|
allowBranchMismatch: input.allowBranchMismatch ?? false,
|
|
1897
1971
|
idempotencyKey: input.idempotencyKey,
|
|
@@ -1899,9 +1973,22 @@ function registerCollabTools(server, context) {
|
|
|
1899
1973
|
});
|
|
1900
1974
|
}
|
|
1901
1975
|
});
|
|
1976
|
+
registerTool(server, context, {
|
|
1977
|
+
name: "remix_collab_drain_finalize_queue",
|
|
1978
|
+
description: "Drain the local finalize queue and record queued finalize_turn jobs immediately. Use this before request-merge, reconcile, or other flows that require the remote change step to exist already.",
|
|
1979
|
+
access: "local_write",
|
|
1980
|
+
inputSchema: drainFinalizeQueueInputSchema,
|
|
1981
|
+
outputSchema: drainFinalizeQueueSuccessSchema,
|
|
1982
|
+
annotations: getAnnotations("local_write", { idempotent: true }),
|
|
1983
|
+
run: async (args) => {
|
|
1984
|
+
const input = z3.object(drainFinalizeQueueInputSchema).parse(args);
|
|
1985
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
1986
|
+
return drainFinalizeQueue({ cwd });
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1902
1989
|
registerTool(server, context, {
|
|
1903
1990
|
name: "remix_collab_sync_preview",
|
|
1904
|
-
description: "Preview whether the current bound repository can
|
|
1991
|
+
description: "Preview whether the current bound repository can pull the server delta into the working tree. Use this instead of raw git pull or rebase for bound-repo alignment.",
|
|
1905
1992
|
access: "read",
|
|
1906
1993
|
inputSchema: previewInputSchema,
|
|
1907
1994
|
outputSchema: syncSuccessSchema,
|
|
@@ -1913,7 +2000,7 @@ function registerCollabTools(server, context) {
|
|
|
1913
2000
|
});
|
|
1914
2001
|
registerTool(server, context, {
|
|
1915
2002
|
name: "remix_collab_sync_apply",
|
|
1916
|
-
description: "
|
|
2003
|
+
description: "Pull the server delta into the local working tree without moving local git history. This is the Remix-native replacement for raw git pull or rebase in a bound repo.",
|
|
1917
2004
|
access: "local_write",
|
|
1918
2005
|
inputSchema: applyInputSchema,
|
|
1919
2006
|
outputSchema: syncSuccessSchema,
|
|
@@ -1924,6 +2011,31 @@ function registerCollabTools(server, context) {
|
|
|
1924
2011
|
return syncCollab({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
|
|
1925
2012
|
}
|
|
1926
2013
|
});
|
|
2014
|
+
registerTool(server, context, {
|
|
2015
|
+
name: "remix_collab_re_anchor_preview",
|
|
2016
|
+
description: "Preview whether the current checkout needs an explicit re-anchor to adopt or recover external Git/GitHub history.",
|
|
2017
|
+
access: "read",
|
|
2018
|
+
inputSchema: previewInputSchema,
|
|
2019
|
+
outputSchema: reAnchorSuccessSchema,
|
|
2020
|
+
run: async (args) => {
|
|
2021
|
+
const input = z3.object(previewInputSchema).parse(args);
|
|
2022
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2023
|
+
return reAnchor({ cwd, dryRun: true });
|
|
2024
|
+
}
|
|
2025
|
+
});
|
|
2026
|
+
registerTool(server, context, {
|
|
2027
|
+
name: "remix_collab_re_anchor_apply",
|
|
2028
|
+
description: "Explicitly re-anchor the current lane to adopted or recovered external Git/GitHub history, without rewriting the local checkout afterward.",
|
|
2029
|
+
access: "local_write",
|
|
2030
|
+
inputSchema: reAnchorInputSchema,
|
|
2031
|
+
outputSchema: reAnchorSuccessSchema,
|
|
2032
|
+
run: async (args) => {
|
|
2033
|
+
const input = z3.object(reAnchorInputSchema).parse(args);
|
|
2034
|
+
assertConfirm(input.confirm, "remix_collab_re_anchor_apply");
|
|
2035
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2036
|
+
return reAnchor({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
|
|
2037
|
+
}
|
|
2038
|
+
});
|
|
1927
2039
|
registerTool(server, context, {
|
|
1928
2040
|
name: "remix_collab_request_merge",
|
|
1929
2041
|
description: "Open a prompt-backed Remix merge request from the current bound repository to its upstream app instead of merging locally with raw git.",
|
|
@@ -2064,7 +2176,7 @@ function registerCollabTools(server, context) {
|
|
|
2064
2176
|
});
|
|
2065
2177
|
registerTool(server, context, {
|
|
2066
2178
|
name: "remix_collab_reconcile_preview",
|
|
2067
|
-
description: "Preview
|
|
2179
|
+
description: "Preview the explicit Remix recovery flow when local and server state diverged beyond a simple pull.",
|
|
2068
2180
|
access: "read",
|
|
2069
2181
|
inputSchema: previewInputSchema,
|
|
2070
2182
|
outputSchema: reconcileSuccessSchema,
|
|
@@ -2076,7 +2188,7 @@ function registerCollabTools(server, context) {
|
|
|
2076
2188
|
});
|
|
2077
2189
|
registerTool(server, context, {
|
|
2078
2190
|
name: "remix_collab_reconcile_apply",
|
|
2079
|
-
description: "
|
|
2191
|
+
description: "Run the explicit Remix recovery flow for diverged local/server state.",
|
|
2080
2192
|
access: "local_write",
|
|
2081
2193
|
inputSchema: applyInputSchema,
|
|
2082
2194
|
outputSchema: reconcileSuccessSchema,
|
|
@@ -3201,6 +3313,19 @@ import { z as z9 } from "zod";
|
|
|
3201
3313
|
// src/contracts/ops.ts
|
|
3202
3314
|
import { z as z8 } from "zod";
|
|
3203
3315
|
var genericRecordSchema4 = z8.record(z8.string(), z8.unknown());
|
|
3316
|
+
var appJobKindSchema = z8.enum([
|
|
3317
|
+
"fork",
|
|
3318
|
+
"edit",
|
|
3319
|
+
"bundle",
|
|
3320
|
+
"import_github",
|
|
3321
|
+
"import_upload",
|
|
3322
|
+
"merge",
|
|
3323
|
+
"revert",
|
|
3324
|
+
"change_step",
|
|
3325
|
+
"reconcile",
|
|
3326
|
+
"change_step_replay"
|
|
3327
|
+
]);
|
|
3328
|
+
var appJobStatusSchema = z8.enum(["pending", "enqueued", "processing", "succeeded", "failed", "cancelled"]);
|
|
3204
3329
|
var appScopedInputSchema = {
|
|
3205
3330
|
...commonRequestFieldsSchema,
|
|
3206
3331
|
appId: z8.string().trim().min(1).optional()
|
|
@@ -3210,6 +3335,13 @@ var editQueueInputSchema = {
|
|
|
3210
3335
|
limit: z8.number().int().positive().max(100).optional(),
|
|
3211
3336
|
offset: z8.number().int().nonnegative().optional()
|
|
3212
3337
|
};
|
|
3338
|
+
var appJobQueueInputSchema = {
|
|
3339
|
+
...appScopedInputSchema,
|
|
3340
|
+
limit: z8.number().int().positive().max(100).optional(),
|
|
3341
|
+
offset: z8.number().int().nonnegative().optional(),
|
|
3342
|
+
kind: z8.array(appJobKindSchema).min(1).optional(),
|
|
3343
|
+
status: z8.array(appJobStatusSchema).min(1).optional()
|
|
3344
|
+
};
|
|
3213
3345
|
var bundleInputSchema = {
|
|
3214
3346
|
...appScopedInputSchema,
|
|
3215
3347
|
bundleId: z8.string().trim().min(1)
|
|
@@ -3242,6 +3374,7 @@ var agentRunEventsInputSchema = {
|
|
|
3242
3374
|
};
|
|
3243
3375
|
var appOverviewSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3244
3376
|
var editQueueSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3377
|
+
var appJobQueueSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3245
3378
|
var bundleSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3246
3379
|
var timelineSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3247
3380
|
var agentRunsSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
@@ -3275,7 +3408,7 @@ async function getAppOverview(params) {
|
|
|
3275
3408
|
data,
|
|
3276
3409
|
warnings: [],
|
|
3277
3410
|
recommendedNextActions: [
|
|
3278
|
-
"Use `remix_ops_list_timeline`, `remix_ops_list_agent_runs`, `remix_ops_get_edit_queue`, or `remix_ops_get_sandbox_status` for the next operational drill-down on this app."
|
|
3411
|
+
"Use `remix_ops_list_timeline`, `remix_ops_list_agent_runs`, `remix_ops_get_edit_queue`, `remix_ops_list_app_job_queue`, or `remix_ops_get_sandbox_status` for the next operational drill-down on this app."
|
|
3279
3412
|
],
|
|
3280
3413
|
logContext: target
|
|
3281
3414
|
};
|
|
@@ -3298,6 +3431,42 @@ async function getEditQueue(params) {
|
|
|
3298
3431
|
logContext: target
|
|
3299
3432
|
};
|
|
3300
3433
|
}
|
|
3434
|
+
async function listAppJobQueue(params) {
|
|
3435
|
+
const api = await createApiClient();
|
|
3436
|
+
const target = await resolveAppTarget(api, params);
|
|
3437
|
+
const data = unwrapResponseObject(
|
|
3438
|
+
await api.listAppJobQueue(target.appId, {
|
|
3439
|
+
limit: params.limit,
|
|
3440
|
+
offset: params.offset,
|
|
3441
|
+
kind: params.kind,
|
|
3442
|
+
status: params.status
|
|
3443
|
+
}),
|
|
3444
|
+
"app job queue"
|
|
3445
|
+
);
|
|
3446
|
+
const pageInfo = typeof data.pageInfo === "object" && data.pageInfo ? data.pageInfo : null;
|
|
3447
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
3448
|
+
const activeBlockingKinds = /* @__PURE__ */ new Set(["merge", "change_step", "change_step_replay", "reconcile", "revert"]);
|
|
3449
|
+
const hasBlockingJobs = items.some((item) => {
|
|
3450
|
+
if (!item || typeof item !== "object") return false;
|
|
3451
|
+
const record = item;
|
|
3452
|
+
return typeof record.kind === "string" && activeBlockingKinds.has(record.kind) && (typeof record.status !== "string" || ["pending", "enqueued", "processing"].includes(record.status));
|
|
3453
|
+
});
|
|
3454
|
+
const recommendedNextActions = [];
|
|
3455
|
+
if (hasBlockingJobs) {
|
|
3456
|
+
recommendedNextActions.push(
|
|
3457
|
+
"Inspect the returned active workflow jobs before concluding that merge, replay, reconcile, or revert work is stuck. This surface reflects backend app-scoped queue state, not local finalize state."
|
|
3458
|
+
);
|
|
3459
|
+
}
|
|
3460
|
+
if (pageInfo?.hasMore === true && typeof pageInfo.limit === "number" && typeof pageInfo.offset === "number") {
|
|
3461
|
+
recommendedNextActions.push(`Pass offset=${pageInfo.offset + pageInfo.limit} to load the next app job queue page.`);
|
|
3462
|
+
}
|
|
3463
|
+
return {
|
|
3464
|
+
data,
|
|
3465
|
+
warnings: [],
|
|
3466
|
+
recommendedNextActions,
|
|
3467
|
+
logContext: target
|
|
3468
|
+
};
|
|
3469
|
+
}
|
|
3301
3470
|
async function getBundle(params) {
|
|
3302
3471
|
const api = await createApiClient();
|
|
3303
3472
|
const target = await resolveAppTarget(api, params);
|
|
@@ -3524,6 +3693,25 @@ function registerOpsTools(server, context) {
|
|
|
3524
3693
|
});
|
|
3525
3694
|
}
|
|
3526
3695
|
});
|
|
3696
|
+
registerTool4(server, context, {
|
|
3697
|
+
name: "remix_ops_list_app_job_queue",
|
|
3698
|
+
description: "Inspect bounded app-scoped workflow queue jobs for one app, including active backend work such as merge, replay, reconcile, revert, bundle, or import processing.",
|
|
3699
|
+
access: "read",
|
|
3700
|
+
inputSchema: appJobQueueInputSchema,
|
|
3701
|
+
outputSchema: appJobQueueSuccessSchema,
|
|
3702
|
+
run: async (args) => {
|
|
3703
|
+
const input = z9.object(appJobQueueInputSchema).parse(args);
|
|
3704
|
+
const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
|
|
3705
|
+
return listAppJobQueue({
|
|
3706
|
+
appId: input.appId,
|
|
3707
|
+
cwd,
|
|
3708
|
+
limit: input.limit,
|
|
3709
|
+
offset: input.offset,
|
|
3710
|
+
kind: input.kind,
|
|
3711
|
+
status: input.status
|
|
3712
|
+
});
|
|
3713
|
+
}
|
|
3714
|
+
});
|
|
3527
3715
|
registerTool4(server, context, {
|
|
3528
3716
|
name: "remix_ops_get_bundle",
|
|
3529
3717
|
description: "Inspect one app bundle by id without downloading the artifact payload.",
|
|
@@ -3654,6 +3842,11 @@ async function startStdioServer(params) {
|
|
|
3654
3842
|
var require2 = createRequire(import.meta.url);
|
|
3655
3843
|
var { version } = require2("../package.json");
|
|
3656
3844
|
async function main() {
|
|
3845
|
+
if (process.argv.includes("--drain-finalize-queue")) {
|
|
3846
|
+
const api = await createCollabApiClient();
|
|
3847
|
+
await drainPendingFinalizeQueue({ api });
|
|
3848
|
+
return;
|
|
3849
|
+
}
|
|
3657
3850
|
await startStdioServer({ version });
|
|
3658
3851
|
}
|
|
3659
3852
|
main().catch((error) => {
|