@sideboard-ai/core 0.1.149 → 0.1.150

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 (60) hide show
  1. package/dist/{abletime-QEVAE67D.js → abletime-223A3LY6.js} +2 -2
  2. package/dist/{abletime-DI55AJX3.js → abletime-CLC5UJC2.js} +2 -2
  3. package/dist/agents/cursor-runner.cjs +40 -35
  4. package/dist/agents/cursor-runner.js +2 -2
  5. package/dist/{agents-HSVQ2Z6Y.js → agents-KKXGTEV6.js} +10 -9
  6. package/dist/{agents-V4MTDWXS.js → agents-YWBQXOA2.js} +10 -9
  7. package/dist/{app-settings-WJDCJFSY.js → app-settings-3CHCNIMD.js} +5 -1
  8. package/dist/{app-settings-ZCN632OJ.js → app-settings-J3JBCUOT.js} +5 -1
  9. package/dist/{chunk-I77RYPOH.js → chunk-4XNSGDXE.js} +12 -0
  10. package/dist/{chunk-KBWND62T.js → chunk-7F4XE6UG.js} +5 -5
  11. package/dist/{chunk-R4JP32P5.js → chunk-AF6O55PJ.js} +4 -4
  12. package/dist/{chunk-BOD5V52W.js → chunk-B7XK37WZ.js} +1 -1
  13. package/dist/{chunk-BGQBELGJ.js → chunk-CZD4NRDZ.js} +12 -0
  14. package/dist/{chunk-ZVYLEF72.js → chunk-DMRIZR6M.js} +6 -6
  15. package/dist/{chunk-TZWO7J4W.js → chunk-DXCNW2JF.js} +113 -52
  16. package/dist/{chunk-KPIYENTF.js → chunk-FNQNJOHY.js} +89 -5
  17. package/dist/{chunk-ED4UPEJX.js → chunk-G7SIQFBQ.js} +1 -1
  18. package/dist/{chunk-3QRGCKYL.js → chunk-HAS5EPBS.js} +2 -2
  19. package/dist/{chunk-LAFA56WD.js → chunk-HEII7W3Q.js} +1 -1
  20. package/dist/{chunk-EGDRPA4B.js → chunk-NOTANBYC.js} +6 -6
  21. package/dist/{chunk-JDYKQ6OI.js → chunk-NUPITVRF.js} +134 -73
  22. package/dist/{chunk-QTUESPAW.js → chunk-PTOSP4AH.js} +1 -1
  23. package/dist/{chunk-Q62SDB53.js → chunk-QGJHIJHV.js} +2 -2
  24. package/dist/{chunk-PTI5H3RK.js → chunk-SDN4ZYQH.js} +24 -13
  25. package/dist/{chunk-DVM4ID64.js → chunk-UFHNSFAS.js} +89 -5
  26. package/dist/{chunk-N3VNOFXQ.js → chunk-VO4SPEBS.js} +4 -4
  27. package/dist/chunk-VSFGPWCQ.js +62 -0
  28. package/dist/{chunk-N5PM7HGQ.js → chunk-W6N6GQME.js} +1 -1
  29. package/dist/{chunk-FKOIHGKV.js → chunk-WHU7YGWP.js} +1 -1
  30. package/dist/{chunk-VZJVKQHH.js → chunk-WLV7TRMR.js} +1 -1
  31. package/dist/chunk-Y4NAPW5I.js +60 -0
  32. package/dist/{chunk-B3SJXYIJ.js → chunk-Y7YBWWIG.js} +1 -1
  33. package/dist/{chunk-Q5DHVMXB.js → chunk-YGGBJY5A.js} +18 -7
  34. package/dist/{chunk-JTPDWTWJ.js → chunk-Z2PVJKMN.js} +137 -114
  35. package/dist/{chunk-YP3CSOZ6.js → chunk-ZGFYFVJF.js} +142 -114
  36. package/dist/{connected-teams-IJUJQ7R6.js → connected-teams-I2WZZZFA.js} +2 -2
  37. package/dist/{connected-teams-4A2AXDNN.js → connected-teams-XO766HB4.js} +2 -2
  38. package/dist/{coordinator-prompt-ZWBMAIWA.js → coordinator-prompt-NYR5TPBD.js} +6 -5
  39. package/dist/{coordinator-prompt-5GXGBGEZ.js → coordinator-prompt-UCQJTFA7.js} +6 -5
  40. package/dist/{global-workspace-2SIPM4PW.js → global-workspace-4QHDAD2D.js} +7 -6
  41. package/dist/{global-workspace-OJRXWXEA.js → global-workspace-4SENNWBU.js} +7 -6
  42. package/dist/index.cjs +998 -574
  43. package/dist/index.d.cts +91 -10
  44. package/dist/index.d.ts +91 -10
  45. package/dist/index.js +268 -93
  46. package/dist/mcp/run-stdio.cjs +976 -579
  47. package/dist/mcp/run-stdio.js +196 -43
  48. package/dist/{orchestrator-6TXQWBXN.js → orchestrator-GZE4X3XV.js} +14 -11
  49. package/dist/{orchestrator-Z2O5OVJA.js → orchestrator-QSBRWME5.js} +16 -13
  50. package/dist/{plan-file-6O7G4VPQ.js → plan-file-5S5TUNAW.js} +2 -2
  51. package/dist/{plan-file-PHVKUAEE.js → plan-file-IBSU2PVW.js} +2 -2
  52. package/dist/{run-XQKGHQZ2.js → run-CAGKZLHA.js} +1 -1
  53. package/dist/{run-USF6FRKV.js → run-EBWL2Q4N.js} +1 -1
  54. package/dist/{workspaces-UUJNOO4T.js → workspaces-7S7QKCUE.js} +8 -7
  55. package/dist/{workspaces-3NUUZTQK.js → workspaces-RDUSB6FD.js} +8 -7
  56. package/dist/{worktree-RZRJNHYL.js → worktree-63AGPPLO.js} +5 -4
  57. package/dist/{worktree-UCWWTVLS.js → worktree-JIZ3QMZX.js} +5 -4
  58. package/dist/worktree-exclude-6MQWGWV5.js +11 -0
  59. package/dist/worktree-exclude-TV6EV6BS.js +9 -0
  60. package/package.json +1 -1
@@ -1764,16 +1764,98 @@ var init_path = __esm({
1764
1764
  }
1765
1765
  });
1766
1766
 
1767
+ // src/git/github-agent-auth.ts
1768
+ function githubAgentAuthDir() {
1769
+ const override = process.env.SIDEBOARD_GIT_AUTH_DIR?.trim();
1770
+ if (override) return override;
1771
+ return (0, import_node_path12.join)((0, import_node_os4.homedir)(), ".sideboard-git-auth");
1772
+ }
1773
+ function githubCredentialStorePath() {
1774
+ return (0, import_node_path12.join)(githubAgentAuthDir(), "git-credentials");
1775
+ }
1776
+ function githubGhConfigDir() {
1777
+ return (0, import_node_path12.join)(githubAgentAuthDir(), "gh");
1778
+ }
1779
+ function writePrivateFile2(file, body) {
1780
+ (0, import_node_fs11.mkdirSync)((0, import_node_path12.dirname)(file), { recursive: true, mode: 448 });
1781
+ try {
1782
+ (0, import_node_fs11.chmodSync)((0, import_node_path12.dirname)(file), 448);
1783
+ } catch {
1784
+ }
1785
+ (0, import_node_fs11.writeFileSync)(file, body, { encoding: "utf8", mode: 384 });
1786
+ try {
1787
+ (0, import_node_fs11.chmodSync)(file, 384);
1788
+ } catch {
1789
+ }
1790
+ }
1791
+ function gitCredentialStoreContents(token) {
1792
+ return `https://x-access-token:${encodeURIComponent(token)}@github.com
1793
+ `;
1794
+ }
1795
+ function ghHostsYml(token, user = "x-access-token") {
1796
+ const u = user.replace(/[^A-Za-z0-9._-]/g, "") || "x-access-token";
1797
+ return [
1798
+ "github.com:",
1799
+ " git_protocol: https",
1800
+ ` user: ${u}`,
1801
+ ` oauth_token: ${token}`,
1802
+ " users:",
1803
+ ` ${u}:`,
1804
+ ` oauth_token: ${token}`,
1805
+ ""
1806
+ ].join("\n");
1807
+ }
1808
+ function materializeGithubAgentAuth(token, user) {
1809
+ const trimmed = token.trim();
1810
+ if (!trimmed) return;
1811
+ const root = githubAgentAuthDir();
1812
+ (0, import_node_fs11.mkdirSync)(root, { recursive: true, mode: 448 });
1813
+ try {
1814
+ (0, import_node_fs11.chmodSync)(root, 448);
1815
+ } catch {
1816
+ }
1817
+ writePrivateFile2(githubCredentialStorePath(), gitCredentialStoreContents(trimmed));
1818
+ const ghDir = githubGhConfigDir();
1819
+ (0, import_node_fs11.mkdirSync)(ghDir, { recursive: true, mode: 448 });
1820
+ writePrivateFile2((0, import_node_path12.join)(ghDir, "hosts.yml"), ghHostsYml(trimmed, user));
1821
+ writePrivateFile2((0, import_node_path12.join)(ghDir, "config.yml"), "git_protocol: https\nprompt: disabled\n");
1822
+ }
1823
+ function githubAgentAuthReady() {
1824
+ return (0, import_node_fs11.existsSync)(githubCredentialStorePath()) && (0, import_node_fs11.existsSync)((0, import_node_path12.join)(githubGhConfigDir(), "hosts.yml"));
1825
+ }
1826
+ function githubCredentialHelperGitConfig() {
1827
+ const file = githubCredentialStorePath();
1828
+ return [
1829
+ { key: "credential.helper", value: "" },
1830
+ { key: "credential.helper", value: `store --file=${file}` }
1831
+ ];
1832
+ }
1833
+ function githubGhConfigEnv() {
1834
+ return {
1835
+ GH_CONFIG_DIR: githubGhConfigDir(),
1836
+ GH_PROMPT_DISABLED: "1"
1837
+ };
1838
+ }
1839
+ var import_node_fs11, import_node_os4, import_node_path12;
1840
+ var init_github_agent_auth = __esm({
1841
+ "src/git/github-agent-auth.ts"() {
1842
+ "use strict";
1843
+ import_node_fs11 = require("fs");
1844
+ import_node_os4 = require("os");
1845
+ import_node_path12 = require("path");
1846
+ }
1847
+ });
1848
+
1767
1849
  // src/git/stale-lock.ts
1768
1850
  function isIndexLockError(text5) {
1769
1851
  return /Unable to create ['"][^'"]*index\.lock['"]: File exists/i.test(text5);
1770
1852
  }
1771
1853
  function clearStaleIndexLock(gitDir, maxAgeMs = STALE_INDEX_LOCK_MS, now = Date.now()) {
1772
- const lockPath = (0, import_node_path12.join)(gitDir, "index.lock");
1854
+ const lockPath = (0, import_node_path13.join)(gitDir, "index.lock");
1773
1855
  try {
1774
- if (!(0, import_node_fs11.existsSync)(lockPath)) return null;
1775
- if (now - (0, import_node_fs11.statSync)(lockPath).mtimeMs < maxAgeMs) return null;
1776
- (0, import_node_fs11.unlinkSync)(lockPath);
1856
+ if (!(0, import_node_fs12.existsSync)(lockPath)) return null;
1857
+ if (now - (0, import_node_fs12.statSync)(lockPath).mtimeMs < maxAgeMs) return null;
1858
+ (0, import_node_fs12.unlinkSync)(lockPath);
1777
1859
  return lockPath;
1778
1860
  } catch {
1779
1861
  return null;
@@ -1788,12 +1870,12 @@ function clearStaleIndexLocks(gitDirs, maxAgeMs = STALE_INDEX_LOCK_MS) {
1788
1870
  }
1789
1871
  return removed;
1790
1872
  }
1791
- var import_node_fs11, import_node_path12, STALE_INDEX_LOCK_MS;
1873
+ var import_node_fs12, import_node_path13, STALE_INDEX_LOCK_MS;
1792
1874
  var init_stale_lock = __esm({
1793
1875
  "src/git/stale-lock.ts"() {
1794
1876
  "use strict";
1795
- import_node_fs11 = require("fs");
1796
- import_node_path12 = require("path");
1877
+ import_node_fs12 = require("fs");
1878
+ import_node_path13 = require("path");
1797
1879
  STALE_INDEX_LOCK_MS = 2e4;
1798
1880
  }
1799
1881
  });
@@ -1890,7 +1972,10 @@ async function gh(args, cwd, opts) {
1890
1972
  timeoutMs: opts?.timeoutMs,
1891
1973
  env: {
1892
1974
  GH_PROMPT_DISABLED: "1",
1893
- GIT_TERMINAL_PROMPT: "0"
1975
+ GIT_TERMINAL_PROMPT: "0",
1976
+ // Isolated hosts.yml uses git_protocol: https so `gh pr create` does not
1977
+ // shell out over SSH when Settings → Git is SSH (ask_git in Electron).
1978
+ ...githubAgentAuthReady() ? githubGhConfigEnv() : {}
1894
1979
  }
1895
1980
  });
1896
1981
  }
@@ -1906,6 +1991,7 @@ var init_run = __esm({
1906
1991
  "use strict";
1907
1992
  import_execa2 = require("execa");
1908
1993
  init_path();
1994
+ init_github_agent_auth();
1909
1995
  init_stale_lock();
1910
1996
  }
1911
1997
  });
