@remixhq/claude-plugin 0.1.12 → 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.
@@ -21513,7 +21513,7 @@ var RemixError = class extends Error {
21513
21513
  }
21514
21514
  };
21515
21515
 
21516
- // node_modules/@remixhq/core/dist/chunk-4276ARDF.js
21516
+ // node_modules/@remixhq/core/dist/chunk-XC2FV57P.js
21517
21517
  async function readJsonSafe(res) {
21518
21518
  const ct = res.headers.get("content-type") ?? "";
21519
21519
  if (!ct.toLowerCase().includes("application/json")) return null;
@@ -21628,10 +21628,29 @@ function createApiClient(config2, opts) {
21628
21628
  if (params?.projectId) qs.set("projectId", params.projectId);
21629
21629
  if (params?.organizationId) qs.set("organizationId", params.organizationId);
21630
21630
  if (params?.forked) qs.set("forked", params.forked);
21631
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21632
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21631
21633
  const suffix = qs.toString() ? `?${qs.toString()}` : "";
21632
21634
  return request(`/v1/apps${suffix}`, { method: "GET" });
21633
21635
  },
21634
21636
  getApp: (appId) => request(`/v1/apps/${encodeURIComponent(appId)}`, { method: "GET" }),
21637
+ getAppContext: (appId) => request(`/v1/apps/${encodeURIComponent(appId)}/context`, { method: "GET" }),
21638
+ getAppOverview: (appId) => request(`/v1/apps/${encodeURIComponent(appId)}/overview`, { method: "GET" }),
21639
+ listAppTimeline: (appId, params) => {
21640
+ const qs = new URLSearchParams();
21641
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21642
+ if (params?.cursor) qs.set("cursor", params.cursor);
21643
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21644
+ return request(`/v1/apps/${encodeURIComponent(appId)}/timeline${suffix}`, { method: "GET" });
21645
+ },
21646
+ getAppTimelineEvent: (appId, eventId) => request(`/v1/apps/${encodeURIComponent(appId)}/timeline/${encodeURIComponent(eventId)}`, { method: "GET" }),
21647
+ listAppEditQueue: (appId, params) => {
21648
+ const qs = new URLSearchParams();
21649
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21650
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21651
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21652
+ return request(`/v1/apps/${encodeURIComponent(appId)}/edit-queue${suffix}`, { method: "GET" });
21653
+ },
21635
21654
  getMergeRequest: (mrId) => request(`/v1/merge-requests/${encodeURIComponent(mrId)}`, { method: "GET" }),
21636
21655
  presignImportUpload: (payload) => request("/v1/apps/import/upload/presign", { method: "POST", body: JSON.stringify(payload) }),
21637
21656
  importFromUpload: (payload) => request("/v1/apps/import/upload", { method: "POST", body: JSON.stringify(payload) }),
@@ -21713,6 +21732,8 @@ function createApiClient(config2, opts) {
21713
21732
  qs.set("status", params.status);
21714
21733
  }
21715
21734
  if (params?.kind) qs.set("kind", params.kind);
21735
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21736
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21716
21737
  const suffix = qs.toString() ? `?${qs.toString()}` : "";
21717
21738
  return request(`/v1/merge-requests${suffix}`, { method: "GET" });
21718
21739
  },
@@ -21731,24 +21752,60 @@ function createApiClient(config2, opts) {
21731
21752
  method: "POST",
21732
21753
  body: JSON.stringify(payload)
21733
21754
  }),
21734
- listOrganizationMembers: (orgId) => request(`/v1/organizations/${encodeURIComponent(orgId)}/members`, { method: "GET" }),
21755
+ listOrganizationMembers: (orgId, params) => {
21756
+ const qs = new URLSearchParams();
21757
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21758
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21759
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21760
+ return request(`/v1/organizations/${encodeURIComponent(orgId)}/members${suffix}`, { method: "GET" });
21761
+ },
21735
21762
  updateOrganizationMember: (orgId, userId, payload) => request(`/v1/organizations/${encodeURIComponent(orgId)}/members/${encodeURIComponent(userId)}`, {
21736
21763
  method: "PATCH",
21737
21764
  body: JSON.stringify(payload)
21738
21765
  }),
21739
- listProjectMembers: (projectId) => request(`/v1/projects/${encodeURIComponent(projectId)}/members`, { method: "GET" }),
21766
+ listProjectMembers: (projectId, params) => {
21767
+ const qs = new URLSearchParams();
21768
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21769
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21770
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21771
+ return request(`/v1/projects/${encodeURIComponent(projectId)}/members${suffix}`, { method: "GET" });
21772
+ },
21740
21773
  updateProjectMember: (projectId, userId, payload) => request(`/v1/projects/${encodeURIComponent(projectId)}/members/${encodeURIComponent(userId)}`, {
21741
21774
  method: "PATCH",
21742
21775
  body: JSON.stringify(payload)
21743
21776
  }),
21744
- listAppMembers: (appId) => request(`/v1/apps/${encodeURIComponent(appId)}/members`, { method: "GET" }),
21777
+ listAppMembers: (appId, params) => {
21778
+ const qs = new URLSearchParams();
21779
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21780
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21781
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21782
+ return request(`/v1/apps/${encodeURIComponent(appId)}/members${suffix}`, { method: "GET" });
21783
+ },
21745
21784
  updateAppMember: (appId, userId, payload) => request(`/v1/apps/${encodeURIComponent(appId)}/members/${encodeURIComponent(userId)}`, {
21746
21785
  method: "PATCH",
21747
21786
  body: JSON.stringify(payload)
21748
21787
  }),
