@remixhq/mcp 0.1.13 → 0.1.14
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 +133 -24
- package/dist/cli.js.map +1 -1
- package/dist/index.js +127 -23
- package/dist/index.js.map +1 -1
- package/dist/server.js +127 -23
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/server.js
CHANGED
|
@@ -619,7 +619,7 @@ var statusDataSchema = z2.object({
|
|
|
619
619
|
status: genericRecordSchema,
|
|
620
620
|
riskLevel: z2.enum(["low", "medium", "high"])
|
|
621
621
|
});
|
|
622
|
-
var
|
|
622
|
+
var initSyncDataSchema = z2.object({
|
|
623
623
|
reused: z2.boolean(),
|
|
624
624
|
projectId: z2.string(),
|
|
625
625
|
appId: z2.string(),
|
|
@@ -631,6 +631,23 @@ var initDataSchema = z2.object({
|
|
|
631
631
|
createdCanonicalFamily: z2.boolean().optional(),
|
|
632
632
|
baselineStatus: z2.enum(["seeded", "existing", "requires_re_anchor", "requires_sync"]).optional()
|
|
633
633
|
});
|
|
634
|
+
var initQueuedDataSchema = z2.object({
|
|
635
|
+
queued: z2.literal(true),
|
|
636
|
+
status: z2.literal("ready_to_record"),
|
|
637
|
+
doNotRetry: z2.literal(true),
|
|
638
|
+
jobId: z2.string(),
|
|
639
|
+
projectId: z2.string(),
|
|
640
|
+
appId: z2.string(),
|
|
641
|
+
dashboardUrl: z2.string(),
|
|
642
|
+
upstreamAppId: z2.string(),
|
|
643
|
+
bindingPath: z2.string(),
|
|
644
|
+
repoRoot: z2.string(),
|
|
645
|
+
bindingMode: z2.enum(["legacy", "lane", "explicit_root"]),
|
|
646
|
+
createdCanonicalFamily: z2.boolean(),
|
|
647
|
+
remoteUrl: z2.string().nullable(),
|
|
648
|
+
defaultBranch: z2.string().nullable()
|
|
649
|
+
});
|
|
650
|
+
var initDataSchema = initSyncDataSchema;
|
|
634
651
|
var listDataSchema = z2.object({
|
|
635
652
|
apps: genericArraySchema,
|
|
636
653
|
pagination: paginationSchema
|
|
@@ -810,9 +827,15 @@ function getRecommendedNextActions(status) {
|
|
|
810
827
|
case "pull":
|
|
811
828
|
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."];
|
|
812
829
|
case "re_anchor":
|
|
813
|
-
return [
|
|
830
|
+
return [
|
|
831
|
+
"Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply. This seeds a local Remix baseline. It is required because no local baseline exists for this lane yet (fresh clone, deleted .remix/ state, or first init didn't seed) \u2014 not because of any specific git operation. After it succeeds, normal recording (remix_collab_finalize_turn) becomes available."
|
|
832
|
+
];
|
|
833
|
+
case "record":
|
|
834
|
+
return [
|
|
835
|
+
"Run remix_collab_finalize_turn to capture the local boundary delta. This is the catch-all for any local content change since the last recorded turn, regardless of whether the change came from agent edits, manual user edits, git commit, git pull, git merge, git rebase, or git reset."
|
|
836
|
+
];
|
|
814
837
|
case "reconcile":
|
|
815
|
-
return ["Run remix_collab_reconcile_preview before attempting remix_collab_reconcile_apply. Reconcile
|
|
838
|
+
return ["Run remix_collab_reconcile_preview before attempting remix_collab_reconcile_apply. Reconcile applies only when both the local workspace and the server lane changed since the last agreed baseline."];
|
|
816
839
|
case "await_finalize":
|
|
817
840
|
return [
|
|
818
841
|
"Run remix_collab_drain_finalize_queue before merge-related or recovery flows. finalize_turn is queued only until the local finalize queue is drained."
|
|
@@ -861,6 +884,10 @@ function spawnFinalizeQueueDrainer() {
|
|
|
861
884
|
child.unref();
|
|
862
885
|
return true;
|
|
863
886
|
}
|
|
887
|
+
async function drainBeforeMutation(api) {
|
|
888
|
+
const results = await coreDrainPendingFinalizeQueue({ api });
|
|
889
|
+
return results.flatMap((result) => collectResultWarnings(result));
|
|
890
|
+
}
|
|
864
891
|
async function getStatus(params) {
|
|
865
892
|
const api = params.includeRemote ? await createCollabApiClient() : null;
|
|
866
893
|
const status = await coreCollabStatus({
|
|
@@ -886,19 +913,26 @@ async function initCollab(params) {
|
|
|
886
913
|
api,
|
|
887
914
|
cwd: params.cwd,
|
|
888
915
|
appName: params.appName ?? null,
|
|
889
|
-
forceNew: params.forceNew ?? false
|
|
916
|
+
forceNew: params.forceNew ?? false,
|
|
917
|
+
asyncSubmit: false
|
|
890
918
|
});
|
|
919
|
+
if ("queued" in result && result.queued) {
|
|
920
|
+
throw new Error(
|
|
921
|
+
"Unexpected queued init result on the MCP path. Async init is currently disabled for agent callers; if you intended to re-enable it, also restore the queued shape in initDataSchema (see remix-mcp/src/contracts/collab.ts)."
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
const syncResult = result;
|
|
891
925
|
return {
|
|
892
|
-
data:
|
|
926
|
+
data: syncResult,
|
|
893
927
|
warnings: collectResultWarnings(result),
|
|
894
|
-
recommendedNextActions:
|
|
895
|
-
"Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply to
|
|
896
|
-
] :
|
|
928
|
+
recommendedNextActions: syncResult.baselineStatus === "requires_re_anchor" ? [
|
|
929
|
+
"This checkout has no local Remix baseline yet. Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply to seed one. After it succeeds, normal recording (remix_collab_finalize_turn) becomes available."
|
|
930
|
+
] : syncResult.baselineStatus === "requires_sync" ? [
|
|
897
931
|
"Run remix_collab_sync_preview, then remix_collab_sync_apply to pull the server delta and create the first local baseline for this checkout."
|
|
898
932
|
] : ["Run remix_collab_status to inspect sync, reconcile, and merge-request readiness before mutating bound-repo state."],
|
|
899
933
|
logContext: {
|
|
900
|
-
repoRoot:
|
|
901
|
-
appId:
|
|
934
|
+
repoRoot: syncResult.repoRoot,
|
|
935
|
+
appId: syncResult.appId
|
|
902
936
|
}
|
|
903
937
|
};
|
|
904
938
|
}
|
|
@@ -968,9 +1002,11 @@ async function finalizeCollabTurn(params) {
|
|
|
968
1002
|
sync: params.sync,
|
|
969
1003
|
allowBranchMismatch: params.allowBranchMismatch ?? false,
|
|
970
1004
|
idempotencyKey: params.idempotencyKey ?? null,
|
|
971
|
-
actor: params.agent
|
|
1005
|
+
actor: params.agent,
|
|
1006
|
+
awaitingUsageDeadlineMs: params.awaitingUsageDeadlineMs ?? null
|
|
972
1007
|
});
|
|
973
|
-
|
|
1008
|
+
const hasAwaitingDeadline = typeof params.awaitingUsageDeadlineMs === "number" && params.awaitingUsageDeadlineMs > 0;
|
|
1009
|
+
if (result.queued && !hasAwaitingDeadline) {
|
|
974
1010
|
if (!spawnFinalizeQueueDrainer()) {
|
|
975
1011
|
await coreDrainPendingFinalizeQueue({ api });
|
|
976
1012
|
}
|
|
@@ -1034,7 +1070,9 @@ async function reAnchor(params) {
|
|
|
1034
1070
|
return {
|
|
1035
1071
|
data: result,
|
|
1036
1072
|
warnings: collectWarnings(result.warnings),
|
|
1037
|
-
recommendedNextActions: params.dryRun ? [
|
|
1073
|
+
recommendedNextActions: params.dryRun ? [
|
|
1074
|
+
"Run remix_collab_re_anchor_apply with confirm=true to seed a local Remix baseline for this checkout. Re-anchor is for missing-baseline cases only and does not replace remix_collab_finalize_turn for ordinary local content changes."
|
|
1075
|
+
] : [],
|
|
1038
1076
|
logContext: {
|
|
1039
1077
|
repoRoot: result.repoRoot,
|
|
1040
1078
|
appId: result.currentAppId
|
|
@@ -1043,13 +1081,14 @@ async function reAnchor(params) {
|
|
|
1043
1081
|
}
|
|
1044
1082
|
async function requestMerge(params) {
|
|
1045
1083
|
const api = await createCollabApiClient();
|
|
1084
|
+
const drainWarnings = await drainBeforeMutation(api);
|
|
1046
1085
|
const result = await coreCollabRequestMerge({
|
|
1047
1086
|
api,
|
|
1048
1087
|
cwd: params.cwd
|
|
1049
1088
|
});
|
|
1050
1089
|
return {
|
|
1051
1090
|
data: result,
|
|
1052
|
-
warnings:
|
|
1091
|
+
warnings: drainWarnings,
|
|
1053
1092
|
recommendedNextActions: result.id ? [`Run remix_collab_view_merge_request with mrId=${String(result.id)} to inspect the request before deciding whether to approve or reject it.`] : [],
|
|
1054
1093
|
logContext: {
|
|
1055
1094
|
mrId: typeof result.id === "string" ? result.id : null
|
|
@@ -1205,6 +1244,7 @@ async function syncUpstream(params) {
|
|
|
1205
1244
|
}
|
|
1206
1245
|
async function reconcile(params) {
|
|
1207
1246
|
const api = await createCollabApiClient();
|
|
1247
|
+
const drainWarnings = params.dryRun ? [] : await drainBeforeMutation(api);
|
|
1208
1248
|
const result = await coreCollabReconcile({
|
|
1209
1249
|
api,
|
|
1210
1250
|
cwd: params.cwd,
|
|
@@ -1213,9 +1253,9 @@ async function reconcile(params) {
|
|
|
1213
1253
|
});
|
|
1214
1254
|
return {
|
|
1215
1255
|
data: result,
|
|
1216
|
-
warnings: collectWarnings(result.warnings),
|
|
1256
|
+
warnings: [...drainWarnings, ...collectWarnings(result.warnings)],
|
|
1217
1257
|
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."] : [],
|
|
1218
|
-
risks: params.dryRun ? ["Reconcile may upload local history to
|
|
1258
|
+
risks: params.dryRun ? ["Reconcile may upload local history to recover the server lane onto the latest agreed state before future recording continues."] : [],
|
|
1219
1259
|
logContext: {
|
|
1220
1260
|
repoRoot: result.repoRoot ?? null
|
|
1221
1261
|
}
|
|
@@ -1762,6 +1802,50 @@ async function accessDebug(params) {
|
|
|
1762
1802
|
};
|
|
1763
1803
|
}
|
|
1764
1804
|
|
|
1805
|
+
// src/tools/collab/autoSpawnHistoryImport.ts
|
|
1806
|
+
import { spawn as spawn2 } from "child_process";
|
|
1807
|
+
import { existsSync, mkdirSync, openSync } from "fs";
|
|
1808
|
+
import path2 from "path";
|
|
1809
|
+
var MARKER_REL_PATH = path2.join(".remix", ".history-imported");
|
|
1810
|
+
var LOG_REL_PATH = path2.join(".remix", "history-import.log");
|
|
1811
|
+
function shouldAutoSpawnHistoryImport(repoRoot) {
|
|
1812
|
+
try {
|
|
1813
|
+
return !existsSync(path2.join(repoRoot, MARKER_REL_PATH));
|
|
1814
|
+
} catch {
|
|
1815
|
+
return false;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
function spawnHistoryImportDetached(repoRoot) {
|
|
1819
|
+
const remixDir = path2.join(repoRoot, ".remix");
|
|
1820
|
+
try {
|
|
1821
|
+
mkdirSync(remixDir, { recursive: true });
|
|
1822
|
+
} catch {
|
|
1823
|
+
}
|
|
1824
|
+
const logPath = path2.join(repoRoot, LOG_REL_PATH);
|
|
1825
|
+
const out = openSync(logPath, "a");
|
|
1826
|
+
const err = openSync(logPath, "a");
|
|
1827
|
+
const child = spawn2(
|
|
1828
|
+
"remix",
|
|
1829
|
+
[
|
|
1830
|
+
"history",
|
|
1831
|
+
"import",
|
|
1832
|
+
"--repo",
|
|
1833
|
+
repoRoot,
|
|
1834
|
+
// Include prompt text for parity with the CLI auto-spawn path:
|
|
1835
|
+
// first-time UX is a lot worse if the dashboard renders every
|
|
1836
|
+
// historical row as "(prompt not uploaded)".
|
|
1837
|
+
"--include-prompt-text"
|
|
1838
|
+
],
|
|
1839
|
+
{
|
|
1840
|
+
detached: true,
|
|
1841
|
+
stdio: ["ignore", out, err],
|
|
1842
|
+
env: { ...process.env, REMIX_HISTORY_AUTO_SPAWN: "1" }
|
|
1843
|
+
}
|
|
1844
|
+
);
|
|
1845
|
+
child.unref();
|
|
1846
|
+
return { pid: child.pid, logPath };
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1765
1849
|
// src/tools/collab/register.ts
|
|
1766
1850
|
function getAnnotations(access, options) {
|
|
1767
1851
|
if (access === "read") {
|
|
@@ -1883,18 +1967,37 @@ function registerCollabTools(server, context) {
|
|
|
1883
1967
|
});
|
|
1884
1968
|
registerTool(server, context, {
|
|
1885
1969
|
name: "remix_collab_init",
|
|
1886
|
-
description: "Import the current repository into Remix and write the local binding file.",
|
|
1970
|
+
description: "Import the current repository into Remix and write the local binding file. Synchronous: by the time this tool resolves, the local binding file AND the local Remix baseline are both on disk, so the very next call to remix_collab_finalize_turn will succeed. Brand-new init on the default branch typically takes ~10s; non-default-branch init can take 30-90s while the server provisions a feature lane. The result includes `reused: boolean` (false for a brand-new app, true if a binding already existed) plus the canonical app/project identifiers and the dashboard URL. Use forceNew=true only when intentionally creating a new canonical family from scratch in a previously-bound repo; do NOT use forceNew as a retry mechanism for a failed init \u2014 it creates orphan backend apps and triggers canonical-family ambiguity errors on subsequent inits in this directory.",
|
|
1887
1971
|
access: "remote_write",
|
|
1888
1972
|
inputSchema: initInputSchema,
|
|
1889
1973
|
outputSchema: initSuccessSchema,
|
|
1890
1974
|
run: async (args) => {
|
|
1891
1975
|
const input = z3.object(initInputSchema).parse(args);
|
|
1892
1976
|
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
1893
|
-
|
|
1977
|
+
const result = await initCollab({
|
|
1894
1978
|
cwd,
|
|
1895
1979
|
appName: input.appName,
|
|
1896
1980
|
forceNew: input.forceNew
|
|
1897
1981
|
});
|
|
1982
|
+
try {
|
|
1983
|
+
const repoRoot = result && typeof result === "object" && "data" in result && result.data && typeof result.data.repoRoot === "string" ? result.data.repoRoot : null;
|
|
1984
|
+
if (repoRoot && shouldAutoSpawnHistoryImport(repoRoot)) {
|
|
1985
|
+
const spawned = spawnHistoryImportDetached(repoRoot);
|
|
1986
|
+
context.logger.log({
|
|
1987
|
+
level: "info",
|
|
1988
|
+
message: `history_import_auto_spawned pid=${spawned.pid ?? "?"} log=${spawned.logPath}`,
|
|
1989
|
+
tool: "remix_collab_init",
|
|
1990
|
+
repoRoot
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
} catch (spawnError) {
|
|
1994
|
+
context.logger.log({
|
|
1995
|
+
level: "error",
|
|
1996
|
+
message: `history_import_auto_spawn_failed: ${spawnError instanceof Error ? spawnError.message : String(spawnError)}`,
|
|
1997
|
+
tool: "remix_collab_init"
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
return result;
|
|
1898
2001
|
}
|
|
1899
2002
|
});
|
|
1900
2003
|
registerTool(server, context, {
|
|
@@ -1950,7 +2053,7 @@ function registerCollabTools(server, context) {
|
|
|
1950
2053
|
});
|
|
1951
2054
|
registerTool(server, context, {
|
|
1952
2055
|
name: "remix_collab_finalize_turn",
|
|
1953
|
-
description: "Primary turn recorder for the current bound repository.
|
|
2056
|
+
description: "Primary turn recorder for the current bound repository. Required: call this exactly once before the final response on every turn that touched a Remix-bound repo. Captures the current boundary locally and queues remote processing. Queued only: no remote change step exists yet until the finalize queue drains. Runtime exception: if this turn's context contains a [Remix runtime status] system note from the Remix UserPromptSubmit hook stating that the Stop hook will record this turn automatically, do NOT call this tool \u2014 the runtime is recording it on your behalf and a manual call would create a duplicate record. The runtime note is the only sanctioned override; trivial prompts, error states, and ambiguity all still require this call.",
|
|
1954
2057
|
access: "local_write",
|
|
1955
2058
|
inputSchema: finalizeTurnInputSchema,
|
|
1956
2059
|
outputSchema: finalizeTurnSuccessSchema,
|
|
@@ -1965,13 +2068,14 @@ function registerCollabTools(server, context) {
|
|
|
1965
2068
|
sync: input.sync,
|
|
1966
2069
|
allowBranchMismatch: input.allowBranchMismatch ?? false,
|
|
1967
2070
|
idempotencyKey: input.idempotencyKey,
|
|
1968
|
-
agent: context.agentMetadata
|
|
2071
|
+
agent: context.agentMetadata,
|
|
2072
|
+
awaitingUsageDeadlineMs: 3e4
|
|
1969
2073
|
});
|
|
1970
2074
|
}
|
|
1971
2075
|
});
|
|
1972
2076
|
registerTool(server, context, {
|
|
1973
2077
|
name: "remix_collab_drain_finalize_queue",
|
|
1974
|
-
description: "Drain the local finalize queue and record queued finalize_turn jobs immediately.
|
|
2078
|
+
description: "Drain the local finalize queue and record queued finalize_turn jobs immediately. NOT required as a precondition for `remix_collab_request_merge` or `remix_collab_reconcile_apply` \u2014 those tools drain the queue internally before they run. Useful only for explicit recovery flows (e.g. status reports `await_finalize` and you want to flush before re-checking). Runtime exception: if this turn's context contains a [Remix runtime status] system note from the Remix UserPromptSubmit hook, the runtime drains the queue automatically in the background; do NOT call this tool unless an explicit recovery flow requires it.",
|
|
1975
2079
|
access: "local_write",
|
|
1976
2080
|
inputSchema: drainFinalizeQueueInputSchema,
|
|
1977
2081
|
outputSchema: drainFinalizeQueueSuccessSchema,
|
|
@@ -2009,7 +2113,7 @@ function registerCollabTools(server, context) {
|
|
|
2009
2113
|
});
|
|
2010
2114
|
registerTool(server, context, {
|
|
2011
2115
|
name: "remix_collab_re_anchor_preview",
|
|
2012
|
-
description: "Preview whether
|
|
2116
|
+
description: "Preview whether this checkout needs a fresh local Remix baseline. Use only when status reports `re_anchor` (no local baseline exists for this lane yet \u2014 fresh clone, deleted `.remix/` state, or first init didn't seed). Re-anchor does not replace `remix_collab_finalize_turn`; ordinary local content changes (including merges, pulls, and rebases) are recorded by `finalize-turn`, not by re-anchor.",
|
|
2013
2117
|
access: "read",
|
|
2014
2118
|
inputSchema: previewInputSchema,
|
|
2015
2119
|
outputSchema: reAnchorSuccessSchema,
|
|
@@ -2021,7 +2125,7 @@ function registerCollabTools(server, context) {
|
|
|
2021
2125
|
});
|
|
2022
2126
|
registerTool(server, context, {
|
|
2023
2127
|
name: "remix_collab_re_anchor_apply",
|
|
2024
|
-
description: "
|
|
2128
|
+
description: "Establish a local Remix baseline for the current checkout against the existing app head, without rewriting the local checkout afterward. Required only when status reports `re_anchor` (missing local baseline). It does not replace `remix_collab_finalize_turn` \u2014 local commits, pulls, merges, and rebases must still be recorded with `finalize-turn`.",
|
|
2025
2129
|
access: "local_write",
|
|
2026
2130
|
inputSchema: reAnchorInputSchema,
|
|
2027
2131
|
outputSchema: reAnchorSuccessSchema,
|