@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.
Files changed (75) hide show
  1. package/dist/{abletime-L3ODNTBT.js → abletime-CRRLEYTE.js} +4 -4
  2. package/dist/{abletime-DVYO4IVS.js → abletime-ICW6Q2AB.js} +4 -4
  3. package/dist/{abletime-oauth-2VWQCXGT.js → abletime-oauth-P3KPPJE4.js} +3 -3
  4. package/dist/{abletime-oauth-6DTX7LB7.js → abletime-oauth-Q76XBSPG.js} +3 -3
  5. package/dist/{abletime-rest-PYQM234B.js → abletime-rest-DNFAYYLP.js} +3 -3
  6. package/dist/{abletime-rest-7NICZYOC.js → abletime-rest-DQ3PNKXP.js} +3 -3
  7. package/dist/agents/cursor-runner.js +2 -2
  8. package/dist/{agents-KABJUDPI.js → agents-PFVYC36J.js} +10 -10
  9. package/dist/{agents-2AR7I6B6.js → agents-ROFDNE7Z.js} +11 -11
  10. package/dist/{app-settings-CZVFMOI5.js → app-settings-KMPVI2B7.js} +1 -1
  11. package/dist/{app-settings-V6YPYY57.js → app-settings-P3H4SEIK.js} +1 -1
  12. package/dist/{chunk-QVN2WOKJ.js → chunk-2TUE6JKQ.js} +12 -0
  13. package/dist/{chunk-OO3R6CS2.js → chunk-45UB2J47.js} +537 -90
  14. package/dist/{chunk-DD67JWTN.js → chunk-4YXZOXMT.js} +1 -1
  15. package/dist/{chunk-5OM7UCTQ.js → chunk-56M6VEXI.js} +3 -3
  16. package/dist/{chunk-ZM43NTJJ.js → chunk-5OQONGHO.js} +5 -5
  17. package/dist/{chunk-V5BTGQ6M.js → chunk-6U6QRUHY.js} +1 -1
  18. package/dist/{chunk-QPLQXWND.js → chunk-72PUPPV2.js} +3 -3
  19. package/dist/{chunk-53OCOGND.js → chunk-7JV5QP5K.js} +3 -3
  20. package/dist/{chunk-HOLB6ZFZ.js → chunk-B4CDSSRN.js} +2 -0
  21. package/dist/{chunk-ST2UQBJT.js → chunk-BANN5HUX.js} +8 -8
  22. package/dist/{chunk-AESO2KJS.js → chunk-BPDVHB4U.js} +3 -3
  23. package/dist/{chunk-WLZHRNE6.js → chunk-BTIXTH4N.js} +3 -3
  24. package/dist/{chunk-AUJ4XB6H.js → chunk-DFUXW2IT.js} +2 -2
  25. package/dist/{chunk-I5TESHDN.js → chunk-EQ52JR3Q.js} +5 -5
  26. package/dist/{chunk-E52SQENB.js → chunk-FZKAAAEB.js} +3 -1
  27. package/dist/{chunk-YALFZKSC.js → chunk-H6GXYV5Q.js} +23 -20
  28. package/dist/{chunk-JD7CC6SW.js → chunk-HHPTGIKS.js} +2 -2
  29. package/dist/{chunk-GCHSJCCX.js → chunk-IFU4W4C4.js} +3 -1
  30. package/dist/{chunk-YEIHCSZS.js → chunk-IFWGG2M2.js} +9 -0
  31. package/dist/{chunk-IAPO7ZFG.js → chunk-JNEFEA2A.js} +8 -8
  32. package/dist/{chunk-U2N32EA6.js → chunk-LD2DXHBP.js} +543 -92
  33. package/dist/{chunk-7Q327QF2.js → chunk-QIC2JXHW.js} +5 -5
  34. package/dist/{chunk-2P4XKWV2.js → chunk-QKHEVSB5.js} +1 -1
  35. package/dist/{chunk-NRSC4N5X.js → chunk-RGUZPMTF.js} +8 -8
  36. package/dist/{chunk-AZ4GEEGL.js → chunk-RHJKZOPQ.js} +3 -3
  37. package/dist/{chunk-HH3VVTEC.js → chunk-S5CJUIPY.js} +5 -5
  38. package/dist/{chunk-A6C67DY5.js → chunk-SPMIZSKH.js} +16 -13
  39. package/dist/{chunk-FDXHNROD.js → chunk-TEFFR6PB.js} +2 -2
  40. package/dist/{chunk-TUJFCJJU.js → chunk-UWJUARFA.js} +546 -187
  41. package/dist/{chunk-MBYGWNLQ.js → chunk-UX6EDDGR.js} +1 -1
  42. package/dist/{chunk-LXBZQP2Q.js → chunk-V76MQ7MH.js} +5 -5
  43. package/dist/{chunk-GH7UMULC.js → chunk-XDQSHSL6.js} +8 -8
  44. package/dist/{chunk-2UG25RMK.js → chunk-YBTSZ2SW.js} +12 -0
  45. package/dist/{chunk-A46CIARU.js → chunk-YWBLQBSN.js} +549 -188
  46. package/dist/{chunk-Y3Y7SZBZ.js → chunk-ZHGMRBSC.js} +1 -1
  47. package/dist/{conductor-MG2RAQNT.js → conductor-CNT3VL3S.js} +8 -8
  48. package/dist/{conductor-NIUBJLTS.js → conductor-XLXJU24U.js} +8 -8
  49. package/dist/{connected-teams-IHK2WXUI.js → connected-teams-H4LZOJDC.js} +2 -2
  50. package/dist/{connected-teams-IFW3NQ6G.js → connected-teams-XYRZMQO4.js} +2 -2
  51. package/dist/{coordinator-prompt-X74F3JIC.js → coordinator-prompt-D5EE2YTV.js} +6 -6
  52. package/dist/{coordinator-prompt-3CT3D5UM.js → coordinator-prompt-TU24TQ4I.js} +6 -6
  53. package/dist/{global-workspace-BUWMRNW5.js → global-workspace-743TPQNF.js} +6 -6
  54. package/dist/{global-workspace-FTNJEML3.js → global-workspace-UPMXDQM7.js} +6 -6
  55. package/dist/index.cjs +2160 -867
  56. package/dist/index.d.cts +211 -74
  57. package/dist/index.d.ts +211 -74
  58. package/dist/index.js +496 -78
  59. package/dist/mcp/run-stdio.cjs +1377 -478
  60. package/dist/mcp/run-stdio.js +80 -32
  61. package/dist/{optional-cli-6GGESWGE.js → optional-cli-2YDQQCCD.js} +5 -5
  62. package/dist/{optional-cli-EMSZBYDE.js → optional-cli-USD6PROE.js} +4 -4
  63. package/dist/{orchestrator-NCA4LSCC.js → orchestrator-B4STIW4B.js} +14 -14
  64. package/dist/{orchestrator-H3DSZ5RM.js → orchestrator-VQ4HRYVB.js} +13 -13
  65. package/dist/{run-PCX5QWR4.js → run-7QMRYP3F.js} +3 -1
  66. package/dist/{run-TB7RBII2.js → run-UPNCCRCN.js} +3 -1
  67. package/dist/{thread-store-PLNIWFZ4.js → thread-store-3F7GR64A.js} +1 -1
  68. package/dist/{thread-store-7BBA63KV.js → thread-store-JRSL4HQY.js} +1 -1
  69. package/dist/{workspaces-MGKEAYNM.js → workspaces-47WQL5IG.js} +6 -6
  70. package/dist/{workspaces-2V5UYPYC.js → workspaces-75FD4QUR.js} +6 -6
  71. package/dist/{worktree-XJKLDYOL.js → worktree-G3E6WP66.js} +11 -9
  72. package/dist/{worktree-THAXW5U7.js → worktree-PDNPL5CQ.js} +11 -9
  73. package/dist/{worktree-exclude-RXZPTCNH.js → worktree-exclude-INRDQFWV.js} +2 -2
  74. package/dist/{worktree-exclude-5N2KTYIT.js → worktree-exclude-P5NJODWP.js} +2 -2
  75. package/package.json +1 -1
