@sideboard-ai/core 0.1.320 → 0.1.321

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.
Files changed (56) hide show
  1. package/dist/agents/cursor-runner.js +2 -2
  2. package/dist/{agents-CJZYJOWP.js → agents-PFVYC36J.js} +8 -8
  3. package/dist/{agents-F2E2HACB.js → agents-ROFDNE7Z.js} +9 -9
  4. package/dist/{chunk-QVN2WOKJ.js → chunk-2TUE6JKQ.js} +12 -0
  5. package/dist/{chunk-B7UL36WH.js → chunk-45UB2J47.js} +335 -65
  6. package/dist/{chunk-DD67JWTN.js → chunk-4YXZOXMT.js} +1 -1
  7. package/dist/{chunk-V5BTGQ6M.js → chunk-6U6QRUHY.js} +1 -1
  8. package/dist/{chunk-QGY3UNNL.js → chunk-7JV5QP5K.js} +1 -1
  9. package/dist/{chunk-DJZRDTZY.js → chunk-BANN5HUX.js} +6 -6
  10. package/dist/{chunk-CB5Z5C3J.js → chunk-BPDVHB4U.js} +1 -1
  11. package/dist/{chunk-RHJPP26T.js → chunk-H6GXYV5Q.js} +14 -11
  12. package/dist/{chunk-4GZC6DDH.js → chunk-IFWGG2M2.js} +7 -0
  13. package/dist/{chunk-LLIEU2ZB.js → chunk-JNEFEA2A.js} +6 -6
  14. package/dist/{chunk-IDPFLXRY.js → chunk-LD2DXHBP.js} +341 -67
  15. package/dist/{chunk-2P4XKWV2.js → chunk-QKHEVSB5.js} +1 -1
  16. package/dist/{chunk-P74GXA5V.js → chunk-RGUZPMTF.js} +6 -6
  17. package/dist/{chunk-UK2KESBN.js → chunk-RHJKZOPQ.js} +3 -3
  18. package/dist/{chunk-HH3VVTEC.js → chunk-S5CJUIPY.js} +5 -5
  19. package/dist/{chunk-5S3YP5KH.js → chunk-SPMIZSKH.js} +14 -11
  20. package/dist/{chunk-EQ5JY3MJ.js → chunk-TEFFR6PB.js} +1 -1
  21. package/dist/{chunk-EDXTP2VR.js → chunk-UWJUARFA.js} +545 -186
  22. package/dist/{chunk-OAFBHCSM.js → chunk-UX6EDDGR.js} +1 -1
  23. package/dist/{chunk-ZYZBCLJR.js → chunk-XDQSHSL6.js} +6 -6
  24. package/dist/{chunk-2UG25RMK.js → chunk-YBTSZ2SW.js} +12 -0
  25. package/dist/{chunk-PNLGWAYE.js → chunk-YWBLQBSN.js} +548 -187
  26. package/dist/{chunk-Y3Y7SZBZ.js → chunk-ZHGMRBSC.js} +1 -1
  27. package/dist/{conductor-FPFY6Z5S.js → conductor-CNT3VL3S.js} +6 -6
  28. package/dist/{conductor-JA5W3L7C.js → conductor-XLXJU24U.js} +6 -6
  29. package/dist/{connected-teams-IHK2WXUI.js → connected-teams-H4LZOJDC.js} +2 -2
  30. package/dist/{connected-teams-IFW3NQ6G.js → connected-teams-XYRZMQO4.js} +2 -2
  31. package/dist/{coordinator-prompt-NUC2ZXMJ.js → coordinator-prompt-D5EE2YTV.js} +5 -5
  32. package/dist/{coordinator-prompt-M75TAN43.js → coordinator-prompt-TU24TQ4I.js} +5 -5
  33. package/dist/{global-workspace-5A4H34AF.js → global-workspace-743TPQNF.js} +5 -5
  34. package/dist/{global-workspace-SCLQUNUI.js → global-workspace-UPMXDQM7.js} +5 -5
  35. package/dist/index.cjs +1577 -471
  36. package/dist/index.d.cts +182 -70
  37. package/dist/index.d.ts +182 -70
  38. package/dist/index.js +480 -62
  39. package/dist/mcp/run-stdio.cjs +1159 -447
  40. package/dist/mcp/run-stdio.js +65 -17
  41. package/dist/{optional-cli-MRNQKVIR.js → optional-cli-2YDQQCCD.js} +4 -4
  42. package/dist/{optional-cli-2V3SLME5.js → optional-cli-USD6PROE.js} +3 -3
  43. package/dist/{orchestrator-NPFWU3XU.js → orchestrator-B4STIW4B.js} +12 -12
  44. package/dist/{orchestrator-7BDZ5GWD.js → orchestrator-VQ4HRYVB.js} +11 -11
  45. package/dist/{run-PCX5QWR4.js → run-7QMRYP3F.js} +3 -1
  46. package/dist/{run-TB7RBII2.js → run-UPNCCRCN.js} +3 -1
  47. package/dist/{thread-store-JWHVXLDX.js → thread-store-3F7GR64A.js} +1 -1
  48. package/dist/{thread-store-QHYN2ZMQ.js → thread-store-JRSL4HQY.js} +1 -1
  49. package/dist/{workspaces-KWMBOTQD.js → workspaces-47WQL5IG.js} +5 -5
  50. package/dist/{workspaces-5QA3YM4L.js → workspaces-75FD4QUR.js} +5 -5
  51. package/dist/{worktree-O6BANO2I.js → worktree-G3E6WP66.js} +10 -8
  52. package/dist/{worktree-7EATHVYQ.js → worktree-PDNPL5CQ.js} +10 -8
  53. package/dist/{worktree-exclude-RXZPTCNH.js → worktree-exclude-INRDQFWV.js} +2 -2
  54. package/dist/{worktree-exclude-5N2KTYIT.js → worktree-exclude-P5NJODWP.js} +2 -2
  55. package/package.json +1 -1
  56. /package/dist/{chunk-XMHMVEIY.js → chunk-B4CDSSRN.js} +0 -0
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-N2WW62LF.js";
6
6
  import {
7
7
  ensureWorktreeSideboardIgnored
8
- } from "./chunk-DD67JWTN.js";
8
+ } from "./chunk-4YXZOXMT.js";
9
9
  import {
10
10
  gh,
11
11
  git,
@@ -14,8 +14,9 @@ import {
14
14
  githubCredentialHelperGitConfig,
15
15
  githubGhConfigEnv,
16
16
  materializeGithubAgentAuth,
17
+ originCli,
17
18
  resolveGhAuthToken
18
- } from "./chunk-2UG25RMK.js";
19
+ } from "./chunk-YBTSZ2SW.js";
19
20
  import {
20
21
  getGithubGitAuthMode,
21
22
  getGithubPat
@@ -26,15 +27,13 @@ import {
26
27
  normalizeTakenSlug,
27
28
  normalizeWorktreePath,
28
29
  takenSlugsFromThread
29
- } from "./chunk-XMHMVEIY.js";
30
+ } from "./chunk-B4CDSSRN.js";
30
31
  import {
31
32
  worktreesRoot
32
33
  } from "./chunk-WOU343SI.js";
