@sideboard-ai/core 0.1.77 → 0.1.81

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-LKUHPPEQ.js → agents-EBKJ55PN.js} +5 -5
  2. package/dist/{agents-XJV6J3K2.js → agents-UQ7D6U6L.js} +6 -6
  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-2FJI5JXX.js → chunk-4AVU4QXO.js} +54 -20
  6. package/dist/{chunk-XJNCWYFR.js → chunk-4HJK57XU.js} +8 -14
  7. package/dist/{chunk-VERLUKN2.js → chunk-6F2TQFJE.js} +6 -6
  8. package/dist/{chunk-EEKSZTMU.js → chunk-CQBQWX73.js} +10 -16
  9. package/dist/{chunk-OE7YBB5X.js → chunk-FUUVPN4A.js} +108 -3
  10. package/dist/{chunk-W7YOTDVO.js → chunk-IFPN6TER.js} +47 -2
  11. package/dist/{chunk-75U65MBE.js → chunk-K553XK7M.js} +2 -2
  12. package/dist/{chunk-GWTLKXXP.js → chunk-P5VDPGAN.js} +3 -3
  13. package/dist/{chunk-2ZIPJRJE.js → chunk-TDJ43HUD.js} +2 -2
  14. package/dist/{chunk-PTJJIQK7.js → chunk-XKNBSYA7.js} +47 -2
  15. package/dist/{chunk-QD37IILI.js → chunk-XNECBC6T.js} +66 -20
  16. package/dist/{chunk-MF2YMEGD.js → chunk-YVVXWWCC.js} +110 -3
  17. package/dist/{coordinator-prompt-LLFJUO2R.js → coordinator-prompt-JQF72JKX.js} +4 -4
  18. package/dist/{coordinator-prompt-MRCMMRSF.js → coordinator-prompt-NYNEFKIV.js} +3 -3
  19. package/dist/{global-workspace-YNMNO4CL.js → global-workspace-DZM4ZFGU.js} +5 -5
  20. package/dist/{global-workspace-TWHMYLTZ.js → global-workspace-NUMUJRWG.js} +4 -4
  21. package/dist/index.cjs +585 -251
  22. package/dist/index.d.cts +97 -8
  23. package/dist/index.d.ts +97 -8
  24. package/dist/index.js +363 -222
  25. package/dist/mcp/run-stdio.cjs +2502 -2286
  26. package/dist/mcp/run-stdio.js +162 -133
  27. package/dist/{workspaces-O3U5BENH.js → workspaces-QLRCOXAY.js} +6 -6
  28. package/dist/{workspaces-GQ4XKBD3.js → workspaces-SUU2GO3A.js} +5 -5
  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
@@ -2,6 +2,7 @@
2
2
 
3
3
  import {
4
4
  PLAN_MODE_INSTRUCTION,
5
+ applyTurnUsage,
5
6
  fallbackTurnFailDetail,
6
7
  formatTurnExitError,
7
8
  getAdapter,
@@ -16,14 +17,14 @@ import {
16
17
  resolveQuotaFallbackAgent,
17
18
  sideboardMcpProfile,
18
19
  summarizeTurnStderr
19
- } from "../chunk-2FJI5JXX.js";
20
+ } from "../chunk-4AVU4QXO.js";
20
21
  import "../chunk-VROPG6QF.js";