21749
- listOrganizationInvites: (orgId) => request(`/v1/organizations/${encodeURIComponent(orgId)}/invitations`, { method: "GET" }),
21750
- listProjectInvites: (projectId) => request(`/v1/projects/${encodeURIComponent(projectId)}/invitations`, { method: "GET" }),
21751
- listAppInvites: (appId) => request(`/v1/apps/${encodeURIComponent(appId)}/invitations`, { method: "GET" }),
21788
+ listOrganizationInvites: (orgId, params) => {
21789
+ const qs = new URLSearchParams();
21790
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21791
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21792
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21793
+ return request(`/v1/organizations/${encodeURIComponent(orgId)}/invitations${suffix}`, { method: "GET" });
21794
+ },
21795
+ listProjectInvites: (projectId, params) => {
21796
+ const qs = new URLSearchParams();
21797
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21798
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21799
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21800
+ return request(`/v1/projects/${encodeURIComponent(projectId)}/invitations${suffix}`, { method: "GET" });
21801
+ },
21802
+ listAppInvites: (appId, params) => {
21803
+ const qs = new URLSearchParams();
21804
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21805
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21806
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21807
+ return request(`/v1/apps/${encodeURIComponent(appId)}/invitations${suffix}`, { method: "GET" });
21808
+ },
21752
21809
  resendOrganizationInvite: (orgId, inviteId, payload) => request(`/v1/organizations/${encodeURIComponent(orgId)}/invitations/${encodeURIComponent(inviteId)}/resend`, {
21753
21810
  method: "POST",
21754
21811
  body: JSON.stringify(payload ?? {})
@@ -21770,6 +21827,7 @@ function createApiClient(config2, opts) {
21770
21827
  revokeAppInvite: (appId, inviteId) => request(`/v1/apps/${encodeURIComponent(appId)}/invitations/${encodeURIComponent(inviteId)}`, {
21771
21828
  method: "DELETE"
21772
21829
  }),
21830
+ acceptInvitation: (payload) => request("/v1/invitations/accept", { method: "POST", body: JSON.stringify(payload) }),
21773
21831
  syncUpstreamApp: (appId) => request(`/v1/apps/${encodeURIComponent(appId)}/sync-upstream`, {
21774
21832
  method: "POST",
21775
21833
  body: JSON.stringify({})
@@ -21805,7 +21863,31 @@ function createApiClient(config2, opts) {
21805
21863
  `/v1/apps/${encodeURIComponent(appId)}/bundles/${encodeURIComponent(bundleId)}/assets/download?${qs.toString()}`,
21806
21864
  { method: "GET" }
21807
21865
  );
21808
- }
21866
+ },
21867
+ listAgentRuns: (appId, params) => {
21868
+ const qs = new URLSearchParams();
21869
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21870
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21871
+ if (params?.status) qs.set("status", params.status);
21872
+ if (params?.currentPhase) qs.set("currentPhase", params.currentPhase);
21873
+ if (params?.createdAfter) qs.set("createdAfter", params.createdAfter);
21874
+ if (params?.createdBefore) qs.set("createdBefore", params.createdBefore);
21875
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21876
+ return request(`/v1/apps/${encodeURIComponent(appId)}/agent-runs${suffix}`, { method: "GET" });
21877
+ },
21878
+ getAgentRun: (appId, runId) => request(`/v1/apps/${encodeURIComponent(appId)}/agent-runs/${encodeURIComponent(runId)}`, { method: "GET" }),
21879
+ listAgentRunEvents: (appId, runId, params) => {
21880
+ const qs = new URLSearchParams();
21881
+ if (typeof params?.limit === "number") qs.set("limit", String(params.limit));
21882
+ if (typeof params?.offset === "number") qs.set("offset", String(params.offset));
21883
+ if (params?.createdAfter) qs.set("createdAfter", params.createdAfter);
21884
+ if (params?.createdBefore) qs.set("createdBefore", params.createdBefore);
21885
+ const suffix = qs.toString() ? `?${qs.toString()}` : "";
21886
+ return request(`/v1/apps/${encodeURIComponent(appId)}/agent-runs/${encodeURIComponent(runId)}/events${suffix}`, {
21887
+ method: "GET"
21888
+ });
21889
+ },
21890
+ getSandboxStatus: (appId) => request(`/v1/apps/${encodeURIComponent(appId)}/sandbox/status`, { method: "GET" })
21809
21891
  };
21810
21892
  }
21811
21893
 
@@ -41707,7 +41789,7 @@ var REMIX_ERROR_CODES = {
41707
41789
  // node_modules/@remixhq/mcp/dist/index.js
41708
41790
  var import_path11 = __toESM(require("path"), 1);
41709
41791
 
41710
- // node_modules/@remixhq/core/dist/chunk-FAZUMWBS.js
41792
+ // node_modules/@remixhq/core/dist/chunk-GEHSFPCD.js
41711
41793
  var import_promises2 = __toESM(require("fs/promises"), 1);
41712
41794
  var import_path2 = __toESM(require("path"), 1);
41713
41795
  var import_promises3 = __toESM(require("fs/promises"), 1);
@@ -41785,7 +41867,7 @@ async function writeCollabBinding(repoRoot, binding) {
41785
41867
  return filePath;
41786
41868
  }
41787
41869
 
41788
- // node_modules/@remixhq/core/dist/chunk-RREREIGW.js
41870
+ // node_modules/@remixhq/core/dist/chunk-J3J4PBQ7.js
41789
41871
  var import_promises15 = __toESM(require("fs/promises"), 1);
41790
41872
  var import_crypto = require("crypto");
41791
41873
  var import_os2 = __toESM(require("os"), 1);
@@ -48566,7 +48648,7 @@ var {
48566
48648
  getCancelSignal: getCancelSignal2
48567
48649
  } = getIpcExport();
48568
48650
 
48569
- // node_modules/@remixhq/core/dist/chunk-RREREIGW.js
48651
+ // node_modules/@remixhq/core/dist/chunk-J3J4PBQ7.js
48570
48652
  var GIT_REMOTE_PROTOCOL_RE = /^(https?|ssh):\/\//i;
48571
48653
  var SCP_LIKE_GIT_REMOTE_RE = /^(?<user>[^@\s]+)@(?<host>[^:\s]+):(?<path>[^\\\s]+)$/;
48572
48654
  var CANONICAL_GIT_REMOTE_RE = /^(?<host>(?:localhost|[a-z0-9.-]+))\/(?<path>[^\\\s]+)$/i;
@@ -48813,6 +48895,13 @@ async function listUntrackedFiles(cwd) {
48813
48895
  return [];
48814
48896
  }
48815
48897
  }
48898
+ async function getWorkspaceDiff(cwd) {
48899
+ const snapshot = await getWorkspaceSnapshot(cwd);
48900
+ return {
48901
+ diff: snapshot.diff,
48902
+ includedUntrackedPaths: snapshot.includedUntrackedPaths
48903
+ };
48904
+ }
48816
48905
  async function getWorkspaceSnapshot(cwd) {
48817
48906
  const headCommitHash = await getHeadCommitHash(cwd);
48818
48907
  if (!headCommitHash) {
@@ -49252,6 +49341,26 @@ function unwrapResponseObject(resp, label) {
49252
49341
  }
49253
49342
  return obj;
49254
49343
  }
49344
+ var DEFAULT_PAGINATION_LIMIT = 25;
49345
+ var MAX_PAGINATION_LIMIT = 50;
49346
+ function normalizePagination(params) {
49347
+ const rawLimit = typeof params?.limit === "number" && Number.isFinite(params.limit) ? Math.trunc(params.limit) : DEFAULT_PAGINATION_LIMIT;
49348
+ const rawOffset = typeof params?.offset === "number" && Number.isFinite(params.offset) ? Math.trunc(params.offset) : 0;
49349
+ return {
49350
+ limit: Math.max(1, Math.min(MAX_PAGINATION_LIMIT, rawLimit)),
49351
+ offset: Math.max(0, rawOffset)
49352
+ };
49353
+ }
49354
+ function paginateOverfetchedItems(items, params) {
49355
+ const pagination = normalizePagination(params);
49356
+ return {
49357
+ items: items.slice(0, pagination.limit),
49358
+ pagination: {
49359
+ ...pagination,
49360
+ hasMore: items.length > pagination.limit
49361
+ }
49362
+ };
49363
+ }
49255
49364
  function unwrapMergeRequest(resp) {
49256
49365
  return unwrapResponseObject(resp, "merge request");
49257
49366
  }
@@ -50201,6 +50310,7 @@ async function collabAdd(params) {
50201
50310
  }
50202
50311
  const { backupPath } = await writeTempUnifiedDiffBackup(diff, "remix-add");
50203
50312
  try {
50313
+ await pollAppReady(params.api, binding.currentAppId);
50204
50314
  if (submissionSnapshot) {
50205
50315
  await assertRepoSnapshotUnchanged(repoRoot, submissionSnapshot, {
50206
50316
  operation: "`remix collab add` auto-sync",
@@ -50345,6 +50455,105 @@ async function collabRecordTurn(params) {
50345
50455
  });
50346
50456
  return unwrapResponseObject(resp, "collab turn");
50347
50457
  }
50458
+ function collectWarnings(value) {
50459
+ if (!Array.isArray(value)) return [];
50460
+ return value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
50461
+ }
50462
+ async function collabFinalizeTurn(params) {
50463
+ const repoRoot = await findGitRoot(params.cwd);
50464
+ const binding = await readCollabBinding(repoRoot);
50465
+ if (!binding) {
50466
+ throw new RemixError("Repository is not bound to Remix.", {
50467
+ exitCode: 2,
50468
+ hint: "Run `remix collab init` first."
50469
+ });
50470
+ }
50471
+ const prompt = params.prompt.trim();
50472
+ const assistantResponse = params.assistantResponse.trim();
50473
+ if (!prompt) throw new RemixError("Prompt is required.", { exitCode: 2 });
50474
+ if (!assistantResponse) throw new RemixError("Assistant response is required.", { exitCode: 2 });
50475
+ const diffSource = params.diffSource ?? (params.diff ? "external" : "worktree");
50476
+ const externalDiff = params.diff?.trim() ?? "";
50477
+ const workspaceDiff = diffSource === "worktree" ? await getWorkspaceDiff(repoRoot) : null;
50478
+ const hasChangedTurn = diffSource === "external" ? externalDiff.length > 0 : Boolean(workspaceDiff?.diff.trim());
50479
+ const currentHeadCommitHash = await getHeadCommitHash(repoRoot);
50480
+ const idempotencyKey = params.idempotencyKey?.trim() || buildDeterministicIdempotencyKey({
50481
+ kind: "collab_finalize_turn_v1",
50482
+ appId: binding.currentAppId,
50483
+ upstreamAppId: binding.upstreamAppId,
50484
+ headCommitHash: currentHeadCommitHash,
50485
+ modeHint: hasChangedTurn ? "changed_turn" : "no_diff_turn",
50486
+ prompt,
50487
+ assistantResponse,
50488
+ externalDiff: diffSource === "external" ? externalDiff : null
50489
+ });
50490
+ if (diffSource === "external" && !hasChangedTurn) {
50491
+ throw new RemixError("External diff is empty.", {
50492
+ exitCode: 2,
50493
+ hint: "Pass a non-empty diff when using external diff submission, or omit the external diff so finalize-turn can inspect the live worktree."
50494
+ });
50495
+ }
50496
+ if (hasChangedTurn) {
50497
+ const localHeadBefore = currentHeadCommitHash;
50498
+ const capturedUntrackedPathsCandidate = diffSource === "worktree" ? await listUntrackedFiles(repoRoot) : [];
50499
+ const changeStep = await collabAdd({
50500
+ api: params.api,
50501
+ cwd: repoRoot,
50502
+ prompt,
50503
+ assistantResponse,
50504
+ diff: diffSource === "external" ? externalDiff : null,
50505
+ diffSource,
50506
+ sync: params.sync,
50507
+ allowBranchMismatch: params.allowBranchMismatch,
50508
+ idempotencyKey,
50509
+ actor: params.actor
50510
+ });
50511
+ const localHeadAfter = await getHeadCommitHash(repoRoot);
50512
+ const warnings = [
50513
+ ...collectWarnings(changeStep.warnings),
50514
+ ...diffSource === "external" && params.sync !== false ? [
50515
+ "Automatic local discard+sync was skipped because the diff came from an external source and may not match the current worktree."
50516
+ ] : []
50517
+ ];
50518
+ const autoSyncRequested = params.sync !== false;
50519
+ const autoSyncEligible = diffSource === "worktree";
50520
+ return {
50521
+ mode: "changed_turn",
50522
+ idempotencyKey,
50523
+ changeStep,
50524
+ collabTurn: null,
50525
+ autoSync: {
50526
+ requested: autoSyncRequested,
50527
+ eligible: autoSyncEligible,
50528
+ attempted: autoSyncRequested && autoSyncEligible,
50529
+ applied: autoSyncRequested && autoSyncEligible,
50530
+ trackedChangesDiscarded: autoSyncRequested && autoSyncEligible,
50531
+ capturedUntrackedPathsCandidate,
50532
+ localHeadBefore,
50533
+ localHeadAfter,
50534
+ localRepoMutated: autoSyncRequested && autoSyncEligible && localHeadBefore !== localHeadAfter
50535
+ },
50536
+ warnings
50537
+ };
50538
+ }
50539
+ const collabTurn = await collabRecordTurn({
50540
+ api: params.api,
50541
+ cwd: repoRoot,
50542
+ prompt,
50543
+ assistantResponse,
50544
+ allowBranchMismatch: params.allowBranchMismatch,
50545
+ idempotencyKey,
50546
+ actor: params.actor
50547
+ });
50548
+ return {
50549
+ mode: "no_diff_turn",
50550
+ idempotencyKey,
50551
+ changeStep: null,
50552
+ collabTurn,
50553
+ autoSync: null,
50554
+ warnings: []
50555
+ };
50556
+ }
50348
50557
  async function collabApprove(params) {
50349
50558
  if (params.mode === "sync-target-repo") {
50350
50559
  if (!params.cwd?.trim()) {
@@ -50602,17 +50811,22 @@ async function collabListMergeRequests(params) {
50602
50811
  appId: params.appId,
50603
50812
  queue: params.queue
50604
50813
  });
50814
+ const pageRequest = normalizePagination(params);
50605
50815
  const resp = await params.api.listMergeRequests({
50606
50816
  queue: params.queue,
50607
50817
  appId,
50608
50818
  status: params.status,
50609
- kind: params.kind ?? "merge"
50819
+ kind: params.kind ?? "merge",
50820
+ limit: pageRequest.limit + 1,
50821
+ offset: pageRequest.offset
50610
50822
  });
50611
50823
  const mergeRequests = unwrapResponseObject(resp, "merge requests");
50824
+ const page = paginateOverfetchedItems(mergeRequests, params);
50612
50825
  return {
50613
50826
  queue: params.queue,
50614
50827
  appId: appId ?? null,
50615
- mergeRequests
50828
+ mergeRequests: page.items,
50829
+ pagination: page.pagination
50616
50830
  };
50617
50831
  }
50618
50832
  async function resolveScopeTarget(params) {
@@ -50651,12 +50865,24 @@ async function collabListMembers(params) {
50651
50865
  scope: params.scope,
50652
50866
  targetId: params.targetId
50653
50867
  });
50654
- const resp = params.scope === "organization" ? await params.api.listOrganizationMembers(targetId) : params.scope === "project" ? await params.api.listProjectMembers(targetId) : await params.api.listAppMembers(targetId);
50868
+ const pageRequest = normalizePagination(params);
50869
+ const resp = params.scope === "organization" ? await params.api.listOrganizationMembers(targetId, {
50870
+ limit: pageRequest.limit + 1,
50871
+ offset: pageRequest.offset
50872
+ }) : params.scope === "project" ? await params.api.listProjectMembers(targetId, {
50873
+ limit: pageRequest.limit + 1,
50874
+ offset: pageRequest.offset
50875
+ }) : await params.api.listAppMembers(targetId, {
50876
+ limit: pageRequest.limit + 1,
50877
+ offset: pageRequest.offset
50878
+ });
50655
50879
  const members = params.scope === "organization" ? unwrapResponseObject(resp, "members") : params.scope === "project" ? unwrapResponseObject(resp, "members") : unwrapResponseObject(resp, "members");
50880
+ const page = paginateOverfetchedItems(members, params);
50656
50881
  return {
50657
50882
  scopeType: params.scope,
50658
50883
  targetId,
50659
- members
50884
+ members: page.items,
50885
+ pagination: page.pagination
50660
50886
  };
50661
50887
  }
50662
50888
  async function collabUpdateMemberRole(params) {
@@ -50850,6 +51076,20 @@ async function collabInvite(params) {
50850
51076
  targetId
50851
51077
  };
50852
51078
  }
51079
+ async function collabList(params) {
51080
+ const pageRequest = normalizePagination(params);
51081
+ const resp = await params.api.listApps({
51082
+ forked: params.forked ?? "all",
51083
+ limit: pageRequest.limit + 1,
51084
+ offset: pageRequest.offset
51085
+ });
51086
+ const apps = unwrapResponseObject(resp, "apps");
51087
+ const page = paginateOverfetchedItems(apps, params);
51088
+ return {
51089
+ apps: page.items,
51090
+ pagination: page.pagination
51091
+ };
51092
+ }
50853
51093
  async function collabReconcile(params) {
50854
51094
  const repoRoot = await findGitRoot(params.cwd);
50855
51095
  const binding = await readCollabBinding(repoRoot);
@@ -51465,6 +51705,8 @@ var ERROR_CODES = {
51465
51705
  METADATA_CONFLICT: "METADATA_CONFLICT",
51466
51706
  DESTRUCTIVE_OPERATION_BLOCKED: "DESTRUCTIVE_OPERATION_BLOCKED",
51467
51707
  CONFIG_INVALID: "CONFIG_INVALID",
51708
+ ACCESS_DENIED: "ACCESS_DENIED",
51709
+ RESOURCE_NOT_FOUND: "RESOURCE_NOT_FOUND",
51468
51710
  INTERNAL_ERROR: "INTERNAL_ERROR"
51469
51711
  };
51470
51712
  var RemixMcpError = class extends Error {
@@ -51480,6 +51722,15 @@ function toStringOrNull(value) {
51480
51722
  const trimmed = value.trim();
51481
51723
  return trimmed.length > 0 ? trimmed : null;
51482
51724
  }
51725
+ function parseJsonObject(value) {
51726
+ if (!value) return null;
51727
+ try {
51728
+ const parsed = JSON.parse(value);
51729
+ return parsed && typeof parsed === "object" ? parsed : null;
51730
+ } catch {
51731
+ return null;
51732
+ }
51733
+ }
51483
51734
  function makeNormalized(params) {
51484
51735
  return {
51485
51736
  code: params.code,
@@ -51493,6 +51744,8 @@ function normalizeByMessage(err) {
51493
51744
  const code = toStringOrNull(err.code);
51494
51745
  const message = toStringOrNull(err.message) ?? "Unexpected error.";
51495
51746
  const hint = toStringOrNull(err.hint);
51747
+ const hintBody = parseJsonObject(hint);
51748
+ const statusCode = typeof hintBody?.statusCode === "number" ? hintBody.statusCode : typeof hintBody?.statusCode === "string" ? Number(hintBody.statusCode) : null;
51496
51749
  if (code === ERROR_CODES.REPO_LOCK_HELD) {
51497
51750
  return makeNormalized({
51498
51751
  code: ERROR_CODES.REPO_LOCK_HELD,
@@ -51593,6 +51846,22 @@ function normalizeByMessage(err) {
51593
51846
  category: "remote_state"
51594
51847
  });
51595
51848
  }
51849
+ if (statusCode === 403) {
51850
+ return makeNormalized({
51851
+ code: ERROR_CODES.ACCESS_DENIED,
51852
+ message,
51853
+ hint,
51854
+ category: "remote_state"
51855
+ });
51856
+ }
51857
+ if (statusCode === 404) {
51858
+ return makeNormalized({
51859
+ code: ERROR_CODES.RESOURCE_NOT_FOUND,
51860
+ message,
51861
+ hint,
51862
+ category: "remote_state"
51863
+ });
51864
+ }
51596
51865
  if (message.includes("Timed out") || message.includes("failed") || message.includes("error state")) {
51597
51866
  return makeNormalized({
51598
51867
  code: ERROR_CODES.REMOTE_ERROR,
@@ -51770,6 +52039,11 @@ function makeErrorResult(envelope) {
51770
52039
  }
51771
52040
  var genericRecordSchema = external_exports.record(external_exports.string(), external_exports.unknown());
51772
52041
  var genericArraySchema = external_exports.array(genericRecordSchema);
52042
+ var paginationSchema = external_exports.object({
52043
+ limit: external_exports.number().int().positive(),
52044
+ offset: external_exports.number().int().nonnegative(),
52045
+ hasMore: external_exports.boolean()
52046
+ });
51773
52047
  var mergeRequestQueueSchema = external_exports.enum([
51774
52048
  "reviewable",
51775
52049
  "created_by_me",
@@ -51791,7 +52065,9 @@ var initInputSchema = {
51791
52065
  var listInputSchema = {
51792
52066
  requestId: external_exports.string().trim().min(1).optional(),
51793
52067
  outputMode: external_exports.enum(["summary", "full"]).optional(),
51794
- forked: external_exports.enum(["only", "exclude", "all"]).optional()
52068
+ forked: external_exports.enum(["only", "exclude", "all"]).optional(),
52069
+ limit: external_exports.number().int().positive().max(50).optional(),
52070
+ offset: external_exports.number().int().nonnegative().optional()
51795
52071
  };
51796
52072
  var remixInputSchema = {
51797
52073
  ...commonRequestFieldsSchema,
@@ -51820,6 +52096,16 @@ var recordTurnInputSchema = {
51820
52096
  allowBranchMismatch: external_exports.boolean().optional(),
51821
52097
  idempotencyKey: external_exports.string().trim().min(1).optional()
51822
52098
  };
52099
+ var finalizeTurnInputSchema = {
52100
+ ...commonRequestFieldsSchema,
52101
+ prompt: external_exports.string().trim().min(1),
52102
+ assistantResponse: external_exports.string().trim().min(1),
52103
+ diffSource: external_exports.enum(["worktree", "external"]).optional(),
52104
+ externalDiff: external_exports.string().optional(),
52105
+ sync: external_exports.boolean().optional(),
52106
+ allowBranchMismatch: external_exports.boolean().optional(),
52107
+ idempotencyKey: external_exports.string().trim().min(1).optional()
52108
+ };
51823
52109
  var previewInputSchema = {
51824
52110
  ...commonRequestFieldsSchema
51825
52111
  };
@@ -51835,20 +52121,26 @@ var reviewQueueInputSchema = {
51835
52121
  requestId: external_exports.string().trim().min(1).optional(),
51836
52122
  outputMode: external_exports.enum(["summary", "full"]).optional(),
51837
52123
  status: external_exports.string().trim().min(1).optional(),
51838
- kind: external_exports.enum(["merge", "sync", "all"]).optional()
52124
+ kind: external_exports.enum(["merge", "sync", "all"]).optional(),
52125
+ limit: external_exports.number().int().positive().max(50).optional(),
52126
+ offset: external_exports.number().int().nonnegative().optional()
51839
52127
  };
51840
52128
  var myMergeRequestsInputSchema = {
51841
52129
  requestId: external_exports.string().trim().min(1).optional(),
51842
52130
  outputMode: external_exports.enum(["summary", "full"]).optional(),
51843
52131
  status: external_exports.string().trim().min(1).optional(),
51844
- kind: external_exports.enum(["merge", "sync", "all"]).optional()
52132
+ kind: external_exports.enum(["merge", "sync", "all"]).optional(),
52133
+ limit: external_exports.number().int().positive().max(50).optional(),
52134
+ offset: external_exports.number().int().nonnegative().optional()
51845
52135
  };
51846
52136
  var appMergeRequestsInputSchema = {
51847
52137
  ...commonRequestFieldsSchema,
51848
52138
  queue: appScopedMergeRequestQueueSchema,
51849
52139
  appId: external_exports.string().trim().min(1).optional(),
51850
52140
  status: external_exports.string().trim().min(1).optional(),
51851
- kind: external_exports.enum(["merge", "sync", "all"]).optional()
52141
+ kind: external_exports.enum(["merge", "sync", "all"]).optional(),
52142
+ limit: external_exports.number().int().positive().max(50).optional(),
52143
+ offset: external_exports.number().int().nonnegative().optional()
51852
52144
  };
51853
52145
  var viewMergeRequestInputSchema = {
51854
52146
  requestId: external_exports.string().trim().min(1).optional(),
@@ -51879,6 +52171,38 @@ var inviteInputSchema = {
51879
52171
  var listMembersInputSchema = {
51880
52172
  ...commonRequestFieldsSchema,
51881
52173
  scope: memberScopeSchema,
52174
+ targetId: external_exports.string().trim().min(1).optional(),
52175
+ limit: external_exports.number().int().positive().max(50).optional(),
52176
+ offset: external_exports.number().int().nonnegative().optional()
52177
+ };
52178
+ var listInvitesInputSchema = {
52179
+ ...commonRequestFieldsSchema,
52180
+ scope: memberScopeSchema.optional(),
52181
+ targetId: external_exports.string().trim().min(1).optional(),
52182
+ limit: external_exports.number().int().positive().max(50).optional(),
52183
+ offset: external_exports.number().int().nonnegative().optional()
52184
+ };
52185
+ var resendInviteInputSchema = {
52186
+ ...commonRequestFieldsSchema,
52187
+ scope: memberScopeSchema.optional(),
52188
+ targetId: external_exports.string().trim().min(1).optional(),
52189
+ inviteId: external_exports.string().trim().min(1),
52190
+ ttlDays: external_exports.number().int().positive().max(30).optional()
52191
+ };
52192
+ var revokeInviteInputSchema = {
52193
+ ...commonRequestFieldsSchema,
52194
+ scope: memberScopeSchema.optional(),
52195
+ targetId: external_exports.string().trim().min(1).optional(),
52196
+ inviteId: external_exports.string().trim().min(1),
52197
+ confirm: external_exports.boolean()
52198
+ };
52199
+ var acceptInvitationInputSchema = {
52200
+ requestId: external_exports.string().trim().min(1).optional(),
52201
+ token: external_exports.string().trim().min(20)
52202
+ };
52203
+ var accessDebugInputSchema = {
52204
+ ...commonRequestFieldsSchema,
52205
+ scope: memberScopeSchema.optional(),
51882
52206
  targetId: external_exports.string().trim().min(1).optional()
51883
52207
  };
51884
52208
  var updateMemberRoleInputSchema = {
@@ -51902,7 +52226,8 @@ var initDataSchema = external_exports.object({
51902
52226
  repoRoot: external_exports.string()
51903
52227
  });
51904
52228
  var listDataSchema = external_exports.object({
51905
- apps: genericArraySchema
52229
+ apps: genericArraySchema,
52230
+ pagination: paginationSchema
51906
52231
  });
51907
52232
  var remixDataSchema = external_exports.object({
51908
52233
  appId: external_exports.string(),
@@ -51925,12 +52250,21 @@ var addDataSchema = external_exports.object({
51925
52250
  autoSync: genericRecordSchema
51926
52251
  });
51927
52252
  var recordTurnDataSchema = genericRecordSchema;
52253
+ var finalizeTurnDataSchema = external_exports.object({
52254
+ mode: external_exports.enum(["changed_turn", "no_diff_turn"]),
52255
+ idempotencyKey: external_exports.string().min(1),
52256
+ changeStep: genericRecordSchema.nullable(),
52257
+ collabTurn: genericRecordSchema.nullable(),
52258
+ autoSync: genericRecordSchema.nullable(),
52259
+ warnings: external_exports.array(external_exports.string())
52260
+ });
51928
52261
  var syncDataSchema = genericRecordSchema;
51929
52262
  var requestMergeDataSchema = genericRecordSchema;
51930
52263
  var mergeRequestQueueDataSchema = external_exports.object({
51931
52264
  queue: mergeRequestQueueSchema,
51932
52265
  appId: external_exports.string().nullable(),
51933
- mergeRequests: external_exports.array(genericRecordSchema)
52266
+ mergeRequests: external_exports.array(genericRecordSchema),
52267
+ pagination: paginationSchema
51934
52268
  });
51935
52269
  var viewMergeRequestDataSchema = genericRecordSchema;
51936
52270
  var approveDataSchema = genericRecordSchema;
@@ -51942,7 +52276,49 @@ var memberRecordSchema = genericRecordSchema;
51942
52276
  var listMembersDataSchema = external_exports.object({
51943
52277
  scopeType: memberScopeSchema,
51944
52278
  targetId: external_exports.string(),
51945
- members: external_exports.array(memberRecordSchema)
52279
+ members: external_exports.array(memberRecordSchema),
52280
+ pagination: paginationSchema
52281
+ });
52282
+ var listInvitesDataSchema = external_exports.object({
52283
+ scopeType: memberScopeSchema,
52284
+ targetId: external_exports.string(),
52285
+ invites: external_exports.array(genericRecordSchema),
52286
+ pagination: paginationSchema
52287
+ });
52288
+ var resendInviteDataSchema = genericRecordSchema;
52289
+ var revokeInviteDataSchema = external_exports.object({
52290
+ scopeType: memberScopeSchema,
52291
+ targetId: external_exports.string(),
52292
+ inviteId: external_exports.string(),
52293
+ revoked: external_exports.boolean()
52294
+ });
52295
+ var acceptInvitationDataSchema = genericRecordSchema;
52296
+ var accessDebugDataSchema = external_exports.object({
52297
+ viewer: genericRecordSchema,
52298
+ scope: external_exports.object({
52299
+ scopeType: memberScopeSchema,
52300
+ targetId: external_exports.string()
52301
+ }),
52302
+ entities: external_exports.object({
52303
+ organization: genericRecordSchema.nullable(),
52304
+ project: genericRecordSchema.nullable(),
52305
+ app: genericRecordSchema.nullable()
52306
+ }),
52307
+ binding: external_exports.object({
52308
+ repoRoot: external_exports.string().nullable(),
52309
+ binding: genericRecordSchema.nullable()
52310
+ }),
52311
+ access: external_exports.object({
52312
+ appContext: genericRecordSchema.nullable(),
52313
+ viewerMember: genericRecordSchema.nullable(),
52314
+ viewerProjectMember: genericRecordSchema.nullable(),
52315
+ inviteForViewerEmail: genericRecordSchema.nullable(),
52316
+ effectiveAppAccess: genericRecordSchema.nullable()
52317
+ }),
52318
+ members: external_exports.array(genericRecordSchema),
52319
+ membersPageInfo: paginationSchema,
52320
+ invites: external_exports.array(genericRecordSchema),
52321
+ invitesPageInfo: paginationSchema
51946
52322
  });
51947
52323
  var updateMemberRoleDataSchema = external_exports.object({
51948
52324
  scopeType: memberScopeSchema,
@@ -51956,6 +52332,7 @@ var remixSuccessSchema = makeSuccessSchema(remixDataSchema);
51956
52332
  var checkoutSuccessSchema = makeSuccessSchema(checkoutDataSchema);
51957
52333
  var addSuccessSchema = makeSuccessSchema(addDataSchema);
51958
52334
  var recordTurnSuccessSchema = makeSuccessSchema(recordTurnDataSchema);
52335
+ var finalizeTurnSuccessSchema = makeSuccessSchema(finalizeTurnDataSchema);
51959
52336
  var syncSuccessSchema = makeSuccessSchema(syncDataSchema);
51960
52337
  var requestMergeSuccessSchema = makeSuccessSchema(requestMergeDataSchema);
51961
52338
  var mergeRequestQueueSuccessSchema = makeSuccessSchema(mergeRequestQueueDataSchema);
@@ -51966,14 +52343,12 @@ var syncUpstreamSuccessSchema = makeSuccessSchema(syncUpstreamDataSchema);
51966
52343
  var reconcileSuccessSchema = makeSuccessSchema(reconcileDataSchema);
51967
52344
  var inviteSuccessSchema = makeSuccessSchema(inviteDataSchema);
51968
52345
  var listMembersSuccessSchema = makeSuccessSchema(listMembersDataSchema);
52346
+ var listInvitesSuccessSchema = makeSuccessSchema(listInvitesDataSchema);
52347
+ var resendInviteSuccessSchema = makeSuccessSchema(resendInviteDataSchema);
52348
+ var revokeInviteSuccessSchema = makeSuccessSchema(revokeInviteDataSchema);
52349
+ var acceptInvitationSuccessSchema = makeSuccessSchema(acceptInvitationDataSchema);
52350
+ var accessDebugSuccessSchema = makeSuccessSchema(accessDebugDataSchema);
51969
52351
  var updateMemberRoleSuccessSchema = makeSuccessSchema(updateMemberRoleDataSchema);
51970
- function unwrapResponseObject2(resp, label) {
51971
- const obj = resp?.responseObject;
51972
- if (obj === void 0 || obj === null) {
51973
- throw new Error(typeof resp?.message === "string" && resp.message.trim() ? resp.message : `Missing ${label} response`);
51974
- }
51975
- return obj;
51976
- }
51977
52352
  function getRiskLevel(status) {
51978
52353
  if (status.recommendedAction === "reconcile") return "high";
51979
52354
  if (status.recommendedAction === "sync" || status.remote.incomingOpenMergeRequestCount) return "medium";
@@ -51999,12 +52374,12 @@ function getRecommendedNextActions(status) {
51999
52374
  return [];
52000
52375
  }
52001
52376
  }
52002
- function collectWarnings(value) {
52377
+ function collectWarnings2(value) {
52003
52378
  if (!value || !Array.isArray(value)) return [];
52004
52379
  return value.filter((entry) => typeof entry === "string" && entry.trim().length > 0);
52005
52380
  }
52006
52381
  function collectResultWarnings(value) {
52007
- return collectWarnings(value.warnings);
52382
+ return collectWarnings2(value.warnings);
52008
52383
  }
52009
52384
  function truncateText(value, maxChars) {
52010
52385
  if (value.length <= maxChars) {
@@ -52060,13 +52435,17 @@ async function initCollab(params) {
52060
52435
  };
52061
52436
  }
52062
52437
  async function listApps(params) {
52063
- const api = await createApiClient2();
52064
- const resp = await api.listApps({ forked: params.forked ?? "all" });
52065
- const apps = unwrapResponseObject2(resp, "apps");
52438
+ const api = await createCollabApiClient();
52439
+ const result = await collabList({
52440
+ api,
52441
+ forked: params.forked,
52442
+ limit: params.limit,
52443
+ offset: params.offset
52444
+ });
52066
52445
  return {
52067
- data: { apps },
52446
+ data: result,
52068
52447
  warnings: [],
52069
- recommendedNextActions: [],
52448
+ recommendedNextActions: result.pagination.hasMore ? [`Pass offset=${result.pagination.offset + result.pagination.limit} to load the next page.`] : [],
52070
52449
  logContext: {}
52071
52450
  };
52072
52451
  }
@@ -52107,70 +52486,28 @@ async function checkoutCollab(params) {
52107
52486
  }
52108
52487
  };
52109
52488
  }
52110
- async function addCollabStep(params) {
52489
+ async function finalizeCollabTurn(params) {
52111
52490
  const api = await createCollabApiClient();
52112
52491
  const repoRoot = await findGitRoot(params.cwd);
52113
- const preHead = await getHeadCommitHash(repoRoot);
52114
- const untrackedBefore = params.diffSource === "worktree" ? await listUntrackedFiles(repoRoot) : [];
52115
- const changeStep = await collabAdd({
52492
+ const result = await collabFinalizeTurn({
52116
52493
  api,
52117
52494
  cwd: params.cwd,
52118
52495
  prompt: params.prompt,
52119
- assistantResponse: params.assistantResponse ?? null,
52496
+ assistantResponse: params.assistantResponse,
52120
52497
  diff: params.externalDiff ?? null,
52121
52498
  diffSource: params.diffSource,
52122
- allowBranchMismatch: params.allowBranchMismatch ?? false,
52123
- idempotencyKey: params.idempotencyKey ?? null,
52124
- actor: params.agent
52125
- });
52126
- const postHead = await getHeadCommitHash(repoRoot);
52127
- const autoSyncEligible = params.diffSource === "worktree";
52128
- const localRepoMutated = autoSyncEligible && preHead !== postHead;
52129
- return {
52130
- data: {
52131
- changeStep,
52132
- autoSync: {
52133
- requested: true,
52134
- eligible: autoSyncEligible,
52135
- attempted: autoSyncEligible,
52136
- applied: autoSyncEligible,
52137
- trackedChangesDiscarded: autoSyncEligible,
52138
- capturedUntrackedPathsCandidate: untrackedBefore,
52139
- localHeadBefore: preHead,
52140
- localHeadAfter: postHead,
52141
- localRepoMutated
52142
- }
52143
- },
52144
- warnings: [
52145
- ...collectResultWarnings(changeStep),
52146
- ...params.diffSource === "external" ? [
52147
- "Automatic local discard+sync was skipped because the diff came from an external source and may not match the current worktree."
52148
- ] : []
52149
- ],
52150
- recommendedNextActions: [],
52151
- logContext: {
52152
- repoRoot
52153
- }
52154
- };
52155
- }
52156
- async function recordCollabTurn(params) {
52157
- const api = await createCollabApiClient();
52158
- const result = await collabRecordTurn({
52159
- api,
52160
- cwd: params.cwd,
52161
- prompt: params.prompt,
52162
- assistantResponse: params.assistantResponse,
52499
+ sync: params.sync,
52163
52500
  allowBranchMismatch: params.allowBranchMismatch ?? false,
52164
52501
  idempotencyKey: params.idempotencyKey ?? null,
52165
52502
  actor: params.agent
52166
52503
  });
52167
52504
  return {
52168
52505
  data: result,
52169
- warnings: [],
52506
+ warnings: result.warnings,
52170
52507
  recommendedNextActions: [],
52171
52508
  logContext: {
52172
- repoRoot: params.cwd,
52173
- appId: result.appId
52509
+ repoRoot,
52510
+ appId: result.changeStep?.appId ?? result.collabTurn?.appId ?? null
52174
52511
  }
52175
52512
  };
52176
52513
  }
@@ -52212,16 +52549,19 @@ async function reviewQueue(params) {
52212
52549
  api,
52213
52550
  queue: "reviewable",
52214
52551
  status: params.status ?? "open",
52215
- kind: params.kind ?? "merge"
52552
+ kind: params.kind ?? "merge",
52553
+ limit: params.limit,
52554
+ offset: params.offset
52216
52555
  });
52217
52556
  return {
52218
52557
  data: {
52219
52558
  queue: result.queue,
52220
52559
  appId: result.appId,
52221
- mergeRequests: result.mergeRequests
52560
+ mergeRequests: result.mergeRequests,
52561
+ pagination: result.pagination
52222
52562
  },
52223
52563
  warnings: [],
52224
- recommendedNextActions: [],
52564
+ recommendedNextActions: result.pagination.hasMore ? [`Pass offset=${result.pagination.offset + result.pagination.limit} to load the next page.`] : [],
52225
52565
  logContext: {}
52226
52566
  };
52227
52567
  }
@@ -52231,16 +52571,19 @@ async function myMergeRequests(params) {
52231
52571
  api,
52232
52572
  queue: "created_by_me",
52233
52573
  status: params.status ?? "open",
52234
- kind: params.kind ?? "merge"
52574
+ kind: params.kind ?? "merge",
52575
+ limit: params.limit,
52576
+ offset: params.offset
52235
52577
  });
52236
52578
  return {
52237
52579
  data: {
52238
52580
  queue: result.queue,
52239
52581
  appId: result.appId,
52240
- mergeRequests: result.mergeRequests
52582
+ mergeRequests: result.mergeRequests,
52583
+ pagination: result.pagination
52241
52584
  },
52242
52585
  warnings: [],
52243
- recommendedNextActions: [],
52586
+ recommendedNextActions: result.pagination.hasMore ? [`Pass offset=${result.pagination.offset + result.pagination.limit} to load the next page.`] : [],
52244
52587
  logContext: {}
52245
52588
  };
52246
52589
  }
@@ -52252,16 +52595,19 @@ async function listAppMergeRequests(params) {
52252
52595
  appId: params.appId,
52253
52596
  queue: params.queue,
52254
52597
  status: params.status ?? "open",
52255
- kind: params.kind ?? "merge"
52598
+ kind: params.kind ?? "merge",
52599
+ limit: params.limit,
52600
+ offset: params.offset
52256
52601
  });
52257
52602
  return {
52258
52603
  data: {
52259
52604
  queue: result.queue,
52260
52605
  appId: result.appId,
52261
- mergeRequests: result.mergeRequests
52606
+ mergeRequests: result.mergeRequests,
52607
+ pagination: result.pagination
52262
52608
  },
52263
52609
  warnings: [],
52264
- recommendedNextActions: [],
52610
+ recommendedNextActions: result.pagination.hasMore ? [`Pass offset=${result.pagination.offset + result.pagination.limit} to load the next page.`] : [],
52265
52611
  logContext: {
52266
52612
  appId: result.appId
52267
52613
  }
@@ -52354,7 +52700,7 @@ async function reconcile(params) {
52354
52700
  });
52355
52701
  return {
52356
52702
  data: result,
52357
- warnings: collectWarnings(result.warnings),
52703
+ warnings: collectWarnings2(result.warnings),
52358
52704
  recommendedNextActions: params.dryRun ? ["Run remix_collab_reconcile_apply with confirm=true only if the preview is acceptable. Do not replace this with raw git history-rewrite commands."] : [],
52359
52705
  risks: params.dryRun ? ["Reconcile apply rewrites local history and creates a backup branch."] : [],
52360
52706
  logContext: {
@@ -52386,12 +52732,14 @@ async function listMembers(params) {
52386
52732
  api,
52387
52733
  cwd: params.cwd,
52388
52734
  scope: params.scope,
52389
- targetId: params.targetId ?? null
52735
+ targetId: params.targetId ?? null,
52736
+ limit: params.limit,
52737
+ offset: params.offset
52390
52738
  });
52391
52739
  return {
52392
52740
  data: result,
52393
52741
  warnings: [],
52394
- recommendedNextActions: [],
52742
+ recommendedNextActions: result.pagination.hasMore ? [`Pass offset=${result.pagination.offset + result.pagination.limit} to load the next page.`] : [],
52395
52743
  logContext: {}
52396
52744
  };
52397
52745
  }
@@ -52412,35 +52760,517 @@ async function updateMemberRole(params) {
52412
52760
  logContext: {}
52413
52761
  };
52414
52762
  }
52415
- function getAnnotations(access) {
52416
- if (access === "read") {
52417
- return {
52418
- readOnlyHint: true,
52419
- idempotentHint: true,
52420
- openWorldHint: false
52421
- };
52763
+ function unwrapResponseObject2(resp, label) {
52764
+ const obj = resp?.responseObject;
52765
+ if (obj === void 0 || obj === null) {
52766
+ throw new Error(typeof resp?.message === "string" && resp.message.trim() ? resp.message : `Missing ${label} response`);
52767
+ }
52768
+ return obj;
52769
+ }
52770
+ async function maybeFindGitRoot(cwd) {
52771
+ try {
52772
+ return await findGitRoot(cwd);
52773
+ } catch {
52774
+ return null;
52422
52775
  }
52776
+ }
52777
+ async function loadBindingContext(cwd) {
52778
+ if (!cwd) return { repoRoot: null, binding: null };
52779
+ const repoRoot = await maybeFindGitRoot(cwd);
52780
+ if (!repoRoot) return { repoRoot: null, binding: null };
52781
+ const binding = await readCollabBinding(repoRoot);
52423
52782
  return {
52424
- readOnlyHint: false,
52425
- destructiveHint: access === "local_write",
52426
- idempotentHint: false,
52427
- openWorldHint: false
52783
+ repoRoot,
52784
+ binding
52428
52785
  };
52429
52786
  }
52430
- function buildSuccessEnvelope(tool, requestId, result) {
52431
- return {
52432
- schemaVersion: SCHEMA_VERSION,
52433
- ok: true,
52434
- tool,
52435
- requestId: requestId ?? null,
52436
- data: result.data,
52437
- warnings: result.warnings ?? [],
52787
+ function makeNotBoundError(message = "Repository is not bound to Remix.", hint) {
52788
+ const error2 = new Error(message);
52789
+ error2.hint = hint ?? "Run `remix_collab_init` in this repository, or pass the explicit id for a direct read.";
52790
+ return error2;
52791
+ }
52792
+ function parseJsonObject2(value) {
52793
+ if (!value) return null;
52794
+ try {
52795
+ const parsed = JSON.parse(value);
52796
+ return parsed && typeof parsed === "object" ? parsed : null;
52797
+ } catch {
52798
+ return null;
52799
+ }
52800
+ }
52801
+ function getBackendStatusCode(error2) {
52802
+ if (!error2 || typeof error2 !== "object") return null;
52803
+ const hint = "hint" in error2 && typeof error2.hint === "string" ? error2.hint : null;
52804
+ const body = parseJsonObject2(hint);
52805
+ if (typeof body?.statusCode === "number") return body.statusCode;
52806
+ if (typeof body?.statusCode === "string") {
52807
+ const parsed = Number(body.statusCode);
52808
+ return Number.isFinite(parsed) ? parsed : null;
52809
+ }
52810
+ return null;
52811
+ }
52812
+ function isBackendForbidden(error2) {
52813
+ return getBackendStatusCode(error2) === 403;
52814
+ }
52815
+ function isBackendNotFound(error2) {
52816
+ return getBackendStatusCode(error2) === 404;
52817
+ }
52818
+ function createAccessDeniedError(message, hint) {
52819
+ return new RemixMcpError({
52820
+ code: ERROR_CODES.ACCESS_DENIED,
52821
+ message,
52822
+ hint: hint ?? null,
52823
+ retryable: false,
52824
+ category: "remote_state"
52825
+ });
52826
+ }
52827
+ var MEMBERSHIP_PAGE_SIZE = 100;
52828
+ function normalizePagination2(params) {
52829
+ const rawLimit = typeof params?.limit === "number" ? Math.trunc(params.limit) : 25;
52830
+ const rawOffset = typeof params?.offset === "number" ? Math.trunc(params.offset) : 0;
52831
+ return {
52832
+ limit: Math.max(1, Math.min(50, rawLimit)),
52833
+ offset: Math.max(0, rawOffset)
52834
+ };
52835
+ }
52836
+ async function resolveScopeTarget2(api, params) {
52837
+ const explicitTargetId = params.targetId?.trim();
52838
+ const bindingContext = await loadBindingContext(params.cwd);
52839
+ if (explicitTargetId) {
52840
+ return {
52841
+ scopeType: params.scope,
52842
+ targetId: explicitTargetId,
52843
+ repoRoot: bindingContext.repoRoot
52844
+ };
52845
+ }
52846
+ if (!bindingContext.binding) {
52847
+ throw makeNotBoundError("Scope target was not provided and the current repository is not bound to Remix.");
52848
+ }
52849
+ if (params.scope === "app") {
52850
+ return {
52851
+ scopeType: "app",
52852
+ targetId: bindingContext.binding.currentAppId,
52853
+ repoRoot: bindingContext.repoRoot
52854
+ };
52855
+ }
52856
+ const appContext = unwrapResponseObject2(
52857
+ await api.getAppContext(bindingContext.binding.currentAppId),
52858
+ "bound app context"
52859
+ );
52860
+ if (params.scope === "project") {
52861
+ if (!appContext.readableScopes.project) {
52862
+ throw createAccessDeniedError(
52863
+ "The bound app's project is not readable to the current user.",
52864
+ "Use `scope=app` for app-level diagnostics, or pass an explicit readable project id."
52865
+ );
52866
+ }
52867
+ return {
52868
+ scopeType: "project",
52869
+ targetId: appContext.projectId,
52870
+ repoRoot: bindingContext.repoRoot
52871
+ };
52872
+ }
52873
+ if (!appContext.readableScopes.organization) {
52874
+ throw createAccessDeniedError(
52875
+ "The bound app's organization is not readable to the current user.",
52876
+ "Use `scope=app` or `scope=project` for narrower diagnostics, or pass an explicit readable organization id."
52877
+ );
52878
+ }
52879
+ return {
52880
+ scopeType: "organization",
52881
+ targetId: appContext.organizationId,
52882
+ repoRoot: bindingContext.repoRoot
52883
+ };
52884
+ }
52885
+ async function getScopeEntity(api, scopeType, targetId) {
52886
+ if (scopeType === "organization") {
52887
+ return {
52888
+ organization: unwrapResponseObject2(await api.getOrganization(targetId), "organization"),
52889
+ project: null,
52890
+ app: null
52891
+ };
52892
+ }
52893
+ if (scopeType === "project") {
52894
+ const project2 = unwrapResponseObject2(await api.getProject(targetId), "project");
52895
+ const organizationId2 = typeof project2.organizationId === "string" ? project2.organizationId : null;
52896
+ return {
52897
+ organization: organizationId2 == null ? null : unwrapResponseObject2(await api.getOrganization(organizationId2), "organization"),
52898
+ project: project2,
52899
+ app: null
52900
+ };
52901
+ }
52902
+ const app = unwrapResponseObject2(await api.getApp(targetId), "app");
52903
+ const projectId = typeof app.projectId === "string" ? app.projectId : null;
52904
+ const project = projectId == null ? null : unwrapResponseObject2(await api.getProject(projectId), "project");
52905
+ const organizationId = typeof project?.organizationId === "string" ? project.organizationId : null;
52906
+ return {
52907
+ organization: organizationId == null ? null : unwrapResponseObject2(await api.getOrganization(organizationId), "organization"),
52908
+ project,
52909
+ app
52910
+ };
52911
+ }
52912
+ function mapProjectRoleToAppRole(role) {
52913
+ switch (role) {
52914
+ case "owner":
52915
+ case "maintainer":
52916
+ return "maintainer";
52917
+ case "editor":
52918
+ return "editor";
52919
+ case "viewer":
52920
+ return "viewer";
52921
+ default:
52922
+ return null;
52923
+ }
52924
+ }
52925
+ function strongestRole(...roles) {
52926
+ if (roles.includes("owner")) return "owner";
52927
+ if (roles.includes("maintainer")) return "maintainer";
52928
+ if (roles.includes("editor")) return "editor";
52929
+ if (roles.includes("viewer")) return "viewer";
52930
+ return null;
52931
+ }
52932
+ function resolveAppAccessSource(params) {
52933
+ if (params.directAppRole && params.inheritedProjectRole) return "both";
52934
+ if (params.directAppRole) return "direct_app_membership";
52935
+ if (params.inheritedProjectRole) return "project_membership";
52936
+ return "none";
52937
+ }
52938
+ function buildEffectiveAppAccess(params) {
52939
+ const directAppRole = params.directAppRole ?? null;
52940
+ const inheritedProjectRole = mapProjectRoleToAppRole(params.projectRole);
52941
+ const effectiveRole = strongestRole(directAppRole, inheritedProjectRole);
52942
+ return {
52943
+ effectiveRole,
52944
+ directAppRole,
52945
+ projectRole: params.projectRole,
52946
+ inheritedProjectRole,
52947
+ accessSource: resolveAppAccessSource({ directAppRole, inheritedProjectRole }),
52948
+ canReadWorkflow: effectiveRole !== null,
52949
+ canEdit: effectiveRole === "owner" || effectiveRole === "maintainer" || effectiveRole === "editor",
52950
+ canManage: effectiveRole === "owner" || effectiveRole === "maintainer",
52951
+ isOwner: directAppRole === "owner"
52952
+ };
52953
+ }
52954
+ async function listMembersForScope(api, scopeType, targetId, params) {
52955
+ if (scopeType === "organization") {
52956
+ return unwrapResponseObject2(await api.listOrganizationMembers(targetId, params), "organization members");
52957
+ }
52958
+ if (scopeType === "project") {
52959
+ return unwrapResponseObject2(await api.listProjectMembers(targetId, params), "project members");
52960
+ }
52961
+ return unwrapResponseObject2(await api.listAppMembers(targetId, params), "app members");
52962
+ }
52963
+ async function loadFirstPageSample(fetchPage) {
52964
+ const items = await fetchPage(MEMBERSHIP_PAGE_SIZE, 0);
52965
+ const hasMore = items.length < MEMBERSHIP_PAGE_SIZE ? false : (await fetchPage(1, MEMBERSHIP_PAGE_SIZE)).length > 0;
52966
+ return {
52967
+ items,
52968
+ pageInfo: {
52969
+ limit: MEMBERSHIP_PAGE_SIZE,
52970
+ offset: 0,
52971
+ hasMore
52972
+ }
52973
+ };
52974
+ }
52975
+ function emptyFirstPageSample() {
52976
+ return {
52977
+ items: [],
52978
+ pageInfo: {
52979
+ limit: MEMBERSHIP_PAGE_SIZE,
52980
+ offset: 0,
52981
+ hasMore: false
52982
+ }
52983
+ };
52984
+ }
52985
+ async function listInvitesForScope(api, scopeType, targetId, params) {
52986
+ if (scopeType === "organization") {
52987
+ return unwrapResponseObject2(
52988
+ await api.listOrganizationInvites(targetId, params),
52989
+ "organization invites"
52990
+ );
52991
+ }
52992
+ if (scopeType === "project") {
52993
+ return unwrapResponseObject2(
52994
+ await api.listProjectInvites(targetId, params),
52995
+ "project invites"
52996
+ );
52997
+ }
52998
+ return unwrapResponseObject2(await api.listAppInvites(targetId, params), "app invites");
52999
+ }
53000
+ function getSelfMember(meId, members) {
53001
+ return meId == null ? null : members.find((member) => member.userId === meId || member.user_id === meId) ?? null;
53002
+ }
53003
+ async function findPendingInviteForEmailForScope(api, scopeType, targetId, email2) {
53004
+ if (email2 == null) return null;
53005
+ let offset = 0;
53006
+ for (; ; ) {
53007
+ const invites = await listInvitesForScope(api, scopeType, targetId, {
53008
+ limit: MEMBERSHIP_PAGE_SIZE,
53009
+ offset
53010
+ });
53011
+ const match = invites.find((invite) => invite.email.toLowerCase() === email2 && invite.state === "pending") ?? null;
53012
+ if (match) return match;
53013
+ if (invites.length < MEMBERSHIP_PAGE_SIZE) return null;
53014
+ offset += MEMBERSHIP_PAGE_SIZE;
53015
+ }
53016
+ }
53017
+ async function findSelfMemberForScope(api, scopeType, targetId, meId) {
53018
+ if (meId == null) return null;
53019
+ let offset = 0;
53020
+ for (; ; ) {
53021
+ const members = scopeType === "organization" ? unwrapResponseObject2(
53022
+ await api.listOrganizationMembers(targetId, { limit: MEMBERSHIP_PAGE_SIZE, offset }),
53023
+ "organization members"
53024
+ ) : scopeType === "project" ? unwrapResponseObject2(
53025
+ await api.listProjectMembers(targetId, { limit: MEMBERSHIP_PAGE_SIZE, offset }),
53026
+ "project members"
53027
+ ) : unwrapResponseObject2(
53028
+ await api.listAppMembers(targetId, { limit: MEMBERSHIP_PAGE_SIZE, offset }),
53029
+ "app members"
53030
+ );
53031
+ const match = getSelfMember(meId, members);
53032
+ if (match) return match;
53033
+ if (members.length < MEMBERSHIP_PAGE_SIZE) return null;
53034
+ offset += MEMBERSHIP_PAGE_SIZE;
53035
+ }
53036
+ }
53037
+ async function listInvites(params) {
53038
+ const api = await createApiClient2();
53039
+ const target = await resolveScopeTarget2(api, {
53040
+ scope: params.scope ?? "project",
53041
+ targetId: params.targetId,
53042
+ cwd: params.cwd
53043
+ });
53044
+ const pagination = normalizePagination2(params);
53045
+ const invites = await listInvitesForScope(api, target.scopeType, target.targetId, {
53046
+ limit: pagination.limit + 1,
53047
+ offset: pagination.offset
53048
+ });
53049
+ return {
53050
+ data: {
53051
+ scopeType: target.scopeType,
53052
+ targetId: target.targetId,
53053
+ invites: invites.slice(0, pagination.limit),
53054
+ pagination: {
53055
+ ...pagination,
53056
+ hasMore: invites.length > pagination.limit
53057
+ }
53058
+ },
53059
+ warnings: [],
53060
+ recommendedNextActions: invites.length > pagination.limit ? [`Pass offset=${pagination.offset + pagination.limit} to load the next page.`] : invites.length > 0 ? ["Use `remix_collab_resend_invite` or `remix_collab_revoke_invite` for lifecycle actions on a selected invite id."] : ["Use `remix_collab_invite` if you need to create a new invitation for this scope."],
53061
+ logContext: {
53062
+ repoRoot: target.repoRoot
53063
+ }
53064
+ };
53065
+ }
53066
+ async function resendInvite(params) {
53067
+ const api = await createApiClient2();
53068
+ const target = await resolveScopeTarget2(api, {
53069
+ scope: params.scope ?? "project",
53070
+ targetId: params.targetId,
53071
+ cwd: params.cwd
53072
+ });
53073
+ const response = target.scopeType === "organization" ? await api.resendOrganizationInvite(target.targetId, params.inviteId, { ttlDays: params.ttlDays }) : target.scopeType === "project" ? await api.resendProjectInvite(target.targetId, params.inviteId, { ttlDays: params.ttlDays }) : await api.resendAppInvite(target.targetId, params.inviteId, { ttlDays: params.ttlDays });
53074
+ return {
53075
+ data: unwrapResponseObject2(response, "resent invite"),
53076
+ warnings: [],
53077
+ recommendedNextActions: ["Use `remix_collab_list_invites` to confirm the refreshed expiry and delivery state."],
53078
+ logContext: {
53079
+ repoRoot: target.repoRoot
53080
+ }
53081
+ };
53082
+ }
53083
+ async function revokeInvite(params) {
53084
+ const api = await createApiClient2();
53085
+ const target = await resolveScopeTarget2(api, {
53086
+ scope: params.scope ?? "project",
53087
+ targetId: params.targetId,
53088
+ cwd: params.cwd
53089
+ });
53090
+ if (target.scopeType === "organization") {
53091
+ await api.revokeOrganizationInvite(target.targetId, params.inviteId);
53092
+ } else if (target.scopeType === "project") {
53093
+ await api.revokeProjectInvite(target.targetId, params.inviteId);
53094
+ } else {
53095
+ await api.revokeAppInvite(target.targetId, params.inviteId);
53096
+ }
53097
+ return {
53098
+ data: {
53099
+ scopeType: target.scopeType,
53100
+ targetId: target.targetId,
53101
+ inviteId: params.inviteId,
53102
+ revoked: true
53103
+ },
53104
+ warnings: [],
53105
+ recommendedNextActions: ["Use `remix_collab_list_invites` to confirm the invite state is now `revoked`."],
53106
+ logContext: {
53107
+ repoRoot: target.repoRoot
53108
+ }
53109
+ };
53110
+ }
53111
+ async function acceptInvitation(params) {
53112
+ const api = await createApiClient2();
53113
+ const result = unwrapResponseObject2(await api.acceptInvitation({ token: params.token }), "accepted invitation");
53114
+ return {
53115
+ data: result,
53116
+ warnings: [],
53117
+ recommendedNextActions: result.redirectPath ? [`The invitation is accepted. Use the returned redirect metadata to continue with the newly granted Remix scope.`] : [],
53118
+ logContext: {}
53119
+ };
53120
+ }
53121
+ async function accessDebug(params) {
53122
+ const api = await createApiClient2();
53123
+ const target = await resolveScopeTarget2(api, {
53124
+ scope: params.scope ?? "project",
53125
+ targetId: params.targetId,
53126
+ cwd: params.cwd
53127
+ });
53128
+ const bindingContext = await loadBindingContext(params.cwd);
53129
+ const viewer = unwrapResponseObject2(await api.getMe(), "current user");
53130
+ const viewerId = typeof viewer.id === "string" ? viewer.id : null;
53131
+ const viewerEmail = typeof viewer.email === "string" ? viewer.email.toLowerCase() : null;
53132
+ const warnings = [...bindingContext.binding ? [] : ["No local Remix binding was detected for the provided cwd."]];
53133
+ let entities;
53134
+ let appContext = null;
53135
+ let membersPage = emptyFirstPageSample();
53136
+ let invitesPage = emptyFirstPageSample();
53137
+ let viewerMember = null;
53138
+ let viewerProjectMember = null;
53139
+ let inviteForViewerEmail = null;
53140
+ let effectiveAppAccess = null;
53141
+ if (target.scopeType !== "app") {
53142
+ [entities, membersPage, invitesPage] = await Promise.all([
53143
+ getScopeEntity(api, target.scopeType, target.targetId),
53144
+ loadFirstPageSample((limit, offset) => listMembersForScope(api, target.scopeType, target.targetId, { limit, offset })),
53145
+ loadFirstPageSample((limit, offset) => listInvitesForScope(api, target.scopeType, target.targetId, { limit, offset }))
53146
+ ]);
53147
+ viewerMember = await findSelfMemberForScope(api, target.scopeType, target.targetId, viewerId);
53148
+ inviteForViewerEmail = await findPendingInviteForEmailForScope(api, target.scopeType, target.targetId, viewerEmail);
53149
+ } else {
53150
+ const [app, loadedAppContext] = await Promise.all([
53151
+ unwrapResponseObject2(await api.getApp(target.targetId), "app"),
53152
+ unwrapResponseObject2(await api.getAppContext(target.targetId), "app context")
53153
+ ]);
53154
+ appContext = loadedAppContext;
53155
+ entities = {
53156
+ organization: null,
53157
+ project: null,
53158
+ app
53159
+ };
53160
+ if (appContext.readableScopes.project) {
53161
+ try {
53162
+ entities.project = unwrapResponseObject2(await api.getProject(appContext.projectId), "project");
53163
+ } catch (error2) {
53164
+ if (!isBackendForbidden(error2) && !isBackendNotFound(error2)) throw error2;
53165
+ warnings.push("The app is readable, but its project/workspace metadata is not currently readable to the current user.");
53166
+ }
53167
+ } else {
53168
+ warnings.push("The app is readable, but its project/workspace is not readable to the current user.");
53169
+ }
53170
+ if (appContext.readableScopes.organization) {
53171
+ try {
53172
+ entities.organization = unwrapResponseObject2(await api.getOrganization(appContext.organizationId), "organization");
53173
+ } catch (error2) {
53174
+ if (!isBackendForbidden(error2) && !isBackendNotFound(error2)) throw error2;
53175
+ warnings.push("The app is readable, but its organization metadata is not currently readable to the current user.");
53176
+ }
53177
+ } else {
53178
+ warnings.push("The app's organization is not readable to the current user.");
53179
+ }
53180
+ effectiveAppAccess = {
53181
+ ...buildEffectiveAppAccess({
53182
+ directAppRole: appContext.roles.appRole,
53183
+ projectRole: appContext.roles.projectRole
53184
+ }),
53185
+ accessPath: appContext.accessPath,
53186
+ readableScopes: appContext.readableScopes,
53187
+ visibility: appContext.visibility,
53188
+ organizationRole: appContext.roles.organizationRole ?? null
53189
+ };
53190
+ if (appContext.capabilities.canReadWorkflow) {
53191
+ membersPage = await loadFirstPageSample((limit, offset) => listMembersForScope(api, "app", target.targetId, { limit, offset }));
53192
+ viewerMember = await findSelfMemberForScope(api, "app", target.targetId, viewerId);
53193
+ if (appContext.readableScopes.project) {
53194
+ viewerProjectMember = await findSelfMemberForScope(api, "project", appContext.projectId, viewerId);
53195
+ }
53196
+ try {
53197
+ invitesPage = await loadFirstPageSample((limit, offset) => listInvitesForScope(api, "app", target.targetId, { limit, offset }));
53198
+ inviteForViewerEmail = await findPendingInviteForEmailForScope(api, "app", target.targetId, viewerEmail);
53199
+ } catch (error2) {
53200
+ if (!isBackendForbidden(error2) && !isBackendNotFound(error2)) throw error2;
53201
+ warnings.push("App invite data is not readable to the current user, so invite diagnostics are partial.");
53202
+ }
53203
+ } else {
53204
+ warnings.push("The current viewer can read the app, but cannot read workflow-scoped app membership data.");
53205
+ }
53206
+ }
53207
+ if (membersPage.pageInfo.hasMore) {
53208
+ warnings.push("The `members` array is a first-page sample only; use the scope member list tool to inspect the remaining entries.");
53209
+ }
53210
+ if (invitesPage.pageInfo.hasMore) {
53211
+ warnings.push("The `invites` array is a first-page sample only; use the invite list tool with pagination to inspect the remaining entries.");
53212
+ }
53213
+ return {
53214
+ data: {
53215
+ viewer,
53216
+ scope: {
53217
+ scopeType: target.scopeType,
53218
+ targetId: target.targetId
53219
+ },
53220
+ entities,
53221
+ binding: {
53222
+ repoRoot: bindingContext.repoRoot,
53223
+ binding: bindingContext.binding
53224
+ },
53225
+ access: {
53226
+ appContext,
53227
+ viewerMember,
53228
+ viewerProjectMember,
53229
+ inviteForViewerEmail,
53230
+ effectiveAppAccess: effectiveAppAccess ?? null
53231
+ },
53232
+ members: membersPage.items,
53233
+ membersPageInfo: membersPage.pageInfo,
53234
+ invites: invitesPage.items,
53235
+ invitesPageInfo: invitesPage.pageInfo
53236
+ },
53237
+ warnings,
53238
+ recommendedNextActions: inviteForViewerEmail ? ["A pending invite exists for the current viewer email. Use `remix_collab_accept_invitation` if the viewer should accept it."] : target.scopeType === "app" ? ["Use `access.appContext` and `access.effectiveAppAccess` together to explain app-level visibility versus workspace-level visibility."] : ["Use the member and invite lists above to explain missing access, role mismatches, or stale invite state."],
53239
+ logContext: {
53240
+ repoRoot: target.repoRoot,
53241
+ appId: entities.app?.id ?? bindingContext.binding?.currentAppId ?? null
53242
+ }
53243
+ };
53244
+ }
53245
+ function getAnnotations(access, options) {
53246
+ if (access === "read") {
53247
+ return {
53248
+ readOnlyHint: true,
53249
+ idempotentHint: true,
53250
+ openWorldHint: false
53251
+ };
53252
+ }
53253
+ return {
53254
+ readOnlyHint: false,
53255
+ destructiveHint: access === "local_write",
53256
+ idempotentHint: options?.idempotent ?? false,
53257
+ openWorldHint: false
53258
+ };
53259
+ }
53260
+ function buildSuccessEnvelope(tool, requestId, result) {
53261
+ return {
53262
+ schemaVersion: SCHEMA_VERSION,
53263
+ ok: true,
53264
+ tool,
53265
+ requestId: requestId ?? null,
53266
+ data: result.data,
53267
+ warnings: result.warnings ?? [],
52438
53268
  risks: result.risks ?? [],
52439
53269
  recommendedNextActions: result.recommendedNextActions ?? []
52440
53270
  };
52441
53271
  }
52442
53272
  function deriveErrorRisks(tool, normalized) {
52443
- if ((tool === "remix_collab_add" || tool === "remix_collab_add_change_step") && normalized.message === "Change step succeeded remotely, but automatic local sync failed.") {
53273
+ if (isFinalizeTurnLocalSyncFailure(tool, normalized)) {
52444
53274
  return ["The change step succeeded remotely, but the local repository may need manual recovery or a follow-up sync."];
52445
53275
  }
52446
53276
  if (normalized.code === "DESTRUCTIVE_OPERATION_BLOCKED") {
@@ -52454,9 +53284,16 @@ function deriveErrorRisks(tool, normalized) {
52454
53284
  }
52455
53285
  return [];
52456
53286
  }
53287
+ function isFinalizeTurnLocalSyncFailure(tool, normalized) {
53288
+ return tool === "remix_collab_finalize_turn" && normalized.message === "Change step succeeded remotely, but automatic local sync failed.";
53289
+ }
52457
53290
  function buildErrorEnvelope(tool, requestId, error2) {
52458
53291
  const normalized = normalizeToolError(error2);
52459
- 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."] : [];
53292
+ const recommendedNextActions = isFinalizeTurnLocalSyncFailure(tool, normalized) ? [
53293
+ "Run `remix_collab_status` to confirm the bound repo state before attempting recovery.",
53294
+ "Run `remix_collab_sync_preview` next, then `remix_collab_sync_apply` with `confirm=true` if the preview looks correct.",
53295
+ "Inspect `error.hint` for any preserved diff backup path before retrying local recovery, and do not rerun `remix_collab_finalize_turn` immediately."
53296
+ ] : 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."] : [];
52460
53297
  return {
52461
53298
  schemaVersion: SCHEMA_VERSION,
52462
53299
  ok: false,
@@ -52477,7 +53314,7 @@ function registerTool(server, context, params) {
52477
53314
  description: params.description,
52478
53315
  inputSchema: params.inputSchema,
52479
53316
  outputSchema: params.outputSchema,
52480
- annotations: getAnnotations(params.access)
53317
+ annotations: params.annotations ?? getAnnotations(params.access)
52481
53318
  },
52482
53319
  async (rawArgs) => {
52483
53320
  const requestId = typeof rawArgs.requestId === "string" ? rawArgs.requestId : void 0;
@@ -52557,7 +53394,11 @@ function registerCollabTools(server, context) {
52557
53394
  outputSchema: listSuccessSchema,
52558
53395
  run: async (args) => {
52559
53396
  const input = external_exports.object(listInputSchema).parse(args);
52560
- return listApps({ forked: input.forked });
53397
+ return listApps({
53398
+ forked: input.forked,
53399
+ limit: input.limit,
53400
+ offset: input.offset
53401
+ });
52561
53402
  }
52562
53403
  });
52563
53404
  registerTool(server, context, {
@@ -52594,89 +53435,25 @@ function registerCollabTools(server, context) {
52594
53435
  }
52595
53436
  });
52596
53437
  registerTool(server, context, {
52597
- name: "remix_collab_add",
52598
- description: "Authoritative way to record completed code changes for the current bound repository, using the live worktree diff by default instead of raw git commit or push.",
52599
- access: "local_write",
52600
- inputSchema: addInputSchema,
52601
- outputSchema: addSuccessSchema,
52602
- run: async (args) => {
52603
- const input = external_exports.object(addInputSchema).parse(args);
52604
- const cwd = resolvePolicyCwd(context.policy, input.cwd);
52605
- const diffSource = input.diffSource ?? "worktree";
52606
- if (diffSource === "external") {
52607
- const externalDiff = input.externalDiff ?? "";
52608
- assertDiffWithinLimit(context.policy, externalDiff);
52609
- }
52610
- return addCollabStep({
52611
- cwd,
52612
- prompt: input.prompt,
52613
- assistantResponse: input.assistantResponse,
52614
- diffSource,
52615
- externalDiff: input.externalDiff,
52616
- allowBranchMismatch: input.allowBranchMismatch ?? false,
52617
- idempotencyKey: input.idempotencyKey,
52618
- agent: context.agentMetadata
52619
- });
52620
- }
52621
- });
52622
- registerTool(server, context, {
52623
- name: "remix_collab_add_change_step",
52624
- description: "Alias of remix_collab_add with a more explicit name: record a code-diff change step for the current bound repository.",
53438
+ name: "remix_collab_finalize_turn",
53439
+ description: "Primary turn recorder for the current bound repository. Call this exactly once before the final response; it records a changed turn when the worktree has a diff, records a no-diff turn when it does not, and can accept an explicit external diff when needed.",
52625
53440
  access: "local_write",
52626
- inputSchema: addInputSchema,
52627
- outputSchema: addSuccessSchema,
53441
+ inputSchema: finalizeTurnInputSchema,
53442
+ outputSchema: finalizeTurnSuccessSchema,
53443
+ annotations: getAnnotations("local_write", { idempotent: true }),
52628
53444
  run: async (args) => {
52629
- const input = external_exports.object(addInputSchema).parse(args);
53445
+ const input = external_exports.object(finalizeTurnInputSchema).parse(args);
52630
53446
  const cwd = resolvePolicyCwd(context.policy, input.cwd);
52631
- const diffSource = input.diffSource ?? "worktree";
52632
- if (diffSource === "external") {
52633
- const externalDiff = input.externalDiff ?? "";
52634
- assertDiffWithinLimit(context.policy, externalDiff);
53447
+ if ((input.diffSource ?? "worktree") === "external" || typeof input.externalDiff === "string") {
53448
+ assertDiffWithinLimit(context.policy, input.externalDiff ?? "");
52635
53449
  }
52636
- return addCollabStep({
53450
+ return finalizeCollabTurn({
52637
53451
  cwd,
52638
53452
  prompt: input.prompt,
52639
53453
  assistantResponse: input.assistantResponse,
52640
- diffSource,
53454
+ diffSource: input.diffSource,
52641
53455
  externalDiff: input.externalDiff,
52642
- allowBranchMismatch: input.allowBranchMismatch ?? false,
52643
- idempotencyKey: input.idempotencyKey,
52644
- agent: context.agentMetadata
52645
- });
52646
- }
52647
- });
52648
- registerTool(server, context, {
52649
- name: "remix_collab_record_turn",
52650
- description: "Record one no-diff collaboration turn for the current bound repository after a completed assistant response. This is for prompt/response history only and will fail if the worktree has code changes.",
52651
- access: "remote_write",
52652
- inputSchema: recordTurnInputSchema,
52653
- outputSchema: recordTurnSuccessSchema,
52654
- run: async (args) => {
52655
- const input = external_exports.object(recordTurnInputSchema).parse(args);
52656
- const cwd = resolvePolicyCwd(context.policy, input.cwd);
52657
- return recordCollabTurn({
52658
- cwd,
52659
- prompt: input.prompt,
52660
- assistantResponse: input.assistantResponse,
52661
- allowBranchMismatch: input.allowBranchMismatch ?? false,
52662
- idempotencyKey: input.idempotencyKey,
52663
- agent: context.agentMetadata
52664
- });
52665
- }
52666
- });
52667
- registerTool(server, context, {
52668
- name: "remix_collab_record_no_diff_turn",
52669
- description: "Alias of remix_collab_record_turn with a more explicit name: record a prompt/response turn only when the worktree has no code diff.",
52670
- access: "remote_write",
52671
- inputSchema: recordTurnInputSchema,
52672
- outputSchema: recordTurnSuccessSchema,
52673
- run: async (args) => {
52674
- const input = external_exports.object(recordTurnInputSchema).parse(args);
52675
- const cwd = resolvePolicyCwd(context.policy, input.cwd);
52676
- return recordCollabTurn({
52677
- cwd,
52678
- prompt: input.prompt,
52679
- assistantResponse: input.assistantResponse,
53456
+ sync: input.sync,
52680
53457
  allowBranchMismatch: input.allowBranchMismatch ?? false,
52681
53458
  idempotencyKey: input.idempotencyKey,
52682
53459
  agent: context.agentMetadata
@@ -52728,7 +53505,12 @@ function registerCollabTools(server, context) {
52728
53505
  outputSchema: mergeRequestQueueSuccessSchema,
52729
53506
  run: async (args) => {
52730
53507
  const input = external_exports.object(reviewQueueInputSchema).parse(args);
52731
- return reviewQueue({ status: input.status, kind: input.kind });
53508
+ return reviewQueue({
53509
+ status: input.status,
53510
+ kind: input.kind,
53511
+ limit: input.limit,
53512
+ offset: input.offset
53513
+ });
52732
53514
  }
52733
53515
  });
52734
53516
  registerTool(server, context, {
@@ -52739,7 +53521,12 @@ function registerCollabTools(server, context) {
52739
53521
  outputSchema: mergeRequestQueueSuccessSchema,
52740
53522
  run: async (args) => {
52741
53523
  const input = external_exports.object(myMergeRequestsInputSchema).parse(args);
52742
- return myMergeRequests({ status: input.status, kind: input.kind });
53524
+ return myMergeRequests({
53525
+ status: input.status,
53526
+ kind: input.kind,
53527
+ limit: input.limit,
53528
+ offset: input.offset
53529
+ });
52743
53530
  }
52744
53531
  });
52745
53532
  registerTool(server, context, {
@@ -52756,7 +53543,9 @@ function registerCollabTools(server, context) {
52756
53543
  appId: input.appId,
52757
53544
  queue: input.queue,
52758
53545
  status: input.status,
52759
- kind: input.kind
53546
+ kind: input.kind,
53547
+ limit: input.limit,
53548
+ offset: input.offset
52760
53549
  });
52761
53550
  }
52762
53551
  });
@@ -52890,33 +53679,665 @@ function registerCollabTools(server, context) {
52890
53679
  return listMembers({
52891
53680
  cwd,
52892
53681
  scope: input.scope,
52893
- targetId: input.targetId
53682
+ targetId: input.targetId,
53683
+ limit: input.limit,
53684
+ offset: input.offset
52894
53685
  });
52895
53686
  }
52896
53687
  });
52897
53688
  registerTool(server, context, {
52898
- name: "remix_collab_update_member_role",
52899
- description: "Update an organization, project, or app member role, using the current repository binding unless targetId is provided.",
53689
+ name: "remix_collab_list_invites",
53690
+ description: "List invitations for an organization, project, or app, using the current repository binding unless targetId is provided.",
53691
+ access: "read",
53692
+ inputSchema: listInvitesInputSchema,
53693
+ outputSchema: listInvitesSuccessSchema,
53694
+ run: async (args) => {
53695
+ const input = external_exports.object(listInvitesInputSchema).parse(args);
53696
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
53697
+ return listInvites({
53698
+ cwd,
53699
+ scope: input.scope ?? "project",
53700
+ targetId: input.targetId,
53701
+ limit: input.limit,
53702
+ offset: input.offset
53703
+ });
53704
+ }
53705
+ });
53706
+ registerTool(server, context, {
53707
+ name: "remix_collab_resend_invite",
53708
+ description: "Resend an existing invitation for an organization, project, or app, using the current repository binding unless targetId is provided.",
52900
53709
  access: "remote_write",
52901
- inputSchema: updateMemberRoleInputSchema,
52902
- outputSchema: updateMemberRoleSuccessSchema,
53710
+ inputSchema: resendInviteInputSchema,
53711
+ outputSchema: resendInviteSuccessSchema,
52903
53712
  run: async (args) => {
52904
- const input = external_exports.object(updateMemberRoleInputSchema).parse(args);
52905
- const cwd = resolvePolicyCwd(context.policy, input.cwd);
52906
- return updateMemberRole({
53713
+ const input = external_exports.object(resendInviteInputSchema).parse(args);
53714
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
53715
+ return resendInvite({
52907
53716
  cwd,
52908
- scope: input.scope,
53717
+ scope: input.scope ?? "project",
52909
53718
  targetId: input.targetId,
52910
- userId: input.userId,
52911
- role: input.role
53719
+ inviteId: input.inviteId,
53720
+ ttlDays: input.ttlDays
52912
53721
  });
52913
53722
  }
52914
53723
  });
52915
- }
52916
- var genericRecordSchema2 = external_exports.record(external_exports.string(), external_exports.unknown());
53724
+ registerTool(server, context, {
53725
+ name: "remix_collab_revoke_invite",
53726
+ description: "Revoke an existing invitation for an organization, project, or app, using the current repository binding unless targetId is provided.",
53727
+ access: "remote_write",
53728
+ inputSchema: revokeInviteInputSchema,
53729
+ outputSchema: revokeInviteSuccessSchema,
53730
+ run: async (args) => {
53731
+ const input = external_exports.object(revokeInviteInputSchema).parse(args);
53732
+ assertConfirm(input.confirm, "remix_collab_revoke_invite");
53733
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
53734
+ return revokeInvite({
53735
+ cwd,
53736
+ scope: input.scope ?? "project",
53737
+ targetId: input.targetId,
53738
+ inviteId: input.inviteId
53739
+ });
53740
+ }
53741
+ });
53742
+ registerTool(server, context, {
53743
+ name: "remix_collab_accept_invitation",
53744
+ description: "Accept an invitation token for the currently authenticated Remix user.",
53745
+ access: "remote_write",
53746
+ inputSchema: acceptInvitationInputSchema,
53747
+ outputSchema: acceptInvitationSuccessSchema,
53748
+ annotations: getAnnotations("remote_write", { idempotent: true }),
53749
+ run: async (args) => {
53750
+ const input = external_exports.object(acceptInvitationInputSchema).parse(args);
53751
+ return acceptInvitation({
53752
+ token: input.token
53753
+ });
53754
+ }
53755
+ });
53756
+ registerTool(server, context, {
53757
+ name: "remix_access_debug",
53758
+ description: "Explain why the current user does or does not have access to an organization, project, or app by composing binding, membership, invite, and scope context.",
53759
+ access: "read",
53760
+ inputSchema: accessDebugInputSchema,
53761
+ outputSchema: accessDebugSuccessSchema,
53762
+ run: async (args) => {
53763
+ const input = external_exports.object(accessDebugInputSchema).parse(args);
53764
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
53765
+ return accessDebug({
53766
+ cwd,
53767
+ scope: input.scope ?? "project",
53768
+ targetId: input.targetId
53769
+ });
53770
+ }
53771
+ });
53772
+ registerTool(server, context, {
53773
+ name: "remix_collab_update_member_role",
53774
+ description: "Update an organization, project, or app member role, using the current repository binding unless targetId is provided.",
53775
+ access: "remote_write",
53776
+ inputSchema: updateMemberRoleInputSchema,
53777
+ outputSchema: updateMemberRoleSuccessSchema,
53778
+ run: async (args) => {
53779
+ const input = external_exports.object(updateMemberRoleInputSchema).parse(args);
53780
+ const cwd = resolvePolicyCwd(context.policy, input.cwd);
53781
+ return updateMemberRole({
53782
+ cwd,
53783
+ scope: input.scope,
53784
+ targetId: input.targetId,
53785
+ userId: input.userId,
53786
+ role: input.role
53787
+ });
53788
+ }
53789
+ });
53790
+ }
53791
+ var genericRecordSchema2 = external_exports.record(external_exports.string(), external_exports.unknown());
52917
53792
  var genericArraySchema2 = external_exports.array(genericRecordSchema2);
53793
+ var paginationSchema2 = external_exports.object({
53794
+ limit: external_exports.number().int().positive(),
53795
+ offset: external_exports.number().int().nonnegative(),
53796
+ hasMore: external_exports.boolean()
53797
+ });
53798
+ var whoamiInputSchema = {
53799
+ ...commonRequestFieldsSchema
53800
+ };
53801
+ var directoryOrganizationInputSchema = {
53802
+ ...commonRequestFieldsSchema,
53803
+ organizationId: external_exports.string().trim().min(1).optional()
53804
+ };
53805
+ var directoryListProjectsInputSchema = {
53806
+ ...commonRequestFieldsSchema,
53807
+ organizationId: external_exports.string().trim().min(1).optional(),
53808
+ clientAppId: external_exports.string().trim().min(1).optional()
53809
+ };
53810
+ var directoryProjectInputSchema = {
53811
+ ...commonRequestFieldsSchema,
53812
+ projectId: external_exports.string().trim().min(1).optional()
53813
+ };
53814
+ var directoryListAppsInputSchema = {
53815
+ ...commonRequestFieldsSchema,
53816
+ projectId: external_exports.string().trim().min(1).optional(),
53817
+ organizationId: external_exports.string().trim().min(1).optional(),
53818
+ forked: external_exports.enum(["only", "exclude", "all"]).optional(),
53819
+ limit: external_exports.number().int().positive().max(50).optional(),
53820
+ offset: external_exports.number().int().nonnegative().optional()
53821
+ };
53822
+ var directoryAppInputSchema = {
53823
+ ...commonRequestFieldsSchema,
53824
+ appId: external_exports.string().trim().min(1).optional()
53825
+ };
53826
+ var whoamiDataSchema = external_exports.object({
53827
+ id: external_exports.string().nullable(),
53828
+ name: external_exports.string().nullable(),
53829
+ email: external_exports.string().nullable(),
53830
+ organizationId: external_exports.string().nullable(),
53831
+ roles: genericRecordSchema2,
53832
+ binding: genericRecordSchema2.nullable()
53833
+ });
53834
+ var listOrganizationsDataSchema = external_exports.object({
53835
+ organizations: genericArraySchema2
53836
+ });
53837
+ var getOrganizationDataSchema = external_exports.object({
53838
+ organization: genericRecordSchema2
53839
+ });
53840
+ var listProjectsDataSchema = external_exports.object({
53841
+ organizationId: external_exports.string().nullable(),
53842
+ projects: genericArraySchema2
53843
+ });
53844
+ var getProjectDataSchema = external_exports.object({
53845
+ project: genericRecordSchema2
53846
+ });
53847
+ var listAppsDataSchema = external_exports.object({
53848
+ apps: genericArraySchema2,
53849
+ pagination: paginationSchema2,
53850
+ filters: external_exports.object({
53851
+ projectId: external_exports.string().nullable(),
53852
+ organizationId: external_exports.string().nullable(),
53853
+ forked: external_exports.enum(["only", "exclude", "all"])
53854
+ })
53855
+ });
53856
+ var getAppDataSchema = external_exports.object({
53857
+ app: genericRecordSchema2
53858
+ });
53859
+ var whoamiSuccessSchema = makeSuccessSchema(whoamiDataSchema);
53860
+ var listOrganizationsSuccessSchema = makeSuccessSchema(listOrganizationsDataSchema);
53861
+ var getOrganizationSuccessSchema = makeSuccessSchema(getOrganizationDataSchema);
53862
+ var listProjectsSuccessSchema = makeSuccessSchema(listProjectsDataSchema);
53863
+ var getProjectSuccessSchema = makeSuccessSchema(getProjectDataSchema);
53864
+ var listAppsSuccessSchema = makeSuccessSchema(listAppsDataSchema);
53865
+ var getAppSuccessSchema = makeSuccessSchema(getAppDataSchema);
53866
+ function normalizePagination22(params) {
53867
+ const rawLimit = typeof params?.limit === "number" ? Math.trunc(params.limit) : 25;
53868
+ const rawOffset = typeof params?.offset === "number" ? Math.trunc(params.offset) : 0;
53869
+ return {
53870
+ limit: Math.max(1, Math.min(50, rawLimit)),
53871
+ offset: Math.max(0, rawOffset)
53872
+ };
53873
+ }
53874
+ async function resolveOrganizationId(api, params) {
53875
+ const explicitId = params.organizationId?.trim();
53876
+ if (explicitId) return explicitId;
53877
+ const bindingContext = await loadBindingContext(params.cwd);
53878
+ if (!bindingContext.binding) {
53879
+ throw makeNotBoundError("Organization id was not provided and the current repository is not bound to Remix.");
53880
+ }
53881
+ const appContext = unwrapResponseObject2(
53882
+ await api.getAppContext(bindingContext.binding.currentAppId),
53883
+ "bound app context"
53884
+ );
53885
+ if (!appContext.readableScopes.organization) {
53886
+ throw createAccessDeniedError(
53887
+ "The bound app's organization is not readable to the current user.",
53888
+ "Use an explicit organization id you can access, or inspect the bound app with `remix_directory_get_app` / `remix_access_debug` for app-level context."
53889
+ );
53890
+ }
53891
+ return appContext.organizationId;
53892
+ }
53893
+ async function resolveProjectId(api, params) {
53894
+ const explicitId = params.projectId?.trim();
53895
+ if (explicitId) {
53896
+ const bindingContext2 = await loadBindingContext(params.cwd);
53897
+ return { projectId: explicitId, repoRoot: bindingContext2.repoRoot };
53898
+ }
53899
+ const bindingContext = await loadBindingContext(params.cwd);
53900
+ if (!bindingContext.binding) {
53901
+ throw makeNotBoundError("Project id was not provided and the current repository is not bound to Remix.");
53902
+ }
53903
+ const appContext = unwrapResponseObject2(
53904
+ await api.getAppContext(bindingContext.binding.currentAppId),
53905
+ "bound app context"
53906
+ );
53907
+ if (!appContext.readableScopes.project) {
53908
+ throw createAccessDeniedError(
53909
+ "The bound app's project is not readable to the current user.",
53910
+ "Use `remix_directory_get_app` or `remix_access_debug` for app-level diagnostics, or pass an explicit project id you can access."
53911
+ );
53912
+ }
53913
+ return {
53914
+ projectId: appContext.projectId,
53915
+ repoRoot: bindingContext.repoRoot
53916
+ };
53917
+ }
53918
+ async function resolveAppId(params) {
53919
+ const explicitId = params.appId?.trim();
53920
+ if (explicitId) {
53921
+ const bindingContext2 = await loadBindingContext(params.cwd);
53922
+ return { appId: explicitId, repoRoot: bindingContext2.repoRoot };
53923
+ }
53924
+ const bindingContext = await loadBindingContext(params.cwd);
53925
+ if (!bindingContext.binding) {
53926
+ throw makeNotBoundError("App id was not provided and the current repository is not bound to Remix.");
53927
+ }
53928
+ return {
53929
+ appId: bindingContext.binding.currentAppId,
53930
+ repoRoot: bindingContext.repoRoot
53931
+ };
53932
+ }
53933
+ function toEffectiveAppRole(role) {
53934
+ switch (role) {
53935
+ case "owner":
53936
+ case "maintainer":
53937
+ case "editor":
53938
+ case "viewer":
53939
+ return role;
53940
+ default:
53941
+ return null;
53942
+ }
53943
+ }
53944
+ function resolveAppAccessSource2(params) {
53945
+ if (params.directAppRole && params.inheritedProjectRole) return "both";
53946
+ if (params.directAppRole) return "direct_app_membership";
53947
+ if (params.inheritedProjectRole) return "project_membership";
53948
+ return "none";
53949
+ }
53950
+ function emptySelfRoles() {
53951
+ return {
53952
+ organizationRole: null,
53953
+ projectRole: null,
53954
+ appRole: null,
53955
+ directAppRole: null,
53956
+ inheritedProjectRole: null,
53957
+ appAccessSource: "none"
53958
+ };
53959
+ }
53960
+ function resolveSelfRolesFromAppContext(appContext) {
53961
+ if (!appContext) return emptySelfRoles();
53962
+ const directAppRole = toEffectiveAppRole(appContext.roles.appRole);
53963
+ const inheritedProjectRole = toEffectiveAppRole(appContext.roles.inheritedProjectRole);
53964
+ return {
53965
+ organizationRole: appContext.roles.organizationRole ?? null,
53966
+ projectRole: appContext.roles.projectRole ?? null,
53967
+ appRole: toEffectiveAppRole(appContext.roles.effectiveAppRole),
53968
+ directAppRole,
53969
+ inheritedProjectRole,
53970
+ appAccessSource: resolveAppAccessSource2({ directAppRole, inheritedProjectRole })
53971
+ };
53972
+ }
53973
+ async function whoAmI(params) {
53974
+ const api = await createApiClient2();
53975
+ const bindingContext = await loadBindingContext(params.cwd);
53976
+ const me = unwrapResponseObject2(await api.getMe(), "current user");
53977
+ const warnings = bindingContext.binding ? [] : ["No local Remix binding was detected for the provided cwd."];
53978
+ let boundAppContext = null;
53979
+ let boundProject = null;
53980
+ let boundApp = null;
53981
+ if (bindingContext.binding) {
53982
+ try {
53983
+ boundAppContext = unwrapResponseObject2(
53984
+ await api.getAppContext(bindingContext.binding.currentAppId),
53985
+ "bound app context"
53986
+ );
53987
+ } catch (error2) {
53988
+ if (!isBackendForbidden(error2) && !isBackendNotFound(error2)) throw error2;
53989
+ warnings.push(
53990
+ "The bound app context could not be loaded. The local binding may be stale, or the current user may no longer be able to read that app."
53991
+ );
53992
+ }
53993
+ try {
53994
+ boundApp = unwrapResponseObject2(await api.getApp(bindingContext.binding.currentAppId), "app");
53995
+ } catch (error2) {
53996
+ if (!isBackendForbidden(error2) && !isBackendNotFound(error2)) throw error2;
53997
+ warnings.push("The bound app metadata could not be loaded for the current user.");
53998
+ }
53999
+ if (boundAppContext?.readableScopes.project) {
54000
+ try {
54001
+ boundProject = unwrapResponseObject2(await api.getProject(boundAppContext.projectId), "project");
54002
+ } catch (error2) {
54003
+ if (!isBackendForbidden(error2) && !isBackendNotFound(error2)) throw error2;
54004
+ warnings.push("The bound app is readable, but its project metadata is not currently readable to the current user.");
54005
+ }
54006
+ } else if (boundAppContext) {
54007
+ warnings.push("The bound app is readable, but its project/workspace is not readable to the current user.");
54008
+ }
54009
+ }
54010
+ const roles = resolveSelfRolesFromAppContext(boundAppContext);
54011
+ return {
54012
+ data: {
54013
+ id: me.id ?? null,
54014
+ name: me.name ?? null,
54015
+ email: me.email ?? null,
54016
+ organizationId: me.organizationId ?? null,
54017
+ roles,
54018
+ binding: bindingContext.binding == null ? null : {
54019
+ repoRoot: bindingContext.repoRoot,
54020
+ ...bindingContext.binding,
54021
+ projectId: boundAppContext?.projectId ?? bindingContext.binding.projectId,
54022
+ organizationId: boundAppContext?.organizationId ?? null,
54023
+ visibility: boundAppContext?.visibility ?? null,
54024
+ accessPath: boundAppContext?.accessPath ?? null,
54025
+ readableScopes: boundAppContext?.readableScopes ?? null,
54026
+ projectName: boundProject?.name ?? null,
54027
+ appName: boundApp?.name ?? null
54028
+ }
54029
+ },
54030
+ warnings,
54031
+ recommendedNextActions: bindingContext.binding ? ["Use `remix_access_debug` next when you need to explain repository binding or membership issues in this workspace."] : ["Use `remix_directory_list_organizations` to inspect visible tenancy context, or run `remix_collab_init` in a repository to create a local binding."],
54032
+ logContext: {
54033
+ repoRoot: bindingContext.repoRoot,
54034
+ appId: bindingContext.binding?.currentAppId ?? null
54035
+ }
54036
+ };
54037
+ }
54038
+ async function listOrganizations() {
54039
+ const api = await createApiClient2();
54040
+ const organizations = unwrapResponseObject2(await api.listOrganizations(), "organizations");
54041
+ return {
54042
+ data: { organizations },
54043
+ warnings: [],
54044
+ recommendedNextActions: organizations.length ? ["Use `remix_directory_get_organization` for one organization, or `remix_directory_list_projects` to inspect projects under a chosen organization."] : [],
54045
+ logContext: {}
54046
+ };
54047
+ }
54048
+ async function getOrganization(params) {
54049
+ const api = await createApiClient2();
54050
+ const organizationId = await resolveOrganizationId(api, params);
54051
+ const bindingContext = await loadBindingContext(params.cwd);
54052
+ const organization = unwrapResponseObject2(await api.getOrganization(organizationId), "organization");
54053
+ return {
54054
+ data: { organization },
54055
+ warnings: [],
54056
+ recommendedNextActions: ["Use `remix_directory_list_projects` with this organization id to inspect its project directory."],
54057
+ logContext: {
54058
+ repoRoot: bindingContext.repoRoot
54059
+ }
54060
+ };
54061
+ }
54062
+ async function listProjects(params) {
54063
+ const api = await createApiClient2();
54064
+ const bindingContext = await loadBindingContext(params.cwd);
54065
+ const organizationId = params.organizationId !== void 0 ? await resolveOrganizationId(api, { organizationId: params.organizationId, cwd: params.cwd }) : null;
54066
+ const projects = unwrapResponseObject2(
54067
+ await api.listProjects({
54068
+ organizationId: organizationId ?? void 0,
54069
+ clientAppId: params.clientAppId
54070
+ }),
54071
+ "projects"
54072
+ );
54073
+ return {
54074
+ data: {
54075
+ projects,
54076
+ organizationId
54077
+ },
54078
+ warnings: [],
54079
+ recommendedNextActions: projects.length ? ["Use `remix_directory_get_project` for one project, or `remix_directory_list_apps` to inspect apps under a chosen project or organization."] : [],
54080
+ logContext: {
54081
+ repoRoot: bindingContext.repoRoot,
54082
+ appId: bindingContext.binding?.currentAppId ?? null
54083
+ }
54084
+ };
54085
+ }
54086
+ async function getProject(params) {
54087
+ const api = await createApiClient2();
54088
+ const target = await resolveProjectId(api, params);
54089
+ const project = unwrapResponseObject2(await api.getProject(target.projectId), "project");
54090
+ return {
54091
+ data: { project },
54092
+ warnings: [],
54093
+ recommendedNextActions: ["Use `remix_directory_list_apps` with this project id to inspect apps under the project."],
54094
+ logContext: {
54095
+ repoRoot: target.repoRoot
54096
+ }
54097
+ };
54098
+ }
54099
+ async function listApps2(params) {
54100
+ const api = await createApiClient2();
54101
+ const bindingContext = await loadBindingContext(params.cwd);
54102
+ const pagination = normalizePagination22(params);
54103
+ const apps = unwrapResponseObject2(
54104
+ await api.listApps({
54105
+ projectId: params.projectId,
54106
+ organizationId: params.organizationId,
54107
+ forked: params.forked,
54108
+ limit: pagination.limit + 1,
54109
+ offset: pagination.offset
54110
+ }),
54111
+ "apps"
54112
+ );
54113
+ return {
54114
+ data: {
54115
+ apps: apps.slice(0, pagination.limit),
54116
+ pagination: {
54117
+ ...pagination,
54118
+ hasMore: apps.length > pagination.limit
54119
+ },
54120
+ filters: {
54121
+ projectId: params.projectId ?? null,
54122
+ organizationId: params.organizationId ?? null,
54123
+ forked: params.forked ?? "all"
54124
+ }
54125
+ },
54126
+ warnings: [],
54127
+ recommendedNextActions: apps.length > pagination.limit ? [`Pass offset=${pagination.offset + pagination.limit} to load the next page.`] : ["Use `remix_directory_get_app` for one app, or `remix_context_get_app_overview` for operational context on a chosen app."],
54128
+ logContext: {
54129
+ repoRoot: bindingContext.repoRoot,
54130
+ appId: bindingContext.binding?.currentAppId ?? null
54131
+ }
54132
+ };
54133
+ }
54134
+ async function getApp(params) {
54135
+ const api = await createApiClient2();
54136
+ const target = await resolveAppId(params);
54137
+ const app = unwrapResponseObject2(await api.getApp(target.appId), "app");
54138
+ return {
54139
+ data: { app },
54140
+ warnings: [],
54141
+ recommendedNextActions: ["Use `remix_context_get_app_overview` for status and capability context, or `remix_ops_list_timeline` for bounded historical activity."],
54142
+ logContext: {
54143
+ repoRoot: target.repoRoot,
54144
+ appId: target.appId
54145
+ }
54146
+ };
54147
+ }
54148
+ function getAnnotations2(access) {
54149
+ return {
54150
+ readOnlyHint: access === "read",
54151
+ destructiveHint: false,
54152
+ idempotentHint: true,
54153
+ openWorldHint: false
54154
+ };
54155
+ }
54156
+ function buildSuccessEnvelope2(tool, requestId, result) {
54157
+ return {
54158
+ schemaVersion: SCHEMA_VERSION,
54159
+ ok: true,
54160
+ tool,
54161
+ requestId: requestId ?? null,
54162
+ data: result.data,
54163
+ warnings: result.warnings ?? [],
54164
+ risks: result.risks ?? [],
54165
+ recommendedNextActions: result.recommendedNextActions ?? []
54166
+ };
54167
+ }
54168
+ function deriveErrorRisks2(normalized) {
54169
+ if (normalized.code === "DESTRUCTIVE_OPERATION_BLOCKED") {
54170
+ return ["A policy guard blocked a disallowed operation."];
54171
+ }
54172
+ return [];
54173
+ }
54174
+ function buildErrorEnvelope2(tool, requestId, error2) {
54175
+ const normalized = normalizeToolError(error2);
54176
+ return {
54177
+ schemaVersion: SCHEMA_VERSION,
54178
+ ok: false,
54179
+ tool,
54180
+ requestId: requestId ?? null,
54181
+ error: normalized,
54182
+ warnings: [],
54183
+ risks: deriveErrorRisks2(normalized),
54184
+ recommendedNextActions: normalized.code === "AUTH_REQUIRED" ? ["Run `remix login` or set COMERGE_ACCESS_TOKEN, then retry."] : []
54185
+ };
54186
+ }
54187
+ function registerTool2(server, context, params) {
54188
+ const errorSchema = makeErrorSchema();
54189
+ server.registerTool(
54190
+ params.name,
54191
+ {
54192
+ title: params.name,
54193
+ description: params.description,
54194
+ inputSchema: params.inputSchema,
54195
+ outputSchema: params.outputSchema,
54196
+ annotations: getAnnotations2(params.access)
54197
+ },
54198
+ async (rawArgs) => {
54199
+ const requestId = typeof rawArgs.requestId === "string" ? rawArgs.requestId : void 0;
54200
+ const startedAt = Date.now();
54201
+ try {
54202
+ assertToolAccess(context.policy, params.access);
54203
+ const result = await params.run(rawArgs);
54204
+ const envelope = buildSuccessEnvelope2(params.name, requestId, result);
54205
+ params.outputSchema.parse(envelope);
54206
+ context.logger.log({
54207
+ level: "info",
54208
+ message: "tool_completed",
54209
+ tool: params.name,
54210
+ requestId: envelope.requestId,
54211
+ durationMs: Date.now() - startedAt,
54212
+ result: "success",
54213
+ repoRoot: result.logContext?.repoRoot ?? null,
54214
+ appId: result.logContext?.appId ?? null,
54215
+ mrId: result.logContext?.mrId ?? null
54216
+ });
54217
+ return makeSuccessResult2(envelope);
54218
+ } catch (error2) {
54219
+ const envelope = buildErrorEnvelope2(params.name, requestId, error2);
54220
+ errorSchema.parse(envelope);
54221
+ context.logger.log({
54222
+ level: "error",
54223
+ message: "tool_failed",
54224
+ tool: params.name,
54225
+ requestId: envelope.requestId,
54226
+ durationMs: Date.now() - startedAt,
54227
+ result: "error",
54228
+ errorCode: envelope.error.code
54229
+ });
54230
+ return makeErrorResult(envelope);
54231
+ }
54232
+ }
54233
+ );
54234
+ }
54235
+ function registerIdentityTools(server, context) {
54236
+ registerTool2(server, context, {
54237
+ name: "remix_identity_whoami",
54238
+ description: "Show the authenticated Remix user and, when cwd is provided, the current local binding and any immediately derivable roles for that bound workspace.",
54239
+ access: "read",
54240
+ inputSchema: whoamiInputSchema,
54241
+ outputSchema: whoamiSuccessSchema,
54242
+ run: async (args) => {
54243
+ const input = external_exports.object(whoamiInputSchema).parse(args);
54244
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
54245
+ return whoAmI({ cwd });
54246
+ }
54247
+ });
54248
+ registerTool2(server, context, {
54249
+ name: "remix_directory_list_organizations",
54250
+ description: "List organizations visible to the authenticated Remix user.",
54251
+ access: "read",
54252
+ inputSchema: whoamiInputSchema,
54253
+ outputSchema: listOrganizationsSuccessSchema,
54254
+ run: async () => listOrganizations()
54255
+ });
54256
+ registerTool2(server, context, {
54257
+ name: "remix_directory_get_organization",
54258
+ description: "Fetch one organization by id, or infer the bound repository's organization from cwd when possible.",
54259
+ access: "read",
54260
+ inputSchema: directoryOrganizationInputSchema,
54261
+ outputSchema: getOrganizationSuccessSchema,
54262
+ run: async (args) => {
54263
+ const input = external_exports.object(directoryOrganizationInputSchema).parse(args);
54264
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
54265
+ return getOrganization({
54266
+ organizationId: input.organizationId,
54267
+ cwd
54268
+ });
54269
+ }
54270
+ });
54271
+ registerTool2(server, context, {
54272
+ name: "remix_directory_list_projects",
54273
+ description: "List projects visible to the authenticated user, optionally narrowed to one organization or client app.",
54274
+ access: "read",
54275
+ inputSchema: directoryListProjectsInputSchema,
54276
+ outputSchema: listProjectsSuccessSchema,
54277
+ run: async (args) => {
54278
+ const input = external_exports.object(directoryListProjectsInputSchema).parse(args);
54279
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
54280
+ return listProjects({
54281
+ organizationId: input.organizationId,
54282
+ clientAppId: input.clientAppId,
54283
+ cwd
54284
+ });
54285
+ }
54286
+ });
54287
+ registerTool2(server, context, {
54288
+ name: "remix_directory_get_project",
54289
+ description: "Fetch one project by id, or infer the bound repository's project from cwd when possible.",
54290
+ access: "read",
54291
+ inputSchema: directoryProjectInputSchema,
54292
+ outputSchema: getProjectSuccessSchema,
54293
+ run: async (args) => {
54294
+ const input = external_exports.object(directoryProjectInputSchema).parse(args);
54295
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
54296
+ return getProject({
54297
+ projectId: input.projectId,
54298
+ cwd
54299
+ });
54300
+ }
54301
+ });
54302
+ registerTool2(server, context, {
54303
+ name: "remix_directory_list_apps",
54304
+ description: "List apps visible to the authenticated user, with optional organization or project filters and built-in pagination.",
54305
+ access: "read",
54306
+ inputSchema: directoryListAppsInputSchema,
54307
+ outputSchema: listAppsSuccessSchema,
54308
+ run: async (args) => {
54309
+ const input = external_exports.object(directoryListAppsInputSchema).parse(args);
54310
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
54311
+ return listApps2({
54312
+ projectId: input.projectId,
54313
+ organizationId: input.organizationId,
54314
+ forked: input.forked,
54315
+ limit: input.limit,
54316
+ offset: input.offset,
54317
+ cwd
54318
+ });
54319
+ }
54320
+ });
54321
+ registerTool2(server, context, {
54322
+ name: "remix_directory_get_app",
54323
+ description: "Fetch one app by id, or infer the bound repository's current app from cwd when possible.",
54324
+ access: "read",
54325
+ inputSchema: directoryAppInputSchema,
54326
+ outputSchema: getAppSuccessSchema,
54327
+ run: async (args) => {
54328
+ const input = external_exports.object(directoryAppInputSchema).parse(args);
54329
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
54330
+ return getApp({
54331
+ appId: input.appId,
54332
+ cwd
54333
+ });
54334
+ }
54335
+ });
54336
+ }
54337
+ var genericRecordSchema3 = external_exports.record(external_exports.string(), external_exports.unknown());
54338
+ var genericArraySchema3 = external_exports.array(genericRecordSchema3);
52918
54339
  var memoryKindSchema = external_exports.enum(["collab_turn", "change_step", "merge_request", "reconcile"]);
52919
- var paginationSchema = external_exports.object({
54340
+ var paginationSchema3 = external_exports.object({
52920
54341
  limit: external_exports.number().int().nonnegative(),
52921
54342
  offset: external_exports.number().int().nonnegative(),
52922
54343
  hasMore: external_exports.boolean()
@@ -52949,14 +54370,14 @@ var changeStepDiffInputSchema = {
52949
54370
  appId: external_exports.string().trim().min(1).optional(),
52950
54371
  changeStepId: external_exports.string().trim().min(1)
52951
54372
  };
52952
- var memorySummaryDataSchema = genericRecordSchema2;
54373
+ var memorySummaryDataSchema = genericRecordSchema3;
52953
54374
  var memorySearchDataSchema = external_exports.object({
52954
- items: genericArraySchema2,
52955
- pagination: paginationSchema
54375
+ items: genericArraySchema3,
54376
+ pagination: paginationSchema3
52956
54377
  });
52957
54378
  var memoryTimelineDataSchema = external_exports.object({
52958
- items: genericArraySchema2,
52959
- pagination: paginationSchema
54379
+ items: genericArraySchema3,
54380
+ pagination: paginationSchema3
52960
54381
  });
52961
54382
  var changeStepDiffDataSchema = external_exports.object({
52962
54383
  changeStepId: external_exports.string(),
@@ -53044,12 +54465,12 @@ function unwrapResponseObject22(resp, label) {
53044
54465
  }
53045
54466
  return obj;
53046
54467
  }
53047
- function makeNotBoundError() {
54468
+ function makeNotBoundError2() {
53048
54469
  const error2 = new Error("Repository is not bound to Remix.");
53049
54470
  error2.hint = "Run `remix_collab_init` in this repository, or pass `appId` explicitly for a direct memory read.";
53050
54471
  return error2;
53051
54472
  }
53052
- async function maybeFindGitRoot(cwd) {
54473
+ async function maybeFindGitRoot2(cwd) {
53053
54474
  try {
53054
54475
  return await findGitRoot(cwd);
53055
54476
  } catch {
@@ -53061,13 +54482,13 @@ async function resolveMemoryTarget(params) {
53061
54482
  if (explicitAppId) {
53062
54483
  return {
53063
54484
  appId: explicitAppId,
53064
- repoRoot: await maybeFindGitRoot(params.cwd)
54485
+ repoRoot: await maybeFindGitRoot2(params.cwd)
53065
54486
  };
53066
54487
  }
53067
54488
  const repoRoot = await findGitRoot(params.cwd);
53068
54489
  const binding = await readCollabBinding(repoRoot);
53069
54490
  if (!binding) {
53070
- throw makeNotBoundError();
54491
+ throw makeNotBoundError2();
53071
54492
  }
53072
54493
  return {
53073
54494
  appId: binding.currentAppId,
@@ -53135,7 +54556,7 @@ async function getChangeStepDiff(params) {
53135
54556
  logContext: target
53136
54557
  };
53137
54558
  }
53138
- function getAnnotations2(access) {
54559
+ function getAnnotations3(access) {
53139
54560
  return {
53140
54561
  readOnlyHint: access === "read",
53141
54562
  destructiveHint: false,
@@ -53143,7 +54564,7 @@ function getAnnotations2(access) {
53143
54564
  openWorldHint: false
53144
54565
  };
53145
54566
  }
53146
- function buildSuccessEnvelope2(tool, requestId, result) {
54567
+ function buildSuccessEnvelope3(tool, requestId, result) {
53147
54568
  return {
53148
54569
  schemaVersion: SCHEMA_VERSION,
53149
54570
  ok: true,
@@ -53155,7 +54576,7 @@ function buildSuccessEnvelope2(tool, requestId, result) {
53155
54576
  recommendedNextActions: result.recommendedNextActions ?? []
53156
54577
  };
53157
54578
  }
53158
- function buildErrorEnvelope2(tool, requestId, error2) {
54579
+ function buildErrorEnvelope3(tool, requestId, error2) {
53159
54580
  const normalized = normalizeToolError(error2);
53160
54581
  return {
53161
54582
  schemaVersion: SCHEMA_VERSION,
@@ -53164,17 +54585,17 @@ function buildErrorEnvelope2(tool, requestId, error2) {
53164
54585
  requestId: requestId ?? null,
53165
54586
  error: normalized,
53166
54587
  warnings: [],
53167
- risks: deriveErrorRisks2(normalized),
54588
+ risks: deriveErrorRisks3(normalized),
53168
54589
  recommendedNextActions: normalized.code === "AUTH_REQUIRED" ? ["Run `remix login` or set COMERGE_ACCESS_TOKEN, then retry."] : []
53169
54590
  };
53170
54591
  }
53171
- function deriveErrorRisks2(normalized) {
54592
+ function deriveErrorRisks3(normalized) {
53172
54593
  if (normalized.code === "DESTRUCTIVE_OPERATION_BLOCKED") {
53173
54594
  return ["A policy guard blocked a disallowed operation."];
53174
54595
  }
53175
54596
  return [];
53176
54597
  }
53177
- function registerTool2(server, context, params) {
54598
+ function registerTool3(server, context, params) {
53178
54599
  const errorSchema = makeErrorSchema();
53179
54600
  server.registerTool(
53180
54601
  params.name,
@@ -53183,7 +54604,7 @@ function registerTool2(server, context, params) {
53183
54604
  description: params.description,
53184
54605
  inputSchema: params.inputSchema,
53185
54606
  outputSchema: params.outputSchema,
53186
- annotations: getAnnotations2(params.access)
54607
+ annotations: getAnnotations3(params.access)
53187
54608
  },
53188
54609
  async (rawArgs) => {
53189
54610
  const requestId = typeof rawArgs.requestId === "string" ? rawArgs.requestId : void 0;
@@ -53191,7 +54612,7 @@ function registerTool2(server, context, params) {
53191
54612
  try {
53192
54613
  assertToolAccess(context.policy, params.access);
53193
54614
  const result = await params.run(rawArgs);
53194
- const envelope = buildSuccessEnvelope2(params.name, requestId, result);
54615
+ const envelope = buildSuccessEnvelope3(params.name, requestId, result);
53195
54616
  params.outputSchema.parse(envelope);
53196
54617
  context.logger.log({
53197
54618
  level: "info",
@@ -53207,7 +54628,7 @@ function registerTool2(server, context, params) {
53207
54628
  });
53208
54629
  return makeSuccessResult2(envelope);
53209
54630
  } catch (error2) {
53210
- const envelope = buildErrorEnvelope2(params.name, requestId, error2);
54631
+ const envelope = buildErrorEnvelope3(params.name, requestId, error2);
53211
54632
  errorSchema.parse(envelope);
53212
54633
  context.logger.log({
53213
54634
  level: "error",
@@ -53224,7 +54645,7 @@ function registerTool2(server, context, params) {
53224
54645
  );
53225
54646
  }
53226
54647
  function registerMemoryTools(server, context) {
53227
- registerTool2(server, context, {
54648
+ registerTool3(server, context, {
53228
54649
  name: "remix_collab_memory_summary",
53229
54650
  description: "First read for a bound app's current collaboration state, recent reasoning context, and merge or reconcile history before deeper inspection or any raw git history lookup.",
53230
54651
  access: "read",
@@ -53239,7 +54660,7 @@ function registerMemoryTools(server, context) {
53239
54660
  });
53240
54661
  }
53241
54662
  });
53242
- registerTool2(server, context, {
54663
+ registerTool3(server, context, {
53243
54664
  name: "remix_collab_memory_search",
53244
54665
  description: "Default tool for why/history/failed-attempt/user-intent questions. Search prompts, diffs, merge activity, reconciles, and other historical context before using raw git for exact repository facts.",
53245
54666
  access: "read",
@@ -53260,7 +54681,7 @@ function registerMemoryTools(server, context) {
53260
54681
  });
53261
54682
  }
53262
54683
  });
53263
- registerTool2(server, context, {
54684
+ registerTool3(server, context, {
53264
54685
  name: "remix_collab_memory_timeline",
53265
54686
  description: "Chronological view of collaboration memory for understanding what happened and in what order, with optional filters for bounded historical inspection before any exact-facts raw git follow-up.",
53266
54687
  access: "read",
@@ -53280,7 +54701,7 @@ function registerMemoryTools(server, context) {
53280
54701
  });
53281
54702
  }
53282
54703
  });
53283
- registerTool2(server, context, {
54704
+ registerTool3(server, context, {
53284
54705
  name: "remix_collab_memory_change_step_diff",
53285
54706
  description: "Second-hop expansion tool that fetches the full stored diff for a specific change step after memory search, timeline, or review work has identified the relevant `changeStepId`, keeping historical inspection inside Remix before raw git fallback.",
53286
54707
  access: "read",
@@ -53297,13 +54718,441 @@ function registerMemoryTools(server, context) {
53297
54718
  }
53298
54719
  });
53299
54720
  }
54721
+ var genericRecordSchema4 = external_exports.record(external_exports.string(), external_exports.unknown());
54722
+ var appScopedInputSchema = {
54723
+ ...commonRequestFieldsSchema,
54724
+ appId: external_exports.string().trim().min(1).optional()
54725
+ };
54726
+ var editQueueInputSchema = {
54727
+ ...appScopedInputSchema,
54728
+ limit: external_exports.number().int().positive().max(100).optional(),
54729
+ offset: external_exports.number().int().nonnegative().optional()
54730
+ };
54731
+ var bundleInputSchema = {
54732
+ ...appScopedInputSchema,
54733
+ bundleId: external_exports.string().trim().min(1)
54734
+ };
54735
+ var timelineInputSchema = {
54736
+ ...appScopedInputSchema,
54737
+ limit: external_exports.number().int().positive().max(100).optional(),
54738
+ cursor: external_exports.string().trim().min(1).optional()
54739
+ };
54740
+ var agentRunsInputSchema = {
54741
+ ...appScopedInputSchema,
54742
+ limit: external_exports.number().int().positive().max(100).optional(),
54743
+ offset: external_exports.number().int().nonnegative().optional(),
54744
+ status: external_exports.string().trim().min(1).optional(),
54745
+ currentPhase: external_exports.string().trim().min(1).optional(),
54746
+ createdAfter: external_exports.string().datetime().optional(),
54747
+ createdBefore: external_exports.string().datetime().optional()
54748
+ };
54749
+ var agentRunInputSchema = {
54750
+ ...appScopedInputSchema,
54751
+ runId: external_exports.string().trim().min(1)
54752
+ };
54753
+ var agentRunEventsInputSchema = {
54754
+ ...appScopedInputSchema,
54755
+ runId: external_exports.string().trim().min(1),
54756
+ limit: external_exports.number().int().positive().max(100).optional(),
54757
+ offset: external_exports.number().int().nonnegative().optional(),
54758
+ createdAfter: external_exports.string().datetime().optional(),
54759
+ createdBefore: external_exports.string().datetime().optional()
54760
+ };
54761
+ var appOverviewSuccessSchema = makeSuccessSchema(genericRecordSchema4);
54762
+ var editQueueSuccessSchema = makeSuccessSchema(genericRecordSchema4);
54763
+ var bundleSuccessSchema = makeSuccessSchema(genericRecordSchema4);
54764
+ var timelineSuccessSchema = makeSuccessSchema(genericRecordSchema4);
54765
+ var agentRunsSuccessSchema = makeSuccessSchema(genericRecordSchema4);
54766
+ var agentRunSuccessSchema = makeSuccessSchema(genericRecordSchema4);
54767
+ var agentRunEventsSuccessSchema = makeSuccessSchema(genericRecordSchema4);
54768
+ var sandboxStatusSuccessSchema = makeSuccessSchema(genericRecordSchema4);
54769
+ async function resolveAppTarget(_api, params) {
54770
+ const explicitAppId = params.appId?.trim();
54771
+ const bindingContext = await loadBindingContext(params.cwd);
54772
+ if (explicitAppId) {
54773
+ return {
54774
+ appId: explicitAppId,
54775
+ repoRoot: bindingContext.repoRoot
54776
+ };
54777
+ }
54778
+ if (!bindingContext.binding) {
54779
+ throw makeNotBoundError("App id was not provided and the current repository is not bound to Remix.");
54780
+ }
54781
+ return {
54782
+ appId: bindingContext.binding.currentAppId,
54783
+ repoRoot: bindingContext.repoRoot
54784
+ };
54785
+ }
54786
+ async function getAppOverview(params) {
54787
+ const api = await createApiClient2();
54788
+ const target = await resolveAppTarget(api, params);
54789
+ const data = unwrapResponseObject2(await api.getAppOverview(target.appId), "app overview");
54790
+ return {
54791
+ data,
54792
+ warnings: [],
54793
+ recommendedNextActions: [
54794
+ "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."
54795
+ ],
54796
+ logContext: target
54797
+ };
54798
+ }
54799
+ async function getEditQueue(params) {
54800
+ const api = await createApiClient2();
54801
+ const target = await resolveAppTarget(api, params);
54802
+ const data = unwrapResponseObject2(
54803
+ await api.listAppEditQueue(target.appId, {
54804
+ limit: params.limit,
54805
+ offset: params.offset
54806
+ }),
54807
+ "edit queue"
54808
+ );
54809
+ const pageInfo = typeof data.pageInfo === "object" && data.pageInfo ? data.pageInfo : null;
54810
+ return {
54811
+ data,
54812
+ warnings: [],
54813
+ recommendedNextActions: pageInfo?.hasMore === true && typeof pageInfo.limit === "number" && typeof pageInfo.offset === "number" ? [`Pass offset=${pageInfo.offset + pageInfo.limit} to load the next edit queue page.`] : [],
54814
+ logContext: target
54815
+ };
54816
+ }
54817
+ async function getBundle(params) {
54818
+ const api = await createApiClient2();
54819
+ const target = await resolveAppTarget(api, params);
54820
+ const data = unwrapResponseObject2(await api.getBundle(target.appId, params.bundleId), "bundle");
54821
+ return {
54822
+ data,
54823
+ warnings: [],
54824
+ recommendedNextActions: ["Use the bundle download-url client methods only when you need the actual artifact, not just metadata inspection."],
54825
+ logContext: target
54826
+ };
54827
+ }
54828
+ async function listTimeline(params) {
54829
+ const api = await createApiClient2();
54830
+ const target = await resolveAppTarget(api, params);
54831
+ const data = unwrapResponseObject2(
54832
+ await api.listAppTimeline(target.appId, {
54833
+ limit: params.limit,
54834
+ cursor: params.cursor
54835
+ }),
54836
+ "app timeline"
54837
+ );
54838
+ const pageInfo = typeof data.pageInfo === "object" && data.pageInfo ? data.pageInfo : null;
54839
+ return {
54840
+ data,
54841
+ warnings: [],
54842
+ recommendedNextActions: pageInfo?.hasMore === true && typeof pageInfo.nextCursor === "string" ? [`Pass cursor=${pageInfo.nextCursor} to load the next timeline page.`] : [],
54843
+ logContext: target
54844
+ };
54845
+ }
54846
+ async function listAgentRuns(params) {
54847
+ const api = await createApiClient2();
54848
+ const target = await resolveAppTarget(api, params);
54849
+ const data = unwrapResponseObject2(
54850
+ await api.listAgentRuns(target.appId, {
54851
+ limit: params.limit,
54852
+ offset: params.offset,
54853
+ status: params.status,
54854
+ currentPhase: params.currentPhase,
54855
+ createdAfter: params.createdAfter,
54856
+ createdBefore: params.createdBefore
54857
+ }),
54858
+ "agent runs"
54859
+ );
54860
+ const pageInfo = typeof data.pageInfo === "object" && data.pageInfo ? data.pageInfo : null;
54861
+ const items = Array.isArray(data.items) ? data.items : [];
54862
+ const firstRunId = items.length > 0 && items[0] && typeof items[0] === "object" && typeof items[0].id === "string" ? items[0].id : null;
54863
+ const recommendedNextActions = [];
54864
+ if (firstRunId) {
54865
+ recommendedNextActions.push(
54866
+ `Use \`remix_ops_get_agent_run\` with \`runId=${firstRunId}\` for the top listed run, then \`remix_ops_list_agent_run_events\` if you need its event stream.`
54867
+ );
54868
+ }
54869
+ if (pageInfo?.hasMore === true && typeof pageInfo.limit === "number" && typeof pageInfo.offset === "number") {
54870
+ recommendedNextActions.push(`Pass offset=${pageInfo.offset + pageInfo.limit} to load the next agent-runs page.`);
54871
+ }
54872
+ return {
54873
+ data,
54874
+ warnings: [],
54875
+ recommendedNextActions,
54876
+ logContext: target
54877
+ };
54878
+ }
54879
+ async function getAgentRun(params) {
54880
+ const api = await createApiClient2();
54881
+ const target = await resolveAppTarget(api, params);
54882
+ const data = unwrapResponseObject2(await api.getAgentRun(target.appId, params.runId), "agent run");
54883
+ return {
54884
+ data,
54885
+ warnings: [],
54886
+ recommendedNextActions: [`Use \`remix_ops_list_agent_run_events\` with \`runId=${params.runId}\` for the event stream behind this run.`],
54887
+ logContext: target
54888
+ };
54889
+ }
54890
+ async function listAgentRunEvents(params) {
54891
+ const api = await createApiClient2();
54892
+ const target = await resolveAppTarget(api, params);
54893
+ const data = unwrapResponseObject2(
54894
+ await api.listAgentRunEvents(target.appId, params.runId, {
54895
+ limit: params.limit,
54896
+ offset: params.offset,
54897
+ createdAfter: params.createdAfter,
54898
+ createdBefore: params.createdBefore
54899
+ }),
54900
+ "agent run events"
54901
+ );
54902
+ const pageInfo = typeof data.pageInfo === "object" && data.pageInfo ? data.pageInfo : null;
54903
+ return {
54904
+ data,
54905
+ warnings: [],
54906
+ recommendedNextActions: pageInfo?.hasMore === true && typeof pageInfo.limit === "number" && typeof pageInfo.offset === "number" ? [`Pass offset=${pageInfo.offset + pageInfo.limit} to load the next event page.`] : [],
54907
+ logContext: target
54908
+ };
54909
+ }
54910
+ async function getSandboxStatus(params) {
54911
+ const api = await createApiClient2();
54912
+ const target = await resolveAppTarget(api, params);
54913
+ const data = unwrapResponseObject2(await api.getSandboxStatus(target.appId), "sandbox status");
54914
+ return {
54915
+ data,
54916
+ warnings: [],
54917
+ recommendedNextActions: ["Use the sandbox metadata here to decide whether a resume is plausible before attempting any write-side sandbox action."],
54918
+ logContext: target
54919
+ };
54920
+ }
54921
+ function getAnnotations4(access) {
54922
+ return {
54923
+ readOnlyHint: access === "read",
54924
+ destructiveHint: false,
54925
+ idempotentHint: true,
54926
+ openWorldHint: false
54927
+ };
54928
+ }
54929
+ function buildSuccessEnvelope4(tool, requestId, result) {
54930
+ return {
54931
+ schemaVersion: SCHEMA_VERSION,
54932
+ ok: true,
54933
+ tool,
54934
+ requestId: requestId ?? null,
54935
+ data: result.data,
54936
+ warnings: result.warnings ?? [],
54937
+ risks: result.risks ?? [],
54938
+ recommendedNextActions: result.recommendedNextActions ?? []
54939
+ };
54940
+ }
54941
+ function deriveErrorRisks4(normalized) {
54942
+ if (normalized.code === "DESTRUCTIVE_OPERATION_BLOCKED") {
54943
+ return ["A policy guard blocked a disallowed operation."];
54944
+ }
54945
+ return [];
54946
+ }
54947
+ function buildErrorEnvelope4(tool, requestId, error2) {
54948
+ const normalized = normalizeToolError(error2);
54949
+ return {
54950
+ schemaVersion: SCHEMA_VERSION,
54951
+ ok: false,
54952
+ tool,
54953
+ requestId: requestId ?? null,
54954
+ error: normalized,
54955
+ warnings: [],
54956
+ risks: deriveErrorRisks4(normalized),
54957
+ recommendedNextActions: normalized.code === "AUTH_REQUIRED" ? ["Run `remix login` or set COMERGE_ACCESS_TOKEN, then retry."] : []
54958
+ };
54959
+ }
54960
+ function registerTool4(server, context, params) {
54961
+ const errorSchema = makeErrorSchema();
54962
+ server.registerTool(
54963
+ params.name,
54964
+ {
54965
+ title: params.name,
54966
+ description: params.description,
54967
+ inputSchema: params.inputSchema,
54968
+ outputSchema: params.outputSchema,
54969
+ annotations: getAnnotations4(params.access)
54970
+ },
54971
+ async (rawArgs) => {
54972
+ const requestId = typeof rawArgs.requestId === "string" ? rawArgs.requestId : void 0;
54973
+ const startedAt = Date.now();
54974
+ try {
54975
+ assertToolAccess(context.policy, params.access);
54976
+ const result = await params.run(rawArgs);
54977
+ const envelope = buildSuccessEnvelope4(params.name, requestId, result);
54978
+ params.outputSchema.parse(envelope);
54979
+ context.logger.log({
54980
+ level: "info",
54981
+ message: "tool_completed",
54982
+ tool: params.name,
54983
+ requestId: envelope.requestId,
54984
+ durationMs: Date.now() - startedAt,
54985
+ result: "success",
54986
+ repoRoot: result.logContext?.repoRoot ?? null,
54987
+ appId: result.logContext?.appId ?? null,
54988
+ mrId: result.logContext?.mrId ?? null
54989
+ });
54990
+ return makeSuccessResult2(envelope);
54991
+ } catch (error2) {
54992
+ const envelope = buildErrorEnvelope4(params.name, requestId, error2);
54993
+ errorSchema.parse(envelope);
54994
+ context.logger.log({
54995
+ level: "error",
54996
+ message: "tool_failed",
54997
+ tool: params.name,
54998
+ requestId: envelope.requestId,
54999
+ durationMs: Date.now() - startedAt,
55000
+ result: "error",
55001
+ errorCode: envelope.error.code
55002
+ });
55003
+ return makeErrorResult(envelope);
55004
+ }
55005
+ }
55006
+ );
55007
+ }
55008
+ function registerOpsTools(server, context) {
55009
+ registerTool4(server, context, {
55010
+ name: "remix_context_get_app_overview",
55011
+ description: "Read the current app's overview, capabilities, and workflow readiness without mutating state.",
55012
+ access: "read",
55013
+ inputSchema: appScopedInputSchema,
55014
+ outputSchema: appOverviewSuccessSchema,
55015
+ run: async (args) => {
55016
+ const input = external_exports.object(appScopedInputSchema).parse(args);
55017
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
55018
+ return getAppOverview({
55019
+ appId: input.appId,
55020
+ cwd
55021
+ });
55022
+ }
55023
+ });
55024
+ registerTool4(server, context, {
55025
+ name: "remix_ops_get_edit_queue",
55026
+ description: "Inspect the pending edit queue for one app with bounded pagination.",
55027
+ access: "read",
55028
+ inputSchema: editQueueInputSchema,
55029
+ outputSchema: editQueueSuccessSchema,
55030
+ run: async (args) => {
55031
+ const input = external_exports.object(editQueueInputSchema).parse(args);
55032
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
55033
+ return getEditQueue({
55034
+ appId: input.appId,
55035
+ cwd,
55036
+ limit: input.limit,
55037
+ offset: input.offset
55038
+ });
55039
+ }
55040
+ });
55041
+ registerTool4(server, context, {
55042
+ name: "remix_ops_get_bundle",
55043
+ description: "Inspect one app bundle by id without downloading the artifact payload.",
55044
+ access: "read",
55045
+ inputSchema: bundleInputSchema,
55046
+ outputSchema: bundleSuccessSchema,
55047
+ run: async (args) => {
55048
+ const input = external_exports.object(bundleInputSchema).parse(args);
55049
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
55050
+ return getBundle({
55051
+ appId: input.appId,
55052
+ cwd,
55053
+ bundleId: input.bundleId
55054
+ });
55055
+ }
55056
+ });
55057
+ registerTool4(server, context, {
55058
+ name: "remix_ops_list_timeline",
55059
+ description: "List bounded timeline events for one app using the backend cursor pagination model.",
55060
+ access: "read",
55061
+ inputSchema: timelineInputSchema,
55062
+ outputSchema: timelineSuccessSchema,
55063
+ run: async (args) => {
55064
+ const input = external_exports.object(timelineInputSchema).parse(args);
55065
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
55066
+ return listTimeline({
55067
+ appId: input.appId,
55068
+ cwd,
55069
+ limit: input.limit,
55070
+ cursor: input.cursor
55071
+ });
55072
+ }
55073
+ });
55074
+ registerTool4(server, context, {
55075
+ name: "remix_ops_get_agent_run",
55076
+ description: "Fetch one stored agent run for an app, including status, phase, and summary metadata.",
55077
+ access: "read",
55078
+ inputSchema: agentRunInputSchema,
55079
+ outputSchema: agentRunSuccessSchema,
55080
+ run: async (args) => {
55081
+ const input = external_exports.object(agentRunInputSchema).parse(args);
55082
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
55083
+ return getAgentRun({
55084
+ appId: input.appId,
55085
+ cwd,
55086
+ runId: input.runId
55087
+ });
55088
+ }
55089
+ });
55090
+ registerTool4(server, context, {
55091
+ name: "remix_ops_list_agent_runs",
55092
+ description: "List paginated agent runs for one app so run ids can be discovered before deeper inspection.",
55093
+ access: "read",
55094
+ inputSchema: agentRunsInputSchema,
55095
+ outputSchema: agentRunsSuccessSchema,
55096
+ run: async (args) => {
55097
+ const input = external_exports.object(agentRunsInputSchema).parse(args);
55098
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
55099
+ return listAgentRuns({
55100
+ appId: input.appId,
55101
+ cwd,
55102
+ limit: input.limit,
55103
+ offset: input.offset,
55104
+ status: input.status,
55105
+ currentPhase: input.currentPhase,
55106
+ createdAfter: input.createdAfter,
55107
+ createdBefore: input.createdBefore
55108
+ });
55109
+ }
55110
+ });
55111
+ registerTool4(server, context, {
55112
+ name: "remix_ops_list_agent_run_events",
55113
+ description: "List paginated agent-run events for one stored run, with optional time bounds.",
55114
+ access: "read",
55115
+ inputSchema: agentRunEventsInputSchema,
55116
+ outputSchema: agentRunEventsSuccessSchema,
55117
+ run: async (args) => {
55118
+ const input = external_exports.object(agentRunEventsInputSchema).parse(args);
55119
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
55120
+ return listAgentRunEvents({
55121
+ appId: input.appId,
55122
+ cwd,
55123
+ runId: input.runId,
55124
+ limit: input.limit,
55125
+ offset: input.offset,
55126
+ createdAfter: input.createdAfter,
55127
+ createdBefore: input.createdBefore
55128
+ });
55129
+ }
55130
+ });
55131
+ registerTool4(server, context, {
55132
+ name: "remix_ops_get_sandbox_status",
55133
+ description: "Read safe sandbox metadata and the latest migration status for one app without exposing execution handles or secrets.",
55134
+ access: "read",
55135
+ inputSchema: appScopedInputSchema,
55136
+ outputSchema: sandboxStatusSuccessSchema,
55137
+ run: async (args) => {
55138
+ const input = external_exports.object(appScopedInputSchema).parse(args);
55139
+ const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
55140
+ return getSandboxStatus({
55141
+ appId: input.appId,
55142
+ cwd
55143
+ });
55144
+ }
55145
+ });
55146
+ }
53300
55147
  function createRemixMcpServer(params) {
53301
55148
  const context = createServerContext({ version: params.version });
53302
55149
  const server = new McpServer({
53303
55150
  name: context.serverName,
53304
55151
  version: context.version
53305
55152
  });
55153
+ registerIdentityTools(server, context);
53306
55154
  registerCollabTools(server, context);
55155
+ registerOpsTools(server, context);
53307
55156
  registerMemoryTools(server, context);
53308
55157
  return { server, context };
53309
55158
  }
@@ -53450,7 +55299,7 @@ async function listPendingTurnStateSummaries() {
53450
55299
  // package.json
53451
55300
  var package_default = {
53452
55301
  name: "@remixhq/claude-plugin",
53453
- version: "0.1.12",
55302
+ version: "0.1.14",
53454
55303
  description: "Claude Code plugin for Remix collaboration workflows",
53455
55304
  homepage: "https://github.com/RemixDotOne/remix-claude-plugin",
53456
55305
  license: "MIT",
@@ -53481,8 +55330,8 @@ var package_default = {
53481
55330
  prepack: "npm run build"
53482
55331
  },
53483
55332
  dependencies: {
53484
- "@remixhq/core": "^0.1.8",
53485
- "@remixhq/mcp": "^0.1.8"
55333
+ "@remixhq/core": "^0.1.9",
55334
+ "@remixhq/mcp": "^0.1.9"
53486
55335
  },
53487
55336
  devDependencies: {
53488
55337
  "@types/node": "^25.4.0",
@@ -53575,7 +55424,7 @@ var outputSchema = external_exports.object({
53575
55424
  toolName: external_exports.string().nullable(),
53576
55425
  repoRoot: external_exports.string().nullable(),
53577
55426
  message: external_exports.string().nullable(),
53578
- fields: external_exports.record(external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]))
55427
+ fields: external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean(), external_exports.null()]))
53579
55428
  })
53580
55429
  ),
53581
55430
  pendingStates: external_exports.array(