@@ -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 schedules";
1408
+ if (/list_schedules$/i.test(name)) return "List automations";
1409
1409
  if (/create_schedule$/i.test(name)) {
1410
- return str(input?.name) ? `Schedule ${str(input?.name)}` : "Create schedule";
1410
+ return str(input?.name) ? `Automation ${str(input?.name)}` : "Create automation";
1411
1411
  }
1412
- if (/update_schedule$/i.test(name)) return "Update schedule";
1413
- if (/delete_schedule$/i.test(name)) return "Delete schedule";
1414
- if (/run_schedule$/i.test(name)) return "Run schedule";
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((t) => !expiredIds.has(t.id));
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(remoteUrl) {
6543
+ function githubHttpsInsteadOfEntries(remoteUrl2) {
6530
6544
  const entries = [...HTTPS_REWRITE];
6531
- const url = remoteUrl?.trim() ?? "";
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 trimmed = url.trim().replace(/\.git$/i, "");
8051
- if (!trimmed) return null;
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 slugFromGitRemote(repoPath, remote) {
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 parseGithubSlugFromRemoteUrl(result.stdout);
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 fromOrigin = await slugFromGitRemote(repoPath, "origin");
8068
- if (fromOrigin) return fromOrigin;
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(remoteUrl) {
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 remoteUrl = await originRemoteUrl(repoPath);
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(remoteUrl),
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 remoteUrl = await originRemoteUrl(worktreePath);
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(remoteUrl),
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 import_node_crypto4, import_node_fs18, import_node_os7, import_node_path21, PR_INFO_JSON_FIELDS, PR_DETAILS_JSON_FIELDS;
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, import_node_crypto5.randomUUID)()}${ext}`;
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, import_node_crypto5.randomUUID)(),
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, import_node_crypto5.randomUUID)(),
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, import_node_crypto5.randomUUID)(),
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, import_node_crypto5.randomUUID)(),
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, import_node_crypto5.randomUUID)(),
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, import_node_crypto5.randomUUID)(),
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, import_node_crypto5.randomUUID)(),
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, import_node_crypto5.randomUUID)(),
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, import_node_crypto5, IMAGE_EXTENSIONS, IMAGE_MIME_BY_EXT, MAX_INLINE_BYTES, MAX_PREVIEW_BYTES, IMAGE_HINT_RE, PLACEHOLDER_CONTENT_RE;
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
- import_node_crypto5 = require("crypto");
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, import_node_os8.homedir)(), "sideboard", "workspaces", slug);
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, import_node_os8, import_node_path23;
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
- import_node_os8 = require("os");
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 githubSlug = await resolveGithubRepoSlug(w.path).catch(() => null);
10056
- return { ...w, githubSlug };
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 (or send_to_chat `gh pr create --draft --assignee @me -R <origin-owner/name>`).",
10068
- "- Always target the child worktree's **origin** (`github:` slug from list_projects / `git remote get-url origin` in that worktree). Never open PRs against `upstream`.",
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 slug + project context when set). Use account / project context to pick the right repo for tickets or reviews.",
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
- `- 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 \u2014 do not use it for that review-inbox ask. When they ask for tickets to work on, follow their context (assignee=me or unassigned as it says).`,
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 local jobs 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. 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 schedules are enabled (or call set_caffeinate).",
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. (draft PRs: `gh pr create --draft --assignee @me -R <origin-owner/name>` using the workspace `github:` slug \u2014 never upstream). Never run git/gh from this orchestration cwd, and never merge the PR yourself.',
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, import_node_os9.homedir)(), ".brightsy", "config.json");
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, import_node_os9, import_node_path26;
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
- import_node_os9 = require("os");
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, import_node_os10.homedir)();
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, import_node_os10, import_node_path28;
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
- import_node_os10 = require("os");
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, import_node_os11.homedir)()) {
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, import_node_os11, import_node_path30, AGENT_RUNNER_MAX_OLD_SPACE_MB, MAX_OLD_SPACE_FLAG, PREFERRED_LTS_MAJORS;
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
- import_node_os11 = require("os");
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, import_node_os12.tmpdir)(), "sideboard-mcp-"));
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, import_node_os12, 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;
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
- import_node_os12 = require("os");
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, import_node_os13.homedir)(), ".claude.json");
13049
+ return (0, import_node_path32.join)((0, import_node_os12.homedir)(), ".claude.json");
12659
13050
  }
12660
- var import_node_fs30, import_node_os13, import_node_path32;
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
- import_node_os13 = require("os");
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, import_node_os14.homedir)()) {
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, import_node_os14.homedir)()) {
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, import_node_os14.homedir)()) {
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, import_node_os14.homedir)()) {
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, import_node_os14.homedir)()) {
12748
- const claudePath = home === (0, import_node_os14.homedir)() ? userClaudeMcpConfigPath() : (0, import_node_path33.join)(home, ".claude.json");
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, import_node_os14.homedir)(), projectDirs) {
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, import_node_os14, import_node_path33, import_smol_toml2;
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
- import_node_os14 = require("os");
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, import_node_crypto6.randomBytes)(24).toString("hex");
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, import_node_crypto6.randomUUID)(),
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 import_node_crypto6, import_node_http, import_server2, import_streamableHttp;
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
- import_node_crypto6 = require("crypto");
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, import_node_os15.homedir)(), projectDirs)
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, import_node_os15.homedir)(), dirs), dirs);
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, import_node_crypto7.createHash)("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
13530
+ return (0, import_node_crypto6.createHash)("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
13140
13531
  }
13141
- var import_node_crypto7, import_node_os15, TOOL_TTL_MS, McpUiHost, singleton;
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
- import_node_crypto7 = require("crypto");
13146
- import_node_os15 = require("os");
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, import_node_os16.homedir)(), ".codex", "config.toml"),
14268
- (0, import_node_path36.join)((0, import_node_os16.homedir)(), ".config", "codex", "config.toml")
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, import_node_os16.homedir)(), ".codex", "auth.json");
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, import_node_os16, import_node_path36, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
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
- import_node_os16 = require("os");
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, import_node_os17, import_node_path41, CLAUDE_USAGE_ERROR_TTL_MS, CLAUDE_USAGE_STALE_OK_MS;
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
- import_node_os17 = require("os");
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 expandCanonicalGitRequest(prompt) {
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
- let expansion = action ? gitActionExpansion(action, base) : null;
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, import_node_crypto8.randomUUID)(),
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 import_node_crypto8;
17713
+ var import_node_crypto7;
17270
17714
  var init_chat_tabs = __esm({
17271
17715
  "src/threads/chat-tabs.ts"() {
17272
17716
  "use strict";
17273
- import_node_crypto8 = require("crypto");
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, import_node_crypto9.randomUUID)(),
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 import_node_crypto9, 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;
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
- import_node_crypto9 = require("crypto");
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, import_node_crypto10.createHash)("sha1").update(norm).digest("hex").slice(0, 16)}`;
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 import_node_crypto10, import_node_fs48, import_node_path48, EMPTY, MAX_SETUP_LOG_CHARS, memory, persistTimers, PERSIST_DEBOUNCE_MS;
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
- import_node_crypto10 = require("crypto");
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, import_node_crypto11.createHash)("sha1").update(norm).digest("hex").slice(0, 16);
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 import_node_crypto11, import_node_fs49, import_node_path49, EMPTY2, MAX_RUN_LOG_CHARS, DEFAULT_RUN_LOG_TAIL_CHARS, memory2, persistTimers2, PERSIST_DEBOUNCE_MS2;
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
- import_node_crypto11 = require("crypto");
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 import_node_crypto12, import_node_path53, queues, publisher;
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
- import_node_crypto12 = require("crypto");
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, import_node_crypto13.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
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 import_node_crypto13;
20498
+ var import_node_crypto12;
20055
20499
  var init_steer_request = __esm({
20056
20500
  "src/orchestrator/steer-request.ts"() {
20057
20501
  "use strict";
20058
- import_node_crypto13 = require("crypto");
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, import_node_crypto14.randomBytes)(32).toString("base64url");
20856
- const challenge = (0, import_node_crypto14.createHash)("sha256").update(verifier).digest("base64url");
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, import_node_crypto14.randomBytes)(16).toString("hex");
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 import_node_crypto14, 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;
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
- import_node_crypto14 = require("crypto");
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, import_node_crypto15.randomUUID)()}${ext}`;
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, import_node_crypto15, ISSUE_ATTACHMENT_MAX_BYTES, MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE, EXT_BY_MIME;
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
- import_node_crypto15 = require("crypto");
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, import_node_crypto16.randomUUID)(),
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 import_node_crypto16, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
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
- import_node_crypto16 = require("crypto");
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, import_node_os18.tmpdir)(), "sideboard-conductor-"));
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, import_node_os18.tmpdir)(), "sideboard-conductor-"));
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, import_node_os18, import_node_path56, import_node_module6, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
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
- import_node_os18 = require("os");
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, import_node_fs58.existsSync)(worktreePath)) return "missing_worktree";
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, import_node_fs58.existsSync)(worktreePath)) {
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, import_node_path57.join)(worktreePath, path);
25049
+ const abs = (0, import_node_path58.join)(worktreePath, path);
24351
25050
  try {
24352
- const st = (0, import_node_fs58.statSync)(abs);
25051
+ const st = (0, import_node_fs59.statSync)(abs);
24353
25052
  if (st.isFile() && st.size > maxHunk) {
24354
- const buf = (0, import_node_fs58.readFileSync)(abs).subarray(0, maxHunk);
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, import_node_path57.join)(worktreePath, relativePath);
24822
- if (!(0, import_node_fs58.existsSync)(abs)) return "missing";
24823
- const st = (0, import_node_fs58.statSync)(abs);
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, import_node_path57.join)(worktreePath, relativePath);
24853
- const st = (0, import_node_fs58.statSync)(abs);
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, import_node_fs58.readFileSync)(abs);
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, import_node_path57.join)(worktreePath, relativePath);
24873
- const st = (0, import_node_fs58.statSync)(abs);
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, import_node_fs58.readFileSync)(abs);
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, import_node_path57.join)(worktreePath, relativePath);
24921
- (0, import_node_fs58.mkdirSync)((0, import_node_path57.dirname)(abs), { recursive: true });
24922
- (0, import_node_fs58.writeFileSync)(abs, content, "utf8");
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 import_node_fs58, import_node_path57, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
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
- import_node_fs58 = require("fs");
24967
- import_node_path57 = require("path");
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, import_node_path58.join)(worktreePath, relativePath);
25190
- const fd = (0, import_node_fs59.openSync)(abs, "r");
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, import_node_fs59.fstatSync)(fd);
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, import_node_fs59.readSync)(fd, buf, 0, length, start);
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, import_node_fs59.closeSync)(fd);
25916
+ (0, import_node_fs60.closeSync)(fd);
25217
25917
  }
25218
25918
  }
25219
- var import_node_fs59, import_node_path58, WORKTREE_TAIL_BYTES;
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
- import_node_fs59 = require("fs");
25224
- import_node_path58 = require("path");
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, import_node_fs60.readFileSync)(skillMd, "utf8");
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, import_node_fs60.existsSync)(dir)) return;
26092
+ if (!(0, import_node_fs61.existsSync)(dir)) return;
25393
26093
  let entries;
25394
26094
  try {
25395
- entries = (0, import_node_fs60.readdirSync)(dir);
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, import_node_path59.join)(dir, entry, "SKILL.md");
25402
- if (!(0, import_node_fs60.existsSync)(skillMd)) continue;
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, import_node_fs60.statSync)(skillMd).isFile()) continue;
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, import_node_fs60.existsSync)(pluginsRoot)) return;
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, import_node_fs60.readdirSync)(dir);
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, import_node_path59.join)(dir, "SKILL.md"), "cli");
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, import_node_path59.join)(dir, entry);
26128
+ const full = (0, import_node_path60.join)(dir, entry);
25429
26129
  try {
25430
- if (!(0, import_node_fs60.statSync)(full).isDirectory()) continue;
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, import_node_path59.join)(worktreePath, rel), "workspace", collected);
26148
+ scanSkillsDir((0, import_node_path60.join)(worktreePath, rel), "workspace", collected);
25449
26149
  }
25450
26150
  for (const abs of [
25451
- (0, import_node_path59.join)(home, ".claude/skills"),
25452
- (0, import_node_path59.join)(home, ".cursor/skills"),
25453
- (0, import_node_path59.join)(home, ".sideboard/skills"),
25454
- (0, import_node_path59.join)(home, ".brightsy/skills")
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, import_node_path59.join)(home, ".claude/plugins"), collected);
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, import_node_fs60.readFileSync)(skillPath, "utf8");
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 import_node_fs60, import_node_os19, import_node_path59, BUNDLED_SKILL_PREFIX;
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
- import_node_fs60 = require("fs");
26202
+ import_node_fs61 = require("fs");
25503
26203
  import_node_os19 = require("os");
25504
- import_node_path59 = require("path");
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, import_node_path60.dirname)((0, import_node_url5.fileURLToPath)(url));
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, import_node_path60.join)(dir, "scripts", name);
25839
- if ((0, import_node_fs61.existsSync)(script)) return script;
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, import_node_path60.dirname)(dir);
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 import_node_fs61, import_node_path60, import_node_url5, import_meta8, DETACHED_JOB_FILENAMES;
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
- import_node_fs61 = require("fs");
25872
- import_node_path60 = require("path");
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
- lines.push(formatGitAuthModeDirective(opts?.gitAuthMode ?? "auto"));
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
- "Schedules: `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 schedules are enabled."
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. Schedules: create_schedule (at / every / cron; chatId=self for this chat). set_workspace_tags updates labels on this workspace (omit ref).";
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, import_node_fs62.existsSync)((0, import_node_path61.join)(worktree, ".claude/skills/graph-engineering/SKILL.md"));
26089
- const hasReviewSkill = !!worktree && (0, import_node_fs62.existsSync)((0, import_node_path61.join)(worktree, ".claude/skills/review/SKILL.md"));
26090
- const hasInstallSkill = !!worktree && (0, import_node_fs62.existsSync)((0, import_node_path61.join)(worktree, ".claude/skills/worktree-install/SKILL.md"));
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 import_node_fs62, import_node_path61, PR_GOAL_RE, GITHUB_TICKET_REF, KEYED_TICKET_REF;
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
- import_node_fs62 = require("fs");
26135
- import_node_path61 = require("path");
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
- ...truncated ? {
26493
- hint: 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."
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, import_node_path62.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
26877
- if ((0, import_node_fs63.existsSync)(gitignoreAbs)) return;
26878
- (0, import_node_fs63.mkdirSync)((0, import_node_path62.dirname)(gitignoreAbs), { recursive: true });
26879
- (0, import_node_fs63.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
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, import_node_path62.join)(worktreePath, PLAN_FILE_REL);
27629
+ return (0, import_node_path63.join)(worktreePath, PLAN_FILE_REL);
26883
27630
  }