21
22
  import {
22
23
  addWorkspace,
23
24
  ensureWorkspace,
24
25
  removeWorkspace,
25
26
  syncWorkspacesFromThreads
26
- } from "../chunk-2ZIPJRJE.js";
27
+ } from "../chunk-TDJ43HUD.js";
27
28
  import {
28
29
  extractPresentedPlan,
29
30
  readPlanFile,
@@ -41,14 +42,14 @@ import {
41
42
  isGlobalThread,
42
43
  isOrchestratorThread,
43
44
  orchestratorSessionPoisonedByBuiltins
44
- } from "../chunk-GWTLKXXP.js";
45
+ } from "../chunk-P5VDPGAN.js";
45
46
  import {
46
47
  SLACK_REPLY_FORMATTING,
47
48
  coordinatorSystemPrompt,
48
49
  coordinatorTurnReminder,
49
50
  enrichWorkspacesWithGithub,
50
51
  ensureGlobalCoordinatorCwd
51
- } from "../chunk-XJNCWYFR.js";
52
+ } from "../chunk-4HJK57XU.js";
52
53
  import {
53
54
  addPrStackLayer,
54
55
  allocateTeamName,
@@ -57,9 +58,11 @@ import {
57
58
  createExistingBranchWorktree,
58
59
  createOrUpdatePr,
59
60
  createThreadWorktree,
61
+ currentBranch,
60
62
  detectGhStack,
61
63
  fetchPrHead,
62
64
  formatGhLandError,
65
+ formatGitAuthModeDirective,
63
66
  getPr,
64
67
  getPrChecks,
65
68
  getPrDetails,
@@ -85,7 +88,7 @@ import {
85
88
  takenSlugsFromThread,
86
89
  threadDisplayLabel,
87
90
  worktreeNameFromPath
88
- } from "../chunk-OE7YBB5X.js";
91
+ } from "../chunk-FUUVPN4A.js";
89
92
  import {
90
93
  ATTACHMENTS_DIR,
91
94
  LEGACY_ATTACHMENTS_DIR,
@@ -125,7 +128,7 @@ import {
125
128
  saveLinearOAuth,
126
129
  updateAdvancedSettings,
127
130
  writeSecureJson
128
- } from "../chunk-PTJJIQK7.js";
131
+ } from "../chunk-XKNBSYA7.js";
129
132
  import {
130
133
  writePrivateFile
131
134
  } from "../chunk-NSTQ6QKD.js";
@@ -880,34 +883,6 @@ function normalizeParseResult(parsed) {
880
883
  return Array.isArray(parsed) ? parsed : [parsed];
881
884
  }
882
885
 
883
- // src/agents/usage.ts
884
- function sumOptional(a, b) {
885
- if (a == null && b == null) return void 0;
886
- return (a ?? 0) + (b ?? 0);
887
- }
888
- function requestOccupancy(u) {
889
- return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
890
- }
891
- function mergeUsage(a, b) {
892
- return {
893
- inputTokens: (a?.inputTokens ?? 0) + b.inputTokens,
894
- outputTokens: (a?.outputTokens ?? 0) + b.outputTokens,
895
- cacheReadTokens: sumOptional(a?.cacheReadTokens, b.cacheReadTokens),
896
- cacheWriteTokens: sumOptional(a?.cacheWriteTokens, b.cacheWriteTokens),
897
- lastRequestTokens: b.lastRequestTokens ?? a?.lastRequestTokens
898
- };
899
- }
900
- function applyTurnUsage(current, incoming, scope = "request") {
901
- if (scope === "turn") {
902
- return {
903
- ...incoming,
904
- lastRequestTokens: current?.lastRequestTokens ?? requestOccupancy(incoming)
905
- };
906
- }
907
- const merged = mergeUsage(current, incoming);
908
- return { ...merged, lastRequestTokens: requestOccupancy(incoming) };
909
- }
910
-
911
886
  // src/agents/spawn.ts
912
887
  async function spawnAgentTurn(thread, input, onEvent) {
913
888
  ensureAgentPath();
@@ -916,9 +891,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
916
891
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
917
892
  );
918
893
  }
