@sideboard-ai/core 0.1.83 → 0.1.85

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 (43) hide show
  1. package/dist/agents/cursor-runner.cjs +88 -53
  2. package/dist/agents/cursor-runner.js +93 -56
  3. package/dist/{agents-4KRD46KG.js → agents-FD77JUOP.js} +10 -10
  4. package/dist/{agents-3YPUZME7.js → agents-YAYNNJWC.js} +9 -9
  5. package/dist/{app-settings-MQXL7OUF.js → app-settings-K6RFCRF6.js} +2 -2
  6. package/dist/{app-settings-GVSOIJLZ.js → app-settings-PEXK5VEX.js} +1 -1
  7. package/dist/{chunk-LVTNWH7B.js → chunk-2N5DVTGH.js} +2 -2
  8. package/dist/{chunk-JMRJ4F5B.js → chunk-3CCRD6WS.js} +69 -20
  9. package/dist/{chunk-AE5VBOFE.js → chunk-3KETJKYA.js} +9 -2
  10. package/dist/{chunk-IZ7RPF54.js → chunk-5KLC2MWZ.js} +5 -1
  11. package/dist/{chunk-DJFGX4RT.js → chunk-6GKDYUTJ.js} +3 -3
  12. package/dist/{chunk-VROPG6QF.js → chunk-6K5VAPVR.js} +3 -3
  13. package/dist/{chunk-HBBXS2FR.js → chunk-6WQAYBVE.js} +89 -21
  14. package/dist/{chunk-UHGN4KCL.js → chunk-AY53MPDE.js} +4 -0
  15. package/dist/{chunk-SEOICVGB.js → chunk-CBJSPTBG.js} +30 -6
  16. package/dist/{chunk-BTL7EMGT.js → chunk-HHTHF5BQ.js} +4 -4
  17. package/dist/{chunk-YO3CYL6B.js → chunk-I3FRXL7J.js} +1 -1
  18. package/dist/{chunk-RS54WYYH.js → chunk-JFQ2M6NQ.js} +2 -2
  19. package/dist/{chunk-NXXT5SE3.js → chunk-JTHWVYSD.js} +3 -3
  20. package/dist/{chunk-GD2FM6FN.js → chunk-KRAUS3RF.js} +87 -17
  21. package/dist/{chunk-D4DPQ552.js → chunk-LGXBYZZA.js} +9 -2
  22. package/dist/{chunk-E2MIA7DO.js → chunk-SH75CXJR.js} +2 -2
  23. package/dist/{chunk-JW6YFPQE.js → chunk-WGI6KXKJ.js} +36 -11
  24. package/dist/{chunk-ERJS3ZDP.js → chunk-XVFV56JG.js} +2 -2
  25. package/dist/{connected-teams-ZRS53OAT.js → connected-teams-BNBM7OIC.js} +2 -2
  26. package/dist/{connected-teams-35EIRJCP.js → connected-teams-LSQ5TAZP.js} +2 -2
  27. package/dist/{coordinator-prompt-46JE4NQR.js → coordinator-prompt-VN7FF76K.js} +5 -5
  28. package/dist/{coordinator-prompt-6ICA54JR.js → coordinator-prompt-YYQSPLIP.js} +4 -4
  29. package/dist/{global-workspace-GSLCEB5E.js → global-workspace-BRIDCBMY.js} +6 -6
  30. package/dist/{global-workspace-OBRWUPZG.js → global-workspace-U3XVTQLL.js} +5 -5
  31. package/dist/index.cjs +235 -94
  32. package/dist/index.d.cts +32 -6
  33. package/dist/index.d.ts +32 -6
  34. package/dist/index.js +47 -33
  35. package/dist/mcp/run-stdio.cjs +532 -399
  36. package/dist/mcp/run-stdio.js +43 -36
  37. package/dist/{run-V2WZUMJA.js → run-CFKZPY7F.js} +1 -1
  38. package/dist/{run-A4RHY7HH.js → run-XKTAJRWF.js} +1 -1
  39. package/dist/{workspaces-RJIWQB6J.js → workspaces-AIZDG6PS.js} +6 -6
  40. package/dist/{workspaces-7B3PTEF4.js → workspaces-IWQIWAMQ.js} +7 -7
  41. package/dist/{worktree-U3UIID2K.js → worktree-6ZALJUWG.js} +3 -3
  42. package/dist/{worktree-HSN5LWY6.js → worktree-7Y22WHR7.js} +4 -4
  43. package/package.json +1 -1
@@ -54,6 +54,9 @@ function wrapElectronAsNodeLaunch(file, args) {
54
54
  args: ["-c", STRIP_NESTED_ELECTRON_THEN_EXEC, "sh", file, ...args]
55
55
  };
56
56
  }
57
+ function isStrippedElectronLaunch(command, args) {
58
+ return command === "/bin/sh" && Boolean(args?.[1]?.includes("ELECTRON_RUN_AS_NODE") && args[1].includes("unset"));
59
+ }
57
60
  var NESTED_ELECTRON_ENV_PREFIXES, STRIP_NESTED_ELECTRON_THEN_EXEC;
