@sideboard-ai/core 0.1.149 → 0.1.151

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/{abletime-QEVAE67D.js → abletime-223A3LY6.js} +2 -2
  2. package/dist/{abletime-DI55AJX3.js → abletime-CLC5UJC2.js} +2 -2
  3. package/dist/agents/cursor-runner.cjs +40 -35
  4. package/dist/agents/cursor-runner.js +2 -2
  5. package/dist/{agents-V4MTDWXS.js → agents-JAOMXDXN.js} +10 -9
  6. package/dist/{agents-HSVQ2Z6Y.js → agents-U2JCFXW4.js} +10 -9
  7. package/dist/{app-settings-WJDCJFSY.js → app-settings-3CHCNIMD.js} +5 -1
  8. package/dist/{app-settings-ZCN632OJ.js → app-settings-J3JBCUOT.js} +5 -1
  9. package/dist/{chunk-N5PM7HGQ.js → chunk-2SDMENAL.js} +1 -1
  10. package/dist/{chunk-JTPDWTWJ.js → chunk-3JBMACYX.js} +137 -114
  11. package/dist/{chunk-I77RYPOH.js → chunk-4XNSGDXE.js} +12 -0
  12. package/dist/{chunk-JDYKQ6OI.js → chunk-6H4TJ5TA.js} +136 -75
  13. package/dist/{chunk-EGDRPA4B.js → chunk-77MP647F.js} +6 -6
  14. package/dist/{chunk-KBWND62T.js → chunk-7F4XE6UG.js} +5 -5
  15. package/dist/{chunk-R4JP32P5.js → chunk-ADCEGG4A.js} +4 -4
  16. package/dist/{chunk-BOD5V52W.js → chunk-B7XK37WZ.js} +1 -1
  17. package/dist/{chunk-BGQBELGJ.js → chunk-CZD4NRDZ.js} +12 -0
  18. package/dist/{chunk-KPIYENTF.js → chunk-FNQNJOHY.js} +89 -5
  19. package/dist/{chunk-ED4UPEJX.js → chunk-G7SIQFBQ.js} +1 -1
  20. package/dist/{chunk-TZWO7J4W.js → chunk-GCWAAPJ5.js} +115 -54
  21. package/dist/{chunk-YP3CSOZ6.js → chunk-GE7M3QTE.js} +142 -114
  22. package/dist/{chunk-LAFA56WD.js → chunk-HEII7W3Q.js} +1 -1
  23. package/dist/{chunk-3QRGCKYL.js → chunk-HWZWBJWL.js} +2 -2
  24. package/dist/{chunk-FKOIHGKV.js → chunk-JRH62XC4.js} +5 -1
  25. package/dist/{chunk-Q5DHVMXB.js → chunk-LZAOX76N.js} +18 -7
  26. package/dist/{chunk-B3SJXYIJ.js → chunk-NVXVEGGM.js} +1 -1
  27. package/dist/{chunk-ZVYLEF72.js → chunk-OXOU2FRS.js} +6 -6
  28. package/dist/chunk-QQKDGPUI.js +63 -0
  29. package/dist/{chunk-QTUESPAW.js → chunk-RFMQY56R.js} +1 -1
  30. package/dist/chunk-SYFVUF2C.js +65 -0
  31. package/dist/{chunk-Q62SDB53.js → chunk-TSCWSAQL.js} +2 -2
  32. package/dist/{chunk-DVM4ID64.js → chunk-UFHNSFAS.js} +89 -5
  33. package/dist/{chunk-PTI5H3RK.js → chunk-UIS7BVCX.js} +24 -13
  34. package/dist/{chunk-VZJVKQHH.js → chunk-WLV7TRMR.js} +1 -1
  35. package/dist/{chunk-N3VNOFXQ.js → chunk-Y2FNDKHF.js} +4 -4
  36. package/dist/{connected-teams-IJUJQ7R6.js → connected-teams-I2WZZZFA.js} +2 -2
  37. package/dist/{connected-teams-4A2AXDNN.js → connected-teams-XO766HB4.js} +2 -2
  38. package/dist/{coordinator-prompt-ZWBMAIWA.js → coordinator-prompt-LWYUZAXR.js} +6 -5
  39. package/dist/{coordinator-prompt-5GXGBGEZ.js → coordinator-prompt-ZK5MII7W.js} +6 -5
  40. package/dist/{global-workspace-OJRXWXEA.js → global-workspace-IB2CCEJF.js} +7 -6
  41. package/dist/{global-workspace-2SIPM4PW.js → global-workspace-T7RL3XEC.js} +7 -6
  42. package/dist/index.cjs +1010 -577
  43. package/dist/index.d.cts +96 -11
  44. package/dist/index.d.ts +96 -11
  45. package/dist/index.js +272 -93
  46. package/dist/mcp/run-stdio.cjs +981 -581
  47. package/dist/mcp/run-stdio.js +196 -43
  48. package/dist/{orchestrator-Z2O5OVJA.js → orchestrator-DPTRBKEH.js} +16 -13
  49. package/dist/{orchestrator-6TXQWBXN.js → orchestrator-Y5MWUU7C.js} +14 -11
  50. package/dist/{plan-file-6O7G4VPQ.js → plan-file-LSSSQGTQ.js} +2 -2
  51. package/dist/{plan-file-PHVKUAEE.js → plan-file-OZEBFSGQ.js} +2 -2
  52. package/dist/{run-XQKGHQZ2.js → run-CAGKZLHA.js} +1 -1
  53. package/dist/{run-USF6FRKV.js → run-EBWL2Q4N.js} +1 -1
  54. package/dist/{workspaces-UUJNOO4T.js → workspaces-VICQYI2N.js} +8 -7
  55. package/dist/{workspaces-3NUUZTQK.js → workspaces-ZXCUVCZC.js} +8 -7
  56. package/dist/{worktree-RZRJNHYL.js → worktree-DPIREDFR.js} +5 -4
  57. package/dist/{worktree-UCWWTVLS.js → worktree-HTUM35NT.js} +5 -4
  58. package/dist/worktree-exclude-N5X5I422.js +13 -0
  59. package/dist/worktree-exclude-X54WSOXM.js +11 -0
  60. package/package.json +1 -1