919
- const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-TWHMYLTZ.js");
894
+ const { isGlobalThread: isGlobalThread2 } = await import("../global-workspace-NUMUJRWG.js");
920
895
  if (isGlobalThread2(thread)) {
921
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-MRCMMRSF.js");
896
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("../coordinator-prompt-NYNEFKIV.js");
922
897
  ensureGlobalCoordinatorCwd2(
923
898
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
924
899
  );
@@ -935,8 +910,13 @@ async function spawnAgentTurn(thread, input, onEvent) {
935
910
  }
936
911
  const env = childEnvWithAppSettings(cmd.env);
937
912
  if (!isOrchestratorThread(thread)) {
938
- const originEnv = await originGhRepoEnv(thread.worktreePath).catch(() => ({}));
939
- Object.assign(env, originEnv);
913
+ try {
914
+ const originEnv = await originGhRepoEnv(thread.worktreePath, { env });
915
+ Object.assign(env, originEnv);
916
+ } catch (err) {
917
+ const detail = err instanceof Error ? err.message : String(err);
918
+ console.warn(`[sideboard] originGhRepoEnv failed (${thread.worktreePath}): ${detail}`);
919
+ }
940
920
  }
941
921
  const child = execa(cmd.file, cmd.args, {
942
922
  cwd: cmd.cwd,
@@ -1020,6 +1000,80 @@ async function spawnAgentTurn(thread, input, onEvent) {
1020
1000
  };
1021
1001
  }
1022
1002
 
1003
+ // src/land/pr-metadata.ts
1004
+ var GENERIC_TITLE_RE = /^(sideboard:|thread\/|wip\b|tmp\b|update\b|changes?\b|misc\b|stuff\b)/i;
1005
+ function looksGeneric(title) {
1006
+ const t = title.trim();
1007
+ if (!t) return true;
1008
+ if (t.length < 4) return true;
1009
+ if (GENERIC_TITLE_RE.test(t)) return true;
1010
+ if (!/[\s:/\-_]/.test(t) && t.length <= 24) return true;
1011
+ return false;
1012
+ }
1013
+ function truncateTitle(title, max = 72) {
1014
+ const t = title.trim().replace(/\s+/g, " ");
1015
+ if (t.length <= max) return t;
1016
+ return `${t.slice(0, max - 1).trimEnd()}\u2026`;
1017
+ }
1018
+ function titleFromPaths(paths) {
1019
+ if (paths.length === 0) return null;
1020
+ const top = paths.slice(0, 3).map((p) => p.replace(/\/$/, ""));
1021
+ if (paths.length === 1) return truncateTitle(`Update ${top[0]}`);
1022
+ if (paths.length <= 3) return truncateTitle(`Update ${top.join(", ")}`);
1023
+ return truncateTitle(`Update ${top[0]} and ${paths.length - 1} more files`);
1024
+ }
1025
+ async function suggestPrMetadata(worktreePath, opts) {
1026
+ const [{ stdout: logOut }, { stdout: nameStatus }, { stdout: statOut }] = await Promise.all([
1027
+ git(["log", "--format=%s", `${opts.base}..HEAD`], worktreePath, {
1028
+ reject: false
1029
+ }),
1030
+ git(["diff", "--name-only", `${opts.base}...HEAD`], worktreePath, {
1031
+ reject: false
1032
+ }),
1033
+ git(["diff", "--stat", `${opts.base}...HEAD`], worktreePath, {
1034
+ reject: false
1035
+ })
1036
+ ]);
1037
+ const { stdout: dirtyNames } = await git(["diff", "--name-only", "HEAD"], worktreePath, {
1038
+ reject: false
1039
+ });
1040
+ const { stdout: untracked } = await git(
1041
+ ["ls-files", "--others", "--exclude-standard"],
1042
+ worktreePath,
1043
+ { reject: false }
1044
+ );
1045
+ const subjects = logOut.split("\n").map((s) => s.trim()).filter(Boolean);
1046
+ const paths = [
1047
+ ...new Set(
1048
+ [...nameStatus.split("\n"), ...dirtyNames.split("\n"), ...untracked.split("\n")].map((p) => p.trim()).filter(Boolean)
1049
+ )
1050
+ ].sort((a, b) => a.localeCompare(b));
1051
+ const meaningfulSubjects = subjects.filter((s) => !looksGeneric(s));
1052
+ const fallback = opts.fallbackTitle?.trim() || null;
1053
+ const fallbackOk = fallback && !looksGeneric(fallback) ? fallback : null;
1054
+ const title = truncateTitle(
1055
+ meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? "Sideboard changes"
1056
+ );
1057
+ const commitMessage = meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? `sideboard: ${title}`;
1058
+ const summaryLines = meaningfulSubjects.length > 0 ? meaningfulSubjects.slice(0, 12).map((s) => `- ${s}`) : paths.slice(0, 20).map((p) => `- \`${p}\``);
1059
+ const bodyParts = [
1060
+ "## Summary",
1061
+ summaryLines.length > 0 ? summaryLines.join("\n") : "- Changes landed via Sideboard.",
1062
+ ""
1063
+ ];
1064
+ if (statOut.trim()) {
1065
+ bodyParts.push("## Diff", "```", statOut.trim(), "```", "");
1066
+ }
1067
+ if (opts.sourceLabel) {
1068
+ bodyParts.push(`_Landed via Sideboard from ${opts.sourceLabel}_`);
1069
+ }
1070
+ return {
1071
+ title,
1072
+ body: bodyParts.join("\n").trim() + "\n",
1073
+ commitMessage: truncateTitle(commitMessage, 72)
1074
+ };
1075
+ }
1076
+
1023
1077
  // src/git/agent-git-actions.ts
1024
1078
  var AGENT_GIT_ACTIONS = [
1025
1079
  "commit-push",
@@ -1154,8 +1208,20 @@ async function captureLoginEnv() {
1154
1208
  cachedLoginEnv = { ...process.env };
1155
1209
  return { ...cachedLoginEnv };
1156
1210
  }
1211
+ var NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
1212
+ function stripNestedElectronEnv(env) {
1213
+ const out = { ...env };
1214
+ for (const key of Object.keys(out)) {
1215
+ if (NESTED_ELECTRON_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
1216
+ delete out[key];
1217
+ }
1218
+ }
1219
+ return out;
1220
+ }
1157
1221
  function buildWorkspaceScriptEnv(opts, baseEnv) {
1158
- const env = { ...baseEnv ?? process.env };
1222
+ const env = stripNestedElectronEnv({
1223
+ ...baseEnv ?? process.env
1224
+ });
1159
1225
  const name = opts.workspaceName ?? basename(opts.worktreePath);
1160
1226
  const ports = opts.ports ?? [];
1161
1227
  const primary = ports[0];
@@ -1759,7 +1825,7 @@ async function createThread(input, _onSetupLine) {
1759
1825
  return readThread(thread.id) ?? thread;
1760
1826
  }
1761
1827
  async function listLinearIssues(agent, repoPath) {
1762
- const { getAdapter: getAdapter2 } = await import("../agents-LKUHPPEQ.js");
1828
+ const { getAdapter: getAdapter2 } = await import("../agents-EBKJ55PN.js");
1763
1829
  await requireAgent(agent, { requireLinear: true });
1764
1830
  const adapter = getAdapter2(agent);
1765
1831
  if (!adapter.listLinearIssues) {
@@ -2623,7 +2689,7 @@ async function adoptThread(input) {
2623
2689
  messages: input.messages ?? []
2624
2690
  });
2625
2691
  writeThread(thread);
2626
- const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-GQ4XKBD3.js");
2692
+ const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-SUU2GO3A.js");
2627
2693
  await ensureWorkspace2(repoPath);
2628
2694
  return thread;
2629
2695
  }
@@ -3842,80 +3908,6 @@ async function getDiffSummary(worktreePath, repoPath, opts) {
3842
3908
  };
3843
3909
  }
3844
3910
 
3845
- // src/land/pr-metadata.ts
3846
- var GENERIC_TITLE_RE = /^(sideboard:|thread\/|wip\b|tmp\b|update\b|changes?\b|misc\b|stuff\b)/i;
3847
- function looksGeneric(title) {
3848
- const t = title.trim();
3849
- if (!t) return true;
3850
- if (t.length < 4) return true;
3851
- if (GENERIC_TITLE_RE.test(t)) return true;
3852
- if (!/[\s:/\-_]/.test(t) && t.length <= 24) return true;
3853
- return false;
3854
- }
3855
- function truncateTitle(title, max = 72) {
3856
- const t = title.trim().replace(/\s+/g, " ");
3857
- if (t.length <= max) return t;
3858
- return `${t.slice(0, max - 1).trimEnd()}\u2026`;
3859
- }
3860
- function titleFromPaths(paths) {
3861
- if (paths.length === 0) return null;
3862
- const top = paths.slice(0, 3).map((p) => p.replace(/\/$/, ""));
3863
- if (paths.length === 1) return truncateTitle(`Update ${top[0]}`);
3864
- if (paths.length <= 3) return truncateTitle(`Update ${top.join(", ")}`);
3865
- return truncateTitle(`Update ${top[0]} and ${paths.length - 1} more files`);
3866
- }
3867
- async function suggestPrMetadata(worktreePath, opts) {
3868
- const [{ stdout: logOut }, { stdout: nameStatus }, { stdout: statOut }] = await Promise.all([
3869
- git(["log", "--format=%s", `${opts.base}..HEAD`], worktreePath, {
3870
- reject: false
3871
- }),
3872
- git(["diff", "--name-only", `${opts.base}...HEAD`], worktreePath, {
3873
- reject: false
3874
- }),
3875
- git(["diff", "--stat", `${opts.base}...HEAD`], worktreePath, {
3876
- reject: false
3877
- })
3878
- ]);
3879
- const { stdout: dirtyNames } = await git(["diff", "--name-only", "HEAD"], worktreePath, {
3880
- reject: false
3881
- });
3882
- const { stdout: untracked } = await git(
3883
- ["ls-files", "--others", "--exclude-standard"],
3884
- worktreePath,
3885
- { reject: false }
3886
- );
3887
- const subjects = logOut.split("\n").map((s) => s.trim()).filter(Boolean);
3888
- const paths = [
3889
- ...new Set(
3890
- [...nameStatus.split("\n"), ...dirtyNames.split("\n"), ...untracked.split("\n")].map((p) => p.trim()).filter(Boolean)
3891
- )
3892
- ].sort((a, b) => a.localeCompare(b));
3893
- const meaningfulSubjects = subjects.filter((s) => !looksGeneric(s));
3894
- const fallback = opts.fallbackTitle?.trim() || null;
3895
- const fallbackOk = fallback && !looksGeneric(fallback) ? fallback : null;
3896
- const title = truncateTitle(
3897
- meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? "Sideboard changes"
3898
- );
3899
- const commitMessage = meaningfulSubjects[0] ?? fallbackOk ?? titleFromPaths(paths) ?? `sideboard: ${title}`;
3900
- const summaryLines = meaningfulSubjects.length > 0 ? meaningfulSubjects.slice(0, 12).map((s) => `- ${s}`) : paths.slice(0, 20).map((p) => `- \`${p}\``);
3901
- const bodyParts = [
3902
- "## Summary",
3903
- summaryLines.length > 0 ? summaryLines.join("\n") : "- Changes landed via Sideboard.",
3904
- ""
3905
- ];
3906
- if (statOut.trim()) {
3907
- bodyParts.push("## Diff", "```", statOut.trim(), "```", "");
3908
- }
3909
- if (opts.sourceLabel) {
3910
- bodyParts.push(`_Landed via Sideboard from ${opts.sourceLabel}_`);
3911
- }
3912
- return {
3913
- title,
3914
- body: bodyParts.join("\n").trim() + "\n",
3915
- commitMessage: truncateTitle(commitMessage, 72)
3916
- };
3917
- }
3918
-
3919
3911
  // src/land/land.ts
3920
3912
  async function previewLand(thread) {
3921
3913
  if (thread.sourceIsFork) {
@@ -4459,6 +4451,8 @@ function formatWorktreeDirective(thread, opts) {
4459
4451
  lines.push(
4460
4452
  "- Push with `git push -u origin HEAD` (or the current branch name). Do not `git push upstream`."
4461
4453
  );
4454
+ lines.push("");
4455
+ lines.push(formatGitAuthModeDirective(opts?.gitAuthMode ?? "auto"));
4462
4456
  lines.push(
4463
4457
  "- 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."
4464
4458
  );
@@ -4466,7 +4460,7 @@ function formatWorktreeDirective(thread, opts) {
4466
4460
  "- Prefer a concise imperative title (Conventional Commits style when it fits: feat:/fix:/chore:/docs:). Body should summarize intent, key changes, and test notes."
4467
4461
  );
4468
4462
  lines.push(
4469
- "- Commit with messages that state the purpose of the change (same standard as the PR). Stay on this thread branch. Never push directly to main/master or merge locally into the main checkout. When asked to merge the PR, use GitHub from this worktree (`gh pr merge` / `gh stack merge`)."
4463
+ "- Commit with messages that state the purpose of the change (same standard as the PR). Stay on this thread branch. Never push directly to main/master or merge locally into the main checkout. Do not merge the PR unless this turn is a Merge PR request or the user explicitly asked. When merging, use GitHub from this worktree (`gh pr merge` / `gh stack merge`)."
4470
4464
  );
4471
4465
  if (thread.prUrl) {
4472
4466
  lines.push(
@@ -4504,7 +4498,7 @@ function formatWorktreeDirective(thread, opts) {
4504
4498
  '- "Address review comments." \u2192 read PR review feedback, make the requested changes, commit, and push.'
4505
4499
  );
4506
4500
  lines.push(
4507
- '- "Merge PR." \u2192 merge this thread\'s open pull request on GitHub. If `gh stack view` shows a stack, use `gh stack merge`; otherwise `gh pr merge` (respect repo defaults / squash vs merge). Do not force-push main/master or merge locally into the main checkout.'
4501
+ '- "Merge PR." \u2192 merge this thread\'s open pull request on GitHub (this phrase is the explicit ask). If `gh stack view` shows a stack, use `gh stack merge`; otherwise `gh pr merge` (respect repo defaults / squash vs merge). Do not force-push main/master or merge locally into the main checkout.'
4508
4502
  );
4509
4503
  return lines.join("\n");
4510
4504
  }
@@ -4663,7 +4657,7 @@ var Orchestrator = class {
4663
4657
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
4664
4658
  });
4665
4659
  }
4666
- const { autoCleanupOrphansEnabled } = await import("../app-settings-P42Z3VOB.js");
4660
+ const { autoCleanupOrphansEnabled } = await import("../app-settings-NILNWNNQ.js");
4667
4661
  if (autoCleanupOrphansEnabled() && shouldRunWorktreeCleanup() && orphans.length > 0) {
4668
4662
  await cleanupOrphanWorktrees({ repoPaths });
4669
4663
  }
@@ -4820,7 +4814,7 @@ var Orchestrator = class {
4820
4814
  }
4821
4815
  async finishCreateThread(threadId, prompt) {
4822
4816
  await this.runSetupAfterCreate(threadId);
4823
- const { autoRunAfterSetupEnabled } = await import("../app-settings-P42Z3VOB.js");
4817
+ const { autoRunAfterSetupEnabled } = await import("../app-settings-NILNWNNQ.js");
4824
4818
  if (autoRunAfterSetupEnabled()) {
4825
4819
  try {
4826
4820
  await this.startDev(threadId);
@@ -5098,14 +5092,16 @@ var Orchestrator = class {
5098
5092
  }
5099
5093
  const isBrightsy = fresh.agent === "brightsy";
5100
5094
  const isOrchestration = isOrchestratorThread(fresh);
5095
+ const { autoRenameBranchEnabled, getGithubGitAuthMode } = await import("../app-settings-NILNWNNQ.js");
5096
+ const gitAuthMode = getGithubGitAuthMode();
5101
5097
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
5102
5098
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
5103
5099
  () => null
5104
- )
5100
+ ),
5101
+ gitAuthMode
5105
5102
  });
5106
5103
  const artifactDirective = isBrightsy ? null : formatArtifactDirective();
5107
5104
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
5108
- const { autoRenameBranchEnabled } = await import("../app-settings-P42Z3VOB.js");
5109
5105
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled() ? formatRenameBranchDirective(fresh, {
5110
5106
  customPrompt: settings?.prompts?.renameBranch
5111
5107
  }) : null;
@@ -5811,7 +5807,7 @@ var Orchestrator = class {
5811
5807
  updateThread(thread.id, { title: meta.title });
5812
5808
  }
5813
5809
  }
5814
- const { autoArchiveOnMergeEnabled } = await import("../app-settings-P42Z3VOB.js");
5810
+ const { autoArchiveOnMergeEnabled } = await import("../app-settings-NILNWNNQ.js");
5815
5811
  const latest = this.requireThread(thread.id);
5816
5812
  if (!shouldAutoArchiveOnPrMerge({
5817
5813
  previousPrState: prevState || null,
@@ -5942,8 +5938,9 @@ var Orchestrator = class {
5942
5938
  return tab;
5943
5939
  }
5944
5940
  /**
5945
- * Queue a desktop-git-button prompt on a worktree agent (commit/push/PR/merge).
5946
- * Orchestrators use this instead of running git/gh from the synthetic home.
5941
+ * Desktop git buttons + MCP `ask_git`.
5942
+ * When the worktree is clean, push / open the PR here (HTTPS via `gh` if SSH
5943
+ * is missing). When dirty, queue the worktree agent to commit first.
5947
5944
  */
5948
5945
  async askGit(threadRef, action) {
5949
5946
  if (!AGENT_GIT_ACTIONS.includes(action)) {
@@ -5961,6 +5958,10 @@ var Orchestrator = class {
5961
5958
  "No pull request linked. Ask the worktree agent to open a draft PR first (ask_git create-draft)."
5962
5959
  );
5963
5960
  }
5961
+ if ((action === "commit-push" || action === "create-draft" || action === "create-web") && thread.worktreePath?.trim() && !await isDirty(thread.worktreePath)) {
5962
+ await this.pushAndMaybeOpenPr(thread, action);
5963
+ return this.requireThread(threadRef);
5964
+ }
5964
5965
  let prBase;
5965
5966
  if (action === "resolve-conflicts") {
5966
5967
  try {
@@ -5971,6 +5972,34 @@ var Orchestrator = class {
5971
5972
  }
5972
5973
  return this.send(threadRef, agentGitPrompt(action, { prBase }));
5973
5974
  }
5975
+ /** Push origin (gh HTTPS fallback) and create/update the PR when requested. */
5976
+ async pushAndMaybeOpenPr(thread, action) {
5977
+ const cwd = thread.worktreePath;
5978
+ const branch = await currentBranch(cwd);
5979
+ await pushBranch(cwd, branch);
5980
+ if (action === "commit-push") return;
5981
+ const base = await resolveDefaultBranch(thread.repoPath);
5982
+ const meta = await suggestPrMetadata(cwd, {
5983
+ base,
5984
+ fallbackTitle: thread.prTitle ?? thread.title
5985
+ });
5986
+ const url = await createOrUpdatePr(cwd, {
5987
+ title: meta.title,
5988
+ body: meta.body,
5989
+ base,
5990
+ head: branch,
5991
+ draft: action === "create-draft",
5992
+ web: action === "create-web"
5993
+ });
5994
+ if (!url) return;
5995
+ const patch = { prUrl: url, prTitle: meta.title };
5996
+ try {
5997
+ const fetched = await getPrMeta(cwd, url);
5998
+ if (fetched?.title) patch.prTitle = fetched.title;
5999
+ } catch {
6000
+ }
6001
+ updateThread(thread.id, patch);
6002
+ }
5974
6003
  setThreadOptions(threadRef, patch) {
5975
6004
  const thread = this.requireThread(threadRef);
5976
6005
  const next = {};
@@ -6066,7 +6095,7 @@ var Orchestrator = class {
6066
6095
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
6067
6096
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
6068
6097
  try {
6069
- const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-GQ4XKBD3.js");
6098
+ const { ensureWorkspace: ensureWorkspace2 } = await import("../workspaces-SUU2GO3A.js");
6070
6099
  await ensureWorkspace2(thread.repoPath);
6071
6100
  } catch {
6072
6101
  }
@@ -6088,7 +6117,7 @@ var Orchestrator = class {
6088
6117
  await runArchiveScript(thread.repoPath, thread.worktreePath);
6089
6118
  } catch {
6090
6119
  }
6091
- const { deleteBranchOnPurgeEnabled } = await import("../app-settings-P42Z3VOB.js");
6120
+ const { deleteBranchOnPurgeEnabled } = await import("../app-settings-NILNWNNQ.js");
6092
6121
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled();
6093
6122
  await removeWorktree(thread.repoPath, thread.worktreePath, {
6094
6123
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -6109,7 +6138,7 @@ var Orchestrator = class {
6109
6138
  return restored2;
6110
6139
  }
6111
6140
  if (!existsSync15(thread.worktreePath)) {
6112
- const { createThreadWorktree: createThreadWorktree2 } = await import("../worktree-KWXHMKRN.js");
6141
+ const { createThreadWorktree: createThreadWorktree2 } = await import("../worktree-UNSOCYZU.js");
6113
6142
  const { execa: execa6 } = await import("execa");
6114
6143
  const slug = thread.worktreePath.split("/").pop();
6115
6144
  const dest = thread.worktreePath;
@@ -7280,7 +7309,7 @@ function withTimeout(promise, ms, label) {
7280
7309
  async function startMcpServer() {
7281
7310
  const orch = getOrchestrator();
7282
7311
  try {
7283
- const { maxConcurrentAgents } = await import("../app-settings-P42Z3VOB.js");
7312
+ const { maxConcurrentAgents } = await import("../app-settings-NILNWNNQ.js");
7284
7313
  orch.setMaxConcurrent(maxConcurrentAgents());
7285
7314
  } catch {
7286
7315
  }
@@ -7490,7 +7519,7 @@ async function startMcpServer() {
7490
7519
  registerLinearTools(server);
7491
7520
  if (sideboardMcpProfile() !== "worktree") {
7492
7521
  const { getCaffeinateHold, setCaffeinateHold } = await import("../caffeinate-hold-OHAUWOA6.js");
7493
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults } = await import("../app-settings-P42Z3VOB.js");
7522
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults } = await import("../app-settings-NILNWNNQ.js");
7494
7523
  const accountDefaults = resolveThreadDefaults();
7495
7524
  const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
7496
7525
  server.tool(
@@ -7648,7 +7677,7 @@ async function startMcpServer() {
7648
7677
  );
7649
7678
  server.tool(
7650
7679
  "send_to_thread",
7651
- "Queue a prompt on a worktree thread chat (runs under concurrency cap). Use after create_thread to start or continue a conversation. For commit/push/PR/merge, prefer ask_git (canonical desktop-button phrases). Set force_stop=true to interrupt an in-flight/queued turn (kill + clear queue) before queueing this prompt \u2014 use when the thread is mid-turn or has stale queued prompts you need to replace.",
7680
+ 'Queue a prompt on a worktree thread chat (runs under concurrency cap). Use after create_thread to start or continue a conversation. For commit/push/PR, prefer ask_git (canonical desktop-button phrases). Send "Merge PR." / ask_git merge only when the user explicitly asked to merge. Set force_stop=true to interrupt an in-flight/queued turn (kill + clear queue) before queueing this prompt \u2014 use when the thread is mid-turn or has stale queued prompts you need to replace.',
7652
7681
  {
7653
7682
  ref: z3.string(),
7654
7683
  prompt: z3.string(),
@@ -7744,7 +7773,7 @@ async function startMcpServer() {
7744
7773
  );
7745
7774
  server.tool(
7746
7775
  "archive_thread",
7747
- "Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, open PRs, and merge only by asking the worktree agent (ask_git).",
7776
+ "Archive a thread (stops agent/dev, runs archive script, removes worktree when last chat tab). Coordinators commit, push, and open PRs by asking the worktree agent (ask_git). Merge only when the user explicitly asked.",
7748
7777
  { ref: z3.string() },
7749
7778
  async ({ ref }) => {
7750
7779
  const t = orch.getThread(ref);
@@ -7852,7 +7881,7 @@ async function startMcpServer() {
7852
7881
  );
7853
7882
  server.tool(
7854
7883
  "ask_git",
7855
- "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.",
7884
+ "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). action=merge only when the user explicitly asked to merge that PR. Do not run git or gh from the orchestration cwd.",
7856
7885
  {
7857
7886
  ref: z3.string().describe("Worktree thread id/ref"),
7858
7887
  action: z3.enum(AGENT_GIT_ACTIONS).describe(
@@ -8153,7 +8182,7 @@ async function startMcpServer() {
8153
8182
  );
8154
8183
  server.tool(
8155
8184
  "get_pr_stack",
8156
- "Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before ask_git merge on stacked PRs.",
8185
+ "Load the GitHub PR stack for a thread worktree (`gh stack view --json`). Returns null JSON when the branch is not stacked. Prefer this before ask_git merge on stacked PRs (and only merge when the user explicitly asked).",
8157
8186
  { ref: z3.string() },
8158
8187
  async ({ ref }) => {
8159
8188
  const stack = await orch.getPrStack(ref);
@@ -4,13 +4,13 @@ import {
4
4
  listWorkspaces,
5
5
  removeWorkspace,
6
6
  syncWorkspacesFromThreads
7
- } from "./chunk-75U65MBE.js";
8
- import "./chunk-VERLUKN2.js";
9
- import "./chunk-EEKSZTMU.js";
10
- import "./chunk-W7YOTDVO.js";
11
- import "./chunk-JT7R45JB.js";
12
- import "./chunk-MF2YMEGD.js";
7
+ } from "./chunk-K553XK7M.js";
8
+ import "./chunk-6F2TQFJE.js";
9
+ import "./chunk-CQBQWX73.js";
10
+ import "./chunk-YVVXWWCC.js";
13
11
  import "./chunk-FKOIHGKV.js";
12
+ import "./chunk-IFPN6TER.js";
13
+ import "./chunk-JT7R45JB.js";
14
14
  import "./chunk-FWJYLBO6.js";
15
15
  import "./chunk-77WWLBCI.js";
16
16
  import "./chunk-M37RITA6.js";
@@ -6,14 +6,14 @@ import {
6
6
  listWorkspaces,
7
7
  removeWorkspace,
8
8
  syncWorkspacesFromThreads
9
- } from "./chunk-2ZIPJRJE.js";
10
- import "./chunk-GWTLKXXP.js";
11
- import "./chunk-XJNCWYFR.js";
12
- import "./chunk-OE7YBB5X.js";
9
+ } from "./chunk-TDJ43HUD.js";
10
+ import "./chunk-P5VDPGAN.js";
11
+ import "./chunk-4HJK57XU.js";
12
+ import "./chunk-FUUVPN4A.js";
13
13
  import "./chunk-B3SJXYIJ.js";
14
14
  import "./chunk-7FD7COKE.js";
15
15
  import "./chunk-AE5VBOFE.js";
16
- import "./chunk-PTJJIQK7.js";
16
+ import "./chunk-XKNBSYA7.js";
17
17
  import "./chunk-NSTQ6QKD.js";
18
18
  import "./chunk-KGZBYWZ3.js";
19
19
  import "./chunk-7MV3RXSC.js";
@@ -18,6 +18,7 @@ import {
18
18
  getPrMeta,
19
19
  ghHeadRef,
20
20
  ghRepoSelectArgs,
21
+ githubAgentGitEnv,
21
22
  isDirty,
22
23
  isPlaceholderBranch,
23
24
  isSideboardScratchPath,
@@ -43,8 +44,10 @@ import {
43
44
  worktreeDisplayLabel,
44
45
  worktreeDisplayLabelForGroup,
45
46
  worktreeNameFromPath
46
- } from "./chunk-MF2YMEGD.js";
47
+ } from "./chunk-YVVXWWCC.js";
47
48
  import "./chunk-FKOIHGKV.js";
49
+ import "./chunk-IFPN6TER.js";
50
+ import "./chunk-JT7R45JB.js";
48
51
  import "./chunk-FWJYLBO6.js";
49
52
  import "./chunk-77WWLBCI.js";
50
53
  import "./chunk-M37RITA6.js";
@@ -69,6 +72,7 @@ export {
69
72
  getPrMeta,
70
73
  ghHeadRef,
71
74
  ghRepoSelectArgs,
75
+ githubAgentGitEnv,
72
76
  isDirty,
73
77
  isPlaceholderBranch,
74
78
  isSideboardScratchPath,
@@ -20,6 +20,7 @@ import {
20
20
  getPrMeta,
21
21
  ghHeadRef,
22
22
  ghRepoSelectArgs,
23
+ githubAgentGitEnv,
23
24
  isDirty,
24
25
  isPlaceholderBranch,
25
26
  isSideboardScratchPath,
@@ -45,10 +46,12 @@ import {
45
46
  worktreeDisplayLabel,
46
47
  worktreeDisplayLabelForGroup,
47
48
  worktreeNameFromPath
48
- } from "./chunk-OE7YBB5X.js";
49
+ } from "./chunk-FUUVPN4A.js";
49
50
  import "./chunk-B3SJXYIJ.js";
50
51
  import "./chunk-7FD7COKE.js";
51
52
  import "./chunk-AE5VBOFE.js";
53
+ import "./chunk-XKNBSYA7.js";
54
+ import "./chunk-NSTQ6QKD.js";
52
55
  import "./chunk-KGZBYWZ3.js";
53
56
  import "./chunk-7MV3RXSC.js";
54
57
  export {
@@ -71,6 +74,7 @@ export {
71
74
  getPrMeta,
72
75
  ghHeadRef,
73
76
  ghRepoSelectArgs,
77
+ githubAgentGitEnv,
74
78
  isDirty,
75
79
  isPlaceholderBranch,
76
80
  isSideboardScratchPath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sideboard-ai/core",
3
- "version": "0.1.77",
3
+ "version": "0.1.81",
4
4
  "description": "Sideboard core — orchestration, agents, git worktrees, MCP server",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",