@remixhq/mcp 0.1.17 → 0.1.19

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 CHANGED
@@ -255,6 +255,15 @@ function normalizeByMessage(err) {
255
255
  category: "remote_state"
256
256
  });
257
257
  }
258
+ if (statusCode === 409) {
259
+ return makeNormalized({
260
+ code: ERROR_CODES.REMOTE_ERROR,
261
+ message,
262
+ hint,
263
+ retryable: true,
264
+ category: "remote_state"
265
+ });
266
+ }
258
267
  if (message.includes("Timed out") || message.includes("failed") || message.includes("error state")) {
259
268
  return makeNormalized({
260
269
  code: ERROR_CODES.REMOTE_ERROR,
@@ -431,7 +440,6 @@ function makeSuccessResult(envelope) {
431
440
  function makeErrorResult(envelope) {
432
441
  return {
433
442
  content: [{ type: "text", text: toJsonText(envelope) }],
434
- structuredContent: envelope,
435
443
  isError: true
436
444
  };
437
445
  }
@@ -510,9 +518,6 @@ var applyInputSchema = {
510
518
  confirm: z2.boolean(),
511
519
  allowBranchMismatch: z2.boolean().optional()
512
520
  };
513
- var reAnchorInputSchema = {
514
- ...applyInputSchema
515
- };
516
521
  var requestMergeInputSchema = {
517
522
  ...commonRequestFieldsSchema
518
523
  };
@@ -625,7 +630,7 @@ var initSyncDataSchema = z2.object({
625
630
  repoRoot: z2.string(),
626
631
  bindingMode: z2.enum(["legacy", "lane", "explicit_root"]).optional(),
627
632
  createdCanonicalFamily: z2.boolean().optional(),
628
- baselineStatus: z2.enum(["seeded", "existing", "requires_re_anchor", "requires_sync"]).optional()
633
+ baselineStatus: z2.enum(["seeded", "existing", "baseline_missing", "requires_sync"]).optional()
629
634
  });
630
635
  var initQueuedDataSchema = z2.object({
631
636
  queued: z2.literal(true),
@@ -673,7 +678,6 @@ var drainFinalizeQueueDataSchema = z2.object({
673
678
  results: z2.array(genericRecordSchema)
674
679
  });
675
680
  var syncDataSchema = genericRecordSchema;
676
- var reAnchorDataSchema = genericRecordSchema;
677
681
  var requestMergeDataSchema = genericRecordSchema;
678
682
  var mergeRequestQueueDataSchema = z2.object({
679
683
  queue: mergeRequestQueueSchema,
@@ -749,7 +753,6 @@ var addSuccessSchema = makeSuccessSchema(addDataSchema);
749
753
  var recordTurnSuccessSchema = makeSuccessSchema(recordTurnDataSchema);
750
754
  var drainFinalizeQueueSuccessSchema = makeSuccessSchema(drainFinalizeQueueDataSchema);
751
755
  var syncSuccessSchema = makeSuccessSchema(syncDataSchema);
752
- var reAnchorSuccessSchema = makeSuccessSchema(reAnchorDataSchema);
753
756
  var requestMergeSuccessSchema = makeSuccessSchema(requestMergeDataSchema);
754
757
  var mergeRequestQueueSuccessSchema = makeSuccessSchema(mergeRequestQueueDataSchema);
755
758
  var viewMergeRequestSuccessSchema = makeSuccessSchema(viewMergeRequestDataSchema);
@@ -776,7 +779,6 @@ import {
776
779
  collabCheckout as coreCollabCheckout,
777
780
  collabListMergeRequests as coreCollabListMergeRequests,
778
781
  collabInit as coreCollabInit,
779
- collabReAnchor as coreCollabReAnchor,
780
782
  collabInvite as coreCollabInvite,
781
783
  collabReconcile as coreCollabReconcile,
782
784
  collabReject as coreCollabReject,
@@ -791,7 +793,7 @@ import { findGitRoot } from "@remixhq/core/repo";
791
793
  function getRiskLevel(status) {
792
794
  if (status.recommendedAction === "reconcile") return "high";
793
795
  if (status.recommendedAction === "choose_family" || status.recommendedAction === "await_finalize") return "medium";
794
- if (status.recommendedAction === "pull" || status.recommendedAction === "re_anchor" || status.remote.incomingOpenMergeRequestCount) {
796
+ if (status.recommendedAction === "pull" || status.remote.incomingOpenMergeRequestCount) {
795
797
  return "medium";
796
798
  }
797
799
  if (status.repo.branchMismatch || !status.repo.isGitRepo || !status.binding.isBound || !status.repo.worktree.isClean) return "medium";
@@ -808,10 +810,6 @@ function getRecommendedNextActions(status) {
808
810
  return ["Run remix_collab_init to bind the repository to Remix before using any Remix collaboration mutation flow."];
809
811
  case "pull":
810
812
  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."];
811
- case "re_anchor":
812
- return [
813
- "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, automatic hook recording can capture completed turns."
814
- ];
815
813
  case "record":
816
814
  return [
817
815
  "No MCP recording tool is required. Automatic hook finalization will capture the local boundary at the end of the completed turn; this covers agent edits, manual user edits, git commit, git pull, git merge, git rebase, and git reset."
@@ -896,8 +894,8 @@ async function initCollab(params) {
896
894
  return {
897
895
  data: syncResult,
898
896
  warnings: collectResultWarnings(result),
899
- recommendedNextActions: syncResult.baselineStatus === "requires_re_anchor" ? [
900
- "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, automatic hook recording can capture completed turns."
897
+ recommendedNextActions: syncResult.baselineStatus === "baseline_missing" ? [
898
+ "This checkout has no local Remix revision baseline yet. Run remix_collab_init or remix_collab_sync_preview/apply to seed one. After it succeeds, automatic hook recording can capture completed turns."
901
899
  ] : syncResult.baselineStatus === "requires_sync" ? [
902
900
  "Run remix_collab_sync_preview, then remix_collab_sync_apply to pull the server delta and create the first local baseline for this checkout."
903
901
  ] : ["Run remix_collab_status to inspect sync, reconcile, and merge-request readiness before mutating bound-repo state."],
@@ -1000,26 +998,6 @@ async function syncCollab(params) {
1000
998
  }
1001
999
  };
1002
1000
  }
1003
- async function reAnchor(params) {
1004
- const api = await createCollabApiClient();
1005
- const result = await coreCollabReAnchor({
1006
- api,
1007
- cwd: params.cwd,
1008
- dryRun: params.dryRun,
1009
- allowBranchMismatch: params.allowBranchMismatch ?? false
1010
- });
1011
- return {
1012
- data: result,
1013
- warnings: collectWarnings(result.warnings),
1014
- recommendedNextActions: params.dryRun ? [
1015
- "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 automatic hook recording for ordinary local content changes."
1016
- ] : [],
1017
- logContext: {
1018
- repoRoot: result.repoRoot,
1019
- appId: result.currentAppId
1020
- }
1021
- };
1022
- }
1023
1001
  async function requestMerge(params) {
1024
1002
  const api = await createCollabApiClient();
1025
1003
  const drainWarnings = await drainBeforeMutation(api);
@@ -1756,7 +1734,10 @@ function shouldAutoSpawnHistoryImport(repoRoot) {
1756
1734
  return false;
1757
1735
  }
1758
1736
  }
1759
- function spawnHistoryImportDetached(repoRoot) {
1737
+ function isAutoSpawnEligibleBindingMode(bindingMode) {
1738
+ return bindingMode === "explicit_root";
1739
+ }
1740
+ function spawnHistoryImportDetached(repoRoot, options) {
1760
1741
  const remixDir = path2.join(repoRoot, ".remix");
1761
1742
  try {
1762
1743
  mkdirSync(remixDir, { recursive: true });
@@ -1772,6 +1753,8 @@ function spawnHistoryImportDetached(repoRoot) {
1772
1753
  "import",
1773
1754
  "--repo",
1774
1755
  repoRoot,
1756
+ "--before",
1757
+ options.cutoffAt,
1775
1758
  // Include prompt text for parity with the CLI auto-spawn path:
1776
1759
  // first-time UX is a lot worse if the dashboard renders every
1777
1760
  // historical row as "(prompt not uploaded)".
@@ -1841,6 +1824,24 @@ function buildErrorEnvelope(tool, requestId, error) {
1841
1824
  recommendedNextActions
1842
1825
  };
1843
1826
  }
1827
+ function buildOutputValidationErrorEnvelope(tool, requestId, error) {
1828
+ return {
1829
+ schemaVersion: SCHEMA_VERSION,
1830
+ ok: false,
1831
+ tool,
1832
+ requestId: requestId ?? null,
1833
+ error: {
1834
+ code: ERROR_CODES.INTERNAL_ERROR,
1835
+ message: "Tool output failed validation against its declared MCP schema.",
1836
+ hint: error.issues.map((issue) => `${issue.path.join(".") || "output"}: ${issue.message}`).join("; "),
1837
+ retryable: false,
1838
+ category: "internal"
1839
+ },
1840
+ warnings: [],
1841
+ risks: ["The MCP server returned an output shape that did not match the tool descriptor."],
1842
+ recommendedNextActions: ["Report this MCP schema mismatch with the tool name and error hint."]
1843
+ };
1844
+ }
1844
1845
  function registerTool(server, context, params) {
1845
1846
  const errorSchema = makeErrorSchema();
1846
1847
  server.registerTool(
@@ -1859,7 +1860,20 @@ function registerTool(server, context, params) {
1859
1860
  assertToolAccess(context.policy, params.access);
1860
1861
  const result = await params.run(rawArgs);
1861
1862
  const envelope = buildSuccessEnvelope(params.name, requestId, result);
1862
- params.outputSchema.parse(envelope);
1863
+ const parsedEnvelope = params.outputSchema.safeParse(envelope);
1864
+ if (!parsedEnvelope.success) {
1865
+ const errorEnvelope = buildOutputValidationErrorEnvelope(params.name, requestId, parsedEnvelope.error);
1866
+ context.logger.log({
1867
+ level: "error",
1868
+ message: "tool_output_validation_failed",
1869
+ tool: params.name,
1870
+ requestId: errorEnvelope.requestId,
1871
+ durationMs: Date.now() - startedAt,
1872
+ result: "error",
1873
+ errorCode: errorEnvelope.error.code
1874
+ });
1875
+ return makeErrorResult(errorEnvelope);
1876
+ }
1863
1877
  context.logger.log({
1864
1878
  level: "info",
1865
1879
  message: "tool_completed",
@@ -1922,11 +1936,13 @@ function registerCollabTools(server, context) {
1922
1936
  });
1923
1937
  try {
1924
1938
  const repoRoot = result && typeof result === "object" && "data" in result && result.data && typeof result.data.repoRoot === "string" ? result.data.repoRoot : null;
1925
- if (repoRoot && shouldAutoSpawnHistoryImport(repoRoot)) {
1926
- const spawned = spawnHistoryImportDetached(repoRoot);
1939
+ const bindingMode = result && typeof result === "object" && "data" in result && result.data && typeof result.data.bindingMode === "string" ? result.data.bindingMode : null;
1940
+ if (repoRoot && isAutoSpawnEligibleBindingMode(bindingMode) && shouldAutoSpawnHistoryImport(repoRoot)) {
1941
+ const cutoffAt = (/* @__PURE__ */ new Date()).toISOString();
1942
+ const spawned = spawnHistoryImportDetached(repoRoot, { cutoffAt });
1927
1943
  context.logger.log({
1928
1944
  level: "info",
1929
- message: `history_import_auto_spawned pid=${spawned.pid ?? "?"} log=${spawned.logPath}`,
1945
+ message: `history_import_auto_spawned pid=${spawned.pid ?? "?"} log=${spawned.logPath} cutoffAt=${cutoffAt}`,
1930
1946
  tool: "remix_collab_init",
1931
1947
  repoRoot
1932
1948
  });
@@ -2030,31 +2046,6 @@ function registerCollabTools(server, context) {
2030
2046
  return syncCollab({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
2031
2047
  }
2032
2048
  });
2033
- registerTool(server, context, {
2034
- name: "remix_collab_re_anchor_preview",
2035
- 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 automatic hook recording; ordinary local content changes (including merges, pulls, and rebases) are captured at the completed-turn boundary, not by re-anchor.",
2036
- access: "read",
2037
- inputSchema: previewInputSchema,
2038
- outputSchema: reAnchorSuccessSchema,
2039
- run: async (args) => {
2040
- const input = z3.object(previewInputSchema).parse(args);
2041
- const cwd = resolvePolicyCwd(context.policy, input.cwd);
2042
- return reAnchor({ cwd, dryRun: true });
2043
- }
2044
- });
2045
- registerTool(server, context, {
2046
- name: "remix_collab_re_anchor_apply",
2047
- 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 automatic hook recording \u2014 local commits, pulls, merges, and rebases are still captured at the completed-turn boundary.",
2048
- access: "local_write",
2049
- inputSchema: reAnchorInputSchema,
2050
- outputSchema: reAnchorSuccessSchema,
2051
- run: async (args) => {
2052
- const input = z3.object(reAnchorInputSchema).parse(args);
2053
- assertConfirm(input.confirm, "remix_collab_re_anchor_apply");
2054
- const cwd = resolvePolicyCwd(context.policy, input.cwd);
2055
- return reAnchor({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
2056
- }
2057
- });
2058
2049
  registerTool(server, context, {
2059
2050
  name: "remix_collab_request_merge",
2060
2051
  description: "Open a prompt-backed Remix merge request from the current bound repository to its upstream app instead of merging locally with raw git.",