@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.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  listWorkspaces,
5
5
  removeWorkspace,
6
6
  syncWorkspacesFromThreads
7
- } from "./chunk-75U65MBE.js";
7
+ } from "./chunk-PW5YHHP3.js";
8
8
  import {
9
9
  BRIGHTSY_MCP_ALLOWED_TOOLS,
10
10
  CLAUDE_MODEL_CATALOG,
@@ -58,7 +58,7 @@ import {
58
58
  sideboardMcpProfile,
59
59
  threadRequestsBrightsyMcp,
60
60
  writeInjectedMcpConfig
61
- } from "./chunk-QD37IILI.js";
61
+ } from "./chunk-SHCR6RPU.js";
62
62
  import {
63
63
  CLOUD_COORDINATOR_BUSY_REPLY,
64
64
  CLOUD_COORDINATOR_STOPPED_REPLY,
@@ -85,7 +85,7 @@ import {
85
85
  parseForceStopMessage,
86
86
  slackCoordinatorSourceRef,
87
87
  takenTeamSlugsForOrchestration
88
- } from "./chunk-VERLUKN2.js";
88
+ } from "./chunk-5LXWTU3J.js";
89
89
  import {
90
90
  COORDINATOR_TOOL_PLAYBOOK,
91
91
  SLACK_REPLY_FORMATTING,
@@ -94,7 +94,7 @@ import {
94
94
  enrichWorkspacesWithGithub,
95
95
  ensureGlobalCoordinatorCwd,
96
96
  formatWorkspaceInventory
97
- } from "./chunk-EEKSZTMU.js";
97
+ } from "./chunk-HCGEFU3J.js";
98
98
  import {
99
99
  brightsyConfigPath,
100
100
  brightsyMcpServerName,
@@ -129,66 +129,6 @@ import {
129
129
  pushTurnStderr,
130
130
  summarizeTurnStderr
131
131
  } from "./chunk-SEOICVGB.js";
132
- import {
133
- HARNESS_ENV_KEYS,
134
- appSettingsPath,
135
- applyAppEnvironment,
136
- autoArchiveOnMergeEnabled,
137
- autoCleanupOrphansEnabled,
138
- autoRenameBranchEnabled,
139
- autoRunAfterSetupEnabled,
140
- brightsyCloudConnectAgent,
141
- brightsyCloudConnectEnabled,
142
- brightsyInjectWorktreeMcpEnabled,
143
- caffeinateWhileCloudConnectEnabled,
144
- caffeinateWhileRunningEnabled,
145
- caffeinateWhileSlackListenEnabled,
146
- childEnvWithAppSettings,
147
- claudeChromeEnabled,
148
- claudeUserSettingsPath,
149
- deleteBranchOnPurgeEnabled,
150
- disconnectLinearConnection,
151
- ensureSlackDeviceIdentity,
152
- getDefaultAgent,
153
- getDefaultEffort,
154
- getDefaultFast,
155
- getDefaultModel,
156
- getIssueSource,
157
- getLinearApiKey,
158
- harnessEnvKey,
159
- isLinearConnected,
160
- isSecureFileEncrypted,
161
- loadAppSettings,
162
- maxConcurrentAgents,
163
- orchestrationQuotaFallbackAgent,
164
- orchestrationQuotaOnLimit,
165
- persistVaultKeyInKeychain,
166
- readKeychainVaultKey,
167
- readSecureJson,
168
- resolveAgentExecutable,
169
- resolveClaudeExecutable,
170
- resolveEffectiveIssueSource,
171
- resolveNewThreadOptions,
172
- resolveThreadDefaults,
173
- resolveVaultKey,
174
- saveAppSettings,
175
- saveLinearOAuth,
176
- secureFileUnlocksWith,
177
- setVaultMasterKey,
178
- slackAppLevelToken,
179
- slackListenEnabled,
180
- toPublicAppSettings,
181
- updateAdvancedSettings,
182
- updateAgentExecutable,
183
- updateAppEnvironment,
184
- updateBrightsySettings,
185
- updateClaudeSettings,
186
- updateCodexSettings,
187
- updateDefaultsSettings,
188
- updateIntegrationsSettings,
189
- updateOpencodeSettings,
190
- writeSecureJson
191
- } from "./chunk-W7YOTDVO.js";
192
132
  import {
193
133
  caffeinateHoldPath,
194
134
  getCaffeinateHold,
@@ -196,14 +136,13 @@ import {
196
136
  releaseCaffeinateHoldForThread,
197
137
  setCaffeinateHold
198
138
  } from "./chunk-MFF2RE3I.js";
199
- import {
200
- writePrivateFile
201
- } from "./chunk-JT7R45JB.js";
202
139
  import {
203
140
  FAMOUS_SOCCER_TEAMS,
204
141
  addPrStackLayer,
205
142
  allocateTeamName,
206
143
  allocateTeamSlug,
144
+ appendIndexedGitConfig,
145
+ applyGithubGitAuthEnv,
207
146
  branchDisplayLabel,
208
147
  checkoutPrStackLayer,
209
148
  collectTakenTeamSlugs,
@@ -218,6 +157,7 @@ import {
218
157
  extractGhErrorDetail,
219
158
  fetchPrHead,
220
159
  formatGhLandError,
160
+ formatGitAuthModeDirective,
221
161
  formatIpcInvokeError,
222
162
  formatRateLimitResetHint,
223
163
  getPr,
@@ -227,6 +167,7 @@ import {
227
167
  getPrStack,
228
168
  ghHeadRef,
229
169
  ghRepoSelectArgs,
170
+ githubAgentGitEnv,
230
171
  initPrStack,
231
172
  isDirty,
232
173
  isGhRateLimitError,
@@ -261,13 +202,79 @@ import {
261
202
  worktreeDisplayLabel,
262
203
  worktreeDisplayLabelForGroup,
263
204
  worktreeNameFromPath
264
- } from "./chunk-MF2YMEGD.js";
205
+ } from "./chunk-YVVXWWCC.js";
265
206
  import {
266
207
  ATTACHMENTS_DIR,
267
208
  LEGACY_ATTACHMENTS_DIR,
268
209
  attachmentsGitignoreBody,
269
210
  isWorkspaceScratchPath
270
211
  } from "./chunk-FKOIHGKV.js";
212
+ import {
213
+ GITHUB_GIT_AUTH_MODES,
214
+ HARNESS_ENV_KEYS,
215
+ appSettingsPath,
216
+ applyAppEnvironment,
217
+ autoArchiveOnMergeEnabled,
218
+ autoCleanupOrphansEnabled,
219
+ autoRenameBranchEnabled,
220
+ autoRunAfterSetupEnabled,
221
+ brightsyCloudConnectAgent,
222
+ brightsyCloudConnectEnabled,
223
+ brightsyInjectWorktreeMcpEnabled,
224
+ caffeinateWhileCloudConnectEnabled,
225
+ caffeinateWhileRunningEnabled,
226
+ caffeinateWhileSlackListenEnabled,
227
+ childEnvWithAppSettings,
228
+ claudeChromeEnabled,
229
+ claudeUserSettingsPath,
230
+ deleteBranchOnPurgeEnabled,
231
+ disconnectLinearConnection,
232
+ ensureSlackDeviceIdentity,
233
+ getDefaultAgent,
234
+ getDefaultEffort,
235
+ getDefaultFast,
236
+ getDefaultModel,
237
+ getGithubGitAuthMode,
238
+ getGithubPat,
239
+ getIssueSource,
240
+ getLinearApiKey,
241
+ harnessEnvKey,
242
+ isLinearConnected,
243
+ isSecureFileEncrypted,
244
+ loadAppSettings,
245
+ maxConcurrentAgents,
246
+ orchestrationQuotaFallbackAgent,
247
+ orchestrationQuotaOnLimit,
248
+ persistVaultKeyInKeychain,
249
+ readKeychainVaultKey,
250
+ readSecureJson,
251
+ resolveAgentExecutable,
252
+ resolveClaudeExecutable,
253
+ resolveEffectiveIssueSource,
254
+ resolveNewThreadOptions,
255
+ resolveThreadDefaults,
256
+ resolveVaultKey,
257
+ saveAppSettings,
258
+ saveLinearOAuth,
259
+ secureFileUnlocksWith,
260
+ setVaultMasterKey,
261
+ slackAppLevelToken,
262
+ slackListenEnabled,
263
+ toPublicAppSettings,
264
+ updateAdvancedSettings,
265
+ updateAgentExecutable,
266
+ updateAppEnvironment,
267
+ updateBrightsySettings,
268
+ updateClaudeSettings,
269
+ updateCodexSettings,
270
+ updateDefaultsSettings,
271
+ updateIntegrationsSettings,
272
+ updateOpencodeSettings,
273
+ writeSecureJson
274
+ } from "./chunk-IFPN6TER.js";
275
+ import {
276
+ writePrivateFile
277
+ } from "./chunk-JT7R45JB.js";
271
278
  import {
272
279
  appendMessage,
273
280
  createEmptyThread,
@@ -1075,8 +1082,8 @@ function toolDetail(name, input) {
1075
1082
  if (!input) return void 0;
1076
1083
  const command = str(input.command) ?? str(input.cmd);
1077
1084
  if (command) return command;
1078
- const path = str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
1079
- if (path) return path;
1085
+ const path2 = str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
1086
+ if (path2) return path2;
1080
1087
  const pattern = str(input.pattern) ?? str(input.glob) ?? str(input.glob_pattern);
1081
1088
  if (pattern) return pattern;
1082
1089
  const query = str(input.query) ?? str(input.prompt);
@@ -1120,12 +1127,12 @@ function toolDescription(name, input) {
1120
1127
  return "Run shell command";
1121
1128
  }
1122
1129
  if (/edit|write|apply/i.test(name)) {
1123
- const path = str(input.file_path) ?? str(input.path);
1124
- return path ? `Edit ${path.split("/").pop()}` : "Edit file";
1130
+ const path2 = str(input.file_path) ?? str(input.path);
1131
+ return path2 ? `Edit ${path2.split("/").pop()}` : "Edit file";
1125
1132
  }
1126
1133
  if (/read/i.test(name)) {
1127
- const path = str(input.file_path) ?? str(input.path);
1128
- return path ? `Read ${path.split("/").pop()}` : "Read file";
1134
+ const path2 = str(input.file_path) ?? str(input.path);
1135
+ return path2 ? `Read ${path2.split("/").pop()}` : "Read file";
1129
1136
  }
1130
1137
  if (/grep/i.test(name)) return "Search files";
1131
1138
  if (/glob/i.test(name)) return "Find files";
@@ -1332,9 +1339,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
1332
1339
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
1333
1340
  );
1334
1341
  }
1335
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-YNMNO4CL.js");
1342
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-VG44RZUH.js");
1336
1343
  if (isGlobalThread2(thread)) {
1337
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-LLFJUO2R.js");
1344
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-2OVON2LQ.js");
1338
1345
  ensureGlobalCoordinatorCwd2(
1339
1346
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
1340
1347
  );
@@ -1351,8 +1358,13 @@ async function spawnAgentTurn(thread, input, onEvent) {
1351
1358
  }
1352
1359
  const env = childEnvWithAppSettings(cmd.env);
1353
1360
  if (!isOrchestratorThread(thread)) {
1354
- const originEnv = await originGhRepoEnv(thread.worktreePath).catch(() => ({}));
1355
- Object.assign(env, originEnv);
1361
+ try {
1362
+ const originEnv = await originGhRepoEnv(thread.worktreePath, { env });
1363
+ Object.assign(env, originEnv);
1364
+ } catch (err) {
1365
+ const detail = err instanceof Error ? err.message : String(err);
1366
+ console.warn(`[sideboard] originGhRepoEnv failed (${thread.worktreePath}): ${detail}`);
1367
+ }
1356
1368
  }
1357
1369
  const child = execa(cmd.file, cmd.args, {
1358
1370
  cwd: cmd.cwd,
@@ -1487,6 +1499,8 @@ function formatWorktreeDirective(thread, opts) {
1487
1499
  lines.push(
1488
1500
  "- Push with `git push -u origin HEAD` (or the current branch name). Do not `git push upstream`."
1489
1501
  );
1502
+ lines.push("");
1503
+ lines.push(formatGitAuthModeDirective(opts?.gitAuthMode ?? "auto"));
1490
1504
  lines.push(
1491
1505
  "- 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."
1492
1506
  );
@@ -1672,9 +1686,9 @@ function matchSimpleGlob(pattern, name) {
1672
1686
  return new RegExp(`^${escaped}$`).test(name);
1673
1687
  }
1674
1688
  function readWorktreeInclude(repoPath) {
1675
- const path = join2(repoPath, ".worktreeinclude");
1676
- if (!existsSync2(path)) return [];
1677
- return readFileSync2(path, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1689
+ const path2 = join2(repoPath, ".worktreeinclude");
1690
+ if (!existsSync2(path2)) return [];
1691
+ return readFileSync2(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1678
1692
  }
1679
1693
  function resolveFilesToCopy(repoPath) {
1680
1694
  const fromInclude = readWorktreeInclude(repoPath);
@@ -1749,8 +1763,20 @@ async function captureLoginEnv() {
1749
1763
  cachedLoginEnv = { ...process.env };
1750
1764
  return { ...cachedLoginEnv };
1751
1765
  }
1766
+ var NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
1767
+ function stripNestedElectronEnv(env) {
1768
+ const out = { ...env };
1769
+ for (const key of Object.keys(out)) {
1770
+ if (NESTED_ELECTRON_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
1771
+ delete out[key];
1772
+ }
1773
+ }
1774
+ return out;
1775
+ }
1752
1776
  function buildWorkspaceScriptEnv(opts, baseEnv) {
1753
- const env = { ...baseEnv ?? process.env };
1777
+ const env = stripNestedElectronEnv({
1778
+ ...baseEnv ?? process.env
1779
+ });
1754
1780
  const name = opts.workspaceName ?? basename(opts.worktreePath);
1755
1781
  const ports = opts.ports ?? [];
1756
1782
  const primary = ports[0];
@@ -1975,10 +2001,10 @@ import { join as join3 } from "path";
1975
2001
  import { execa as execa3 } from "execa";
1976
2002
  import { createInterface as createInterface3 } from "readline";
1977
2003
  function loadCursorWorktreesJson(rootPath) {
1978
- const path = join3(rootPath, ".cursor", "worktrees.json");
1979
- if (!existsSync3(path)) return null;
2004
+ const path2 = join3(rootPath, ".cursor", "worktrees.json");
2005
+ if (!existsSync3(path2)) return null;
1980
2006
  try {
1981
- return JSON.parse(readFileSync3(path, "utf8"));
2007
+ return JSON.parse(readFileSync3(path2, "utf8"));
1982
2008
  } catch {
1983
2009
  return null;
1984
2010
  }
@@ -2079,9 +2105,9 @@ function parseNameStatus(stdout) {
2079
2105
  const out = [];
2080
2106
  for (const line of stdout.split("\n").filter(Boolean)) {
2081
2107
  const [status, ...pathParts] = line.split(" ");
2082
- const path = pathParts[pathParts.length - 1];
2083
- if (!status || !path) continue;
2084
- out.push({ status: status[0] ?? status, path });
2108
+ const path2 = pathParts[pathParts.length - 1];
2109
+ if (!status || !path2) continue;
2110
+ out.push({ status: status[0] ?? status, path: path2 });
2085
2111
  }
2086
2112
  return out;
2087
2113
  }
@@ -2089,12 +2115,12 @@ function parseNumstat(stdout) {
2089
2115
  const map = /* @__PURE__ */ new Map();
2090
2116
  for (const line of stdout.split("\n").filter(Boolean)) {
2091
2117
  const [addRaw, delRaw, ...pathParts] = line.split(" ");
2092
- const path = pathParts[pathParts.length - 1];
2093
- if (!path) continue;
2118
+ const path2 = pathParts[pathParts.length - 1];
2119
+ if (!path2) continue;
2094
2120
  const additions = addRaw === "-" ? 0 : Number(addRaw);
2095
2121
  const deletions = delRaw === "-" ? 0 : Number(delRaw);
2096
2122
  if (!Number.isFinite(additions) && !Number.isFinite(deletions)) continue;
2097
- map.set(path, {
2123
+ map.set(path2, {
2098
2124
  additions: Number.isFinite(additions) ? additions : 0,
2099
2125
  deletions: Number.isFinite(deletions) ? deletions : 0
2100
2126
  });
@@ -2171,70 +2197,70 @@ function splitCombinedDiff(stdout) {
2171
2197
  }
2172
2198
  return map;
2173
2199
  }
2174
- function syntheticAddPatch(path, content, maxHunk) {
2200
+ function syntheticAddPatch(path2, content, maxHunk) {
2175
2201
  const lines = content.split("\n");
2176
2202
  const body = lines.map((l) => `+${l}`).join("\n");
2177
- const header = `diff --git a/${path} b/${path}
2203
+ const header = `diff --git a/${path2} b/${path2}
2178
2204
  new file mode 100644
2179
2205
  --- /dev/null
2180
- +++ b/${path}
2206
+ +++ b/${path2}
2181
2207
  @@ -0,0 +1,${lines.length} @@
2182
2208
  `;
2183
2209
  return {
2184
- path,
2210
+ path: path2,
2185
2211
  status: "A",
2186
2212
  patch: capPatch(`${header}${body}`, maxHunk),
2187
2213
  additions: lines.length,
2188
2214
  deletions: 0
2189
2215
  };
2190
2216
  }
2191
- async function untrackedPatch(worktreePath, path, maxHunk) {
2192
- const abs = join4(worktreePath, path);
2217
+ async function untrackedPatch(worktreePath, path2, maxHunk) {
2218
+ const abs = join4(worktreePath, path2);
2193
2219
  try {
2194
2220
  const st = statSync2(abs);
2195
2221
  if (st.isFile() && st.size > maxHunk) {
2196
2222
  const buf = readFileSync4(abs).subarray(0, maxHunk);
2197
- return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
2223
+ return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
2198
2224
  }
2199
2225
  } catch {
2200
2226
  }
2201
2227
  const { stdout: patch } = await git(
2202
- ["diff", "--no-ext-diff", "--no-color", "--no-index", "--", "/dev/null", path],
2228
+ ["diff", "--no-ext-diff", "--no-color", "--no-index", "--", "/dev/null", path2],
2203
2229
  worktreePath,
2204
2230
  { reject: false, timeoutMs: 4e3 }
2205
2231
  );
2206
- const normalized = patch.replace(/^diff --git a\/.+? b\/.+?$/m, `diff --git a/${path} b/${path}`).replace(/^--- .*$/m, "--- /dev/null").replace(/^\+\+\+ .*$/m, `+++ b/${path}`);
2232
+ const normalized = patch.replace(/^diff --git a\/.+? b\/.+?$/m, `diff --git a/${path2} b/${path2}`).replace(/^--- .*$/m, "--- /dev/null").replace(/^\+\+\+ .*$/m, `+++ b/${path2}`);
2207
2233
  const addLines = normalized.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
2208
2234
  return {
2209
- path,
2235
+ path: path2,
2210
2236
  status: "A",
2211
2237
  patch: capPatch(normalized, maxHunk),
2212
2238
  additions: addLines,
2213
2239
  deletions: 0
2214
2240
  };
2215
2241
  }
2216
- async function listUntrackedPaths(worktreePath, path) {
2242
+ async function listUntrackedPaths(worktreePath, path2) {
2217
2243
  const args = ["ls-files", "-z", "--others", "--exclude-standard"];
2218
- if (path) args.push("--", path);
2244
+ if (path2) args.push("--", path2);
2219
2245
  const { stdout } = await git(args, worktreePath, { reject: false });
2220
2246
  return stdout.split("\0").filter(Boolean).filter((p) => !isSideboardScratchPath(p));
2221
2247
  }
2222
2248
  async function listUntrackedFiles(worktreePath, maxHunk, paths) {
2223
2249
  const list = paths ?? await listUntrackedPaths(worktreePath);
2224
2250
  if (list.length === 0) return [];
2225
- return Promise.all(list.map((path) => untrackedPatch(worktreePath, path, maxHunk)));
2251
+ return Promise.all(list.map((path2) => untrackedPatch(worktreePath, path2, maxHunk)));
2226
2252
  }
2227
2253
  function untrackedStubs(paths) {
2228
- return paths.map((path) => ({
2229
- path,
2254
+ return paths.map((path2) => ({
2255
+ path: path2,
2230
2256
  status: "A",
2231
2257
  patch: "",
2232
2258
  additions: 0,
2233
2259
  deletions: 0
2234
2260
  }));
2235
2261
  }
2236
- function pathspec(path) {
2237
- return path ? ["--", path] : [];
2262
+ function pathspec(path2) {
2263
+ return path2 ? ["--", path2] : [];
2238
2264
  }
2239
2265
  function gitDiff(args, cwd, opts) {
2240
2266
  return git(["diff", "--no-ext-diff", "--no-color", ...args], cwd, {
@@ -2242,7 +2268,7 @@ function gitDiff(args, cwd, opts) {
2242
2268
  timeoutMs: opts?.timeoutMs
2243
2269
  });
2244
2270
  }
2245
- function fileFromPatch(path, patch, maxHunk) {
2271
+ function fileFromPatch(path2, patch, maxHunk) {
2246
2272
  let status = "M";
2247
2273
  if (/^new file mode /m.test(patch) || /^--- \/dev\/null/m.test(patch)) {
2248
2274
  status = "A";
@@ -2254,15 +2280,15 @@ function fileFromPatch(path, patch, maxHunk) {
2254
2280
  const additions = patch.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
2255
2281
  const deletions = patch.split("\n").filter((l) => l.startsWith("-") && !l.startsWith("---")).length;
2256
2282
  return {
2257
- path,
2283
+ path: path2,
2258
2284
  status,
2259
2285
  patch: capPatch(patch, maxHunk),
2260
2286
  additions,
2261
2287
  deletions
2262
2288
  };
2263
2289
  }
2264
- async function gitDiffParts(worktreePath, diffArgs, includePatches, path) {
2265
- const extra = pathspec(path);
2290
+ async function gitDiffParts(worktreePath, diffArgs, includePatches, path2) {
2291
+ const extra = pathspec(path2);
2266
2292
  if (includePatches) {
2267
2293
  const diff = await gitDiff([...diffArgs, ...extra], worktreePath, {
2268
2294
  timeoutMs: 4e3
@@ -2300,17 +2326,17 @@ function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
2300
2326
  const patches = splitCombinedDiff(combinedDiff);
2301
2327
  if (!nameStatus.trim() && patches.size > 0) {
2302
2328
  return [...patches.entries()].map(
2303
- ([path, patch]) => fileFromPatch(path, patch, maxHunk)
2329
+ ([path2, patch]) => fileFromPatch(path2, patch, maxHunk)
2304
2330
  );
2305
2331
  }
2306
2332
  const counts = parseNumstat(numstat);
2307
2333
  const files = [];
2308
- for (const { status, path } of parseNameStatus(nameStatus)) {
2309
- const n = counts.get(path);
2334
+ for (const { status, path: path2 } of parseNameStatus(nameStatus)) {
2335
+ const n = counts.get(path2);
2310
2336
  files.push({
2311
- path,
2337
+ path: path2,
2312
2338
  status,
2313
- patch: capPatch(patches.get(path) ?? "", maxHunk),
2339
+ patch: capPatch(patches.get(path2) ?? "", maxHunk),
2314
2340
  additions: n?.additions,
2315
2341
  deletions: n?.deletions
2316
2342
  });
@@ -2394,7 +2420,7 @@ function emptyPathDiff(scope, base, hasLastTurnBase, commitSha = null) {
2394
2420
  };
2395
2421
  }
2396
2422
  async function getPathDiff(worktreePath, repoPath, opts) {
2397
- const { path, scope, commitSha, lastTurnBase, hasLastTurnBase, includePatches, maxHunk } = opts;
2423
+ const { path: path2, scope, commitSha, lastTurnBase, hasLastTurnBase, includePatches, maxHunk } = opts;
2398
2424
  if (scope === "last_turn" && !lastTurnBase) {
2399
2425
  return emptyPathDiff(scope, "last turn", false);
2400
2426
  }
@@ -2433,7 +2459,7 @@ async function getPathDiff(worktreePath, repoPath, opts) {
2433
2459
  labelBase = "HEAD";
2434
2460
  }
2435
2461
  const diff = await gitDiff(
2436
- includePatches ? [...diffArgs, "--", path] : ["--name-status", ...diffArgs, "--", path],
2462
+ includePatches ? [...diffArgs, "--", path2] : ["--name-status", ...diffArgs, "--", path2],
2437
2463
  worktreePath,
2438
2464
  { timeoutMs: 4e3 }
2439
2465
  );
@@ -2441,8 +2467,8 @@ async function getPathDiff(worktreePath, repoPath, opts) {
2441
2467
  if (includePatches) {
2442
2468
  const parsed = filesFromDiff("", "", diff.stdout, maxHunk);
2443
2469
  for (const file of parsed) filesMap.set(file.path, file);
2444
- if (diff.stdout.trim() && !filesMap.has(path)) {
2445
- filesMap.set(path, fileFromPatch(path, diff.stdout, maxHunk));
2470
+ if (diff.stdout.trim() && !filesMap.has(path2)) {
2471
+ filesMap.set(path2, fileFromPatch(path2, diff.stdout, maxHunk));
2446
2472
  }
2447
2473
  } else {
2448
2474
  for (const file of filesFromDiff(diff.stdout, "", "", maxHunk)) {
@@ -2450,7 +2476,7 @@ async function getPathDiff(worktreePath, repoPath, opts) {
2450
2476
  }
2451
2477
  }
2452
2478
  if (filesMap.size === 0) {
2453
- const maybe = await listUntrackedPaths(worktreePath, path);
2479
+ const maybe = await listUntrackedPaths(worktreePath, path2);
2454
2480
  if (maybe.length > 0) {
2455
2481
  const extra = includePatches ? await listUntrackedFiles(worktreePath, maxHunk, maybe) : untrackedStubs(maybe);
2456
2482
  for (const file of extra) filesMap.set(file.path, file);
@@ -3473,8 +3499,8 @@ function splitForCompaction(messages, thresholds = {}) {
3473
3499
  function formatToolPart(t, detail) {
3474
3500
  if (detail === "summary") {
3475
3501
  const label = t.description || t.detail || t.name;
3476
- const path = t.filePath ? ` (${t.filePath})` : "";
3477
- return `- ${t.name}: ${label}${path}`;
3502
+ const path2 = t.filePath ? ` (${t.filePath})` : "";
3503
+ return `- ${t.name}: ${label}${path2}`;
3478
3504
  }
3479
3505
  const lines = [`#### Tool: ${t.name}`, `Status: ${t.status}`];
3480
3506
  if (t.description) lines.push(`Description: ${t.description}`);
@@ -3812,7 +3838,7 @@ async function createThread(input, _onSetupLine) {
3812
3838
  return readThread(thread.id) ?? thread;
3813
3839
  }
3814
3840
  async function listLinearIssues(agent, repoPath) {
3815
- const { getAdapter: getAdapter2 } = await import("./agents-XJV6J3K2.js");
3841
+ const { getAdapter: getAdapter2 } = await import("./agents-EWPHOM6Y.js");
3816
3842
  await requireAgent(agent, { requireLinear: true });
3817
3843
  const adapter = getAdapter2(agent);
3818
3844
  if (!adapter.listLinearIssues) {
@@ -4355,7 +4381,7 @@ async function adoptThread(input) {
4355
4381
  messages: input.messages ?? []
4356
4382
  });
4357
4383
  writeThread(thread);
4358
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-O3U5BENH.js");
4384
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-TYPJNUSM.js");
4359
4385
  await ensureWorkspace2(repoPath);
4360
4386
  return thread;
4361
4387
  }
@@ -4606,10 +4632,10 @@ function storePath() {
4606
4632
  return join8(appDataDir(), "slack-reply-to.json");
4607
4633
  }
4608
4634
  function readStore() {
4609
- const path = storePath();
4610
- if (!existsSync10(path)) return {};
4635
+ const path2 = storePath();
4636
+ if (!existsSync10(path2)) return {};
4611
4637
  try {
4612
- const parsed = isSecureFileEncrypted(path) ? readSecureJson(path) : JSON.parse(readFileSync8(path, "utf8"));
4638
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync8(path2, "utf8"));
4613
4639
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
4614
4640
  } catch {
4615
4641
  return {};
@@ -4632,12 +4658,12 @@ function storePath2() {
4632
4658
  }
4633
4659
  function readStore2() {
4634
4660
  try {
4635
- const path = storePath2();
4636
- const wasEncrypted = isSecureFileEncrypted(path);
4637
- const parsed = readSecureJson(path);
4661
+ const path2 = storePath2();
4662
+ const wasEncrypted = isSecureFileEncrypted(path2);
4663
+ const parsed = readSecureJson(path2);
4638
4664
  const workspaces = Array.isArray(parsed?.workspaces) ? parsed.workspaces : [];
4639
4665
  if (workspaces.length > 0 && !wasEncrypted && resolveVaultKey()) {
4640
- writeSecureJson(path, { workspaces });
4666
+ writeSecureJson(path2, { workspaces });
4641
4667
  }
4642
4668
  return workspaces;
4643
4669
  } catch {
@@ -4773,10 +4799,10 @@ function tsNewer(a, b) {
4773
4799
  return Number(a) > Number(b);
4774
4800
  }
4775
4801
  function readStore3() {
4776
- const path = storePath3();
4777
- if (!existsSync11(path)) return [];
4802
+ const path2 = storePath3();
4803
+ if (!existsSync11(path2)) return [];
4778
4804
  try {
4779
- const parsed = isSecureFileEncrypted(path) ? readSecureJson(path) : JSON.parse(readFileSync9(path, "utf8"));
4805
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync9(path2, "utf8"));
4780
4806
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
4781
4807
  } catch {
4782
4808
  return [];
@@ -5140,8 +5166,8 @@ function shouldAutoArchiveOnPrMerge(opts) {
5140
5166
  // src/git/orphan-cleanup.ts
5141
5167
  import { existsSync as existsSync12, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
5142
5168
  import { join as join11 } from "path";
5143
- function isSideboardWorktreePath(path) {
5144
- return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
5169
+ function isSideboardWorktreePath(path2) {
5170
+ return path2.includes("/.sideboard/worktrees/") || path2.includes("/sideboard/workspaces/");
5145
5171
  }
5146
5172
  async function findOrphanWorktrees(repoPaths) {
5147
5173
  const threads = listThreads({ includeArchived: true });
@@ -5166,18 +5192,18 @@ async function findOrphanWorktrees(repoPaths) {
5166
5192
  try {
5167
5193
  const wts = await listWorktrees(repoPath);
5168
5194
  for (const wt of wts) {
5169
- const path = wt.path.replace(/\/$/, "");
5170
- if (!isSideboardWorktreePath(path)) continue;
5171
- if (known.has(path)) continue;
5172
- if (seen.has(path)) continue;
5173
- seen.add(path);
5195
+ const path2 = wt.path.replace(/\/$/, "");
5196
+ if (!isSideboardWorktreePath(path2)) continue;
5197
+ if (known.has(path2)) continue;
5198
+ if (seen.has(path2)) continue;
5199
+ seen.add(path2);
5174
5200
  let mtimeMs = 0;
5175
5201
  try {
5176
- mtimeMs = statSync5(path).mtimeMs;
5202
+ mtimeMs = statSync5(path2).mtimeMs;
5177
5203
  } catch {
5178
5204
  mtimeMs = 0;
5179
5205
  }
5180
- orphans.push({ path, repoPath, mtimeMs });
5206
+ orphans.push({ path: path2, repoPath, mtimeMs });
5181
5207
  }
5182
5208
  } catch {
5183
5209
  }
@@ -5186,17 +5212,17 @@ async function findOrphanWorktrees(repoPaths) {
5186
5212
  if (existsSync12(root)) {
5187
5213
  for (const entry of readdirSync4(root, { withFileTypes: true })) {
5188
5214
  if (!entry.isDirectory()) continue;
5189
- const path = join11(root, entry.name).replace(/\/$/, "");
5190
- if (known.has(path) || seen.has(path)) continue;
5191
- if (!existsSync12(join11(path, ".git"))) continue;
5192
- seen.add(path);
5215
+ const path2 = join11(root, entry.name).replace(/\/$/, "");
5216
+ if (known.has(path2) || seen.has(path2)) continue;
5217
+ if (!existsSync12(join11(path2, ".git"))) continue;
5218
+ seen.add(path2);
5193
5219
  let mtimeMs = 0;
5194
5220
  try {
5195
- mtimeMs = statSync5(path).mtimeMs;
5221
+ mtimeMs = statSync5(path2).mtimeMs;
5196
5222
  } catch {
5197
5223
  mtimeMs = Date.now();
5198
5224
  }
5199
- orphans.push({ path, repoPath, mtimeMs });
5225
+ orphans.push({ path: path2, repoPath, mtimeMs });
5200
5226
  }
5201
5227
  }
5202
5228
  } catch {
@@ -5579,9 +5605,9 @@ function ensureReviewRequestFile(worktreePath) {
5579
5605
  const localAbs = join13(worktreePath, REVIEW_REQUEST_PATH);
5580
5606
  const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent(join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5581
5607
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
5582
- const path = existsSync14(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
5608
+ const path2 = existsSync14(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
5583
5609
  return {
5584
- path,
5610
+ path: path2,
5585
5611
  name: REVIEW_REQUEST_NAME,
5586
5612
  content: localContent,
5587
5613
  source: "local"
@@ -5597,13 +5623,13 @@ function ensureReviewRequestFile(worktreePath) {
5597
5623
  };
5598
5624
  }
5599
5625
  function buildReviewRequestAttachment(content, opts) {
5600
- const path = opts?.path ?? REVIEW_REQUEST_PATH;
5601
- const name = opts?.name ?? (path === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
5626
+ const path2 = opts?.path ?? REVIEW_REQUEST_PATH;
5627
+ const name = opts?.name ?? (path2 === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
5602
5628
  return {
5603
5629
  id: randomUUID4(),
5604
5630
  name,
5605
5631
  kind: "file",
5606
- path,
5632
+ path: path2,
5607
5633
  content
5608
5634
  };
5609
5635
  }
@@ -5872,7 +5898,7 @@ var Orchestrator = class {
5872
5898
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
5873
5899
  });
5874
5900
  }
5875
- const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
5901
+ const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-6IOQYGBK.js");
5876
5902
  if (autoCleanupOrphansEnabled2() && shouldRunWorktreeCleanup() && orphans.length > 0) {
5877
5903
  await cleanupOrphanWorktrees({ repoPaths });
5878
5904
  }
@@ -6029,7 +6055,7 @@ var Orchestrator = class {
6029
6055
  }
6030
6056
  async finishCreateThread(threadId, prompt) {
6031
6057
  await this.runSetupAfterCreate(threadId);
6032
- const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
6058
+ const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-6IOQYGBK.js");
6033
6059
  if (autoRunAfterSetupEnabled2()) {
6034
6060
  try {
6035
6061
  await this.startDev(threadId);
@@ -6307,14 +6333,16 @@ var Orchestrator = class {
6307
6333
  }
6308
6334
  const isBrightsy = fresh.agent === "brightsy";
6309
6335
  const isOrchestration = isOrchestratorThread(fresh);
6336
+ const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2 } = await import("./app-settings-6IOQYGBK.js");
6337
+ const gitAuthMode = getGithubGitAuthMode2();
6310
6338
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
6311
6339
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
6312
6340
  () => null
6313
- )
6341
+ ),
6342
+ gitAuthMode
6314
6343
  });
6315
6344
  const artifactDirective = isBrightsy ? null : formatArtifactDirective();
6316
6345
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
6317
- const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
6318
6346
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
6319
6347
  customPrompt: settings?.prompts?.renameBranch
6320
6348
  }) : null;
@@ -7020,7 +7048,7 @@ var Orchestrator = class {
7020
7048
  updateThread(thread.id, { title: meta.title });
7021
7049
  }
7022
7050
  }
7023
- const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
7051
+ const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await import("./app-settings-6IOQYGBK.js");
7024
7052
  const latest = this.requireThread(thread.id);
7025
7053
  if (!shouldAutoArchiveOnPrMerge({
7026
7054
  previousPrState: prevState || null,
@@ -7151,8 +7179,9 @@ var Orchestrator = class {
7151
7179
  return tab;
7152
7180
  }
7153
7181
  /**
7154
- * Queue a desktop-git-button prompt on a worktree agent (commit/push/PR/merge).
7155
- * Orchestrators use this instead of running git/gh from the synthetic home.
7182
+ * Desktop git buttons + MCP `ask_git`.
7183
+ * When the worktree is clean, push / open the PR here (HTTPS via `gh` if SSH
7184
+ * is missing). When dirty, queue the worktree agent to commit first.
7156
7185
  */
7157
7186
  async askGit(threadRef, action) {
7158
7187
  if (!AGENT_GIT_ACTIONS.includes(action)) {
@@ -7170,6 +7199,10 @@ var Orchestrator = class {
7170
7199
  "No pull request linked. Ask the worktree agent to open a draft PR first (ask_git create-draft)."
7171
7200
  );
7172
7201
  }
7202
+ if ((action === "commit-push" || action === "create-draft" || action === "create-web") && thread.worktreePath?.trim() && !await isDirty(thread.worktreePath)) {
7203
+ await this.pushAndMaybeOpenPr(thread, action);
7204
+ return this.requireThread(threadRef);
7205
+ }
7173
7206
  let prBase;
7174
7207
  if (action === "resolve-conflicts") {
7175
7208
  try {
@@ -7180,6 +7213,34 @@ var Orchestrator = class {
7180
7213
  }
7181
7214
  return this.send(threadRef, agentGitPrompt(action, { prBase }));
7182
7215
  }
7216
+ /** Push origin (gh HTTPS fallback) and create/update the PR when requested. */
7217
+ async pushAndMaybeOpenPr(thread, action) {
7218
+ const cwd = thread.worktreePath;
7219
+ const branch = await currentBranch(cwd);
7220
+ await pushBranch(cwd, branch);
7221
+ if (action === "commit-push") return;
7222
+ const base = await resolveDefaultBranch(thread.repoPath);
7223
+ const meta = await suggestPrMetadata(cwd, {
7224
+ base,
7225
+ fallbackTitle: thread.prTitle ?? thread.title
7226
+ });
7227
+ const url = await createOrUpdatePr(cwd, {
7228
+ title: meta.title,
7229
+ body: meta.body,
7230
+ base,
7231
+ head: branch,
7232
+ draft: action === "create-draft",
7233
+ web: action === "create-web"
7234
+ });
7235
+ if (!url) return;
7236
+ const patch = { prUrl: url, prTitle: meta.title };
7237
+ try {
7238
+ const fetched = await getPrMeta(cwd, url);
7239
+ if (fetched?.title) patch.prTitle = fetched.title;
7240
+ } catch {
7241
+ }
7242
+ updateThread(thread.id, patch);
7243
+ }
7183
7244
  setThreadOptions(threadRef, patch) {
7184
7245
  const thread = this.requireThread(threadRef);
7185
7246
  const next = {};
@@ -7275,7 +7336,7 @@ var Orchestrator = class {
7275
7336
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
7276
7337
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
7277
7338
  try {
7278
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-O3U5BENH.js");
7339
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-TYPJNUSM.js");
7279
7340
  await ensureWorkspace2(thread.repoPath);
7280
7341
  } catch {
7281
7342
  }
@@ -7297,7 +7358,7 @@ var Orchestrator = class {
7297
7358
  await runArchiveScript(thread.repoPath, thread.worktreePath);
7298
7359
  } catch {
7299
7360
  }
7300
- const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
7361
+ const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-6IOQYGBK.js");
7301
7362
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled2();
7302
7363
  await removeWorktree(thread.repoPath, thread.worktreePath, {
7303
7364
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -7318,7 +7379,7 @@ var Orchestrator = class {
7318
7379
  return restored2;
7319
7380
  }
7320
7381
  if (!existsSync15(thread.worktreePath)) {
7321
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-VOCH3WS3.js");
7382
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-5VLLFI5W.js");
7322
7383
  const { execa: execa6 } = await import("execa");
7323
7384
  const slug = thread.worktreePath.split("/").pop();
7324
7385
  const dest = thread.worktreePath;
@@ -7427,7 +7488,7 @@ async function startOrchestration(opts) {
7427
7488
  sourceRef: "default",
7428
7489
  ...createOpts
7429
7490
  }).catch(async () => {
7430
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-VOCH3WS3.js");
7491
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-5VLLFI5W.js");
7431
7492
  const repo = await resolveRepoRoot2(repoPath);
7432
7493
  const def = await resolveDefaultBranch2(repo);
7433
7494
  return createThread({
@@ -7745,9 +7806,9 @@ function labelGithubUrl(url) {
7745
7806
  return null;
7746
7807
  }
7747
7808
  if (!/(^|\.)github\.com$/i.test(parsed.hostname)) return null;
7748
- const path = parsed.pathname;
7809
+ const path2 = parsed.pathname;
7749
7810
  const hash = parsed.hash || "";
7750
- const pr = path.match(/\/([^/]+)\/([^/]+)\/pull\/(\d+)/i);
7811
+ const pr = path2.match(/\/([^/]+)\/([^/]+)\/pull\/(\d+)/i);
7751
7812
  if (pr) {
7752
7813
  const n = pr[3];
7753
7814
  if (/#discussion_r\d+/i.test(hash) || /#pullrequestreview-\d+/i.test(hash)) {
@@ -7758,11 +7819,11 @@ function labelGithubUrl(url) {
7758
7819
  }
7759
7820
  return { kind: "pr", label: `PR #${n}`, url: raw };
7760
7821
  }
7761
- const issueComment = path.match(/\/([^/]+)\/([^/]+)\/issues\/(\d+)/i);
7822
+ const issueComment = path2.match(/\/([^/]+)\/([^/]+)\/issues\/(\d+)/i);
7762
7823
  if (issueComment && /#issuecomment-\d+/i.test(hash)) {
7763
7824
  return { kind: "comment", label: `Issue #${issueComment[3]} comment`, url: raw };
7764
7825
  }
7765
- const blob = path.match(/\/([^/]+)\/([^/]+)\/blob\/[^/]+\/(.+)$/i);
7826
+ const blob = path2.match(/\/([^/]+)\/([^/]+)\/blob\/[^/]+\/(.+)$/i);
7766
7827
  if (blob) {
7767
7828
  const filePath = decodeURIComponent(blob[3]);
7768
7829
  const fileName = filePath.split("/").pop() || filePath;
@@ -8179,7 +8240,7 @@ function withTimeout(promise, ms, label) {
8179
8240
  async function startMcpServer() {
8180
8241
  const orch = getOrchestrator();
8181
8242
  try {
8182
- const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-3TLA2EJ7.js");
8243
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-6IOQYGBK.js");
8183
8244
  orch.setMaxConcurrent(maxConcurrentAgents2());
8184
8245
  } catch {
8185
8246
  }
@@ -8324,10 +8385,10 @@ async function startMcpServer() {
8324
8385
  const t = orch.getThread(thread_id.trim());
8325
8386
  if (t?.worktreePath?.trim()) root = t.worktreePath;
8326
8387
  }
8327
- const path = writePlanFile2(root, content);
8388
+ const path2 = writePlanFile2(root, content);
8328
8389
  const payload = {
8329
8390
  ok: true,
8330
- path,
8391
+ path: path2,
8331
8392
  title: title?.trim() || "Plan",
8332
8393
  message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
8333
8394
  };
@@ -8389,7 +8450,7 @@ async function startMcpServer() {
8389
8450
  registerLinearTools(server);
8390
8451
  if (sideboardMcpProfile() !== "worktree") {
8391
8452
  const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await import("./caffeinate-hold-EAZNWJBE.js");
8392
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-3TLA2EJ7.js");
8453
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-6IOQYGBK.js");
8393
8454
  const accountDefaults = resolveThreadDefaults2();
8394
8455
  const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
8395
8456
  server.tool(
@@ -8751,7 +8812,7 @@ async function startMcpServer() {
8751
8812
  );
8752
8813
  server.tool(
8753
8814
  "ask_git",
8754
- "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.",
8815
+ "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.",
8755
8816
  {
8756
8817
  ref: z3.string().describe("Worktree thread id/ref"),
8757
8818
  action: z3.enum(AGENT_GIT_ACTIONS).describe(
@@ -9219,9 +9280,9 @@ var BrightsySideboardApi = class {
9219
9280
  get endpoint() {
9220
9281
  return (this.cfg.endpoint || "https://brightsy.ai").replace(/\/$/, "");
9221
9282
  }
9222
- async request(path, init = {}) {
9283
+ async request(path2, init = {}) {
9223
9284
  await this.refreshIfNeeded();
9224
- const url = `${this.endpoint}${path}`;
9285
+ const url = `${this.endpoint}${path2}`;
9225
9286
  const headers = {
9226
9287
  Authorization: `Bearer ${this.cfg.access_token}`,
9227
9288
  Accept: "application/json"
@@ -10809,6 +10870,83 @@ var SlackRelayHub = class {
10809
10870
  // src/slack/relay-server.ts
10810
10871
  import { createServer as createServer3 } from "http";
10811
10872
  import { WebSocketServer } from "ws";
10873
+
10874
+ // src/slack/relay-static.ts
10875
+ import { createReadStream } from "fs";
10876
+ import { stat } from "fs/promises";
10877
+ import path from "path";
10878
+ var TYPES = {
10879
+ ".css": "text/css; charset=utf-8",
10880
+ ".html": "text/html; charset=utf-8",
10881
+ ".ico": "image/x-icon",
10882
+ ".jpeg": "image/jpeg",
10883
+ ".jpg": "image/jpeg",
10884
+ ".js": "text/javascript; charset=utf-8",
10885
+ ".json": "application/json; charset=utf-8",
10886
+ ".png": "image/png",
10887
+ ".svg": "image/svg+xml",
10888
+ ".txt": "text/plain; charset=utf-8",
10889
+ ".webp": "image/webp",
10890
+ ".woff2": "font/woff2"
10891
+ };
10892
+ function requestHostname(req) {
10893
+ const raw = req.headers.host ?? "";
10894
+ return raw.split(":")[0]?.toLowerCase() ?? "";
10895
+ }
10896
+ function hostnameAllowed(host, allowed) {
10897
+ if (!allowed || allowed.length === 0) return true;
10898
+ return allowed.includes(host);
10899
+ }
10900
+ function resolveStaticPath(root, requestUrl) {
10901
+ let pathname;
10902
+ try {
10903
+ pathname = decodeURIComponent(new URL(requestUrl, "http://relay.local").pathname);
10904
+ } catch {
10905
+ return null;
10906
+ }
10907
+ if (!pathname.startsWith("/") || pathname.includes("\0")) return null;
10908
+ const rootResolved = path.resolve(root);
10909
+ const candidate = path.resolve(rootResolved, `.${pathname}`);
10910
+ if (candidate !== rootResolved && !candidate.startsWith(rootResolved + path.sep)) {
10911
+ return null;
10912
+ }
10913
+ return candidate;
10914
+ }
10915
+ async function fileSize(file) {
10916
+ try {
10917
+ const st = await stat(file);
10918
+ return st.isFile() ? st.size : null;
10919
+ } catch {
10920
+ return null;
10921
+ }
10922
+ }
10923
+ function sendFile(req, res, file, size) {
10924
+ const ext = path.extname(file).toLowerCase();
10925
+ res.writeHead(200, {
10926
+ "Content-Type": TYPES[ext] ?? "application/octet-stream",
10927
+ "Content-Length": size,
10928
+ "Cache-Control": ext === ".html" ? "no-cache" : "public, max-age=3600"
10929
+ });
10930
+ if (req.method === "HEAD") {
10931
+ res.end();
10932
+ return true;
10933
+ }
10934
+ createReadStream(file).pipe(res);
10935
+ return true;
10936
+ }
10937
+ async function tryServeStatic(req, res, root) {
10938
+ if (req.method !== "GET" && req.method !== "HEAD") return false;
10939
+ const candidate = resolveStaticPath(root, req.url || "/");
10940
+ if (!candidate) return false;
10941
+ const direct = await fileSize(candidate);
10942
+ if (direct != null) return sendFile(req, res, candidate, direct);
10943
+ const asIndex = path.join(candidate, "index.html");
10944
+ const indexSize = await fileSize(asIndex);
10945
+ if (indexSize != null) return sendFile(req, res, asIndex, indexSize);
10946
+ return false;
10947
+ }
10948
+
10949
+ // src/slack/relay-server.ts
10812
10950
  function sendHtml(res, status, title, body) {
10813
10951
  res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
10814
10952
  res.end(slackOAuthHtmlPage(title, body));
@@ -10907,7 +11045,26 @@ async function startSlackRelayServer(opts) {
10907
11045
  const reqUrl = req.url || "/";
10908
11046
  if (await handleCallback(reqUrl, res)) return;
10909
11047
  if (handleResult(reqUrl, res)) return;
10910
- if (req.url === "/health" || req.url === "/") {
11048
+ if (req.url === "/health") {
11049
+ sendJson(res, 200, {
11050
+ ok: true,
11051
+ service: "sideboard-slack-relay",
11052
+ sessions: hub.listSessions().length,
11053
+ oauth: Boolean(clientSecret)
11054
+ });
11055
+ return;
11056
+ }
11057
+ const hostname = requestHostname(req);
11058
+ const canonical = (opts.canonicalSiteHost ?? "www.sideboard.cloud").toLowerCase();
11059
+ if (opts.redirectHosts?.includes(hostname) && hostname !== canonical) {
11060
+ res.writeHead(301, { Location: `https://${canonical}${reqUrl}` });
11061
+ res.end();
11062
+ return;
11063
+ }
11064
+ if (opts.staticRoot && hostnameAllowed(hostname, opts.staticHosts) && await tryServeStatic(req, res, opts.staticRoot)) {
11065
+ return;
11066
+ }
11067
+ if (req.url === "/") {
10911
11068
  sendJson(res, 200, {
10912
11069
  ok: true,
10913
11070
  service: "sideboard-slack-relay",
@@ -11016,6 +11173,7 @@ export {
11016
11173
  CONTEXT_MIN_MESSAGES,
11017
11174
  COORDINATOR_TOOL_PLAYBOOK,
11018
11175
  FAMOUS_SOCCER_TEAMS,
11176
+ GITHUB_GIT_AUTH_MODES,
11019
11177
  GLOBAL_WORKSPACE_ID,
11020
11178
  HARNESS_ENV_KEYS,
11021
11179
  LEGACY_ATTACHMENTS_DIR,
@@ -11065,10 +11223,12 @@ export {
11065
11223
  allocateTeamSlug,
11066
11224
  appDataDir,
11067
11225
  appSettingsPath,
11226
+ appendIndexedGitConfig,
11068
11227
  appendMessage,
11069
11228
  applyAgentEvent,
11070
11229
  applyAppEnvironment,
11071
11230
  applyCompaction,
11231
+ applyGithubGitAuthEnv,
11072
11232
  applyThreadIntoMain,
11073
11233
  applyTurnUsage,
11074
11234
  assertOrchestratorCapableAgent,
@@ -11183,6 +11343,7 @@ export {
11183
11343
  formatBrightsyFetchError,
11184
11344
  formatFetchError,
11185
11345
  formatGhLandError,
11346
+ formatGitAuthModeDirective,
11186
11347
  formatIpcInvokeError,
11187
11348
  formatMessagesAsTranscript,
11188
11349
  formatPlanQuestionAnswers,
@@ -11209,6 +11370,8 @@ export {
11209
11370
  getDiff,
11210
11371
  getDiffSummary,
11211
11372
  getGitHubStatus,
11373
+ getGithubGitAuthMode,
11374
+ getGithubPat,
11212
11375
  getIssueSource,
11213
11376
  getLinearApiKey,
11214
11377
  getLinearAuthToken,
@@ -11227,6 +11390,7 @@ export {
11227
11390
  ghHeadRef,
11228
11391
  ghRepoSelectArgs,
11229
11392
  git,
11393
+ githubAgentGitEnv,
11230
11394
  globalAgentCwd,
11231
11395
  handleSlackInbound,
11232
11396
  harnessEnvKey,
@@ -11449,6 +11613,7 @@ export {
11449
11613
  startSlackOAuth,
11450
11614
  startSlackRelayServer,
11451
11615
  stripBrightsyNdjsonNoise,
11616
+ stripNestedElectronEnv,
11452
11617
  submitPrStack,
11453
11618
  suggestSlug,
11454
11619
  summarizeConversation,