@sideboard-ai/core 0.1.320 → 0.1.322

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
@@ -4,10 +4,10 @@ import {
4
4
  normalizeTakenSlug,
5
5
  normalizeWorktreePath,
6
6
  takenSlugsFromThread
7
- } from "./chunk-4GZC6DDH.js";
7
+ } from "./chunk-IFWGG2M2.js";
8
8
  import {
9
9
  ensureWorktreeSideboardIgnored
10
- } from "./chunk-2P4XKWV2.js";
10
+ } from "./chunk-QKHEVSB5.js";
11
11
  import {
12
12
  isWorkspaceScratchPath
13
13
  } from "./chunk-6K64FJDD.js";
@@ -19,8 +19,9 @@ import {
19
19
  githubCredentialHelperGitConfig,
20
20
  githubGhConfigEnv,
21
21
  materializeGithubAgentAuth,
22
+ originCli,
22
23
  resolveGhAuthToken
23
- } from "./chunk-QVN2WOKJ.js";
24
+ } from "./chunk-2TUE6JKQ.js";
24
25
  import {
25
26
  getGithubGitAuthMode,
26
27
  getGithubPat
@@ -30,9 +31,7 @@ import {
30
31
  } from "./chunk-H4UEFJRI.js";
31
32
 
32
33
  // src/git/worktree.ts
33
- import { randomBytes } from "crypto";
34
- import { existsSync as existsSync2, realpathSync, unlinkSync, writeFileSync } from "fs";
35
- import { tmpdir } from "os";
34
+ import { existsSync as existsSync2, realpathSync } from "fs";
36
35
  import { join as join2 } from "path";
37
36
 
38
37
  // src/git/gh-errors.ts
@@ -145,9 +144,9 @@ var HTTPS_REWRITE = [
145
144
  { key: "url.https://github.com/.insteadOf", value: "git@github.com:" },
146
145
  { key: "url.https://github.com/.insteadOf", value: "ssh://git@github.com/" }
147
146
  ];
148
- function githubHttpsInsteadOfEntries(remoteUrl) {
147
+ function githubHttpsInsteadOfEntries(remoteUrl2) {
149
148
  const entries = [...HTTPS_REWRITE];
150
- const url = remoteUrl?.trim() ?? "";
149
+ const url = remoteUrl2?.trim() ?? "";
151
150
  const scpAlias = url.match(/^(git@github\.com-[^:]+):/i);
152
151
  if (scpAlias?.[1]) {
153
152
  entries.push({
@@ -343,6 +342,53 @@ function formatGitAuthModeDirective(mode) {
343
342
  }
344
343
  }
345
344
 
345
+ // src/git/remote-forge.ts
346
+ function formatRemoteTag(remote) {
347
+ const name = remote.forge === "other" ? remote.host : remote.forge;
348
+ return `${name}:${remote.slug}`;
349
+ }
350
+ function forgeForHost(host) {
351
+ const h = host.toLowerCase();
352
+ if (h === "github.com" || h.startsWith("github.com-")) return "github";
353
+ if (h === "origin.cursor.com") return "cursor";
354
+ if (h === "entire.io" || h.endsWith(".entire.io") || h === "entire.com" || h.endsWith(".entire.com")) {
355
+ return "entire";
356
+ }
357
+ return "other";
358
+ }
359
+ function repoFromParts(host, owner, name) {
360
+ if (!host || !owner || !name || owner.includes("@")) return null;
361
+ const normalized = host.toLowerCase();
362
+ return {
363
+ forge: forgeForHost(normalized),
364
+ host: normalized,
365
+ slug: `${owner}/${name}`
366
+ };
367
+ }
368
+ function parseRemoteRepo(url) {
369
+ const trimmed = url.trim().replace(/\.git$/i, "");
370
+ if (!trimmed) return null;
371
+ if (trimmed.toLowerCase().startsWith("entire://")) {
372
+ const parts = trimmed.slice("entire://".length).replace(/^\/+/, "").split("/").filter(Boolean);
373
+ if (parts.length === 2) return { forge: "entire", host: "entire.io", slug: `${parts[0]}/${parts[1]}` };
374
+ if (parts.length >= 3) {
375
+ return repoFromParts(parts[0], parts[1], parts[2]);
376
+ }
377
+ return null;
378
+ }
379
+ const ssh = trimmed.match(/^git@([^:]+):([^/]+)\/([^/]+)$/i);
380
+ if (ssh?.[1] && ssh[2] && ssh[3]) return repoFromParts(ssh[1], ssh[2], ssh[3]);
381
+ try {
382
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
383
+ const parsed = new URL(withScheme);
384
+ const parts = parsed.pathname.split("/").filter(Boolean);
385
+ if (parts.length < 2) return null;
386
+ return repoFromParts(parsed.hostname, parts[parts.length - 2], parts[parts.length - 1]);
387
+ } catch {
388
+ return null;
389
+ }
390
+ }
391
+
346
392
  // src/git/list-prs.ts
347
393
  var PR_LIST_JSON_FIELDS = [
348
394
  "number",
@@ -704,30 +750,6 @@ async function listGithubViewerTeamSlugs(repoPath) {
704
750
  return slugs;
705
751
  }
706
752
 
707
- // src/util/enqueue-by-key.ts
708
- var tails = /* @__PURE__ */ new Map();
709
- function enqueueByKey(key, fn) {
710
- const prev = tails.get(key) ?? Promise.resolve();
711
- const next = prev.then(fn, fn);
712
- tails.set(
713
- key,
714
- next.then(
715
- () => void 0,
716
- () => void 0
717
- )
718
- );
719
- return next;
720
- }
721
-
722
- // src/git/repo-git-lock.ts
723
- function repoLockKey(repoPath) {
724
- const trimmed = repoPath.replace(/\/+$/, "");
725
- return `git-worktree:${trimmed || "/"}`;
726
- }
727
- function withRepoGitLock(repoPath, fn) {
728
- return enqueueByKey(repoLockKey(repoPath), fn);
729
- }
730
-
731
753
  // src/git/pr-gates.ts
732
754
  function prIsInMergeQueue(gate) {
733
755
  if (gate.isInMergeQueue) return true;
@@ -873,6 +895,359 @@ function buildMergeGateChecks(gate, opts = {}) {
873
895
  return rows;
874
896
  }
875
897
 
898
+ // src/git/cursor-pr.ts
899
+ var PLAIN_LIST_FIELDS = "number,title,headRefName,url,author,state,isDraft,reviewDecision";
900
+ var SEARCH_LIST_FIELDS = `${PLAIN_LIST_FIELDS},labels,reviewRequests`;
901
+ var VIEW_FIELDS = "number,title,body,url,state,isDraft,reviewDecision,author,createdAt,baseRefName,headRefName,additions,deletions,changedFiles,commits,comments,reviews,labels,mergeability";
902
+ function repoArgs(slug) {
903
+ return ["-R", slug];
904
+ }
905
+ function cursorHeadBranch(head, slug) {
906
+ const branch = head.trim().replace(/^refs\/heads\//, "");
907
+ const owner = slug.split("/")[0];
908
+ if (owner && branch.startsWith(`${owner}:`)) return branch.slice(owner.length + 1);
909
+ return branch;
910
+ }
911
+ function cursorMergeFields(value) {
912
+ if (typeof value === "string" && value.trim()) {
913
+ const upper = value.trim().toUpperCase();
914
+ return { mergeable: upper, mergeStateStatus: upper };
915
+ }
916
+ if (value && typeof value === "object") {
917
+ const row = value;
918
+ const conflictState = String(row.mergeConflictState ?? "").trim().toUpperCase();
919
+ if ("hasMergeConflicts" in row || conflictState) {
920
+ if (conflictState.includes("PENDING")) {
921
+ return { mergeable: "UNKNOWN", mergeStateStatus: "UNKNOWN" };
922
+ }
923
+ const conflicting = row.hasMergeConflicts === true || conflictState.includes("CONFLICT") && !conflictState.includes("CLEAN");
924
+ if (conflicting) return { mergeable: "CONFLICTING", mergeStateStatus: "DIRTY" };
925
+ if (row.hasMergeConflicts === false || conflictState.includes("CLEAN")) {
926
+ return { mergeable: "MERGEABLE", mergeStateStatus: "CLEAN" };
927
+ }
928
+ }
929
+ const verdict = String(row.verdict ?? row.verdictKind ?? "").trim().toUpperCase();
930
+ if (verdict === "BLOCKED") return { mergeable: null, mergeStateStatus: "BLOCKED" };
931
+ if (verdict === "MERGEABLE") return { mergeable: "MERGEABLE", mergeStateStatus: "CLEAN" };
932
+ const status = String(row.status ?? row.state ?? row.mergeable ?? "").trim().toUpperCase();
933
+ return { mergeable: status || null, mergeStateStatus: status || null };
934
+ }
935
+ return { mergeable: null, mergeStateStatus: null };
936
+ }
937
+ function cursorReviewDecision(value) {
938
+ const raw = typeof value === "string" ? value.trim().toUpperCase().replace(/[\s-]+/g, "_") : "";
939
+ if (!raw) return null;
940
+ if (raw === "REQUIRED" || raw === "REVIEW_REQUIRED") return "REVIEW_REQUIRED";
941
+ if (raw === "CHANGES_REQUESTED") return "CHANGES_REQUESTED";
942
+ if (raw === "APPROVED") return "APPROVED";
943
+ return raw;
944
+ }
945
+ function cursorViewToPrMeta(view) {
946
+ const merge = cursorMergeFields(view.mergeability ?? view.mergeable);
947
+ const author = view.author ?? {};
948
+ return {
949
+ number: Number(view.number),
950
+ title: String(view.title ?? ""),
951
+ url: String(view.url ?? ""),
952
+ state: String(view.state ?? ""),
953
+ isDraft: Boolean(view.isDraft),
954
+ reviewDecision: cursorReviewDecision(view.reviewDecision),
955
+ baseRefName: String(view.baseRefName ?? ""),
956
+ headRefName: String(view.headRefName ?? ""),
957
+ isInMergeQueue: false,
958
+ mergeable: merge.mergeable,
959
+ mergeStateStatus: merge.mergeStateStatus,
960
+ authorLogin: author.login?.trim() || null,
961
+ reviewerLogins: [],
962
+ labels: labelNames(view.labels)
963
+ };
964
+ }
965
+ function loginOf(author) {
966
+ if (!author || typeof author !== "object") return "";
967
+ const row = author;
968
+ for (const key of ["login", "handle", "email", "displayName", "name", "id"]) {
969
+ const value = row[key];
970
+ if (typeof value === "string" && value.trim()) return value.trim();
971
+ }
972
+ return "";
973
+ }
974
+ function upperState(value, isDraft) {
975
+ const raw = typeof value === "string" ? value.trim().toUpperCase() : "";
976
+ if (raw === "DRAFT") return "OPEN";
977
+ if (raw === "OPEN" || raw === "CLOSED" || raw === "MERGED") return raw;
978
+ return isDraft ? "OPEN" : raw;
979
+ }
980
+ function normalizeOriginPr(raw) {
981
+ const isDraft = raw.isDraft === true || String(raw.status ?? raw.state ?? "").toLowerCase() === "draft";
982
+ const login = loginOf(raw.author);
983
+ const author = raw.author && typeof raw.author === "object" ? { ...raw.author, login: login || "unknown" } : login ? { login } : { login: "unknown" };
984
+ return {
985
+ ...raw,
986
+ isDraft,
987
+ state: upperState(raw.state ?? raw.status, isDraft),
988
+ author,
989
+ body: raw.body ?? raw.description ?? "",
990
+ isCrossRepository: false,
991
+ reviewDecision: cursorReviewDecision(raw.reviewDecision),
992
+ reviewRequests: originReviewRequests(raw.reviewRequests)
993
+ };
994
+ }
995
+ function originReviewRequests(raw) {
996
+ if (!Array.isArray(raw)) return raw;
997
+ return raw.map(originReviewer).filter((row) => row != null);
998
+ }
999
+ function originReviewer(raw) {
1000
+ if (typeof raw === "string" && raw.trim()) return { login: raw.trim() };
1001
+ if (!raw || typeof raw !== "object") return null;
1002
+ const row = raw;
1003
+ if (typeof row.login === "string" && row.login.trim()) return row;
1004
+ const kind = String(row.kind ?? row.__typename ?? "").toLowerCase();
1005
+ const slug = typeof row.slug === "string" ? row.slug.trim() : "";
1006
+ const name = typeof row.name === "string" ? row.name.trim() : "";
1007
+ const id = typeof row.id === "string" ? row.id.trim() : "";
1008
+ if (kind.includes("team") || kind.includes("group") || slug.includes("/") && !row.login) {
1009
+ return { __typename: "Team", slug: slug || name, name };
1010
+ }
1011
+ if (kind.includes("service") || kind.includes("bot")) {
1012
+ const login2 = name || id || "cursor-bot";
1013
+ return { __typename: "Bot", login: login2.toLowerCase().endsWith("[bot]") ? login2 : `${login2}[bot]` };
1014
+ }
1015
+ const login = name || id;
1016
+ return login ? { login } : null;
1017
+ }
1018
+ function originChecksToRuns(stdout) {
1019
+ let parsed;
1020
+ try {
1021
+ parsed = JSON.parse(stdout);
1022
+ } catch {
1023
+ return [];
1024
+ }
1025
+ const rows = Array.isArray(parsed) ? parsed : [];
1026
+ return rows.map((row) => {
1027
+ const item = row ?? {};
1028
+ const conclusion = typeof item.conclusion === "string" ? item.conclusion.toLowerCase() : "";
1029
+ const status = typeof item.status === "string" ? item.status.toLowerCase() : "";
1030
+ const bucket = conclusion === "success" || conclusion === "neutral" ? "pass" : conclusion === "failure" || conclusion === "timed_out" || conclusion === "action_required" ? "fail" : conclusion === "cancelled" ? "cancel" : conclusion === "skipped" ? "skipping" : "pending";
1031
+ const state = conclusion || status || "pending";
1032
+ const link = typeof item.link === "string" ? item.link : typeof item.detailsUrl === "string" ? item.detailsUrl : null;
1033
+ const description = typeof item.summary === "string" ? item.summary : typeof item.description === "string" ? item.description : null;
1034
+ return {
1035
+ name: String(item.name ?? "check"),
1036
+ state,
1037
+ bucket,
1038
+ startedAt: typeof item.startedAt === "string" ? item.startedAt : null,
1039
+ completedAt: typeof item.completedAt === "string" ? item.completedAt : null,
1040
+ link,
1041
+ description,
1042
+ workflow: typeof item.group === "string" ? item.group : null,
1043
+ kind: "ci"
1044
+ };
1045
+ });
1046
+ }
1047
+ function listedHasLabels(pr, labels) {
1048
+ if (labels.length === 0) return true;
1049
+ const have = new Set((pr.labels ?? []).map((name) => name.toLowerCase()));
1050
+ return labels.every((label) => have.has(label.toLowerCase()));
1051
+ }
1052
+ async function listCursorPrs(repoPath, slug, opts, head) {
1053
+ const resolved = resolveListPrsOptions(opts);
1054
+ const limit = Math.max(1, Math.min(250, resolved.limit ?? 200));
1055
+ const fetchLimit = prListFetchLimit(limit, resolved.reviewer);
1056
+ const useSearch = resolved.labels.length > 0 || Boolean(resolved.reviewer) || Boolean(resolved.query);
1057
+ const state = resolved.draft === true ? "draft" : resolved.state;
1058
+ const args = [
1059
+ "pr",
1060
+ "list",
1061
+ ...repoArgs(slug),
1062
+ "--state",
1063
+ state,
1064
+ "--limit",
1065
+ String(fetchLimit),
1066
+ "--json",
1067
+ useSearch ? SEARCH_LIST_FIELDS : PLAIN_LIST_FIELDS
1068
+ ];
1069
+ if (head?.trim()) args.push("--head", head.trim());
1070
+ if (useSearch) {
1071
+ for (const label of resolved.labels) args.push("--label", label);
1072
+ if (resolved.query) args.push("--search", resolved.query);
1073
+ else if (resolved.labels.length === 0) {
1074
+ const token = state === "all" ? "is:open OR is:closed OR is:merged" : `is:${state}`;
1075
+ args.push("--search", token);
1076
+ }
1077
+ }
1078
+ const { stdout, exitCode } = await originCli(args, repoPath, { reject: false, timeoutMs: 2e4 });
1079
+ if (exitCode !== 0 || !stdout.trim()) return [];
1080
+ let parsed;
1081
+ try {
1082
+ parsed = JSON.parse(stdout);
1083
+ } catch {
1084
+ return [];
1085
+ }
1086
+ if (!Array.isArray(parsed)) return [];
1087
+ const normalized = parsed.filter((row) => row && typeof row === "object").map((row) => normalizeOriginPr(row));
1088
+ const listed = applyPrListResponse(JSON.stringify(normalized), { ...opts, limit: void 0 });
1089
+ return listed.filter((pr) => listedHasLabels(pr, resolved.labels)).slice(0, limit);
1090
+ }
1091
+ async function viewCursorPr(repoPath, slug, selector, fields) {
1092
+ const { stdout, exitCode } = await originCli(
1093
+ ["pr", "view", selector, ...repoArgs(slug), "--json", fields],
1094
+ repoPath,
1095
+ { reject: false, timeoutMs: 2e4 }
1096
+ );
1097
+ if (exitCode !== 0 || !stdout.trim()) return null;
1098
+ try {
1099
+ const parsed = JSON.parse(stdout);
1100
+ return normalizeOriginPr(parsed);
1101
+ } catch {
1102
+ return null;
1103
+ }
1104
+ }
1105
+ var CURSOR_PR_VIEW_FIELDS = VIEW_FIELDS;
1106
+ async function createOrUpdateCursorPr(repoPath, slug, opts) {
1107
+ const branch = cursorHeadBranch(opts.head, slug);
1108
+ const existing = await originCli(
1109
+ ["pr", "view", branch, ...repoArgs(slug), "--json", "url"],
1110
+ repoPath,
1111
+ { reject: false, timeoutMs: 2e4 }
1112
+ );
1113
+ if (existing.exitCode === 0 && existing.stdout.trim()) {
1114
+ let url2 = "";
1115
+ try {
1116
+ url2 = String(JSON.parse(existing.stdout).url ?? "");
1117
+ } catch {
1118
+ url2 = "";
1119
+ }
1120
+ if (url2) {
1121
+ await originCli(
1122
+ ["pr", "edit", branch, ...repoArgs(slug), "--title", opts.title, "-F", opts.bodyFile],
1123
+ repoPath,
1124
+ { reject: false, timeoutMs: 2e4 }
1125
+ );
1126
+ return url2;
1127
+ }
1128
+ }
1129
+ const args = [
1130
+ "pr",
1131
+ "create",
1132
+ ...repoArgs(slug),
1133
+ "--title",
1134
+ opts.title,
1135
+ "-F",
1136
+ opts.bodyFile,
1137
+ "--base",
1138
+ opts.base,
1139
+ "--head",
1140
+ branch,
1141
+ "--push"
1142
+ ];
1143
+ if (opts.draft) args.push("--draft");
1144
+ const created = await originCli(args, repoPath, { reject: false, timeoutMs: 6e4 });
1145
+ if (created.exitCode !== 0) {
1146
+ throw new Error(created.stderr.trim() || created.stdout.trim() || "origin pr create failed");
1147
+ }
1148
+ const printed = created.stdout.trim().split("\n").find((line) => line.startsWith("http"));
1149
+ if (printed) return printed;
1150
+ const viewed = await viewCursorPr(repoPath, slug, branch, "url");
1151
+ const url = typeof viewed?.url === "string" ? viewed.url.trim() : "";
1152
+ return url || created.stdout.trim();
1153
+ }
1154
+ async function readyCursorPr(repoPath, slug, selector) {
1155
+ const ready = await originCli(["pr", "ready", selector, ...repoArgs(slug)], repoPath, {
1156
+ reject: false,
1157
+ timeoutMs: 2e4
1158
+ });
1159
+ if (ready.exitCode !== 0) {
1160
+ throw new Error(ready.stderr.trim() || ready.stdout.trim() || "Could not mark the Cursor pull request ready");
1161
+ }
1162
+ }
1163
+ async function mergeCursorPr(repoPath, slug, selector, method) {
1164
+ const args = ["pr", "merge", selector, ...repoArgs(slug)];
1165
+ if (method === "merge") args.push("--merge");
1166
+ else if (method === "squash") args.push("--squash");
1167
+ const merged = await originCli(args, repoPath, {
1168
+ reject: false,
1169
+ timeoutMs: 6e4
1170
+ });
1171
+ if (merged.exitCode !== 0) {
1172
+ throw new Error(merged.stderr.trim() || merged.stdout.trim() || "origin pr merge failed");
1173
+ }
1174
+ }
1175
+ async function fetchCursorPrHead(repoPath, slug, number, localBranch) {
1176
+ const view = await viewCursorPr(repoPath, slug, String(number), "headRefName");
1177
+ const head = typeof view?.headRefName === "string" ? view.headRefName.trim() : "";
1178
+ if (!head) {
1179
+ throw new Error(`Could not resolve the head branch for Cursor pull request #${number}`);
1180
+ }
1181
+ const result = await git(["fetch", "origin", `${head}:${localBranch}`], repoPath, { reject: false });
1182
+ if (result.exitCode !== 0) {
1183
+ throw new Error((result.stderr || result.stdout).trim() || `git fetch origin ${head} failed`);
1184
+ }
1185
+ }
1186
+ async function cursorPrChecks(repoPath, slug, selector) {
1187
+ const { stdout, exitCode, stderr } = await originCli(
1188
+ ["pr", "checks", selector, ...repoArgs(slug), "--json", "name,status,conclusion,detailsUrl,startedAt,completedAt,summary,group,link"],
1189
+ repoPath,
1190
+ { reject: false, timeoutMs: 2e4 }
1191
+ );
1192
+ const err = stderr.trim();
1193
+ let ci;
1194
+ if (!stdout.trim()) {
1195
+ if (/no pull request|not found|no change/i.test(err)) return null;
1196
+ if (exitCode !== 0 && err) throw new Error(err);
1197
+ ci = [];
1198
+ } else {
1199
+ ci = originChecksToRuns(stdout);
1200
+ }
1201
+ const view = await viewCursorPr(
1202
+ repoPath,
1203
+ slug,
1204
+ selector,
1205
+ "url,baseRefName,reviewDecision,mergeability"
1206
+ );
1207
+ if (!view) return ci;
1208
+ return cursorChecksWithGate(ci, cursorViewToPrMeta(view));
1209
+ }
1210
+ function cursorChecksWithGate(ci, meta) {
1211
+ const gate = {
1212
+ mergeable: meta.mergeable,
1213
+ mergeStateStatus: meta.mergeStateStatus,
1214
+ reviewDecision: meta.reviewDecision,
1215
+ baseRefName: meta.baseRefName,
1216
+ url: meta.url,
1217
+ isInMergeQueue: meta.isInMergeQueue
1218
+ };
1219
+ const review = (gate.reviewDecision ?? "").toUpperCase();
1220
+ const hasReviewRow = review === "CHANGES_REQUESTED" || review === "REVIEW_REQUIRED";
1221
+ const gateRows = buildMergeGateChecks(gate, {
1222
+ suppressGenericBlocked: ci.some((check) => check.bucket === "fail") || hasReviewRow
1223
+ });
1224
+ return [...gateRows, ...ci];
1225
+ }
1226
+
1227
+ // src/util/enqueue-by-key.ts
1228
+ var tails = /* @__PURE__ */ new Map();
1229
+ function enqueueByKey(key, fn) {
1230
+ const prev = tails.get(key) ?? Promise.resolve();
1231
+ const next = prev.then(fn, fn);
1232
+ tails.set(
1233
+ key,
1234
+ next.then(
1235
+ () => void 0,
1236
+ () => void 0
1237
+ )
1238
+ );
1239
+ return next;
1240
+ }
1241
+
1242
+ // src/git/repo-git-lock.ts
1243
+ function repoLockKey(repoPath) {
1244
+ const trimmed = repoPath.replace(/\/+$/, "");
1245
+ return `git-worktree:${trimmed || "/"}`;
1246
+ }
1247
+ function withRepoGitLock(repoPath, fn) {
1248
+ return enqueueByKey(repoLockKey(repoPath), fn);
1249
+ }
1250
+
876
1251
  // src/git/stack.ts
877
1252
  var cachedStatus = null;
878
1253
  var STATUS_TTL_MS = 6e4;
@@ -1138,31 +1513,6 @@ function allocateTeamSlug(repoPath) {
1138
1513
  }
1139
1514
 
1140
1515
  // src/git/worktree.ts
1141
- async function lookupGithubGraphqlReset(cwd) {
1142
- const result = await gh(["api", "rate_limit"], cwd, { reject: false });
1143
- if (result.exitCode !== 0 || !result.stdout.trim()) return void 0;
1144
- try {
1145
- const data = JSON.parse(result.stdout);
1146
- const reset = data.resources?.graphql?.reset;
1147
- return typeof reset === "number" ? reset : void 0;
1148
- } catch {
1149
- return void 0;
1150
- }
1151
- }
1152
- async function formatPrCreateFailure(raw, cwd, ctx) {
1153
- if (!isGhRateLimitError(raw)) {
1154
- return formatGhLandError(raw, {
1155
- targetedRepo: ctx?.slug,
1156
- headRef: ctx?.head
1157
- });
1158
- }
1159
- const resetAt = await lookupGithubGraphqlReset(cwd);
1160
- return formatGhLandError(raw, {
1161
- resetAt,
1162
- targetedRepo: ctx?.slug,
1163
- headRef: ctx?.head
1164
- });
1165
- }
1166
1516
  function slugify(input) {
1167
1517
  return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
1168
1518
  }
@@ -1185,25 +1535,39 @@ function canonicalizeRepoPath(path) {
1185
1535
  }
1186
1536
  }
1187
1537
  function parseGithubSlugFromRemoteUrl(url) {
1188
- const trimmed = url.trim().replace(/\.git$/i, "");
1189
- if (!trimmed) return null;
1190
- const github = trimmed.match(/github\.com[:/]([^/]+)\/([^/]+)$/i);
1191
- if (github?.[1] && github[2]) return `${github[1]}/${github[2]}`;
1192
- const sshAlias = trimmed.match(/^git@[^:]+:([^/]+)\/([^/]+)$/i);
1193
- if (sshAlias?.[1] && sshAlias[2]) return `${sshAlias[1]}/${sshAlias[2]}`;
1194
- return null;
1538
+ const parsed = parseRemoteRepo(url);
1539
+ return parsed?.forge === "github" ? parsed.slug : null;
1195
1540
  }
1196
- async function slugFromGitRemote(repoPath, remote) {
1541
+ async function remoteUrl(repoPath, remote) {
1197
1542
  const result = await git(["remote", "get-url", remote], repoPath, {
1198
1543
  reject: false,
1199
1544
  timeoutMs: 5e3
1200
1545
  });
1201
1546
  if (result.exitCode !== 0 || !result.stdout.trim()) return null;
1202
- return parseGithubSlugFromRemoteUrl(result.stdout);
1547
+ return result.stdout.trim();
1548
+ }
1549
+ async function slugFromGitRemote(repoPath, remote) {
1550
+ const url = await remoteUrl(repoPath, remote);
1551
+ if (!url) return null;
1552
+ return parseGithubSlugFromRemoteUrl(url);
1553
+ }
1554
+ async function resolveOriginRemote(repoPath) {
1555
+ const url = await remoteUrl(repoPath, "origin");
1556
+ if (!url) return null;
1557
+ return parseRemoteRepo(url);
1558
+ }
1559
+ async function resolveProjectRemoteTag(repoPath) {
1560
+ const origin = await resolveOriginRemote(repoPath);
1561
+ if (origin) return formatRemoteTag(origin);
1562
+ const slug = await resolveGithubRepoSlug(repoPath);
1563
+ return slug ? `github:${slug}` : null;
1203
1564
  }
1204
1565
  async function resolveGithubRepoSlug(repoPath) {
1205
- const fromOrigin = await slugFromGitRemote(repoPath, "origin");
1206
- if (fromOrigin) return fromOrigin;
1566
+ const originUrl = await remoteUrl(repoPath, "origin");
1567
+ if (originUrl) {
1568
+ const parsed = parseRemoteRepo(originUrl);
1569
+ return parsed?.forge === "github" ? parsed.slug : null;
1570
+ }
1207
1571
  const hasAlt = Boolean(await slugFromGitRemote(repoPath, "upstream")) || Boolean(await slugFromGitRemote(repoPath, "github"));
1208
1572
  if (!hasAlt) {
1209
1573
  const viaGh = await gh(
@@ -1249,6 +1613,8 @@ ${view.stderr}`;
1249
1613
  });
1250
1614
  }
1251
1615
  async function originGhRepoEnv(cwd, opts) {
1616
+ const origin = await resolveOriginRemote(cwd);
1617
+ if (origin?.forge === "cursor") return { ORIGIN_REPO: origin.slug };
1252
1618
  await ensureGhPreferOrigin(cwd);
1253
1619
  const slug = await resolveGithubRepoSlug(cwd);
1254
1620
  const mode = opts?.mode ?? getGithubGitAuthMode();
@@ -1363,7 +1729,16 @@ async function listBranches(repoPath, opts) {
1363
1729
  }
1364
1730
  return sorted.filter((b) => keep.has(b.name));
1365
1731
  }
1732
+ function entireUsesTrails() {
1733
+ return new Error(
1734
+ "This checkout is on Entire. Trails replace pull requests \u2014 use `entire trail` (Settings \u2192 Git \u2192 Entire), not `gh`."
1735
+ );
1736
+ }
1366
1737
  async function listPrs(repoPath, opts) {
1738
+ const origin = await resolveOriginRemote(repoPath);
1739
+ if (origin?.forge === "entire") return [];
1740
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
1741
+ if (cursorSlug) return listCursorPrs(repoPath, cursorSlug, opts);
1367
1742
  const slug = await resolveGithubRepoSlug(repoPath);
1368
1743
  const resolved = resolveListPrsOptions(opts);
1369
1744
  const limit = resolved.limit ?? 200;
@@ -1401,6 +1776,18 @@ function prInfoFromGhView(view) {
1401
1776
  };
1402
1777
  }
1403
1778
  async function getPr(repoPath, number) {
1779
+ const origin = await resolveOriginRemote(repoPath);
1780
+ if (origin?.forge === "entire") return null;
1781
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
1782
+ if (cursorSlug) {
1783
+ const view = await viewCursorPr(
1784
+ repoPath,
1785
+ cursorSlug,
1786
+ String(number),
1787
+ "number,title,headRefName,url,author,isDraft,state"
1788
+ );
1789
+ return view ? prInfoFromGhView(view) : null;
1790
+ }
1404
1791
  const slug = await resolveGithubRepoSlug(repoPath);
1405
1792
  const args = [
1406
1793
  "pr",
@@ -1462,6 +1849,13 @@ function connectedPrSelectors(thread, currentHeadPrUrl) {
1462
1849
  async function getPrForHeadBranch(repoPath, branch) {
1463
1850
  const head = normalizeGitBranchName(branch);
1464
1851
  if (!head || isDefaultishSourceRef(head)) return null;
1852
+ const origin = await resolveOriginRemote(repoPath);
1853
+ if (origin?.forge === "entire") return null;
1854
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
1855
+ if (cursorSlug) {
1856
+ const rows = await listCursorPrs(repoPath, cursorSlug, { state: "open", limit: 5 }, head);
1857
+ return rows[0] ?? null;
1858
+ }
1465
1859
  const slug = await resolveGithubRepoSlug(repoPath);
1466
1860
  const listHead = slug ? ghHeadRef(slug, head) : head;
1467
1861
  const listArgs = [
@@ -1626,6 +2020,10 @@ async function probeLocalMergeGate(cwd, gate) {
1626
2020
  return { gate, files: [] };
1627
2021
  }
1628
2022
  async function getPrChecks(cwd, selector) {
2023
+ const origin = await resolveOriginRemote(cwd);
2024
+ if (origin?.forge === "entire") return null;
2025
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2026
+ if (cursorSlug) return cursorPrChecks(cwd, cursorSlug, selector);
1629
2027
  const slug = await resolveGithubRepoSlug(cwd);
1630
2028
  const args = [
1631
2029
  "pr",
@@ -1681,6 +2079,18 @@ async function getPrChecks(cwd, selector) {
1681
2079
  return [...gateRows, ...ciChecks];
1682
2080
  }
1683
2081
  async function getPrMeta(cwd, selector) {
2082
+ const origin = await resolveOriginRemote(cwd);
2083
+ if (origin?.forge === "entire") return null;
2084
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2085
+ if (cursorSlug) {
2086
+ const view = await viewCursorPr(
2087
+ cwd,
2088
+ cursorSlug,
2089
+ selector,
2090
+ "number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,author,labels,mergeability"
2091
+ );
2092
+ return view ? cursorViewToPrMeta(view) : null;
2093
+ }
1684
2094
  const slug = await resolveGithubRepoSlug(cwd);
1685
2095
  const viewArgs = [
1686
2096
  "pr",
@@ -1831,6 +2241,13 @@ function parsePrDetailsView(view) {
1831
2241
  };
1832
2242
  }
1833
2243
  async function getPrDetails(cwd, selector) {
2244
+ const origin = await resolveOriginRemote(cwd);
2245
+ if (origin?.forge === "entire") return null;
2246
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2247
+ if (cursorSlug) {
2248
+ const view2 = await viewCursorPr(cwd, cursorSlug, selector, CURSOR_PR_VIEW_FIELDS);
2249
+ return view2 ? parsePrDetailsView(view2) : null;
2250
+ }
1834
2251
  const slug = await resolveGithubRepoSlug(cwd);
1835
2252
  const viewArgs = ["pr", "view", selector, "--json", PR_DETAILS_JSON_FIELDS];
1836
2253
  if (slug) viewArgs.push("--repo", slug);
@@ -1850,6 +2267,13 @@ async function getPrDetails(cwd, selector) {
1850
2267
  return parsePrDetailsView(view);
1851
2268
  }
1852
2269
  async function fetchPrHead(repoPath, number, localBranch) {
2270
+ const origin = await resolveOriginRemote(repoPath);
2271
+ if (origin?.forge === "entire") throw entireUsesTrails();
2272
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2273
+ if (cursorSlug) {
2274
+ await fetchCursorPrHead(repoPath, cursorSlug, number, localBranch);
2275
+ return;
2276
+ }
1853
2277
  const slug = await resolveGithubRepoSlug(repoPath);
1854
2278
  const refspec = `+pull/${number}/head:${localBranch}`;
1855
2279
  const errors = [];
@@ -1992,10 +2416,10 @@ async function originRemoteUrl(cwd) {
1992
2416
  const result = await git(["remote", "get-url", "origin"], cwd, { reject: false });
1993
2417
  return result.exitCode === 0 ? result.stdout.trim() : "";
1994
2418
  }
1995
- function githubHttpsRetryEnv(remoteUrl) {
2419
+ function githubHttpsRetryEnv(remoteUrl2) {
1996
2420
  return {
1997
2421
  GIT_TERMINAL_PROMPT: "0",
1998
- ...githubAgentGitEnv(void 0, { remoteUrl })
2422
+ ...githubAgentGitEnv(void 0, { remoteUrl: remoteUrl2 })
1999
2423
  };
2000
2424
  }
2001
2425
  async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
@@ -2009,11 +2433,11 @@ async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
2009
2433
  }
2010
2434
  const token = await resolveGithubAgentToken(mode, repoPath);
2011
2435
  if (!token) return false;
2012
- const remoteUrl = await originRemoteUrl(repoPath);
2436
+ const remoteUrl2 = await originRemoteUrl(repoPath);
2013
2437
  const tryHttps = async (header) => git(args, repoPath, {
2014
2438
  reject: false,
2015
2439
  timeoutMs,
2016
- env: githubHttpsRetryEnv(remoteUrl),
2440
+ env: githubHttpsRetryEnv(remoteUrl2),
2017
2441
  config: {
2018
2442
  "http.extraHeader": header
2019
2443
  }
@@ -2362,10 +2786,10 @@ async function pushBranch(worktreePath, branchName) {
2362
2786
  sshErr || `git push origin ${branchName} failed` + (!token ? " (SSH remote and gh auth token unavailable \u2014 unlock ssh-agent or run: gh auth login)" : "")
2363
2787
  );
2364
2788
  }
2365
- const remoteUrl = await originRemoteUrl(worktreePath);
2789
+ const remoteUrl2 = await originRemoteUrl(worktreePath);
2366
2790
  const tryHttps = async (header) => git(["push", "-u", "origin", branchName], worktreePath, {
2367
2791
  reject: false,
2368
- env: githubHttpsRetryEnv(remoteUrl),
2792
+ env: githubHttpsRetryEnv(remoteUrl2),
2369
2793
  config: {
2370
2794
  "http.extraHeader": header
2371
2795
  }
@@ -2378,6 +2802,40 @@ async function pushBranch(worktreePath, branchName) {
2378
2802
  const httpsErr = (basicPush.stderr || bearer.stderr || bearer.stdout).trim();
2379
2803
  throw new Error(httpsErr || sshErr || `git push origin ${branchName} failed`);
2380
2804
  }
2805
+ async function markCursorPrReady(cwd, slug, selector) {
2806
+ const view = await viewCursorPr(cwd, slug, selector, "url,state,isDraft");
2807
+ if (!view) throw new Error("Could not load the Cursor pull request");
2808
+ const url = String(view.url ?? "");
2809
+ const state = String(view.state ?? "").toUpperCase();
2810
+ const isDraft = Boolean(view.isDraft);
2811
+ if (state === "MERGED" || state === "CLOSED") {
2812
+ return { url, state, isDraft: false };
2813
+ }
2814
+ if (isDraft) {
2815
+ if (await isDirty(cwd)) {
2816
+ throw new Error(
2817
+ "Commit and push local work before marking the pull request ready for review."
2818
+ );
2819
+ }
2820
+ const unpushed = await countUnpushedVsOrigin(cwd);
2821
+ if (unpushed > 0) {
2822
+ throw new Error(
2823
+ "Push this branch to origin before marking the pull request ready for review."
2824
+ );
2825
+ }
2826
+ await readyCursorPr(cwd, slug, selector);
2827
+ }
2828
+ return { url, state: state || "OPEN", isDraft: false };
2829
+ }
2830
+ async function mergeCursorPullRequest(cwd, slug, selector, method) {
2831
+ const view = await viewCursorPr(cwd, slug, selector, "url,state,isDraft");
2832
+ if (!view) throw new Error("Could not load the Cursor pull request");
2833
+ const url = String(view.url ?? "");
2834
+ if (String(view.state ?? "").toUpperCase() === "MERGED") return { url, state: "MERGED" };
2835
+ if (view.isDraft) await readyCursorPr(cwd, slug, selector);
2836
+ await mergeCursorPr(cwd, slug, selector, method);
2837
+ return { url, state: "MERGED" };
2838
+ }
2381
2839
  async function runPrReady(cwd, selector, slug) {
2382
2840
  const readyArgs = ["pr", "ready", selector];
2383
2841
  if (slug) readyArgs.push("--repo", slug);
@@ -2389,6 +2847,10 @@ async function runPrReady(cwd, selector, slug) {
2389
2847
  }
2390
2848
  }
2391
2849
  async function markPrReady(cwd, selector) {
2850
+ const origin = await resolveOriginRemote(cwd);
2851
+ if (origin?.forge === "entire") throw entireUsesTrails();
2852
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2853
+ if (cursorSlug) return markCursorPrReady(cwd, cursorSlug, selector);
2392
2854
  const slug = await resolveGithubRepoSlug(cwd);
2393
2855
  const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft"];
2394
2856
  if (slug) viewArgs.push("--repo", slug);
@@ -2427,6 +2889,10 @@ async function markPrReady(cwd, selector) {
2427
2889
  return { url, state: state || "OPEN", isDraft: false };
2428
2890
  }
2429
2891
  async function mergePr(cwd, selector, opts) {
2892
+ const origin = await resolveOriginRemote(cwd);
2893
+ if (origin?.forge === "entire") throw entireUsesTrails();
2894
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
2895
+ if (cursorSlug) return mergeCursorPullRequest(cwd, cursorSlug, selector, opts?.method ?? "squash");
2430
2896
  const slug = await resolveGithubRepoSlug(cwd);
2431
2897
  const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft,number"];
2432
2898
  if (slug) viewArgs.push("--repo", slug);
@@ -2485,116 +2951,6 @@ async function mergePr(cwd, selector, opts) {
2485
2951
  }
2486
2952
  return { url, state: "MERGED" };
2487
2953
  }
2488
- function ghPrCreateAssigneeArgs(assignee) {
2489
- const raw = assignee?.trim();
2490
- if (raw && /^(none|unassigned)$/i.test(raw)) return [];
2491
- return ["--assignee", raw || "@me"];
2492
- }
2493
- async function createOrUpdatePr(worktreePath, opts) {
2494
- await ensureGhPreferOrigin(worktreePath);
2495
- const slug = await resolveGithubRepoSlug(worktreePath);
2496
- if (!slug) {
2497
- throw new Error(
2498
- "Could not resolve the origin GitHub repo (owner/name) for this worktree. Check that `git remote get-url origin` points at github.com."
2499
- );
2500
- }
2501
- const repoArgs = ghRepoSelectArgs(slug);
2502
- const headRef = ghHeadRef(slug, opts.head);
2503
- const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
2504
- const bodyText = clampGithubPrBody(opts.body ?? opts.title);
2505
- const bodyFile = join2(
2506
- tmpdir(),
2507
- `sideboard-pr-body-${process.pid}-${randomBytes(6).toString("hex")}.md`
2508
- );
2509
- writeFileSync(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
2510
- `, "utf8");
2511
- const bodyArgs = ["--body-file", bodyFile];
2512
- try {
2513
- return await createOrUpdatePrWithBodyFile(worktreePath, {
2514
- ...opts,
2515
- slug,
2516
- repoArgs,
2517
- headRef,
2518
- branchOnly,
2519
- bodyArgs
2520
- });
2521
- } finally {
2522
- try {
2523
- unlinkSync(bodyFile);
2524
- } catch {
2525
- }
2526
- }
2527
- }
2528
- async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
2529
- const { slug, repoArgs, headRef, branchOnly, bodyArgs } = opts;
2530
- const existing = await gh(
2531
- [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
2532
- worktreePath,
2533
- { reject: false }
2534
- );
2535
- if (existing.exitCode === 0 && existing.stdout.trim() && !opts.web) {
2536
- const url2 = existing.stdout.trim();
2537
- await gh(
2538
- [
2539
- ...repoArgs,
2540
- "pr",
2541
- "edit",
2542
- branchOnly,
2543
- "--title",
2544
- opts.title,
2545
- ...bodyArgs
2546
- ],
2547
- worktreePath,
2548
- { reject: false }
2549
- );
2550
- return url2;
2551
- }
2552
- if (opts.web) {
2553
- const args2 = [
2554
- ...repoArgs,
2555
- "pr",
2556
- "create",
2557
- "--web",
2558
- "--title",
2559
- opts.title,
2560
- ...bodyArgs,
2561
- "--base",
2562
- opts.base,
2563
- "--head",
2564
- headRef
2565
- ];
2566
- if (opts.draft) args2.push("--draft");
2567
- args2.push(...ghPrCreateAssigneeArgs(opts.assignee));
2568
- await gh(args2, worktreePath, { reject: false });
2569
- const again = await gh(
2570
- [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
2571
- worktreePath,
2572
- { reject: false }
2573
- );
2574
- return again.stdout.trim() || "";
2575
- }
2576
- const args = [
2577
- ...repoArgs,
2578
- "pr",
2579
- "create",
2580
- "--title",
2581
- opts.title,
2582
- ...bodyArgs,
2583
- "--base",
2584
- opts.base,
2585
- "--head",
2586
- headRef
2587
- ];
2588
- if (opts.draft) args.push("--draft");
2589
- args.push(...ghPrCreateAssigneeArgs(opts.assignee));
2590
- const created = await gh(args, worktreePath, { reject: false });
2591
- if (created.exitCode !== 0) {
2592
- const raw = created.stderr.trim() || created.stdout.trim() || "gh pr create failed";
2593
- throw new Error(await formatPrCreateFailure(raw, worktreePath, { slug, head: headRef }));
2594
- }
2595
- const url = created.stdout.trim().split("\n").find((l) => l.startsWith("http")) ?? created.stdout.trim();
2596
- return url;
2597
- }
2598
2954
  function suggestSlug(source) {
2599
2955
  const base = slugify(source || "thread");
2600
2956
  const stamp = Date.now().toString(36).slice(-4);
@@ -2631,7 +2987,9 @@ export {
2631
2987
  codexSandboxWritableRootsArgs,
2632
2988
  codexUnattendedGitConfigArgs,
2633
2989
  formatGitAuthModeDirective,
2990
+ formatRemoteTag,
2634
2991
  resolveListPrsOptions,
2992
+ createOrUpdateCursorPr,
2635
2993
  enqueueByKey,
2636
2994
  withRepoGitLock,
2637
2995
  detectGhStack,
@@ -2651,6 +3009,8 @@ export {
2651
3009
  resolveRepoRoot,
2652
3010
  canonicalizeRepoPath,
2653
3011
  parseGithubSlugFromRemoteUrl,
3012
+ resolveOriginRemote,
3013
+ resolveProjectRemoteTag,
2654
3014
  resolveGithubRepoSlug,
2655
3015
  ghRepoSelectArgs,
2656
3016
  ghHeadRef,
@@ -2659,6 +3019,7 @@ export {
2659
3019
  resolveDefaultBranch,
2660
3020
  resolveDiffBaseRef,
2661
3021
  listBranches,
3022
+ entireUsesTrails,
2662
3023
  listPrs,
2663
3024
  getPr,
2664
3025
  isDefaultishSourceRef,
@@ -2690,7 +3051,5 @@ export {
2690
3051
  pushBranch,
2691
3052
  markPrReady,
2692
3053
  mergePr,
2693
- ghPrCreateAssigneeArgs,
2694
- createOrUpdatePr,
2695
3054
  suggestSlug
2696
3055
  };