@@ -2744,6 +2830,18 @@ var init_worktree_labels = __esm({
2744
2830
  function isGhRateLimitError(text5) {
2745
2831
  return /API rate limit (already )?exceeded/i.test(text5) || /rate limit exceeded/i.test(text5);
2746
2832
  }
2833
+ function isGhPrBodyTooLongError(text5) {
2834
+ return /body is too long|body.{0,20}too long|maximum is 65536|exceeds the maximum allowed size|request (entity |is )?too large|payload too large|413 Request Entity Too Large/i.test(
2835
+ text5
2836
+ );
2837
+ }
2838
+ function clampGithubPrBody(body, max = GITHUB_PR_BODY_SAFE_CHARS) {
2839
+ const t = body.replace(/\s+$/u, "");
2840
+ if (t.length <= max) return t;
2841
+ const note = "\n\n_(truncated to fit GitHub\u2019s PR body limit)_";
2842
+ const keep = Math.max(0, max - note.length);
2843
+ return `${t.slice(0, keep).replace(/\s+$/u, "")}${note}`;
2844
+ }
2747
2845
  function formatRateLimitResetHint(resetEpochSec, nowMs = Date.now()) {
2748
2846
  const ms = resetEpochSec * 1e3 - nowMs;
2749
2847
  if (ms <= 0) return "soon";
@@ -2754,18 +2852,32 @@ function formatRateLimitResetHint(resetEpochSec, nowMs = Date.now()) {
2754
2852
  const hours = Math.ceil(mins / 60);
2755
2853
  return `in about ${hours} hour${hours === 1 ? "" : "s"}`;
2756
2854
  }
2855
+ function omitGhBodyArg(text5) {
2856
+ return text5.replace(
2857
+ /--body(?:-file)?(\s+|=)(?:'[^']*'|"[^"]*"|\S+)/g,
2858
+ "--body <omitted>"
2859
+ );
2860
+ }
2861
+ function capGhErrorText(text5, max = 400) {
2862
+ const t = text5.trim();
2863
+ if (t.length <= max) return t;
2864
+ return `${t.slice(0, max - 1).trimEnd()}\u2026`;
2865
+ }
2757
2866
  function extractGhErrorDetail(text5) {
2758
- const trimmed = text5.trim();
2867
+ const trimmed = omitGhBodyArg(text5.trim());
2759
2868
  if (!trimmed) return "";
2869
+ if (isGhPrBodyTooLongError(text5) || isGhPrBodyTooLongError(trimmed)) {
2870
+ return "GraphQL: body is too long (GitHub PR body / request size limit).";
2871
+ }
2760
2872
  const graphql = trimmed.match(/\bGraphQL:\s*(.+)$/im);
2761
- if (graphql?.[1]) return `GraphQL: ${graphql[1].trim()}`;
2873
+ if (graphql?.[1]) return capGhErrorText(`GraphQL: ${graphql[1].trim()}`);
2762
2874
  const http = trimmed.match(/\bHTTP\s+\d{3}:\s*(.+)$/im);
2763
- if (http?.[1]) return `HTTP: ${http[1].trim()}`;
2875
+ if (http?.[1]) return capGhErrorText(`HTTP: ${http[1].trim()}`);
2764
2876
  const lines = trimmed.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
2765
2877
  if (lines.length > 1 && /^Command failed with exit code/i.test(lines[0])) {
2766
- return lines.slice(1).join(" ").trim() || lines[0];
2878
+ return capGhErrorText(lines.slice(1).join(" ").trim() || lines[0]);
2767
2879
  }
2768
- return trimmed;
2880
+ return capGhErrorText(trimmed);
2769
2881
  }
2770
2882
  function formatGhLandError(raw, opts) {
2771
2883
  const trimmed = raw.trim();
@@ -2783,6 +2895,10 @@ function formatGhLandError(raw, opts) {
2783
2895
  const hint = opts?.targetedRepo ? ` Branch was pushed to origin \u2014 confirm it exists on GitHub and differs from the base branch.${target}${head}.` : " Often `gh` targeted upstream instead of origin. Branch was pushed \u2014 retry in the latest Sideboard, or run: gh pr create -R <owner/name> --base main --head <branch>.";
2784
2896
  return `Could not create the pull request.${hint}`;
2785
2897
  }
2898
+ if (isGhPrBodyTooLongError(raw) || isGhPrBodyTooLongError(detail)) {
2899
+ const pushNote = opts?.pushed === false ? "" : " Your branch was already pushed.";
2900
+ return `GitHub rejected the pull request: the GraphQL body is too long (PR description / request size, limit ${GITHUB_PR_BODY_MAX_CHARS} characters).${pushNote} Retry with a short title and body via \`gh pr create --body-file\` (or send_to_thread so the worktree agent writes a brief description). Do not paste a changelog or diff into --body.`;
2901
+ }
2786
2902
  if (isGhRateLimitError(raw) || isGhRateLimitError(detail)) {
2787
2903
  const when = opts?.resetAt ? ` Try again ${formatRateLimitResetHint(opts.resetAt, opts.nowMs)}.` : " Wait a few minutes and try again.";
2788
2904
  const pushNote = opts?.pushed === false ? "" : " Your branch was already pushed.";
@@ -2803,15 +2919,82 @@ function formatMergePrError(raw) {
2803
2919
  }
2804
2920
  return extractGhErrorDetail(trimmed) || trimmed;
2805
2921
  }
2922
+ var GITHUB_PR_BODY_MAX_CHARS, GITHUB_PR_BODY_SAFE_CHARS;
2806
2923
  var init_gh_errors = __esm({
2807
2924
  "src/git/gh-errors.ts"() {
2808
2925
  "use strict";
2926
+ GITHUB_PR_BODY_MAX_CHARS = 65536;
2927
+ GITHUB_PR_BODY_SAFE_CHARS = 6e4;
2928
+ }
2929
+ });
2930
+
2931
+ // src/git/worktree-exclude.ts
2932
+ var worktree_exclude_exports = {};
2933
+ __export(worktree_exclude_exports, {
2934
+ WORKTREE_SIDEBOARD_EXCLUDE: () => WORKTREE_SIDEBOARD_EXCLUDE,
2935
+ ensureWorktreeSideboardIgnored: () => ensureWorktreeSideboardIgnored
2936
+ });
2937
+ function excludeHasSideboard(body) {
2938
+ return body.split(/\r?\n/).some((line) => {
2939
+ const t = line.trim();
2940
+ return t === ".sideboard/" || t === ".sideboard" || t === "/.sideboard/";
2941
+ });
2942
+ }
2943
+ async function ensureWorktreeSideboardIgnored(worktreePath) {
2944
+ if (!worktreePath.trim()) return;
2945
+ const gitDir = await git(["rev-parse", "--absolute-git-dir"], worktreePath, {
2946
+ reject: false,
2947
+ timeoutMs: 5e3
2948
+ });
2949
+ if (gitDir.exitCode !== 0) return;
2950
+ const dir = gitDir.stdout.trim();
2951
+ if (!dir) return;
2952
+ const excludePath = (0, import_node_path14.join)(dir, "info", "exclude");
2953
+ (0, import_node_fs13.mkdirSync)((0, import_node_path14.dirname)(excludePath), { recursive: true });
2954
+ let body = "";
2955
+ try {
2956
+ body = (0, import_node_fs13.readFileSync)(excludePath, "utf8");
2957
+ } catch {
2958
+ body = "";
2959
+ }
2960
+ if (!excludeHasSideboard(body)) {
2961
+ const prefix = body && !body.endsWith("\n") ? `${body}
2962
+ ` : body;
2963
+ (0, import_node_fs13.writeFileSync)(
2964
+ excludePath,
2965
+ `${prefix}${EXCLUDE_COMMENT}
2966
+ ${WORKTREE_SIDEBOARD_EXCLUDE}
2967
+ `,
2968
+ "utf8"
2969
+ );
2970
+ }
2971
+ const listed = await git(["ls-files", "-z", "--", ".sideboard"], worktreePath, {
2972
+ reject: false,
2973
+ timeoutMs: 5e3
2974
+ });
2975
+ if (listed.exitCode !== 0 || !listed.stdout) return;
2976
+ const files = listed.stdout.split("\0").filter(Boolean);
2977
+ if (files.length === 0) return;
2978
+ await git(["update-index", "--skip-worktree", "--", ...files], worktreePath, {
2979
+ reject: false,
2980
+ timeoutMs: 8e3
2981
+ });
2982
+ }
2983
+ var import_node_fs13, import_node_path14, WORKTREE_SIDEBOARD_EXCLUDE, EXCLUDE_COMMENT;
2984
+ var init_worktree_exclude = __esm({
2985
+ "src/git/worktree-exclude.ts"() {
2986
+ "use strict";
2987
+ import_node_fs13 = require("fs");
2988
+ import_node_path14 = require("path");
2989
+ init_run();
2990
+ WORKTREE_SIDEBOARD_EXCLUDE = ".sideboard/";
2991
+ EXCLUDE_COMMENT = "# Sideboard worktree scratch \u2014 do not commit";
2809
2992
  }
2810
2993
  });
2811
2994
 
2812
2995
  // src/store/secret-vault.ts
2813
2996
  function secretVaultPath() {
2814
- return (0, import_node_path13.join)(appDataDir(), "secrets.json");
2997
+ return (0, import_node_path15.join)(appDataDir(), "secrets.json");
2815
2998
  }
2816
2999
  function loadSecretVault() {
2817
3000
  const parsed = readSecureJson(secretVaultPath());
@@ -2870,11 +3053,11 @@ function normalizeVault(raw) {
2870
3053
  }
2871
3054
  return out;
2872
3055
  }
2873
- var import_node_path13;
3056
+ var import_node_path15;
2874
3057
  var init_secret_vault = __esm({
2875
3058
  "src/store/secret-vault.ts"() {
2876
3059
  "use strict";
2877
- import_node_path13 = require("path");
3060
+ import_node_path15 = require("path");
2878
3061
  init_paths();
2879
3062
  init_secure_file();
2880
3063
  }
@@ -2903,6 +3086,7 @@ var init_issue_source_labels = __esm({
2903
3086
  // src/store/app-settings.ts
2904
3087
  var app_settings_exports = {};
2905
3088
  __export(app_settings_exports, {
3089
+ FOLLOW_UP_BEHAVIORS: () => FOLLOW_UP_BEHAVIORS,
2906
3090
  GITHUB_GIT_AUTH_MODES: () => GITHUB_GIT_AUTH_MODES,
2907
3091
  HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
2908
3092
  ISSUE_SOURCE_LABELS: () => ISSUE_SOURCE_LABELS,
@@ -2928,6 +3112,7 @@ __export(app_settings_exports, {
2928
3112
  disconnectLinearConnection: () => disconnectLinearConnection,
2929
3113
  emptyPublicIntegrations: () => emptyPublicIntegrations,
2930
3114
  ensureSlackDeviceIdentity: () => ensureSlackDeviceIdentity,
3115
+ followUpBehavior: () => followUpBehavior,
2931
3116
  getAbleTimeAccessToken: () => getAbleTimeAccessToken,
2932
3117
  getAbleTimeHost: () => getAbleTimeHost,
2933
3118
  getDefaultAgent: () => getDefaultAgent,
@@ -3036,10 +3221,10 @@ function toPublicAppSettings(settings) {
3036
3221
  };
3037
3222
  }
3038
3223
  function appSettingsPath() {
3039
- return (0, import_node_path14.join)(appDataDir(), "settings.json");
3224
+ return (0, import_node_path16.join)(appDataDir(), "settings.json");
3040
3225
  }
3041
3226
  function claudeUserSettingsPath() {
3042
- return (0, import_node_path14.join)((0, import_node_os4.homedir)(), ".claude", "settings.json");
3227
+ return (0, import_node_path16.join)((0, import_node_os5.homedir)(), ".claude", "settings.json");
3043
3228
  }
3044
3229
  function normalizeClaude(raw) {
3045
3230
  if (!raw || typeof raw !== "object") return {};
@@ -3284,6 +3469,9 @@ function normalizeAdvanced(raw) {
3284
3469
  if (typeof source.orchestrationQuotaFallbackAgent === "string" && DEFAULT_AGENTS.has(source.orchestrationQuotaFallbackAgent) && source.orchestrationQuotaFallbackAgent !== "brightsy") {
3285
3470
  out.orchestrationQuotaFallbackAgent = source.orchestrationQuotaFallbackAgent;
3286
3471
  }
3472
+ if (source.followUpBehavior === "queue" || source.followUpBehavior === "steer") {
3473
+ out.followUpBehavior = source.followUpBehavior;
3474
+ }
3287
3475
  return out;
3288
3476
  }
3289
3477
  function normalizeSettings(raw) {
@@ -3343,10 +3531,10 @@ function normalizeSettings(raw) {
3343
3531
  }
3344
3532
  function readSettingsFile() {
3345
3533
  const path = appSettingsPath();
3346
- if (!(0, import_node_fs12.existsSync)(path)) return { ...EMPTY_SETTINGS };
3534
+ if (!(0, import_node_fs14.existsSync)(path)) return { ...EMPTY_SETTINGS };
3347
3535
  try {
3348
3536
  chmodOwnerOnly(path);
3349
- const parsed = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
3537
+ const parsed = JSON.parse((0, import_node_fs14.readFileSync)(path, "utf8"));
3350
3538
  return normalizeSettings(parsed);
3351
3539
  } catch {
3352
3540
  return { ...EMPTY_SETTINGS };
@@ -3892,7 +4080,7 @@ function ensureSlackDeviceIdentity(settings = loadAppSettings()) {
3892
4080
  }
3893
4081
  if (!deviceLabel) {
3894
4082
  try {
3895
- deviceLabel = (0, import_node_os4.hostname)().split(".")[0]?.trim() || "This Mac";
4083
+ deviceLabel = (0, import_node_os5.hostname)().split(".")[0]?.trim() || "This Mac";
3896
4084
  } catch {
3897
4085
  deviceLabel = "This Mac";
3898
4086
  }
@@ -3975,6 +4163,9 @@ function updateAdvancedSettings(patch) {
3975
4163
  if (typeof patch.orchestrationQuotaFallbackAgent === "string" && DEFAULT_AGENTS.has(patch.orchestrationQuotaFallbackAgent) && patch.orchestrationQuotaFallbackAgent !== "brightsy") {
3976
4164
  advanced.orchestrationQuotaFallbackAgent = patch.orchestrationQuotaFallbackAgent;
3977
4165
  }
4166
+ if (patch.followUpBehavior === "queue" || patch.followUpBehavior === "steer") {
4167
+ advanced.followUpBehavior = patch.followUpBehavior;
4168
+ }
3978
4169
  return saveAppSettings({ ...current, advanced });
3979
4170
  }
3980
4171
  function autoRenameBranchEnabled(settings = loadAppSettings()) {
@@ -4020,6 +4211,9 @@ function orchestrationQuotaFallbackAgent(settings = loadAppSettings()) {
4020
4211
  }
4021
4212
  return "cursor";
4022
4213
  }
4214
+ function followUpBehavior(settings = loadAppSettings()) {
4215
+ return settings.advanced.followUpBehavior === "queue" ? "queue" : "steer";
4216
+ }
4023
4217
  function maxConcurrentAgents(settings = loadAppSettings()) {
4024
4218
  const n = settings.advanced.maxConcurrent;
4025
4219
  if (typeof n === "number" && Number.isFinite(n)) {
@@ -4107,14 +4301,14 @@ function childEnvWithAppSettings(extra) {
4107
4301
  function harnessEnvKey(harness) {
4108
4302
  return HARNESS_ENV_KEYS[harness];
4109
4303
  }
4110
- var import_node_crypto3, import_node_fs12, import_node_os4, import_node_path14, HARNESS_ENV_KEYS, DEFAULT_AGENTS, GITHUB_GIT_AUTH_MODES, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, GIT_AUTH_MODES, EMPTY_SETTINGS, DEFAULT_CLI_BIN;
4304
+ var import_node_crypto3, import_node_fs14, import_node_os5, import_node_path16, HARNESS_ENV_KEYS, DEFAULT_AGENTS, GITHUB_GIT_AUTH_MODES, FOLLOW_UP_BEHAVIORS, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, GIT_AUTH_MODES, EMPTY_SETTINGS, DEFAULT_CLI_BIN;
4111
4305
  var init_app_settings = __esm({
4112
4306
  "src/store/app-settings.ts"() {
4113
4307
  "use strict";
4114
4308
  import_node_crypto3 = require("crypto");
4115
- import_node_fs12 = require("fs");
4116
- import_node_os4 = require("os");
4117
- import_node_path14 = require("path");
4309
+ import_node_fs14 = require("fs");
4310
+ import_node_os5 = require("os");
4311
+ import_node_path16 = require("path");
4118
4312
  init_thinking_effort();
4119
4313
  init_nested_electron_env();
4120
4314
  init_paths();
@@ -4136,6 +4330,7 @@ var init_app_settings = __esm({
4136
4330
  "cursor"
4137
4331
  ]);
4138
4332
  GITHUB_GIT_AUTH_MODES = ["auto", "gh", "ssh", "token"];
4333
+ FOLLOW_UP_BEHAVIORS = ["steer", "queue"];
4139
4334
  CLOUD_CONNECT_AGENTS = /* @__PURE__ */ new Set([
4140
4335
  "claude",
4141
4336
  "codex",
@@ -4163,89 +4358,26 @@ var init_app_settings = __esm({
4163
4358
  }
4164
4359
  });
4165
4360
 
4166
- // src/git/github-agent-auth.ts
4167
- function githubAgentAuthDir() {
4168
- const override = process.env.SIDEBOARD_GIT_AUTH_DIR?.trim();
4169
- if (override) return override;
4170
- return (0, import_node_path15.join)((0, import_node_os5.homedir)(), ".sideboard-git-auth");
4171
- }
4172
- function githubCredentialStorePath() {
4173
- return (0, import_node_path15.join)(githubAgentAuthDir(), "git-credentials");
4174
- }
4175
- function githubGhConfigDir() {
4176
- return (0, import_node_path15.join)(githubAgentAuthDir(), "gh");
4177
- }
4178
- function writePrivateFile2(file, body) {
4179
- (0, import_node_fs13.mkdirSync)((0, import_node_path15.dirname)(file), { recursive: true, mode: 448 });
4180
- try {
4181
- (0, import_node_fs13.chmodSync)((0, import_node_path15.dirname)(file), 448);
4182
- } catch {
4183
- }
4184
- (0, import_node_fs13.writeFileSync)(file, body, { encoding: "utf8", mode: 384 });
4185
- try {
4186
- (0, import_node_fs13.chmodSync)(file, 384);
4187
- } catch {
4361
+ // src/git/git-auth-mode.ts
4362
+ function githubHttpsInsteadOfEntries(remoteUrl) {
4363
+ const entries = [...HTTPS_REWRITE];
4364
+ const url = remoteUrl?.trim() ?? "";
4365
+ const scpAlias = url.match(/^(git@github\.com-[^:]+):/i);
4366
+ if (scpAlias?.[1]) {
4367
+ entries.push({
4368
+ key: "url.https://github.com/.insteadOf",
4369
+ value: `${scpAlias[1]}:`
4370
+ });
4188
4371
  }
4189
- }
4190
- function gitCredentialStoreContents(token) {
4191
- return `https://x-access-token:${encodeURIComponent(token)}@github.com
4192
- `;
4193
- }
4194
- function ghHostsYml(token, user = "x-access-token") {
4195
- const u = user.replace(/[^A-Za-z0-9._-]/g, "") || "x-access-token";
4196
- return [
4197
- "github.com:",
4198
- " git_protocol: https",
4199
- ` user: ${u}`,
4200
- ` oauth_token: ${token}`,
4201
- " users:",
4202
- ` ${u}:`,
4203
- ` oauth_token: ${token}`,
4204
- ""
4205
- ].join("\n");
4206
- }
4207
- function materializeGithubAgentAuth(token, user) {
4208
- const trimmed = token.trim();
4209
- if (!trimmed) return;
4210
- const root = githubAgentAuthDir();
4211
- (0, import_node_fs13.mkdirSync)(root, { recursive: true, mode: 448 });
4212
- try {
4213
- (0, import_node_fs13.chmodSync)(root, 448);
4214
- } catch {
4372
+ const sshAlias = url.match(/^ssh:\/\/git@(github\.com-[^/]+)\//i);
4373
+ if (sshAlias?.[1]) {
4374
+ entries.push({
4375
+ key: "url.https://github.com/.insteadOf",
4376
+ value: `ssh://git@${sshAlias[1]}/`
4377
+ });
4215
4378
  }
4216
- writePrivateFile2(githubCredentialStorePath(), gitCredentialStoreContents(trimmed));
4217
- const ghDir = githubGhConfigDir();
4218
- (0, import_node_fs13.mkdirSync)(ghDir, { recursive: true, mode: 448 });
4219
- writePrivateFile2((0, import_node_path15.join)(ghDir, "hosts.yml"), ghHostsYml(trimmed, user));
4220
- writePrivateFile2((0, import_node_path15.join)(ghDir, "config.yml"), "git_protocol: https\nprompt: disabled\n");
4221
- }
4222
- function githubAgentAuthReady() {
4223
- return (0, import_node_fs13.existsSync)(githubCredentialStorePath()) && (0, import_node_fs13.existsSync)((0, import_node_path15.join)(githubGhConfigDir(), "hosts.yml"));
4224
- }
4225
- function githubCredentialHelperGitConfig() {
4226
- const file = githubCredentialStorePath();
4227
- return [
4228
- { key: "credential.helper", value: "" },
4229
- { key: "credential.helper", value: `store --file=${file}` }
4230
- ];
4231
- }
4232
- function githubGhConfigEnv() {
4233
- return {
4234
- GH_CONFIG_DIR: githubGhConfigDir(),
4235
- GH_PROMPT_DISABLED: "1"
4236
- };
4379
+ return entries;
4237
4380
  }
4238
- var import_node_fs13, import_node_os5, import_node_path15;
4239
- var init_github_agent_auth = __esm({
4240
- "src/git/github-agent-auth.ts"() {
4241
- "use strict";
4242
- import_node_fs13 = require("fs");
4243
- import_node_os5 = require("os");
4244
- import_node_path15 = require("path");
4245
- }
4246
- });
4247
-
4248
- // src/git/git-auth-mode.ts
4249
4381
  function nonInteractiveGitProcessEnv() {
4250
4382
  return {
4251
4383
  GIT_TERMINAL_PROMPT: "0",
@@ -4268,16 +4400,19 @@ function appendIndexedGitConfig(existing, entries) {
4268
4400
  out.GIT_CONFIG_COUNT = String(count + entries.length);
4269
4401
  return out;
4270
4402
  }
4271
- function githubAgentGitEnv(existing) {
4403
+ function githubAgentGitEnv(existing, opts) {
4272
4404
  const helpers = githubAgentAuthReady() ? githubCredentialHelperGitConfig() : [{ key: "credential.helper", value: "" }];
4273
- return appendIndexedGitConfig(existing, [...HTTPS_REWRITE, ...helpers]);
4405
+ return appendIndexedGitConfig(existing, [
4406
+ ...githubHttpsInsteadOfEntries(opts?.remoteUrl),
4407
+ ...helpers
4408
+ ]);
4274
4409
  }
4275
4410
  function applyGithubGitAuthEnv(existing, opts) {
4276
4411
  const out = { ...nonInteractiveGitProcessEnv() };
4277
4412
  const token = opts.token?.trim() || existing?.GH_TOKEN?.trim() || "" || "";
4278
4413
  if (token) materializeGithubAgentAuth(token);
4279
4414
  Object.assign(out, githubGhConfigEnv());
4280
- if (opts.mode === "ssh") return out;
4415
+ if (opts.mode === "ssh" && !token) return out;
4281
4416
  Object.assign(out, githubAgentGitEnv(existing));
4282
4417
  return out;
4283
4418
  }
@@ -4333,7 +4468,7 @@ async function warmGithubAgentAuth(opts) {
4333
4468
  }
4334
4469
  function normalizeWritableRoot(raw) {
4335
4470
  const trimmed = raw.trim().replace(/\/+$/, "");
4336
- return trimmed && (0, import_node_path16.isAbsolute)(trimmed) ? trimmed : null;
4471
+ return trimmed && (0, import_node_path17.isAbsolute)(trimmed) ? trimmed : null;
4337
4472
  }
4338
4473
  async function resolveCodexGitWritableRoots(cwd) {
4339
4474
  const roots = /* @__PURE__ */ new Set();
@@ -4390,8 +4525,9 @@ function formatGitAuthModeDirective(mode) {
4390
4525
  case "ssh":
4391
4526
  return [
4392
4527
  "Git authentication (Settings \u2192 Git mode: SSH):",
4393
- "- Keep SSH remotes (`git@github.com:\u2026`). Do not rewrite them to HTTPS.",
4394
- "- SSH is batch-mode: it will not prompt for a Keychain password. If push fails with `Permission denied (publickey)`, tell the user to unlock ssh-agent or switch Settings \u2192 Git to Auto / gh CLI \u2014 do not rewrite remotes yourself.",
4528
+ "- Stored remotes stay SSH (`git@github.com:\u2026`). Do not `git remote set-url` or rewrite them to HTTPS.",
4529
+ "- This process may push over HTTPS via `gh` (process-only `insteadOf`) so ask_git / unattended turns work when ssh-agent is missing \u2014 that does not change the stored remote.",
4530
+ "- SSH is batch-mode: it will not prompt for a Keychain password. If both SSH and HTTPS fail, tell the user to unlock ssh-agent or run `gh auth login` (or switch Settings \u2192 Git to Auto).",
4395
4531
  "- `gh` already authenticates for PRs/API (no token in the environment). Do not set GitHub token environment variables or run `gh auth login` from this turn.",
4396
4532
  "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
4397
4533
  ].join("\n");
@@ -4410,11 +4546,11 @@ function formatGitAuthModeDirective(mode) {
4410
4546
  ].join("\n");
4411
4547
  }
4412
4548
  }
4413
- var import_node_path16, HTTPS_REWRITE, TOKEN_TTL_MS, tokenMemo, GITHUB_CHILD_TOKEN_KEYS;
4549
+ var import_node_path17, HTTPS_REWRITE, TOKEN_TTL_MS, tokenMemo, GITHUB_CHILD_TOKEN_KEYS;
4414
4550
  var init_git_auth_mode = __esm({
4415
4551
  "src/git/git-auth-mode.ts"() {
4416
4552
  "use strict";
4417
- import_node_path16 = require("path");
4553
+ import_node_path17 = require("path");
4418
4554
  init_app_settings();
4419
4555
  init_github_agent_auth();
4420
4556
  init_run();
@@ -4762,7 +4898,7 @@ function attachmentsGitignoreBody() {
4762
4898
  }
4763
4899
  function isWorkspaceScratchPath(relativePath) {
4764
4900
  const p = relativePath.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "");
4765
- return p === ATTACHMENTS_DIR || p.startsWith(`${ATTACHMENTS_DIR}/`) || p === LEGACY_ATTACHMENTS_DIR || p.startsWith(`${LEGACY_ATTACHMENTS_DIR}/`) || p === ".context" || p.startsWith(".context/");
4901
+ return p === ATTACHMENTS_DIR || p.startsWith(`${ATTACHMENTS_DIR}/`) || p === LEGACY_ATTACHMENTS_DIR || p.startsWith(`${LEGACY_ATTACHMENTS_DIR}/`) || p === ".sideboard" || p.startsWith(".sideboard/") || p === ".context" || p.startsWith(".context/");
4766
4902
  }
4767
4903
  var ATTACHMENTS_DIR, LEGACY_ATTACHMENTS_DIR, ATTACHMENTS_GITIGNORE;
4768
4904
  var init_workspace_scratch = __esm({
@@ -4869,7 +5005,7 @@ async function resolveRepoRoot(cwd) {
4869
5005
  }
4870
5006
  function canonicalizeRepoPath(path) {
4871
5007
  try {
4872
- return (0, import_node_fs14.realpathSync)(path);
5008
+ return (0, import_node_fs15.realpathSync)(path);
4873
5009
  } catch {
4874
5010
  return path.replace(/\/+$/, "");
4875
5011
  }
@@ -5608,6 +5744,16 @@ function originFetchBranch(sourceRef) {
5608
5744
  if (ref.startsWith("origin/")) return ref.slice("origin/".length) || null;
5609
5745
  return ref;
5610
5746
  }
5747
+ async function originRemoteUrl(cwd) {
5748
+ const result = await git(["remote", "get-url", "origin"], cwd, { reject: false });
5749
+ return result.exitCode === 0 ? result.stdout.trim() : "";
5750
+ }
5751
+ function githubHttpsRetryEnv(remoteUrl) {
5752
+ return {
5753
+ GIT_TERMINAL_PROMPT: "0",
5754
+ ...githubAgentGitEnv(void 0, { remoteUrl })
5755
+ };
5756
+ }
5611
5757
  async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
5612
5758
  const first = await git(args, repoPath, { reject: false, timeoutMs });
5613
5759
  if (first.exitCode === 0) return true;
@@ -5617,15 +5763,14 @@ async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
5617
5763
  } catch {
5618
5764
  return false;
5619
5765
  }
5620
- if (mode === "ssh") return false;
5621
5766
  const token = await resolveGithubAgentToken(mode, repoPath);
5622
5767
  if (!token) return false;
5768
+ const remoteUrl = await originRemoteUrl(repoPath);
5623
5769
  const tryHttps = async (header) => git(args, repoPath, {
5624
5770
  reject: false,
5625
5771
  timeoutMs,
5626
- env: { GIT_TERMINAL_PROMPT: "0" },
5772
+ env: githubHttpsRetryEnv(remoteUrl),
5627
5773
  config: {
5628
- "url.https://github.com/.insteadOf": "git@github.com:",
5629
5774
  "http.extraHeader": header
5630
5775
  }
5631
5776
  });
@@ -5707,8 +5852,8 @@ async function refreshOriginAndMaybeFastForwardMain(repoPath, sourceRef) {
5707
5852
  }
5708
5853
  async function createThreadWorktree(opts) {
5709
5854
  let branchName = `thread/${opts.slug}`;
5710
- const worktreePath = (0, import_node_path17.join)(worktreesRoot(opts.repoPath), opts.slug);
5711
- if ((0, import_node_fs14.existsSync)(worktreePath)) {
5855
+ const worktreePath = (0, import_node_path18.join)(worktreesRoot(opts.repoPath), opts.slug);
5856
+ if ((0, import_node_fs15.existsSync)(worktreePath)) {
5712
5857
  throw new Error(`Worktree already exists at ${worktreePath}`);
5713
5858
  }
5714
5859
  await ensureGhPreferOrigin(opts.repoPath);
@@ -5743,13 +5888,14 @@ ${add.stdout}`;
5743
5888
  });
5744
5889
  branchName = added;
5745
5890
  await ensureGhPreferOrigin(worktreePath);
5891
+ await ensureWorktreeSideboardIgnored(worktreePath);
5746
5892
  return { branchName, worktreePath };
5747
5893
  }
5748
5894
  async function createExistingBranchWorktree(opts) {
5749
5895
  const branchName = opts.branchName.trim();
5750
5896
  if (!branchName) throw new Error("branch name required");
5751
- const worktreePath = (0, import_node_path17.join)(worktreesRoot(opts.repoPath), opts.slug);
5752
- if ((0, import_node_fs14.existsSync)(worktreePath)) {
5897
+ const worktreePath = (0, import_node_path18.join)(worktreesRoot(opts.repoPath), opts.slug);
5898
+ if ((0, import_node_fs15.existsSync)(worktreePath)) {
5753
5899
  throw new Error(`Worktree already exists at ${worktreePath}`);
5754
5900
  }
5755
5901
  await ensureGhPreferOrigin(opts.repoPath);
@@ -5787,6 +5933,7 @@ async function createExistingBranchWorktree(opts) {
5787
5933
  await git(["checkout", "-B", branchName], worktreePath, { reject: false });
5788
5934
  }
5789
5935
  await ensureGhPreferOrigin(worktreePath);
5936
+ await ensureWorktreeSideboardIgnored(worktreePath);
5790
5937
  return { branchName, worktreePath };
5791
5938
  }
5792
5939
  async function removeWorktree(repoPath, worktreePath, opts) {
@@ -5875,7 +6022,9 @@ async function currentBranch(worktreePath) {
5875
6022
  async function commitAll(worktreePath, message) {
5876
6023
  const dirty = await isDirty(worktreePath);
5877
6024
  if (!dirty) return false;
6025
+ await ensureWorktreeSideboardIgnored(worktreePath);
5878
6026
  await git(["add", "-A"], worktreePath);
6027
+ await git(["reset", "-q", "--", ".sideboard"], worktreePath, { reject: false });
5879
6028
  await git(["commit", "-m", message], worktreePath);
5880
6029
  return true;
5881
6030
  }
@@ -5889,14 +6038,14 @@ async function pushBranch(worktreePath, branchName) {
5889
6038
  const token = await resolveGithubAgentToken(getGithubGitAuthMode(), worktreePath);
5890
6039
  if (!slug || !token) {
5891
6040
  throw new Error(
5892
- sshErr || `git push origin ${branchName} failed` + (!token ? " (no SSH agent and gh auth token unavailable \u2014 run: gh auth login)" : "")
6041
+ sshErr || `git push origin ${branchName} failed` + (!token ? " (SSH remote and gh auth token unavailable \u2014 unlock ssh-agent or run: gh auth login)" : "")
5893
6042
  );
5894
6043
  }
6044
+ const remoteUrl = await originRemoteUrl(worktreePath);
5895
6045
  const tryHttps = async (header) => git(["push", "-u", "origin", branchName], worktreePath, {
5896
6046
  reject: false,
5897
- env: { GIT_TERMINAL_PROMPT: "0" },
6047
+ env: githubHttpsRetryEnv(remoteUrl),
5898
6048
  config: {
5899
- "url.https://github.com/.insteadOf": "git@github.com:",
5900
6049
  "http.extraHeader": header
5901
6050
  }
5902
6051
  });
@@ -6026,6 +6175,32 @@ async function createOrUpdatePr(worktreePath, opts) {
6026
6175
  const repoArgs = ghRepoSelectArgs(slug);
6027
6176
  const headRef = ghHeadRef(slug, opts.head);
6028
6177
  const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
6178
+ const bodyText = clampGithubPrBody(opts.body ?? opts.title);
6179
+ const bodyFile = (0, import_node_path18.join)(
6180
+ (0, import_node_os6.tmpdir)(),
6181
+ `sideboard-pr-body-${process.pid}-${(0, import_node_crypto4.randomBytes)(6).toString("hex")}.md`
6182
+ );
6183
+ (0, import_node_fs15.writeFileSync)(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
6184
+ `, "utf8");
6185
+ const bodyArgs = ["--body-file", bodyFile];
6186
+ try {
6187
+ return await createOrUpdatePrWithBodyFile(worktreePath, {
6188
+ ...opts,
6189
+ slug,
6190
+ repoArgs,
6191
+ headRef,
6192
+ branchOnly,
6193
+ bodyArgs
6194
+ });
6195
+ } finally {
6196
+ try {
6197
+ (0, import_node_fs15.unlinkSync)(bodyFile);
6198
+ } catch {
6199
+ }
6200
+ }
6201
+ }
6202
+ async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
6203
+ const { slug, repoArgs, headRef, branchOnly, bodyArgs } = opts;
6029
6204
  const existing = await gh(
6030
6205
  [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
6031
6206
  worktreePath,
@@ -6041,8 +6216,7 @@ async function createOrUpdatePr(worktreePath, opts) {
6041
6216
  branchOnly,
6042
6217
  "--title",
6043
6218
  opts.title,
6044
- "--body",
6045
- opts.body ?? opts.title
6219
+ ...bodyArgs
6046
6220
  ],
6047
6221
  worktreePath,
6048
6222
  { reject: false }
@@ -6057,8 +6231,7 @@ async function createOrUpdatePr(worktreePath, opts) {
6057
6231
  "--web",
6058
6232
  "--title",
6059
6233
  opts.title,
6060
- "--body",
6061
- opts.body ?? opts.title,
6234
+ ...bodyArgs,
6062
6235
  "--base",
6063
6236
  opts.base,
6064
6237
  "--head",
@@ -6079,8 +6252,7 @@ async function createOrUpdatePr(worktreePath, opts) {
6079
6252
  "create",
6080
6253
  "--title",
6081
6254
  opts.title,
6082
- "--body",
6083
- opts.body ?? opts.title,
6255
+ ...bodyArgs,
6084
6256
  "--base",
6085
6257
  opts.base,
6086
6258
  "--head",
@@ -6104,10 +6276,10 @@ function sameRepoPath(a, b) {
6104
6276
  return normalizeWorktreePath(a) === normalizeWorktreePath(b);
6105
6277
  }
6106
6278
  function listLocalThreadBranchSlugs(repoPath) {
6107
- const refsDir = (0, import_node_path17.join)(repoPath, ".git", "refs", "heads", "thread");
6108
- if (!(0, import_node_fs14.existsSync)(refsDir)) return [];
6279
+ const refsDir = (0, import_node_path18.join)(repoPath, ".git", "refs", "heads", "thread");
6280
+ if (!(0, import_node_fs15.existsSync)(refsDir)) return [];
6109
6281
  try {
6110
- return (0, import_node_fs14.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
6282
+ return (0, import_node_fs15.readdirSync)(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
6111
6283
  } catch {
6112
6284
  return [];
6113
6285
  }
@@ -6115,8 +6287,8 @@ function listLocalThreadBranchSlugs(repoPath) {
6115
6287
  function collectTakenTeamSlugs(repoPath) {
6116
6288
  const taken = /* @__PURE__ */ new Set();
6117
6289
  const root = worktreesRoot(repoPath);
6118
- if ((0, import_node_fs14.existsSync)(root)) {
6119
- for (const entry of (0, import_node_fs14.readdirSync)(root, { withFileTypes: true })) {
6290
+ if ((0, import_node_fs15.existsSync)(root)) {
6291
+ for (const entry of (0, import_node_fs15.readdirSync)(root, { withFileTypes: true })) {
6120
6292
  if (entry.isDirectory() && entry.name !== ".DS_Store") {
6121
6293
  taken.add(normalizeTakenSlug(entry.name));
6122
6294
  }
@@ -6137,23 +6309,26 @@ function allocateTeamSlug(repoPath) {
6137
6309
  const taken = collectTakenTeamSlugs(repoPath);
6138
6310
  for (let attempt = 0; attempt < 32; attempt++) {
6139
6311
  const team = allocateTeamName(taken);
6140
- const path = (0, import_node_path17.join)(worktreesRoot(repoPath), team.slug);
6141
- if (!(0, import_node_fs14.existsSync)(path)) return team;
6312
+ const path = (0, import_node_path18.join)(worktreesRoot(repoPath), team.slug);
6313
+ if (!(0, import_node_fs15.existsSync)(path)) return team;
6142
6314
  taken.add(team.slug);
6143
6315
  }
6144
6316
  throw new Error("No available soccer team worktree directories left");
6145
6317
  }
6146
- var import_node_fs14, import_node_path17;
6318
+ var import_node_crypto4, import_node_fs15, import_node_os6, import_node_path18;
6147
6319
  var init_worktree = __esm({
6148
6320
  "src/git/worktree.ts"() {
6149
6321
  "use strict";
6150
- import_node_fs14 = require("fs");
6151
- import_node_path17 = require("path");
6322
+ import_node_crypto4 = require("crypto");
6323
+ import_node_fs15 = require("fs");
6324
+ import_node_os6 = require("os");
6325
+ import_node_path18 = require("path");
6152
6326
  init_paths();
6153
6327
  init_thread_store();
6154
6328
  init_teams();
6155
6329
  init_worktree_labels();
6156
6330
  init_gh_errors();
6331
+ init_worktree_exclude();
6157
6332
  init_git_auth_mode();
6158
6333
  init_run();
6159
6334
  init_repo_git_lock();
@@ -6197,7 +6372,7 @@ var init_cloud_connect_constants = __esm({
6197
6372
 
6198
6373
  // src/composer/stage-files.ts
6199
6374
  function fileExtension(filePath) {
6200
- const base = (0, import_node_path18.basename)(filePath).toLowerCase();
6375
+ const base = (0, import_node_path19.basename)(filePath).toLowerCase();
6201
6376
  return base.includes(".") ? base.split(".").pop() || "" : "";
6202
6377
  }
6203
6378
  function isImageFilePath(filePath) {
@@ -6207,24 +6382,24 @@ function imageMimeType(filePath) {
6207
6382
  return IMAGE_MIME_BY_EXT[fileExtension(filePath)] || "image/png";
6208
6383
  }
6209
6384
  function ensureAttachmentsDir(worktreePath) {
6210
- const dir = (0, import_node_path18.join)(worktreePath, ATTACHMENTS_DIR);
6211
- (0, import_node_fs15.mkdirSync)(dir, { recursive: true });
6212
- const gi = (0, import_node_path18.join)(dir, ".gitignore");
6213
- if (!(0, import_node_fs15.existsSync)(gi)) {
6214
- (0, import_node_fs15.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
6385
+ const dir = (0, import_node_path19.join)(worktreePath, ATTACHMENTS_DIR);
6386
+ (0, import_node_fs16.mkdirSync)(dir, { recursive: true });
6387
+ const gi = (0, import_node_path19.join)(dir, ".gitignore");
6388
+ if (!(0, import_node_fs16.existsSync)(gi)) {
6389
+ (0, import_node_fs16.writeFileSync)(gi, attachmentsGitignoreBody(), "utf8");
6215
6390
  }
6216
6391
  return dir;
6217
6392
  }
6218
6393
  function uniqueAttachmentName(dir, originalName) {
6219
6394
  const safe = originalName.replace(/[/\\]/g, "_") || "file";
6220
- if (!(0, import_node_fs15.existsSync)((0, import_node_path18.join)(dir, safe))) return safe;
6221
- const ext = (0, import_node_path18.extname)(safe);
6395
+ if (!(0, import_node_fs16.existsSync)((0, import_node_path19.join)(dir, safe))) return safe;
6396
+ const ext = (0, import_node_path19.extname)(safe);
6222
6397
  const stem = ext ? safe.slice(0, -ext.length) : safe;
6223
6398
  for (let i = 1; i < 1e4; i++) {
6224
6399
  const candidate = `${stem}-${i}${ext}`;
6225
- if (!(0, import_node_fs15.existsSync)((0, import_node_path18.join)(dir, candidate))) return candidate;
6400
+ if (!(0, import_node_fs16.existsSync)((0, import_node_path19.join)(dir, candidate))) return candidate;
6226
6401
  }
6227
- return `${stem}-${(0, import_node_crypto4.randomUUID)()}${ext}`;
6402
+ return `${stem}-${(0, import_node_crypto5.randomUUID)()}${ext}`;
6228
6403
  }
6229
6404
  function previewDataUrlFromBuf(filePath, buf) {
6230
6405
  if (!isImageFilePath(filePath)) return void 0;
@@ -6236,7 +6411,7 @@ function attachmentFromBuffer(name, buf, opts) {
6236
6411
  if (isImageFilePath(name)) {
6237
6412
  const pathHint = opts.path ? `\`${opts.path}\`` : opts.sourceLabel || name;
6238
6413
  return {
6239
- id: (0, import_node_crypto4.randomUUID)(),
6414
+ id: (0, import_node_crypto5.randomUUID)(),
6240
6415
  name,
6241
6416
  kind: "file",
6242
6417
  path: opts.path,
@@ -6249,7 +6424,7 @@ function attachmentFromBuffer(name, buf, opts) {
6249
6424
  }
6250
6425
  if (buf.length > MAX_INLINE_BYTES) {
6251
6426
  return {
6252
- id: (0, import_node_crypto4.randomUUID)(),
6427
+ id: (0, import_node_crypto5.randomUUID)(),
6253
6428
  name,
6254
6429
  kind: "file",
6255
6430
  path: opts.path,
@@ -6258,7 +6433,7 @@ function attachmentFromBuffer(name, buf, opts) {
6258
6433
  }
6259
6434
  if (buf.includes(0)) {
6260
6435
  return {
6261
- id: (0, import_node_crypto4.randomUUID)(),
6436
+ id: (0, import_node_crypto5.randomUUID)(),
6262
6437
  name,
6263
6438
  kind: "file",
6264
6439
  path: opts.path,
@@ -6266,7 +6441,7 @@ function attachmentFromBuffer(name, buf, opts) {
6266
6441
  };
6267
6442
  }
6268
6443
  return {
6269
- id: (0, import_node_crypto4.randomUUID)(),
6444
+ id: (0, import_node_crypto5.randomUUID)(),
6270
6445
  name,
6271
6446
  kind: "file",
6272
6447
  path: opts.path,
@@ -6278,19 +6453,19 @@ function stageAbsolutePathsAsAttachments(worktreePath, absolutePaths) {
6278
6453
  const dir = ensureAttachmentsDir(worktreePath);
6279
6454
  const out = [];
6280
6455
  for (const abs of absolutePaths) {
6281
- const originalName = (0, import_node_path18.basename)(abs);
6456
+ const originalName = (0, import_node_path19.basename)(abs);
6282
6457
  try {
6283
- const st = (0, import_node_fs15.statSync)(abs);
6458
+ const st = (0, import_node_fs16.statSync)(abs);
6284
6459
  if (!st.isFile()) continue;
6285
6460
  const name = uniqueAttachmentName(dir, originalName);
6286
- const destAbs = (0, import_node_path18.join)(dir, name);
6287
- (0, import_node_fs15.copyFileSync)(abs, destAbs);
6461
+ const destAbs = (0, import_node_path19.join)(dir, name);
6462
+ (0, import_node_fs16.copyFileSync)(abs, destAbs);
6288
6463
  const rel = `${ATTACHMENTS_DIR}/${name}`;
6289
- const buf = (0, import_node_fs15.readFileSync)(destAbs);
6464
+ const buf = (0, import_node_fs16.readFileSync)(destAbs);
6290
6465
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
6291
6466
  } catch (err) {
6292
6467
  out.push({
6293
- id: (0, import_node_crypto4.randomUUID)(),
6468
+ id: (0, import_node_crypto5.randomUUID)(),
6294
6469
  name: originalName,
6295
6470
  kind: "file",
6296
6471
  content: `(could not attach ${abs}: ${err instanceof Error ? err.message : String(err)})`
@@ -6308,13 +6483,13 @@ function stageBuffersAsAttachments(worktreePath, buffers2) {
6308
6483
  try {
6309
6484
  const buf = Buffer.from(item.dataBase64, "base64");
6310
6485
  const name = uniqueAttachmentName(dir, originalName);
6311
- const destAbs = (0, import_node_path18.join)(dir, name);
6312
- (0, import_node_fs15.writeFileSync)(destAbs, buf);
6486
+ const destAbs = (0, import_node_path19.join)(dir, name);
6487
+ (0, import_node_fs16.writeFileSync)(destAbs, buf);
6313
6488
  const rel = `${ATTACHMENTS_DIR}/${name}`;
6314
6489
  out.push(attachmentFromBuffer(name, buf, { path: rel }));
6315
6490
  } catch (err) {
6316
6491
  out.push({
6317
- id: (0, import_node_crypto4.randomUUID)(),
6492
+ id: (0, import_node_crypto5.randomUUID)(),
6318
6493
  name: originalName,
6319
6494
  kind: "file",
6320
6495
  content: `(could not attach ${originalName}: ${err instanceof Error ? err.message : String(err)})`
@@ -6369,23 +6544,23 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
6369
6544
  for (const rel of relativePaths) {
6370
6545
  if (!rel || rel.includes("..") || rel.startsWith("/")) {
6371
6546
  out.push({
6372
- id: (0, import_node_crypto4.randomUUID)(),
6373
- name: (0, import_node_path18.basename)(rel) || "file",
6547
+ id: (0, import_node_crypto5.randomUUID)(),
6548
+ name: (0, import_node_path19.basename)(rel) || "file",
6374
6549
  kind: "file",
6375
6550
  content: `(invalid path: ${rel})`
6376
6551
  });
6377
6552
  continue;
6378
6553
  }
6379
- const name = (0, import_node_path18.basename)(rel);
6554
+ const name = (0, import_node_path19.basename)(rel);
6380
6555
  try {
6381
- const abs = (0, import_node_path18.join)(worktreePath, rel);
6382
- const st = (0, import_node_fs15.statSync)(abs);
6556
+ const abs = (0, import_node_path19.join)(worktreePath, rel);
6557
+ const st = (0, import_node_fs16.statSync)(abs);
6383
6558
  if (!st.isFile()) continue;
6384
- const buf = (0, import_node_fs15.readFileSync)(abs);
6559
+ const buf = (0, import_node_fs16.readFileSync)(abs);
6385
6560
  out.push(attachmentFromBuffer(name, buf, { path: rel, sourceLabel: abs }));
6386
6561
  } catch (err) {
6387
6562
  out.push({
6388
- id: (0, import_node_crypto4.randomUUID)(),
6563
+ id: (0, import_node_crypto5.randomUUID)(),
6389
6564
  name,
6390
6565
  kind: "file",
6391
6566
  content: `(could not read ${rel}: ${err instanceof Error ? err.message : String(err)})`
@@ -6394,13 +6569,13 @@ function attachmentsFromWorktreePaths(worktreePath, relativePaths) {
6394
6569
  }
6395
6570
  return out;
6396
6571
  }
6397
- var import_node_fs15, import_node_path18, import_node_crypto4, IMAGE_EXTENSIONS, IMAGE_MIME_BY_EXT, MAX_INLINE_BYTES, MAX_PREVIEW_BYTES, IMAGE_HINT_RE, PLACEHOLDER_CONTENT_RE;
6572
+ var import_node_fs16, import_node_path19, import_node_crypto5, IMAGE_EXTENSIONS, IMAGE_MIME_BY_EXT, MAX_INLINE_BYTES, MAX_PREVIEW_BYTES, IMAGE_HINT_RE, PLACEHOLDER_CONTENT_RE;
6398
6573
  var init_stage_files = __esm({
6399
6574
  "src/composer/stage-files.ts"() {
6400
6575
  "use strict";
6401
- import_node_fs15 = require("fs");
6402
- import_node_path18 = require("path");
6403
- import_node_crypto4 = require("crypto");
6576
+ import_node_fs16 = require("fs");
6577
+ import_node_path19 = require("path");
6578
+ import_node_crypto5 = require("crypto");
6404
6579
  init_workspace_scratch();
6405
6580
  IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
6406
6581
  "png",
@@ -6517,7 +6692,7 @@ function coordinatorTurnReminder(opts) {
6517
6692
  function ensureGlobalCoordinatorCwd(opts) {
6518
6693
  const dir = globalAgentCwd();
6519
6694
  try {
6520
- (0, import_node_fs16.mkdirSync)(dir, { recursive: true });
6695
+ (0, import_node_fs17.mkdirSync)(dir, { recursive: true });
6521
6696
  } catch {
6522
6697
  return dir;
6523
6698
  }
@@ -6525,7 +6700,7 @@ function ensureGlobalCoordinatorCwd(opts) {
6525
6700
  let orchId = opts?.orchestratorThreadId?.trim() || "";
6526
6701
  if (!orchId) {
6527
6702
  try {
6528
- const existing = (0, import_node_fs16.readFileSync)((0, import_node_path19.join)(dir, "AGENTS.md"), "utf8");
6703
+ const existing = (0, import_node_fs17.readFileSync)((0, import_node_path20.join)(dir, "AGENTS.md"), "utf8");
6529
6704
  const m = existing.match(
6530
6705
  /YOUR orchestration thread id is `([0-9a-f-]{36})`/i
6531
6706
  );
@@ -6568,9 +6743,9 @@ function ensureGlobalCoordinatorCwd(opts) {
6568
6743
  "Always ask worktree agents to commit, push, and open draft PRs (`ask_git` / `send_to_thread`). Tell them to merge only when the user explicitly asked. The worktree agent runs git/gh; never merge from this orchestration cwd."
6569
6744
  ].join("\n");
6570
6745
  try {
6571
- (0, import_node_fs16.writeFileSync)((0, import_node_path19.join)(dir, "CLAUDE.md"), `${body}
6746
+ (0, import_node_fs17.writeFileSync)((0, import_node_path20.join)(dir, "CLAUDE.md"), `${body}
6572
6747
  `, "utf8");
6573
- (0, import_node_fs16.writeFileSync)((0, import_node_path19.join)(dir, "AGENTS.md"), `${body}
6748
+ (0, import_node_fs17.writeFileSync)((0, import_node_path20.join)(dir, "AGENTS.md"), `${body}
6574
6749
  `, "utf8");
6575
6750
  } catch {
6576
6751
  }
@@ -6602,12 +6777,12 @@ function coordinatorSystemPrompt(opts) {
6602
6777
  formatWorkspaceInventory(opts.workspaces)
6603
6778
  ].join("\n");
6604
6779
  }
6605
- var import_node_fs16, import_node_path19, COORDINATOR_TOOL_PLAYBOOK, SLACK_REPLY_FORMATTING;
6780
+ var import_node_fs17, import_node_path20, COORDINATOR_TOOL_PLAYBOOK, SLACK_REPLY_FORMATTING;
6606
6781
  var init_coordinator_prompt = __esm({
6607
6782
  "src/orchestrator/coordinator-prompt.ts"() {
6608
6783
  "use strict";
6609
- import_node_fs16 = require("fs");
6610
- import_node_path19 = require("path");
6784
+ import_node_fs17 = require("fs");
6785
+ import_node_path20 = require("path");
6611
6786
  init_worktree();
6612
6787
  init_app_settings();
6613
6788
  init_paths();
@@ -6617,8 +6792,8 @@ var init_coordinator_prompt = __esm({
6617
6792
  "Discover:",
6618
6793
  "- list_workspaces \u2014 registered repos (path + github slug when known)",
6619
6794
  "- list_board \u2014 Home Kanban of worktrees (New / Draft / Review / Merged; one card per checkout). Path to merge: no PR \u2192 draft PR \u2192 open PR \u2192 merged. Archive removes the card to Settings \u2192 History. Queued/running are activity on the card, not columns. Orchestration chats are not on the board. Filters: query, repoPath, kind, column, limit.",
6620
- "- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear, AbleTime MCP, or GitHub Issues)",
6621
- "- linear_list_teams / linear_get_issue / linear_create_issue / linear_update_issue / linear_comment \u2014 Linear Account connection; call linear_list_teams for team key and workflow states; pass ENG-123 or uuid. linear_get_issue returns comments, relations, parent/children, and the rest of the issue metadata. If mutations fail with a scope error, Disconnect and Connect Linear in Account settings.",
6795
+ "- list_branches / list_prs / list_issues \u2014 pass repoPath from list_workspaces (issues: Linear, AbleTime MCP, or GitHub Issues). list_issues accepts query and assignee (me / unassigned / all / user id) so Linear is not limited to tickets assigned to you.",
6796
+ "- linear_list_teams / linear_search_issues / linear_get_issue / linear_create_issue / linear_update_issue / linear_comment \u2014 Linear Account connection; call linear_list_teams for team key and workflow states; pass ENG-123 or uuid. linear_search_issues finds unassigned and other people's open issues (assignee=unassigned|all|me|user). linear_get_issue returns comments, relations, parent/children, and the rest of the issue metadata. If mutations fail with a scope error, Disconnect and Connect Linear in Account settings.",
6622
6797
  "- abletime_orientation / abletime_list_projects / abletime_list_tasks / abletime_search_tasks / abletime_get_task / abletime_create_task / abletime_ensure_task \u2014 AbleTime Account personal access token (hosted MCP). Call orientation first. If work has no task yet, abletime_ensure_task creates one to track against (or create_thread from the default branch does that automatically when AbleTime is the preferred issue source).",
6623
6798
  "- list_teams / slack_list_channels / slack_list_users / slack_search / slack_read / slack_post / slack_replies \u2014 Slack workspaces from Settings \u2192 Remote; pass team_id from list_teams",
6624
6799
  "- Optional connectors (Vercel, Supabase, PostHog, Sentry) in Settings \u2192 Connectors inject tokens into worktree agent env when connected. Prefer official CLIs (`vercel`, `supabase`, `sentry-cli`) with those env vars. PostHog has no first-class CLI \u2014 use the HTTP API (`POSTHOG_PERSONAL_API_KEY`). If a CLI is missing, the user can Install CLI on that row (not auto-installed on Connect). Do not add vendor MCPs or ask the user to paste tokens again. Git (`gh`) stays Settings \u2192 Git; issue tracking stays Settings \u2192 Issues; Slack stays Settings \u2192 Remote (Sideboard MCP).",
@@ -6637,7 +6812,7 @@ var init_coordinator_prompt = __esm({
6637
6812
  "- fork_worktree \u2014 fork a worktree chat into a NEW git worktree + chat (transcript attached); optional agent; leave model unset (Auto) unless you have a reason. Not for orchestration chats.",
6638
6813
  "- fork_chat \u2014 fork a worktree chat (same worktree tab) OR a Global orchestration chat (new orchestration tab); optional agent; leave model unset (Auto) unless you have a reason. Remote coordinators: use this to continue another orchestration chat on a different agent after session limits.",
6639
6814
  "- send_to_thread \u2014 queue a prompt (start/continue a chat turn). force_stop: true only to replace a wrong in-flight request \u2014 never to check in, resume after a halt notice, or because wait_for_turn returned stillRunning (that kills the child mid-thought)",
6640
- "- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply (includes last-turn usage / costUsd when the child agent reported it). wait_for_turn returns within ~45s even if the child is still working (MCP clients kill longer tool calls). If stillRunning is true, progress is a live snapshot of tools/thinking \u2014 call wait_for_turn again. status=queued with no lastActivityAt means the child has not started yet (concurrency cap) \u2014 keep waiting; do not force_stop or send a check-in. Do not send \u201Care you stuck?\u201D or assume a hang while lastActivityAt is recent. On status error, lastError (and text) is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success. On status stopped or broken (or incomplete=true), the child was interrupted or died \u2014 resume with send_to_thread or tell the user; never treat stopped as a finished turn. Sideboard also injects a notice into this chat when a child stops unexpectedly.",
6815
+ "- wait_for_turn / get_turn_result \u2014 wait for and read the agent reply (includes last-turn usage / costUsd when the child agent reported it). wait_for_turn returns within ~45s even if the child is still working (MCP clients kill longer tool calls). stillRunning is the source of truth \u2014 if it is false, the child is not working; do not tell the user it is running or \u201Cwaiting for a gate,\u201D even if status still says running/queued. If stillRunning is true, progress is a live snapshot of tools/thinking \u2014 call wait_for_turn again. status=queued with no lastActivityAt means the child has not started yet (concurrency cap) \u2014 keep waiting; do not force_stop or send a check-in. Do not send \u201Care you stuck?\u201D or assume a hang while lastActivityAt is recent. If stillRunning stays true across many waits with the same lastActivityAt, the child is stuck \u2014 tell the user; do not invent a gate. On status error, lastError (and text) is the failure \u2014 switch agent, tell the user, or retry; do not treat empty text as success. On status stopped or broken (or incomplete=true), the child was interrupted or died \u2014 resume with send_to_thread or tell the user; never treat stopped as a finished turn. Sideboard also injects a notice into this chat when a child stops unexpectedly.",
6641
6816
  "- stop_thread \u2014 force-stop: kill in-flight turn AND clear queued prompts (do not leave stale queue after an interrupt)",
6642
6817
  "- archive_thread / restore_thread \u2014 archive (tears down worktree when last tab) or restore",
6643
6818
  "Setup / run:",
@@ -6646,7 +6821,7 @@ var init_coordinator_prompt = __esm({
6646
6821
  "Inspect / review / PRs:",
6647
6822
  "- get_diff \u2014 compact diff summary",
6648
6823
  '- request_review \u2014 open a Review chat tab on a worktree thread (attaches .claude/skills/review/SKILL.md when present, else seeds that skill / local guidelines; sends "Review changes in this workspace."); then wait_for_turn (loop while stillRunning) / get_turn_result on the returned id',
6649
- "- ask_git \u2014 commit & push, open a draft PR, resolve conflicts, or merge. When the worktree is clean, Sideboard pushes / opens the PR itself. When dirty, it queues the worktree agent \u2014 then wait_for_turn (loop while stillRunning). Prefer this over paraphrasing.",
6824
+ "- ask_git \u2014 commit & push, open a draft PR, resolve conflicts, or merge. When the worktree is clean, Sideboard pushes / opens the PR itself. When dirty, it queues the worktree agent \u2014 then wait_for_turn (loop while stillRunning). Prefer this over paraphrasing. If ask_git / get_thread lastError says the GraphQL/PR body is too long, the branch is already pushed \u2014 send_to_thread so the worktree agent runs `gh pr create --body-file` with a short description. Do not invent SSH or auth failures from that error.",
6650
6825
  '- Merge (`ask_git` action=merge / send_to_thread "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.',
6651
6826
  '- Or send_to_thread with those exact phrases: "Commit and push.", "Commit, push, and open a draft PR.", "Merge the remote branch (main) into your branch and resolve conflicts. Then, commit and push your changes.", "Merge PR." (draft PRs: `gh pr create --draft -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.',
6652
6827
  "Process guides:",
@@ -6917,32 +7092,32 @@ var init_global_workspace = __esm({
6917
7092
  function brightsyConfigPath() {
6918
7093
  const override = process.env.BRIGHTSY_CONFIG?.trim();
6919
7094
  if (override) return override;
6920
- return (0, import_node_path20.join)((0, import_node_os6.homedir)(), ".brightsy", "config.json");
7095
+ return (0, import_node_path21.join)((0, import_node_os7.homedir)(), ".brightsy", "config.json");
6921
7096
  }
6922
7097
  function loadBrightsyConfig() {
6923
7098
  const path = brightsyConfigPath();
6924
- if (!(0, import_node_fs17.existsSync)(path)) {
7099
+ if (!(0, import_node_fs18.existsSync)(path)) {
6925
7100
  throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
6926
7101
  }
6927
- const raw = JSON.parse((0, import_node_fs17.readFileSync)(path, "utf8"));
7102
+ const raw = JSON.parse((0, import_node_fs18.readFileSync)(path, "utf8"));
6928
7103
  if (!raw.access_token || !raw.account_id) {
6929
7104
  throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
6930
7105
  }
6931
7106
  return raw;
6932
7107
  }
6933
7108
  function saveBrightsyConfig(cfg) {
6934
- (0, import_node_fs17.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
7109
+ (0, import_node_fs18.writeFileSync)(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
6935
7110
  `, {
6936
7111
  mode: 384
6937
7112
  });
6938
7113
  }
6939
- var import_node_fs17, import_node_os6, import_node_path20;
7114
+ var import_node_fs18, import_node_os7, import_node_path21;
6940
7115
  var init_config = __esm({
6941
7116
  "src/brightsy/config.ts"() {
6942
7117
  "use strict";
6943
- import_node_fs17 = require("fs");
6944
- import_node_os6 = require("os");
6945
- import_node_path20 = require("path");
7118
+ import_node_fs18 = require("fs");
7119
+ import_node_os7 = require("os");
7120
+ import_node_path21 = require("path");
6946
7121
  }
6947
7122
  });
6948
7123
 
@@ -7059,22 +7234,22 @@ var init_oauth = __esm({
7059
7234
 
7060
7235
  // src/brightsy/connected-teams.ts
7061
7236
  function storePath4() {
7062
- return (0, import_node_path21.join)(appDataDir(), "brightsy-teams.json");
7237
+ return (0, import_node_path22.join)(appDataDir(), "brightsy-teams.json");
7063
7238
  }
7064
7239
  function readStore4() {
7065
7240
  const path = storePath4();
7066
- if (!(0, import_node_fs18.existsSync)(path)) return [];
7241
+ if (!(0, import_node_fs19.existsSync)(path)) return [];
7067
7242
  try {
7068
- const parsed = JSON.parse((0, import_node_fs18.readFileSync)(path, "utf8"));
7243
+ const parsed = JSON.parse((0, import_node_fs19.readFileSync)(path, "utf8"));
7069
7244
  return Array.isArray(parsed.teams) ? parsed.teams : [];
7070
7245
  } catch {
7071
7246
  return [];
7072
7247
  }
7073
7248
  }
7074
7249
  function writeStore2(teams) {
7075
- (0, import_node_fs18.mkdirSync)(appDataDir(), { recursive: true });
7250
+ (0, import_node_fs19.mkdirSync)(appDataDir(), { recursive: true });
7076
7251
  const path = storePath4();
7077
- (0, import_node_fs18.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
7252
+ (0, import_node_fs19.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
7078
7253
  `, {
7079
7254
  mode: 384
7080
7255
  });
@@ -7180,12 +7355,12 @@ function brightsyMcpServerName(slug) {
7180
7355
  const cleaned = slug.replace(/[^A-Za-z0-9_-]/g, "_").replace(/^_+|_+$/g, "");
7181
7356
  return `brightsy_${cleaned || "team"}`;
7182
7357
  }
7183
- var import_node_fs18, import_node_path21;
7358
+ var import_node_fs19, import_node_path22;
7184
7359
  var init_connected_teams = __esm({
7185
7360
  "src/brightsy/connected-teams.ts"() {
7186
7361
  "use strict";
7187
- import_node_fs18 = require("fs");
7188
- import_node_path21 = require("path");
7362
+ import_node_fs19 = require("fs");
7363
+ import_node_path22 = require("path");
7189
7364
  init_paths();
7190
7365
  init_accounts();
7191
7366
  init_config();
@@ -7557,11 +7732,11 @@ async function syncCliForTarget(accountId) {
7557
7732
  }
7558
7733
  applyConnectedTeamToCli(team);
7559
7734
  }
7560
- var import_node_fs19, brightsyAdapter;
7735
+ var import_node_fs20, brightsyAdapter;
7561
7736
  var init_brightsy = __esm({
7562
7737
  "src/agents/brightsy.ts"() {
7563
7738
  "use strict";
7564
- import_node_fs19 = require("fs");
7739
+ import_node_fs20 = require("fs");
7565
7740
  init_run();
7566
7741
  init_connected_teams();
7567
7742
  init_config();
@@ -7576,7 +7751,7 @@ var init_brightsy = __esm({
7576
7751
  async detect() {
7577
7752
  const brightsy = resolveAgentExecutable("brightsy");
7578
7753
  if (brightsy !== "brightsy") {
7579
- if (!(0, import_node_fs19.existsSync)(brightsy)) {
7754
+ if (!(0, import_node_fs20.existsSync)(brightsy)) {
7580
7755
  return {
7581
7756
  agent: "brightsy",
7582
7757
  installed: false,
@@ -7807,6 +7982,13 @@ function isSubagentToolName(name) {
7807
7982
  if (/^(task|agent|spawn_agent)$/i.test(n)) return true;
7808
7983
  return /connectedAgentRequest/i.test(n);
7809
7984
  }
7985
+ function isInternalAgentStatusText(text5) {
7986
+ const t = text5.trim();
7987
+ if (!t) return false;
7988
+ if (/waiting for gate to pass/i.test(t)) return true;
7989
+ if (/^agent is running\b/i.test(t) && t.length < 160) return true;
7990
+ return false;
7991
+ }
7810
7992
  function isPollWrapperToolName(name) {
7811
7993
  const n = (name ?? "").replace(/[_-]/g, "");
7812
7994
  return /^(taskoutput|taskstop|sleep)$/i.test(n);
@@ -7814,7 +7996,10 @@ function isPollWrapperToolName(name) {
7814
7996
  function lastTextPart(parts, type) {
7815
7997
  for (let i = parts.length - 1; i >= 0; i--) {
7816
7998
  const p = parts[i];
7817
- if (p.type === type && p.text.trim()) return p.text.trim();
7999
+ if (p.type !== type) continue;
8000
+ const text5 = p.text.trim();
8001
+ if (!text5 || isInternalAgentStatusText(text5)) continue;
8002
+ return text5;
7818
8003
  }
7819
8004
  return "";
7820
8005
  }
@@ -8151,43 +8336,43 @@ function electronResourcesPath() {
8151
8336
  function packagedCursorRuntimeDir() {
8152
8337
  const resources = electronResourcesPath();
8153
8338
  if (!resources) return null;
8154
- const dir = (0, import_node_path22.join)(resources, "cursor-runtime");
8155
- if (!(0, import_node_fs20.existsSync)((0, import_node_path22.join)(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
8339
+ const dir = (0, import_node_path23.join)(resources, "cursor-runtime");
8340
+ if (!(0, import_node_fs21.existsSync)((0, import_node_path23.join)(dir, "core-dist", "agents", "cursor-runner.js"))) return null;
8156
8341
  return dir;
8157
8342
  }
8158
8343
  function packagedCursorRunnerPath() {
8159
8344
  const dir = packagedCursorRuntimeDir();
8160
- return dir ? (0, import_node_path22.join)(dir, "core-dist", "agents", "cursor-runner.js") : null;
8345
+ return dir ? (0, import_node_path23.join)(dir, "core-dist", "agents", "cursor-runner.js") : null;
8161
8346
  }
8162
8347
  function packagedMcpDir() {
8163
8348
  const resources = electronResourcesPath();
8164
8349
  if (!resources) return null;
8165
- const dir = (0, import_node_path22.join)(resources, "sideboard-mcp");
8166
- if (!(0, import_node_fs20.existsSync)((0, import_node_path22.join)(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
8350
+ const dir = (0, import_node_path23.join)(resources, "sideboard-mcp");
8351
+ if (!(0, import_node_fs21.existsSync)((0, import_node_path23.join)(dir, "core-dist", "mcp", "run-stdio.js"))) return null;
8167
8352
  return dir;
8168
8353
  }
8169
8354
  function packagedMcpStdioPath() {
8170
8355
  const dir = packagedMcpDir();
8171
- return dir ? (0, import_node_path22.join)(dir, "core-dist", "mcp", "run-stdio.js") : null;
8356
+ return dir ? (0, import_node_path23.join)(dir, "core-dist", "mcp", "run-stdio.js") : null;
8172
8357
  }
8173
8358
  function packagedBundledNodePath() {
8174
8359
  const resources = electronResourcesPath();
8175
8360
  if (!resources) return null;
8176
- const bin = (0, import_node_path22.join)(resources, "node", "bin", "node");
8177
- if (!(0, import_node_fs20.existsSync)(bin)) return null;
8361
+ const bin = (0, import_node_path23.join)(resources, "node", "bin", "node");
8362
+ if (!(0, import_node_fs21.existsSync)(bin)) return null;
8178
8363
  return bin;
8179
8364
  }
8180
8365
  function packagedCursorRipgrepCandidate(platformPkg, binName) {
8181
8366
  const dir = packagedCursorRuntimeDir();
8182
8367
  if (!dir) return null;
8183
- return (0, import_node_path22.join)(dir, "node_modules", platformPkg, "bin", binName);
8368
+ return (0, import_node_path23.join)(dir, "node_modules", platformPkg, "bin", binName);
8184
8369
  }
8185
- var import_node_fs20, import_node_path22;
8370
+ var import_node_fs21, import_node_path23;
8186
8371
  var init_packaged_runtime = __esm({
8187
8372
  "src/agents/packaged-runtime.ts"() {
8188
8373
  "use strict";
8189
- import_node_fs20 = require("fs");
8190
- import_node_path22 = require("path");
8374
+ import_node_fs21 = require("fs");
8375
+ import_node_path23 = require("path");
8191
8376
  }
8192
8377
  });
8193
8378
 
@@ -8221,7 +8406,7 @@ function unpackedAsarPath(filePath) {
8221
8406
  if (!isAsarPath(filePath)) return null;
8222
8407
  const unpacked = filePath.replace(/\.asar(?=[/\\])/, ".asar.unpacked");
8223
8408
  if (unpacked === filePath) return null;
8224
- return (0, import_node_fs21.existsSync)(unpacked) ? unpacked : null;
8409
+ return (0, import_node_fs22.existsSync)(unpacked) ? unpacked : null;
8225
8410
  }
8226
8411
  function nodeReadableScriptPath(scriptPath) {
8227
8412
  return unpackedAsarPath(scriptPath) ?? scriptPath;
@@ -8261,37 +8446,37 @@ function pickPreferredNode(candidates) {
8261
8446
  return best;
8262
8447
  }
8263
8448
  function versionDirNodeBins(root, toBin) {
8264
- if (!(0, import_node_fs21.existsSync)(root)) return [];
8449
+ if (!(0, import_node_fs22.existsSync)(root)) return [];
8265
8450
  try {
8266
- return (0, import_node_fs21.readdirSync)(root).map(toBin);
8451
+ return (0, import_node_fs22.readdirSync)(root).map(toBin);
8267
8452
  } catch {
8268
8453
  return [];
8269
8454
  }
8270
8455
  }
8271
- function defaultNodeBinCandidates(home = (0, import_node_os7.homedir)()) {
8456
+ function defaultNodeBinCandidates(home = (0, import_node_os8.homedir)()) {
8272
8457
  const kegs = ["/opt/homebrew", "/usr/local"].flatMap(
8273
- (prefix) => PREFERRED_LTS_MAJORS.map((major) => (0, import_node_path23.join)(prefix, "opt", `node@${major}`, "bin", "node"))
8458
+ (prefix) => PREFERRED_LTS_MAJORS.map((major) => (0, import_node_path24.join)(prefix, "opt", `node@${major}`, "bin", "node"))
8274
8459
  );
8275
8460
  return [
8276
8461
  ...kegs,
8277
8462
  "/opt/homebrew/bin/node",
8278
8463
  "/usr/local/bin/node",
8279
- (0, import_node_path23.join)(home, ".local/share/fnm/aliases/default/bin/node"),
8280
- (0, import_node_path23.join)(home, ".nvm/current/bin/node"),
8281
- (0, import_node_path23.join)(home, ".volta/bin/node"),
8282
- (0, import_node_path23.join)(home, ".asdf/shims/node"),
8283
- (0, import_node_path23.join)(home, ".local/share/mise/shims/node"),
8464
+ (0, import_node_path24.join)(home, ".local/share/fnm/aliases/default/bin/node"),
8465
+ (0, import_node_path24.join)(home, ".nvm/current/bin/node"),
8466
+ (0, import_node_path24.join)(home, ".volta/bin/node"),
8467
+ (0, import_node_path24.join)(home, ".asdf/shims/node"),
8468
+ (0, import_node_path24.join)(home, ".local/share/mise/shims/node"),
8284
8469
  ...versionDirNodeBins(
8285
- (0, import_node_path23.join)(home, ".nvm", "versions", "node"),
8286
- (name) => (0, import_node_path23.join)(home, ".nvm", "versions", "node", name, "bin", "node")
8470
+ (0, import_node_path24.join)(home, ".nvm", "versions", "node"),
8471
+ (name) => (0, import_node_path24.join)(home, ".nvm", "versions", "node", name, "bin", "node")
8287
8472
  ),
8288
8473
  ...versionDirNodeBins(
8289
- (0, import_node_path23.join)(home, ".local/share/fnm", "node-versions"),
8290
- (name) => (0, import_node_path23.join)(home, ".local/share/fnm", "node-versions", name, "installation", "bin", "node")
8474
+ (0, import_node_path24.join)(home, ".local/share/fnm", "node-versions"),
8475
+ (name) => (0, import_node_path24.join)(home, ".local/share/fnm", "node-versions", name, "installation", "bin", "node")
8291
8476
  ),
8292
8477
  ...versionDirNodeBins(
8293
- (0, import_node_path23.join)(home, ".volta", "tools", "image", "node"),
8294
- (name) => (0, import_node_path23.join)(home, ".volta", "tools", "image", "node", name, "bin", "node")
8478
+ (0, import_node_path24.join)(home, ".volta", "tools", "image", "node"),
8479
+ (name) => (0, import_node_path24.join)(home, ".volta", "tools", "image", "node", name, "bin", "node")
8295
8480
  )
8296
8481
  ];
8297
8482
  }
@@ -8300,10 +8485,10 @@ function uniqueExistingNodeBins(paths) {
8300
8485
  const out = [];
8301
8486
  for (const raw of paths) {
8302
8487
  const p = raw.trim();
8303
- if (!p || !(0, import_node_fs21.existsSync)(p) || isElectronLikeCommand(p)) continue;
8488
+ if (!p || !(0, import_node_fs22.existsSync)(p) || isElectronLikeCommand(p)) continue;
8304
8489
  let key = p;
8305
8490
  try {
8306
- key = (0, import_node_fs21.realpathSync)(p);
8491
+ key = (0, import_node_fs22.realpathSync)(p);
8307
8492
  } catch {
8308
8493
  continue;
8309
8494
  }
@@ -8383,13 +8568,13 @@ async function resolveNodeLaunch(scriptPath) {
8383
8568
  env: { ELECTRON_RUN_AS_NODE: "1" }
8384
8569
  };
8385
8570
  }
8386
- var import_node_fs21, import_node_os7, import_node_path23, AGENT_RUNNER_MAX_OLD_SPACE_MB, MAX_OLD_SPACE_FLAG, PREFERRED_LTS_MAJORS;
8571
+ var import_node_fs22, import_node_os8, import_node_path24, AGENT_RUNNER_MAX_OLD_SPACE_MB, MAX_OLD_SPACE_FLAG, PREFERRED_LTS_MAJORS;
8387
8572
  var init_node_launch = __esm({
8388
8573
  "src/agents/node-launch.ts"() {
8389
8574
  "use strict";
8390
- import_node_fs21 = require("fs");
8391
- import_node_os7 = require("os");
8392
- import_node_path23 = require("path");
8575
+ import_node_fs22 = require("fs");
8576
+ import_node_os8 = require("os");
8577
+ import_node_path24 = require("path");
8393
8578
  init_nested_electron_env();
8394
8579
  init_run();
8395
8580
  init_packaged_runtime();
@@ -8483,37 +8668,37 @@ function corePackageDir() {
8483
8668
  try {
8484
8669
  const url = import_meta.url;
8485
8670
  if (typeof url === "string" && url.length > 0) {
8486
- return (0, import_node_path24.dirname)((0, import_node_url.fileURLToPath)(url));
8671
+ return (0, import_node_path25.dirname)((0, import_node_url.fileURLToPath)(url));
8487
8672
  }
8488
8673
  } catch {
8489
8674
  }
8490
8675
  try {
8491
- const req = (0, import_node_module.createRequire)((0, import_node_path24.join)(process.cwd(), "package.json"));
8492
- return (0, import_node_path24.dirname)(req.resolve("@sideboard-ai/core"));
8676
+ const req = (0, import_node_module.createRequire)((0, import_node_path25.join)(process.cwd(), "package.json"));
8677
+ return (0, import_node_path25.dirname)(req.resolve("@sideboard-ai/core"));
8493
8678
  } catch {
8494
8679
  return process.cwd();
8495
8680
  }
8496
8681
  }
8497
8682
  function findSideboardMcpJsEntry() {
8498
8683
  const override = process.env.SIDEBOARD_MCP_ENTRY?.trim() || process.env.SIDEBOARD_CLI?.trim();
8499
- if (override && (0, import_node_fs22.existsSync)(override)) return override;
8684
+ if (override && (0, import_node_fs23.existsSync)(override)) return override;
8500
8685
  const packaged = packagedMcpStdioPath();
8501
8686
  if (packaged) return packaged;
8502
8687
  let dir = corePackageDir();
8503
8688
  for (let i = 0; i < 10; i++) {
8504
8689
  const candidates = [
8505
- (0, import_node_path24.join)(dir, "mcp/run-stdio.js"),
8506
- (0, import_node_path24.join)(dir, "mcp/run-stdio.cjs"),
8507
- (0, import_node_path24.join)(dir, "dist/mcp/run-stdio.js"),
8508
- (0, import_node_path24.join)(dir, "dist/mcp/run-stdio.cjs"),
8509
- (0, import_node_path24.join)(dir, "packages/core/dist/mcp/run-stdio.js"),
8510
- (0, import_node_path24.join)(dir, "packages/cli/dist/index.js"),
8511
- (0, import_node_path24.join)(dir, "cli/dist/index.js")
8690
+ (0, import_node_path25.join)(dir, "mcp/run-stdio.js"),
8691
+ (0, import_node_path25.join)(dir, "mcp/run-stdio.cjs"),
8692
+ (0, import_node_path25.join)(dir, "dist/mcp/run-stdio.js"),
8693
+ (0, import_node_path25.join)(dir, "dist/mcp/run-stdio.cjs"),
8694
+ (0, import_node_path25.join)(dir, "packages/core/dist/mcp/run-stdio.js"),
8695
+ (0, import_node_path25.join)(dir, "packages/cli/dist/index.js"),
8696
+ (0, import_node_path25.join)(dir, "cli/dist/index.js")
8512
8697
  ];
8513
8698
  for (const p of candidates) {
8514
- if ((0, import_node_fs22.existsSync)(p) && !isAsarPath(p)) return p;
8699
+ if ((0, import_node_fs23.existsSync)(p) && !isAsarPath(p)) return p;
8515
8700
  }
8516
- const parent = (0, import_node_path24.dirname)(dir);
8701
+ const parent = (0, import_node_path25.dirname)(dir);
8517
8702
  if (parent === dir) break;
8518
8703
  dir = parent;
8519
8704
  }
@@ -8655,19 +8840,19 @@ function writeMcpServersConfig(servers) {
8655
8840
  ...env ? { env } : {}
8656
8841
  };
8657
8842
  }
8658
- const dir = (0, import_node_fs22.mkdtempSync)((0, import_node_path24.join)((0, import_node_os8.tmpdir)(), "sideboard-mcp-"));
8659
- const cfgPath = (0, import_node_path24.join)(dir, "mcp.json");
8660
- (0, import_node_fs22.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
8843
+ const dir = (0, import_node_fs23.mkdtempSync)((0, import_node_path25.join)((0, import_node_os9.tmpdir)(), "sideboard-mcp-"));
8844
+ const cfgPath = (0, import_node_path25.join)(dir, "mcp.json");
8845
+ (0, import_node_fs23.writeFileSync)(cfgPath, JSON.stringify({ mcpServers }, null, 2));
8661
8846
  return cfgPath;
8662
8847
  }
8663
- var import_node_fs22, import_node_module, import_node_os8, import_node_path24, import_node_url, import_meta, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
8848
+ var import_node_fs23, import_node_module, import_node_os9, import_node_path25, import_node_url, import_meta, SIDEBOARD_MCP_ALLOWED_TOOLS, SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS, brightsyMcpCommandCache, BRIGHTSY_WORD;
8664
8849
  var init_injected_mcp = __esm({
8665
8850
  "src/agents/injected-mcp.ts"() {
8666
8851
  "use strict";
8667
- import_node_fs22 = require("fs");
8852
+ import_node_fs23 = require("fs");
8668
8853
  import_node_module = require("module");
8669
- import_node_os8 = require("os");
8670
- import_node_path24 = require("path");
8854
+ import_node_os9 = require("os");
8855
+ import_node_path25 = require("path");
8671
8856
  import_node_url = require("url");
8672
8857
  init_run();
8673
8858
  init_config();
@@ -9006,11 +9191,11 @@ function parseIssuesJson(raw) {
9006
9191
  }
9007
9192
  return [];
9008
9193
  }
9009
- var import_node_fs23, BASE_ALLOWED_TOOLS, CLAUDE_PRINT_BG_WAIT_CEILING_MS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, mcpListCache, claudeAdapter;
9194
+ var import_node_fs24, BASE_ALLOWED_TOOLS, CLAUDE_PRINT_BG_WAIT_CEILING_MS, CLAUDE_CHROME_ALLOWED_TOOLS, CLAUDE_PROMPT_ARG_MAX, mcpListCache, claudeAdapter;
9010
9195
  var init_claude = __esm({
9011
9196
  "src/agents/claude.ts"() {
9012
9197
  "use strict";
9013
- import_node_fs23 = require("fs");
9198
+ import_node_fs24 = require("fs");
9014
9199
  init_run();
9015
9200
  init_app_settings();
9016
9201
  init_claude_mcp();
@@ -9050,7 +9235,7 @@ var init_claude = __esm({
9050
9235
  async detect() {
9051
9236
  const claude = resolveClaudeExecutable();
9052
9237
  if (claude !== "claude") {
9053
- if (!(0, import_node_fs23.existsSync)(claude)) {
9238
+ if (!(0, import_node_fs24.existsSync)(claude)) {
9054
9239
  return {
9055
9240
  agent: "claude",
9056
9241
  installed: false,
@@ -9305,7 +9490,7 @@ async function listCodexModels() {
9305
9490
  if (codex === "codex") {
9306
9491
  const which = await run("which", ["codex"], { reject: false });
9307
9492
  if (which.exitCode !== 0) return FALLBACK_CODEX_MODELS;
9308
- } else if (!(0, import_node_fs24.existsSync)(codex)) {
9493
+ } else if (!(0, import_node_fs25.existsSync)(codex)) {
9309
9494
  return FALLBACK_CODEX_MODELS;
9310
9495
  }
9311
9496
  const listed = await run(codex, ["debug", "models"], { reject: false });
@@ -9340,12 +9525,12 @@ function usageFromCodex(usage) {
9340
9525
  }
9341
9526
  function codexConfigHasNetworkAccess() {
9342
9527
  const candidates = [
9343
- (0, import_node_path25.join)((0, import_node_os9.homedir)(), ".codex", "config.toml"),
9344
- (0, import_node_path25.join)((0, import_node_os9.homedir)(), ".config", "codex", "config.toml")
9528
+ (0, import_node_path26.join)((0, import_node_os10.homedir)(), ".codex", "config.toml"),
9529
+ (0, import_node_path26.join)((0, import_node_os10.homedir)(), ".config", "codex", "config.toml")
9345
9530
  ];
9346
9531
  for (const path of candidates) {
9347
- if (!(0, import_node_fs24.existsSync)(path)) continue;
9348
- const text5 = (0, import_node_fs24.readFileSync)(path, "utf8");
9532
+ if (!(0, import_node_fs25.existsSync)(path)) continue;
9533
+ const text5 = (0, import_node_fs25.readFileSync)(path, "utf8");
9349
9534
  if (/network_access\s*=\s*true/.test(text5)) return true;
9350
9535
  }
9351
9536
  return false;
@@ -9377,21 +9562,21 @@ function asRecord2(value) {
9377
9562
  return void 0;
9378
9563
  }
9379
9564
  function codexLooksAuthenticated() {
9380
- const authPath = (0, import_node_path25.join)((0, import_node_os9.homedir)(), ".codex", "auth.json");
9381
- if (!(0, import_node_fs24.existsSync)(authPath)) return false;
9565
+ const authPath = (0, import_node_path26.join)((0, import_node_os10.homedir)(), ".codex", "auth.json");
9566
+ if (!(0, import_node_fs25.existsSync)(authPath)) return false;
9382
9567
  try {
9383
- return (0, import_node_fs24.statSync)(authPath).size > 2;
9568
+ return (0, import_node_fs25.statSync)(authPath).size > 2;
9384
9569
  } catch {
9385
9570
  return false;
9386
9571
  }
9387
9572
  }
9388
- var import_node_fs24, import_node_os9, import_node_path25, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
9573
+ var import_node_fs25, import_node_os10, import_node_path26, CODEX_PROMPT_ARG_MAX, FALLBACK_CODEX_MODELS, cachedCodexModels, CODEX_MODEL_CACHE_MS, codexAdapter;
9389
9574
  var init_codex = __esm({
9390
9575
  "src/agents/codex.ts"() {
9391
9576
  "use strict";
9392
- import_node_fs24 = require("fs");
9393
- import_node_os9 = require("os");
9394
- import_node_path25 = require("path");
9577
+ import_node_fs25 = require("fs");
9578
+ import_node_os10 = require("os");
9579
+ import_node_path26 = require("path");
9395
9580
  init_run();
9396
9581
  init_app_settings();
9397
9582
  init_global_workspace();
@@ -9416,7 +9601,7 @@ var init_codex = __esm({
9416
9601
  async detect() {
9417
9602
  const codex = resolveAgentExecutable("codex");
9418
9603
  if (codex !== "codex") {
9419
- if (!(0, import_node_fs24.existsSync)(codex)) {
9604
+ if (!(0, import_node_fs25.existsSync)(codex)) {
9420
9605
  return {
9421
9606
  agent: "codex",
9422
9607
  installed: false,
@@ -9903,21 +10088,21 @@ function platformRipgrepPackage() {
9903
10088
  }
9904
10089
  function usableRipgrepPath(candidate) {
9905
10090
  const raw = candidate?.trim();
9906
- if (!raw || !(0, import_node_path26.isAbsolute)(raw)) return null;
10091
+ if (!raw || !(0, import_node_path27.isAbsolute)(raw)) return null;
9907
10092
  const readable = nodeReadableScriptPath(raw);
9908
- if (!(0, import_node_fs25.existsSync)(readable) || isAsarPath(readable)) return null;
10093
+ if (!(0, import_node_fs26.existsSync)(readable) || isAsarPath(readable)) return null;
9909
10094
  return readable;
9910
10095
  }
9911
10096
  function walkForBundledRipgrep(startFile) {
9912
10097
  if (!startFile) return null;
9913
10098
  const pkg = platformRipgrepPackage();
9914
10099
  const name = rgBinaryName();
9915
- let dir = (0, import_node_path26.dirname)((0, import_node_path26.resolve)(startFile));
9916
- const root = (0, import_node_path26.parse)(dir).root;
10100
+ let dir = (0, import_node_path27.dirname)((0, import_node_path27.resolve)(startFile));
10101
+ const root = (0, import_node_path27.parse)(dir).root;
9917
10102
  while (dir !== root) {
9918
- const hit = usableRipgrepPath((0, import_node_path26.join)(dir, "node_modules", pkg, "bin", name));
10103
+ const hit = usableRipgrepPath((0, import_node_path27.join)(dir, "node_modules", pkg, "bin", name));
9919
10104
  if (hit) return hit;
9920
- const next = (0, import_node_path26.dirname)(dir);
10105
+ const next = (0, import_node_path27.dirname)(dir);
9921
10106
  if (next === dir) break;
9922
10107
  dir = next;
9923
10108
  }
@@ -9927,7 +10112,7 @@ function requireResolveBundledRipgrep(fromFile) {
9927
10112
  try {
9928
10113
  const req = (0, import_node_module2.createRequire)(fromFile);
9929
10114
  const pkgJson = req.resolve(`${platformRipgrepPackage()}/package.json`);
9930
- return usableRipgrepPath((0, import_node_path26.join)((0, import_node_path26.dirname)(pkgJson), "bin", rgBinaryName()));
10115
+ return usableRipgrepPath((0, import_node_path27.join)((0, import_node_path27.dirname)(pkgJson), "bin", rgBinaryName()));
9931
10116
  } catch {
9932
10117
  return null;
9933
10118
  }
@@ -9949,13 +10134,13 @@ function cursorRipgrepEnv(opts) {
9949
10134
  const path = resolveCursorRipgrepPath(opts);
9950
10135
  return path ? { [RIPGREP_ENV]: path } : {};
9951
10136
  }
9952
- var import_node_fs25, import_node_module2, import_node_path26, RIPGREP_ENV;
10137
+ var import_node_fs26, import_node_module2, import_node_path27, RIPGREP_ENV;
9953
10138
  var init_cursor_ripgrep = __esm({
9954
10139
  "src/agents/cursor-ripgrep.ts"() {
9955
10140
  "use strict";
9956
- import_node_fs25 = require("fs");
10141
+ import_node_fs26 = require("fs");
9957
10142
  import_node_module2 = require("module");
9958
- import_node_path26 = require("path");
10143
+ import_node_path27 = require("path");
9959
10144
  init_node_launch();
9960
10145
  init_packaged_runtime();
9961
10146
  RIPGREP_ENV = "CURSOR_RIPGREP_PATH";
@@ -10001,11 +10186,11 @@ function entryDir() {
10001
10186
  const cjsDir = typeof __dirname !== "undefined" ? __dirname : "";
10002
10187
  if (cjsDir) return cjsDir;
10003
10188
  try {
10004
- return (0, import_node_path27.dirname)((0, import_node_url2.fileURLToPath)(import_meta2.url));
10189
+ return (0, import_node_path28.dirname)((0, import_node_url2.fileURLToPath)(import_meta2.url));
10005
10190
  } catch {
10006
10191
  try {
10007
10192
  const req = (0, import_node_module3.createRequire)(process.cwd() + "/");
10008
- return (0, import_node_path27.dirname)(req.resolve("@sideboard-ai/core"));
10193
+ return (0, import_node_path28.dirname)(req.resolve("@sideboard-ai/core"));
10009
10194
  } catch {
10010
10195
  return process.cwd();
10011
10196
  }
@@ -10016,27 +10201,27 @@ function cursorRunnerPath() {
10016
10201
  if (packaged) return packaged;
10017
10202
  const root = entryDir();
10018
10203
  const candidates = [
10019
- (0, import_node_path27.join)(root, "agents", "cursor-runner.js"),
10020
- (0, import_node_path27.join)(root, "agents", "cursor-runner.cjs"),
10204
+ (0, import_node_path28.join)(root, "agents", "cursor-runner.js"),
10205
+ (0, import_node_path28.join)(root, "agents", "cursor-runner.cjs"),
10021
10206
  // If somehow resolved from package root instead of dist/
10022
- (0, import_node_path27.join)(root, "dist", "agents", "cursor-runner.js"),
10023
- (0, import_node_path27.join)(root, "dist", "agents", "cursor-runner.cjs"),
10207
+ (0, import_node_path28.join)(root, "dist", "agents", "cursor-runner.js"),
10208
+ (0, import_node_path28.join)(root, "dist", "agents", "cursor-runner.cjs"),
10024
10209
  // Source tree (dev): packages/core/src/agents/cursor-runner.ts
10025
- (0, import_node_path27.join)(root, "cursor-runner.ts"),
10026
- (0, import_node_path27.join)(root, "src", "agents", "cursor-runner.ts")
10210
+ (0, import_node_path28.join)(root, "cursor-runner.ts"),
10211
+ (0, import_node_path28.join)(root, "src", "agents", "cursor-runner.ts")
10027
10212
  ];
10028
10213
  for (const candidate of candidates) {
10029
- if ((0, import_node_fs26.existsSync)(candidate)) return candidate;
10214
+ if ((0, import_node_fs27.existsSync)(candidate)) return candidate;
10030
10215
  }
10031
10216
  return candidates[0];
10032
10217
  }
10033
- var import_node_fs26, import_node_module3, import_node_path27, import_node_url2, import_sdk, import_meta2, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
10218
+ var import_node_fs27, import_node_module3, import_node_path28, import_node_url2, import_sdk, import_meta2, FALLBACK_CURSOR_MODELS, cachedModels, MODEL_CACHE_MS, cursorAdapter;
10034
10219
  var init_cursor = __esm({
10035
10220
  "src/agents/cursor.ts"() {
10036
10221
  "use strict";
10037
- import_node_fs26 = require("fs");
10222
+ import_node_fs27 = require("fs");
10038
10223
  import_node_module3 = require("module");
10039
- import_node_path27 = require("path");
10224
+ import_node_path28 = require("path");
10040
10225
  import_node_url2 = require("url");
10041
10226
  import_sdk = require("@cursor/sdk");
10042
10227
  init_run();
@@ -10187,7 +10372,7 @@ async function listOpencodeModels() {
10187
10372
  if (opencode === "opencode") {
10188
10373
  const which = await run("which", ["opencode"], { reject: false });
10189
10374
  if (which.exitCode !== 0) return FALLBACK_OPENCODE_MODELS;
10190
- } else if (!(0, import_node_fs27.existsSync)(opencode)) {
10375
+ } else if (!(0, import_node_fs28.existsSync)(opencode)) {
10191
10376
  return FALLBACK_OPENCODE_MODELS;
10192
10377
  }
10193
10378
  const listed = await run(opencode, ["models"], { reject: false });
@@ -10217,11 +10402,11 @@ function usageFromOpencode(tokens) {
10217
10402
  cacheWriteTokens: tokens.cache?.write ? Number(tokens.cache.write) : void 0
10218
10403
  };
10219
10404
  }
10220
- var import_node_fs27, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
10405
+ var import_node_fs28, FALLBACK_OPENCODE_MODELS, cachedOpencodeModels, OPENCODE_MODEL_CACHE_MS, opencodeAdapter;
10221
10406
  var init_opencode = __esm({
10222
10407
  "src/agents/opencode.ts"() {
10223
10408
  "use strict";
10224
- import_node_fs27 = require("fs");
10409
+ import_node_fs28 = require("fs");
10225
10410
  init_run();
10226
10411
  init_app_settings();
10227
10412
  init_global_workspace();
@@ -10248,7 +10433,7 @@ var init_opencode = __esm({
10248
10433
  async detect() {
10249
10434
  const opencode = resolveAgentExecutable("opencode");
10250
10435
  if (opencode !== "opencode") {
10251
- if (!(0, import_node_fs27.existsSync)(opencode)) {
10436
+ if (!(0, import_node_fs28.existsSync)(opencode)) {
10252
10437
  return {
10253
10438
  agent: "opencode",
10254
10439
  installed: false,
@@ -11849,7 +12034,7 @@ function forkMessageSlice(from, throughIndex) {
11849
12034
  function buildForkTranscriptAttachment(baseTitle, messages) {
11850
12035
  const title = baseTitle || "Chat";
11851
12036
  return {
11852
- id: (0, import_node_crypto5.randomUUID)(),
12037
+ id: (0, import_node_crypto6.randomUUID)(),
11853
12038
  name: `Transcript of ${title}.md`,
11854
12039
  kind: "transcript",
11855
12040
  content: formatTranscriptMarkdown(title, messages)
@@ -11906,11 +12091,11 @@ function forkChatTab(input) {
11906
12091
  }
11907
12092
  return tab;
11908
12093
  }
11909
- var import_node_crypto5;
12094
+ var import_node_crypto6;
11910
12095
  var init_chat_tabs = __esm({
11911
12096
  "src/threads/chat-tabs.ts"() {
11912
12097
  "use strict";
11913
- import_node_crypto5 = require("crypto");
12098
+ import_node_crypto6 = require("crypto");
11914
12099
  init_context_compact();
11915
12100
  init_teams();
11916
12101
  init_worktree_labels();
@@ -12080,21 +12265,21 @@ function shouldRefreshReviewRequestTemplate(content) {
12080
12265
  return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
12081
12266
  }
12082
12267
  function readTextIfPresent(abs) {
12083
- if (!(0, import_node_fs28.existsSync)(abs)) return null;
12268
+ if (!(0, import_node_fs29.existsSync)(abs)) return null;
12084
12269
  try {
12085
- const content = (0, import_node_fs28.readFileSync)(abs, "utf8");
12270
+ const content = (0, import_node_fs29.readFileSync)(abs, "utf8");
12086
12271
  return content.trim() ? content : null;
12087
12272
  } catch {
12088
12273
  return null;
12089
12274
  }
12090
12275
  }
12091
12276
  function readLocalGuidelines(worktreePath) {
12092
- const localAbs = (0, import_node_path28.join)(worktreePath, REVIEW_REQUEST_PATH);
12277
+ const localAbs = (0, import_node_path29.join)(worktreePath, REVIEW_REQUEST_PATH);
12093
12278
  const localContent = readTextIfPresent(localAbs);
12094
12279
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
12095
12280
  return { path: REVIEW_REQUEST_PATH, content: localContent };
12096
12281
  }
12097
- const legacyAbs = (0, import_node_path28.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
12282
+ const legacyAbs = (0, import_node_path29.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
12098
12283
  const legacyContent = readTextIfPresent(legacyAbs);
12099
12284
  if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
12100
12285
  return { path: LEGACY_REVIEW_REQUEST_PATH, content: legacyContent };
@@ -12110,20 +12295,20 @@ function skillGuidelines(content, source) {
12110
12295
  };
12111
12296
  }
12112
12297
  function ensureReviewSkillFile(worktreePath) {
12113
- const abs = (0, import_node_path28.join)(worktreePath, REVIEW_SKILL_PATH);
12298
+ const abs = (0, import_node_path29.join)(worktreePath, REVIEW_SKILL_PATH);
12114
12299
  const existing = readTextIfPresent(abs);
12115
12300
  if (existing) {
12116
12301
  return { path: REVIEW_SKILL_PATH, content: existing, wrote: false };
12117
12302
  }
12118
- const fromRepo = readTextIfPresent((0, import_node_path28.join)(worktreePath, REPO_REVIEW_PATH));
12303
+ const fromRepo = readTextIfPresent((0, import_node_path29.join)(worktreePath, REPO_REVIEW_PATH));
12119
12304
  const fromLocal = readLocalGuidelines(worktreePath)?.content ?? null;
12120
12305
  const content = wrapReviewSkillMarkdown(fromRepo ?? fromLocal ?? REVIEW_REQUEST_TEMPLATE);
12121
- (0, import_node_fs28.mkdirSync)((0, import_node_path28.dirname)(abs), { recursive: true });
12122
- (0, import_node_fs28.writeFileSync)(abs, content, "utf8");
12306
+ (0, import_node_fs29.mkdirSync)((0, import_node_path29.dirname)(abs), { recursive: true });
12307
+ (0, import_node_fs29.writeFileSync)(abs, content, "utf8");
12123
12308
  return { path: REVIEW_SKILL_PATH, content, wrote: true };
12124
12309
  }
12125
12310
  function resolveReviewGuidelines(worktreePath) {
12126
- const skillContent = readTextIfPresent((0, import_node_path28.join)(worktreePath, REVIEW_SKILL_PATH));
12311
+ const skillContent = readTextIfPresent((0, import_node_path29.join)(worktreePath, REVIEW_SKILL_PATH));
12127
12312
  if (skillContent) return skillGuidelines(skillContent, "skill");
12128
12313
  const local = readLocalGuidelines(worktreePath);
12129
12314
  if (local) {
@@ -12141,7 +12326,7 @@ function buildReviewRequestAttachment(content, opts) {
12141
12326
  const path = opts?.path ?? REVIEW_SKILL_PATH;
12142
12327
  const name = opts?.name ?? (path === REVIEW_SKILL_PATH ? REVIEW_SKILL_NAME : path === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
12143
12328
  return {
12144
- id: (0, import_node_crypto6.randomUUID)(),
12329
+ id: (0, import_node_crypto7.randomUUID)(),
12145
12330
  name,
12146
12331
  kind: "file",
12147
12332
  path,
@@ -12173,13 +12358,13 @@ async function requestReview(threadRef, send) {
12173
12358
  const started = await send(tab.id, REVIEW_REQUEST_PREFILL);
12174
12359
  return { tab: started, from };
12175
12360
  }
12176
- var import_node_crypto6, import_node_fs28, import_node_path28, REPO_REVIEW_PATH, REPO_REVIEW_NAME, REVIEW_REQUEST_PATH, LEGACY_REVIEW_REQUEST_PATH, REVIEW_REQUEST_NAME, REVIEW_REQUEST_PREFILL, LEGACY_REVIEW_TEMPLATE_MARKERS;
12361
+ var import_node_crypto7, import_node_fs29, import_node_path29, REPO_REVIEW_PATH, REPO_REVIEW_NAME, REVIEW_REQUEST_PATH, LEGACY_REVIEW_REQUEST_PATH, REVIEW_REQUEST_NAME, REVIEW_REQUEST_PREFILL, LEGACY_REVIEW_TEMPLATE_MARKERS;
12177
12362
  var init_request_review = __esm({
12178
12363
  "src/review/request-review.ts"() {
12179
12364
  "use strict";
12180
- import_node_crypto6 = require("crypto");
12181
- import_node_fs28 = require("fs");
12182
- import_node_path28 = require("path");
12365
+ import_node_crypto7 = require("crypto");
12366
+ import_node_fs29 = require("fs");
12367
+ import_node_path29 = require("path");
12183
12368
  init_global_workspace();
12184
12369
  init_chat_tabs();
12185
12370
  init_thread_store();
@@ -12204,9 +12389,9 @@ function matchSimpleGlob(pattern, name) {
12204
12389
  return new RegExp(`^${escaped}$`).test(name);
12205
12390
  }
12206
12391
  function readWorktreeInclude(repoPath) {
12207
- const path = (0, import_node_path29.join)(repoPath, ".worktreeinclude");
12208
- if (!(0, import_node_fs29.existsSync)(path)) return [];
12209
- return (0, import_node_fs29.readFileSync)(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
12392
+ const path = (0, import_node_path30.join)(repoPath, ".worktreeinclude");
12393
+ if (!(0, import_node_fs30.existsSync)(path)) return [];
12394
+ return (0, import_node_fs30.readFileSync)(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
12210
12395
  }
12211
12396
  function resolveFilesToCopy(repoPath) {
12212
12397
  const fromInclude = readWorktreeInclude(repoPath);
@@ -12216,10 +12401,10 @@ function resolveFilesToCopy(repoPath) {
12216
12401
  if (settings?.fileIncludeGlobs?.length) {
12217
12402
  const matched = [];
12218
12403
  try {
12219
- for (const entry of (0, import_node_fs29.readdirSync)(repoPath, { withFileTypes: true })) {
12404
+ for (const entry of (0, import_node_fs30.readdirSync)(repoPath, { withFileTypes: true })) {
12220
12405
  if (!entry.isFile()) continue;
12221
12406
  for (const glob of settings.fileIncludeGlobs) {
12222
- if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path29.basename)(glob), entry.name)) {
12407
+ if (matchSimpleGlob(glob, entry.name) || matchSimpleGlob((0, import_node_path30.basename)(glob), entry.name)) {
12223
12408
  matched.push(entry.name);
12224
12409
  break;
12225
12410
  }
@@ -12231,7 +12416,7 @@ function resolveFilesToCopy(repoPath) {
12231
12416
  }
12232
12417
  const defaults = [];
12233
12418
  try {
12234
- for (const entry of (0, import_node_fs29.readdirSync)(repoPath, { withFileTypes: true })) {
12419
+ for (const entry of (0, import_node_fs30.readdirSync)(repoPath, { withFileTypes: true })) {
12235
12420
  if (entry.isFile() && entry.name.startsWith(".env")) {
12236
12421
  defaults.push(entry.name);
12237
12422
  }
@@ -12245,11 +12430,11 @@ function copyConfiguredFiles(repoPath, worktreePath) {
12245
12430
  const patterns = resolveFilesToCopy(repoPath);
12246
12431
  const copied = [];
12247
12432
  for (const rel of patterns) {
12248
- const src = (0, import_node_path29.join)(repoPath, rel);
12249
- if (!(0, import_node_fs29.existsSync)(src)) continue;
12250
- const dest = (0, import_node_path29.join)(worktreePath, rel);
12251
- (0, import_node_fs29.mkdirSync)((0, import_node_path29.dirname)(dest), { recursive: true });
12252
- (0, import_node_fs29.copyFileSync)(src, dest);
12433
+ const src = (0, import_node_path30.join)(repoPath, rel);
12434
+ if (!(0, import_node_fs30.existsSync)(src)) continue;
12435
+ const dest = (0, import_node_path30.join)(worktreePath, rel);
12436
+ (0, import_node_fs30.mkdirSync)((0, import_node_path30.dirname)(dest), { recursive: true });
12437
+ (0, import_node_fs30.copyFileSync)(src, dest);
12253
12438
  copied.push(rel);
12254
12439
  }
12255
12440
  return copied;
@@ -12284,7 +12469,7 @@ function buildWorkspaceScriptEnv(opts, baseEnv) {
12284
12469
  const env = stripNestedElectronEnv({
12285
12470
  ...baseEnv ?? process.env
12286
12471
  });
12287
- const name = opts.workspaceName ?? (0, import_node_path29.basename)(opts.worktreePath);
12472
+ const name = opts.workspaceName ?? (0, import_node_path30.basename)(opts.worktreePath);
12288
12473
  const ports = opts.ports ?? [];
12289
12474
  const primary = ports[0];
12290
12475
  env.SIDEBOARD_WORKSPACE_NAME = name;
@@ -12439,6 +12624,7 @@ async function runConventionSetup(repoPath, worktreePath, onLine, opts) {
12439
12624
  };
12440
12625
  }
12441
12626
  async function runWorkspaceSetup(repoPath, worktreePath, onLine, opts) {
12627
+ await ensureWorktreeSideboardIgnored(worktreePath);
12442
12628
  const reviewSkill = ensureReviewSkillFile(worktreePath);
12443
12629
  if (reviewSkill.wrote) {
12444
12630
  onLine?.(
@@ -12545,13 +12731,13 @@ async function startDevServer(repoPath, worktreePath, onLine, opts) {
12545
12731
  done: handle.done
12546
12732
  };
12547
12733
  }
12548
- var import_node_fs29, import_node_net, import_node_path29, import_execa4, import_node_readline3, PORT_RANGE_SIZE, cachedLoginEnv;
12734
+ var import_node_fs30, import_node_net, import_node_path30, import_execa4, import_node_readline3, PORT_RANGE_SIZE, cachedLoginEnv;
12549
12735
  var init_conductor = __esm({
12550
12736
  "src/hook/conductor.ts"() {
12551
12737
  "use strict";
12552
- import_node_fs29 = require("fs");
12738
+ import_node_fs30 = require("fs");
12553
12739
  import_node_net = require("net");
12554
- import_node_path29 = require("path");
12740
+ import_node_path30 = require("path");
12555
12741
  import_execa4 = require("execa");
12556
12742
  import_node_readline3 = require("readline");
12557
12743
  init_settings();
@@ -12560,6 +12746,7 @@ var init_conductor = __esm({
12560
12746
  init_cursor_worktrees();
12561
12747
  init_git_auth_mode();
12562
12748
  init_request_review();
12749
+ init_worktree_exclude();
12563
12750
  init_nested_electron_env();
12564
12751
  PORT_RANGE_SIZE = 10;
12565
12752
  cachedLoginEnv = null;
@@ -12577,9 +12764,9 @@ async function findOrphanWorktrees(repoPaths) {
12577
12764
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
12578
12765
  );
12579
12766
  const homeRoot = sideboardWorkspacesDir();
12580
- if ((0, import_node_fs30.existsSync)(homeRoot)) {
12767
+ if ((0, import_node_fs31.existsSync)(homeRoot)) {
12581
12768
  try {
12582
- for (const entry of (0, import_node_fs30.readdirSync)(homeRoot, { withFileTypes: true })) {
12769
+ for (const entry of (0, import_node_fs31.readdirSync)(homeRoot, { withFileTypes: true })) {
12583
12770
  if (!entry.isDirectory()) continue;
12584
12771
  void entry;
12585
12772
  }
@@ -12589,7 +12776,7 @@ async function findOrphanWorktrees(repoPaths) {
12589
12776
  const orphans = [];
12590
12777
  const seen = /* @__PURE__ */ new Set();
12591
12778
  for (const repoPath of repos) {
12592
- if (!repoPath || !(0, import_node_fs30.existsSync)(repoPath)) continue;
12779
+ if (!repoPath || !(0, import_node_fs31.existsSync)(repoPath)) continue;
12593
12780
  try {
12594
12781
  const wts = await listWorktrees(repoPath);
12595
12782
  for (const wt of wts) {
@@ -12600,7 +12787,7 @@ async function findOrphanWorktrees(repoPaths) {
12600
12787
  seen.add(path);
12601
12788
  let mtimeMs = 0;
12602
12789
  try {
12603
- mtimeMs = (0, import_node_fs30.statSync)(path).mtimeMs;
12790
+ mtimeMs = (0, import_node_fs31.statSync)(path).mtimeMs;
12604
12791
  } catch {
12605
12792
  mtimeMs = 0;
12606
12793
  }
@@ -12610,16 +12797,16 @@ async function findOrphanWorktrees(repoPaths) {
12610
12797
  }
12611
12798
  try {
12612
12799
  const root = worktreesRoot(repoPath);
12613
- if ((0, import_node_fs30.existsSync)(root)) {
12614
- for (const entry of (0, import_node_fs30.readdirSync)(root, { withFileTypes: true })) {
12800
+ if ((0, import_node_fs31.existsSync)(root)) {
12801
+ for (const entry of (0, import_node_fs31.readdirSync)(root, { withFileTypes: true })) {
12615
12802
  if (!entry.isDirectory()) continue;
12616
- const path = (0, import_node_path30.join)(root, entry.name).replace(/\/$/, "");
12803
+ const path = (0, import_node_path31.join)(root, entry.name).replace(/\/$/, "");
12617
12804
  if (known.has(path) || seen.has(path)) continue;
12618
- if (!(0, import_node_fs30.existsSync)((0, import_node_path30.join)(path, ".git"))) continue;
12805
+ if (!(0, import_node_fs31.existsSync)((0, import_node_path31.join)(path, ".git"))) continue;
12619
12806
  seen.add(path);
12620
12807
  let mtimeMs = 0;
12621
12808
  try {
12622
- mtimeMs = (0, import_node_fs30.statSync)(path).mtimeMs;
12809
+ mtimeMs = (0, import_node_fs31.statSync)(path).mtimeMs;
12623
12810
  } catch {
12624
12811
  mtimeMs = Date.now();
12625
12812
  }
@@ -12671,12 +12858,12 @@ function shouldRunWorktreeCleanup(settings = loadAppSettings()) {
12671
12858
  const elapsed = Date.now() - Date.parse(last);
12672
12859
  return elapsed >= intervalHours * 36e5;
12673
12860
  }
12674
- var import_node_fs30, import_node_path30;
12861
+ var import_node_fs31, import_node_path31;
12675
12862
  var init_orphan_cleanup = __esm({
12676
12863
  "src/git/orphan-cleanup.ts"() {
12677
12864
  "use strict";
12678
- import_node_fs30 = require("fs");
12679
- import_node_path30 = require("path");
12865
+ import_node_fs31 = require("fs");
12866
+ import_node_path31 = require("path");
12680
12867
  init_worktree();
12681
12868
  init_thread_store();
12682
12869
  init_paths();
@@ -12783,38 +12970,38 @@ __export(workspaces_exports, {
12783
12970
  syncWorkspacesFromThreads: () => syncWorkspacesFromThreads
12784
12971
  });
12785
12972
  function workspacesFile() {
12786
- return (0, import_node_path31.join)(appDataDir(), "workspaces.json");
12973
+ return (0, import_node_path32.join)(appDataDir(), "workspaces.json");
12787
12974
  }
12788
12975
  function removedWorkspacesFile() {
12789
- return (0, import_node_path31.join)(appDataDir(), "removed-workspaces.json");
12976
+ return (0, import_node_path32.join)(appDataDir(), "removed-workspaces.json");
12790
12977
  }
12791
12978
  function readAll() {
12792
12979
  const path = workspacesFile();
12793
- if (!(0, import_node_fs31.existsSync)(path)) return [];
12980
+ if (!(0, import_node_fs32.existsSync)(path)) return [];
12794
12981
  try {
12795
- const raw = JSON.parse((0, import_node_fs31.readFileSync)(path, "utf8"));
12982
+ const raw = JSON.parse((0, import_node_fs32.readFileSync)(path, "utf8"));
12796
12983
  return Array.isArray(raw) ? raw : [];
12797
12984
  } catch {
12798
12985
  return [];
12799
12986
  }
12800
12987
  }
12801
12988
  function writeAll(list) {
12802
- (0, import_node_fs31.mkdirSync)(appDataDir(), { recursive: true });
12803
- (0, import_node_fs31.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
12989
+ (0, import_node_fs32.mkdirSync)(appDataDir(), { recursive: true });
12990
+ (0, import_node_fs32.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
12804
12991
  }
12805
12992
  function readRemoved() {
12806
12993
  const path = removedWorkspacesFile();
12807
- if (!(0, import_node_fs31.existsSync)(path)) return /* @__PURE__ */ new Set();
12994
+ if (!(0, import_node_fs32.existsSync)(path)) return /* @__PURE__ */ new Set();
12808
12995
  try {
12809
- const raw = JSON.parse((0, import_node_fs31.readFileSync)(path, "utf8"));
12996
+ const raw = JSON.parse((0, import_node_fs32.readFileSync)(path, "utf8"));
12810
12997
  return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
12811
12998
  } catch {
12812
12999
  return /* @__PURE__ */ new Set();
12813
13000
  }
12814
13001
  }
12815
13002
  function writeRemoved(paths) {
12816
- (0, import_node_fs31.mkdirSync)(appDataDir(), { recursive: true });
12817
- (0, import_node_fs31.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
13003
+ (0, import_node_fs32.mkdirSync)(appDataDir(), { recursive: true });
13004
+ (0, import_node_fs32.writeFileSync)(removedWorkspacesFile(), JSON.stringify([...paths].sort(), null, 2), "utf8");
12818
13005
  }
12819
13006
  function rememberRemoved(repoPath) {
12820
13007
  const next = readRemoved();
@@ -12837,7 +13024,7 @@ function listWorkspaces() {
12837
13024
  async function addWorkspace(repoPath) {
12838
13025
  const root = await resolveRepoRoot(repoPath);
12839
13026
  if (!root || root === "/") throw new Error(`Invalid repo path: ${repoPath}`);
12840
- if (!(0, import_node_fs31.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
13027
+ if (!(0, import_node_fs32.existsSync)(root)) throw new Error(`Repo not found: ${root}`);
12841
13028
  forgetRemoved(root);
12842
13029
  await ensureGhPreferOrigin(root);
12843
13030
  const current = readAll();
@@ -12845,7 +13032,7 @@ async function addWorkspace(repoPath) {
12845
13032
  if (existing) return existing;
12846
13033
  const next = {
12847
13034
  path: root,
12848
- name: (0, import_node_path31.basename)(root),
13035
+ name: (0, import_node_path32.basename)(root),
12849
13036
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
12850
13037
  };
12851
13038
  writeAll([...current, next]);
@@ -12867,10 +13054,10 @@ function syncWorkspacesFromThreads(repoPaths) {
12867
13054
  if (!path || path === "/" || isGlobalRepoPath(path) || byPath.has(path) || removed.has(path)) {
12868
13055
  continue;
12869
13056
  }
12870
- if (!(0, import_node_fs31.existsSync)(path)) continue;
13057
+ if (!(0, import_node_fs32.existsSync)(path)) continue;
12871
13058
  const ws = {
12872
13059
  path,
12873
- name: (0, import_node_path31.basename)(path),
13060
+ name: (0, import_node_path32.basename)(path),
12874
13061
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
12875
13062
  };
12876
13063
  byPath.set(path, ws);
@@ -12880,12 +13067,12 @@ function syncWorkspacesFromThreads(repoPaths) {
12880
13067
  if (dirty) writeAll(next);
12881
13068
  return next.sort((a, b) => a.name.localeCompare(b.name));
12882
13069
  }
12883
- var import_node_fs31, import_node_path31;
13070
+ var import_node_fs32, import_node_path32;
12884
13071
  var init_workspaces2 = __esm({
12885
13072
  "src/store/workspaces.ts"() {
12886
13073
  "use strict";
12887
- import_node_fs31 = require("fs");
12888
- import_node_path31 = require("path");
13074
+ import_node_fs32 = require("fs");
13075
+ import_node_path32 = require("path");
12889
13076
  init_paths();
12890
13077
  init_global_workspace();
12891
13078
  init_worktree();
@@ -12898,12 +13085,12 @@ async function cloneRepoIntoSideboard(opts) {
12898
13085
  if (!url) throw new Error("Clone URL is required");
12899
13086
  let name = opts.name?.trim();
12900
13087
  if (!name) {
12901
- const leaf = (0, import_node_path32.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
13088
+ const leaf = (0, import_node_path33.basename)(url.replace(/\/$/, "").replace(/\.git$/, ""));
12902
13089
  name = leaf || "repo";
12903
13090
  }
12904
13091
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
12905
- const dest = (0, import_node_path32.join)(sideboardReposDir(), name);
12906
- if ((0, import_node_fs32.existsSync)(dest)) {
13092
+ const dest = (0, import_node_path33.join)(sideboardReposDir(), name);
13093
+ if ((0, import_node_fs33.existsSync)(dest)) {
12907
13094
  const repoPath2 = await resolveRepoRoot(dest);
12908
13095
  const workspace2 = await ensureWorkspace(repoPath2);
12909
13096
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -12918,12 +13105,12 @@ async function cloneRepoIntoSideboard(opts) {
12918
13105
  const workspace = await ensureWorkspace(repoPath);
12919
13106
  return { repoPath, workspace };
12920
13107
  }
12921
- var import_node_fs32, import_node_path32, import_execa6;
13108
+ var import_node_fs33, import_node_path33, import_execa6;
12922
13109
  var init_clone_repo = __esm({
12923
13110
  "src/git/clone-repo.ts"() {
12924
13111
  "use strict";
12925
- import_node_fs32 = require("fs");
12926
- import_node_path32 = require("path");
13112
+ import_node_fs33 = require("fs");
13113
+ import_node_path33 = require("path");
12927
13114
  import_execa6 = require("execa");
12928
13115
  init_paths();
12929
13116
  init_workspaces2();
@@ -12933,11 +13120,11 @@ var init_clone_repo = __esm({
12933
13120
 
12934
13121
  // src/store/desktop-host.ts
12935
13122
  function desktopHostPidPath() {
12936
- return (0, import_node_path33.join)(appDataDir(), "desktop-host.pid");
13123
+ return (0, import_node_path34.join)(appDataDir(), "desktop-host.pid");
12937
13124
  }
12938
13125
  function readDesktopHostPid() {
12939
13126
  try {
12940
- const pid = Number.parseInt((0, import_node_fs33.readFileSync)(desktopHostPidPath(), "utf8").trim(), 10);
13127
+ const pid = Number.parseInt((0, import_node_fs34.readFileSync)(desktopHostPidPath(), "utf8").trim(), 10);
12941
13128
  if (!Number.isFinite(pid) || pid <= 0) return null;
12942
13129
  return pid;
12943
13130
  } catch {
@@ -12963,12 +13150,12 @@ function thisProcessShouldDrainAgentQueues() {
12963
13150
  if (isThisProcessDesktopHost()) return true;
12964
13151
  return !isDesktopHostAlive();
12965
13152
  }
12966
- var import_node_fs33, import_node_path33;
13153
+ var import_node_fs34, import_node_path34;
12967
13154
  var init_desktop_host = __esm({
12968
13155
  "src/store/desktop-host.ts"() {
12969
13156
  "use strict";
12970
- import_node_fs33 = require("fs");
12971
- import_node_path33 = require("path");
13157
+ import_node_fs34 = require("fs");
13158
+ import_node_path34 = require("path");
12972
13159
  init_paths();
12973
13160
  }
12974
13161
  });
@@ -13970,7 +14157,7 @@ function persistCreateAttachments(worktreePath, attachments) {
13970
14157
  }
13971
14158
  async function createThread(input, _onSetupLine) {
13972
14159
  const repoPath = await resolveRepoRoot(input.repoPath);
13973
- if (!(0, import_node_fs34.existsSync)(repoPath)) {
14160
+ if (!(0, import_node_fs35.existsSync)(repoPath)) {
13974
14161
  throw new Error(`Repo not found: ${repoPath}`);
13975
14162
  }
13976
14163
  if (input.reuseExisting !== false) {
@@ -14145,11 +14332,11 @@ async function listLinearIssues(agent, repoPath) {
14145
14332
  }
14146
14333
  return adapter.listLinearIssues(repoPath);
14147
14334
  }
14148
- var import_node_fs34;
14335
+ var import_node_fs35;
14149
14336
  var init_create = __esm({
14150
14337
  "src/threads/create.ts"() {
14151
14338
  "use strict";
14152
- import_node_fs34 = require("fs");
14339
+ import_node_fs35 = require("fs");
14153
14340
  init_detect();
14154
14341
  init_worktree();
14155
14342
  init_home_board();
@@ -14252,20 +14439,20 @@ function writeTurnLive(threadId, progress) {
14252
14439
  const path = threadLivePath(threadId);
14253
14440
  const tmp = `${path}.${process.pid}.tmp`;
14254
14441
  try {
14255
- (0, import_node_fs35.writeFileSync)(tmp, JSON.stringify(progress), "utf8");
14256
- (0, import_node_fs35.renameSync)(tmp, path);
14442
+ (0, import_node_fs36.writeFileSync)(tmp, JSON.stringify(progress), "utf8");
14443
+ (0, import_node_fs36.renameSync)(tmp, path);
14257
14444
  } catch {
14258
14445
  try {
14259
- (0, import_node_fs35.unlinkSync)(tmp);
14446
+ (0, import_node_fs36.unlinkSync)(tmp);
14260
14447
  } catch {
14261
14448
  }
14262
14449
  }
14263
14450
  }
14264
14451
  function readTurnLive(threadId) {
14265
14452
  const path = threadLivePath(threadId);
14266
- if (!(0, import_node_fs35.existsSync)(path)) return null;
14453
+ if (!(0, import_node_fs36.existsSync)(path)) return null;
14267
14454
  try {
14268
- const raw = JSON.parse((0, import_node_fs35.readFileSync)(path, "utf8"));
14455
+ const raw = JSON.parse((0, import_node_fs36.readFileSync)(path, "utf8"));
14269
14456
  if (!raw || typeof raw.summary !== "string") return null;
14270
14457
  return raw;
14271
14458
  } catch {
@@ -14277,17 +14464,17 @@ function clearTurnLive(threadId) {
14277
14464
  if (buf?.timer) clearTimeout(buf.timer);
14278
14465
  buffers.delete(threadId);
14279
14466
  const path = threadLivePath(threadId);
14280
- if (!(0, import_node_fs35.existsSync)(path)) return;
14467
+ if (!(0, import_node_fs36.existsSync)(path)) return;
14281
14468
  try {
14282
- (0, import_node_fs35.unlinkSync)(path);
14469
+ (0, import_node_fs36.unlinkSync)(path);
14283
14470
  } catch {
14284
14471
  }
14285
14472
  }
14286
- var import_node_fs35, buffers, FLUSH_MS, MAX_PARTS;
14473
+ var import_node_fs36, buffers, FLUSH_MS, MAX_PARTS;
14287
14474
  var init_turn_live = __esm({
14288
14475
  "src/store/turn-live.ts"() {
14289
14476
  "use strict";
14290
- import_node_fs35 = require("fs");
14477
+ import_node_fs36 = require("fs");
14291
14478
  init_message_parts();
14292
14479
  init_paths();
14293
14480
  buffers = /* @__PURE__ */ new Map();
@@ -14461,7 +14648,7 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
14461
14648
  `- Do not wait on the limited ${from.agent} account; keep going on ${fallbackAgent}.`
14462
14649
  ].join("\n");
14463
14650
  return {
14464
- id: (0, import_node_crypto7.randomUUID)(),
14651
+ id: (0, import_node_crypto8.randomUUID)(),
14465
14652
  name: "Orchestration quota handoff.md",
14466
14653
  kind: "transcript",
14467
14654
  content: body
@@ -14482,11 +14669,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
14482
14669
  sourceType: "orchestration"
14483
14670
  });
14484
14671
  }
14485
- var import_node_crypto7, QUOTA_CONTINUE_PROMPT, QUOTA_RESUME_PROMPT;
14672
+ var import_node_crypto8, QUOTA_CONTINUE_PROMPT, QUOTA_RESUME_PROMPT;
14486
14673
  var init_quota_failover = __esm({
14487
14674
  "src/orchestrator/quota-failover.ts"() {
14488
14675
  "use strict";
14489
- import_node_crypto7 = require("crypto");
14676
+ import_node_crypto8 = require("crypto");
14490
14677
  init_session_quota();
14491
14678
  init_app_settings();
14492
14679
  init_global_workspace();
@@ -14503,7 +14690,7 @@ var init_quota_failover = __esm({
14503
14690
  // src/threads/adopt.ts
14504
14691
  function thisModuleFile() {
14505
14692
  const cjsFile = typeof __filename !== "undefined" ? __filename : "";
14506
- return cjsFile || process.argv[1] || (0, import_node_path34.join)(process.cwd(), "package.json");
14693
+ return cjsFile || process.argv[1] || (0, import_node_path35.join)(process.cwd(), "package.json");
14507
14694
  }
14508
14695
  function openReadonlySqlite(file) {
14509
14696
  const req = (0, import_node_module4.createRequire)(thisModuleFile());
@@ -14521,21 +14708,21 @@ function mapAgentType(raw) {
14521
14708
  return null;
14522
14709
  }
14523
14710
  function resolveConductorCursorAgentId(workspacePath) {
14524
- if (!workspacePath || !(0, import_node_fs36.existsSync)(CURSOR_SDK_STORE)) return null;
14711
+ if (!workspacePath || !(0, import_node_fs37.existsSync)(CURSOR_SDK_STORE)) return null;
14525
14712
  const normalized = workspacePath.replace(/\/$/, "");
14526
14713
  let best = null;
14527
14714
  let hashes;
14528
14715
  try {
14529
- hashes = (0, import_node_fs36.readdirSync)(CURSOR_SDK_STORE);
14716
+ hashes = (0, import_node_fs37.readdirSync)(CURSOR_SDK_STORE);
14530
14717
  } catch {
14531
14718
  return null;
14532
14719
  }
14533
14720
  for (const hash of hashes) {
14534
- const agentsFile = (0, import_node_path34.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
14535
- if (!(0, import_node_fs36.existsSync)(agentsFile)) continue;
14721
+ const agentsFile = (0, import_node_path35.join)(CURSOR_SDK_STORE, hash, "agents.ndjson");
14722
+ if (!(0, import_node_fs37.existsSync)(agentsFile)) continue;
14536
14723
  let text5;
14537
14724
  try {
14538
- text5 = (0, import_node_fs36.readFileSync)(agentsFile, "utf8");
14725
+ text5 = (0, import_node_fs37.readFileSync)(agentsFile, "utf8");
14539
14726
  } catch {
14540
14727
  continue;
14541
14728
  }
@@ -14559,7 +14746,7 @@ function resolveConductorCursorAgentId(workspacePath) {
14559
14746
  return best?.agentId ?? null;
14560
14747
  }
14561
14748
  async function adoptThread(input) {
14562
- if (!(0, import_node_fs36.existsSync)(input.worktreePath)) {
14749
+ if (!(0, import_node_fs37.existsSync)(input.worktreePath)) {
14563
14750
  throw new Error(`Worktree not found: ${input.worktreePath}`);
14564
14751
  }
14565
14752
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -14580,21 +14767,23 @@ async function adoptThread(input) {
14580
14767
  writeThread(thread);
14581
14768
  const { ensureWorkspace: ensureWorkspace2 } = await Promise.resolve().then(() => (init_workspaces2(), workspaces_exports));
14582
14769
  await ensureWorkspace2(repoPath);
14770
+ const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await Promise.resolve().then(() => (init_worktree_exclude(), worktree_exclude_exports));
14771
+ await ensureWorktreeSideboardIgnored2(input.worktreePath);
14583
14772
  return thread;
14584
14773
  }
14585
14774
  function listConductorWorkspaces() {
14586
- if (!(0, import_node_fs36.existsSync)(CONDUCTOR_DB)) {
14775
+ if (!(0, import_node_fs37.existsSync)(CONDUCTOR_DB)) {
14587
14776
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
14588
14777
  }
14589
- const tmp = (0, import_node_fs36.mkdtempSync)((0, import_node_path34.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
14590
- const snapshot = (0, import_node_path34.join)(tmp, "conductor.db");
14778
+ const tmp = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0, import_node_os11.tmpdir)(), "sideboard-conductor-"));
14779
+ const snapshot = (0, import_node_path35.join)(tmp, "conductor.db");
14591
14780
  try {
14592
- (0, import_node_fs36.copyFileSync)(CONDUCTOR_DB, snapshot);
14781
+ (0, import_node_fs37.copyFileSync)(CONDUCTOR_DB, snapshot);
14593
14782
  for (const suffix of ["-wal", "-shm"]) {
14594
14783
  const src = `${CONDUCTOR_DB}${suffix}`;
14595
- if ((0, import_node_fs36.existsSync)(src)) {
14784
+ if ((0, import_node_fs37.existsSync)(src)) {
14596
14785
  try {
14597
- (0, import_node_fs36.copyFileSync)(src, `${snapshot}${suffix}`);
14786
+ (0, import_node_fs37.copyFileSync)(src, `${snapshot}${suffix}`);
14598
14787
  } catch {
14599
14788
  }
14600
14789
  }
@@ -14670,22 +14859,22 @@ function listConductorWorkspaces() {
14670
14859
  db.close();
14671
14860
  }
14672
14861
  } finally {
14673
- (0, import_node_fs36.rmSync)(tmp, { recursive: true, force: true });
14862
+ (0, import_node_fs37.rmSync)(tmp, { recursive: true, force: true });
14674
14863
  }
14675
14864
  }
14676
14865
  function importConductorWorkspace(workspaceId) {
14677
- if (!(0, import_node_fs36.existsSync)(CONDUCTOR_DB)) {
14866
+ if (!(0, import_node_fs37.existsSync)(CONDUCTOR_DB)) {
14678
14867
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
14679
14868
  }
14680
- const tmp = (0, import_node_fs36.mkdtempSync)((0, import_node_path34.join)((0, import_node_os10.tmpdir)(), "sideboard-conductor-"));
14681
- const snapshot = (0, import_node_path34.join)(tmp, "conductor.db");
14869
+ const tmp = (0, import_node_fs37.mkdtempSync)((0, import_node_path35.join)((0, import_node_os11.tmpdir)(), "sideboard-conductor-"));
14870
+ const snapshot = (0, import_node_path35.join)(tmp, "conductor.db");
14682
14871
  try {
14683
- (0, import_node_fs36.copyFileSync)(CONDUCTOR_DB, snapshot);
14872
+ (0, import_node_fs37.copyFileSync)(CONDUCTOR_DB, snapshot);
14684
14873
  for (const suffix of ["-wal", "-shm"]) {
14685
14874
  const src = `${CONDUCTOR_DB}${suffix}`;
14686
- if ((0, import_node_fs36.existsSync)(src)) {
14875
+ if ((0, import_node_fs37.existsSync)(src)) {
14687
14876
  try {
14688
- (0, import_node_fs36.copyFileSync)(src, `${snapshot}${suffix}`);
14877
+ (0, import_node_fs37.copyFileSync)(src, `${snapshot}${suffix}`);
14689
14878
  } catch {
14690
14879
  }
14691
14880
  }
@@ -14703,7 +14892,7 @@ function importConductorWorkspace(workspaceId) {
14703
14892
  ).get(workspaceId);
14704
14893
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
14705
14894
  const worktreePath = String(row.workspacePath);
14706
- if (!(0, import_node_fs36.existsSync)(worktreePath)) {
14895
+ if (!(0, import_node_fs37.existsSync)(worktreePath)) {
14707
14896
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
14708
14897
  }
14709
14898
  let sessionId = null;
@@ -14766,31 +14955,31 @@ function importConductorWorkspace(workspaceId) {
14766
14955
  db.close();
14767
14956
  }
14768
14957
  } finally {
14769
- (0, import_node_fs36.rmSync)(tmp, { recursive: true, force: true });
14958
+ (0, import_node_fs37.rmSync)(tmp, { recursive: true, force: true });
14770
14959
  }
14771
14960
  }
14772
14961
  async function importConductorWorkspaceAsync(workspaceId) {
14773
14962
  return importConductorWorkspace(workspaceId);
14774
14963
  }
14775
- var import_node_child_process3, import_node_fs36, import_node_os10, import_node_path34, import_node_module4, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
14964
+ var import_node_child_process3, import_node_fs37, import_node_os11, import_node_path35, import_node_module4, CONDUCTOR_APP_SUPPORT, CONDUCTOR_DB, CURSOR_SDK_STORE;
14776
14965
  var init_adopt = __esm({
14777
14966
  "src/threads/adopt.ts"() {
14778
14967
  "use strict";
14779
14968
  import_node_child_process3 = require("child_process");
14780
- import_node_fs36 = require("fs");
14781
- import_node_os10 = require("os");
14782
- import_node_path34 = require("path");
14969
+ import_node_fs37 = require("fs");
14970
+ import_node_os11 = require("os");
14971
+ import_node_path35 = require("path");
14783
14972
  import_node_module4 = require("module");
14784
14973
  init_worktree();
14785
14974
  init_thread_store();
14786
- CONDUCTOR_APP_SUPPORT = (0, import_node_path34.join)(
14975
+ CONDUCTOR_APP_SUPPORT = (0, import_node_path35.join)(
14787
14976
  process.env.HOME ?? "",
14788
14977
  "Library",
14789
14978
  "Application Support",
14790
14979
  "com.conductor.app"
14791
14980
  );
14792
- CONDUCTOR_DB = (0, import_node_path34.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
14793
- CURSOR_SDK_STORE = (0, import_node_path34.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
14981
+ CONDUCTOR_DB = (0, import_node_path35.join)(CONDUCTOR_APP_SUPPORT, "conductor.db");
14982
+ CURSOR_SDK_STORE = (0, import_node_path35.join)(CONDUCTOR_APP_SUPPORT, "cursor-sdk-store");
14794
14983
  }
14795
14984
  });
14796
14985
 
@@ -14857,7 +15046,7 @@ async function openStackLayer(input, _onSetupLine) {
14857
15046
  let createdWorktree = false;
14858
15047
  const trees = await listWorktrees(repoPath);
14859
15048
  const checkedOut = trees.find((w) => w.branch === branchName);
14860
- if (checkedOut?.path && (0, import_node_fs37.existsSync)(checkedOut.path)) {
15049
+ if (checkedOut?.path && (0, import_node_fs38.existsSync)(checkedOut.path)) {
14861
15050
  if (input.reuseExistingWorktree !== false) {
14862
15051
  worktreePath = checkedOut.path;
14863
15052
  } else {
@@ -14999,7 +15188,7 @@ async function initStackFromThread(input, onSetupLine) {
14999
15188
  async function createPrStack(input, onSetupLine) {
15000
15189
  await requireAgent(input.agent);
15001
15190
  const repoPath = await resolveRepoRoot(input.repoPath);
15002
- if (!(0, import_node_fs37.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
15191
+ if (!(0, import_node_fs38.existsSync)(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
15003
15192
  if (!input.branches.length) throw new Error("At least one branch name required");
15004
15193
  const status = await detectGhStack(repoPath);
15005
15194
  if (!status.available) throw new Error(status.reason);
@@ -15066,7 +15255,7 @@ async function createPrStack(input, onSetupLine) {
15066
15255
  }
15067
15256
  }
15068
15257
  const claimed = new Set(threads.map((t) => t.worktreePath));
15069
- if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs37.existsSync)(bootstrap.worktreePath)) {
15258
+ if (!claimed.has(bootstrap.worktreePath) && (0, import_node_fs38.existsSync)(bootstrap.worktreePath)) {
15070
15259
  try {
15071
15260
  await removeWorktree(repoPath, bootstrap.worktreePath, {
15072
15261
  deleteBranch: bootstrap.branchName
@@ -15076,11 +15265,11 @@ async function createPrStack(input, onSetupLine) {
15076
15265
  }
15077
15266
  return { stack, threads, createdThreadIds };
15078
15267
  }
15079
- var import_node_fs37;
15268
+ var import_node_fs38;
15080
15269
  var init_stack_layers = __esm({
15081
15270
  "src/threads/stack-layers.ts"() {
15082
15271
  "use strict";
15083
- import_node_fs37 = require("fs");
15272
+ import_node_fs38 = require("fs");
15084
15273
  init_detect();
15085
15274
  init_run();
15086
15275
  init_stack();
@@ -15093,7 +15282,7 @@ var init_stack_layers = __esm({
15093
15282
 
15094
15283
  // src/diff/diff.ts
15095
15284
  async function inspectGitWorktree(worktreePath) {
15096
- if (!worktreePath || !(0, import_node_fs38.existsSync)(worktreePath)) return "missing_worktree";
15285
+ if (!worktreePath || !(0, import_node_fs39.existsSync)(worktreePath)) return "missing_worktree";
15097
15286
  const check = await git(["rev-parse", "--is-inside-work-tree"], worktreePath, {
15098
15287
  reject: false
15099
15288
  });
@@ -15101,7 +15290,7 @@ async function inspectGitWorktree(worktreePath) {
15101
15290
  return "ok";
15102
15291
  }
15103
15292
  async function initializeGitRepository(worktreePath) {
15104
- if (!worktreePath || !(0, import_node_fs38.existsSync)(worktreePath)) {
15293
+ if (!worktreePath || !(0, import_node_fs39.existsSync)(worktreePath)) {
15105
15294
  throw new Error("Worktree not found");
15106
15295
  }
15107
15296
  const status = await inspectGitWorktree(worktreePath);
@@ -15235,11 +15424,11 @@ new file mode 100644
15235
15424
  };
15236
15425
  }
15237
15426
  async function untrackedPatch(worktreePath, path, maxHunk) {
15238
- const abs = (0, import_node_path35.join)(worktreePath, path);
15427
+ const abs = (0, import_node_path36.join)(worktreePath, path);
15239
15428
  try {
15240
- const st = (0, import_node_fs38.statSync)(abs);
15429
+ const st = (0, import_node_fs39.statSync)(abs);
15241
15430
  if (st.isFile() && st.size > maxHunk) {
15242
- const buf = (0, import_node_fs38.readFileSync)(abs).subarray(0, maxHunk);
15431
+ const buf = (0, import_node_fs39.readFileSync)(abs).subarray(0, maxHunk);
15243
15432
  return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
15244
15433
  }
15245
15434
  } catch {
@@ -15728,8 +15917,8 @@ function isImageRelativePath(relativePath) {
15728
15917
  function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
15729
15918
  assertSafeRelativePath(relativePath);
15730
15919
  const maxBytes = opts?.maxBytes ?? DEFAULT_UPLOAD_MAX_BYTES;
15731
- const abs = (0, import_node_path35.join)(worktreePath, relativePath);
15732
- const st = (0, import_node_fs38.statSync)(abs);
15920
+ const abs = (0, import_node_path36.join)(worktreePath, relativePath);
15921
+ const st = (0, import_node_fs39.statSync)(abs);
15733
15922
  if (!st.isFile()) {
15734
15923
  throw new Error(`Not a file: ${relativePath}`);
15735
15924
  }
@@ -15738,7 +15927,7 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
15738
15927
  `File too large to upload (${st.size} bytes; max ${maxBytes})`
15739
15928
  );
15740
15929
  }
15741
- const buf = (0, import_node_fs38.readFileSync)(abs);
15930
+ const buf = (0, import_node_fs39.readFileSync)(abs);
15742
15931
  return {
15743
15932
  path: relativePath,
15744
15933
  contentBase64: buf.toString("base64"),
@@ -15748,12 +15937,12 @@ function readWorktreeFileForUpload(worktreePath, relativePath, opts) {
15748
15937
  function readWorktreeFile(worktreePath, relativePath, opts) {
15749
15938
  assertSafeRelativePath(relativePath);
15750
15939
  const maxBytes = opts?.maxBytes ?? 2e5;
15751
- const abs = (0, import_node_path35.join)(worktreePath, relativePath);
15752
- const st = (0, import_node_fs38.statSync)(abs);
15940
+ const abs = (0, import_node_path36.join)(worktreePath, relativePath);
15941
+ const st = (0, import_node_fs39.statSync)(abs);
15753
15942
  if (!st.isFile()) {
15754
15943
  throw new Error(`Not a file: ${relativePath}`);
15755
15944
  }
15756
- const buf = (0, import_node_fs38.readFileSync)(abs);
15945
+ const buf = (0, import_node_fs39.readFileSync)(abs);
15757
15946
  if (isImageRelativePath(relativePath)) {
15758
15947
  const maxImageBytes = Math.max(maxBytes, 15e6);
15759
15948
  const truncated2 = buf.length > maxImageBytes;
@@ -15796,9 +15985,9 @@ function assertSafeRelativePath(relativePath) {
15796
15985
  }
15797
15986
  function writeWorktreeFile(worktreePath, relativePath, content) {
15798
15987
  assertSafeRelativePath(relativePath);
15799
- const abs = (0, import_node_path35.join)(worktreePath, relativePath);
15800
- (0, import_node_fs38.mkdirSync)((0, import_node_path35.dirname)(abs), { recursive: true });
15801
- (0, import_node_fs38.writeFileSync)(abs, content, "utf8");
15988
+ const abs = (0, import_node_path36.join)(worktreePath, relativePath);
15989
+ (0, import_node_fs39.mkdirSync)((0, import_node_path36.dirname)(abs), { recursive: true });
15990
+ (0, import_node_fs39.writeFileSync)(abs, content, "utf8");
15802
15991
  return { path: relativePath };
15803
15992
  }
15804
15993
  async function getDiffSummary(worktreePath, repoPath, opts) {
@@ -15815,12 +16004,12 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
15815
16004
  truncated: full.files.length > maxFiles
15816
16005
  };
15817
16006
  }
15818
- var import_node_fs38, import_node_path35, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
16007
+ var import_node_fs39, import_node_path36, mergeBaseCache, MERGE_BASE_TTL_MS, SHA_RE, IMAGE_EXTENSIONS2, DEFAULT_UPLOAD_MAX_BYTES;
15819
16008
  var init_diff = __esm({
15820
16009
  "src/diff/diff.ts"() {
15821
16010
  "use strict";
15822
- import_node_fs38 = require("fs");
15823
- import_node_path35 = require("path");
16011
+ import_node_fs39 = require("fs");
16012
+ import_node_path36 = require("path");
15824
16013
  init_run();
15825
16014
  init_worktree();
15826
16015
  mergeBaseCache = /* @__PURE__ */ new Map();
@@ -16078,7 +16267,7 @@ function parseFrontmatter(content) {
16078
16267
  }
16079
16268
  function readSkill(skillMd, source) {
16080
16269
  try {
16081
- const content = (0, import_node_fs39.readFileSync)(skillMd, "utf8");
16270
+ const content = (0, import_node_fs40.readFileSync)(skillMd, "utf8");
16082
16271
  const { name: fmName, description } = parseFrontmatter(content);
16083
16272
  const dirName = skillMd.split("/").slice(-2, -1)[0] || "skill";
16084
16273
  const name = fmName || dirName;
@@ -16097,19 +16286,19 @@ function readSkill(skillMd, source) {
16097
16286
  }
16098
16287
  }
16099
16288
  function scanSkillsDir(dir, source, out) {
16100
- if (!(0, import_node_fs39.existsSync)(dir)) return;
16289
+ if (!(0, import_node_fs40.existsSync)(dir)) return;
16101
16290
  let entries;
16102
16291
  try {
16103
- entries = (0, import_node_fs39.readdirSync)(dir);
16292
+ entries = (0, import_node_fs40.readdirSync)(dir);
16104
16293
  } catch {
16105
16294
  return;
16106
16295
  }
16107
16296
  for (const entry of entries) {
16108
16297
  if (entry.startsWith(".")) continue;
16109
- const skillMd = (0, import_node_path36.join)(dir, entry, "SKILL.md");
16110
- if (!(0, import_node_fs39.existsSync)(skillMd)) continue;
16298
+ const skillMd = (0, import_node_path37.join)(dir, entry, "SKILL.md");
16299
+ if (!(0, import_node_fs40.existsSync)(skillMd)) continue;
16111
16300
  try {
16112
- if (!(0, import_node_fs39.statSync)(skillMd).isFile()) continue;
16301
+ if (!(0, import_node_fs40.statSync)(skillMd).isFile()) continue;
16113
16302
  } catch {
16114
16303
  continue;
16115
16304
  }
@@ -16118,24 +16307,24 @@ function scanSkillsDir(dir, source, out) {
16118
16307
  }
16119
16308
  }
16120
16309
  function scanClaudePluginSkills(pluginsRoot, out) {
16121
- if (!(0, import_node_fs39.existsSync)(pluginsRoot)) return;
16310
+ if (!(0, import_node_fs40.existsSync)(pluginsRoot)) return;
16122
16311
  const walk = (dir, depth, lookingForSkillsDir) => {
16123
16312
  if (depth > 7) return;
16124
16313
  let entries;
16125
16314
  try {
16126
- entries = (0, import_node_fs39.readdirSync)(dir);
16315
+ entries = (0, import_node_fs40.readdirSync)(dir);
16127
16316
  } catch {
16128
16317
  return;
16129
16318
  }
16130
16319
  if (lookingForSkillsDir && entries.includes("SKILL.md")) {
16131
- const skill = readSkill((0, import_node_path36.join)(dir, "SKILL.md"), "cli");
16320
+ const skill = readSkill((0, import_node_path37.join)(dir, "SKILL.md"), "cli");
16132
16321
  if (skill) out.push(skill);
16133
16322
  }
16134
16323
  for (const entry of entries) {
16135
16324
  if (entry === "node_modules" || entry === ".git") continue;
16136
- const full = (0, import_node_path36.join)(dir, entry);
16325
+ const full = (0, import_node_path37.join)(dir, entry);
16137
16326
  try {
16138
- if (!(0, import_node_fs39.statSync)(full).isDirectory()) continue;
16327
+ if (!(0, import_node_fs40.statSync)(full).isDirectory()) continue;
16139
16328
  } catch {
16140
16329
  continue;
16141
16330
  }
@@ -16150,20 +16339,20 @@ function scanClaudePluginSkills(pluginsRoot, out) {
16150
16339
  walk(pluginsRoot, 0, false);
16151
16340
  }
16152
16341
  function discoverSkills(worktreePath) {
16153
- const home = (0, import_node_os11.homedir)();
16342
+ const home = (0, import_node_os12.homedir)();
16154
16343
  const collected = [];
16155
16344
  for (const rel of [".claude/skills", ".cursor/skills", ".sideboard/skills", ".brightsy/skills", "skills"]) {
16156
- scanSkillsDir((0, import_node_path36.join)(worktreePath, rel), "workspace", collected);
16345
+ scanSkillsDir((0, import_node_path37.join)(worktreePath, rel), "workspace", collected);
16157
16346
  }
16158
16347
  for (const abs of [
16159
- (0, import_node_path36.join)(home, ".claude/skills"),
16160
- (0, import_node_path36.join)(home, ".cursor/skills"),
16161
- (0, import_node_path36.join)(home, ".sideboard/skills"),
16162
- (0, import_node_path36.join)(home, ".brightsy/skills")
16348
+ (0, import_node_path37.join)(home, ".claude/skills"),
16349
+ (0, import_node_path37.join)(home, ".cursor/skills"),
16350
+ (0, import_node_path37.join)(home, ".sideboard/skills"),
16351
+ (0, import_node_path37.join)(home, ".brightsy/skills")
16163
16352
  ]) {
16164
16353
  scanSkillsDir(abs, "user", collected);
16165
16354
  }
16166
- scanClaudePluginSkills((0, import_node_path36.join)(home, ".claude/plugins"), collected);
16355
+ scanClaudePluginSkills((0, import_node_path37.join)(home, ".claude/plugins"), collected);
16167
16356
  collected.push(...bundledSkills());
16168
16357
  const rank = {
16169
16358
  workspace: 0,
@@ -16189,7 +16378,7 @@ function readSkillBody(skillPath, maxChars = 12e3) {
16189
16378
  \u2026(truncated)` : body;
16190
16379
  }
16191
16380
  }
16192
- const raw = (0, import_node_fs39.readFileSync)(skillPath, "utf8");
16381
+ const raw = (0, import_node_fs40.readFileSync)(skillPath, "utf8");
16193
16382
  if (raw.startsWith("---")) {
16194
16383
  const end = raw.indexOf("\n---", 3);
16195
16384
  if (end >= 0) {
@@ -16203,13 +16392,13 @@ function readSkillBody(skillPath, maxChars = 12e3) {
16203
16392
 
16204
16393
  \u2026(truncated)` : raw;
16205
16394
  }
16206
- var import_node_fs39, import_node_os11, import_node_path36, BUNDLED_SKILL_PREFIX;
16395
+ var import_node_fs40, import_node_os12, import_node_path37, BUNDLED_SKILL_PREFIX;
16207
16396
  var init_discover = __esm({
16208
16397
  "src/skills/discover.ts"() {
16209
16398
  "use strict";
16210
- import_node_fs39 = require("fs");
16211
- import_node_os11 = require("os");
16212
- import_node_path36 = require("path");
16399
+ import_node_fs40 = require("fs");
16400
+ import_node_os12 = require("os");
16401
+ import_node_path37 = require("path");
16213
16402
  init_long_running();
16214
16403
  BUNDLED_SKILL_PREFIX = "bundled:";
16215
16404
  }
@@ -16304,17 +16493,17 @@ var init_expand = __esm({
16304
16493
  function packagedDetachedJobPath() {
16305
16494
  const dir = packagedMcpDir();
16306
16495
  if (!dir) return null;
16307
- const script = (0, import_node_path37.join)(dir, "scripts", "detached-job.js");
16308
- return (0, import_node_fs40.existsSync)(script) ? script : null;
16496
+ const script = (0, import_node_path38.join)(dir, "scripts", "detached-job.js");
16497
+ return (0, import_node_fs41.existsSync)(script) ? script : null;
16309
16498
  }
16310
16499
  function resolveDetachedJobScript() {
16311
16500
  const packaged = packagedDetachedJobPath();
16312
16501
  if (packaged) return packaged;
16313
- let dir = (0, import_node_path37.dirname)((0, import_node_url3.fileURLToPath)(import_meta3.url));
16502
+ let dir = (0, import_node_path38.dirname)((0, import_node_url3.fileURLToPath)(import_meta3.url));
16314
16503
  for (let i = 0; i < 8; i++) {
16315
- const candidate = (0, import_node_path37.join)(dir, "scripts", "detached-job.js");
16316
- if ((0, import_node_fs40.existsSync)(candidate)) return candidate;
16317
- const parent = (0, import_node_path37.dirname)(dir);
16504
+ const candidate = (0, import_node_path38.join)(dir, "scripts", "detached-job.js");
16505
+ if ((0, import_node_fs41.existsSync)(candidate)) return candidate;
16506
+ const parent = (0, import_node_path38.dirname)(dir);
16318
16507
  if (parent === dir) break;
16319
16508
  dir = parent;
16320
16509
  }
@@ -16325,12 +16514,12 @@ function formatDetachedJobInvoke(scriptPath) {
16325
16514
  if (resolved) return `node ${JSON.stringify(resolved)}`;
16326
16515
  return "node scripts/detached-job.js";
16327
16516
  }
16328
- var import_node_fs40, import_node_path37, import_node_url3, import_meta3;
16517
+ var import_node_fs41, import_node_path38, import_node_url3, import_meta3;
16329
16518
  var init_detached_job_path = __esm({
16330
16519
  "src/skills/detached-job-path.ts"() {
16331
16520
  "use strict";
16332
- import_node_fs40 = require("fs");
16333
- import_node_path37 = require("path");
16521
+ import_node_fs41 = require("fs");
16522
+ import_node_path38 = require("path");
16334
16523
  import_node_url3 = require("url");
16335
16524
  init_packaged_runtime();
16336
16525
  import_meta3 = {};
@@ -16403,11 +16592,11 @@ function formatWorktreeDirective(thread, opts) {
16403
16592
  );
16404
16593
  } else if (opts?.githubSlug) {
16405
16594
  lines.push(
16406
- `- Prefer a draft PR first: \`gh pr create --draft -R ${opts.githubSlug}\` (or update via \`gh pr edit -R ${opts.githubSlug}\`) once the change set is coherent. Always pass \`-R ${opts.githubSlug}\` (this worktree's origin). Bare \`gh pr create\` may target upstream instead of origin on dual-remote checkouts. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name.`
16595
+ `- Prefer a draft PR first: \`gh pr create --draft -R ${opts.githubSlug}\` (or update via \`gh pr edit -R ${opts.githubSlug}\`) once the change set is coherent. Always pass \`-R ${opts.githubSlug}\` (this worktree's origin). Bare \`gh pr create\` may target upstream instead of origin on dual-remote checkouts. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name. If GitHub rejects the GraphQL body as too long, retry with a short \`--body-file\` (limit 65,536 characters) \u2014 do not paste a changelog or diff into \`--body\`.`
16407
16596
  );
16408
16597
  } else {
16409
16598
  lines.push(
16410
- "- Prefer a draft PR first: `gh pr create --draft -R <origin-owner/name>` (or update via `gh pr edit -R \u2026`) once the change set is coherent. Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree \u2014 never from `upstream`. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name."
16599
+ "- Prefer a draft PR first: `gh pr create --draft -R <origin-owner/name>` (or update via `gh pr edit -R \u2026`) once the change set is coherent. Resolve `<origin-owner/name>` with `git remote get-url origin` in this worktree \u2014 never from `upstream`. Mark ready for review only when asked. Title/body must reflect the change purpose, not the worktree name. If GitHub rejects the GraphQL body as too long, retry with a short `--body-file` (limit 65,536 characters) \u2014 do not paste a changelog or diff into `--body`."
16411
16600
  );
16412
16601
  }
16413
16602
  lines.push("");
@@ -16498,12 +16687,12 @@ function formatArtifactDirective() {
16498
16687
  function formatUiReminder() {
16499
16688
  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). ask_user only for a real multiple-choice (not hellos or \u201Cwhat next?\u201D) \u2014 reply in chat. Do not say artifacts/CMS UI are unavailable.";
16500
16689
  }
16501
- var import_node_fs41, import_node_path38;
16690
+ var import_node_fs42, import_node_path39;
16502
16691
  var init_instructions = __esm({
16503
16692
  "src/agents/instructions.ts"() {
16504
16693
  "use strict";
16505
- import_node_fs41 = require("fs");
16506
- import_node_path38 = require("path");
16694
+ import_node_fs42 = require("fs");
16695
+ import_node_path39 = require("path");
16507
16696
  init_git_auth_mode();
16508
16697
  init_worktree_labels();
16509
16698
  init_detached_job_path();
@@ -16673,40 +16862,40 @@ __export(plan_file_exports, {
16673
16862
  writePlanFile: () => writePlanFile
16674
16863
  });
16675
16864
  function ensureAttachmentsGitignore(worktreePath) {
16676
- const gitignoreAbs = (0, import_node_path39.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
16677
- if ((0, import_node_fs42.existsSync)(gitignoreAbs)) return;
16678
- (0, import_node_fs42.mkdirSync)((0, import_node_path39.dirname)(gitignoreAbs), { recursive: true });
16679
- (0, import_node_fs42.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
16865
+ const gitignoreAbs = (0, import_node_path40.join)(worktreePath, ATTACHMENTS_DIR, ".gitignore");
16866
+ if ((0, import_node_fs43.existsSync)(gitignoreAbs)) return;
16867
+ (0, import_node_fs43.mkdirSync)((0, import_node_path40.dirname)(gitignoreAbs), { recursive: true });
16868
+ (0, import_node_fs43.writeFileSync)(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
16680
16869
  }
16681
16870
  function planFileAbs(worktreePath) {
16682
- return (0, import_node_path39.join)(worktreePath, PLAN_FILE_REL);
16871
+ return (0, import_node_path40.join)(worktreePath, PLAN_FILE_REL);
16683
16872
  }
16684
16873
  function readTextIfPresent2(abs) {
16685
- if (!(0, import_node_fs42.existsSync)(abs)) return null;
16874
+ if (!(0, import_node_fs43.existsSync)(abs)) return null;
16686
16875
  try {
16687
- const content = (0, import_node_fs42.readFileSync)(abs, "utf8");
16876
+ const content = (0, import_node_fs43.readFileSync)(abs, "utf8");
16688
16877
  return content.trim() ? content : null;
16689
16878
  } catch {
16690
16879
  return null;
16691
16880
  }
16692
16881
  }
16693
16882
  function readPlanFile(worktreePath) {
16694
- return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path39.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path39.join)(worktreePath, LEGACY_PLAN_FILE_REL));
16883
+ return readTextIfPresent2(planFileAbs(worktreePath)) ?? readTextIfPresent2((0, import_node_path40.join)(worktreePath, `${LEGACY_ATTACHMENTS_DIR}/plan.md`)) ?? readTextIfPresent2((0, import_node_path40.join)(worktreePath, LEGACY_PLAN_FILE_REL));
16695
16884
  }
16696
16885
  function writePlanFile(worktreePath, content) {
16697
16886
  ensureAttachmentsGitignore(worktreePath);
16698
16887
  const abs = planFileAbs(worktreePath);
16699
- (0, import_node_fs42.mkdirSync)((0, import_node_path39.dirname)(abs), { recursive: true });
16888
+ (0, import_node_fs43.mkdirSync)((0, import_node_path40.dirname)(abs), { recursive: true });
16700
16889
  const body = content.trimEnd() + (content.endsWith("\n") ? "" : "\n");
16701
- (0, import_node_fs42.writeFileSync)(abs, body, "utf8");
16890
+ (0, import_node_fs43.writeFileSync)(abs, body, "utf8");
16702
16891
  return PLAN_FILE_REL;
16703
16892
  }
16704
- var import_node_fs42, import_node_path39;
16893
+ var import_node_fs43, import_node_path40;
16705
16894
  var init_plan_file = __esm({
16706
16895
  "src/plan/plan-file.ts"() {
16707
16896
  "use strict";
16708
- import_node_fs42 = require("fs");
16709
- import_node_path39 = require("path");
16897
+ import_node_fs43 = require("fs");
16898
+ import_node_path40 = require("path");
16710
16899
  init_workspace_scratch();
16711
16900
  init_plan_present();
16712
16901
  init_plan_present();
@@ -16766,7 +16955,7 @@ function setCaffeinateHoldHooks(next) {
16766
16955
  hooks = next;
16767
16956
  }
16768
16957
  function caffeinateHoldPath() {
16769
- return (0, import_node_path40.join)(appDataDir(), "caffeinate-hold.json");
16958
+ return (0, import_node_path41.join)(appDataDir(), "caffeinate-hold.json");
16770
16959
  }
16771
16960
  function processAlive(pid) {
16772
16961
  if (hooks.processAlive) return hooks.processAlive(pid);
@@ -16800,9 +16989,9 @@ function uniqueIds(ids) {
16800
16989
  }
16801
16990
  function readHold() {
16802
16991
  const path = caffeinateHoldPath();
16803
- if (!(0, import_node_fs43.existsSync)(path)) return null;
16992
+ if (!(0, import_node_fs44.existsSync)(path)) return null;
16804
16993
  try {
16805
- const parsed = JSON.parse((0, import_node_fs43.readFileSync)(path, "utf8"));
16994
+ const parsed = JSON.parse((0, import_node_fs44.readFileSync)(path, "utf8"));
16806
16995
  if (typeof parsed?.pid === "number" && parsed.pid > 0) {
16807
16996
  return {
16808
16997
  pid: parsed.pid,
@@ -16824,7 +17013,7 @@ function writeHold(pid, threadIds) {
16824
17013
  }
16825
17014
  function clearHold() {
16826
17015
  try {
16827
- (0, import_node_fs43.unlinkSync)(caffeinateHoldPath());
17016
+ (0, import_node_fs44.unlinkSync)(caffeinateHoldPath());
16828
17017
  } catch {
16829
17018
  }
16830
17019
  }
@@ -16909,13 +17098,13 @@ function releaseCaffeinateHoldForThread(threadId) {
16909
17098
  }
16910
17099
  return setCaffeinateHold(false, { threadId: id });
16911
17100
  }
16912
- var import_node_child_process4, import_node_fs43, import_node_path40, hooks;
17101
+ var import_node_child_process4, import_node_fs44, import_node_path41, hooks;
16913
17102
  var init_caffeinate_hold = __esm({
16914
17103
  "src/store/caffeinate-hold.ts"() {
16915
17104
  "use strict";
16916
17105
  import_node_child_process4 = require("child_process");
16917
- import_node_fs43 = require("fs");
16918
- import_node_path40 = require("path");
17106
+ import_node_fs44 = require("fs");
17107
+ import_node_path41 = require("path");
16919
17108
  init_paths();
16920
17109
  init_private_file();
16921
17110
  hooks = {};
@@ -16924,7 +17113,7 @@ var init_caffeinate_hold = __esm({
16924
17113
 
16925
17114
  // src/store/schedules.ts
16926
17115
  function schedulesPath() {
16927
- return (0, import_node_path41.join)(appDataDir(), "schedules.json");
17116
+ return (0, import_node_path42.join)(appDataDir(), "schedules.json");
16928
17117
  }
16929
17118
  function parseDurationMs(every) {
16930
17119
  const m = every.trim().match(DURATION_RE);
@@ -17024,9 +17213,9 @@ function normalizeTask(raw) {
17024
17213
  }
17025
17214
  function readAll2() {
17026
17215
  const path = schedulesPath();
17027
- if (!(0, import_node_fs44.existsSync)(path)) return [];
17216
+ if (!(0, import_node_fs45.existsSync)(path)) return [];
17028
17217
  try {
17029
- const parsed = JSON.parse((0, import_node_fs44.readFileSync)(path, "utf8"));
17218
+ const parsed = JSON.parse((0, import_node_fs45.readFileSync)(path, "utf8"));
17030
17219
  if (!Array.isArray(parsed)) return [];
17031
17220
  return parsed.map((row) => {
17032
17221
  try {
@@ -17051,7 +17240,7 @@ function buildTask(input, now) {
17051
17240
  const ts = nowIso2(now);
17052
17241
  const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
17053
17242
  return {
17054
- id: (0, import_node_crypto8.randomUUID)(),
17243
+ id: (0, import_node_crypto9.randomUUID)(),
17055
17244
  name: input.name?.trim() || defaultScheduleName(prompt),
17056
17245
  prompt,
17057
17246
  enabled: input.enabled !== false,
@@ -17138,13 +17327,13 @@ function recordScheduleRun(id, result) {
17138
17327
  writeAll2(rows);
17139
17328
  return next;
17140
17329
  }
17141
- var import_node_crypto8, import_node_fs44, import_node_path41, import_croner, DURATION_RE, UNIT_MS;
17330
+ var import_node_crypto9, import_node_fs45, import_node_path42, import_croner, DURATION_RE, UNIT_MS;
17142
17331
  var init_schedules = __esm({
17143
17332
  "src/store/schedules.ts"() {
17144
17333
  "use strict";
17145
- import_node_crypto8 = require("crypto");
17146
- import_node_fs44 = require("fs");
17147
- import_node_path41 = require("path");
17334
+ import_node_crypto9 = require("crypto");
17335
+ import_node_fs45 = require("fs");
17336
+ import_node_path42 = require("path");
17148
17337
  import_croner = require("croner");
17149
17338
  init_orchestrator_capable();
17150
17339
  init_paths();
@@ -17286,13 +17475,13 @@ var init_schedule_runner = __esm({
17286
17475
 
17287
17476
  // src/agents/cursor-store.ts
17288
17477
  function cursorSdkStoreDir(threadId) {
17289
- const root = (0, import_node_path42.join)(appDataDir(), CURSOR_SDK_STORE_DIR);
17478
+ const root = (0, import_node_path43.join)(appDataDir(), CURSOR_SDK_STORE_DIR);
17290
17479
  const id = sanitizeCursorStoreSegment(threadId);
17291
17480
  if (!id) return root;
17292
- return (0, import_node_path42.join)(root, "threads", id);
17481
+ return (0, import_node_path43.join)(root, "threads", id);
17293
17482
  }
17294
17483
  function cursorSdkRunsNdjsonPath(threadId) {
17295
- return (0, import_node_path42.join)(cursorSdkStoreDir(threadId), "runs.ndjson");
17484
+ return (0, import_node_path43.join)(cursorSdkStoreDir(threadId), "runs.ndjson");
17296
17485
  }
17297
17486
  function cursorSdkRunsNdjsonSearchPaths(threadId) {
17298
17487
  const scoped = cursorSdkRunsNdjsonPath(threadId);
@@ -17305,11 +17494,11 @@ function cursorSdkRunsNdjsonSearchPaths(threadId) {
17305
17494
  function sanitizeCursorStoreSegment(threadId) {
17306
17495
  return (threadId ?? "").trim().replace(/[^a-zA-Z0-9._-]/g, "_");
17307
17496
  }
17308
- var import_node_path42, CURSOR_SDK_STORE_DIR;
17497
+ var import_node_path43, CURSOR_SDK_STORE_DIR;
17309
17498
  var init_cursor_store = __esm({
17310
17499
  "src/agents/cursor-store.ts"() {
17311
17500
  "use strict";
17312
- import_node_path42 = require("path");
17501
+ import_node_path43 = require("path");
17313
17502
  init_paths();
17314
17503
  CURSOR_SDK_STORE_DIR = "cursor-sdk-store";
17315
17504
  }
@@ -17331,9 +17520,9 @@ function recoverFinishedCursorRun(opts) {
17331
17520
  return best;
17332
17521
  }
17333
17522
  function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
17334
- if (!(0, import_node_fs45.existsSync)(runsPath)) return null;
17523
+ if (!(0, import_node_fs46.existsSync)(runsPath)) return null;
17335
17524
  try {
17336
- const lines = (0, import_node_fs45.readFileSync)(runsPath, "utf8").split("\n");
17525
+ const lines = (0, import_node_fs46.readFileSync)(runsPath, "utf8").split("\n");
17337
17526
  let best = null;
17338
17527
  for (const line of lines) {
17339
17528
  const trimmed = line.trim();
@@ -17359,11 +17548,11 @@ function scanFinishedCursorRuns(runsPath, agentId, startedAfterMs) {
17359
17548
  return null;
17360
17549
  }
17361
17550
  }
17362
- var import_node_fs45;
17551
+ var import_node_fs46;
17363
17552
  var init_cursor_recover = __esm({
17364
17553
  "src/agents/cursor-recover.ts"() {
17365
17554
  "use strict";
17366
- import_node_fs45 = require("fs");
17555
+ import_node_fs46 = require("fs");
17367
17556
  init_cursor_store();
17368
17557
  }
17369
17558
  });
@@ -17402,6 +17591,7 @@ var init_title = __esm({
17402
17591
  // src/orchestrator/orchestrator.ts
17403
17592
  var orchestrator_exports = {};
17404
17593
  __export(orchestrator_exports, {
17594
+ LIVE_TURN_SPAWN_GRACE_MS: () => LIVE_TURN_SPAWN_GRACE_MS,
17405
17595
  Orchestrator: () => Orchestrator,
17406
17596
  getOrchestrator: () => getOrchestrator,
17407
17597
  isPidAlive: () => isPidAlive,
@@ -17494,13 +17684,13 @@ async function startOrchestration(opts) {
17494
17684
  }
17495
17685
  return updated;
17496
17686
  }
17497
- var import_node_events, import_node_fs46, STALE_AGENT_PID_WAIT_MS, Orchestrator, singleton;
17687
+ var import_node_events, import_node_fs47, LIVE_TURN_SPAWN_GRACE_MS, STALE_AGENT_PID_WAIT_MS, Orchestrator, singleton;
17498
17688
  var init_orchestrator = __esm({
17499
17689
  "src/orchestrator/orchestrator.ts"() {
17500
17690
  "use strict";
17501
17691
  import_node_events = require("events");
17502
17692
  init_outbound_watch();
17503
- import_node_fs46 = require("fs");
17693
+ import_node_fs47 = require("fs");
17504
17694
  init_error_detail();
17505
17695
  init_run();
17506
17696
  init_stale_lock();
@@ -17515,6 +17705,7 @@ var init_orchestrator = __esm({
17515
17705
  init_orphan_cleanup();
17516
17706
  init_apply_into_main();
17517
17707
  init_clone_repo();
17708
+ init_message_parts();
17518
17709
  init_usage();
17519
17710
  init_thread_store();
17520
17711
  init_desktop_host();
@@ -17552,6 +17743,7 @@ var init_orchestrator = __esm({
17552
17743
  init_caffeinate_hold();
17553
17744
  init_schedule_runner();
17554
17745
  init_coordinator_prompt();
17746
+ LIVE_TURN_SPAWN_GRACE_MS = 15e3;
17555
17747
  STALE_AGENT_PID_WAIT_MS = 2500;
17556
17748
  Orchestrator = class {
17557
17749
  events = new import_node_events.EventEmitter();
@@ -17608,6 +17800,39 @@ var init_orchestrator = __esm({
17608
17800
  if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) return false;
17609
17801
  return true;
17610
17802
  }
17803
+ /**
17804
+ * Whether a child is actually turning — not leftover disk `running` after
17805
+ * the process died. Coordinators were telling users “Agent is running.
17806
+ * Waiting for gate to pass” from stale stillRunning.
17807
+ */
17808
+ threadLooksLive(thread) {
17809
+ if (thread.status === "queued") {
17810
+ if (this.draining.has(thread.id) || this.startingTurns.has(thread.id)) return true;
17811
+ return thread.queue.length > 0;
17812
+ }
17813
+ if (thread.status !== "running") return false;
17814
+ if (this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id)) return true;
17815
+ const pid = thread.agentPid;
17816
+ if (typeof pid === "number" && pid > 0 && isPidAlive(pid)) return true;
17817
+ const updated = Date.parse(thread.updatedAt ?? "");
17818
+ if (Number.isFinite(updated) && Date.now() - updated < LIVE_TURN_SPAWN_GRACE_MS) return true;
17819
+ return false;
17820
+ }
17821
+ /** Desktop-only: persist idle/stopped when MCP would otherwise keep reporting live. */
17822
+ healStaleReportedActivity(thread) {
17823
+ if (!thisProcessShouldDrainAgentQueues()) return thread;
17824
+ if (this.threadLooksLive(thread)) return thread;
17825
+ if (thread.status === "running") {
17826
+ this.healStaleRunningTurns();
17827
+ return readThread(thread.id) ?? thread;
17828
+ }
17829
+ if (thread.status === "queued" && thread.queue.length === 0 && !this.activeTurns.has(thread.id) && !this.startingTurns.has(thread.id) && !this.draining.has(thread.id)) {
17830
+ setStatus(thread.id, "idle");
17831
+ this.emit({ type: "status_changed", threadId: thread.id, status: "idle" });
17832
+ return readThread(thread.id) ?? thread;
17833
+ }
17834
+ return thread;
17835
+ }
17611
17836
  async reconcile(repoPath, opts) {
17612
17837
  const reclaimStaleTurns = opts?.reclaimStaleTurns === true;
17613
17838
  const drainQueues = opts?.drainQueues !== false;
@@ -17632,7 +17857,7 @@ var init_orchestrator = __esm({
17632
17857
  }
17633
17858
  continue;
17634
17859
  }
17635
- if (!(0, import_node_fs46.existsSync)(thread.worktreePath)) {
17860
+ if (!(0, import_node_fs47.existsSync)(thread.worktreePath)) {
17636
17861
  setStatus(thread.id, "broken", "Worktree missing on disk");
17637
17862
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
17638
17863
  continue;
@@ -17942,12 +18167,14 @@ var init_orchestrator = __esm({
17942
18167
  this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
17943
18168
  return thread;
17944
18169
  }
17945
- async send(threadRef, prompt) {
18170
+ async send(threadRef, prompt, opts) {
17946
18171
  const thread = this.requireThread(threadRef);
17947
18172
  if (thread.status === "archived") {
17948
18173
  throw new Error(`Thread is archived: ${thread.id}`);
17949
18174
  }
17950
- return withThreadLock(thread.id, async () => {
18175
+ const followUp = opts?.followUp === "steer" ? "steer" : "queue";
18176
+ let shouldSteer = false;
18177
+ const sent = await withThreadLock(thread.id, async () => {
17951
18178
  const current = this.requireThread(thread.id);
17952
18179
  if (current.status === "archived") {
17953
18180
  throw new Error(`Thread is archived: ${thread.id}`);
@@ -17956,6 +18183,7 @@ var init_orchestrator = __esm({
17956
18183
  this.crashContinued.delete(thread.id);
17957
18184
  this.haltDrain.delete(thread.id);
17958
18185
  const inFlight = this.activeTurns.has(thread.id) || this.startingTurns.has(thread.id) || current.status === "running";
18186
+ shouldSteer = followUp === "steer" && (inFlight || current.queue.length > 0);
17959
18187
  const patch = { queue };
17960
18188
  if (!inFlight) patch.status = "queued";
17961
18189
  const pid = current.agentPid;
@@ -17967,16 +18195,20 @@ var init_orchestrator = __esm({
17967
18195
  if (!inFlight) {
17968
18196
  this.emit({ type: "status_changed", threadId: thread.id, status: "queued" });
17969
18197
  }
17970
- if (thisProcessShouldDrainAgentQueues()) {
18198
+ if (thisProcessShouldDrainAgentQueues() && !shouldSteer) {
17971
18199
  void this.drainQueue(thread.id);
17972
18200
  }
17973
18201
  return this.requireThread(thread.id);
17974
18202
  });
18203
+ if (shouldSteer && sent.queue.length > 0) {
18204
+ return this.sendQueuedMessageNow(sent.id, sent.queue.length - 1);
18205
+ }
18206
+ return sent;
17975
18207
  }
17976
- async fanOut(threadRefs, prompt) {
18208
+ async fanOut(threadRefs, prompt, opts) {
17977
18209
  const results = [];
17978
18210
  for (const ref of threadRefs) {
17979
- results.push(await this.send(ref, prompt));
18211
+ results.push(await this.send(ref, prompt, opts));
17980
18212
  }
17981
18213
  return results;
17982
18214
  }
@@ -18780,8 +19012,8 @@ var init_orchestrator = __esm({
18780
19012
  return created;
18781
19013
  }
18782
19014
  async waitForTurn(threadRef, timeoutMs = 6e5, opts) {
18783
- const thread = this.requireThread(threadRef);
18784
- if (!["running", "queued"].includes(thread.status)) {
19015
+ const thread = this.healStaleReportedActivity(this.requireThread(threadRef));
19016
+ if (!this.threadLooksLive(thread)) {
18785
19017
  return thread;
18786
19018
  }
18787
19019
  const start = Date.now();
@@ -18818,12 +19050,13 @@ var init_orchestrator = __esm({
18818
19050
  }
18819
19051
  });
18820
19052
  timer = setInterval(() => {
18821
- const current = readThread(thread.id);
18822
- if (!current) {
19053
+ const raw = readThread(thread.id);
19054
+ if (!raw) {
18823
19055
  finish(() => reject(new Error(`Thread not found: ${thread.id}`)));
18824
19056
  return;
18825
19057
  }
18826
- if (!["running", "queued"].includes(current.status)) {
19058
+ const current = this.healStaleReportedActivity(raw);
19059
+ if (!this.threadLooksLive(current)) {
18827
19060
  finish(() => resolve(current));
18828
19061
  return;
18829
19062
  }
@@ -18846,20 +19079,22 @@ var init_orchestrator = __esm({
18846
19079
  };
18847
19080
  }
18848
19081
  getTurnResult(threadRef) {
18849
- const thread = this.requireThread(threadRef);
19082
+ const thread = this.healStaleReportedActivity(this.requireThread(threadRef));
18850
19083
  const lastAgent = [...thread.messages].reverse().find((m) => m.role === "agent");
18851
19084
  const lastError = thread.lastError ?? null;
18852
- const text5 = (lastAgent?.text ?? "").trim() || (thread.status === "error" || thread.status === "stopped" || thread.status === "broken" ? lastError ?? "" : "");
18853
- const stillRunning = thread.status === "running" || thread.status === "queued";
19085
+ const rawText = (lastAgent?.text ?? "").trim();
19086
+ const text5 = (rawText && !isInternalAgentStatusText(rawText) ? rawText : "") || (thread.status === "error" || thread.status === "stopped" || thread.status === "broken" ? lastError ?? "" : "");
19087
+ const stillRunning = this.threadLooksLive(thread);
18854
19088
  const live = stillRunning ? readTurnLive(thread.id) : null;
18855
- const queuedHint = thread.status === "queued" && !live?.summary ? "Queued \u2014 waiting for a concurrency slot" : null;
19089
+ const liveSummary = live?.summary && !isInternalAgentStatusText(live.summary) ? live.summary : null;
19090
+ const queuedHint = stillRunning && thread.status === "queued" && !liveSummary ? "Queued \u2014 waiting for a concurrency slot" : null;
18856
19091
  return {
18857
19092
  text: text5,
18858
19093
  status: thread.status,
18859
19094
  sessionId: thread.sessionId,
18860
19095
  lastError,
18861
19096
  stillRunning,
18862
- progress: live?.summary ?? queuedHint,
19097
+ progress: liveSummary ?? queuedHint,
18863
19098
  lastActivityAt: live?.updatedAt ?? null,
18864
19099
  usage: lastAgent?.usage ?? null
18865
19100
  };
@@ -19207,7 +19442,14 @@ var init_orchestrator = __esm({
19207
19442
  );
19208
19443
  }
19209
19444
  if ((action === "commit-push" || action === "create-draft" || action === "create-web") && thread.worktreePath?.trim() && !await isDirty(thread.worktreePath)) {
19210
- await this.pushAndMaybeOpenPr(thread, action);
19445
+ try {
19446
+ await this.pushAndMaybeOpenPr(thread, action);
19447
+ } catch (err) {
19448
+ const message = err instanceof Error ? err.message : String(err);
19449
+ updateThread(thread.id, { lastError: message });
19450
+ this.emit({ type: "error", threadId: thread.id, message });
19451
+ throw err;
19452
+ }
19211
19453
  return this.requireThread(threadRef);
19212
19454
  }
19213
19455
  let prBase;
@@ -19410,7 +19652,7 @@ var init_orchestrator = __esm({
19410
19652
  this.emit({ type: "status_changed", threadId: restored2.id, status: restored2.status });
19411
19653
  return restored2;
19412
19654
  }
19413
- if (!(0, import_node_fs46.existsSync)(thread.worktreePath)) {
19655
+ if (!(0, import_node_fs47.existsSync)(thread.worktreePath)) {
19414
19656
  if (isCowboyThread(thread) || isPrimaryCheckoutThread(thread)) {
19415
19657
  throw new Error(
19416
19658
  `Cowboy checkout missing: ${thread.worktreePath}. Re-add the project folder, then restore.`
@@ -19422,6 +19664,8 @@ var init_orchestrator = __esm({
19422
19664
  await withRepoGitLock(thread.repoPath, async () => {
19423
19665
  await git(["worktree", "add", dest, thread.branchName], thread.repoPath);
19424
19666
  });
19667
+ const { ensureWorktreeSideboardIgnored: ensureWorktreeSideboardIgnored2 } = await Promise.resolve().then(() => (init_worktree_exclude(), worktree_exclude_exports));
19668
+ await ensureWorktreeSideboardIgnored2(dest);
19425
19669
  void createThreadWorktree2;
19426
19670
  void slug;
19427
19671
  }
@@ -19490,8 +19734,8 @@ init_nested_electron_env();
19490
19734
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
19491
19735
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
19492
19736
  var import_zod5 = require("zod");
19493
- var import_node_crypto11 = require("crypto");
19494
- var import_node_path45 = require("path");
19737
+ var import_node_crypto12 = require("crypto");
19738
+ var import_node_path46 = require("path");
19495
19739
  init_orchestrator();
19496
19740
  init_worktree();
19497
19741
 
@@ -19505,7 +19749,7 @@ init_abletime();
19505
19749
  init_fetch();
19506
19750
 
19507
19751
  // src/integrations/linear-oauth.ts
19508
- var import_node_crypto9 = require("crypto");
19752
+ var import_node_crypto10 = require("crypto");
19509
19753
  var import_node_http = require("http");
19510
19754
  init_app_settings();
19511
19755
  init_fetch();
@@ -19683,6 +19927,22 @@ query SideboardAssignedIssues($first: Int!) {
19683
19927
  }
19684
19928
  }
19685
19929
  `;
19930
+ var ISSUES_QUERY = `
19931
+ query SideboardIssues($first: Int!, $filter: IssueFilter) {
19932
+ viewer { id name }
19933
+ issues(first: $first, orderBy: updatedAt, filter: $filter) {
19934
+ nodes { ${LIST_ISSUE_FIELDS} }
19935
+ }
19936
+ }
19937
+ `;
19938
+ var SEARCH_ISSUES_QUERY = `
19939
+ query SideboardSearchIssues($term: String!, $first: Int!, $filter: IssueFilter) {
19940
+ viewer { id name }
19941
+ searchIssues(term: $term, first: $first, includeArchived: false, filter: $filter) {
19942
+ nodes { ${LIST_ISSUE_FIELDS} }
19943
+ }
19944
+ }
19945
+ `;
19686
19946
  var TEAMS_QUERY = `
19687
19947
  query SideboardTeams {
19688
19948
  viewer { id name }
@@ -19966,19 +20226,73 @@ function normalizePriority(priority) {
19966
20226
  }
19967
20227
  return priority;
19968
20228
  }
19969
- async function listLinearAssignedIssues(opts) {
19970
- const first = Math.max(1, Math.min(250, opts?.limit ?? 200));
19971
- const json = await linearGraphql(ASSIGNED_ISSUES_QUERY, { first }, opts);
20229
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20230
+ function assigneeFilterKey(assignee) {
20231
+ return (assignee ?? "").trim();
20232
+ }
20233
+ function buildLinearIssueFilter(input) {
20234
+ const filter = {
20235
+ state: { type: { nin: ["completed", "canceled"] } }
20236
+ };
20237
+ const raw = assigneeFilterKey(input?.assignee) || "me";
20238
+ const key = raw.toLowerCase();
20239
+ if (key === "all" || key === "*") return filter;
20240
+ if (key === "unassigned" || key === "none" || key === "null") {
20241
+ filter.assignee = { null: true };
20242
+ return filter;
20243
+ }
20244
+ if (key === "me" || key === "@me") {
20245
+ filter.assignee = { isMe: true };
20246
+ return filter;
20247
+ }
20248
+ if (UUID_RE.test(raw)) {
20249
+ filter.assignee = { id: { eq: raw } };
20250
+ return filter;
20251
+ }
20252
+ filter.assignee = { name: { eqIgnoreCase: raw } };
20253
+ return filter;
20254
+ }
20255
+ function isAssignedToMe(assignee) {
20256
+ const key = assignee.toLowerCase();
20257
+ return key === "me" || key === "@me";
20258
+ }
20259
+ function listedLinearIssues(json, nodes) {
19972
20260
  return {
19973
20261
  viewer: {
19974
20262
  id: String(json.viewer?.id ?? ""),
19975
20263
  name: String(json.viewer?.name ?? "")
19976
20264
  },
19977
- issues: (json.viewer?.assignedIssues?.nodes ?? []).map(
19978
- (node) => toIssueInfo(mapIssue(node))
19979
- )
20265
+ issues: (nodes ?? []).map((node) => toIssueInfo(mapIssue(node)))
19980
20266
  };
19981
20267
  }
20268
+ async function listLinearIssuesFiltered(opts) {
20269
+ const first = Math.max(1, Math.min(250, opts?.limit ?? 200));
20270
+ const query = opts?.query?.trim() ?? "";
20271
+ const assignee = assigneeFilterKey(opts?.assignee) || (query ? "all" : "me");
20272
+ const filter = buildLinearIssueFilter({ assignee });
20273
+ if (query) {
20274
+ const json2 = await linearGraphql(
20275
+ SEARCH_ISSUES_QUERY,
20276
+ { term: query, first, filter },
20277
+ opts
20278
+ );
20279
+ return listedLinearIssues(json2, json2.searchIssues?.nodes);
20280
+ }
20281
+ if (isAssignedToMe(assignee)) {
20282
+ const json2 = await linearGraphql(
20283
+ ASSIGNED_ISSUES_QUERY,
20284
+ { first },
20285
+ opts
20286
+ );
20287
+ return listedLinearIssues(json2, json2.viewer?.assignedIssues?.nodes);
20288
+ }
20289
+ const json = await linearGraphql(
20290
+ ISSUES_QUERY,
20291
+ { first, filter },
20292
+ opts
20293
+ );
20294
+ return listedLinearIssues(json, json.issues?.nodes);
20295
+ }
19982
20296
  async function listLinearTeams(opts) {
19983
20297
  const json = await linearGraphql(TEAMS_QUERY, void 0, opts);
19984
20298
  return {
@@ -20085,6 +20399,22 @@ async function commentLinearIssue(input, opts) {
20085
20399
  }
20086
20400
 
20087
20401
  // src/integrations/issues.ts
20402
+ function assigneeKey(assignee) {
20403
+ return (assignee ?? "").trim().toLowerCase();
20404
+ }
20405
+ function issueMatchesAssignee(issue, assignee, viewerName = "") {
20406
+ const key = assigneeKey(assignee);
20407
+ if (!key || key === "all" || key === "*") return true;
20408
+ const names = [...issue.assignees ?? [], issue.assignee ?? ""].map((name) => name.trim().toLowerCase()).filter(Boolean);
20409
+ if (key === "unassigned" || key === "none" || key === "null") {
20410
+ return names.length === 0;
20411
+ }
20412
+ if (key === "me" || key === "@me") {
20413
+ const me = viewerName.trim().toLowerCase();
20414
+ return Boolean(me && names.includes(me));
20415
+ }
20416
+ return names.includes(key);
20417
+ }
20088
20418
  async function listGitHubIssues(repoPath, opts) {
20089
20419
  const limit = Math.max(1, Math.min(1e3, opts?.limit ?? 200));
20090
20420
  const slug = await resolveGithubRepoSlug(repoPath);
@@ -20098,6 +20428,17 @@ async function listGitHubIssues(repoPath, opts) {
20098
20428
  "--state",
20099
20429
  "open"
20100
20430
  ];
20431
+ const query = opts?.query?.trim() ?? "";
20432
+ const assignee = opts?.assignee?.trim() ?? "";
20433
+ const key = assignee.toLowerCase();
20434
+ const searchParts = [];
20435
+ if (query) searchParts.push(query);
20436
+ if (key === "unassigned" || key === "none" || key === "null") {
20437
+ searchParts.push("no:assignee");
20438
+ } else if (key && key !== "all" && key !== "*") {
20439
+ args.push("--assignee", key === "me" || key === "@me" ? "@me" : assignee);
20440
+ }
20441
+ if (searchParts.length) args.push("--search", searchParts.join(" "));
20101
20442
  if (slug) args.push("--repo", slug);
20102
20443
  const { stdout, exitCode } = await gh(args, repoPath, { reject: false });
20103
20444
  if (exitCode !== 0 || !stdout.trim()) return [];
@@ -20135,14 +20476,20 @@ async function githubViewerLogin(repoPath) {
20135
20476
  if (exitCode !== 0) return "";
20136
20477
  return stdout.trim();
20137
20478
  }
20138
- async function listIssues(repoPath) {
20479
+ async function listIssues(repoPath, opts) {
20139
20480
  const settings = loadAppSettings();
20140
20481
  const preferredSource = settings.integrations.issueSource ?? "github";
20141
20482
  const linearConnected = isLinearConnected(settings);
20142
20483
  const abletimeConnected = isAbleTimeConnected(settings);
20143
20484
  const source = resolveEffectiveIssueSource(settings);
20485
+ const query = opts?.query?.trim() || void 0;
20486
+ const assignee = opts?.assignee?.trim() || void 0;
20144
20487
  if (source === "linear") {
20145
- const listed = await listLinearAssignedIssues();
20488
+ const listed = await listLinearIssuesFiltered({
20489
+ assignee,
20490
+ query,
20491
+ limit: opts?.limit
20492
+ });
20146
20493
  return {
20147
20494
  source,
20148
20495
  preferredSource,
@@ -20156,13 +20503,20 @@ async function listIssues(repoPath) {
20156
20503
  };
20157
20504
  }
20158
20505
  if (source === "abletime") {
20159
- const listed = await listAbleTimeAssignedIssues();
20506
+ const [listed, searched] = await Promise.all([
20507
+ listAbleTimeAssignedIssues(),
20508
+ query ? searchAbleTimeTasks(query) : Promise.resolve(null)
20509
+ ]);
20510
+ const viewerName = listed.viewer.name ?? listed.viewer.id ?? "";
20511
+ const issues2 = (searched ? searched.map(toAbleTimeIssueInfo) : listed.issues).filter(
20512
+ (issue) => issueMatchesAssignee(issue, assignee, viewerName)
20513
+ );
20160
20514
  return {
20161
20515
  source,
20162
20516
  preferredSource,
20163
20517
  linearConnected,
20164
20518
  abletimeConnected,
20165
- issues: listed.issues,
20519
+ issues: issues2,
20166
20520
  viewer: {
20167
20521
  login: listed.viewer.name,
20168
20522
  name: listed.viewer.name
@@ -20170,7 +20524,7 @@ async function listIssues(repoPath) {
20170
20524
  };
20171
20525
  }
20172
20526
  const [issues, login] = await Promise.all([
20173
- listGitHubIssues(repoPath),
20527
+ listGitHubIssues(repoPath, { assignee, query, limit: opts?.limit }),
20174
20528
  githubViewerLogin(repoPath)
20175
20529
  ]);
20176
20530
  return {
@@ -20223,14 +20577,16 @@ function mcpWaitFinishedHint(status) {
20223
20577
  }
20224
20578
 
20225
20579
  // src/mcp/server.ts
20580
+ init_message_parts();
20226
20581
  init_turn_live();
20227
20582
 
20228
20583
  // src/mcp/thread-visibility.ts
20584
+ init_message_parts();
20229
20585
  function lastMessagePreview(messages, max = 160) {
20230
20586
  if (!messages?.length) return null;
20231
20587
  for (let i = messages.length - 1; i >= 0; i--) {
20232
20588
  const text5 = messages[i]?.text?.trim();
20233
- if (!text5) continue;
20589
+ if (!text5 || isInternalAgentStatusText(text5)) continue;
20234
20590
  const flat = text5.replace(/\s+/g, " ");
20235
20591
  return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
20236
20592
  }
@@ -20900,6 +21256,22 @@ function registerLinearTools(server) {
20900
21256
  }
20901
21257
  }
20902
21258
  );
21259
+ server.tool(
21260
+ "linear_search_issues",
21261
+ "Search or list open Linear issues. Not limited to the current user \u2014 pass assignee=unassigned for issues with no owner, assignee=all (default when query is set) for anyone including unassigned, assignee=me for yours, or a user id / name. Optional query uses Linear search (title, identifier, description).",
21262
+ {
21263
+ query: import_zod3.z.string().optional().describe("Search text (identifier, title, description). Omit to list by assignee only."),
21264
+ assignee: import_zod3.z.string().optional().describe("me, unassigned, all, a Linear user id, or a display name. Default: all when query is set, otherwise me."),
21265
+ limit: import_zod3.z.number().int().positive().max(250).optional()
21266
+ },
21267
+ async ({ query, assignee, limit }) => {
21268
+ try {
21269
+ return text3(await listLinearIssuesFiltered({ query, assignee, limit }));
21270
+ } catch (err) {
21271
+ return fail3(err);
21272
+ }
21273
+ }
21274
+ );
20903
21275
  server.tool(
20904
21276
  "linear_get_issue",
20905
21277
  "Get a Linear issue by uuid or identifier (ENG-123). Returns description, comments, relations (blocks/blockedBy/related/duplicate), parent/children, project, cycle, labels, and other metadata.",
@@ -21134,30 +21506,31 @@ function registerScheduleTools(server) {
21134
21506
 
21135
21507
  // src/mcp/server.ts
21136
21508
  init_agent_git_actions();
21509
+ init_gh_errors();
21137
21510
  init_git_auth_mode();
21138
21511
 
21139
21512
  // src/board/load-home-board.ts
21140
- var import_node_fs48 = require("fs");
21141
- var import_node_path44 = require("path");
21513
+ var import_node_fs49 = require("fs");
21514
+ var import_node_path45 = require("path");
21142
21515
  init_worktree();
21143
21516
  init_paths();
21144
21517
 
21145
21518
  // src/board/board-pins.ts
21146
- var import_node_crypto10 = require("crypto");
21147
- var import_node_fs47 = require("fs");
21148
- var import_node_path43 = require("path");
21519
+ var import_node_crypto11 = require("crypto");
21520
+ var import_node_fs48 = require("fs");
21521
+ var import_node_path44 = require("path");
21149
21522
  init_paths();
21150
21523
  init_home_board();
21151
21524
  var FILE = "home-board-pins.json";
21152
21525
  var VERSION = 1;
21153
21526
  function pinsFile() {
21154
- return (0, import_node_path43.join)(appDataDir(), FILE);
21527
+ return (0, import_node_path44.join)(appDataDir(), FILE);
21155
21528
  }
21156
21529
  function readDisk() {
21157
21530
  const path = pinsFile();
21158
- if (!(0, import_node_fs47.existsSync)(path)) return [];
21531
+ if (!(0, import_node_fs48.existsSync)(path)) return [];
21159
21532
  try {
21160
- const raw = JSON.parse((0, import_node_fs47.readFileSync)(path, "utf8"));
21533
+ const raw = JSON.parse((0, import_node_fs48.readFileSync)(path, "utf8"));
21161
21534
  if (raw?.version !== VERSION || !Array.isArray(raw.items)) return [];
21162
21535
  return raw.items.filter((item) => item?.id && item.kind && item.ref);
21163
21536
  } catch {
@@ -21165,8 +21538,8 @@ function readDisk() {
21165
21538
  }
21166
21539
  }
21167
21540
  function writeDisk(items) {
21168
- (0, import_node_fs47.mkdirSync)(appDataDir(), { recursive: true });
21169
- (0, import_node_fs47.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
21541
+ (0, import_node_fs48.mkdirSync)(appDataDir(), { recursive: true });
21542
+ (0, import_node_fs48.writeFileSync)(pinsFile(), JSON.stringify({ version: VERSION, items }, null, 2), "utf8");
21170
21543
  }
21171
21544
  function listBoardPins() {
21172
21545
  return readDisk();
@@ -21187,7 +21560,7 @@ function homeBoardWorkspaceKey(workspaces) {
21187
21560
  return workspaces.map((w) => w.path).filter(Boolean).sort().join("\n");
21188
21561
  }
21189
21562
  function cacheFile() {
21190
- return (0, import_node_path44.join)(appDataDir(), "home-board-cache.json");
21563
+ return (0, import_node_path45.join)(appDataDir(), "home-board-cache.json");
21191
21564
  }
21192
21565
  function emptyInputs() {
21193
21566
  return {
@@ -21213,9 +21586,9 @@ function shouldCacheHomeBoardInputs(inputs) {
21213
21586
  }
21214
21587
  function readDiskCache() {
21215
21588
  const path = cacheFile();
21216
- if (!(0, import_node_fs48.existsSync)(path)) return null;
21589
+ if (!(0, import_node_fs49.existsSync)(path)) return null;
21217
21590
  try {
21218
- const raw = JSON.parse((0, import_node_fs48.readFileSync)(path, "utf8"));
21591
+ const raw = JSON.parse((0, import_node_fs49.readFileSync)(path, "utf8"));
21219
21592
  if (raw?.version !== CACHE_VERSION || typeof raw.fetchedAt !== "number") {
21220
21593
  return null;
21221
21594
  }
@@ -21229,8 +21602,8 @@ function readDiskCache() {
21229
21602
  }
21230
21603
  function writeDiskCache(entry) {
21231
21604
  try {
21232
- (0, import_node_fs48.mkdirSync)(appDataDir(), { recursive: true });
21233
- (0, import_node_fs48.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
21605
+ (0, import_node_fs49.mkdirSync)(appDataDir(), { recursive: true });
21606
+ (0, import_node_fs49.writeFileSync)(cacheFile(), JSON.stringify(entry), "utf8");
21234
21607
  } catch {
21235
21608
  }
21236
21609
  }
@@ -21519,13 +21892,13 @@ async function startMcpServer() {
21519
21892
  async () => {
21520
21893
  const threads = orch.getThreads(true);
21521
21894
  const lines = threads.map((t) => {
21522
- const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path45.basename)(t.repoPath) || t.repoPath;
21523
- const live = t.status === "running" || t.status === "queued" ? readTurnLive(t.id) : null;
21895
+ const repo = t.repoPath === GLOBAL_WORKSPACE_ID ? "Orchestration" : (0, import_node_path46.basename)(t.repoPath) || t.repoPath;
21896
+ const live = orch.threadLooksLive(t) ? readTurnLive(t.id) : null;
21524
21897
  const parent = t.parentThreadId ? ` parent:${t.parentThreadId.slice(0, 8)}` : "";
21525
21898
  const preview = lastMessagePreview(t.messages, 80);
21526
21899
  const previewBit = preview ? ` ${preview}` : "";
21527
21900
  const err = t.lastError ? ` error:${t.lastError.replace(/\s+/g, " ").slice(0, 60)}` : "";
21528
- const progress = live?.summary ? ` ${live.summary}` : "";
21901
+ const progress = live?.summary && !isInternalAgentStatusText(live.summary) ? ` ${live.summary}` : "";
21529
21902
  return `${t.id.slice(0, 8)} ${t.status.padEnd(9)} ${t.agent.padEnd(8)} ${repo} ${t.sourceType}:${t.sourceRef} ${t.title}${parent}${previewBit}${err} sideboard://thread/${t.id}${t.devPort ? ` http://localhost:${t.devPort}` : ""}${progress}`;
21530
21903
  });
21531
21904
  return {
@@ -21587,7 +21960,9 @@ async function startMcpServer() {
21587
21960
  if (!t) {
21588
21961
  return { content: [{ type: "text", text: `Thread not found: ${ref}` }], isError: true };
21589
21962
  }
21590
- const live = t.status === "running" || t.status === "queued" ? readTurnLive(t.id) : null;
21963
+ const stillRunning = orch.threadLooksLive(t);
21964
+ const live = stillRunning ? readTurnLive(t.id) : null;
21965
+ const liveSummary = live?.summary && !isInternalAgentStatusText(live.summary) ? live.summary : null;
21591
21966
  const spend = orch.getThreadUsage(t.id);
21592
21967
  const summary = {
21593
21968
  id: t.id,
@@ -21607,8 +21982,8 @@ async function startMcpServer() {
21607
21982
  devPort: t.devPort,
21608
21983
  prUrl: t.prUrl,
21609
21984
  lastError: t.lastError ?? null,
21610
- stillRunning: t.status === "running" || t.status === "queued",
21611
- progress: live?.summary ?? (t.status === "queued" ? "Queued \u2014 waiting for a concurrency slot" : null),
21985
+ stillRunning,
21986
+ progress: liveSummary ?? (stillRunning && t.status === "queued" ? "Queued \u2014 waiting for a concurrency slot" : null),
21612
21987
  lastActivityAt: live?.updatedAt ?? null,
21613
21988
  usage: spend.usage,
21614
21989
  lastTurnUsage: spend.lastTurnUsage
@@ -21889,7 +22264,7 @@ async function startMcpServer() {
21889
22264
  const ident = issue?.identifier ?? ref.trim();
21890
22265
  const attachments = issue ? [
21891
22266
  {
21892
- id: (0, import_node_crypto11.randomUUID)(),
22267
+ id: (0, import_node_crypto12.randomUUID)(),
21893
22268
  name: issue.identifier,
21894
22269
  kind: "issue",
21895
22270
  content: [
@@ -21965,7 +22340,7 @@ async function startMcpServer() {
21965
22340
  );
21966
22341
  server.tool(
21967
22342
  "wait_for_turn",
21968
- "Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. If stillRunning is true, progress is tools/thinking (or \u201Cqueued, waiting for a concurrency slot\u201D if it has not started). Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume a hang. On status error, lastError/text is the failure. On status stopped or broken, the child did not finish \u2014 resume with send_to_thread or tell the user; do not treat that as success. When finished, usage is the last agent turn\u2019s tokens + costUsd (when the provider reported cost).",
22343
+ "Wait until the thread finishes its current/queued turn, or return early with a live progress snapshot. MCP clients often kill tools around 60s, so this returns within 45s even while the child is still working. stillRunning is the source of truth \u2014 if false, the child is not working (do not say it is waiting for a gate). If stillRunning is true, progress is tools/thinking (or \u201Cqueued, waiting for a concurrency slot\u201D if it has not started). Call wait_for_turn again. Do not send a check-in prompt, force_stop, or assume a hang. On status error, lastError/text is the failure. On status stopped or broken, the child did not finish \u2014 resume with send_to_thread or tell the user; do not treat that as success. When finished, usage is the last agent turn\u2019s tokens + costUsd (when the provider reported cost).",
21969
22344
  {
21970
22345
  ref: import_zod5.z.string(),
21971
22346
  timeoutMs: import_zod5.z.number().optional()
@@ -22157,7 +22532,7 @@ async function startMcpServer() {
22157
22532
  );
22158
22533
  server.tool(
22159
22534
  "ask_git",
22160
- "Commit & push, open a draft PR, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` if SSH is missing). When dirty, queues the worktree agent to commit; then wait_for_turn (loop while stillRunning). Pass a worktree thread ref (not the orchestrator). action=merge only when the user explicitly asked to merge that PR. Do not run git or gh from the orchestration cwd.",
22535
+ "Commit & push, open a draft PR, resolve conflicts, or merge \u2014 same actions as the desktop git buttons. When the worktree is clean, Sideboard pushes / opens the PR itself (HTTPS via `gh` even when origin is SSH / Settings \u2192 Git is SSH). When dirty, queues the worktree agent to commit; then wait_for_turn (loop while stillRunning). Pass a worktree thread ref (not the orchestrator). action=merge only when the user explicitly asked to merge that PR. Do not run git or gh from the orchestration cwd. If this tool errors that the GraphQL/PR body is too long, the branch is already pushed \u2014 have the worktree agent retry `gh pr create --body-file` with a short description (GitHub limit 65,536 characters). Do not invent SSH/auth failures from that error.",
22161
22536
  {
22162
22537
  ref: import_zod5.z.string().describe("Worktree thread id/ref"),
22163
22538
  action: import_zod5.z.enum(AGENT_GIT_ACTIONS).describe(
@@ -22182,8 +22557,26 @@ async function startMcpServer() {
22182
22557
  ]
22183
22558
  };
22184
22559
  } catch (err) {
22185
- const message = err instanceof Error ? err.message : String(err);
22186
- return { content: [{ type: "text", text: message }], isError: true };
22560
+ const raw = err instanceof Error ? err.message : String(err);
22561
+ const message = formatGhLandError(raw);
22562
+ return {
22563
+ content: [
22564
+ {
22565
+ type: "text",
22566
+ text: JSON.stringify(
22567
+ {
22568
+ error: true,
22569
+ action,
22570
+ lastError: message,
22571
+ hint: /body is too long/i.test(message) ? "Branch is already pushed. send_to_thread so the worktree agent runs gh pr create --draft -R <origin> --body-file <short.md>. Keep the description short." : void 0
22572
+ },
22573
+ null,
22574
+ 2
22575
+ )
22576
+ }
22577
+ ],
22578
+ isError: true
22579
+ };
22187
22580
  }
22188
22581
  }
22189
22582
  );
@@ -22578,11 +22971,15 @@ async function startMcpServer() {
22578
22971
  );
22579
22972
  server.tool(
22580
22973
  "list_issues",
22581
- "List issues from Sideboard Account connections (Linear, AbleTime MCP, or GitHub Issues; falls back to GitHub when the preferred tracker is not connected). Linear/AbleTime are account-wide; GitHub Issues are scoped to repoPath from list_workspaces. When AbleTime is the issue source and work has no ticket, call abletime_ensure_task (or create_thread from the default branch \u2014 Sideboard auto-creates a task to track against). Prefer list_board for Home columns. Then create_thread with sourceType=ticket.",
22582
- { repoPath: import_zod5.z.string() },
22583
- async ({ repoPath }) => {
22974
+ "List or search issues from Sideboard Account connections (Linear, AbleTime MCP, or GitHub Issues; falls back to GitHub when the preferred tracker is not connected). Linear defaults to issues assigned to you; pass assignee=unassigned for no owner, assignee=all for everyone (including unassigned), or a user id / GitHub login. Optional query searches title/identifier. Linear/AbleTime are account-wide; GitHub Issues are scoped to repoPath from list_workspaces. Prefer linear_search_issues when Linear is connected and you need unassigned or other people's tickets. When AbleTime is the issue source and work has no ticket, call abletime_ensure_task (or create_thread from the default branch \u2014 Sideboard auto-creates a task to track against). Prefer list_board for Home columns. Then create_thread with sourceType=ticket.",
22975
+ {
22976
+ repoPath: import_zod5.z.string(),
22977
+ query: import_zod5.z.string().optional().describe("Search title, identifier, or description"),
22978
+ assignee: import_zod5.z.string().optional().describe("me (Linear default), unassigned, all, a user id, or a GitHub login")
22979
+ },
22980
+ async ({ repoPath, query, assignee }) => {
22584
22981
  const root = await resolveRepoRoot(repoPath);
22585
- const result = await listIssues(root);
22982
+ const result = await listIssues(root, { query, assignee });
22586
22983
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
22587
22984
  }
22588
22985
  );