@@ -2,26 +2,36 @@
2
2
 
3
3
  import {
4
4
  isWorkspaceScratchPath
5
- } from "./chunk-B3SJXYIJ.js";
5
+ } from "./chunk-NVXVEGGM.js";
6
6
  import {
7
7
  listThreads
8
8
  } from "./chunk-EUXOHTUK.js";
9
+ import {
10
+ ensureWorktreeSideboardIgnored
11
+ } from "./chunk-SYFVUF2C.js";
9
12
  import {
10
13
  gh,
11
14
  git,
15
+ githubAgentAuthDir,
16
+ githubAgentAuthReady,
17
+ githubCredentialHelperGitConfig,
18
+ githubGhConfigEnv,
19
+ materializeGithubAgentAuth,
12
20
  resolveGhAuthToken
13
- } from "./chunk-KPIYENTF.js";
21
+ } from "./chunk-FNQNJOHY.js";
14
22
  import {
15
23
  getGithubGitAuthMode,
16
24
  getGithubPat
17
- } from "./chunk-BGQBELGJ.js";
25
+ } from "./chunk-CZD4NRDZ.js";
18
26
  import {
19
27
  worktreesRoot
20
28
  } from "./chunk-BD2ICC4D.js";
21
29
 
22
30
  // src/git/worktree.ts
23
- import { existsSync as existsSync2, readdirSync, realpathSync } from "fs";
24
- import { join as join2 } from "path";
31
+ import { randomBytes } from "crypto";
32
+ import { existsSync, readdirSync, realpathSync, unlinkSync, writeFileSync } from "fs";
33
+ import { tmpdir } from "os";
34
+ import { join } from "path";
25
35
 
26
36
  // src/git/team-meta.ts
