@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
package/dist/mcp/run-stdio.cjs
CHANGED
|
@@ -1405,13 +1405,13 @@ function toolDescription(name, input) {
|
|
|
1405
1405
|
else if (/notify_orchestrator$/i.test(name)) return "Notify orchestrator";
|
|
1406
1406
|
else if (/set_workspace_tags$/i.test(name)) return "Set workspace tags";
|
|
1407
1407
|
if (/(?:list|set|delete)_env$/i.test(name)) return /list_env$/i.test(name) ? "List env vars" : /set_env$/i.test(name) ? "Set env var" : "Delete env var";
|
|
1408
|
-
if (/list_schedules$/i.test(name)) return "List
|
|
1408
|
+
if (/list_schedules$/i.test(name)) return "List automations";
|
|
1409
1409
|
if (/create_schedule$/i.test(name)) {
|
|
1410
|
-
return str(input?.name) ? `
|
|
1410
|
+
return str(input?.name) ? `Automation ${str(input?.name)}` : "Create automation";
|
|
1411
1411
|
}
|
|
1412
|
-
if (/update_schedule$/i.test(name)) return "Update
|
|
1413
|
-
if (/delete_schedule$/i.test(name)) return "Delete
|
|
1414
|
-
if (/run_schedule$/i.test(name)) return "Run
|
|
1412
|
+
if (/update_schedule$/i.test(name)) return "Update automation";
|
|
1413
|
+
if (/delete_schedule$/i.test(name)) return "Delete automation";
|
|
1414
|
+
if (/run_schedule$/i.test(name)) return "Run automation";
|
|
1415
1415
|
if (isSkillToolName(name)) {
|
|
1416
1416
|
const cmd = skillCommandFromInput(input);
|
|
1417
1417
|
return cmd ? `Using /${cmd}` : "Using skill";
|
|
@@ -2541,6 +2541,7 @@ var run_exports = {};
|
|
|
2541
2541
|
__export(run_exports, {
|
|
2542
2542
|
gh: () => gh,
|
|
2543
2543
|
git: () => git,
|
|
2544
|
+
originCli: () => originCli,
|
|
2544
2545
|
resolveGhAuthToken: () => resolveGhAuthToken,
|
|
2545
2546
|
resolveGitDirsForLockRecovery: () => resolveGitDirsForLockRecovery,
|
|
2546
2547
|
run: () => run
|
|
@@ -2621,6 +2622,17 @@ ${result.stderr}`
|
|
|
2621
2622
|
}
|
|
2622
2623
|
return result;
|
|
2623
2624
|
}
|
|
2625
|
+
async function originCli(args, cwd, opts) {
|
|
2626
|
+
return run("origin", args, {
|
|
2627
|
+
cwd,
|
|
2628
|
+
reject: opts?.reject,
|
|
2629
|
+
timeoutMs: opts?.timeoutMs,
|
|
2630
|
+
env: {
|
|
2631
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
2632
|
+
NO_COLOR: "1"
|
|
2633
|
+
}
|
|
2634
|
+
});
|
|
2635
|
+
}
|
|
2624
2636
|
async function gh(args, cwd, opts) {
|
|
2625
2637
|
return run("gh", args, {
|
|
2626
2638
|
cwd,
|
|
@@ -4532,7 +4544,9 @@ function planHistoryRetention(archived, thresholds = {}) {
|
|
|
4532
4544
|
if (archivedUpdatedMs(thread) < cutoff) expiredIds.add(thread.id);
|
|
4533
4545
|
}
|
|
4534
4546
|
}
|
|
4535
|
-
const remaining = oldestFirst.filter(
|
|
4547
|
+
const remaining = oldestFirst.filter(
|
|
4548
|
+
(t) => !expiredIds.has(t.id) && !t.ephemeralAutomationId?.trim()
|
|
4549
|
+
);
|
|
4536
4550
|
const overflow = remaining.length > maxCount ? remaining.slice(0, remaining.length - maxCount) : [];
|
|
4537
4551
|
const stripIds = [];
|
|
4538
4552
|
const purgeIds = [...expiredIds];
|
|
@@ -6526,9 +6540,9 @@ var init_app_settings = __esm({
|
|
|
6526
6540
|
});
|
|
6527
6541
|
|
|
6528
6542
|
// src/git/git-auth-mode.ts
|
|
6529
|
-
function githubHttpsInsteadOfEntries(
|
|
6543
|
+
function githubHttpsInsteadOfEntries(remoteUrl2) {
|
|
6530
6544
|
const entries = [...HTTPS_REWRITE];
|
|
6531
|
-
const url =
|
|
6545
|
+
const url = remoteUrl2?.trim() ?? "";
|
|
6532
6546
|
const scpAlias = url.match(/^(git@github\.com-[^:]+):/i);
|
|
6533
6547
|
if (scpAlias?.[1]) {
|
|
6534
6548
|
entries.push({
|
|
@@ -6735,6 +6749,58 @@ var init_git_auth_mode = __esm({
|
|
|
6735
6749
|
}
|
|
6736
6750
|
});
|
|
6737
6751
|
|
|
6752
|
+
// src/git/remote-forge.ts
|
|
6753
|
+
function formatRemoteTag(remote) {
|
|
6754
|
+
const name = remote.forge === "other" ? remote.host : remote.forge;
|
|
6755
|
+
return `${name}:${remote.slug}`;
|
|
6756
|
+
}
|
|
6757
|
+
function forgeForHost(host) {
|
|
6758
|
+
const h = host.toLowerCase();
|
|
6759
|
+
if (h === "github.com" || h.startsWith("github.com-")) return "github";
|
|
6760
|
+
if (h === "origin.cursor.com") return "cursor";
|
|
6761
|
+
if (h === "entire.io" || h.endsWith(".entire.io") || h === "entire.com" || h.endsWith(".entire.com")) {
|
|
6762
|
+
return "entire";
|
|
6763
|
+
}
|
|
6764
|
+
return "other";
|
|
6765
|
+
}
|
|
6766
|
+
function repoFromParts(host, owner, name) {
|
|
6767
|
+
if (!host || !owner || !name || owner.includes("@")) return null;
|
|
6768
|
+
const normalized = host.toLowerCase();
|
|
6769
|
+
return {
|
|
6770
|
+
forge: forgeForHost(normalized),
|
|
6771
|
+
host: normalized,
|
|
6772
|
+
slug: `${owner}/${name}`
|
|
6773
|
+
};
|
|
6774
|
+
}
|
|
6775
|
+
function parseRemoteRepo(url) {
|
|
6776
|
+
const trimmed = url.trim().replace(/\.git$/i, "");
|
|
6777
|
+
if (!trimmed) return null;
|
|
6778
|
+
if (trimmed.toLowerCase().startsWith("entire://")) {
|
|
6779
|
+
const parts = trimmed.slice("entire://".length).replace(/^\/+/, "").split("/").filter(Boolean);
|
|
6780
|
+
if (parts.length === 2) return { forge: "entire", host: "entire.io", slug: `${parts[0]}/${parts[1]}` };
|
|
6781
|
+
if (parts.length >= 3) {
|
|
6782
|
+
return repoFromParts(parts[0], parts[1], parts[2]);
|
|
6783
|
+
}
|
|
6784
|
+
return null;
|
|
6785
|
+
}
|
|
6786
|
+
const ssh = trimmed.match(/^git@([^:]+):([^/]+)\/([^/]+)$/i);
|
|
6787
|
+
if (ssh?.[1] && ssh[2] && ssh[3]) return repoFromParts(ssh[1], ssh[2], ssh[3]);
|
|
6788
|
+
try {
|
|
6789
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
6790
|
+
const parsed = new URL(withScheme);
|
|
6791
|
+
const parts = parsed.pathname.split("/").filter(Boolean);
|
|
6792
|
+
if (parts.length < 2) return null;
|
|
6793
|
+
return repoFromParts(parsed.hostname, parts[parts.length - 2], parts[parts.length - 1]);
|
|
6794
|
+
} catch {
|
|
6795
|
+
return null;
|
|
6796
|
+
}
|
|
6797
|
+
}
|
|
6798
|
+
var init_remote_forge = __esm({
|
|
6799
|
+
"src/git/remote-forge.ts"() {
|
|
6800
|
+
"use strict";
|
|
6801
|
+
}
|
|
6802
|
+
});
|
|
6803
|
+
|
|
6738
6804
|
// src/git/list-prs.ts
|
|
6739
6805
|
function normalizePrListState(state) {
|
|
6740
6806
|
const key = (state ?? "").trim().toLowerCase();
|
|
@@ -7103,42 +7169,6 @@ var init_list_prs = __esm({
|
|
|
7103
7169
|
}
|
|
7104
7170
|
});
|
|
7105
7171
|
|
|
7106
|
-
// src/util/enqueue-by-key.ts
|
|
7107
|
-
function enqueueByKey(key, fn) {
|
|
7108
|
-
const prev = tails.get(key) ?? Promise.resolve();
|
|
7109
|
-
const next = prev.then(fn, fn);
|
|
7110
|
-
tails.set(
|
|
7111
|
-
key,
|
|
7112
|
-
next.then(
|
|
7113
|
-
() => void 0,
|
|
7114
|
-
() => void 0
|
|
7115
|
-
)
|
|
7116
|
-
);
|
|
7117
|
-
return next;
|
|
7118
|
-
}
|
|
7119
|
-
var tails;
|
|
7120
|
-
var init_enqueue_by_key = __esm({
|
|
7121
|
-
"src/util/enqueue-by-key.ts"() {
|
|
7122
|
-
"use strict";
|
|
7123
|
-
tails = /* @__PURE__ */ new Map();
|
|
7124
|
-
}
|
|
7125
|
-
});
|
|
7126
|
-
|
|
7127
|
-
// src/git/repo-git-lock.ts
|
|
7128
|
-
function repoLockKey(repoPath) {
|
|
7129
|
-
const trimmed = repoPath.replace(/\/+$/, "");
|
|
7130
|
-
return `git-worktree:${trimmed || "/"}`;
|
|
7131
|
-
}
|
|
7132
|
-
function withRepoGitLock(repoPath, fn) {
|
|
7133
|
-
return enqueueByKey(repoLockKey(repoPath), fn);
|
|
7134
|
-
}
|
|
7135
|
-
var init_repo_git_lock = __esm({
|
|
7136
|
-
"src/git/repo-git-lock.ts"() {
|
|
7137
|
-
"use strict";
|
|
7138
|
-
init_enqueue_by_key();
|
|
7139
|
-
}
|
|
7140
|
-
});
|
|
7141
|
-
|
|
7142
7172
|
// src/git/pr-gates.ts
|
|
7143
7173
|
function prIsInMergeQueue(gate) {
|
|
7144
7174
|
if (gate.isInMergeQueue) return true;
|
|
@@ -7271,6 +7301,381 @@ var init_pr_gates = __esm({
|
|
|
7271
7301
|
}
|
|
7272
7302
|
});
|
|
7273
7303
|
|
|
7304
|
+
// src/git/cursor-pr.ts
|
|
7305
|
+
function repoArgs(slug) {
|
|
7306
|
+
return ["-R", slug];
|
|
7307
|
+
}
|
|
7308
|
+
function cursorHeadBranch(head, slug) {
|
|
7309
|
+
const branch = head.trim().replace(/^refs\/heads\//, "");
|
|
7310
|
+
const owner = slug.split("/")[0];
|
|
7311
|
+
if (owner && branch.startsWith(`${owner}:`)) return branch.slice(owner.length + 1);
|
|
7312
|
+
return branch;
|
|
7313
|
+
}
|
|
7314
|
+
function cursorMergeFields(value) {
|
|
7315
|
+
if (typeof value === "string" && value.trim()) {
|
|
7316
|
+
const upper = value.trim().toUpperCase();
|
|
7317
|
+
return { mergeable: upper, mergeStateStatus: upper };
|
|
7318
|
+
}
|
|
7319
|
+
if (value && typeof value === "object") {
|
|
7320
|
+
const row = value;
|
|
7321
|
+
const conflictState = String(row.mergeConflictState ?? "").trim().toUpperCase();
|
|
7322
|
+
if ("hasMergeConflicts" in row || conflictState) {
|
|
7323
|
+
if (conflictState.includes("PENDING")) {
|
|
7324
|
+
return { mergeable: "UNKNOWN", mergeStateStatus: "UNKNOWN" };
|
|
7325
|
+
}
|
|
7326
|
+
const conflicting = row.hasMergeConflicts === true || conflictState.includes("CONFLICT") && !conflictState.includes("CLEAN");
|
|
7327
|
+
if (conflicting) return { mergeable: "CONFLICTING", mergeStateStatus: "DIRTY" };
|
|
7328
|
+
if (row.hasMergeConflicts === false || conflictState.includes("CLEAN")) {
|
|
7329
|
+
return { mergeable: "MERGEABLE", mergeStateStatus: "CLEAN" };
|
|
7330
|
+
}
|
|
7331
|
+
}
|
|
7332
|
+
const verdict = String(row.verdict ?? row.verdictKind ?? "").trim().toUpperCase();
|
|
7333
|
+
if (verdict === "BLOCKED") return { mergeable: null, mergeStateStatus: "BLOCKED" };
|
|
7334
|
+
if (verdict === "MERGEABLE") return { mergeable: "MERGEABLE", mergeStateStatus: "CLEAN" };
|
|
7335
|
+
const status = String(row.status ?? row.state ?? row.mergeable ?? "").trim().toUpperCase();
|
|
7336
|
+
return { mergeable: status || null, mergeStateStatus: status || null };
|
|
7337
|
+
}
|
|
7338
|
+
return { mergeable: null, mergeStateStatus: null };
|
|
7339
|
+
}
|
|
7340
|
+
function cursorReviewDecision(value) {
|
|
7341
|
+
const raw = typeof value === "string" ? value.trim().toUpperCase().replace(/[\s-]+/g, "_") : "";
|
|
7342
|
+
if (!raw) return null;
|
|
7343
|
+
if (raw === "REQUIRED" || raw === "REVIEW_REQUIRED") return "REVIEW_REQUIRED";
|
|
7344
|
+
if (raw === "CHANGES_REQUESTED") return "CHANGES_REQUESTED";
|
|
7345
|
+
if (raw === "APPROVED") return "APPROVED";
|
|
7346
|
+
return raw;
|
|
7347
|
+
}
|
|
7348
|
+
function cursorViewToPrMeta(view) {
|
|
7349
|
+
const merge = cursorMergeFields(view.mergeability ?? view.mergeable);
|
|
7350
|
+
const author = view.author ?? {};
|
|
7351
|
+
return {
|
|
7352
|
+
number: Number(view.number),
|
|
7353
|
+
title: String(view.title ?? ""),
|
|
7354
|
+
url: String(view.url ?? ""),
|
|
7355
|
+
state: String(view.state ?? ""),
|
|
7356
|
+
isDraft: Boolean(view.isDraft),
|
|
7357
|
+
reviewDecision: cursorReviewDecision(view.reviewDecision),
|
|
7358
|
+
baseRefName: String(view.baseRefName ?? ""),
|
|
7359
|
+
headRefName: String(view.headRefName ?? ""),
|
|
7360
|
+
isInMergeQueue: false,
|
|
7361
|
+
mergeable: merge.mergeable,
|
|
7362
|
+
mergeStateStatus: merge.mergeStateStatus,
|
|
7363
|
+
authorLogin: author.login?.trim() || null,
|
|
7364
|
+
reviewerLogins: [],
|
|
7365
|
+
labels: labelNames(view.labels)
|
|
7366
|
+
};
|
|
7367
|
+
}
|
|
7368
|
+
function loginOf(author) {
|
|
7369
|
+
if (!author || typeof author !== "object") return "";
|
|
7370
|
+
const row = author;
|
|
7371
|
+
for (const key of ["login", "handle", "email", "displayName", "name", "id"]) {
|
|
7372
|
+
const value = row[key];
|
|
7373
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
7374
|
+
}
|
|
7375
|
+
return "";
|
|
7376
|
+
}
|
|
7377
|
+
function upperState(value, isDraft) {
|
|
7378
|
+
const raw = typeof value === "string" ? value.trim().toUpperCase() : "";
|
|
7379
|
+
if (raw === "DRAFT") return "OPEN";
|
|
7380
|
+
if (raw === "OPEN" || raw === "CLOSED" || raw === "MERGED") return raw;
|
|
7381
|
+
return isDraft ? "OPEN" : raw;
|
|
7382
|
+
}
|
|
7383
|
+
function normalizeOriginPr(raw) {
|
|
7384
|
+
const isDraft = raw.isDraft === true || String(raw.status ?? raw.state ?? "").toLowerCase() === "draft";
|
|
7385
|
+
const login = loginOf(raw.author);
|
|
7386
|
+
const author = raw.author && typeof raw.author === "object" ? { ...raw.author, login: login || "unknown" } : login ? { login } : { login: "unknown" };
|
|
7387
|
+
return {
|
|
7388
|
+
...raw,
|
|
7389
|
+
isDraft,
|
|
7390
|
+
state: upperState(raw.state ?? raw.status, isDraft),
|
|
7391
|
+
author,
|
|
7392
|
+
body: raw.body ?? raw.description ?? "",
|
|
7393
|
+
isCrossRepository: false,
|
|
7394
|
+
reviewDecision: cursorReviewDecision(raw.reviewDecision),
|
|
7395
|
+
reviewRequests: originReviewRequests(raw.reviewRequests)
|
|
7396
|
+
};
|
|
7397
|
+
}
|
|
7398
|
+
function originReviewRequests(raw) {
|
|
7399
|
+
if (!Array.isArray(raw)) return raw;
|
|
7400
|
+
return raw.map(originReviewer).filter((row) => row != null);
|
|
7401
|
+
}
|
|
7402
|
+
function originReviewer(raw) {
|
|
7403
|
+
if (typeof raw === "string" && raw.trim()) return { login: raw.trim() };
|
|
7404
|
+
if (!raw || typeof raw !== "object") return null;
|
|
7405
|
+
const row = raw;
|
|
7406
|
+
if (typeof row.login === "string" && row.login.trim()) return row;
|
|
7407
|
+
const kind = String(row.kind ?? row.__typename ?? "").toLowerCase();
|
|
7408
|
+
const slug = typeof row.slug === "string" ? row.slug.trim() : "";
|
|
7409
|
+
const name = typeof row.name === "string" ? row.name.trim() : "";
|
|
7410
|
+
const id = typeof row.id === "string" ? row.id.trim() : "";
|
|
7411
|
+
if (kind.includes("team") || kind.includes("group") || slug.includes("/") && !row.login) {
|
|
7412
|
+
return { __typename: "Team", slug: slug || name, name };
|
|
7413
|
+
}
|
|
7414
|
+
if (kind.includes("service") || kind.includes("bot")) {
|
|
7415
|
+
const login2 = name || id || "cursor-bot";
|
|
7416
|
+
return { __typename: "Bot", login: login2.toLowerCase().endsWith("[bot]") ? login2 : `${login2}[bot]` };
|
|
7417
|
+
}
|
|
7418
|
+
const login = name || id;
|
|
7419
|
+
return login ? { login } : null;
|
|
7420
|
+
}
|
|
7421
|
+
function originChecksToRuns(stdout) {
|
|
7422
|
+
let parsed;
|
|
7423
|
+
try {
|
|
7424
|
+
parsed = JSON.parse(stdout);
|
|
7425
|
+
} catch {
|
|
7426
|
+
return [];
|
|
7427
|
+
}
|
|
7428
|
+
const rows = Array.isArray(parsed) ? parsed : [];
|
|
7429
|
+
return rows.map((row) => {
|
|
7430
|
+
const item = row ?? {};
|
|
7431
|
+
const conclusion = typeof item.conclusion === "string" ? item.conclusion.toLowerCase() : "";
|
|
7432
|
+
const status = typeof item.status === "string" ? item.status.toLowerCase() : "";
|
|
7433
|
+
const bucket = conclusion === "success" || conclusion === "neutral" ? "pass" : conclusion === "failure" || conclusion === "timed_out" || conclusion === "action_required" ? "fail" : conclusion === "cancelled" ? "cancel" : conclusion === "skipped" ? "skipping" : "pending";
|
|
7434
|
+
const state = conclusion || status || "pending";
|
|
7435
|
+
const link = typeof item.link === "string" ? item.link : typeof item.detailsUrl === "string" ? item.detailsUrl : null;
|
|
7436
|
+
const description = typeof item.summary === "string" ? item.summary : typeof item.description === "string" ? item.description : null;
|
|
7437
|
+
return {
|
|
7438
|
+
name: String(item.name ?? "check"),
|
|
7439
|
+
state,
|
|
7440
|
+
bucket,
|
|
7441
|
+
startedAt: typeof item.startedAt === "string" ? item.startedAt : null,
|
|
7442
|
+
completedAt: typeof item.completedAt === "string" ? item.completedAt : null,
|
|
7443
|
+
link,
|
|
7444
|
+
description,
|
|
7445
|
+
workflow: typeof item.group === "string" ? item.group : null,
|
|
7446
|
+
kind: "ci"
|
|
7447
|
+
};
|
|
7448
|
+
});
|
|
7449
|
+
}
|
|
7450
|
+
function listedHasLabels(pr, labels) {
|
|
7451
|
+
if (labels.length === 0) return true;
|
|
7452
|
+
const have = new Set((pr.labels ?? []).map((name) => name.toLowerCase()));
|
|
7453
|
+
return labels.every((label) => have.has(label.toLowerCase()));
|
|
7454
|
+
}
|
|
7455
|
+
async function listCursorPrs(repoPath, slug, opts, head) {
|
|
7456
|
+
const resolved = resolveListPrsOptions(opts);
|
|
7457
|
+
const limit = Math.max(1, Math.min(250, resolved.limit ?? 200));
|
|
7458
|
+
const fetchLimit = prListFetchLimit(limit, resolved.reviewer);
|
|
7459
|
+
const useSearch = resolved.labels.length > 0 || Boolean(resolved.reviewer) || Boolean(resolved.query);
|
|
7460
|
+
const state = resolved.draft === true ? "draft" : resolved.state;
|
|
7461
|
+
const args = [
|
|
7462
|
+
"pr",
|
|
7463
|
+
"list",
|
|
7464
|
+
...repoArgs(slug),
|
|
7465
|
+
"--state",
|
|
7466
|
+
state,
|
|
7467
|
+
"--limit",
|
|
7468
|
+
String(fetchLimit),
|
|
7469
|
+
"--json",
|
|
7470
|
+
useSearch ? SEARCH_LIST_FIELDS : PLAIN_LIST_FIELDS
|
|
7471
|
+
];
|
|
7472
|
+
if (head?.trim()) args.push("--head", head.trim());
|
|
7473
|
+
if (useSearch) {
|
|
7474
|
+
for (const label of resolved.labels) args.push("--label", label);
|
|
7475
|
+
if (resolved.query) args.push("--search", resolved.query);
|
|
7476
|
+
else if (resolved.labels.length === 0) {
|
|
7477
|
+
const token = state === "all" ? "is:open OR is:closed OR is:merged" : `is:${state}`;
|
|
7478
|
+
args.push("--search", token);
|
|
7479
|
+
}
|
|
7480
|
+
}
|
|
7481
|
+
const { stdout, exitCode } = await originCli(args, repoPath, { reject: false, timeoutMs: 2e4 });
|
|
7482
|
+
if (exitCode !== 0 || !stdout.trim()) return [];
|
|
7483
|
+
let parsed;
|
|
7484
|
+
try {
|
|
7485
|
+
parsed = JSON.parse(stdout);
|
|
7486
|
+
} catch {
|
|
7487
|
+
return [];
|
|
7488
|
+
}
|
|
7489
|
+
if (!Array.isArray(parsed)) return [];
|
|
7490
|
+
const normalized = parsed.filter((row) => row && typeof row === "object").map((row) => normalizeOriginPr(row));
|
|
7491
|
+
const listed = applyPrListResponse(JSON.stringify(normalized), { ...opts, limit: void 0 });
|
|
7492
|
+
return listed.filter((pr) => listedHasLabels(pr, resolved.labels)).slice(0, limit);
|
|
7493
|
+
}
|
|
7494
|
+
async function viewCursorPr(repoPath, slug, selector, fields) {
|
|
7495
|
+
const { stdout, exitCode } = await originCli(
|
|
7496
|
+
["pr", "view", selector, ...repoArgs(slug), "--json", fields],
|
|
7497
|
+
repoPath,
|
|
7498
|
+
{ reject: false, timeoutMs: 2e4 }
|
|
7499
|
+
);
|
|
7500
|
+
if (exitCode !== 0 || !stdout.trim()) return null;
|
|
7501
|
+
try {
|
|
7502
|
+
const parsed = JSON.parse(stdout);
|
|
7503
|
+
return normalizeOriginPr(parsed);
|
|
7504
|
+
} catch {
|
|
7505
|
+
return null;
|
|
7506
|
+
}
|
|
7507
|
+
}
|
|
7508
|
+
async function createOrUpdateCursorPr(repoPath, slug, opts) {
|
|
7509
|
+
const branch = cursorHeadBranch(opts.head, slug);
|
|
7510
|
+
const existing = await originCli(
|
|
7511
|
+
["pr", "view", branch, ...repoArgs(slug), "--json", "url"],
|
|
7512
|
+
repoPath,
|
|
7513
|
+
{ reject: false, timeoutMs: 2e4 }
|
|
7514
|
+
);
|
|
7515
|
+
if (existing.exitCode === 0 && existing.stdout.trim()) {
|
|
7516
|
+
let url2 = "";
|
|
7517
|
+
try {
|
|
7518
|
+
url2 = String(JSON.parse(existing.stdout).url ?? "");
|
|
7519
|
+
} catch {
|
|
7520
|
+
url2 = "";
|
|
7521
|
+
}
|
|
7522
|
+
if (url2) {
|
|
7523
|
+
await originCli(
|
|
7524
|
+
["pr", "edit", branch, ...repoArgs(slug), "--title", opts.title, "-F", opts.bodyFile],
|
|
7525
|
+
repoPath,
|
|
7526
|
+
{ reject: false, timeoutMs: 2e4 }
|
|
7527
|
+
);
|
|
7528
|
+
return url2;
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
const args = [
|
|
7532
|
+
"pr",
|
|
7533
|
+
"create",
|
|
7534
|
+
...repoArgs(slug),
|
|
7535
|
+
"--title",
|
|
7536
|
+
opts.title,
|
|
7537
|
+
"-F",
|
|
7538
|
+
opts.bodyFile,
|
|
7539
|
+
"--base",
|
|
7540
|
+
opts.base,
|
|
7541
|
+
"--head",
|
|
7542
|
+
branch,
|
|
7543
|
+
"--push"
|
|
7544
|
+
];
|
|
7545
|
+
if (opts.draft) args.push("--draft");
|
|
7546
|
+
const created = await originCli(args, repoPath, { reject: false, timeoutMs: 6e4 });
|
|
7547
|
+
if (created.exitCode !== 0) {
|
|
7548
|
+
throw new Error(created.stderr.trim() || created.stdout.trim() || "origin pr create failed");
|
|
7549
|
+
}
|
|
7550
|
+
const printed = created.stdout.trim().split("\n").find((line) => line.startsWith("http"));
|
|
7551
|
+
if (printed) return printed;
|
|
7552
|
+
const viewed = await viewCursorPr(repoPath, slug, branch, "url");
|
|
7553
|
+
const url = typeof viewed?.url === "string" ? viewed.url.trim() : "";
|
|
7554
|
+
return url || created.stdout.trim();
|
|
7555
|
+
}
|
|
7556
|
+
async function readyCursorPr(repoPath, slug, selector) {
|
|
7557
|
+
const ready = await originCli(["pr", "ready", selector, ...repoArgs(slug)], repoPath, {
|
|
7558
|
+
reject: false,
|
|
7559
|
+
timeoutMs: 2e4
|
|
7560
|
+
});
|
|
7561
|
+
if (ready.exitCode !== 0) {
|
|
7562
|
+
throw new Error(ready.stderr.trim() || ready.stdout.trim() || "Could not mark the Cursor pull request ready");
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
async function mergeCursorPr(repoPath, slug, selector, method) {
|
|
7566
|
+
const args = ["pr", "merge", selector, ...repoArgs(slug)];
|
|
7567
|
+
if (method === "merge") args.push("--merge");
|
|
7568
|
+
else if (method === "squash") args.push("--squash");
|
|
7569
|
+
const merged = await originCli(args, repoPath, {
|
|
7570
|
+
reject: false,
|
|
7571
|
+
timeoutMs: 6e4
|
|
7572
|
+
});
|
|
7573
|
+
if (merged.exitCode !== 0) {
|
|
7574
|
+
throw new Error(merged.stderr.trim() || merged.stdout.trim() || "origin pr merge failed");
|
|
7575
|
+
}
|
|
7576
|
+
}
|
|
7577
|
+
async function fetchCursorPrHead(repoPath, slug, number, localBranch) {
|
|
7578
|
+
const view = await viewCursorPr(repoPath, slug, String(number), "headRefName");
|
|
7579
|
+
const head = typeof view?.headRefName === "string" ? view.headRefName.trim() : "";
|
|
7580
|
+
if (!head) {
|
|
7581
|
+
throw new Error(`Could not resolve the head branch for Cursor pull request #${number}`);
|
|
7582
|
+
}
|
|
7583
|
+
const result = await git(["fetch", "origin", `${head}:${localBranch}`], repoPath, { reject: false });
|
|
7584
|
+
if (result.exitCode !== 0) {
|
|
7585
|
+
throw new Error((result.stderr || result.stdout).trim() || `git fetch origin ${head} failed`);
|
|
7586
|
+
}
|
|
7587
|
+
}
|
|
7588
|
+
async function cursorPrChecks(repoPath, slug, selector) {
|
|
7589
|
+
const { stdout, exitCode, stderr } = await originCli(
|
|
7590
|
+
["pr", "checks", selector, ...repoArgs(slug), "--json", "name,status,conclusion,detailsUrl,startedAt,completedAt,summary,group,link"],
|
|
7591
|
+
repoPath,
|
|
7592
|
+
{ reject: false, timeoutMs: 2e4 }
|
|
7593
|
+
);
|
|
7594
|
+
const err = stderr.trim();
|
|
7595
|
+
let ci;
|
|
7596
|
+
if (!stdout.trim()) {
|
|
7597
|
+
if (/no pull request|not found|no change/i.test(err)) return null;
|
|
7598
|
+
if (exitCode !== 0 && err) throw new Error(err);
|
|
7599
|
+
ci = [];
|
|
7600
|
+
} else {
|
|
7601
|
+
ci = originChecksToRuns(stdout);
|
|
7602
|
+
}
|
|
7603
|
+
const view = await viewCursorPr(
|
|
7604
|
+
repoPath,
|
|
7605
|
+
slug,
|
|
7606
|
+
selector,
|
|
7607
|
+
"url,baseRefName,reviewDecision,mergeability"
|
|
7608
|
+
);
|
|
7609
|
+
if (!view) return ci;
|
|
7610
|
+
return cursorChecksWithGate(ci, cursorViewToPrMeta(view));
|
|
7611
|
+
}
|
|
7612
|
+
function cursorChecksWithGate(ci, meta) {
|
|
7613
|
+
const gate = {
|
|
7614
|
+
mergeable: meta.mergeable,
|
|
7615
|
+
mergeStateStatus: meta.mergeStateStatus,
|
|
7616
|
+
reviewDecision: meta.reviewDecision,
|
|
7617
|
+
baseRefName: meta.baseRefName,
|
|
7618
|
+
url: meta.url,
|
|
7619
|
+
isInMergeQueue: meta.isInMergeQueue
|
|
7620
|
+
};
|
|
7621
|
+
const review = (gate.reviewDecision ?? "").toUpperCase();
|
|
7622
|
+
const hasReviewRow = review === "CHANGES_REQUESTED" || review === "REVIEW_REQUIRED";
|
|
7623
|
+
const gateRows = buildMergeGateChecks(gate, {
|
|
7624
|
+
suppressGenericBlocked: ci.some((check) => check.bucket === "fail") || hasReviewRow
|
|
7625
|
+
});
|
|
7626
|
+
return [...gateRows, ...ci];
|
|
7627
|
+
}
|
|
7628
|
+
var PLAIN_LIST_FIELDS, SEARCH_LIST_FIELDS, VIEW_FIELDS, CURSOR_PR_VIEW_FIELDS;
|
|
7629
|
+
var init_cursor_pr = __esm({
|
|
7630
|
+
"src/git/cursor-pr.ts"() {
|
|
7631
|
+
"use strict";
|
|
7632
|
+
init_run();
|
|
7633
|
+
init_remote_forge();
|
|
7634
|
+
init_list_prs();
|
|
7635
|
+
init_pr_gates();
|
|
7636
|
+
PLAIN_LIST_FIELDS = "number,title,headRefName,url,author,state,isDraft,reviewDecision";
|
|
7637
|
+
SEARCH_LIST_FIELDS = `${PLAIN_LIST_FIELDS},labels,reviewRequests`;
|
|
7638
|
+
VIEW_FIELDS = "number,title,body,url,state,isDraft,reviewDecision,author,createdAt,baseRefName,headRefName,additions,deletions,changedFiles,commits,comments,reviews,labels,mergeability";
|
|
7639
|
+
CURSOR_PR_VIEW_FIELDS = VIEW_FIELDS;
|
|
7640
|
+
}
|
|
7641
|
+
});
|
|
7642
|
+
|
|
7643
|
+
// src/util/enqueue-by-key.ts
|
|
7644
|
+
function enqueueByKey(key, fn) {
|
|
7645
|
+
const prev = tails.get(key) ?? Promise.resolve();
|
|
7646
|
+
const next = prev.then(fn, fn);
|
|
7647
|
+
tails.set(
|
|
7648
|
+
key,
|
|
7649
|
+
next.then(
|
|
7650
|
+
() => void 0,
|
|
7651
|
+
() => void 0
|
|
7652
|
+
)
|
|
7653
|
+
);
|
|
7654
|
+
return next;
|
|
7655
|
+
}
|
|
7656
|
+
var tails;
|
|
7657
|
+
var init_enqueue_by_key = __esm({
|
|
7658
|
+
"src/util/enqueue-by-key.ts"() {
|
|
7659
|
+
"use strict";
|
|
7660
|
+
tails = /* @__PURE__ */ new Map();
|
|
7661
|
+
}
|
|
7662
|
+
});
|
|
7663
|
+
|
|
7664
|
+
// src/git/repo-git-lock.ts
|
|
7665
|
+
function repoLockKey(repoPath) {
|
|
7666
|
+
const trimmed = repoPath.replace(/\/+$/, "");
|
|
7667
|
+
return `git-worktree:${trimmed || "/"}`;
|
|
7668
|
+
}
|
|
7669
|
+
function withRepoGitLock(repoPath, fn) {
|
|
7670
|
+
return enqueueByKey(repoLockKey(repoPath), fn);
|
|
7671
|
+
}
|
|
7672
|
+
var init_repo_git_lock = __esm({
|
|
7673
|
+
"src/git/repo-git-lock.ts"() {
|
|
7674
|
+
"use strict";
|
|
7675
|
+
init_enqueue_by_key();
|
|
7676
|
+
}
|
|
7677
|
+
});
|
|
7678
|
+
|
|
7274
7679
|
// src/git/stack.ts
|
|
7275
7680
|
async function detectGhStack(cwd) {
|
|
7276
7681
|
const now = Date.now();
|
|
@@ -7638,6 +8043,7 @@ function normalizeThread(raw) {
|
|
|
7638
8043
|
quotaContinuedFromId: raw.quotaContinuedFromId ?? null,
|
|
7639
8044
|
accountId: raw.accountId === null ? null : typeof raw.accountId === "string" && raw.accountId.trim() ? raw.accountId.trim() : void 0,
|
|
7640
8045
|
archivedAt: typeof raw.archivedAt === "string" && raw.archivedAt.trim() ? raw.archivedAt.trim() : void 0,
|
|
8046
|
+
ephemeralAutomationId: typeof raw.ephemeralAutomationId === "string" && raw.ephemeralAutomationId.trim() ? raw.ephemeralAutomationId.trim() : void 0,
|
|
7641
8047
|
compactedMessages: Array.isArray(raw.compactedMessages) ? raw.compactedMessages : void 0
|
|
7642
8048
|
};
|
|
7643
8049
|
}
|
|
@@ -7824,6 +8230,7 @@ function updateThread(id, patch) {
|
|
|
7824
8230
|
} else {
|
|
7825
8231
|
delete next.archivedAt;
|
|
7826
8232
|
}
|
|
8233
|
+
if (!next.ephemeralAutomationId?.trim()) delete next.ephemeralAutomationId;
|
|
7827
8234
|
writeThread(next);
|
|
7828
8235
|
return next;
|
|
7829
8236
|
}
|
|
@@ -7947,11 +8354,11 @@ __export(worktree_exports, {
|
|
|
7947
8354
|
connectedPrSelectors: () => connectedPrSelectors,
|
|
7948
8355
|
countUnpushedVsOrigin: () => countUnpushedVsOrigin,
|
|
7949
8356
|
createExistingBranchWorktree: () => createExistingBranchWorktree,
|
|
7950
|
-
createOrUpdatePr: () => createOrUpdatePr,
|
|
7951
8357
|
createThreadWorktree: () => createThreadWorktree,
|
|
7952
8358
|
currentBranch: () => currentBranch,
|
|
7953
8359
|
detectLocalMergeConflicts: () => detectLocalMergeConflicts,
|
|
7954
8360
|
ensureGhPreferOrigin: () => ensureGhPreferOrigin,
|
|
8361
|
+
entireUsesTrails: () => entireUsesTrails,
|
|
7955
8362
|
fastForwardMainCheckoutIfSafe: () => fastForwardMainCheckoutIfSafe,
|
|
7956
8363
|
fetchOriginForWorktree: () => fetchOriginForWorktree,
|
|
7957
8364
|
fetchPrHead: () => fetchPrHead,
|
|
@@ -7961,7 +8368,6 @@ __export(worktree_exports, {
|
|
|
7961
8368
|
getPrForHeadBranch: () => getPrForHeadBranch,
|
|
7962
8369
|
getPrMeta: () => getPrMeta,
|
|
7963
8370
|
ghHeadRef: () => ghHeadRef,
|
|
7964
|
-
ghPrCreateAssigneeArgs: () => ghPrCreateAssigneeArgs,
|
|
7965
8371
|
ghRepoSelectArgs: () => ghRepoSelectArgs,
|
|
7966
8372
|
githubAgentGitEnv: () => githubAgentGitEnv,
|
|
7967
8373
|
isDefaultishSourceRef: () => isDefaultishSourceRef,
|
|
@@ -7985,8 +8391,10 @@ __export(worktree_exports, {
|
|
|
7985
8391
|
resolveDefaultBranch: () => resolveDefaultBranch,
|
|
7986
8392
|
resolveDiffBaseRef: () => resolveDiffBaseRef,
|
|
7987
8393
|
resolveGithubRepoSlug: () => resolveGithubRepoSlug,
|
|
8394
|
+
resolveOriginRemote: () => resolveOriginRemote,
|
|
7988
8395
|
resolvePrSelector: () => resolvePrSelector,
|
|
7989
8396
|
resolvePrSelectors: () => resolvePrSelectors,
|
|
8397
|
+
resolveProjectRemoteTag: () => resolveProjectRemoteTag,
|
|
7990
8398
|
resolveRepoRoot: () => resolveRepoRoot,
|
|
7991
8399
|
resolveWorktreeStartPoint: () => resolveWorktreeStartPoint,
|
|
7992
8400
|
slugify: () => slugify,
|
|
@@ -8000,31 +8408,6 @@ __export(worktree_exports, {
|
|
|
8000
8408
|
worktreeNameFromPath: () => worktreeNameFromPath,
|
|
8001
8409
|
worktreeSlugForTicket: () => worktreeSlugForTicket
|
|
8002
8410
|
});
|
|
8003
|
-
async function lookupGithubGraphqlReset(cwd) {
|
|
8004
|
-
const result = await gh(["api", "rate_limit"], cwd, { reject: false });
|
|
8005
|
-
if (result.exitCode !== 0 || !result.stdout.trim()) return void 0;
|
|
8006
|
-
try {
|
|
8007
|
-
const data = JSON.parse(result.stdout);
|
|
8008
|
-
const reset = data.resources?.graphql?.reset;
|
|
8009
|
-
return typeof reset === "number" ? reset : void 0;
|
|
8010
|
-
} catch {
|
|
8011
|
-
return void 0;
|
|
8012
|
-
}
|
|
8013
|
-
}
|
|
8014
|
-
async function formatPrCreateFailure(raw, cwd, ctx) {
|
|
8015
|
-
if (!isGhRateLimitError(raw)) {
|
|
8016
|
-
return formatGhLandError(raw, {
|
|
8017
|
-
targetedRepo: ctx?.slug,
|
|
8018
|
-
headRef: ctx?.head
|
|
8019
|
-
});
|
|
8020
|
-
}
|
|
8021
|
-
const resetAt = await lookupGithubGraphqlReset(cwd);
|
|
8022
|
-
return formatGhLandError(raw, {
|
|
8023
|
-
resetAt,
|
|
8024
|
-
targetedRepo: ctx?.slug,
|
|
8025
|
-
headRef: ctx?.head
|
|
8026
|
-
});
|
|
8027
|
-
}
|
|
8028
8411
|
function slugify(input) {
|
|
8029
8412
|
return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
8030
8413
|
}
|
|
@@ -8047,25 +8430,39 @@ function canonicalizeRepoPath(path) {
|
|
|
8047
8430
|
}
|
|
8048
8431
|
}
|
|
8049
8432
|
function parseGithubSlugFromRemoteUrl(url) {
|
|
8050
|
-
const
|
|
8051
|
-
|
|
8052
|
-
const github = trimmed.match(/github\.com[:/]([^/]+)\/([^/]+)$/i);
|
|
8053
|
-
if (github?.[1] && github[2]) return `${github[1]}/${github[2]}`;
|
|
8054
|
-
const sshAlias = trimmed.match(/^git@[^:]+:([^/]+)\/([^/]+)$/i);
|
|
8055
|
-
if (sshAlias?.[1] && sshAlias[2]) return `${sshAlias[1]}/${sshAlias[2]}`;
|
|
8056
|
-
return null;
|
|
8433
|
+
const parsed = parseRemoteRepo(url);
|
|
8434
|
+
return parsed?.forge === "github" ? parsed.slug : null;
|
|
8057
8435
|
}
|
|
8058
|
-
async function
|
|
8436
|
+
async function remoteUrl(repoPath, remote) {
|
|
8059
8437
|
const result = await git(["remote", "get-url", remote], repoPath, {
|
|
8060
8438
|
reject: false,
|
|
8061
8439
|
timeoutMs: 5e3
|
|
8062
8440
|
});
|
|
8063
8441
|
if (result.exitCode !== 0 || !result.stdout.trim()) return null;
|
|
8064
|
-
return
|
|
8442
|
+
return result.stdout.trim();
|
|
8443
|
+
}
|
|
8444
|
+
async function slugFromGitRemote(repoPath, remote) {
|
|
8445
|
+
const url = await remoteUrl(repoPath, remote);
|
|
8446
|
+
if (!url) return null;
|
|
8447
|
+
return parseGithubSlugFromRemoteUrl(url);
|
|
8448
|
+
}
|
|
8449
|
+
async function resolveOriginRemote(repoPath) {
|
|
8450
|
+
const url = await remoteUrl(repoPath, "origin");
|
|
8451
|
+
if (!url) return null;
|
|
8452
|
+
return parseRemoteRepo(url);
|
|
8453
|
+
}
|
|
8454
|
+
async function resolveProjectRemoteTag(repoPath) {
|
|
8455
|
+
const origin = await resolveOriginRemote(repoPath);
|
|
8456
|
+
if (origin) return formatRemoteTag(origin);
|
|
8457
|
+
const slug = await resolveGithubRepoSlug(repoPath);
|
|
8458
|
+
return slug ? `github:${slug}` : null;
|
|
8065
8459
|
}
|
|
8066
8460
|
async function resolveGithubRepoSlug(repoPath) {
|
|
8067
|
-
const
|
|
8068
|
-
if (
|
|
8461
|
+
const originUrl = await remoteUrl(repoPath, "origin");
|
|
8462
|
+
if (originUrl) {
|
|
8463
|
+
const parsed = parseRemoteRepo(originUrl);
|
|
8464
|
+
return parsed?.forge === "github" ? parsed.slug : null;
|
|
8465
|
+
}
|
|
8069
8466
|
const hasAlt = Boolean(await slugFromGitRemote(repoPath, "upstream")) || Boolean(await slugFromGitRemote(repoPath, "github"));
|
|
8070
8467
|
if (!hasAlt) {
|
|
8071
8468
|
const viaGh = await gh(
|
|
@@ -8111,6 +8508,8 @@ ${view.stderr}`;
|
|
|
8111
8508
|
});
|
|
8112
8509
|
}
|
|
8113
8510
|
async function originGhRepoEnv(cwd, opts) {
|
|
8511
|
+
const origin = await resolveOriginRemote(cwd);
|
|
8512
|
+
if (origin?.forge === "cursor") return { ORIGIN_REPO: origin.slug };
|
|
8114
8513
|
await ensureGhPreferOrigin(cwd);
|
|
8115
8514
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
8116
8515
|
const mode = opts?.mode ?? getGithubGitAuthMode();
|
|
@@ -8225,7 +8624,16 @@ async function listBranches(repoPath, opts) {
|
|
|
8225
8624
|
}
|
|
8226
8625
|
return sorted.filter((b) => keep.has(b.name));
|
|
8227
8626
|
}
|
|
8627
|
+
function entireUsesTrails() {
|
|
8628
|
+
return new Error(
|
|
8629
|
+
"This checkout is on Entire. Trails replace pull requests \u2014 use `entire trail` (Settings \u2192 Git \u2192 Entire), not `gh`."
|
|
8630
|
+
);
|
|
8631
|
+
}
|
|
8228
8632
|
async function listPrs(repoPath, opts) {
|
|
8633
|
+
const origin = await resolveOriginRemote(repoPath);
|
|
8634
|
+
if (origin?.forge === "entire") return [];
|
|
8635
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
8636
|
+
if (cursorSlug) return listCursorPrs(repoPath, cursorSlug, opts);
|
|
8229
8637
|
const slug = await resolveGithubRepoSlug(repoPath);
|
|
8230
8638
|
const resolved = resolveListPrsOptions(opts);
|
|
8231
8639
|
const limit = resolved.limit ?? 200;
|
|
@@ -8262,6 +8670,18 @@ function prInfoFromGhView(view) {
|
|
|
8262
8670
|
};
|
|
8263
8671
|
}
|
|
8264
8672
|
async function getPr(repoPath, number) {
|
|
8673
|
+
const origin = await resolveOriginRemote(repoPath);
|
|
8674
|
+
if (origin?.forge === "entire") return null;
|
|
8675
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
8676
|
+
if (cursorSlug) {
|
|
8677
|
+
const view = await viewCursorPr(
|
|
8678
|
+
repoPath,
|
|
8679
|
+
cursorSlug,
|
|
8680
|
+
String(number),
|
|
8681
|
+
"number,title,headRefName,url,author,isDraft,state"
|
|
8682
|
+
);
|
|
8683
|
+
return view ? prInfoFromGhView(view) : null;
|
|
8684
|
+
}
|
|
8265
8685
|
const slug = await resolveGithubRepoSlug(repoPath);
|
|
8266
8686
|
const args = [
|
|
8267
8687
|
"pr",
|
|
@@ -8323,6 +8743,13 @@ function connectedPrSelectors(thread, currentHeadPrUrl) {
|
|
|
8323
8743
|
async function getPrForHeadBranch(repoPath, branch) {
|
|
8324
8744
|
const head = normalizeGitBranchName(branch);
|
|
8325
8745
|
if (!head || isDefaultishSourceRef(head)) return null;
|
|
8746
|
+
const origin = await resolveOriginRemote(repoPath);
|
|
8747
|
+
if (origin?.forge === "entire") return null;
|
|
8748
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
8749
|
+
if (cursorSlug) {
|
|
8750
|
+
const rows = await listCursorPrs(repoPath, cursorSlug, { state: "open", limit: 5 }, head);
|
|
8751
|
+
return rows[0] ?? null;
|
|
8752
|
+
}
|
|
8326
8753
|
const slug = await resolveGithubRepoSlug(repoPath);
|
|
8327
8754
|
const listHead = slug ? ghHeadRef(slug, head) : head;
|
|
8328
8755
|
const listArgs = [
|
|
@@ -8487,6 +8914,10 @@ async function probeLocalMergeGate(cwd, gate) {
|
|
|
8487
8914
|
return { gate, files: [] };
|
|
8488
8915
|
}
|
|
8489
8916
|
async function getPrChecks(cwd, selector) {
|
|
8917
|
+
const origin = await resolveOriginRemote(cwd);
|
|
8918
|
+
if (origin?.forge === "entire") return null;
|
|
8919
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
8920
|
+
if (cursorSlug) return cursorPrChecks(cwd, cursorSlug, selector);
|
|
8490
8921
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
8491
8922
|
const args = [
|
|
8492
8923
|
"pr",
|
|
@@ -8542,6 +8973,18 @@ async function getPrChecks(cwd, selector) {
|
|
|
8542
8973
|
return [...gateRows, ...ciChecks];
|
|
8543
8974
|
}
|
|
8544
8975
|
async function getPrMeta(cwd, selector) {
|
|
8976
|
+
const origin = await resolveOriginRemote(cwd);
|
|
8977
|
+
if (origin?.forge === "entire") return null;
|
|
8978
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
8979
|
+
if (cursorSlug) {
|
|
8980
|
+
const view = await viewCursorPr(
|
|
8981
|
+
cwd,
|
|
8982
|
+
cursorSlug,
|
|
8983
|
+
selector,
|
|
8984
|
+
"number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,author,labels,mergeability"
|
|
8985
|
+
);
|
|
8986
|
+
return view ? cursorViewToPrMeta(view) : null;
|
|
8987
|
+
}
|
|
8545
8988
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
8546
8989
|
const viewArgs = [
|
|
8547
8990
|
"pr",
|
|
@@ -8670,6 +9113,13 @@ function parsePrDetailsView(view) {
|
|
|
8670
9113
|
};
|
|
8671
9114
|
}
|
|
8672
9115
|
async function getPrDetails(cwd, selector) {
|
|
9116
|
+
const origin = await resolveOriginRemote(cwd);
|
|
9117
|
+
if (origin?.forge === "entire") return null;
|
|
9118
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
9119
|
+
if (cursorSlug) {
|
|
9120
|
+
const view2 = await viewCursorPr(cwd, cursorSlug, selector, CURSOR_PR_VIEW_FIELDS);
|
|
9121
|
+
return view2 ? parsePrDetailsView(view2) : null;
|
|
9122
|
+
}
|
|
8673
9123
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
8674
9124
|
const viewArgs = ["pr", "view", selector, "--json", PR_DETAILS_JSON_FIELDS];
|
|
8675
9125
|
if (slug) viewArgs.push("--repo", slug);
|
|
@@ -8689,6 +9139,13 @@ async function getPrDetails(cwd, selector) {
|
|
|
8689
9139
|
return parsePrDetailsView(view);
|
|
8690
9140
|
}
|
|
8691
9141
|
async function fetchPrHead(repoPath, number, localBranch) {
|
|
9142
|
+
const origin = await resolveOriginRemote(repoPath);
|
|
9143
|
+
if (origin?.forge === "entire") throw entireUsesTrails();
|
|
9144
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
9145
|
+
if (cursorSlug) {
|
|
9146
|
+
await fetchCursorPrHead(repoPath, cursorSlug, number, localBranch);
|
|
9147
|
+
return;
|
|
9148
|
+
}
|
|
8692
9149
|
const slug = await resolveGithubRepoSlug(repoPath);
|
|
8693
9150
|
const refspec = `+pull/${number}/head:${localBranch}`;
|
|
8694
9151
|
const errors = [];
|
|
@@ -8831,10 +9288,10 @@ async function originRemoteUrl(cwd) {
|
|
|
8831
9288
|
const result = await git(["remote", "get-url", "origin"], cwd, { reject: false });
|
|
8832
9289
|
return result.exitCode === 0 ? result.stdout.trim() : "";
|
|
8833
9290
|
}
|
|
8834
|
-
function githubHttpsRetryEnv(
|
|
9291
|
+
function githubHttpsRetryEnv(remoteUrl2) {
|
|
8835
9292
|
return {
|
|
8836
9293
|
GIT_TERMINAL_PROMPT: "0",
|
|
8837
|
-
...githubAgentGitEnv(void 0, { remoteUrl })
|
|
9294
|
+
...githubAgentGitEnv(void 0, { remoteUrl: remoteUrl2 })
|
|
8838
9295
|
};
|
|
8839
9296
|
}
|
|
8840
9297
|
async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
|
|
@@ -8848,11 +9305,11 @@ async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
|
|
|
8848
9305
|
}
|
|
8849
9306
|
const token = await resolveGithubAgentToken(mode, repoPath);
|
|
8850
9307
|
if (!token) return false;
|
|
8851
|
-
const
|
|
9308
|
+
const remoteUrl2 = await originRemoteUrl(repoPath);
|
|
8852
9309
|
const tryHttps = async (header) => git(args, repoPath, {
|
|
8853
9310
|
reject: false,
|
|
8854
9311
|
timeoutMs,
|
|
8855
|
-
env: githubHttpsRetryEnv(
|
|
9312
|
+
env: githubHttpsRetryEnv(remoteUrl2),
|
|
8856
9313
|
config: {
|
|
8857
9314
|
"http.extraHeader": header
|
|
8858
9315
|
}
|
|
@@ -9201,10 +9658,10 @@ async function pushBranch(worktreePath, branchName) {
|
|
|
9201
9658
|
sshErr || `git push origin ${branchName} failed` + (!token ? " (SSH remote and gh auth token unavailable \u2014 unlock ssh-agent or run: gh auth login)" : "")
|
|
9202
9659
|
);
|
|
9203
9660
|
}
|
|
9204
|
-
const
|
|
9661
|
+
const remoteUrl2 = await originRemoteUrl(worktreePath);
|
|
9205
9662
|
const tryHttps = async (header) => git(["push", "-u", "origin", branchName], worktreePath, {
|
|
9206
9663
|
reject: false,
|
|
9207
|
-
env: githubHttpsRetryEnv(
|
|
9664
|
+
env: githubHttpsRetryEnv(remoteUrl2),
|
|
9208
9665
|
config: {
|
|
9209
9666
|
"http.extraHeader": header
|
|
9210
9667
|
}
|
|
@@ -9217,6 +9674,40 @@ async function pushBranch(worktreePath, branchName) {
|
|
|
9217
9674
|
const httpsErr = (basicPush.stderr || bearer.stderr || bearer.stdout).trim();
|
|
9218
9675
|
throw new Error(httpsErr || sshErr || `git push origin ${branchName} failed`);
|
|
9219
9676
|
}
|
|
9677
|
+
async function markCursorPrReady(cwd, slug, selector) {
|
|
9678
|
+
const view = await viewCursorPr(cwd, slug, selector, "url,state,isDraft");
|
|
9679
|
+
if (!view) throw new Error("Could not load the Cursor pull request");
|
|
9680
|
+
const url = String(view.url ?? "");
|
|
9681
|
+
const state = String(view.state ?? "").toUpperCase();
|
|
9682
|
+
const isDraft = Boolean(view.isDraft);
|
|
9683
|
+
if (state === "MERGED" || state === "CLOSED") {
|
|
9684
|
+
return { url, state, isDraft: false };
|
|
9685
|
+
}
|
|
9686
|
+
if (isDraft) {
|
|
9687
|
+
if (await isDirty(cwd)) {
|
|
9688
|
+
throw new Error(
|
|
9689
|
+
"Commit and push local work before marking the pull request ready for review."
|
|
9690
|
+
);
|
|
9691
|
+
}
|
|
9692
|
+
const unpushed = await countUnpushedVsOrigin(cwd);
|
|
9693
|
+
if (unpushed > 0) {
|
|
9694
|
+
throw new Error(
|
|
9695
|
+
"Push this branch to origin before marking the pull request ready for review."
|
|
9696
|
+
);
|
|
9697
|
+
}
|
|
9698
|
+
await readyCursorPr(cwd, slug, selector);
|
|
9699
|
+
}
|
|
9700
|
+
return { url, state: state || "OPEN", isDraft: false };
|
|
9701
|
+
}
|
|
9702
|
+
async function mergeCursorPullRequest(cwd, slug, selector, method) {
|
|
9703
|
+
const view = await viewCursorPr(cwd, slug, selector, "url,state,isDraft");
|
|
9704
|
+
if (!view) throw new Error("Could not load the Cursor pull request");
|
|
9705
|
+
const url = String(view.url ?? "");
|
|
9706
|
+
if (String(view.state ?? "").toUpperCase() === "MERGED") return { url, state: "MERGED" };
|
|
9707
|
+
if (view.isDraft) await readyCursorPr(cwd, slug, selector);
|
|
9708
|
+
await mergeCursorPr(cwd, slug, selector, method);
|
|
9709
|
+
return { url, state: "MERGED" };
|
|
9710
|
+
}
|
|
9220
9711
|
async function runPrReady(cwd, selector, slug) {
|
|
9221
9712
|
const readyArgs = ["pr", "ready", selector];
|
|
9222
9713
|
if (slug) readyArgs.push("--repo", slug);
|
|
@@ -9228,6 +9719,10 @@ async function runPrReady(cwd, selector, slug) {
|
|
|
9228
9719
|
}
|
|
9229
9720
|
}
|
|
9230
9721
|
async function markPrReady(cwd, selector) {
|
|
9722
|
+
const origin = await resolveOriginRemote(cwd);
|
|
9723
|
+
if (origin?.forge === "entire") throw entireUsesTrails();
|
|
9724
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
9725
|
+
if (cursorSlug) return markCursorPrReady(cwd, cursorSlug, selector);
|
|
9231
9726
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
9232
9727
|
const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft"];
|
|
9233
9728
|
if (slug) viewArgs.push("--repo", slug);
|
|
@@ -9266,6 +9761,10 @@ async function markPrReady(cwd, selector) {
|
|
|
9266
9761
|
return { url, state: state || "OPEN", isDraft: false };
|
|
9267
9762
|
}
|
|
9268
9763
|
async function mergePr(cwd, selector, opts) {
|
|
9764
|
+
const origin = await resolveOriginRemote(cwd);
|
|
9765
|
+
if (origin?.forge === "entire") throw entireUsesTrails();
|
|
9766
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
9767
|
+
if (cursorSlug) return mergeCursorPullRequest(cwd, cursorSlug, selector, opts?.method ?? "squash");
|
|
9269
9768
|
const slug = await resolveGithubRepoSlug(cwd);
|
|
9270
9769
|
const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft,number"];
|
|
9271
9770
|
if (slug) viewArgs.push("--repo", slug);
|
|
@@ -9324,128 +9823,16 @@ async function mergePr(cwd, selector, opts) {
|
|
|
9324
9823
|
}
|
|
9325
9824
|
return { url, state: "MERGED" };
|
|
9326
9825
|
}
|
|
9327
|
-
function ghPrCreateAssigneeArgs(assignee) {
|
|
9328
|
-
const raw = assignee?.trim();
|
|
9329
|
-
if (raw && /^(none|unassigned)$/i.test(raw)) return [];
|
|
9330
|
-
return ["--assignee", raw || "@me"];
|
|
9331
|
-
}
|
|
9332
|
-
async function createOrUpdatePr(worktreePath, opts) {
|
|
9333
|
-
await ensureGhPreferOrigin(worktreePath);
|
|
9334
|
-
const slug = await resolveGithubRepoSlug(worktreePath);
|
|
9335
|
-
if (!slug) {
|
|
9336
|
-
throw new Error(
|
|
9337
|
-
"Could not resolve the origin GitHub repo (owner/name) for this worktree. Check that `git remote get-url origin` points at github.com."
|
|
9338
|
-
);
|
|
9339
|
-
}
|
|
9340
|
-
const repoArgs = ghRepoSelectArgs(slug);
|
|
9341
|
-
const headRef = ghHeadRef(slug, opts.head);
|
|
9342
|
-
const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
|
|
9343
|
-
const bodyText = clampGithubPrBody(opts.body ?? opts.title);
|
|
9344
|
-
const bodyFile = (0, import_node_path21.join)(
|
|
9345
|
-
(0, import_node_os7.tmpdir)(),
|
|
9346
|
-
`sideboard-pr-body-${process.pid}-${(0, import_node_crypto4.randomBytes)(6).toString("hex")}.md`
|
|
9347
|
-
);
|
|
9348
|
-
(0, import_node_fs18.writeFileSync)(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
|
|
9349
|
-
`, "utf8");
|
|
9350
|
-
const bodyArgs = ["--body-file", bodyFile];
|
|
9351
|
-
try {
|
|
9352
|
-
return await createOrUpdatePrWithBodyFile(worktreePath, {
|
|
9353
|
-
...opts,
|
|
9354
|
-
slug,
|
|
9355
|
-
repoArgs,
|
|
9356
|
-
headRef,
|
|
9357
|
-
branchOnly,
|
|
9358
|
-
bodyArgs
|
|
9359
|
-
});
|
|
9360
|
-
} finally {
|
|
9361
|
-
try {
|
|
9362
|
-
(0, import_node_fs18.unlinkSync)(bodyFile);
|
|
9363
|
-
} catch {
|
|
9364
|
-
}
|
|
9365
|
-
}
|
|
9366
|
-
}
|
|
9367
|
-
async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
|
|
9368
|
-
const { slug, repoArgs, headRef, branchOnly, bodyArgs } = opts;
|
|
9369
|
-
const existing = await gh(
|
|
9370
|
-
[...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
|
|
9371
|
-
worktreePath,
|
|
9372
|
-
{ reject: false }
|
|
9373
|
-
);
|
|
9374
|
-
if (existing.exitCode === 0 && existing.stdout.trim() && !opts.web) {
|
|
9375
|
-
const url2 = existing.stdout.trim();
|
|
9376
|
-
await gh(
|
|
9377
|
-
[
|
|
9378
|
-
...repoArgs,
|
|
9379
|
-
"pr",
|
|
9380
|
-
"edit",
|
|
9381
|
-
branchOnly,
|
|
9382
|
-
"--title",
|
|
9383
|
-
opts.title,
|
|
9384
|
-
...bodyArgs
|
|
9385
|
-
],
|
|
9386
|
-
worktreePath,
|
|
9387
|
-
{ reject: false }
|
|
9388
|
-
);
|
|
9389
|
-
return url2;
|
|
9390
|
-
}
|
|
9391
|
-
if (opts.web) {
|
|
9392
|
-
const args2 = [
|
|
9393
|
-
...repoArgs,
|
|
9394
|
-
"pr",
|
|
9395
|
-
"create",
|
|
9396
|
-
"--web",
|
|
9397
|
-
"--title",
|
|
9398
|
-
opts.title,
|
|
9399
|
-
...bodyArgs,
|
|
9400
|
-
"--base",
|
|
9401
|
-
opts.base,
|
|
9402
|
-
"--head",
|
|
9403
|
-
headRef
|
|
9404
|
-
];
|
|
9405
|
-
if (opts.draft) args2.push("--draft");
|
|
9406
|
-
args2.push(...ghPrCreateAssigneeArgs(opts.assignee));
|
|
9407
|
-
await gh(args2, worktreePath, { reject: false });
|
|
9408
|
-
const again = await gh(
|
|
9409
|
-
[...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
|
|
9410
|
-
worktreePath,
|
|
9411
|
-
{ reject: false }
|
|
9412
|
-
);
|
|
9413
|
-
return again.stdout.trim() || "";
|
|
9414
|
-
}
|
|
9415
|
-
const args = [
|
|
9416
|
-
...repoArgs,
|
|
9417
|
-
"pr",
|
|
9418
|
-
"create",
|
|
9419
|
-
"--title",
|
|
9420
|
-
opts.title,
|
|
9421
|
-
...bodyArgs,
|
|
9422
|
-
"--base",
|
|
9423
|
-
opts.base,
|
|
9424
|
-
"--head",
|
|
9425
|
-
headRef
|
|
9426
|
-
];
|
|
9427
|
-
if (opts.draft) args.push("--draft");
|
|
9428
|
-
args.push(...ghPrCreateAssigneeArgs(opts.assignee));
|
|
9429
|
-
const created = await gh(args, worktreePath, { reject: false });
|
|
9430
|
-
if (created.exitCode !== 0) {
|
|
9431
|
-
const raw = created.stderr.trim() || created.stdout.trim() || "gh pr create failed";
|
|
9432
|
-
throw new Error(await formatPrCreateFailure(raw, worktreePath, { slug, head: headRef }));
|
|
9433
|
-
}
|
|
9434
|
-
const url = created.stdout.trim().split("\n").find((l) => l.startsWith("http")) ?? created.stdout.trim();
|
|
9435
|
-
return url;
|
|
9436
|
-
}
|
|
9437
9826
|
function suggestSlug(source) {
|
|
9438
9827
|
const base = slugify(source || "thread");
|
|
9439
9828
|
const stamp = Date.now().toString(36).slice(-4);
|
|
9440
9829
|
return `${base || "thread"}-${stamp}`;
|
|
9441
9830
|
}
|
|
9442
|
-
var
|
|
9831
|
+
var import_node_fs18, import_node_path21, PR_INFO_JSON_FIELDS, PR_DETAILS_JSON_FIELDS;
|
|
9443
9832
|
var init_worktree = __esm({
|
|
9444
9833
|
"src/git/worktree.ts"() {
|
|
9445
9834
|
"use strict";
|
|
9446
|
-
import_node_crypto4 = require("crypto");
|
|
9447
9835
|
import_node_fs18 = require("fs");
|
|
9448
|
-
import_node_os7 = require("os");
|
|
9449
9836
|
import_node_path21 = require("path");
|
|
9450
9837
|
init_paths();
|
|
9451
9838
|
init_worktree_labels();
|
|
@@ -9453,6 +9840,8 @@ var init_worktree = __esm({
|
|
|
9453
9840
|
init_worktree_exclude();
|
|
9454
9841
|
init_git_auth_mode();
|
|
9455
9842
|
init_run();
|
|
9843
|
+
init_remote_forge();
|
|
9844
|
+
init_cursor_pr();
|
|
9456
9845
|
init_list_prs();
|
|
9457
9846
|
init_repo_git_lock();
|
|
9458
9847
|
init_app_settings();
|
|
@@ -9557,7 +9946,7 @@ function uniqueAttachmentName(dir, originalName) {
|
|
|
9557
9946
|
const candidate = `${stem}-${i}${ext}`;
|
|
9558
9947
|
if (!(0, import_node_fs19.existsSync)((0, import_node_path22.join)(dir, candidate))) return candidate;
|
|
9559
9948
|
}
|
|
9560
|
-
return `${stem}-${(0,
|
|
9949
|
+
return `${stem}-${(0, import_node_crypto4.randomUUID)()}${ext}`;
|
|
9561
9950
|
}
|
|
9562
9951
|
function previewDataUrlFromBuf(filePath, buf) {
|
|
9563
9952
|
if (!isImageFilePath(filePath)) return void 0;
|
|
@@ -9569,7 +9958,7 @@ function attachmentFromBuffer(name, buf, opts) {
|
|
|
9569
9958
|
if (isImageFilePath(name)) {
|
|
9570
9959
|
const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
|
|
9571
9960
|
return {
|
|
9572
|
-
id: (0,
|
|
9961
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
9573
9962
|
name,
|
|
9574
9963
|
kind: "file",
|
|
9575
9964
|
path: opts.path,
|
|
@@ -9582,7 +9971,7 @@ function attachmentFromBuffer(name, buf, opts) {
|
|
|
9582
9971
|
}
|
|
9583
9972
|
if (buf.length > MAX_INLINE_BYTES) {
|
|
9584
9973
|
return {
|
|
9585
|
-
id: (0,
|
|
9974
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
9586
9975
|
name,
|
|
9587
9976
|
kind: "file",
|
|
9588
9977
|
path: opts.path,
|
|
@@ -9591,7 +9980,7 @@ function attachmentFromBuffer(name, buf, opts) {
|
|
|
9591
9980
|
}
|
|
9592
9981
|
if (buf.includes(0)) {
|
|
9593
9982
|
return {
|
|
9594
|
-
id: (0,
|
|
9983
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
9595
9984
|
name,
|
|
9596
9985
|
kind: "file",
|
|
9597
9986
|
path: opts.path,
|
|
@@ -9599,7 +9988,7 @@ function attachmentFromBuffer(name, buf, opts) {
|
|
|
9599
9988
|
};
|
|
9600
9989
|
}
|
|
9601
9990
|
return {
|
|
9602
|
-
id: (0,
|
|
9991
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
9603
9992
|
name,
|
|
9604
9993
|
kind: "file",
|
|
9605
9994
|
path: opts.path,
|
|
@@ -9623,7 +10012,7 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
|
|
|
9623
10012
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
9624
10013
|
} catch (err) {
|
|
9625
10014
|
out.push({
|
|
9626
|
-
id: (0,
|
|
10015
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
9627
10016
|
name: originalName,
|
|
9628
10017
|
kind: "file",
|
|
9629
10018
|
content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
|
|
@@ -9647,7 +10036,7 @@ function stageBuffersAsAttachments(worktreePath, buffers2) {
|
|
|
9647
10036
|
out.push(attachmentFromBuffer(name, buf, { path: rel }));
|
|
9648
10037
|
} catch (err) {
|
|
9649
10038
|
out.push({
|
|
9650
|
-
id: (0,
|
|
10039
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
9651
10040
|
name: originalName,
|
|
9652
10041
|
kind: "file",
|
|
9653
10042
|
content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
|
|
@@ -9702,7 +10091,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
9702
10091
|
for (const rel of relativePaths) {
|
|
9703
10092
|
if (!rel || rel.includes("..") || rel.startsWith("/")) {
|
|
9704
10093
|
out.push({
|
|
9705
|
-
id: (0,
|
|
10094
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
9706
10095
|
name: (0, import_node_path22.basename)(rel) || "file",
|
|
9707
10096
|
kind: "file",
|
|
9708
10097
|
content: `(invalid path: ${rel})`
|
|
@@ -9718,7 +10107,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
9718
10107
|
out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
|
|
9719
10108
|
} catch (err) {
|
|
9720
10109
|
out.push({
|
|
9721
|
-
id: (0,
|
|
10110
|
+
id: (0, import_node_crypto4.randomUUID)(),
|
|
9722
10111
|
name,
|
|
9723
10112
|
kind: "file",
|
|
9724
10113
|
content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
|
|
@@ -9727,13 +10116,13 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
|
|
|
9727
10116
|
}
|
|
9728
10117
|
return out;
|
|
9729
10118
|
}
|
|
9730
|
-
var import_node_fs19, import_node_path22,
|
|
10119
|
+
var import_node_fs19, import_node_path22, import_node_crypto4, IMAGE_EXTENSIONS, IMAGE_MIME_BY_EXT, MAX_INLINE_BYTES, MAX_PREVIEW_BYTES, IMAGE_HINT_RE, PLACEHOLDER_CONTENT_RE;
|
|
9731
10120
|
var init_stage_files = __esm({
|
|
9732
10121
|
"src/composer/stage-files.ts"() {
|
|
9733
10122
|
"use strict";
|
|
9734
10123
|
import_node_fs19 = require("fs");
|
|
9735
10124
|
import_node_path22 = require("path");
|
|
9736
|
-
|
|
10125
|
+
import_node_crypto4 = require("crypto");
|
|
9737
10126
|
init_workspace_scratch();
|
|
9738
10127
|
IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
9739
10128
|
"png",
|
|
@@ -9832,7 +10221,7 @@ function pathIsInside(parent, child) {
|
|
|
9832
10221
|
}
|
|
9833
10222
|
function defaultWorktreesRoot(repoPath) {
|
|
9834
10223
|
const slug = (0, import_node_path23.basename)(repoPath.replace(/\/+$/, "")) || "repo";
|
|
9835
|
-
return (0, import_node_path23.join)((0,
|
|
10224
|
+
return (0, import_node_path23.join)((0, import_node_os7.homedir)(), "sideboard", "workspaces", slug);
|
|
9836
10225
|
}
|
|
9837
10226
|
function removedWorktreeCoversRepo(saved, repoPath) {
|
|
9838
10227
|
if (!saved || !repoPath) return false;
|
|
@@ -10015,12 +10404,12 @@ async function archiveRemovedWorkspaceChats(threads, archive) {
|
|
|
10015
10404
|
}
|
|
10016
10405
|
if (failures.length > 0) throw new Error(failures.join("\n"));
|
|
10017
10406
|
}
|
|
10018
|
-
var import_node_fs20,
|
|
10407
|
+
var import_node_fs20, import_node_os7, import_node_path23;
|
|
10019
10408
|
var init_workspaces = __esm({
|
|
10020
10409
|
"src/store/workspaces.ts"() {
|
|
10021
10410
|
"use strict";
|
|
10022
10411
|
import_node_fs20 = require("fs");
|
|
10023
|
-
|
|
10412
|
+
import_node_os7 = require("os");
|
|
10024
10413
|
import_node_path23 = require("path");
|
|
10025
10414
|
init_settings();
|
|
10026
10415
|
init_paths();
|
|
@@ -10044,7 +10433,7 @@ __export(coordinator_prompt_exports, {
|
|
|
10044
10433
|
function formatWorkspaceInventory(workspaces) {
|
|
10045
10434
|
if (workspaces.length === 0) return "(no registered workspaces)";
|
|
10046
10435
|
return workspaces.map((w) => {
|
|
10047
|
-
const slug = w.githubSlug?.trim() ? ` github:${w.githubSlug.trim()}` : "";
|
|
10436
|
+
const slug = w.originRemote ? ` ${formatRemoteTag(w.originRemote)}` : w.githubSlug?.trim() ? ` github:${w.githubSlug.trim()}` : "";
|
|
10048
10437
|
const profile = formatWorkspaceProfileSuffix(safeViewerProfile(w.path));
|
|
10049
10438
|
return `- ${w.name}: ${w.path}${slug}${profile}`;
|
|
10050
10439
|
}).join("\n");
|
|
@@ -10052,8 +10441,9 @@ function formatWorkspaceInventory(workspaces) {
|
|
|
10052
10441
|
async function enrichWorkspacesWithGithub(workspaces) {
|
|
10053
10442
|
return Promise.all(
|
|
10054
10443
|
workspaces.map(async (w) => {
|
|
10055
|
-
const
|
|
10056
|
-
|
|
10444
|
+
const originRemote = await resolveOriginRemote(w.path).catch(() => null);
|
|
10445
|
+
const githubSlug = originRemote ? originRemote.forge === "github" ? originRemote.slug : null : await resolveGithubRepoSlug(w.path).catch(() => null);
|
|
10446
|
+
return { ...w, githubSlug, originRemote };
|
|
10057
10447
|
})
|
|
10058
10448
|
);
|
|
10059
10449
|
}
|
|
@@ -10064,8 +10454,8 @@ function coordinatorGreenfieldPlaybook(reposDir) {
|
|
|
10064
10454
|
"- Examples:",
|
|
10065
10455
|
` - Clone: \`git clone <url> ${reposDir}/<name>\``,
|
|
10066
10456
|
` - New GitHub repo: \`gh repo create <owner>/<name> --private --clone -- ${reposDir}/<name>\` (or mkdir + git init + gh repo create + remote add + push)`,
|
|
10067
|
-
"- Then: add_project with that absolute path \u2192 create_workspace (repoPath + parentChatId) \u2192 send_to_chat (build) \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft (
|
|
10068
|
-
"- Always target the child worktree's **origin** (`github:`
|
|
10457
|
+
"- Then: add_project with that absolute path \u2192 create_workspace (repoPath + parentChatId) \u2192 send_to_chat (build) \u2192 wait_for_turn (loop while stillRunning) \u2192 ask_git create-draft (GitHub: `gh pr create --draft --assignee @me -R <slug>`; Cursor: `origin pr create --draft --push -R <slug>`; Entire: `entire trail create --title`).",
|
|
10458
|
+
"- Always target the child worktree's **origin** (`github:`, `cursor:`, or `entire:` from list_projects / `git remote get-url origin` in that worktree). Never open PRs against `upstream`.",
|
|
10069
10459
|
"- Do coding work in the child worktree thread, not by editing files in this home cwd."
|
|
10070
10460
|
].join("\n");
|
|
10071
10461
|
}
|
|
@@ -10226,6 +10616,7 @@ var init_coordinator_prompt = __esm({
|
|
|
10226
10616
|
"use strict";
|
|
10227
10617
|
import_node_fs21 = require("fs");
|
|
10228
10618
|
import_node_path24 = require("path");
|
|
10619
|
+
init_remote_forge();
|
|
10229
10620
|
init_worktree();
|
|
10230
10621
|
init_app_settings();
|
|
10231
10622
|
init_paths();
|
|
@@ -10234,9 +10625,9 @@ var init_coordinator_prompt = __esm({
|
|
|
10234
10625
|
"Role: you oversee Sideboard workspaces across registered repos. A workspace is one isolated checkout and can have many agent chats on those same files. You do not live inside one of those worktrees. Do not treat a workspace as one agent.",
|
|
10235
10626
|
"Sideboard MCP (fleet control \u2014 prefer these for status and orchestration):",
|
|
10236
10627
|
"Discover:",
|
|
10237
|
-
"- list_projects \u2014 registered repos (path + github
|
|
10628
|
+
"- list_projects \u2014 registered repos (path + that repo's origin host:owner/repo, such as github:, cursor:, or entire:, and project context when set). Projects do not share one git host. Use account / project context to pick the right repo for tickets or reviews.",
|
|
10238
10629
|
"- list_board \u2014 Home Kanban of workspaces (New / Draft / Review / Approved / Merged; one card per checkout). Sibling chats on a card are separate agents, not one agent. Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 approved \u2192 merged. Archive removes the card to Settings \u2192 History (list_archive to browse, restore_workspace to bring it back). Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind, column, limit.",
|
|
10239
|
-
|
|
10630
|
+
'- list_branches / list_prs / list_issues \u2014 pass repoPath from list_projects. Review is PRs (the surface for assigned ticket work), not the tickets: "Get me N tickets to review" \u2192 list_prs(queue=review, limit=N) then create_workspace sourceType=pr. That is open non-draft PRs labeled with this project\'s ready-for-review GitHub label (Settings \u2192 Projects; default eng-review) with no individual user reviewer yet. A team request (engineering-team) is not a claim \u2014 the viewer is on that team and can pick it up; claimed means an individual account is the reviewer. Bots ignored. Use Settings \u2192 Agents / Projects context (roles, teams, labels \u2014 freeform text; project adds to account) to prefer the right queues. queue=mine is review-requested:@me; queue=approved|changes uses those labels. Also: state, label, reviewer=me|unassigned|login, query, limit default 40 max 250; raise limit or tighten when truncated. list_issues (query, assignee=me|unassigned|all|user, limit default 40 max 250) lists Linear, AbleTime, or GitHub tickets. Cursor Origin has no issues; a Cursor repo uses Linear or AbleTime (unavailableReason otherwise) \u2014 do not use it for that review-inbox ask. Entire tickets are Trails (`entire trail`), not GitHub Issues. When they ask for tickets to work on, follow their context (assignee=me or unassigned as it says).',
|
|
10240
10631
|
"- Find work: when they ask to find work, pick up tickets, or list reviews, use Settings \u2192 Agents (account) plus Settings \u2192 Projects (per-repo) context. Tickets \u2192 Sideboard list_issues / linear_* (Account Linear). Reviews \u2192 list_prs(queue=review). Do not call Claude Linear MCP or any other vendor Linear MCP \u2014 those HTTP connectors hang or flap on the first turn. If a vendor MCP is down or reconnecting, ignore it and keep going with Sideboard tools. A failed Claude connector is not a Sideboard outage \u2014 do not mention it when the Account connection works. Show the options \u2014 do not create_workspace or start unless they also asked to start (e.g. \u201Cfind me work and start it\u201D). Do not start this unprompted. To change that context, show the proposed text, ask_user (Save this context / Do not save), wait, then update_viewer_context with confirmed=true. Never write it without confirmation.",
|
|
10241
10632
|
"- Ticket updates: list_issues / linear_search_issues / github_search_issues with updatedSince (yesterday, 2d, or ISO). One call returns new assignments, ticket edits, and new comments \u2014 do not get_issue every ticket just to check for comments. kind=created vs updated distinguishes new tickets from existing ones.",
|
|
10242
10633
|
"- linear_* (when Linear is connected) \u2014 list_teams for team key/states; linear_list_projects for project names; get/search include the project a ticket is on; get/create/update/comment with ENG-123. Worktree agents have the same Account tools \u2014 prefer they comment, update status, and create spin-offs (parent=) on their own ticket when they were asked to do the work. When reviewing a PR or ticket, do not comment or update until the user types a next step \u2014 they should work through the feedback in chat first. Do not tell the child to ask_user after the review. Scope errors: reconnect Linear in Account settings. Prefer these over any Linear MCP the CLI may still list. Worktree agents use the Claude Linear connector only when Account Linear is not set or the Sideboard call fails, and they do not mention that connector startup failure when Sideboard works.",
|
|
@@ -10250,7 +10641,7 @@ var init_coordinator_prompt = __esm({
|
|
|
10250
10641
|
"- get_viewer_context / update_viewer_context \u2014 read or replace Settings \u2192 Agents (account) and Settings \u2192 Projects (per-repo) context. You can update a project: pass scope=project and repoPath from list_projects (this cwd is not a project). update_viewer_context requires confirmed=true after ask_user. Never write context unprompted.",
|
|
10251
10642
|
"- list_env / set_env / delete_env \u2014 Settings \u2192 Environment. scope=project requires repoPath from list_projects and stores the key under that project prefix (the project name, uppercased, plus _). APPLE_ID for sideboard is SIDEBOARD_APPLE_ID. scope=account is shared by every project and is not prefixed. list_env returns names only.",
|
|
10252
10643
|
"- set_caffeinate \u2014 keep this Mac awake across turns (macOS caffeinate). Turn on for phone remote / away-from-keyboard work, overnight schedules, or when the user will be away. Turn OFF when they say they are done, wrapping up, going to sleep, or no longer need the machine awake. Closing this chat also releases it.",
|
|
10253
|
-
"- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014
|
|
10644
|
+
"- list_schedules / create_schedule / update_schedule / delete_schedule / run_schedule \u2014 automations that send a prompt to a chat (chatId or self) or start a new Global chat (omit chatId). One-shot `at`, interval `every` (15m/1h/6h/1d), or 5-field `cron`. Recurring jobs without chatId open a new chat each run and archive it when that run finishes (History keeps the latest run for that automation). Worktree agents have the same tools (`chatId=self` continues that coding chat). Jobs fire only while Sideboard.app is running; sleep skips until wake. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled (or call set_caffeinate). The desktop lists them in the sidebar after Board.",
|
|
10254
10645
|
"Projects:",
|
|
10255
10646
|
"- add_project / remove_project \u2014 register or unregister a git repo",
|
|
10256
10647
|
"Workspaces and chats:",
|
|
@@ -10273,11 +10664,11 @@ var init_coordinator_prompt = __esm({
|
|
|
10273
10664
|
"- list_run_scripts / run_dev_script / stop_dev_script / get_run_log \u2014 start/stop named run scripts and read the Run-tab terminal (tail). Pass name so desktop and mobile (or any pair) can run at once; each gets its own port. run_dev_script returns {url, port, preview} (SIDEBOARD_PORT). preview=url \u2192 tell the worktree agent to open that URL; preview=window \u2192 Electron: the native window is the app (url is renderer/HMR only). Never localhost:3000. list_run_scripts active[].url if already running.",
|
|
10274
10665
|
"Inspect / review / PRs:",
|
|
10275
10666
|
"- get_diff \u2014 compact diff summary",
|
|
10276
|
-
"- get_pr_checks \u2014 snapshot of a worktree thread's PR checks (null = no PR). Use this to inspect status. If the user gave a goal, the worktree agent watches with `gh pr checks --watch` \u2014 do not poll for the human.",
|
|
10667
|
+
"- get_pr_checks \u2014 snapshot of a worktree thread's PR checks (null = no PR). Use this to inspect status. If the user gave a goal, the worktree agent watches with `gh pr checks --watch` on GitHub, `origin pr watch` on Cursor, or `entire trail watch` on Entire \u2014 do not poll for the human.",
|
|
10277
10668
|
'- request_review \u2014 run a Review on a worktree thread. Reuses the only unused idle agent when the checkout has a single chat with no messages; otherwise opens a Review tab (attaches .claude/skills/review/SKILL.md when present, else .context/review.md copied from .sideboard/review.md / stock; sends "Review changes in this workspace."); then wait_for_turn (loop while stillRunning) / get_turn_result on the returned id. The review stays in that chat so they can read it and type next steps \u2014 do not tell the child to comment or update the PR or ticket.',
|
|
10278
10669
|
"- ask_git \u2014 commit & push, open a draft PR, mark ready for review, resolve conflicts, or merge \u2014 same prompts as the desktop git buttons (Resolve, Create PR, \u2026). Always steers the worktree agent; then wait_for_turn (loop while stillRunning). Prefer this over paraphrasing. If the user gave a goal (Greptile 5/5, CI green), send_to_chat that goal so the worktree agent enters the watch-fix-push loop \u2014 do not tell the human to poll.",
|
|
10279
10670
|
'- Merge (`ask_git` action=merge / send_to_chat "Merge PR.") only when the user explicitly asked to merge that PR. Do not merge because the work looks done, CI is green, or a typical flow includes it.',
|
|
10280
|
-
'- Or send_to_chat with those exact phrases: "Commit and push.", "Commit, push, and open a draft PR.", "Ready for review.", "Merge PR." Prefer ask_git resolve-conflicts (it names the actual PR base \u2014 not always main) over paraphrasing.
|
|
10671
|
+
'- Or send_to_chat with those exact phrases: "Commit and push.", "Commit, push, and open a draft PR.", "Ready for review.", "Merge PR." Prefer ask_git resolve-conflicts (it names the actual PR base \u2014 not always main) over paraphrasing. Draft PRs follow that workspace origin: GitHub (`github:`) uses `gh pr create --draft --assignee @me -R <slug>`; Cursor (`cursor:`) uses `origin pr create --draft --push -R <slug>`; Entire (`entire:`) uses `entire trail create --title` (Trails have no draft state). Never upstream. Never run git, gh, origin, or entire from this orchestration cwd, and never merge the PR yourself.',
|
|
10281
10672
|
"Process guides:",
|
|
10282
10673
|
"- Long child jobs (pack, test, deploy, anything that may run more than ~30s): workers always have `/long-running` (Sideboard product skill). Tell them to detach and wait; they may `stop_job` if it hangs or is doing the wrong thing; do not ask the human to poll.",
|
|
10283
10674
|
"- Recurring multi-item / fan-out: if the child worktree has `.claude/skills/graph-engineering/SKILL.md`, tell the worker to follow it (`/graph-engineering`). Judge first; state on disk; grow the rulebook; do not patch three threads.",
|
|
@@ -10620,7 +11011,7 @@ function resolveBrightsyConfigFile(path) {
|
|
|
10620
11011
|
}
|
|
10621
11012
|
function brightsyConfigPath() {
|
|
10622
11013
|
const override = process.env.BRIGHTSY_CONFIG?.trim();
|
|
10623
|
-
const raw = override || (0, import_node_path26.join)((0,
|
|
11014
|
+
const raw = override || (0, import_node_path26.join)((0, import_node_os8.homedir)(), ".brightsy", "config.json");
|
|
10624
11015
|
return resolveBrightsyConfigFile(raw);
|
|
10625
11016
|
}
|
|
10626
11017
|
function loadBrightsyConfig() {
|
|
@@ -10654,12 +11045,12 @@ function saveBrightsyConfig(cfg) {
|
|
|
10654
11045
|
mode: 384
|
|
10655
11046
|
});
|
|
10656
11047
|
}
|
|
10657
|
-
var import_node_fs23,
|
|
11048
|
+
var import_node_fs23, import_node_os8, import_node_path26;
|
|
10658
11049
|
var init_config = __esm({
|
|
10659
11050
|
"src/brightsy/config.ts"() {
|
|
10660
11051
|
"use strict";
|
|
10661
11052
|
import_node_fs23 = require("fs");
|
|
10662
|
-
|
|
11053
|
+
import_node_os8 = require("os");
|
|
10663
11054
|
import_node_path26 = require("path");
|
|
10664
11055
|
}
|
|
10665
11056
|
});
|
|
@@ -11768,7 +12159,7 @@ function claudePluginServerId(pluginName, serverName) {
|
|
|
11768
12159
|
return `plugin_${pluginName}_${serverName}`.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
11769
12160
|
}
|
|
11770
12161
|
function listEnabledClaudePluginMcpServers(opts) {
|
|
11771
|
-
const home = opts?.home ?? (0,
|
|
12162
|
+
const home = opts?.home ?? (0, import_node_os9.homedir)();
|
|
11772
12163
|
const pluginsRoot = claudePluginsRoot(home);
|
|
11773
12164
|
const enabled = enabledPluginIds(home, opts?.projectDirs ?? []);
|
|
11774
12165
|
const installed = readInstalledPlugins((0, import_node_path28.join)(pluginsRoot, "installed_plugins.json"));
|
|
@@ -11950,12 +12341,12 @@ function readJson(path) {
|
|
|
11950
12341
|
return {};
|
|
11951
12342
|
}
|
|
11952
12343
|
}
|
|
11953
|
-
var import_node_fs26,
|
|
12344
|
+
var import_node_fs26, import_node_os9, import_node_path28;
|
|
11954
12345
|
var init_claude_plugins = __esm({
|
|
11955
12346
|
"src/agents/claude-plugins.ts"() {
|
|
11956
12347
|
"use strict";
|
|
11957
12348
|
import_node_fs26 = require("fs");
|
|
11958
|
-
|
|
12349
|
+
import_node_os9 = require("os");
|
|
11959
12350
|
import_node_path28 = require("path");
|
|
11960
12351
|
init_meta();
|
|
11961
12352
|
}
|
|
@@ -12172,7 +12563,7 @@ function versionDirNodeBins(root, toBin) {
|
|
|
12172
12563
|
return [];
|
|
12173
12564
|
}
|
|
12174
12565
|
}
|
|
12175
|
-
function defaultNodeBinCandidates(home = (0,
|
|
12566
|
+
function defaultNodeBinCandidates(home = (0, import_node_os10.homedir)()) {
|
|
12176
12567
|
const kegs = ["/opt/homebrew", "/usr/local"].flatMap(
|
|
12177
12568
|
(prefix) => PREFERRED_LTS_MAJORS.map((major) => (0, import_node_path30.join)(prefix, "opt", `node@${major}`, "bin", "node"))
|
|
12178
12569
|
);
|
|
@@ -12287,12 +12678,12 @@ async function resolveNodeLaunch(scriptPath) {
|
|
|
12287
12678
|
env: { ELECTRON_RUN_AS_NODE: "1" }
|
|
12288
12679
|
};
|
|
12289
12680
|
}
|
|
12290
|
-
var import_node_fs28,
|
|
12681
|
+
var import_node_fs28, import_node_os10, import_node_path30, AGENT_RUNNER_MAX_OLD_SPACE_MB, MAX_OLD_SPACE_FLAG, PREFERRED_LTS_MAJORS;
|
|
12291
12682
|
var init_node_launch = __esm({
|
|
12292
12683
|
"src/agents/node-launch.ts"() {
|
|
12293
12684
|
"use strict";
|
|
12294
12685
|
import_node_fs28 = require("fs");
|
|
12295
|
-
|
|
12686
|
+
import_node_os10 = require("os");
|
|
12296
12687
|
import_node_path30 = require("path");
|
|
12297
12688
|
init_nested_electron_env();
|
|
12298
12689
|
init_system_ca();
|
|
@@ -12589,18 +12980,18 @@ function mcpServersRecord(servers, extraServers) {
|
|
|
12589
12980
|
function writeMcpServersConfig(servers, extraServers) {
|
|
12590
12981
|
const mcpServers = mcpServersRecord(servers, extraServers);
|
|
12591
12982
|
if (Object.keys(mcpServers).length === 0) return null;
|
|
12592
|
-
const dir = (0, import_node_fs29.mkdtempSync)((0, import_node_path31.join)((0,
|
|
12983
|
+
const dir = (0, import_node_fs29.mkdtempSync)((0, import_node_path31.join)((0, import_node_os11.tmpdir)(), "sideboard-mcp-"));
|
|
12593
12984
|
const cfgPath = (0, import_node_path31.join)(dir, "mcp.json");
|
|
12594
12985
|
(0, import_node_fs29.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
|
|
12595
12986
|
return cfgPath;
|
|
12596
12987
|
}
|
|
12597
|
-
var import_node_fs29, import_node_module,
|
|
12988
|
+
var import_node_fs29, import_node_module, import_node_os11, import_node_path31, import_node_url, import_meta2, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, SIDEBOARD_GITHUB_MCP_ALLOWED_TOOLS, SIDEBOARD_LINEAR_MCP_ALLOWED_TOOLS, SIDEBOARD_ABLETIME_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
|
|
12598
12989
|
var init_injected_mcp = __esm({
|
|
12599
12990
|
"src/agents/injected-mcp.ts"() {
|
|
12600
12991
|
"use strict";
|
|
12601
12992
|
import_node_fs29 = require("fs");
|
|
12602
12993
|
import_node_module = require("module");
|
|
12603
|
-
|
|
12994
|
+
import_node_os11 = require("os");
|
|
12604
12995
|
import_node_path31 = require("path");
|
|
12605
12996
|
import_node_url = require("url");
|
|
12606
12997
|
init_run();
|
|
@@ -12655,14 +13046,14 @@ var init_injected_mcp = __esm({
|
|
|
12655
13046
|
|
|
12656
13047
|
// src/agents/user-mcp-config.ts
|
|
12657
13048
|
function userClaudeMcpConfigPath() {
|
|
12658
|
-
return (0, import_node_path32.join)((0,
|
|
13049
|
+
return (0, import_node_path32.join)((0, import_node_os12.homedir)(), ".claude.json");
|
|
12659
13050
|
}
|
|
12660
|
-
var import_node_fs30,
|
|
13051
|
+
var import_node_fs30, import_node_os12, import_node_path32;
|
|
12661
13052
|
var init_user_mcp_config = __esm({
|
|
12662
13053
|
"src/agents/user-mcp-config.ts"() {
|
|
12663
13054
|
"use strict";
|
|
12664
13055
|
import_node_fs30 = require("fs");
|
|
12665
|
-
|
|
13056
|
+
import_node_os12 = require("os");
|
|
12666
13057
|
import_node_path32 = require("path");
|
|
12667
13058
|
init_paths();
|
|
12668
13059
|
init_injected_mcp();
|
|
@@ -12703,10 +13094,10 @@ function readText(path) {
|
|
|
12703
13094
|
return "";
|
|
12704
13095
|
}
|
|
12705
13096
|
}
|
|
12706
|
-
function userCodexConfigPaths(home = (0,
|
|
13097
|
+
function userCodexConfigPaths(home = (0, import_node_os13.homedir)()) {
|
|
12707
13098
|
return [(0, import_node_path33.join)(home, ".codex", "config.toml"), (0, import_node_path33.join)(home, ".config", "codex", "config.toml")];
|
|
12708
13099
|
}
|
|
12709
|
-
function userOpencodeConfigPaths(home = (0,
|
|
13100
|
+
function userOpencodeConfigPaths(home = (0, import_node_os13.homedir)()) {
|
|
12710
13101
|
return [
|
|
12711
13102
|
(0, import_node_path33.join)(home, ".config", "opencode", "opencode.json"),
|
|
12712
13103
|
(0, import_node_path33.join)(home, ".config", "opencode", "opencode.jsonc")
|
|
@@ -12728,7 +13119,7 @@ function userMcpNamesToDisable(opts) {
|
|
|
12728
13119
|
}
|
|
12729
13120
|
return out;
|
|
12730
13121
|
}
|
|
12731
|
-
function listUserCodexMcpNames(home = (0,
|
|
13122
|
+
function listUserCodexMcpNames(home = (0, import_node_os13.homedir)()) {
|
|
12732
13123
|
const names = [];
|
|
12733
13124
|
for (const path of userCodexConfigPaths(home)) {
|
|
12734
13125
|
if (!(0, import_node_fs31.existsSync)(path)) continue;
|
|
@@ -12736,7 +13127,7 @@ function listUserCodexMcpNames(home = (0, import_node_os14.homedir)()) {
|
|
|
12736
13127
|
}
|
|
12737
13128
|
return names;
|
|
12738
13129
|
}
|
|
12739
|
-
function listUserOpencodeMcpNames(home = (0,
|
|
13130
|
+
function listUserOpencodeMcpNames(home = (0, import_node_os13.homedir)()) {
|
|
12740
13131
|
const names = [];
|
|
12741
13132
|
for (const path of userOpencodeConfigPaths(home)) {
|
|
12742
13133
|
if (!(0, import_node_fs31.existsSync)(path)) continue;
|
|
@@ -12744,12 +13135,12 @@ function listUserOpencodeMcpNames(home = (0, import_node_os14.homedir)()) {
|
|
|
12744
13135
|
}
|
|
12745
13136
|
return names;
|
|
12746
13137
|
}
|
|
12747
|
-
function userClaudeMcpConfigObject(home = (0,
|
|
12748
|
-
const claudePath = home === (0,
|
|
13138
|
+
function userClaudeMcpConfigObject(home = (0, import_node_os13.homedir)()) {
|
|
13139
|
+
const claudePath = home === (0, import_node_os13.homedir)() ? userClaudeMcpConfigPath() : (0, import_node_path33.join)(home, ".claude.json");
|
|
12749
13140
|
if (!(0, import_node_fs31.existsSync)(claudePath)) return {};
|
|
12750
13141
|
return asObject(asObject(readJsonObject2(claudePath)).mcpServers);
|
|
12751
13142
|
}
|
|
12752
|
-
function listUserClaudeMcpServerEntries(home = (0,
|
|
13143
|
+
function listUserClaudeMcpServerEntries(home = (0, import_node_os13.homedir)(), projectDirs) {
|
|
12753
13144
|
const out = {};
|
|
12754
13145
|
for (const [name, entry] of Object.entries(
|
|
12755
13146
|
listEnabledClaudePluginMcpServers({ home, projectDirs })
|
|
@@ -12785,13 +13176,13 @@ function toCodexUnattendedAppsArgs() {
|
|
|
12785
13176
|
"features.plugins=false"
|
|
12786
13177
|
];
|
|
12787
13178
|
}
|
|
12788
|
-
var import_node_fs31,
|
|
13179
|
+
var import_node_fs31, import_node_os13, import_node_path33, import_smol_toml2;
|
|
12789
13180
|
var init_orch_mcp_isolation = __esm({
|
|
12790
13181
|
"src/agents/orch-mcp-isolation.ts"() {
|
|
12791
13182
|
"use strict";
|
|
12792
13183
|
import_node_fs31 = require("fs");
|
|
12793
13184
|
init_claude_plugins();
|
|
12794
|
-
|
|
13185
|
+
import_node_os13 = require("os");
|
|
12795
13186
|
import_node_path33 = require("path");
|
|
12796
13187
|
import_smol_toml2 = require("smol-toml");
|
|
12797
13188
|
init_user_mcp_config();
|
|
@@ -12892,7 +13283,7 @@ var init_proxy_server = __esm({
|
|
|
12892
13283
|
|
|
12893
13284
|
// src/mcp-ui/proxy-http.ts
|
|
12894
13285
|
async function listenMcpUiProxy(opts) {
|
|
12895
|
-
const token = (0,
|
|
13286
|
+
const token = (0, import_node_crypto5.randomBytes)(24).toString("hex");
|
|
12896
13287
|
const transports = /* @__PURE__ */ new Map();
|
|
12897
13288
|
const httpServer = (0, import_node_http.createServer)((req, res) => {
|
|
12898
13289
|
void handle(req, res);
|
|
@@ -12930,7 +13321,7 @@ async function listenMcpUiProxy(opts) {
|
|
|
12930
13321
|
return;
|
|
12931
13322
|
}
|
|
12932
13323
|
const created = new import_streamableHttp.StreamableHTTPServerTransport({
|
|
12933
|
-
sessionIdGenerator: () => (0,
|
|
13324
|
+
sessionIdGenerator: () => (0, import_node_crypto5.randomUUID)(),
|
|
12934
13325
|
allowedHosts: ["127.0.0.1", "localhost"],
|
|
12935
13326
|
onsessioninitialized: (id) => {
|
|
12936
13327
|
transports.set(`${sessionKey}:${id}`, created);
|
|
@@ -12983,11 +13374,11 @@ function parseMcpUiProxyPath(url) {
|
|
|
12983
13374
|
return null;
|
|
12984
13375
|
}
|
|
12985
13376
|
}
|
|
12986
|
-
var
|
|
13377
|
+
var import_node_crypto5, import_node_http, import_server2, import_streamableHttp;
|
|
12987
13378
|
var init_proxy_http = __esm({
|
|
12988
13379
|
"src/mcp-ui/proxy-http.ts"() {
|
|
12989
13380
|
"use strict";
|
|
12990
|
-
|
|
13381
|
+
import_node_crypto5 = require("crypto");
|
|
12991
13382
|
import_node_http = require("http");
|
|
12992
13383
|
import_server2 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
12993
13384
|
import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
@@ -13124,7 +13515,7 @@ var init_session = __esm({
|
|
|
13124
13515
|
function getMcpUiHost() {
|
|
13125
13516
|
if (!singleton) {
|
|
13126
13517
|
singleton = new McpUiHost({
|
|
13127
|
-
entries: (projectDirs) => listUserClaudeMcpServerEntries((0,
|
|
13518
|
+
entries: (projectDirs) => listUserClaudeMcpServerEntries((0, import_node_os14.homedir)(), projectDirs)
|
|
13128
13519
|
});
|
|
13129
13520
|
}
|
|
13130
13521
|
return singleton;
|
|
@@ -13132,18 +13523,18 @@ function getMcpUiHost() {
|
|
|
13132
13523
|
async function proxyClaudeUserMcp(orchestrator, projectDirs) {
|
|
13133
13524
|
if (orchestrator) return {};
|
|
13134
13525
|
const dirs = projectDirs.filter((d) => typeof d === "string" && d.length > 0);
|
|
13135
|
-
return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries((0,
|
|
13526
|
+
return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries((0, import_node_os14.homedir)(), dirs), dirs);
|
|
13136
13527
|
}
|
|
13137
13528
|
function dirsKeyFor(projectDirs) {
|
|
13138
13529
|
if (!projectDirs?.length) return "";
|
|
13139
|
-
return (0,
|
|
13530
|
+
return (0, import_node_crypto6.createHash)("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
|
|
13140
13531
|
}
|
|
13141
|
-
var
|
|
13532
|
+
var import_node_crypto6, import_node_os14, TOOL_TTL_MS, McpUiHost, singleton;
|
|
13142
13533
|
var init_host = __esm({
|
|
13143
13534
|
"src/mcp-ui/host.ts"() {
|
|
13144
13535
|
"use strict";
|
|
13145
|
-
|
|
13146
|
-
|
|
13536
|
+
import_node_crypto6 = require("crypto");
|
|
13537
|
+
import_node_os14 = require("os");
|
|
13147
13538
|
init_orch_mcp_isolation();
|
|
13148
13539
|
init_meta();
|
|
13149
13540
|
init_proxy_http();
|
|
@@ -14264,8 +14655,8 @@ function usageFromCodex(usage) {
|
|
|
14264
14655
|
}
|
|
14265
14656
|
function codexConfigHasNetworkAccess() {
|
|
14266
14657
|
const candidates = [
|
|
14267
|
-
(0, import_node_path36.join)((0,
|
|
14268
|
-
(0, import_node_path36.join)((0,
|
|
14658
|
+
(0, import_node_path36.join)((0, import_node_os15.homedir)(), ".codex", "config.toml"),
|
|
14659
|
+
(0, import_node_path36.join)((0, import_node_os15.homedir)(), ".config", "codex", "config.toml")
|
|
14269
14660
|
];
|
|
14270
14661
|
for (const path of candidates) {
|
|
14271
14662
|
if (!(0, import_node_fs35.existsSync)(path)) continue;
|
|
@@ -14301,7 +14692,7 @@ function asRecord5(value) {
|
|
|
14301
14692
|
return void 0;
|
|
14302
14693
|
}
|
|
14303
14694
|
function codexLooksAuthenticated() {
|
|
14304
|
-
const authPath = (0, import_node_path36.join)((0,
|
|
14695
|
+
const authPath = (0, import_node_path36.join)((0, import_node_os15.homedir)(), ".codex", "auth.json");
|
|
14305
14696
|
if (!(0, import_node_fs35.existsSync)(authPath)) return false;
|
|
14306
14697
|
try {
|
|
14307
14698
|
return (0, import_node_fs35.statSync)(authPath).size > 2;
|
|
@@ -14309,12 +14700,12 @@ function codexLooksAuthenticated() {
|
|
|
14309
14700
|
return false;
|
|
14310
14701
|
}
|
|
14311
14702
|
}
|
|
14312
|
-
var import_node_fs35,
|
|
14703
|
+
var import_node_fs35, import_node_os15, import_node_path36, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
|
|
14313
14704
|
var init_codex = __esm({
|
|
14314
14705
|
"src/agents/codex.ts"() {
|
|
14315
14706
|
"use strict";
|
|
14316
14707
|
import_node_fs35 = require("fs");
|
|
14317
|
-
|
|
14708
|
+
import_node_os15 = require("os");
|
|
14318
14709
|
import_node_path36 = require("path");
|
|
14319
14710
|
init_run();
|
|
14320
14711
|
init_app_settings();
|
|
@@ -15767,12 +16158,12 @@ var init_claude_usage = __esm({
|
|
|
15767
16158
|
});
|
|
15768
16159
|
|
|
15769
16160
|
// src/agents/claude-usage-client.ts
|
|
15770
|
-
var import_node_fs41,
|
|
16161
|
+
var import_node_fs41, import_node_os16, import_node_path41, CLAUDE_USAGE_ERROR_TTL_MS, CLAUDE_USAGE_STALE_OK_MS;
|
|
15771
16162
|
var init_claude_usage_client = __esm({
|
|
15772
16163
|
"src/agents/claude-usage-client.ts"() {
|
|
15773
16164
|
"use strict";
|
|
15774
16165
|
import_node_fs41 = require("fs");
|
|
15775
|
-
|
|
16166
|
+
import_node_os16 = require("os");
|
|
15776
16167
|
import_node_path41 = require("path");
|
|
15777
16168
|
init_run();
|
|
15778
16169
|
init_fetch();
|
|
@@ -16422,7 +16813,9 @@ function resolveSidebarGitPrompt(action, opts) {
|
|
|
16422
16813
|
}
|
|
16423
16814
|
return agentGitPrompt(action, opts);
|
|
16424
16815
|
}
|
|
16425
|
-
function gitActionExpansion(action, base) {
|
|
16816
|
+
function gitActionExpansion(action, base, forge) {
|
|
16817
|
+
if (forge === "cursor") return cursorGitActionExpansion(action, base);
|
|
16818
|
+
if (forge === "entire") return entireGitActionExpansion(action, base);
|
|
16426
16819
|
switch (action) {
|
|
16427
16820
|
case "commit-push":
|
|
16428
16821
|
return "Commit any uncommitted work with a purpose-stating message, then push to origin (`git push -u origin HEAD`; this updates an existing PR when one is linked). Do not start a checks loop unless a goal was given.";
|
|
@@ -16447,7 +16840,57 @@ function gitActionExpansion(action, base) {
|
|
|
16447
16840
|
return "This phrase is the explicit ask to merge this thread's open pull request on GitHub. If `gh stack view` shows a stack, use `gh stack merge`; otherwise `gh pr merge` (respect repo defaults / squash vs merge). Do not force-push main/master or merge locally into the main checkout.";
|
|
16448
16841
|
}
|
|
16449
16842
|
}
|
|
16450
|
-
function
|
|
16843
|
+
function cursorGitActionExpansion(action, base) {
|
|
16844
|
+
switch (action) {
|
|
16845
|
+
case "commit-push":
|
|
16846
|
+
return "Commit any uncommitted work with a purpose-stating message, then push to origin (`git push -u origin HEAD`; this updates an existing PR when one is linked). Use `origin`, not `gh`. Do not start a checks loop unless a goal was given.";
|
|
16847
|
+
case "create-draft":
|
|
16848
|
+
return "Commit, push, then create a draft PR with `origin pr create --draft --push -R <origin-owner/name> --body-file <short.md>` (title/body from the change purpose). Take the URL from `--json url`. Update the existing PR with `origin pr edit` if one is linked. Do not use `gh`.";
|
|
16849
|
+
case "create-web":
|
|
16850
|
+
return "Commit, push, then `origin pr create --push -R <origin-owner/name>` and open the URL from `--json url`. Origin has no `--web` form. Add `--draft` when they asked for a draft. Do not use `gh`.";
|
|
16851
|
+
case "resolve-conflicts":
|
|
16852
|
+
if (base && !isMainishPrBase(base)) {
|
|
16853
|
+
return `Fetch \`origin/${base}\` (this PR's base \u2014 not main/master), merge it into this branch, resolve conflicts carefully, then commit and push until the PR is mergeable. Do not merge origin/main.`;
|
|
16854
|
+
}
|
|
16855
|
+
if (base) {
|
|
16856
|
+
return `Fetch the PR base (\`${base}\`), merge it into this branch, resolve conflicts carefully, then commit and push until the PR is mergeable.`;
|
|
16857
|
+
}
|
|
16858
|
+
return "Look up this PR's base with `origin pr view --json baseRefName` and merge that remote branch into this branch (do not assume main). Resolve conflicts carefully, then commit and push until the PR is mergeable. Do not use `gh`.";
|
|
16859
|
+
case "ready-for-review":
|
|
16860
|
+
return "This phrase is the explicit ask to mark this thread's draft pull request ready for review on Cursor (`origin pr ready -R <origin-owner/name>`). Update title/body if the purpose drifted. Do not merge. Do not use `gh`.";
|
|
16861
|
+
case "merge":
|
|
16862
|
+
if (base && !isMainishPrBase(base)) {
|
|
16863
|
+
return `This phrase is the explicit ask to merge this thread's open pull request into \`${base}\` on Cursor with \`origin pr merge\`. This PR targets \`${base}\`, not main \u2014 do not use \`origin stack merge\` and do not retarget or merge into main/master. Do not force-push main/master or merge locally into the main checkout. Do not use \`gh\`.`;
|
|
16864
|
+
}
|
|
16865
|
+
return "This phrase is the explicit ask to merge this thread's open pull request on Cursor. If `origin stack view` shows a stack, use `origin stack merge`; otherwise `origin pr merge`. Do not force-push main/master or merge locally into the main checkout. Do not use `gh`.";
|
|
16866
|
+
}
|
|
16867
|
+
}
|
|
16868
|
+
function entireGitActionExpansion(action, base) {
|
|
16869
|
+
switch (action) {
|
|
16870
|
+
case "commit-push":
|
|
16871
|
+
return "Commit any uncommitted work with a purpose-stating message, then push to origin (`git push -u origin HEAD`). Use `entire`, not `gh` or `origin`. Do not start a checks loop unless a goal was given.";
|
|
16872
|
+
case "create-draft":
|
|
16873
|
+
return 'Commit, push, then open a Trail with `entire trail create --title "<title>" --body "<short>"` (title/body from the change purpose). Take the URL from `--json`. If this branch already has a trail, `entire trail update` instead of creating another. Trails have no draft flag. Do not use `gh` or `origin`.';
|
|
16874
|
+
case "create-web":
|
|
16875
|
+
return 'Commit, push, then `entire trail create --title "<title>" --body "<short>"` and open the URL from `--json`. Entire has no `--web` form. Do not use `gh` or `origin`.';
|
|
16876
|
+
case "resolve-conflicts":
|
|
16877
|
+
if (base && !isMainishPrBase(base)) {
|
|
16878
|
+
return `Fetch \`origin/${base}\` (this Trail's base \u2014 not main/master), merge it into this branch, resolve conflicts carefully, then commit and push. Do not merge origin/main.`;
|
|
16879
|
+
}
|
|
16880
|
+
if (base) {
|
|
16881
|
+
return `Fetch the Trail base (\`${base}\`), merge it into this branch, resolve conflicts carefully, then commit and push.`;
|
|
16882
|
+
}
|
|
16883
|
+
return "Look up this Trail's base with `entire trail show --json` and merge that remote branch into this branch (do not assume main). Resolve conflicts carefully, then commit and push. Do not use `gh`.";
|
|
16884
|
+
case "ready-for-review":
|
|
16885
|
+
return "This phrase means the Trail should be ready for a person to review. Trails have no draft flag and start open. Update title or body with `entire trail update` if the purpose drifted. Do not approve the trail and do not merge. Do not use `gh`.";
|
|
16886
|
+
case "merge":
|
|
16887
|
+
if (base && !isMainishPrBase(base)) {
|
|
16888
|
+
return `This phrase is the explicit ask to merge this thread's Trail change into \`${base}\` with \`entire trail merge\`. This change targets \`${base}\`, not main \u2014 do not retarget or merge into main/master. Do not force-push main/master or merge locally into the main checkout. Do not use \`gh\`.`;
|
|
16889
|
+
}
|
|
16890
|
+
return "This phrase is the explicit ask to merge this thread's Trail change with `entire trail merge`. Do not force-push main/master or merge locally into the main checkout. Do not use `gh`.";
|
|
16891
|
+
}
|
|
16892
|
+
}
|
|
16893
|
+
function expandCanonicalGitRequest(prompt, opts) {
|
|
16451
16894
|
const trimmed = prompt.trim();
|
|
16452
16895
|
let action = null;
|
|
16453
16896
|
let base = null;
|
|
@@ -16472,11 +16915,12 @@ function expandCanonicalGitRequest(prompt) {
|
|
|
16472
16915
|
base = m[1]?.trim() || null;
|
|
16473
16916
|
}
|
|
16474
16917
|
}
|
|
16475
|
-
|
|
16918
|
+
const forge = opts?.forge ?? null;
|
|
16919
|
+
let expansion = action ? gitActionExpansion(action, base, forge) : null;
|
|
16476
16920
|
if (!expansion && trimmed === "Address review comments.") {
|
|
16477
|
-
expansion = "Read the PR review feedback (`gh pr view --comments`, `gh api` review comments), make the requested changes, commit, and push.";
|
|
16921
|
+
expansion = forge === "cursor" ? "Read the PR review feedback (`origin pr view --comments`, `origin pr thread list`), make the requested changes, commit, and push. Do not use `gh`." : forge === "entire" ? "Read open findings (`entire trail finding list`), make the requested changes, commit, and push. Do not use `gh` or `entire trail comment` (agent sessions cannot post trail comments)." : "Read the PR review feedback (`gh pr view --comments`, `gh api` review comments), make the requested changes, commit, and push.";
|
|
16478
16922
|
} else if (!expansion && FIX_CI_RE.test(trimmed)) {
|
|
16479
|
-
expansion = "Investigate that failing check (`gh pr checks`, `gh run view --log-failed`), fix it, commit, and push. Loop only if a goal to keep going until it passes was given.";
|
|
16923
|
+
expansion = forge === "cursor" ? "Investigate that failing check (`origin pr checks`, `origin pr watch`), fix it, commit, and push. Loop only if a goal to keep going until it passes was given. Do not use `gh run`." : forge === "entire" ? "Investigate the blocking gate (`entire trail merge --dry-run`, `entire trail finding list`, `entire trail watch`), fix it, commit, and push. Loop only if a goal to keep going until it passes was given. Do not use `gh run`." : "Investigate that failing check (`gh pr checks`, `gh run view --log-failed`), fix it, commit, and push. Loop only if a goal to keep going until it passes was given.";
|
|
16480
16924
|
}
|
|
16481
16925
|
if (!expansion) return prompt;
|
|
16482
16926
|
return `${trimmed}
|
|
@@ -17207,7 +17651,7 @@ function forkMessageSlice(from, throughIndex) {
|
|
|
17207
17651
|
function buildForkTranscriptAttachment(baseTitle, messages) {
|
|
17208
17652
|
const title = baseTitle || "Chat";
|
|
17209
17653
|
return {
|
|
17210
|
-
id: (0,
|
|
17654
|
+
id: (0, import_node_crypto7.randomUUID)(),
|
|
17211
17655
|
name: `Transcript of ${title}.md`,
|
|
17212
17656
|
kind: "transcript",
|
|
17213
17657
|
content: formatTranscriptMarkdown(title, messages)
|
|
@@ -17266,11 +17710,11 @@ function forkChatTab(input) {
|
|
|
17266
17710
|
}
|
|
17267
17711
|
return tab;
|
|
17268
17712
|
}
|
|
17269
|
-
var
|
|
17713
|
+
var import_node_crypto7;
|
|
17270
17714
|
var init_chat_tabs = __esm({
|
|
17271
17715
|
"src/threads/chat-tabs.ts"() {
|
|
17272
17716
|
"use strict";
|
|
17273
|
-
|
|
17717
|
+
import_node_crypto7 = require("crypto");
|
|
17274
17718
|
init_context_compact();
|
|
17275
17719
|
init_teams();
|
|
17276
17720
|
init_worktree_labels();
|
|
@@ -17512,7 +17956,7 @@ function buildReviewRequestAttachment(content, opts) {
|
|
|
17512
17956
|
const path = opts?.path ?? REVIEW_SKILL_PATH;
|
|
17513
17957
|
const name = opts?.name ?? (path === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path === REPO_REVIEW_PATH || path === CONTEXT_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
|
|
17514
17958
|
return {
|
|
17515
|
-
id: (0,
|
|
17959
|
+
id: (0, import_node_crypto8.randomUUID)(),
|
|
17516
17960
|
name,
|
|
17517
17961
|
kind: "file",
|
|
17518
17962
|
path,
|
|
@@ -17565,11 +18009,11 @@ async function requestReview(threadRef, send) {
|
|
|
17565
18009
|
const started = await send(tab.id, REVIEW_REQUEST_PREFILL);
|
|
17566
18010
|
return { tab: started, from };
|
|
17567
18011
|
}
|
|
17568
|
-
var
|
|
18012
|
+
var import_node_crypto8, import_node_fs44, import_node_path44, REPO_REVIEW_PATH, REPO_REVIEW_NAME, CONTEXT_REVIEW_PATH, REVIEW_REQUEST_PATH, LEGACY_REVIEW_REQUEST_PATH, REVIEW_REQUEST_NAME, REVIEW_REQUEST_PREFILL, LEGACY_REVIEW_TEMPLATE_MARKERS;
|
|
17569
18013
|
var init_request_review = __esm({
|
|
17570
18014
|
"src/review/request-review.ts"() {
|
|
17571
18015
|
"use strict";
|
|
17572
|
-
|
|
18016
|
+
import_node_crypto8 = require("crypto");
|
|
17573
18017
|
import_node_fs44 = require("fs");
|
|
17574
18018
|
import_node_path44 = require("path");
|
|
17575
18019
|
init_global_workspace();
|
|
@@ -18827,7 +19271,7 @@ function setupLogPath(threadId) {
|
|
|
18827
19271
|
}
|
|
18828
19272
|
function setupLogKeyForWorktree(worktreePath) {
|
|
18829
19273
|
const norm = normalizeWorktreePath(worktreePath);
|
|
18830
|
-
return `wt-${(0,
|
|
19274
|
+
return `wt-${(0, import_node_crypto9.createHash)("sha1").update(norm).digest("hex").slice(0, 16)}`;
|
|
18831
19275
|
}
|
|
18832
19276
|
function emptySetupLog() {
|
|
18833
19277
|
return { ...EMPTY };
|
|
@@ -18920,11 +19364,11 @@ function finishSetupLog(threadId, exitCode, source) {
|
|
|
18920
19364
|
persistNow(threadId);
|
|
18921
19365
|
return materialize(mem);
|
|
18922
19366
|
}
|
|
18923
|
-
var
|
|
19367
|
+
var import_node_crypto9, import_node_fs48, import_node_path48, EMPTY, MAX_SETUP_LOG_CHARS, memory, persistTimers, PERSIST_DEBOUNCE_MS;
|
|
18924
19368
|
var init_setup_log = __esm({
|
|
18925
19369
|
"src/store/setup-log.ts"() {
|
|
18926
19370
|
"use strict";
|
|
18927
|
-
|
|
19371
|
+
import_node_crypto9 = require("crypto");
|
|
18928
19372
|
import_node_fs48 = require("fs");
|
|
18929
19373
|
import_node_path48 = require("path");
|
|
18930
19374
|
init_worktree_labels();
|
|
@@ -18975,7 +19419,7 @@ function runLogPath(key) {
|
|
|
18975
19419
|
}
|
|
18976
19420
|
function runLogKeyForWorktree(worktreePath, scriptName) {
|
|
18977
19421
|
const norm = normalizeWorktreePath(worktreePath);
|
|
18978
|
-
const hash = (0,
|
|
19422
|
+
const hash = (0, import_node_crypto10.createHash)("sha1").update(norm).digest("hex").slice(0, 16);
|
|
18979
19423
|
return `wt-${hash}-run-${safeScriptToken(scriptName)}`;
|
|
18980
19424
|
}
|
|
18981
19425
|
function emptyRunLog() {
|
|
@@ -19076,11 +19520,11 @@ function finishRunLog(key, exitCode) {
|
|
|
19076
19520
|
function flushRunLog(key) {
|
|
19077
19521
|
persistNow2(key);
|
|
19078
19522
|
}
|
|
19079
|
-
var
|
|
19523
|
+
var import_node_crypto10, import_node_fs49, import_node_path49, EMPTY2, MAX_RUN_LOG_CHARS, DEFAULT_RUN_LOG_TAIL_CHARS, memory2, persistTimers2, PERSIST_DEBOUNCE_MS2;
|
|
19080
19524
|
var init_run_log = __esm({
|
|
19081
19525
|
"src/store/run-log.ts"() {
|
|
19082
19526
|
"use strict";
|
|
19083
|
-
|
|
19527
|
+
import_node_crypto10 = require("crypto");
|
|
19084
19528
|
import_node_fs49 = require("fs");
|
|
19085
19529
|
import_node_path49 = require("path");
|
|
19086
19530
|
init_worktree_labels();
|
|
@@ -19506,11 +19950,11 @@ function denyPendingToolPermissions(threadId) {
|
|
|
19506
19950
|
const last = pending[pending.length - 1];
|
|
19507
19951
|
if (last) clearPrompt(threadId, last.id);
|
|
19508
19952
|
}
|
|
19509
|
-
var
|
|
19953
|
+
var import_node_crypto11, import_node_path53, queues, publisher;
|
|
19510
19954
|
var init_tool_permission = __esm({
|
|
19511
19955
|
"src/agents/tool-permission.ts"() {
|
|
19512
19956
|
"use strict";
|
|
19513
|
-
|
|
19957
|
+
import_node_crypto11 = require("crypto");
|
|
19514
19958
|
import_node_path53 = require("path");
|
|
19515
19959
|
init_claude_mcp();
|
|
19516
19960
|
queues = /* @__PURE__ */ new Map();
|
|
@@ -20042,7 +20486,7 @@ function isUnclaimedSteerRequest(req) {
|
|
|
20042
20486
|
}
|
|
20043
20487
|
function nextSteerRequest(existing) {
|
|
20044
20488
|
if (existing?.requestId && !existing.claimedAt) return existing;
|
|
20045
|
-
return { requestId: (0,
|
|
20489
|
+
return { requestId: (0, import_node_crypto12.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
20046
20490
|
}
|
|
20047
20491
|
function claimSteerRequest(threadId) {
|
|
20048
20492
|
const pending = readThread(threadId)?.steerRequest;
|
|
@@ -20051,11 +20495,82 @@ function claimSteerRequest(threadId) {
|
|
|
20051
20495
|
steerRequest: { ...pending, claimedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
20052
20496
|
});
|
|
20053
20497
|
}
|
|
20054
|
-
var
|
|
20498
|
+
var import_node_crypto12;
|
|
20055
20499
|
var init_steer_request = __esm({
|
|
20056
20500
|
"src/orchestrator/steer-request.ts"() {
|
|
20057
20501
|
"use strict";
|
|
20058
|
-
|
|
20502
|
+
import_node_crypto12 = require("crypto");
|
|
20503
|
+
init_thread_store();
|
|
20504
|
+
}
|
|
20505
|
+
});
|
|
20506
|
+
|
|
20507
|
+
// src/orchestrator/ephemeral-automation.ts
|
|
20508
|
+
function ephemeralRunReadyToArchive(thread) {
|
|
20509
|
+
if (!thread.ephemeralAutomationId?.trim()) return false;
|
|
20510
|
+
if (thread.status !== "idle" && thread.status !== "error") return false;
|
|
20511
|
+
if (thread.queue.length > 0) return false;
|
|
20512
|
+
if (thread.agentBlock?.reason?.trim()) return false;
|
|
20513
|
+
if (thread.quotaResumeAt) return false;
|
|
20514
|
+
return true;
|
|
20515
|
+
}
|
|
20516
|
+
function markEphemeralAutomation(threadId, opts) {
|
|
20517
|
+
const id = opts.ephemeralAutomationId?.trim();
|
|
20518
|
+
const current = readThread(threadId);
|
|
20519
|
+
if (!current) throw new Error(`Thread not found: ${threadId}`);
|
|
20520
|
+
if (!id) return current;
|
|
20521
|
+
const title = opts.ephemeralTitle?.trim();
|
|
20522
|
+
return updateThread(threadId, {
|
|
20523
|
+
ephemeralAutomationId: id,
|
|
20524
|
+
...title ? { title, userSetTitle: true } : {}
|
|
20525
|
+
});
|
|
20526
|
+
}
|
|
20527
|
+
async function archiveEphemeralAutomationIfReady(threadId, archive) {
|
|
20528
|
+
const keep = await withThreadLock(threadId, async () => {
|
|
20529
|
+
const thread = readThread(threadId);
|
|
20530
|
+
if (!thread || !ephemeralRunReadyToArchive(thread)) return null;
|
|
20531
|
+
const automationId = thread.ephemeralAutomationId?.trim();
|
|
20532
|
+
if (!automationId) return null;
|
|
20533
|
+
await archive(thread.id);
|
|
20534
|
+
return {
|
|
20535
|
+
automationId,
|
|
20536
|
+
keepId: archivedContinuationId(automationId, threadId) ?? threadId
|
|
20537
|
+
};
|
|
20538
|
+
});
|
|
20539
|
+
if (keep) forgetOlderEphemeralRuns(keep.automationId, keep.keepId);
|
|
20540
|
+
}
|
|
20541
|
+
function archivedContinuationId(automationId, sourceId) {
|
|
20542
|
+
for (const thread of listThreads({ includeArchived: true })) {
|
|
20543
|
+
if (thread.id === sourceId || thread.status !== "archived") continue;
|
|
20544
|
+
if (thread.ephemeralAutomationId !== automationId) continue;
|
|
20545
|
+
if (thread.quotaContinuedFromId !== sourceId) continue;
|
|
20546
|
+
return thread.id;
|
|
20547
|
+
}
|
|
20548
|
+
return null;
|
|
20549
|
+
}
|
|
20550
|
+
function clearEphemeralAutomation(threadId) {
|
|
20551
|
+
const thread = readThread(threadId);
|
|
20552
|
+
if (!thread?.ephemeralAutomationId?.trim()) return;
|
|
20553
|
+
updateThread(threadId, { ephemeralAutomationId: void 0 });
|
|
20554
|
+
}
|
|
20555
|
+
function forgetOlderEphemeralRuns(automationId, keepId) {
|
|
20556
|
+
const id = automationId.trim();
|
|
20557
|
+
if (!id || !keepId) return [];
|
|
20558
|
+
const removed = [];
|
|
20559
|
+
for (const thread of listThreads({ includeArchived: true })) {
|
|
20560
|
+
if (thread.id === keepId) continue;
|
|
20561
|
+
if (thread.status !== "archived") continue;
|
|
20562
|
+
if (thread.ephemeralAutomationId !== id) continue;
|
|
20563
|
+
try {
|
|
20564
|
+
deleteThreadRecord(thread.id);
|
|
20565
|
+
removed.push(thread.id);
|
|
20566
|
+
} catch {
|
|
20567
|
+
}
|
|
20568
|
+
}
|
|
20569
|
+
return removed;
|
|
20570
|
+
}
|
|
20571
|
+
var init_ephemeral_automation = __esm({
|
|
20572
|
+
"src/orchestrator/ephemeral-automation.ts"() {
|
|
20573
|
+
"use strict";
|
|
20059
20574
|
init_thread_store();
|
|
20060
20575
|
}
|
|
20061
20576
|
});
|
|
@@ -20852,8 +21367,8 @@ function resolveAbleTimeOAuthHost(requested, saved) {
|
|
|
20852
21367
|
return fromSaved || void 0;
|
|
20853
21368
|
}
|
|
20854
21369
|
function createAbleTimePkce() {
|
|
20855
|
-
const verifier = (0,
|
|
20856
|
-
const challenge = (0,
|
|
21370
|
+
const verifier = (0, import_node_crypto13.randomBytes)(32).toString("base64url");
|
|
21371
|
+
const challenge = (0, import_node_crypto13.createHash)("sha256").update(verifier).digest("base64url");
|
|
20857
21372
|
return { verifier, challenge };
|
|
20858
21373
|
}
|
|
20859
21374
|
function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
|
|
@@ -20958,7 +21473,7 @@ async function startAbleTimeOAuth(opts) {
|
|
|
20958
21473
|
opts?.host,
|
|
20959
21474
|
loadAppSettings().integrations.abletimeHost
|
|
20960
21475
|
);
|
|
20961
|
-
const state = (0,
|
|
21476
|
+
const state = (0, import_node_crypto13.randomBytes)(16).toString("hex");
|
|
20962
21477
|
const pkce = createAbleTimePkce();
|
|
20963
21478
|
const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
|
|
20964
21479
|
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
@@ -21058,11 +21573,11 @@ async function startAbleTimeOAuth(opts) {
|
|
|
21058
21573
|
return loadAppSettings();
|
|
21059
21574
|
}
|
|
21060
21575
|
}
|
|
21061
|
-
var
|
|
21576
|
+
var import_node_crypto13, import_node_http2, ABLETIME_OAUTH_CALLBACK_PATH, ABLETIME_OAUTH_PORT, ABLETIME_OAUTH_LOCAL_CALLBACK, ABLETIME_OAUTH_REDIRECT, BOUNCE_QUERY_KEYS, BAKED_ABLETIME_OAUTH_CLIENT_ID, ABLETIME_OAUTH_RESOURCE, REFRESH_SKEW_MS2, refreshInFlight2, ABLETIME_OAUTH_CANCELLED, AbleTimeOAuthCancelledError;
|
|
21062
21577
|
var init_abletime_oauth = __esm({
|
|
21063
21578
|
"src/integrations/abletime-oauth.ts"() {
|
|
21064
21579
|
"use strict";
|
|
21065
|
-
|
|
21580
|
+
import_node_crypto13 = require("crypto");
|
|
21066
21581
|
import_node_http2 = require("http");
|
|
21067
21582
|
init_fetch();
|
|
21068
21583
|
init_app_settings();
|
|
@@ -21823,7 +22338,7 @@ function uniqueAttachmentFileName(dir, originalName) {
|
|
|
21823
22338
|
const candidate = `${stem}-${i}${ext}`;
|
|
21824
22339
|
if (!(0, import_node_fs53.existsSync)((0, import_node_path54.join)(dir, candidate))) return candidate;
|
|
21825
22340
|
}
|
|
21826
|
-
return `${stem}-${(0,
|
|
22341
|
+
return `${stem}-${(0, import_node_crypto14.randomUUID)()}${ext}`;
|
|
21827
22342
|
}
|
|
21828
22343
|
function extractHttpUrls(markdown) {
|
|
21829
22344
|
const text7 = markdown ?? "";
|
|
@@ -21929,13 +22444,13 @@ async function downloadAuthenticatedFile(opts) {
|
|
|
21929
22444
|
...contentType ? { contentType } : {}
|
|
21930
22445
|
};
|
|
21931
22446
|
}
|
|
21932
|
-
var import_node_fs53, import_node_path54,
|
|
22447
|
+
var import_node_fs53, import_node_path54, import_node_crypto14, ISSUE_ATTACHMENT_MAX_BYTES, MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE, EXT_BY_MIME;
|
|
21933
22448
|
var init_issue_attachments = __esm({
|
|
21934
22449
|
"src/integrations/issue-attachments.ts"() {
|
|
21935
22450
|
"use strict";
|
|
21936
22451
|
import_node_fs53 = require("fs");
|
|
21937
22452
|
import_node_path54 = require("path");
|
|
21938
|
-
|
|
22453
|
+
import_node_crypto14 = require("crypto");
|
|
21939
22454
|
init_fetch();
|
|
21940
22455
|
init_workspace_scratch();
|
|
21941
22456
|
ISSUE_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;
|
|
@@ -23554,7 +24069,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
|
23554
24069
|
...instructions
|
|
23555
24070
|
].join("\n");
|
|
23556
24071
|
return {
|
|
23557
|
-
id: (0,
|
|
24072
|
+
id: (0, import_node_crypto15.randomUUID)(),
|
|
23558
24073
|
name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
|
|
23559
24074
|
kind: "transcript",
|
|
23560
24075
|
content: body
|
|
@@ -23568,17 +24083,23 @@ async function isolateQuotaFailover(run2) {
|
|
|
23568
24083
|
}
|
|
23569
24084
|
}
|
|
23570
24085
|
function createQuotaFailoverChat(from, fallbackAgent, limitText, opts) {
|
|
24086
|
+
const ephemeralAutomationId = from.ephemeralAutomationId?.trim();
|
|
24087
|
+
const ephemeralTitle = ephemeralAutomationId ? from.title.trim() : "";
|
|
23571
24088
|
const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
|
|
23572
24089
|
const tab = createChatTab({
|
|
23573
24090
|
fromThreadId: from.id,
|
|
23574
24091
|
agent: fallbackAgent,
|
|
23575
24092
|
model: null,
|
|
23576
24093
|
attachments: [handoff],
|
|
24094
|
+
...ephemeralTitle ? { title: ephemeralTitle } : {},
|
|
23577
24095
|
...opts && "accountId" in opts ? { accountId: opts.accountId } : {}
|
|
23578
24096
|
});
|
|
23579
24097
|
const patch = {
|
|
23580
24098
|
quotaContinuedFromId: from.id,
|
|
23581
|
-
sourceRef: from.sourceRef
|
|
24099
|
+
sourceRef: from.sourceRef,
|
|
24100
|
+
// The original drain archives as idle. This sibling has to carry the one-off
|
|
24101
|
+
// so it archives when the continued turn finishes and History keeps it.
|
|
24102
|
+
...ephemeralAutomationId ? { ephemeralAutomationId } : {}
|
|
23582
24103
|
};
|
|
23583
24104
|
if (isOrchestratorThread(from)) {
|
|
23584
24105
|
patch.parentThreadId = from.id;
|
|
@@ -23586,11 +24107,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText, opts) {
|
|
|
23586
24107
|
}
|
|
23587
24108
|
return updateThread(tab.id, patch);
|
|
23588
24109
|
}
|
|
23589
|
-
var
|
|
24110
|
+
var import_node_crypto15, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
|
|
23590
24111
|
var init_quota_failover = __esm({
|
|
23591
24112
|
"src/orchestrator/quota-failover.ts"() {
|
|
23592
24113
|
"use strict";
|
|
23593
|
-
|
|
24114
|
+
import_node_crypto15 = require("crypto");
|
|
23594
24115
|
init_session_quota();
|
|
23595
24116
|
init_app_settings();
|
|
23596
24117
|
init_managed_accounts();
|
|
@@ -23697,7 +24218,7 @@ function listConductorWorkspaces() {
|
|
|
23697
24218
|
if (!(0, import_node_fs56.existsSync)(CONDUCTOR_DB)) {
|
|
23698
24219
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
23699
24220
|
}
|
|
23700
|
-
const tmp = (0, import_node_fs56.mkdtempSync)((0, import_node_path56.join)((0,
|
|
24221
|
+
const tmp = (0, import_node_fs56.mkdtempSync)((0, import_node_path56.join)((0, import_node_os17.tmpdir)(), "sideboard-conductor-"));
|
|
23701
24222
|
const snapshot = (0, import_node_path56.join)(tmp, "conductor.db");
|
|
23702
24223
|
try {
|
|
23703
24224
|
(0, import_node_fs56.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
@@ -23788,7 +24309,7 @@ function importConductorWorkspace(workspaceId) {
|
|
|
23788
24309
|
if (!(0, import_node_fs56.existsSync)(CONDUCTOR_DB)) {
|
|
23789
24310
|
throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
|
|
23790
24311
|
}
|
|
23791
|
-
const tmp = (0, import_node_fs56.mkdtempSync)((0, import_node_path56.join)((0,
|
|
24312
|
+
const tmp = (0, import_node_fs56.mkdtempSync)((0, import_node_path56.join)((0, import_node_os17.tmpdir)(), "sideboard-conductor-"));
|
|
23792
24313
|
const snapshot = (0, import_node_path56.join)(tmp, "conductor.db");
|
|
23793
24314
|
try {
|
|
23794
24315
|
(0, import_node_fs56.copyFileSync)(CONDUCTOR_DB, snapshot);
|
|
@@ -23883,13 +24404,13 @@ function importConductorWorkspace(workspaceId) {
|
|
|
23883
24404
|
async function importConductorWorkspaceAsync(workspaceId) {
|
|
23884
24405
|
return importConductorWorkspace(workspaceId);
|
|
23885
24406
|
}
|
|
23886
|
-
var import_node_child_process5, import_node_fs56,
|
|
24407
|
+
var import_node_child_process5, import_node_fs56, import_node_os17, import_node_path56, import_node_module6, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
|
|
23887
24408
|
var init_adopt = __esm({
|
|
23888
24409
|
"src/threads/adopt.ts"() {
|
|
23889
24410
|
"use strict";
|
|
23890
24411
|
import_node_child_process5 = require("child_process");
|
|
23891
24412
|
import_node_fs56 = require("fs");
|
|
23892
|
-
|
|
24413
|
+
import_node_os17 = require("os");
|
|
23893
24414
|
import_node_path56 = require("path");
|
|
23894
24415
|
import_node_module6 = require("module");
|
|
23895
24416
|
init_worktree();
|
|
@@ -24203,9 +24724,187 @@ var init_stack_layers = __esm({
|
|
|
24203
24724
|
}
|
|
24204
24725
|
});
|
|
24205
24726
|
|
|
24727
|
+
// src/git/pr-write.ts
|
|
24728
|
+
async function lookupGithubGraphqlReset(cwd) {
|
|
24729
|
+
const result = await gh(["api", "rate_limit"], cwd, { reject: false });
|
|
24730
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) return void 0;
|
|
24731
|
+
try {
|
|
24732
|
+
const data = JSON.parse(result.stdout);
|
|
24733
|
+
const reset = data.resources?.graphql?.reset;
|
|
24734
|
+
return typeof reset === "number" ? reset : void 0;
|
|
24735
|
+
} catch {
|
|
24736
|
+
return void 0;
|
|
24737
|
+
}
|
|
24738
|
+
}
|
|
24739
|
+
async function formatPrCreateFailure(raw, cwd, ctx) {
|
|
24740
|
+
if (!isGhRateLimitError(raw)) {
|
|
24741
|
+
return formatGhLandError(raw, {
|
|
24742
|
+
targetedRepo: ctx?.slug,
|
|
24743
|
+
headRef: ctx?.head
|
|
24744
|
+
});
|
|
24745
|
+
}
|
|
24746
|
+
const resetAt = await lookupGithubGraphqlReset(cwd);
|
|
24747
|
+
return formatGhLandError(raw, {
|
|
24748
|
+
resetAt,
|
|
24749
|
+
targetedRepo: ctx?.slug,
|
|
24750
|
+
headRef: ctx?.head
|
|
24751
|
+
});
|
|
24752
|
+
}
|
|
24753
|
+
async function createCursorPullRequest(worktreePath, slug, opts) {
|
|
24754
|
+
const bodyText = (opts.body ?? opts.title).trim() || opts.title;
|
|
24755
|
+
const bodyFile = (0, import_node_path57.join)(
|
|
24756
|
+
(0, import_node_os18.tmpdir)(),
|
|
24757
|
+
`sideboard-pr-body-${process.pid}-${(0, import_node_crypto16.randomBytes)(6).toString("hex")}.md`
|
|
24758
|
+
);
|
|
24759
|
+
(0, import_node_fs58.writeFileSync)(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
|
|
24760
|
+
`, "utf8");
|
|
24761
|
+
try {
|
|
24762
|
+
return await createOrUpdateCursorPr(worktreePath, slug, {
|
|
24763
|
+
title: opts.title,
|
|
24764
|
+
bodyFile,
|
|
24765
|
+
base: opts.base,
|
|
24766
|
+
head: opts.head,
|
|
24767
|
+
draft: opts.draft
|
|
24768
|
+
});
|
|
24769
|
+
} finally {
|
|
24770
|
+
try {
|
|
24771
|
+
(0, import_node_fs58.unlinkSync)(bodyFile);
|
|
24772
|
+
} catch {
|
|
24773
|
+
}
|
|
24774
|
+
}
|
|
24775
|
+
}
|
|
24776
|
+
function ghPrCreateAssigneeArgs(assignee) {
|
|
24777
|
+
const raw = assignee?.trim();
|
|
24778
|
+
if (raw && /^(none|unassigned)$/i.test(raw)) return [];
|
|
24779
|
+
return ["--assignee", raw || "@me"];
|
|
24780
|
+
}
|
|
24781
|
+
async function createOrUpdatePr(worktreePath, opts) {
|
|
24782
|
+
const origin = await resolveOriginRemote(worktreePath);
|
|
24783
|
+
if (origin?.forge === "entire") throw entireUsesTrails();
|
|
24784
|
+
const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
|
|
24785
|
+
if (cursorSlug) return createCursorPullRequest(worktreePath, cursorSlug, opts);
|
|
24786
|
+
await ensureGhPreferOrigin(worktreePath);
|
|
24787
|
+
const slug = await resolveGithubRepoSlug(worktreePath);
|
|
24788
|
+
if (!slug) {
|
|
24789
|
+
throw new Error(
|
|
24790
|
+
"Could not resolve the origin GitHub repo (owner/name) for this worktree. Check that `git remote get-url origin` points at github.com."
|
|
24791
|
+
);
|
|
24792
|
+
}
|
|
24793
|
+
const repoArgs2 = ghRepoSelectArgs(slug);
|
|
24794
|
+
const headRef = ghHeadRef(slug, opts.head);
|
|
24795
|
+
const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
|
|
24796
|
+
const bodyText = clampGithubPrBody(opts.body ?? opts.title);
|
|
24797
|
+
const bodyFile = (0, import_node_path57.join)(
|
|
24798
|
+
(0, import_node_os18.tmpdir)(),
|
|
24799
|
+
`sideboard-pr-body-${process.pid}-${(0, import_node_crypto16.randomBytes)(6).toString("hex")}.md`
|
|
24800
|
+
);
|
|
24801
|
+
(0, import_node_fs58.writeFileSync)(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
|
|
24802
|
+
`, "utf8");
|
|
24803
|
+
const bodyArgs = ["--body-file", bodyFile];
|
|
24804
|
+
try {
|
|
24805
|
+
return await createOrUpdatePrWithBodyFile(worktreePath, {
|
|
24806
|
+
...opts,
|
|
24807
|
+
slug,
|
|
24808
|
+
repoArgs: repoArgs2,
|
|
24809
|
+
headRef,
|
|
24810
|
+
branchOnly,
|
|
24811
|
+
bodyArgs
|
|
24812
|
+
});
|
|
24813
|
+
} finally {
|
|
24814
|
+
try {
|
|
24815
|
+
(0, import_node_fs58.unlinkSync)(bodyFile);
|
|
24816
|
+
} catch {
|
|
24817
|
+
}
|
|
24818
|
+
}
|
|
24819
|
+
}
|
|
24820
|
+
async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
|
|
24821
|
+
const { slug, repoArgs: repoArgs2, headRef, branchOnly, bodyArgs } = opts;
|
|
24822
|
+
const existing = await gh(
|
|
24823
|
+
[...repoArgs2, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
|
|
24824
|
+
worktreePath,
|
|
24825
|
+
{ reject: false }
|
|
24826
|
+
);
|
|
24827
|
+
if (existing.exitCode === 0 && existing.stdout.trim() && !opts.web) {
|
|
24828
|
+
const url2 = existing.stdout.trim();
|
|
24829
|
+
await gh(
|
|
24830
|
+
[
|
|
24831
|
+
...repoArgs2,
|
|
24832
|
+
"pr",
|
|
24833
|
+
"edit",
|
|
24834
|
+
branchOnly,
|
|
24835
|
+
"--title",
|
|
24836
|
+
opts.title,
|
|
24837
|
+
...bodyArgs
|
|
24838
|
+
],
|
|
24839
|
+
worktreePath,
|
|
24840
|
+
{ reject: false }
|
|
24841
|
+
);
|
|
24842
|
+
return url2;
|
|
24843
|
+
}
|
|
24844
|
+
if (opts.web) {
|
|
24845
|
+
const args2 = [
|
|
24846
|
+
...repoArgs2,
|
|
24847
|
+
"pr",
|
|
24848
|
+
"create",
|
|
24849
|
+
"--web",
|
|
24850
|
+
"--title",
|
|
24851
|
+
opts.title,
|
|
24852
|
+
...bodyArgs,
|
|
24853
|
+
"--base",
|
|
24854
|
+
opts.base,
|
|
24855
|
+
"--head",
|
|
24856
|
+
headRef
|
|
24857
|
+
];
|
|
24858
|
+
if (opts.draft) args2.push("--draft");
|
|
24859
|
+
args2.push(...ghPrCreateAssigneeArgs(opts.assignee));
|
|
24860
|
+
await gh(args2, worktreePath, { reject: false });
|
|
24861
|
+
const again = await gh(
|
|
24862
|
+
[...repoArgs2, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
|
|
24863
|
+
worktreePath,
|
|
24864
|
+
{ reject: false }
|
|
24865
|
+
);
|
|
24866
|
+
return again.stdout.trim() || "";
|
|
24867
|
+
}
|
|
24868
|
+
const args = [
|
|
24869
|
+
...repoArgs2,
|
|
24870
|
+
"pr",
|
|
24871
|
+
"create",
|
|
24872
|
+
"--title",
|
|
24873
|
+
opts.title,
|
|
24874
|
+
...bodyArgs,
|
|
24875
|
+
"--base",
|
|
24876
|
+
opts.base,
|
|
24877
|
+
"--head",
|
|
24878
|
+
headRef
|
|
24879
|
+
];
|
|
24880
|
+
if (opts.draft) args.push("--draft");
|
|
24881
|
+
args.push(...ghPrCreateAssigneeArgs(opts.assignee));
|
|
24882
|
+
const created = await gh(args, worktreePath, { reject: false });
|
|
24883
|
+
if (created.exitCode !== 0) {
|
|
24884
|
+
const raw = created.stderr.trim() || created.stdout.trim() || "gh pr create failed";
|
|
24885
|
+
throw new Error(await formatPrCreateFailure(raw, worktreePath, { slug, head: headRef }));
|
|
24886
|
+
}
|
|
24887
|
+
const url = created.stdout.trim().split("\n").find((l) => l.startsWith("http")) ?? created.stdout.trim();
|
|
24888
|
+
return url;
|
|
24889
|
+
}
|
|
24890
|
+
var import_node_crypto16, import_node_fs58, import_node_os18, import_node_path57;
|
|
24891
|
+
var init_pr_write = __esm({
|
|
24892
|
+
"src/git/pr-write.ts"() {
|
|
24893
|
+
"use strict";
|
|
24894
|
+
import_node_crypto16 = require("crypto");
|
|
24895
|
+
import_node_fs58 = require("fs");
|
|
24896
|
+
import_node_os18 = require("os");
|
|
24897
|
+
import_node_path57 = require("path");
|
|
24898
|
+
init_gh_errors();
|
|
24899
|
+
init_run();
|
|
24900
|
+
init_cursor_pr();
|
|
24901
|
+
init_worktree();
|
|
24902
|
+
}
|
|
24903
|
+
});
|
|
24904
|
+
|
|
24206
24905
|
// src/diff/diff.ts
|
|
24207
24906
|
async function inspectGitWorktree(worktreePath) {
|
|
24208
|
-
if (!worktreePath || !(0,
|
|
24907
|
+
if (!worktreePath || !(0, import_node_fs59.existsSync)(worktreePath)) return "missing_worktree";
|
|
24209
24908
|
const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
|
|
24210
24909
|
reject: false
|
|
24211
24910
|
});
|
|
@@ -24213,7 +24912,7 @@ async function inspectGitWorktree(worktreePath) {
|
|
|
24213
24912
|
return "ok";
|
|
24214
24913
|
}
|
|
24215
24914
|
async function initializeGitRepository(worktreePath) {
|
|
24216
|
-
if (!worktreePath || !(0,
|
|
24915
|
+
if (!worktreePath || !(0, import_node_fs59.existsSync)(worktreePath)) {
|
|
24217
24916
|
throw new Error("Worktree not found");
|
|
24218
24917
|
}
|
|
24219
24918
|
const status = await inspectGitWorktree(worktreePath);
|
|
@@ -24347,11 +25046,11 @@ new file mode 100644
|
|
|
24347
25046
|
};
|
|
24348
25047
|
}
|
|
24349
25048
|
async function untrackedPatch(worktreePath, path, maxHunk) {
|
|
24350
|
-
const abs = (0,
|
|
25049
|
+
const abs = (0, import_node_path58.join)(worktreePath, path);
|
|
24351
25050
|
try {
|
|
24352
|
-
const st = (0,
|
|
25051
|
+
const st = (0, import_node_fs59.statSync)(abs);
|
|
24353
25052
|
if (st.isFile() && st.size > maxHunk) {
|
|
24354
|
-
const buf = (0,
|
|
25053
|
+
const buf = (0, import_node_fs59.readFileSync)(abs).subarray(0, maxHunk);
|
|
24355
25054
|
return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
|
|
24356
25055
|
}
|
|
24357
25056
|
} catch {
|
|
@@ -24818,9 +25517,9 @@ async function getDiff(worktreePath, repoPath, opts) {
|
|
|
24818
25517
|
}
|
|
24819
25518
|
function statWorktreePath(worktreePath, relativePath) {
|
|
24820
25519
|
assertSafeRelativePath(relativePath);
|
|
24821
|
-
const abs = (0,
|
|
24822
|
-
if (!(0,
|
|
24823
|
-
const st = (0,
|
|
25520
|
+
const abs = (0, import_node_path58.join)(worktreePath, relativePath);
|
|
25521
|
+
if (!(0, import_node_fs59.existsSync)(abs)) return "missing";
|
|
25522
|
+
const st = (0, import_node_fs59.statSync)(abs);
|
|
24824
25523
|
if (st.isDirectory()) return "dir";
|
|
24825
25524
|
if (st.isFile()) return "file";
|
|
24826
25525
|
return "missing";
|
|
@@ -24849,8 +25548,8 @@ function isImageRelativePath(relativePath) {
|
|
|
24849
25548
|
function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
24850
25549
|
assertSafeRelativePath(relativePath);
|
|
24851
25550
|
const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
|
|
24852
|
-
const abs = (0,
|
|
24853
|
-
const st = (0,
|
|
25551
|
+
const abs = (0, import_node_path58.join)(worktreePath, relativePath);
|
|
25552
|
+
const st = (0, import_node_fs59.statSync)(abs);
|
|
24854
25553
|
if (!st.isFile()) {
|
|
24855
25554
|
throw new Error(`Not a file: ${relativePath}`);
|
|
24856
25555
|
}
|
|
@@ -24859,7 +25558,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
24859
25558
|
`File too large to upload (${st.size} bytes; max ${maxBytes})`
|
|
24860
25559
|
);
|
|
24861
25560
|
}
|
|
24862
|
-
const buf = (0,
|
|
25561
|
+
const buf = (0, import_node_fs59.readFileSync)(abs);
|
|
24863
25562
|
return {
|
|
24864
25563
|
path: relativePath,
|
|
24865
25564
|
contentBase64: buf.toString("base64"),
|
|
@@ -24869,12 +25568,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
|
|
|
24869
25568
|
function readWorktreeFile(worktreePath, relativePath, opts) {
|
|
24870
25569
|
assertSafeRelativePath(relativePath);
|
|
24871
25570
|
const maxBytes = opts?.maxBytes ?? 2e5;
|
|
24872
|
-
const abs = (0,
|
|
24873
|
-
const st = (0,
|
|
25571
|
+
const abs = (0, import_node_path58.join)(worktreePath, relativePath);
|
|
25572
|
+
const st = (0, import_node_fs59.statSync)(abs);
|
|
24874
25573
|
if (!st.isFile()) {
|
|
24875
25574
|
throw new Error(`Not a file: ${relativePath}`);
|
|
24876
25575
|
}
|
|
24877
|
-
const buf = (0,
|
|
25576
|
+
const buf = (0, import_node_fs59.readFileSync)(abs);
|
|
24878
25577
|
if (isImageRelativePath(relativePath)) {
|
|
24879
25578
|
const maxImageBytes = Math.max(maxBytes, 15e6);
|
|
24880
25579
|
const truncated2 = buf.length > maxImageBytes;
|
|
@@ -24917,9 +25616,9 @@ function assertSafeRelativePath(relativePath) {
|
|
|
24917
25616
|
}
|
|
24918
25617
|
function writeWorktreeFile(worktreePath, relativePath, content) {
|
|
24919
25618
|
assertSafeRelativePath(relativePath);
|
|
24920
|
-
const abs = (0,
|
|
24921
|
-
(0,
|
|
24922
|
-
(0,
|
|
25619
|
+
const abs = (0, import_node_path58.join)(worktreePath, relativePath);
|
|
25620
|
+
(0, import_node_fs59.mkdirSync)((0, import_node_path58.dirname)(abs), { recursive: true });
|
|
25621
|
+
(0, import_node_fs59.writeFileSync)(abs, content, "utf8");
|
|
24923
25622
|
return { path: relativePath };
|
|
24924
25623
|
}
|
|
24925
25624
|
async function getWorktreeDirtyStat(worktreePath) {
|
|
@@ -24959,12 +25658,12 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
|
|
|
24959
25658
|
truncated: full.files.length > maxFiles
|
|
24960
25659
|
};
|
|
24961
25660
|
}
|
|
24962
|
-
var
|
|
25661
|
+
var import_node_fs59, import_node_path58, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
|
|
24963
25662
|
var init_diff = __esm({
|
|
24964
25663
|
"src/diff/diff.ts"() {
|
|
24965
25664
|
"use strict";
|
|
24966
|
-
|
|
24967
|
-
|
|
25665
|
+
import_node_fs59 = require("fs");
|
|
25666
|
+
import_node_path58 = require("path");
|
|
24968
25667
|
init_run();
|
|
24969
25668
|
init_worktree();
|
|
24970
25669
|
mergeBaseCache = /* @__PURE__ */ new Map();
|
|
@@ -25173,6 +25872,7 @@ async function confirmLand(thread, opts) {
|
|
|
25173
25872
|
var init_land = __esm({
|
|
25174
25873
|
"src/land/land.ts"() {
|
|
25175
25874
|
"use strict";
|
|
25875
|
+
init_pr_write();
|
|
25176
25876
|
init_worktree();
|
|
25177
25877
|
init_diff();
|
|
25178
25878
|
init_pr_metadata();
|
|
@@ -25186,16 +25886,16 @@ function readWorktreeFileTail(worktreePath, relativePath, maxBytes = WORKTREE_TA
|
|
|
25186
25886
|
if (!relativePath || relativePath.includes("..") || relativePath.startsWith("/")) {
|
|
25187
25887
|
throw new Error("Invalid path");
|
|
25188
25888
|
}
|
|
25189
|
-
const abs = (0,
|
|
25190
|
-
const fd = (0,
|
|
25889
|
+
const abs = (0, import_node_path59.join)(worktreePath, relativePath);
|
|
25890
|
+
const fd = (0, import_node_fs60.openSync)(abs, "r");
|
|
25191
25891
|
try {
|
|
25192
|
-
const st = (0,
|
|
25892
|
+
const st = (0, import_node_fs60.fstatSync)(fd);
|
|
25193
25893
|
if (!st.isFile()) throw new Error(`Not a file: ${relativePath}`);
|
|
25194
25894
|
const size = st.size;
|
|
25195
25895
|
const start = Math.max(0, size - maxBytes);
|
|
25196
25896
|
const length = size - start;
|
|
25197
25897
|
const buf = Buffer.alloc(length);
|
|
25198
|
-
if (length > 0) (0,
|
|
25898
|
+
if (length > 0) (0, import_node_fs60.readSync)(fd, buf, 0, length, start);
|
|
25199
25899
|
if (buf.subarray(0, Math.min(buf.length, 8e3)).includes(0)) {
|
|
25200
25900
|
return {
|
|
25201
25901
|
path: relativePath,
|
|
@@ -25213,15 +25913,15 @@ function readWorktreeFileTail(worktreePath, relativePath, maxBytes = WORKTREE_TA
|
|
|
25213
25913
|
}
|
|
25214
25914
|
return { path: relativePath, content, truncated, binary: false, encoding: "utf8" };
|
|
25215
25915
|
} finally {
|
|
25216
|
-
(0,
|
|
25916
|
+
(0, import_node_fs60.closeSync)(fd);
|
|
25217
25917
|
}
|
|
25218
25918
|
}
|
|
25219
|
-
var
|
|
25919
|
+
var import_node_fs60, import_node_path59, WORKTREE_TAIL_BYTES;
|
|
25220
25920
|
var init_read_worktree_tail = __esm({
|
|
25221
25921
|
"src/diff/read-worktree-tail.ts"() {
|
|
25222
25922
|
"use strict";
|
|
25223
|
-
|
|
25224
|
-
|
|
25923
|
+
import_node_fs60 = require("fs");
|
|
25924
|
+
import_node_path59 = require("path");
|
|
25225
25925
|
WORKTREE_TAIL_BYTES = 2e5;
|
|
25226
25926
|
}
|
|
25227
25927
|
});
|
|
@@ -25370,7 +26070,7 @@ function parseFrontmatter(content) {
|
|
|
25370
26070
|
}
|
|
25371
26071
|
function readSkill(skillMd, source) {
|
|
25372
26072
|
try {
|
|
25373
|
-
const content = (0,
|
|
26073
|
+
const content = (0, import_node_fs61.readFileSync)(skillMd, "utf8");
|
|
25374
26074
|
const { name: fmName, description } = parseFrontmatter(content);
|
|
25375
26075
|
const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
|
|
25376
26076
|
const name = fmName || dirName;
|
|
@@ -25389,19 +26089,19 @@ function readSkill(skillMd, source) {
|
|
|
25389
26089
|
}
|
|
25390
26090
|
}
|
|
25391
26091
|
function scanSkillsDir(dir, source, out) {
|
|
25392
|
-
if (!(0,
|
|
26092
|
+
if (!(0, import_node_fs61.existsSync)(dir)) return;
|
|
25393
26093
|
let entries;
|
|
25394
26094
|
try {
|
|
25395
|
-
entries = (0,
|
|
26095
|
+
entries = (0, import_node_fs61.readdirSync)(dir);
|
|
25396
26096
|
} catch {
|
|
25397
26097
|
return;
|
|
25398
26098
|
}
|
|
25399
26099
|
for (const entry of entries) {
|
|
25400
26100
|
if (entry.startsWith(".")) continue;
|
|
25401
|
-
const skillMd = (0,
|
|
25402
|
-
if (!(0,
|
|
26101
|
+
const skillMd = (0, import_node_path60.join)(dir, entry, "SKILL.md");
|
|
26102
|
+
if (!(0, import_node_fs61.existsSync)(skillMd)) continue;
|
|
25403
26103
|
try {
|
|
25404
|
-
if (!(0,
|
|
26104
|
+
if (!(0, import_node_fs61.statSync)(skillMd).isFile()) continue;
|
|
25405
26105
|
} catch {
|
|
25406
26106
|
continue;
|
|
25407
26107
|
}
|
|
@@ -25410,24 +26110,24 @@ function scanSkillsDir(dir, source, out) {
|
|
|
25410
26110
|
}
|
|
25411
26111
|
}
|
|
25412
26112
|
function scanClaudePluginSkills(pluginsRoot, out) {
|
|
25413
|
-
if (!(0,
|
|
26113
|
+
if (!(0, import_node_fs61.existsSync)(pluginsRoot)) return;
|
|
25414
26114
|
const walk = (dir, depth, lookingForSkillsDir) => {
|
|
25415
26115
|
if (depth > 7) return;
|
|
25416
26116
|
let entries;
|
|
25417
26117
|
try {
|
|
25418
|
-
entries = (0,
|
|
26118
|
+
entries = (0, import_node_fs61.readdirSync)(dir);
|
|
25419
26119
|
} catch {
|
|
25420
26120
|
return;
|
|
25421
26121
|
}
|
|
25422
26122
|
if (lookingForSkillsDir && entries.includes("SKILL.md")) {
|
|
25423
|
-
const skill = readSkill((0,
|
|
26123
|
+
const skill = readSkill((0, import_node_path60.join)(dir, "SKILL.md"), "cli");
|
|
25424
26124
|
if (skill) out.push(skill);
|
|
25425
26125
|
}
|
|
25426
26126
|
for (const entry of entries) {
|
|
25427
26127
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
25428
|
-
const full = (0,
|
|
26128
|
+
const full = (0, import_node_path60.join)(dir, entry);
|
|
25429
26129
|
try {
|
|
25430
|
-
if (!(0,
|
|
26130
|
+
if (!(0, import_node_fs61.statSync)(full).isDirectory()) continue;
|
|
25431
26131
|
} catch {
|
|
25432
26132
|
continue;
|
|
25433
26133
|
}
|
|
@@ -25445,17 +26145,17 @@ function discoverSkills(worktreePath) {
|
|
|
25445
26145
|
const home = (0, import_node_os19.homedir)();
|
|
25446
26146
|
const collected = [];
|
|
25447
26147
|
for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
|
|
25448
|
-
scanSkillsDir((0,
|
|
26148
|
+
scanSkillsDir((0, import_node_path60.join)(worktreePath, rel), "workspace", collected);
|
|
25449
26149
|
}
|
|
25450
26150
|
for (const abs of [
|
|
25451
|
-
(0,
|
|
25452
|
-
(0,
|
|
25453
|
-
(0,
|
|
25454
|
-
(0,
|
|
26151
|
+
(0, import_node_path60.join)(home, ".claude/skills"),
|
|
26152
|
+
(0, import_node_path60.join)(home, ".cursor/skills"),
|
|
26153
|
+
(0, import_node_path60.join)(home, ".sideboard/skills"),
|
|
26154
|
+
(0, import_node_path60.join)(home, ".brightsy/skills")
|
|
25455
26155
|
]) {
|
|
25456
26156
|
scanSkillsDir(abs, "user", collected);
|
|
25457
26157
|
}
|
|
25458
|
-
scanClaudePluginSkills((0,
|
|
26158
|
+
scanClaudePluginSkills((0, import_node_path60.join)(home, ".claude/plugins"), collected);
|
|
25459
26159
|
collected.push(...bundledSkills());
|
|
25460
26160
|
const rank = {
|
|
25461
26161
|
workspace: 0,
|
|
@@ -25481,7 +26181,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
|
|
|
25481
26181
|
\u2026(truncated)` : body;
|
|
25482
26182
|
}
|
|
25483
26183
|
}
|
|
25484
|
-
const raw = (0,
|
|
26184
|
+
const raw = (0, import_node_fs61.readFileSync)(skillPath, "utf8");
|
|
25485
26185
|
if (raw.startsWith("---")) {
|
|
25486
26186
|
const end = raw.indexOf("\n---", 3);
|
|
25487
26187
|
if (end >= 0) {
|
|
@@ -25495,13 +26195,13 @@ function readSkillBody(skillPath, maxChars = 12e3) {
|
|
|
25495
26195
|
|
|
25496
26196
|
\u2026(truncated)` : raw;
|
|
25497
26197
|
}
|
|
25498
|
-
var
|
|
26198
|
+
var import_node_fs61, import_node_os19, import_node_path60, BUNDLED_SKILL_PREFIX;
|
|
25499
26199
|
var init_discover = __esm({
|
|
25500
26200
|
"src/skills/discover.ts"() {
|
|
25501
26201
|
"use strict";
|
|
25502
|
-
|
|
26202
|
+
import_node_fs61 = require("fs");
|
|
25503
26203
|
import_node_os19 = require("os");
|
|
25504
|
-
|
|
26204
|
+
import_node_path60 = require("path");
|
|
25505
26205
|
init_long_running();
|
|
25506
26206
|
BUNDLED_SKILL_PREFIX = "bundled:";
|
|
25507
26207
|
}
|
|
@@ -25712,7 +26412,7 @@ function promptForJoinedSteer(input) {
|
|
|
25712
26412
|
rewriteTrailingUserPrompt(input.threadId, input.promptText, input.latestPrompt);
|
|
25713
26413
|
const { agentPrompt: expandedLatest } = expandComposerPrompt(
|
|
25714
26414
|
input.worktreePath,
|
|
25715
|
-
expandCanonicalGitRequest(input.latestPrompt),
|
|
26415
|
+
expandCanonicalGitRequest(input.latestPrompt, { forge: input.gitForge }),
|
|
25716
26416
|
{ attachments: input.attachments }
|
|
25717
26417
|
);
|
|
25718
26418
|
return input.expandedPrompt && input.agentPrompt.endsWith(input.expandedPrompt) ? input.agentPrompt.slice(0, -input.expandedPrompt.length) + expandedLatest : `${input.agentPrompt}
|
|
@@ -25820,6 +26520,33 @@ var init_enqueue_thread_send = __esm({
|
|
|
25820
26520
|
}
|
|
25821
26521
|
});
|
|
25822
26522
|
|
|
26523
|
+
// src/agents/forge-git-directives.ts
|
|
26524
|
+
function formatCursorGitDirective(slug) {
|
|
26525
|
+
const repo = slug?.trim() || "<origin-owner/name>";
|
|
26526
|
+
return [
|
|
26527
|
+
`Git host (Settings \u2192 Git \u2192 Cursor): this checkout is on Cursor (\`origin.cursor.com\`, \`${repo}\`).`,
|
|
26528
|
+
"- Use the `origin` CLI, not `gh`. Where commands overlap, flags follow `gh` (`origin <command> --help`).",
|
|
26529
|
+
"- `origin` already authenticates from `CURSOR_AUTH_TOKEN`, then `CURSOR_API_KEY`, then the login stored in Settings \u2192 Git \u2192 Cursor. Do not read those values, set them, or run `origin auth login` from this turn.",
|
|
26530
|
+
"- Push with `git push -u origin HEAD`. If git asks for credentials, tell the user to open Settings \u2192 Git \u2192 Cursor and choose Configure git (`origin auth setup-git`). Do not wait for a Keychain dialog.",
|
|
26531
|
+
"- Origin has no issue tracker. Tickets for this repo come from Linear or AbleTime via `list_issues`. Do not use `gh issue`."
|
|
26532
|
+
].join("\n");
|
|
26533
|
+
}
|
|
26534
|
+
function formatEntireGitDirective(slug) {
|
|
26535
|
+
const repo = slug?.trim() || "<origin-owner/name>";
|
|
26536
|
+
return [
|
|
26537
|
+
`Git host (Settings \u2192 Git \u2192 Entire): this checkout is on Entire (\`${repo}\`).`,
|
|
26538
|
+
"- Use the `entire` CLI, not `gh` or `origin`. Trails replace pull requests and GitHub issues.",
|
|
26539
|
+
"- `entire` uses the login from Settings \u2192 Git \u2192 Entire. Do not read tokens, set `ENTIRE_TOKEN`, or run `entire login` from this turn.",
|
|
26540
|
+
"- Push with `git push -u origin HEAD`. If git asks for credentials, tell the user to open Settings \u2192 Git \u2192 Entire and log in. Do not wait for a Keychain dialog.",
|
|
26541
|
+
"- Tickets for this repo are Trails (`entire trail list` / `entire trail show`). Do not use `gh issue`."
|
|
26542
|
+
].join("\n");
|
|
26543
|
+
}
|
|
26544
|
+
var init_forge_git_directives = __esm({
|
|
26545
|
+
"src/agents/forge-git-directives.ts"() {
|
|
26546
|
+
"use strict";
|
|
26547
|
+
}
|
|
26548
|
+
});
|
|
26549
|
+
|
|
25823
26550
|
// src/skills/detached-job-path.ts
|
|
25824
26551
|
function compiledModuleDir() {
|
|
25825
26552
|
const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
|
|
@@ -25827,7 +26554,7 @@ function compiledModuleDir() {
|
|
|
25827
26554
|
try {
|
|
25828
26555
|
const url = import_meta8.url;
|
|
25829
26556
|
if (typeof url === "string" && url.length > 0) {
|
|
25830
|
-
return (0,
|
|
26557
|
+
return (0, import_node_path61.dirname)((0, import_node_url5.fileURLToPath)(url));
|
|
25831
26558
|
}
|
|
25832
26559
|
} catch {
|
|
25833
26560
|
}
|
|
@@ -25835,8 +26562,8 @@ function compiledModuleDir() {
|
|
|
25835
26562
|
}
|
|
25836
26563
|
function detachedJobScriptIn(dir) {
|
|
25837
26564
|
for (const name of DETACHED_JOB_FILENAMES) {
|
|
25838
|
-
const script = (0,
|
|
25839
|
-
if ((0,
|
|
26565
|
+
const script = (0, import_node_path61.join)(dir, "scripts", name);
|
|
26566
|
+
if ((0, import_node_fs62.existsSync)(script)) return script;
|
|
25840
26567
|
}
|
|
25841
26568
|
return null;
|
|
25842
26569
|
}
|
|
@@ -25853,7 +26580,7 @@ function resolveDetachedJobScript() {
|
|
|
25853
26580
|
for (let i = 0; i < 8; i++) {
|
|
25854
26581
|
const candidate = detachedJobScriptIn(dir);
|
|
25855
26582
|
if (candidate) return candidate;
|
|
25856
|
-
const parent = (0,
|
|
26583
|
+
const parent = (0, import_node_path61.dirname)(dir);
|
|
25857
26584
|
if (parent === dir) break;
|
|
25858
26585
|
dir = parent;
|
|
25859
26586
|
}
|
|
@@ -25864,12 +26591,12 @@ function formatDetachedJobInvoke(scriptPath) {
|
|
|
25864
26591
|
if (resolved) return `node ${JSON.stringify(resolved)}`;
|
|
25865
26592
|
return "node scripts/detached-job.cjs";
|
|
25866
26593
|
}
|
|
25867
|
-
var
|
|
26594
|
+
var import_node_fs62, import_node_path61, import_node_url5, import_meta8, DETACHED_JOB_FILENAMES;
|
|
25868
26595
|
var init_detached_job_path = __esm({
|
|
25869
26596
|
"src/skills/detached-job-path.ts"() {
|
|
25870
26597
|
"use strict";
|
|
25871
|
-
|
|
25872
|
-
|
|
26598
|
+
import_node_fs62 = require("fs");
|
|
26599
|
+
import_node_path61 = require("path");
|
|
25873
26600
|
import_node_url5 = require("url");
|
|
25874
26601
|
init_packaged_runtime();
|
|
25875
26602
|
import_meta8 = {};
|
|
@@ -25957,17 +26684,35 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
25957
26684
|
lines.push(
|
|
25958
26685
|
"- 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)."
|
|
25959
26686
|
);
|
|
25960
|
-
|
|
26687
|
+
const cursorRepo = opts?.originForge === "cursor";
|
|
26688
|
+
const entireRepo = opts?.originForge === "entire";
|
|
26689
|
+
if (cursorRepo) {
|
|
26690
|
+
lines.push(formatCursorGitDirective(opts?.originSlug));
|
|
26691
|
+
} else if (entireRepo) {
|
|
26692
|
+
lines.push(formatEntireGitDirective(opts?.originSlug));
|
|
26693
|
+
} else {
|
|
26694
|
+
lines.push(formatGitAuthModeDirective(opts?.gitAuthMode ?? "auto"));
|
|
26695
|
+
}
|
|
25961
26696
|
lines.push(
|
|
25962
26697
|
"- PR title/body and commit messages state what the change does and why (inspect the diff/commits and the user request) \u2014 never the worktree nickname or placeholder branch. Concise imperative title (Conventional Commits when it fits: feat:/fix:/chore:/docs:); body = intent, key changes, test notes."
|
|
25963
26698
|
);
|
|
25964
26699
|
lines.push(
|
|
25965
|
-
"- Stay on this workspace branch. Never push to main/master or merge locally into the main checkout. Merge the PR only when this turn is a Merge PR request or the user explicitly asked \u2014 via GitHub from this worktree (`gh pr merge` / `gh stack merge`)."
|
|
26700
|
+
cursorRepo ? "- Stay on this workspace branch. Never push to main/master or merge locally into the main checkout. Merge the PR only when this turn is a Merge PR request or the user explicitly asked \u2014 via Origin from this worktree (`origin pr merge` / `origin stack merge`)." : entireRepo ? "- Stay on this workspace branch. Never push to main/master or merge locally into the main checkout. Merge only when this turn is a Merge PR request or the user explicitly asked \u2014 via Entire from this worktree (`entire trail merge`)." : "- Stay on this workspace branch. Never push to main/master or merge locally into the main checkout. Merge the PR only when this turn is a Merge PR request or the user explicitly asked \u2014 via GitHub from this worktree (`gh pr merge` / `gh stack merge`)."
|
|
25966
26701
|
);
|
|
25967
26702
|
if (thread.prUrl) {
|
|
25968
26703
|
lines.push(
|
|
25969
26704
|
`- A PR already exists (${thread.prUrl}). Update it (push + edit title/body if the purpose drifted) instead of opening a duplicate.`
|
|
25970
26705
|
);
|
|
26706
|
+
} else if (entireRepo) {
|
|
26707
|
+
lines.push(
|
|
26708
|
+
'- Prefer a Trail: `entire trail create --title "<title>" --body "<short>"`. Take the URL from `--json`. If this branch already has a trail, `entire trail update` instead of creating another. Trails have no draft state. Do not use `gh` or `origin`.'
|
|
26709
|
+
);
|
|
26710
|
+
} else if (cursorRepo) {
|
|
26711
|
+
const slug = opts?.originSlug?.trim() || "<origin-owner/name>";
|
|
26712
|
+
const resolve3 = opts?.originSlug?.trim() ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
|
|
26713
|
+
lines.push(
|
|
26714
|
+
`- Prefer a draft PR first: \`origin pr create --draft --push -R ${slug}\` (update via \`origin pr edit -R ${slug}\`). Take the URL from \`--json url\` \u2014 do not build it. Do not use \`gh\`.${resolve3} Mark ready only when asked (\`origin pr ready\`). If the body is too long, retry with a short \`--body-file\`.`
|
|
26715
|
+
);
|
|
25971
26716
|
} else {
|
|
25972
26717
|
const slug = opts?.githubSlug ?? "<origin-owner/name>";
|
|
25973
26718
|
const resolve3 = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
|
|
@@ -25982,7 +26727,7 @@ function formatWorktreeDirective(thread, opts) {
|
|
|
25982
26727
|
lines.push(formatDevAccessHint(opts?.activeRuns));
|
|
25983
26728
|
lines.push("");
|
|
25984
26729
|
lines.push(
|
|
25985
|
-
"
|
|
26730
|
+
"Automations (sidebar, after Board): `create_schedule` / `list_schedules` (`at` / `every` / `cron`). `chatId=self` continues this chat; omit chatId for a new Global chat. Fires only while Sideboard.app is running. A future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled."
|
|
25986
26731
|
);
|
|
25987
26732
|
lines.push(
|
|
25988
26733
|
"`set_workspace_tags` labels this checkout (omit ref; replace, add, or remove). Not another checkout."
|
|
@@ -26002,7 +26747,7 @@ function connectedOrchestratorTurnReminder(thread, find) {
|
|
|
26002
26747
|
return formatConnectedOrchestratorReminder(connectedOrchestrator(thread, find));
|
|
26003
26748
|
}
|
|
26004
26749
|
function formatWorktreeReminder() {
|
|
26005
|
-
return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push.
|
|
26750
|
+
return "Sideboard workspace: stay in this cwd for all file and git work. Push and open PRs against origin, never upstream. Do not edit the main repo checkout. If a goal is given (Greptile 5/5, CI green), watch-fix-push until it lands \u2014 do not watch after every push. Automations: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
|
|
26006
26751
|
}
|
|
26007
26752
|
function formatClaudeSubagentReminder() {
|
|
26008
26753
|
return 'Claude subagents: if Task/Agent workers will edit files, write a short plan in this chat and continue in the same turn. Do not ask the user whether to isolate, and do not set worktree.bgIsolation to none. One writer may edit this workspace. Every other writer is an Agent with isolation "worktree" and a disjoint file list (read-only explore agents may overlap). While those agents run, leave their files alone. Merge each returned branch into this workspace, then remove the temporary worktree. You stay in this cwd. If Claude says another write-capable agent is in this directory, or that writes to the shared checkout are blocked, re-spawn that agent with isolation "worktree" or make the edit yourself. That note is another worker in this chat, or a worker Claude still has marked running. Do not mention it to the user.';
|
|
@@ -26085,9 +26830,9 @@ function formatPrGateDirective() {
|
|
|
26085
26830
|
}
|
|
26086
26831
|
function formatProcessGuideDirective(opts) {
|
|
26087
26832
|
const worktree = opts?.worktreePath?.trim();
|
|
26088
|
-
const hasGraphSkill = !!worktree && (0,
|
|
26089
|
-
const hasReviewSkill = !!worktree && (0,
|
|
26090
|
-
const hasInstallSkill = !!worktree && (0,
|
|
26833
|
+
const hasGraphSkill = !!worktree && (0, import_node_fs63.existsSync)((0, import_node_path62.join)(worktree, ".claude/skills/graph-engineering/SKILL.md"));
|
|
26834
|
+
const hasReviewSkill = !!worktree && (0, import_node_fs63.existsSync)((0, import_node_path62.join)(worktree, ".claude/skills/review/SKILL.md"));
|
|
26835
|
+
const hasInstallSkill = !!worktree && (0, import_node_fs63.existsSync)((0, import_node_path62.join)(worktree, ".claude/skills/worktree-install/SKILL.md"));
|
|
26091
26836
|
return [
|
|
26092
26837
|
"Process guides (recurring work only):",
|
|
26093
26838
|
hasGraphSkill ? "- Migrations, ports, batch fixes, and other fan-out: follow `.claude/skills/graph-engineering/SKILL.md` (`/graph-engineering`). Judge first; state on disk; grow the rulebook; do not patch around it." : null,
|
|
@@ -26127,13 +26872,14 @@ function formatArtifactDirective() {
|
|
|
26127
26872
|
function formatUiReminder() {
|
|
26128
26873
|
return "Sideboard UI: markdown table is enough to read data; present_schema if they ask to edit/filter (even after markdown); present_files for the file manager. html fence or present_artifact, not both for the same document. type=log appends (same artifact_id, new lines only). Chat links: `[name](sideboard://artifact/<id-or-title>)` opens that pane; `[name](sideboard://file/path#L12-L40)` opens a file. ask_user only for a real multiple-choice (not hellos, \u201Cwhat next?\u201D, or after a review) \u2014 reply in chat. ask_user wakes the parent orchestrator; notify_orchestrator if blocked without a picker. Do not say artifacts/CMS UI are unavailable. Dev / preview: run_dev_script (same as the Dev button; pass name \u2014 several scripts can run at once) returns {url, port, preview} (SIDEBOARD_PORT \u2014 never guess :3000). preview=url \u2192 open url; preview=window \u2192 Electron window is the app, url is renderer/HMR only. list_run_scripts active[].url if already running; get_run_log for that script\u2019s Run-tab terminal; stop_dev_script to stop one.";
|
|
26129
26874
|
}
|
|
26130
|
-
var
|
|
26875
|
+
var import_node_fs63, import_node_path62, PR_GOAL_RE, GITHUB_TICKET_REF, KEYED_TICKET_REF;
|
|
26131
26876
|
var init_instructions = __esm({
|
|
26132
26877
|
"src/agents/instructions.ts"() {
|
|
26133
26878
|
"use strict";
|
|
26134
|
-
|
|
26135
|
-
|
|
26879
|
+
import_node_fs63 = require("fs");
|
|
26880
|
+
import_node_path62 = require("path");
|
|
26136
26881
|
init_git_auth_mode();
|
|
26882
|
+
init_forge_git_directives();
|
|
26137
26883
|
init_branch_prefix();
|
|
26138
26884
|
init_worktree_labels();
|
|
26139
26885
|
init_detached_job_path();
|
|
@@ -26482,6 +27228,7 @@ function compactIssueRow(issue, opts) {
|
|
|
26482
27228
|
function formatMcpIssueList(input) {
|
|
26483
27229
|
const truncated = input.truncated || Boolean(input.commentsTruncated);
|
|
26484
27230
|
const comments = input.comments ?? [];
|
|
27231
|
+
const pageHint = input.unavailableReason ? input.unavailableReason : truncated ? input.since ? "More issues or comments match. Pass a higher limit (max 250) or a tighter query." : "More issues match. Pass a higher limit (max 250) or a tighter query." : void 0;
|
|
26485
27232
|
return {
|
|
26486
27233
|
...input.source ? { source: input.source } : {},
|
|
26487
27234
|
...input.viewer ? { viewer: input.viewer } : {},
|
|
@@ -26489,9 +27236,8 @@ function formatMcpIssueList(input) {
|
|
|
26489
27236
|
returned: input.issues.length,
|
|
26490
27237
|
truncated,
|
|
26491
27238
|
...input.since ? { since: input.since } : {},
|
|
26492
|
-
...
|
|
26493
|
-
|
|
26494
|
-
} : {},
|
|
27239
|
+
...input.unavailableReason ? { unavailableReason: input.unavailableReason } : {},
|
|
27240
|
+
...pageHint ? { hint: pageHint } : {},
|
|
26495
27241
|
issues: input.issues.map((issue) => compactIssueRow(issue, { since: input.since })),
|
|
26496
27242
|
...input.since || comments.length > 0 ? { comments } : {}
|
|
26497
27243
|
};
|
|
@@ -26508,7 +27254,8 @@ function formatListedIssuesForMcp(result, page) {
|
|
|
26508
27254
|
truncated: windowed.truncated,
|
|
26509
27255
|
since: result.since,
|
|
26510
27256
|
comments: commentWindow.items,
|
|
26511
|
-
commentsTruncated: commentWindow.truncated
|
|
27257
|
+
commentsTruncated: commentWindow.truncated,
|
|
27258
|
+
unavailableReason: result.unavailableReason
|
|
26512
27259
|
});
|
|
26513
27260
|
}
|
|
26514
27261
|
function mcpJson(payload, isError = false) {
|
|
@@ -26873,40 +27620,40 @@ __export(plan_file_exports, {
|
|
|
26873
27620
|
writePlanFile: () => writePlanFile
|
|
26874
27621
|
});
|
|
26875
27622
|
function ensureAttachmentsGitignore(worktreePath) {
|
|
26876
|
-
const gitignoreAbs = (0,
|
|
26877
|
-
if ((0,
|
|
26878
|
-
(0,
|
|
26879
|
-
(0,
|
|
27623
|
+
const gitignoreAbs = (0, import_node_path63.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
27624
|
+
if ((0, import_node_fs64.existsSync)(gitignoreAbs)) return;
|
|
27625
|
+
(0, import_node_fs64.mkdirSync)((0, import_node_path63.dirname)(gitignoreAbs), { recursive: true });
|
|
27626
|
+
(0, import_node_fs64.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
|
|
26880
27627
|
}
|
|
26881
27628
|
function planFileAbs(worktreePath) {
|
|
26882
|
-
return (0,
|
|
27629
|
+
return (0, import_node_path63.join)(worktreePath, PLAN_FILE_REL);
|
|
26883
27630
|
}
|
|
26884
27631
|
function readTextIfPresent2(abs) {
|
|
26885
|
-
if (!(0,
|
|
27632
|
+
if (!(0, import_node_fs64.existsSync)(abs)) return null;
|
|
26886
27633
|
try {
|
|
26887
|
-
const content = (0,
|
|
27634
|
+
const content = (0, import_node_fs64.readFileSync)(abs, "utf8");
|
|
26888
27635
|
return content.trim() ? content : null;
|
|
26889
27636
|
} catch {
|
|
26890
27637
|
return null;
|
|
26891
27638
|
}
|
|
26892
27639
|
}
|
|
26893
27640
|
function readPlanFile(worktreePath) {
|
|
26894
|
-
return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0,
|
|
27641
|
+
return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path63.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path63.join)(worktreePath, LEGACY_PLAN_FILE_REL));
|
|
26895
27642
|
}
|
|
26896
27643
|
function writePlanFile(worktreePath, content) {
|
|
26897
27644
|
ensureAttachmentsGitignore(worktreePath);
|
|
26898
27645
|
const abs = planFileAbs(worktreePath);
|
|
26899
|
-
(0,
|
|
27646
|
+
(0, import_node_fs64.mkdirSync)((0, import_node_path63.dirname)(abs), { recursive: true });
|
|
26900
27647
|
const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
|
|
26901
|
-
(0,
|
|
27648
|
+
(0, import_node_fs64.writeFileSync)(abs, body, "utf8");
|
|
26902
27649
|
return PLAN_FILE_REL;
|
|
26903
27650
|
}
|
|
26904
|
-
var
|
|
27651
|
+
var import_node_fs64, import_node_path63;
|
|
26905
27652
|
var init_plan_file = __esm({
|
|
26906
27653
|
"src/plan/plan-file.ts"() {
|
|
26907
27654
|
"use strict";
|
|
26908
|
-
|
|
26909
|
-
|
|
27655
|
+
import_node_fs64 = require("fs");
|
|
27656
|
+
import_node_path63 = require("path");
|
|
26910
27657
|
init_workspace_scratch();
|
|
26911
27658
|
init_plan_present();
|
|
26912
27659
|
init_plan_present();
|
|
@@ -26955,7 +27702,7 @@ function setCaffeinateHoldHooks(next) {
|
|
|
26955
27702
|
hooks = next;
|
|
26956
27703
|
}
|
|
26957
27704
|
function caffeinateHoldPath() {
|
|
26958
|
-
return (0,
|
|
27705
|
+
return (0, import_node_path64.join)(appDataDir(), "caffeinate-hold.json");
|
|
26959
27706
|
}
|
|
26960
27707
|
function processAlive(pid) {
|
|
26961
27708
|
if (hooks.processAlive) return hooks.processAlive(pid);
|
|
@@ -26989,9 +27736,9 @@ function uniqueIds(ids) {
|
|
|
26989
27736
|
}
|
|
26990
27737
|
function readHold() {
|
|
26991
27738
|
const path = caffeinateHoldPath();
|
|
26992
|
-
if (!(0,
|
|
27739
|
+
if (!(0, import_node_fs65.existsSync)(path)) return null;
|
|
26993
27740
|
try {
|
|
26994
|
-
const parsed = JSON.parse((0,
|
|
27741
|
+
const parsed = JSON.parse((0, import_node_fs65.readFileSync)(path, "utf8"));
|
|
26995
27742
|
if (typeof parsed?.pid === "number" && parsed.pid > 0) {
|
|
26996
27743
|
return {
|
|
26997
27744
|
pid: parsed.pid,
|
|
@@ -27013,7 +27760,7 @@ function writeHold(pid, threadIds) {
|
|
|
27013
27760
|
}
|
|
27014
27761
|
function clearHold() {
|
|
27015
27762
|
try {
|
|
27016
|
-
(0,
|
|
27763
|
+
(0, import_node_fs65.unlinkSync)(caffeinateHoldPath());
|
|
27017
27764
|
} catch {
|
|
27018
27765
|
}
|
|
27019
27766
|
}
|
|
@@ -27098,13 +27845,13 @@ function releaseCaffeinateHoldForThread(threadId) {
|
|
|
27098
27845
|
}
|
|
27099
27846
|
return setCaffeinateHold(false, { threadId: id });
|
|
27100
27847
|
}
|
|
27101
|
-
var import_node_child_process6,
|
|
27848
|
+
var import_node_child_process6, import_node_fs65, import_node_path64, hooks;
|
|
27102
27849
|
var init_caffeinate_hold = __esm({
|
|
27103
27850
|
"src/store/caffeinate-hold.ts"() {
|
|
27104
27851
|
"use strict";
|
|
27105
27852
|
import_node_child_process6 = require("child_process");
|
|
27106
|
-
|
|
27107
|
-
|
|
27853
|
+
import_node_fs65 = require("fs");
|
|
27854
|
+
import_node_path64 = require("path");
|
|
27108
27855
|
init_paths();
|
|
27109
27856
|
init_private_file();
|
|
27110
27857
|
hooks = {};
|
|
@@ -27113,7 +27860,7 @@ var init_caffeinate_hold = __esm({
|
|
|
27113
27860
|
|
|
27114
27861
|
// src/store/schedules.ts
|
|
27115
27862
|
function schedulesPath() {
|
|
27116
|
-
return (0,
|
|
27863
|
+
return (0, import_node_path65.join)(appDataDir(), "schedules.json");
|
|
27117
27864
|
}
|
|
27118
27865
|
function parseDurationMs(every) {
|
|
27119
27866
|
const m = every.trim().match(DURATION_RE);
|
|
@@ -27125,7 +27872,7 @@ function parseDurationMs(every) {
|
|
|
27125
27872
|
}
|
|
27126
27873
|
function defaultScheduleName(prompt) {
|
|
27127
27874
|
const line = prompt.trim().split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
27128
|
-
if (!line) return "Untitled
|
|
27875
|
+
if (!line) return "Untitled automation";
|
|
27129
27876
|
return line.length > 60 ? `${line.slice(0, 57).trimEnd()}\u2026` : line;
|
|
27130
27877
|
}
|
|
27131
27878
|
function resolveScheduleThreadId(raw, env = process.env) {
|
|
@@ -27190,6 +27937,37 @@ function normalizeWhen(raw) {
|
|
|
27190
27937
|
}
|
|
27191
27938
|
throw new Error("Schedule `when` must be once, every, or cron");
|
|
27192
27939
|
}
|
|
27940
|
+
function normalizeOptionalBool(raw) {
|
|
27941
|
+
if (raw === true || raw === false) return raw;
|
|
27942
|
+
return null;
|
|
27943
|
+
}
|
|
27944
|
+
function normalizeAutonomy2(raw) {
|
|
27945
|
+
return raw === "default" || raw === "full" ? raw : null;
|
|
27946
|
+
}
|
|
27947
|
+
function normalizeAttachments(raw) {
|
|
27948
|
+
if (!Array.isArray(raw)) return [];
|
|
27949
|
+
const out = [];
|
|
27950
|
+
for (const item of raw) {
|
|
27951
|
+
if (!item || typeof item !== "object") continue;
|
|
27952
|
+
const row = item;
|
|
27953
|
+
if (typeof row.id !== "string" || !row.id.trim()) continue;
|
|
27954
|
+
if (typeof row.name !== "string" || !row.name) continue;
|
|
27955
|
+
if (typeof row.content !== "string") continue;
|
|
27956
|
+
if (!ATTACHMENT_KINDS.has(row.kind)) continue;
|
|
27957
|
+
const next = {
|
|
27958
|
+
id: row.id,
|
|
27959
|
+
name: row.name,
|
|
27960
|
+
kind: row.kind,
|
|
27961
|
+
content: row.content
|
|
27962
|
+
};
|
|
27963
|
+
if (typeof row.path === "string" && row.path) next.path = row.path;
|
|
27964
|
+
if (typeof row.previewDataUrl === "string" && row.previewDataUrl) {
|
|
27965
|
+
next.previewDataUrl = row.previewDataUrl;
|
|
27966
|
+
}
|
|
27967
|
+
out.push(next);
|
|
27968
|
+
}
|
|
27969
|
+
return out;
|
|
27970
|
+
}
|
|
27193
27971
|
function normalizeTask(raw) {
|
|
27194
27972
|
const when = normalizeWhen(raw.when);
|
|
27195
27973
|
const agent = raw.agent && isOrchestratorCapableAgent(raw.agent) ? raw.agent : null;
|
|
@@ -27202,6 +27980,11 @@ function normalizeTask(raw) {
|
|
|
27202
27980
|
threadId: raw.threadId?.trim() || null,
|
|
27203
27981
|
agent,
|
|
27204
27982
|
model: raw.model?.trim() || null,
|
|
27983
|
+
effort: normalizeThinkingEffort(raw.effort),
|
|
27984
|
+
fast: normalizeOptionalBool(raw.fast),
|
|
27985
|
+
planMode: normalizeOptionalBool(raw.planMode),
|
|
27986
|
+
autonomy: normalizeAutonomy2(raw.autonomy),
|
|
27987
|
+
attachments: normalizeAttachments(raw.attachments),
|
|
27205
27988
|
nextRunAt: raw.nextRunAt,
|
|
27206
27989
|
lastRunAt: raw.lastRunAt ?? null,
|
|
27207
27990
|
lastThreadId: raw.lastThreadId ?? null,
|
|
@@ -27213,9 +27996,9 @@ function normalizeTask(raw) {
|
|
|
27213
27996
|
}
|
|
27214
27997
|
function readAll2() {
|
|
27215
27998
|
const path = schedulesPath();
|
|
27216
|
-
if (!(0,
|
|
27999
|
+
if (!(0, import_node_fs66.existsSync)(path)) return [];
|
|
27217
28000
|
try {
|
|
27218
|
-
const parsed = JSON.parse((0,
|
|
28001
|
+
const parsed = JSON.parse((0, import_node_fs66.readFileSync)(path, "utf8"));
|
|
27219
28002
|
if (!Array.isArray(parsed)) return [];
|
|
27220
28003
|
return parsed.map((row) => {
|
|
27221
28004
|
try {
|
|
@@ -27248,6 +28031,11 @@ function buildTask(input, now) {
|
|
|
27248
28031
|
threadId: input.threadId?.trim() || null,
|
|
27249
28032
|
agent,
|
|
27250
28033
|
model: input.model?.trim() || null,
|
|
28034
|
+
effort: input.effort != null ? normalizeThinkingEffort(input.effort) : null,
|
|
28035
|
+
fast: normalizeOptionalBool(input.fast),
|
|
28036
|
+
planMode: normalizeOptionalBool(input.planMode),
|
|
28037
|
+
autonomy: normalizeAutonomy2(input.autonomy),
|
|
28038
|
+
attachments: normalizeAttachments(input.attachments),
|
|
27251
28039
|
nextRunAt,
|
|
27252
28040
|
lastRunAt: null,
|
|
27253
28041
|
lastThreadId: null,
|
|
@@ -27289,7 +28077,7 @@ function createSchedule(input) {
|
|
|
27289
28077
|
function updateSchedule(id, patch) {
|
|
27290
28078
|
const rows = readAll2();
|
|
27291
28079
|
const idx = rows.findIndex((row) => row.id === id || id.length >= 8 && row.id.startsWith(id));
|
|
27292
|
-
if (idx < 0) throw new Error(`
|
|
28080
|
+
if (idx < 0) throw new Error(`Automation not found: ${id}`);
|
|
27293
28081
|
const prev = rows[idx];
|
|
27294
28082
|
const when = patch.when ? normalizeWhen(patch.when) : prev.when;
|
|
27295
28083
|
const prompt = patch.prompt !== void 0 ? patch.prompt.trim() : prev.prompt;
|
|
@@ -27304,6 +28092,11 @@ function updateSchedule(id, patch) {
|
|
|
27304
28092
|
threadId: patch.threadId !== void 0 ? patch.threadId?.trim() || null : prev.threadId,
|
|
27305
28093
|
agent: patch.agent !== void 0 ? patch.agent ? coerceOrchestratorAgent(patch.agent) : null : prev.agent,
|
|
27306
28094
|
model: patch.model !== void 0 ? patch.model?.trim() || null : prev.model,
|
|
28095
|
+
effort: patch.effort !== void 0 ? normalizeThinkingEffort(patch.effort) : prev.effort,
|
|
28096
|
+
fast: patch.fast !== void 0 ? normalizeOptionalBool(patch.fast) : prev.fast,
|
|
28097
|
+
planMode: patch.planMode !== void 0 ? normalizeOptionalBool(patch.planMode) : prev.planMode,
|
|
28098
|
+
autonomy: patch.autonomy !== void 0 ? normalizeAutonomy2(patch.autonomy) : prev.autonomy,
|
|
28099
|
+
attachments: patch.attachments !== void 0 ? normalizeAttachments(patch.attachments) : prev.attachments,
|
|
27307
28100
|
nextRunAt: whenChanged ? computeNextRunAt(when).toISOString() : prev.nextRunAt,
|
|
27308
28101
|
lastError: whenChanged ? null : prev.lastError,
|
|
27309
28102
|
updatedAt: nowIso2()
|
|
@@ -27318,13 +28111,13 @@ function deleteSchedule(id) {
|
|
|
27318
28111
|
const next = rows.filter(
|
|
27319
28112
|
(row) => row.id !== id && !(id.length >= 8 && row.id.startsWith(id))
|
|
27320
28113
|
);
|
|
27321
|
-
if (next.length === rows.length) throw new Error(`
|
|
28114
|
+
if (next.length === rows.length) throw new Error(`Automation not found: ${id}`);
|
|
27322
28115
|
writeAll2(next);
|
|
27323
28116
|
}
|
|
27324
28117
|
function recordScheduleRun(id, result) {
|
|
27325
28118
|
const rows = readAll2();
|
|
27326
28119
|
const idx = rows.findIndex((row) => row.id === id);
|
|
27327
|
-
if (idx < 0) throw new Error(`
|
|
28120
|
+
if (idx < 0) throw new Error(`Automation not found: ${id}`);
|
|
27328
28121
|
const prev = rows[idx];
|
|
27329
28122
|
const firedAt = result.firedAt ?? /* @__PURE__ */ new Date();
|
|
27330
28123
|
const next = {
|
|
@@ -27340,15 +28133,16 @@ function recordScheduleRun(id, result) {
|
|
|
27340
28133
|
writeAll2(rows);
|
|
27341
28134
|
return next;
|
|
27342
28135
|
}
|
|
27343
|
-
var import_node_crypto17,
|
|
28136
|
+
var import_node_crypto17, import_node_fs66, import_node_path65, import_croner, DURATION_RE, UNIT_MS, ATTACHMENT_KINDS;
|
|
27344
28137
|
var init_schedules = __esm({
|
|
27345
28138
|
"src/store/schedules.ts"() {
|
|
27346
28139
|
"use strict";
|
|
27347
28140
|
import_node_crypto17 = require("crypto");
|
|
27348
|
-
|
|
27349
|
-
|
|
28141
|
+
import_node_fs66 = require("fs");
|
|
28142
|
+
import_node_path65 = require("path");
|
|
27350
28143
|
import_croner = require("croner");
|
|
27351
28144
|
init_orchestrator_capable();
|
|
28145
|
+
init_thinking_effort();
|
|
27352
28146
|
init_paths();
|
|
27353
28147
|
init_private_file();
|
|
27354
28148
|
init_app_settings();
|
|
@@ -27359,6 +28153,14 @@ var init_schedules = __esm({
|
|
|
27359
28153
|
h: 36e5,
|
|
27360
28154
|
d: 864e5
|
|
27361
28155
|
};
|
|
28156
|
+
ATTACHMENT_KINDS = /* @__PURE__ */ new Set([
|
|
28157
|
+
"transcript",
|
|
28158
|
+
"file",
|
|
28159
|
+
"issue",
|
|
28160
|
+
"workspace",
|
|
28161
|
+
"diff-comment",
|
|
28162
|
+
"code-ref"
|
|
28163
|
+
]);
|
|
27362
28164
|
}
|
|
27363
28165
|
});
|
|
27364
28166
|
|
|
@@ -27372,17 +28174,52 @@ async function defaultDeps() {
|
|
|
27372
28174
|
const orch = getOrchestrator2();
|
|
27373
28175
|
return {
|
|
27374
28176
|
findThread: (id) => findThreadByRef(id),
|
|
27375
|
-
send: (id, prompt) =>
|
|
28177
|
+
send: async (id, prompt, opts) => {
|
|
28178
|
+
if (opts?.options && Object.keys(opts.options).length > 0) {
|
|
28179
|
+
try {
|
|
28180
|
+
orch.setThreadOptions(id, opts.options);
|
|
28181
|
+
} catch {
|
|
28182
|
+
const rest = scheduleOptionsAfterRefusedAgentSwitch(opts.options);
|
|
28183
|
+
if (Object.keys(rest).length > 0) {
|
|
28184
|
+
try {
|
|
28185
|
+
orch.setThreadOptions(id, rest);
|
|
28186
|
+
} catch {
|
|
28187
|
+
}
|
|
28188
|
+
}
|
|
28189
|
+
}
|
|
28190
|
+
}
|
|
28191
|
+
const thread = findThreadByRef(id);
|
|
28192
|
+
const attachments = thread ? persistPendingFileAttachments(thread.worktreePath, opts?.attachments ?? []) : opts?.attachments ?? [];
|
|
28193
|
+
return orch.send(id, prompt, { attachments });
|
|
28194
|
+
},
|
|
27376
28195
|
startOrchestration: (opts) => startOrchestration2(opts)
|
|
27377
28196
|
};
|
|
27378
28197
|
}
|
|
28198
|
+
function scheduleOptionsAfterRefusedAgentSwitch(options) {
|
|
28199
|
+
const { agent: _agent, model: _model, ...rest } = options;
|
|
28200
|
+
return rest;
|
|
28201
|
+
}
|
|
28202
|
+
function scheduleSendOptions(schedule) {
|
|
28203
|
+
const patch = {};
|
|
28204
|
+
if (schedule.agent) {
|
|
28205
|
+
patch.agent = schedule.agent;
|
|
28206
|
+
patch.model = schedule.model;
|
|
28207
|
+
} else if (schedule.model) {
|
|
28208
|
+
patch.model = schedule.model;
|
|
28209
|
+
}
|
|
28210
|
+
if (schedule.effort) patch.effort = schedule.effort;
|
|
28211
|
+
if (schedule.fast != null) patch.fast = schedule.fast;
|
|
28212
|
+
if (schedule.planMode != null) patch.planMode = schedule.planMode;
|
|
28213
|
+
if (schedule.autonomy) patch.autonomy = schedule.autonomy;
|
|
28214
|
+
return Object.keys(patch).length > 0 ? patch : void 0;
|
|
28215
|
+
}
|
|
27379
28216
|
function pickAgent(schedule) {
|
|
27380
28217
|
if (schedule.agent) return schedule.agent;
|
|
27381
28218
|
return coerceOrchestratorAgent(resolveOrchestratorDefaults().agent);
|
|
27382
28219
|
}
|
|
27383
28220
|
async function fireSchedule(id) {
|
|
27384
28221
|
const schedule = getSchedule(id);
|
|
27385
|
-
if (!schedule) throw new Error(`
|
|
28222
|
+
if (!schedule) throw new Error(`Automation not found: ${id}`);
|
|
27386
28223
|
if (firing.has(schedule.id)) return schedule;
|
|
27387
28224
|
firing.add(schedule.id);
|
|
27388
28225
|
try {
|
|
@@ -27390,18 +28227,28 @@ async function fireSchedule(id) {
|
|
|
27390
28227
|
const prompt = formatScheduledPrompt(schedule.name, schedule.prompt);
|
|
27391
28228
|
const target = schedule.threadId ? deps.findThread(schedule.threadId) : null;
|
|
27392
28229
|
if (target && target.status !== "archived") {
|
|
27393
|
-
const sent = await deps.send(target.id, prompt
|
|
28230
|
+
const sent = await deps.send(target.id, prompt, {
|
|
28231
|
+
attachments: schedule.attachments,
|
|
28232
|
+
options: scheduleSendOptions(schedule)
|
|
28233
|
+
});
|
|
27394
28234
|
return recordScheduleRun(schedule.id, {
|
|
27395
28235
|
lastThreadId: sent.id,
|
|
27396
28236
|
lastError: null
|
|
27397
28237
|
});
|
|
27398
28238
|
}
|
|
28239
|
+
const pinReplacement = Boolean(schedule.threadId) && schedule.when.kind !== "once";
|
|
27399
28240
|
const created = await deps.startOrchestration({
|
|
27400
28241
|
goal: prompt,
|
|
27401
28242
|
agent: pickAgent(schedule),
|
|
27402
|
-
model: schedule.model
|
|
28243
|
+
model: schedule.model,
|
|
28244
|
+
...schedule.effort ? { effort: schedule.effort } : {},
|
|
28245
|
+
...schedule.fast != null ? { fast: schedule.fast } : {},
|
|
28246
|
+
...schedule.planMode != null ? { planMode: schedule.planMode } : {},
|
|
28247
|
+
...schedule.autonomy ? { autonomy: schedule.autonomy } : {},
|
|
28248
|
+
attachments: schedule.attachments,
|
|
28249
|
+
...pinReplacement ? {} : { ephemeralAutomationId: schedule.id, ephemeralTitle: schedule.name }
|
|
27403
28250
|
});
|
|
27404
|
-
if (
|
|
28251
|
+
if (pinReplacement) {
|
|
27405
28252
|
updateSchedule(schedule.id, { threadId: created.id });
|
|
27406
28253
|
}
|
|
27407
28254
|
return recordScheduleRun(schedule.id, {
|
|
@@ -27471,6 +28318,7 @@ var init_schedule_runner = __esm({
|
|
|
27471
28318
|
init_desktop_host();
|
|
27472
28319
|
init_app_settings();
|
|
27473
28320
|
init_schedules();
|
|
28321
|
+
init_stage_files();
|
|
27474
28322
|
init_thread_store();
|
|
27475
28323
|
SET_TIMEOUT_MAX = 2147483647;
|
|
27476
28324
|
fireHooks = null;
|
|
@@ -27495,9 +28343,9 @@ function recoverFinishedCursorRun(opts) {
|
|
|
27495
28343
|
return best;
|
|
27496
28344
|
}
|
|
27497
28345
|
function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
|
|
27498
|
-
if (!(0,
|
|
28346
|
+
if (!(0, import_node_fs67.existsSync)(runsPath)) return null;
|
|
27499
28347
|
try {
|
|
27500
|
-
const lines = (0,
|
|
28348
|
+
const lines = (0, import_node_fs67.readFileSync)(runsPath, "utf8").split("\n");
|
|
27501
28349
|
let best = null;
|
|
27502
28350
|
for (const line of lines) {
|
|
27503
28351
|
const trimmed = line.trim();
|
|
@@ -27523,11 +28371,11 @@ function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
|
|
|
27523
28371
|
return null;
|
|
27524
28372
|
}
|
|
27525
28373
|
}
|
|
27526
|
-
var
|
|
28374
|
+
var import_node_fs67;
|
|
27527
28375
|
var init_cursor_recover = __esm({
|
|
27528
28376
|
"src/agents/cursor-recover.ts"() {
|
|
27529
28377
|
"use strict";
|
|
27530
|
-
|
|
28378
|
+
import_node_fs67 = require("fs");
|
|
27531
28379
|
init_cursor_store();
|
|
27532
28380
|
}
|
|
27533
28381
|
});
|
|
@@ -27603,10 +28451,11 @@ async function startOrchestration(opts) {
|
|
|
27603
28451
|
planMode: opts.planMode,
|
|
27604
28452
|
attachments: opts.attachments
|
|
27605
28453
|
});
|
|
28454
|
+
const ready2 = markEphemeralAutomation(thread2.id, opts);
|
|
27606
28455
|
if (goal) {
|
|
27607
|
-
return orch.send(
|
|
28456
|
+
return orch.send(ready2.id, goal);
|
|
27608
28457
|
}
|
|
27609
|
-
return
|
|
28458
|
+
return ready2;
|
|
27610
28459
|
}
|
|
27611
28460
|
const { titleFromPrompt: titleFromPrompt2 } = await Promise.resolve().then(() => (init_title(), title_exports));
|
|
27612
28461
|
const title = titleFromPrompt2(goal) || "Orchestration";
|
|
@@ -27641,12 +28490,13 @@ async function startOrchestration(opts) {
|
|
|
27641
28490
|
sourceType: "orchestration",
|
|
27642
28491
|
sourceRef: goal
|
|
27643
28492
|
});
|
|
28493
|
+
const ready = markEphemeralAutomation(updated.id, opts);
|
|
27644
28494
|
if (goal) {
|
|
27645
|
-
return orch.send(
|
|
28495
|
+
return orch.send(ready.id, goal);
|
|
27646
28496
|
}
|
|
27647
|
-
return
|
|
28497
|
+
return ready;
|
|
27648
28498
|
}
|
|
27649
|
-
var import_node_events, import_node_crypto18,
|
|
28499
|
+
var import_node_events, import_node_crypto18, import_node_fs68, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton2;
|
|
27650
28500
|
var init_orchestrator = __esm({
|
|
27651
28501
|
"src/orchestrator/orchestrator.ts"() {
|
|
27652
28502
|
"use strict";
|
|
@@ -27655,7 +28505,7 @@ var init_orchestrator = __esm({
|
|
|
27655
28505
|
init_task_state();
|
|
27656
28506
|
init_wait_for_turn();
|
|
27657
28507
|
import_node_crypto18 = require("crypto");
|
|
27658
|
-
|
|
28508
|
+
import_node_fs68 = require("fs");
|
|
27659
28509
|
init_error_detail();
|
|
27660
28510
|
init_run();
|
|
27661
28511
|
init_stale_lock();
|
|
@@ -27685,6 +28535,7 @@ var init_orchestrator = __esm({
|
|
|
27685
28535
|
init_child_halt();
|
|
27686
28536
|
init_stop_reason();
|
|
27687
28537
|
init_steer_request();
|
|
28538
|
+
init_ephemeral_automation();
|
|
27688
28539
|
init_wait_for_job();
|
|
27689
28540
|
init_agent_purpose_title();
|
|
27690
28541
|
init_create();
|
|
@@ -27853,7 +28704,7 @@ var init_orchestrator = __esm({
|
|
|
27853
28704
|
this.lightReconcileAt.set(repoPath, Date.now());
|
|
27854
28705
|
for (const thread of listThreads()) {
|
|
27855
28706
|
if (isGlobalThread(thread) || thread.repoPath !== repoPath) continue;
|
|
27856
|
-
if (!(0,
|
|
28707
|
+
if (!(0, import_node_fs68.existsSync)(thread.worktreePath)) {
|
|
27857
28708
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
27858
28709
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
27859
28710
|
}
|
|
@@ -27882,7 +28733,7 @@ var init_orchestrator = __esm({
|
|
|
27882
28733
|
}
|
|
27883
28734
|
continue;
|
|
27884
28735
|
}
|
|
27885
|
-
if (!(0,
|
|
28736
|
+
if (!(0, import_node_fs68.existsSync)(thread.worktreePath)) {
|
|
27886
28737
|
setStatus(thread.id, "broken", "Worktree missing on disk");
|
|
27887
28738
|
this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
|
|
27888
28739
|
continue;
|
|
@@ -28522,6 +29373,7 @@ var init_orchestrator = __esm({
|
|
|
28522
29373
|
} finally {
|
|
28523
29374
|
this.draining.delete(threadId);
|
|
28524
29375
|
}
|
|
29376
|
+
await archiveEphemeralAutomationIfReady(threadId, (id) => this.archive(id));
|
|
28525
29377
|
}
|
|
28526
29378
|
async runTurn(threadId, prompt) {
|
|
28527
29379
|
const existing = readThread(threadId);
|
|
@@ -28605,7 +29457,8 @@ var init_orchestrator = __esm({
|
|
|
28605
29457
|
return;
|
|
28606
29458
|
}
|
|
28607
29459
|
thread = this.requireThread(threadId);
|
|
28608
|
-
const
|
|
29460
|
+
const turnOrigin = thread.worktreePath?.trim() ? await resolveOriginRemote(thread.worktreePath).catch(() => null) : null;
|
|
29461
|
+
const gitPrompt = expandCanonicalGitRequest(promptText, { forge: turnOrigin?.forge });
|
|
28609
29462
|
const { agentPrompt: expandedPrompt } = expandComposerPrompt(
|
|
28610
29463
|
thread.worktreePath,
|
|
28611
29464
|
gitPrompt,
|
|
@@ -28692,11 +29545,9 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
|
|
|
28692
29545
|
const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2, gitBranchPrefixSetting: gitBranchPrefixSetting2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
|
|
28693
29546
|
const gitAuthMode = getGithubGitAuthMode2();
|
|
28694
29547
|
const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
|
|
28695
|
-
githubSlug: await withTimeout(
|
|
28696
|
-
|
|
28697
|
-
|
|
28698
|
-
"resolveGithubRepoSlug"
|
|
28699
|
-
).catch(() => null),
|
|
29548
|
+
githubSlug: turnOrigin?.forge === "github" ? turnOrigin.slug : await withTimeout(resolveGithubRepoSlug(fresh.worktreePath), 8e3, "resolveGithubRepoSlug").catch(() => null),
|
|
29549
|
+
originForge: turnOrigin?.forge ?? null,
|
|
29550
|
+
originSlug: turnOrigin?.slug ?? null,
|
|
28700
29551
|
gitAuthMode,
|
|
28701
29552
|
activeRuns: annotateDevAccessRuns(
|
|
28702
29553
|
mergeWorktreeActiveRuns(
|
|
@@ -28789,7 +29640,8 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
|
|
|
28789
29640
|
agentPrompt,
|
|
28790
29641
|
expandedPrompt,
|
|
28791
29642
|
worktreePath: fresh.worktreePath,
|
|
28792
|
-
attachments: autoContinue ? [] : sentAttachments ?? []
|
|
29643
|
+
attachments: autoContinue ? [] : sentAttachments ?? [],
|
|
29644
|
+
gitForge: turnOrigin?.forge
|
|
28793
29645
|
});
|
|
28794
29646
|
const handle = await spawnAgentTurn(
|
|
28795
29647
|
fresh,
|
|
@@ -30355,6 +31207,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
|
|
|
30355
31207
|
if (thread.status !== "archived") {
|
|
30356
31208
|
throw new Error("Thread is not archived");
|
|
30357
31209
|
}
|
|
31210
|
+
clearEphemeralAutomation(thread.id);
|
|
30358
31211
|
if (isGlobalThread(thread)) {
|
|
30359
31212
|
const { globalAgentCwd: globalAgentCwd2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
30360
31213
|
updateThread(thread.id, { worktreePath: globalAgentCwd2() });
|
|
@@ -30362,7 +31215,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
|
|
|
30362
31215
|
this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
|
|
30363
31216
|
return restored2;
|
|
30364
31217
|
}
|
|
30365
|
-
if (!(0,
|
|
31218
|
+
if (!(0, import_node_fs68.existsSync)(thread.worktreePath)) {
|
|
30366
31219
|
if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
|
|
30367
31220
|
throw new Error(
|
|
30368
31221
|
`Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
|
|
@@ -30553,7 +31406,7 @@ function createClippedMcpServer() {
|
|
|
30553
31406
|
// src/mcp/server.ts
|
|
30554
31407
|
var import_zod11 = require("zod");
|
|
30555
31408
|
var import_node_crypto22 = require("crypto");
|
|
30556
|
-
var
|
|
31409
|
+
var import_node_path69 = require("path");
|
|
30557
31410
|
init_orchestrator();
|
|
30558
31411
|
init_task_state();
|
|
30559
31412
|
init_worktree();
|
|
@@ -30661,9 +31514,9 @@ function githubProjectsOnIssue(raw) {
|
|
|
30661
31514
|
}
|
|
30662
31515
|
return out;
|
|
30663
31516
|
}
|
|
30664
|
-
async function readGitHubIssueProjects(number, cwd,
|
|
31517
|
+
async function readGitHubIssueProjects(number, cwd, repoArgs2) {
|
|
30665
31518
|
const result = await gh(
|
|
30666
|
-
["issue", "view", String(number), ...
|
|
31519
|
+
["issue", "view", String(number), ...repoArgs2, "--json", "projectItems"],
|
|
30667
31520
|
cwd,
|
|
30668
31521
|
{ reject: false }
|
|
30669
31522
|
);
|
|
@@ -30681,8 +31534,8 @@ async function readGitHubIssueProjects(number, cwd, repoArgs) {
|
|
|
30681
31534
|
return { projects: [] };
|
|
30682
31535
|
}
|
|
30683
31536
|
}
|
|
30684
|
-
async function listGitHubIssueProjectNames(number, cwd,
|
|
30685
|
-
const read = await readGitHubIssueProjects(number, cwd,
|
|
31537
|
+
async function listGitHubIssueProjectNames(number, cwd, repoArgs2) {
|
|
31538
|
+
const read = await readGitHubIssueProjects(number, cwd, repoArgs2);
|
|
30686
31539
|
return read.projects.map((project) => project.name).filter(Boolean);
|
|
30687
31540
|
}
|
|
30688
31541
|
function parseGitHubProjectList(stdout) {
|
|
@@ -30887,14 +31740,14 @@ async function listGitHubIssueCommentsSince(opts) {
|
|
|
30887
31740
|
}
|
|
30888
31741
|
async function getGitHubIssue(id, opts) {
|
|
30889
31742
|
const number = parseGitHubIssueNumber(id);
|
|
30890
|
-
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
31743
|
+
const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
30891
31744
|
const stdout = requireGhOk(
|
|
30892
31745
|
await gh(
|
|
30893
31746
|
[
|
|
30894
31747
|
"issue",
|
|
30895
31748
|
"view",
|
|
30896
31749
|
String(number),
|
|
30897
|
-
...
|
|
31750
|
+
...repoArgs2,
|
|
30898
31751
|
"--json",
|
|
30899
31752
|
"number,title,body,url,state,labels,assignees,comments,author"
|
|
30900
31753
|
],
|
|
@@ -30913,7 +31766,7 @@ async function getGitHubIssue(id, opts) {
|
|
|
30913
31766
|
throw new Error(`GitHub issue not found: #${number}`);
|
|
30914
31767
|
}
|
|
30915
31768
|
const issue = toGitHubIssue(parsed);
|
|
30916
|
-
const projects = await readGitHubIssueProjects(number, cwd,
|
|
31769
|
+
const projects = await readGitHubIssueProjects(number, cwd, repoArgs2);
|
|
30917
31770
|
issue.projects = projects.projects;
|
|
30918
31771
|
if (projects.error) issue.projectsError = projects.error;
|
|
30919
31772
|
return issue;
|
|
@@ -30922,10 +31775,10 @@ async function commentGitHubIssue(input, opts) {
|
|
|
30922
31775
|
const number = parseGitHubIssueNumber(input.id);
|
|
30923
31776
|
const body = input.body.trim();
|
|
30924
31777
|
if (!body) throw new Error("GitHub comment body is required");
|
|
30925
|
-
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
31778
|
+
const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
30926
31779
|
const stdout = requireGhOk(
|
|
30927
31780
|
await gh(
|
|
30928
|
-
["issue", "comment", String(number), ...
|
|
31781
|
+
["issue", "comment", String(number), ...repoArgs2, "--body", body],
|
|
30929
31782
|
cwd,
|
|
30930
31783
|
{ reject: false }
|
|
30931
31784
|
),
|
|
@@ -30967,21 +31820,21 @@ async function updateGitHubIssue(input, opts) {
|
|
|
30967
31820
|
"github_update_issue needs at least one of title, body, state, labels, project, parent, relations, removeRelations"
|
|
30968
31821
|
);
|
|
30969
31822
|
}
|
|
30970
|
-
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
31823
|
+
const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
30971
31824
|
if (state === "closed" || state === "close") {
|
|
30972
31825
|
requireGhOk(
|
|
30973
|
-
await gh(["issue", "close", String(number), ...
|
|
31826
|
+
await gh(["issue", "close", String(number), ...repoArgs2], cwd, { reject: false }),
|
|
30974
31827
|
`GitHub close #${number}`
|
|
30975
31828
|
);
|
|
30976
31829
|
} else if (state === "open" || state === "reopen") {
|
|
30977
31830
|
requireGhOk(
|
|
30978
|
-
await gh(["issue", "reopen", String(number), ...
|
|
31831
|
+
await gh(["issue", "reopen", String(number), ...repoArgs2], cwd, { reject: false }),
|
|
30979
31832
|
`GitHub reopen #${number}`
|
|
30980
31833
|
);
|
|
30981
31834
|
} else if (state) {
|
|
30982
31835
|
throw new Error(`GitHub issue state must be open or closed (got ${input.state})`);
|
|
30983
31836
|
}
|
|
30984
|
-
const args = ["issue", "edit", String(number), ...
|
|
31837
|
+
const args = ["issue", "edit", String(number), ...repoArgs2];
|
|
30985
31838
|
if (title) args.push("--title", title);
|
|
30986
31839
|
if (body !== void 0) args.push("--body", body);
|
|
30987
31840
|
if (input.labels !== void 0) {
|
|
@@ -30993,7 +31846,7 @@ async function updateGitHubIssue(input, opts) {
|
|
|
30993
31846
|
if (remove.length) args.push("--remove-label", remove.join(","));
|
|
30994
31847
|
}
|
|
30995
31848
|
if (input.project !== void 0) {
|
|
30996
|
-
const current = await listGitHubIssueProjectNames(number, cwd,
|
|
31849
|
+
const current = await listGitHubIssueProjectNames(number, cwd, repoArgs2);
|
|
30997
31850
|
if (isGitHubNoneToken(input.project)) {
|
|
30998
31851
|
for (const name of current) args.push("--remove-project", name);
|
|
30999
31852
|
} else {
|
|
@@ -31019,7 +31872,7 @@ async function updateGitHubIssue(input, opts) {
|
|
|
31019
31872
|
for (const rel of addRelations ?? []) {
|
|
31020
31873
|
args.push(githubRelationEditFlag(rel.type, false), String(parseGitHubIssueNumber(rel.issue)));
|
|
31021
31874
|
}
|
|
31022
|
-
const editChanged = args.length > 3 +
|
|
31875
|
+
const editChanged = args.length > 3 + repoArgs2.length;
|
|
31023
31876
|
if (editChanged) {
|
|
31024
31877
|
requireGhOk(await gh(args, cwd, { reject: false }), `GitHub edit #${number}`);
|
|
31025
31878
|
}
|
|
@@ -31034,8 +31887,8 @@ async function createGitHubIssue(input, opts) {
|
|
|
31034
31887
|
parentNumber ? `Spin-off of #${parentNumber}.` : null,
|
|
31035
31888
|
input.body?.trim() || null
|
|
31036
31889
|
].filter(Boolean);
|
|
31037
|
-
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
31038
|
-
const args = ["issue", "create", ...
|
|
31890
|
+
const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
31891
|
+
const args = ["issue", "create", ...repoArgs2, "--title", title];
|
|
31039
31892
|
if (bodyParts.length) args.push("--body", bodyParts.join("\n\n"));
|
|
31040
31893
|
const created = await gh([...args, "--json", "number,url,title"], cwd, { reject: false });
|
|
31041
31894
|
if (created.exitCode === 0 && created.stdout.trim()) {
|
|
@@ -32259,6 +33112,18 @@ async function commentLinearIssue(input, opts) {
|
|
|
32259
33112
|
}
|
|
32260
33113
|
|
|
32261
33114
|
// src/integrations/issues.ts
|
|
33115
|
+
var CURSOR_ISSUES_UNAVAILABLE = "Cursor Origin has no issue tracker. Tickets for this repo come from Linear or AbleTime (Settings \u2192 Issues).";
|
|
33116
|
+
var ENTIRE_ISSUES_ARE_TRAILS = "Entire does not use GitHub Issues. Tickets for this repo are Trails (`entire trail list`). Linear or AbleTime still work when selected in Settings \u2192 Issues.";
|
|
33117
|
+
async function cursorIssuesUnavailableReason(repoPath) {
|
|
33118
|
+
try {
|
|
33119
|
+
const remote = await resolveOriginRemote(repoPath);
|
|
33120
|
+
if (remote?.forge === "cursor") return CURSOR_ISSUES_UNAVAILABLE;
|
|
33121
|
+
if (remote?.forge === "entire") return ENTIRE_ISSUES_ARE_TRAILS;
|
|
33122
|
+
return null;
|
|
33123
|
+
} catch {
|
|
33124
|
+
return null;
|
|
33125
|
+
}
|
|
33126
|
+
}
|
|
32262
33127
|
function assigneeKey(assignee) {
|
|
32263
33128
|
return (assignee ?? "").trim().toLowerCase();
|
|
32264
33129
|
}
|
|
@@ -32420,6 +33285,17 @@ async function listIssues(repoPath, opts) {
|
|
|
32420
33285
|
...since ? { since, comments: commentsFromAbleTimeIssues(tasks, issues2, since) } : {}
|
|
32421
33286
|
};
|
|
32422
33287
|
}
|
|
33288
|
+
const unavailableReason = await cursorIssuesUnavailableReason(repoPath);
|
|
33289
|
+
if (unavailableReason) {
|
|
33290
|
+
return {
|
|
33291
|
+
source,
|
|
33292
|
+
preferredSource,
|
|
33293
|
+
linearConnected,
|
|
33294
|
+
abletimeConnected,
|
|
33295
|
+
issues: [],
|
|
33296
|
+
unavailableReason
|
|
33297
|
+
};
|
|
33298
|
+
}
|
|
32423
33299
|
const [issues, login] = await Promise.all([
|
|
32424
33300
|
listGitHubIssues(repoPath, { assignee, query, limit: opts?.limit, updatedSince: since }),
|
|
32425
33301
|
githubViewerLogin(repoPath)
|
|
@@ -33229,11 +34105,15 @@ function fail2(err) {
|
|
|
33229
34105
|
true
|
|
33230
34106
|
);
|
|
33231
34107
|
}
|
|
34108
|
+
async function cursorIssueBlock(repoPath) {
|
|
34109
|
+
const cwd = await resolveRepoRoot((repoPath ?? "").trim() || process.cwd());
|
|
34110
|
+
return cursorIssuesUnavailableReason(cwd);
|
|
34111
|
+
}
|
|
33232
34112
|
var repoPathSchema = import_zod6.z.string().optional().describe("Workspace / worktree path. Omit on a worktree turn (uses cwd).");
|
|
33233
34113
|
function registerGithubIssueTools(server) {
|
|
33234
34114
|
server.tool(
|
|
33235
34115
|
"github_search_issues",
|
|
33236
|
-
"Search or list open GitHub issues in this repo via Account gh. Default 40; pass query and/or limit (max 250) when truncated. assignee: me, unassigned, all, or a login. Pass updatedSince for new/updated tickets and new comments (e.g. \u201Cany updates since yesterday?\u201D).",
|
|
34116
|
+
"Search or list open GitHub issues in this repo via Account gh. A Cursor origin returns unavailableReason \u2014 Origin has no issue tracker. An Entire origin does the same \u2014 tickets there are Trails (`entire trail`), not GitHub Issues. Default 40; pass query and/or limit (max 250) when truncated. assignee: me, unassigned, all, or a login. Pass updatedSince for new/updated tickets and new comments (e.g. \u201Cany updates since yesterday?\u201D).",
|
|
33237
34117
|
{
|
|
33238
34118
|
query: import_zod6.z.string().optional().describe("Search text (title, body, identifiers). Omit to list by assignee only."),
|
|
33239
34119
|
assignee: import_zod6.z.string().optional().describe("me, unassigned, all, or a GitHub login. Default: all when query is set, otherwise me."),
|
|
@@ -33245,6 +34125,15 @@ function registerGithubIssueTools(server) {
|
|
|
33245
34125
|
try {
|
|
33246
34126
|
const cwd = await resolveRepoRoot((repoPath ?? "").trim() || process.cwd());
|
|
33247
34127
|
const page = clampMcpIssueLimit(limit);
|
|
34128
|
+
const blocked = await cursorIssuesUnavailableReason(cwd);
|
|
34129
|
+
if (blocked) {
|
|
34130
|
+
return mcpJson(
|
|
34131
|
+
formatListedIssuesForMcp(
|
|
34132
|
+
{ source: "github", issues: [], unavailableReason: blocked },
|
|
34133
|
+
page
|
|
34134
|
+
)
|
|
34135
|
+
);
|
|
34136
|
+
}
|
|
33248
34137
|
const since = updatedSince ? parseIssueSince(updatedSince) : void 0;
|
|
33249
34138
|
const issues = await listGitHubIssues(cwd, {
|
|
33250
34139
|
query,
|
|
@@ -33280,6 +34169,8 @@ function registerGithubIssueTools(server) {
|
|
|
33280
34169
|
{ repoPath: repoPathSchema },
|
|
33281
34170
|
async ({ repoPath }) => {
|
|
33282
34171
|
try {
|
|
34172
|
+
const blocked = await cursorIssueBlock(repoPath);
|
|
34173
|
+
if (blocked) throw new Error(blocked);
|
|
33283
34174
|
return text4({ projects: await listGitHubProjects({ repoPath }) });
|
|
33284
34175
|
} catch (err) {
|
|
33285
34176
|
return fail2(err);
|
|
@@ -33292,6 +34183,8 @@ function registerGithubIssueTools(server) {
|
|
|
33292
34183
|
{ id: import_zod6.z.string(), include: mcpIssueIncludeSchema, repoPath: repoPathSchema },
|
|
33293
34184
|
async ({ id, include, repoPath }) => {
|
|
33294
34185
|
try {
|
|
34186
|
+
const blocked = await cursorIssueBlock(repoPath);
|
|
34187
|
+
if (blocked) throw new Error(blocked);
|
|
33295
34188
|
return text4(formatGitHubIssuePayload(await getGitHubIssue(id, { repoPath }), include));
|
|
33296
34189
|
} catch (err) {
|
|
33297
34190
|
return fail2(err);
|
|
@@ -33320,6 +34213,8 @@ function registerGithubIssueTools(server) {
|
|
|
33320
34213
|
{ id: import_zod6.z.string(), body: import_zod6.z.string(), repoPath: repoPathSchema },
|
|
33321
34214
|
async ({ id, body, repoPath }) => {
|
|
33322
34215
|
try {
|
|
34216
|
+
const blocked = await cursorIssueBlock(repoPath);
|
|
34217
|
+
if (blocked) throw new Error(blocked);
|
|
33323
34218
|
return text4(await commentGitHubIssue({ id, body }, { repoPath }));
|
|
33324
34219
|
} catch (err) {
|
|
33325
34220
|
return fail2(err);
|
|
@@ -33347,6 +34242,8 @@ function registerGithubIssueTools(server) {
|
|
|
33347
34242
|
},
|
|
33348
34243
|
async (args) => {
|
|
33349
34244
|
try {
|
|
34245
|
+
const blocked = await cursorIssueBlock(args.repoPath);
|
|
34246
|
+
if (blocked) throw new Error(blocked);
|
|
33350
34247
|
return text4(await updateGitHubIssue(args, { repoPath: args.repoPath }));
|
|
33351
34248
|
} catch (err) {
|
|
33352
34249
|
return fail2(err);
|
|
@@ -33364,6 +34261,8 @@ function registerGithubIssueTools(server) {
|
|
|
33364
34261
|
},
|
|
33365
34262
|
async (args) => {
|
|
33366
34263
|
try {
|
|
34264
|
+
const blocked = await cursorIssueBlock(args.repoPath);
|
|
34265
|
+
if (blocked) throw new Error(blocked);
|
|
33367
34266
|
return text4(await createGitHubIssue(args, { repoPath: args.repoPath }));
|
|
33368
34267
|
} catch (err) {
|
|
33369
34268
|
return fail2(err);
|
|
@@ -33907,7 +34806,7 @@ function parseWhen(args) {
|
|
|
33907
34806
|
function registerScheduleTools(server) {
|
|
33908
34807
|
server.tool(
|
|
33909
34808
|
"list_schedules",
|
|
33910
|
-
"List local Sideboard
|
|
34809
|
+
"List local Sideboard automations that trigger a chat when due. Jobs fire only while Sideboard.app is running on this Mac (sleep skips until wake). Shown in the sidebar after Board.",
|
|
33911
34810
|
{},
|
|
33912
34811
|
async () => {
|
|
33913
34812
|
try {
|
|
@@ -33919,9 +34818,9 @@ function registerScheduleTools(server) {
|
|
|
33919
34818
|
);
|
|
33920
34819
|
server.tool(
|
|
33921
34820
|
"create_schedule",
|
|
33922
|
-
"Create a local
|
|
34821
|
+
"Create a local automation that, when due, sends a prompt to an existing chat (chatId) or starts a new Global orchestration chat (omit chatId). Pass chatId=self to continue this chat. If that chat is missing or archived when the job fires, Sideboard starts a new Global chat (recurring jobs then continue that chat). Exactly one of at (ISO datetime), every (15m/1h/6h/1d), or cron (5-field). Recurring jobs without chatId open a new Global chat each run and archive it when that run finishes. History keeps the latest run for that automation. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while automations are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
|
|
33923
34822
|
{
|
|
33924
|
-
prompt: import_zod10.z.string().describe("User message / goal queued when the
|
|
34823
|
+
prompt: import_zod10.z.string().describe("User message / goal queued when the automation fires"),
|
|
33925
34824
|
name: import_zod10.z.string().optional(),
|
|
33926
34825
|
at: import_zod10.z.string().optional().describe("ISO datetime for a one-shot"),
|
|
33927
34826
|
every: import_zod10.z.string().optional().describe("Interval such as 15m, 1h, 6h, 1d"),
|
|
@@ -33957,7 +34856,7 @@ function registerScheduleTools(server) {
|
|
|
33957
34856
|
});
|
|
33958
34857
|
return text6({
|
|
33959
34858
|
schedule,
|
|
33960
|
-
hint: scheduleNeedsCaffeinate(schedule) ? "Fires while Sideboard.app is running. Recurring jobs without chatId create a new orchestration chat each run. Caffeinate while
|
|
34859
|
+
hint: scheduleNeedsCaffeinate(schedule) ? "Fires while Sideboard.app is running. Recurring jobs without chatId create a new orchestration chat each run and archive it when that run finishes (History keeps the latest). Caffeinate while automations are enabled is now on so the Mac can stay awake until this job is due." : "Fires while Sideboard.app is running. Recurring jobs without chatId create a new orchestration chat each run and archive it when that run finishes (History keeps the latest)."
|
|
33961
34860
|
});
|
|
33962
34861
|
} catch (err) {
|
|
33963
34862
|
return fail4(err);
|
|
@@ -33966,7 +34865,7 @@ function registerScheduleTools(server) {
|
|
|
33966
34865
|
);
|
|
33967
34866
|
server.tool(
|
|
33968
34867
|
"update_schedule",
|
|
33969
|
-
"Update a local
|
|
34868
|
+
"Update a local automation (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
|
|
33970
34869
|
{
|
|
33971
34870
|
id: import_zod10.z.string(),
|
|
33972
34871
|
prompt: import_zod10.z.string().optional(),
|
|
@@ -34021,7 +34920,7 @@ function registerScheduleTools(server) {
|
|
|
34021
34920
|
);
|
|
34022
34921
|
server.tool(
|
|
34023
34922
|
"delete_schedule",
|
|
34024
|
-
"Delete a local
|
|
34923
|
+
"Delete a local automation. Pass id from list_schedules.",
|
|
34025
34924
|
{ id: import_zod10.z.string() },
|
|
34026
34925
|
async ({ id }) => {
|
|
34027
34926
|
try {
|
|
@@ -34034,12 +34933,12 @@ function registerScheduleTools(server) {
|
|
|
34034
34933
|
);
|
|
34035
34934
|
server.tool(
|
|
34036
34935
|
"run_schedule",
|
|
34037
|
-
"Fire
|
|
34936
|
+
"Fire an automation now (does not wait for nextRunAt). Queues the prompt on the target chat or starts a new Global chat. If Sideboard.app is running, the desktop drains the turn.",
|
|
34038
34937
|
{ id: import_zod10.z.string() },
|
|
34039
34938
|
async ({ id }) => {
|
|
34040
34939
|
try {
|
|
34041
34940
|
if (!getSchedule(id)) {
|
|
34042
|
-
return fail4(new Error(`
|
|
34941
|
+
return fail4(new Error(`Automation not found: ${id}`));
|
|
34043
34942
|
}
|
|
34044
34943
|
const schedule = await fireSchedule(id);
|
|
34045
34944
|
return text6({ schedule });
|
|
@@ -34052,7 +34951,7 @@ function registerScheduleTools(server) {
|
|
|
34052
34951
|
|
|
34053
34952
|
// src/agent-rpc/handlers.ts
|
|
34054
34953
|
var import_node_crypto20 = require("crypto");
|
|
34055
|
-
var
|
|
34954
|
+
var import_node_path66 = require("path");
|
|
34056
34955
|
init_wait_for_job();
|
|
34057
34956
|
init_wait_for_turn();
|
|
34058
34957
|
|
|
@@ -34117,27 +35016,27 @@ init_gh_errors();
|
|
|
34117
35016
|
init_git_auth_mode();
|
|
34118
35017
|
|
|
34119
35018
|
// src/board/load-home-board.ts
|
|
34120
|
-
var
|
|
34121
|
-
var
|
|
35019
|
+
var import_node_fs70 = require("fs");
|
|
35020
|
+
var import_node_path68 = require("path");
|
|
34122
35021
|
init_worktree();
|
|
34123
35022
|
init_paths();
|
|
34124
35023
|
|
|
34125
35024
|
// src/board/board-pins.ts
|
|
34126
35025
|
var import_node_crypto21 = require("crypto");
|
|
34127
|
-
var
|
|
34128
|
-
var
|
|
35026
|
+
var import_node_fs69 = require("fs");
|
|
35027
|
+
var import_node_path67 = require("path");
|
|
34129
35028
|
init_paths();
|
|
34130
35029
|
init_home_board();
|
|
34131
35030
|
var FILE = "home-board-pins.json";
|
|
34132
35031
|
var VERSION = 1;
|
|
34133
35032
|
function pinsFile() {
|
|
34134
|
-
return (0,
|
|
35033
|
+
return (0, import_node_path67.join)(appDataDir(), FILE);
|
|
34135
35034
|
}
|
|
34136
35035
|
function readDisk() {
|
|
34137
35036
|
const path = pinsFile();
|
|
34138
|
-
if (!(0,
|
|
35037
|
+
if (!(0, import_node_fs69.existsSync)(path)) return [];
|
|
34139
35038
|
try {
|
|
34140
|
-
const raw = JSON.parse((0,
|
|
35039
|
+
const raw = JSON.parse((0, import_node_fs69.readFileSync)(path, "utf8"));
|
|
34141
35040
|
if (raw?.version !== VERSION || !Array.isArray(raw.items)) return [];
|
|
34142
35041
|
return raw.items.filter((item) => item?.id && item.kind && item.ref);
|
|
34143
35042
|
} catch {
|
|
@@ -34145,8 +35044,8 @@ function readDisk() {
|
|
|
34145
35044
|
}
|
|
34146
35045
|
}
|
|
34147
35046
|
function writeDisk(items) {
|
|
34148
|
-
(0,
|
|
34149
|
-
(0,
|
|
35047
|
+
(0, import_node_fs69.mkdirSync)(appDataDir(), { recursive: true });
|
|
35048
|
+
(0, import_node_fs69.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
|
|
34150
35049
|
}
|
|
34151
35050
|
function listBoardPins() {
|
|
34152
35051
|
return readDisk();
|
|
@@ -34167,7 +35066,7 @@ function homeBoardWorkspaceKey(workspaces) {
|
|
|
34167
35066
|
return workspaces.map((w) => w.path).filter(Boolean).sort().join("\n");
|
|
34168
35067
|
}
|
|
34169
35068
|
function cacheFile() {
|
|
34170
|
-
return (0,
|
|
35069
|
+
return (0, import_node_path68.join)(appDataDir(), "home-board-cache.json");
|
|
34171
35070
|
}
|
|
34172
35071
|
function emptyInputs() {
|
|
34173
35072
|
return {
|
|
@@ -34193,9 +35092,9 @@ function shouldCacheHomeBoardInputs(inputs) {
|
|
|
34193
35092
|
}
|
|
34194
35093
|
function readDiskCache() {
|
|
34195
35094
|
const path = cacheFile();
|
|
34196
|
-
if (!(0,
|
|
35095
|
+
if (!(0, import_node_fs70.existsSync)(path)) return null;
|
|
34197
35096
|
try {
|
|
34198
|
-
const raw = JSON.parse((0,
|
|
35097
|
+
const raw = JSON.parse((0, import_node_fs70.readFileSync)(path, "utf8"));
|
|
34199
35098
|
if (raw?.version !== CACHE_VERSION || typeof raw.fetchedAt !== "number") {
|
|
34200
35099
|
return null;
|
|
34201
35100
|
}
|
|
@@ -34209,8 +35108,8 @@ function readDiskCache() {
|
|
|
34209
35108
|
}
|
|
34210
35109
|
function writeDiskCache(entry) {
|
|
34211
35110
|
try {
|
|
34212
|
-
(0,
|
|
34213
|
-
(0,
|
|
35111
|
+
(0, import_node_fs70.mkdirSync)(appDataDir(), { recursive: true });
|
|
35112
|
+
(0, import_node_fs70.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
|
|
34214
35113
|
} catch {
|
|
34215
35114
|
}
|
|
34216
35115
|
}
|
|
@@ -34434,13 +35333,13 @@ async function startMcpServer() {
|
|
|
34434
35333
|
if (!worktreeProfile) {
|
|
34435
35334
|
server.tool(
|
|
34436
35335
|
"list_projects",
|
|
34437
|
-
"List registered projects (git repos). Each line is name, path,
|
|
35336
|
+
"List registered projects (git repos). Each line is name, path, that repo's origin as host:owner/repo (github:, cursor:, entire:, or another hostname), and project context when set. Projects do not share one git host. Use path as repoPath for list_board/list_branches/list_prs/list_issues/create_workspace. Account / project context (Settings \u2192 Agents / Projects) says which tickets and review PRs belong to the user.",
|
|
34438
35337
|
{},
|
|
34439
35338
|
async () => {
|
|
34440
35339
|
const workspaces = orch.listWorkspaces();
|
|
34441
35340
|
const lines = await Promise.all(
|
|
34442
35341
|
workspaces.map(async (w) => {
|
|
34443
|
-
const
|
|
35342
|
+
const tag = await resolveProjectRemoteTag(w.path).catch(() => null);
|
|
34444
35343
|
let profile;
|
|
34445
35344
|
try {
|
|
34446
35345
|
profile = resolveViewerProfileForRepo(w.path);
|
|
@@ -34448,7 +35347,7 @@ async function startMcpServer() {
|
|
|
34448
35347
|
profile = resolveViewerProfile();
|
|
34449
35348
|
}
|
|
34450
35349
|
const suffix = formatWorkspaceProfileSuffix(profile);
|
|
34451
|
-
return
|
|
35350
|
+
return tag ? `${w.name} ${w.path} ${tag}${suffix}` : `${w.name} ${w.path}${suffix}`;
|
|
34452
35351
|
})
|
|
34453
35352
|
);
|
|
34454
35353
|
return {
|
|
@@ -34465,7 +35364,7 @@ async function startMcpServer() {
|
|
|
34465
35364
|
async () => {
|
|
34466
35365
|
const threads = orch.getThreads();
|
|
34467
35366
|
const lines = threads.map((t) => {
|
|
34468
|
-
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0,
|
|
35367
|
+
const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path69.basename)(t.repoPath) || t.repoPath;
|
|
34469
35368
|
const live = orch.threadLooksLive(t) ? readTurnLive(t.id) : null;
|
|
34470
35369
|
const parent = t.parentThreadId ? ` parent:${t.parentThreadId.slice(0, 8)}` : "";
|
|
34471
35370
|
const preview = lastMessagePreview(t.messages, 80);
|
|
@@ -35295,7 +36194,7 @@ async function startMcpServer() {
|
|
|
35295
36194
|
error: true,
|
|
35296
36195
|
action,
|
|
35297
36196
|
lastError: message,
|
|
35298
|
-
hint: /body is too long/i.test(message) ? "Branch is already pushed. send_to_chat so the worktree agent
|
|
36197
|
+
hint: /body is too long/i.test(message) ? "Branch is already pushed. send_to_chat so the worktree agent retries with a short body (GitHub: gh pr create --draft --assignee @me -R <slug>; Cursor: origin pr create --draft --push -R <slug>; Entire: entire trail create --title with a short --body). Keep the description short." : void 0
|
|
35299
36198
|
},
|
|
35300
36199
|
true
|
|
35301
36200
|
);
|
|
@@ -35605,7 +36504,7 @@ async function startMcpServer() {
|
|
|
35605
36504
|
);
|
|
35606
36505
|
server.tool(
|
|
35607
36506
|
"list_issues",
|
|
35608
|
-
"List or search issues (Linear, AbleTime, or GitHub; falls back to GitHub). Use Settings \u2192 Agents / Projects notes and roles to pick tickets relevant to the viewer (query + assignee=me or unassigned as the notes say). Default 40; pass query and/or limit (max 250) when truncated. assignee: me (Linear default), unassigned, all, or a user id. Pass updatedSince for new/updated tickets and new comments (\u201Cany updates since yesterday?\u201D). Then create_workspace with sourceType=ticket.",
|
|
36507
|
+
"List or search issues (Linear, AbleTime, or GitHub; falls back to GitHub). Cursor Origin has no issue tracker \u2014 a Cursor repo returns unavailableReason unless Linear or AbleTime is the source. Entire tickets are Trails (`entire trail`), not GitHub Issues, so an Entire repo does the same unless Linear or AbleTime is the source. Use Settings \u2192 Agents / Projects notes and roles to pick tickets relevant to the viewer (query + assignee=me or unassigned as the notes say). Default 40; pass query and/or limit (max 250) when truncated. assignee: me (Linear default), unassigned, all, or a user id. Pass updatedSince for new/updated tickets and new comments (\u201Cany updates since yesterday?\u201D). Then create_workspace with sourceType=ticket.",
|
|
35609
36508
|
{
|
|
35610
36509
|
repoPath: import_zod11.z.string(),
|
|
35611
36510
|
query: import_zod11.z.string().optional().describe("Search title, identifier, or description"),
|