@sideboard-ai/core 0.1.10 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-OAX7XPKX.js → agents-2XMZO3CY.js} +1 -1
- package/dist/{chunk-2M4OHXYX.js → chunk-5263JXQY.js} +2 -2
- package/dist/{chunk-WMCPLDW3.js → chunk-JQJZTL2Q.js} +1 -1
- package/dist/{chunk-TLJH3L2C.js → chunk-LIUV5ONW.js} +44 -4
- package/dist/{chunk-LL7DTZ5B.js → chunk-LXHSRNJJ.js} +225 -48
- package/dist/{chunk-2R5VV4BA.js → chunk-SNHWAARD.js} +6 -5
- package/dist/{chunk-E4PWXO2C.js → chunk-WWBC56EL.js} +78 -29
- package/dist/{coordinator-prompt-6R2TX4WQ.js → coordinator-prompt-QPTX6YCW.js} +2 -2
- package/dist/{global-workspace-R44HGBU6.js → global-workspace-IV6LIDTO.js} +3 -3
- package/dist/index.cjs +360 -72
- package/dist/index.d.cts +93 -5
- package/dist/index.d.ts +93 -5
- package/dist/index.js +26 -6
- package/dist/mcp/run-stdio.cjs +2761 -2501
- package/dist/mcp/run-stdio.js +6 -6
- package/dist/{workspaces-TCJFYI35.js → workspaces-JBWIRU55.js} +4 -4
- package/dist/{worktree-NGFDN3J4.js → worktree-TYI2SANE.js} +11 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1357,6 +1357,69 @@ var init_worktree_labels = __esm({
|
|
|
1357
1357
|
}
|
|
1358
1358
|
});
|
|
1359
1359
|
|
|
1360
|
+
// src/git/gh-errors.ts
|
|
1361
|
+
function isGhRateLimitError(text) {
|
|
1362
|
+
return /API rate limit (already )?exceeded/i.test(text) || /rate limit exceeded/i.test(text);
|
|
1363
|
+
}
|
|
1364
|
+
function formatRateLimitResetHint(resetEpochSec, nowMs = Date.now()) {
|
|
1365
|
+
const ms = resetEpochSec * 1e3 - nowMs;
|
|
1366
|
+
if (ms <= 0) return "soon";
|
|
1367
|
+
const mins = Math.max(1, Math.ceil(ms / 6e4));
|
|
1368
|
+
if (mins < 60) {
|
|
1369
|
+
return `in about ${mins} minute${mins === 1 ? "" : "s"}`;
|
|
1370
|
+
}
|
|
1371
|
+
const hours = Math.ceil(mins / 60);
|
|
1372
|
+
return `in about ${hours} hour${hours === 1 ? "" : "s"}`;
|
|
1373
|
+
}
|
|
1374
|
+
function extractGhErrorDetail(text) {
|
|
1375
|
+
const trimmed = text.trim();
|
|
1376
|
+
if (!trimmed) return "";
|
|
1377
|
+
const graphql = trimmed.match(/\bGraphQL:\s*(.+)$/im);
|
|
1378
|
+
if (graphql?.[1]) return `GraphQL: ${graphql[1].trim()}`;
|
|
1379
|
+
const http = trimmed.match(/\bHTTP\s+\d{3}:\s*(.+)$/im);
|
|
1380
|
+
if (http?.[1]) return `HTTP: ${http[1].trim()}`;
|
|
1381
|
+
const lines = trimmed.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
1382
|
+
if (lines.length > 1 && /^Command failed with exit code/i.test(lines[0])) {
|
|
1383
|
+
return lines.slice(1).join(" ").trim() || lines[0];
|
|
1384
|
+
}
|
|
1385
|
+
return trimmed;
|
|
1386
|
+
}
|
|
1387
|
+
function formatGhLandError(raw, opts) {
|
|
1388
|
+
const trimmed = raw.trim();
|
|
1389
|
+
if (trimmed.startsWith("GitHub API rate limit exceeded.")) {
|
|
1390
|
+
return trimmed;
|
|
1391
|
+
}
|
|
1392
|
+
const detail = extractGhErrorDetail(raw);
|
|
1393
|
+
if (/Head ref must be a branch|No commits between|Head sha can't be blank/i.test(
|
|
1394
|
+
raw
|
|
1395
|
+
) || /Head ref must be a branch|No commits between|Head sha can't be blank/i.test(
|
|
1396
|
+
detail
|
|
1397
|
+
)) {
|
|
1398
|
+
const target = opts?.targetedRepo ? ` Targeted ${opts.targetedRepo}` : "";
|
|
1399
|
+
const head = opts?.headRef ? ` with head ${opts.headRef}` : "";
|
|
1400
|
+
const hint = opts?.targetedRepo ? ` Branch was pushed to origin \u2014 confirm it exists on GitHub and differs from the base branch.${target}${head}.` : " Often `gh` targeted upstream instead of origin. Branch was pushed \u2014 retry in the latest Sideboard, or run: gh pr create -R <owner/name> --base main --head <branch>.";
|
|
1401
|
+
return `Could not create the pull request.${hint}`;
|
|
1402
|
+
}
|
|
1403
|
+
if (isGhRateLimitError(raw) || isGhRateLimitError(detail)) {
|
|
1404
|
+
const when = opts?.resetAt ? ` Try again ${formatRateLimitResetHint(opts.resetAt, opts.nowMs)}.` : " Wait a few minutes and try again.";
|
|
1405
|
+
const pushNote = opts?.pushed === false ? "" : " Your branch was already pushed.";
|
|
1406
|
+
return `GitHub API rate limit exceeded.${pushNote}${when} Or create the pull request in the browser (Push & open on GitHub).`;
|
|
1407
|
+
}
|
|
1408
|
+
return detail || "Failed to create or update pull request";
|
|
1409
|
+
}
|
|
1410
|
+
function formatIpcInvokeError(err) {
|
|
1411
|
+
let msg = err instanceof Error ? err.message : String(err);
|
|
1412
|
+
msg = msg.replace(/^Error invoking remote method '[^']+':\s*/i, "");
|
|
1413
|
+
msg = msg.replace(/^ExecaError:\s*/i, "");
|
|
1414
|
+
msg = msg.replace(/^Error:\s*/i, "");
|
|
1415
|
+
return formatGhLandError(msg);
|
|
1416
|
+
}
|
|
1417
|
+
var init_gh_errors = __esm({
|
|
1418
|
+
"src/git/gh-errors.ts"() {
|
|
1419
|
+
"use strict";
|
|
1420
|
+
}
|
|
1421
|
+
});
|
|
1422
|
+
|
|
1360
1423
|
// src/agents/path.ts
|
|
1361
1424
|
function ensureAgentPath(env = process.env) {
|
|
1362
1425
|
const home = env.HOME || env.USERPROFILE || (0, import_node_os4.homedir)();
|
|
@@ -1547,10 +1610,14 @@ __export(worktree_exports, {
|
|
|
1547
1610
|
createThreadWorktree: () => createThreadWorktree,
|
|
1548
1611
|
currentBranch: () => currentBranch,
|
|
1549
1612
|
detectLocalMergeConflicts: () => detectLocalMergeConflicts,
|
|
1613
|
+
ensureGhPreferOrigin: () => ensureGhPreferOrigin,
|
|
1550
1614
|
fetchPrHead: () => fetchPrHead,
|
|
1551
1615
|
getPr: () => getPr,
|
|
1552
1616
|
getPrChecks: () => getPrChecks,
|
|
1553
1617
|
getPrDetails: () => getPrDetails,
|
|
1618
|
+
getPrMeta: () => getPrMeta,
|
|
1619
|
+
ghHeadRef: () => ghHeadRef,
|
|
1620
|
+
ghRepoSelectArgs: () => ghRepoSelectArgs,
|
|
1554
1621
|
isDirty: () => isDirty,
|
|
1555
1622
|
isPlaceholderBranch: () => isPlaceholderBranch,
|
|
1556
1623
|
listBranches: () => listBranches,
|
|
@@ -1558,6 +1625,7 @@ __export(worktree_exports, {
|
|
|
1558
1625
|
listWorktrees: () => listWorktrees,
|
|
1559
1626
|
mergePr: () => mergePr,
|
|
1560
1627
|
normalizeWorktreePath: () => normalizeWorktreePath,
|
|
1628
|
+
originGhRepoEnv: () => originGhRepoEnv,
|
|
1561
1629
|
parseGithubSlugFromRemoteUrl: () => parseGithubSlugFromRemoteUrl,
|
|
1562
1630
|
pushBranch: () => pushBranch,
|
|
1563
1631
|
removeWorktree: () => removeWorktree,
|
|
@@ -1574,6 +1642,31 @@ __export(worktree_exports, {
|
|
|
1574
1642
|
worktreeDisplayLabelForGroup: () => worktreeDisplayLabelForGroup,
|
|
1575
1643
|
worktreeNameFromPath: () => worktreeNameFromPath
|
|
1576
1644
|
});
|
|
1645
|
+
async function lookupGithubGraphqlReset(cwd) {
|
|
1646
|
+
const result = await gh(["api", "rate_limit"], cwd, { reject: false });
|
|
1647
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) return void 0;
|
|
1648
|
+
try {
|
|
1649
|
+
const data = JSON.parse(result.stdout);
|
|
1650
|
+
const reset = data.resources?.graphql?.reset;
|
|
1651
|
+
return typeof reset === "number" ? reset : void 0;
|
|
1652
|
+
} catch {
|
|
1653
|
+
return void 0;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
async function formatPrCreateFailure(raw, cwd, ctx) {
|
|
1657
|
+
if (!isGhRateLimitError(raw)) {
|
|
1658
|
+
return formatGhLandError(raw, {
|
|
1659
|
+
targetedRepo: ctx?.slug,
|
|
1660
|
+
headRef: ctx?.head
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
const resetAt = await lookupGithubGraphqlReset(cwd);
|
|
1664
|
+
return formatGhLandError(raw, {
|
|
1665
|
+
resetAt,
|
|
1666
|
+
targetedRepo: ctx?.slug,
|
|
1667
|
+
headRef: ctx?.head
|
|
1668
|
+
});
|
|
1669
|
+
}
|
|
1577
1670
|
function slugify(input) {
|
|
1578
1671
|
return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
1579
1672
|
}
|
|
@@ -1582,10 +1675,13 @@ async function resolveRepoRoot(cwd) {
|
|
|
1582
1675
|
return stdout.trim();
|
|
1583
1676
|
}
|
|
1584
1677
|
function parseGithubSlugFromRemoteUrl(url) {
|
|
1585
|
-
const trimmed = url.trim();
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
return `${
|
|
1678
|
+
const trimmed = url.trim().replace(/\.git$/i, "");
|
|
1679
|
+
if (!trimmed) return null;
|
|
1680
|
+
const github = trimmed.match(/github\.com[:/]([^/]+)\/([^/]+)$/i);
|
|
1681
|
+
if (github?.[1] && github[2]) return `${github[1]}/${github[2]}`;
|
|
1682
|
+
const sshAlias = trimmed.match(/^git@[^:]+:([^/]+)\/([^/]+)$/i);
|
|
1683
|
+
if (sshAlias?.[1] && sshAlias[2]) return `${sshAlias[1]}/${sshAlias[2]}`;
|
|
1684
|
+
return null;
|
|
1589
1685
|
}
|
|
1590
1686
|
async function slugFromGitRemote(repoPath, remote) {
|
|
1591
1687
|
const result = await git(["remote", "get-url", remote], repoPath, {
|
|
@@ -1597,13 +1693,16 @@ async function slugFromGitRemote(repoPath, remote) {
|
|
|
1597
1693
|
async function resolveGithubRepoSlug(repoPath) {
|
|
1598
1694
|
const fromOrigin = await slugFromGitRemote(repoPath, "origin");
|
|
1599
1695
|
if (fromOrigin) return fromOrigin;
|
|
1600
|
-
const
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1696
|
+
const hasAlt = Boolean(await slugFromGitRemote(repoPath, "upstream")) || Boolean(await slugFromGitRemote(repoPath, "github"));
|
|
1697
|
+
if (!hasAlt) {
|
|
1698
|
+
const viaGh = await gh(
|
|
1699
|
+
["repo", "view", "--json", "nameWithOwner", "--jq", ".nameWithOwner"],
|
|
1700
|
+
repoPath,
|
|
1701
|
+
{ reject: false }
|
|
1702
|
+
);
|
|
1703
|
+
if (viaGh.exitCode === 0 && viaGh.stdout.trim()) {
|
|
1704
|
+
return viaGh.stdout.trim();
|
|
1705
|
+
}
|
|
1607
1706
|
}
|
|
1608
1707
|
for (const remote of ["upstream", "github"]) {
|
|
1609
1708
|
const slug = await slugFromGitRemote(repoPath, remote);
|
|
@@ -1611,15 +1710,35 @@ async function resolveGithubRepoSlug(repoPath) {
|
|
|
1611
1710
|
}
|
|
1612
1711
|
return null;
|
|
1613
1712
|
}
|
|
1713
|
+
function ghRepoSelectArgs(slug) {
|
|
1714
|
+
return ["-R", slug];
|
|
1715
|
+
}
|
|
1716
|
+
function ghHeadRef(slug, branch) {
|
|
1717
|
+
const owner = slug.split("/")[0];
|
|
1718
|
+
const head = branch.trim().replace(/^refs\/heads\//, "");
|
|
1719
|
+
if (!owner || !head) return head || branch;
|
|
1720
|
+
if (head.includes(":")) return head;
|
|
1721
|
+
return `${owner}:${head}`;
|
|
1722
|
+
}
|
|
1723
|
+
async function ensureGhPreferOrigin(cwd) {
|
|
1724
|
+
const originSlug = await slugFromGitRemote(cwd, "origin");
|
|
1725
|
+
if (!originSlug) return;
|
|
1726
|
+
const hasAlt = Boolean(await slugFromGitRemote(cwd, "upstream")) || Boolean(await slugFromGitRemote(cwd, "github"));
|
|
1727
|
+
if (!hasAlt) return;
|
|
1728
|
+
const view = await gh(["repo", "set-default", "--view"], cwd, {
|
|
1729
|
+
reject: false
|
|
1730
|
+
});
|
|
1731
|
+
const current = `${view.stdout}
|
|
1732
|
+
${view.stderr}`;
|
|
1733
|
+
if (view.exitCode === 0 && current.includes(originSlug)) return;
|
|
1734
|
+
await gh(["repo", "set-default", "origin"], cwd, { reject: false });
|
|
1735
|
+
}
|
|
1736
|
+
async function originGhRepoEnv(cwd) {
|
|
1737
|
+
await ensureGhPreferOrigin(cwd);
|
|
1738
|
+
const slug = await resolveGithubRepoSlug(cwd);
|
|
1739
|
+
return slug ? { GH_REPO: slug } : {};
|
|
1740
|
+
}
|
|
1614
1741
|
async function resolveDefaultBranch(repoPath) {
|
|
1615
|
-
const viaGh = await gh(
|
|
1616
|
-
["repo", "view", "--json", "defaultBranchRef", "--jq", ".defaultBranchRef.name"],
|
|
1617
|
-
repoPath,
|
|
1618
|
-
{ reject: false }
|
|
1619
|
-
);
|
|
1620
|
-
if (viaGh.exitCode === 0 && viaGh.stdout.trim()) {
|
|
1621
|
-
return viaGh.stdout.trim();
|
|
1622
|
-
}
|
|
1623
1742
|
const viaOrigin = await git(
|
|
1624
1743
|
["symbolic-ref", "refs/remotes/origin/HEAD"],
|
|
1625
1744
|
repoPath,
|
|
@@ -1628,6 +1747,31 @@ async function resolveDefaultBranch(repoPath) {
|
|
|
1628
1747
|
if (viaOrigin.exitCode === 0 && viaOrigin.stdout.trim()) {
|
|
1629
1748
|
return viaOrigin.stdout.trim().replace(/^refs\/remotes\/origin\//, "");
|
|
1630
1749
|
}
|
|
1750
|
+
const slug = await resolveGithubRepoSlug(repoPath);
|
|
1751
|
+
const viaGh = await gh(
|
|
1752
|
+
[
|
|
1753
|
+
"repo",
|
|
1754
|
+
"view",
|
|
1755
|
+
...slug ? ["--repo", slug] : [],
|
|
1756
|
+
"--json",
|
|
1757
|
+
"defaultBranchRef",
|
|
1758
|
+
"--jq",
|
|
1759
|
+
".defaultBranchRef.name"
|
|
1760
|
+
],
|
|
1761
|
+
repoPath,
|
|
1762
|
+
{ reject: false }
|
|
1763
|
+
);
|
|
1764
|
+
if (viaGh.exitCode === 0 && viaGh.stdout.trim()) {
|
|
1765
|
+
return viaGh.stdout.trim();
|
|
1766
|
+
}
|
|
1767
|
+
for (const candidate of ["main", "master"]) {
|
|
1768
|
+
const check = await git(
|
|
1769
|
+
["show-ref", "--verify", "--quiet", `refs/remotes/origin/${candidate}`],
|
|
1770
|
+
repoPath,
|
|
1771
|
+
{ reject: false }
|
|
1772
|
+
);
|
|
1773
|
+
if (check.exitCode === 0) return candidate;
|
|
1774
|
+
}
|
|
1631
1775
|
for (const candidate of ["main", "master"]) {
|
|
1632
1776
|
const check = await git(
|
|
1633
1777
|
["show-ref", "--verify", "--quiet", `refs/heads/${candidate}`],
|
|
@@ -1888,6 +2032,37 @@ async function getPrChecks(cwd, selector) {
|
|
|
1888
2032
|
});
|
|
1889
2033
|
return [...gateRows, ...ciChecks];
|
|
1890
2034
|
}
|
|
2035
|
+
async function getPrMeta(cwd, selector) {
|
|
2036
|
+
const slug = await resolveGithubRepoSlug(cwd);
|
|
2037
|
+
const viewArgs = [
|
|
2038
|
+
"pr",
|
|
2039
|
+
"view",
|
|
2040
|
+
selector,
|
|
2041
|
+
"--json",
|
|
2042
|
+
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName"
|
|
2043
|
+
];
|
|
2044
|
+
if (slug) viewArgs.push("--repo", slug);
|
|
2045
|
+
const { stdout, exitCode, stderr } = await gh(viewArgs, cwd, { reject: false });
|
|
2046
|
+
if (exitCode !== 0 || !stdout.trim()) {
|
|
2047
|
+
if (/no pull requests found/i.test(stderr)) return null;
|
|
2048
|
+
return null;
|
|
2049
|
+
}
|
|
2050
|
+
try {
|
|
2051
|
+
const view = JSON.parse(stdout);
|
|
2052
|
+
return {
|
|
2053
|
+
number: Number(view.number),
|
|
2054
|
+
title: String(view.title ?? ""),
|
|
2055
|
+
url: String(view.url ?? ""),
|
|
2056
|
+
state: String(view.state ?? ""),
|
|
2057
|
+
isDraft: Boolean(view.isDraft),
|
|
2058
|
+
reviewDecision: typeof view.reviewDecision === "string" && view.reviewDecision ? view.reviewDecision : null,
|
|
2059
|
+
baseRefName: String(view.baseRefName ?? ""),
|
|
2060
|
+
headRefName: String(view.headRefName ?? "")
|
|
2061
|
+
};
|
|
2062
|
+
} catch {
|
|
2063
|
+
return null;
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
1891
2066
|
async function getPrDetails(cwd, selector) {
|
|
1892
2067
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
1893
2068
|
const viewArgs = [
|
|
@@ -1909,7 +2084,6 @@ async function getPrDetails(cwd, selector) {
|
|
|
1909
2084
|
"additions",
|
|
1910
2085
|
"deletions",
|
|
1911
2086
|
"changedFiles",
|
|
1912
|
-
"commits",
|
|
1913
2087
|
"comments",
|
|
1914
2088
|
"reviews"
|
|
1915
2089
|
].join(",")
|
|
@@ -1928,14 +2102,7 @@ async function getPrDetails(cwd, selector) {
|
|
|
1928
2102
|
} catch {
|
|
1929
2103
|
throw new Error(stderr.trim() || "gh pr view returned invalid JSON");
|
|
1930
2104
|
}
|
|
1931
|
-
let checks = [];
|
|
1932
|
-
try {
|
|
1933
|
-
checks = await getPrChecks(cwd, selector) ?? [];
|
|
1934
|
-
} catch {
|
|
1935
|
-
checks = [];
|
|
1936
|
-
}
|
|
1937
2105
|
const author = view.author ?? {};
|
|
1938
|
-
const commits = Array.isArray(view.commits) ? view.commits : [];
|
|
1939
2106
|
const comments = Array.isArray(view.comments) ? view.comments : [];
|
|
1940
2107
|
const reviews = Array.isArray(view.reviews) ? view.reviews : [];
|
|
1941
2108
|
return {
|
|
@@ -1952,19 +2119,8 @@ async function getPrDetails(cwd, selector) {
|
|
|
1952
2119
|
additions: Number(view.additions ?? 0),
|
|
1953
2120
|
deletions: Number(view.deletions ?? 0),
|
|
1954
2121
|
changedFiles: Number(view.changedFiles ?? 0),
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
const authors = Array.isArray(row.authors) ? row.authors.map((a) => {
|
|
1958
|
-
const actor = a;
|
|
1959
|
-
return { login: actor.login ?? "unknown", name: actor.name ?? null };
|
|
1960
|
-
}) : [];
|
|
1961
|
-
return {
|
|
1962
|
-
oid: String(row.oid ?? ""),
|
|
1963
|
-
messageHeadline: String(row.messageHeadline ?? ""),
|
|
1964
|
-
committedDate: String(row.committedDate ?? ""),
|
|
1965
|
-
authors
|
|
1966
|
-
};
|
|
1967
|
-
}),
|
|
2122
|
+
// Commits live in Changes; omit from GraphQL to save rate-limit points.
|
|
2123
|
+
commits: [],
|
|
1968
2124
|
comments: comments.map((c) => {
|
|
1969
2125
|
const row = c;
|
|
1970
2126
|
const a = row.author ?? {};
|
|
@@ -1984,7 +2140,8 @@ async function getPrDetails(cwd, selector) {
|
|
|
1984
2140
|
submittedAt: normalizeGhTime(row.submittedAt)
|
|
1985
2141
|
};
|
|
1986
2142
|
}),
|
|
1987
|
-
|
|
2143
|
+
// CI lives in Checks tab via getPrChecks — nesting burned GraphQL points.
|
|
2144
|
+
checks: []
|
|
1988
2145
|
};
|
|
1989
2146
|
}
|
|
1990
2147
|
async function fetchPrHead(repoPath, number, localBranch) {
|
|
@@ -2020,6 +2177,7 @@ async function createThreadWorktree(opts) {
|
|
|
2020
2177
|
if ((0, import_node_fs5.existsSync)(worktreePath)) {
|
|
2021
2178
|
throw new Error(`Worktree already exists at ${worktreePath}`);
|
|
2022
2179
|
}
|
|
2180
|
+
await ensureGhPreferOrigin(opts.repoPath);
|
|
2023
2181
|
await git(["fetch", "origin", "--prune"], opts.repoPath, { reject: false });
|
|
2024
2182
|
if (!opts.sourceRef.startsWith("origin/") && !opts.sourceRef.startsWith("refs/")) {
|
|
2025
2183
|
await git(["fetch", "origin", opts.sourceRef], opts.repoPath, {
|
|
@@ -2048,6 +2206,7 @@ ${add.stdout}`;
|
|
|
2048
2206
|
);
|
|
2049
2207
|
if (retry.exitCode === 0) {
|
|
2050
2208
|
branchName = alt;
|
|
2209
|
+
await ensureGhPreferOrigin(worktreePath);
|
|
2051
2210
|
return { branchName, worktreePath };
|
|
2052
2211
|
}
|
|
2053
2212
|
}
|
|
@@ -2056,6 +2215,7 @@ ${add.stdout}`;
|
|
|
2056
2215
|
`Failed to create worktree: ${add.stderr.trim() || add.stdout.trim() || `exit ${add.exitCode}`}`
|
|
2057
2216
|
);
|
|
2058
2217
|
}
|
|
2218
|
+
await ensureGhPreferOrigin(worktreePath);
|
|
2059
2219
|
return { branchName, worktreePath };
|
|
2060
2220
|
}
|
|
2061
2221
|
async function removeWorktree(repoPath, worktreePath, opts) {
|
|
@@ -2156,8 +2316,18 @@ async function mergePr(cwd, selector, opts) {
|
|
|
2156
2316
|
return { url, state: "MERGED" };
|
|
2157
2317
|
}
|
|
2158
2318
|
async function createOrUpdatePr(worktreePath, opts) {
|
|
2319
|
+
await ensureGhPreferOrigin(worktreePath);
|
|
2320
|
+
const slug = await resolveGithubRepoSlug(worktreePath);
|
|
2321
|
+
if (!slug) {
|
|
2322
|
+
throw new Error(
|
|
2323
|
+
"Could not resolve the origin GitHub repo (owner/name) for this worktree. Check that `git remote get-url origin` points at github.com."
|
|
2324
|
+
);
|
|
2325
|
+
}
|
|
2326
|
+
const repoArgs = ghRepoSelectArgs(slug);
|
|
2327
|
+
const headRef = ghHeadRef(slug, opts.head);
|
|
2328
|
+
const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
|
|
2159
2329
|
const existing = await gh(
|
|
2160
|
-
["pr", "view",
|
|
2330
|
+
[...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
|
|
2161
2331
|
worktreePath,
|
|
2162
2332
|
{ reject: false }
|
|
2163
2333
|
);
|
|
@@ -2165,9 +2335,10 @@ async function createOrUpdatePr(worktreePath, opts) {
|
|
|
2165
2335
|
const url2 = existing.stdout.trim();
|
|
2166
2336
|
await gh(
|
|
2167
2337
|
[
|
|
2338
|
+
...repoArgs,
|
|
2168
2339
|
"pr",
|
|
2169
2340
|
"edit",
|
|
2170
|
-
|
|
2341
|
+
branchOnly,
|
|
2171
2342
|
"--title",
|
|
2172
2343
|
opts.title,
|
|
2173
2344
|
"--body",
|
|
@@ -2180,6 +2351,7 @@ async function createOrUpdatePr(worktreePath, opts) {
|
|
|
2180
2351
|
}
|
|
2181
2352
|
if (opts.web) {
|
|
2182
2353
|
const args2 = [
|
|
2354
|
+
...repoArgs,
|
|
2183
2355
|
"pr",
|
|
2184
2356
|
"create",
|
|
2185
2357
|
"--web",
|
|
@@ -2190,18 +2362,19 @@ async function createOrUpdatePr(worktreePath, opts) {
|
|
|
2190
2362
|
"--base",
|
|
2191
2363
|
opts.base,
|
|
2192
2364
|
"--head",
|
|
2193
|
-
|
|
2365
|
+
headRef
|
|
2194
2366
|
];
|
|
2195
2367
|
if (opts.draft) args2.push("--draft");
|
|
2196
2368
|
await gh(args2, worktreePath, { reject: false });
|
|
2197
2369
|
const again = await gh(
|
|
2198
|
-
["pr", "view",
|
|
2370
|
+
[...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
|
|
2199
2371
|
worktreePath,
|
|
2200
2372
|
{ reject: false }
|
|
2201
2373
|
);
|
|
2202
2374
|
return again.stdout.trim() || "";
|
|
2203
2375
|
}
|
|
2204
2376
|
const args = [
|
|
2377
|
+
...repoArgs,
|
|
2205
2378
|
"pr",
|
|
2206
2379
|
"create",
|
|
2207
2380
|
"--title",
|
|
@@ -2211,11 +2384,15 @@ async function createOrUpdatePr(worktreePath, opts) {
|
|
|
2211
2384
|
"--base",
|
|
2212
2385
|
opts.base,
|
|
2213
2386
|
"--head",
|
|
2214
|
-
|
|
2387
|
+
headRef
|
|
2215
2388
|
];
|
|
2216
2389
|
if (opts.draft) args.push("--draft");
|
|
2217
|
-
const
|
|
2218
|
-
|
|
2390
|
+
const created = await gh(args, worktreePath, { reject: false });
|
|
2391
|
+
if (created.exitCode !== 0) {
|
|
2392
|
+
const raw = created.stderr.trim() || created.stdout.trim() || "gh pr create failed";
|
|
2393
|
+
throw new Error(await formatPrCreateFailure(raw, worktreePath, { slug, head: headRef }));
|
|
2394
|
+
}
|
|
2395
|
+
const url = created.stdout.trim().split("\n").find((l) => l.startsWith("http")) ?? created.stdout.trim();
|
|
2219
2396
|
return url;
|
|
2220
2397
|
}
|
|
2221
2398
|
function suggestSlug(source) {
|
|
@@ -2276,6 +2453,7 @@ var init_worktree = __esm({
|
|
|
2276
2453
|
init_thread_store();
|
|
2277
2454
|
init_teams();
|
|
2278
2455
|
init_worktree_labels();
|
|
2456
|
+
init_gh_errors();
|
|
2279
2457
|
init_run();
|
|
2280
2458
|
init_pr_gates();
|
|
2281
2459
|
init_teams();
|
|
@@ -2316,7 +2494,8 @@ function coordinatorGreenfieldPlaybook(reposDir) {
|
|
|
2316
2494
|
"- Examples:",
|
|
2317
2495
|
` - Clone: \`git clone <url> ${reposDir}/<name>\``,
|
|
2318
2496
|
` - New GitHub repo: \`gh repo create <owner>/<name> --private --clone -- ${reposDir}/<name>\` (or mkdir + git init + gh repo create + remote add + push)`,
|
|
2319
|
-
"- Then: add_workspace with that absolute path \u2192 create_thread (repoPath + parentThreadId) \u2192 send_to_thread (build) \u2192 wait_for_turn \u2192 send_to_thread (`gh pr create --draft
|
|
2497
|
+
"- Then: add_workspace with that absolute path \u2192 create_thread (repoPath + parentThreadId) \u2192 send_to_thread (build) \u2192 wait_for_turn \u2192 send_to_thread (`gh pr create --draft -R <origin-owner/name>`) or create_draft_pr.",
|
|
2498
|
+
"- Always target the child worktree's **origin** (`github:` slug from list_workspaces / `git remote get-url origin` in that worktree). Never open PRs against `upstream`.",
|
|
2320
2499
|
"- Do coding work in the child worktree thread, not by editing files in this home cwd."
|
|
2321
2500
|
].join("\n");
|
|
2322
2501
|
}
|
|
@@ -2381,7 +2560,7 @@ function coordinatorSystemPrompt(opts) {
|
|
|
2381
2560
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
2382
2561
|
coordinatorGreenfieldPlaybook(reposDir),
|
|
2383
2562
|
"When creating threads, pass the correct repoPath for the target workspace and parentThreadId for children.",
|
|
2384
|
-
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 send_to_thread (ask for `gh pr create --draft
|
|
2563
|
+
"Typical flow (existing): list_workspaces \u2192 list_branches|list_prs|list_issues \u2192 create_thread \u2192 send_to_thread (implement) \u2192 wait_for_turn \u2192 send_to_thread (ask for `gh pr create --draft -R <origin-owner/name>` using the workspace github slug) \u2192 wait_for_turn. Use create_draft_pr only if the child cannot open the PR. Never target upstream.",
|
|
2385
2564
|
"Typical flow (new app): Bash under repos dir (clone or gh repo create) \u2192 add_workspace \u2192 create_thread \u2192 send_to_thread \u2192 wait_for_turn \u2192 draft PR.",
|
|
2386
2565
|
`Goal: ${opts.goal}`,
|
|
2387
2566
|
`Parent thread id (pass as parentThreadId when creating children): ${opts.parentId}`,
|
|
@@ -2418,8 +2597,8 @@ var init_coordinator_prompt = __esm({
|
|
|
2418
2597
|
"Inspect / PRs:",
|
|
2419
2598
|
"- get_diff \u2014 compact diff summary",
|
|
2420
2599
|
"- preview_land \u2014 preview push+PR (does not push)",
|
|
2421
|
-
"- Prefer asking the worktree agent via send_to_thread to open a draft PR
|
|
2422
|
-
"- create_draft_pr \u2014 fallback: commit (if dirty), push, open/update a DRAFT PR from the orchestrator",
|
|
2600
|
+
"- Prefer asking the worktree agent via send_to_thread to open a draft PR with `gh pr create --draft -R <origin-owner/name>` (use the workspace `github:` slug / that worktree's origin \u2014 never upstream) so it owns title/body from the diff.",
|
|
2601
|
+
"- create_draft_pr \u2014 fallback: commit (if dirty), push to origin, open/update a DRAFT PR from the orchestrator (always against origin)",
|
|
2423
2602
|
"Human-only (do not attempt): ready-for-review confirm_land, purge_thread.",
|
|
2424
2603
|
"Thread links in replies: when mentioning a chat/thread for the user, include a markdown link `[Title](sideboard://thread/<id>)` using the full id (or the link field from create_thread / list_threads). Sideboard renders these as clickable opens.",
|
|
2425
2604
|
"Bash / Read / etc: allowed for (1) inspecting target worktrees / registered repo paths from MCP, and (2) greenfield setup under ~/sideboard/repos (git clone, gh repo create, git init+remote). Never git init/clone *inside* this synthetic home cwd \u2014 emptiness here is expected, not a bug."
|
|
@@ -2596,6 +2775,9 @@ __export(workspaces_exports, {
|
|
|
2596
2775
|
function workspacesFile() {
|
|
2597
2776
|
return (0, import_node_path8.join)(appDataDir(), "workspaces.json");
|
|
2598
2777
|
}
|
|
2778
|
+
function removedWorkspacesFile() {
|
|
2779
|
+
return (0, import_node_path8.join)(appDataDir(), "removed-workspaces.json");
|
|
2780
|
+
}
|
|
2599
2781
|
function readAll() {
|
|
2600
2782
|
const path = workspacesFile();
|
|
2601
2783
|
if (!(0, import_node_fs7.existsSync)(path)) return [];
|
|
@@ -2610,12 +2792,44 @@ function writeAll(list) {
|
|
|
2610
2792
|
(0, import_node_fs7.mkdirSync)(appDataDir(), { recursive: true });
|
|
2611
2793
|
(0, import_node_fs7.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
|
|
2612
2794
|
}
|
|
2795
|
+
function readRemoved() {
|
|
2796
|
+
const path = removedWorkspacesFile();
|
|
2797
|
+
if (!(0, import_node_fs7.existsSync)(path)) return /* @__PURE__ */ new Set();
|
|
2798
|
+
try {
|
|
2799
|
+
const raw = JSON.parse((0, import_node_fs7.readFileSync)(path, "utf8"));
|
|
2800
|
+
return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
|
|
2801
|
+
} catch {
|
|
2802
|
+
return /* @__PURE__ */ new Set();
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
function writeRemoved(paths) {
|
|
2806
|
+
(0, import_node_fs7.mkdirSync)(appDataDir(), { recursive: true });
|
|
2807
|
+
(0, import_node_fs7.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
|
|
2808
|
+
}
|
|
2809
|
+
function rememberRemoved(repoPath) {
|
|
2810
|
+
const next = readRemoved();
|
|
2811
|
+
next.add(repoPath);
|
|
2812
|
+
writeRemoved(next);
|
|
2813
|
+
}
|
|
2814
|
+
function forgetRemoved(repoPath) {
|
|
2815
|
+
const next = readRemoved();
|
|
2816
|
+
if (!next.delete(repoPath)) return;
|
|
2817
|
+
writeRemoved(next);
|
|
2818
|
+
}
|
|
2613
2819
|
function listWorkspaces() {
|
|
2614
|
-
|
|
2820
|
+
const all = readAll();
|
|
2821
|
+
const valid = all.filter(
|
|
2822
|
+
(w) => Boolean(w.path) && w.path !== "/" && w.path !== "." && !isGlobalRepoPath(w.path)
|
|
2823
|
+
);
|
|
2824
|
+
if (valid.length !== all.length) writeAll(valid);
|
|
2825
|
+
return valid.sort((a, b) => a.name.localeCompare(b.name));
|
|
2615
2826
|
}
|
|
2616
2827
|
async function addWorkspace(repoPath) {
|
|
2617
2828
|
const root = await resolveRepoRoot(repoPath);
|
|
2829
|
+
if (!root || root === "/") throw new Error(`Invalid repo path: ${repoPath}`);
|
|
2618
2830
|
if (!(0, import_node_fs7.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
|
|
2831
|
+
forgetRemoved(root);
|
|
2832
|
+
await ensureGhPreferOrigin(root);
|
|
2619
2833
|
const current = readAll();
|
|
2620
2834
|
const existing = current.find((w) => w.path === root);
|
|
2621
2835
|
if (existing) return existing;
|
|
@@ -2629,16 +2843,20 @@ async function addWorkspace(repoPath) {
|
|
|
2629
2843
|
}
|
|
2630
2844
|
function removeWorkspace(repoPath) {
|
|
2631
2845
|
writeAll(readAll().filter((w) => w.path !== repoPath));
|
|
2846
|
+
rememberRemoved(repoPath);
|
|
2632
2847
|
}
|
|
2633
2848
|
async function ensureWorkspace(repoPath) {
|
|
2634
2849
|
return addWorkspace(repoPath);
|
|
2635
2850
|
}
|
|
2636
2851
|
function syncWorkspacesFromThreads(repoPaths) {
|
|
2637
2852
|
const current = readAll();
|
|
2853
|
+
const removed = readRemoved();
|
|
2638
2854
|
const byPath = new Map(current.map((w) => [w.path, w]));
|
|
2639
2855
|
let dirty = false;
|
|
2640
2856
|
for (const path of repoPaths) {
|
|
2641
|
-
if (!path || isGlobalRepoPath(path) || byPath.has(path))
|
|
2857
|
+
if (!path || path === "/" || isGlobalRepoPath(path) || byPath.has(path) || removed.has(path)) {
|
|
2858
|
+
continue;
|
|
2859
|
+
}
|
|
2642
2860
|
if (!(0, import_node_fs7.existsSync)(path)) continue;
|
|
2643
2861
|
const ws = {
|
|
2644
2862
|
path,
|
|
@@ -4683,11 +4901,13 @@ __export(index_exports, {
|
|
|
4683
4901
|
enrichWorkspacesWithGithub: () => enrichWorkspacesWithGithub,
|
|
4684
4902
|
ensureAgentPath: () => ensureAgentPath,
|
|
4685
4903
|
ensureCloudCoordinator: () => ensureCloudCoordinator,
|
|
4904
|
+
ensureGhPreferOrigin: () => ensureGhPreferOrigin,
|
|
4686
4905
|
ensureGlobalCoordinatorCwd: () => ensureGlobalCoordinatorCwd,
|
|
4687
4906
|
ensureWorkspace: () => ensureWorkspace,
|
|
4688
4907
|
estimateMessageChars: () => estimateMessageChars,
|
|
4689
4908
|
estimateThreadChars: () => estimateThreadChars,
|
|
4690
4909
|
expandComposerPrompt: () => expandComposerPrompt,
|
|
4910
|
+
extractGhErrorDetail: () => extractGhErrorDetail,
|
|
4691
4911
|
extractiveSummary: () => extractiveSummary,
|
|
4692
4912
|
fetchPrHead: () => fetchPrHead,
|
|
4693
4913
|
finalizeParts: () => finalizeParts,
|
|
@@ -4700,7 +4920,10 @@ __export(index_exports, {
|
|
|
4700
4920
|
forkThreadWorktree: () => forkThreadWorktree,
|
|
4701
4921
|
formatAgentInstructions: () => formatAgentInstructions,
|
|
4702
4922
|
formatBrightsyFetchError: () => formatBrightsyFetchError,
|
|
4923
|
+
formatGhLandError: () => formatGhLandError,
|
|
4924
|
+
formatIpcInvokeError: () => formatIpcInvokeError,
|
|
4703
4925
|
formatMessagesAsTranscript: () => formatMessagesAsTranscript,
|
|
4926
|
+
formatRateLimitResetHint: () => formatRateLimitResetHint,
|
|
4704
4927
|
formatRenameBranchDirective: () => formatRenameBranchDirective,
|
|
4705
4928
|
formatTranscriptMarkdown: () => formatTranscriptMarkdown,
|
|
4706
4929
|
formatWorkspaceInventory: () => formatWorkspaceInventory,
|
|
@@ -4717,10 +4940,13 @@ __export(index_exports, {
|
|
|
4717
4940
|
getPr: () => getPr,
|
|
4718
4941
|
getPrChecks: () => getPrChecks,
|
|
4719
4942
|
getPrDetails: () => getPrDetails,
|
|
4943
|
+
getPrMeta: () => getPrMeta,
|
|
4720
4944
|
getRepoSetupInfo: () => getRepoSetupInfo,
|
|
4721
4945
|
getRunMode: () => getRunMode,
|
|
4722
4946
|
getRunScript: () => getRunScript,
|
|
4723
4947
|
gh: () => gh,
|
|
4948
|
+
ghHeadRef: () => ghHeadRef,
|
|
4949
|
+
ghRepoSelectArgs: () => ghRepoSelectArgs,
|
|
4724
4950
|
git: () => git,
|
|
4725
4951
|
globalAgentCwd: () => globalAgentCwd,
|
|
4726
4952
|
harnessEnvKey: () => harnessEnvKey,
|
|
@@ -4737,6 +4963,7 @@ __export(index_exports, {
|
|
|
4737
4963
|
isBrightsyNdjsonLine: () => isBrightsyNdjsonLine,
|
|
4738
4964
|
isCloudCoordinatorThread: () => isCloudCoordinatorThread,
|
|
4739
4965
|
isDirty: () => isDirty,
|
|
4966
|
+
isGhRateLimitError: () => isGhRateLimitError,
|
|
4740
4967
|
isGlobalRepoPath: () => isGlobalRepoPath,
|
|
4741
4968
|
isGlobalThread: () => isGlobalThread,
|
|
4742
4969
|
isLinearConnected: () => isLinearConnected,
|
|
@@ -4779,6 +5006,7 @@ __export(index_exports, {
|
|
|
4779
5006
|
opencodeAdapter: () => opencodeAdapter,
|
|
4780
5007
|
orchestrationTitleNeedsSoccerNickname: () => orchestrationTitleNeedsSoccerNickname,
|
|
4781
5008
|
orchestratorSessionPoisonedByBuiltins: () => orchestratorSessionPoisonedByBuiltins,
|
|
5009
|
+
originGhRepoEnv: () => originGhRepoEnv,
|
|
4782
5010
|
parseCursorRunnerLine: () => parseCursorRunnerLine,
|
|
4783
5011
|
parseForceStopMessage: () => parseForceStopMessage,
|
|
4784
5012
|
parseGithubSlugFromRemoteUrl: () => parseGithubSlugFromRemoteUrl,
|
|
@@ -4870,6 +5098,7 @@ init_thread_store();
|
|
|
4870
5098
|
init_workspaces();
|
|
4871
5099
|
init_global_workspace();
|
|
4872
5100
|
init_run();
|
|
5101
|
+
init_gh_errors();
|
|
4873
5102
|
init_worktree();
|
|
4874
5103
|
|
|
4875
5104
|
// src/integrations/github.ts
|
|
@@ -5053,7 +5282,9 @@ init_agents();
|
|
|
5053
5282
|
// src/agents/spawn.ts
|
|
5054
5283
|
var import_node_readline = require("readline");
|
|
5055
5284
|
var import_execa2 = require("execa");
|
|
5285
|
+
init_worktree();
|
|
5056
5286
|
init_app_settings();
|
|
5287
|
+
init_global_workspace();
|
|
5057
5288
|
init_brightsy();
|
|
5058
5289
|
init_agents();
|
|
5059
5290
|
|
|
@@ -5287,9 +5518,14 @@ async function spawnAgentTurn(thread, input, onEvent) {
|
|
|
5287
5518
|
`Agent cwd must be the thread worktree (got ${cmd.cwd}, expected ${thread.worktreePath})`
|
|
5288
5519
|
);
|
|
5289
5520
|
}
|
|
5521
|
+
const env = childEnvWithAppSettings(cmd.env);
|
|
5522
|
+
if (!isOrchestratorThread(thread)) {
|
|
5523
|
+
const originEnv = await originGhRepoEnv(thread.worktreePath).catch(() => ({}));
|
|
5524
|
+
Object.assign(env, originEnv);
|
|
5525
|
+
}
|
|
5290
5526
|
const child = (0, import_execa2.execa)(cmd.file, cmd.args, {
|
|
5291
5527
|
cwd: cmd.cwd,
|
|
5292
|
-
env
|
|
5528
|
+
env,
|
|
5293
5529
|
reject: false,
|
|
5294
5530
|
stdout: "pipe",
|
|
5295
5531
|
stderr: "pipe",
|
|
@@ -5396,7 +5632,7 @@ function formatRenameBranchDirective(thread, opts) {
|
|
|
5396
5632
|
}
|
|
5397
5633
|
return lines.join("\n");
|
|
5398
5634
|
}
|
|
5399
|
-
function formatWorktreeDirective(thread) {
|
|
5635
|
+
function formatWorktreeDirective(thread, opts) {
|
|
5400
5636
|
const worktree = normPath2(thread.worktreePath);
|
|
5401
5637
|
const repo = normPath2(thread.repoPath);
|
|
5402
5638
|
const dir = worktreeNameFromPath(thread.worktreePath);
|
|
@@ -5417,7 +5653,13 @@ function formatWorktreeDirective(thread) {
|
|
|
5417
5653
|
);
|
|
5418
5654
|
}
|
|
5419
5655
|
lines.push("");
|
|
5420
|
-
lines.push("
|
|
5656
|
+
lines.push("Git remotes + pull requests (when the work is ready to share):");
|
|
5657
|
+
lines.push(
|
|
5658
|
+
"- Always use this worktree's `origin` remote (`git remote get-url origin` from this cwd). Never push to or open PRs against `upstream` (template remotes)."
|
|
5659
|
+
);
|
|
5660
|
+
lines.push(
|
|
5661
|
+
"- Push with `git push -u origin HEAD` (or the current branch name). Do not `git push upstream`."
|
|
5662
|
+
);
|
|
5421
5663
|
lines.push(
|
|
5422
5664
|
"- Derive the PR title and body from what the changes actually do and why \u2014 inspect the diff/commits and the user request. Do not use the soccer-team worktree nickname or placeholder branch as the PR title."
|
|
5423
5665
|
);
|
|
@@ -5431,9 +5673,13 @@ function formatWorktreeDirective(thread) {
|
|
|
5431
5673
|
lines.push(
|
|
5432
5674
|
`- A PR already exists (${thread.prUrl}). Update it (push + edit title/body if the purpose drifted) instead of opening a duplicate.`
|
|
5433
5675
|
);
|
|
5676
|
+
} else if (opts?.githubSlug) {
|
|
5677
|
+
lines.push(
|
|
5678
|
+
`- Prefer a draft PR first: \`gh pr create --draft -R ${opts.githubSlug}\` (or update via \`gh pr edit -R ${opts.githubSlug}\`) once the change set is coherent. Always pass \`-R ${opts.githubSlug}\` (this worktree's origin). Bare \`gh pr create\` may target upstream instead of origin on dual-remote checkouts. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name.`
|
|
5679
|
+
);
|
|
5434
5680
|
} else {
|
|
5435
5681
|
lines.push(
|
|
5436
|
-
"- Prefer a draft PR first: `gh pr create --draft
|
|
5682
|
+
"- Prefer a draft PR first: `gh pr create --draft -R <origin-owner/name>` (or update via `gh pr edit -R \u2026`) once the change set is coherent. Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree \u2014 never from `upstream`. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name."
|
|
5437
5683
|
);
|
|
5438
5684
|
}
|
|
5439
5685
|
return lines.join("\n");
|
|
@@ -6954,6 +7200,7 @@ async function suggestPrMetadata(worktreePath, opts) {
|
|
|
6954
7200
|
}
|
|
6955
7201
|
|
|
6956
7202
|
// src/land/land.ts
|
|
7203
|
+
init_gh_errors();
|
|
6957
7204
|
async function previewLand(thread) {
|
|
6958
7205
|
if (thread.sourceIsFork) {
|
|
6959
7206
|
return {
|
|
@@ -7017,15 +7264,24 @@ async function confirmLand(thread, opts) {
|
|
|
7017
7264
|
const head = headOut.trim();
|
|
7018
7265
|
const branch = head && head !== "HEAD" ? head : thread.branchName;
|
|
7019
7266
|
await pushBranch(thread.worktreePath, branch);
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7267
|
+
try {
|
|
7268
|
+
const prUrl = await createOrUpdatePr(thread.worktreePath, {
|
|
7269
|
+
title: meta.title,
|
|
7270
|
+
body: meta.body,
|
|
7271
|
+
base: preview.target,
|
|
7272
|
+
head: branch,
|
|
7273
|
+
draft: opts?.draft,
|
|
7274
|
+
web: opts?.web
|
|
7275
|
+
});
|
|
7276
|
+
return { prUrl, pushed: true, committed };
|
|
7277
|
+
} catch (err) {
|
|
7278
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
7279
|
+
if (raw.startsWith("GitHub API rate limit exceeded.")) throw err;
|
|
7280
|
+
if (/Command failed with exit code|API rate limit/i.test(raw)) {
|
|
7281
|
+
throw new Error(formatGhLandError(raw));
|
|
7282
|
+
}
|
|
7283
|
+
throw err;
|
|
7284
|
+
}
|
|
7029
7285
|
}
|
|
7030
7286
|
|
|
7031
7287
|
// src/threads/create.ts
|
|
@@ -7923,7 +8179,7 @@ var Orchestrator = class {
|
|
|
7923
8179
|
return thread;
|
|
7924
8180
|
}
|
|
7925
8181
|
listWorkspaces() {
|
|
7926
|
-
const fromThreads = listThreads({ includeArchived:
|
|
8182
|
+
const fromThreads = listThreads({ includeArchived: false }).map((t) => t.repoPath);
|
|
7927
8183
|
return syncWorkspacesFromThreads(fromThreads);
|
|
7928
8184
|
}
|
|
7929
8185
|
async addWorkspace(repoPath) {
|
|
@@ -8069,7 +8325,11 @@ var Orchestrator = class {
|
|
|
8069
8325
|
}
|
|
8070
8326
|
const isBrightsy = fresh.agent === "brightsy";
|
|
8071
8327
|
const isOrchestration = isOrchestratorThread(fresh);
|
|
8072
|
-
const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh
|
|
8328
|
+
const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
|
|
8329
|
+
githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
|
|
8330
|
+
() => null
|
|
8331
|
+
)
|
|
8332
|
+
});
|
|
8073
8333
|
const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
|
|
8074
8334
|
const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
|
|
8075
8335
|
const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
|
|
@@ -8540,8 +8800,8 @@ var Orchestrator = class {
|
|
|
8540
8800
|
if (result.prUrl) {
|
|
8541
8801
|
const patch = { prUrl: result.prUrl };
|
|
8542
8802
|
try {
|
|
8543
|
-
const
|
|
8544
|
-
if (
|
|
8803
|
+
const meta = await getPrMeta(thread.worktreePath, result.prUrl);
|
|
8804
|
+
if (meta?.title) patch.prTitle = meta.title;
|
|
8545
8805
|
} catch {
|
|
8546
8806
|
}
|
|
8547
8807
|
updateThread(thread.id, patch);
|
|
@@ -8574,6 +8834,24 @@ var Orchestrator = class {
|
|
|
8574
8834
|
if (!selector) return null;
|
|
8575
8835
|
return getPrChecks(cwd, selector);
|
|
8576
8836
|
}
|
|
8837
|
+
async getPrMeta(threadRef) {
|
|
8838
|
+
const { thread, selector, cwd } = await this.withPrSelector(threadRef);
|
|
8839
|
+
if (!selector) return null;
|
|
8840
|
+
const meta = await getPrMeta(cwd, selector);
|
|
8841
|
+
if (meta) {
|
|
8842
|
+
const patch = {};
|
|
8843
|
+
if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
|
|
8844
|
+
if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
|
|
8845
|
+
if (Object.keys(patch).length > 0) {
|
|
8846
|
+
updateThread(thread.id, patch);
|
|
8847
|
+
const latest = this.requireThread(thread.id);
|
|
8848
|
+
if (!latest.userSetTitle && meta.title && latest.title !== meta.title) {
|
|
8849
|
+
updateThread(thread.id, { title: meta.title });
|
|
8850
|
+
}
|
|
8851
|
+
}
|
|
8852
|
+
}
|
|
8853
|
+
return meta;
|
|
8854
|
+
}
|
|
8577
8855
|
async getPrDetails(threadRef) {
|
|
8578
8856
|
const { thread, selector, cwd } = await this.withPrSelector(threadRef);
|
|
8579
8857
|
if (!selector) return null;
|
|
@@ -9232,7 +9510,7 @@ async function startMcpServer() {
|
|
|
9232
9510
|
);
|
|
9233
9511
|
server.tool(
|
|
9234
9512
|
"create_draft_pr",
|
|
9235
|
-
"Commit dirty changes if needed, push the thread branch, and create/update a DRAFT GitHub PR. Ready-for-review / non-draft land stays human-only (CLI/app confirm_land). Prefer this when the orchestrator should open a PR itself; alternatively send_to_thread asking the worktree agent to run `gh pr create --draft
|
|
9513
|
+
"Commit dirty changes if needed, push the thread branch to origin, and create/update a DRAFT GitHub PR against that worktree's origin (never upstream). Ready-for-review / non-draft land stays human-only (CLI/app confirm_land). Prefer this when the orchestrator should open a PR itself; alternatively send_to_thread asking the worktree agent to run `gh pr create --draft -R <origin-owner/name>`.",
|
|
9236
9514
|
{ ref: import_zod.z.string() },
|
|
9237
9515
|
async ({ ref }) => {
|
|
9238
9516
|
try {
|
|
@@ -9758,11 +10036,13 @@ init_injected_mcp();
|
|
|
9758
10036
|
enrichWorkspacesWithGithub,
|
|
9759
10037
|
ensureAgentPath,
|
|
9760
10038
|
ensureCloudCoordinator,
|
|
10039
|
+
ensureGhPreferOrigin,
|
|
9761
10040
|
ensureGlobalCoordinatorCwd,
|
|
9762
10041
|
ensureWorkspace,
|
|
9763
10042
|
estimateMessageChars,
|
|
9764
10043
|
estimateThreadChars,
|
|
9765
10044
|
expandComposerPrompt,
|
|
10045
|
+
extractGhErrorDetail,
|
|
9766
10046
|
extractiveSummary,
|
|
9767
10047
|
fetchPrHead,
|
|
9768
10048
|
finalizeParts,
|
|
@@ -9775,7 +10055,10 @@ init_injected_mcp();
|
|
|
9775
10055
|
forkThreadWorktree,
|
|
9776
10056
|
formatAgentInstructions,
|
|
9777
10057
|
formatBrightsyFetchError,
|
|
10058
|
+
formatGhLandError,
|
|
10059
|
+
formatIpcInvokeError,
|
|
9778
10060
|
formatMessagesAsTranscript,
|
|
10061
|
+
formatRateLimitResetHint,
|
|
9779
10062
|
formatRenameBranchDirective,
|
|
9780
10063
|
formatTranscriptMarkdown,
|
|
9781
10064
|
formatWorkspaceInventory,
|
|
@@ -9792,10 +10075,13 @@ init_injected_mcp();
|
|
|
9792
10075
|
getPr,
|
|
9793
10076
|
getPrChecks,
|
|
9794
10077
|
getPrDetails,
|
|
10078
|
+
getPrMeta,
|
|
9795
10079
|
getRepoSetupInfo,
|
|
9796
10080
|
getRunMode,
|
|
9797
10081
|
getRunScript,
|
|
9798
10082
|
gh,
|
|
10083
|
+
ghHeadRef,
|
|
10084
|
+
ghRepoSelectArgs,
|
|
9799
10085
|
git,
|
|
9800
10086
|
globalAgentCwd,
|
|
9801
10087
|
harnessEnvKey,
|
|
@@ -9812,6 +10098,7 @@ init_injected_mcp();
|
|
|
9812
10098
|
isBrightsyNdjsonLine,
|
|
9813
10099
|
isCloudCoordinatorThread,
|
|
9814
10100
|
isDirty,
|
|
10101
|
+
isGhRateLimitError,
|
|
9815
10102
|
isGlobalRepoPath,
|
|
9816
10103
|
isGlobalThread,
|
|
9817
10104
|
isLinearConnected,
|
|
@@ -9854,6 +10141,7 @@ init_injected_mcp();
|
|
|
9854
10141
|
opencodeAdapter,
|
|
9855
10142
|
orchestrationTitleNeedsSoccerNickname,
|
|
9856
10143
|
orchestratorSessionPoisonedByBuiltins,
|
|
10144
|
+
originGhRepoEnv,
|
|
9857
10145
|
parseCursorRunnerLine,
|
|
9858
10146
|
parseForceStopMessage,
|
|
9859
10147
|
parseGithubSlugFromRemoteUrl,
|