33
34
 
34
35
  // src/git/worktree.ts
35
- import { randomBytes } from "crypto";
36
- import { existsSync as existsSync2, realpathSync, unlinkSync, writeFileSync } from "fs";
37
- import { tmpdir } from "os";
36
+ import { existsSync as existsSync2, realpathSync } from "fs";
38
37
  import { join as join2 } from "path";
39
38
 
40
39
  // src/git/gh-errors.ts
@@ -139,9 +138,9 @@ var HTTPS_REWRITE = [
139
138
  { key: "url.https://github.com/.insteadOf", value: "git@github.com:" },
140
139
  { key: "url.https://github.com/.insteadOf", value: "ssh://git@github.com/" }
141
140
  ];
142
- function githubHttpsInsteadOfEntries(remoteUrl) {
141
+ function githubHttpsInsteadOfEntries(remoteUrl2) {
143
142
  const entries = [...HTTPS_REWRITE];
144
- const url = remoteUrl?.trim() ?? "";
143
+ const url = remoteUrl2?.trim() ?? "";
145
144
  const scpAlias = url.match(/^(git@github\.com-[^:]+):/i);
146
145
  if (scpAlias?.[1]) {
147
146
  entries.push({
@@ -334,6 +333,53 @@ function formatGitAuthModeDirective(mode) {
334
333
  }
335
334
  }
336
335
 
336
+ // src/git/remote-forge.ts
337
+ function formatRemoteTag(remote) {
338
+ const name = remote.forge === "other" ? remote.host : remote.forge;
339
+ return `${name}:${remote.slug}`;
340
+ }
341
+ function forgeForHost(host) {
342
+ const h = host.toLowerCase();
343
+ if (h === "github.com" || h.startsWith("github.com-")) return "github";
344
+ if (h === "origin.cursor.com") return "cursor";
345
+ if (h === "entire.io" || h.endsWith(".entire.io") || h === "entire.com" || h.endsWith(".entire.com")) {
346
+ return "entire";
347
+ }
348
+ return "other";
349
+ }
350
+ function repoFromParts(host, owner, name) {
351
+ if (!host || !owner || !name || owner.includes("@")) return null;
352
+ const normalized = host.toLowerCase();
353
+ return {
354
+ forge: forgeForHost(normalized),
355
+ host: normalized,
356
+ slug: `${owner}/${name}`
357
+ };
358
+ }
359
+ function parseRemoteRepo(url) {
360
+ const trimmed = url.trim().replace(/\.git$/i, "");
361
+ if (!trimmed) return null;
362
+ if (trimmed.toLowerCase().startsWith("entire://")) {
363
+ const parts = trimmed.slice("entire://".length).replace(/^\/+/, "").split("/").filter(Boolean);
364
+ if (parts.length === 2) return { forge: "entire", host: "entire.io", slug: `${parts[0]}/${parts[1]}` };
365
+ if (parts.length >= 3) {
366
+ return repoFromParts(parts[0], parts[1], parts[2]);
367
+ }
368
+ return null;
369
+ }
370
+ const ssh = trimmed.match(/^git@([^:]+):([^/]+)\/([^/]+)$/i);
371
+ if (ssh?.[1] && ssh[2] && ssh[3]) return repoFromParts(ssh[1], ssh[2], ssh[3]);
372
+ try {
373
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
374
+ const parsed = new URL(withScheme);
375
+ const parts = parsed.pathname.split("/").filter(Boolean);
376
+ if (parts.length < 2) return null;
377
+ return repoFromParts(parsed.hostname, parts[parts.length - 2], parts[parts.length - 1]);
378
+ } catch {
379
+ return null;
380
+ }
381
+ }
382
+
337
383
  // src/git/list-prs.ts
338
384
  var PR_LIST_JSON_FIELDS = [
339
385
  "number",
@@ -695,30 +741,6 @@ async function listGithubViewerTeamSlugs(repoPath) {
695
741
  return slugs;
696
742
  }
697
743
 
698
- // src/util/enqueue-by-key.ts
699
- var tails = /* @__PURE__ */ new Map();
700
- function enqueueByKey(key, fn) {
701
- const prev = tails.get(key) ?? Promise.resolve();
702
- const next = prev.then(fn, fn);
703
- tails.set(
704
- key,
705
- next.then(
706
- () => void 0,
707
- () => void 0
708
- )
709
- );
710
- return next;
711
- }
712
-
713
- // src/git/repo-git-lock.ts
714
- function repoLockKey(repoPath) {
715
- const trimmed = repoPath.replace(/\/+$/, "");
716
- return `git-worktree:${trimmed || "/"}`;
717
- }
718
- function withRepoGitLock(repoPath, fn) {
719
- return enqueueByKey(repoLockKey(repoPath), fn);
720
- }
721
-
722
744
  // src/git/pr-gates.ts
723
745
  function prIsInMergeQueue(gate) {
724
746
  if (gate.isInMergeQueue) return true;
@@ -846,6 +868,359 @@ function buildMergeGateChecks(gate, opts = {}) {
846
868
  return rows;
847
869
  }
848
870
 
871
+ // src/git/cursor-pr.ts
872
+ var PLAIN_LIST_FIELDS = "number,title,headRefName,url,author,state,isDraft,reviewDecision";
873
+ var SEARCH_LIST_FIELDS = `${PLAIN_LIST_FIELDS},labels,reviewRequests`;
874
+ var VIEW_FIELDS = "number,title,body,url,state,isDraft,reviewDecision,author,createdAt,baseRefName,headRefName,additions,deletions,changedFiles,commits,comments,reviews,labels,mergeability";
875
+ function repoArgs(slug) {
876
+ return ["-R", slug];
877
+ }
878
+ function cursorHeadBranch(head, slug) {
879
+ const branch = head.trim().replace(/^refs\/heads\//, "");
880
+ const owner = slug.split("/")[0];
881
+ if (owner && branch.startsWith(`${owner}:`)) return branch.slice(owner.length + 1);
882
+ return branch;
883
+ }
884
+ function cursorMergeFields(value) {
885
+ if (typeof value === "string" && value.trim()) {
886
+ const upper = value.trim().toUpperCase();
887
+ return { mergeable: upper, mergeStateStatus: upper };
888
+ }
889
+ if (value && typeof value === "object") {
890
+ const row = value;
891
+ const conflictState = String(row.mergeConflictState ?? "").trim().toUpperCase();
892
+ if ("hasMergeConflicts" in row || conflictState) {
893
+ if (conflictState.includes("PENDING")) {
894
+ return { mergeable: "UNKNOWN", mergeStateStatus: "UNKNOWN" };
895
+ }
896
+ const conflicting = row.hasMergeConflicts === true || conflictState.includes("CONFLICT") && !conflictState.includes("CLEAN");
897
+ if (conflicting) return { mergeable: "CONFLICTING", mergeStateStatus: "DIRTY" };
898
+ if (row.hasMergeConflicts === false || conflictState.includes("CLEAN")) {
899
+ return { mergeable: "MERGEABLE", mergeStateStatus: "CLEAN" };
900
+ }
901
+ }
902
+ const verdict = String(row.verdict ?? row.verdictKind ?? "").trim().toUpperCase();
903
+ if (verdict === "BLOCKED") return { mergeable: null, mergeStateStatus: "BLOCKED" };
904
+ if (verdict === "MERGEABLE") return { mergeable: "MERGEABLE", mergeStateStatus: "CLEAN" };
905
+ const status = String(row.status ?? row.state ?? row.mergeable ?? "").trim().toUpperCase();
906
+ return { mergeable: status || null, mergeStateStatus: status || null };
907
+ }
908
+ return { mergeable: null, mergeStateStatus: null };
909
+ }
910
+ function cursorReviewDecision(value) {
911
+ const raw = typeof value === "string" ? value.trim().toUpperCase().replace(/[\s-]+/g, "_") : "";
912
+ if (!raw) return null;
913
+ if (raw === "REQUIRED" || raw === "REVIEW_REQUIRED") return "REVIEW_REQUIRED";
914
+ if (raw === "CHANGES_REQUESTED") return "CHANGES_REQUESTED";
915
+ if (raw === "APPROVED") return "APPROVED";
916
+ return raw;
917
+ }
918
+ function cursorViewToPrMeta(view) {
919
+ const merge = cursorMergeFields(view.mergeability ?? view.mergeable);
920
+ const author = view.author ?? {};
921
+ return {
922
+ number: Number(view.number),
923
+ title: String(view.title ?? ""),
924
+ url: String(view.url ?? ""),
925
+ state: String(view.state ?? ""),
926
+ isDraft: Boolean(view.isDraft),
927
+ reviewDecision: cursorReviewDecision(view.reviewDecision),
928
+ baseRefName: String(view.baseRefName ?? ""),
929
+ headRefName: String(view.headRefName ?? ""),
930
+ isInMergeQueue: false,
931
+ mergeable: merge.mergeable,
932
+ mergeStateStatus: merge.mergeStateStatus,
933
+ authorLogin: author.login?.trim() || null,
934
+ reviewerLogins: [],
935
+ labels: labelNames(view.labels)
936
+ };
937
+ }
938
+ function loginOf(author) {
939
+ if (!author || typeof author !== "object") return "";
940
+ const row = author;
941
+ for (const key of ["login", "handle", "email", "displayName", "name", "id"]) {
942
+ const value = row[key];
943
+ if (typeof value === "string" && value.trim()) return value.trim();
944
+ }
945
+ return "";
946
+ }
947
+ function upperState(value, isDraft) {
948
+ const raw = typeof value === "string" ? value.trim().toUpperCase() : "";
949
+ if (raw === "DRAFT") return "OPEN";
950
+ if (raw === "OPEN" || raw === "CLOSED" || raw === "MERGED") return raw;
951
+ return isDraft ? "OPEN" : raw;
952
+ }
953
+ function normalizeOriginPr(raw) {
954
+ const isDraft = raw.isDraft === true || String(raw.status ?? raw.state ?? "").toLowerCase() === "draft";
955
+ const login = loginOf(raw.author);
956
+ const author = raw.author && typeof raw.author === "object" ? { ...raw.author, login: login || "unknown" } : login ? { login } : { login: "unknown" };
957
+ return {
958
+ ...raw,
959
+ isDraft,
960
+ state: upperState(raw.state ?? raw.status, isDraft),
961
+ author,
962
+ body: raw.body ?? raw.description ?? "",
963
+ isCrossRepository: false,
964
+ reviewDecision: cursorReviewDecision(raw.reviewDecision),
965
+ reviewRequests: originReviewRequests(raw.reviewRequests)
966
+ };
967
+ }
968
+ function originReviewRequests(raw) {
969
+ if (!Array.isArray(raw)) return raw;
970
+ return raw.map(originReviewer).filter((row) => row != null);
971
+ }
972
+ function originReviewer(raw) {
973
+ if (typeof raw === "string" && raw.trim()) return { login: raw.trim() };
974
+ if (!raw || typeof raw !== "object") return null;
975
+ const row = raw;
976
+ if (typeof row.login === "string" && row.login.trim()) return row;
977
+ const kind = String(row.kind ?? row.__typename ?? "").toLowerCase();
978
+ const slug = typeof row.slug === "string" ? row.slug.trim() : "";
979
+ const name = typeof row.name === "string" ? row.name.trim() : "";
980
+ const id = typeof row.id === "string" ? row.id.trim() : "";
981
+ if (kind.includes("team") || kind.includes("group") || slug.includes("/") && !row.login) {
982
+ return { __typename: "Team", slug: slug || name, name };
983
+ }
984
+ if (kind.includes("service") || kind.includes("bot")) {
985
+ const login2 = name || id || "cursor-bot";
986
+ return { __typename: "Bot", login: login2.toLowerCase().endsWith("[bot]") ? login2 : `${login2}[bot]` };
987
+ }
988
+ const login = name || id;
989
+ return login ? { login } : null;
990
+ }
991
+ function originChecksToRuns(stdout) {
992
+ let parsed;
993
+ try {
994
+ parsed = JSON.parse(stdout);
995
+ } catch {
996
+ return [];
997
+ }
998
+ const rows = Array.isArray(parsed) ? parsed : [];
999
+ return rows.map((row) => {
1000
+ const item = row ?? {};
1001
+ const conclusion = typeof item.conclusion === "string" ? item.conclusion.toLowerCase() : "";
1002
+ const status = typeof item.status === "string" ? item.status.toLowerCase() : "";
1003
+ const bucket = conclusion === "success" || conclusion === "neutral" ? "pass" : conclusion === "failure" || conclusion === "timed_out" || conclusion === "action_required" ? "fail" : conclusion === "cancelled" ? "cancel" : conclusion === "skipped" ? "skipping" : "pending";
1004
+ const state = conclusion || status || "pending";
1005
+ const link = typeof item.link === "string" ? item.link : typeof item.detailsUrl === "string" ? item.detailsUrl : null;
1006
+ const description = typeof item.summary === "string" ? item.summary : typeof item.description === "string" ? item.description : null;
1007
+ return {
1008
+ name: String(item.name ?? "check"),
1009
+ state,
1010
+ bucket,
1011
+ startedAt: typeof item.startedAt === "string" ? item.startedAt : null,
1012
+ completedAt: typeof item.completedAt === "string" ? item.completedAt : null,
1013
+ link,
1014
+ description,
1015
+ workflow: typeof item.group === "string" ? item.group : null,
1016
+ kind: "ci"
1017
+ };
1018
+ });
1019
+ }
1020
+ function listedHasLabels(pr, labels) {
1021
+ if (labels.length === 0) return true;
1022
+ const have = new Set((pr.labels ?? []).map((name) => name.toLowerCase()));
1023
+ return labels.every((label) => have.has(label.toLowerCase()));
1024
+ }
1025
+ async function listCursorPrs(repoPath, slug, opts, head) {
1026
+ const resolved = resolveListPrsOptions(opts);
1027
+ const limit = Math.max(1, Math.min(250, resolved.limit ?? 200));
1028
+ const fetchLimit = prListFetchLimit(limit, resolved.reviewer);
1029
+ const useSearch = resolved.labels.length > 0 || Boolean(resolved.reviewer) || Boolean(resolved.query);
1030
+ const state = resolved.draft === true ? "draft" : resolved.state;
1031
+ const args = [
1032
+ "pr",
1033
+ "list",
1034
+ ...repoArgs(slug),
1035
+ "--state",
1036
+ state,
1037
+ "--limit",
1038
+ String(fetchLimit),
1039
+ "--json",
1040
+ useSearch ? SEARCH_LIST_FIELDS : PLAIN_LIST_FIELDS
1041
+ ];
1042
+ if (head?.trim()) args.push("--head", head.trim());
1043
+ if (useSearch) {
1044
+ for (const label of resolved.labels) args.push("--label", label);
1045
+ if (resolved.query) args.push("--search", resolved.query);
1046
+ else if (resolved.labels.length === 0) {
1047
+ const token = state === "all" ? "is:open OR is:closed OR is:merged" : `is:${state}`;
1048
+ args.push("--search", token);
1049
+ }
1050
+ }
1051
+ const { stdout, exitCode } = await originCli(args, repoPath, { reject: false, timeoutMs: 2e4 });
1052
+ if (exitCode !== 0 || !stdout.trim()) return [];
1053
+ let parsed;
1054
+ try {
1055
+ parsed = JSON.parse(stdout);
1056
+ } catch {
1057
+ return [];
1058
+ }
1059
+ if (!Array.isArray(parsed)) return [];
1060
+ const normalized = parsed.filter((row) => row && typeof row === "object").map((row) => normalizeOriginPr(row));
1061
+ const listed = applyPrListResponse(JSON.stringify(normalized), { ...opts, limit: void 0 });
1062
+ return listed.filter((pr) => listedHasLabels(pr, resolved.labels)).slice(0, limit);
1063
+ }
1064
+ async function viewCursorPr(repoPath, slug, selector, fields) {
1065
+ const { stdout, exitCode } = await originCli(
1066
+ ["pr", "view", selector, ...repoArgs(slug), "--json", fields],
1067
+ repoPath,
1068
+ { reject: false, timeoutMs: 2e4 }
1069
+ );
1070
+ if (exitCode !== 0 || !stdout.trim()) return null;
1071
+ try {
1072
+ const parsed = JSON.parse(stdout);
1073
+ return normalizeOriginPr(parsed);
1074
+ } catch {
1075
+ return null;
1076
+ }
1077
+ }
1078
+ var CURSOR_PR_VIEW_FIELDS = VIEW_FIELDS;
1079
+ async function createOrUpdateCursorPr(repoPath, slug, opts) {
1080
+ const branch = cursorHeadBranch(opts.head, slug);
1081
+ const existing = await originCli(
1082
+ ["pr", "view", branch, ...repoArgs(slug), "--json", "url"],
1083
+ repoPath,
1084
+ { reject: false, timeoutMs: 2e4 }
1085
+ );
1086
+ if (existing.exitCode === 0 && existing.stdout.trim()) {
1087
+ let url2 = "";
1088
+ try {
1089
+ url2 = String(JSON.parse(existing.stdout).url ?? "");
1090
+ } catch {
1091
+ url2 = "";
1092
+ }
1093
+ if (url2) {
1094
+ await originCli(
1095
+ ["pr", "edit", branch, ...repoArgs(slug), "--title", opts.title, "-F", opts.bodyFile],
1096
+ repoPath,
1097
+ { reject: false, timeoutMs: 2e4 }
1098
+ );
1099
+ return url2;
1100
+ }
1101
+ }
1102
+ const args = [
1103
+ "pr",
1104
+ "create",
1105
+ ...repoArgs(slug),
1106
+ "--title",
1107
+ opts.title,
1108
+ "-F",
1109
+ opts.bodyFile,
1110
+ "--base",
1111
+ opts.base,
1112
+ "--head",
1113
+ branch,
1114
+ "--push"
1115
+ ];
1116
+ if (opts.draft) args.push("--draft");
1117
+ const created = await originCli(args, repoPath, { reject: false, timeoutMs: 6e4 });
1118
+ if (created.exitCode !== 0) {
1119
+ throw new Error(created.stderr.trim() || created.stdout.trim() || "origin pr create failed");
1120
+ }
1121
+ const printed = created.stdout.trim().split("\n").find((line) => line.startsWith("http"));
1122
+ if (printed) return printed;
1123
+ const viewed = await viewCursorPr(repoPath, slug, branch, "url");
1124
+ const url = typeof viewed?.url === "string" ? viewed.url.trim() : "";
1125
+ return url || created.stdout.trim();
1126
+ }
1127
+ async function readyCursorPr(repoPath, slug, selector) {
1128
+ const ready = await originCli(["pr", "ready", selector, ...repoArgs(slug)], repoPath, {
1129
+ reject: false,
1130
+ timeoutMs: 2e4
1131
+ });
1132
+ if (ready.exitCode !== 0) {
1133
+ throw new Error(ready.stderr.trim() || ready.stdout.trim() || "Could not mark the Cursor pull request ready");
1134
+ }
1135
+ }
1136
+ async function mergeCursorPr(repoPath, slug, selector, method) {
1137
+ const args = ["pr", "merge", selector, ...repoArgs(slug)];
1138
+ if (method === "merge") args.push("--merge");
1139
+ else if (method === "squash") args.push("--squash");
1140
+ const merged = await originCli(args, repoPath, {
1141
+ reject: false,
1142
+ timeoutMs: 6e4
1143
+ });
1144
+ if (merged.exitCode !== 0) {
1145
+ throw new Error(merged.stderr.trim() || merged.stdout.trim() || "origin pr merge failed");
1146
+ }
1147
+ }
1148
+ async function fetchCursorPrHead(repoPath, slug, number, localBranch) {
1149
+ const view = await viewCursorPr(repoPath, slug, String(number), "headRefName");
1150
+ const head = typeof view?.headRefName === "string" ? view.headRefName.trim() : "";
1151
+ if (!head) {
1152
+ throw new Error(`Could not resolve the head branch for Cursor pull request #${number}`);
1153
+ }
1154
+ const result = await git(["fetch", "origin", `${head}:${localBranch}`], repoPath, { reject: false });
1155
+ if (result.exitCode !== 0) {
1156
+ throw new Error((result.stderr || result.stdout).trim() || `git fetch origin ${head} failed`);
1157
+ }
1158
+ }
1159
+ async function cursorPrChecks(repoPath, slug, selector) {
1160
+ const { stdout, exitCode, stderr } = await originCli(
1161
+ ["pr", "checks", selector, ...repoArgs(slug), "--json", "name,status,conclusion,detailsUrl,startedAt,completedAt,summary,group,link"],
1162
+ repoPath,
1163
+ { reject: false, timeoutMs: 2e4 }
1164
+ );
1165
+ const err = stderr.trim();
1166
+ let ci;
1167
+ if (!stdout.trim()) {
1168
+ if (/no pull request|not found|no change/i.test(err)) return null;
1169
+ if (exitCode !== 0 && err) throw new Error(err);
1170
+ ci = [];
1171
+ } else {
1172
+ ci = originChecksToRuns(stdout);
1173
+ }
1174
+ const view = await viewCursorPr(
1175
+ repoPath,
1176
+ slug,
1177
+ selector,
1178
+ "url,baseRefName,reviewDecision,mergeability"
1179
+ );
1180
+ if (!view) return ci;
1181
+ return cursorChecksWithGate(ci, cursorViewToPrMeta(view));
1182
+ }
1183
+ function cursorChecksWithGate(ci, meta) {
1184
+ const gate = {
1185
+ mergeable: meta.mergeable,
1186
+ mergeStateStatus: meta.mergeStateStatus,
1187
+ reviewDecision: meta.reviewDecision,
1188
+ baseRefName: meta.baseRefName,
1189
+ url: meta.url,
1190
+ isInMergeQueue: meta.isInMergeQueue
1191
+ };
1192
+ const review = (gate.reviewDecision ?? "").toUpperCase();
1193
+ const hasReviewRow = review === "CHANGES_REQUESTED" || review === "REVIEW_REQUIRED";
1194
+ const gateRows = buildMergeGateChecks(gate, {
1195
+ suppressGenericBlocked: ci.some((check) => check.bucket === "fail") || hasReviewRow
1196
+ });
1197
+ return [...gateRows, ...ci];
1198
+ }
1199
+
1200
+ // src/util/enqueue-by-key.ts
1201
+ var tails = /* @__PURE__ */ new Map();
1202
+ function enqueueByKey(key, fn) {
1203
+ const prev = tails.get(key) ?? Promise.resolve();
1204
+ const next = prev.then(fn, fn);
1205
+ tails.set(
1206
+ key,
1207
+ next.then(
1208
+ () => void 0,
1209
+ () => void 0
1210
+ )
1211
+ );
1212
+ return next;
1213
+ }
1214
+
1215
+ // src/git/repo-git-lock.ts
1216
+ function repoLockKey(repoPath) {
1217
+ const trimmed = repoPath.replace(/\/+$/, "");
1218
+ return `git-worktree:${trimmed || "/"}`;
1219
+ }
1220
+ function withRepoGitLock(repoPath, fn) {
1221
+ return enqueueByKey(repoLockKey(repoPath), fn);
1222
+ }
1223
+
849
1224
  // src/git/stack.ts
850
1225
  var cachedStatus = null;
851
1226
  var STATUS_TTL_MS = 6e4;
@@ -1083,31 +1458,6 @@ function allocateTeamSlug(repoPath) {
1083
1458
  }
1084
1459
 
1085
1460
  // src/git/worktree.ts
1086
- async function lookupGithubGraphqlReset(cwd) {
1087
- const result = await gh(["api", "rate_limit"], cwd, { reject: false });
1088
- if (result.exitCode !== 0 || !result.stdout.trim()) return void 0;
1089
- try {
1090
- const data = JSON.parse(result.stdout);
1091
- const reset = data.resources?.graphql?.reset;
1092
- return typeof reset === "number" ? reset : void 0;
1093
- } catch {
1094
- return void 0;
1095
- }
1096
- }
1097
- async function formatPrCreateFailure(raw, cwd, ctx) {
1098
- if (!isGhRateLimitError(raw)) {
1099
- return formatGhLandError(raw, {
1100
- targetedRepo: ctx?.slug,
1101
- headRef: ctx?.head
1102
- });
1103
- }
1104
- const resetAt = await lookupGithubGraphqlReset(cwd);
1105
- return formatGhLandError(raw, {
1106
- resetAt,
1107
- targetedRepo: ctx?.slug,
1108
- headRef: ctx?.head
1109
- });
1110
- }
1111
1461
  function slugify(input) {
1112
1462
  return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
1113
1463
  }
@@ -1130,25 +1480,39 @@ function canonicalizeRepoPath(path) {
1130
1480
  }
1131
1481
  }
1132
1482
  function parseGithubSlugFromRemoteUrl(url) {
1133
- const trimmed = url.trim().replace(/\.git$/i, "");
1134
- if (!trimmed) return null;
1135
- const github = trimmed.match(/github\.com[:/]([^/]+)\/([^/]+)$/i);
1136
- if (github?.[1] && github[2]) return `${github[1]}/${github[2]}`;
1137
- const sshAlias = trimmed.match(/^git@[^:]+:([^/]+)\/([^/]+)$/i);
1138
- if (sshAlias?.[1] && sshAlias[2]) return `${sshAlias[1]}/${sshAlias[2]}`;
1139
- return null;
1483
+ const parsed = parseRemoteRepo(url);
1484
+ return parsed?.forge === "github" ? parsed.slug : null;
1140
1485
  }
1141
- async function slugFromGitRemote(repoPath, remote) {
1486
+ async function remoteUrl(repoPath, remote) {
1142
1487
  const result = await git(["remote", "get-url", remote], repoPath, {
1143
1488
  reject: false,
1144
1489
  timeoutMs: 5e3
1145
1490
  });
1146
1491
  if (result.exitCode !== 0 || !result.stdout.trim()) return null;
1147
- return parseGithubSlugFromRemoteUrl(result.stdout);
1492
+ return result.stdout.trim();
1493
+ }
1494
+ async function slugFromGitRemote(repoPath, remote) {
1495
+ const url = await remoteUrl(repoPath, remote);
1496
+ if (!url) return null;
1497
+ return parseGithubSlugFromRemoteUrl(url);
1498
+ }
1499
+ async function resolveOriginRemote(repoPath) {
1500
+ const url = await remoteUrl(repoPath, "origin");
1501
+ if (!url) return null;
1502
+ return parseRemoteRepo(url);
1503
+ }
1504
+ async function resolveProjectRemoteTag(repoPath) {
1505
+ const origin = await resolveOriginRemote(repoPath);
1506
+ if (origin) return formatRemoteTag(origin);
1507
+ const slug = await resolveGithubRepoSlug(repoPath);
1508
+ return slug ? `github:${slug}` : null;
1148
1509
  }
1149
1510
  async function resolveGithubRepoSlug(repoPath) {
1150
- const fromOrigin = await slugFromGitRemote(repoPath, "origin");
1151
- if (fromOrigin) return fromOrigin;
1511
+ const originUrl = await remoteUrl(repoPath, "origin");
1512
+ if (originUrl) {
1513
+ const parsed = parseRemoteRepo(originUrl);
1514
+ return parsed?.forge === "github" ? parsed.slug : null;
1515
+ }
1152
1516
  const hasAlt = Boolean(await slugFromGitRemote(repoPath, "upstream")) || Boolean(await slugFromGitRemote(repoPath, "github"));
1153
1517
  if (!hasAlt) {
1154
1518
  const viaGh = await gh(
@@ -1194,6 +1558,8 @@ ${view.stderr}`;
1194
1558
  });
1195
1559
  }
1196
1560
  async function originGhRepoEnv(cwd, opts) {
1561
+ const origin = await resolveOriginRemote(cwd);
1562
+ if (origin?.forge === "cursor") return { ORIGIN_REPO: origin.slug };
1197
1563
  await ensureGhPreferOrigin(cwd);
1198
1564
  const slug = await resolveGithubRepoSlug(cwd);
1199
1565
  const mode = opts?.mode ?? getGithubGitAuthMode();
@@ -1308,7 +1674,16 @@ async function listBranches(repoPath, opts) {
1308
1674
  }
1309
1675
  return sorted.filter((b) => keep.has(b.name));
1310
1676
  }
1677
+ function entireUsesTrails() {
1678
+ return new Error(
1679
+ "This checkout is on Entire. Trails replace pull requests \u2014 use `entire trail` (Settings \u2192 Git \u2192 Entire), not `gh`."
1680
+ );
1681
+ }
1311
1682
  async function listPrs(repoPath, opts) {
1683
+ const origin = await resolveOriginRemote(repoPath);
1684
+ if (origin?.forge === "entire") return [];
1685
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
1686
+ if (cursorSlug) return listCursorPrs(repoPath, cursorSlug, opts);
1312
1687
  const slug = await resolveGithubRepoSlug(repoPath);
1313
1688
  const resolved = resolveListPrsOptions(opts);
1314
1689
  const limit = resolved.limit ?? 200;
@@ -1346,6 +1721,18 @@ function prInfoFromGhView(view) {
1346
1721
  };
1347
1722
  }
1348
1723
  async function getPr(repoPath, number) {
1724
+ const origin = await resolveOriginRemote(repoPath);
1725
+ if (origin?.forge === "entire") return null;
1726
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
1727
+ if (cursorSlug) {
1728
+ const view = await viewCursorPr(
1729
+ repoPath,
1730
+ cursorSlug,
1731
+ String(number),
1732
+ "number,title,headRefName,url,author,isDraft,state"
1733
+ );
1734
+ return view ? prInfoFromGhView(view) : null;
1735
+ }
1349
1736
  const slug = await resolveGithubRepoSlug(repoPath);
1350
1737
  const args = [
1351
1738
  "pr",
@@ -1407,6 +1794,13 @@ function connectedPrSelectors(thread, currentHeadPrUrl) {
1407
1794
  async function getPrForHeadBranch(repoPath, branch) {
1408
1795
  const head = normalizeGitBranchName(branch);
1409
1796
  if (!head || isDefaultishSourceRef(head)) return null;
1797
+ const origin = await resolveOriginRemote(repoPath);
1798
+ if (origin?.forge === "entire") return null;
1799
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
1800
+ if (cursorSlug) {
1801
+ const rows = await listCursorPrs(repoPath, cursorSlug, { state: "open", limit: 5 }, head);
1802
+ return rows[0] ?? null;
1803
+ }
1410
1804
  const slug = await resolveGithubRepoSlug(repoPath);
1411
1805
  const listHead = slug ? ghHeadRef(slug, head) : head;
1412
1806
  const listArgs = [
@@ -1571,6 +1965,10 @@ async function probeLocalMergeGate(cwd, gate) {
1571
1965
  return { gate, files: [] };
1572
1966
  }
1573
1967
  async function getPrChecks(cwd, selector) {
1968
+ const origin = await resolveOriginRemote(cwd);
1969
+ if (origin?.forge === "entire") return null;
1970
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
1971
+ if (cursorSlug) return cursorPrChecks(cwd, cursorSlug, selector);
1574
1972
  const slug = await resolveGithubRepoSlug(cwd);
1575
1973
  const args = [
1576
1974
  "pr",
@@ -1626,6 +2024,18 @@ async function getPrChecks(cwd, selector) {
1626
2024
  return [...gateRows, ...ciChecks];
1627
2025
  }
1628
2026
  async function getPrMeta(cwd, selector) {
2027
+ const origin = await resolveOriginRemote(cwd);
2028
+ if (origin?.forge === "entire") return null;
2029
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2030
+ if (cursorSlug) {
2031
+ const view = await viewCursorPr(
2032
+ cwd,
2033
+ cursorSlug,
2034
+ selector,
2035
+ "number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,author,labels,mergeability"
2036
+ );
2037
+ return view ? cursorViewToPrMeta(view) : null;
2038
+ }
1629
2039
  const slug = await resolveGithubRepoSlug(cwd);
1630
2040
  const viewArgs = [
1631
2041
  "pr",
@@ -1776,6 +2186,13 @@ function parsePrDetailsView(view) {
1776
2186
  };
1777
2187
  }
1778
2188
  async function getPrDetails(cwd, selector) {
2189
+ const origin = await resolveOriginRemote(cwd);
2190
+ if (origin?.forge === "entire") return null;
2191
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2192
+ if (cursorSlug) {
2193
+ const view2 = await viewCursorPr(cwd, cursorSlug, selector, CURSOR_PR_VIEW_FIELDS);
2194
+ return view2 ? parsePrDetailsView(view2) : null;
2195
+ }
1779
2196
  const slug = await resolveGithubRepoSlug(cwd);
1780
2197
  const viewArgs = ["pr", "view", selector, "--json", PR_DETAILS_JSON_FIELDS];
1781
2198
  if (slug) viewArgs.push("--repo", slug);
@@ -1795,6 +2212,13 @@ async function getPrDetails(cwd, selector) {
1795
2212
  return parsePrDetailsView(view);
1796
2213
  }
1797
2214
  async function fetchPrHead(repoPath, number, localBranch) {
2215
+ const origin = await resolveOriginRemote(repoPath);
2216
+ if (origin?.forge === "entire") throw entireUsesTrails();
2217
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2218
+ if (cursorSlug) {
2219
+ await fetchCursorPrHead(repoPath, cursorSlug, number, localBranch);
2220
+ return;
2221
+ }
1798
2222
  const slug = await resolveGithubRepoSlug(repoPath);
1799
2223
  const refspec = `+pull/${number}/head:${localBranch}`;
1800
2224
  const errors = [];
@@ -1937,10 +2361,10 @@ async function originRemoteUrl(cwd) {
1937
2361
  const result = await git(["remote", "get-url", "origin"], cwd, { reject: false });
1938
2362
  return result.exitCode === 0 ? result.stdout.trim() : "";
1939
2363
  }
1940
- function githubHttpsRetryEnv(remoteUrl) {
2364
+ function githubHttpsRetryEnv(remoteUrl2) {
1941
2365
  return {
1942
2366
  GIT_TERMINAL_PROMPT: "0",
1943
- ...githubAgentGitEnv(void 0, { remoteUrl })
2367
+ ...githubAgentGitEnv(void 0, { remoteUrl: remoteUrl2 })
1944
2368
  };
1945
2369
  }
1946
2370
  async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
@@ -1954,11 +2378,11 @@ async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
1954
2378
  }
1955
2379
  const token = await resolveGithubAgentToken(mode, repoPath);
1956
2380
  if (!token) return false;
1957
- const remoteUrl = await originRemoteUrl(repoPath);
2381
+ const remoteUrl2 = await originRemoteUrl(repoPath);
1958
2382
  const tryHttps = async (header) => git(args, repoPath, {
1959
2383
  reject: false,
1960
2384
  timeoutMs,
1961
- env: githubHttpsRetryEnv(remoteUrl),
2385
+ env: githubHttpsRetryEnv(remoteUrl2),
1962
2386
  config: {
1963
2387
  "http.extraHeader": header
1964
2388
  }
@@ -2307,10 +2731,10 @@ async function pushBranch(worktreePath, branchName) {
2307
2731
  sshErr || `git push origin ${branchName} failed` + (!token ? " (SSH remote and gh auth token unavailable \u2014 unlock ssh-agent or run: gh auth login)" : "")
2308
2732
  );
2309
2733
  }
2310
- const remoteUrl = await originRemoteUrl(worktreePath);
2734
+ const remoteUrl2 = await originRemoteUrl(worktreePath);
2311
2735
  const tryHttps = async (header) => git(["push", "-u", "origin", branchName], worktreePath, {
2312
2736
  reject: false,
2313
- env: githubHttpsRetryEnv(remoteUrl),
2737
+ env: githubHttpsRetryEnv(remoteUrl2),
2314
2738
  config: {
2315
2739
  "http.extraHeader": header
2316
2740
  }
@@ -2323,6 +2747,40 @@ async function pushBranch(worktreePath, branchName) {
2323
2747
  const httpsErr = (basicPush.stderr || bearer.stderr || bearer.stdout).trim();
2324
2748
  throw new Error(httpsErr || sshErr || `git push origin ${branchName} failed`);
2325
2749
  }
2750
+ async function markCursorPrReady(cwd, slug, selector) {
2751
+ const view = await viewCursorPr(cwd, slug, selector, "url,state,isDraft");
2752
+ if (!view) throw new Error("Could not load the Cursor pull request");
2753
+ const url = String(view.url ?? "");
2754
+ const state = String(view.state ?? "").toUpperCase();
2755
+ const isDraft = Boolean(view.isDraft);
2756
+ if (state === "MERGED" || state === "CLOSED") {
2757
+ return { url, state, isDraft: false };
2758
+ }
2759
+ if (isDraft) {
2760
+ if (await isDirty(cwd)) {
2761
+ throw new Error(
2762
+ "Commit and push local work before marking the pull request ready for review."
2763
+ );
2764
+ }
2765
+ const unpushed = await countUnpushedVsOrigin(cwd);
2766
+ if (unpushed > 0) {
2767
+ throw new Error(
2768
+ "Push this branch to origin before marking the pull request ready for review."
2769
+ );
2770
+ }
2771
+ await readyCursorPr(cwd, slug, selector);
2772
+ }
2773
+ return { url, state: state || "OPEN", isDraft: false };
2774
+ }
2775
+ async function mergeCursorPullRequest(cwd, slug, selector, method) {
2776
+ const view = await viewCursorPr(cwd, slug, selector, "url,state,isDraft");
2777
+ if (!view) throw new Error("Could not load the Cursor pull request");
2778
+ const url = String(view.url ?? "");
2779
+ if (String(view.state ?? "").toUpperCase() === "MERGED") return { url, state: "MERGED" };
2780
+ if (view.isDraft) await readyCursorPr(cwd, slug, selector);
2781
+ await mergeCursorPr(cwd, slug, selector, method);
2782
+ return { url, state: "MERGED" };
2783
+ }
2326
2784
  async function runPrReady(cwd, selector, slug) {
2327
2785
  const readyArgs = ["pr", "ready", selector];
2328
2786
  if (slug) readyArgs.push("--repo", slug);
@@ -2334,6 +2792,10 @@ async function runPrReady(cwd, selector, slug) {
2334
2792
  }
2335
2793
  }
2336
2794
  async function markPrReady(cwd, selector) {
2795
+ const origin = await resolveOriginRemote(cwd);
2796
+ if (origin?.forge === "entire") throw entireUsesTrails();
2797
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2798
+ if (cursorSlug) return markCursorPrReady(cwd, cursorSlug, selector);
2337
2799
  const slug = await resolveGithubRepoSlug(cwd);
2338
2800
  const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft"];
2339
2801
  if (slug) viewArgs.push("--repo", slug);
@@ -2372,6 +2834,10 @@ async function markPrReady(cwd, selector) {
2372
2834
  return { url, state: state || "OPEN", isDraft: false };
2373
2835
  }
2374
2836
  async function mergePr(cwd, selector, opts) {
2837
+ const origin = await resolveOriginRemote(cwd);
2838
+ if (origin?.forge === "entire") throw entireUsesTrails();
2839
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2840
+ if (cursorSlug) return mergeCursorPullRequest(cwd, cursorSlug, selector, opts?.method ?? "squash");
2375
2841
  const slug = await resolveGithubRepoSlug(cwd);
2376
2842
  const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft,number"];
2377
2843
  if (slug) viewArgs.push("--repo", slug);
@@ -2430,116 +2896,6 @@ async function mergePr(cwd, selector, opts) {
2430
2896
  }
2431
2897
  return { url, state: "MERGED" };
2432
2898
  }
2433
- function ghPrCreateAssigneeArgs(assignee) {
2434
- const raw = assignee?.trim();
2435
- if (raw && /^(none|unassigned)$/i.test(raw)) return [];
2436
- return ["--assignee", raw || "@me"];
2437
- }
2438
- async function createOrUpdatePr(worktreePath, opts) {
2439
- await ensureGhPreferOrigin(worktreePath);
2440
- const slug = await resolveGithubRepoSlug(worktreePath);
2441
- if (!slug) {
2442
- throw new Error(
2443
- "Could not resolve the origin GitHub repo (owner/name) for this worktree. Check that `git remote get-url origin` points at github.com."
2444
- );
2445
- }
2446
- const repoArgs = ghRepoSelectArgs(slug);
2447
- const headRef = ghHeadRef(slug, opts.head);
2448
- const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
2449
- const bodyText = clampGithubPrBody(opts.body ?? opts.title);
2450
- const bodyFile = join2(
2451
- tmpdir(),
2452
- `sideboard-pr-body-${process.pid}-${randomBytes(6).toString("hex")}.md`
2453
- );
2454
- writeFileSync(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
2455
- `, "utf8");
2456
- const bodyArgs = ["--body-file", bodyFile];
2457
- try {
2458
- return await createOrUpdatePrWithBodyFile(worktreePath, {
2459
- ...opts,
2460
- slug,
2461
- repoArgs,
2462
- headRef,
2463
- branchOnly,
2464
- bodyArgs
2465
- });
2466
- } finally {
2467
- try {
2468
- unlinkSync(bodyFile);
2469
- } catch {
2470
- }
2471
- }
2472
- }
2473
- async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
2474
- const { slug, repoArgs, headRef, branchOnly, bodyArgs } = opts;
2475
- const existing = await gh(
2476
- [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
2477
- worktreePath,
2478
- { reject: false }
2479
- );
2480
- if (existing.exitCode === 0 && existing.stdout.trim() && !opts.web) {
2481
- const url2 = existing.stdout.trim();
2482
- await gh(
2483
- [
2484
- ...repoArgs,
2485
- "pr",
2486
- "edit",
2487
- branchOnly,
2488
- "--title",
2489
- opts.title,
2490
- ...bodyArgs
2491
- ],
2492
- worktreePath,
2493
- { reject: false }
2494
- );
2495
- return url2;
2496
- }
2497
- if (opts.web) {
2498
- const args2 = [
2499
- ...repoArgs,
2500
- "pr",
2501
- "create",
2502
- "--web",
2503
- "--title",
2504
- opts.title,
2505
- ...bodyArgs,
2506
- "--base",
2507
- opts.base,
2508
- "--head",
2509
- headRef
2510
- ];
2511
- if (opts.draft) args2.push("--draft");
2512
- args2.push(...ghPrCreateAssigneeArgs(opts.assignee));
2513
- await gh(args2, worktreePath, { reject: false });
2514
- const again = await gh(
2515
- [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
2516
- worktreePath,
2517
- { reject: false }
2518
- );
2519
- return again.stdout.trim() || "";
2520
- }
2521
- const args = [
2522
- ...repoArgs,
2523
- "pr",
2524
- "create",
2525
- "--title",
2526
- opts.title,
2527
- ...bodyArgs,
2528
- "--base",
2529
- opts.base,
2530
- "--head",
2531
- headRef
2532
- ];
2533
- if (opts.draft) args.push("--draft");
2534
- args.push(...ghPrCreateAssigneeArgs(opts.assignee));
2535
- const created = await gh(args, worktreePath, { reject: false });
2536
- if (created.exitCode !== 0) {
2537
- const raw = created.stderr.trim() || created.stdout.trim() || "gh pr create failed";
2538
- throw new Error(await formatPrCreateFailure(raw, worktreePath, { slug, head: headRef }));
2539
- }
2540
- const url = created.stdout.trim().split("\n").find((l) => l.startsWith("http")) ?? created.stdout.trim();
2541
- return url;
2542
- }
2543
2899
  function suggestSlug(source) {
2544
2900
  const base = slugify(source || "thread");
2545
2901
  const stamp = Date.now().toString(36).slice(-4);
@@ -2547,6 +2903,8 @@ function suggestSlug(source) {
2547
2903
  }
2548
2904
 
2549
2905
  export {
2906
+ isGhRateLimitError,
2907
+ clampGithubPrBody,
2550
2908
  formatGhLandError,
2551
2909
  githubAgentGitEnv,
2552
2910
  mergeAgentGitAuthEnv,
@@ -2555,10 +2913,12 @@ export {
2555
2913
  resolveCodexGitWritableRoots,
2556
2914
  codexUnattendedGitConfigArgs,
2557
2915
  formatGitAuthModeDirective,
2916
+ formatRemoteTag,
2558
2917
  resolveListPrsOptions,
2918
+ ciChecksFailed,
2919
+ createOrUpdateCursorPr,
2559
2920
  enqueueByKey,
2560
2921
  withRepoGitLock,
2561
- ciChecksFailed,
2562
2922
  detectGhStack,
2563
2923
  getPrStack,
2564
2924
  initPrStack,
@@ -2569,6 +2929,8 @@ export {
2569
2929
  resolveRepoRoot,
2570
2930
  canonicalizeRepoPath,
2571
2931
  parseGithubSlugFromRemoteUrl,
2932
+ resolveOriginRemote,
2933
+ resolveProjectRemoteTag,
2572
2934
  resolveGithubRepoSlug,
2573
2935
  ghRepoSelectArgs,
2574
2936
  ghHeadRef,
@@ -2577,6 +2939,7 @@ export {
2577
2939
  resolveDefaultBranch,
2578
2940
  resolveDiffBaseRef,
2579
2941
  listBranches,
2942
+ entireUsesTrails,
2580
2943
  listPrs,
2581
2944
  getPr,
2582
2945
  isDefaultishSourceRef,
@@ -2608,7 +2971,5 @@ export {
2608
2971
  pushBranch,
2609
2972
  markPrReady,
2610
2973
  mergePr,
2611
- ghPrCreateAssigneeArgs,
2612
- createOrUpdatePr,
2613
2974
  suggestSlug
2614
2975
  };