27
37
  var SOCCER_TEAM_META = {
@@ -835,9 +845,23 @@ function worktreeDisplayLabelForGroup(threads) {
835
845
  }
836
846
 
837
847
  // src/git/gh-errors.ts
848
+ var GITHUB_PR_BODY_MAX_CHARS = 65536;
849
+ var GITHUB_PR_BODY_SAFE_CHARS = 6e4;
838
850
  function isGhRateLimitError(text) {
839
851
  return /API rate limit (already )?exceeded/i.test(text) || /rate limit exceeded/i.test(text);
840
852
  }
853
+ function isGhPrBodyTooLongError(text) {
854
+ return /body is too long|body.{0,20}too long|maximum is 65536|exceeds the maximum allowed size|request (entity |is )?too large|payload too large|413 Request Entity Too Large/i.test(
855
+ text
856
+ );
857
+ }
858
+ function clampGithubPrBody(body, max = GITHUB_PR_BODY_SAFE_CHARS) {
859
+ const t = body.replace(/\s+$/u, "");
860
+ if (t.length <= max) return t;
861
+ const note = "\n\n_(truncated to fit GitHub\u2019s PR body limit)_";
862
+ const keep = Math.max(0, max - note.length);
863
+ return `${t.slice(0, keep).replace(/\s+$/u, "")}${note}`;
864
+ }
841
865
  function formatRateLimitResetHint(resetEpochSec, nowMs = Date.now()) {
842
866
  const ms = resetEpochSec * 1e3 - nowMs;
843
867
  if (ms <= 0) return "soon";
@@ -848,18 +872,32 @@ function formatRateLimitResetHint(resetEpochSec, nowMs = Date.now()) {
848
872
  const hours = Math.ceil(mins / 60);
849
873
  return `in about ${hours} hour${hours === 1 ? "" : "s"}`;
850
874
  }
875
+ function omitGhBodyArg(text) {
876
+ return text.replace(
877
+ /--body(?:-file)?(\s+|=)(?:'[^']*'|"[^"]*"|\S+)/g,
878
+ "--body <omitted>"
879
+ );
880
+ }
881
+ function capGhErrorText(text, max = 400) {
882
+ const t = text.trim();
883
+ if (t.length <= max) return t;
884
+ return `${t.slice(0, max - 1).trimEnd()}\u2026`;
885
+ }
851
886
  function extractGhErrorDetail(text) {
852
- const trimmed = text.trim();
887
+ const trimmed = omitGhBodyArg(text.trim());
853
888
  if (!trimmed) return "";
889
+ if (isGhPrBodyTooLongError(text) || isGhPrBodyTooLongError(trimmed)) {
890
+ return "GraphQL: body is too long (GitHub PR body / request size limit).";
891
+ }
854
892
  const graphql = trimmed.match(/\bGraphQL:\s*(.+)$/im);
855
- if (graphql?.[1]) return `GraphQL: ${graphql[1].trim()}`;
893
+ if (graphql?.[1]) return capGhErrorText(`GraphQL: ${graphql[1].trim()}`);
856
894
  const http = trimmed.match(/\bHTTP\s+\d{3}:\s*(.+)$/im);
857
- if (http?.[1]) return `HTTP: ${http[1].trim()}`;
895
+ if (http?.[1]) return capGhErrorText(`HTTP: ${http[1].trim()}`);
858
896
  const lines = trimmed.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
859
897
  if (lines.length > 1 && /^Command failed with exit code/i.test(lines[0])) {
860
- return lines.slice(1).join(" ").trim() || lines[0];
898
+ return capGhErrorText(lines.slice(1).join(" ").trim() || lines[0]);
861
899
  }
862
- return trimmed;
900
+ return capGhErrorText(trimmed);
863
901
  }
864
902
  function formatGhLandError(raw, opts) {
865
903
  const trimmed = raw.trim();
@@ -877,6 +915,10 @@ function formatGhLandError(raw, opts) {
877
915
  const hint = opts?.targetedRepo ? ` Branch was pushed to origin \u2014 confirm it exists on GitHub and differs from the base branch.${target}${head}.` : " Often `gh` targeted upstream instead of origin. Branch was pushed \u2014 retry in the latest Sideboard, or run: gh pr create -R <owner/name> --base main --head <branch>.";
878
916
  return `Could not create the pull request.${hint}`;
879
917
  }
918
+ if (isGhPrBodyTooLongError(raw) || isGhPrBodyTooLongError(detail)) {
919
+ const pushNote = opts?.pushed === false ? "" : " Your branch was already pushed.";
920
+ return `GitHub rejected the pull request: the GraphQL body is too long (PR description / request size, limit ${GITHUB_PR_BODY_MAX_CHARS} characters).${pushNote} Retry with a short title and body via \`gh pr create --body-file\` (or send_to_thread so the worktree agent writes a brief description). Do not paste a changelog or diff into --body.`;
921
+ }
880
922
  if (isGhRateLimitError(raw) || isGhRateLimitError(detail)) {
881
923
  const when = opts?.resetAt ? ` Try again ${formatRateLimitResetHint(opts.resetAt, opts.nowMs)}.` : " Wait a few minutes and try again.";
882
924
  const pushNote = opts?.pushed === false ? "" : " Your branch was already pushed.";
@@ -900,88 +942,29 @@ function formatMergePrError(raw) {
900
942
 
901
943
  // src/git/git-auth-mode.ts
902
944
  import { isAbsolute } from "path";
903
-
904
- // src/git/github-agent-auth.ts
905
- import { chmodSync, existsSync, mkdirSync, writeFileSync } from "fs";
906
- import { homedir } from "os";
907
- import { dirname, join } from "path";
908
- function githubAgentAuthDir() {
909
- const override = process.env.SIDEBOARD_GIT_AUTH_DIR?.trim();
910
- if (override) return override;
911
- return join(homedir(), ".sideboard-git-auth");
912
- }
913
- function githubCredentialStorePath() {
914
- return join(githubAgentAuthDir(), "git-credentials");
915
- }
916
- function githubGhConfigDir() {
917
- return join(githubAgentAuthDir(), "gh");
918
- }
919
- function writePrivateFile(file, body) {
920
- mkdirSync(dirname(file), { recursive: true, mode: 448 });
921
- try {
922
- chmodSync(dirname(file), 448);
923
- } catch {
924
- }
925
- writeFileSync(file, body, { encoding: "utf8", mode: 384 });
926
- try {
927
- chmodSync(file, 384);
928
- } catch {
929
- }
930
- }
931
- function gitCredentialStoreContents(token) {
932
- return `https://x-access-token:${encodeURIComponent(token)}@github.com
933
- `;
934
- }
935
- function ghHostsYml(token, user = "x-access-token") {
936
- const u = user.replace(/[^A-Za-z0-9._-]/g, "") || "x-access-token";
937
- return [
938
- "github.com:",
939
- " git_protocol: https",
940
- ` user: ${u}`,
941
- ` oauth_token: ${token}`,
942
- " users:",
943
- ` ${u}:`,
944
- ` oauth_token: ${token}`,
945
- ""
946
- ].join("\n");
947
- }
948
- function materializeGithubAgentAuth(token, user) {
949
- const trimmed = token.trim();
950
- if (!trimmed) return;
951
- const root = githubAgentAuthDir();
952
- mkdirSync(root, { recursive: true, mode: 448 });
953
- try {
954
- chmodSync(root, 448);
955
- } catch {
956
- }
957
- writePrivateFile(githubCredentialStorePath(), gitCredentialStoreContents(trimmed));
958
- const ghDir = githubGhConfigDir();
959
- mkdirSync(ghDir, { recursive: true, mode: 448 });
960
- writePrivateFile(join(ghDir, "hosts.yml"), ghHostsYml(trimmed, user));
961
- writePrivateFile(join(ghDir, "config.yml"), "git_protocol: https\nprompt: disabled\n");
962
- }
963
- function githubAgentAuthReady() {
964
- return existsSync(githubCredentialStorePath()) && existsSync(join(githubGhConfigDir(), "hosts.yml"));
965
- }
966
- function githubCredentialHelperGitConfig() {
967
- const file = githubCredentialStorePath();
968
- return [
969
- { key: "credential.helper", value: "" },
970
- { key: "credential.helper", value: `store --file=${file}` }
971
- ];
972
- }
973
- function githubGhConfigEnv() {
974
- return {
975
- GH_CONFIG_DIR: githubGhConfigDir(),
976
- GH_PROMPT_DISABLED: "1"
977
- };
978
- }
979
-
980
- // src/git/git-auth-mode.ts
981
945
  var HTTPS_REWRITE = [
982
946
  { key: "url.https://github.com/.insteadOf", value: "git@github.com:" },
983
947
  { key: "url.https://github.com/.insteadOf", value: "ssh://git@github.com/" }
984
948
  ];
949
+ function githubHttpsInsteadOfEntries(remoteUrl) {
950
+ const entries = [...HTTPS_REWRITE];
951
+ const url = remoteUrl?.trim() ?? "";
952
+ const scpAlias = url.match(/^(git@github\.com-[^:]+):/i);
953
+ if (scpAlias?.[1]) {
954
+ entries.push({
955
+ key: "url.https://github.com/.insteadOf",
956
+ value: `${scpAlias[1]}:`
957
+ });
958
+ }
959
+ const sshAlias = url.match(/^ssh:\/\/git@(github\.com-[^/]+)\//i);
960
+ if (sshAlias?.[1]) {
961
+ entries.push({
962
+ key: "url.https://github.com/.insteadOf",
963
+ value: `ssh://git@${sshAlias[1]}/`
964
+ });
965
+ }
966
+ return entries;
967
+ }
985
968
  var TOKEN_TTL_MS = 12 * 60 * 60 * 1e3;
986
969
  var tokenMemo = null;
987
970
  var GITHUB_CHILD_TOKEN_KEYS = [
@@ -1011,16 +994,19 @@ function appendIndexedGitConfig(existing, entries) {
1011
994
  out.GIT_CONFIG_COUNT = String(count + entries.length);
1012
995
  return out;
1013
996
  }
1014
- function githubAgentGitEnv(existing) {
997
+ function githubAgentGitEnv(existing, opts) {
1015
998
  const helpers = githubAgentAuthReady() ? githubCredentialHelperGitConfig() : [{ key: "credential.helper", value: "" }];
1016
- return appendIndexedGitConfig(existing, [...HTTPS_REWRITE, ...helpers]);
999
+ return appendIndexedGitConfig(existing, [
1000
+ ...githubHttpsInsteadOfEntries(opts?.remoteUrl),
1001
+ ...helpers
1002
+ ]);
1017
1003
  }
1018
1004
  function applyGithubGitAuthEnv(existing, opts) {
1019
1005
  const out = { ...nonInteractiveGitProcessEnv() };
1020
1006
  const token = opts.token?.trim() || existing?.GH_TOKEN?.trim() || "" || "";
1021
1007
  if (token) materializeGithubAgentAuth(token);
1022
1008
  Object.assign(out, githubGhConfigEnv());
1023
- if (opts.mode === "ssh") return out;
1009
+ if (opts.mode === "ssh" && !token) return out;
1024
1010
  Object.assign(out, githubAgentGitEnv(existing));
1025
1011
  return out;
1026
1012
  }
@@ -1133,8 +1119,9 @@ function formatGitAuthModeDirective(mode) {
1133
1119
  case "ssh":
1134
1120
  return [
1135
1121
  "Git authentication (Settings \u2192 Git mode: SSH):",
1136
- "- Keep SSH remotes (`git@github.com:\u2026`). Do not rewrite them to HTTPS.",
1137
- "- SSH is batch-mode: it will not prompt for a Keychain password. If push fails with `Permission denied (publickey)`, tell the user to unlock ssh-agent or switch Settings \u2192 Git to Auto / gh CLI \u2014 do not rewrite remotes yourself.",
1122
+ "- Stored remotes stay SSH (`git@github.com:\u2026`). Do not `git remote set-url` or rewrite them to HTTPS.",
1123
+ "- This process may push over HTTPS via `gh` (process-only `insteadOf`) so ask_git / unattended turns work when ssh-agent is missing \u2014 that does not change the stored remote.",
1124
+ "- SSH is batch-mode: it will not prompt for a Keychain password. If both SSH and HTTPS fail, tell the user to unlock ssh-agent or run `gh auth login` (or switch Settings \u2192 Git to Auto).",
1138
1125
  "- `gh` already authenticates for PRs/API (no token in the environment). Do not set GitHub token environment variables or run `gh auth login` from this turn.",
1139
1126
  "- Push with `git push -u origin HEAD`. Create/update PRs with `gh` as below."
1140
1127
  ].join("\n");
@@ -2228,6 +2215,16 @@ function originFetchBranch(sourceRef) {
2228
2215
  if (ref.startsWith("origin/")) return ref.slice("origin/".length) || null;
2229
2216
  return ref;
2230
2217
  }
2218
+ async function originRemoteUrl(cwd) {
2219
+ const result = await git(["remote", "get-url", "origin"], cwd, { reject: false });
2220
+ return result.exitCode === 0 ? result.stdout.trim() : "";
2221
+ }
2222
+ function githubHttpsRetryEnv(remoteUrl) {
2223
+ return {
2224
+ GIT_TERMINAL_PROMPT: "0",
2225
+ ...githubAgentGitEnv(void 0, { remoteUrl })
2226
+ };
2227
+ }
2231
2228
  async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
2232
2229
  const first = await git(args, repoPath, { reject: false, timeoutMs });
2233
2230
  if (first.exitCode === 0) return true;
@@ -2237,15 +2234,14 @@ async function fetchOriginWithAuth(repoPath, args, timeoutMs) {
2237
2234
  } catch {
2238
2235
  return false;
2239
2236
  }
2240
- if (mode === "ssh") return false;
2241
2237
  const token = await resolveGithubAgentToken(mode, repoPath);
2242
2238
  if (!token) return false;
2239
+ const remoteUrl = await originRemoteUrl(repoPath);
2243
2240
  const tryHttps = async (header) => git(args, repoPath, {
2244
2241
  reject: false,
2245
2242
  timeoutMs,
2246
- env: { GIT_TERMINAL_PROMPT: "0" },
2243
+ env: githubHttpsRetryEnv(remoteUrl),
2247
2244
  config: {
2248
- "url.https://github.com/.insteadOf": "git@github.com:",
2249
2245
  "http.extraHeader": header
2250
2246
  }
2251
2247
  });
@@ -2327,8 +2323,8 @@ async function refreshOriginAndMaybeFastForwardMain(repoPath, sourceRef) {
2327
2323
  }
2328
2324
  async function createThreadWorktree(opts) {
2329
2325
  let branchName = `thread/${opts.slug}`;
2330
- const worktreePath = join2(worktreesRoot(opts.repoPath), opts.slug);
2331
- if (existsSync2(worktreePath)) {
2326
+ const worktreePath = join(worktreesRoot(opts.repoPath), opts.slug);
2327
+ if (existsSync(worktreePath)) {
2332
2328
  throw new Error(`Worktree already exists at ${worktreePath}`);
2333
2329
  }
2334
2330
  await ensureGhPreferOrigin(opts.repoPath);
@@ -2363,13 +2359,14 @@ ${add.stdout}`;
2363
2359
  });
2364
2360
  branchName = added;
2365
2361
  await ensureGhPreferOrigin(worktreePath);
2362
+ await ensureWorktreeSideboardIgnored(worktreePath);
2366
2363
  return { branchName, worktreePath };
2367
2364
  }
2368
2365
  async function createExistingBranchWorktree(opts) {
2369
2366
  const branchName = opts.branchName.trim();
2370
2367
  if (!branchName) throw new Error("branch name required");
2371
- const worktreePath = join2(worktreesRoot(opts.repoPath), opts.slug);
2372
- if (existsSync2(worktreePath)) {
2368
+ const worktreePath = join(worktreesRoot(opts.repoPath), opts.slug);
2369
+ if (existsSync(worktreePath)) {
2373
2370
  throw new Error(`Worktree already exists at ${worktreePath}`);
2374
2371
  }
2375
2372
  await ensureGhPreferOrigin(opts.repoPath);
@@ -2407,6 +2404,7 @@ async function createExistingBranchWorktree(opts) {
2407
2404
  await git(["checkout", "-B", branchName], worktreePath, { reject: false });
2408
2405
  }
2409
2406
  await ensureGhPreferOrigin(worktreePath);
2407
+ await ensureWorktreeSideboardIgnored(worktreePath);
2410
2408
  return { branchName, worktreePath };
2411
2409
  }
2412
2410
  async function removeWorktree(repoPath, worktreePath, opts) {
@@ -2495,7 +2493,9 @@ async function currentBranch(worktreePath) {
2495
2493
  async function commitAll(worktreePath, message) {
2496
2494
  const dirty = await isDirty(worktreePath);
2497
2495
  if (!dirty) return false;
2496
+ await ensureWorktreeSideboardIgnored(worktreePath);
2498
2497
  await git(["add", "-A"], worktreePath);
2498
+ await git(["reset", "-q", "--", ".sideboard"], worktreePath, { reject: false });
2499
2499
  await git(["commit", "-m", message], worktreePath);
2500
2500
  return true;
2501
2501
  }
@@ -2509,14 +2509,14 @@ async function pushBranch(worktreePath, branchName) {
2509
2509
  const token = await resolveGithubAgentToken(getGithubGitAuthMode(), worktreePath);
2510
2510
  if (!slug || !token) {
2511
2511
  throw new Error(
2512
- sshErr || `git push origin ${branchName} failed` + (!token ? " (no SSH agent and gh auth token unavailable \u2014 run: gh auth login)" : "")
2512
+ sshErr || `git push origin ${branchName} failed` + (!token ? " (SSH remote and gh auth token unavailable \u2014 unlock ssh-agent or run: gh auth login)" : "")
2513
2513
  );
2514
2514
  }
2515
+ const remoteUrl = await originRemoteUrl(worktreePath);
2515
2516
  const tryHttps = async (header) => git(["push", "-u", "origin", branchName], worktreePath, {
2516
2517
  reject: false,
2517
- env: { GIT_TERMINAL_PROMPT: "0" },
2518
+ env: githubHttpsRetryEnv(remoteUrl),
2518
2519
  config: {
2519
- "url.https://github.com/.insteadOf": "git@github.com:",
2520
2520
  "http.extraHeader": header
2521
2521
  }
2522
2522
  });
@@ -2646,6 +2646,32 @@ async function createOrUpdatePr(worktreePath, opts) {
2646
2646
  const repoArgs = ghRepoSelectArgs(slug);
2647
2647
  const headRef = ghHeadRef(slug, opts.head);
2648
2648
  const branchOnly = opts.head.trim().replace(/^refs\/heads\//, "");
2649
+ const bodyText = clampGithubPrBody(opts.body ?? opts.title);
2650
+ const bodyFile = join(
2651
+ tmpdir(),
2652
+ `sideboard-pr-body-${process.pid}-${randomBytes(6).toString("hex")}.md`
2653
+ );
2654
+ writeFileSync(bodyFile, bodyText.endsWith("\n") ? bodyText : `${bodyText}
2655
+ `, "utf8");
2656
+ const bodyArgs = ["--body-file", bodyFile];
2657
+ try {
2658
+ return await createOrUpdatePrWithBodyFile(worktreePath, {
2659
+ ...opts,
2660
+ slug,
2661
+ repoArgs,
2662
+ headRef,
2663
+ branchOnly,
2664
+ bodyArgs
2665
+ });
2666
+ } finally {
2667
+ try {
2668
+ unlinkSync(bodyFile);
2669
+ } catch {
2670
+ }
2671
+ }
2672
+ }
2673
+ async function createOrUpdatePrWithBodyFile(worktreePath, opts) {
2674
+ const { slug, repoArgs, headRef, branchOnly, bodyArgs } = opts;
2649
2675
  const existing = await gh(
2650
2676
  [...repoArgs, "pr", "view", branchOnly, "--json", "url", "--jq", ".url"],
2651
2677
  worktreePath,
@@ -2661,8 +2687,7 @@ async function createOrUpdatePr(worktreePath, opts) {
2661
2687
  branchOnly,
2662
2688
  "--title",
2663
2689
  opts.title,
2664
- "--body",
2665
- opts.body ?? opts.title
2690
+ ...bodyArgs
2666
2691
  ],
2667
2692
  worktreePath,
2668
2693
  { reject: false }
@@ -2677,8 +2702,7 @@ async function createOrUpdatePr(worktreePath, opts) {
2677
2702
  "--web",
2678
2703
  "--title",
2679
2704
  opts.title,
2680
- "--body",
2681
- opts.body ?? opts.title,
2705
+ ...bodyArgs,
2682
2706
  "--base",
2683
2707
  opts.base,
2684
2708
  "--head",
@@ -2699,8 +2723,7 @@ async function createOrUpdatePr(worktreePath, opts) {
2699
2723
  "create",
2700
2724
  "--title",
2701
2725
  opts.title,
2702
- "--body",
2703
- opts.body ?? opts.title,
2726
+ ...bodyArgs,
2704
2727
  "--base",
2705
2728
  opts.base,
2706
2729
  "--head",
@@ -2724,8 +2747,8 @@ function sameRepoPath(a, b) {
2724
2747
  return normalizeWorktreePath(a) === normalizeWorktreePath(b);
2725
2748
  }
2726
2749
  function listLocalThreadBranchSlugs(repoPath) {
2727
- const refsDir = join2(repoPath, ".git", "refs", "heads", "thread");
2728
- if (!existsSync2(refsDir)) return [];
2750
+ const refsDir = join(repoPath, ".git", "refs", "heads", "thread");
2751
+ if (!existsSync(refsDir)) return [];
2729
2752
  try {
2730
2753
  return readdirSync(refsDir).filter((name) => !name.startsWith(".")).map((name) => normalizeTakenSlug(name));
2731
2754
  } catch {
@@ -2735,7 +2758,7 @@ function listLocalThreadBranchSlugs(repoPath) {
2735
2758
  function collectTakenTeamSlugs(repoPath) {
2736
2759
  const taken = /* @__PURE__ */ new Set();
2737
2760
  const root = worktreesRoot(repoPath);
2738
- if (existsSync2(root)) {
2761
+ if (existsSync(root)) {
2739
2762
  for (const entry of readdirSync(root, { withFileTypes: true })) {
2740
2763
  if (entry.isDirectory() && entry.name !== ".DS_Store") {
2741
2764
  taken.add(normalizeTakenSlug(entry.name));
@@ -2757,8 +2780,8 @@ function allocateTeamSlug(repoPath) {
2757
2780
  const taken = collectTakenTeamSlugs(repoPath);
2758
2781
  for (let attempt = 0; attempt < 32; attempt++) {
2759
2782
  const team = allocateTeamName(taken);
2760
- const path = join2(worktreesRoot(repoPath), team.slug);
2761
- if (!existsSync2(path)) return team;
2783
+ const path = join(worktreesRoot(repoPath), team.slug);
2784
+ if (!existsSync(path)) return team;
2762
2785
  taken.add(team.slug);
2763
2786
  }
2764
2787
  throw new Error("No available soccer team worktree directories left");
@@ -250,6 +250,7 @@ var DEFAULT_AGENTS = /* @__PURE__ */ new Set([
250
250
  "cursor"
251
251
  ]);
252
252
  var GITHUB_GIT_AUTH_MODES = ["auto", "gh", "ssh", "token"];
253
+ var FOLLOW_UP_BEHAVIORS = ["steer", "queue"];
253
254
  function emptyPublicIntegrations() {
254
255
  return {
255
256
  hasLinearApiKey: false,
@@ -573,6 +574,9 @@ function normalizeAdvanced(raw) {
573
574
  if (typeof source.orchestrationQuotaFallbackAgent === "string" && DEFAULT_AGENTS.has(source.orchestrationQuotaFallbackAgent) && source.orchestrationQuotaFallbackAgent !== "brightsy") {
574
575
  out.orchestrationQuotaFallbackAgent = source.orchestrationQuotaFallbackAgent;
575
576
  }
577
+ if (source.followUpBehavior === "queue" || source.followUpBehavior === "steer") {
578
+ out.followUpBehavior = source.followUpBehavior;
579
+ }
576
580
  return out;
577
581
  }
578
582
  function normalizeSettings(raw) {
@@ -1274,6 +1278,9 @@ function updateAdvancedSettings(patch) {
1274
1278
  if (typeof patch.orchestrationQuotaFallbackAgent === "string" && DEFAULT_AGENTS.has(patch.orchestrationQuotaFallbackAgent) && patch.orchestrationQuotaFallbackAgent !== "brightsy") {
1275
1279
  advanced.orchestrationQuotaFallbackAgent = patch.orchestrationQuotaFallbackAgent;
1276
1280
  }
1281
+ if (patch.followUpBehavior === "queue" || patch.followUpBehavior === "steer") {
1282
+ advanced.followUpBehavior = patch.followUpBehavior;
1283
+ }
1277
1284
  return saveAppSettings({ ...current, advanced });
1278
1285
  }
1279
1286
  function autoRenameBranchEnabled(settings = loadAppSettings()) {
@@ -1319,6 +1326,9 @@ function orchestrationQuotaFallbackAgent(settings = loadAppSettings()) {
1319
1326
  }
1320
1327
  return "cursor";
1321
1328
  }
1329
+ function followUpBehavior(settings = loadAppSettings()) {
1330
+ return settings.advanced.followUpBehavior === "queue" ? "queue" : "steer";
1331
+ }
1322
1332
  function maxConcurrentAgents(settings = loadAppSettings()) {
1323
1333
  const n = settings.advanced.maxConcurrent;
1324
1334
  if (typeof n === "number" && Number.isFinite(n)) {
@@ -1426,6 +1436,7 @@ export {
1426
1436
  issueSourceLabel,
1427
1437
  HARNESS_ENV_KEYS,
1428
1438
  GITHUB_GIT_AUTH_MODES,
1439
+ FOLLOW_UP_BEHAVIORS,
1429
1440
  emptyPublicIntegrations,
1430
1441
  toPublicAppSettings,
1431
1442
  appSettingsPath,
@@ -1479,6 +1490,7 @@ export {
1479
1490
  autoCleanupOrphansEnabled,
1480
1491
  orchestrationQuotaOnLimit,
1481
1492
  orchestrationQuotaFallbackAgent,
1493
+ followUpBehavior,
1482
1494
  maxConcurrentAgents,
1483
1495
  resolveClaudeExecutable,
1484
1496
  resolveAgentExecutable,