@sideboard-ai/core 0.1.77 → 0.1.79

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 (31) hide show
  1. package/dist/{agents-XJV6J3K2.js → agents-EWPHOM6Y.js} +6 -6
  2. package/dist/{agents-LKUHPPEQ.js → agents-GRAPUXWW.js} +5 -5
  3. package/dist/{app-settings-3TLA2EJ7.js → app-settings-6IOQYGBK.js} +7 -1
  4. package/dist/{app-settings-P42Z3VOB.js → app-settings-NILNWNNQ.js} +7 -1
  5. package/dist/{chunk-VERLUKN2.js → chunk-5LXWTU3J.js} +6 -6
  6. package/dist/{chunk-2ZIPJRJE.js → chunk-5VTWBI3I.js} +2 -2
  7. package/dist/{chunk-GWTLKXXP.js → chunk-6T2SKGCS.js} +3 -3
  8. package/dist/{chunk-OE7YBB5X.js → chunk-FUUVPN4A.js} +108 -3
  9. package/dist/{chunk-EEKSZTMU.js → chunk-HCGEFU3J.js} +5 -5
  10. package/dist/{chunk-XJNCWYFR.js → chunk-HSLQXL6M.js} +3 -3
  11. package/dist/{chunk-W7YOTDVO.js → chunk-IFPN6TER.js} +47 -2
  12. package/dist/{chunk-2FJI5JXX.js → chunk-P33ZQ7ZC.js} +3 -3
  13. package/dist/{chunk-75U65MBE.js → chunk-PW5YHHP3.js} +2 -2
  14. package/dist/{chunk-QD37IILI.js → chunk-SHCR6RPU.js} +3 -3
  15. package/dist/{chunk-PTJJIQK7.js → chunk-XKNBSYA7.js} +47 -2
  16. package/dist/{chunk-MF2YMEGD.js → chunk-YVVXWWCC.js} +110 -3
  17. package/dist/{coordinator-prompt-LLFJUO2R.js → coordinator-prompt-2OVON2LQ.js} +4 -4
  18. package/dist/{coordinator-prompt-MRCMMRSF.js → coordinator-prompt-K2R34A5T.js} +3 -3
  19. package/dist/{global-workspace-TWHMYLTZ.js → global-workspace-RI367AM6.js} +4 -4
  20. package/dist/{global-workspace-YNMNO4CL.js → global-workspace-VG44RZUH.js} +5 -5
  21. package/dist/index.cjs +500 -178
  22. package/dist/index.d.cts +83 -8
  23. package/dist/index.d.ts +83 -8
  24. package/dist/index.js +344 -179
  25. package/dist/mcp/run-stdio.cjs +2439 -2228
  26. package/dist/mcp/run-stdio.js +156 -100
  27. package/dist/{workspaces-GQ4XKBD3.js → workspaces-BQWQEZWE.js} +5 -5
  28. package/dist/{workspaces-O3U5BENH.js → workspaces-TYPJNUSM.js} +6 -6
  29. package/dist/{worktree-VOCH3WS3.js → worktree-5VLLFI5W.js} +5 -1
  30. package/dist/{worktree-KWXHMKRN.js → worktree-UNSOCYZU.js} +5 -1
  31. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -79,11 +79,11 @@ var init_thinking_effort = __esm({
79
79
  });
80
80
 
81
81
  // src/hook/settings.ts
