@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
package/dist/index.cjs CHANGED
@@ -5083,6 +5083,12 @@ function workspaceNameLabel(thread) {
5083
5083
  if (repo && worktreeNameFromPath(repo).toLowerCase() === dir.toLowerCase()) return null;
5084
5084
  return agentNicknameFromWorktreePath(path2) ?? dir;
5085
5085
  }
5086
+ function workspaceRowLabel(threads) {
5087
+ if (threads.length === 0) return { label: "Worktree" };
5088
+ const label = worktreeAnchorLabel(threads).title;
5089
+ const named = workspaceNameLabel(threads[0]);
5090
+ return named && named !== label ? { label, workspace: named } : { label };
5091
+ }
5086
5092
  function worktreeIdentityTitles(thread, parentTitle) {
5087
5093
  const out = [];
5088
5094
  const push2 = (value) => {
@@ -9339,6 +9345,7 @@ var run_exports = {};
9339
9345
  __export(run_exports, {
9340
9346
  gh: () => gh,
9341
9347
  git: () => git,
9348
+ originCli: () => originCli,
9342
9349
  resolveGhAuthToken: () => resolveGhAuthToken,
9343
9350
  resolveGitDirsForLockRecovery: () => resolveGitDirsForLockRecovery,
9344
9351
  run: () => run
@@ -9419,6 +9426,17 @@ ${result.stderr}`
9419
9426
  }
9420
9427
  return result;
9421
9428
  }
9429
+ async function originCli(args, cwd, opts) {
9430
+ return run("origin", args, {
9431
+ cwd,
9432
+ reject: opts?.reject,
9433
+ timeoutMs: opts?.timeoutMs,
9434
+ env: {
9435
+ GIT_TERMINAL_PROMPT: "0",
9436
+ NO_COLOR: "1"
9437
+ }
9438
+ });
9439
+ }
9422
9440
  async function gh(args, cwd, opts) {
9423
9441
  return run("gh", args, {
9424
9442
  cwd,
@@ -9629,9 +9647,9 @@ var init_worktree_exclude = __esm({
9629
9647
  });
9630
9648
 
9631
9649
  // src/git/git-auth-mode.ts
9632
- function githubHttpsInsteadOfEntries(remoteUrl) {
9650
+ function githubHttpsInsteadOfEntries(remoteUrl2) {
9633
9651
  const entries = [...HTTPS_REWRITE];
9634
- const url = remoteUrl?.trim() ?? "";
9652
+ const url = remoteUrl2?.trim() ?? "";
9635
9653
  const scpAlias = url.match(/^(git@github\.com-[^:]+):/i);
9636
9654
  if (scpAlias?.[1]) {
9637
9655
  entries.push({
@@ -9841,6 +9859,58 @@ var init_git_auth_mode = __esm({
9841
9859
  }
9842
9860
  });
9843
9861
 
9862
+ // src/git/remote-forge.ts
9863
+ function formatRemoteTag(remote) {
9864
+ const name = remote.forge === "other" ? remote.host : remote.forge;
9865
+ return `${name}:${remote.slug}`;
9866
+ }
9867
+ function forgeForHost(host) {
9868
+ const h = host.toLowerCase();
9869
+ if (h === "github.com" || h.startsWith("github.com-")) return "github";
9870
+ if (h === "origin.cursor.com") return "cursor";
9871
+ if (h === "entire.io" || h.endsWith(".entire.io") || h === "entire.com" || h.endsWith(".entire.com")) {
9872
+ return "entire";
9873
+ }
9874
+ return "other";
9875
+ }
9876
+ function repoFromParts(host, owner, name) {
9877
+ if (!host || !owner || !name || owner.includes("@")) return null;
9878
+ const normalized = host.toLowerCase();
9879
+ return {
9880
+ forge: forgeForHost(normalized),
9881
+ host: normalized,
9882
+ slug: `${owner}/${name}`
9883
+ };
9884
+ }
9885
+ function parseRemoteRepo(url) {
9886
+ const trimmed = url.trim().replace(/\.git$/i, "");
9887
+ if (!trimmed) return null;
9888
+ if (trimmed.toLowerCase().startsWith("entire://")) {
9889
+ const parts = trimmed.slice("entire://".length).replace(/^\/+/, "").split("/").filter(Boolean);
9890
+ if (parts.length === 2) return { forge: "entire", host: "entire.io", slug: `${parts[0]}/${parts[1]}` };
9891
+ if (parts.length >= 3) {
9892
+ return repoFromParts(parts[0], parts[1], parts[2]);
9893
+ }
9894
+ return null;
9895
+ }
9896
+ const ssh = trimmed.match(/^git@([^:]+):([^/]+)\/([^/]+)$/i);
9897
+ if (ssh?.[1] && ssh[2] && ssh[3]) return repoFromParts(ssh[1], ssh[2], ssh[3]);
9898
+ try {
9899
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
9900
+ const parsed = new URL(withScheme);
9901
+ const parts = parsed.pathname.split("/").filter(Boolean);
9902
+ if (parts.length < 2) return null;
9903
+ return repoFromParts(parsed.hostname, parts[parts.length - 2], parts[parts.length - 1]);
9904
+ } catch {
9905
+ return null;
9906
+ }
9907
+ }
9908
+ var init_remote_forge = __esm({
9909
+ "src/git/remote-forge.ts"() {
9910
+ "use strict";
9911
+ }
9912
+ });
9913
+
9844
9914
  // src/git/list-prs.ts
9845
9915
  function normalizePrListState(state) {
9846
9916
  const key = (state ?? "").trim().toLowerCase();
@@ -10209,6 +10279,345 @@ var init_list_prs = __esm({
10209
10279
  }
10210
10280
  });
10211
10281
 
10282
+ // src/git/cursor-pr.ts
10283
+ function repoArgs(slug) {
10284
+ return ["-R", slug];
10285
+ }
10286
+ function cursorHeadBranch(head, slug) {
10287
+ const branch = head.trim().replace(/^refs\/heads\//, "");
10288
+ const owner = slug.split("/")[0];
10289
+ if (owner && branch.startsWith(`${owner}:`)) return branch.slice(owner.length + 1);
10290
+ return branch;
10291
+ }
10292
+ function cursorMergeFields(value) {
10293
+ if (typeof value === "string" && value.trim()) {
10294
+ const upper = value.trim().toUpperCase();
10295
+ return { mergeable: upper, mergeStateStatus: upper };
10296
+ }
10297
+ if (value && typeof value === "object") {
10298
+ const row2 = value;
10299
+ const conflictState = String(row2.mergeConflictState ?? "").trim().toUpperCase();
10300
+ if ("hasMergeConflicts" in row2 || conflictState) {
10301
+ if (conflictState.includes("PENDING")) {
10302
+ return { mergeable: "UNKNOWN", mergeStateStatus: "UNKNOWN" };
10303
+ }
10304
+ const conflicting = row2.hasMergeConflicts === true || conflictState.includes("CONFLICT") && !conflictState.includes("CLEAN");
10305
+ if (conflicting) return { mergeable: "CONFLICTING", mergeStateStatus: "DIRTY" };
10306
+ if (row2.hasMergeConflicts === false || conflictState.includes("CLEAN")) {
10307
+ return { mergeable: "MERGEABLE", mergeStateStatus: "CLEAN" };
10308
+ }
10309
+ }
10310
+ const verdict = String(row2.verdict ?? row2.verdictKind ?? "").trim().toUpperCase();
10311
+ if (verdict === "BLOCKED") return { mergeable: null, mergeStateStatus: "BLOCKED" };
10312
+ if (verdict === "MERGEABLE") return { mergeable: "MERGEABLE", mergeStateStatus: "CLEAN" };
10313
+ const status = String(row2.status ?? row2.state ?? row2.mergeable ?? "").trim().toUpperCase();
10314
+ return { mergeable: status || null, mergeStateStatus: status || null };
10315
+ }
10316
+ return { mergeable: null, mergeStateStatus: null };
10317
+ }
10318
+ function cursorReviewDecision(value) {
10319
+ const raw = typeof value === "string" ? value.trim().toUpperCase().replace(/[\s-]+/g, "_") : "";
10320
+ if (!raw) return null;
10321
+ if (raw === "REQUIRED" || raw === "REVIEW_REQUIRED") return "REVIEW_REQUIRED";
10322
+ if (raw === "CHANGES_REQUESTED") return "CHANGES_REQUESTED";
10323
+ if (raw === "APPROVED") return "APPROVED";
10324
+ return raw;
10325
+ }
10326
+ function cursorViewToPrMeta(view) {
10327
+ const merge = cursorMergeFields(view.mergeability ?? view.mergeable);
10328
+ const author = view.author ?? {};
10329
+ return {
10330
+ number: Number(view.number),
10331
+ title: String(view.title ?? ""),
10332
+ url: String(view.url ?? ""),
10333
+ state: String(view.state ?? ""),
10334
+ isDraft: Boolean(view.isDraft),
10335
+ reviewDecision: cursorReviewDecision(view.reviewDecision),
10336
+ baseRefName: String(view.baseRefName ?? ""),
10337
+ headRefName: String(view.headRefName ?? ""),
10338
+ isInMergeQueue: false,
10339
+ mergeable: merge.mergeable,
10340
+ mergeStateStatus: merge.mergeStateStatus,
10341
+ authorLogin: author.login?.trim() || null,
10342
+ reviewerLogins: [],
10343
+ labels: labelNames(view.labels)
10344
+ };
10345
+ }
10346
+ function loginOf(author) {
10347
+ if (!author || typeof author !== "object") return "";
10348
+ const row2 = author;
10349
+ for (const key of ["login", "handle", "email", "displayName", "name", "id"]) {
10350
+ const value = row2[key];
10351
+ if (typeof value === "string" && value.trim()) return value.trim();
10352
+ }
10353
+ return "";
10354
+ }
10355
+ function upperState(value, isDraft) {
10356
+ const raw = typeof value === "string" ? value.trim().toUpperCase() : "";
10357
+ if (raw === "DRAFT") return "OPEN";
10358
+ if (raw === "OPEN" || raw === "CLOSED" || raw === "MERGED") return raw;
10359
+ return isDraft ? "OPEN" : raw;
10360
+ }
10361
+ function normalizeOriginPr(raw) {
10362
+ const isDraft = raw.isDraft === true || String(raw.status ?? raw.state ?? "").toLowerCase() === "draft";
10363
+ const login = loginOf(raw.author);
10364
+ const author = raw.author && typeof raw.author === "object" ? { ...raw.author, login: login || "unknown" } : login ? { login } : { login: "unknown" };
10365
+ return {
10366
+ ...raw,
10367
+ isDraft,
10368
+ state: upperState(raw.state ?? raw.status, isDraft),
10369
+ author,
10370
+ body: raw.body ?? raw.description ?? "",
10371
+ isCrossRepository: false,
10372
+ reviewDecision: cursorReviewDecision(raw.reviewDecision),
10373
+ reviewRequests: originReviewRequests(raw.reviewRequests)
10374
+ };
10375
+ }
10376
+ function originReviewRequests(raw) {
10377
+ if (!Array.isArray(raw)) return raw;
10378
+ return raw.map(originReviewer).filter((row2) => row2 != null);
10379
+ }
10380
+ function originReviewer(raw) {
10381
+ if (typeof raw === "string" && raw.trim()) return { login: raw.trim() };
10382
+ if (!raw || typeof raw !== "object") return null;
10383
+ const row2 = raw;
10384
+ if (typeof row2.login === "string" && row2.login.trim()) return row2;
10385
+ const kind = String(row2.kind ?? row2.__typename ?? "").toLowerCase();
10386
+ const slug = typeof row2.slug === "string" ? row2.slug.trim() : "";
10387
+ const name = typeof row2.name === "string" ? row2.name.trim() : "";
10388
+ const id = typeof row2.id === "string" ? row2.id.trim() : "";
10389
+ if (kind.includes("team") || kind.includes("group") || slug.includes("/") && !row2.login) {
10390
+ return { __typename: "Team", slug: slug || name, name };
10391
+ }
10392
+ if (kind.includes("service") || kind.includes("bot")) {
10393
+ const login2 = name || id || "cursor-bot";
10394
+ return { __typename: "Bot", login: login2.toLowerCase().endsWith("[bot]") ? login2 : `${login2}[bot]` };
10395
+ }
10396
+ const login = name || id;
10397
+ return login ? { login } : null;
10398
+ }
10399
+ function originChecksToRuns(stdout) {
10400
+ let parsed;
10401
+ try {
10402
+ parsed = JSON.parse(stdout);
10403
+ } catch {
10404
+ return [];
10405
+ }
10406
+ const rows = Array.isArray(parsed) ? parsed : [];
10407
+ return rows.map((row2) => {
10408
+ const item = row2 ?? {};
10409
+ const conclusion = typeof item.conclusion === "string" ? item.conclusion.toLowerCase() : "";
10410
+ const status = typeof item.status === "string" ? item.status.toLowerCase() : "";
10411
+ const bucket = conclusion === "success" || conclusion === "neutral" ? "pass" : conclusion === "failure" || conclusion === "timed_out" || conclusion === "action_required" ? "fail" : conclusion === "cancelled" ? "cancel" : conclusion === "skipped" ? "skipping" : "pending";
10412
+ const state = conclusion || status || "pending";
10413
+ const link = typeof item.link === "string" ? item.link : typeof item.detailsUrl === "string" ? item.detailsUrl : null;
10414
+ const description = typeof item.summary === "string" ? item.summary : typeof item.description === "string" ? item.description : null;
10415
+ return {
10416
+ name: String(item.name ?? "check"),
10417
+ state,
10418
+ bucket,
10419
+ startedAt: typeof item.startedAt === "string" ? item.startedAt : null,
10420
+ completedAt: typeof item.completedAt === "string" ? item.completedAt : null,
10421
+ link,
10422
+ description,
10423
+ workflow: typeof item.group === "string" ? item.group : null,
10424
+ kind: "ci"
10425
+ };
10426
+ });
10427
+ }
10428
+ function listedHasLabels(pr, labels) {
10429
+ if (labels.length === 0) return true;
10430
+ const have = new Set((pr.labels ?? []).map((name) => name.toLowerCase()));
10431
+ return labels.every((label) => have.has(label.toLowerCase()));
10432
+ }
10433
+ async function listCursorPrs(repoPath, slug, opts, head) {
10434
+ const resolved = resolveListPrsOptions(opts);
10435
+ const limit = Math.max(1, Math.min(250, resolved.limit ?? 200));
10436
+ const fetchLimit = prListFetchLimit(limit, resolved.reviewer);
10437
+ const useSearch = resolved.labels.length > 0 || Boolean(resolved.reviewer) || Boolean(resolved.query);
10438
+ const state = resolved.draft === true ? "draft" : resolved.state;
10439
+ const args = [
10440
+ "pr",
10441
+ "list",
10442
+ ...repoArgs(slug),
10443
+ "--state",
10444
+ state,
10445
+ "--limit",
10446
+ String(fetchLimit),
10447
+ "--json",
10448
+ useSearch ? SEARCH_LIST_FIELDS : PLAIN_LIST_FIELDS
10449
+ ];
10450
+ if (head?.trim()) args.push("--head", head.trim());
10451
+ if (useSearch) {
10452
+ for (const label of resolved.labels) args.push("--label", label);
10453
+ if (resolved.query) args.push("--search", resolved.query);
10454
+ else if (resolved.labels.length === 0) {
10455
+ const token = state === "all" ? "is:open OR is:closed OR is:merged" : `is:${state}`;
10456
+ args.push("--search", token);
10457
+ }
10458
+ }
10459
+ const { stdout, exitCode } = await originCli(args, repoPath, { reject: false, timeoutMs: 2e4 });
10460
+ if (exitCode !== 0 || !stdout.trim()) return [];
10461
+ let parsed;
10462
+ try {
10463
+ parsed = JSON.parse(stdout);
10464
+ } catch {
10465
+ return [];
10466
+ }
10467
+ if (!Array.isArray(parsed)) return [];
10468
+ const normalized = parsed.filter((row2) => row2 && typeof row2 === "object").map((row2) => normalizeOriginPr(row2));
10469
+ const listed = applyPrListResponse(JSON.stringify(normalized), { ...opts, limit: void 0 });
10470
+ return listed.filter((pr) => listedHasLabels(pr, resolved.labels)).slice(0, limit);
10471
+ }
10472
+ async function viewCursorPr(repoPath, slug, selector, fields) {
10473
+ const { stdout, exitCode } = await originCli(
10474
+ ["pr", "view", selector, ...repoArgs(slug), "--json", fields],
10475
+ repoPath,
10476
+ { reject: false, timeoutMs: 2e4 }
10477
+ );
10478
+ if (exitCode !== 0 || !stdout.trim()) return null;
10479
+ try {
10480
+ const parsed = JSON.parse(stdout);
10481
+ return normalizeOriginPr(parsed);
10482
+ } catch {
10483
+ return null;
10484
+ }
10485
+ }
10486
+ async function createOrUpdateCursorPr(repoPath, slug, opts) {
10487
+ const branch = cursorHeadBranch(opts.head, slug);
10488
+ const existing = await originCli(
10489
+ ["pr", "view", branch, ...repoArgs(slug), "--json", "url"],
10490
+ repoPath,
10491
+ { reject: false, timeoutMs: 2e4 }
10492
+ );
10493
+ if (existing.exitCode === 0 && existing.stdout.trim()) {
10494
+ let url2 = "";
10495
+ try {
10496
+ url2 = String(JSON.parse(existing.stdout).url ?? "");
10497
+ } catch {
10498
+ url2 = "";
10499
+ }
10500
+ if (url2) {
10501
+ await originCli(
10502
+ ["pr", "edit", branch, ...repoArgs(slug), "--title", opts.title, "-F", opts.bodyFile],
10503
+ repoPath,
10504
+ { reject: false, timeoutMs: 2e4 }
10505
+ );
10506
+ return url2;
10507
+ }
10508
+ }
10509
+ const args = [
10510
+ "pr",
10511
+ "create",
10512
+ ...repoArgs(slug),
10513
+ "--title",
10514
+ opts.title,
10515
+ "-F",
10516
+ opts.bodyFile,
10517
+ "--base",
10518
+ opts.base,
10519
+ "--head",
10520
+ branch,
10521
+ "--push"
10522
+ ];
10523
+ if (opts.draft) args.push("--draft");
10524
+ const created = await originCli(args, repoPath, { reject: false, timeoutMs: 6e4 });
10525
+ if (created.exitCode !== 0) {
10526
+ throw new Error(created.stderr.trim() || created.stdout.trim() || "origin pr create failed");
10527
+ }
10528
+ const printed = created.stdout.trim().split("\n").find((line) => line.startsWith("http"));
10529
+ if (printed) return printed;
10530
+ const viewed = await viewCursorPr(repoPath, slug, branch, "url");
10531
+ const url = typeof viewed?.url === "string" ? viewed.url.trim() : "";
10532
+ return url || created.stdout.trim();
10533
+ }
10534
+ async function readyCursorPr(repoPath, slug, selector) {
10535
+ const ready = await originCli(["pr", "ready", selector, ...repoArgs(slug)], repoPath, {
10536
+ reject: false,
10537
+ timeoutMs: 2e4
10538
+ });
10539
+ if (ready.exitCode !== 0) {
10540
+ throw new Error(ready.stderr.trim() || ready.stdout.trim() || "Could not mark the Cursor pull request ready");
10541
+ }
10542
+ }
10543
+ async function mergeCursorPr(repoPath, slug, selector, method) {
10544
+ const args = ["pr", "merge", selector, ...repoArgs(slug)];
10545
+ if (method === "merge") args.push("--merge");
10546
+ else if (method === "squash") args.push("--squash");
10547
+ const merged = await originCli(args, repoPath, {
10548
+ reject: false,
10549
+ timeoutMs: 6e4
10550
+ });
10551
+ if (merged.exitCode !== 0) {
10552
+ throw new Error(merged.stderr.trim() || merged.stdout.trim() || "origin pr merge failed");
10553
+ }
10554
+ }
10555
+ async function fetchCursorPrHead(repoPath, slug, number, localBranch) {
10556
+ const view = await viewCursorPr(repoPath, slug, String(number), "headRefName");
10557
+ const head = typeof view?.headRefName === "string" ? view.headRefName.trim() : "";
10558
+ if (!head) {
10559
+ throw new Error(`Could not resolve the head branch for Cursor pull request #${number}`);
10560
+ }
10561
+ const result = await git(["fetch", "origin", `${head}:${localBranch}`], repoPath, { reject: false });
10562
+ if (result.exitCode !== 0) {
10563
+ throw new Error((result.stderr || result.stdout).trim() || `git fetch origin ${head} failed`);
10564
+ }
10565
+ }
10566
+ async function cursorPrChecks(repoPath, slug, selector) {
10567
+ const { stdout, exitCode, stderr } = await originCli(
10568
+ ["pr", "checks", selector, ...repoArgs(slug), "--json", "name,status,conclusion,detailsUrl,startedAt,completedAt,summary,group,link"],
10569
+ repoPath,
10570
+ { reject: false, timeoutMs: 2e4 }
10571
+ );
10572
+ const err = stderr.trim();
10573
+ let ci;
10574
+ if (!stdout.trim()) {
10575
+ if (/no pull request|not found|no change/i.test(err)) return null;
10576
+ if (exitCode !== 0 && err) throw new Error(err);
10577
+ ci = [];
10578
+ } else {
10579
+ ci = originChecksToRuns(stdout);
10580
+ }
10581
+ const view = await viewCursorPr(
10582
+ repoPath,
10583
+ slug,
10584
+ selector,
10585
+ "url,baseRefName,reviewDecision,mergeability"
10586
+ );
10587
+ if (!view) return ci;
10588
+ return cursorChecksWithGate(ci, cursorViewToPrMeta(view));
10589
+ }
10590
+ function cursorChecksWithGate(ci, meta) {
10591
+ const gate = {
10592
+ mergeable: meta.mergeable,
10593
+ mergeStateStatus: meta.mergeStateStatus,
10594
+ reviewDecision: meta.reviewDecision,
10595
+ baseRefName: meta.baseRefName,
10596
+ url: meta.url,
10597
+ isInMergeQueue: meta.isInMergeQueue
10598
+ };
10599
+ const review = (gate.reviewDecision ?? "").toUpperCase();
10600
+ const hasReviewRow = review === "CHANGES_REQUESTED" || review === "REVIEW_REQUIRED";
10601
+ const gateRows = buildMergeGateChecks(gate, {
10602
+ suppressGenericBlocked: ci.some((check) => check.bucket === "fail") || hasReviewRow
10603
+ });
10604
+ return [...gateRows, ...ci];
10605
+ }
10606
+ var PLAIN_LIST_FIELDS, SEARCH_LIST_FIELDS, VIEW_FIELDS, CURSOR_PR_VIEW_FIELDS;
10607
+ var init_cursor_pr = __esm({
10608
+ "src/git/cursor-pr.ts"() {
10609
+ "use strict";
10610
+ init_run();
10611
+ init_remote_forge();
10612
+ init_list_prs();
10613
+ init_pr_gates();
10614
+ PLAIN_LIST_FIELDS = "number,title,headRefName,url,author,state,isDraft,reviewDecision";
10615
+ SEARCH_LIST_FIELDS = `${PLAIN_LIST_FIELDS},labels,reviewRequests`;
10616
+ VIEW_FIELDS = "number,title,body,url,state,isDraft,reviewDecision,author,createdAt,baseRefName,headRefName,additions,deletions,changedFiles,commits,comments,reviews,labels,mergeability";
10617
+ CURSOR_PR_VIEW_FIELDS = VIEW_FIELDS;
10618
+ }
10619
+ });
10620
+
10212
10621
  // src/util/enqueue-by-key.ts
10213
10622
  function enqueueByKey(key, fn) {
10214
10623
  const prev = tails.get(key) ?? Promise.resolve();
@@ -10540,11 +10949,11 @@ __export(worktree_exports, {
10540
10949
  connectedPrSelectors: () => connectedPrSelectors,
10541
10950
  countUnpushedVsOrigin: () => countUnpushedVsOrigin,
10542
10951
  createExistingBranchWorktree: () => createExistingBranchWorktree,
10543
- createOrUpdatePr: () => createOrUpdatePr,
10544
10952
  createThreadWorktree: () => createThreadWorktree,
10545
10953
  currentBranch: () => currentBranch,
10546
10954
  detectLocalMergeConflicts: () => detectLocalMergeConflicts,
10547
10955
  ensureGhPreferOrigin: () => ensureGhPreferOrigin,
10956
+ entireUsesTrails: () => entireUsesTrails,
10548
10957
  fastForwardMainCheckoutIfSafe: () => fastForwardMainCheckoutIfSafe,
10549
10958
  fetchOriginForWorktree: () => fetchOriginForWorktree,
10550
10959
  fetchPrHead: () => fetchPrHead,
@@ -10554,7 +10963,6 @@ __export(worktree_exports, {
10554
10963
  getPrForHeadBranch: () => getPrForHeadBranch,
10555
10964
  getPrMeta: () => getPrMeta,
10556
10965
  ghHeadRef: () => ghHeadRef,
10557
- ghPrCreateAssigneeArgs: () => ghPrCreateAssigneeArgs,
10558
10966
  ghRepoSelectArgs: () => ghRepoSelectArgs,
10559
10967
  githubAgentGitEnv: () => githubAgentGitEnv,
10560
10968
  isDefaultishSourceRef: () => isDefaultishSourceRef,
@@ -10578,8 +10986,10 @@ __export(worktree_exports, {
10578
10986
  resolveDefaultBranch: () => resolveDefaultBranch,
10579
10987
  resolveDiffBaseRef: () => resolveDiffBaseRef,
10580
10988
  resolveGithubRepoSlug: () => resolveGithubRepoSlug,
10989
+ resolveOriginRemote: () => resolveOriginRemote,
10581
10990
  resolvePrSelector: () => resolvePrSelector,
10582
10991
  resolvePrSelectors: () => resolvePrSelectors,
10992
+ resolveProjectRemoteTag: () => resolveProjectRemoteTag,
10583
10993
  resolveRepoRoot: () => resolveRepoRoot,
10584
10994
  resolveWorktreeStartPoint: () => resolveWorktreeStartPoint,
10585
10995
  slugify: () => slugify,
@@ -10593,31 +11003,6 @@ __export(worktree_exports, {
10593
11003
  worktreeNameFromPath: () => worktreeNameFromPath,
10594
11004
  worktreeSlugForTicket: () => worktreeSlugForTicket
10595
11005
  });
10596
- async function lookupGithubGraphqlReset(cwd) {
10597
- const result = await gh(["api", "rate_limit"], cwd, { reject: false });
10598
- if (result.exitCode !== 0 || !result.stdout.trim()) return void 0;
10599
- try {
10600
- const data = JSON.parse(result.stdout);
10601
- const reset = data.resources?.graphql?.reset;
10602
- return typeof reset === "number" ? reset : void 0;
10603
- } catch {
10604
- return void 0;
10605
- }
10606
- }
10607
- async function formatPrCreateFailure(raw, cwd, ctx) {
10608
- if (!isGhRateLimitError(raw)) {
10609
- return formatGhLandError(raw, {
10610
- targetedRepo: ctx?.slug,
10611
- headRef: ctx?.head
10612
- });
10613
- }
10614
- const resetAt = await lookupGithubGraphqlReset(cwd);
10615
- return formatGhLandError(raw, {
10616
- resetAt,
10617
- targetedRepo: ctx?.slug,
10618
- headRef: ctx?.head
10619
- });
10620
- }
10621
11006
  function slugify(input) {
10622
11007
  return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
10623
11008
  }
@@ -10640,25 +11025,39 @@ function canonicalizeRepoPath(path2) {
10640
11025
  }
10641
11026
  }
10642
11027
  function parseGithubSlugFromRemoteUrl(url) {
10643
- const trimmed = url.trim().replace(/\.git$/i, "");
10644
- if (!trimmed) return null;
10645
- const github = trimmed.match(/github\.com[:/]([^/]+)\/([^/]+)$/i);
10646
- if (github?.[1] && github[2]) return `${github[1]}/${github[2]}`;
10647
- const sshAlias = trimmed.match(/^git@[^:]+:([^/]+)\/([^/]+)$/i);
10648
- if (sshAlias?.[1] && sshAlias[2]) return `${sshAlias[1]}/${sshAlias[2]}`;
10649
- return null;
11028
+ const parsed = parseRemoteRepo(url);
11029
+ return parsed?.forge === "github" ? parsed.slug : null;
10650
11030
  }
10651
- async function slugFromGitRemote(repoPath, remote) {
11031
+ async function remoteUrl(repoPath, remote) {
10652
11032
  const result = await git(["remote", "get-url", remote], repoPath, {
10653
11033
  reject: false,
10654
11034
  timeoutMs: 5e3
10655
11035
  });
10656
11036
  if (result.exitCode !== 0 || !result.stdout.trim()) return null;
10657
- return parseGithubSlugFromRemoteUrl(result.stdout);
11037
+ return result.stdout.trim();
11038
+ }
11039
+ async function slugFromGitRemote(repoPath, remote) {
11040
+ const url = await remoteUrl(repoPath, remote);
11041
+ if (!url) return null;
11042
+ return parseGithubSlugFromRemoteUrl(url);
11043
+ }
11044
+ async function resolveOriginRemote(repoPath) {
11045
+ const url = await remoteUrl(repoPath, "origin");
11046
+ if (!url) return null;
11047
+ return parseRemoteRepo(url);
11048
+ }
11049
+ async function resolveProjectRemoteTag(repoPath) {
11050
+ const origin = await resolveOriginRemote(repoPath);
11051
+ if (origin) return formatRemoteTag(origin);
11052
+ const slug = await resolveGithubRepoSlug(repoPath);
11053
+ return slug ? `github:${slug}` : null;
10658
11054
  }
10659
11055
  async function resolveGithubRepoSlug(repoPath) {
10660
- const fromOrigin = await slugFromGitRemote(repoPath, "origin");
10661
- if (fromOrigin) return fromOrigin;
11056
+ const originUrl = await remoteUrl(repoPath, "origin");
11057
+ if (originUrl) {
11058
+ const parsed = parseRemoteRepo(originUrl);
11059
+ return parsed?.forge === "github" ? parsed.slug : null;
11060
+ }
10662
11061
  const hasAlt = Boolean(await slugFromGitRemote(repoPath, "upstream")) || Boolean(await slugFromGitRemote(repoPath, "github"));
10663
11062
  if (!hasAlt) {
10664
11063
  const viaGh = await gh(
@@ -10704,6 +11103,8 @@ ${view.stderr}`;
10704
11103
  });
10705
11104
  }
10706
11105
  async function originGhRepoEnv(cwd, opts) {
11106
+ const origin = await resolveOriginRemote(cwd);
11107
+ if (origin?.forge === "cursor") return { ORIGIN_REPO: origin.slug };
10707
11108
  await ensureGhPreferOrigin(cwd);
10708
11109
  const slug = await resolveGithubRepoSlug(cwd);
10709
11110
  const mode = opts?.mode ?? getGithubGitAuthMode();
@@ -10818,7 +11219,16 @@ async function listBranches(repoPath, opts) {
10818
11219
  }
10819
11220
  return sorted.filter((b) => keep.has(b.name));
10820
11221
  }
11222
+ function entireUsesTrails() {
11223
+ return new Error(
11224
+ "This checkout is on Entire. Trails replace pull requests \u2014 use `entire trail` (Settings \u2192 Git \u2192 Entire), not `gh`."
11225
+ );
11226
+ }
10821
11227
  async function listPrs(repoPath, opts) {
11228
+ const origin = await resolveOriginRemote(repoPath);
11229
+ if (origin?.forge === "entire") return [];
11230
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
11231
+ if (cursorSlug) return listCursorPrs(repoPath, cursorSlug, opts);
10822
11232
  const slug = await resolveGithubRepoSlug(repoPath);
10823
11233
  const resolved = resolveListPrsOptions(opts);
10824
11234
  const limit = resolved.limit ?? 200;
@@ -10855,6 +11265,18 @@ function prInfoFromGhView(view) {
10855
11265
  };
10856
11266
  }
10857
11267
  async function getPr(repoPath, number) {
11268
+ const origin = await resolveOriginRemote(repoPath);
11269
+ if (origin?.forge === "entire") return null;
11270
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
11271
+ if (cursorSlug) {
11272
+ const view = await viewCursorPr(
11273
+ repoPath,
11274
+ cursorSlug,
11275
+ String(number),
11276
+ "number,title,headRefName,url,author,isDraft,state"
11277
+ );
11278
+ return view ? prInfoFromGhView(view) : null;
11279
+ }
10858
11280
  const slug = await resolveGithubRepoSlug(repoPath);
10859
11281
  const args = [
10860
11282
  "pr",
@@ -10916,6 +11338,13 @@ function connectedPrSelectors(thread, currentHeadPrUrl) {
10916
11338
  async function getPrForHeadBranch(repoPath, branch) {
10917
11339
  const head = normalizeGitBranchName(branch);
10918
11340
  if (!head || isDefaultishSourceRef(head)) return null;
11341
+ const origin = await resolveOriginRemote(repoPath);
11342
+ if (origin?.forge === "entire") return null;
11343
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
11344
+ if (cursorSlug) {
11345
+ const rows = await listCursorPrs(repoPath, cursorSlug, { state: "open", limit: 5 }, head);
11346
+ return rows[0] ?? null;
11347
+ }
10919
11348
  const slug = await resolveGithubRepoSlug(repoPath);
10920
11349
  const listHead = slug ? ghHeadRef(slug, head) : head;
10921
11350
  const listArgs = [
@@ -11080,6 +11509,10 @@ async function probeLocalMergeGate(cwd, gate) {
11080
11509
  return { gate, files: [] };
11081
11510
  }
11082
11511
  async function getPrChecks(cwd, selector) {
11512
+ const origin = await resolveOriginRemote(cwd);
11513
+ if (origin?.forge === "entire") return null;
11514
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
11515
+ if (cursorSlug) return cursorPrChecks(cwd, cursorSlug, selector);
11083
11516
  const slug = await resolveGithubRepoSlug(cwd);
11084
11517
  const args = [
11085
11518
  "pr",
@@ -11135,6 +11568,18 @@ async function getPrChecks(cwd, selector) {
11135
11568
  return [...gateRows, ...ciChecks];
11136
11569
  }
11137
11570
  async function getPrMeta(cwd, selector) {
11571
+ const origin = await resolveOriginRemote(cwd);
11572
+ if (origin?.forge === "entire") return null;
11573
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
11574
+ if (cursorSlug) {
11575
+ const view = await viewCursorPr(
11576
+ cwd,
11577
+ cursorSlug,
11578
+ selector,
11579
+ "number,title,url,state,isDraft,reviewDecision,baseRefName,headRefName,author,labels,mergeability"
11580
+ );
11581
+ return view ? cursorViewToPrMeta(view) : null;
11582
+ }
11138
11583
  const slug = await resolveGithubRepoSlug(cwd);
11139
11584
  const viewArgs = [
11140
11585
  "pr",
@@ -11263,6 +11708,13 @@ function parsePrDetailsView(view) {
11263
11708
  };
11264
11709
  }
11265
11710
  async function getPrDetails(cwd, selector) {
11711
+ const origin = await resolveOriginRemote(cwd);
11712
+ if (origin?.forge === "entire") return null;
11713
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
11714
+ if (cursorSlug) {
11715
+ const view2 = await viewCursorPr(cwd, cursorSlug, selector, CURSOR_PR_VIEW_FIELDS);
11716
+ return view2 ? parsePrDetailsView(view2) : null;
11717
+ }
11266
11718
  const slug = await resolveGithubRepoSlug(cwd);
11267
11719
  const viewArgs = ["pr", "view", selector, "--json", PR_DETAILS_JSON_FIELDS];
11268
11720
  if (slug) viewArgs.push("--repo", slug);
@@ -11282,6 +11734,13 @@ async function getPrDetails(cwd, selector) {
11282
11734
  return parsePrDetailsView(view);
11283
11735
  }
11284
11736
  async function fetchPrHead(repoPath, number, localBranch) {
11737
+ const origin = await resolveOriginRemote(repoPath);
11738
+ if (origin?.forge === "entire") throw entireUsesTrails();
11739
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
11740
+ if (cursorSlug) {
11741
+ await fetchCursorPrHead(repoPath, cursorSlug, number, localBranch);
11742
+ return;
11743
+ }
11285
11744
  const slug = await resolveGithubRepoSlug(repoPath);
11286
11745
  const refspec = `+pull/${number}/head:${localBranch}`;
11287
11746
  const errors = [];
@@ -11312,10 +11771,10 @@ async function fetchPrHead(repoPath, number, localBranch) {
11312
11771
  if (!fetched) fetched = await tryFetch(httpsRemote, { ghAuth: true });
11313
11772
  }
11314
11773
  const localOk = async () => {
11315
- const verify = await git(["rev-parse", "--verify", localBranch], repoPath, {
11774
+ const verify2 = await git(["rev-parse", "--verify", localBranch], repoPath, {
11316
11775
  reject: false
11317
11776
  });
11318
- return verify.exitCode === 0;
11777
+ return verify2.exitCode === 0;
11319
11778
  };
11320
11779
  if (!await localOk()) {
11321
11780
  const viewArgs = [
@@ -11424,10 +11883,10 @@ async function originRemoteUrl(cwd) {
11424
11883
  const result = await git(["remote", "get-url", "origin"], cwd, { reject: false });
11425
11884
  return result.exitCode === 0 ? result.stdout.trim() : "";
11426
11885
  }
11427
- function githubHttpsRetryEnv(remoteUrl) {
11886
+ function githubHttpsRetryEnv(remoteUrl2) {
11428
11887
  return {
11429
11888
  GIT_TERMINAL_PROMPT: "0",
11430
- ...githubAgentGitEnv(void 0, { remoteUrl })
11889
+ ...githubAgentGitEnv(void 0, { remoteUrl: remoteUrl2 })
11431
11890
  };
11432
11891
  }
11433
11892
  async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
@@ -11441,11 +11900,11 @@ async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
11441
11900
  }
11442
11901
  const token = await resolveGithubAgentToken(mode, repoPath);
11443
11902
  if (!token) return false;
11444
- const remoteUrl = await originRemoteUrl(repoPath);
11903
+ const remoteUrl2 = await originRemoteUrl(repoPath);
11445
11904
  const tryHttps = async (header) => git(args, repoPath, {
11446
11905
  reject: false,
11447
11906
  timeoutMs,
11448
- env: githubHttpsRetryEnv(remoteUrl),
11907
+ env: githubHttpsRetryEnv(remoteUrl2),
11449
11908
  config: {
11450
11909
  "http.extraHeader": header
11451
11910
  }
@@ -11794,10 +12253,10 @@ async function pushBranch(worktreePath, branchName) {
11794
12253
  sshErr || `git push origin ${branchName} failed` + (!token ? " (SSH remote and gh auth token unavailable \u2014 unlock ssh-agent or run: gh auth login)" : "")
11795
12254
  );
11796
12255
  }
11797
- const remoteUrl = await originRemoteUrl(worktreePath);
12256
+ const remoteUrl2 = await originRemoteUrl(worktreePath);
11798
12257
  const tryHttps = async (header) => git(["push", "-u", "origin", branchName], worktreePath, {
11799
12258
  reject: false,
11800
- env: githubHttpsRetryEnv(remoteUrl),
12259
+ env: githubHttpsRetryEnv(remoteUrl2),
11801
12260
  config: {
11802
12261
  "http.extraHeader": header
11803
12262
  }
@@ -11810,6 +12269,40 @@ async function pushBranch(worktreePath, branchName) {
11810
12269
  const httpsErr = (basicPush.stderr || bearer.stderr || bearer.stdout).trim();
11811
12270
  throw new Error(httpsErr || sshErr || `git push origin ${branchName} failed`);
11812
12271
  }
12272
+ async function markCursorPrReady(cwd, slug, selector) {
12273
+ const view = await viewCursorPr(cwd, slug, selector, "url,state,isDraft");
12274
+ if (!view) throw new Error("Could not load the Cursor pull request");
12275
+ const url = String(view.url ?? "");
12276
+ const state = String(view.state ?? "").toUpperCase();
12277
+ const isDraft = Boolean(view.isDraft);
12278
+ if (state === "MERGED" || state === "CLOSED") {
12279
+ return { url, state, isDraft: false };
12280
+ }
12281
+ if (isDraft) {
12282
+ if (await isDirty(cwd)) {
12283
+ throw new Error(
12284
+ "Commit and push local work before marking the pull request ready for review."
12285
+ );
12286
+ }
12287
+ const unpushed = await countUnpushedVsOrigin(cwd);
12288
+ if (unpushed > 0) {
12289
+ throw new Error(
12290
+ "Push this branch to origin before marking the pull request ready for review."
12291
+ );
12292
+ }
12293
+ await readyCursorPr(cwd, slug, selector);
12294
+ }
12295
+ return { url, state: state || "OPEN", isDraft: false };
12296
+ }
12297
+ async function mergeCursorPullRequest(cwd, slug, selector, method) {
12298
+ const view = await viewCursorPr(cwd, slug, selector, "url,state,isDraft");
12299
+ if (!view) throw new Error("Could not load the Cursor pull request");
12300
+ const url = String(view.url ?? "");
12301
+ if (String(view.state ?? "").toUpperCase() === "MERGED") return { url, state: "MERGED" };
12302
+ if (view.isDraft) await readyCursorPr(cwd, slug, selector);
12303
+ await mergeCursorPr(cwd, slug, selector, method);
12304
+ return { url, state: "MERGED" };
12305
+ }
11813
12306
  async function runPrReady(cwd, selector, slug) {
11814
12307
  const readyArgs = ["pr", "ready", selector];
11815
12308
  if (slug) readyArgs.push("--repo", slug);
@@ -11821,6 +12314,10 @@ async function runPrReady(cwd, selector, slug) {
11821
12314
  }
11822
12315
  }
11823
12316
  async function markPrReady(cwd, selector) {
12317
+ const origin = await resolveOriginRemote(cwd);
12318
+ if (origin?.forge === "entire") throw entireUsesTrails();
12319
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
12320
+ if (cursorSlug) return markCursorPrReady(cwd, cursorSlug, selector);
11824
12321
  const slug = await resolveGithubRepoSlug(cwd);
11825
12322
  const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft"];
11826
12323
  if (slug) viewArgs.push("--repo", slug);
@@ -11859,6 +12356,10 @@ async function markPrReady(cwd, selector) {
11859
12356
  return { url, state: state || "OPEN", isDraft: false };
11860
12357
  }
11861
12358
  async function mergePr(cwd, selector, opts) {
12359
+ const origin = await resolveOriginRemote(cwd);
12360
+ if (origin?.forge === "entire") throw entireUsesTrails();
12361
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
12362
+ if (cursorSlug) return mergeCursorPullRequest(cwd, cursorSlug, selector, opts?.method ?? "squash");
11862
12363
  const slug = await resolveGithubRepoSlug(cwd);
11863
12364
  const viewArgs = ["pr", "view", selector, "--json", "url,state,isDraft,number"];
11864
12365
  if (slug) viewArgs.push("--repo", slug);
@@ -11917,128 +12418,16 @@ async function mergePr(cwd, selector, opts) {
11917
12418
  }
11918
12419
  return { url, state: "MERGED" };
11919
12420
  }
11920
- function ghPrCreateAssigneeArgs(assignee) {
11921
- const raw = assignee?.trim();
11922
- if (raw && /^(none|unassigned)$/i.test(raw)) return [];
11923
- return ["--assignee", raw || "@me"];
11924
- }
11925
- async function createOrUpdatePr(worktreePath, opts) {
11926
- await ensureGhPreferOrigin(worktreePath);
11927
- const slug = await resolveGithubRepoSlug(worktreePath);
11928
- if (!slug) {
11929
- throw new Error(
11930
- "Could not resolve the origin GitHub repo (owner/name) for this worktree. Check that `git remote get-url origin` points at github.com."
11931
- );
11932
- }
11933
- const repoArgs = ghRepoSelectArgs(slug);
11934
- const headRef = ghHeadRef(slug, opts.head);
11935
- const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
11936
- const bodyText = clampGithubPrBody(opts.body ?? opts.title);
11937
- const bodyFile = (0, import_node_path27.join)(
11938
- (0, import_node_os8.tmpdir)(),
11939
- `sideboard-pr-body-${process.pid}-${(0, import_node_crypto10.randomBytes)(6).toString("hex")}.md`
11940
- );
11941
- (0, import_node_fs23.writeFileSync)(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
11942
- `, "utf8");
11943
- const bodyArgs = ["--body-file", bodyFile];
11944
- try {
11945
- return await createOrUpdatePrWithBodyFile(worktreePath, {
11946
- ...opts,
11947
- slug,
11948
- repoArgs,
11949
- headRef,
11950
- branchOnly,
11951
- bodyArgs
11952
- });
11953
- } finally {
11954
- try {
11955
- (0, import_node_fs23.unlinkSync)(bodyFile);
11956
- } catch {
11957
- }
11958
- }
11959
- }
11960
- async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
11961
- const { slug, repoArgs, headRef, branchOnly, bodyArgs } = opts;
11962
- const existing = await gh(
11963
- [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
11964
- worktreePath,
11965
- { reject: false }
11966
- );
11967
- if (existing.exitCode === 0 && existing.stdout.trim() && !opts.web) {
11968
- const url2 = existing.stdout.trim();
11969
- await gh(
11970
- [
11971
- ...repoArgs,
11972
- "pr",
11973
- "edit",
11974
- branchOnly,
11975
- "--title",
11976
- opts.title,
11977
- ...bodyArgs
11978
- ],
11979
- worktreePath,
11980
- { reject: false }
11981
- );
11982
- return url2;
11983
- }
11984
- if (opts.web) {
11985
- const args2 = [
11986
- ...repoArgs,
11987
- "pr",
11988
- "create",
11989
- "--web",
11990
- "--title",
11991
- opts.title,
11992
- ...bodyArgs,
11993
- "--base",
11994
- opts.base,
11995
- "--head",
11996
- headRef
11997
- ];
11998
- if (opts.draft) args2.push("--draft");
11999
- args2.push(...ghPrCreateAssigneeArgs(opts.assignee));
12000
- await gh(args2, worktreePath, { reject: false });
12001
- const again = await gh(
12002
- [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
12003
- worktreePath,
12004
- { reject: false }
12005
- );
12006
- return again.stdout.trim() || "";
12007
- }
12008
- const args = [
12009
- ...repoArgs,
12010
- "pr",
12011
- "create",
12012
- "--title",
12013
- opts.title,
12014
- ...bodyArgs,
12015
- "--base",
12016
- opts.base,
12017
- "--head",
12018
- headRef
12019
- ];
12020
- if (opts.draft) args.push("--draft");
12021
- args.push(...ghPrCreateAssigneeArgs(opts.assignee));
12022
- const created = await gh(args, worktreePath, { reject: false });
12023
- if (created.exitCode !== 0) {
12024
- const raw = created.stderr.trim() || created.stdout.trim() || "gh pr create failed";
12025
- throw new Error(await formatPrCreateFailure(raw, worktreePath, { slug, head: headRef }));
12026
- }
12027
- const url = created.stdout.trim().split("\n").find((l) => l.startsWith("http")) ?? created.stdout.trim();
12028
- return url;
12029
- }
12030
12421
  function suggestSlug(source) {
12031
12422
  const base = slugify(source || "thread");
12032
12423
  const stamp = Date.now().toString(36).slice(-4);
12033
12424
  return `${base || "thread"}-${stamp}`;
12034
12425
  }
12035
- var import_node_crypto10, import_node_fs23, import_node_os8, import_node_path27, PR_INFO_JSON_FIELDS, PR_DETAILS_JSON_FIELDS;
12426
+ var import_node_fs23, import_node_path27, PR_INFO_JSON_FIELDS, PR_DETAILS_JSON_FIELDS;
12036
12427
  var init_worktree = __esm({
12037
12428
  "src/git/worktree.ts"() {
12038
12429
  "use strict";
12039
- import_node_crypto10 = require("crypto");
12040
12430
  import_node_fs23 = require("fs");
12041
- import_node_os8 = require("os");
12042
12431
  import_node_path27 = require("path");
12043
12432
  init_paths();
12044
12433
  init_worktree_labels();
@@ -12046,6 +12435,8 @@ var init_worktree = __esm({
12046
12435
  init_worktree_exclude();
12047
12436
  init_git_auth_mode();
12048
12437
  init_run();
12438
+ init_remote_forge();
12439
+ init_cursor_pr();
12049
12440
  init_list_prs();
12050
12441
  init_repo_git_lock();
12051
12442
  init_app_settings();
@@ -12199,7 +12590,7 @@ function pathIsInside(parent, child) {
12199
12590
  }
12200
12591
  function defaultWorktreesRoot(repoPath) {
12201
12592
  const slug = (0, import_node_path28.basename)(repoPath.replace(/\/+$/, "")) || "repo";
12202
- return (0, import_node_path28.join)((0, import_node_os9.homedir)(), "sideboard", "workspaces", slug);
12593
+ return (0, import_node_path28.join)((0, import_node_os8.homedir)(), "sideboard", "workspaces", slug);
12203
12594
  }
12204
12595
  function removedWorktreeCoversRepo(saved, repoPath) {
12205
12596
  if (!saved || !repoPath) return false;
@@ -12382,12 +12773,12 @@ async function archiveRemovedWorkspaceChats(threads, archive) {
12382
12773
  }
12383
12774
  if (failures.length > 0) throw new Error(failures.join("\n"));
12384
12775
  }
12385
- var import_node_fs24, import_node_os9, import_node_path28;
12776
+ var import_node_fs24, import_node_os8, import_node_path28;
12386
12777
  var init_workspaces = __esm({
12387
12778
  "src/store/workspaces.ts"() {
12388
12779
  "use strict";
12389
12780
  import_node_fs24 = require("fs");
12390
- import_node_os9 = require("os");
12781
+ import_node_os8 = require("os");
12391
12782
  import_node_path28 = require("path");
12392
12783
  init_settings();
12393
12784
  init_paths();
@@ -12411,7 +12802,7 @@ __export(coordinator_prompt_exports, {
12411
12802
  function formatWorkspaceInventory(workspaces) {
12412
12803
  if (workspaces.length === 0) return "(no registered workspaces)";
12413
12804
  return workspaces.map((w) => {
12414
- const slug = w.githubSlug?.trim() ? ` github:${w.githubSlug.trim()}` : "";
12805
+ const slug = w.originRemote ? ` ${formatRemoteTag(w.originRemote)}` : w.githubSlug?.trim() ? ` github:${w.githubSlug.trim()}` : "";
12415
12806
  const profile = formatWorkspaceProfileSuffix(safeViewerProfile(w.path));
12416
12807
  return `- ${w.name}: ${w.path}${slug}${profile}`;
12417
12808
  }).join("\n");
@@ -12419,8 +12810,9 @@ function formatWorkspaceInventory(workspaces) {
12419
12810
  async function enrichWorkspacesWithGithub(workspaces) {
12420
12811
  return Promise.all(
12421
12812
  workspaces.map(async (w) => {
12422
- const githubSlug = await resolveGithubRepoSlug(w.path).catch(() => null);
12423
- return { ...w, githubSlug };
12813
+ const originRemote = await resolveOriginRemote(w.path).catch(() => null);
12814
+ const githubSlug = originRemote ? originRemote.forge === "github" ? originRemote.slug : null : await resolveGithubRepoSlug(w.path).catch(() => null);
12815
+ return { ...w, githubSlug, originRemote };
12424
12816
  })
12425
12817
  );
12426
12818
  }
@@ -12431,8 +12823,8 @@ function coordinatorGreenfieldPlaybook(reposDir) {
12431
12823
  "- Examples:",
12432
12824
  ` - Clone: \`git clone <url> ${reposDir}/<name>\``,
12433
12825
  ` - New GitHub repo: \`gh repo create <owner>/<name> --private --clone -- ${reposDir}/<name>\` (or mkdir + git init + gh repo create + remote add + push)`,
12434
- "- 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>`).",
12435
- "- 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`.",
12826
+ "- 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`).",
12827
+ "- 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`.",
12436
12828
  "- Do coding work in the child worktree thread, not by editing files in this home cwd."
12437
12829
  ].join("\n");
12438
12830
  }
@@ -12593,6 +12985,7 @@ var init_coordinator_prompt = __esm({
12593
12985
  "use strict";
12594
12986
  import_node_fs25 = require("fs");
12595
12987
  import_node_path29 = require("path");
12988
+ init_remote_forge();
12596
12989
  init_worktree();
12597
12990
  init_app_settings();
12598
12991
  init_paths();
@@ -12601,9 +12994,9 @@ var init_coordinator_prompt = __esm({
12601
12994
  "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.",
12602
12995
  "Sideboard MCP (fleet control \u2014 prefer these for status and orchestration):",
12603
12996
  "Discover:",
12604
- "- 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.",
12997
+ "- 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.",
12605
12998
  "- 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.",
12606
- `- 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).`,
12999
+ '- 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).',
12607
13000
  "- 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.",
12608
13001
  "- 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.",
12609
13002
  "- 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.",
@@ -12640,11 +13033,11 @@ var init_coordinator_prompt = __esm({
12640
13033
  "- 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.",
12641
13034
  "Inspect / review / PRs:",
12642
13035
  "- get_diff \u2014 compact diff summary",
12643
- "- 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.",
13036
+ "- 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.",
12644
13037
  '- 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.',
12645
13038
  "- 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.",
12646
13039
  '- 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.',
12647
- '- 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.',
13040
+ '- 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.',
12648
13041
  "Process guides:",
12649
13042
  "- 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.",
12650
13043
  "- 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.",
@@ -12987,7 +13380,7 @@ function resolveBrightsyConfigFile(path2) {
12987
13380
  }
12988
13381
  function brightsyConfigPath() {
12989
13382
  const override = process.env.BRIGHTSY_CONFIG?.trim();
12990
- const raw = override || (0, import_node_path31.join)((0, import_node_os10.homedir)(), ".brightsy", "config.json");
13383
+ const raw = override || (0, import_node_path31.join)((0, import_node_os9.homedir)(), ".brightsy", "config.json");
12991
13384
  return resolveBrightsyConfigFile(raw);
12992
13385
  }
12993
13386
  function loadBrightsyConfig() {
@@ -13021,12 +13414,12 @@ function saveBrightsyConfig(cfg) {
13021
13414
  mode: 384
13022
13415
  });
13023
13416
  }
13024
- var import_node_fs27, import_node_os10, import_node_path31;
13417
+ var import_node_fs27, import_node_os9, import_node_path31;
13025
13418
  var init_config = __esm({
13026
13419
  "src/brightsy/config.ts"() {
13027
13420
  "use strict";
13028
13421
  import_node_fs27 = require("fs");
13029
- import_node_os10 = require("os");
13422
+ import_node_os9 = require("os");
13030
13423
  import_node_path31 = require("path");
13031
13424
  }
13032
13425
  });
@@ -14337,7 +14730,7 @@ function claudePluginServerId(pluginName, serverName) {
14337
14730
  return `plugin_${pluginName}_${serverName}`.replace(/[^A-Za-z0-9_-]/g, "_");
14338
14731
  }
14339
14732
  function listEnabledClaudePluginMcpServers(opts) {
14340
- const home = opts?.home ?? (0, import_node_os11.homedir)();
14733
+ const home = opts?.home ?? (0, import_node_os10.homedir)();
14341
14734
  const pluginsRoot = claudePluginsRoot(home);
14342
14735
  const enabled = enabledPluginIds(home, opts?.projectDirs ?? []);
14343
14736
  const installed = readInstalledPlugins((0, import_node_path33.join)(pluginsRoot, "installed_plugins.json"));
@@ -14519,12 +14912,12 @@ function readJson(path2) {
14519
14912
  return {};
14520
14913
  }
14521
14914
  }
14522
- var import_node_fs30, import_node_os11, import_node_path33;
14915
+ var import_node_fs30, import_node_os10, import_node_path33;
14523
14916
  var init_claude_plugins = __esm({
14524
14917
  "src/agents/claude-plugins.ts"() {
14525
14918
  "use strict";
14526
14919
  import_node_fs30 = require("fs");
14527
- import_node_os11 = require("os");
14920
+ import_node_os10 = require("os");
14528
14921
  import_node_path33 = require("path");
14529
14922
  init_meta();
14530
14923
  }
@@ -14970,7 +15363,7 @@ function versionDirNodeBins(root, toBin) {
14970
15363
  return [];
14971
15364
  }
14972
15365
  }
14973
- function defaultNodeBinCandidates(home = (0, import_node_os12.homedir)()) {
15366
+ function defaultNodeBinCandidates(home = (0, import_node_os11.homedir)()) {
14974
15367
  const kegs = ["/opt/homebrew", "/usr/local"].flatMap(
14975
15368
  (prefix) => PREFERRED_LTS_MAJORS.map((major) => (0, import_node_path36.join)(prefix, "opt", `node@${major}`, "bin", "node"))
14976
15369
  );
@@ -15085,12 +15478,12 @@ async function resolveNodeLaunch(scriptPath) {
15085
15478
  env: { ELECTRON_RUN_AS_NODE: "1" }
15086
15479
  };
15087
15480
  }
15088
- var import_node_fs33, import_node_os12, import_node_path36, AGENT_RUNNER_MAX_OLD_SPACE_MB, MAX_OLD_SPACE_FLAG, PREFERRED_LTS_MAJORS;
15481
+ var import_node_fs33, import_node_os11, import_node_path36, AGENT_RUNNER_MAX_OLD_SPACE_MB, MAX_OLD_SPACE_FLAG, PREFERRED_LTS_MAJORS;
15089
15482
  var init_node_launch = __esm({
15090
15483
  "src/agents/node-launch.ts"() {
15091
15484
  "use strict";
15092
15485
  import_node_fs33 = require("fs");
15093
- import_node_os12 = require("os");
15486
+ import_node_os11 = require("os");
15094
15487
  import_node_path36 = require("path");
15095
15488
  init_nested_electron_env();
15096
15489
  init_system_ca();
@@ -15387,7 +15780,7 @@ function mcpServersRecord(servers, extraServers) {
15387
15780
  function writeMcpServersConfig(servers, extraServers) {
15388
15781
  const mcpServers = mcpServersRecord(servers, extraServers);
15389
15782
  if (Object.keys(mcpServers).length === 0) return null;
15390
- const dir = (0, import_node_fs34.mkdtempSync)((0, import_node_path37.join)((0, import_node_os13.tmpdir)(), "sideboard-mcp-"));
15783
+ const dir = (0, import_node_fs34.mkdtempSync)((0, import_node_path37.join)((0, import_node_os12.tmpdir)(), "sideboard-mcp-"));
15391
15784
  const cfgPath = (0, import_node_path37.join)(dir, "mcp.json");
15392
15785
  (0, import_node_fs34.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
15393
15786
  return cfgPath;
@@ -15395,13 +15788,13 @@ function writeMcpServersConfig(servers, extraServers) {
15395
15788
  async function writeInjectedMcpConfig(opts) {
15396
15789
  return writeMcpServersConfig(await buildInjectedMcpServers(opts));
15397
15790
  }
15398
- var import_node_fs34, import_node_module, import_node_os13, import_node_path37, 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, BRIGHTSY_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
15791
+ var import_node_fs34, import_node_module, import_node_os12, import_node_path37, 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, BRIGHTSY_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
15399
15792
  var init_injected_mcp = __esm({
15400
15793
  "src/agents/injected-mcp.ts"() {
15401
15794
  "use strict";
15402
15795
  import_node_fs34 = require("fs");
15403
15796
  import_node_module = require("module");
15404
- import_node_os13 = require("os");
15797
+ import_node_os12 = require("os");
15405
15798
  import_node_path37 = require("path");
15406
15799
  import_node_url = require("url");
15407
15800
  init_run();
@@ -15460,10 +15853,10 @@ var init_injected_mcp = __esm({
15460
15853
 
15461
15854
  // src/agents/user-mcp-config.ts
15462
15855
  function userCursorMcpConfigPath() {
15463
- return (0, import_node_path38.join)((0, import_node_os14.homedir)(), ".cursor", "mcp.json");
15856
+ return (0, import_node_path38.join)((0, import_node_os13.homedir)(), ".cursor", "mcp.json");
15464
15857
  }
15465
15858
  function userClaudeMcpConfigPath() {
15466
- return (0, import_node_path38.join)((0, import_node_os14.homedir)(), ".claude.json");
15859
+ return (0, import_node_path38.join)((0, import_node_os13.homedir)(), ".claude.json");
15467
15860
  }
15468
15861
  function asObject(value) {
15469
15862
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
@@ -15523,12 +15916,12 @@ async function registerPackagedUserMcpClients() {
15523
15916
  }
15524
15917
  return { cursor };
15525
15918
  }
15526
- var import_node_fs35, import_node_os14, import_node_path38;
15919
+ var import_node_fs35, import_node_os13, import_node_path38;
15527
15920
  var init_user_mcp_config = __esm({
15528
15921
  "src/agents/user-mcp-config.ts"() {
15529
15922
  "use strict";
15530
15923
  import_node_fs35 = require("fs");
15531
- import_node_os14 = require("os");
15924
+ import_node_os13 = require("os");
15532
15925
  import_node_path38 = require("path");
15533
15926
  init_paths();
15534
15927
  init_injected_mcp();
@@ -15569,10 +15962,10 @@ function readText(path2) {
15569
15962
  return "";
15570
15963
  }
15571
15964
  }
15572
- function userCodexConfigPaths(home = (0, import_node_os15.homedir)()) {
15965
+ function userCodexConfigPaths(home = (0, import_node_os14.homedir)()) {
15573
15966
  return [(0, import_node_path39.join)(home, ".codex", "config.toml"), (0, import_node_path39.join)(home, ".config", "codex", "config.toml")];
15574
15967
  }
15575
- function userOpencodeConfigPaths(home = (0, import_node_os15.homedir)()) {
15968
+ function userOpencodeConfigPaths(home = (0, import_node_os14.homedir)()) {
15576
15969
  return [
15577
15970
  (0, import_node_path39.join)(home, ".config", "opencode", "opencode.json"),
15578
15971
  (0, import_node_path39.join)(home, ".config", "opencode", "opencode.jsonc")
@@ -15594,7 +15987,7 @@ function userMcpNamesToDisable(opts) {
15594
15987
  }
15595
15988
  return out;
15596
15989
  }
15597
- function listUserCodexMcpNames(home = (0, import_node_os15.homedir)()) {
15990
+ function listUserCodexMcpNames(home = (0, import_node_os14.homedir)()) {
15598
15991
  const names = [];
15599
15992
  for (const path2 of userCodexConfigPaths(home)) {
15600
15993
  if (!(0, import_node_fs36.existsSync)(path2)) continue;
@@ -15602,7 +15995,7 @@ function listUserCodexMcpNames(home = (0, import_node_os15.homedir)()) {
15602
15995
  }
15603
15996
  return names;
15604
15997
  }
15605
- function listUserOpencodeMcpNames(home = (0, import_node_os15.homedir)()) {
15998
+ function listUserOpencodeMcpNames(home = (0, import_node_os14.homedir)()) {
15606
15999
  const names = [];
15607
16000
  for (const path2 of userOpencodeConfigPaths(home)) {
15608
16001
  if (!(0, import_node_fs36.existsSync)(path2)) continue;
@@ -15610,12 +16003,12 @@ function listUserOpencodeMcpNames(home = (0, import_node_os15.homedir)()) {
15610
16003
  }
15611
16004
  return names;
15612
16005
  }
15613
- function userClaudeMcpConfigObject(home = (0, import_node_os15.homedir)()) {
15614
- const claudePath = home === (0, import_node_os15.homedir)() ? userClaudeMcpConfigPath() : (0, import_node_path39.join)(home, ".claude.json");
16006
+ function userClaudeMcpConfigObject(home = (0, import_node_os14.homedir)()) {
16007
+ const claudePath = home === (0, import_node_os14.homedir)() ? userClaudeMcpConfigPath() : (0, import_node_path39.join)(home, ".claude.json");
15615
16008
  if (!(0, import_node_fs36.existsSync)(claudePath)) return {};
15616
16009
  return asObject2(asObject2(readJsonObject2(claudePath)).mcpServers);
15617
16010
  }
15618
- function listUserClaudeMcpServerEntries(home = (0, import_node_os15.homedir)(), projectDirs) {
16011
+ function listUserClaudeMcpServerEntries(home = (0, import_node_os14.homedir)(), projectDirs) {
15619
16012
  const out = {};
15620
16013
  for (const [name, entry] of Object.entries(
15621
16014
  listEnabledClaudePluginMcpServers({ home, projectDirs })
@@ -15651,13 +16044,13 @@ function toCodexUnattendedAppsArgs() {
15651
16044
  "features.plugins=false"
15652
16045
  ];
15653
16046
  }
15654
- var import_node_fs36, import_node_os15, import_node_path39, import_smol_toml2;
16047
+ var import_node_fs36, import_node_os14, import_node_path39, import_smol_toml2;
15655
16048
  var init_orch_mcp_isolation = __esm({
15656
16049
  "src/agents/orch-mcp-isolation.ts"() {
15657
16050
  "use strict";
15658
16051
  import_node_fs36 = require("fs");
15659
16052
  init_claude_plugins();
15660
- import_node_os15 = require("os");
16053
+ import_node_os14 = require("os");
15661
16054
  import_node_path39 = require("path");
15662
16055
  import_smol_toml2 = require("smol-toml");
15663
16056
  init_user_mcp_config();
@@ -15758,7 +16151,7 @@ var init_proxy_server = __esm({
15758
16151
 
15759
16152
  // src/mcp-ui/proxy-http.ts
15760
16153
  async function listenMcpUiProxy(opts) {
15761
- const token = (0, import_node_crypto11.randomBytes)(24).toString("hex");
16154
+ const token = (0, import_node_crypto10.randomBytes)(24).toString("hex");
15762
16155
  const transports = /* @__PURE__ */ new Map();
15763
16156
  const httpServer = (0, import_node_http.createServer)((req, res) => {
15764
16157
  void handle(req, res);
@@ -15796,7 +16189,7 @@ async function listenMcpUiProxy(opts) {
15796
16189
  return;
15797
16190
  }
15798
16191
  const created = new import_streamableHttp.StreamableHTTPServerTransport({
15799
- sessionIdGenerator: () => (0, import_node_crypto11.randomUUID)(),
16192
+ sessionIdGenerator: () => (0, import_node_crypto10.randomUUID)(),
15800
16193
  allowedHosts: ["127.0.0.1", "localhost"],
15801
16194
  onsessioninitialized: (id) => {
15802
16195
  transports.set(`${sessionKey}:${id}`, created);
@@ -15849,11 +16242,11 @@ function parseMcpUiProxyPath(url) {
15849
16242
  return null;
15850
16243
  }
15851
16244
  }
15852
- var import_node_crypto11, import_node_http, import_server2, import_streamableHttp;
16245
+ var import_node_crypto10, import_node_http, import_server2, import_streamableHttp;
15853
16246
  var init_proxy_http = __esm({
15854
16247
  "src/mcp-ui/proxy-http.ts"() {
15855
16248
  "use strict";
15856
- import_node_crypto11 = require("crypto");
16249
+ import_node_crypto10 = require("crypto");
15857
16250
  import_node_http = require("http");
15858
16251
  import_server2 = require("@modelcontextprotocol/sdk/server/index.js");
15859
16252
  import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
@@ -15990,7 +16383,7 @@ var init_session = __esm({
15990
16383
  function getMcpUiHost() {
15991
16384
  if (!singleton) {
15992
16385
  singleton = new McpUiHost({
15993
- entries: (projectDirs) => listUserClaudeMcpServerEntries((0, import_node_os16.homedir)(), projectDirs)
16386
+ entries: (projectDirs) => listUserClaudeMcpServerEntries((0, import_node_os15.homedir)(), projectDirs)
15994
16387
  });
15995
16388
  }
15996
16389
  return singleton;
@@ -15998,7 +16391,7 @@ function getMcpUiHost() {
15998
16391
  async function proxyClaudeUserMcp(orchestrator, projectDirs) {
15999
16392
  if (orchestrator) return {};
16000
16393
  const dirs = projectDirs.filter((d) => typeof d === "string" && d.length > 0);
16001
- return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries((0, import_node_os16.homedir)(), dirs), dirs);
16394
+ return getMcpUiHost().proxyEntries(listUserClaudeMcpServerEntries((0, import_node_os15.homedir)(), dirs), dirs);
16002
16395
  }
16003
16396
  function closeMcpUiHost() {
16004
16397
  const host = singleton;
@@ -16007,14 +16400,14 @@ function closeMcpUiHost() {
16007
16400
  }
16008
16401
  function dirsKeyFor(projectDirs) {
16009
16402
  if (!projectDirs?.length) return "";
16010
- return (0, import_node_crypto12.createHash)("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
16403
+ return (0, import_node_crypto11.createHash)("sha256").update(projectDirs.join("\0")).digest("hex").slice(0, 16);
16011
16404
  }
16012
- var import_node_crypto12, import_node_os16, TOOL_TTL_MS, McpUiHost, singleton;
16405
+ var import_node_crypto11, import_node_os15, TOOL_TTL_MS, McpUiHost, singleton;
16013
16406
  var init_host = __esm({
16014
16407
  "src/mcp-ui/host.ts"() {
16015
16408
  "use strict";
16016
- import_node_crypto12 = require("crypto");
16017
- import_node_os16 = require("os");
16409
+ import_node_crypto11 = require("crypto");
16410
+ import_node_os15 = require("os");
16018
16411
  init_orch_mcp_isolation();
16019
16412
  init_meta();
16020
16413
  init_proxy_http();
@@ -17150,8 +17543,8 @@ function usageFromCodex(usage) {
17150
17543
  }
17151
17544
  function codexConfigHasNetworkAccess() {
17152
17545
  const candidates = [
17153
- (0, import_node_path42.join)((0, import_node_os17.homedir)(), ".codex", "config.toml"),
17154
- (0, import_node_path42.join)((0, import_node_os17.homedir)(), ".config", "codex", "config.toml")
17546
+ (0, import_node_path42.join)((0, import_node_os16.homedir)(), ".codex", "config.toml"),
17547
+ (0, import_node_path42.join)((0, import_node_os16.homedir)(), ".config", "codex", "config.toml")
17155
17548
  ];
17156
17549
  for (const path2 of candidates) {
17157
17550
  if (!(0, import_node_fs40.existsSync)(path2)) continue;
@@ -17187,7 +17580,7 @@ function asRecord5(value) {
17187
17580
  return void 0;
17188
17581
  }
17189
17582
  function codexLooksAuthenticated() {
17190
- const authPath = (0, import_node_path42.join)((0, import_node_os17.homedir)(), ".codex", "auth.json");
17583
+ const authPath = (0, import_node_path42.join)((0, import_node_os16.homedir)(), ".codex", "auth.json");
17191
17584
  if (!(0, import_node_fs40.existsSync)(authPath)) return false;
17192
17585
  try {
17193
17586
  return (0, import_node_fs40.statSync)(authPath).size > 2;
@@ -17195,12 +17588,12 @@ function codexLooksAuthenticated() {
17195
17588
  return false;
17196
17589
  }
17197
17590
  }
17198
- var import_node_fs40, import_node_os17, import_node_path42, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
17591
+ var import_node_fs40, import_node_os16, import_node_path42, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
17199
17592
  var init_codex = __esm({
17200
17593
  "src/agents/codex.ts"() {
17201
17594
  "use strict";
17202
17595
  import_node_fs40 = require("fs");
17203
- import_node_os17 = require("os");
17596
+ import_node_os16 = require("os");
17204
17597
  import_node_path42 = require("path");
17205
17598
  init_run();
17206
17599
  init_app_settings();
@@ -19096,7 +19489,7 @@ function accessTokenFromCredentialsJson(raw) {
19096
19489
  }
19097
19490
  }
19098
19491
  function claudeCredentialsPath(configDir) {
19099
- const root = configDir?.trim() || process.env.CLAUDE_CONFIG_DIR?.trim() || (0, import_node_path47.join)((0, import_node_os18.homedir)(), ".claude");
19492
+ const root = configDir?.trim() || process.env.CLAUDE_CONFIG_DIR?.trim() || (0, import_node_path47.join)((0, import_node_os17.homedir)(), ".claude");
19100
19493
  return (0, import_node_path47.join)(root, ".credentials.json");
19101
19494
  }
19102
19495
  async function readFileAccessToken(configDir) {
@@ -19214,12 +19607,12 @@ async function getClaudePlanUsage(opts = {}) {
19214
19607
  })();
19215
19608
  return inflight;
19216
19609
  }
19217
- var import_node_fs46, import_node_os18, import_node_path47, CLAUDE_OAUTH_USAGE_URL, CLAUDE_USAGE_TTL_MS, CLAUDE_USAGE_ERROR_TTL_MS, CLAUDE_USAGE_STALE_OK_MS, KEYCHAIN_SERVICES, cache, errorUntil, inflight, lastGood, claudeVersion, cacheDirKey;
19610
+ var import_node_fs46, import_node_os17, import_node_path47, CLAUDE_OAUTH_USAGE_URL, CLAUDE_USAGE_TTL_MS, CLAUDE_USAGE_ERROR_TTL_MS, CLAUDE_USAGE_STALE_OK_MS, KEYCHAIN_SERVICES, cache, errorUntil, inflight, lastGood, claudeVersion, cacheDirKey;
19218
19611
  var init_claude_usage_client = __esm({
19219
19612
  "src/agents/claude-usage-client.ts"() {
19220
19613
  "use strict";
19221
19614
  import_node_fs46 = require("fs");
19222
- import_node_os18 = require("os");
19615
+ import_node_os17 = require("os");
19223
19616
  import_node_path47 = require("path");
19224
19617
  init_run();
19225
19618
  init_fetch();
@@ -20125,7 +20518,9 @@ function resolveSidebarGitPrompt(action, opts) {
20125
20518
  }
20126
20519
  return agentGitPrompt(action, opts);
20127
20520
  }
20128
- function gitActionExpansion(action, base) {
20521
+ function gitActionExpansion(action, base, forge) {
20522
+ if (forge === "cursor") return cursorGitActionExpansion(action, base);
20523
+ if (forge === "entire") return entireGitActionExpansion(action, base);
20129
20524
  switch (action) {
20130
20525
  case "commit-push":
20131
20526
  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.";
@@ -20150,7 +20545,57 @@ function gitActionExpansion(action, base) {
20150
20545
  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.";
20151
20546
  }
20152
20547
  }
20153
- function expandCanonicalGitRequest(prompt) {
20548
+ function cursorGitActionExpansion(action, base) {
20549
+ switch (action) {
20550
+ case "commit-push":
20551
+ 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.";
20552
+ case "create-draft":
20553
+ 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`.";
20554
+ case "create-web":
20555
+ 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`.";
20556
+ case "resolve-conflicts":
20557
+ if (base && !isMainishPrBase(base)) {
20558
+ 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.`;
20559
+ }
20560
+ if (base) {
20561
+ return `Fetch the PR base (\`${base}\`), merge it into this branch, resolve conflicts carefully, then commit and push until the PR is mergeable.`;
20562
+ }
20563
+ 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`.";
20564
+ case "ready-for-review":
20565
+ 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`.";
20566
+ case "merge":
20567
+ if (base && !isMainishPrBase(base)) {
20568
+ 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\`.`;
20569
+ }
20570
+ 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`.";
20571
+ }
20572
+ }
20573
+ function entireGitActionExpansion(action, base) {
20574
+ switch (action) {
20575
+ case "commit-push":
20576
+ 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.";
20577
+ case "create-draft":
20578
+ 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`.';
20579
+ case "create-web":
20580
+ 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`.';
20581
+ case "resolve-conflicts":
20582
+ if (base && !isMainishPrBase(base)) {
20583
+ 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.`;
20584
+ }
20585
+ if (base) {
20586
+ return `Fetch the Trail base (\`${base}\`), merge it into this branch, resolve conflicts carefully, then commit and push.`;
20587
+ }
20588
+ 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`.";
20589
+ case "ready-for-review":
20590
+ 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`.";
20591
+ case "merge":
20592
+ if (base && !isMainishPrBase(base)) {
20593
+ 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\`.`;
20594
+ }
20595
+ 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`.";
20596
+ }
20597
+ }
20598
+ function expandCanonicalGitRequest(prompt, opts) {
20154
20599
  const trimmed = prompt.trim();
20155
20600
  let action = null;
20156
20601
  let base = null;
@@ -20175,11 +20620,12 @@ function expandCanonicalGitRequest(prompt) {
20175
20620
  base = m[1]?.trim() || null;
20176
20621
  }
20177
20622
  }
20178
- let expansion = action ? gitActionExpansion(action, base) : null;
20623
+ const forge = opts?.forge ?? null;
20624
+ let expansion = action ? gitActionExpansion(action, base, forge) : null;
20179
20625
  if (!expansion && trimmed === "Address review comments.") {
20180
- expansion = "Read the PR review feedback (`gh pr view --comments`, `gh api` review comments), make the requested changes, commit, and push.";
20626
+ 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.";
20181
20627
  } else if (!expansion && FIX_CI_RE.test(trimmed)) {
20182
- 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.";
20628
+ 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.";
20183
20629
  }
20184
20630
  if (!expansion) return prompt;
20185
20631
  return `${trimmed}
@@ -20935,7 +21381,7 @@ function forkMessageSlice(from, throughIndex) {
20935
21381
  function buildForkTranscriptAttachment(baseTitle, messages) {
20936
21382
  const title = baseTitle || "Chat";
20937
21383
  return {
20938
- id: (0, import_node_crypto13.randomUUID)(),
21384
+ id: (0, import_node_crypto12.randomUUID)(),
20939
21385
  name: `Transcript of ${title}.md`,
20940
21386
  kind: "transcript",
20941
21387
  content: formatTranscriptMarkdown(title, messages)
@@ -20994,11 +21440,11 @@ function forkChatTab(input) {
20994
21440
  }
20995
21441
  return tab;
20996
21442
  }
20997
- var import_node_crypto13;
21443
+ var import_node_crypto12;
20998
21444
  var init_chat_tabs = __esm({
20999
21445
  "src/threads/chat-tabs.ts"() {
21000
21446
  "use strict";
21001
- import_node_crypto13 = require("crypto");
21447
+ import_node_crypto12 = require("crypto");
21002
21448
  init_context_compact();
21003
21449
  init_teams();
21004
21450
  init_worktree_labels();
@@ -21265,7 +21711,7 @@ function buildReviewRequestAttachment(content, opts) {
21265
21711
  const path2 = opts?.path ?? REVIEW_SKILL_PATH;
21266
21712
  const name = opts?.name ?? (path2 === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path2 === REPO_REVIEW_PATH || path2 === CONTEXT_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
21267
21713
  return {
21268
- id: (0, import_node_crypto14.randomUUID)(),
21714
+ id: (0, import_node_crypto13.randomUUID)(),
21269
21715
  name,
21270
21716
  kind: "file",
21271
21717
  path: path2,
@@ -21321,11 +21767,11 @@ async function requestReview(threadRef, send3) {
21321
21767
  const started = await send3(tab.id, REVIEW_REQUEST_PREFILL);
21322
21768
  return { tab: started, from };
21323
21769
  }
21324
- var import_node_crypto14, import_node_fs49, import_node_path50, 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;
21770
+ var import_node_crypto13, import_node_fs49, import_node_path50, 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;
21325
21771
  var init_request_review = __esm({
21326
21772
  "src/review/request-review.ts"() {
21327
21773
  "use strict";
21328
- import_node_crypto14 = require("crypto");
21774
+ import_node_crypto13 = require("crypto");
21329
21775
  import_node_fs49 = require("fs");
21330
21776
  import_node_path50 = require("path");
21331
21777
  init_global_workspace();
@@ -23369,7 +23815,7 @@ function isUnclaimedSteerRequest(req) {
23369
23815
  }
23370
23816
  function nextSteerRequest(existing) {
23371
23817
  if (existing?.requestId && !existing.claimedAt) return existing;
23372
- return { requestId: (0, import_node_crypto15.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
23818
+ return { requestId: (0, import_node_crypto14.randomUUID)(), requestedAt: (/* @__PURE__ */ new Date()).toISOString() };
23373
23819
  }
23374
23820
  function claimSteerRequest(threadId) {
23375
23821
  const pending = readThread(threadId)?.steerRequest;
@@ -23378,11 +23824,11 @@ function claimSteerRequest(threadId) {
23378
23824
  steerRequest: { ...pending, claimedAt: (/* @__PURE__ */ new Date()).toISOString() }
23379
23825
  });
23380
23826
  }
23381
- var import_node_crypto15;
23827
+ var import_node_crypto14;
23382
23828
  var init_steer_request = __esm({
23383
23829
  "src/orchestrator/steer-request.ts"() {
23384
23830
  "use strict";
23385
- import_node_crypto15 = require("crypto");
23831
+ import_node_crypto14 = require("crypto");
23386
23832
  init_thread_store();
23387
23833
  }
23388
23834
  });
@@ -23698,8 +24144,8 @@ function resolveAbleTimeOAuthHost(requested, saved) {
23698
24144
  return fromSaved || void 0;
23699
24145
  }
23700
24146
  function createAbleTimePkce() {
23701
- const verifier = (0, import_node_crypto16.randomBytes)(32).toString("base64url");
23702
- const challenge = (0, import_node_crypto16.createHash)("sha256").update(verifier).digest("base64url");
24147
+ const verifier = (0, import_node_crypto15.randomBytes)(32).toString("base64url");
24148
+ const challenge = (0, import_node_crypto15.createHash)("sha256").update(verifier).digest("base64url");
23703
24149
  return { verifier, challenge };
23704
24150
  }
23705
24151
  function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
@@ -23804,7 +24250,7 @@ async function startAbleTimeOAuth(opts) {
23804
24250
  opts?.host,
23805
24251
  loadAppSettings().integrations.abletimeHost
23806
24252
  );
23807
- const state = (0, import_node_crypto16.randomBytes)(16).toString("hex");
24253
+ const state = (0, import_node_crypto15.randomBytes)(16).toString("hex");
23808
24254
  const pkce = createAbleTimePkce();
23809
24255
  const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
23810
24256
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
@@ -23904,11 +24350,11 @@ async function startAbleTimeOAuth(opts) {
23904
24350
  return loadAppSettings();
23905
24351
  }
23906
24352
  }
23907
- var import_node_crypto16, 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;
24353
+ var import_node_crypto15, 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;
23908
24354
  var init_abletime_oauth = __esm({
23909
24355
  "src/integrations/abletime-oauth.ts"() {
23910
24356
  "use strict";
23911
- import_node_crypto16 = require("crypto");
24357
+ import_node_crypto15 = require("crypto");
23912
24358
  import_node_http2 = require("http");
23913
24359
  init_fetch();
23914
24360
  init_app_settings();
@@ -24669,7 +25115,7 @@ function uniqueAttachmentFileName(dir, originalName) {
24669
25115
  const candidate = `${stem}-${i}${ext}`;
24670
25116
  if (!(0, import_node_fs55.existsSync)((0, import_node_path56.join)(dir, candidate))) return candidate;
24671
25117
  }
24672
- return `${stem}-${(0, import_node_crypto17.randomUUID)()}${ext}`;
25118
+ return `${stem}-${(0, import_node_crypto16.randomUUID)()}${ext}`;
24673
25119
  }
24674
25120
  function extractHttpUrls(markdown) {
24675
25121
  const text7 = markdown ?? "";
@@ -24775,13 +25221,13 @@ async function downloadAuthenticatedFile(opts) {
24775
25221
  ...contentType ? { contentType } : {}
24776
25222
  };
24777
25223
  }
24778
- var import_node_fs55, import_node_path56, import_node_crypto17, ISSUE_ATTACHMENT_MAX_BYTES, MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE, EXT_BY_MIME;
25224
+ var import_node_fs55, import_node_path56, import_node_crypto16, ISSUE_ATTACHMENT_MAX_BYTES, MARKDOWN_IMAGE_RE, MARKDOWN_LINK_RE, HTML_SRC_RE, BARE_URL_RE, EXT_BY_MIME;
24779
25225
  var init_issue_attachments = __esm({
24780
25226
  "src/integrations/issue-attachments.ts"() {
24781
25227
  "use strict";
24782
25228
  import_node_fs55 = require("fs");
24783
25229
  import_node_path56 = require("path");
24784
- import_node_crypto17 = require("crypto");
25230
+ import_node_crypto16 = require("crypto");
24785
25231
  init_fetch();
24786
25232
  init_workspace_scratch();
24787
25233
  ISSUE_ATTACHMENT_MAX_BYTES = 25 * 1024 * 1024;
@@ -26412,7 +26858,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
26412
26858
  ...instructions
26413
26859
  ].join("\n");
26414
26860
  return {
26415
- id: (0, import_node_crypto18.randomUUID)(),
26861
+ id: (0, import_node_crypto17.randomUUID)(),
26416
26862
  name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
26417
26863
  kind: "transcript",
26418
26864
  content: body
@@ -26450,11 +26896,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText, opts) {
26450
26896
  }
26451
26897
  return updateThread(tab.id, patch);
26452
26898
  }
26453
- var import_node_crypto18, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
26899
+ var import_node_crypto17, QUOTA_CONTINUE_PROMPT, QUOTA_CONTINUE_ACCOUNT_PROMPT, QUOTA_RESUME_PROMPT;
26454
26900
  var init_quota_failover = __esm({
26455
26901
  "src/orchestrator/quota-failover.ts"() {
26456
26902
  "use strict";
26457
- import_node_crypto18 = require("crypto");
26903
+ import_node_crypto17 = require("crypto");
26458
26904
  init_session_quota();
26459
26905
  init_app_settings();
26460
26906
  init_managed_accounts();
@@ -26564,7 +27010,7 @@ function listConductorWorkspaces() {
26564
27010
  if (!(0, import_node_fs58.existsSync)(CONDUCTOR_DB)) {
26565
27011
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
26566
27012
  }
26567
- const tmp = (0, import_node_fs58.mkdtempSync)((0, import_node_path58.join)((0, import_node_os19.tmpdir)(), "sideboard-conductor-"));
27013
+ const tmp = (0, import_node_fs58.mkdtempSync)((0, import_node_path58.join)((0, import_node_os18.tmpdir)(), "sideboard-conductor-"));
26568
27014
  const snapshot = (0, import_node_path58.join)(tmp, "conductor.db");
26569
27015
  try {
26570
27016
  (0, import_node_fs58.copyFileSync)(CONDUCTOR_DB, snapshot);
@@ -26655,7 +27101,7 @@ function importConductorWorkspace(workspaceId) {
26655
27101
  if (!(0, import_node_fs58.existsSync)(CONDUCTOR_DB)) {
26656
27102
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
26657
27103
  }
26658
- const tmp = (0, import_node_fs58.mkdtempSync)((0, import_node_path58.join)((0, import_node_os19.tmpdir)(), "sideboard-conductor-"));
27104
+ const tmp = (0, import_node_fs58.mkdtempSync)((0, import_node_path58.join)((0, import_node_os18.tmpdir)(), "sideboard-conductor-"));
26659
27105
  const snapshot = (0, import_node_path58.join)(tmp, "conductor.db");
26660
27106
  try {
26661
27107
  (0, import_node_fs58.copyFileSync)(CONDUCTOR_DB, snapshot);
@@ -26750,13 +27196,13 @@ function importConductorWorkspace(workspaceId) {
26750
27196
  async function importConductorWorkspaceAsync(workspaceId) {
26751
27197
  return importConductorWorkspace(workspaceId);
26752
27198
  }
26753
- var import_node_child_process6, import_node_fs58, import_node_os19, import_node_path58, import_node_module6, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
27199
+ var import_node_child_process6, import_node_fs58, import_node_os18, import_node_path58, import_node_module6, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
26754
27200
  var init_adopt = __esm({
26755
27201
  "src/threads/adopt.ts"() {
26756
27202
  "use strict";
26757
27203
  import_node_child_process6 = require("child_process");
26758
27204
  import_node_fs58 = require("fs");
26759
- import_node_os19 = require("os");
27205
+ import_node_os18 = require("os");
26760
27206
  import_node_path58 = require("path");
26761
27207
  import_node_module6 = require("module");
26762
27208
  init_worktree();
@@ -27080,9 +27526,187 @@ var init_stack_layers = __esm({
27080
27526
  }
27081
27527
  });
27082
27528
 
27529
+ // src/git/pr-write.ts
27530
+ async function lookupGithubGraphqlReset(cwd) {
27531
+ const result = await gh(["api", "rate_limit"], cwd, { reject: false });
27532
+ if (result.exitCode !== 0 || !result.stdout.trim()) return void 0;
27533
+ try {
27534
+ const data = JSON.parse(result.stdout);
27535
+ const reset = data.resources?.graphql?.reset;
27536
+ return typeof reset === "number" ? reset : void 0;
27537
+ } catch {
27538
+ return void 0;
27539
+ }
27540
+ }
27541
+ async function formatPrCreateFailure(raw, cwd, ctx) {
27542
+ if (!isGhRateLimitError(raw)) {
27543
+ return formatGhLandError(raw, {
27544
+ targetedRepo: ctx?.slug,
27545
+ headRef: ctx?.head
27546
+ });
27547
+ }
27548
+ const resetAt = await lookupGithubGraphqlReset(cwd);
27549
+ return formatGhLandError(raw, {
27550
+ resetAt,
27551
+ targetedRepo: ctx?.slug,
27552
+ headRef: ctx?.head
27553
+ });
27554
+ }
27555
+ async function createCursorPullRequest(worktreePath, slug, opts) {
27556
+ const bodyText = (opts.body ?? opts.title).trim() || opts.title;
27557
+ const bodyFile = (0, import_node_path59.join)(
27558
+ (0, import_node_os19.tmpdir)(),
27559
+ `sideboard-pr-body-${process.pid}-${(0, import_node_crypto18.randomBytes)(6).toString("hex")}.md`
27560
+ );
27561
+ (0, import_node_fs60.writeFileSync)(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
27562
+ `, "utf8");
27563
+ try {
27564
+ return await createOrUpdateCursorPr(worktreePath, slug, {
27565
+ title: opts.title,
27566
+ bodyFile,
27567
+ base: opts.base,
27568
+ head: opts.head,
27569
+ draft: opts.draft
27570
+ });
27571
+ } finally {
27572
+ try {
27573
+ (0, import_node_fs60.unlinkSync)(bodyFile);
27574
+ } catch {
27575
+ }
27576
+ }
27577
+ }
27578
+ function ghPrCreateAssigneeArgs(assignee) {
27579
+ const raw = assignee?.trim();
27580
+ if (raw && /^(none|unassigned)$/i.test(raw)) return [];
27581
+ return ["--assignee", raw || "@me"];
27582
+ }
27583
+ async function createOrUpdatePr(worktreePath, opts) {
27584
+ const origin = await resolveOriginRemote(worktreePath);
27585
+ if (origin?.forge === "entire") throw entireUsesTrails();
27586
+ const cursorSlug = origin?.forge === "cursor" ? origin.slug : null;
27587
+ if (cursorSlug) return createCursorPullRequest(worktreePath, cursorSlug, opts);
27588
+ await ensureGhPreferOrigin(worktreePath);
27589
+ const slug = await resolveGithubRepoSlug(worktreePath);
27590
+ if (!slug) {
27591
+ throw new Error(
27592
+ "Could not resolve the origin GitHub repo (owner/name) for this worktree. Check that `git remote get-url origin` points at github.com."
27593
+ );
27594
+ }
27595
+ const repoArgs2 = ghRepoSelectArgs(slug);
27596
+ const headRef = ghHeadRef(slug, opts.head);
27597
+ const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
27598
+ const bodyText = clampGithubPrBody(opts.body ?? opts.title);
27599
+ const bodyFile = (0, import_node_path59.join)(
27600
+ (0, import_node_os19.tmpdir)(),
27601
+ `sideboard-pr-body-${process.pid}-${(0, import_node_crypto18.randomBytes)(6).toString("hex")}.md`
27602
+ );
27603
+ (0, import_node_fs60.writeFileSync)(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
27604
+ `, "utf8");
27605
+ const bodyArgs = ["--body-file", bodyFile];
27606
+ try {
27607
+ return await createOrUpdatePrWithBodyFile(worktreePath, {
27608
+ ...opts,
27609
+ slug,
27610
+ repoArgs: repoArgs2,
27611
+ headRef,
27612
+ branchOnly,
27613
+ bodyArgs
27614
+ });
27615
+ } finally {
27616
+ try {
27617
+ (0, import_node_fs60.unlinkSync)(bodyFile);
27618
+ } catch {
27619
+ }
27620
+ }
27621
+ }
27622
+ async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
27623
+ const { slug, repoArgs: repoArgs2, headRef, branchOnly, bodyArgs } = opts;
27624
+ const existing = await gh(
27625
+ [...repoArgs2, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
27626
+ worktreePath,
27627
+ { reject: false }
27628
+ );
27629
+ if (existing.exitCode === 0 && existing.stdout.trim() && !opts.web) {
27630
+ const url2 = existing.stdout.trim();
27631
+ await gh(
27632
+ [
27633
+ ...repoArgs2,
27634
+ "pr",
27635
+ "edit",
27636
+ branchOnly,
27637
+ "--title",
27638
+ opts.title,
27639
+ ...bodyArgs
27640
+ ],
27641
+ worktreePath,
27642
+ { reject: false }
27643
+ );
27644
+ return url2;
27645
+ }
27646
+ if (opts.web) {
27647
+ const args2 = [
27648
+ ...repoArgs2,
27649
+ "pr",
27650
+ "create",
27651
+ "--web",
27652
+ "--title",
27653
+ opts.title,
27654
+ ...bodyArgs,
27655
+ "--base",
27656
+ opts.base,
27657
+ "--head",
27658
+ headRef
27659
+ ];
27660
+ if (opts.draft) args2.push("--draft");
27661
+ args2.push(...ghPrCreateAssigneeArgs(opts.assignee));
27662
+ await gh(args2, worktreePath, { reject: false });
27663
+ const again = await gh(
27664
+ [...repoArgs2, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
27665
+ worktreePath,
27666
+ { reject: false }
27667
+ );
27668
+ return again.stdout.trim() || "";
27669
+ }
27670
+ const args = [
27671
+ ...repoArgs2,
27672
+ "pr",
27673
+ "create",
27674
+ "--title",
27675
+ opts.title,
27676
+ ...bodyArgs,
27677
+ "--base",
27678
+ opts.base,
27679
+ "--head",
27680
+ headRef
27681
+ ];
27682
+ if (opts.draft) args.push("--draft");
27683
+ args.push(...ghPrCreateAssigneeArgs(opts.assignee));
27684
+ const created = await gh(args, worktreePath, { reject: false });
27685
+ if (created.exitCode !== 0) {
27686
+ const raw = created.stderr.trim() || created.stdout.trim() || "gh pr create failed";
27687
+ throw new Error(await formatPrCreateFailure(raw, worktreePath, { slug, head: headRef }));
27688
+ }
27689
+ const url = created.stdout.trim().split("\n").find((l) => l.startsWith("http")) ?? created.stdout.trim();
27690
+ return url;
27691
+ }
27692
+ var import_node_crypto18, import_node_fs60, import_node_os19, import_node_path59;
27693
+ var init_pr_write = __esm({
27694
+ "src/git/pr-write.ts"() {
27695
+ "use strict";
27696
+ import_node_crypto18 = require("crypto");
27697
+ import_node_fs60 = require("fs");
27698
+ import_node_os19 = require("os");
27699
+ import_node_path59 = require("path");
27700
+ init_gh_errors();
27701
+ init_run();
27702
+ init_cursor_pr();
27703
+ init_worktree();
27704
+ }
27705
+ });
27706
+
27083
27707
  // src/diff/diff.ts
27084
27708
  async function inspectGitWorktree(worktreePath) {
27085
- if (!worktreePath || !(0, import_node_fs60.existsSync)(worktreePath)) return "missing_worktree";
27709
+ if (!worktreePath || !(0, import_node_fs61.existsSync)(worktreePath)) return "missing_worktree";
27086
27710
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
27087
27711
  reject: false
27088
27712
  });
@@ -27090,7 +27714,7 @@ async function inspectGitWorktree(worktreePath) {
27090
27714
  return "ok";
27091
27715
  }
27092
27716
  async function initializeGitRepository(worktreePath) {
27093
- if (!worktreePath || !(0, import_node_fs60.existsSync)(worktreePath)) {
27717
+ if (!worktreePath || !(0, import_node_fs61.existsSync)(worktreePath)) {
27094
27718
  throw new Error("Worktree not found");
27095
27719
  }
27096
27720
  const status = await inspectGitWorktree(worktreePath);
@@ -27224,11 +27848,11 @@ new file mode 100644
27224
27848
  };
27225
27849
  }
27226
27850
  async function untrackedPatch(worktreePath, path2, maxHunk) {
27227
- const abs = (0, import_node_path59.join)(worktreePath, path2);
27851
+ const abs = (0, import_node_path60.join)(worktreePath, path2);
27228
27852
  try {
27229
- const st = (0, import_node_fs60.statSync)(abs);
27853
+ const st = (0, import_node_fs61.statSync)(abs);
27230
27854
  if (st.isFile() && st.size > maxHunk) {
27231
- const buf = (0, import_node_fs60.readFileSync)(abs).subarray(0, maxHunk);
27855
+ const buf = (0, import_node_fs61.readFileSync)(abs).subarray(0, maxHunk);
27232
27856
  return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
27233
27857
  }
27234
27858
  } catch {
@@ -27695,9 +28319,9 @@ async function getDiff(worktreePath, repoPath, opts) {
27695
28319
  }
27696
28320
  function statWorktreePath(worktreePath, relativePath) {
27697
28321
  assertSafeRelativePath(relativePath);
27698
- const abs = (0, import_node_path59.join)(worktreePath, relativePath);
27699
- if (!(0, import_node_fs60.existsSync)(abs)) return "missing";
27700
- const st = (0, import_node_fs60.statSync)(abs);
28322
+ const abs = (0, import_node_path60.join)(worktreePath, relativePath);
28323
+ if (!(0, import_node_fs61.existsSync)(abs)) return "missing";
28324
+ const st = (0, import_node_fs61.statSync)(abs);
27701
28325
  if (st.isDirectory()) return "dir";
27702
28326
  if (st.isFile()) return "file";
27703
28327
  return "missing";
@@ -27726,8 +28350,8 @@ function isImageRelativePath(relativePath) {
27726
28350
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
27727
28351
  assertSafeRelativePath(relativePath);
27728
28352
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
27729
- const abs = (0, import_node_path59.join)(worktreePath, relativePath);
27730
- const st = (0, import_node_fs60.statSync)(abs);
28353
+ const abs = (0, import_node_path60.join)(worktreePath, relativePath);
28354
+ const st = (0, import_node_fs61.statSync)(abs);
27731
28355
  if (!st.isFile()) {
27732
28356
  throw new Error(`Not a file: ${relativePath}`);
27733
28357
  }
@@ -27736,7 +28360,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
27736
28360
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
27737
28361
  );
27738
28362
  }
27739
- const buf = (0, import_node_fs60.readFileSync)(abs);
28363
+ const buf = (0, import_node_fs61.readFileSync)(abs);
27740
28364
  return {
27741
28365
  path: relativePath,
27742
28366
  contentBase64: buf.toString("base64"),
@@ -27746,12 +28370,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
27746
28370
  function readWorktreeFile(worktreePath, relativePath, opts) {
27747
28371
  assertSafeRelativePath(relativePath);
27748
28372
  const maxBytes = opts?.maxBytes ?? 2e5;
27749
- const abs = (0, import_node_path59.join)(worktreePath, relativePath);
27750
- const st = (0, import_node_fs60.statSync)(abs);
28373
+ const abs = (0, import_node_path60.join)(worktreePath, relativePath);
28374
+ const st = (0, import_node_fs61.statSync)(abs);
27751
28375
  if (!st.isFile()) {
27752
28376
  throw new Error(`Not a file: ${relativePath}`);
27753
28377
  }
27754
- const buf = (0, import_node_fs60.readFileSync)(abs);
28378
+ const buf = (0, import_node_fs61.readFileSync)(abs);
27755
28379
  if (isImageRelativePath(relativePath)) {
27756
28380
  const maxImageBytes = Math.max(maxBytes, 15e6);
27757
28381
  const truncated2 = buf.length > maxImageBytes;
@@ -27794,9 +28418,9 @@ function assertSafeRelativePath(relativePath) {
27794
28418
  }
27795
28419
  function writeWorktreeFile(worktreePath, relativePath, content) {
27796
28420
  assertSafeRelativePath(relativePath);
27797
- const abs = (0, import_node_path59.join)(worktreePath, relativePath);
27798
- (0, import_node_fs60.mkdirSync)((0, import_node_path59.dirname)(abs), { recursive: true });
27799
- (0, import_node_fs60.writeFileSync)(abs, content, "utf8");
28421
+ const abs = (0, import_node_path60.join)(worktreePath, relativePath);
28422
+ (0, import_node_fs61.mkdirSync)((0, import_node_path60.dirname)(abs), { recursive: true });
28423
+ (0, import_node_fs61.writeFileSync)(abs, content, "utf8");
27800
28424
  return { path: relativePath };
27801
28425
  }
27802
28426
  async function getWorktreeDirtyStat(worktreePath) {
@@ -27836,12 +28460,12 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
27836
28460
  truncated: full.files.length > maxFiles
27837
28461
  };
27838
28462
  }
27839
- var import_node_fs60, import_node_path59, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
28463
+ var import_node_fs61, import_node_path60, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
27840
28464
  var init_diff = __esm({
27841
28465
  "src/diff/diff.ts"() {
27842
28466
  "use strict";
27843
- import_node_fs60 = require("fs");
27844
- import_node_path59 = require("path");
28467
+ import_node_fs61 = require("fs");
28468
+ import_node_path60 = require("path");
27845
28469
  init_run();
27846
28470
  init_worktree();
27847
28471
  mergeBaseCache = /* @__PURE__ */ new Map();
@@ -28050,6 +28674,7 @@ async function confirmLand(thread, opts) {
28050
28674
  var init_land = __esm({
28051
28675
  "src/land/land.ts"() {
28052
28676
  "use strict";
28677
+ init_pr_write();
28053
28678
  init_worktree();
28054
28679
  init_diff();
28055
28680
  init_pr_metadata();
@@ -28063,16 +28688,16 @@ function readWorktreeFileTail(worktreePath, relativePath, maxBytes = WORKTREE_TA
28063
28688
  if (!relativePath || relativePath.includes("..") || relativePath.startsWith("/")) {
28064
28689
  throw new Error("Invalid path");
28065
28690
  }
28066
- const abs = (0, import_node_path60.join)(worktreePath, relativePath);
28067
- const fd = (0, import_node_fs61.openSync)(abs, "r");
28691
+ const abs = (0, import_node_path61.join)(worktreePath, relativePath);
28692
+ const fd = (0, import_node_fs62.openSync)(abs, "r");
28068
28693
  try {
28069
- const st = (0, import_node_fs61.fstatSync)(fd);
28694
+ const st = (0, import_node_fs62.fstatSync)(fd);
28070
28695
  if (!st.isFile()) throw new Error(`Not a file: ${relativePath}`);
28071
28696
  const size = st.size;
28072
28697
  const start = Math.max(0, size - maxBytes);
28073
28698
  const length = size - start;
28074
28699
  const buf = Buffer.alloc(length);
28075
- if (length > 0) (0, import_node_fs61.readSync)(fd, buf, 0, length, start);
28700
+ if (length > 0) (0, import_node_fs62.readSync)(fd, buf, 0, length, start);
28076
28701
  if (buf.subarray(0, Math.min(buf.length, 8e3)).includes(0)) {
28077
28702
  return {
28078
28703
  path: relativePath,
@@ -28090,15 +28715,15 @@ function readWorktreeFileTail(worktreePath, relativePath, maxBytes = WORKTREE_TA
28090
28715
  }
28091
28716
  return { path: relativePath, content, truncated, binary: false, encoding: "utf8" };
28092
28717
  } finally {
28093
- (0, import_node_fs61.closeSync)(fd);
28718
+ (0, import_node_fs62.closeSync)(fd);
28094
28719
  }
28095
28720
  }
28096
- var import_node_fs61, import_node_path60, WORKTREE_TAIL_BYTES;
28721
+ var import_node_fs62, import_node_path61, WORKTREE_TAIL_BYTES;
28097
28722
  var init_read_worktree_tail = __esm({
28098
28723
  "src/diff/read-worktree-tail.ts"() {
28099
28724
  "use strict";
28100
- import_node_fs61 = require("fs");
28101
- import_node_path60 = require("path");
28725
+ import_node_fs62 = require("fs");
28726
+ import_node_path61 = require("path");
28102
28727
  WORKTREE_TAIL_BYTES = 2e5;
28103
28728
  }
28104
28729
  });
@@ -28247,7 +28872,7 @@ function parseFrontmatter(content) {
28247
28872
  }
28248
28873
  function readSkill(skillMd, source) {
28249
28874
  try {
28250
- const content = (0, import_node_fs62.readFileSync)(skillMd, "utf8");
28875
+ const content = (0, import_node_fs63.readFileSync)(skillMd, "utf8");
28251
28876
  const { name: fmName, description } = parseFrontmatter(content);
28252
28877
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
28253
28878
  const name = fmName || dirName;
@@ -28266,19 +28891,19 @@ function readSkill(skillMd, source) {
28266
28891
  }
28267
28892
  }
28268
28893
  function scanSkillsDir(dir, source, out) {
28269
- if (!(0, import_node_fs62.existsSync)(dir)) return;
28894
+ if (!(0, import_node_fs63.existsSync)(dir)) return;
28270
28895
  let entries;
28271
28896
  try {
28272
- entries = (0, import_node_fs62.readdirSync)(dir);
28897
+ entries = (0, import_node_fs63.readdirSync)(dir);
28273
28898
  } catch {
28274
28899
  return;
28275
28900
  }
28276
28901
  for (const entry of entries) {
28277
28902
  if (entry.startsWith(".")) continue;
28278
- const skillMd = (0, import_node_path61.join)(dir, entry, "SKILL.md");
28279
- if (!(0, import_node_fs62.existsSync)(skillMd)) continue;
28903
+ const skillMd = (0, import_node_path62.join)(dir, entry, "SKILL.md");
28904
+ if (!(0, import_node_fs63.existsSync)(skillMd)) continue;
28280
28905
  try {
28281
- if (!(0, import_node_fs62.statSync)(skillMd).isFile()) continue;
28906
+ if (!(0, import_node_fs63.statSync)(skillMd).isFile()) continue;
28282
28907
  } catch {
28283
28908
  continue;
28284
28909
  }
@@ -28287,24 +28912,24 @@ function scanSkillsDir(dir, source, out) {
28287
28912
  }
28288
28913
  }
28289
28914
  function scanClaudePluginSkills(pluginsRoot, out) {
28290
- if (!(0, import_node_fs62.existsSync)(pluginsRoot)) return;
28915
+ if (!(0, import_node_fs63.existsSync)(pluginsRoot)) return;
28291
28916
  const walk = (dir, depth, lookingForSkillsDir) => {
28292
28917
  if (depth > 7) return;
28293
28918
  let entries;
28294
28919
  try {
28295
- entries = (0, import_node_fs62.readdirSync)(dir);
28920
+ entries = (0, import_node_fs63.readdirSync)(dir);
28296
28921
  } catch {
28297
28922
  return;
28298
28923
  }
28299
28924
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
28300
- const skill = readSkill((0, import_node_path61.join)(dir, "SKILL.md"), "cli");
28925
+ const skill = readSkill((0, import_node_path62.join)(dir, "SKILL.md"), "cli");
28301
28926
  if (skill) out.push(skill);
28302
28927
  }
28303
28928
  for (const entry of entries) {
28304
28929
  if (entry === "node_modules" || entry === ".git") continue;
28305
- const full = (0, import_node_path61.join)(dir, entry);
28930
+ const full = (0, import_node_path62.join)(dir, entry);
28306
28931
  try {
28307
- if (!(0, import_node_fs62.statSync)(full).isDirectory()) continue;
28932
+ if (!(0, import_node_fs63.statSync)(full).isDirectory()) continue;
28308
28933
  } catch {
28309
28934
  continue;
28310
28935
  }
@@ -28322,17 +28947,17 @@ function discoverSkills(worktreePath) {
28322
28947
  const home = (0, import_node_os20.homedir)();
28323
28948
  const collected = [];
28324
28949
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
28325
- scanSkillsDir((0, import_node_path61.join)(worktreePath, rel), "workspace", collected);
28950
+ scanSkillsDir((0, import_node_path62.join)(worktreePath, rel), "workspace", collected);
28326
28951
  }
28327
28952
  for (const abs of [
28328
- (0, import_node_path61.join)(home, ".claude/skills"),
28329
- (0, import_node_path61.join)(home, ".cursor/skills"),
28330
- (0, import_node_path61.join)(home, ".sideboard/skills"),
28331
- (0, import_node_path61.join)(home, ".brightsy/skills")
28953
+ (0, import_node_path62.join)(home, ".claude/skills"),
28954
+ (0, import_node_path62.join)(home, ".cursor/skills"),
28955
+ (0, import_node_path62.join)(home, ".sideboard/skills"),
28956
+ (0, import_node_path62.join)(home, ".brightsy/skills")
28332
28957
  ]) {
28333
28958
  scanSkillsDir(abs, "user", collected);
28334
28959
  }
28335
- scanClaudePluginSkills((0, import_node_path61.join)(home, ".claude/plugins"), collected);
28960
+ scanClaudePluginSkills((0, import_node_path62.join)(home, ".claude/plugins"), collected);
28336
28961
  collected.push(...bundledSkills());
28337
28962
  const rank = {
28338
28963
  workspace: 0,
@@ -28358,7 +28983,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
28358
28983
  \u2026(truncated)` : body;
28359
28984
  }
28360
28985
  }
28361
- const raw = (0, import_node_fs62.readFileSync)(skillPath, "utf8");
28986
+ const raw = (0, import_node_fs63.readFileSync)(skillPath, "utf8");
28362
28987
  if (raw.startsWith("---")) {
28363
28988
  const end = raw.indexOf("\n---", 3);
28364
28989
  if (end >= 0) {
@@ -28372,13 +28997,13 @@ function readSkillBody(skillPath, maxChars = 12e3) {
28372
28997
 
28373
28998
  \u2026(truncated)` : raw;
28374
28999
  }
28375
- var import_node_fs62, import_node_os20, import_node_path61, BUNDLED_SKILL_PREFIX;
29000
+ var import_node_fs63, import_node_os20, import_node_path62, BUNDLED_SKILL_PREFIX;
28376
29001
  var init_discover = __esm({
28377
29002
  "src/skills/discover.ts"() {
28378
29003
  "use strict";
28379
- import_node_fs62 = require("fs");
29004
+ import_node_fs63 = require("fs");
28380
29005
  import_node_os20 = require("os");
28381
- import_node_path61 = require("path");
29006
+ import_node_path62 = require("path");
28382
29007
  init_long_running();
28383
29008
  BUNDLED_SKILL_PREFIX = "bundled:";
28384
29009
  }
@@ -28589,7 +29214,7 @@ function promptForJoinedSteer(input) {
28589
29214
  rewriteTrailingUserPrompt(input.threadId, input.promptText, input.latestPrompt);
28590
29215
  const { agentPrompt: expandedLatest } = expandComposerPrompt(
28591
29216
  input.worktreePath,
28592
- expandCanonicalGitRequest(input.latestPrompt),
29217
+ expandCanonicalGitRequest(input.latestPrompt, { forge: input.gitForge }),
28593
29218
  { attachments: input.attachments }
28594
29219
  );
28595
29220
  return input.expandedPrompt && input.agentPrompt.endsWith(input.expandedPrompt) ? input.agentPrompt.slice(0, -input.expandedPrompt.length) + expandedLatest : `${input.agentPrompt}
@@ -28697,6 +29322,33 @@ var init_enqueue_thread_send = __esm({
28697
29322
  }
28698
29323
  });
28699
29324
 
29325
+ // src/agents/forge-git-directives.ts
29326
+ function formatCursorGitDirective(slug) {
29327
+ const repo = slug?.trim() || "<origin-owner/name>";
29328
+ return [
29329
+ `Git host (Settings \u2192 Git \u2192 Cursor): this checkout is on Cursor (\`origin.cursor.com\`, \`${repo}\`).`,
29330
+ "- Use the `origin` CLI, not `gh`. Where commands overlap, flags follow `gh` (`origin <command> --help`).",
29331
+ "- `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.",
29332
+ "- 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.",
29333
+ "- Origin has no issue tracker. Tickets for this repo come from Linear or AbleTime via `list_issues`. Do not use `gh issue`."
29334
+ ].join("\n");
29335
+ }
29336
+ function formatEntireGitDirective(slug) {
29337
+ const repo = slug?.trim() || "<origin-owner/name>";
29338
+ return [
29339
+ `Git host (Settings \u2192 Git \u2192 Entire): this checkout is on Entire (\`${repo}\`).`,
29340
+ "- Use the `entire` CLI, not `gh` or `origin`. Trails replace pull requests and GitHub issues.",
29341
+ "- `entire` uses the login from Settings \u2192 Git \u2192 Entire. Do not read tokens, set `ENTIRE_TOKEN`, or run `entire login` from this turn.",
29342
+ "- 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.",
29343
+ "- Tickets for this repo are Trails (`entire trail list` / `entire trail show`). Do not use `gh issue`."
29344
+ ].join("\n");
29345
+ }
29346
+ var init_forge_git_directives = __esm({
29347
+ "src/agents/forge-git-directives.ts"() {
29348
+ "use strict";
29349
+ }
29350
+ });
29351
+
28700
29352
  // src/skills/detached-job-path.ts
28701
29353
  function compiledModuleDir() {
28702
29354
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
@@ -28704,7 +29356,7 @@ function compiledModuleDir() {
28704
29356
  try {
28705
29357
  const url = import_meta8.url;
28706
29358
  if (typeof url === "string" && url.length > 0) {
28707
- return (0, import_node_path62.dirname)((0, import_node_url5.fileURLToPath)(url));
29359
+ return (0, import_node_path63.dirname)((0, import_node_url5.fileURLToPath)(url));
28708
29360
  }
28709
29361
  } catch {
28710
29362
  }
@@ -28712,8 +29364,8 @@ function compiledModuleDir() {
28712
29364
  }
28713
29365
  function detachedJobScriptIn(dir) {
28714
29366
  for (const name of DETACHED_JOB_FILENAMES) {
28715
- const script = (0, import_node_path62.join)(dir, "scripts", name);
28716
- if ((0, import_node_fs63.existsSync)(script)) return script;
29367
+ const script = (0, import_node_path63.join)(dir, "scripts", name);
29368
+ if ((0, import_node_fs64.existsSync)(script)) return script;
28717
29369
  }
28718
29370
  return null;
28719
29371
  }
@@ -28730,7 +29382,7 @@ function resolveDetachedJobScript() {
28730
29382
  for (let i = 0; i < 8; i++) {
28731
29383
  const candidate = detachedJobScriptIn(dir);
28732
29384
  if (candidate) return candidate;
28733
- const parent = (0, import_node_path62.dirname)(dir);
29385
+ const parent = (0, import_node_path63.dirname)(dir);
28734
29386
  if (parent === dir) break;
28735
29387
  dir = parent;
28736
29388
  }
@@ -28741,12 +29393,12 @@ function formatDetachedJobInvoke(scriptPath) {
28741
29393
  if (resolved) return `node ${JSON.stringify(resolved)}`;
28742
29394
  return "node scripts/detached-job.cjs";
28743
29395
  }
28744
- var import_node_fs63, import_node_path62, import_node_url5, import_meta8, DETACHED_JOB_FILENAMES;
29396
+ var import_node_fs64, import_node_path63, import_node_url5, import_meta8, DETACHED_JOB_FILENAMES;
28745
29397
  var init_detached_job_path = __esm({
28746
29398
  "src/skills/detached-job-path.ts"() {
28747
29399
  "use strict";
28748
- import_node_fs63 = require("fs");
28749
- import_node_path62 = require("path");
29400
+ import_node_fs64 = require("fs");
29401
+ import_node_path63 = require("path");
28750
29402
  import_node_url5 = require("url");
28751
29403
  init_packaged_runtime();
28752
29404
  import_meta8 = {};
@@ -28834,17 +29486,35 @@ function formatWorktreeDirective(thread, opts) {
28834
29486
  lines.push(
28835
29487
  "- 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)."
28836
29488
  );
28837
- lines.push(formatGitAuthModeDirective(opts?.gitAuthMode ?? "auto"));
29489
+ const cursorRepo = opts?.originForge === "cursor";
29490
+ const entireRepo = opts?.originForge === "entire";
29491
+ if (cursorRepo) {
29492
+ lines.push(formatCursorGitDirective(opts?.originSlug));
29493
+ } else if (entireRepo) {
29494
+ lines.push(formatEntireGitDirective(opts?.originSlug));
29495
+ } else {
29496
+ lines.push(formatGitAuthModeDirective(opts?.gitAuthMode ?? "auto"));
29497
+ }
28838
29498
  lines.push(
28839
29499
  "- 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."
28840
29500
  );
28841
29501
  lines.push(
28842
- "- 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`)."
29502
+ 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`)."
28843
29503
  );
28844
29504
  if (thread.prUrl) {
28845
29505
  lines.push(
28846
29506
  `- A PR already exists (${thread.prUrl}). Update it (push + edit title/body if the purpose drifted) instead of opening a duplicate.`
28847
29507
  );
29508
+ } else if (entireRepo) {
29509
+ lines.push(
29510
+ '- 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`.'
29511
+ );
29512
+ } else if (cursorRepo) {
29513
+ const slug = opts?.originSlug?.trim() || "<origin-owner/name>";
29514
+ const resolve3 = opts?.originSlug?.trim() ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
29515
+ lines.push(
29516
+ `- 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\`.`
29517
+ );
28848
29518
  } else {
28849
29519
  const slug = opts?.githubSlug ?? "<origin-owner/name>";
28850
29520
  const resolve3 = opts?.githubSlug ? "" : " Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree.";
@@ -28984,9 +29654,9 @@ function formatPrGateDirective() {
28984
29654
  }
28985
29655
  function formatProcessGuideDirective(opts) {
28986
29656
  const worktree = opts?.worktreePath?.trim();
28987
- const hasGraphSkill = !!worktree && (0, import_node_fs64.existsSync)((0, import_node_path63.join)(worktree, ".claude/skills/graph-engineering/SKILL.md"));
28988
- const hasReviewSkill = !!worktree && (0, import_node_fs64.existsSync)((0, import_node_path63.join)(worktree, ".claude/skills/review/SKILL.md"));
28989
- const hasInstallSkill = !!worktree && (0, import_node_fs64.existsSync)((0, import_node_path63.join)(worktree, ".claude/skills/worktree-install/SKILL.md"));
29657
+ const hasGraphSkill = !!worktree && (0, import_node_fs65.existsSync)((0, import_node_path64.join)(worktree, ".claude/skills/graph-engineering/SKILL.md"));
29658
+ const hasReviewSkill = !!worktree && (0, import_node_fs65.existsSync)((0, import_node_path64.join)(worktree, ".claude/skills/review/SKILL.md"));
29659
+ const hasInstallSkill = !!worktree && (0, import_node_fs65.existsSync)((0, import_node_path64.join)(worktree, ".claude/skills/worktree-install/SKILL.md"));
28990
29660
  return [
28991
29661
  "Process guides (recurring work only):",
28992
29662
  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,
@@ -29026,13 +29696,14 @@ function formatArtifactDirective() {
29026
29696
  function formatUiReminder() {
29027
29697
  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.";
29028
29698
  }
29029
- var import_node_fs64, import_node_path63, PR_GOAL_RE, GITHUB_TICKET_REF, KEYED_TICKET_REF;
29699
+ var import_node_fs65, import_node_path64, PR_GOAL_RE, GITHUB_TICKET_REF, KEYED_TICKET_REF;
29030
29700
  var init_instructions = __esm({
29031
29701
  "src/agents/instructions.ts"() {
29032
29702
  "use strict";
29033
- import_node_fs64 = require("fs");
29034
- import_node_path63 = require("path");
29703
+ import_node_fs65 = require("fs");
29704
+ import_node_path64 = require("path");
29035
29705
  init_git_auth_mode();
29706
+ init_forge_git_directives();
29036
29707
  init_branch_prefix();
29037
29708
  init_worktree_labels();
29038
29709
  init_detached_job_path();
@@ -29524,6 +30195,7 @@ function compactIssueRow(issue, opts) {
29524
30195
  function formatMcpIssueList(input) {
29525
30196
  const truncated = input.truncated || Boolean(input.commentsTruncated);
29526
30197
  const comments = input.comments ?? [];
30198
+ 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;
29527
30199
  return {
29528
30200
  ...input.source ? { source: input.source } : {},
29529
30201
  ...input.viewer ? { viewer: input.viewer } : {},
@@ -29531,9 +30203,8 @@ function formatMcpIssueList(input) {
29531
30203
  returned: input.issues.length,
29532
30204
  truncated,
29533
30205
  ...input.since ? { since: input.since } : {},
29534
- ...truncated ? {
29535
- 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."
29536
- } : {},
30206
+ ...input.unavailableReason ? { unavailableReason: input.unavailableReason } : {},
30207
+ ...pageHint ? { hint: pageHint } : {},
29537
30208
  issues: input.issues.map((issue) => compactIssueRow(issue, { since: input.since })),
29538
30209
  ...input.since || comments.length > 0 ? { comments } : {}
29539
30210
  };
@@ -29550,7 +30221,8 @@ function formatListedIssuesForMcp(result, page) {
29550
30221
  truncated: windowed.truncated,
29551
30222
  since: result.since,
29552
30223
  comments: commentWindow.items,
29553
- commentsTruncated: commentWindow.truncated
30224
+ commentsTruncated: commentWindow.truncated,
30225
+ unavailableReason: result.unavailableReason
29554
30226
  });
29555
30227
  }
29556
30228
  function mcpJson(payload, isError = false) {
@@ -29915,40 +30587,40 @@ __export(plan_file_exports, {
29915
30587
  writePlanFile: () => writePlanFile
29916
30588
  });
29917
30589
  function ensureAttachmentsGitignore(worktreePath) {
29918
- const gitignoreAbs = (0, import_node_path64.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
29919
- if ((0, import_node_fs65.existsSync)(gitignoreAbs)) return;
29920
- (0, import_node_fs65.mkdirSync)((0, import_node_path64.dirname)(gitignoreAbs), { recursive: true });
29921
- (0, import_node_fs65.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
30590
+ const gitignoreAbs = (0, import_node_path65.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
30591
+ if ((0, import_node_fs66.existsSync)(gitignoreAbs)) return;
30592
+ (0, import_node_fs66.mkdirSync)((0, import_node_path65.dirname)(gitignoreAbs), { recursive: true });
30593
+ (0, import_node_fs66.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
29922
30594
  }
29923
30595
  function planFileAbs(worktreePath) {
29924
- return (0, import_node_path64.join)(worktreePath, PLAN_FILE_REL);
30596
+ return (0, import_node_path65.join)(worktreePath, PLAN_FILE_REL);
29925
30597
  }
29926
30598
  function readTextIfPresent2(abs) {
29927
- if (!(0, import_node_fs65.existsSync)(abs)) return null;
30599
+ if (!(0, import_node_fs66.existsSync)(abs)) return null;
29928
30600
  try {
29929
- const content = (0, import_node_fs65.readFileSync)(abs, "utf8");
30601
+ const content = (0, import_node_fs66.readFileSync)(abs, "utf8");
29930
30602
  return content.trim() ? content : null;
29931
30603
  } catch {
29932
30604
  return null;
29933
30605
  }
29934
30606
  }
29935
30607
  function readPlanFile(worktreePath) {
29936
- return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path64.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path64.join)(worktreePath, LEGACY_PLAN_FILE_REL));
30608
+ return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path65.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path65.join)(worktreePath, LEGACY_PLAN_FILE_REL));
29937
30609
  }
29938
30610
  function writePlanFile(worktreePath, content) {
29939
30611
  ensureAttachmentsGitignore(worktreePath);
29940
30612
  const abs = planFileAbs(worktreePath);
29941
- (0, import_node_fs65.mkdirSync)((0, import_node_path64.dirname)(abs), { recursive: true });
30613
+ (0, import_node_fs66.mkdirSync)((0, import_node_path65.dirname)(abs), { recursive: true });
29942
30614
  const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
29943
- (0, import_node_fs65.writeFileSync)(abs, body, "utf8");
30615
+ (0, import_node_fs66.writeFileSync)(abs, body, "utf8");
29944
30616
  return PLAN_FILE_REL;
29945
30617
  }
29946
- var import_node_fs65, import_node_path64;
30618
+ var import_node_fs66, import_node_path65;
29947
30619
  var init_plan_file = __esm({
29948
30620
  "src/plan/plan-file.ts"() {
29949
30621
  "use strict";
29950
- import_node_fs65 = require("fs");
29951
- import_node_path64 = require("path");
30622
+ import_node_fs66 = require("fs");
30623
+ import_node_path65 = require("path");
29952
30624
  init_workspace_scratch();
29953
30625
  init_plan_present();
29954
30626
  init_plan_present();
@@ -29999,9 +30671,9 @@ function recoverFinishedCursorRun(opts) {
29999
30671
  return best;
30000
30672
  }
30001
30673
  function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
30002
- if (!(0, import_node_fs66.existsSync)(runsPath)) return null;
30674
+ if (!(0, import_node_fs67.existsSync)(runsPath)) return null;
30003
30675
  try {
30004
- const lines = (0, import_node_fs66.readFileSync)(runsPath, "utf8").split("\n");
30676
+ const lines = (0, import_node_fs67.readFileSync)(runsPath, "utf8").split("\n");
30005
30677
  let best = null;
30006
30678
  for (const line of lines) {
30007
30679
  const trimmed = line.trim();
@@ -30027,11 +30699,11 @@ function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
30027
30699
  return null;
30028
30700
  }
30029
30701
  }
30030
- var import_node_fs66;
30702
+ var import_node_fs67;
30031
30703
  var init_cursor_recover = __esm({
30032
30704
  "src/agents/cursor-recover.ts"() {
30033
30705
  "use strict";
30034
- import_node_fs66 = require("fs");
30706
+ import_node_fs67 = require("fs");
30035
30707
  init_cursor_store();
30036
30708
  }
30037
30709
  });
@@ -30152,7 +30824,7 @@ async function startOrchestration(opts) {
30152
30824
  }
30153
30825
  return ready;
30154
30826
  }
30155
- var import_node_events, import_node_crypto19, import_node_fs67, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton2;
30827
+ var import_node_events, import_node_crypto19, import_node_fs68, LIVE_TURN_SPAWN_GRACE_MS, RUN_SCRIPT_DESKTOP_ADOPT_MS, STALE_AGENT_PID_WAIT_MS, LIGHT_RECONCILE_THROTTLE_MS, Orchestrator, singleton2;
30156
30828
  var init_orchestrator = __esm({
30157
30829
  "src/orchestrator/orchestrator.ts"() {
30158
30830
  "use strict";
@@ -30161,7 +30833,7 @@ var init_orchestrator = __esm({
30161
30833
  init_task_state();
30162
30834
  init_wait_for_turn();
30163
30835
  import_node_crypto19 = require("crypto");
30164
- import_node_fs67 = require("fs");
30836
+ import_node_fs68 = require("fs");
30165
30837
  init_error_detail();
30166
30838
  init_run();
30167
30839
  init_stale_lock();
@@ -30360,7 +31032,7 @@ var init_orchestrator = __esm({
30360
31032
  this.lightReconcileAt.set(repoPath, Date.now());
30361
31033
  for (const thread of listThreads()) {
30362
31034
  if (isGlobalThread(thread) || thread.repoPath !== repoPath) continue;
30363
- if (!(0, import_node_fs67.existsSync)(thread.worktreePath)) {
31035
+ if (!(0, import_node_fs68.existsSync)(thread.worktreePath)) {
30364
31036
  setStatus(thread.id, "broken", "Worktree missing on disk");
30365
31037
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
30366
31038
  }
@@ -30389,7 +31061,7 @@ var init_orchestrator = __esm({
30389
31061
  }
30390
31062
  continue;
30391
31063
  }
30392
- if (!(0, import_node_fs67.existsSync)(thread.worktreePath)) {
31064
+ if (!(0, import_node_fs68.existsSync)(thread.worktreePath)) {
30393
31065
  setStatus(thread.id, "broken", "Worktree missing on disk");
30394
31066
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
30395
31067
  continue;
@@ -31113,7 +31785,8 @@ var init_orchestrator = __esm({
31113
31785
  return;
31114
31786
  }
31115
31787
  thread = this.requireThread(threadId);
31116
- const gitPrompt = expandCanonicalGitRequest(promptText);
31788
+ const turnOrigin = thread.worktreePath?.trim() ? await resolveOriginRemote(thread.worktreePath).catch(() => null) : null;
31789
+ const gitPrompt = expandCanonicalGitRequest(promptText, { forge: turnOrigin?.forge });
31117
31790
  const { agentPrompt: expandedPrompt } = expandComposerPrompt(
31118
31791
  thread.worktreePath,
31119
31792
  gitPrompt,
@@ -31200,11 +31873,9 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31200
31873
  const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2, gitBranchPrefixSetting: gitBranchPrefixSetting2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
31201
31874
  const gitAuthMode = getGithubGitAuthMode2();
31202
31875
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
31203
- githubSlug: await withTimeout(
31204
- resolveGithubRepoSlug(fresh.worktreePath),
31205
- 8e3,
31206
- "resolveGithubRepoSlug"
31207
- ).catch(() => null),
31876
+ githubSlug: turnOrigin?.forge === "github" ? turnOrigin.slug : await withTimeout(resolveGithubRepoSlug(fresh.worktreePath), 8e3, "resolveGithubRepoSlug").catch(() => null),
31877
+ originForge: turnOrigin?.forge ?? null,
31878
+ originSlug: turnOrigin?.slug ?? null,
31208
31879
  gitAuthMode,
31209
31880
  activeRuns: annotateDevAccessRuns(
31210
31881
  mergeWorktreeActiveRuns(
@@ -31297,7 +31968,8 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
31297
31968
  agentPrompt,
31298
31969
  expandedPrompt,
31299
31970
  worktreePath: fresh.worktreePath,
31300
- attachments: autoContinue ? [] : sentAttachments ?? []
31971
+ attachments: autoContinue ? [] : sentAttachments ?? [],
31972
+ gitForge: turnOrigin?.forge
31301
31973
  });
31302
31974
  const handle = await spawnAgentTurn(
31303
31975
  fresh,
@@ -32871,7 +33543,7 @@ ${formatEnvReminder(projectNameForRepoPath(thread.repoPath || thread.worktreePat
32871
33543
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
32872
33544
  return restored2;
32873
33545
  }
32874
- if (!(0, import_node_fs67.existsSync)(thread.worktreePath)) {
33546
+ if (!(0, import_node_fs68.existsSync)(thread.worktreePath)) {
32875
33547
  if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
32876
33548
  throw new Error(
32877
33549
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
@@ -33166,12 +33838,14 @@ __export(index_exports, {
33166
33838
  CONTEXT_REVIEW_PATH: () => CONTEXT_REVIEW_PATH,
33167
33839
  CONVENTION_SETUP_RELPATHS: () => CONVENTION_SETUP_RELPATHS,
33168
33840
  COORDINATOR_TOOL_PLAYBOOK: () => COORDINATOR_TOOL_PLAYBOOK,
33841
+ CURSOR_ISSUES_UNAVAILABLE: () => CURSOR_ISSUES_UNAVAILABLE,
33169
33842
  CURSOR_SDK_INSTALL_HINT: () => CURSOR_SDK_INSTALL_HINT,
33170
33843
  DEFAULT_ABLETIME_HOST: () => DEFAULT_ABLETIME_HOST,
33171
33844
  DEFAULT_RUN_LOG_TAIL_CHARS: () => DEFAULT_RUN_LOG_TAIL_CHARS,
33172
33845
  DEFAULT_WORKTREE_SORT: () => DEFAULT_WORKTREE_SORT,
33173
33846
  DETACHED_JOBS_DIR: () => DETACHED_JOBS_DIR,
33174
33847
  EMPTY_MANAGED_ACCOUNTS: () => EMPTY_MANAGED_ACCOUNTS,
33848
+ ENTIRE_ISSUES_ARE_TRAILS: () => ENTIRE_ISSUES_ARE_TRAILS,
33175
33849
  FAMOUS_SOCCER_TEAMS: () => FAMOUS_SOCCER_TEAMS,
33176
33850
  FOLLOW_UP_BEHAVIORS: () => FOLLOW_UP_BEHAVIORS,
33177
33851
  GITHUB_GIT_AUTH_MODES: () => GITHUB_GIT_AUTH_MODES,
@@ -33439,6 +34113,7 @@ __export(index_exports, {
33439
34113
  createThreadWorktree: () => createThreadWorktree,
33440
34114
  currentBranch: () => currentBranch,
33441
34115
  cursorAdapter: () => cursorAdapter,
34116
+ cursorIssuesUnavailableReason: () => cursorIssuesUnavailableReason,
33442
34117
  cursorSdkMessageToEvents: () => cursorSdkMessageToEvents,
33443
34118
  decodeBrightsyTarget: () => decodeBrightsyTarget,
33444
34119
  defaultScheduleName: () => defaultScheduleName,
@@ -33488,6 +34163,7 @@ __export(index_exports, {
33488
34163
  ensureSlackCoordinator: () => ensureSlackCoordinator,
33489
34164
  ensureSlackDeviceIdentity: () => ensureSlackDeviceIdentity,
33490
34165
  ensureWorkspace: () => ensureWorkspace,
34166
+ entireUsesTrails: () => entireUsesTrails,
33491
34167
  estimateMessageChars: () => estimateMessageChars,
33492
34168
  estimateOccupancyTokens: () => estimateOccupancyTokens,
33493
34169
  estimateThreadChars: () => estimateThreadChars,
@@ -33538,8 +34214,10 @@ __export(index_exports, {
33538
34214
  formatClaudeUsageTooltip: () => formatClaudeUsageTooltip,
33539
34215
  formatCloudTurnReply: () => formatCloudTurnReply,
33540
34216
  formatConnectedOrchestratorReminder: () => formatConnectedOrchestratorReminder,
34217
+ formatCursorGitDirective: () => formatCursorGitDirective,
33541
34218
  formatDetachedJobInvoke: () => formatDetachedJobInvoke,
33542
34219
  formatDevAccessHint: () => formatDevAccessHint,
34220
+ formatEntireGitDirective: () => formatEntireGitDirective,
33543
34221
  formatFetchError: () => formatFetchError,
33544
34222
  formatGhLandError: () => formatGhLandError,
33545
34223
  formatGitAuthModeDirective: () => formatGitAuthModeDirective,
@@ -33596,6 +34274,7 @@ __export(index_exports, {
33596
34274
  getBrightsySession: () => getBrightsySession,
33597
34275
  getCaffeinateHold: () => getCaffeinateHold,
33598
34276
  getClaudePlanUsage: () => getClaudePlanUsage,
34277
+ getCursorGitStatus: () => getCursorGitStatus,
33599
34278
  getDefaultAgent: () => getDefaultAgent,
33600
34279
  getDefaultAutonomy: () => getDefaultAutonomy,
33601
34280
  getDefaultEffort: () => getDefaultEffort,
@@ -33604,6 +34283,7 @@ __export(index_exports, {
33604
34283
  getDefaultRunScript: () => getDefaultRunScript,
33605
34284
  getDiff: () => getDiff,
33606
34285
  getDiffSummary: () => getDiffSummary,
34286
+ getEntireGitStatus: () => getEntireGitStatus,
33607
34287
  getGitHubIssue: () => getGitHubIssue,
33608
34288
  getGitHubStatus: () => getGitHubStatus,
33609
34289
  getGithubGitAuthMode: () => getGithubGitAuthMode,
@@ -33821,6 +34501,8 @@ __export(index_exports, {
33821
34501
  localhostPreviewUrl: () => localhostPreviewUrl,
33822
34502
  locksDir: () => locksDir,
33823
34503
  loginAgent: () => loginAgent,
34504
+ loginCursorGit: () => loginCursorGit,
34505
+ loginEntireGit: () => loginEntireGit,
33824
34506
  loginEnvPrefix: () => loginEnvPrefix,
33825
34507
  loginManagedAccount: () => loginManagedAccount,
33826
34508
  looksLikeComposerSkillExpansion: () => looksLikeComposerSkillExpansion,
@@ -33887,6 +34569,7 @@ __export(index_exports, {
33887
34569
  orchestrationQuotaOnLimit: () => orchestrationQuotaOnLimit,
33888
34570
  orchestrationTitleNeedsSoccerNickname: () => orchestrationTitleNeedsSoccerNickname,
33889
34571
  orchestratorSessionPoisonedByBuiltins: () => orchestratorSessionPoisonedByBuiltins,
34572
+ originCli: () => originCli,
33890
34573
  originFetchBranch: () => originFetchBranch,
33891
34574
  originGhRepoEnv: () => originGhRepoEnv,
33892
34575
  outboundReplyFromTurn: () => outboundReplyFromTurn,
@@ -33897,11 +34580,13 @@ __export(index_exports, {
33897
34580
  parseCursorRunnerLine: () => parseCursorRunnerLine,
33898
34581
  parseDevPreview: () => parseDevPreview,
33899
34582
  parseDurationMs: () => parseDurationMs,
34583
+ parseEntireAuthContexts: () => parseEntireAuthContexts,
33900
34584
  parseForceStopMessage: () => parseForceStopMessage,
33901
34585
  parseGhStackViewJson: () => parseGhStackViewJson,
33902
34586
  parseGitHubIssueNumber: () => parseGitHubIssueNumber,
33903
34587
  parseGithubSlugFromRemoteUrl: () => parseGithubSlugFromRemoteUrl,
33904
34588
  parseMcpList: () => parseMcpList,
34589
+ parseOriginAuthStatus: () => parseOriginAuthStatus,
33905
34590
  parsePlanQuestionsInput: () => parsePlanQuestionsInput,
33906
34591
  parsePrDetailsView: () => parsePrDetailsView,
33907
34592
  parseSessionQuotaResetAt: () => parseSessionQuotaResetAt,
@@ -34004,9 +34689,11 @@ __export(index_exports, {
34004
34689
  resolveNewThreadOptions: () => resolveNewThreadOptions,
34005
34690
  resolveOrchChildFollowUp: () => resolveOrchChildFollowUp,
34006
34691
  resolveOrchestratorDefaults: () => resolveOrchestratorDefaults,
34692
+ resolveOriginRemote: () => resolveOriginRemote,
34007
34693
  resolvePlanMarkdown: () => resolvePlanMarkdown,
34008
34694
  resolvePrSelector: () => resolvePrSelector,
34009
34695
  resolvePrSelectors: () => resolvePrSelectors,
34696
+ resolveProjectRemoteTag: () => resolveProjectRemoteTag,
34010
34697
  resolveProjectReviewLabel: () => resolveProjectReviewLabel,
34011
34698
  resolveQuotaFallbackAgent: () => resolveQuotaFallbackAgent,
34012
34699
  resolveRepoRoot: () => resolveRepoRoot,
@@ -34057,6 +34744,7 @@ __export(index_exports, {
34057
34744
  setStatus: () => setStatus,
34058
34745
  setVaultMasterKey: () => setVaultMasterKey,
34059
34746
  settingsSourceLabel: () => settingsSourceLabel,
34747
+ setupCursorGit: () => setupCursorGit,
34060
34748
  shiftQueuedItem: () => shiftQueuedItem,
34061
34749
  shouldAttachPastedText: () => shouldAttachPastedText,
34062
34750
  shouldCompactContext: () => shouldCompactContext,
@@ -35897,7 +36585,7 @@ function listPhoneSidebar(viewerLogin = phoneViewerLogin()) {
35897
36585
  const badge = classifyWorktreeListBadges(group, viewerLogin, resolveProjectReviewLabel(settings, path2))[0];
35898
36586
  const blockedBy = issueBlockedByIds(group);
35899
36587
  return {
35900
- label: worktreeAnchorLabel(group).title,
36588
+ ...workspaceRowLabel(group),
35901
36589
  chats: group.map(summarize),
35902
36590
  ...tags.length > 0 ? { tags } : {},
35903
36591
  ...blockedBy.length > 0 ? { blockedBy } : {},
@@ -36343,8 +37031,8 @@ init_message_parts();
36343
37031
  init_turn_live();
36344
37032
 
36345
37033
  // src/remote/phone-media.ts
36346
- var import_node_fs68 = require("fs");
36347
- var import_node_path65 = require("path");
37034
+ var import_node_fs69 = require("fs");
37035
+ var import_node_path66 = require("path");
36348
37036
  init_diff();
36349
37037
  var PHONE_IMAGE_MAX_BYTES = 15e5;
36350
37038
  var PHONE_TEXT_MAX_BYTES = 18e4;
@@ -36397,7 +37085,7 @@ function phoneImageDataUrl(worktreePath, raw) {
36397
37085
  }
36398
37086
  function phoneDirectoryListing(worktreePath, relative2) {
36399
37087
  try {
36400
- const entries = (0, import_node_fs68.readdirSync)((0, import_node_path65.join)(worktreePath, relative2), { withFileTypes: true });
37088
+ const entries = (0, import_node_fs69.readdirSync)((0, import_node_path66.join)(worktreePath, relative2), { withFileTypes: true });
36401
37089
  const dirs = new Set(entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
36402
37090
  const names = entries.map((entry) => entry.name).filter((name) => name && !name.includes("/") && !name.includes("\0")).sort((a, b) => a.localeCompare(b));
36403
37091
  const shown = names.slice(0, PHONE_DIR_MAX);
@@ -36423,7 +37111,7 @@ function phoneFilePreview(worktreePath, relative2, kind) {
36423
37111
  }
36424
37112
  if (relative2.toLowerCase().endsWith(".svg")) {
36425
37113
  try {
36426
- const buf = (0, import_node_fs68.readFileSync)((0, import_node_path65.join)(worktreePath, relative2));
37114
+ const buf = (0, import_node_fs69.readFileSync)((0, import_node_path66.join)(worktreePath, relative2));
36427
37115
  const truncated = buf.length > PHONE_TEXT_MAX_BYTES;
36428
37116
  return {
36429
37117
  content: buf.subarray(0, PHONE_TEXT_MAX_BYTES).toString("utf8"),
@@ -36719,9 +37407,9 @@ function githubProjectsOnIssue(raw) {
36719
37407
  }
36720
37408
  return out;
36721
37409
  }
36722
- async function readGitHubIssueProjects(number, cwd, repoArgs) {
37410
+ async function readGitHubIssueProjects(number, cwd, repoArgs2) {
36723
37411
  const result = await gh(
36724
- ["issue", "view", String(number), ...repoArgs, "--json", "projectItems"],
37412
+ ["issue", "view", String(number), ...repoArgs2, "--json", "projectItems"],
36725
37413
  cwd,
36726
37414
  { reject: false }
36727
37415
  );
@@ -36739,8 +37427,8 @@ async function readGitHubIssueProjects(number, cwd, repoArgs) {
36739
37427
  return { projects: [] };
36740
37428
  }
36741
37429
  }
36742
- async function listGitHubIssueProjectNames(number, cwd, repoArgs) {
36743
- const read = await readGitHubIssueProjects(number, cwd, repoArgs);
37430
+ async function listGitHubIssueProjectNames(number, cwd, repoArgs2) {
37431
+ const read = await readGitHubIssueProjects(number, cwd, repoArgs2);
36744
37432
  return read.projects.map((project) => project.name).filter(Boolean);
36745
37433
  }
36746
37434
  function parseGitHubProjectList(stdout) {
@@ -36958,14 +37646,14 @@ async function listGitHubIssueCommentsSince(opts) {
36958
37646
  }
36959
37647
  async function getGitHubIssue(id, opts) {
36960
37648
  const number = parseGitHubIssueNumber(id);
36961
- const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
37649
+ const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
36962
37650
  const stdout = requireGhOk(
36963
37651
  await gh(
36964
37652
  [
36965
37653
  "issue",
36966
37654
  "view",
36967
37655
  String(number),
36968
- ...repoArgs,
37656
+ ...repoArgs2,
36969
37657
  "--json",
36970
37658
  "number,title,body,url,state,labels,assignees,comments,author"
36971
37659
  ],
@@ -36984,7 +37672,7 @@ async function getGitHubIssue(id, opts) {
36984
37672
  throw new Error(`GitHub issue not found: #${number}`);
36985
37673
  }
36986
37674
  const issue = toGitHubIssue(parsed);
36987
- const projects = await readGitHubIssueProjects(number, cwd, repoArgs);
37675
+ const projects = await readGitHubIssueProjects(number, cwd, repoArgs2);
36988
37676
  issue.projects = projects.projects;
36989
37677
  if (projects.error) issue.projectsError = projects.error;
36990
37678
  return issue;
@@ -36993,10 +37681,10 @@ async function commentGitHubIssue(input, opts) {
36993
37681
  const number = parseGitHubIssueNumber(input.id);
36994
37682
  const body = input.body.trim();
36995
37683
  if (!body) throw new Error("GitHub comment body is required");
36996
- const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
37684
+ const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
36997
37685
  const stdout = requireGhOk(
36998
37686
  await gh(
36999
- ["issue", "comment", String(number), ...repoArgs, "--body", body],
37687
+ ["issue", "comment", String(number), ...repoArgs2, "--body", body],
37000
37688
  cwd,
37001
37689
  { reject: false }
37002
37690
  ),
@@ -37038,21 +37726,21 @@ async function updateGitHubIssue(input, opts) {
37038
37726
  "github_update_issue needs at least one of title, body, state, labels, project, parent, relations, removeRelations"
37039
37727
  );
37040
37728
  }
37041
- const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
37729
+ const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
37042
37730
  if (state === "closed" || state === "close") {
37043
37731
  requireGhOk(
37044
- await gh(["issue", "close", String(number), ...repoArgs], cwd, { reject: false }),
37732
+ await gh(["issue", "close", String(number), ...repoArgs2], cwd, { reject: false }),
37045
37733
  `GitHub close #${number}`
37046
37734
  );
37047
37735
  } else if (state === "open" || state === "reopen") {
37048
37736
  requireGhOk(
37049
- await gh(["issue", "reopen", String(number), ...repoArgs], cwd, { reject: false }),
37737
+ await gh(["issue", "reopen", String(number), ...repoArgs2], cwd, { reject: false }),
37050
37738
  `GitHub reopen #${number}`
37051
37739
  );
37052
37740
  } else if (state) {
37053
37741
  throw new Error(`GitHub issue state must be open or closed (got ${input.state})`);
37054
37742
  }
37055
- const args = ["issue", "edit", String(number), ...repoArgs];
37743
+ const args = ["issue", "edit", String(number), ...repoArgs2];
37056
37744
  if (title) args.push("--title", title);
37057
37745
  if (body !== void 0) args.push("--body", body);
37058
37746
  if (input.labels !== void 0) {
@@ -37064,7 +37752,7 @@ async function updateGitHubIssue(input, opts) {
37064
37752
  if (remove.length) args.push("--remove-label", remove.join(","));
37065
37753
  }
37066
37754
  if (input.project !== void 0) {
37067
- const current = await listGitHubIssueProjectNames(number, cwd, repoArgs);
37755
+ const current = await listGitHubIssueProjectNames(number, cwd, repoArgs2);
37068
37756
  if (isGitHubNoneToken(input.project)) {
37069
37757
  for (const name of current) args.push("--remove-project", name);
37070
37758
  } else {
@@ -37090,7 +37778,7 @@ async function updateGitHubIssue(input, opts) {
37090
37778
  for (const rel of addRelations ?? []) {
37091
37779
  args.push(githubRelationEditFlag(rel.type, false), String(parseGitHubIssueNumber(rel.issue)));
37092
37780
  }
37093
- const editChanged = args.length > 3 + repoArgs.length;
37781
+ const editChanged = args.length > 3 + repoArgs2.length;
37094
37782
  if (editChanged) {
37095
37783
  requireGhOk(await gh(args, cwd, { reject: false }), `GitHub edit #${number}`);
37096
37784
  }
@@ -37105,8 +37793,8 @@ async function createGitHubIssue(input, opts) {
37105
37793
  parentNumber ? `Spin-off of #${parentNumber}.` : null,
37106
37794
  input.body?.trim() || null
37107
37795
  ].filter(Boolean);
37108
- const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
37109
- const args = ["issue", "create", ...repoArgs, "--title", title];
37796
+ const { cwd, repoArgs: repoArgs2 } = await resolveGitHubIssueRepo(opts?.repoPath);
37797
+ const args = ["issue", "create", ...repoArgs2, "--title", title];
37110
37798
  if (bodyParts.length) args.push("--body", bodyParts.join("\n\n"));
37111
37799
  const created = await gh([...args, "--json", "number,url,title"], cwd, { reject: false });
37112
37800
  if (created.exitCode === 0 && created.stdout.trim()) {
@@ -37219,12 +37907,12 @@ async function persistLinearTokens(data, viewer) {
37219
37907
  organizationName: viewer?.organizationName
37220
37908
  });
37221
37909
  }
37222
- async function fetchLinearViewer(accessToken2) {
37910
+ async function fetchLinearViewer(accessToken3) {
37223
37911
  const res = await httpFetch(LINEAR_GRAPHQL, {
37224
37912
  method: "POST",
37225
37913
  headers: {
37226
37914
  "Content-Type": "application/json",
37227
- Authorization: linearAuthorizationHeader(accessToken2)
37915
+ Authorization: linearAuthorizationHeader(accessToken3)
37228
37916
  },
37229
37917
  body: JSON.stringify({
37230
37918
  query: "{ viewer { name organization { name } } }"
@@ -38520,6 +39208,18 @@ async function validateLinearApiKey(apiKey) {
38520
39208
  }
38521
39209
 
38522
39210
  // src/integrations/issues.ts
39211
+ var CURSOR_ISSUES_UNAVAILABLE = "Cursor Origin has no issue tracker. Tickets for this repo come from Linear or AbleTime (Settings \u2192 Issues).";
39212
+ 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.";
39213
+ async function cursorIssuesUnavailableReason(repoPath) {
39214
+ try {
39215
+ const remote = await resolveOriginRemote(repoPath);
39216
+ if (remote?.forge === "cursor") return CURSOR_ISSUES_UNAVAILABLE;
39217
+ if (remote?.forge === "entire") return ENTIRE_ISSUES_ARE_TRAILS;
39218
+ return null;
39219
+ } catch {
39220
+ return null;
39221
+ }
39222
+ }
38523
39223
  function assigneeKey(assignee) {
38524
39224
  return (assignee ?? "").trim().toLowerCase();
38525
39225
  }
@@ -38681,6 +39381,17 @@ async function listIssues(repoPath, opts) {
38681
39381
  ...since ? { since, comments: commentsFromAbleTimeIssues(tasks, issues2, since) } : {}
38682
39382
  };
38683
39383
  }
39384
+ const unavailableReason = await cursorIssuesUnavailableReason(repoPath);
39385
+ if (unavailableReason) {
39386
+ return {
39387
+ source,
39388
+ preferredSource,
39389
+ linearConnected,
39390
+ abletimeConnected,
39391
+ issues: [],
39392
+ unavailableReason
39393
+ };
39394
+ }
38684
39395
  const [issues, login] = await Promise.all([
38685
39396
  listGitHubIssues(repoPath, { assignee, query, limit: opts?.limit, updatedSince: since }),
38686
39397
  githubViewerLogin(repoPath)
@@ -39303,7 +40014,7 @@ function handleRemoteInbound(text7, opts) {
39303
40014
  }
39304
40015
 
39305
40016
  // src/remote/phone-board-sync.ts
39306
- var import_node_path66 = require("path");
40017
+ var import_node_path67 = require("path");
39307
40018
  var import_chokidar = require("chokidar");
39308
40019
  init_orchestrator();
39309
40020
  init_paths();
@@ -39356,7 +40067,7 @@ function subscribePhoneBoard(notify) {
39356
40067
  threads.on("error", () => void 0);
39357
40068
  const data = (0, import_chokidar.watch)(appDataDir(), { ignoreInitial: true, depth: 0 });
39358
40069
  const onData = (changed) => {
39359
- const name = (0, import_node_path66.basename)(changed);
40070
+ const name = (0, import_node_path67.basename)(changed);
39360
40071
  if (name !== "workspaces.json" && name !== "removed-workspaces.json" && name !== "settings.json") return;
39361
40072
  notify();
39362
40073
  };
@@ -40603,9 +41314,9 @@ var SlackRelayHub = class {
40603
41314
  };
40604
41315
 
40605
41316
  // src/relay/static.ts
40606
- var import_node_fs69 = require("fs");
41317
+ var import_node_fs70 = require("fs");
40607
41318
  var import_promises = require("fs/promises");
40608
- var import_node_path67 = __toESM(require("path"), 1);
41319
+ var import_node_path68 = __toESM(require("path"), 1);
40609
41320
  var TYPES = {
40610
41321
  ".css": "text/css; charset=utf-8",
40611
41322
  ".html": "text/html; charset=utf-8",
@@ -40636,9 +41347,9 @@ function resolveStaticPath(root, requestUrl) {
40636
41347
  return null;
40637
41348
  }
40638
41349
  if (!pathname.startsWith("/") || pathname.includes("\0")) return null;
40639
- const rootResolved = import_node_path67.default.resolve(root);
40640
- const candidate = import_node_path67.default.resolve(rootResolved, `.${pathname}`);
40641
- if (candidate !== rootResolved && !candidate.startsWith(rootResolved + import_node_path67.default.sep)) {
41350
+ const rootResolved = import_node_path68.default.resolve(root);
41351
+ const candidate = import_node_path68.default.resolve(rootResolved, `.${pathname}`);
41352
+ if (candidate !== rootResolved && !candidate.startsWith(rootResolved + import_node_path68.default.sep)) {
40642
41353
  return null;
40643
41354
  }
40644
41355
  return candidate;
@@ -40652,7 +41363,7 @@ async function fileSize(file) {
40652
41363
  }
40653
41364
  }
40654
41365
  function sendFile(req, res, file, size) {
40655
- const ext = import_node_path67.default.extname(file).toLowerCase();
41366
+ const ext = import_node_path68.default.extname(file).toLowerCase();
40656
41367
  res.writeHead(200, {
40657
41368
  "Content-Type": TYPES[ext] ?? "application/octet-stream",
40658
41369
  "Content-Length": size,
@@ -40662,7 +41373,7 @@ function sendFile(req, res, file, size) {
40662
41373
  res.end();
40663
41374
  return true;
40664
41375
  }
40665
- (0, import_node_fs69.createReadStream)(file).pipe(res);
41376
+ (0, import_node_fs70.createReadStream)(file).pipe(res);
40666
41377
  return true;
40667
41378
  }
40668
41379
  async function tryServeStatic(req, res, root) {
@@ -40671,7 +41382,7 @@ async function tryServeStatic(req, res, root) {
40671
41382
  if (!candidate) return false;
40672
41383
  const direct = await fileSize(candidate);
40673
41384
  if (direct != null) return sendFile(req, res, candidate, direct);
40674
- const asIndex = import_node_path67.default.join(candidate, "index.html");
41385
+ const asIndex = import_node_path68.default.join(candidate, "index.html");
40675
41386
  const indexSize = await fileSize(asIndex);
40676
41387
  if (indexSize != null) return sendFile(req, res, asIndex, indexSize);
40677
41388
  return false;
@@ -41014,14 +41725,15 @@ init_protocol();
41014
41725
 
41015
41726
  // src/relay/accounts.ts
41016
41727
  var import_node_crypto24 = require("crypto");
41017
- var import_node_fs70 = require("fs");
41728
+ var import_node_fs71 = require("fs");
41018
41729
  var import_node_module7 = require("module");
41019
- var import_node_path68 = require("path");
41730
+ var import_node_path69 = require("path");
41020
41731
  var GITHUB_ACCOUNT_ISSUER = "https://github.com";
41732
+ var CURSOR_ACCOUNT_ISSUER = "https://api.cursor.com/v1/origin";
41021
41733
  var RELAY_DEVICE_CAP = 3;
41022
41734
  function thisModuleFile3() {
41023
41735
  const cjsFile = typeof __filename !== "undefined" ? __filename : "";
41024
- return cjsFile || process.argv[1] || (0, import_node_path68.join)(process.cwd(), "package.json");
41736
+ return cjsFile || process.argv[1] || (0, import_node_path69.join)(process.cwd(), "package.json");
41025
41737
  }
41026
41738
  function openDatabase(filePath) {
41027
41739
  const req = (0, import_node_module7.createRequire)(thisModuleFile3());
@@ -41058,8 +41770,8 @@ function assertRelayAccountsDurable(filePath) {
41058
41770
  if (process.env.SIDEBOARD_RELAY_ACCOUNTS_EPHEMERAL === "1") return;
41059
41771
  if (process.env.NODE_ENV !== "production") return;
41060
41772
  if (process.platform !== "linux") return;
41061
- const dir = (0, import_node_fs70.realpathSync)((0, import_node_path68.dirname)(filePath));
41062
- const mounts = (0, import_node_fs70.readFileSync)("/proc/mounts", "utf8");
41773
+ const dir = (0, import_node_fs71.realpathSync)((0, import_node_path69.dirname)(filePath));
41774
+ const mounts = (0, import_node_fs71.readFileSync)("/proc/mounts", "utf8");
41063
41775
  const point = longestMountPoint(dir, mounts);
41064
41776
  if (point === "/") {
41065
41777
  throw new Error(
@@ -41070,7 +41782,7 @@ function assertRelayAccountsDurable(filePath) {
41070
41782
  var RelayAccountStore = class {
41071
41783
  db;
41072
41784
  constructor(filePath) {
41073
- (0, import_node_fs70.mkdirSync)((0, import_node_path68.dirname)(filePath), { recursive: true });
41785
+ (0, import_node_fs71.mkdirSync)((0, import_node_path69.dirname)(filePath), { recursive: true });
41074
41786
  this.db = openDatabase(filePath);
41075
41787
  this.db.exec(`
41076
41788
  CREATE TABLE IF NOT EXISTS accounts (
@@ -41270,20 +41982,224 @@ var RelayAccountStore = class {
41270
41982
  }
41271
41983
  };
41272
41984
 
41985
+ // src/relay/account-cursor.ts
41986
+ var import_node_crypto25 = require("crypto");
41987
+ var JWKS_URL = "https://api.cursor.com/v1/origin/keys";
41988
+ var RECEIPT_TYP = "origin-user-confirmation-receipt+jwt";
41989
+ var usedJti = /* @__PURE__ */ new Map();
41990
+ function b64urlJson(segment) {
41991
+ return JSON.parse(b64urlBuf(segment).toString("utf8"));
41992
+ }
41993
+ function b64urlBuf(segment) {
41994
+ const pad = segment.length % 4 === 0 ? "" : "=".repeat(4 - segment.length % 4);
41995
+ return Buffer.from(segment.replace(/-/g, "+").replace(/_/g, "/") + pad, "base64");
41996
+ }
41997
+ function rememberJti(jti, expMs, nowMs) {
41998
+ for (const [id, exp] of usedJti) {
41999
+ if (exp < nowMs) usedJti.delete(id);
42000
+ }
42001
+ if (usedJti.has(jti)) return false;
42002
+ usedJti.set(jti, expMs);
42003
+ return true;
42004
+ }
42005
+ function cursorAuthorizeUrl(clientId, redirectUri, state) {
42006
+ const url = new URL("https://cursor.com/codebase/apps/user-confirmation");
42007
+ url.searchParams.set("client_id", clientId);
42008
+ url.searchParams.set("redirect_uri", redirectUri);
42009
+ url.searchParams.set("state", state);
42010
+ return url.toString();
42011
+ }
42012
+ async function cursorIdentityFromReceipt(receipt, expected, fetchImpl, nowMs = Date.now()) {
42013
+ const parts = receipt.split(".");
42014
+ if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {
42015
+ throw new Error("Cursor sign-in receipt was not a signed token.");
42016
+ }
42017
+ const [hSeg, pSeg, sSeg] = parts;
42018
+ const header = b64urlJson(hSeg);
42019
+ if (header.alg !== "EdDSA" || header.typ !== RECEIPT_TYP || typeof header.kid !== "string") {
42020
+ throw new Error("Cursor sign-in receipt was not a user confirmation.");
42021
+ }
42022
+ const payload = b64urlJson(pSeg);
42023
+ if (payload.iss !== CURSOR_ACCOUNT_ISSUER) {
42024
+ throw new Error("Cursor sign-in receipt came from an unexpected issuer.");
42025
+ }
42026
+ if (payload.aud !== expected.clientId) throw new Error("Cursor sign-in receipt was for a different app.");
42027
+ if (payload.state !== expected.state) throw new Error("Cursor sign-in did not match this attempt.");
42028
+ const exp = typeof payload.exp === "number" ? payload.exp : 0;
42029
+ if (!exp || exp * 1e3 < nowMs) throw new Error("Cursor sign-in receipt expired.");
42030
+ const sub = typeof payload.sub === "string" ? payload.sub.trim() : "";
42031
+ if (!sub) throw new Error("Cursor did not return an account id.");
42032
+ const jti = typeof payload.jti === "string" ? payload.jti.trim() : "";
42033
+ if (!jti) throw new Error("Cursor sign-in receipt was missing an id.");
42034
+ const jwksRes = await fetchImpl(JWKS_URL);
42035
+ const body = await jwksRes.json();
42036
+ const jwk = (body.keys ?? []).find(
42037
+ (key2) => key2.kid === header.kid && key2.kty === "OKP" && key2.crv === "Ed25519" && key2.x
42038
+ );
42039
+ if (!jwk?.x) throw new Error("Cursor did not publish the key that signed this receipt.");
42040
+ const key = (0, import_node_crypto25.createPublicKey)({ key: { kty: "OKP", crv: "Ed25519", x: jwk.x }, format: "jwk" });
42041
+ const ok = (0, import_node_crypto25.verify)(null, Buffer.from(`${hSeg}.${pSeg}`), key, b64urlBuf(sSeg));
42042
+ if (!ok) throw new Error("Cursor sign-in receipt signature did not verify.");
42043
+ if (!rememberJti(jti, exp * 1e3, nowMs)) throw new Error("Cursor sign-in receipt was already used.");
42044
+ const email = typeof payload.email === "string" && payload.email.trim() ? payload.email.trim() : null;
42045
+ return {
42046
+ issuer: CURSOR_ACCOUNT_ISSUER,
42047
+ subject: sub,
42048
+ email,
42049
+ emailVerified: Boolean(email)
42050
+ };
42051
+ }
42052
+ function cursorGitHostEntry() {
42053
+ return {
42054
+ label: "Cursor",
42055
+ requiresClientSecret: false,
42056
+ credentialParam: "confirmation_receipt",
42057
+ authorizeUrl(client, redirectUri, state) {
42058
+ return cursorAuthorizeUrl(client.clientId, redirectUri, state);
42059
+ },
42060
+ fetchIdentity(client, receipt, _redirectUri, state, fetchImpl) {
42061
+ return cursorIdentityFromReceipt(receipt, { clientId: client.clientId, state }, fetchImpl);
42062
+ }
42063
+ };
42064
+ }
42065
+
42066
+ // src/relay/account-entire.ts
42067
+ var import_node_crypto26 = require("crypto");
42068
+ var ENTIRE_AUTHORIZE_ORIGIN = "https://auth.entire.io";
42069
+ var ENTIRE_DEFAULT_ISSUER = "https://us.auth.entire.io";
42070
+ var ISSUER_SUFFIX = ".auth.entire.io";
42071
+ function entireAccountIssuer(value) {
42072
+ const raw = value?.trim() ?? "";
42073
+ if (!raw) return null;
42074
+ let url;
42075
+ try {
42076
+ url = new URL(raw);
42077
+ } catch {
42078
+ return null;
42079
+ }
42080
+ if (url.protocol !== "https:" || url.username || url.password || url.port) return null;
42081
+ if (url.pathname !== "/" && url.pathname !== "") return null;
42082
+ if (url.search || url.hash) return null;
42083
+ const host = url.hostname.toLowerCase();
42084
+ if (!host.endsWith(ISSUER_SUFFIX)) return null;
42085
+ const label = host.slice(0, -ISSUER_SUFFIX.length);
42086
+ if (!/^[a-z0-9]+$/.test(label)) return null;
42087
+ return `https://${host}`;
42088
+ }
42089
+ function newEntireCodeVerifier() {
42090
+ return (0, import_node_crypto26.randomBytes)(32).toString("base64url");
42091
+ }
42092
+ function entireCodeChallenge(verifier) {
42093
+ return (0, import_node_crypto26.createHash)("sha256").update(verifier).digest("base64url");
42094
+ }
42095
+ function entireAuthorizeUrl(clientId, redirectUri, state, codeChallenge) {
42096
+ const url = new URL("/authorize", ENTIRE_AUTHORIZE_ORIGIN);
42097
+ url.searchParams.set("client_id", clientId);
42098
+ url.searchParams.set("redirect_uri", redirectUri);
42099
+ url.searchParams.set("response_type", "code");
42100
+ url.searchParams.set("scope", "openid email");
42101
+ url.searchParams.set("state", state);
42102
+ url.searchParams.set("code_challenge", codeChallenge);
42103
+ url.searchParams.set("code_challenge_method", "S256");
42104
+ return url.toString();
42105
+ }
42106
+ async function readJson3(res) {
42107
+ const text7 = await res.text();
42108
+ if (!text7) return {};
42109
+ try {
42110
+ return JSON.parse(text7);
42111
+ } catch {
42112
+ throw new Error("Entire returned a non-JSON response.");
42113
+ }
42114
+ }
42115
+ function accessToken(body) {
42116
+ const token = body.access_token;
42117
+ if (typeof token === "string" && token.trim()) return token.trim();
42118
+ const described = body.error_description;
42119
+ const code = body.error;
42120
+ const message = typeof described === "string" && described.trim() ? described : code;
42121
+ throw new Error(
42122
+ typeof message === "string" && message.trim() ? message : "Entire rejected the sign-in code."
42123
+ );
42124
+ }
42125
+ async function entireIdentityFromCode(client, code, redirectUri, ctx, fetchImpl) {
42126
+ if (!ctx.codeVerifier) throw new Error("Entire sign-in could not be verified.");
42127
+ const issuer = ctx.issuer?.trim() ? entireAccountIssuer(ctx.issuer) : ENTIRE_DEFAULT_ISSUER;
42128
+ if (!issuer) throw new Error("Entire sign-in came from an unexpected issuer.");
42129
+ const body = new URLSearchParams({
42130
+ grant_type: "authorization_code",
42131
+ code,
42132
+ redirect_uri: redirectUri,
42133
+ client_id: client.clientId,
42134
+ code_verifier: ctx.codeVerifier
42135
+ });
42136
+ const secret = client.clientSecret?.trim() ?? "";
42137
+ const headers = {
42138
+ Accept: "application/json",
42139
+ "Content-Type": "application/x-www-form-urlencoded"
42140
+ };
42141
+ if (secret) {
42142
+ headers.Authorization = `Basic ${Buffer.from(`${client.clientId}:${secret}`).toString("base64")}`;
42143
+ }
42144
+ const tokenRes = await fetchImpl(`${issuer}/oauth/token`, {
42145
+ method: "POST",
42146
+ redirect: "error",
42147
+ headers,
42148
+ body
42149
+ });
42150
+ const token = accessToken(await readJson3(tokenRes));
42151
+ const userRes = await fetchImpl(`${issuer}/userinfo`, {
42152
+ redirect: "error",
42153
+ headers: { Accept: "application/json", Authorization: `Bearer ${token}` }
42154
+ });
42155
+ if (!userRes.ok) throw new Error("Entire did not return an account.");
42156
+ const user = await readJson3(userRes);
42157
+ const subject = typeof user.sub === "string" ? user.sub.trim() : "";
42158
+ if (!subject) throw new Error("Entire did not return an account id.");
42159
+ const email = typeof user.email === "string" ? user.email.trim() : "";
42160
+ const emailVerified = user.email_verified === true && Boolean(email);
42161
+ return {
42162
+ issuer,
42163
+ subject,
42164
+ email: emailVerified ? email : null,
42165
+ emailVerified
42166
+ };
42167
+ }
42168
+ function entireGitHostEntry() {
42169
+ return {
42170
+ label: "Entire",
42171
+ requiresClientSecret: false,
42172
+ credentialParam: "code",
42173
+ authorizeUrl(client, redirectUri, state, ctx) {
42174
+ if (!ctx.codeVerifier) throw new Error("Entire sign-in could not start.");
42175
+ return entireAuthorizeUrl(client.clientId, redirectUri, state, entireCodeChallenge(ctx.codeVerifier));
42176
+ },
42177
+ fetchIdentity(client, code, redirectUri, _state, fetchImpl, ctx) {
42178
+ return entireIdentityFromCode(client, code, redirectUri, ctx, fetchImpl);
42179
+ }
42180
+ };
42181
+ }
42182
+
41273
42183
  // src/relay/account-oauth.ts
41274
- var RELAY_GIT_HOSTS = ["github"];
42184
+ var RELAY_GIT_HOSTS = ["github", "cursor", "entire"];
41275
42185
  var REMOTE_LOGIN_PATH = "/remote/login";
41276
42186
  var REMOTE_OAUTH_CALLBACK_PATH = "/remote/oauth/callback";
41277
42187
  var REMOTE_OAUTH_RESULT_PATH = "/remote/oauth/result";
41278
42188
  var REMOTE_LOGOUT_PATH = "/remote/logout";
41279
42189
  var STATE_RE = /^[A-Za-z0-9_-]{16,128}$/;
41280
42190
  var LOGIN_TTL_MS = 10 * 60 * 1e3;
42191
+ var EMPTY_AUTH = { codeVerifier: null, issuer: null };
41281
42192
  var RelayLoginSessions = class {
41282
42193
  waiting = /* @__PURE__ */ new Map();
41283
42194
  results = /* @__PURE__ */ new Map();
41284
- begin(state, provider, accountId) {
42195
+ begin(state, provider, accountId, codeVerifier = null) {
41285
42196
  this.sweep();
41286
- this.waiting.set(state, { provider, accountId, expiresAt: Date.now() + LOGIN_TTL_MS });
42197
+ this.waiting.set(state, {
42198
+ provider,
42199
+ accountId,
42200
+ codeVerifier,
42201
+ expiresAt: Date.now() + LOGIN_TTL_MS
42202
+ });
41287
42203
  }
41288
42204
  takeLogin(state) {
41289
42205
  this.sweep();
@@ -41318,24 +42234,29 @@ var RelayLoginSessions = class {
41318
42234
  var GIT_HOST_AUTH = {
41319
42235
  github: {
41320
42236
  label: "GitHub",
41321
- authorizeUrl: "https://github.com/login/oauth/authorize",
41322
- scope: "user:email",
42237
+ requiresClientSecret: true,
42238
+ credentialParam: "code",
42239
+ authorizeUrl: (client, redirectUri, state) => githubAuthorizeUrl(client, redirectUri, state),
41323
42240
  fetchIdentity: fetchGithubIdentity
41324
- }
42241
+ },
42242
+ cursor: cursorGitHostEntry(),
42243
+ entire: entireGitHostEntry()
41325
42244
  };
41326
- function providerOf(value) {
41327
- return RELAY_GIT_HOSTS.includes(value) ? value : null;
41328
- }
41329
- function relayAuthorizeUrl(provider, client, redirectUri, state) {
41330
- const host = GIT_HOST_AUTH[provider];
41331
- const url = new URL(host.authorizeUrl);
42245
+ function githubAuthorizeUrl(client, redirectUri, state) {
42246
+ const url = new URL("https://github.com/login/oauth/authorize");
41332
42247
  url.searchParams.set("client_id", client.clientId);
41333
42248
  url.searchParams.set("redirect_uri", redirectUri);
41334
- url.searchParams.set("scope", host.scope);
42249
+ url.searchParams.set("scope", "user:email");
41335
42250
  url.searchParams.set("state", state);
41336
42251
  return url.toString();
41337
42252
  }
41338
- async function readJson3(res) {
42253
+ function providerOf(value) {
42254
+ return RELAY_GIT_HOSTS.includes(value) ? value : null;
42255
+ }
42256
+ function relayAuthorizeUrl(provider, client, redirectUri, state, ctx = EMPTY_AUTH) {
42257
+ return GIT_HOST_AUTH[provider].authorizeUrl(client, redirectUri, state, ctx);
42258
+ }
42259
+ async function readJson4(res) {
41339
42260
  const text7 = await res.text();
41340
42261
  if (!text7) return {};
41341
42262
  try {
@@ -41344,7 +42265,7 @@ async function readJson3(res) {
41344
42265
  throw new Error("Sign-in provider returned a non-JSON response.");
41345
42266
  }
41346
42267
  }
41347
- function accessToken(body) {
42268
+ function accessToken2(body) {
41348
42269
  const token = body.access_token;
41349
42270
  if (typeof token !== "string" || !token.trim()) {
41350
42271
  const message = body.error_description;
@@ -41352,7 +42273,10 @@ function accessToken(body) {
41352
42273
  }
41353
42274
  return token.trim();
41354
42275
  }
41355
- async function fetchGithubIdentity(client, code, redirectUri, fetchImpl) {
42276
+ async function fetchGithubIdentity(client, code, redirectUri, state, fetchImpl, _ctx) {
42277
+ void state;
42278
+ void _ctx;
42279
+ if (!client.clientSecret) throw new Error("GitHub sign-in is not configured on this relay.");
41356
42280
  const tokenRes = await fetchImpl("https://github.com/login/oauth/access_token", {
41357
42281
  method: "POST",
41358
42282
  headers: { Accept: "application/json", "Content-Type": "application/json" },
@@ -41363,17 +42287,17 @@ async function fetchGithubIdentity(client, code, redirectUri, fetchImpl) {
41363
42287
  redirect_uri: redirectUri
41364
42288
  })
41365
42289
  });
41366
- const token = accessToken(await readJson3(tokenRes));
42290
+ const token = accessToken2(await readJson4(tokenRes));
41367
42291
  const userRes = await fetchImpl("https://api.github.com/user", {
41368
42292
  headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json", "User-Agent": "sideboard-relay" }
41369
42293
  });
41370
- const user = await readJson3(userRes);
42294
+ const user = await readJson4(userRes);
41371
42295
  const subject = typeof user.id === "number" ? String(user.id) : "";
41372
42296
  if (!subject) throw new Error("GitHub did not return an account id.");
41373
42297
  const emailRes = await fetchImpl("https://api.github.com/user/emails", {
41374
42298
  headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json", "User-Agent": "sideboard-relay" }
41375
42299
  });
41376
- const emails = await readJson3(emailRes);
42300
+ const emails = await readJson4(emailRes);
41377
42301
  const list = Array.isArray(emails) ? emails : [];
41378
42302
  const primary = list.find((row2) => row2.primary === true && row2.verified === true);
41379
42303
  const anyVerified = list.find((row2) => row2.verified === true);
@@ -41381,8 +42305,8 @@ async function fetchGithubIdentity(client, code, redirectUri, fetchImpl) {
41381
42305
  const email = chosen && typeof chosen.email === "string" ? chosen.email : null;
41382
42306
  return { issuer: GITHUB_ACCOUNT_ISSUER, subject, email, emailVerified: Boolean(email) };
41383
42307
  }
41384
- async function fetchRelayIdentity(provider, client, code, redirectUri, fetchImpl) {
41385
- return GIT_HOST_AUTH[provider].fetchIdentity(client, code, redirectUri, fetchImpl);
42308
+ async function fetchRelayIdentity(provider, client, credential, redirectUri, state, fetchImpl, ctx = EMPTY_AUTH) {
42309
+ return GIT_HOST_AUTH[provider].fetchIdentity(client, credential, redirectUri, state, fetchImpl, ctx);
41386
42310
  }
41387
42311
  async function readBody(req) {
41388
42312
  const chunks = [];
@@ -41492,7 +42416,8 @@ async function handleRelayAccountHttp(opts) {
41492
42416
  return true;
41493
42417
  }
41494
42418
  const client = opts.oauth[provider];
41495
- if (!client?.clientId || !client.clientSecret) {
42419
+ const host = provider ? GIT_HOST_AUTH[provider] : null;
42420
+ if (!client?.clientId || host?.requiresClientSecret && !client.clientSecret) {
41496
42421
  sendJson(opts.res, 503, {
41497
42422
  ok: false,
41498
42423
  error: `${GIT_HOST_AUTH[provider].label} sign-in is not configured on this relay.`
@@ -41508,21 +42433,26 @@ async function handleRelayAccountHttp(opts) {
41508
42433
  return true;
41509
42434
  }
41510
42435
  }
41511
- opts.sessions.begin(state, provider, accountId);
41512
- sendJson(opts.res, 200, { ok: true, url: relayAuthorizeUrl(provider, client, redirectUri, state) });
42436
+ const codeVerifier = provider === "entire" ? newEntireCodeVerifier() : null;
42437
+ const auth = { codeVerifier, issuer: null };
42438
+ opts.sessions.begin(state, provider, accountId, codeVerifier);
42439
+ sendJson(opts.res, 200, {
42440
+ ok: true,
42441
+ url: relayAuthorizeUrl(provider, client, redirectUri, state, auth)
42442
+ });
41513
42443
  return true;
41514
42444
  }
41515
42445
  if (method === "GET" && path2 === REMOTE_OAUTH_CALLBACK_PATH) {
41516
42446
  const url = new URL(reqUrl, origin);
41517
42447
  const state = url.searchParams.get("state")?.trim() ?? "";
41518
- const code = url.searchParams.get("code")?.trim() ?? "";
41519
42448
  const providerError = url.searchParams.get("error")?.trim() ?? "";
41520
42449
  const pending = STATE_RE.test(state) ? opts.sessions.takeLogin(state) : null;
41521
42450
  if (!pending) {
41522
42451
  sendHtml(opts.res, 400, "Sideboard", "<h1>Sign-in expired</h1><p>Return to Sideboard and try again.</p>");
41523
42452
  return true;
41524
42453
  }
41525
- if (providerError || !code) {
42454
+ const credential = url.searchParams.get(GIT_HOST_AUTH[pending.provider].credentialParam)?.trim() ?? "";
42455
+ if (providerError || !credential) {
41526
42456
  const message = providerError || "Sign-in was cancelled.";
41527
42457
  opts.sessions.putResult(state, { ok: false, error: message });
41528
42458
  sendHtml(opts.res, 400, "Sideboard", `<h1>Could not sign in</h1><p>${escapeHtml2(message)}</p>`);
@@ -41535,7 +42465,15 @@ async function handleRelayAccountHttp(opts) {
41535
42465
  return true;
41536
42466
  }
41537
42467
  try {
41538
- const identity = await fetchRelayIdentity(pending.provider, client, code, redirectUri, fetchImpl);
42468
+ const identity = await fetchRelayIdentity(
42469
+ pending.provider,
42470
+ client,
42471
+ credential,
42472
+ redirectUri,
42473
+ state,
42474
+ fetchImpl,
42475
+ { codeVerifier: pending.codeVerifier, issuer: url.searchParams.get("iss") }
42476
+ );
41539
42477
  const signed = opts.store.signIn(identity, pending.accountId);
41540
42478
  opts.sessions.putResult(state, {
41541
42479
  ok: true,
@@ -42091,9 +43029,138 @@ init_run();
42091
43029
  init_gh_errors();
42092
43030
  init_git_auth_mode();
42093
43031
  init_worktree();
43032
+ init_pr_write();
42094
43033
  init_stack();
42095
43034
  init_branch_prefix();
42096
43035
  init_agent_git_actions();
43036
+
43037
+ // src/integrations/cursor-git.ts
43038
+ init_install();
43039
+ init_run();
43040
+ function parseOriginAuthStatus(text7) {
43041
+ const account = text7.match(/^Account:\s+(.+)$/m)?.[1]?.trim() || null;
43042
+ const authMethod = text7.match(/^Auth method:\s+(.+)$/m)?.[1]?.trim() || null;
43043
+ return { account, authMethod };
43044
+ }
43045
+ async function getCursorGitStatus() {
43046
+ const which = await run("which", ["origin"], { reject: false, timeoutMs: 5e3 });
43047
+ if (which.exitCode !== 0 || !which.stdout.trim()) {
43048
+ return {
43049
+ installed: false,
43050
+ connected: false,
43051
+ account: null,
43052
+ authMethod: null,
43053
+ reason: "origin CLI not found on PATH"
43054
+ };
43055
+ }
43056
+ const status = await run("origin", ["auth", "status"], { reject: false, timeoutMs: 15e3 });
43057
+ const text7 = `${status.stdout}
43058
+ ${status.stderr}`;
43059
+ const parsed = parseOriginAuthStatus(text7);
43060
+ if (status.exitCode === 0 && parsed.account) {
43061
+ return {
43062
+ installed: true,
43063
+ connected: true,
43064
+ account: parsed.account,
43065
+ authMethod: parsed.authMethod,
43066
+ reason: null
43067
+ };
43068
+ }
43069
+ return {
43070
+ installed: true,
43071
+ connected: false,
43072
+ account: null,
43073
+ authMethod: null,
43074
+ reason: status.stderr.trim() || status.stdout.trim() || "Not signed in to Cursor (Settings \u2192 Git \u2192 Cursor, or origin auth login)"
43075
+ };
43076
+ }
43077
+ async function loginCursorGit() {
43078
+ const status = await getCursorGitStatus();
43079
+ if (!status.installed) {
43080
+ return { ok: false, message: status.reason || "origin CLI not found on PATH" };
43081
+ }
43082
+ await openInSystemTerminal("origin auth login");
43083
+ return {
43084
+ ok: true,
43085
+ openedTerminal: true,
43086
+ command: "origin auth login",
43087
+ message: "Opened Terminal to run: origin auth login"
43088
+ };
43089
+ }
43090
+ async function setupCursorGit() {
43091
+ const status = await getCursorGitStatus();
43092
+ if (!status.installed) {
43093
+ return { ok: false, message: status.reason || "origin CLI not found on PATH" };
43094
+ }
43095
+ const result = await run("origin", ["auth", "setup-git"], {
43096
+ reject: false,
43097
+ timeoutMs: 2e4
43098
+ });
43099
+ const message = result.stdout.trim() || result.stderr.trim() || (result.exitCode === 0 ? "Git is configured to authenticate Origin through the origin CLI." : "origin auth setup-git failed");
43100
+ return {
43101
+ ok: result.exitCode === 0,
43102
+ command: "origin auth setup-git",
43103
+ message,
43104
+ stdout: result.stdout,
43105
+ stderr: result.stderr,
43106
+ exitCode: result.exitCode
43107
+ };
43108
+ }
43109
+
43110
+ // src/integrations/entire-git.ts
43111
+ init_install();
43112
+ init_run();
43113
+ function parseEntireAuthContexts(text7) {
43114
+ for (const line of text7.split("\n")) {
43115
+ if (!/\(active\)/i.test(line)) continue;
43116
+ const cols = line.replace(/\(active\)/gi, "").trim().split(/\s{2,}/).map((part) => part.trim()).filter(Boolean);
43117
+ const handle = cols[1] || cols[0] || null;
43118
+ if (handle && !/^context$/i.test(handle)) return { account: handle };
43119
+ }
43120
+ return { account: null };
43121
+ }
43122
+ async function getEntireGitStatus() {
43123
+ const which = await run("which", ["entire"], { reject: false, timeoutMs: 5e3 });
43124
+ if (which.exitCode !== 0 || !which.stdout.trim()) {
43125
+ return {
43126
+ installed: false,
43127
+ connected: false,
43128
+ account: null,
43129
+ reason: "entire CLI not found on PATH"
43130
+ };
43131
+ }
43132
+ const status = await run("entire", ["auth", "contexts"], { reject: false, timeoutMs: 15e3 });
43133
+ const parsed = parseEntireAuthContexts(status.stdout);
43134
+ if (status.exitCode === 0 && parsed.account) {
43135
+ return {
43136
+ installed: true,
43137
+ connected: true,
43138
+ account: parsed.account,
43139
+ reason: null
43140
+ };
43141
+ }
43142
+ return {
43143
+ installed: true,
43144
+ connected: false,
43145
+ account: null,
43146
+ reason: status.stderr.trim() || "Not signed in to Entire (Settings \u2192 Git \u2192 Entire, or entire login)"
43147
+ };
43148
+ }
43149
+ async function loginEntireGit() {
43150
+ const status = await getEntireGitStatus();
43151
+ if (!status.installed) {
43152
+ return { ok: false, message: status.reason || "entire CLI not found on PATH" };
43153
+ }
43154
+ await openInSystemTerminal("entire login");
43155
+ return {
43156
+ ok: true,
43157
+ openedTerminal: true,
43158
+ command: "entire login",
43159
+ message: "Opened Terminal to run: entire login"
43160
+ };
43161
+ }
43162
+
43163
+ // src/index.ts
42097
43164
  init_abletime();
42098
43165
  init_abletime_mcp();
42099
43166
  init_abletime_oauth();
@@ -42174,7 +43241,7 @@ init_stage_files();
42174
43241
  init_consume_attachments();
42175
43242
 
42176
43243
  // src/composer/pasted-text.ts
42177
- var import_node_crypto25 = require("crypto");
43244
+ var import_node_crypto27 = require("crypto");
42178
43245
  var PASTE_ATTACH_MIN_CHARS = 1200;
42179
43246
  var PASTE_ATTACH_MIN_LINES = 15;
42180
43247
  var PASTED_NAME_RE = /^Pasted text #(\d+)\.txt$/i;
@@ -42201,7 +43268,7 @@ function nextPastedTextName(existing) {
42201
43268
  }
42202
43269
  function buildPastedTextAttachment(text7, opts) {
42203
43270
  return {
42204
- id: opts?.id ?? (0, import_node_crypto25.randomUUID)(),
43271
+ id: opts?.id ?? (0, import_node_crypto27.randomUUID)(),
42205
43272
  name: opts?.name ?? "Pasted text #1.txt",
42206
43273
  kind: "file",
42207
43274
  path: opts?.path,
@@ -42276,22 +43343,22 @@ init_error_detail();
42276
43343
  init_cursor_stream_cutoff();
42277
43344
 
42278
43345
  // src/agents/tool-result-clip.ts
42279
- var import_node_fs71 = require("fs");
42280
- var import_node_path69 = require("path");
43346
+ var import_node_fs72 = require("fs");
43347
+ var import_node_path70 = require("path");
42281
43348
  init_workspace_scratch();
42282
43349
  init_error_detail();
42283
43350
  function spillPath(cwd, prefix) {
42284
43351
  const stamp = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
42285
43352
  const safe = prefix.replace(/[^a-zA-Z0-9_-]+/g, "-").slice(0, 40) || "tool";
42286
- return (0, import_node_path69.join)(cwd, CLI_DIR, `${safe}-${stamp}.txt`);
43353
+ return (0, import_node_path70.join)(cwd, CLI_DIR, `${safe}-${stamp}.txt`);
42287
43354
  }
42288
43355
  function spillToolResultToCli(content, opts) {
42289
43356
  const cwd = opts?.cwd?.trim();
42290
43357
  if (!cwd || !content) return null;
42291
43358
  const file = spillPath(cwd, opts?.prefix ?? "tool-result");
42292
43359
  try {
42293
- (0, import_node_fs71.mkdirSync)((0, import_node_path69.join)(cwd, CLI_DIR), { recursive: true });
42294
- (0, import_node_fs71.writeFileSync)(file, content, "utf8");
43360
+ (0, import_node_fs72.mkdirSync)((0, import_node_path70.join)(cwd, CLI_DIR), { recursive: true });
43361
+ (0, import_node_fs72.writeFileSync)(file, content, "utf8");
42295
43362
  return file;
42296
43363
  } catch {
42297
43364
  return null;
@@ -42377,8 +43444,8 @@ function createClippedMcpServer() {
42377
43444
 
42378
43445
  // src/mcp/server.ts
42379
43446
  var import_zod11 = require("zod");
42380
- var import_node_crypto28 = require("crypto");
42381
- var import_node_path73 = require("path");
43447
+ var import_node_crypto30 = require("crypto");
43448
+ var import_node_path74 = require("path");
42382
43449
  init_orchestrator();
42383
43450
  init_task_state();
42384
43451
  init_worktree();
@@ -43148,11 +44215,15 @@ function fail2(err) {
43148
44215
  true
43149
44216
  );
43150
44217
  }
44218
+ async function cursorIssueBlock(repoPath) {
44219
+ const cwd = await resolveRepoRoot((repoPath ?? "").trim() || process.cwd());
44220
+ return cursorIssuesUnavailableReason(cwd);
44221
+ }
43151
44222
  var repoPathSchema = import_zod6.z.string().optional().describe("Workspace / worktree path. Omit on a worktree turn (uses cwd).");
43152
44223
  function registerGithubIssueTools(server) {
43153
44224
  server.tool(
43154
44225
  "github_search_issues",
43155
- "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).",
44226
+ "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).",
43156
44227
  {
43157
44228
  query: import_zod6.z.string().optional().describe("Search text (title, body, identifiers). Omit to list by assignee only."),
43158
44229
  assignee: import_zod6.z.string().optional().describe("me, unassigned, all, or a GitHub login. Default: all when query is set, otherwise me."),
@@ -43164,6 +44235,15 @@ function registerGithubIssueTools(server) {
43164
44235
  try {
43165
44236
  const cwd = await resolveRepoRoot((repoPath ?? "").trim() || process.cwd());
43166
44237
  const page = clampMcpIssueLimit(limit);
44238
+ const blocked = await cursorIssuesUnavailableReason(cwd);
44239
+ if (blocked) {
44240
+ return mcpJson(
44241
+ formatListedIssuesForMcp(
44242
+ { source: "github", issues: [], unavailableReason: blocked },
44243
+ page
44244
+ )
44245
+ );
44246
+ }
43167
44247
  const since = updatedSince ? parseIssueSince(updatedSince) : void 0;
43168
44248
  const issues = await listGitHubIssues(cwd, {
43169
44249
  query,
@@ -43199,6 +44279,8 @@ function registerGithubIssueTools(server) {
43199
44279
  { repoPath: repoPathSchema },
43200
44280
  async ({ repoPath }) => {
43201
44281
  try {
44282
+ const blocked = await cursorIssueBlock(repoPath);
44283
+ if (blocked) throw new Error(blocked);
43202
44284
  return text4({ projects: await listGitHubProjects({ repoPath }) });
43203
44285
  } catch (err) {
43204
44286
  return fail2(err);
@@ -43211,6 +44293,8 @@ function registerGithubIssueTools(server) {
43211
44293
  { id: import_zod6.z.string(), include: mcpIssueIncludeSchema, repoPath: repoPathSchema },
43212
44294
  async ({ id, include, repoPath }) => {
43213
44295
  try {
44296
+ const blocked = await cursorIssueBlock(repoPath);
44297
+ if (blocked) throw new Error(blocked);
43214
44298
  return text4(formatGitHubIssuePayload(await getGitHubIssue(id, { repoPath }), include));
43215
44299
  } catch (err) {
43216
44300
  return fail2(err);
@@ -43239,6 +44323,8 @@ function registerGithubIssueTools(server) {
43239
44323
  { id: import_zod6.z.string(), body: import_zod6.z.string(), repoPath: repoPathSchema },
43240
44324
  async ({ id, body, repoPath }) => {
43241
44325
  try {
44326
+ const blocked = await cursorIssueBlock(repoPath);
44327
+ if (blocked) throw new Error(blocked);
43242
44328
  return text4(await commentGitHubIssue({ id, body }, { repoPath }));
43243
44329
  } catch (err) {
43244
44330
  return fail2(err);
@@ -43266,6 +44352,8 @@ function registerGithubIssueTools(server) {
43266
44352
  },
43267
44353
  async (args) => {
43268
44354
  try {
44355
+ const blocked = await cursorIssueBlock(args.repoPath);
44356
+ if (blocked) throw new Error(blocked);
43269
44357
  return text4(await updateGitHubIssue(args, { repoPath: args.repoPath }));
43270
44358
  } catch (err) {
43271
44359
  return fail2(err);
@@ -43283,6 +44371,8 @@ function registerGithubIssueTools(server) {
43283
44371
  },
43284
44372
  async (args) => {
43285
44373
  try {
44374
+ const blocked = await cursorIssueBlock(args.repoPath);
44375
+ if (blocked) throw new Error(blocked);
43286
44376
  return text4(await createGitHubIssue(args, { repoPath: args.repoPath }));
43287
44377
  } catch (err) {
43288
44378
  return fail2(err);
@@ -43970,8 +45060,8 @@ function registerScheduleTools(server) {
43970
45060
  }
43971
45061
 
43972
45062
  // src/agent-rpc/handlers.ts
43973
- var import_node_crypto26 = require("crypto");
43974
- var import_node_path70 = require("path");
45063
+ var import_node_crypto28 = require("crypto");
45064
+ var import_node_path71 = require("path");
43975
45065
  init_wait_for_job();
43976
45066
  init_wait_for_turn();
43977
45067
 
@@ -44053,7 +45143,7 @@ var handlePermissionAsk = async (params, _ctx) => {
44053
45143
  // src/agent-rpc/handlers.ts
44054
45144
  init_protocol2();
44055
45145
  function presentArtifactResult(input) {
44056
- const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${(0, import_node_crypto26.randomUUID)().slice(0, 8)}`;
45146
+ const id = input.artifact_id?.trim() || `artifact_${Date.now().toString(36)}_${(0, import_node_crypto28.randomUUID)().slice(0, 8)}`;
44057
45147
  return {
44058
45148
  ok: true,
44059
45149
  artifact_id: id,
@@ -44078,7 +45168,7 @@ function invalidParams2(message) {
44078
45168
  function jobCwd(rec) {
44079
45169
  const raw = str7(rec.cwd).trim();
44080
45170
  if (!raw) throw invalidParams2("cwd is required");
44081
- if (!(0, import_node_path70.isAbsolute)(raw)) throw invalidParams2("cwd must be an absolute path");
45171
+ if (!(0, import_node_path71.isAbsolute)(raw)) throw invalidParams2("cwd must be an absolute path");
44082
45172
  return raw;
44083
45173
  }
44084
45174
  async function handleRuntimePing(_params, _ctx) {
@@ -44211,27 +45301,27 @@ init_gh_errors();
44211
45301
  init_git_auth_mode();
44212
45302
 
44213
45303
  // src/board/load-home-board.ts
44214
- var import_node_fs73 = require("fs");
44215
- var import_node_path72 = require("path");
45304
+ var import_node_fs74 = require("fs");
45305
+ var import_node_path73 = require("path");
44216
45306
  init_worktree();
44217
45307
  init_paths();
44218
45308
 
44219
45309
  // src/board/board-pins.ts
44220
- var import_node_crypto27 = require("crypto");
44221
- var import_node_fs72 = require("fs");
44222
- var import_node_path71 = require("path");
45310
+ var import_node_crypto29 = require("crypto");
45311
+ var import_node_fs73 = require("fs");
45312
+ var import_node_path72 = require("path");
44223
45313
  init_paths();
44224
45314
  init_home_board();
44225
45315
  var FILE = "home-board-pins.json";
44226
45316
  var VERSION = 1;
44227
45317
  function pinsFile() {
44228
- return (0, import_node_path71.join)(appDataDir(), FILE);
45318
+ return (0, import_node_path72.join)(appDataDir(), FILE);
44229
45319
  }
44230
45320
  function readDisk() {
44231
45321
  const path2 = pinsFile();
44232
- if (!(0, import_node_fs72.existsSync)(path2)) return [];
45322
+ if (!(0, import_node_fs73.existsSync)(path2)) return [];
44233
45323
  try {
44234
- const raw = JSON.parse((0, import_node_fs72.readFileSync)(path2, "utf8"));
45324
+ const raw = JSON.parse((0, import_node_fs73.readFileSync)(path2, "utf8"));
44235
45325
  if (raw?.version !== VERSION || !Array.isArray(raw.items)) return [];
44236
45326
  return raw.items.filter((item) => item?.id && item.kind && item.ref);
44237
45327
  } catch {
@@ -44239,8 +45329,8 @@ function readDisk() {
44239
45329
  }
44240
45330
  }
44241
45331
  function writeDisk(items) {
44242
- (0, import_node_fs72.mkdirSync)(appDataDir(), { recursive: true });
44243
- (0, import_node_fs72.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
45332
+ (0, import_node_fs73.mkdirSync)(appDataDir(), { recursive: true });
45333
+ (0, import_node_fs73.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
44244
45334
  }
44245
45335
  function listBoardPins() {
44246
45336
  return readDisk();
@@ -44249,7 +45339,7 @@ function addBoardPin(input) {
44249
45339
  const ref = input.ref.trim();
44250
45340
  if (!ref) throw new Error("Board item needs a ref");
44251
45341
  const pin = {
44252
- id: (0, import_node_crypto27.randomUUID)(),
45342
+ id: (0, import_node_crypto29.randomUUID)(),
44253
45343
  kind: input.kind,
44254
45344
  ref,
44255
45345
  repoPath: input.repoPath.trim(),
@@ -44292,7 +45382,7 @@ function replaceBoardPins(items) {
44292
45382
  }
44293
45383
  function clearBoardPins() {
44294
45384
  try {
44295
- (0, import_node_fs72.unlinkSync)(pinsFile());
45385
+ (0, import_node_fs73.unlinkSync)(pinsFile());
44296
45386
  } catch {
44297
45387
  }
44298
45388
  }
@@ -44309,7 +45399,7 @@ function homeBoardWorkspaceKey(workspaces) {
44309
45399
  return workspaces.map((w) => w.path).filter(Boolean).sort().join("\n");
44310
45400
  }
44311
45401
  function cacheFile() {
44312
- return (0, import_node_path72.join)(appDataDir(), "home-board-cache.json");
45402
+ return (0, import_node_path73.join)(appDataDir(), "home-board-cache.json");
44313
45403
  }
44314
45404
  function emptyInputs() {
44315
45405
  return {
@@ -44335,9 +45425,9 @@ function shouldCacheHomeBoardInputs(inputs) {
44335
45425
  }
44336
45426
  function readDiskCache() {
44337
45427
  const path2 = cacheFile();
44338
- if (!(0, import_node_fs73.existsSync)(path2)) return null;
45428
+ if (!(0, import_node_fs74.existsSync)(path2)) return null;
44339
45429
  try {
44340
- const raw = JSON.parse((0, import_node_fs73.readFileSync)(path2, "utf8"));
45430
+ const raw = JSON.parse((0, import_node_fs74.readFileSync)(path2, "utf8"));
44341
45431
  if (raw?.version !== CACHE_VERSION || typeof raw.fetchedAt !== "number") {
44342
45432
  return null;
44343
45433
  }
@@ -44351,14 +45441,14 @@ function readDiskCache() {
44351
45441
  }
44352
45442
  function writeDiskCache(entry) {
44353
45443
  try {
44354
- (0, import_node_fs73.mkdirSync)(appDataDir(), { recursive: true });
44355
- (0, import_node_fs73.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
45444
+ (0, import_node_fs74.mkdirSync)(appDataDir(), { recursive: true });
45445
+ (0, import_node_fs74.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
44356
45446
  } catch {
44357
45447
  }
44358
45448
  }
44359
45449
  function deleteDiskCache() {
44360
45450
  try {
44361
- (0, import_node_fs73.unlinkSync)(cacheFile());
45451
+ (0, import_node_fs74.unlinkSync)(cacheFile());
44362
45452
  } catch {
44363
45453
  }
44364
45454
  }
@@ -44543,13 +45633,13 @@ async function startMcpServer() {
44543
45633
  if (!worktreeProfile) {
44544
45634
  server.tool(
44545
45635
  "list_projects",
44546
- "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.",
45636
+ "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.",
44547
45637
  {},
44548
45638
  async () => {
44549
45639
  const workspaces = orch.listWorkspaces();
44550
45640
  const lines = await Promise.all(
44551
45641
  workspaces.map(async (w) => {
44552
- const slug = await resolveGithubRepoSlug(w.path).catch(() => null);
45642
+ const tag = await resolveProjectRemoteTag(w.path).catch(() => null);
44553
45643
  let profile;
44554
45644
  try {
44555
45645
  profile = resolveViewerProfileForRepo(w.path);
@@ -44557,7 +45647,7 @@ async function startMcpServer() {
44557
45647
  profile = resolveViewerProfile();
44558
45648
  }
44559
45649
  const suffix = formatWorkspaceProfileSuffix(profile);
44560
- return slug ? `${w.name} ${w.path} github:${slug}${suffix}` : `${w.name} ${w.path}${suffix}`;
45650
+ return tag ? `${w.name} ${w.path} ${tag}${suffix}` : `${w.name} ${w.path}${suffix}`;
44561
45651
  })
44562
45652
  );
44563
45653
  return {
@@ -44574,7 +45664,7 @@ async function startMcpServer() {
44574
45664
  async () => {
44575
45665
  const threads = orch.getThreads();
44576
45666
  const lines = threads.map((t) => {
44577
- const repo = t.repoPath === GLOBAL_WORKSPACE_ID2 ? "Orchestration" : (0, import_node_path73.basename)(t.repoPath) || t.repoPath;
45667
+ const repo = t.repoPath === GLOBAL_WORKSPACE_ID2 ? "Orchestration" : (0, import_node_path74.basename)(t.repoPath) || t.repoPath;
44578
45668
  const live = orch.threadLooksLive(t) ? readTurnLive(t.id) : null;
44579
45669
  const parent = t.parentThreadId ? ` parent:${t.parentThreadId.slice(0, 8)}` : "";
44580
45670
  const preview = lastMessagePreview(t.messages, 80);
@@ -45182,7 +46272,7 @@ async function startMcpServer() {
45182
46272
  const ident = issue?.identifier ?? ref.trim();
45183
46273
  const attachments = issue ? [
45184
46274
  {
45185
- id: (0, import_node_crypto28.randomUUID)(),
46275
+ id: (0, import_node_crypto30.randomUUID)(),
45186
46276
  name: issue.identifier,
45187
46277
  kind: "issue",
45188
46278
  content: [
@@ -45404,7 +46494,7 @@ async function startMcpServer() {
45404
46494
  error: true,
45405
46495
  action,
45406
46496
  lastError: message,
45407
- 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
46497
+ 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
45408
46498
  },
45409
46499
  true
45410
46500
  );
@@ -45714,7 +46804,7 @@ async function startMcpServer() {
45714
46804
  );
45715
46805
  server.tool(
45716
46806
  "list_issues",
45717
- "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.",
46807
+ "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.",
45718
46808
  {
45719
46809
  repoPath: import_zod11.z.string(),
45720
46810
  query: import_zod11.z.string().optional().describe("Search title, identifier, or description"),
@@ -45744,7 +46834,7 @@ init_protocol2();
45744
46834
  init_metadata();
45745
46835
 
45746
46836
  // src/agent-rpc/server.ts
45747
- var import_node_crypto29 = require("crypto");
46837
+ var import_node_crypto31 = require("crypto");
45748
46838
  var import_node_http6 = require("http");
45749
46839
  var import_ws5 = require("ws");
45750
46840
  init_protocol2();
@@ -45753,7 +46843,7 @@ function tokensEqual(a, b) {
45753
46843
  const left = Buffer.from(a);
45754
46844
  const right = Buffer.from(b);
45755
46845
  if (left.length !== right.length) return false;
45756
- return (0, import_node_crypto29.timingSafeEqual)(left, right);
46846
+ return (0, import_node_crypto31.timingSafeEqual)(left, right);
45757
46847
  }
45758
46848
  function authTokenFromUpgrade(req) {
45759
46849
  try {
@@ -45786,9 +46876,9 @@ var AgentRpcServer = class {
45786
46876
  async listen(opts = {}) {
45787
46877
  const host = opts.host ?? "127.0.0.1";
45788
46878
  const port = opts.port ?? 0;
45789
- const authToken = (0, import_node_crypto29.randomBytes)(24).toString("base64url");
46879
+ const authToken = (0, import_node_crypto31.randomBytes)(24).toString("base64url");
45790
46880
  this.meta = {
45791
- runtimeId: (0, import_node_crypto29.randomUUID)(),
46881
+ runtimeId: (0, import_node_crypto31.randomUUID)(),
45792
46882
  pid: process.pid,
45793
46883
  protocolVersion: AGENT_RPC_PROTOCOL_VERSION,
45794
46884
  authToken,
@@ -46587,11 +47677,11 @@ init_model_context();
46587
47677
  init_meta();
46588
47678
 
46589
47679
  // src/slack/workspaces.ts
46590
- var import_node_path74 = require("path");
47680
+ var import_node_path75 = require("path");
46591
47681
  init_paths();
46592
47682
  init_secure_file();
46593
47683
  function storePath2() {
46594
- return (0, import_node_path74.join)(appDataDir(), "slack-workspaces.json");
47684
+ return (0, import_node_path75.join)(appDataDir(), "slack-workspaces.json");
46595
47685
  }
46596
47686
  function readStore2() {
46597
47687
  try {
@@ -46682,7 +47772,7 @@ function slackTokenFor(ws, kind = "read") {
46682
47772
  }
46683
47773
 
46684
47774
  // src/slack/oauth.ts
46685
- var import_node_crypto30 = require("crypto");
47775
+ var import_node_crypto32 = require("crypto");
46686
47776
  init_app_settings();
46687
47777
  var SLACK_BOT_SCOPES = [
46688
47778
  "app_mentions:read",
@@ -46776,7 +47866,7 @@ async function startSlackOAuth(opts) {
46776
47866
  throw new SlackOAuthCancelledError();
46777
47867
  }
46778
47868
  const { clientId } = slackOAuthCredentials();
46779
- const state = (0, import_node_crypto30.randomBytes)(16).toString("hex");
47869
+ const state = (0, import_node_crypto32.randomBytes)(16).toString("hex");
46780
47870
  const authorizeUrl = slackOAuthAuthorizeUrl(clientId, state);
46781
47871
  const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
46782
47872
  const pollIntervalMs = opts?.pollIntervalMs ?? 400;
@@ -46838,19 +47928,19 @@ init_turn_live();
46838
47928
  init_thread_store();
46839
47929
 
46840
47930
  // src/slack/reply-target.ts
46841
- var import_node_fs74 = require("fs");
46842
- var import_node_path75 = require("path");
47931
+ var import_node_fs75 = require("fs");
47932
+ var import_node_path76 = require("path");
46843
47933
  init_paths();
46844
47934
  init_private_file();
46845
47935
  init_secure_file();
46846
47936
  function storePath3() {
46847
- return (0, import_node_path75.join)(appDataDir(), "slack-reply-to.json");
47937
+ return (0, import_node_path76.join)(appDataDir(), "slack-reply-to.json");
46848
47938
  }
46849
47939
  function readStore3() {
46850
47940
  const path2 = storePath3();
46851
- if (!(0, import_node_fs74.existsSync)(path2)) return {};
47941
+ if (!(0, import_node_fs75.existsSync)(path2)) return {};
46852
47942
  try {
46853
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs74.readFileSync)(path2, "utf8"));
47943
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs75.readFileSync)(path2, "utf8"));
46854
47944
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
46855
47945
  } catch {
46856
47946
  return {};
@@ -47685,7 +48775,7 @@ function resolveSlackListenMode(opts) {
47685
48775
  init_protocol();
47686
48776
 
47687
48777
  // src/relay/account-login.ts
47688
- var import_node_crypto31 = require("crypto");
48778
+ var import_node_crypto33 = require("crypto");
47689
48779
  init_fetch();
47690
48780
  init_protocol();
47691
48781
  var RemoteAccountLoginCancelled = class extends Error {
@@ -47740,7 +48830,7 @@ async function relayFetch(fetchImpl, url, init) {
47740
48830
  throw relayFetchError(err, url);
47741
48831
  }
47742
48832
  }
47743
- async function readJson4(res, url) {
48833
+ async function readJson5(res, url) {
47744
48834
  try {
47745
48835
  return await res.json();
47746
48836
  } catch {
@@ -47753,7 +48843,7 @@ async function readJson4(res, url) {
47753
48843
  async function startRemoteAccountLogin(opts) {
47754
48844
  if (opts.signal?.aborted) throw new RemoteAccountLoginCancelled();
47755
48845
  const origin = (opts.origin ?? remoteAccountHttpOrigin()).replace(/\/+$/, "");
47756
- const state = (0, import_node_crypto31.randomBytes)(16).toString("hex");
48846
+ const state = (0, import_node_crypto33.randomBytes)(16).toString("hex");
47757
48847
  const timeoutMs = opts.timeoutMs ?? 5 * 6e4;
47758
48848
  const pollIntervalMs = opts.pollIntervalMs ?? 400;
47759
48849
  const linkToken = opts.linkToken?.trim() ?? "";
@@ -47767,7 +48857,7 @@ async function startRemoteAccountLogin(opts) {
47767
48857
  ...linkToken ? { accountToken: linkToken } : {}
47768
48858
  })
47769
48859
  });
47770
- const startBody = await readJson4(started, startUrl);
48860
+ const startBody = await readJson5(started, startUrl);
47771
48861
  if (!started.ok || !startBody.url) {
47772
48862
  throw new Error(startBody.error || `Could not start sign-in (HTTP ${started.status}).`);
47773
48863
  }
@@ -47815,8 +48905,8 @@ async function disconnectRemoteAccount(opts) {
47815
48905
  }
47816
48906
 
47817
48907
  // src/slack/outbound-watch.ts
47818
- var import_node_fs75 = require("fs");
47819
- var import_node_path76 = require("path");
48908
+ var import_node_fs76 = require("fs");
48909
+ var import_node_path77 = require("path");
47820
48910
  init_paths();
47821
48911
  init_private_file();
47822
48912
  init_secure_file();
@@ -47829,7 +48919,7 @@ var POLL_INTERVAL_MS = 12e3;
47829
48919
  var lastPollMs = 0;
47830
48920
  var nameCache = /* @__PURE__ */ new Map();
47831
48921
  function storePath4() {
47832
- return (0, import_node_path76.join)(appDataDir(), "slack-outbound-watch.json");
48922
+ return (0, import_node_path77.join)(appDataDir(), "slack-outbound-watch.json");
47833
48923
  }
47834
48924
  function watchId(teamId, channelId, ts) {
47835
48925
  return `${teamId}:${channelId}:${ts}`;
@@ -47842,9 +48932,9 @@ function tsNewer(a, b) {
47842
48932
  }
47843
48933
  function readStore4() {
47844
48934
  const path2 = storePath4();
47845
- if (!(0, import_node_fs75.existsSync)(path2)) return [];
48935
+ if (!(0, import_node_fs76.existsSync)(path2)) return [];
47846
48936
  try {
47847
- const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs75.readFileSync)(path2, "utf8"));
48937
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs76.readFileSync)(path2, "utf8"));
47848
48938
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
47849
48939
  } catch {
47850
48940
  return [];
@@ -48211,12 +49301,14 @@ async function pollSlackOutboundWatches(opts) {
48211
49301
  CONTEXT_REVIEW_PATH,
48212
49302
  CONVENTION_SETUP_RELPATHS,
48213
49303
  COORDINATOR_TOOL_PLAYBOOK,
49304
+ CURSOR_ISSUES_UNAVAILABLE,
48214
49305
  CURSOR_SDK_INSTALL_HINT,
48215
49306
  DEFAULT_ABLETIME_HOST,
48216
49307
  DEFAULT_RUN_LOG_TAIL_CHARS,
48217
49308
  DEFAULT_WORKTREE_SORT,
48218
49309
  DETACHED_JOBS_DIR,
48219
49310
  EMPTY_MANAGED_ACCOUNTS,
49311
+ ENTIRE_ISSUES_ARE_TRAILS,
48220
49312
  FAMOUS_SOCCER_TEAMS,
48221
49313
  FOLLOW_UP_BEHAVIORS,
48222
49314
  GITHUB_GIT_AUTH_MODES,
@@ -48484,6 +49576,7 @@ async function pollSlackOutboundWatches(opts) {
48484
49576
  createThreadWorktree,
48485
49577
  currentBranch,
48486
49578
  cursorAdapter,
49579
+ cursorIssuesUnavailableReason,
48487
49580
  cursorSdkMessageToEvents,
48488
49581
  decodeBrightsyTarget,
48489
49582
  defaultScheduleName,
@@ -48533,6 +49626,7 @@ async function pollSlackOutboundWatches(opts) {
48533
49626
  ensureSlackCoordinator,
48534
49627
  ensureSlackDeviceIdentity,
48535
49628
  ensureWorkspace,
49629
+ entireUsesTrails,
48536
49630
  estimateMessageChars,
48537
49631
  estimateOccupancyTokens,
48538
49632
  estimateThreadChars,
@@ -48583,8 +49677,10 @@ async function pollSlackOutboundWatches(opts) {
48583
49677
  formatClaudeUsageTooltip,
48584
49678
  formatCloudTurnReply,
48585
49679
  formatConnectedOrchestratorReminder,
49680
+ formatCursorGitDirective,
48586
49681
  formatDetachedJobInvoke,
48587
49682
  formatDevAccessHint,
49683
+ formatEntireGitDirective,
48588
49684
  formatFetchError,
48589
49685
  formatGhLandError,
48590
49686
  formatGitAuthModeDirective,
@@ -48641,6 +49737,7 @@ async function pollSlackOutboundWatches(opts) {
48641
49737
  getBrightsySession,
48642
49738
  getCaffeinateHold,
48643
49739
  getClaudePlanUsage,
49740
+ getCursorGitStatus,
48644
49741
  getDefaultAgent,
48645
49742
  getDefaultAutonomy,
48646
49743
  getDefaultEffort,
@@ -48649,6 +49746,7 @@ async function pollSlackOutboundWatches(opts) {
48649
49746
  getDefaultRunScript,
48650
49747
  getDiff,
48651
49748
  getDiffSummary,
49749
+ getEntireGitStatus,
48652
49750
  getGitHubIssue,
48653
49751
  getGitHubStatus,
48654
49752
  getGithubGitAuthMode,
@@ -48866,6 +49964,8 @@ async function pollSlackOutboundWatches(opts) {
48866
49964
  localhostPreviewUrl,
48867
49965
  locksDir,
48868
49966
  loginAgent,
49967
+ loginCursorGit,
49968
+ loginEntireGit,
48869
49969
  loginEnvPrefix,
48870
49970
  loginManagedAccount,
48871
49971
  looksLikeComposerSkillExpansion,
@@ -48932,6 +50032,7 @@ async function pollSlackOutboundWatches(opts) {
48932
50032
  orchestrationQuotaOnLimit,
48933
50033
  orchestrationTitleNeedsSoccerNickname,
48934
50034
  orchestratorSessionPoisonedByBuiltins,
50035
+ originCli,
48935
50036
  originFetchBranch,
48936
50037
  originGhRepoEnv,
48937
50038
  outboundReplyFromTurn,
@@ -48942,11 +50043,13 @@ async function pollSlackOutboundWatches(opts) {
48942
50043
  parseCursorRunnerLine,
48943
50044
  parseDevPreview,
48944
50045
  parseDurationMs,
50046
+ parseEntireAuthContexts,
48945
50047
  parseForceStopMessage,
48946
50048
  parseGhStackViewJson,
48947
50049
  parseGitHubIssueNumber,
48948
50050
  parseGithubSlugFromRemoteUrl,
48949
50051
  parseMcpList,
50052
+ parseOriginAuthStatus,
48950
50053
  parsePlanQuestionsInput,
48951
50054
  parsePrDetailsView,
48952
50055
  parseSessionQuotaResetAt,
@@ -49049,9 +50152,11 @@ async function pollSlackOutboundWatches(opts) {
49049
50152
  resolveNewThreadOptions,
49050
50153
  resolveOrchChildFollowUp,
49051
50154
  resolveOrchestratorDefaults,
50155
+ resolveOriginRemote,
49052
50156
  resolvePlanMarkdown,
49053
50157
  resolvePrSelector,
49054
50158
  resolvePrSelectors,
50159
+ resolveProjectRemoteTag,
49055
50160
  resolveProjectReviewLabel,
49056
50161
  resolveQuotaFallbackAgent,
49057
50162
  resolveRepoRoot,
@@ -49102,6 +50207,7 @@ async function pollSlackOutboundWatches(opts) {
49102
50207
  setStatus,
49103
50208
  setVaultMasterKey,
49104
50209
  settingsSourceLabel,
50210
+ setupCursorGit,
49105
50211
  shiftQueuedItem,
49106
50212
  shouldAttachPastedText,
49107
50213
  shouldCompactContext,