@sideboard-ai/core 0.1.320 → 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 (56) hide show
  1. package/dist/agents/cursor-runner.js +2 -2
  2. package/dist/{agents-CJZYJOWP.js → agents-PFVYC36J.js} +8 -8
  3. package/dist/{agents-F2E2HACB.js → agents-ROFDNE7Z.js} +9 -9
  4. package/dist/{chunk-QVN2WOKJ.js → chunk-2TUE6JKQ.js} +12 -0
  5. package/dist/{chunk-B7UL36WH.js → chunk-45UB2J47.js} +335 -65
  6. package/dist/{chunk-DD67JWTN.js → chunk-4YXZOXMT.js} +1 -1
  7. package/dist/{chunk-V5BTGQ6M.js → chunk-6U6QRUHY.js} +1 -1
  8. package/dist/{chunk-QGY3UNNL.js → chunk-7JV5QP5K.js} +1 -1
  9. package/dist/{chunk-DJZRDTZY.js → chunk-BANN5HUX.js} +6 -6
  10. package/dist/{chunk-CB5Z5C3J.js → chunk-BPDVHB4U.js} +1 -1
  11. package/dist/{chunk-RHJPP26T.js → chunk-H6GXYV5Q.js} +14 -11
  12. package/dist/{chunk-4GZC6DDH.js → chunk-IFWGG2M2.js} +7 -0
  13. package/dist/{chunk-LLIEU2ZB.js → chunk-JNEFEA2A.js} +6 -6
  14. package/dist/{chunk-IDPFLXRY.js → chunk-LD2DXHBP.js} +341 -67
  15. package/dist/{chunk-2P4XKWV2.js → chunk-QKHEVSB5.js} +1 -1
  16. package/dist/{chunk-P74GXA5V.js → chunk-RGUZPMTF.js} +6 -6
  17. package/dist/{chunk-UK2KESBN.js → chunk-RHJKZOPQ.js} +3 -3
  18. package/dist/{chunk-HH3VVTEC.js → chunk-S5CJUIPY.js} +5 -5
  19. package/dist/{chunk-5S3YP5KH.js → chunk-SPMIZSKH.js} +14 -11
  20. package/dist/{chunk-EQ5JY3MJ.js → chunk-TEFFR6PB.js} +1 -1
  21. package/dist/{chunk-EDXTP2VR.js → chunk-UWJUARFA.js} +545 -186
  22. package/dist/{chunk-OAFBHCSM.js → chunk-UX6EDDGR.js} +1 -1
  23. package/dist/{chunk-ZYZBCLJR.js → chunk-XDQSHSL6.js} +6 -6
  24. package/dist/{chunk-2UG25RMK.js → chunk-YBTSZ2SW.js} +12 -0
  25. package/dist/{chunk-PNLGWAYE.js → chunk-YWBLQBSN.js} +548 -187
  26. package/dist/{chunk-Y3Y7SZBZ.js → chunk-ZHGMRBSC.js} +1 -1
  27. package/dist/{conductor-FPFY6Z5S.js → conductor-CNT3VL3S.js} +6 -6
  28. package/dist/{conductor-JA5W3L7C.js → conductor-XLXJU24U.js} +6 -6
  29. package/dist/{connected-teams-IHK2WXUI.js → connected-teams-H4LZOJDC.js} +2 -2
  30. package/dist/{connected-teams-IFW3NQ6G.js → connected-teams-XYRZMQO4.js} +2 -2
  31. package/dist/{coordinator-prompt-NUC2ZXMJ.js → coordinator-prompt-D5EE2YTV.js} +5 -5
  32. package/dist/{coordinator-prompt-M75TAN43.js → coordinator-prompt-TU24TQ4I.js} +5 -5
  33. package/dist/{global-workspace-5A4H34AF.js → global-workspace-743TPQNF.js} +5 -5
  34. package/dist/{global-workspace-SCLQUNUI.js → global-workspace-UPMXDQM7.js} +5 -5
  35. package/dist/index.cjs +1577 -471
  36. package/dist/index.d.cts +182 -70
  37. package/dist/index.d.ts +182 -70
  38. package/dist/index.js +480 -62
  39. package/dist/mcp/run-stdio.cjs +1159 -447
  40. package/dist/mcp/run-stdio.js +65 -17
  41. package/dist/{optional-cli-MRNQKVIR.js → optional-cli-2YDQQCCD.js} +4 -4
  42. package/dist/{optional-cli-2V3SLME5.js → optional-cli-USD6PROE.js} +3 -3
  43. package/dist/{orchestrator-NPFWU3XU.js → orchestrator-B4STIW4B.js} +12 -12
  44. package/dist/{orchestrator-7BDZ5GWD.js → orchestrator-VQ4HRYVB.js} +11 -11
  45. package/dist/{run-PCX5QWR4.js → run-7QMRYP3F.js} +3 -1
  46. package/dist/{run-TB7RBII2.js → run-UPNCCRCN.js} +3 -1
  47. package/dist/{thread-store-JWHVXLDX.js → thread-store-3F7GR64A.js} +1 -1
  48. package/dist/{thread-store-QHYN2ZMQ.js → thread-store-JRSL4HQY.js} +1 -1
  49. package/dist/{workspaces-KWMBOTQD.js → workspaces-47WQL5IG.js} +5 -5
  50. package/dist/{workspaces-5QA3YM4L.js → workspaces-75FD4QUR.js} +5 -5
  51. package/dist/{worktree-O6BANO2I.js → worktree-G3E6WP66.js} +10 -8
  52. package/dist/{worktree-7EATHVYQ.js → worktree-PDNPL5CQ.js} +10 -8
  53. package/dist/{worktree-exclude-RXZPTCNH.js → worktree-exclude-INRDQFWV.js} +2 -2
  54. package/dist/{worktree-exclude-5N2KTYIT.js → worktree-exclude-P5NJODWP.js} +2 -2
  55. package/package.json +1 -1
  56. /package/dist/{chunk-XMHMVEIY.js → chunk-B4CDSSRN.js} +0 -0
@@ -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,
@@ -6528,9 +6540,9 @@ var init_app_settings = __esm({
6528
6540
  });
6529
6541
 
6530
6542
  // src/git/git-auth-mode.ts
