@sideboard-ai/core 0.1.319 → 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.
- package/dist/{abletime-L3ODNTBT.js → abletime-CRRLEYTE.js} +4 -4
- package/dist/{abletime-DVYO4IVS.js → abletime-ICW6Q2AB.js} +4 -4
- package/dist/{abletime-oauth-2VWQCXGT.js → abletime-oauth-P3KPPJE4.js} +3 -3
- package/dist/{abletime-oauth-6DTX7LB7.js → abletime-oauth-Q76XBSPG.js} +3 -3
- package/dist/{abletime-rest-PYQM234B.js → abletime-rest-DNFAYYLP.js} +3 -3
- package/dist/{abletime-rest-7NICZYOC.js → abletime-rest-DQ3PNKXP.js} +3 -3
- package/dist/agents/cursor-runner.js +2 -2
- package/dist/{agents-KABJUDPI.js → agents-PFVYC36J.js} +10 -10
- package/dist/{agents-2AR7I6B6.js → agents-ROFDNE7Z.js} +11 -11
- package/dist/{app-settings-CZVFMOI5.js → app-settings-KMPVI2B7.js} +1 -1
- package/dist/{app-settings-V6YPYY57.js → app-settings-P3H4SEIK.js} +1 -1
- package/dist/{chunk-QVN2WOKJ.js → chunk-2TUE6JKQ.js} +12 -0
- package/dist/{chunk-OO3R6CS2.js → chunk-45UB2J47.js} +537 -90
- package/dist/{chunk-DD67JWTN.js → chunk-4YXZOXMT.js} +1 -1
- package/dist/{chunk-5OM7UCTQ.js → chunk-56M6VEXI.js} +3 -3
- package/dist/{chunk-ZM43NTJJ.js → chunk-5OQONGHO.js} +5 -5
- package/dist/{chunk-V5BTGQ6M.js → chunk-6U6QRUHY.js} +1 -1
- package/dist/{chunk-QPLQXWND.js → chunk-72PUPPV2.js} +3 -3
- package/dist/{chunk-53OCOGND.js → chunk-7JV5QP5K.js} +3 -3
- package/dist/{chunk-HOLB6ZFZ.js → chunk-B4CDSSRN.js} +2 -0
- package/dist/{chunk-ST2UQBJT.js → chunk-BANN5HUX.js} +8 -8
- package/dist/{chunk-AESO2KJS.js → chunk-BPDVHB4U.js} +3 -3
- package/dist/{chunk-WLZHRNE6.js → chunk-BTIXTH4N.js} +3 -3
- package/dist/{chunk-AUJ4XB6H.js → chunk-DFUXW2IT.js} +2 -2
- package/dist/{chunk-I5TESHDN.js → chunk-EQ52JR3Q.js} +5 -5
- package/dist/{chunk-E52SQENB.js → chunk-FZKAAAEB.js} +3 -1
- package/dist/{chunk-YALFZKSC.js → chunk-H6GXYV5Q.js} +23 -20
- package/dist/{chunk-JD7CC6SW.js → chunk-HHPTGIKS.js} +2 -2
- package/dist/{chunk-GCHSJCCX.js → chunk-IFU4W4C4.js} +3 -1
- package/dist/{chunk-YEIHCSZS.js → chunk-IFWGG2M2.js} +9 -0
- package/dist/{chunk-IAPO7ZFG.js → chunk-JNEFEA2A.js} +8 -8
- package/dist/{chunk-U2N32EA6.js → chunk-LD2DXHBP.js} +543 -92
- package/dist/{chunk-7Q327QF2.js → chunk-QIC2JXHW.js} +5 -5
- package/dist/{chunk-2P4XKWV2.js → chunk-QKHEVSB5.js} +1 -1
- package/dist/{chunk-NRSC4N5X.js → chunk-RGUZPMTF.js} +8 -8
- package/dist/{chunk-AZ4GEEGL.js → chunk-RHJKZOPQ.js} +3 -3
- package/dist/{chunk-HH3VVTEC.js → chunk-S5CJUIPY.js} +5 -5
- package/dist/{chunk-A6C67DY5.js → chunk-SPMIZSKH.js} +16 -13
- package/dist/{chunk-FDXHNROD.js → chunk-TEFFR6PB.js} +2 -2
- package/dist/{chunk-TUJFCJJU.js → chunk-UWJUARFA.js} +546 -187
- package/dist/{chunk-MBYGWNLQ.js → chunk-UX6EDDGR.js} +1 -1
- package/dist/{chunk-LXBZQP2Q.js → chunk-V76MQ7MH.js} +5 -5
- package/dist/{chunk-GH7UMULC.js → chunk-XDQSHSL6.js} +8 -8
- package/dist/{chunk-2UG25RMK.js → chunk-YBTSZ2SW.js} +12 -0
- package/dist/{chunk-A46CIARU.js → chunk-YWBLQBSN.js} +549 -188
- package/dist/{chunk-Y3Y7SZBZ.js → chunk-ZHGMRBSC.js} +1 -1
- package/dist/{conductor-MG2RAQNT.js → conductor-CNT3VL3S.js} +8 -8
- package/dist/{conductor-NIUBJLTS.js → conductor-XLXJU24U.js} +8 -8
- package/dist/{connected-teams-IHK2WXUI.js → connected-teams-H4LZOJDC.js} +2 -2
- package/dist/{connected-teams-IFW3NQ6G.js → connected-teams-XYRZMQO4.js} +2 -2
- package/dist/{coordinator-prompt-X74F3JIC.js → coordinator-prompt-D5EE2YTV.js} +6 -6
- package/dist/{coordinator-prompt-3CT3D5UM.js → coordinator-prompt-TU24TQ4I.js} +6 -6
- package/dist/{global-workspace-BUWMRNW5.js → global-workspace-743TPQNF.js} +6 -6
- package/dist/{global-workspace-FTNJEML3.js → global-workspace-UPMXDQM7.js} +6 -6
- package/dist/index.cjs +2160 -867
- package/dist/index.d.cts +211 -74
- package/dist/index.d.ts +211 -74
- package/dist/index.js +496 -78
- package/dist/mcp/run-stdio.cjs +1377 -478
- package/dist/mcp/run-stdio.js +80 -32
- package/dist/{optional-cli-6GGESWGE.js → optional-cli-2YDQQCCD.js} +5 -5
- package/dist/{optional-cli-EMSZBYDE.js → optional-cli-USD6PROE.js} +4 -4
- package/dist/{orchestrator-NCA4LSCC.js → orchestrator-B4STIW4B.js} +14 -14
- package/dist/{orchestrator-H3DSZ5RM.js → orchestrator-VQ4HRYVB.js} +13 -13
- package/dist/{run-PCX5QWR4.js → run-7QMRYP3F.js} +3 -1
- package/dist/{run-TB7RBII2.js → run-UPNCCRCN.js} +3 -1
- package/dist/{thread-store-PLNIWFZ4.js → thread-store-3F7GR64A.js} +1 -1
- package/dist/{thread-store-7BBA63KV.js → thread-store-JRSL4HQY.js} +1 -1
- package/dist/{workspaces-MGKEAYNM.js → workspaces-47WQL5IG.js} +6 -6
- package/dist/{workspaces-2V5UYPYC.js → workspaces-75FD4QUR.js} +6 -6
- package/dist/{worktree-XJKLDYOL.js → worktree-G3E6WP66.js} +11 -9
- package/dist/{worktree-THAXW5U7.js → worktree-PDNPL5CQ.js} +11 -9
- package/dist/{worktree-exclude-RXZPTCNH.js → worktree-exclude-INRDQFWV.js} +2 -2
- package/dist/{worktree-exclude-5N2KTYIT.js → worktree-exclude-P5NJODWP.js} +2 -2
- package/package.json +1 -1
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
normalizeTakenSlug,
|
|
5
5
|
normalizeWorktreePath,
|
|
6
6
|
takenSlugsFromThread
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-IFWGG2M2.js";
|
|
8
8
|
import {
|
|
9
9
|
ensureWorktreeSideboardIgnored
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-QKHEVSB5.js";
|
|
11
11
|
import {
|
|
12
12
|
isWorkspaceScratchPath
|
|
13
13
|
} from "./chunk-6K64FJDD.js";
|
|
@@ -19,20 +19,19 @@ import {
|
|
|
19
19
|
githubCredentialHelperGitConfig,
|
|
20
20
|
githubGhConfigEnv,
|
|
21
21
|
materializeGithubAgentAuth,
|
|
22
|
+
originCli,
|
|
22
23
|
resolveGhAuthToken
|
|
23
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-2TUE6JKQ.js";
|
|
24
25
|
import {
|
|
25
26
|
getGithubGitAuthMode,
|
|
26
27
|
getGithubPat
|
|
27
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-FZKAAAEB.js";
|
|
28
29
|
import {
|
|
29
30
|
worktreesRoot
|
|
30
31
|
} from "./chunk-H4UEFJRI.js";
|
|
31
32
|
|
|
32
33
|
// src/git/worktree.ts
|
|
33
|
-
import {
|
|
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(
|
|
147
|
+
function githubHttpsInsteadOfEntries(remoteUrl2) {
|
|
149
148
|
const entries = [...HTTPS_REWRITE];
|
|
150
|
-
const url =
|
|
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
|
|
1189
|
-
|
|
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
|
|
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
|
|
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
|
|
1206
|
-
if (
|
|
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(
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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
|
};
|