26884
27631
  function readTextIfPresent2(abs) {
26885
- if (!(0, import_node_fs63.existsSync)(abs)) return null;
27632
+ if (!(0, import_node_fs64.existsSync)(abs)) return null;
26886
27633
  try {
26887
- const content = (0, import_node_fs63.readFileSync)(abs, "utf8");
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, import_node_path62.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path62.join)(worktreePath, LEGACY_PLAN_FILE_REL));
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, import_node_fs63.mkdirSync)((0, import_node_path62.dirname)(abs), { recursive: true });
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, import_node_fs63.writeFileSync)(abs, body, "utf8");
27648
+ (0, import_node_fs64.writeFileSync)(abs, body, "utf8");
26902
27649
  return PLAN_FILE_REL;
26903
27650
  }
26904
- var import_node_fs63, import_node_path62;
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
- import_node_fs63 = require("fs");
26909
- import_node_path62 = require("path");
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, import_node_path63.join)(appDataDir(), "caffeinate-hold.json");
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, import_node_fs64.existsSync)(path)) return null;
27739
+ if (!(0, import_node_fs65.existsSync)(path)) return null;
26993
27740
  try {
26994
- const parsed = JSON.parse((0, import_node_fs64.readFileSync)(path, "utf8"));
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, import_node_fs64.unlinkSync)(caffeinateHoldPath());
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, import_node_fs64, import_node_path63, hooks;
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
- import_node_fs64 = require("fs");
27107
- import_node_path63 = require("path");
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, import_node_path64.join)(appDataDir(), "schedules.json");
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 schedule";
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, import_node_fs65.existsSync)(path)) return [];
27999
+ if (!(0, import_node_fs66.existsSync)(path)) return [];
27217
28000
  try {
27218
- const parsed = JSON.parse((0, import_node_fs65.readFileSync)(path, "utf8"));
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(`Schedule not found: ${id}`);
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(`Schedule not found: ${id}`);
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(`Schedule not found: ${id}`);
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, import_node_fs65, import_node_path64, import_croner, DURATION_RE, UNIT_MS;
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
- import_node_fs65 = require("fs");
27349
- import_node_path64 = require("path");
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) => orch.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(`Schedule not found: ${id}`);
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 (schedule.threadId && schedule.when.kind !== "once") {
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, import_node_fs66.existsSync)(runsPath)) return null;
28346
+ if (!(0, import_node_fs67.existsSync)(runsPath)) return null;
27499
28347
  try {
27500
- const lines = (0, import_node_fs66.readFileSync)(runsPath, "utf8").split("\n");
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 import_node_fs66;
28374
+ var import_node_fs67;
27527
28375
  var init_cursor_recover = __esm({
27528
28376
  "src/agents/cursor-recover.ts"() {
27529
28377
  "use strict";
27530
- import_node_fs66 = require("fs");
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(thread2.id, goal);
28456
+ return orch.send(ready2.id, goal);
27608
28457
  }
27609
- return thread2;
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(updated.id, goal);
28495
+ return orch.send(ready.id, goal);
27646
28496
  }
27647
- return updated;
28497
+ return ready;
27648
28498
  }
27649
- var import_node_events, import_node_crypto18, import_node_fs67, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton2;
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
- import_node_fs67 = require("fs");
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, import_node_fs67.existsSync)(thread.worktreePath)) {
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, import_node_fs67.existsSync)(thread.worktreePath)) {
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 gitPrompt = expandCanonicalGitRequest(promptText);
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
- resolveGithubRepoSlug(fresh.worktreePath),
28697
- 8e3,
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, import_node_fs67.existsSync)(thread.worktreePath)) {
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 import_node_path68 = require("path");
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, repoArgs) {
31517
+ async function readGitHubIssueProjects(number, cwd, repoArgs2) {
30665
31518
  const result = await gh(
30666
- ["issue", "view", String(number), ...repoArgs, "--json", "projectItems"],
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, repoArgs) {
30685
- const read = await readGitHubIssueProjects(number, cwd, repoArgs);
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
- ...repoArgs,
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, repoArgs);
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), ...repoArgs, "--body", body],
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), ...repoArgs], cwd, { reject: false }),
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), ...repoArgs], cwd, { reject: false }),
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), ...repoArgs];
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, repoArgs);
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 + repoArgs.length;
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", ...repoArgs, "--title", title];
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 schedules that trigger a chat when due. Jobs fire only while Sideboard.app is running on this Mac (sleep skips until wake).",
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 schedule 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. Creating or enabling a future job turns on Settings \u2192 Advanced \u2192 Caffeinate while schedules are enabled so the Mac stays awake until due. Sideboard.app must be running for the job to fire.",
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 schedule fires"),
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 schedules 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."
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 schedule (prompt, cadence, target chat, enabled). Pass id from list_schedules.",
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 schedule. Pass id from list_schedules.",
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 a schedule 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.",
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(`Schedule not found: ${id}`));
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 import_node_path65 = require("path");
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 import_node_fs69 = require("fs");
34121
- var import_node_path67 = require("path");
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 import_node_fs68 = require("fs");
34128
- var import_node_path66 = require("path");
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, import_node_path66.join)(appDataDir(), FILE);
35033
+ return (0, import_node_path67.join)(appDataDir(), FILE);
34135
35034
  }