6531
- function githubHttpsInsteadOfEntries(remoteUrl) {
6543
+ function githubHttpsInsteadOfEntries(remoteUrl2) {
6532
6544
  const entries = [...HTTPS_REWRITE];
6533
- const url = remoteUrl?.trim() ?? "";
6545
+ const url = remoteUrl2?.trim() ?? "";
6534
6546
  const scpAlias = url.match(/^(git@github\.com-[^:]+):/i);
6535
6547
  if (scpAlias?.[1]) {
6536
6548
  entries.push({
@@ -6737,6 +6749,58 @@ var init_git_auth_mode = __esm({
6737
6749
  }
6738
6750
  });
6739
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
+
6740
6804
  // src/git/list-prs.ts
6741
6805
  function normalizePrListState(state) {
6742
6806
  const key = (state ?? "").trim().toLowerCase();
@@ -7105,42 +7169,6 @@ var init_list_prs = __esm({
7105
7169
  }
7106
7170
  });
7107
7171
 
7108
- // src/util/enqueue-by-key.ts
7109
- function enqueueByKey(key, fn) {
7110
- const prev = tails.get(key) ?? Promise.resolve();
7111
- const next = prev.then(fn, fn);
7112
- tails.set(
7113
- key,
7114
- next.then(
7115
- () => void 0,
7116
- () => void 0
7117
- )
7118
- );
7119
- return next;
7120
- }
7121
- var tails;
7122
- var init_enqueue_by_key = __esm({
7123
- "src/util/enqueue-by-key.ts"() {
7124
- "use strict";
7125
- tails = /* @__PURE__ */ new Map();
7126
- }
7127
- });
7128
-
7129
- // src/git/repo-git-lock.ts
7130
- function repoLockKey(repoPath) {
7131
- const trimmed = repoPath.replace(/\/+$/, "");
7132
- return `git-worktree:${trimmed || "/"}`;
7133
- }
7134
- function withRepoGitLock(repoPath, fn) {
7135
- return enqueueByKey(repoLockKey(repoPath), fn);
7136
- }
7137
- var init_repo_git_lock = __esm({
7138
- "src/git/repo-git-lock.ts"() {
7139
- "use strict";
7140
- init_enqueue_by_key();
7141
- }
7142
- });
7143
-
7144
7172
  // src/git/pr-gates.ts
7145
7173
  function prIsInMergeQueue(gate) {
7146
7174
  if (gate.isInMergeQueue) return true;
@@ -7273,6 +7301,381 @@ var init_pr_gates = __esm({
7273
7301
  }
7274
7302
  });
7275
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
+
7276
7679
  // src/git/stack.ts
7277
7680
  async function detectGhStack(cwd) {
7278
7681
  const now = Date.now();
@@ -7951,11 +8354,11 @@ __export(worktree_exports, {
7951
8354
  connectedPrSelectors: () => connectedPrSelectors,
7952
8355
  countUnpushedVsOrigin: () => countUnpushedVsOrigin,
7953
8356
  createExistingBranchWorktree: () => createExistingBranchWorktree,
7954
- createOrUpdatePr: () => createOrUpdatePr,
7955
8357
  createThreadWorktree: () => createThreadWorktree,
7956
8358
  currentBranch: () => currentBranch,
7957
8359
  detectLocalMergeConflicts: () => detectLocalMergeConflicts,
7958
8360
  ensureGhPreferOrigin: () => ensureGhPreferOrigin,
8361
+ entireUsesTrails: () => entireUsesTrails,
7959
8362
  fastForwardMainCheckoutIfSafe: () => fastForwardMainCheckoutIfSafe,
7960
8363
  fetchOriginForWorktree: () => fetchOriginForWorktree,
7961
8364
  fetchPrHead: () => fetchPrHead,
@@ -7965,7 +8368,6 @@ __export(worktree_exports, {
7965
8368
  getPrForHeadBranch: () => getPrForHeadBranch,
7966
8369
  getPrMeta: () => getPrMeta,
7967
8370
  ghHeadRef: () => ghHeadRef,
7968
- ghPrCreateAssigneeArgs: () => ghPrCreateAssigneeArgs,
7969
8371
  ghRepoSelectArgs: () => ghRepoSelectArgs,
7970
8372
  githubAgentGitEnv: () => githubAgentGitEnv,
7971
8373
  isDefaultishSourceRef: () => isDefaultishSourceRef,
@@ -7989,8 +8391,10 @@ __export(worktree_exports, {
7989
8391
  resolveDefaultBranch: () => resolveDefaultBranch,
7990
8392
  resolveDiffBaseRef: () => resolveDiffBaseRef,
7991
8393
  resolveGithubRepoSlug: () => resolveGithubRepoSlug,
8394
+ resolveOriginRemote: () => resolveOriginRemote,
7992
8395
  resolvePrSelector: () => resolvePrSelector,
7993
8396
  resolvePrSelectors: () => resolvePrSelectors,
8397
+ resolveProjectRemoteTag: () => resolveProjectRemoteTag,
7994
8398
  resolveRepoRoot: () => resolveRepoRoot,
7995
8399
  resolveWorktreeStartPoint: () => resolveWorktreeStartPoint,
7996
8400
  slugify: () => slugify,
@@ -8004,31 +8408,6 @@ __export(worktree_exports, {
8004
8408
  worktreeNameFromPath: () => worktreeNameFromPath,
8005
8409
  worktreeSlugForTicket: () => worktreeSlugForTicket
8006
8410
  });
8007
- async function lookupGithubGraphqlReset(cwd) {
8008
- const result = await gh(["api", "rate_limit"], cwd, { reject: false });
8009
- if (result.exitCode !== 0 || !result.stdout.trim()) return void 0;
8010
- try {
8011
- const data = JSON.parse(result.stdout);
8012
- const reset = data.resources?.graphql?.reset;
8013
- return typeof reset === "number" ? reset : void 0;
8014
- } catch {
8015
- return void 0;
8016
- }
8017
- }
8018
- async function formatPrCreateFailure(raw, cwd, ctx) {
8019
- if (!isGhRateLimitError(raw)) {
8020
- return formatGhLandError(raw, {
8021
- targetedRepo: ctx?.slug,
8022
- headRef: ctx?.head
8023
- });
8024
- }
8025
- const resetAt = await lookupGithubGraphqlReset(cwd);
8026
- return formatGhLandError(raw, {
8027
- resetAt,
8028
- targetedRepo: ctx?.slug,
8029
- headRef: ctx?.head
8030
- });
8031
- }
8032
8411
  function slugify(input) {
8033
8412
  return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
8034
8413
  }
@@ -8051,25 +8430,39 @@ function canonicalizeRepoPath(path) {
8051
8430
  }
8052
8431
  }
8053
8432
  function parseGithubSlugFromRemoteUrl(url) {
8054
- const trimmed = url.trim().replace(/\.git$/i, "");
8055
- if (!trimmed) return null;
8056
- const github = trimmed.match(/github\.com[:/]([^/]+)\/([^/]+)$/i);
8057
- if (github?.[1] && github[2]) return `${github[1]}/${github[2]}`;
8058
- const sshAlias = trimmed.match(/^git@[^:]+:([^/]+)\/([^/]+)$/i);
8059
- if (sshAlias?.[1] && sshAlias[2]) return `${sshAlias[1]}/${sshAlias[2]}`;
8060
- return null;
8433
+ const parsed = parseRemoteRepo(url);
8434
+ return parsed?.forge === "github" ? parsed.slug : null;
8061
8435
  }
8062
- async function slugFromGitRemote(repoPath, remote) {
8436
+ async function remoteUrl(repoPath, remote) {
8063
8437
  const result = await git(["remote", "get-url", remote], repoPath, {
8064
8438
  reject: false,
8065
8439
  timeoutMs: 5e3
8066
8440
  });
8067
8441
  if (result.exitCode !== 0 || !result.stdout.trim()) return null;
8068
- 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;
8069
8459
  }
8070
8460
  async function resolveGithubRepoSlug(repoPath) {
8071
- const fromOrigin = await slugFromGitRemote(repoPath, "origin");
8072
- 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
+ }
8073
8466
  const hasAlt = Boolean(await slugFromGitRemote(repoPath, "upstream")) || Boolean(await slugFromGitRemote(repoPath, "github"));
8074
8467
  if (!hasAlt) {
8075
8468
  const viaGh = await gh(
@@ -8115,6 +8508,8 @@ ${view.stderr}`;
8115
8508
  });
8116
8509
  }
8117
8510
  async function originGhRepoEnv(cwd, opts) {
8511
+ const origin = await resolveOriginRemote(cwd);
8512
+ if (origin?.forge === "cursor") return { ORIGIN_REPO: origin.slug };
8118
8513
  await ensureGhPreferOrigin(cwd);
8119
8514
  const slug = await resolveGithubRepoSlug(cwd);
8120
8515
  const mode = opts?.mode ?? getGithubGitAuthMode();
@@ -8229,7 +8624,16 @@ async function listBranches(repoPath, opts) {
8229
8624
  }
8230
8625
  return sorted.filter((b) => keep.has(b.name));
8231
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
+ }
8232
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);
8233
8637
  const slug = await resolveGithubRepoSlug(repoPath);
8234
8638
  const resolved = resolveListPrsOptions(opts);
8235
8639
  const limit = resolved.limit ?? 200;
@@ -8266,6 +8670,18 @@ function prInfoFromGhView(view) {
8266
8670
  };
8267
8671
  }
8268
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
+ }
8269
8685
  const slug = await resolveGithubRepoSlug(repoPath);
8270
8686
  const args = [
8271
8687
  "pr",
@@ -8327,6 +8743,13 @@ function connectedPrSelectors(thread, currentHeadPrUrl) {
8327
8743
  async function getPrForHeadBranch(repoPath, branch) {
8328
8744
  const head = normalizeGitBranchName(branch);
8329
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
+ }
8330
8753
  const slug = await resolveGithubRepoSlug(repoPath);
8331
8754
  const listHead = slug ? ghHeadRef(slug, head) : head;
8332
8755
  const listArgs = [
@@ -8491,6 +8914,10 @@ async function probeLocalMergeGate(cwd, gate) {
8491
8914
  return { gate, files: [] };
8492
8915
  }
8493
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);
8494
8921
  const slug = await resolveGithubRepoSlug(cwd);
8495
8922
  const args = [
8496
8923
  "pr",
@@ -8546,6 +8973,18 @@ async function getPrChecks(cwd, selector) {
8546
8973
  return [...gateRows, ...ciChecks];
8547
8974
  }
8548
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
+ }
8549
8988
  const slug = await resolveGithubRepoSlug(cwd);
8550
8989
  const viewArgs = [
8551
8990
  "pr",
@@ -8674,6 +9113,13 @@ function parsePrDetailsView(view) {
8674
9113
  };
8675
9114
  }
8676
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
+ }
8677
9123
  const slug = await resolveGithubRepoSlug(cwd);
8678
9124
  const viewArgs = ["pr", "view", selector, "--json", PR_DETAILS_JSON_FIELDS];
8679
9125
  if (slug) viewArgs.push("--repo", slug);
@@ -8693,6 +9139,13 @@ async function getPrDetails(cwd, selector) {
8693
9139
  return parsePrDetailsView(view);
8694
9140
  }
8695
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
+ }
8696
9149
  const slug = await resolveGithubRepoSlug(repoPath);
8697
9150
  const refspec = `+pull/${number}/head:${localBranch}`;
8698
9151
  const errors = [];
@@ -8835,10 +9288,10 @@ async function originRemoteUrl(cwd) {
8835
9288
  const result = await git(["remote", "get-url", "origin"], cwd, { reject: false });
8836
9289
  return result.exitCode === 0 ? result.stdout.trim() : "";
8837
9290
  }
8838
- function githubHttpsRetryEnv(remoteUrl) {
9291
+ function githubHttpsRetryEnv(remoteUrl2) {
8839
9292
  return {
8840
9293
  GIT_TERMINAL_PROMPT: "0",
8841
- ...githubAgentGitEnv(void 0, { remoteUrl })
9294
+ ...githubAgentGitEnv(void 0, { remoteUrl: remoteUrl2 })
8842
9295
  };
8843
9296
  }
8844
9297
  async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
@@ -8852,11 +9305,11 @@ async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
8852
9305
  }
8853
9306
  const token = await resolveGithubAgentToken(mode, repoPath);
8854
9307
  if (!token) return false;
8855
- const remoteUrl = await originRemoteUrl(repoPath);
9308
+ const remoteUrl2 = await originRemoteUrl(repoPath);
8856
9309
  const tryHttps = async (header) => git(args, repoPath, {
8857
9310
  reject: false,
8858
9311
  timeoutMs,
8859
- env: githubHttpsRetryEnv(remoteUrl),
9312
+ env: githubHttpsRetryEnv(remoteUrl2),
8860
9313
  config: {
8861
9314
  "http.extraHeader": header
8862
9315
  }
@@ -9205,10 +9658,10 @@ async function pushBranch(worktreePath, branchName) {
9205
9658
  sshErr || `git push origin ${branchName} failed` + (!token ? " (SSH remote and gh auth token unavailable \u2014 unlock ssh-agent or run: gh auth login)" : "")
9206
9659
  );
9207
9660
  }
9208
- const remoteUrl = await originRemoteUrl(worktreePath);
9661
+ const remoteUrl2 = await originRemoteUrl(worktreePath);
9209
9662
  const tryHttps = async (header) => git(["push", "-u", "origin", branchName], worktreePath, {
9210
9663
  reject: false,
9211
- env: githubHttpsRetryEnv(remoteUrl),
9664
+ env: githubHttpsRetryEnv(remoteUrl2),
9212
9665
  config: {
9213
9666
  "http.extraHeader": header
9214
9667
  }
@@ -9221,6 +9674,40 @@ async function pushBranch(worktreePath, branchName) {
9221
9674
  const httpsErr = (basicPush.stderr || bearer.stderr || bearer.stdout).trim();
9222
9675
  throw new Error(httpsErr || sshErr || `git push origin ${branchName} failed`);
9223
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
+ }
9224
9711
  async function runPrReady(cwd, selector, slug) {
9225
9712
  const readyArgs = ["pr", "ready", selector];
9226
9713
  if (slug) readyArgs.push("--repo", slug);
@@ -9232,6 +9719,10 @@ async function runPrReady(cwd, selector, slug) {
9232
9719
  }
9233
9720
  }
9234
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);
9235
9726
  const slug = await resolveGithubRepoSlug(cwd);
9236
9727
  const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft"];
9237
9728
  if (slug) viewArgs.push("--repo", slug);
@@ -9270,6 +9761,10 @@ async function markPrReady(cwd, selector) {
9270
9761
  return { url, state: state || "OPEN", isDraft: false };
9271
9762
  }
9272
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");
9273
9768
  const slug = await resolveGithubRepoSlug(cwd);
9274
9769
  const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft,number"];
9275
9770
  if (slug) viewArgs.push("--repo", slug);
@@ -9328,128 +9823,16 @@ async function mergePr(cwd, selector, opts) {
9328
9823
  }
9329
9824
  return { url, state: "MERGED" };
9330
9825
  }
9331
- function ghPrCreateAssigneeArgs(assignee) {
9332
- const raw = assignee?.trim();
9333
- if (raw && /^(none|unassigned)$/i.test(raw)) return [];
9334
- return ["--assignee", raw || "@me"];
9335
- }
9336
- async function createOrUpdatePr(worktreePath, opts) {
9337
- await ensureGhPreferOrigin(worktreePath);
9338
- const slug = await resolveGithubRepoSlug(worktreePath);
9339
- if (!slug) {
9340
- throw new Error(
9341
- "Could not resolve the origin GitHub repo (owner/name) for this worktree. Check that `git remote get-url origin` points at github.com."
9342
- );
9343
- }
9344
- const repoArgs = ghRepoSelectArgs(slug);
9345
- const headRef = ghHeadRef(slug, opts.head);
9346
- const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
9347
- const bodyText = clampGithubPrBody(opts.body ?? opts.title);
9348
- const bodyFile = (0, import_node_path21.join)(
9349
- (0, import_node_os7.tmpdir)(),
9350
- `sideboard-pr-body-${process.pid}-${(0, import_node_crypto4.randomBytes)(6).toString("hex")}.md`
9351
- );
9352
- (0, import_node_fs18.writeFileSync)(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
9353
- `, "utf8");
9354
- const bodyArgs = ["--body-file", bodyFile];
9355
- try {
9356
- return await createOrUpdatePrWithBodyFile(worktreePath, {
9357
- ...opts,
9358
- slug,
9359
- repoArgs,
9360
- headRef,
9361
- branchOnly,
9362
- bodyArgs
9363
- });
9364
- } finally {
9365
- try {
9366
- (0, import_node_fs18.unlinkSync)(bodyFile);
9367
- } catch {
9368
- }
9369
- }
9370
- }
9371
- async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
9372
- const { slug, repoArgs, headRef, branchOnly, bodyArgs } = opts;
9373
- const existing = await gh(
9374
- [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
9375
- worktreePath,
9376
- { reject: false }
9377
- );
9378
- if (existing.exitCode === 0 && existing.stdout.trim() && !opts.web) {
9379
- const url2 = existing.stdout.trim();
9380
- await gh(
9381
- [
9382
- ...repoArgs,
9383
- "pr",
9384
- "edit",
9385
- branchOnly,
9386
- "--title",
9387
- opts.title,
9388
- ...bodyArgs
9389
- ],
9390
- worktreePath,
9391
- { reject: false }
9392
- );
9393
- return url2;
9394
- }
9395
- if (opts.web) {
9396
- const args2 = [
9397
- ...repoArgs,
9398
- "pr",
9399
- "create",
9400
- "--web",
9401
- "--title",
9402
- opts.title,
9403
- ...bodyArgs,
9404
- "--base",
9405
- opts.base,
9406
- "--head",
9407
- headRef
9408
- ];
9409
- if (opts.draft) args2.push("--draft");
9410
- args2.push(...ghPrCreateAssigneeArgs(opts.assignee));
9411
- await gh(args2, worktreePath, { reject: false });
9412
- const again = await gh(
9413
- [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
9414
- worktreePath,
9415
- { reject: false }
9416
- );
9417
- return again.stdout.trim() || "";
9418
- }
9419
- const args = [
9420
- ...repoArgs,
9421
- "pr",
9422
- "create",
9423
- "--title",
9424
- opts.title,
9425
- ...bodyArgs,
9426
- "--base",
9427
- opts.base,
9428
- "--head",
9429
- headRef
9430
- ];
9431
- if (opts.draft) args.push("--draft");
9432
- args.push(...ghPrCreateAssigneeArgs(opts.assignee));
9433
- const created = await gh(args, worktreePath, { reject: false });
9434
- if (created.exitCode !== 0) {
9435
- const raw = created.stderr.trim() || created.stdout.trim() || "gh pr create failed";
9436
- throw new Error(await formatPrCreateFailure(raw, worktreePath, { slug, head: headRef }));
9437
- }
9438
- const url = created.stdout.trim().split("\n").find((l) => l.startsWith("http")) ?? created.stdout.trim();
9439
- return url;
9440
- }
9441
9826
  function suggestSlug(source) {
9442
9827
  const base = slugify(source || "thread");
9443
9828
  const stamp = Date.now().toString(36).slice(-4);
9444
9829
  return `${base || "thread"}-${stamp}`;
9445
9830
  }
9446
- 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;
9447
9832
  var init_worktree = __esm({
9448
9833
  "src/git/worktree.ts"() {
9449
9834
  "use strict";
9450
- import_node_crypto4 = require("crypto");
9451
9835
  import_node_fs18 = require("fs");
9452
- import_node_os7 = require("os");
9453
9836
  import_node_path21 = require("path");
9454
9837
  init_paths();
9455
9838
  init_worktree_labels();
@@ -9457,6 +9840,8 @@ var init_worktree = __esm({
9457
9840
  init_worktree_exclude();
9458
9841
  init_git_auth_mode();
9459
9842
  init_run();
9843
+ init_remote_forge();
9844
+ init_cursor_pr();
9460
9845
  init_list_prs();
9461
9846
  init_repo_git_lock();
9462
9847
  init_app_settings();
@@ -9561,7 +9946,7 @@ function uniqueAttachmentName(dir, originalName) {
9561
9946
  const candidate = `${stem}-${i}${ext}`;
9562
9947
  if (!(0, import_node_fs19.existsSync)((0, import_node_path22.join)(dir, candidate))) return candidate;
9563
9948
  }
9564
- return `${stem}-${(0, import_node_crypto5.randomUUID)()}${ext}`;
9949
+ return `${stem}-${(0, import_node_crypto4.randomUUID)()}${ext}`;
9565
9950
  }
9566
9951
  function previewDataUrlFromBuf(filePath, buf) {
9567
9952
  if (!isImageFilePath(filePath)) return void 0;
@@ -9573,7 +9958,7 @@ function attachmentFromBuffer(name, buf, opts) {
9573
9958
  if (isImageFilePath(name)) {
9574
9959
  const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
9575
9960
  return {
9576
- id: (0, import_node_crypto5.randomUUID)(),
9961
+ id: (0, import_node_crypto4.randomUUID)(),
9577
9962
  name,
9578
9963
  kind: "file",
9579
9964
  path: opts.path,
@@ -9586,7 +9971,7 @@ function attachmentFromBuffer(name, buf, opts) {
9586
9971
  }
9587
9972
  if (buf.length > MAX_INLINE_BYTES) {
9588
9973
  return {
9589
- id: (0, import_node_crypto5.randomUUID)(),
9974
+ id: (0, import_node_crypto4.randomUUID)(),
9590
9975
  name,
9591
9976
  kind: "file",
9592
9977
  path: opts.path,
@@ -9595,7 +9980,7 @@ function attachmentFromBuffer(name, buf, opts) {
9595
9980
  }
9596
9981
  if (buf.includes(0)) {
9597
9982
  return {
9598
- id: (0, import_node_crypto5.randomUUID)(),
9983
+ id: (0, import_node_crypto4.randomUUID)(),
9599
9984
  name,
9600
9985
  kind: "file",
9601
9986
  path: opts.path,
@@ -9603,7 +9988,7 @@ function attachmentFromBuffer(name, buf, opts) {
9603
9988
  };
9604
9989
  }
9605
9990
  return {
9606
- id: (0, import_node_crypto5.randomUUID)(),
9991
+ id: (0, import_node_crypto4.randomUUID)(),
9607
9992
  name,
9608
9993
  kind: "file",
9609
9994
  path: opts.path,
@@ -9627,7 +10012,7 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
9627
10012
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
9628
10013
  } catch (err) {
9629
10014
  out.push({
9630
- id: (0, import_node_crypto5.randomUUID)(),
10015
+ id: (0, import_node_crypto4.randomUUID)(),
9631
10016
  name: originalName,
9632
10017
  kind: "file",
9633
10018
  content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
@@ -9651,7 +10036,7 @@ function stageBuffersAsAttachments(worktreePath, buffers2) {
9651
10036
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
9652
10037
  } catch (err) {
9653
10038
  out.push({
9654
- id: (0, import_node_crypto5.randomUUID)(),
10039
+ id: (0, import_node_crypto4.randomUUID)(),
9655
10040
  name: originalName,
9656
10041
  kind: "file",
9657
10042
  content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
@@ -9706,7 +10091,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
9706
10091
  for (const rel of relativePaths) {
9707
10092
  if (!rel || rel.includes("..") || rel.startsWith("/")) {
9708
10093
  out.push({
9709
- id: (0, import_node_crypto5.randomUUID)(),
10094
+ id: (0, import_node_crypto4.randomUUID)(),
9710
10095
  name: (0, import_node_path22.basename)(rel) || "file",
9711
10096
  kind: "file",
9712
10097
  content: `(invalid path: ${rel})`
@@ -9722,7 +10107,7 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
9722
10107
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
9723
10108
  } catch (err) {
9724
10109
  out.push({
9725
- id: (0, import_node_crypto5.randomUUID)(),
10110
+ id: (0, import_node_crypto4.randomUUID)(),
9726
10111
  name,
9727
10112
  kind: "file",
9728
10113
  content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
@@ -9731,13 +10116,13 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
9731
10116
  }
9732
10117
  return out;
9733
10118
  }
9734
- 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;
9735
10120
  var init_stage_files = __esm({
9736
10121
  "src/composer/stage-files.ts"() {
9737
10122
  "use strict";
9738
10123
  import_node_fs19 = require("fs");
9739
10124
  import_node_path22 = require("path");
9740
- import_node_crypto5 = require("crypto");
10125
+ import_node_crypto4 = require("crypto");
9741
10126
  init_workspace_scratch();
9742
10127
  IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
9743
10128
  "png",
@@ -9836,7 +10221,7 @@ function pathIsInside(parent, child) {
9836
10221
  }
9837
10222
  function defaultWorktreesRoot(repoPath) {
9838
10223
  const slug = (0, import_node_path23.basename)(repoPath.replace(/\/+$/, "")) || "repo";
9839
- 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);
9840
10225
  }
9841
10226
  function removedWorktreeCoversRepo(saved, repoPath) {
9842
10227
  if (!saved || !repoPath) return false;
@@ -10019,12 +10404,12 @@ async function archiveRemovedWorkspaceChats(threads, archive) {
10019
10404
  }
10020
10405
  if (failures.length > 0) throw new Error(failures.join("\n"));
10021
10406
  }
10022
- var import_node_fs20, import_node_os8, import_node_path23;
10407
+ var import_node_fs20, import_node_os7, import_node_path23;
10023
10408
  var init_workspaces = __esm({
10024
10409
  "src/store/workspaces.ts"() {
10025
10410
  "use strict";
10026
10411
  import_node_fs20 = require("fs");
10027
- import_node_os8 = require("os");
10412
+ import_node_os7 = require("os");
10028
10413
  import_node_path23 = require("path");
10029
10414
  init_settings();
10030
10415
  init_paths();
@@ -10048,7 +10433,7 @@ __export(coordinator_prompt_exports, {
10048
10433
  function formatWorkspaceInventory(workspaces) {
10049
10434
  if (workspaces.length === 0) return "(no registered workspaces)";
10050
10435
  return workspaces.map((w) => {
10051
- const slug = w.githubSlug?.trim() ? ` github:${w.githubSlug.trim()}` : "";
10436
+ const slug = w.originRemote ? ` ${formatRemoteTag(w.originRemote)}` : w.githubSlug?.trim() ? ` github:${w.githubSlug.trim()}` : "";
10052
10437
  const profile = formatWorkspaceProfileSuffix(safeViewerProfile(w.path));
10053
10438
  return `- ${w.name}: ${w.path}${slug}${profile}`;
10054
10439
  }).join("\n");
@@ -10056,8 +10441,9 @@ function formatWorkspaceInventory(workspaces) {
10056
10441
  async function enrichWorkspacesWithGithub(workspaces) {
10057
10442
  return Promise.all(
10058
10443
  workspaces.map(async (w) => {
10059
- const githubSlug = await resolveGithubRepoSlug(w.path).catch(() => null);
10060
- 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 };
10061
10447
  })
10062
10448
  );
10063
10449
  }
@@ -10068,8 +10454,8 @@ function coordinatorGreenfieldPlaybook(reposDir) {
10068
10454
  "- Examples:",
10069
10455
  ` - Clone: \`git clone <url> ${reposDir}/<name>\``,
10070
10456
  ` - New GitHub repo: \`gh repo create <owner>/<name> --private --clone -- ${reposDir}/<name>\` (or mkdir + git init + gh repo create + remote add + push)`,
10071
- "- 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>`).",
10072
- "- 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`.",
10073
10459
  "- Do coding work in the child worktree thread, not by editing files in this home cwd."
10074
10460
  ].join("\n");
10075
10461
  }
@@ -10230,6 +10616,7 @@ var init_coordinator_prompt = __esm({
10230
10616
  "use strict";
10231
10617
  import_node_fs21 = require("fs");
10232
10618
  import_node_path24 = require("path");
10619
+ init_remote_forge();
10233
10620
  init_worktree();
10234
10621
  init_app_settings();
10235
10622
  init_paths();
@@ -10238,9 +10625,9 @@ var init_coordinator_prompt = __esm({
10238
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.",
10239
10626
  "Sideboard MCP (fleet control \u2014 prefer these for status and orchestration):",
10240
10627
  "Discover:",
10241
- "- 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.",
10242
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.",
10243
- `- 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).',
10244
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.",
10245
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.",
10246
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.",
@@ -10277,11 +10664,11 @@ var init_coordinator_prompt = __esm({
10277
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.",
10278
10665
  "Inspect / review / PRs:",
10279
10666
  "- get_diff \u2014 compact diff summary",
10280
- "- 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.",
10281
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.',
10282
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.",
10283
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.',
10284
- '- 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.',
10285
10672
  "Process guides:",
10286
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.",
10287
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.",
@@ -10624,7 +11011,7 @@ function resolveBrightsyConfigFile(path) {
10624
11011
  }
10625
11012
  function brightsyConfigPath() {
10626
11013
  const override = process.env.BRIGHTSY_CONFIG?.trim();
10627
- 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");
10628
11015
  return resolveBrightsyConfigFile(raw);
10629
11016
  }
10630
11017
  function loadBrightsyConfig() {
@@ -10658,12 +11045,12 @@ function saveBrightsyConfig(cfg) {
10658
11045
  mode: 384
10659
11046
  });
10660
11047
  }
10661
- var import_node_fs23, import_node_os9, import_node_path26;
11048
+ var import_node_fs23, import_node_os8, import_node_path26;
10662
11049
  var init_config = __esm({
10663
11050
  "src/brightsy/config.ts"() {
10664
11051
  "use strict";
10665
11052
  import_node_fs23 = require("fs");
10666
- import_node_os9 = require("os");
11053
+ import_node_os8 = require("os");
10667
11054
  import_node_path26 = require("path");
10668
11055
  }
10669
11056
  });
@@ -11772,7 +12159,7 @@ function claudePluginServerId(pluginName, serverName) {
11772
12159
  return `plugin_${pluginName}_${serverName}`.replace(/[^A-Za-z0-9_-]/g, "_");
11773
12160
  }
11774
12161
  function listEnabledClaudePluginMcpServers(opts) {
11775
- const home = opts?.home ?? (0, import_node_os10.homedir)();
12162
+ const home = opts?.home ?? (0, import_node_os9.homedir)();
11776
12163
  const pluginsRoot = claudePluginsRoot(home);
11777
12164
  const enabled = enabledPluginIds(home, opts?.projectDirs ?? []);
11778
12165
  const installed = readInstalledPlugins((0, import_node_path28.join)(pluginsRoot, "installed_plugins.json"));
@@ -11954,12 +12341,12 @@ function readJson(path) {
11954
12341
  return {};
11955
12342
  }
11956
12343
  }
11957
- var import_node_fs26, import_node_os10, import_node_path28;
12344
+ var import_node_fs26, import_node_os9, import_node_path28;
11958
12345
  var init_claude_plugins = __esm({
11959
12346
  "src/agents/claude-plugins.ts"() {
11960
12347
  "use strict";
11961
12348
  import_node_fs26 = require("fs");
11962
- import_node_os10 = require("os");
12349
+ import_node_os9 = require("os");
11963
12350
  import_node_path28 = require("path");
11964
12351
  init_meta();
11965
12352
  }
@@ -12176,7 +12563,7 @@ function versionDirNodeBins(root, toBin) {
12176
12563
  return [];
12177
12564
  }
12178
12565
  }
12179
- function defaultNodeBinCandidates(home = (0, import_node_os11.homedir)()) {
12566
+ function defaultNodeBinCandidates(home = (0, import_node_os10.homedir)()) {
12180
12567
  const kegs = ["/opt/homebrew", "/usr/local"].flatMap(
12181
12568
  (prefix) => PREFERRED_LTS_MAJORS.map((major) => (0, import_node_path30.join)(prefix, "opt", `node@${major}`, "bin", "node"))
12182
12569
  );
@@ -12291,12 +12678,12 @@ async function resolveNodeLaunch(scriptPath) {
12291
12678
  env: { ELECTRON_RUN_AS_NODE: "1" }
12292
12679
  };
12293
12680
  }
12294
- 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;
12295
12682
  var init_node_launch = __esm({
12296
12683
  "src/agents/node-launch.ts"() {
12297
12684
  "use strict";
12298
12685
  import_node_fs28 = require("fs");
12299
- import_node_os11 = require("os");
12686
+ import_node_os10 = require("os");
12300
12687
  import_node_path30 = require("path");
12301
12688
  init_nested_electron_env();
12302
12689
  init_system_ca();
@@ -12593,18 +12980,18 @@ function mcpServersRecord(servers, extraServers) {
12593
12980
  function writeMcpServersConfig(servers, extraServers) {
12594
12981
  const mcpServers = mcpServersRecord(servers, extraServers);
12595
12982
  if (Object.keys(mcpServers).length === 0) return null;
12596
- 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-"));
12597
12984
  const cfgPath = (0, import_node_path31.join)(dir, "mcp.json");
12598
12985
  (0, import_node_fs29.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
12599
12986
  return cfgPath;
12600
12987
  }
12601
- 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;
12602
12989
  var init_injected_mcp = __esm({
12603
12990
  "src/agents/injected-mcp.ts"() {
12604
12991
  "use strict";
12605
12992
  import_node_fs29 = require("fs");
12606
12993
  import_node_module = require("module");
12607
- import_node_os12 = require("os");
12994
+ import_node_os11 = require("os");
12608
12995
  import_node_path31 = require("path");
12609
12996
  import_node_url = require("url");
12610
12997
  init_run();
@@ -12659,14 +13046,14 @@ var init_injected_mcp = __esm({
12659
13046
 
12660
13047
  // src/agents/user-mcp-config.ts
12661
13048
  function userClaudeMcpConfigPath() {
12662
- 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");
12663
13050
  }
12664
- var import_node_fs30, import_node_os13, import_node_path32;
13051
+ var import_node_fs30, import_node_os12, import_node_path32;
12665
13052
  var init_user_mcp_config = __esm({
12666
13053
  "src/agents/user-mcp-config.ts"() {
12667
13054
  "use strict";
12668
13055
  import_node_fs30 = require("fs");
12669
- import_node_os13 = require("os");
13056
+ import_node_os12 = require("os");
12670
13057
  import_node_path32 = require("path");
12671
13058
  init_paths();
12672
13059
  init_injected_mcp();
@@ -12707,10 +13094,10 @@ function readText(path) {
12707
13094
  return "";
12708
13095
  }
12709
13096
  }
12710
- function userCodexConfigPaths(home = (0, import_node_os14.homedir)()) {
13097
+ function userCodexConfigPaths(home = (0, import_node_os13.homedir)()) {
12711
13098
  return [(0, import_node_path33.join)(home, ".codex", "config.toml"), (0, import_node_path33.join)(home, ".config", "codex", "config.toml")];
12712
13099
  }
12713
- function userOpencodeConfigPaths(home = (0, import_node_os14.homedir)()) {
13100
+ function userOpencodeConfigPaths(home = (0, import_node_os13.homedir)()) {
12714
13101
  return [
12715
13102
  (0, import_node_path33.join)(home, ".config", "opencode", "opencode.json"),
12716
13103
  (0, import_node_path33.join)(home, ".config", "opencode", "opencode.jsonc")
@@ -12732,7 +13119,7 @@ function userMcpNamesToDisable(opts) {
12732
13119
  }
12733
13120
  return out;
12734
13121
  }
12735
- function listUserCodexMcpNames(home = (0, import_node_os14.homedir)()) {
13122
+ function listUserCodexMcpNames(home = (0, import_node_os13.homedir)()) {
12736
13123
  const names = [];
12737
13124
  for (const path of userCodexConfigPaths(home)) {
12738
13125
  if (!(0, import_node_fs31.existsSync)(path)) continue;
@@ -12740,7 +13127,7 @@ function listUserCodexMcpNames(home = (0, import_node_os14.homedir)()) {
12740
13127
  }
12741
13128
  return names;
12742
13129
  }
12743
- function listUserOpencodeMcpNames(home = (0, import_node_os14.homedir)()) {
13130
+ function listUserOpencodeMcpNames(home = (0, import_node_os13.homedir)()) {
12744
13131
  const names = [];
12745
13132
  for (const path of userOpencodeConfigPaths(home)) {
12746
13133
  if (!(0, import_node_fs31.existsSync)(path)) continue;
@@ -12748,12 +13135,12 @@ function listUserOpencodeMcpNames(home = (0, import_node_os14.homedir)()) {
12748
13135
  }
12749
13136
  return names;
12750
13137
  }
12751
- function userClaudeMcpConfigObject(home = (0, import_node_os14.homedir)()) {
12752
- 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");
12753
13140
  if (!(0, import_node_fs31.existsSync)(claudePath)) return {};
12754
13141
  return asObject(asObject(readJsonObject2(claudePath)).mcpServers);
12755
13142
  }
12756
- function listUserClaudeMcpServerEntries(home = (0, import_node_os14.homedir)(), projectDirs) {
13143
+ function listUserClaudeMcpServerEntries(home = (0, import_node_os13.homedir)(), projectDirs) {
12757
13144
  const out = {};
12758
13145
  for (const [name, entry] of Object.entries(
12759
13146
  listEnabledClaudePluginMcpServers({ home, projectDirs })
@@ -12789,13 +13176,13 @@ function toCodexUnattendedAppsArgs() {
12789
13176
  "features.plugins=false"
12790
13177
  ];
12791
13178
  }
12792
- 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;
12793
13180
  var init_orch_mcp_isolation = __esm({
12794
13181
  "src/agents/orch-mcp-isolation.ts"() {
12795
13182
  "use strict";
12796
13183
  import_node_fs31 = require("fs");
12797
13184
  init_claude_plugins();
12798
- import_node_os14 = require("os");
13185
+ import_node_os13 = require("os");
12799
13186
  import_node_path33 = require("path");
12800
13187
  import_smol_toml2 = require("smol-toml");
12801
13188
  init_user_mcp_config();
@@ -12896,7 +13283,7 @@ var init_proxy_server = __esm({
12896
13283
 
12897
13284
  // src/mcp-ui/proxy-http.ts
12898
13285
  async function listenMcpUiProxy(opts) {
12899
- const token = (0, import_node_crypto6.randomBytes)(24).toString("hex");
13286
+ const token = (0, import_node_crypto5.randomBytes)(24).toString("hex");
12900
13287
  const transports = /* @__PURE__ */ new Map();
12901
13288
  const httpServer = (0, import_node_http.createServer)((req, res) => {
12902
13289
  void handle(req, res);
@@ -12934,7 +13321,7 @@ async function listenMcpUiProxy(opts) {
12934
13321
  return;
12935
13322
  }
12936
13323
  const created = new import_streamableHttp.StreamableHTTPServerTransport({
12937
- sessionIdGenerator: () => (0, import_node_crypto6.randomUUID)(),
13324
+ sessionIdGenerator: () => (0, import_node_crypto5.randomUUID)(),
12938
13325
  allowedHosts: ["127.0.0.1", "localhost"],
12939
13326
  onsessioninitialized: (id) => {
12940
13327
  transports.set(`${sessionKey}:${id}`, created);
@@ -12987,11 +13374,11 @@ function parseMcpUiProxyPath(url) {
12987
13374
  return null;
12988
13375
  }
12989
13376
  }
12990
- var import_node_crypto6, import_node_http, import_server2, import_streamableHttp;
13377
+ var import_node_crypto5, import_node_http, import_server2, import_streamableHttp;
12991
13378
  var init_proxy_http = __esm({
12992
13379
  "src/mcp-ui/proxy-http.ts"() {
12993
13380
  "use strict";
12994
- import_node_crypto6 = require("crypto");
13381
+ import_node_crypto5 = require("crypto");
12995
13382
  import_node_http = require("http");
12996
13383
  import_server2 = require("@modelcontextprotocol/sdk/server/index.js");
12997
13384
  import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
@@ -13128,7 +13515,7 @@ var init_session = __esm({
13128
13515
  function getMcpUiHost() {
13129
13516
  if (!singleton) {
13130
13517
  singleton = new McpUiHost({
13131
- entries: (projectDirs) => listUserClaudeMcpServerEntries((0, import_node_os15.homedir)(), projectDirs)
13518
+ entries: (projectDirs) => listUserClaudeMcpServerEntries((0, import_node_os14.homedir)(), projectDirs)
13132
13519
  });
13133
13520
  }
13134
13521
  return singleton;
@@ -13136,18 +13523,18 @@ function getMcpUiHost() {
13136
13523
  async function proxyClaudeUserMcp(orchestrator, projectDirs) {
13137
13524
  if (orchestrator) return {};
13138
13525
  const dirs = projectDirs.filter((d) => typeof d === "string" && d.length > 0);
13139
- return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries((0, import_node_os15.homedir)(), dirs), dirs);
13526
+ return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries((0, import_node_os14.homedir)(), dirs), dirs);
13140
13527
  }
13141
13528
  function dirsKeyFor(projectDirs) {
13142
13529
  if (!projectDirs?.length) return "";
13143
- 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);
13144
13531
  }
13145
- 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;
13146
13533
  var init_host = __esm({
13147
13534
  "src/mcp-ui/host.ts"() {
13148
13535
  "use strict";
13149
- import_node_crypto7 = require("crypto");
13150
- import_node_os15 = require("os");
13536
+ import_node_crypto6 = require("crypto");
13537
+ import_node_os14 = require("os");
13151
13538
  init_orch_mcp_isolation();
13152
13539
  init_meta();
13153
13540
  init_proxy_http();
@@ -14268,8 +14655,8 @@ function usageFromCodex(usage) {
14268
14655
  }
14269
14656
  function codexConfigHasNetworkAccess() {
14270
14657
  const candidates = [
14271
- (0, import_node_path36.join)((0, import_node_os16.homedir)(), ".codex", "config.toml"),
14272
- (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")
14273
14660
  ];
14274
14661
  for (const path of candidates) {
14275
14662
  if (!(0, import_node_fs35.existsSync)(path)) continue;
@@ -14305,7 +14692,7 @@ function asRecord5(value) {
14305
14692
  return void 0;
14306
14693
  }
14307
14694
  function codexLooksAuthenticated() {
14308
- 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");
14309
14696
  if (!(0, import_node_fs35.existsSync)(authPath)) return false;
14310
14697
  try {
14311
14698
  return (0, import_node_fs35.statSync)(authPath).size > 2;
@@ -14313,12 +14700,12 @@ function codexLooksAuthenticated() {
14313
14700
  return false;
14314
14701
  }
14315
14702
  }
14316
- 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;
14317
14704
  var init_codex = __esm({
14318
14705
  "src/agents/codex.ts"() {
14319
14706
  "use strict";
14320
14707
  import_node_fs35 = require("fs");
14321
- import_node_os16 = require("os");
14708
+ import_node_os15 = require("os");
14322
14709
  import_node_path36 = require("path");
14323
14710
  init_run();
14324
14711
  init_app_settings();
@@ -15771,12 +16158,12 @@ var init_claude_usage = __esm({
15771
16158
  });
15772
16159
 
15773
16160
  // src/agents/claude-usage-client.ts
15774
- 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;
15775
16162
  var init_claude_usage_client = __esm({
15776
16163
  "src/agents/claude-usage-client.ts"() {
15777
16164
  "use strict";
15778
16165
  import_node_fs41 = require("fs");
15779
- import_node_os17 = require("os");
16166
+ import_node_os16 = require("os");
15780
16167
  import_node_path41 = require("path");
15781
16168
  init_run();
15782
16169
  init_fetch();
@@ -16426,7 +16813,9 @@ function resolveSidebarGitPrompt(action, opts) {
16426
16813
  }
16427
16814
  return agentGitPrompt(action, opts);
16428
16815
  }
16429
- 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);
16430
16819
  switch (action) {
16431
16820
  case "commit-push":
16432
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.";
@@ -16451,7 +16840,57 @@ function gitActionExpansion(action, base) {
16451
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.";
16452
16841
  }
16453
16842
  }
16454
- 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) {
16455
16894
  const trimmed = prompt.trim();
16456
16895
  let action = null;
16457
16896
  let base = null;
@@ -16476,11 +16915,12 @@ function expandCanonicalGitRequest(prompt) {
16476
16915
  base = m[1]?.trim() || null;
16477
16916
  }
16478
16917
  }
16479
- let expansion = action ? gitActionExpansion(action, base) : null;
16918
+ const forge = opts?.forge ?? null;
16919
+ let expansion = action ? gitActionExpansion(action, base, forge) : null;
16480
16920
  if (!expansion && trimmed === "Address review comments.") {
16481
- 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.";
16482
16922
  } else if (!expansion && FIX_CI_RE.test(trimmed)) {
16483
- 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.";
16484
16924
  }
16485
16925
  if (!expansion) return prompt;
16486
16926
  return `${trimmed}
@@ -17211,7 +17651,7 @@ function forkMessageSlice(from, throughIndex) {
17211
17651
  function buildForkTranscriptAttachment(baseTitle, messages) {
17212
17652
  const title = baseTitle || "Chat";
17213
17653
  return {
17214
- id: (0, import_node_crypto8.randomUUID)(),
17654
+ id: (0, import_node_crypto7.randomUUID)(),
17215
17655
  name: `Transcript of ${title}.md`,
17216
17656
  kind: "transcript",
17217
17657
  content: formatTranscriptMarkdown(title, messages)
@@ -17270,11 +17710,11 @@ function forkChatTab(input) {
17270
17710
  }
17271
17711
  return tab;
17272
17712
  }
17273
- var import_node_crypto8;
17713
+ var import_node_crypto7;
17274
17714
  var init_chat_tabs = __esm({
17275
17715
  "src/threads/chat-tabs.ts"() {
17276
17716
  "use strict";
17277
- import_node_crypto8 = require("crypto");
17717
+ import_node_crypto7 = require("crypto");
17278
17718
  init_context_compact();
17279
17719
  init_teams();
17280
17720
  init_worktree_labels();
@@ -17516,7 +17956,7 @@ function buildReviewRequestAttachment(content, opts) {
17516
17956
  const path = opts?.path ?? REVIEW_SKILL_PATH;
17517
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);
17518
17958
  return {
17519
- id: (0, import_node_crypto9.randomUUID)(),
17959
+ id: (0, import_node_crypto8.randomUUID)(),
17520
17960
  name,
17521
17961
  kind: "file",
17522
17962
  path,
@@ -17569,11 +18009,11 @@ async function requestReview(threadRef, send) {
17569
18009
  const started = await send(tab.id, REVIEW_REQUEST_PREFILL);
17570
18010
  return { tab: started, from };
17571
18011
  }
17572
- 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;
17573
18013
  var init_request_review = __esm({
17574
18014
  "src/review/request-review.ts"() {
17575
18015
  "use strict";
17576
- import_node_crypto9 = require("crypto");
18016
+ import_node_crypto8 = require("crypto");
17577
18017
  import_node_fs44 = require("fs");
17578
18018
  import_node_path44 = require("path");
17579
18019
  init_global_workspace();
@@ -18831,7 +19271,7 @@ function setupLogPath(threadId) {
18831
19271
  }
18832
19272
  function setupLogKeyForWorktree(worktreePath) {
18833
19273
  const norm = normalizeWorktreePath(worktreePath);
18834
- 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)}`;
18835
19275
  }
18836
19276
  function emptySetupLog() {
18837
19277
  return { ...EMPTY };
@@ -18924,11 +19364,11 @@ function finishSetupLog(threadId, exitCode, source) {
18924
19364
  persistNow(threadId);
18925
19365
  return materialize(mem);
18926
19366
  }
18927
- 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;
18928
19368
  var init_setup_log = __esm({
18929
19369
  "src/store/setup-log.ts"() {
18930
19370
  "use strict";
18931
- import_node_crypto10 = require("crypto");
19371
+ import_node_crypto9 = require("crypto");
18932
19372
  import_node_fs48 = require("fs");
18933
19373
  import_node_path48 = require("path");
18934
19374
  init_worktree_labels();
@@ -18979,7 +19419,7 @@ function runLogPath(key) {
18979
19419
  }
18980
19420
  function runLogKeyForWorktree(worktreePath, scriptName) {
18981
19421
  const norm = normalizeWorktreePath(worktreePath);
18982
- 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);
18983
19423
  return `wt-${hash}-run-${safeScriptToken(scriptName)}`;
18984
19424
  }
18985
19425
  function emptyRunLog() {
@@ -19080,11 +19520,11 @@ function finishRunLog(key, exitCode) {
19080
19520
  function flushRunLog(key) {
19081
19521
  persistNow2(key);
19082
19522
  }
19083
- 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;
19084
19524
  var init_run_log = __esm({
19085
19525
  "src/store/run-log.ts"() {
19086
19526
  "use strict";
19087
- import_node_crypto11 = require("crypto");
19527
+ import_node_crypto10 = require("crypto");
19088
19528
  import_node_fs49 = require("fs");
19089
19529
  import_node_path49 = require("path");
19090
19530
  init_worktree_labels();
@@ -19510,11 +19950,11 @@ function denyPendingToolPermissions(threadId) {
19510
19950
  const last = pending[pending.length - 1];
19511
19951
  if (last) clearPrompt(threadId, last.id);
19512
19952
  }
19513
- var import_node_crypto12, import_node_path53, queues, publisher;
19953
+ var import_node_crypto11, import_node_path53, queues, publisher;
19514
19954
  var init_tool_permission = __esm({
19515
19955
  "src/agents/tool-permission.ts"() {
19516
19956
  "use strict";
19517
- import_node_crypto12 = require("crypto");
19957
+ import_node_crypto11 = require("crypto");
19518
19958
  import_node_path53 = require("path");
19519
19959
  init_claude_mcp();
19520
19960
  queues = /* @__PURE__ */ new Map();
@@ -20046,7 +20486,7 @@ function isUnclaimedSteerRequest(req) {
20046
20486
  }
20047
20487
  function nextSteerRequest(existing) {
20048
20488
  if (existing?.requestId && !existing.claimedAt) return existing;
20049
- 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() };
20050
20490
  }
20051
20491
  function claimSteerRequest(threadId) {
20052
20492
  const pending = readThread(threadId)?.steerRequest;
@@ -20055,11 +20495,11 @@ function claimSteerRequest(threadId) {
20055
20495
  steerRequest: { ...pending, claimedAt: (/* @__PURE__ */ new Date()).toISOString() }
20056
20496
  });
20057
20497
  }
20058
- var import_node_crypto13;
20498
+ var import_node_crypto12;
20059
20499
  var init_steer_request = __esm({
20060
20500
  "src/orchestrator/steer-request.ts"() {
20061
20501
  "use strict";
20062
- import_node_crypto13 = require("crypto");
20502
+ import_node_crypto12 = require("crypto");
20063
20503
  init_thread_store();
20064
20504
  }
20065
20505
  });
@@ -20927,8 +21367,8 @@ function resolveAbleTimeOAuthHost(requested, saved) {
20927
21367
  return fromSaved || void 0;
20928
21368
  }
20929
21369
  function createAbleTimePkce() {
20930
- const verifier = (0, import_node_crypto14.randomBytes)(32).toString("base64url");
20931
- 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");
20932
21372
  return { verifier, challenge };
20933
21373
  }
20934
21374
  function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
@@ -21033,7 +21473,7 @@ async function startAbleTimeOAuth(opts) {
21033
21473
  opts?.host,
21034
21474
  loadAppSettings().integrations.abletimeHost
21035
21475
  );
21036
- const state = (0, import_node_crypto14.randomBytes)(16).toString("hex");
21476
+ const state = (0, import_node_crypto13.randomBytes)(16).toString("hex");
21037
21477
  const pkce = createAbleTimePkce();
21038
21478
  const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
21039
21479
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
@@ -21133,11 +21573,11 @@ async function startAbleTimeOAuth(opts) {
21133
21573
  return loadAppSettings();
21134
21574
  }
21135
21575
  }
21136
- 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;
21137
21577
  var init_abletime_oauth = __esm({
21138
21578
  "src/integrations/abletime-oauth.ts"() {
21139
21579
  "use strict";
21140
- import_node_crypto14 = require("crypto");
21580
+ import_node_crypto13 = require("crypto");
21141
21581
  import_node_http2 = require("http");
21142
21582
  init_fetch();
21143
21583
  init_app_settings();
@@ -21898,7 +22338,7 @@ function uniqueAttachmentFileName(dir, originalName) {
21898
22338
  const candidate = `${stem}-${i}${ext}`;
21899
22339
  if (!(0, import_node_fs53.existsSync)((0, import_node_path54.join)(dir, candidate))) return candidate;
21900
22340
  }
21901
- return `${stem}-${(0, import_node_crypto15.randomUUID)()}${ext}`;
22341
+ return `${stem}-${(0, import_node_crypto14.randomUUID)()}${ext}`;
21902
22342
  }
21903
22343
  function extractHttpUrls(markdown) {
21904
22344
  const text7 = markdown ?? "";
@@ -22004,13 +22444,13 @@ async function downloadAuthenticatedFile(opts) {
22004
22444
  ...contentType ? { contentType } : {}
22005
22445
  };
22006
22446
  }
22007
- 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;
22008
22448
  var init_issue_attachments = __esm({
22009
22449
  "src/integrations/issue-attachments.ts"() {
22010
22450
  "use strict";
22011
22451
  import_node_fs53 = require("fs");
22012
22452
  import_node_path54 = require("path");
22013
- import_node_crypto15 = require("crypto");
22453
+ import_node_crypto14 = require("crypto");
22014
22454
  init_fetch();
22015
22455
  init_workspace_scratch();
22016
22456
  ISSUE_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;
@@ -23629,7 +24069,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
23629
24069
  ...instructions
23630
24070
  ].join("\n");
23631
24071
  return {
23632
- id: (0, import_node_crypto16.randomUUID)(),
24072
+ id: (0, import_node_crypto15.randomUUID)(),
23633
24073
  name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
23634
24074
  kind: "transcript",
23635
24075
  content: body
@@ -23667,11 +24107,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText, opts) {
23667
24107
  }
23668
24108
  return updateThread(tab.id, patch);
23669
24109
  }
23670
- 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;
23671
24111
  var init_quota_failover = __esm({
23672
24112
  "src/orchestrator/quota-failover.ts"() {
23673
24113
  "use strict";
23674
- import_node_crypto16 = require("crypto");
24114
+ import_node_crypto15 = require("crypto");
23675
24115
  init_session_quota();
23676
24116
  init_app_settings();
23677
24117
  init_managed_accounts();
@@ -23778,7 +24218,7 @@ function listConductorWorkspaces() {
23778
24218
  if (!(0, import_node_fs56.existsSync)(CONDUCTOR_DB)) {
23779
24219
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
23780
24220
  }
23781
- 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-"));
23782
24222
  const snapshot = (0, import_node_path56.join)(tmp, "conductor.db");
23783
24223
  try {
23784
24224
  (0, import_node_fs56.copyFileSync)(CONDUCTOR_DB, snapshot);
@@ -23869,7 +24309,7 @@ function importConductorWorkspace(workspaceId) {
23869
24309
  if (!(0, import_node_fs56.existsSync)(CONDUCTOR_DB)) {
23870
24310
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
23871
24311
  }
23872
- 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-"));
23873
24313
  const snapshot = (0, import_node_path56.join)(tmp, "conductor.db");
23874
24314
  try {
23875
24315
  (0, import_node_fs56.copyFileSync)(CONDUCTOR_DB, snapshot);
@@ -23964,13 +24404,13 @@ function importConductorWorkspace(workspaceId) {
23964
24404
  async function importConductorWorkspaceAsync(workspaceId) {
23965
24405
  return importConductorWorkspace(workspaceId);
23966
24406
  }
23967
- 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;
23968
24408
  var init_adopt = __esm({
23969
24409
  "src/threads/adopt.ts"() {
23970
24410
  "use strict";
23971
24411
  import_node_child_process5 = require("child_process");
23972
24412
  import_node_fs56 = require("fs");
23973
- import_node_os18 = require("os");
24413
+ import_node_os17 = require("os");
23974
24414
  import_node_path56 = require("path");
23975
24415
  import_node_module6 = require("module");
23976
24416
  init_worktree();
@@ -24284,9 +24724,187 @@ var init_stack_layers = __esm({
24284
24724
  }
24285
24725
  });
24286
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
+
24287
24905
  // src/diff/diff.ts
24288
24906
  async function inspectGitWorktree(worktreePath) {
24289
- if (!worktreePath || !(0, import_node_fs58.existsSync)(worktreePath)) return "missing_worktree";
24907
+ if (!worktreePath || !(0, import_node_fs59.existsSync)(worktreePath)) return "missing_worktree";
24290
24908
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
24291
24909
  reject: false
24292
24910
  });
@@ -24294,7 +24912,7 @@ async function inspectGitWorktree(worktreePath) {
24294
24912
  return "ok";
24295
24913
  }
24296
24914
  async function initializeGitRepository(worktreePath) {
24297
- if (!worktreePath || !(0, import_node_fs58.existsSync)(worktreePath)) {
24915
+ if (!worktreePath || !(0, import_node_fs59.existsSync)(worktreePath)) {
24298
24916
  throw new Error("Worktree not found");
24299
24917
  }
24300
24918
  const status = await inspectGitWorktree(worktreePath);
@@ -24428,11 +25046,11 @@ new file mode 100644
24428
25046
  };
24429
25047
  }
24430
25048
  async function untrackedPatch(worktreePath, path, maxHunk) {
24431
- const abs = (0, import_node_path57.join)(worktreePath, path);
25049
+ const abs = (0, import_node_path58.join)(worktreePath, path);
24432
25050
  try {
24433
- const st = (0, import_node_fs58.statSync)(abs);
25051
+ const st = (0, import_node_fs59.statSync)(abs);
24434
25052
  if (st.isFile() && st.size > maxHunk) {
24435
- const buf = (0, import_node_fs58.readFileSync)(abs).subarray(0, maxHunk);
25053
+ const buf = (0, import_node_fs59.readFileSync)(abs).subarray(0, maxHunk);
24436
25054
  return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
24437
25055
  }
24438
25056
  } catch {
@@ -24899,9 +25517,9 @@ async function getDiff(worktreePath, repoPath, opts) {
24899
25517
  }
24900
25518
  function statWorktreePath(worktreePath, relativePath) {
24901
25519
  assertSafeRelativePath(relativePath);
24902
- const abs = (0, import_node_path57.join)(worktreePath, relativePath);
24903
- if (!(0, import_node_fs58.existsSync)(abs)) return "missing";
24904
- 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);
24905
25523
  if (st.isDirectory()) return "dir";
24906
25524
  if (st.isFile()) return "file";
24907
25525
  return "missing";
@@ -24930,8 +25548,8 @@ function isImageRelativePath(relativePath) {
24930
25548
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
24931
25549
  assertSafeRelativePath(relativePath);
24932
25550
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
24933
- const abs = (0, import_node_path57.join)(worktreePath, relativePath);
24934
- 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);
24935
25553
  if (!st.isFile()) {
24936
25554
  throw new Error(`Not a file: ${relativePath}`);
24937
25555
  }
@@ -24940,7 +25558,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
24940
25558
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
24941
25559
  );
24942
25560
  }
24943
- const buf = (0, import_node_fs58.readFileSync)(abs);
25561
+ const buf = (0, import_node_fs59.readFileSync)(abs);
24944
25562
  return {
24945
25563
  path: relativePath,
24946
25564
  contentBase64: buf.toString("base64"),
@@ -24950,12 +25568,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
24950
25568
  function readWorktreeFile(worktreePath, relativePath, opts) {
24951
25569
  assertSafeRelativePath(relativePath);
24952
25570
  const maxBytes = opts?.maxBytes ?? 2e5;
24953
- const abs = (0, import_node_path57.join)(worktreePath, relativePath);
24954
- 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);
24955
25573
  if (!st.isFile()) {
24956
25574
  throw new Error(`Not a file: ${relativePath}`);
24957
25575
  }
24958
- const buf = (0, import_node_fs58.readFileSync)(abs);
25576
+ const buf = (0, import_node_fs59.readFileSync)(abs);
24959
25577
  if (isImageRelativePath(relativePath)) {
24960
25578
  const maxImageBytes = Math.max(maxBytes, 15e6);
24961
25579
  const truncated2 = buf.length > maxImageBytes;
@@ -24998,9 +25616,9 @@ function assertSafeRelativePath(relativePath) {
24998
25616
  }
24999
25617
  function writeWorktreeFile(worktreePath, relativePath, content) {
25000
25618
  assertSafeRelativePath(relativePath);
25001
- const abs = (0, import_node_path57.join)(worktreePath, relativePath);
25002
- (0, import_node_fs58.mkdirSync)((0, import_node_path57.dirname)(abs), { recursive: true });
25003
- (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");
25004
25622
  return { path: relativePath };
25005
25623
  }
25006
25624
  async function getWorktreeDirtyStat(worktreePath) {
@@ -25040,12 +25658,12 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
25040
25658
  truncated: full.files.length > maxFiles
25041
25659
  };
25042
25660
  }
25043
- 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;
25044
25662
  var init_diff = __esm({
25045
25663
  "src/diff/diff.ts"() {
25046
25664
  "use strict";
25047
- import_node_fs58 = require("fs");
25048
- import_node_path57 = require("path");
25665
+ import_node_fs59 = require("fs");
25666
+ import_node_path58 = require("path");
25049
25667
  init_run();
25050
25668
  init_worktree();
25051
25669
  mergeBaseCache = /* @__PURE__ */ new Map();
@@ -25254,6 +25872,7 @@ async function confirmLand(thread, opts) {
25254
25872
  var init_land = __esm({
25255
25873
  "src/land/land.ts"() {
25256
25874
  "use strict";
25875
+ init_pr_write();
25257
25876
  init_worktree();
25258
25877
  init_diff();
25259
25878
  init_pr_metadata();
@@ -25267,16 +25886,16 @@ function readWorktreeFileTail(worktreePath, relativePath, maxBytes = WORKTREE_TA
25267
25886
  if (!relativePath || relativePath.includes("..") || relativePath.startsWith("/")) {
25268
25887
  throw new Error("Invalid path");
25269
25888
  }
25270
- const abs = (0, import_node_path58.join)(worktreePath, relativePath);
25271
- 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");
25272
25891
  try {
25273
- const st = (0, import_node_fs59.fstatSync)(fd);
25892
+ const st = (0, import_node_fs60.fstatSync)(fd);
25274
25893
  if (!st.isFile()) throw new Error(`Not a file: ${relativePath}`);
25275
25894
  const size = st.size;
25276
25895
  const start = Math.max(0, size - maxBytes);
25277
25896
  const length = size - start;
25278
25897
  const buf = Buffer.alloc(length);
25279
- 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);
25280
25899
  if (buf.subarray(0, Math.min(buf.length, 8e3)).includes(0)) {
25281
25900
  return {
25282
25901
  path: relativePath,
@@ -25294,15 +25913,15 @@ function readWorktreeFileTail(worktreePath, relativePath, maxBytes = WORKTREE_TA
25294
25913
  }
25295
25914
  return { path: relativePath, content, truncated, binary: false, encoding: "utf8" };
25296
25915
  } finally {
25297
- (0, import_node_fs59.closeSync)(fd);
25916
+ (0, import_node_fs60.closeSync)(fd);
25298
25917
  }
25299
25918
  }
25300
- var import_node_fs59, import_node_path58, WORKTREE_TAIL_BYTES;
25919
+ var import_node_fs60, import_node_path59, WORKTREE_TAIL_BYTES;
25301
25920
  var init_read_worktree_tail = __esm({
25302
25921
  "src/diff/read-worktree-tail.ts"() {
25303
25922
  "use strict";
25304
- import_node_fs59 = require("fs");
25305
- import_node_path58 = require("path");
25923
+ import_node_fs60 = require("fs");
25924
+ import_node_path59 = require("path");
25306
25925
  WORKTREE_TAIL_BYTES = 2e5;
25307
25926
  }
25308
25927
  });
@@ -25451,7 +26070,7 @@ function parseFrontmatter(content) {
25451
26070
  }
25452
26071
  function readSkill(skillMd, source) {
25453
26072
  try {
25454
- const content = (0, import_node_fs60.readFileSync)(skillMd, "utf8");
26073
+ const content = (0, import_node_fs61.readFileSync)(skillMd, "utf8");
25455
26074
  const { name: fmName, description } = parseFrontmatter(content);
25456
26075
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
25457
26076
  const name = fmName || dirName;
@@ -25470,19 +26089,19 @@ function readSkill(skillMd, source) {
25470
26089
  }
25471
26090
  }
25472
26091
  function scanSkillsDir(dir, source, out) {
25473
- if (!(0, import_node_fs60.existsSync)(dir)) return;
26092
+ if (!(0, import_node_fs61.existsSync)(dir)) return;
25474
26093
  let entries;
25475
26094
  try {
25476
- entries = (0, import_node_fs60.readdirSync)(dir);
26095
+ entries = (0, import_node_fs61.readdirSync)(dir);
25477
26096
  } catch {
25478
26097
  return;
25479
26098
  }
25480
26099
  for (const entry of entries) {
25481
26100
  if (entry.startsWith(".")) continue;
25482
- const skillMd = (0, import_node_path59.join)(dir, entry, "SKILL.md");
25483
- 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;
25484
26103
  try {
25485
- if (!(0, import_node_fs60.statSync)(skillMd).isFile()) continue;
26104
+ if (!(0, import_node_fs61.statSync)(skillMd).isFile()) continue;
25486
26105
  } catch {
25487
26106
  continue;
25488
26107
  }
@@ -25491,24 +26110,24 @@ function scanSkillsDir(dir, source, out) {
25491
26110
  }
25492
26111
  }
25493
26112
  function scanClaudePluginSkills(pluginsRoot, out) {
25494
- if (!(0, import_node_fs60.existsSync)(pluginsRoot)) return;
26113
+ if (!(0, import_node_fs61.existsSync)(pluginsRoot)) return;
25495
26114
  const walk = (dir, depth, lookingForSkillsDir) => {
25496
26115
  if (depth > 7) return;
25497
26116
  let entries;
25498
26117
  try {
25499
- entries = (0, import_node_fs60.readdirSync)(dir);
26118
+ entries = (0, import_node_fs61.readdirSync)(dir);
25500
26119
  } catch {
25501
26120
  return;
25502
26121
  }
25503
26122
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
25504
- 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");
25505
26124
  if (skill) out.push(skill);
25506
26125
  }
25507
26126
  for (const entry of entries) {
25508
26127
  if (entry === "node_modules" || entry === ".git") continue;
25509
- const full = (0, import_node_path59.join)(dir, entry);
26128
+ const full = (0, import_node_path60.join)(dir, entry);
25510
26129
  try {
25511
- if (!(0, import_node_fs60.statSync)(full).isDirectory()) continue;
26130
+ if (!(0, import_node_fs61.statSync)(full).isDirectory()) continue;
25512
26131
  } catch {
25513
26132
  continue;
25514
26133
  }
@@ -25526,17 +26145,17 @@ function discoverSkills(worktreePath) {
25526
26145
  const home = (0, import_node_os19.homedir)();
25527
26146
  const collected = [];
25528
26147
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
25529
- scanSkillsDir((0, import_node_path59.join)(worktreePath, rel), "workspace", collected);
26148
+ scanSkillsDir((0, import_node_path60.join)(worktreePath, rel), "workspace", collected);
25530
26149
  }
25531
26150
  for (const abs of [
25532
- (0, import_node_path59.join)(home, ".claude/skills"),
25533
- (0, import_node_path59.join)(home, ".cursor/skills"),
25534
- (0, import_node_path59.join)(home, ".sideboard/skills"),
25535
- (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")
25536
26155
  ]) {
25537
26156
  scanSkillsDir(abs, "user", collected);
25538
26157
  }
25539
- scanClaudePluginSkills((0, import_node_path59.join)(home, ".claude/plugins"), collected);
26158
+ scanClaudePluginSkills((0, import_node_path60.join)(home, ".claude/plugins"), collected);
25540
26159
  collected.push(...bundledSkills());
25541
26160
  const rank = {
25542
26161
  workspace: 0,
@@ -25562,7 +26181,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
25562
26181
  \u2026(truncated)` : body;
25563
26182
  }
25564
26183
  }
25565
- const raw = (0, import_node_fs60.readFileSync)(skillPath, "utf8");
26184
+ const raw = (0, import_node_fs61.readFileSync)(skillPath, "utf8");
25566
26185
  if (raw.startsWith("---")) {
25567
26186
  const end = raw.indexOf("\n---", 3);
25568
26187
  if (end >= 0) {
@@ -25576,13 +26195,13 @@ function readSkillBody(skillPath, maxChars = 12e3) {
25576
26195
 
25577
26196
  \u2026(truncated)` : raw;
25578
26197
  }
25579
- 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;
25580
26199
  var init_discover = __esm({
25581
26200
  "src/skills/discover.ts"() {
25582
26201
  "use strict";
25583
- import_node_fs60 = require("fs");
26202
+ import_node_fs61 = require("fs");
25584
26203
  import_node_os19 = require("os");
25585
- import_node_path59 = require("path");
26204
+ import_node_path60 = require("path");
25586
26205
  init_long_running();
25587
26206
  BUNDLED_SKILL_PREFIX = "bundled:";
25588
26207
  }
@@ -25793,7 +26412,7 @@ function promptForJoinedSteer(input) {
25793
26412
  rewriteTrailingUserPrompt(input.threadId, input.promptText, input.latestPrompt);
25794
26413
  const { agentPrompt: expandedLatest } = expandComposerPrompt(
25795
26414
  input.worktreePath,
25796
- expandCanonicalGitRequest(input.latestPrompt),
26415
+ expandCanonicalGitRequest(input.latestPrompt, { forge: input.gitForge }),
25797
26416
  { attachments: input.attachments }
25798
26417
  );
25799
26418
  return input.expandedPrompt && input.agentPrompt.endsWith(input.expandedPrompt) ? input.agentPrompt.slice(0, -input.expandedPrompt.length) + expandedLatest : `${input.agentPrompt}
@@ -25901,6 +26520,33 @@ var init_enqueue_thread_send = __esm({
25901
26520
  }
25902
26521
  });
25903
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
+
25904
26550
  // src/skills/detached-job-path.ts
25905
26551
  function compiledModuleDir() {
25906
26552
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
@@ -25908,7 +26554,7 @@ function compiledModuleDir() {
25908
26554
  try {
25909
26555
  const url = import_meta8.url;
25910
26556
  if (typeof url === "string" && url.length > 0) {
25911
- 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));
25912
26558
  }
25913
26559
  } catch {
25914
26560
  }
@@ -25916,8 +26562,8 @@ function compiledModuleDir() {
25916
26562
  }
25917
26563
  function detachedJobScriptIn(dir) {
25918
26564
  for (const name of DETACHED_JOB_FILENAMES) {
25919
- const script = (0, import_node_path60.join)(dir, "scripts", name);
25920
- 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;
25921
26567
  }
25922
26568
  return null;
25923
26569
  }
@@ -25934,7 +26580,7 @@ function resolveDetachedJobScript() {
25934
26580
  for (let i = 0; i < 8; i++) {
25935
26581
  const candidate = detachedJobScriptIn(dir);
25936
26582
  if (candidate) return candidate;
25937
- const parent = (0, import_node_path60.dirname)(dir);
26583
+ const parent = (0, import_node_path61.dirname)(dir);
25938
26584
  if (parent === dir) break;
25939
26585
  dir = parent;
25940
26586
  }
@@ -25945,12 +26591,12 @@ function formatDetachedJobInvoke(scriptPath) {
25945
26591
  if (resolved) return `node ${JSON.stringify(resolved)}`;
25946
26592
  return "node scripts/detached-job.cjs";
25947
26593
  }
25948
- 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;
25949
26595
  var init_detached_job_path = __esm({
25950
26596
  "src/skills/detached-job-path.ts"() {
25951
26597
  "use strict";
25952
- import_node_fs61 = require("fs");
25953
- import_node_path60 = require("path");
26598
+ import_node_fs62 = require("fs");
26599
+ import_node_path61 = require("path");
25954
26600
  import_node_url5 = require("url");
25955
26601
  init_packaged_runtime();
25956
26602
  import_meta8 = {};
@@ -26038,17 +26684,35 @@ function formatWorktreeDirective(thread, opts) {
26038
26684
  lines.push(
26039
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)."
26040
26686
  );
26041
- 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
+ }
26042
26696
  lines.push(
26043
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."
26044
26698
  );
26045
26699
  lines.push(
26046
- "- 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`)."
26047
26701
  );
26048
26702
  if (thread.prUrl) {
26049
26703
  lines.push(
26050
26704
  `- A PR already exists (${thread.prUrl}). Update it (push + edit title/body if the purpose drifted) instead of opening a duplicate.`
26051
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
+ );
26052
26716
  } else {
26053
26717
  const slug = opts?.githubSlug ?? "<origin-owner/name>";
26054
26718
  const resolve3 = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
@@ -26166,9 +26830,9 @@ function formatPrGateDirective() {
26166
26830
  }
26167
26831
  function formatProcessGuideDirective(opts) {
26168
26832
  const worktree = opts?.worktreePath?.trim();
26169
- const hasGraphSkill = !!worktree && (0, import_node_fs62.existsSync)((0, import_node_path61.join)(worktree, ".claude/skills/graph-engineering/SKILL.md"));
26170
- const hasReviewSkill = !!worktree && (0, import_node_fs62.existsSync)((0, import_node_path61.join)(worktree, ".claude/skills/review/SKILL.md"));
26171
- 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"));
26172
26836
  return [
26173
26837
  "Process guides (recurring work only):",
26174
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,
@@ -26208,13 +26872,14 @@ function formatArtifactDirective() {
26208
26872
  function formatUiReminder() {
26209
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.";
26210
26874
  }
26211
- 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;
26212
26876
  var init_instructions = __esm({
26213
26877
  "src/agents/instructions.ts"() {
26214
26878
  "use strict";
26215
- import_node_fs62 = require("fs");
26216
- import_node_path61 = require("path");
26879
+ import_node_fs63 = require("fs");
26880
+ import_node_path62 = require("path");
26217
26881
  init_git_auth_mode();
26882
+ init_forge_git_directives();
26218
26883
  init_branch_prefix();
26219
26884
  init_worktree_labels();
26220
26885
  init_detached_job_path();
@@ -26563,6 +27228,7 @@ function compactIssueRow(issue, opts) {
26563
27228
  function formatMcpIssueList(input) {
26564
27229
  const truncated = input.truncated || Boolean(input.commentsTruncated);
26565
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;
26566
27232
  return {
26567
27233
  ...input.source ? { source: input.source } : {},
26568
27234
  ...input.viewer ? { viewer: input.viewer } : {},
@@ -26570,9 +27236,8 @@ function formatMcpIssueList(input) {
26570
27236
  returned: input.issues.length,
26571
27237
  truncated,
26572
27238
  ...input.since ? { since: input.since } : {},
26573
- ...truncated ? {
26574
- 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."
26575
- } : {},
27239
+ ...input.unavailableReason ? { unavailableReason: input.unavailableReason } : {},
27240
+ ...pageHint ? { hint: pageHint } : {},
26576
27241
  issues: input.issues.map((issue) => compactIssueRow(issue, { since: input.since })),
26577
27242
  ...input.since || comments.length > 0 ? { comments } : {}
26578
27243
  };
@@ -26589,7 +27254,8 @@ function formatListedIssuesForMcp(result, page) {
26589
27254
  truncated: windowed.truncated,
26590
27255
  since: result.since,
26591
27256
  comments: commentWindow.items,
26592
- commentsTruncated: commentWindow.truncated
27257
+ commentsTruncated: commentWindow.truncated,
27258
+ unavailableReason: result.unavailableReason
26593
27259
  });
26594
27260
  }
26595
27261
  function mcpJson(payload, isError = false) {
@@ -26954,40 +27620,40 @@ __export(plan_file_exports, {
26954
27620
  writePlanFile: () => writePlanFile
26955
27621
  });
26956
27622
  function ensureAttachmentsGitignore(worktreePath) {
26957
- const gitignoreAbs = (0, import_node_path62.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
26958
- if ((0, import_node_fs63.existsSync)(gitignoreAbs)) return;
26959
- (0, import_node_fs63.mkdirSync)((0, import_node_path62.dirname)(gitignoreAbs), { recursive: true });
26960
- (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");
26961
27627
  }
26962
27628
  function planFileAbs(worktreePath) {
26963
- return (0, import_node_path62.join)(worktreePath, PLAN_FILE_REL);
27629
+ return (0, import_node_path63.join)(worktreePath, PLAN_FILE_REL);
26964
27630
  }
26965
27631
  function readTextIfPresent2(abs) {
26966
- if (!(0, import_node_fs63.existsSync)(abs)) return null;
27632
+ if (!(0, import_node_fs64.existsSync)(abs)) return null;
26967
27633
  try {
26968
- const content = (0, import_node_fs63.readFileSync)(abs, "utf8");
27634
+ const content = (0, import_node_fs64.readFileSync)(abs, "utf8");
26969
27635
  return content.trim() ? content : null;
26970
27636
  } catch {
26971
27637
  return null;
26972
27638
  }
26973
27639
  }
26974
27640
  function readPlanFile(worktreePath) {
26975
- 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));
26976
27642
  }
26977
27643
  function writePlanFile(worktreePath, content) {
26978
27644
  ensureAttachmentsGitignore(worktreePath);
26979
27645
  const abs = planFileAbs(worktreePath);
26980
- (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 });
26981
27647
  const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
26982
- (0, import_node_fs63.writeFileSync)(abs, body, "utf8");
27648
+ (0, import_node_fs64.writeFileSync)(abs, body, "utf8");
26983
27649
  return PLAN_FILE_REL;
26984
27650
  }
26985
- var import_node_fs63, import_node_path62;
27651
+ var import_node_fs64, import_node_path63;
26986
27652
  var init_plan_file = __esm({
26987
27653
  "src/plan/plan-file.ts"() {
26988
27654
  "use strict";
26989
- import_node_fs63 = require("fs");
26990
- import_node_path62 = require("path");
27655
+ import_node_fs64 = require("fs");
27656
+ import_node_path63 = require("path");
26991
27657
  init_workspace_scratch();
26992
27658
  init_plan_present();
26993
27659
  init_plan_present();
@@ -27036,7 +27702,7 @@ function setCaffeinateHoldHooks(next) {
27036
27702
  hooks = next;
27037
27703
  }
27038
27704
  function caffeinateHoldPath() {
27039
- return (0, import_node_path63.join)(appDataDir(), "caffeinate-hold.json");
27705
+ return (0, import_node_path64.join)(appDataDir(), "caffeinate-hold.json");
27040
27706
  }
27041
27707
  function processAlive(pid) {
27042
27708
  if (hooks.processAlive) return hooks.processAlive(pid);
@@ -27070,9 +27736,9 @@ function uniqueIds(ids) {
27070
27736
  }
27071
27737
  function readHold() {
27072
27738
  const path = caffeinateHoldPath();
27073
- if (!(0, import_node_fs64.existsSync)(path)) return null;
27739
+ if (!(0, import_node_fs65.existsSync)(path)) return null;
27074
27740
  try {
27075
- const parsed = JSON.parse((0, import_node_fs64.readFileSync)(path, "utf8"));
27741
+ const parsed = JSON.parse((0, import_node_fs65.readFileSync)(path, "utf8"));
27076
27742
  if (typeof parsed?.pid === "number" && parsed.pid > 0) {
27077
27743
  return {
27078
27744
  pid: parsed.pid,
@@ -27094,7 +27760,7 @@ function writeHold(pid, threadIds) {
27094
27760
  }
27095
27761
  function clearHold() {
27096
27762
  try {
27097
- (0, import_node_fs64.unlinkSync)(caffeinateHoldPath());
27763
+ (0, import_node_fs65.unlinkSync)(caffeinateHoldPath());
27098
27764
  } catch {
27099
27765
  }
27100
27766
  }
@@ -27179,13 +27845,13 @@ function releaseCaffeinateHoldForThread(threadId) {
27179
27845
  }
27180
27846
  return setCaffeinateHold(false, { threadId: id });
27181
27847
  }
27182
- 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;
27183
27849
  var init_caffeinate_hold = __esm({
27184
27850
  "src/store/caffeinate-hold.ts"() {
27185
27851
  "use strict";
27186
27852
  import_node_child_process6 = require("child_process");
27187
- import_node_fs64 = require("fs");
27188
- import_node_path63 = require("path");
27853
+ import_node_fs65 = require("fs");
27854
+ import_node_path64 = require("path");
27189
27855
  init_paths();
27190
27856
  init_private_file();
27191
27857
  hooks = {};
@@ -27194,7 +27860,7 @@ var init_caffeinate_hold = __esm({
27194
27860
 
27195
27861
  // src/store/schedules.ts
27196
27862
  function schedulesPath() {
27197
- return (0, import_node_path64.join)(appDataDir(), "schedules.json");
27863
+ return (0, import_node_path65.join)(appDataDir(), "schedules.json");
27198
27864
  }
27199
27865
  function parseDurationMs(every) {
27200
27866
  const m = every.trim().match(DURATION_RE);
@@ -27330,9 +27996,9 @@ function normalizeTask(raw) {
27330
27996
  }
27331
27997
  function readAll2() {
27332
27998
  const path = schedulesPath();
27333
- if (!(0, import_node_fs65.existsSync)(path)) return [];
27999
+ if (!(0, import_node_fs66.existsSync)(path)) return [];
27334
28000
  try {
27335
- const parsed = JSON.parse((0, import_node_fs65.readFileSync)(path, "utf8"));
28001
+ const parsed = JSON.parse((0, import_node_fs66.readFileSync)(path, "utf8"));
27336
28002
  if (!Array.isArray(parsed)) return [];
27337
28003
  return parsed.map((row) => {
27338
28004
  try {
@@ -27467,13 +28133,13 @@ function recordScheduleRun(id, result) {
27467
28133
  writeAll2(rows);
27468
28134
  return next;
27469
28135
  }
27470
- var import_node_crypto17, import_node_fs65, import_node_path64, import_croner, DURATION_RE, UNIT_MS, ATTACHMENT_KINDS;
28136
+ var import_node_crypto17, import_node_fs66, import_node_path65, import_croner, DURATION_RE, UNIT_MS, ATTACHMENT_KINDS;
27471
28137
  var init_schedules = __esm({
27472
28138
  "src/store/schedules.ts"() {
27473
28139
  "use strict";
27474
28140
  import_node_crypto17 = require("crypto");
27475
- import_node_fs65 = require("fs");
27476
- import_node_path64 = require("path");
28141
+ import_node_fs66 = require("fs");
28142
+ import_node_path65 = require("path");
27477
28143
  import_croner = require("croner");
27478
28144
  init_orchestrator_capable();
27479
28145
  init_thinking_effort();
@@ -27677,9 +28343,9 @@ function recoverFinishedCursorRun(opts) {
27677
28343
  return best;
27678
28344
  }
27679
28345
  function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
27680
- if (!(0, import_node_fs66.existsSync)(runsPath)) return null;
28346
+ if (!(0, import_node_fs67.existsSync)(runsPath)) return null;
27681
28347
  try {
27682
- const lines = (0, import_node_fs66.readFileSync)(runsPath, "utf8").split("\n");
28348
+ const lines = (0, import_node_fs67.readFileSync)(runsPath, "utf8").split("\n");
27683
28349
  let best = null;
27684
28350
  for (const line of lines) {
27685
28351
  const trimmed = line.trim();
@@ -27705,11 +28371,11 @@ function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
27705
28371
  return null;
27706
28372
  }
27707
28373
  }
27708
- var import_node_fs66;
28374
+ var import_node_fs67;
27709
28375
  var init_cursor_recover = __esm({
27710
28376
  "src/agents/cursor-recover.ts"() {
27711
28377
  "use strict";
27712
- import_node_fs66 = require("fs");
28378
+ import_node_fs67 = require("fs");
27713
28379
  init_cursor_store();
27714
28380
  }
27715
28381
  });
@@ -27830,7 +28496,7 @@ async function startOrchestration(opts) {
27830
28496
  }
27831
28497
  return ready;
27832
28498
  }
27833
- 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;
27834
28500
  var init_orchestrator = __esm({
27835
28501
  "src/orchestrator/orchestrator.ts"() {
27836
28502
  "use strict";
@@ -27839,7 +28505,7 @@ var init_orchestrator = __esm({
27839
28505
  init_task_state();
27840
28506
  init_wait_for_turn();
27841
28507
  import_node_crypto18 = require("crypto");
27842
- import_node_fs67 = require("fs");
28508
+ import_node_fs68 = require("fs");
27843
28509
  init_error_detail();
27844
28510
  init_run();
27845
28511
  init_stale_lock();
@@ -28038,7 +28704,7 @@ var init_orchestrator = __esm({
28038
28704
  this.lightReconcileAt.set(repoPath, Date.now());
28039
28705
  for (const thread of listThreads()) {
28040
28706
  if (isGlobalThread(thread) || thread.repoPath !== repoPath) continue;
28041
- if (!(0, import_node_fs67.existsSync)(thread.worktreePath)) {
28707
+ if (!(0, import_node_fs68.existsSync)(thread.worktreePath)) {
28042
28708
  setStatus(thread.id, "broken", "Worktree missing on disk");
28043
28709
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
28044
28710
  }
@@ -28067,7 +28733,7 @@ var init_orchestrator = __esm({
28067
28733
  }
28068
28734
  continue;
28069
28735
  }
28070
- if (!(0, import_node_fs67.existsSync)(thread.worktreePath)) {
28736
+ if (!(0, import_node_fs68.existsSync)(thread.worktreePath)) {
28071
28737
  setStatus(thread.id, "broken", "Worktree missing on disk");
28072
28738
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
28073
28739
  continue;
@@ -28791,7 +29457,8 @@ var init_orchestrator = __esm({
28791
29457
  return;
28792
29458
  }
28793
29459
  thread = this.requireThread(threadId);
28794
- 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 });
28795
29462
  const { agentPrompt: expandedPrompt } = expandComposerPrompt(
28796
29463
  thread.worktreePath,
28797
29464
  gitPrompt,
@@ -28878,11 +29545,9 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
28878
29545
  const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2, gitBranchPrefixSetting: gitBranchPrefixSetting2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
28879
29546
  const gitAuthMode = getGithubGitAuthMode2();
28880
29547
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
28881
- githubSlug: await withTimeout(
28882
- resolveGithubRepoSlug(fresh.worktreePath),
28883
- 8e3,
28884
- "resolveGithubRepoSlug"
28885
- ).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,
28886
29551
  gitAuthMode,
28887
29552
  activeRuns: annotateDevAccessRuns(
28888
29553
  mergeWorktreeActiveRuns(
@@ -28975,7 +29640,8 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
28975
29640
  agentPrompt,
28976
29641
  expandedPrompt,
28977
29642
  worktreePath: fresh.worktreePath,
28978
- attachments: autoContinue ? [] : sentAttachments ?? []
29643
+ attachments: autoContinue ? [] : sentAttachments ?? [],
29644
+ gitForge: turnOrigin?.forge
28979
29645
  });
28980
29646
  const handle = await spawnAgentTurn(
28981
29647
  fresh,
@@ -30549,7 +31215,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
30549
31215
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
30550
31216
  return restored2;
30551
31217
  }
30552
- if (!(0, import_node_fs67.existsSync)(thread.worktreePath)) {
31218
+ if (!(0, import_node_fs68.existsSync)(thread.worktreePath)) {
30553
31219
  if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
30554
31220
  throw new Error(
30555
31221
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
@@ -30740,7 +31406,7 @@ function createClippedMcpServer() {
30740
31406
  // src/mcp/server.ts
30741
31407
  var import_zod11 = require("zod");
30742
31408
  var import_node_crypto22 = require("crypto");
30743
- var import_node_path68 = require("path");
31409
+ var import_node_path69 = require("path");
30744
31410
  init_orchestrator();
30745
31411
  init_task_state();
30746
31412
  init_worktree();
@@ -30848,9 +31514,9 @@ function githubProjectsOnIssue(raw) {
30848
31514
  }
30849
31515
  return out;
30850
31516
  }
30851
- async function readGitHubIssueProjects(number, cwd, repoArgs) {
31517
+ async function readGitHubIssueProjects(number, cwd, repoArgs2) {
30852
31518
  const result = await gh(
30853
- ["issue", "view", String(number), ...repoArgs, "--json", "projectItems"],
31519
+ ["issue", "view", String(number), ...repoArgs2, "--json", "projectItems"],
30854
31520
  cwd,
30855
31521
  { reject: false }
30856
31522
  );
@@ -30868,8 +31534,8 @@ async function readGitHubIssueProjects(number, cwd, repoArgs) {
30868
31534
  return { projects: [] };
30869
31535
  }
30870
31536
  }
30871
- async function listGitHubIssueProjectNames(number, cwd, repoArgs) {
30872
- const read = await readGitHubIssueProjects(number, cwd, repoArgs);
31537
+ async function listGitHubIssueProjectNames(number, cwd, repoArgs2) {
31538
+ const read = await readGitHubIssueProjects(number, cwd, repoArgs2);
30873
31539
  return read.projects.map((project) => project.name).filter(Boolean);
30874
31540
  }
30875
31541
  function parseGitHubProjectList(stdout) {
@@ -31074,14 +31740,14 @@ async function listGitHubIssueCommentsSince(opts) {
31074
31740
  }
31075
31741
  async function getGitHubIssue(id, opts) {
31076
31742
  const number = parseGitHubIssueNumber(id);
31077
- const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
31743
+ const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
31078
31744
  const stdout = requireGhOk(
31079
31745
  await gh(
31080
31746
  [
31081
31747
  "issue",
31082
31748
  "view",
31083
31749
  String(number),
31084
- ...repoArgs,
31750
+ ...repoArgs2,
31085
31751
  "--json",
31086
31752
  "number,title,body,url,state,labels,assignees,comments,author"
31087
31753
  ],
@@ -31100,7 +31766,7 @@ async function getGitHubIssue(id, opts) {
31100
31766
  throw new Error(`GitHub issue not found: #${number}`);
31101
31767
  }
31102
31768
  const issue = toGitHubIssue(parsed);
31103
- const projects = await readGitHubIssueProjects(number, cwd, repoArgs);
31769
+ const projects = await readGitHubIssueProjects(number, cwd, repoArgs2);
31104
31770
  issue.projects = projects.projects;
31105
31771
  if (projects.error) issue.projectsError = projects.error;
31106
31772
  return issue;
@@ -31109,10 +31775,10 @@ async function commentGitHubIssue(input, opts) {
31109
31775
  const number = parseGitHubIssueNumber(input.id);
31110
31776
  const body = input.body.trim();
31111
31777
  if (!body) throw new Error("GitHub comment body is required");
31112
- const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
31778
+ const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
31113
31779
  const stdout = requireGhOk(
31114
31780
  await gh(
31115
- ["issue", "comment", String(number), ...repoArgs, "--body", body],
31781
+ ["issue", "comment", String(number), ...repoArgs2, "--body", body],
31116
31782
  cwd,
31117
31783
  { reject: false }
31118
31784
  ),
@@ -31154,21 +31820,21 @@ async function updateGitHubIssue(input, opts) {
31154
31820
  "github_update_issue needs at least one of title, body, state, labels, project, parent, relations, removeRelations"
31155
31821
  );
31156
31822
  }
31157
- const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
31823
+ const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
31158
31824
  if (state === "closed" || state === "close") {
31159
31825
  requireGhOk(
31160
- await gh(["issue", "close", String(number), ...repoArgs], cwd, { reject: false }),
31826
+ await gh(["issue", "close", String(number), ...repoArgs2], cwd, { reject: false }),
31161
31827
  `GitHub close #${number}`
31162
31828
  );
31163
31829
  } else if (state === "open" || state === "reopen") {
31164
31830
  requireGhOk(
31165
- await gh(["issue", "reopen", String(number), ...repoArgs], cwd, { reject: false }),
31831
+ await gh(["issue", "reopen", String(number), ...repoArgs2], cwd, { reject: false }),
31166
31832
  `GitHub reopen #${number}`
31167
31833
  );
31168
31834
  } else if (state) {
31169
31835
  throw new Error(`GitHub issue state must be open or closed (got ${input.state})`);
31170
31836
  }
31171
- const args = ["issue", "edit", String(number), ...repoArgs];
31837
+ const args = ["issue", "edit", String(number), ...repoArgs2];
31172
31838
  if (title) args.push("--title", title);
31173
31839
  if (body !== void 0) args.push("--body", body);
31174
31840
  if (input.labels !== void 0) {
@@ -31180,7 +31846,7 @@ async function updateGitHubIssue(input, opts) {
31180
31846
  if (remove.length) args.push("--remove-label", remove.join(","));
31181
31847
  }
31182
31848
  if (input.project !== void 0) {
31183
- const current = await listGitHubIssueProjectNames(number, cwd, repoArgs);
31849
+ const current = await listGitHubIssueProjectNames(number, cwd, repoArgs2);
31184
31850
  if (isGitHubNoneToken(input.project)) {
31185
31851
  for (const name of current) args.push("--remove-project", name);
31186
31852
  } else {
@@ -31206,7 +31872,7 @@ async function updateGitHubIssue(input, opts) {
31206
31872
  for (const rel of addRelations ?? []) {
31207
31873
  args.push(githubRelationEditFlag(rel.type, false), String(parseGitHubIssueNumber(rel.issue)));
31208
31874
  }
31209
- const editChanged = args.length > 3 + repoArgs.length;
31875
+ const editChanged = args.length > 3 + repoArgs2.length;
31210
31876
  if (editChanged) {
31211
31877
  requireGhOk(await gh(args, cwd, { reject: false }), `GitHub edit #${number}`);
31212
31878
  }
@@ -31221,8 +31887,8 @@ async function createGitHubIssue(input, opts) {
31221
31887
  parentNumber ? `Spin-off of #${parentNumber}.` : null,
31222
31888
  input.body?.trim() || null
31223
31889
  ].filter(Boolean);
31224
- const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
31225
- const args = ["issue", "create", ...repoArgs, "--title", title];
31890
+ const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
31891
+ const args = ["issue", "create", ...repoArgs2, "--title", title];
31226
31892
  if (bodyParts.length) args.push("--body", bodyParts.join("\n\n"));
31227
31893
  const created = await gh([...args, "--json", "number,url,title"], cwd, { reject: false });
31228
31894
  if (created.exitCode === 0 && created.stdout.trim()) {
@@ -32446,6 +33112,18 @@ async function commentLinearIssue(input, opts) {
32446
33112
  }
32447
33113
 
32448
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
+ }
32449
33127
  function assigneeKey(assignee) {
32450
33128
  return (assignee ?? "").trim().toLowerCase();
32451
33129
  }
@@ -32607,6 +33285,17 @@ async function listIssues(repoPath, opts) {
32607
33285
  ...since ? { since, comments: commentsFromAbleTimeIssues(tasks, issues2, since) } : {}
32608
33286
  };
32609
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
+ }
32610
33299
  const [issues, login] = await Promise.all([
32611
33300
  listGitHubIssues(repoPath, { assignee, query, limit: opts?.limit, updatedSince: since }),
32612
33301
  githubViewerLogin(repoPath)
@@ -33416,11 +34105,15 @@ function fail2(err) {
33416
34105
  true
33417
34106
  );
33418
34107
  }
34108
+ async function cursorIssueBlock(repoPath) {
34109
+ const cwd = await resolveRepoRoot((repoPath ?? "").trim() || process.cwd());
34110
+ return cursorIssuesUnavailableReason(cwd);
34111
+ }
33419
34112
  var repoPathSchema = import_zod6.z.string().optional().describe("Workspace / worktree path. Omit on a worktree turn (uses cwd).");
33420
34113
  function registerGithubIssueTools(server) {
33421
34114
  server.tool(
33422
34115
  "github_search_issues",
33423
- "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).",
33424
34117
  {
33425
34118
  query: import_zod6.z.string().optional().describe("Search text (title, body, identifiers). Omit to list by assignee only."),
33426
34119
  assignee: import_zod6.z.string().optional().describe("me, unassigned, all, or a GitHub login. Default: all when query is set, otherwise me."),
@@ -33432,6 +34125,15 @@ function registerGithubIssueTools(server) {
33432
34125
  try {
33433
34126
  const cwd = await resolveRepoRoot((repoPath ?? "").trim() || process.cwd());
33434
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
+ }
33435
34137
  const since = updatedSince ? parseIssueSince(updatedSince) : void 0;
33436
34138
  const issues = await listGitHubIssues(cwd, {
33437
34139
  query,
@@ -33467,6 +34169,8 @@ function registerGithubIssueTools(server) {
33467
34169
  { repoPath: repoPathSchema },
33468
34170
  async ({ repoPath }) => {
33469
34171
  try {
34172
+ const blocked = await cursorIssueBlock(repoPath);
34173
+ if (blocked) throw new Error(blocked);
33470
34174
  return text4({ projects: await listGitHubProjects({ repoPath }) });
33471
34175
  } catch (err) {
33472
34176
  return fail2(err);
@@ -33479,6 +34183,8 @@ function registerGithubIssueTools(server) {
33479
34183
  { id: import_zod6.z.string(), include: mcpIssueIncludeSchema, repoPath: repoPathSchema },
33480
34184
  async ({ id, include, repoPath }) => {
33481
34185
  try {
34186
+ const blocked = await cursorIssueBlock(repoPath);
34187
+ if (blocked) throw new Error(blocked);
33482
34188
  return text4(formatGitHubIssuePayload(await getGitHubIssue(id, { repoPath }), include));
33483
34189
  } catch (err) {
33484
34190
  return fail2(err);
@@ -33507,6 +34213,8 @@ function registerGithubIssueTools(server) {
33507
34213
  { id: import_zod6.z.string(), body: import_zod6.z.string(), repoPath: repoPathSchema },
33508
34214
  async ({ id, body, repoPath }) => {
33509
34215
  try {
34216
+ const blocked = await cursorIssueBlock(repoPath);
34217
+ if (blocked) throw new Error(blocked);
33510
34218
  return text4(await commentGitHubIssue({ id, body }, { repoPath }));
33511
34219
  } catch (err) {
33512
34220
  return fail2(err);
@@ -33534,6 +34242,8 @@ function registerGithubIssueTools(server) {
33534
34242
  },
33535
34243
  async (args) => {
33536
34244
  try {
34245
+ const blocked = await cursorIssueBlock(args.repoPath);
34246
+ if (blocked) throw new Error(blocked);
33537
34247
  return text4(await updateGitHubIssue(args, { repoPath: args.repoPath }));
33538
34248
  } catch (err) {
33539
34249
  return fail2(err);
@@ -33551,6 +34261,8 @@ function registerGithubIssueTools(server) {
33551
34261
  },
33552
34262
  async (args) => {
33553
34263
  try {
34264
+ const blocked = await cursorIssueBlock(args.repoPath);
34265
+ if (blocked) throw new Error(blocked);
33554
34266
  return text4(await createGitHubIssue(args, { repoPath: args.repoPath }));
33555
34267
  } catch (err) {
33556
34268
  return fail2(err);
@@ -34239,7 +34951,7 @@ function registerScheduleTools(server) {
34239
34951
 
34240
34952
  // src/agent-rpc/handlers.ts
34241
34953
  var import_node_crypto20 = require("crypto");
34242
- var import_node_path65 = require("path");
34954
+ var import_node_path66 = require("path");
34243
34955
  init_wait_for_job();
34244
34956
  init_wait_for_turn();
34245
34957
 
@@ -34304,27 +35016,27 @@ init_gh_errors();
34304
35016
  init_git_auth_mode();
34305
35017
 
34306
35018
  // src/board/load-home-board.ts
34307
- var import_node_fs69 = require("fs");
34308
- var import_node_path67 = require("path");
35019
+ var import_node_fs70 = require("fs");
35020
+ var import_node_path68 = require("path");
34309
35021
  init_worktree();
34310
35022
  init_paths();
34311
35023
 
34312
35024
  // src/board/board-pins.ts
34313
35025
  var import_node_crypto21 = require("crypto");
34314
- var import_node_fs68 = require("fs");
34315
- var import_node_path66 = require("path");
35026
+ var import_node_fs69 = require("fs");
35027
+ var import_node_path67 = require("path");
34316
35028
  init_paths();
34317
35029
  init_home_board();
34318
35030
  var FILE = "home-board-pins.json";
34319
35031
  var VERSION = 1;
34320
35032
  function pinsFile() {
34321
- return (0, import_node_path66.join)(appDataDir(), FILE);
35033
+ return (0, import_node_path67.join)(appDataDir(), FILE);
34322
35034
  }
34323
35035
  function readDisk() {
34324
35036
  const path = pinsFile();
34325
- if (!(0, import_node_fs68.existsSync)(path)) return [];
35037
+ if (!(0, import_node_fs69.existsSync)(path)) return [];
34326
35038
  try {
34327
- const raw = JSON.parse((0, import_node_fs68.readFileSync)(path, "utf8"));
35039
+ const raw = JSON.parse((0, import_node_fs69.readFileSync)(path, "utf8"));
34328
35040
  if (raw?.version !== VERSION || !Array.isArray(raw.items)) return [];
34329
35041
  return raw.items.filter((item) => item?.id && item.kind && item.ref);
34330
35042
  } catch {
@@ -34332,8 +35044,8 @@ function readDisk() {
34332
35044
  }
34333
35045
  }
34334
35046
  function writeDisk(items) {
34335
- (0, import_node_fs68.mkdirSync)(appDataDir(), { recursive: true });
34336
- (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");
34337
35049
  }
34338
35050
  function listBoardPins() {
34339
35051
  return readDisk();
@@ -34354,7 +35066,7 @@ function homeBoardWorkspaceKey(workspaces) {
34354
35066
  return workspaces.map((w) => w.path).filter(Boolean).sort().join("\n");
34355
35067
  }
34356
35068
  function cacheFile() {
34357
- return (0, import_node_path67.join)(appDataDir(), "home-board-cache.json");
35069
+ return (0, import_node_path68.join)(appDataDir(), "home-board-cache.json");
34358
35070
  }
34359
35071
  function emptyInputs() {
34360
35072
  return {
@@ -34380,9 +35092,9 @@ function shouldCacheHomeBoardInputs(inputs) {
34380
35092
  }
34381
35093
  function readDiskCache() {
34382
35094
  const path = cacheFile();
34383
- if (!(0, import_node_fs69.existsSync)(path)) return null;
35095
+ if (!(0, import_node_fs70.existsSync)(path)) return null;
34384
35096
  try {
34385
- const raw = JSON.parse((0, import_node_fs69.readFileSync)(path, "utf8"));
35097
+ const raw = JSON.parse((0, import_node_fs70.readFileSync)(path, "utf8"));
34386
35098
  if (raw?.version !== CACHE_VERSION || typeof raw.fetchedAt !== "number") {
34387
35099
  return null;
34388
35100
  }
@@ -34396,8 +35108,8 @@ function readDiskCache() {
34396
35108
  }
34397
35109
  function writeDiskCache(entry) {
34398
35110
  try {
34399
- (0, import_node_fs69.mkdirSync)(appDataDir(), { recursive: true });
34400
- (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");
34401
35113
  } catch {
34402
35114
  }
34403
35115
  }
@@ -34621,13 +35333,13 @@ async function startMcpServer() {
34621
35333
  if (!worktreeProfile) {
34622
35334
  server.tool(
34623
35335
  "list_projects",
34624
- "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.",
34625
35337
  {},
34626
35338
  async () => {
34627
35339
  const workspaces = orch.listWorkspaces();
34628
35340
  const lines = await Promise.all(
34629
35341
  workspaces.map(async (w) => {
34630
- const slug = await resolveGithubRepoSlug(w.path).catch(() => null);
35342
+ const tag = await resolveProjectRemoteTag(w.path).catch(() => null);
34631
35343
  let profile;
34632
35344
  try {
34633
35345
  profile = resolveViewerProfileForRepo(w.path);
@@ -34635,7 +35347,7 @@ async function startMcpServer() {
34635
35347
  profile = resolveViewerProfile();
34636
35348
  }
34637
35349
  const suffix = formatWorkspaceProfileSuffix(profile);
34638
- 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}`;
34639
35351
  })
34640
35352
  );
34641
35353
  return {
@@ -34652,7 +35364,7 @@ async function startMcpServer() {
34652
35364
  async () => {
34653
35365
  const threads = orch.getThreads();
34654
35366
  const lines = threads.map((t) => {
34655
- 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;
34656
35368
  const live = orch.threadLooksLive(t) ? readTurnLive(t.id) : null;
34657
35369
  const parent = t.parentThreadId ? ` parent:${t.parentThreadId.slice(0, 8)}` : "";
34658
35370
  const preview = lastMessagePreview(t.messages, 80);
@@ -35482,7 +36194,7 @@ async function startMcpServer() {
35482
36194
  error: true,
35483
36195
  action,
35484
36196
  lastError: message,
35485
- 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
35486
36198
  },
35487
36199
  true
35488
36200
  );
@@ -35792,7 +36504,7 @@ async function startMcpServer() {
35792
36504
  );
35793
36505
  server.tool(
35794
36506
  "list_issues",
35795
- "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.",
35796
36508
  {
35797
36509
  repoPath: import_zod11.z.string(),
35798
36510
  query: import_zod11.z.string().optional().describe("Search title, identifier, or description"),