82
- function expandHome(path) {
83
- if (path.startsWith("~/") || path === "~") {
84
- return (0, import_node_path.join)((0, import_node_os.homedir)(), path.slice(2));
82
+ function expandHome(path2) {
83
+ if (path2.startsWith("~/") || path2 === "~") {
84
+ return (0, import_node_path.join)((0, import_node_os.homedir)(), path2.slice(2));
85
85
  }
86
- return path;
86
+ return path2;
87
87
  }
88
88
  function parseAvailableIn(raw) {
89
89
  if (!Array.isArray(raw)) return void 0;
@@ -93,9 +93,9 @@ function parseAvailableIn(raw) {
93
93
  }
94
94
  return out.length ? out : void 0;
95
95
  }
96
- function parseSettingsFile(path, source) {
97
- if (!(0, import_node_fs.existsSync)(path)) return null;
98
- const raw = (0, import_node_fs.readFileSync)(path, "utf8");
96
+ function parseSettingsFile(path2, source) {
97
+ if (!(0, import_node_fs.existsSync)(path2)) return null;
98
+ const raw = (0, import_node_fs.readFileSync)(path2, "utf8");
99
99
  const data = (0, import_smol_toml.parse)(raw);
100
100
  const scripts = data.scripts ?? {};
101
101
  const setup = typeof scripts.setup === "string" ? scripts.setup : void 0;
@@ -386,14 +386,14 @@ var init_paths = __esm({
386
386
  });
387
387
 
388
388
  // src/store/private-file.ts
389
- function writePrivateFile(path, contents) {
390
- (0, import_node_fs3.mkdirSync)((0, import_node_path4.dirname)(path), { recursive: true });
391
- (0, import_node_fs3.writeFileSync)(path, contents, { encoding: "utf8", mode: PRIVATE_FILE_MODE });
392
- chmodOwnerOnly(path);
389
+ function writePrivateFile(path2, contents) {
390
+ (0, import_node_fs3.mkdirSync)((0, import_node_path4.dirname)(path2), { recursive: true });
391
+ (0, import_node_fs3.writeFileSync)(path2, contents, { encoding: "utf8", mode: PRIVATE_FILE_MODE });
392
+ chmodOwnerOnly(path2);
393
393
  }
394
- function chmodOwnerOnly(path) {
394
+ function chmodOwnerOnly(path2) {
395
395
  try {
396
- (0, import_node_fs3.chmodSync)(path, PRIVATE_FILE_MODE);
396
+ (0, import_node_fs3.chmodSync)(path2, PRIVATE_FILE_MODE);
397
397
  } catch {
398
398
  }
399
399
  }
@@ -493,12 +493,12 @@ function decryptEnvelope(envelope, key) {
493
493
  ]);
494
494
  return JSON.parse(plaintext.toString("utf8"));
495
495
  }
496
- function readSecureJson(path) {
497
- if (!(0, import_node_fs4.existsSync)(path)) return null;
498
- chmodOwnerOnly(path);
496
+ function readSecureJson(path2) {
497
+ if (!(0, import_node_fs4.existsSync)(path2)) return null;
498
+ chmodOwnerOnly(path2);
499
499
  let parsed;
500
500
  try {
501
- parsed = JSON.parse((0, import_node_fs4.readFileSync)(path, "utf8"));
501
+ parsed = JSON.parse((0, import_node_fs4.readFileSync)(path2, "utf8"));
502
502
  } catch {
503
503
  return null;
504
504
  }
@@ -511,24 +511,24 @@ function readSecureJson(path) {
511
511
  }
512
512
  return decryptEnvelope(parsed, key);
513
513
  }
514
- function writeSecureJson(path, value) {
514
+ function writeSecureJson(path2, value) {
515
515
  const key = resolveVaultKey();
516
516
  const body = key ? encryptJson(value, key) : value;
517
- writePrivateFile(path, `${JSON.stringify(body, null, 2)}
517
+ writePrivateFile(path2, `${JSON.stringify(body, null, 2)}
518
518
  `);
519
519
  }
520
- function isSecureFileEncrypted(path) {
521
- if (!(0, import_node_fs4.existsSync)(path)) return false;
520
+ function isSecureFileEncrypted(path2) {
521
+ if (!(0, import_node_fs4.existsSync)(path2)) return false;
522
522
  try {
523
- return isEnvelope(JSON.parse((0, import_node_fs4.readFileSync)(path, "utf8")));
523
+ return isEnvelope(JSON.parse((0, import_node_fs4.readFileSync)(path2, "utf8")));
524
524
  } catch {
525
525
  return false;
526
526
  }
527
527
  }
528
- function secureFileUnlocksWith(path, key) {
529
- if (!(0, import_node_fs4.existsSync)(path) || key.length !== 32) return true;
528
+ function secureFileUnlocksWith(path2, key) {
529
+ if (!(0, import_node_fs4.existsSync)(path2) || key.length !== 32) return true;
530
530
  try {
531
- const parsed = JSON.parse((0, import_node_fs4.readFileSync)(path, "utf8"));
531
+ const parsed = JSON.parse((0, import_node_fs4.readFileSync)(path2, "utf8"));
532
532
  if (!isEnvelope(parsed)) return true;
533
533
  decryptEnvelope(parsed, key);
534
534
  return true;
@@ -583,6 +583,9 @@ function normalizeVault(raw) {
583
583
  if (typeof raw.slackAppToken === "string" && raw.slackAppToken.trim()) {
584
584
  out.slackAppToken = raw.slackAppToken.trim();
585
585
  }
586
+ if (typeof raw.githubPat === "string" && raw.githubPat.trim()) {
587
+ out.githubPat = raw.githubPat.trim();
588
+ }
586
589
  if (raw.environment && typeof raw.environment === "object") {
587
590
  const environment = {};
588
591
  for (const [key, value] of Object.entries(raw.environment)) {
@@ -607,6 +610,7 @@ var init_secret_vault = __esm({
607
610
  // src/store/app-settings.ts
608
611
  var app_settings_exports = {};
609
612
  __export(app_settings_exports, {
613
+ GITHUB_GIT_AUTH_MODES: () => GITHUB_GIT_AUTH_MODES,
610
614
  HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
611
615
  appSettingsPath: () => appSettingsPath,
612
616
  applyAppEnvironment: () => applyAppEnvironment,
@@ -630,6 +634,8 @@ __export(app_settings_exports, {
630
634
  getDefaultEffort: () => getDefaultEffort,
631
635
  getDefaultFast: () => getDefaultFast,
632
636
  getDefaultModel: () => getDefaultModel,
637
+ getGithubGitAuthMode: () => getGithubGitAuthMode,
638
+ getGithubPat: () => getGithubPat,
633
639
  getIssueSource: () => getIssueSource,
634
640
  getLinearApiKey: () => getLinearApiKey,
635
641
  harnessEnvKey: () => harnessEnvKey,
@@ -674,12 +680,14 @@ function toPublicAppSettings(settings) {
674
680
  const integrations = { ...settings.integrations };
675
681
  const slackClientSecret = integrations.slackClientSecret;
676
682
  const slackAppToken = integrations.slackAppToken;
683
+ const githubPat = integrations.githubPat;
677
684
  delete integrations.linearApiKey;
678
685
  delete integrations.linearAccessToken;
679
686
  delete integrations.linearRefreshToken;
680
687
  delete integrations.linearClientSecret;
681
688
  delete integrations.slackClientSecret;
682
689
  delete integrations.slackAppToken;
690
+ delete integrations.githubPat;
683
691
  return {
684
692
  ...settings,
685
693
  environment,
@@ -688,7 +696,8 @@ function toPublicAppSettings(settings) {
688
696
  hasLinearApiKey: hasLinearCredentials(settings.integrations),
689
697
  hasLinearOAuth: hasLinearOAuth(settings.integrations),
690
698
  hasSlackClientSecret: Boolean(slackClientSecret?.trim()),
691
- hasSlackAppToken: Boolean(slackAppToken?.trim())
699
+ hasSlackAppToken: Boolean(slackAppToken?.trim()),
700
+ hasGithubPat: Boolean(githubPat?.trim())
692
701
  }
693
702
  };
694
703
  }
@@ -703,8 +712,8 @@ function normalizeClaude(raw) {
703
712
  const source = raw;
704
713
  const out = {};
705
714
  if (typeof source.executablePath === "string") {
706
- const path = source.executablePath.trim();
707
- if (path) out.executablePath = path;
715
+ const path2 = source.executablePath.trim();
716
+ if (path2) out.executablePath = path2;
708
717
  }
709
718
  if (typeof source.chromeEnabled === "boolean") {
710
719
  out.chromeEnabled = source.chromeEnabled;
@@ -716,8 +725,8 @@ function normalizeCliExecutable(raw) {
716
725
  const source = raw;
717
726
  const out = {};
718
727
  if (typeof source.executablePath === "string") {
719
- const path = source.executablePath.trim();
720
- if (path) out.executablePath = path;
728
+ const path2 = source.executablePath.trim();
729
+ if (path2) out.executablePath = path2;
721
730
  }
722
731
  return out;
723
732
  }
@@ -726,8 +735,8 @@ function normalizeBrightsy(raw) {
726
735
  const source = raw;
727
736
  const out = {};
728
737
  if (typeof source.executablePath === "string") {
729
- const path = source.executablePath.trim();
730
- if (path) out.executablePath = path;
738
+ const path2 = source.executablePath.trim();
739
+ if (path2) out.executablePath = path2;
731
740
  }
732
741
  if (typeof source.cloudConnectEnabled === "boolean") {
733
742
  out.cloudConnectEnabled = source.cloudConnectEnabled;
@@ -804,6 +813,13 @@ function normalizeIntegrations(raw) {
804
813
  const label = source.slackDeviceLabel.trim().slice(0, 64);
805
814
  if (label) out.slackDeviceLabel = label;
806
815
  }
816
+ if (typeof source.githubGitAuthMode === "string" && GIT_AUTH_MODES.has(source.githubGitAuthMode)) {
817
+ out.githubGitAuthMode = source.githubGitAuthMode;
818
+ }
819
+ if (typeof source.githubPat === "string") {
820
+ const pat = source.githubPat.trim();
821
+ if (pat) out.githubPat = pat;
822
+ }
807
823
  return out;
808
824
  }
809
825
  function normalizeDefaults(raw) {
@@ -931,11 +947,11 @@ function normalizeSettings(raw) {
931
947
  };
932
948
  }
933
949
  function readSettingsFile() {
934
- const path = appSettingsPath();
935
- if (!(0, import_node_fs5.existsSync)(path)) return { ...EMPTY_SETTINGS };
950
+ const path2 = appSettingsPath();
951
+ if (!(0, import_node_fs5.existsSync)(path2)) return { ...EMPTY_SETTINGS };
936
952
  try {
937
- chmodOwnerOnly(path);
938
- const parsed = JSON.parse((0, import_node_fs5.readFileSync)(path, "utf8"));
953
+ chmodOwnerOnly(path2);
954
+ const parsed = JSON.parse((0, import_node_fs5.readFileSync)(path2, "utf8"));
939
955
  return normalizeSettings(parsed);
940
956
  } catch {
941
957
  return { ...EMPTY_SETTINGS };
@@ -943,7 +959,7 @@ function readSettingsFile() {
943
959
  }
944
960
  function diskHoldsSecrets(disk) {
945
961
  return Boolean(
946
- disk.integrations.linearApiKey || disk.integrations.linearAccessToken || disk.integrations.linearRefreshToken || disk.integrations.linearClientSecret || disk.integrations.slackClientSecret || disk.integrations.slackAppToken || Object.keys(disk.environment).length > 0
962
+ disk.integrations.linearApiKey || disk.integrations.linearAccessToken || disk.integrations.linearRefreshToken || disk.integrations.linearClientSecret || disk.integrations.slackClientSecret || disk.integrations.slackAppToken || disk.integrations.githubPat || Object.keys(disk.environment).length > 0
947
963
  );
948
964
  }
949
965
  function mergeVault(disk) {
@@ -968,6 +984,9 @@ function mergeVault(disk) {
968
984
  if (vault.slackAppToken && !integrations.slackAppToken) {
969
985
  integrations.slackAppToken = vault.slackAppToken;
970
986
  }
987
+ if (vault.githubPat && !integrations.githubPat) {
988
+ integrations.githubPat = vault.githubPat;
989
+ }
971
990
  return { ...disk, environment, integrations };
972
991
  }
973
992
  function persistSplit(settings) {
@@ -978,12 +997,14 @@ function persistSplit(settings) {
978
997
  const linearClientSecret = integrations.linearClientSecret;
979
998
  const slackClientSecret = integrations.slackClientSecret;
980
999
  const slackAppToken = integrations.slackAppToken;
1000
+ const githubPat = integrations.githubPat;
981
1001
  delete integrations.linearApiKey;
982
1002
  delete integrations.linearAccessToken;
983
1003
  delete integrations.linearRefreshToken;
984
1004
  delete integrations.linearClientSecret;
985
1005
  delete integrations.slackClientSecret;
986
1006
  delete integrations.slackAppToken;
1007
+ delete integrations.githubPat;
987
1008
  writePrivateFile(
988
1009
  appSettingsPath(),
989
1010
  `${JSON.stringify({ ...settings, environment: {}, integrations }, null, 2)}
@@ -996,6 +1017,7 @@ function persistSplit(settings) {
996
1017
  linearClientSecret,
997
1018
  slackClientSecret,
998
1019
  slackAppToken,
1020
+ githubPat,
999
1021
  environment: settings.environment
1000
1022
  });
1001
1023
  }
@@ -1161,6 +1183,20 @@ function updateIntegrationsSettings(patch) {
1161
1183
  integrations.slackDeviceLabel = patch.slackDeviceLabel.trim().slice(0, 64);
1162
1184
  }
1163
1185
  }
1186
+ if ("githubGitAuthMode" in patch) {
1187
+ if (patch.githubGitAuthMode == null) {
1188
+ delete integrations.githubGitAuthMode;
1189
+ } else if (GIT_AUTH_MODES.has(patch.githubGitAuthMode)) {
1190
+ integrations.githubGitAuthMode = patch.githubGitAuthMode;
1191
+ }
1192
+ }
1193
+ if ("githubPat" in patch) {
1194
+ if (patch.githubPat == null || patch.githubPat.trim() === "") {
1195
+ delete integrations.githubPat;
1196
+ } else {
1197
+ integrations.githubPat = patch.githubPat.trim();
1198
+ }
1199
+ }
1164
1200
  return saveAppSettings({ ...current, integrations });
1165
1201
  }
1166
1202
  function updateDefaultsSettings(patch) {
@@ -1268,6 +1304,13 @@ function disconnectLinearConnection() {
1268
1304
  function getIssueSource(settings = loadAppSettings()) {
1269
1305
  return settings.integrations.issueSource ?? "github";
1270
1306
  }
1307
+ function getGithubGitAuthMode(settings = loadAppSettings()) {
1308
+ return settings.integrations.githubGitAuthMode ?? "auto";
1309
+ }
1310
+ function getGithubPat(settings = loadAppSettings()) {
1311
+ const pat = settings.integrations.githubPat?.trim();
1312
+ return pat || null;
1313
+ }
1271
1314
  function resolveEffectiveIssueSource(settings = loadAppSettings()) {
1272
1315
  const preferred = getIssueSource(settings);
1273
1316
  if (preferred === "linear" && !isLinearConnected(settings)) return "github";
@@ -1468,7 +1511,7 @@ function childEnvWithAppSettings(extra) {
1468
1511
  function harnessEnvKey(harness) {
1469
1512
  return HARNESS_ENV_KEYS[harness];
1470
1513
  }
1471
- var import_node_crypto2, import_node_fs5, import_node_os3, import_node_path6, HARNESS_ENV_KEYS, DEFAULT_AGENTS, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, EMPTY_SETTINGS, DEFAULT_CLI_BIN;
1514
+ var import_node_crypto2, import_node_fs5, import_node_os3, import_node_path6, HARNESS_ENV_KEYS, DEFAULT_AGENTS, GITHUB_GIT_AUTH_MODES, CLOUD_CONNECT_AGENTS, ISSUE_SOURCES, GIT_AUTH_MODES, EMPTY_SETTINGS, DEFAULT_CLI_BIN;
1472
1515
  var init_app_settings = __esm({
1473
1516
  "src/store/app-settings.ts"() {
1474
1517
  "use strict";
@@ -1494,6 +1537,7 @@ var init_app_settings = __esm({
1494
1537
  "brightsy",
1495
1538
  "cursor"
1496
1539
  ]);
1540
+ GITHUB_GIT_AUTH_MODES = ["auto", "gh", "ssh", "token"];
1497
1541
  CLOUD_CONNECT_AGENTS = /* @__PURE__ */ new Set([
1498
1542
  "claude",
1499
1543
  "codex",
@@ -1501,6 +1545,7 @@ var init_app_settings = __esm({
1501
1545
  "cursor"
1502
1546
  ]);
1503
1547
  ISSUE_SOURCES = /* @__PURE__ */ new Set(["linear", "github"]);
1548
+ GIT_AUTH_MODES = new Set(GITHUB_GIT_AUTH_MODES);
1504
1549
  EMPTY_SETTINGS = {
1505
1550
  environment: {},
1506
1551
  claude: {},
@@ -1567,10 +1612,10 @@ function uniqueIds(ids) {
1567
1612
  return out;
1568
1613
  }
1569
1614
  function readHold() {
1570
- const path = caffeinateHoldPath();
1571
- if (!(0, import_node_fs6.existsSync)(path)) return null;
1615
+ const path2 = caffeinateHoldPath();
1616
+ if (!(0, import_node_fs6.existsSync)(path2)) return null;
1572
1617
  try {
1573
- const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path, "utf8"));
1618
+ const parsed = JSON.parse((0, import_node_fs6.readFileSync)(path2, "utf8"));
1574
1619
  if (typeof parsed?.pid === "number" && parsed.pid > 0) {
1575
1620
  return {
1576
1621
  pid: parsed.pid,
@@ -1790,17 +1835,17 @@ async function withThreadLock(id, fn) {
1790
1835
  }
1791
1836
  }
1792
1837
  function readThread(id) {
1793
- const path = threadFilePath(id);
1794
- if (!(0, import_node_fs7.existsSync)(path)) return null;
1795
- const raw = (0, import_node_fs7.readFileSync)(path, "utf8");
1838
+ const path2 = threadFilePath(id);
1839
+ if (!(0, import_node_fs7.existsSync)(path2)) return null;
1840
+ const raw = (0, import_node_fs7.readFileSync)(path2, "utf8");
1796
1841
  return normalizeThread(JSON.parse(raw));
1797
1842
  }
1798
1843
  function writeThread(thread) {
1799
- const path = threadFilePath(idPath(thread.id));
1800
- const tmp = `${path}.${process.pid}.tmp`;
1844
+ const path2 = threadFilePath(idPath(thread.id));
1845
+ const tmp = `${path2}.${process.pid}.tmp`;
1801
1846
  const next = { ...thread, updatedAt: nowIso() };
1802
1847
  (0, import_node_fs7.writeFileSync)(tmp, JSON.stringify(next, null, 2), "utf8");
1803
- (0, import_node_fs7.renameSync)(tmp, path);
1848
+ (0, import_node_fs7.renameSync)(tmp, path2);
1804
1849
  }
1805
1850
  function idPath(id) {
1806
1851
  return id;
@@ -1820,8 +1865,8 @@ function listThreads(opts) {
1820
1865
  return threads.filter((t) => t.status !== "archived");
1821
1866
  }
1822
1867
  function deleteThreadRecord(id) {
1823
- const path = threadFilePath(id);
1824
- if ((0, import_node_fs7.existsSync)(path)) (0, import_node_fs7.unlinkSync)(path);
1868
+ const path2 = threadFilePath(id);
1869
+ if ((0, import_node_fs7.existsSync)(path2)) (0, import_node_fs7.unlinkSync)(path2);
1825
1870
  const lock = threadLockPath(id);
1826
1871
  if ((0, import_node_fs7.existsSync)(lock)) {
1827
1872
  try {
@@ -2820,6 +2865,80 @@ var init_gh_errors = __esm({
2820
2865
  }
2821
2866
  });
2822
2867
 
2868
+ // src/git/git-auth-mode.ts
2869
+ function appendIndexedGitConfig(existing, entries) {
2870
+ const start = Number.parseInt(String(existing?.GIT_CONFIG_COUNT ?? "0"), 10);
2871
+ const count = Number.isFinite(start) && start > 0 ? start : 0;
2872
+ const out = {};
2873
+ entries.forEach((entry, i) => {
2874
+ const n = count + i;
2875
+ out[`GIT_CONFIG_KEY_${n}`] = entry.key;
2876
+ out[`GIT_CONFIG_VALUE_${n}`] = entry.value;
2877
+ });
2878
+ out.GIT_CONFIG_COUNT = String(count + entries.length);
2879
+ return out;
2880
+ }
2881
+ function githubAgentGitEnv(existing) {
2882
+ return appendIndexedGitConfig(existing, HTTPS_REWRITE);
2883
+ }
2884
+ function applyGithubGitAuthEnv(existing, opts) {
2885
+ const out = {};
2886
+ if (opts.mode === "gh" || opts.mode === "token") {
2887
+ Object.assign(out, githubAgentGitEnv(existing));
2888
+ }
2889
+ if (opts.mode === "token") {
2890
+ const token = opts.token?.trim();
2891
+ if (token && !existing?.GH_TOKEN?.trim()) {
2892
+ out.GH_TOKEN = token;
2893
+ }
2894
+ }
2895
+ return out;
2896
+ }
2897
+ function formatGitAuthModeDirective(mode) {
2898
+ switch (mode) {
2899
+ case "gh":
2900
+ return [
2901
+ "Git authentication (Account \u2192 GitHub mode: gh CLI):",
2902
+ "- This process rewrites `git@github.com:` and `ssh://git@github.com/` to HTTPS.",
2903
+ "- `gh` supplies credentials (`gh auth git-credential`). Do not switch remotes to SSH.",
2904
+ "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
2905
+ ].join("\n");
2906
+ case "ssh":
2907
+ return [
2908
+ "Git authentication (Account \u2192 GitHub mode: SSH):",
2909
+ "- Keep SSH remotes (`git@github.com:\u2026`). Do not rewrite them to HTTPS.",
2910
+ "- 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.",
2911
+ "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below (API still uses `gh`)."
2912
+ ].join("\n");
2913
+ case "token":
2914
+ return [
2915
+ "Git authentication (Account \u2192 GitHub mode: personal access token):",
2916
+ "- This process rewrites GitHub SSH remotes to HTTPS.",
2917
+ "- `GH_TOKEN` is set in the environment. Use HTTPS git and `gh`; do not paste the token into commands or chat.",
2918
+ "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
2919
+ ].join("\n");
2920
+ case "auto":
2921
+ default:
2922
+ return [
2923
+ "Git authentication (Account \u2192 GitHub mode: auto):",
2924
+ "- Prefer the existing remote URL (SSH or HTTPS). Do not rewrite remotes unless a push/fetch fails.",
2925
+ '- 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`.',
2926
+ "- Then create/update the PR with `gh` as below."
2927
+ ].join("\n");
2928
+ }
2929
+ }
2930
+ var HTTPS_REWRITE;
2931
+ var init_git_auth_mode = __esm({
2932
+ "src/git/git-auth-mode.ts"() {
2933
+ "use strict";
2934
+ HTTPS_REWRITE = [
2935
+ { key: "url.https://github.com/.insteadOf", value: "git@github.com:" },
2936
+ { key: "url.https://github.com/.insteadOf", value: "ssh://git@github.com/" },
2937
+ { key: "credential.https://github.com.helper", value: "!gh auth git-credential" }
2938
+ ];
2939
+ }
2940
+ });
2941
+
2823
2942
  // src/agents/path.ts
2824
2943
  function prependPathDir(env, dir) {
2825
2944
  if (!dir || !(0, import_node_fs8.existsSync)(dir)) return;
@@ -3357,6 +3476,7 @@ __export(worktree_exports, {
3357
3476
  getPrMeta: () => getPrMeta,
3358
3477
  ghHeadRef: () => ghHeadRef,
3359
3478
  ghRepoSelectArgs: () => ghRepoSelectArgs,
3479
+ githubAgentGitEnv: () => githubAgentGitEnv,
3360
3480
  isDirty: () => isDirty,
3361
3481
  isPlaceholderBranch: () => isPlaceholderBranch,
3362
3482
  isSideboardScratchPath: () => isSideboardScratchPath,
@@ -3478,10 +3598,15 @@ ${view.stderr}`;
3478
3598
  timeoutMs: 15e3
3479
3599
  });
3480
3600
  }
3481
- async function originGhRepoEnv(cwd) {
3601
+ async function originGhRepoEnv(cwd, opts) {
3482
3602
  await ensureGhPreferOrigin(cwd);
3483
3603
  const slug = await resolveGithubRepoSlug(cwd);
3484
- return slug ? { GH_REPO: slug } : {};
3604
+ const mode = opts?.mode ?? getGithubGitAuthMode();
3605
+ const token = mode === "token" ? getGithubPat() : null;
3606
+ return {
3607
+ ...applyGithubGitAuthEnv(opts?.env, { mode, token }),
3608
+ ...slug ? { GH_REPO: slug } : {}
3609
+ };
3485
3610
  }
3486
3611
  async function resolveDefaultBranch(repoPath, opts) {
3487
3612
  const viaOrigin = await git(
@@ -4230,7 +4355,33 @@ async function commitAll(worktreePath, message) {
4230
4355
  return true;
4231
4356
  }
4232
4357
  async function pushBranch(worktreePath, branchName) {
4233
- await git(["push", "-u", "origin", branchName], worktreePath);
4358
+ const ssh = await git(["push", "-u", "origin", branchName], worktreePath, {
4359
+ reject: false
4360
+ });
4361
+ if (ssh.exitCode === 0) return;
4362
+ const sshErr = (ssh.stderr || ssh.stdout).trim();
4363
+ const slug = await resolveGithubRepoSlug(worktreePath);
4364
+ const token = await resolveGhAuthToken(worktreePath);
4365
+ if (!slug || !token) {
4366
+ throw new Error(
4367
+ sshErr || `git push origin ${branchName} failed` + (!token ? " (no SSH agent and gh auth token unavailable \u2014 run: gh auth login)" : "")
4368
+ );
4369
+ }
4370
+ const tryHttps = async (header) => git(["push", "-u", "origin", branchName], worktreePath, {
4371
+ reject: false,
4372
+ env: { GIT_TERMINAL_PROMPT: "0" },
4373
+ config: {
4374
+ "url.https://github.com/.insteadOf": "git@github.com:",
4375
+ "http.extraHeader": header
4376
+ }
4377
+ });
4378
+ const bearer = await tryHttps(`AUTHORIZATION: bearer ${token}`);
4379
+ if (bearer.exitCode === 0) return;
4380
+ const basic = Buffer.from(`x-access-token:${token}`, "utf8").toString("base64");
4381
+ const basicPush = await tryHttps(`Authorization: Basic ${basic}`);
4382
+ if (basicPush.exitCode === 0) return;
4383
+ const httpsErr = (basicPush.stderr || bearer.stderr || bearer.stdout).trim();
4384
+ throw new Error(httpsErr || sshErr || `git push origin ${branchName} failed`);
4234
4385
  }
4235
4386
  async function mergePr(cwd, selector, opts) {
4236
4387
  const slug = await resolveGithubRepoSlug(cwd);
@@ -4418,8 +4569,8 @@ function allocateTeamSlug(repoPath) {
4418
4569
  const taken = collectTakenTeamSlugs(repoPath);
4419
4570
  for (let attempt = 0; attempt < 32; attempt++) {
4420
4571
  const team = allocateTeamName(taken);
4421
- const path = (0, import_node_path9.join)(worktreesRoot(repoPath), team.slug);
4422
- if (!(0, import_node_fs9.existsSync)(path)) return team;
4572
+ const path2 = (0, import_node_path9.join)(worktreesRoot(repoPath), team.slug);
4573
+ if (!(0, import_node_fs9.existsSync)(path2)) return team;
4423
4574
  taken.add(team.slug);
4424
4575
  }
4425
4576
  throw new Error("No available soccer team worktree directories left");
@@ -4435,10 +4586,13 @@ var init_worktree = __esm({
4435
4586
  init_teams();
4436
4587
  init_worktree_labels();
4437
4588
  init_gh_errors();
4589
+ init_git_auth_mode();
4438
4590
  init_run();
4591
+ init_app_settings();
4439
4592
  init_pr_gates();
4440
4593
  init_stack();
4441
4594
  init_teams();
4595
+ init_git_auth_mode();
4442
4596
  init_workspace_scratch();
4443
4597
  init_worktree_labels();
4444
4598
  }
@@ -4639,7 +4793,7 @@ var init_coordinator_prompt = __esm({
4639
4793
  "Inspect / review / PRs:",
4640
4794
  "- get_diff \u2014 compact diff summary",
4641
4795
  '- request_review \u2014 open a Review chat tab on a worktree thread (attaches .sideboard/review.md when present, else local guidelines; sends "Review changes in this workspace."); then wait_for_turn / get_turn_result on the returned id',
4642
- "- ask_git \u2014 tell a worktree agent to commit & push, open a draft PR, resolve conflicts, or merge (`Merge PR.`). You only queue that prompt \u2014 the worktree agent runs git/gh (including `gh pr merge`). Then wait_for_turn. Prefer this over paraphrasing.",
4796
+ "- 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. Prefer this over paraphrasing.",
4643
4797
  '- Or send_to_thread with those exact phrases: "Commit and push.", "Commit, push, and open a draft PR.", "Fix merge conflicts.", "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.',
4644
4798
  "Human-only (do not attempt): ready-for-review land (confirm_land), purge_thread.",
4645
4799
  "Thread links in replies: when mentioning a chat/thread for the user, include a markdown link `[Title](sideboard://thread/<id>)` using the full id (or the link field from create_thread / list_threads). Sideboard renders these as clickable opens.",
@@ -4912,10 +5066,10 @@ function removedWorkspacesFile() {
4912
5066
  return (0, import_node_path11.join)(appDataDir(), "removed-workspaces.json");
4913
5067
  }
4914
5068
  function readAll() {
4915
- const path = workspacesFile();
4916
- if (!(0, import_node_fs11.existsSync)(path)) return [];
5069
+ const path2 = workspacesFile();
5070
+ if (!(0, import_node_fs11.existsSync)(path2)) return [];
4917
5071
  try {
4918
- const raw = JSON.parse((0, import_node_fs11.readFileSync)(path, "utf8"));
5072
+ const raw = JSON.parse((0, import_node_fs11.readFileSync)(path2, "utf8"));
4919
5073
  return Array.isArray(raw) ? raw : [];
4920
5074
  } catch {
4921
5075
  return [];
@@ -4926,10 +5080,10 @@ function writeAll(list) {
4926
5080
  (0, import_node_fs11.writeFileSync)(workspacesFile(), JSON.stringify(list, null, 2), "utf8");
4927
5081
  }
4928
5082
  function readRemoved() {
4929
- const path = removedWorkspacesFile();
4930
- if (!(0, import_node_fs11.existsSync)(path)) return /* @__PURE__ */ new Set();
5083
+ const path2 = removedWorkspacesFile();
5084
+ if (!(0, import_node_fs11.existsSync)(path2)) return /* @__PURE__ */ new Set();
4931
5085
  try {
4932
- const raw = JSON.parse((0, import_node_fs11.readFileSync)(path, "utf8"));
5086
+ const raw = JSON.parse((0, import_node_fs11.readFileSync)(path2, "utf8"));
4933
5087
  return new Set(Array.isArray(raw) ? raw.filter((p) => typeof p === "string") : []);
4934
5088
  } catch {
4935
5089
  return /* @__PURE__ */ new Set();
@@ -4986,17 +5140,17 @@ function syncWorkspacesFromThreads(repoPaths) {
4986
5140
  const removed = readRemoved();
4987
5141
  const byPath = new Map(current.map((w) => [w.path, w]));
4988
5142
  let dirty = false;
4989
- for (const path of repoPaths) {
4990
- if (!path || path === "/" || isGlobalRepoPath(path) || byPath.has(path) || removed.has(path)) {
5143
+ for (const path2 of repoPaths) {
5144
+ if (!path2 || path2 === "/" || isGlobalRepoPath(path2) || byPath.has(path2) || removed.has(path2)) {
4991
5145
  continue;
4992
5146
  }
4993
- if (!(0, import_node_fs11.existsSync)(path)) continue;
5147
+ if (!(0, import_node_fs11.existsSync)(path2)) continue;
4994
5148
  const ws = {
4995
- path,
4996
- name: (0, import_node_path11.basename)(path),
5149
+ path: path2,
5150
+ name: (0, import_node_path11.basename)(path2),
4997
5151
  addedAt: (/* @__PURE__ */ new Date()).toISOString()
4998
5152
  };
4999
- byPath.set(path, ws);
5153
+ byPath.set(path2, ws);
5000
5154
  dirty = true;
5001
5155
  }
5002
5156
  const next = [...byPath.values()];
@@ -5020,11 +5174,11 @@ function brightsyConfigPath() {
5020
5174
  return (0, import_node_path12.join)((0, import_node_os5.homedir)(), ".brightsy", "config.json");
5021
5175
  }
5022
5176
  function loadBrightsyConfig() {
5023
- const path = brightsyConfigPath();
5024
- if (!(0, import_node_fs12.existsSync)(path)) {
5177
+ const path2 = brightsyConfigPath();
5178
+ if (!(0, import_node_fs12.existsSync)(path2)) {
5025
5179
  throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
5026
5180
  }
5027
- const raw = JSON.parse((0, import_node_fs12.readFileSync)(path, "utf8"));
5181
+ const raw = JSON.parse((0, import_node_fs12.readFileSync)(path2, "utf8"));
5028
5182
  if (!raw.access_token || !raw.account_id) {
5029
5183
  throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
5030
5184
  }
@@ -5148,10 +5302,10 @@ function storePath() {
5148
5302
  return (0, import_node_path13.join)(appDataDir(), "brightsy-teams.json");
5149
5303
  }
5150
5304
  function readStore() {
5151
- const path = storePath();
5152
- if (!(0, import_node_fs13.existsSync)(path)) return [];
5305
+ const path2 = storePath();
5306
+ if (!(0, import_node_fs13.existsSync)(path2)) return [];
5153
5307
  try {
5154
- const parsed = JSON.parse((0, import_node_fs13.readFileSync)(path, "utf8"));
5308
+ const parsed = JSON.parse((0, import_node_fs13.readFileSync)(path2, "utf8"));
5155
5309
  return Array.isArray(parsed.teams) ? parsed.teams : [];
5156
5310
  } catch {
5157
5311
  return [];
@@ -5159,8 +5313,8 @@ function readStore() {
5159
5313
  }
5160
5314
  function writeStore(teams) {
5161
5315
  (0, import_node_fs13.mkdirSync)(appDataDir(), { recursive: true });
5162
- const path = storePath();
5163
- (0, import_node_fs13.writeFileSync)(path, `${JSON.stringify({ teams }, null, 2)}
5316
+ const path2 = storePath();
5317
+ (0, import_node_fs13.writeFileSync)(path2, `${JSON.stringify({ teams }, null, 2)}
5164
5318
  `, {
5165
5319
  mode: 384
5166
5320
  });
@@ -6700,9 +6854,9 @@ function codexConfigHasNetworkAccess() {
6700
6854
  (0, import_node_path15.join)((0, import_node_os7.homedir)(), ".codex", "config.toml"),
6701
6855
  (0, import_node_path15.join)((0, import_node_os7.homedir)(), ".config", "codex", "config.toml")
6702
6856
  ];
6703
- for (const path of candidates) {
6704
- if (!(0, import_node_fs17.existsSync)(path)) continue;
6705
- const text3 = (0, import_node_fs17.readFileSync)(path, "utf8");
6857
+ for (const path2 of candidates) {
6858
+ if (!(0, import_node_fs17.existsSync)(path2)) continue;
6859
+ const text3 = (0, import_node_fs17.readFileSync)(path2, "utf8");
6706
6860
  if (/network_access\s*=\s*true/.test(text3)) return true;
6707
6861
  }
6708
6862
  return false;
@@ -8166,8 +8320,8 @@ function extractPresentedPlan(parts) {
8166
8320
  const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
8167
8321
  if (!content.trim()) continue;
8168
8322
  const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
8169
- const path = typeof input.path === "string" && input.path.trim() ? input.path.trim() : PLAN_FILE_REL;
8170
- return { title, content: content.trim(), path, source: "present_plan" };
8323
+ const path2 = typeof input.path === "string" && input.path.trim() ? input.path.trim() : PLAN_FILE_REL;
8324
+ return { title, content: content.trim(), path: path2, source: "present_plan" };
8171
8325
  }
8172
8326
  return null;
8173
8327
  }
@@ -8356,6 +8510,7 @@ __export(index_exports, {
8356
8510
  CONTEXT_MIN_MESSAGES: () => CONTEXT_MIN_MESSAGES,
8357
8511
  COORDINATOR_TOOL_PLAYBOOK: () => COORDINATOR_TOOL_PLAYBOOK,
8358
8512
  FAMOUS_SOCCER_TEAMS: () => FAMOUS_SOCCER_TEAMS,
8513
+ GITHUB_GIT_AUTH_MODES: () => GITHUB_GIT_AUTH_MODES,
8359
8514
  GLOBAL_WORKSPACE_ID: () => GLOBAL_WORKSPACE_ID,
8360
8515
  HARNESS_ENV_KEYS: () => HARNESS_ENV_KEYS,
8361
8516
  LEGACY_ATTACHMENTS_DIR: () => LEGACY_ATTACHMENTS_DIR,
@@ -8405,10 +8560,12 @@ __export(index_exports, {
8405
8560
  allocateTeamSlug: () => allocateTeamSlug,
8406
8561
  appDataDir: () => appDataDir,
8407
8562
  appSettingsPath: () => appSettingsPath,
8563
+ appendIndexedGitConfig: () => appendIndexedGitConfig,
8408
8564
  appendMessage: () => appendMessage,
8409
8565
  applyAgentEvent: () => applyAgentEvent,
8410
8566
  applyAppEnvironment: () => applyAppEnvironment,
8411
8567
  applyCompaction: () => applyCompaction,
8568
+ applyGithubGitAuthEnv: () => applyGithubGitAuthEnv,
8412
8569
  applyThreadIntoMain: () => applyThreadIntoMain,
8413
8570
  applyTurnUsage: () => applyTurnUsage,
8414
8571
  assertOrchestratorCapableAgent: () => assertOrchestratorCapableAgent,
@@ -8523,6 +8680,7 @@ __export(index_exports, {
8523
8680
  formatBrightsyFetchError: () => formatBrightsyFetchError,
8524
8681
  formatFetchError: () => formatFetchError,
8525
8682
  formatGhLandError: () => formatGhLandError,
8683
+ formatGitAuthModeDirective: () => formatGitAuthModeDirective,
8526
8684
  formatIpcInvokeError: () => formatIpcInvokeError,
8527
8685
  formatMessagesAsTranscript: () => formatMessagesAsTranscript,
8528
8686
  formatPlanQuestionAnswers: () => formatPlanQuestionAnswers,
@@ -8549,6 +8707,8 @@ __export(index_exports, {
8549
8707
  getDiff: () => getDiff,
8550
8708
  getDiffSummary: () => getDiffSummary,
8551
8709
  getGitHubStatus: () => getGitHubStatus,
8710
+ getGithubGitAuthMode: () => getGithubGitAuthMode,
8711
+ getGithubPat: () => getGithubPat,
8552
8712
  getIssueSource: () => getIssueSource,
8553
8713
  getLinearApiKey: () => getLinearApiKey,
8554
8714
  getLinearAuthToken: () => getLinearAuthToken,
@@ -8567,6 +8727,7 @@ __export(index_exports, {
8567
8727
  ghHeadRef: () => ghHeadRef,
8568
8728
  ghRepoSelectArgs: () => ghRepoSelectArgs,
8569
8729
  git: () => git,
8730
+ githubAgentGitEnv: () => githubAgentGitEnv,
8570
8731
  globalAgentCwd: () => globalAgentCwd,
8571
8732
  handleSlackInbound: () => handleSlackInbound,
8572
8733
  harnessEnvKey: () => harnessEnvKey,
@@ -8789,6 +8950,7 @@ __export(index_exports, {
8789
8950
  startSlackOAuth: () => startSlackOAuth,
8790
8951
  startSlackRelayServer: () => startSlackRelayServer,
8791
8952
  stripBrightsyNdjsonNoise: () => stripBrightsyNdjsonNoise,
8953
+ stripNestedElectronEnv: () => stripNestedElectronEnv,
8792
8954
  submitPrStack: () => submitPrStack,
8793
8955
  suggestSlug: () => suggestSlug,
8794
8956
  summarizeConversation: () => summarizeConversation,
@@ -8847,6 +9009,7 @@ init_workspaces();
8847
9009
  init_global_workspace();
8848
9010
  init_run();
8849
9011
  init_gh_errors();
9012
+ init_git_auth_mode();
8850
9013
  init_worktree();
8851
9014
  init_stack();
8852
9015
 
@@ -9613,8 +9776,8 @@ function toolDetail(name, input) {
9613
9776
  if (!input) return void 0;
9614
9777
  const command = str2(input.command) ?? str2(input.cmd);
9615
9778
  if (command) return command;
9616
- const path = str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
9617
- if (path) return path;
9779
+ const path2 = str2(input.file_path) ?? str2(input.path) ?? str2(input.filePath) ?? str2(input.filename);
9780
+ if (path2) return path2;
9618
9781
  const pattern = str2(input.pattern) ?? str2(input.glob) ?? str2(input.glob_pattern);
9619
9782
  if (pattern) return pattern;
9620
9783
  const query = str2(input.query) ?? str2(input.prompt);
@@ -9658,12 +9821,12 @@ function toolDescription(name, input) {
9658
9821
  return "Run shell command";
9659
9822
  }
9660
9823
  if (/edit|write|apply/i.test(name)) {
9661
- const path = str2(input.file_path) ?? str2(input.path);
9662
- return path ? `Edit ${path.split("/").pop()}` : "Edit file";
9824
+ const path2 = str2(input.file_path) ?? str2(input.path);
9825
+ return path2 ? `Edit ${path2.split("/").pop()}` : "Edit file";
9663
9826
  }
9664
9827
  if (/read/i.test(name)) {
9665
- const path = str2(input.file_path) ?? str2(input.path);
9666
- return path ? `Read ${path.split("/").pop()}` : "Read file";
9828
+ const path2 = str2(input.file_path) ?? str2(input.path);
9829
+ return path2 ? `Read ${path2.split("/").pop()}` : "Read file";
9667
9830
  }
9668
9831
  if (/grep/i.test(name)) return "Search files";
9669
9832
  if (/glob/i.test(name)) return "Find files";
@@ -9892,8 +10055,13 @@ async function spawnAgentTurn(thread, input, onEvent) {
9892
10055
  }
9893
10056
  const env = childEnvWithAppSettings(cmd.env);
9894
10057
  if (!isOrchestratorThread(thread)) {
9895
- const originEnv = await originGhRepoEnv(thread.worktreePath).catch(() => ({}));
9896
- Object.assign(env, originEnv);
10058
+ try {
10059
+ const originEnv = await originGhRepoEnv(thread.worktreePath, { env });
10060
+ Object.assign(env, originEnv);
10061
+ } catch (err) {
10062
+ const detail = err instanceof Error ? err.message : String(err);
10063
+ console.warn(`[sideboard] originGhRepoEnv failed (${thread.worktreePath}): ${detail}`);
10064
+ }
9897
10065
  }
9898
10066
  const child = (0, import_execa2.execa)(cmd.file, cmd.args, {
9899
10067
  cwd: cmd.cwd,
@@ -9983,6 +10151,7 @@ init_claude_mcp();
9983
10151
  // src/agents/instructions.ts
9984
10152
  var import_node_fs20 = require("fs");
9985
10153
  var import_node_path17 = require("path");
10154
+ init_git_auth_mode();
9986
10155
  init_worktree_labels();
9987
10156
  function normPath2(p) {
9988
10157
  return p.replace(/\/+$/, "");
@@ -10032,6 +10201,8 @@ function formatWorktreeDirective(thread, opts) {
10032
10201
  lines.push(
10033
10202
  "- Push with `git push -u origin HEAD` (or the current branch name). Do not `git push upstream`."
10034
10203
  );
10204
+ lines.push("");
10205
+ lines.push(formatGitAuthModeDirective(opts?.gitAuthMode ?? "auto"));
10035
10206
  lines.push(
10036
10207
  "- Derive the PR title and body from what the changes actually do and why \u2014 inspect the diff/commits and the user request. Do not use the soccer-team worktree nickname or placeholder branch as the PR title."
10037
10208
  );
@@ -10219,9 +10390,9 @@ function matchSimpleGlob(pattern, name) {
10219
10390
  return new RegExp(`^${escaped}$`).test(name);
10220
10391
  }
10221
10392
  function readWorktreeInclude(repoPath) {
10222
- const path = (0, import_node_path18.join)(repoPath, ".worktreeinclude");
10223
- if (!(0, import_node_fs21.existsSync)(path)) return [];
10224
- return (0, import_node_fs21.readFileSync)(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
10393
+ const path2 = (0, import_node_path18.join)(repoPath, ".worktreeinclude");
10394
+ if (!(0, import_node_fs21.existsSync)(path2)) return [];
10395
+ return (0, import_node_fs21.readFileSync)(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
10225
10396
  }
10226
10397
  function resolveFilesToCopy(repoPath) {
10227
10398
  const fromInclude = readWorktreeInclude(repoPath);
@@ -10296,8 +10467,20 @@ async function captureLoginEnv() {
10296
10467
  cachedLoginEnv = { ...process.env };
10297
10468
  return { ...cachedLoginEnv };
10298
10469
  }
10470
+ var NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
10471
+ function stripNestedElectronEnv(env) {
10472
+ const out = { ...env };
10473
+ for (const key of Object.keys(out)) {
10474
+ if (NESTED_ELECTRON_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
10475
+ delete out[key];
10476
+ }
10477
+ }
10478
+ return out;
10479
+ }
10299
10480
  function buildWorkspaceScriptEnv(opts, baseEnv) {
10300
- const env = { ...baseEnv ?? process.env };
10481
+ const env = stripNestedElectronEnv({
10482
+ ...baseEnv ?? process.env
10483
+ });
10301
10484
  const name = opts.workspaceName ?? (0, import_node_path18.basename)(opts.worktreePath);
10302
10485
  const ports = opts.ports ?? [];
10303
10486
  const primary = ports[0];
@@ -10522,10 +10705,10 @@ var import_node_path19 = require("path");
10522
10705
  var import_execa4 = require("execa");
10523
10706
  var import_node_readline3 = require("readline");
10524
10707
  function loadCursorWorktreesJson(rootPath) {
10525
- const path = (0, import_node_path19.join)(rootPath, ".cursor", "worktrees.json");
10526
- if (!(0, import_node_fs22.existsSync)(path)) return null;
10708
+ const path2 = (0, import_node_path19.join)(rootPath, ".cursor", "worktrees.json");
10709
+ if (!(0, import_node_fs22.existsSync)(path2)) return null;
10527
10710
  try {
10528
- return JSON.parse((0, import_node_fs22.readFileSync)(path, "utf8"));
10711
+ return JSON.parse((0, import_node_fs22.readFileSync)(path2, "utf8"));
10529
10712
  } catch {
10530
10713
  return null;
10531
10714
  }
@@ -10628,9 +10811,9 @@ function parseNameStatus(stdout) {
10628
10811
  const out = [];
10629
10812
  for (const line of stdout.split("\n").filter(Boolean)) {
10630
10813
  const [status, ...pathParts] = line.split(" ");
10631
- const path = pathParts[pathParts.length - 1];
10632
- if (!status || !path) continue;
10633
- out.push({ status: status[0] ?? status, path });
10814
+ const path2 = pathParts[pathParts.length - 1];
10815
+ if (!status || !path2) continue;
10816
+ out.push({ status: status[0] ?? status, path: path2 });
10634
10817
  }
10635
10818
  return out;
10636
10819
  }
@@ -10638,12 +10821,12 @@ function parseNumstat(stdout) {
10638
10821
  const map = /* @__PURE__ */ new Map();
10639
10822
  for (const line of stdout.split("\n").filter(Boolean)) {
10640
10823
  const [addRaw, delRaw, ...pathParts] = line.split(" ");
10641
- const path = pathParts[pathParts.length - 1];
10642
- if (!path) continue;
10824
+ const path2 = pathParts[pathParts.length - 1];
10825
+ if (!path2) continue;
10643
10826
  const additions = addRaw === "-" ? 0 : Number(addRaw);
10644
10827
  const deletions = delRaw === "-" ? 0 : Number(delRaw);
10645
10828
  if (!Number.isFinite(additions) && !Number.isFinite(deletions)) continue;
10646
- map.set(path, {
10829
+ map.set(path2, {
10647
10830
  additions: Number.isFinite(additions) ? additions : 0,
10648
10831
  deletions: Number.isFinite(deletions) ? deletions : 0
10649
10832
  });
@@ -10720,70 +10903,70 @@ function splitCombinedDiff(stdout) {
10720
10903
  }
10721
10904
  return map;
10722
10905
  }
10723
- function syntheticAddPatch(path, content, maxHunk) {
10906
+ function syntheticAddPatch(path2, content, maxHunk) {
10724
10907
  const lines = content.split("\n");
10725
10908
  const body = lines.map((l) => `+${l}`).join("\n");
10726
- const header = `diff --git a/${path} b/${path}
10909
+ const header = `diff --git a/${path2} b/${path2}
10727
10910
  new file mode 100644
10728
10911
  --- /dev/null
10729
- +++ b/${path}
10912
+ +++ b/${path2}
10730
10913
  @@ -0,0 +1,${lines.length} @@
10731
10914
  `;
10732
10915
  return {
10733
- path,
10916
+ path: path2,
10734
10917
  status: "A",
10735
10918
  patch: capPatch(`${header}${body}`, maxHunk),
10736
10919
  additions: lines.length,
10737
10920
  deletions: 0
10738
10921
  };
10739
10922
  }
10740
- async function untrackedPatch(worktreePath, path, maxHunk) {
10741
- const abs = (0, import_node_path20.join)(worktreePath, path);
10923
+ async function untrackedPatch(worktreePath, path2, maxHunk) {
10924
+ const abs = (0, import_node_path20.join)(worktreePath, path2);
10742
10925
  try {
10743
10926
  const st = (0, import_node_fs23.statSync)(abs);
10744
10927
  if (st.isFile() && st.size > maxHunk) {
10745
10928
  const buf = (0, import_node_fs23.readFileSync)(abs).subarray(0, maxHunk);
10746
- return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
10929
+ return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
10747
10930
  }
10748
10931
  } catch {
10749
10932
  }
10750
10933
  const { stdout: patch } = await git(
10751
- ["diff", "--no-ext-diff", "--no-color", "--no-index", "--", "/dev/null", path],
10934
+ ["diff", "--no-ext-diff", "--no-color", "--no-index", "--", "/dev/null", path2],
10752
10935
  worktreePath,
10753
10936
  { reject: false, timeoutMs: 4e3 }
10754
10937
  );
10755
- const normalized = patch.replace(/^diff --git a\/.+? b\/.+?$/m, `diff --git a/${path} b/${path}`).replace(/^--- .*$/m, "--- /dev/null").replace(/^\+\+\+ .*$/m, `+++ b/${path}`);
10938
+ const normalized = patch.replace(/^diff --git a\/.+? b\/.+?$/m, `diff --git a/${path2} b/${path2}`).replace(/^--- .*$/m, "--- /dev/null").replace(/^\+\+\+ .*$/m, `+++ b/${path2}`);
10756
10939
  const addLines = normalized.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
10757
10940
  return {
10758
- path,
10941
+ path: path2,
10759
10942
  status: "A",
10760
10943
  patch: capPatch(normalized, maxHunk),
10761
10944
  additions: addLines,
10762
10945
  deletions: 0
10763
10946
  };
10764
10947
  }
10765
- async function listUntrackedPaths(worktreePath, path) {
10948
+ async function listUntrackedPaths(worktreePath, path2) {
10766
10949
  const args = ["ls-files", "-z", "--others", "--exclude-standard"];
10767
- if (path) args.push("--", path);
10950
+ if (path2) args.push("--", path2);
10768
10951
  const { stdout } = await git(args, worktreePath, { reject: false });
10769
10952
  return stdout.split("\0").filter(Boolean).filter((p) => !isSideboardScratchPath(p));
10770
10953
  }
10771
10954
  async function listUntrackedFiles(worktreePath, maxHunk, paths) {
10772
10955
  const list = paths ?? await listUntrackedPaths(worktreePath);
10773
10956
  if (list.length === 0) return [];
10774
- return Promise.all(list.map((path) => untrackedPatch(worktreePath, path, maxHunk)));
10957
+ return Promise.all(list.map((path2) => untrackedPatch(worktreePath, path2, maxHunk)));
10775
10958
  }
10776
10959
  function untrackedStubs(paths) {
10777
- return paths.map((path) => ({
10778
- path,
10960
+ return paths.map((path2) => ({
10961
+ path: path2,
10779
10962
  status: "A",
10780
10963
  patch: "",
10781
10964
  additions: 0,
10782
10965
  deletions: 0
10783
10966
  }));
10784
10967
  }
10785
- function pathspec(path) {
10786
- return path ? ["--", path] : [];
10968
+ function pathspec(path2) {
10969
+ return path2 ? ["--", path2] : [];
10787
10970
  }
10788
10971
  function gitDiff(args, cwd, opts) {
10789
10972
  return git(["diff", "--no-ext-diff", "--no-color", ...args], cwd, {
@@ -10791,7 +10974,7 @@ function gitDiff(args, cwd, opts) {
10791
10974
  timeoutMs: opts?.timeoutMs
10792
10975
  });
10793
10976
  }
10794
- function fileFromPatch(path, patch, maxHunk) {
10977
+ function fileFromPatch(path2, patch, maxHunk) {
10795
10978
  let status = "M";
10796
10979
  if (/^new file mode /m.test(patch) || /^--- \/dev\/null/m.test(patch)) {
10797
10980
  status = "A";
@@ -10803,15 +10986,15 @@ function fileFromPatch(path, patch, maxHunk) {
10803
10986
  const additions = patch.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
10804
10987
  const deletions = patch.split("\n").filter((l) => l.startsWith("-") && !l.startsWith("---")).length;
10805
10988
  return {
10806
- path,
10989
+ path: path2,
10807
10990
  status,
10808
10991
  patch: capPatch(patch, maxHunk),
10809
10992
  additions,
10810
10993
  deletions
10811
10994
  };
10812
10995
  }
10813
- async function gitDiffParts(worktreePath, diffArgs, includePatches, path) {
10814
- const extra = pathspec(path);
10996
+ async function gitDiffParts(worktreePath, diffArgs, includePatches, path2) {
10997
+ const extra = pathspec(path2);
10815
10998
  if (includePatches) {
10816
10999
  const diff = await gitDiff([...diffArgs, ...extra], worktreePath, {
10817
11000
  timeoutMs: 4e3
@@ -10849,17 +11032,17 @@ function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
10849
11032
  const patches = splitCombinedDiff(combinedDiff);
10850
11033
  if (!nameStatus.trim() && patches.size > 0) {
10851
11034
  return [...patches.entries()].map(
10852
- ([path, patch]) => fileFromPatch(path, patch, maxHunk)
11035
+ ([path2, patch]) => fileFromPatch(path2, patch, maxHunk)
10853
11036
  );
10854
11037
  }
10855
11038
  const counts = parseNumstat(numstat);
10856
11039
  const files = [];
10857
- for (const { status, path } of parseNameStatus(nameStatus)) {
10858
- const n = counts.get(path);
11040
+ for (const { status, path: path2 } of parseNameStatus(nameStatus)) {
11041
+ const n = counts.get(path2);
10859
11042
  files.push({
10860
- path,
11043
+ path: path2,
10861
11044
  status,
10862
- patch: capPatch(patches.get(path) ?? "", maxHunk),
11045
+ patch: capPatch(patches.get(path2) ?? "", maxHunk),
10863
11046
  additions: n?.additions,
10864
11047
  deletions: n?.deletions
10865
11048
  });
@@ -10943,7 +11126,7 @@ function emptyPathDiff(scope, base, hasLastTurnBase, commitSha = null) {
10943
11126
  };
10944
11127
  }
10945
11128
  async function getPathDiff(worktreePath, repoPath, opts) {
10946
- const { path, scope, commitSha, lastTurnBase, hasLastTurnBase, includePatches, maxHunk } = opts;
11129
+ const { path: path2, scope, commitSha, lastTurnBase, hasLastTurnBase, includePatches, maxHunk } = opts;
10947
11130
  if (scope === "last_turn" && !lastTurnBase) {
10948
11131
  return emptyPathDiff(scope, "last turn", false);
10949
11132
  }
@@ -10982,7 +11165,7 @@ async function getPathDiff(worktreePath, repoPath, opts) {
10982
11165
  labelBase = "HEAD";
10983
11166
  }
10984
11167
  const diff = await gitDiff(
10985
- includePatches ? [...diffArgs, "--", path] : ["--name-status", ...diffArgs, "--", path],
11168
+ includePatches ? [...diffArgs, "--", path2] : ["--name-status", ...diffArgs, "--", path2],
10986
11169
  worktreePath,
10987
11170
  { timeoutMs: 4e3 }
10988
11171
  );
@@ -10990,8 +11173,8 @@ async function getPathDiff(worktreePath, repoPath, opts) {
10990
11173
  if (includePatches) {
10991
11174
  const parsed = filesFromDiff("", "", diff.stdout, maxHunk);
10992
11175
  for (const file of parsed) filesMap.set(file.path, file);
10993
- if (diff.stdout.trim() && !filesMap.has(path)) {
10994
- filesMap.set(path, fileFromPatch(path, diff.stdout, maxHunk));
11176
+ if (diff.stdout.trim() && !filesMap.has(path2)) {
11177
+ filesMap.set(path2, fileFromPatch(path2, diff.stdout, maxHunk));
10995
11178
  }
10996
11179
  } else {
10997
11180
  for (const file of filesFromDiff(diff.stdout, "", "", maxHunk)) {
@@ -10999,7 +11182,7 @@ async function getPathDiff(worktreePath, repoPath, opts) {
10999
11182
  }
11000
11183
  }
11001
11184
  if (filesMap.size === 0) {
11002
- const maybe = await listUntrackedPaths(worktreePath, path);
11185
+ const maybe = await listUntrackedPaths(worktreePath, path2);
11003
11186
  if (maybe.length > 0) {
11004
11187
  const extra = includePatches ? await listUntrackedFiles(worktreePath, maxHunk, maybe) : untrackedStubs(maybe);
11005
11188
  for (const file of extra) filesMap.set(file.path, file);
@@ -12025,8 +12208,8 @@ function splitForCompaction(messages, thresholds = {}) {
12025
12208
  function formatToolPart(t, detail) {
12026
12209
  if (detail === "summary") {
12027
12210
  const label = t.description || t.detail || t.name;
12028
- const path = t.filePath ? ` (${t.filePath})` : "";
12029
- return `- ${t.name}: ${label}${path}`;
12211
+ const path2 = t.filePath ? ` (${t.filePath})` : "";
12212
+ return `- ${t.name}: ${label}${path2}`;
12030
12213
  }
12031
12214
  const lines = [`#### Tool: ${t.name}`, `Status: ${t.status}`];
12032
12215
  if (t.description) lines.push(`Description: ${t.description}`);
@@ -13182,10 +13365,10 @@ function storePath2() {
13182
13365
  return (0, import_node_path24.join)(appDataDir(), "slack-reply-to.json");
13183
13366
  }
13184
13367
  function readStore2() {
13185
- const path = storePath2();
13186
- if (!(0, import_node_fs29.existsSync)(path)) return {};
13368
+ const path2 = storePath2();
13369
+ if (!(0, import_node_fs29.existsSync)(path2)) return {};
13187
13370
  try {
13188
- const parsed = isSecureFileEncrypted(path) ? readSecureJson(path) : JSON.parse((0, import_node_fs29.readFileSync)(path, "utf8"));
13371
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs29.readFileSync)(path2, "utf8"));
13189
13372
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
13190
13373
  } catch {
13191
13374
  return {};
@@ -13210,12 +13393,12 @@ function storePath3() {
13210
13393
  }
13211
13394
  function readStore3() {
13212
13395
  try {
13213
- const path = storePath3();
13214
- const wasEncrypted = isSecureFileEncrypted(path);
13215
- const parsed = readSecureJson(path);
13396
+ const path2 = storePath3();
13397
+ const wasEncrypted = isSecureFileEncrypted(path2);
13398
+ const parsed = readSecureJson(path2);
13216
13399
  const workspaces = Array.isArray(parsed?.workspaces) ? parsed.workspaces : [];
13217
13400
  if (workspaces.length > 0 && !wasEncrypted && resolveVaultKey()) {
13218
- writeSecureJson(path, { workspaces });
13401
+ writeSecureJson(path2, { workspaces });
13219
13402
  }
13220
13403
  return workspaces;
13221
13404
  } catch {
@@ -13351,10 +13534,10 @@ function tsNewer(a, b) {
13351
13534
  return Number(a) > Number(b);
13352
13535
  }
13353
13536
  function readStore4() {
13354
- const path = storePath4();
13355
- if (!(0, import_node_fs30.existsSync)(path)) return [];
13537
+ const path2 = storePath4();
13538
+ if (!(0, import_node_fs30.existsSync)(path2)) return [];
13356
13539
  try {
13357
- const parsed = isSecureFileEncrypted(path) ? readSecureJson(path) : JSON.parse((0, import_node_fs30.readFileSync)(path, "utf8"));
13540
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse((0, import_node_fs30.readFileSync)(path2, "utf8"));
13358
13541
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
13359
13542
  } catch {
13360
13543
  return [];
@@ -13726,8 +13909,8 @@ init_worktree();
13726
13909
  init_thread_store();
13727
13910
  init_paths();
13728
13911
  init_app_settings();
13729
- function isSideboardWorktreePath(path) {
13730
- return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
13912
+ function isSideboardWorktreePath(path2) {
13913
+ return path2.includes("/.sideboard/worktrees/") || path2.includes("/sideboard/workspaces/");
13731
13914
  }
13732
13915
  async function findOrphanWorktrees(repoPaths) {
13733
13916
  const threads = listThreads({ includeArchived: true });
@@ -13752,18 +13935,18 @@ async function findOrphanWorktrees(repoPaths) {
13752
13935
  try {
13753
13936
  const wts = await listWorktrees(repoPath);
13754
13937
  for (const wt of wts) {
13755
- const path = wt.path.replace(/\/$/, "");
13756
- if (!isSideboardWorktreePath(path)) continue;
13757
- if (known.has(path)) continue;
13758
- if (seen.has(path)) continue;
13759
- seen.add(path);
13938
+ const path2 = wt.path.replace(/\/$/, "");
13939
+ if (!isSideboardWorktreePath(path2)) continue;
13940
+ if (known.has(path2)) continue;
13941
+ if (seen.has(path2)) continue;
13942
+ seen.add(path2);
13760
13943
  let mtimeMs = 0;
13761
13944
  try {
13762
- mtimeMs = (0, import_node_fs31.statSync)(path).mtimeMs;
13945
+ mtimeMs = (0, import_node_fs31.statSync)(path2).mtimeMs;
13763
13946
  } catch {
13764
13947
  mtimeMs = 0;
13765
13948
  }
13766
- orphans.push({ path, repoPath, mtimeMs });
13949
+ orphans.push({ path: path2, repoPath, mtimeMs });
13767
13950
  }
13768
13951
  } catch {
13769
13952
  }
@@ -13772,17 +13955,17 @@ async function findOrphanWorktrees(repoPaths) {
13772
13955
  if ((0, import_node_fs31.existsSync)(root)) {
13773
13956
  for (const entry of (0, import_node_fs31.readdirSync)(root, { withFileTypes: true })) {
13774
13957
  if (!entry.isDirectory()) continue;
13775
- const path = (0, import_node_path27.join)(root, entry.name).replace(/\/$/, "");
13776
- if (known.has(path) || seen.has(path)) continue;
13777
- if (!(0, import_node_fs31.existsSync)((0, import_node_path27.join)(path, ".git"))) continue;
13778
- seen.add(path);
13958
+ const path2 = (0, import_node_path27.join)(root, entry.name).replace(/\/$/, "");
13959
+ if (known.has(path2) || seen.has(path2)) continue;
13960
+ if (!(0, import_node_fs31.existsSync)((0, import_node_path27.join)(path2, ".git"))) continue;
13961
+ seen.add(path2);
13779
13962
  let mtimeMs = 0;
13780
13963
  try {
13781
- mtimeMs = (0, import_node_fs31.statSync)(path).mtimeMs;
13964
+ mtimeMs = (0, import_node_fs31.statSync)(path2).mtimeMs;
13782
13965
  } catch {
13783
13966
  mtimeMs = Date.now();
13784
13967
  }
13785
- orphans.push({ path, repoPath, mtimeMs });
13968
+ orphans.push({ path: path2, repoPath, mtimeMs });
13786
13969
  }
13787
13970
  }
13788
13971
  } catch {
@@ -14176,9 +14359,9 @@ function ensureReviewRequestFile(worktreePath) {
14176
14359
  const localAbs = (0, import_node_path29.join)(worktreePath, REVIEW_REQUEST_PATH);
14177
14360
  const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent((0, import_node_path29.join)(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
14178
14361
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
14179
- const path = (0, import_node_fs33.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
14362
+ const path2 = (0, import_node_fs33.existsSync)(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
14180
14363
  return {
14181
- path,
14364
+ path: path2,
14182
14365
  name: REVIEW_REQUEST_NAME,
14183
14366
  content: localContent,
14184
14367
  source: "local"
@@ -14194,13 +14377,13 @@ function ensureReviewRequestFile(worktreePath) {
14194
14377
  };
14195
14378
  }
14196
14379
  function buildReviewRequestAttachment(content, opts) {
14197
- const path = opts?.path ?? REVIEW_REQUEST_PATH;
14198
- const name = opts?.name ?? (path === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
14380
+ const path2 = opts?.path ?? REVIEW_REQUEST_PATH;
14381
+ const name = opts?.name ?? (path2 === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
14199
14382
  return {
14200
14383
  id: (0, import_node_crypto8.randomUUID)(),
14201
14384
  name,
14202
14385
  kind: "file",
14203
- path,
14386
+ path: path2,
14204
14387
  content
14205
14388
  };
14206
14389
  }
@@ -14920,14 +15103,16 @@ var Orchestrator = class {
14920
15103
  }
14921
15104
  const isBrightsy = fresh.agent === "brightsy";
14922
15105
  const isOrchestration = isOrchestratorThread(fresh);
15106
+ const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
15107
+ const gitAuthMode = getGithubGitAuthMode2();
14923
15108
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
14924
15109
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
14925
15110
  () => null
14926
- )
15111
+ ),
15112
+ gitAuthMode
14927
15113
  });
14928
15114
  const artifactDirective = isBrightsy ? null : formatArtifactDirective();
14929
15115
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
14930
- const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
14931
15116
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
14932
15117
  customPrompt: settings?.prompts?.renameBranch
14933
15118
  }) : null;
@@ -15764,8 +15949,9 @@ var Orchestrator = class {
15764
15949
  return tab;
15765
15950
  }
15766
15951
  /**
15767
- * Queue a desktop-git-button prompt on a worktree agent (commit/push/PR/merge).
15768
- * Orchestrators use this instead of running git/gh from the synthetic home.
15952
+ * Desktop git buttons + MCP `ask_git`.
15953
+ * When the worktree is clean, push / open the PR here (HTTPS via `gh` if SSH
15954
+ * is missing). When dirty, queue the worktree agent to commit first.
15769
15955
  */
15770
15956
  async askGit(threadRef, action) {
15771
15957
  if (!AGENT_GIT_ACTIONS.includes(action)) {
@@ -15783,6 +15969,10 @@ var Orchestrator = class {
15783
15969
  "No pull request linked. Ask the worktree agent to open a draft PR first (ask_git create-draft)."
15784
15970
  );
15785
15971
  }
15972
+ if ((action === "commit-push" || action === "create-draft" || action === "create-web") && thread.worktreePath?.trim() && !await isDirty(thread.worktreePath)) {
15973
+ await this.pushAndMaybeOpenPr(thread, action);
15974
+ return this.requireThread(threadRef);
15975
+ }
15786
15976
  let prBase;
15787
15977
  if (action === "resolve-conflicts") {
15788
15978
  try {
@@ -15793,6 +15983,34 @@ var Orchestrator = class {
15793
15983
  }
15794
15984
  return this.send(threadRef, agentGitPrompt(action, { prBase }));
15795
15985
  }
15986
+ /** Push origin (gh HTTPS fallback) and create/update the PR when requested. */
15987
+ async pushAndMaybeOpenPr(thread, action) {
15988
+ const cwd = thread.worktreePath;
15989
+ const branch = await currentBranch(cwd);
15990
+ await pushBranch(cwd, branch);
15991
+ if (action === "commit-push") return;
15992
+ const base = await resolveDefaultBranch(thread.repoPath);
15993
+ const meta = await suggestPrMetadata(cwd, {
15994
+ base,
15995
+ fallbackTitle: thread.prTitle ?? thread.title
15996
+ });
15997
+ const url = await createOrUpdatePr(cwd, {
15998
+ title: meta.title,
15999
+ body: meta.body,
16000
+ base,
16001
+ head: branch,
16002
+ draft: action === "create-draft",
16003
+ web: action === "create-web"
16004
+ });
16005
+ if (!url) return;
16006
+ const patch = { prUrl: url, prTitle: meta.title };
16007
+ try {
16008
+ const fetched = await getPrMeta(cwd, url);
16009
+ if (fetched?.title) patch.prTitle = fetched.title;
16010
+ } catch {
16011
+ }
16012
+ updateThread(thread.id, patch);
16013
+ }
15796
16014
  setThreadOptions(threadRef, patch) {
15797
16015
  const thread = this.requireThread(threadRef);
15798
16016
  const next = {};
@@ -16373,9 +16591,9 @@ function labelGithubUrl(url) {
16373
16591
  return null;
16374
16592
  }
16375
16593
  if (!/(^|\.)github\.com$/i.test(parsed.hostname)) return null;
16376
- const path = parsed.pathname;
16594
+ const path2 = parsed.pathname;
16377
16595
  const hash = parsed.hash || "";
16378
- const pr = path.match(/\/([^/]+)\/([^/]+)\/pull\/(\d+)/i);
16596
+ const pr = path2.match(/\/([^/]+)\/([^/]+)\/pull\/(\d+)/i);
16379
16597
  if (pr) {
16380
16598
  const n = pr[3];
16381
16599
  if (/#discussion_r\d+/i.test(hash) || /#pullrequestreview-\d+/i.test(hash)) {
@@ -16386,11 +16604,11 @@ function labelGithubUrl(url) {
16386
16604
  }
16387
16605
  return { kind: "pr", label: `PR #${n}`, url: raw };
16388
16606
  }
16389
- const issueComment = path.match(/\/([^/]+)\/([^/]+)\/issues\/(\d+)/i);
16607
+ const issueComment = path2.match(/\/([^/]+)\/([^/]+)\/issues\/(\d+)/i);
16390
16608
  if (issueComment && /#issuecomment-\d+/i.test(hash)) {
16391
16609
  return { kind: "comment", label: `Issue #${issueComment[3]} comment`, url: raw };
16392
16610
  }
16393
- const blob = path.match(/\/([^/]+)\/([^/]+)\/blob\/[^/]+\/(.+)$/i);
16611
+ const blob = path2.match(/\/([^/]+)\/([^/]+)\/blob\/[^/]+\/(.+)$/i);
16394
16612
  if (blob) {
16395
16613
  const filePath = decodeURIComponent(blob[3]);
16396
16614
  const fileName = filePath.split("/").pop() || filePath;
@@ -16952,10 +17170,10 @@ async function startMcpServer() {
16952
17170
  const t = orch.getThread(thread_id.trim());
16953
17171
  if (t?.worktreePath?.trim()) root = t.worktreePath;
16954
17172
  }
16955
- const path = writePlanFile2(root, content);
17173
+ const path2 = writePlanFile2(root, content);
16956
17174
  const payload = {
16957
17175
  ok: true,
16958
- path,
17176
+ path: path2,
16959
17177
  title: title?.trim() || "Plan",
16960
17178
  message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
16961
17179
  };
@@ -17379,7 +17597,7 @@ async function startMcpServer() {
17379
17597
  );
17380
17598
  server.tool(
17381
17599
  "ask_git",
17382
- "Tell a worktree agent to commit & push, open a draft PR, resolve conflicts, or merge the linked PR \u2014 same short prompts as the desktop git buttons. The worktree agent runs git/gh (`gh pr merge`); this only queues the prompt. Pass a worktree thread ref (not the orchestrator). Then wait_for_turn / get_turn_result. Do not run git or gh from the orchestration cwd.",
17600
+ "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. Pass a worktree thread ref (not the orchestrator). Do not run git or gh from the orchestration cwd.",
17383
17601
  {
17384
17602
  ref: import_zod3.z.string().describe("Worktree thread id/ref"),
17385
17603
  action: import_zod3.z.enum(AGENT_GIT_ACTIONS).describe(
@@ -17852,9 +18070,9 @@ var BrightsySideboardApi = class {
17852
18070
  get endpoint() {
17853
18071
  return (this.cfg.endpoint || "https://brightsy.ai").replace(/\/$/, "");
17854
18072
  }
17855
- async request(path, init = {}) {
18073
+ async request(path2, init = {}) {
17856
18074
  await this.refreshIfNeeded();
17857
- const url = `${this.endpoint}${path}`;
18075
+ const url = `${this.endpoint}${path2}`;
17858
18076
  const headers = {
17859
18077
  Authorization: `Bearer ${this.cfg.access_token}`,
17860
18078
  Accept: "application/json"
@@ -19461,6 +19679,83 @@ var SlackRelayHub = class {
19461
19679
  // src/slack/relay-server.ts
19462
19680
  var import_node_http3 = require("http");
19463
19681
  var import_ws3 = require("ws");
19682
+
19683
+ // src/slack/relay-static.ts
19684
+ var import_node_fs37 = require("fs");
19685
+ var import_promises = require("fs/promises");
19686
+ var import_node_path33 = __toESM(require("path"), 1);
19687
+ var TYPES = {
19688
+ ".css": "text/css; charset=utf-8",
19689
+ ".html": "text/html; charset=utf-8",
19690
+ ".ico": "image/x-icon",
19691
+ ".jpeg": "image/jpeg",
19692
+ ".jpg": "image/jpeg",
19693
+ ".js": "text/javascript; charset=utf-8",
19694
+ ".json": "application/json; charset=utf-8",
19695
+ ".png": "image/png",
19696
+ ".svg": "image/svg+xml",
19697
+ ".txt": "text/plain; charset=utf-8",
19698
+ ".webp": "image/webp",
19699
+ ".woff2": "font/woff2"
19700
+ };
19701
+ function requestHostname(req) {
19702
+ const raw = req.headers.host ?? "";
19703
+ return raw.split(":")[0]?.toLowerCase() ?? "";
19704
+ }
19705
+ function hostnameAllowed(host, allowed) {
19706
+ if (!allowed || allowed.length === 0) return true;
19707
+ return allowed.includes(host);
19708
+ }
19709
+ function resolveStaticPath(root, requestUrl) {
19710
+ let pathname;
19711
+ try {
19712
+ pathname = decodeURIComponent(new URL(requestUrl, "http://relay.local").pathname);
19713
+ } catch {
19714
+ return null;
19715
+ }
19716
+ if (!pathname.startsWith("/") || pathname.includes("\0")) return null;
19717
+ const rootResolved = import_node_path33.default.resolve(root);
19718
+ const candidate = import_node_path33.default.resolve(rootResolved, `.${pathname}`);
19719
+ if (candidate !== rootResolved && !candidate.startsWith(rootResolved + import_node_path33.default.sep)) {
19720
+ return null;
19721
+ }
19722
+ return candidate;
19723
+ }
19724
+ async function fileSize(file) {
19725
+ try {
19726
+ const st = await (0, import_promises.stat)(file);
19727
+ return st.isFile() ? st.size : null;
19728
+ } catch {
19729
+ return null;
19730
+ }
19731
+ }
19732
+ function sendFile(req, res, file, size) {
19733
+ const ext = import_node_path33.default.extname(file).toLowerCase();
19734
+ res.writeHead(200, {
19735
+ "Content-Type": TYPES[ext] ?? "application/octet-stream",
19736
+ "Content-Length": size,
19737
+ "Cache-Control": ext === ".html" ? "no-cache" : "public, max-age=3600"
19738
+ });
19739
+ if (req.method === "HEAD") {
19740
+ res.end();
19741
+ return true;
19742
+ }
19743
+ (0, import_node_fs37.createReadStream)(file).pipe(res);
19744
+ return true;
19745
+ }
19746
+ async function tryServeStatic(req, res, root) {
19747
+ if (req.method !== "GET" && req.method !== "HEAD") return false;
19748
+ const candidate = resolveStaticPath(root, req.url || "/");
19749
+ if (!candidate) return false;
19750
+ const direct = await fileSize(candidate);
19751
+ if (direct != null) return sendFile(req, res, candidate, direct);
19752
+ const asIndex = import_node_path33.default.join(candidate, "index.html");
19753
+ const indexSize = await fileSize(asIndex);
19754
+ if (indexSize != null) return sendFile(req, res, asIndex, indexSize);
19755
+ return false;
19756
+ }
19757
+
19758
+ // src/slack/relay-server.ts
19464
19759
  function sendHtml(res, status, title, body) {
19465
19760
  res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
19466
19761
  res.end(slackOAuthHtmlPage(title, body));
@@ -19559,7 +19854,26 @@ async function startSlackRelayServer(opts) {
19559
19854
  const reqUrl = req.url || "/";
19560
19855
  if (await handleCallback(reqUrl, res)) return;
19561
19856
  if (handleResult(reqUrl, res)) return;
19562
- if (req.url === "/health" || req.url === "/") {
19857
+ if (req.url === "/health") {
19858
+ sendJson(res, 200, {
19859
+ ok: true,
19860
+ service: "sideboard-slack-relay",
19861
+ sessions: hub.listSessions().length,
19862
+ oauth: Boolean(clientSecret)
19863
+ });
19864
+ return;
19865
+ }
19866
+ const hostname2 = requestHostname(req);
19867
+ const canonical = (opts.canonicalSiteHost ?? "www.sideboard.cloud").toLowerCase();
19868
+ if (opts.redirectHosts?.includes(hostname2) && hostname2 !== canonical) {
19869
+ res.writeHead(301, { Location: `https://${canonical}${reqUrl}` });
19870
+ res.end();
19871
+ return;
19872
+ }
19873
+ if (opts.staticRoot && hostnameAllowed(hostname2, opts.staticHosts) && await tryServeStatic(req, res, opts.staticRoot)) {
19874
+ return;
19875
+ }
19876
+ if (req.url === "/") {
19563
19877
  sendJson(res, 200, {
19564
19878
  ok: true,
19565
19879
  service: "sideboard-slack-relay",
@@ -19669,6 +19983,7 @@ async function startSlackRelayServer(opts) {
19669
19983
  CONTEXT_MIN_MESSAGES,
19670
19984
  COORDINATOR_TOOL_PLAYBOOK,
19671
19985
  FAMOUS_SOCCER_TEAMS,
19986
+ GITHUB_GIT_AUTH_MODES,
19672
19987
  GLOBAL_WORKSPACE_ID,
19673
19988
  HARNESS_ENV_KEYS,
19674
19989
  LEGACY_ATTACHMENTS_DIR,
@@ -19718,10 +20033,12 @@ async function startSlackRelayServer(opts) {
19718
20033
  allocateTeamSlug,
19719
20034
  appDataDir,
19720
20035
  appSettingsPath,
20036
+ appendIndexedGitConfig,
19721
20037
  appendMessage,
19722
20038
  applyAgentEvent,
19723
20039
  applyAppEnvironment,
19724
20040
  applyCompaction,
20041
+ applyGithubGitAuthEnv,
19725
20042
  applyThreadIntoMain,
19726
20043
  applyTurnUsage,
19727
20044
  assertOrchestratorCapableAgent,
@@ -19836,6 +20153,7 @@ async function startSlackRelayServer(opts) {
19836
20153
  formatBrightsyFetchError,
19837
20154
  formatFetchError,
19838
20155
  formatGhLandError,
20156
+ formatGitAuthModeDirective,
19839
20157
  formatIpcInvokeError,
19840
20158
  formatMessagesAsTranscript,
19841
20159
  formatPlanQuestionAnswers,
@@ -19862,6 +20180,8 @@ async function startSlackRelayServer(opts) {
19862
20180
  getDiff,
19863
20181
  getDiffSummary,
19864
20182
  getGitHubStatus,
20183
+ getGithubGitAuthMode,
20184
+ getGithubPat,
19865
20185
  getIssueSource,
19866
20186
  getLinearApiKey,
19867
20187
  getLinearAuthToken,
@@ -19880,6 +20200,7 @@ async function startSlackRelayServer(opts) {
19880
20200
  ghHeadRef,
19881
20201
  ghRepoSelectArgs,
19882
20202
  git,
20203
+ githubAgentGitEnv,
19883
20204
  globalAgentCwd,
19884
20205
  handleSlackInbound,
19885
20206
  harnessEnvKey,
@@ -20102,6 +20423,7 @@ async function startSlackRelayServer(opts) {
20102
20423
  startSlackOAuth,
20103
20424
  startSlackRelayServer,
20104
20425
  stripBrightsyNdjsonNoise,
20426
+ stripNestedElectronEnv,
20105
20427
  submitPrStack,
20106
20428
  suggestSlug,
20107
20429
  summarizeConversation,