58
61
  var init_nested_electron_env = __esm({
59
62
  "src/hook/nested-electron-env.ts"() {
@@ -730,20 +733,36 @@ function pushTurnStderr(tail, line, maxLines = 12) {
730
733
  tail.push(trimmed);
731
734
  while (tail.length > maxLines) tail.shift();
732
735
  }
736
+ function looksLikeMinifiedJsDump(line) {
737
+ if (line.length < 200) return false;
738
+ return /yield Promise\.all/.test(line) || /\(0,[A-Za-z$]\.\w+\)/.test(line) || /CURSOR_RIPGREP_PATH/.test(line);
739
+ }
740
+ function looksLikeNestedElectronCrash(line) {
741
+ return /HasCustomHostObject|ElectronInitializeICUandStartNode/i.test(line);
742
+ }
743
+ function clipStderr(text3, maxChars) {
744
+ const trimmed = text3.trim();
745
+ if (trimmed.length <= maxChars) return trimmed;
746
+ return trimmed.slice(0, maxChars);
747
+ }
733
748
  function summarizeTurnStderr(tail, maxChars = 500) {
734
749
  if (tail.length === 0) return "";
750
+ const cursorStartup = [...tail].reverse().find((line) => /cursor startup failed:/i.test(line));
751
+ if (cursorStartup) return clipStderr(cursorStartup, maxChars);
752
+ if (tail.some(looksLikeNestedElectronCrash)) return NESTED_ELECTRON_SUMMARY;
735
753
  const moduleMissing = [...tail].reverse().find((line) => /cannot find module/i.test(line));
736
- if (moduleMissing) {
737
- return moduleMissing.length <= maxChars ? moduleMissing : moduleMissing.slice(0, maxChars);
754
+ if (moduleMissing) return clipStderr(moduleMissing, maxChars);
755
+ const useful = tail.filter((line) => !looksLikeMinifiedJsDump(line));
756
+ if (useful.length === 0 && tail.some(looksLikeMinifiedJsDump)) {
757
+ return MINIFIED_DUMP_SUMMARY;
738
758
  }
739
- const joined = tail.slice(-6).join("\n").trim();
740
- if (joined.length <= maxChars) return joined;
741
- return joined.slice(joined.length - maxChars);
759
+ const joined = (useful.length ? useful : tail).slice(-6).join("\n").trim();
760
+ return clipStderr(joined, maxChars);
742
761
  }
743
762
  function looksLikeInvalidAgentSession(text3) {
744
763
  const lower = text3.trim().toLowerCase();
745
764
  if (!lower) return false;
746
- return /session not found/.test(lower) || /no conversation found/.test(lower) || /conversation .+ not found/.test(lower) || /thread .+ not found/.test(lower) || /unknown session/.test(lower) || /invalid session/.test(lower) || /session .+ (missing|expired|deleted|gone)/.test(lower) || /cannot resume/.test(lower) || /failed to (load|resume|open) session/.test(lower);
765
+ return /session not found/.test(lower) || /no conversation found/.test(lower) || /conversation .+ not found/.test(lower) || /thread .+ not found/.test(lower) || /unknown session/.test(lower) || /invalid session/.test(lower) || /session .+ (missing|expired|deleted|gone)/.test(lower) || /cannot resume/.test(lower) || /failed to (load|resume|open) session/.test(lower) || /corrupt local agent checkpoint/.test(lower) || /missing root blob/.test(lower) || /\bagent\b.{0,120}\bnot found\b/.test(lower);
747
766
  }
748
767
  function looksLikeAgentFailureMessage(text3) {
749
768
  const lower = text3.trim().toLowerCase();
@@ -783,6 +802,12 @@ function humanizeAgentFailDetail(detail) {
783
802
  if (/context.*(too long|exceed)|prompt is too long|conversation too long/.test(lower)) {
784
803
  return `${raw} \u2014 start a new chat or compact context, then retry.`;
785
804
  }
805
+ if (/hascustomhostobject|electroninitializeicuandstartnode|nested chromium/i.test(lower)) {
806
+ return `${raw} \u2014 retry the turn; if it keeps failing, pick another agent.`;
807
+ }
808
+ if (/corrupt local agent checkpoint|missing root blob|truncated crash dump/.test(lower)) {
809
+ return `${raw} \u2014 retry the turn (Sideboard will start a fresh Cursor session).`;
810
+ }
786
811
  return raw;
787
812
  }
788
813
  function formatTurnExitError(exitCode, stderrSummary) {
@@ -798,11 +823,13 @@ function formatTurnExitError(exitCode, stderrSummary) {
798
823
  if (looksLikeAgentFailureMessage(raw)) return detail;
799
824
  return `exit ${code}: ${detail}`;
800
825
  }
801
- var NODE_VERSION_FOOTER;
826
+ var NODE_VERSION_FOOTER, NESTED_ELECTRON_SUMMARY, MINIFIED_DUMP_SUMMARY;
802
827
  var init_error_detail = __esm({
803
828
  "src/agents/error-detail.ts"() {
804
829
  "use strict";
805
830
  NODE_VERSION_FOOTER = /^Node\.js v\d+/i;
831
+ NESTED_ELECTRON_SUMMARY = "Cursor local agent crashed at Electron startup (nested Chromium / HasCustomHostObject)";
832
+ MINIFIED_DUMP_SUMMARY = "Cursor local agent crashed during startup (truncated crash dump)";
806
833
  }
807
834
  });
808
835
 
@@ -1705,261 +1732,9 @@ var init_gh_errors = __esm({
1705
1732
  }
1706
1733
  });
1707
1734
 
1708
- // src/git/git-auth-mode.ts
1709
- function appendIndexedGitConfig(existing, entries) {
1710
- const start = Number.parseInt(String(existing?.GIT_CONFIG_COUNT ?? "0"), 10);
1711
- const count = Number.isFinite(start) && start > 0 ? start : 0;
1712
- const out = {};
1713
- entries.forEach((entry, i) => {
1714
- const n = count + i;
1715
- out[`GIT_CONFIG_KEY_${n}`] = entry.key;
1716
- out[`GIT_CONFIG_VALUE_${n}`] = entry.value;
1717
- });
1718
- out.GIT_CONFIG_COUNT = String(count + entries.length);
1719
- return out;
1720
- }
1721
- function githubAgentGitEnv(existing) {
1722
- return appendIndexedGitConfig(existing, HTTPS_REWRITE);
1723
- }
1724
- function applyGithubGitAuthEnv(existing, opts) {
1725
- const out = {};
1726
- if (opts.mode === "gh" || opts.mode === "token") {
1727
- Object.assign(out, githubAgentGitEnv(existing));
1728
- }
1729
- if (opts.mode === "token") {
1730
- const token = opts.token?.trim();
1731
- if (token && !existing?.GH_TOKEN?.trim()) {
1732
- out.GH_TOKEN = token;
1733
- }
1734
- }
1735
- return out;
1736
- }
1737
- function formatGitAuthModeDirective(mode) {
1738
- switch (mode) {
1739
- case "gh":
1740
- return [
1741
- "Git authentication (Account \u2192 GitHub mode: gh CLI):",
1742
- "- This process rewrites `git@github.com:` and `ssh://git@github.com/` to HTTPS.",
1743
- "- `gh` supplies credentials (`gh auth git-credential`). Do not switch remotes to SSH.",
1744
- "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
1745
- ].join("\n");
1746
- case "ssh":
1747
- return [
1748
- "Git authentication (Account \u2192 GitHub mode: SSH):",
1749
- "- Keep SSH remotes (`git@github.com:\u2026`). Do not rewrite them to HTTPS.",
1750
- "- Use this Mac\u2019s SSH agent / keys. If push fails with `Permission denied (publickey)`, tell the user to start ssh-agent or switch Account \u2192 GitHub to Auto / gh CLI \u2014 do not rewrite remotes yourself.",
1751
- "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below (API still uses `gh`)."
1752
- ].join("\n");
1753
- case "token":
1754
- return [
1755
- "Git authentication (Account \u2192 GitHub mode: personal access token):",
1756
- "- This process rewrites GitHub SSH remotes to HTTPS.",
1757
- "- `GH_TOKEN` is set in the environment. Use HTTPS git and `gh`; do not paste the token into commands or chat.",
1758
- "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
1759
- ].join("\n");
1760
- case "auto":
1761
- default:
1762
- return [
1763
- "Git authentication (Account \u2192 GitHub mode: auto):",
1764
- "- Prefer the existing remote URL (SSH or HTTPS). Do not rewrite remotes unless a push/fetch fails.",
1765
- '- If `git push` fails with `Permission denied (publickey)`, this shell has no ssh-agent. Retry over HTTPS (do not stop): `git -c url.https://github.com/.insteadOf=git@github.com: -c http.extraHeader="AUTHORIZATION: bearer $(gh auth token)" push -u origin HEAD`.',
1766
- "- Then create/update the PR with `gh` as below."
1767
- ].join("\n");
1768
- }
1769
- }
1770
- var HTTPS_REWRITE;
1771
- var init_git_auth_mode = __esm({
1772
- "src/git/git-auth-mode.ts"() {
1773
- "use strict";
1774
- HTTPS_REWRITE = [
1775
- { key: "url.https://github.com/.insteadOf", value: "git@github.com:" },
1776
- { key: "url.https://github.com/.insteadOf", value: "ssh://git@github.com/" },
1777
- { key: "credential.https://github.com.helper", value: "!gh auth git-credential" }
1778
- ];
1779
- }
1780
- });
1781
-
1782
- // src/agents/path.ts
1783
- function prependPathDir(env, dir) {
1784
- if (!dir || !(0, import_node_fs8.existsSync)(dir)) return;
1785
- const current = env.PATH ?? "";
1786
- const parts = current.split(import_node_path8.delimiter).filter(Boolean);
1787
- if (parts.includes(dir)) {
1788
- env.PATH = current;
1789
- return;
1790
- }
1791
- env.PATH = [dir, ...parts].join(import_node_path8.delimiter);
1792
- }
1793
- function conductorBundledBinDir(home = process.env.HOME || process.env.USERPROFILE || (0, import_node_os3.homedir)()) {
1794
- return (0, import_node_path8.join)(home, "Library", "Application Support", "com.conductor.app", "bin");
1795
- }
1796
- function isConductorBundledCli(filePath) {
1797
- const p = (filePath ?? "").replace(/\\/g, "/");
1798
- return p.includes("/com.conductor.app/bin/") || p.endsWith("/com.conductor.app/bin");
1799
- }
1800
- function ensureAgentPath(env = process.env) {
1801
- const home = env.HOME || env.USERPROFILE || (0, import_node_os3.homedir)();
1802
- const current = env.PATH ?? "";
1803
- const parts = current.split(import_node_path8.delimiter).filter(Boolean);
1804
- const seen = new Set(parts);
1805
- const extras = [
1806
- ...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path8.join)(home, rel)),
1807
- "/opt/homebrew/bin",
1808
- "/usr/local/bin",
1809
- // Keep after Homebrew/npm so a user-installed CLI still wins.
1810
- conductorBundledBinDir(home)
1811
- ];
1812
- for (const dir of extras.reverse()) {
1813
- if (!dir || seen.has(dir) || !(0, import_node_fs8.existsSync)(dir)) continue;
1814
- parts.unshift(dir);
1815
- seen.add(dir);
1816
- }
1817
- const next = parts.join(import_node_path8.delimiter);
1818
- env.PATH = next;
1819
- return next;
1820
- }
1821
- function enrichPathWithNpmGlobalBin(env = process.env) {
1822
- ensureAgentPath(env);
1823
- try {
1824
- const prefix = (0, import_node_child_process2.execFileSync)("npm", ["prefix", "-g"], {
1825
- encoding: "utf8",
1826
- env: { ...process.env, ...env },
1827
- timeout: 8e3,
1828
- stdio: ["ignore", "pipe", "ignore"]
1829
- }).trim().split(/\r?\n/).find(Boolean);
1830
- if (prefix) {
1831
- const binDir = process.platform === "win32" ? prefix : (0, import_node_path8.join)(prefix, "bin");
1832
- prependPathDir(env, binDir);
1833
- }
1834
- } catch {
1835
- }
1836
- return env.PATH ?? "";
1837
- }
1838
- function posixShellSingleQuote(value) {
1839
- return `'${value.replace(/'/g, `'\\''`)}'`;
1840
- }
1841
- function resolveCommandBinarySync(command, whichPath) {
1842
- const trimmed = command.trim();
1843
- if (!trimmed) return trimmed;
1844
- const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
1845
- if (!match) return trimmed;
1846
- const bin = match[1];
1847
- const rest = match[2] ?? "";
1848
- if (bin.includes("/") || bin.includes("\\")) return trimmed;
1849
- const abs = whichPath?.trim().split(/\r?\n/).find(Boolean);
1850
- if (!abs) return trimmed;
1851
- return `${abs}${rest}`;
1852
- }
1853
- function withExportedPath(command, pathValue) {
1854
- const trimmed = command.trim();
1855
- if (!trimmed) return trimmed;
1856
- if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
1857
- return `export PATH=${posixShellSingleQuote(pathValue)} && ${trimmed}`;
1858
- }
1859
- var import_node_fs8, import_node_child_process2, import_node_os3, import_node_path8, EXTRA_BIN_DIRS;
1860
- var init_path = __esm({
1861
- "src/agents/path.ts"() {
1862
- "use strict";
1863
- import_node_fs8 = require("fs");
1864
- import_node_child_process2 = require("child_process");
1865
- import_node_os3 = require("os");
1866
- import_node_path8 = require("path");
1867
- EXTRA_BIN_DIRS = [
1868
- ".local/bin",
1869
- ".cargo/bin",
1870
- ".nvm/current/bin",
1871
- ".asdf/shims",
1872
- ".volta/bin",
1873
- ".npm-global/bin",
1874
- // fnm default alias (common when shell init is skipped)
1875
- ".local/share/fnm/aliases/default/bin",
1876
- // pnpm / npm global bins (where `@brightsy/cli` typically lands)
1877
- "Library/pnpm",
1878
- ".pnpm"
1879
- ];
1880
- }
1881
- });
1882
-
1883
- // src/git/run.ts
1884
- var run_exports = {};
1885
- __export(run_exports, {
1886
- gh: () => gh,
1887
- git: () => git,
1888
- resolveGhAuthToken: () => resolveGhAuthToken,
1889
- run: () => run
1890
- });
1891
- async function run(file, args, opts) {
1892
- ensureAgentPath();
1893
- try {
1894
- const result = await (0, import_execa.execa)(file, args, {
1895
- cwd: opts?.cwd,
1896
- env: { ...process.env, ...opts?.env },
1897
- reject: opts?.reject ?? true,
1898
- ...opts?.timeoutMs != null ? { timeout: opts.timeoutMs } : {}
1899
- });
1900
- return {
1901
- stdout: result.stdout ?? "",
1902
- stderr: result.stderr ?? "",
1903
- exitCode: result.exitCode ?? 0
1904
- };
1905
- } catch (err) {
1906
- const e = err;
1907
- if (opts?.reject === false) {
1908
- return {
1909
- stdout: String(e.stdout ?? ""),
1910
- stderr: String(e.stderr ?? ""),
1911
- exitCode: e.exitCode ?? 1
1912
- };
1913
- }
1914
- throw err;
1915
- }
1916
- }
1917
- async function git(args, cwd, opts) {
1918
- const prefix = [];
1919
- if (opts?.config) {
1920
- for (const [key, value] of Object.entries(opts.config)) {
1921
- if (!key) continue;
1922
- prefix.push("-c", `${key}=${value}`);
1923
- }
1924
- }
1925
- return run("git", ["--no-pager", ...prefix, ...args], {
1926
- cwd,
1927
- reject: opts?.reject,
1928
- timeoutMs: opts?.timeoutMs,
1929
- // Never block forever on a credential/SSH prompt inside MCP / Electron.
1930
- env: {
1931
- GIT_TERMINAL_PROMPT: "0",
1932
- GIT_ASKPASS: process.env.GIT_ASKPASS || "echo",
1933
- GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND || "ssh -o BatchMode=yes -o ConnectTimeout=15",
1934
- ...opts?.env
1935
- }
1936
- });
1937
- }
1938
- async function gh(args, cwd, opts) {
1939
- return run("gh", args, {
1940
- cwd,
1941
- reject: opts?.reject,
1942
- timeoutMs: opts?.timeoutMs
1943
- });
1944
- }
1945
- async function resolveGhAuthToken(cwd) {
1946
- const result = await gh(["auth", "token"], cwd, { reject: false });
1947
- if (result.exitCode !== 0) return null;
1948
- const token = result.stdout.trim();
1949
- return token || null;
1950
- }
1951
- var import_execa;
1952
- var init_run = __esm({
1953
- "src/git/run.ts"() {
1954
- "use strict";
1955
- import_execa = require("execa");
1956
- init_path();
1957
- }
1958
- });
1959
-
1960
1735
  // src/store/secret-vault.ts
1961
1736
  function secretVaultPath() {
1962
- return (0, import_node_path9.join)(appDataDir(), "secrets.json");
1737
+ return (0, import_node_path8.join)(appDataDir(), "secrets.json");
1963
1738
  }
1964
1739
  function loadSecretVault() {
1965
1740
  const parsed = readSecureJson(secretVaultPath());
@@ -2003,11 +1778,11 @@ function normalizeVault(raw) {
2003
1778
  }
2004
1779
  return out;
2005
1780
  }
2006
- var import_node_path9;
1781
+ var import_node_path8;
2007
1782
  var init_secret_vault = __esm({
2008
1783
  "src/store/secret-vault.ts"() {
2009
1784
  "use strict";
2010
- import_node_path9 = require("path");
1785
+ import_node_path8 = require("path");
2011
1786
  init_paths();
2012
1787
  init_secure_file();
2013
1788
  }
@@ -2108,10 +1883,10 @@ function toPublicAppSettings(settings) {
2108
1883
  };
2109
1884
  }
2110
1885
  function appSettingsPath() {
2111
- return (0, import_node_path10.join)(appDataDir(), "settings.json");
1886
+ return (0, import_node_path9.join)(appDataDir(), "settings.json");
2112
1887
  }
2113
1888
  function claudeUserSettingsPath() {
2114
- return (0, import_node_path10.join)((0, import_node_os4.homedir)(), ".claude", "settings.json");
1889
+ return (0, import_node_path9.join)((0, import_node_os3.homedir)(), ".claude", "settings.json");
2115
1890
  }
2116
1891
  function normalizeClaude(raw) {
2117
1892
  if (!raw || typeof raw !== "object") return {};
@@ -2354,10 +2129,10 @@ function normalizeSettings(raw) {
2354
2129
  }
2355
2130
  function readSettingsFile() {
2356
2131
  const path = appSettingsPath();
2357
- if (!(0, import_node_fs9.existsSync)(path)) return { ...EMPTY_SETTINGS };
2132
+ if (!(0, import_node_fs8.existsSync)(path)) return { ...EMPTY_SETTINGS };
2358
2133
  try {
2359
2134
  chmodOwnerOnly(path);
2360
- const parsed = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
2135
+ const parsed = JSON.parse((0, import_node_fs8.readFileSync)(path, "utf8"));
2361
2136
  return normalizeSettings(parsed);
2362
2137
  } catch {
2363
2138
  return { ...EMPTY_SETTINGS };
@@ -2742,7 +2517,7 @@ function ensureSlackDeviceIdentity(settings = loadAppSettings()) {
2742
2517
  }
2743
2518
  if (!deviceLabel) {
2744
2519
  try {
2745
- deviceLabel = (0, import_node_os4.hostname)().split(".")[0]?.trim() || "This Mac";
2520
+ deviceLabel = (0, import_node_os3.hostname)().split(".")[0]?.trim() || "This Mac";
2746
2521
  } catch {
2747
2522
  deviceLabel = "This Mac";
2748
2523
  }
@@ -2830,145 +2605,472 @@ function caffeinateWhileRunningEnabled(settings = loadAppSettings()) {
2830
2605
  function caffeinateWhileSlackListenEnabled(settings = loadAppSettings()) {
2831
2606
  return Boolean(settings.advanced.caffeinateWhileSlackListen);
2832
2607
  }
2833
- function caffeinateWhileCloudConnectEnabled(settings = loadAppSettings()) {
2834
- return caffeinateWhileSlackListenEnabled(settings);
2608
+ function caffeinateWhileCloudConnectEnabled(settings = loadAppSettings()) {
2609
+ return caffeinateWhileSlackListenEnabled(settings);
2610
+ }
2611
+ function deleteBranchOnPurgeEnabled(settings = loadAppSettings()) {
2612
+ return Boolean(settings.advanced.deleteBranchOnPurge);
2613
+ }
2614
+ function autoArchiveOnMergeEnabled(settings = loadAppSettings()) {
2615
+ return Boolean(settings.advanced.autoArchiveOnMerge);
2616
+ }
2617
+ function autoCleanupOrphansEnabled(settings = loadAppSettings()) {
2618
+ return Boolean(settings.advanced.autoCleanupOrphans);
2619
+ }
2620
+ function orchestrationQuotaOnLimit(settings = loadAppSettings()) {
2621
+ return settings.advanced.orchestrationQuotaOnLimit ?? "switch_agent";
2622
+ }
2623
+ function orchestrationQuotaFallbackAgent(settings = loadAppSettings()) {
2624
+ const preferred = settings.advanced.orchestrationQuotaFallbackAgent;
2625
+ if (preferred && DEFAULT_AGENTS.has(preferred) && preferred !== "brightsy") {
2626
+ return preferred;
2627
+ }
2628
+ return "cursor";
2629
+ }
2630
+ function maxConcurrentAgents(settings = loadAppSettings()) {
2631
+ const n = settings.advanced.maxConcurrent;
2632
+ if (typeof n === "number" && Number.isFinite(n)) {
2633
+ return Math.max(1, Math.min(32, Math.floor(n)));
2634
+ }
2635
+ return 3;
2636
+ }
2637
+ function resolveClaudeExecutable(settings = loadAppSettings()) {
2638
+ return resolveAgentExecutable("claude", settings);
2639
+ }
2640
+ function resolveAgentExecutable(agent, settings = loadAppSettings()) {
2641
+ const fallback = DEFAULT_CLI_BIN[agent];
2642
+ if (agent === "claude") {
2643
+ const override2 = settings.claude.executablePath?.trim();
2644
+ return override2 || fallback;
2645
+ }
2646
+ if (agent === "codex") {
2647
+ const override2 = settings.codex.executablePath?.trim();
2648
+ return override2 || fallback;
2649
+ }
2650
+ if (agent === "opencode") {
2651
+ const override2 = settings.opencode.executablePath?.trim();
2652
+ return override2 || fallback;
2653
+ }
2654
+ const override = settings.brightsy.executablePath?.trim();
2655
+ return override || fallback;
2656
+ }
2657
+ function updateAgentExecutable(agent, executablePath) {
2658
+ if (agent === "claude") {
2659
+ return updateClaudeSettings({ executablePath });
2660
+ }
2661
+ if (agent === "codex") {
2662
+ return updateCodexSettings({ executablePath });
2663
+ }
2664
+ if (agent === "opencode") {
2665
+ return updateOpencodeSettings({ executablePath });
2666
+ }
2667
+ return updateBrightsySettings({ executablePath });
2668
+ }
2669
+ function claudeChromeEnabled(settings = loadAppSettings()) {
2670
+ return Boolean(settings.claude.chromeEnabled);
2671
+ }
2672
+ function applyAppEnvironment(target = process.env, settings = loadAppSettings()) {
2673
+ for (const [key, value] of Object.entries(settings.environment)) {
2674
+ if (!key || value == null || value === "") continue;
2675
+ if (target[key] == null || target[key] === "") {
2676
+ target[key] = value;
2677
+ }
2678
+ }
2679
+ return target;
2680
+ }
2681
+ function childEnvWithAppSettings(extra) {
2682
+ const settings = loadAppSettings();
2683
+ const env = stripNestedElectronEnv({ ...process.env });
2684
+ applyAppEnvironment(env, settings);
2685
+ if (extra) {
2686
+ for (const [k, v] of Object.entries(extra)) {
2687
+ if (v != null) env[k] = v;
2688
+ }
2689
+ }
2690
+ return env;
2691
+ }
2692
+ function harnessEnvKey(harness) {
2693
+ return HARNESS_ENV_KEYS[harness];
2694
+ }
2695
+ var import_node_crypto3, import_node_fs8, import_node_os3, import_node_path9, HARNESS_ENV_KEYS, DEFAULT_AGENTS, GITHUB_GIT_AUTH_MODES, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, GIT_AUTH_MODES, EMPTY_SETTINGS, DEFAULT_CLI_BIN;
2696
+ var init_app_settings = __esm({
2697
+ "src/store/app-settings.ts"() {
2698
+ "use strict";
2699
+ import_node_crypto3 = require("crypto");
2700
+ import_node_fs8 = require("fs");
2701
+ import_node_os3 = require("os");
2702
+ import_node_path9 = require("path");
2703
+ init_thinking_effort();
2704
+ init_nested_electron_env();
2705
+ init_paths();
2706
+ init_private_file();
2707
+ init_secret_vault();
2708
+ HARNESS_ENV_KEYS = {
2709
+ claude: "ANTHROPIC_API_KEY",
2710
+ codex: "CODEX_API_KEY",
2711
+ cursor: "CURSOR_API_KEY",
2712
+ opencode: null,
2713
+ brightsy: null
2714
+ };
2715
+ DEFAULT_AGENTS = /* @__PURE__ */ new Set([
2716
+ "claude",
2717
+ "codex",
2718
+ "opencode",
2719
+ "brightsy",
2720
+ "cursor"
2721
+ ]);
2722
+ GITHUB_GIT_AUTH_MODES = ["auto", "gh", "ssh", "token"];
2723
+ CLOUD_CONNECT_AGENTS = /* @__PURE__ */ new Set([
2724
+ "claude",
2725
+ "codex",
2726
+ "opencode",
2727
+ "cursor"
2728
+ ]);
2729
+ ISSUE_SOURCES = /* @__PURE__ */ new Set(["linear", "github"]);
2730
+ GIT_AUTH_MODES = new Set(GITHUB_GIT_AUTH_MODES);
2731
+ EMPTY_SETTINGS = {
2732
+ environment: {},
2733
+ claude: {},
2734
+ codex: {},
2735
+ opencode: {},
2736
+ brightsy: {},
2737
+ integrations: {},
2738
+ defaults: {},
2739
+ advanced: {}
2740
+ };
2741
+ DEFAULT_CLI_BIN = {
2742
+ claude: "claude",
2743
+ codex: "codex",
2744
+ opencode: "opencode",
2745
+ brightsy: "brightsy"
2746
+ };
2747
+ }
2748
+ });
2749
+
2750
+ // src/agents/path.ts
2751
+ function prependPathDir(env, dir) {
2752
+ if (!dir || !(0, import_node_fs9.existsSync)(dir)) return;
2753
+ const current = env.PATH ?? "";
2754
+ const parts = current.split(import_node_path10.delimiter).filter(Boolean);
2755
+ if (parts.includes(dir)) {
2756
+ env.PATH = current;
2757
+ return;
2758
+ }
2759
+ env.PATH = [dir, ...parts].join(import_node_path10.delimiter);
2760
+ }
2761
+ function conductorBundledBinDir(home = process.env.HOME || process.env.USERPROFILE || (0, import_node_os4.homedir)()) {
2762
+ return (0, import_node_path10.join)(home, "Library", "Application Support", "com.conductor.app", "bin");
2763
+ }
2764
+ function isConductorBundledCli(filePath) {
2765
+ const p = (filePath ?? "").replace(/\\/g, "/");
2766
+ return p.includes("/com.conductor.app/bin/") || p.endsWith("/com.conductor.app/bin");
2767
+ }
2768
+ function ensureAgentPath(env = process.env) {
2769
+ const home = env.HOME || env.USERPROFILE || (0, import_node_os4.homedir)();
2770
+ const current = env.PATH ?? "";
2771
+ const parts = current.split(import_node_path10.delimiter).filter(Boolean);
2772
+ const seen = new Set(parts);
2773
+ const extras = [
2774
+ ...EXTRA_BIN_DIRS.map((rel) => (0, import_node_path10.join)(home, rel)),
2775
+ "/opt/homebrew/bin",
2776
+ "/usr/local/bin",
2777
+ // Keep after Homebrew/npm so a user-installed CLI still wins.
2778
+ conductorBundledBinDir(home)
2779
+ ];
2780
+ for (const dir of extras.reverse()) {
2781
+ if (!dir || seen.has(dir) || !(0, import_node_fs9.existsSync)(dir)) continue;
2782
+ parts.unshift(dir);
2783
+ seen.add(dir);
2784
+ }
2785
+ const next = parts.join(import_node_path10.delimiter);
2786
+ env.PATH = next;
2787
+ return next;
2788
+ }
2789
+ function enrichPathWithNpmGlobalBin(env = process.env) {
2790
+ ensureAgentPath(env);
2791
+ try {
2792
+ const prefix = (0, import_node_child_process2.execFileSync)("npm", ["prefix", "-g"], {
2793
+ encoding: "utf8",
2794
+ env: { ...process.env, ...env },
2795
+ timeout: 8e3,
2796
+ stdio: ["ignore", "pipe", "ignore"]
2797
+ }).trim().split(/\r?\n/).find(Boolean);
2798
+ if (prefix) {
2799
+ const binDir = process.platform === "win32" ? prefix : (0, import_node_path10.join)(prefix, "bin");
2800
+ prependPathDir(env, binDir);
2801
+ }
2802
+ } catch {
2803
+ }
2804
+ return env.PATH ?? "";
2805
+ }
2806
+ function posixShellSingleQuote(value) {
2807
+ return `'${value.replace(/'/g, `'\\''`)}'`;
2808
+ }
2809
+ function resolveCommandBinarySync(command, whichPath) {
2810
+ const trimmed = command.trim();
2811
+ if (!trimmed) return trimmed;
2812
+ const match = /^(\S+)(\s[\s\S]*)?$/.exec(trimmed);
2813
+ if (!match) return trimmed;
2814
+ const bin = match[1];
2815
+ const rest = match[2] ?? "";
2816
+ if (bin.includes("/") || bin.includes("\\")) return trimmed;
2817
+ const abs = whichPath?.trim().split(/\r?\n/).find(Boolean);
2818
+ if (!abs) return trimmed;
2819
+ return `${abs}${rest}`;
2820
+ }
2821
+ function withExportedPath(command, pathValue) {
2822
+ const trimmed = command.trim();
2823
+ if (!trimmed) return trimmed;
2824
+ if (/^(export\s+PATH=|PATH=)/.test(trimmed)) return trimmed;
2825
+ return `export PATH=${posixShellSingleQuote(pathValue)} && ${trimmed}`;
2826
+ }
2827
+ var import_node_fs9, import_node_child_process2, import_node_os4, import_node_path10, EXTRA_BIN_DIRS;
2828
+ var init_path = __esm({
2829
+ "src/agents/path.ts"() {
2830
+ "use strict";
2831
+ import_node_fs9 = require("fs");
2832
+ import_node_child_process2 = require("child_process");
2833
+ import_node_os4 = require("os");
2834
+ import_node_path10 = require("path");
2835
+ EXTRA_BIN_DIRS = [
2836
+ ".local/bin",
2837
+ ".cargo/bin",
2838
+ ".nvm/current/bin",
2839
+ ".asdf/shims",
2840
+ ".volta/bin",
2841
+ ".npm-global/bin",
2842
+ // fnm default alias (common when shell init is skipped)
2843
+ ".local/share/fnm/aliases/default/bin",
2844
+ // pnpm / npm global bins (where `@brightsy/cli` typically lands)
2845
+ "Library/pnpm",
2846
+ ".pnpm"
2847
+ ];
2848
+ }
2849
+ });
2850
+
2851
+ // src/git/run.ts
2852
+ var run_exports = {};
2853
+ __export(run_exports, {
2854
+ gh: () => gh,
2855
+ git: () => git,
2856
+ resolveGhAuthToken: () => resolveGhAuthToken,
2857
+ run: () => run
2858
+ });
2859
+ async function run(file, args, opts) {
2860
+ ensureAgentPath();
2861
+ try {
2862
+ const result = await (0, import_execa.execa)(file, args, {
2863
+ cwd: opts?.cwd,
2864
+ env: { ...process.env, ...opts?.env },
2865
+ reject: opts?.reject ?? true,
2866
+ ...opts?.timeoutMs != null ? { timeout: opts.timeoutMs } : {}
2867
+ });
2868
+ return {
2869
+ stdout: result.stdout ?? "",
2870
+ stderr: result.stderr ?? "",
2871
+ exitCode: result.exitCode ?? 0
2872
+ };
2873
+ } catch (err) {
2874
+ const e = err;
2875
+ if (opts?.reject === false) {
2876
+ return {
2877
+ stdout: String(e.stdout ?? ""),
2878
+ stderr: String(e.stderr ?? ""),
2879
+ exitCode: e.exitCode ?? 1
2880
+ };
2881
+ }
2882
+ throw err;
2883
+ }
2884
+ }
2885
+ async function git(args, cwd, opts) {
2886
+ const prefix = [];
2887
+ if (opts?.config) {
2888
+ for (const [key, value] of Object.entries(opts.config)) {
2889
+ if (!key) continue;
2890
+ prefix.push("-c", `${key}=${value}`);
2891
+ }
2892
+ }
2893
+ return run("git", ["--no-pager", ...prefix, ...args], {
2894
+ cwd,
2895
+ reject: opts?.reject,
2896
+ timeoutMs: opts?.timeoutMs,
2897
+ // Never block forever on a credential/SSH prompt inside MCP / Electron.
2898
+ env: {
2899
+ GIT_TERMINAL_PROMPT: "0",
2900
+ GIT_ASKPASS: process.env.GIT_ASKPASS || "echo",
2901
+ SSH_ASKPASS: process.env.SSH_ASKPASS || "echo",
2902
+ GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND || "ssh -o BatchMode=yes -o ConnectTimeout=15",
2903
+ GCM_INTERACTIVE: "never",
2904
+ GH_PROMPT_DISABLED: "1",
2905
+ ...opts?.env
2906
+ }
2907
+ });
2835
2908
  }
2836
- function deleteBranchOnPurgeEnabled(settings = loadAppSettings()) {
2837
- return Boolean(settings.advanced.deleteBranchOnPurge);
2909
+ async function gh(args, cwd, opts) {
2910
+ return run("gh", args, {
2911
+ cwd,
2912
+ reject: opts?.reject,
2913
+ timeoutMs: opts?.timeoutMs,
2914
+ env: {
2915
+ GH_PROMPT_DISABLED: "1",
2916
+ GIT_TERMINAL_PROMPT: "0"
2917
+ }
2918
+ });
2838
2919
  }
2839
- function autoArchiveOnMergeEnabled(settings = loadAppSettings()) {
2840
- return Boolean(settings.advanced.autoArchiveOnMerge);
2920
+ async function resolveGhAuthToken(cwd) {
2921
+ const result = await gh(["auth", "token"], cwd, { reject: false, timeoutMs: 8e3 });
2922
+ if (result.exitCode !== 0) return null;
2923
+ const token = result.stdout.trim();
2924
+ return token || null;
2841
2925
  }
2842
- function autoCleanupOrphansEnabled(settings = loadAppSettings()) {
2843
- return Boolean(settings.advanced.autoCleanupOrphans);
2926
+ var import_execa;
2927
+ var init_run = __esm({
2928
+ "src/git/run.ts"() {
2929
+ "use strict";
2930
+ import_execa = require("execa");
2931
+ init_path();
2932
+ }
2933
+ });
2934
+
2935
+ // src/git/git-auth-mode.ts
2936
+ function nonInteractiveGitProcessEnv() {
2937
+ return {
2938
+ GIT_TERMINAL_PROMPT: "0",
2939
+ GIT_ASKPASS: process.env.GIT_ASKPASS?.trim() || "echo",
2940
+ SSH_ASKPASS: process.env.SSH_ASKPASS?.trim() || "echo",
2941
+ GIT_SSH_COMMAND: process.env.GIT_SSH_COMMAND?.trim() || "ssh -o BatchMode=yes -o ConnectTimeout=15",
2942
+ GCM_INTERACTIVE: "never",
2943
+ GH_PROMPT_DISABLED: "1"
2944
+ };
2844
2945
  }
2845
- function orchestrationQuotaOnLimit(settings = loadAppSettings()) {
2846
- return settings.advanced.orchestrationQuotaOnLimit ?? "switch_agent";
2946
+ function appendIndexedGitConfig(existing, entries) {
2947
+ const start = Number.parseInt(String(existing?.GIT_CONFIG_COUNT ?? "0"), 10);
2948
+ const count = Number.isFinite(start) && start > 0 ? start : 0;
2949
+ const out = {};
2950
+ entries.forEach((entry, i) => {
2951
+ const n = count + i;
2952
+ out[`GIT_CONFIG_KEY_${n}`] = entry.key;
2953
+ out[`GIT_CONFIG_VALUE_${n}`] = entry.value;
2954
+ });
2955
+ out.GIT_CONFIG_COUNT = String(count + entries.length);
2956
+ return out;
2847
2957
  }
2848
- function orchestrationQuotaFallbackAgent(settings = loadAppSettings()) {
2849
- const preferred = settings.advanced.orchestrationQuotaFallbackAgent;
2850
- if (preferred && DEFAULT_AGENTS.has(preferred) && preferred !== "brightsy") {
2851
- return preferred;
2852
- }
2853
- return "cursor";
2958
+ function githubAgentGitEnv(existing) {
2959
+ return appendIndexedGitConfig(existing, HTTPS_REWRITE);
2854
2960
  }
2855
- function maxConcurrentAgents(settings = loadAppSettings()) {
2856
- const n = settings.advanced.maxConcurrent;
2857
- if (typeof n === "number" && Number.isFinite(n)) {
2858
- return Math.max(1, Math.min(32, Math.floor(n)));
2859
- }
2860
- return 3;
2961
+ function githubHttpsBearerEnv(existing, token) {
2962
+ const rewrite = githubAgentGitEnv(existing);
2963
+ const header = appendIndexedGitConfig(
2964
+ { ...existing, ...rewrite },
2965
+ [
2966
+ {
2967
+ key: "http.https://github.com/.extraHeader",
2968
+ value: `AUTHORIZATION: bearer ${token}`
2969
+ }
2970
+ ]
2971
+ );
2972
+ return { ...rewrite, ...header };
2861
2973
  }
2862
- function resolveClaudeExecutable(settings = loadAppSettings()) {
2863
- return resolveAgentExecutable("claude", settings);
2974
+ function applyGithubGitAuthEnv(existing, opts) {
2975
+ const out = { ...nonInteractiveGitProcessEnv() };
2976
+ if (opts.mode === "ssh") return out;
2977
+ const existingToken = existing?.GH_TOKEN?.trim() || "";
2978
+ const provided = existingToken ? "" : opts.token?.trim() || "";
2979
+ const token = existingToken || provided;
2980
+ Object.assign(
2981
+ out,
2982
+ token ? githubHttpsBearerEnv(existing, token) : githubAgentGitEnv(existing)
2983
+ );
2984
+ if (provided) out.GH_TOKEN = provided;
2985
+ return out;
2864
2986
  }
2865
- function resolveAgentExecutable(agent, settings = loadAppSettings()) {
2866
- const fallback = DEFAULT_CLI_BIN[agent];
2867
- if (agent === "claude") {
2868
- const override2 = settings.claude.executablePath?.trim();
2869
- return override2 || fallback;
2870
- }
2871
- if (agent === "codex") {
2872
- const override2 = settings.codex.executablePath?.trim();
2873
- return override2 || fallback;
2874
- }
2875
- if (agent === "opencode") {
2876
- const override2 = settings.opencode.executablePath?.trim();
2877
- return override2 || fallback;
2987
+ async function resolveGithubAgentToken(mode, cwd) {
2988
+ if (mode === "ssh") return null;
2989
+ if (mode === "token") return getGithubPat();
2990
+ try {
2991
+ return await resolveGhAuthToken(cwd);
2992
+ } catch {
2993
+ return null;
2878
2994
  }
2879
- const override = settings.brightsy.executablePath?.trim();
2880
- return override || fallback;
2881
2995
  }
2882
- function updateAgentExecutable(agent, executablePath) {
2883
- if (agent === "claude") {
2884
- return updateClaudeSettings({ executablePath });
2885
- }
2886
- if (agent === "codex") {
2887
- return updateCodexSettings({ executablePath });
2996
+ async function resolveAgentGitAuthEnv(existing, opts) {
2997
+ let mode = "auto";
2998
+ if (opts?.mode) {
2999
+ mode = opts.mode;
3000
+ } else {
3001
+ try {
3002
+ mode = getGithubGitAuthMode();
3003
+ } catch {
3004
+ mode = "auto";
3005
+ }
2888
3006
  }
2889
- if (agent === "opencode") {
2890
- return updateOpencodeSettings({ executablePath });
3007
+ const cwd = opts?.cwd?.trim() || process.cwd();
3008
+ let token = null;
3009
+ try {
3010
+ token = await resolveGithubAgentToken(mode, cwd);
3011
+ } catch {
3012
+ token = null;
2891
3013
  }
2892
- return updateBrightsySettings({ executablePath });
2893
- }
2894
- function claudeChromeEnabled(settings = loadAppSettings()) {
2895
- return Boolean(settings.claude.chromeEnabled);
3014
+ return applyGithubGitAuthEnv(existing, { mode, token });
2896
3015
  }
2897
- function applyAppEnvironment(target = process.env, settings = loadAppSettings()) {
2898
- for (const [key, value] of Object.entries(settings.environment)) {
2899
- if (!key || value == null || value === "") continue;
2900
- if (target[key] == null || target[key] === "") {
2901
- target[key] = value;
2902
- }
3016
+ function codexUnattendedGitConfigArgs(sandbox) {
3017
+ const args = [
3018
+ "-c",
3019
+ 'shell_environment_policy.inherit="all"',
3020
+ "-c",
3021
+ "shell_environment_policy.ignore_default_excludes=true"
3022
+ ];
3023
+ if (sandbox === "workspace-write") {
3024
+ args.push("-c", "sandbox_workspace_write.network_access=true");
2903
3025
  }
2904
- return target;
3026
+ return args;
2905
3027
  }
2906
- function childEnvWithAppSettings(extra) {
2907
- const settings = loadAppSettings();
2908
- const env = stripNestedElectronEnv({ ...process.env });
2909
- applyAppEnvironment(env, settings);
2910
- if (extra) {
2911
- for (const [k, v] of Object.entries(extra)) {
2912
- if (v != null) env[k] = v;
2913
- }
3028
+ function formatGitAuthModeDirective(mode) {
3029
+ switch (mode) {
3030
+ case "gh":
3031
+ return [
3032
+ "Git authentication (Account \u2192 GitHub mode: gh CLI):",
3033
+ "- This process rewrites `git@github.com:` and `ssh://git@github.com/` to HTTPS.",
3034
+ "- `GH_TOKEN` is injected from `gh auth token` (no macOS Keychain prompts). Do not switch remotes to SSH.",
3035
+ "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
3036
+ ].join("\n");
3037
+ case "ssh":
3038
+ return [
3039
+ "Git authentication (Account \u2192 GitHub mode: SSH):",
3040
+ "- Keep SSH remotes (`git@github.com:\u2026`). Do not rewrite them to HTTPS.",
3041
+ "- 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 Account \u2192 GitHub to Auto / gh CLI \u2014 do not rewrite remotes yourself.",
3042
+ "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below (API still uses `gh`)."
3043
+ ].join("\n");
3044
+ case "token":
3045
+ return [
3046
+ "Git authentication (Account \u2192 GitHub mode: personal access token):",
3047
+ "- This process rewrites GitHub SSH remotes to HTTPS.",
3048
+ "- `GH_TOKEN` is set in the environment. Use HTTPS git and `gh`; do not paste the token into commands or chat.",
3049
+ "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
3050
+ ].join("\n");
3051
+ case "auto":
3052
+ default:
3053
+ return [
3054
+ "Git authentication (Account \u2192 GitHub mode: auto):",
3055
+ "- This process rewrites GitHub SSH remotes to HTTPS and authenticates with `GH_TOKEN` from `gh` so git/ssh never prompt the macOS Keychain (required for Slack / unattended Cursor).",
3056
+ "- Do not switch remotes to SSH. Push with `git push -u origin HEAD`.",
3057
+ "- If HTTPS auth fails, tell the user to run `gh auth login` on this Mac or set Account \u2192 GitHub to a PAT \u2014 do not wait for a Keychain dialog."
3058
+ ].join("\n");
2914
3059
  }
2915
- return env;
2916
- }
2917
- function harnessEnvKey(harness) {
2918
- return HARNESS_ENV_KEYS[harness];
2919
3060
  }
2920
- var import_node_crypto3, import_node_fs9, import_node_os4, import_node_path10, HARNESS_ENV_KEYS, DEFAULT_AGENTS, GITHUB_GIT_AUTH_MODES, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, GIT_AUTH_MODES, EMPTY_SETTINGS, DEFAULT_CLI_BIN;
2921
- var init_app_settings = __esm({
2922
- "src/store/app-settings.ts"() {
3061
+ var HTTPS_REWRITE;
3062
+ var init_git_auth_mode = __esm({
3063
+ "src/git/git-auth-mode.ts"() {
2923
3064
  "use strict";
2924
- import_node_crypto3 = require("crypto");
2925
- import_node_fs9 = require("fs");
2926
- import_node_os4 = require("os");
2927
- import_node_path10 = require("path");
2928
- init_thinking_effort();
2929
- init_nested_electron_env();
2930
- init_paths();
2931
- init_private_file();
2932
- init_secret_vault();
2933
- HARNESS_ENV_KEYS = {
2934
- claude: "ANTHROPIC_API_KEY",
2935
- codex: "CODEX_API_KEY",
2936
- cursor: "CURSOR_API_KEY",
2937
- opencode: null,
2938
- brightsy: null
2939
- };
2940
- DEFAULT_AGENTS = /* @__PURE__ */ new Set([
2941
- "claude",
2942
- "codex",
2943
- "opencode",
2944
- "brightsy",
2945
- "cursor"
2946
- ]);
2947
- GITHUB_GIT_AUTH_MODES = ["auto", "gh", "ssh", "token"];
2948
- CLOUD_CONNECT_AGENTS = /* @__PURE__ */ new Set([
2949
- "claude",
2950
- "codex",
2951
- "opencode",
2952
- "cursor"
2953
- ]);
2954
- ISSUE_SOURCES = /* @__PURE__ */ new Set(["linear", "github"]);
2955
- GIT_AUTH_MODES = new Set(GITHUB_GIT_AUTH_MODES);
2956
- EMPTY_SETTINGS = {
2957
- environment: {},
2958
- claude: {},
2959
- codex: {},
2960
- opencode: {},
2961
- brightsy: {},
2962
- integrations: {},
2963
- defaults: {},
2964
- advanced: {}
2965
- };
2966
- DEFAULT_CLI_BIN = {
2967
- claude: "claude",
2968
- codex: "codex",
2969
- opencode: "opencode",
2970
- brightsy: "brightsy"
2971
- };
3065
+ init_app_settings();
3066
+ init_run();
3067
+ HTTPS_REWRITE = [
3068
+ { key: "url.https://github.com/.insteadOf", value: "git@github.com:" },
3069
+ { key: "url.https://github.com/.insteadOf", value: "ssh://git@github.com/" },
3070
+ // Empty helper disables ~/.gitconfig osxkeychain so GUI agents cannot pop
3071
+ // "git-credential-osxkeychain wants to use the keychain".
3072
+ { key: "credential.helper", value: "" }
3073
+ ];
2972
3074
  }
2973
3075
  });
2974
3076
 
@@ -3429,7 +3531,7 @@ async function originGhRepoEnv(cwd, opts) {
3429
3531
  await ensureGhPreferOrigin(cwd);
3430
3532
  const slug = await resolveGithubRepoSlug(cwd);
3431
3533
  const mode = opts?.mode ?? getGithubGitAuthMode();
3432
- const token = mode === "token" ? getGithubPat() : null;
3534
+ const token = mode === "token" ? getGithubPat() : mode === "ssh" ? null : await resolveGhAuthToken(cwd);
3433
3535
  return {
3434
3536
  ...applyGithubGitAuthEnv(opts?.env, { mode, token }),
3435
3537
  ...slug ? { GH_REPO: slug } : {}
@@ -5605,7 +5707,12 @@ function applyNodeLaunch(launch, args) {
5605
5707
  return { file: launch.file, args, env: launch.env };
5606
5708
  }
5607
5709
  const wrapped = wrapElectronAsNodeLaunch(launch.file, args);
5608
- return { file: wrapped.file, args: wrapped.args, env: launch.env };
5710
+ if (process.platform === "win32") {
5711
+ return { file: wrapped.file, args: wrapped.args, env: launch.env };
5712
+ }
5713
+ const env = { ...launch.env };
5714
+ delete env.ELECTRON_RUN_AS_NODE;
5715
+ return { file: wrapped.file, args: wrapped.args, env };
5609
5716
  }
5610
5717
  async function resolveNodeLaunch(scriptPath) {
5611
5718
  if (isAsarPath(scriptPath)) {
@@ -5782,6 +5889,10 @@ async function buildInjectedMcpServers(opts) {
5782
5889
  if (orchId) {
5783
5890
  sideboard.env.SIDEBOARD_ORCHESTRATOR_THREAD_ID = orchId;
5784
5891
  }
5892
+ try {
5893
+ Object.assign(sideboard.env, await resolveAgentGitAuthEnv(sideboard.env));
5894
+ } catch {
5895
+ }
5785
5896
  servers.push(sideboard);
5786
5897
  }
5787
5898
  if (opts.includeBrightsy && isBrightsyConnected()) {
@@ -5804,10 +5915,19 @@ async function buildInjectedMcpServers(opts) {
5804
5915
  function toCursorMcpServers(servers) {
5805
5916
  const out = {};
5806
5917
  for (const s of servers) {
5918
+ const env = s.env ? { ...s.env } : void 0;
5919
+ if (env) delete env.ELECTRON_RUN_AS_NODE;
5920
+ let command = s.command;
5921
+ let args = s.args;
5922
+ if (process.platform !== "win32" && !isStrippedElectronLaunch(command, args)) {
5923
+ const wrapped = wrapElectronAsNodeLaunch(command, args ?? []);
5924
+ command = wrapped.file;
5925
+ args = wrapped.args;
5926
+ }
5807
5927
  out[s.name] = {
5808
- command: s.command,
5809
- ...s.args ? { args: s.args } : {},
5810
- ...s.env ? { env: s.env } : {}
5928
+ command,
5929
+ ...args && args.length > 0 ? { args } : {},
5930
+ ...env && Object.keys(env).length > 0 ? { env } : {}
5811
5931
  };
5812
5932
  }
5813
5933
  return out;
@@ -5874,6 +5994,8 @@ var init_injected_mcp = __esm({
5874
5994
  init_app_settings();
5875
5995
  init_paths();
5876
5996
  init_node_launch();
5997
+ init_nested_electron_env();
5998
+ init_git_auth_mode();
5877
5999
  import_meta = {};
5878
6000
  SIDEBOARD_MCP_ALLOWED_TOOLS = [
5879
6001
  "mcp__sideboard",
@@ -6413,6 +6535,7 @@ var init_codex = __esm({
6413
6535
  init_global_workspace();
6414
6536
  init_error_detail();
6415
6537
  init_usage();
6538
+ init_git_auth_mode();
6416
6539
  init_injected_mcp();
6417
6540
  init_turn_input();
6418
6541
  init_types();
@@ -6504,6 +6627,8 @@ var init_codex = __esm({
6504
6627
  // `codex exec` rejects `--ask-for-approval` (global-only on newer CLIs).
6505
6628
  "-c",
6506
6629
  'approval_policy="never"',
6630
+ // Seatbelt cannot use the login Keychain; default policy also strips GH_TOKEN.
6631
+ ...codexUnattendedGitConfigArgs(mode.codexSandbox),
6507
6632
  ...model ? ["--model", model] : [],
6508
6633
  ...mcpOverrides
6509
6634
  ];
@@ -8783,6 +8908,7 @@ init_error_detail();
8783
8908
  var import_node_readline = require("readline");
8784
8909
  var import_execa2 = require("execa");
8785
8910
  init_worktree();
8911
+ init_git_auth_mode();
8786
8912
  init_app_settings();
8787
8913
  init_global_workspace();
8788
8914
  init_brightsy();
@@ -9045,14 +9171,16 @@ async function spawnAgentTurn(thread, input, onEvent) {
9045
9171
  );
9046
9172
  }
9047
9173
  const env = childEnvWithAppSettings(cmd.env);
9048
- if (!isOrchestratorThread(thread)) {
9049
- try {
9174
+ try {
9175
+ if (isOrchestratorThread(thread)) {
9176
+ Object.assign(env, await resolveAgentGitAuthEnv(env));
9177
+ } else {
9050
9178
  const originEnv = await originGhRepoEnv(thread.worktreePath, { env });
9051
9179
  Object.assign(env, originEnv);
9052
- } catch (err) {
9053
- const detail = err instanceof Error ? err.message : String(err);
9054
- console.warn(`[sideboard] originGhRepoEnv failed (${thread.worktreePath}): ${detail}`);
9055
9180
  }
9181
+ } catch (err) {
9182
+ const detail = err instanceof Error ? err.message : String(err);
9183
+ console.warn(`[sideboard] git auth env failed (${thread.worktreePath}): ${detail}`);
9056
9184
  }
9057
9185
  const child = (0, import_execa2.execa)(cmd.file, cmd.args, {
9058
9186
  cwd: cmd.cwd,
@@ -9266,6 +9394,7 @@ var import_execa3 = require("execa");
9266
9394
  var import_node_readline2 = require("readline");
9267
9395
  init_settings();
9268
9396
  init_nested_electron_env();
9397
+ init_git_auth_mode();
9269
9398
  init_nested_electron_env();
9270
9399
  var PORT_RANGE_SIZE = 10;
9271
9400
  function matchSimpleGlob(pattern, name) {
@@ -9439,6 +9568,10 @@ async function spawnWorkspaceScript(command, opts) {
9439
9568
  },
9440
9569
  loginEnv
9441
9570
  );
9571
+ try {
9572
+ Object.assign(env, await resolveAgentGitAuthEnv(env, { cwd: opts.worktreePath }));
9573
+ } catch {
9574
+ }
9442
9575
  const shell = process.platform === "darwin" ? "zsh" : "bash";
9443
9576
  const child = (0, import_execa3.execa)(shell, ["-lc", command], {
9444
9577
  cwd: opts.worktreePath,
@@ -13412,7 +13545,7 @@ var Orchestrator = class {
13412
13545
  }
13413
13546
  let lastStderr = summarizeTurnStderr(stderrTail);
13414
13547
  let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
13415
- if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && looksLikeInvalidAgentSession(detail) && this.requireThread(threadId).sessionId && this.requireThread(threadId).agent !== "cursor" && this.requireThread(threadId).agent !== "brightsy") {
13548
+ if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && looksLikeInvalidAgentSession(detail) && this.requireThread(threadId).sessionId && this.requireThread(threadId).agent !== "brightsy") {
13416
13549
  updateThread(threadId, { sessionId: null });
13417
13550
  pushTurnStderr(
13418
13551
  stderrTail,