34136
35035
  function readDisk() {
34137
35036
  const path = pinsFile();
34138
- if (!(0, import_node_fs68.existsSync)(path)) return [];
35037
+ if (!(0, import_node_fs69.existsSync)(path)) return [];
34139
35038
  try {
34140
- const raw = JSON.parse((0, import_node_fs68.readFileSync)(path, "utf8"));
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, import_node_fs68.mkdirSync)(appDataDir(), { recursive: true });
34149
- (0, import_node_fs68.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
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, import_node_path67.join)(appDataDir(), "home-board-cache.json");
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, import_node_fs69.existsSync)(path)) return null;
35095
+ if (!(0, import_node_fs70.existsSync)(path)) return null;
34197
35096
  try {
34198
- const raw = JSON.parse((0, import_node_fs69.readFileSync)(path, "utf8"));
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, import_node_fs69.mkdirSync)(appDataDir(), { recursive: true });
34213
- (0, import_node_fs69.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
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, github:owner/repo when resolvable, and project context when set \u2014 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.",
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 slug = await resolveGithubRepoSlug(w.path).catch(() => null);
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 slug ? `${w.name} ${w.path} github:${slug}${suffix}` : `${w.name} ${w.path}${suffix}`;
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, import_node_path68.basename)(t.repoPath) || t.repoPath;
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 runs gh pr create --draft --assignee @me -R <origin> --body-file <short.md>. Keep the description short." : void 0
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"),