@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
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-K553XK7M.js";
8
8
  import {
9
9
  BRIGHTSY_MCP_ALLOWED_TOOLS,
10
10
  CLAUDE_MODEL_CATALOG,
@@ -13,12 +13,14 @@ import {
13
13
  SIDEBOARD_MCP_ALLOWED_TOOLS,
14
14
  SIDEBOARD_MCP_PROFILE_ENV,
15
15
  allAdapters,
16
+ applyTurnUsage,
16
17
  brightsyAdapter,
17
18
  brightsyMcpAllowedTools,
18
19
  buildCachedUserContent,
19
20
  buildClaudeStreamJsonUserMessage,
20
21
  claudeAdapter,
21
22
  codexAdapter,
23
+ contextTokens,
22
24
  countCacheControlBlocks,
23
25
  cursorAdapter,
24
26
  decodeBrightsyTarget,
@@ -26,6 +28,7 @@ import {
26
28
  encodeBrightsyTarget,
27
29
  findInvalidCacheControlTtlOrder,
28
30
  flattenTurnInput,
31
+ fromInclusiveInputUsage,
29
32
  getAdapter,
30
33
  getAgentSetupInfo,
31
34
  installAgent,
@@ -41,6 +44,7 @@ import {
41
44
  loginAgent,
42
45
  mcpAllowTools,
43
46
  mcpAuthWarnings,
47
+ mergeUsage,
44
48
  normalizeTurnInput,
45
49
  openInSystemTerminal,
46
50
  opencodeAdapter,
@@ -50,6 +54,7 @@ import {
50
54
  permissionMode,
51
55
  prepareTerminalCommand,
52
56
  promptMentionsBrightsy,
57
+ requestOccupancy,
53
58
  resolveCursorModelId,
54
59
  resolveLoginCommand,
55
60
  resolveQuotaFallbackAgent,
@@ -57,8 +62,9 @@ import {
57
62
  shouldInjectBrightsyMcp,
58
63
  sideboardMcpProfile,
59
64
  threadRequestsBrightsyMcp,
65
+ totalTokens,
60
66
  writeInjectedMcpConfig
61
- } from "./chunk-QD37IILI.js";
67
+ } from "./chunk-XNECBC6T.js";
62
68
  import {
63
69
  CLOUD_COORDINATOR_BUSY_REPLY,
64
70
  CLOUD_COORDINATOR_STOPPED_REPLY,
@@ -85,7 +91,7 @@ import {
85
91
  parseForceStopMessage,
86
92
  slackCoordinatorSourceRef,
87
93
  takenTeamSlugsForOrchestration
88
- } from "./chunk-VERLUKN2.js";
94
+ } from "./chunk-6F2TQFJE.js";
89
95
  import {
90
96
  COORDINATOR_TOOL_PLAYBOOK,
91
97
  SLACK_REPLY_FORMATTING,
@@ -94,7 +100,7 @@ import {
94
100
  enrichWorkspacesWithGithub,
95
101
  ensureGlobalCoordinatorCwd,
96
102
  formatWorkspaceInventory
97
- } from "./chunk-EEKSZTMU.js";
103
+ } from "./chunk-CQBQWX73.js";
98
104
  import {
99
105
  brightsyConfigPath,
100
106
  brightsyMcpServerName,
@@ -129,66 +135,6 @@ import {
129
135
  pushTurnStderr,
130
136
  summarizeTurnStderr
131
137
  } 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
138
  import {
193
139
  caffeinateHoldPath,
194
140
  getCaffeinateHold,
@@ -196,14 +142,13 @@ import {
196
142
  releaseCaffeinateHoldForThread,
197
143
  setCaffeinateHold
198
144
  } from "./chunk-MFF2RE3I.js";
199
- import {
200
- writePrivateFile
201
- } from "./chunk-JT7R45JB.js";
202
145
  import {
203
146
  FAMOUS_SOCCER_TEAMS,
204
147
  addPrStackLayer,
205
148
  allocateTeamName,
206
149
  allocateTeamSlug,
150
+ appendIndexedGitConfig,
151
+ applyGithubGitAuthEnv,
207
152
  branchDisplayLabel,
208
153
  checkoutPrStackLayer,
209
154
  collectTakenTeamSlugs,
@@ -218,6 +163,7 @@ import {
218
163
  extractGhErrorDetail,
219
164
  fetchPrHead,
220
165
  formatGhLandError,
166
+ formatGitAuthModeDirective,
221
167
  formatIpcInvokeError,
222
168
  formatRateLimitResetHint,
223
169
  getPr,
@@ -227,6 +173,7 @@ import {
227
173
  getPrStack,
228
174
  ghHeadRef,
229
175
  ghRepoSelectArgs,
176
+ githubAgentGitEnv,
230
177
  initPrStack,
231
178
  isDirty,
232
179
  isGhRateLimitError,
@@ -261,13 +208,79 @@ import {
261
208
  worktreeDisplayLabel,
262
209
  worktreeDisplayLabelForGroup,
263
210
  worktreeNameFromPath
264
- } from "./chunk-MF2YMEGD.js";
211
+ } from "./chunk-YVVXWWCC.js";
265
212
  import {
266
213
  ATTACHMENTS_DIR,
267
214
  LEGACY_ATTACHMENTS_DIR,
268
215
  attachmentsGitignoreBody,
269
216
  isWorkspaceScratchPath
270
217
  } from "./chunk-FKOIHGKV.js";
218
+ import {
219
+ GITHUB_GIT_AUTH_MODES,
220
+ HARNESS_ENV_KEYS,
221
+ appSettingsPath,
222
+ applyAppEnvironment,
223
+ autoArchiveOnMergeEnabled,
224
+ autoCleanupOrphansEnabled,
225
+ autoRenameBranchEnabled,
226
+ autoRunAfterSetupEnabled,
227
+ brightsyCloudConnectAgent,
228
+ brightsyCloudConnectEnabled,
229
+ brightsyInjectWorktreeMcpEnabled,
230
+ caffeinateWhileCloudConnectEnabled,
231
+ caffeinateWhileRunningEnabled,
232
+ caffeinateWhileSlackListenEnabled,
233
+ childEnvWithAppSettings,
234
+ claudeChromeEnabled,
235
+ claudeUserSettingsPath,
236
+ deleteBranchOnPurgeEnabled,
237
+ disconnectLinearConnection,
238
+ ensureSlackDeviceIdentity,
239
+ getDefaultAgent,
240
+ getDefaultEffort,
241
+ getDefaultFast,
242
+ getDefaultModel,
243
+ getGithubGitAuthMode,
244
+ getGithubPat,
245
+ getIssueSource,
246
+ getLinearApiKey,
247
+ harnessEnvKey,
248
+ isLinearConnected,
249
+ isSecureFileEncrypted,
250
+ loadAppSettings,
251
+ maxConcurrentAgents,
252
+ orchestrationQuotaFallbackAgent,
253
+ orchestrationQuotaOnLimit,
254
+ persistVaultKeyInKeychain,
255
+ readKeychainVaultKey,
256
+ readSecureJson,
257
+ resolveAgentExecutable,
258
+ resolveClaudeExecutable,
259
+ resolveEffectiveIssueSource,
260
+ resolveNewThreadOptions,
261
+ resolveThreadDefaults,
262
+ resolveVaultKey,
263
+ saveAppSettings,
264
+ saveLinearOAuth,
265
+ secureFileUnlocksWith,
266
+ setVaultMasterKey,
267
+ slackAppLevelToken,
268
+ slackListenEnabled,
269
+ toPublicAppSettings,
270
+ updateAdvancedSettings,
271
+ updateAgentExecutable,
272
+ updateAppEnvironment,
273
+ updateBrightsySettings,
274
+ updateClaudeSettings,
275
+ updateCodexSettings,
276
+ updateDefaultsSettings,
277
+ updateIntegrationsSettings,
278
+ updateOpencodeSettings,
279
+ writeSecureJson
280
+ } from "./chunk-IFPN6TER.js";
281
+ import {
282
+ writePrivateFile
283
+ } from "./chunk-JT7R45JB.js";
271
284
  import {
272
285
  appendMessage,
273
286
  createEmptyThread,
@@ -1075,8 +1088,8 @@ function toolDetail(name, input) {
1075
1088
  if (!input) return void 0;
1076
1089
  const command = str(input.command) ?? str(input.cmd);
1077
1090
  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;
1091
+ const path2 = str(input.file_path) ?? str(input.path) ?? str(input.filePath) ?? str(input.filename);
1092
+ if (path2) return path2;
1080
1093
  const pattern = str(input.pattern) ?? str(input.glob) ?? str(input.glob_pattern);
1081
1094
  if (pattern) return pattern;
1082
1095
  const query = str(input.query) ?? str(input.prompt);
@@ -1120,12 +1133,12 @@ function toolDescription(name, input) {
1120
1133
  return "Run shell command";
1121
1134
  }
1122
1135
  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";
1136
+ const path2 = str(input.file_path) ?? str(input.path);
1137
+ return path2 ? `Edit ${path2.split("/").pop()}` : "Edit file";
1125
1138
  }
1126
1139
  if (/read/i.test(name)) {
1127
- const path = str(input.file_path) ?? str(input.path);
1128
- return path ? `Read ${path.split("/").pop()}` : "Read file";
1140
+ const path2 = str(input.file_path) ?? str(input.path);
1141
+ return path2 ? `Read ${path2.split("/").pop()}` : "Read file";
1129
1142
  }
1130
1143
  if (/grep/i.test(name)) return "Search files";
1131
1144
  if (/glob/i.test(name)) return "Find files";
@@ -1289,41 +1302,6 @@ function normalizeParseResult(parsed) {
1289
1302
  return Array.isArray(parsed) ? parsed : [parsed];
1290
1303
  }
1291
1304
 
1292
- // src/agents/usage.ts
1293
- function sumOptional(a, b) {
1294
- if (a == null && b == null) return void 0;
1295
- return (a ?? 0) + (b ?? 0);
1296
- }
1297
- function requestOccupancy(u) {
1298
- return u.inputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
1299
- }
1300
- function mergeUsage(a, b) {
1301
- return {
1302
- inputTokens: (a?.inputTokens ?? 0) + b.inputTokens,
1303
- outputTokens: (a?.outputTokens ?? 0) + b.outputTokens,
1304
- cacheReadTokens: sumOptional(a?.cacheReadTokens, b.cacheReadTokens),
1305
- cacheWriteTokens: sumOptional(a?.cacheWriteTokens, b.cacheWriteTokens),
1306
- lastRequestTokens: b.lastRequestTokens ?? a?.lastRequestTokens
1307
- };
1308
- }
1309
- function applyTurnUsage(current, incoming, scope = "request") {
1310
- if (scope === "turn") {
1311
- return {
1312
- ...incoming,
1313
- lastRequestTokens: current?.lastRequestTokens ?? requestOccupancy(incoming)
1314
- };
1315
- }
1316
- const merged = mergeUsage(current, incoming);
1317
- return { ...merged, lastRequestTokens: requestOccupancy(incoming) };
1318
- }
1319
- function totalTokens(u) {
1320
- return u.inputTokens + u.outputTokens + (u.cacheReadTokens ?? 0) + (u.cacheWriteTokens ?? 0);
1321
- }
1322
- function contextTokens(u) {
1323
- if (u.lastRequestTokens != null && u.lastRequestTokens > 0) return u.lastRequestTokens;
1324
- return requestOccupancy(u);
1325
- }
1326
-
1327
1305
  // src/agents/spawn.ts
1328
1306
  async function spawnAgentTurn(thread, input, onEvent) {
1329
1307
  ensureAgentPath();
@@ -1332,9 +1310,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
1332
1310
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
1333
1311
  );
1334
1312
  }
1335
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-YNMNO4CL.js");
1313
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-DZM4ZFGU.js");
1336
1314
  if (isGlobalThread2(thread)) {
1337
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-LLFJUO2R.js");
1315
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-JQF72JKX.js");
1338
1316
  ensureGlobalCoordinatorCwd2(
1339
1317
  isOrchestratorThread(thread) ? { orchestratorThreadId: thread.id } : void 0
1340
1318
  );
@@ -1351,8 +1329,13 @@ async function spawnAgentTurn(thread, input, onEvent) {
1351
1329
  }
1352
1330
  const env = childEnvWithAppSettings(cmd.env);
1353
1331
  if (!isOrchestratorThread(thread)) {
1354
- const originEnv = await originGhRepoEnv(thread.worktreePath).catch(() => ({}));
1355
- Object.assign(env, originEnv);
1332
+ try {
1333
+ const originEnv = await originGhRepoEnv(thread.worktreePath, { env });
1334
+ Object.assign(env, originEnv);
1335
+ } catch (err) {
1336
+ const detail = err instanceof Error ? err.message : String(err);
1337
+ console.warn(`[sideboard] originGhRepoEnv failed (${thread.worktreePath}): ${detail}`);
1338
+ }
1356
1339
  }
1357
1340
  const child = execa(cmd.file, cmd.args, {
1358
1341
  cwd: cmd.cwd,
@@ -1487,6 +1470,8 @@ function formatWorktreeDirective(thread, opts) {
1487
1470
  lines.push(
1488
1471
  "- Push with `git push -u origin HEAD` (or the current branch name). Do not `git push upstream`."
1489
1472
  );
1473
+ lines.push("");
1474
+ lines.push(formatGitAuthModeDirective(opts?.gitAuthMode ?? "auto"));
1490
1475
  lines.push(
1491
1476
  "- 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
1477
  );
@@ -1494,7 +1479,7 @@ function formatWorktreeDirective(thread, opts) {
1494
1479
  "- Prefer a concise imperative title (Conventional Commits style when it fits: feat:/fix:/chore:/docs:). Body should summarize intent, key changes, and test notes."
1495
1480
  );
1496
1481
  lines.push(
1497
- "- 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`)."
1482
+ "- 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`)."
1498
1483
  );
1499
1484
  if (thread.prUrl) {
1500
1485
  lines.push(
@@ -1532,7 +1517,7 @@ function formatWorktreeDirective(thread, opts) {
1532
1517
  '- "Address review comments." \u2192 read PR review feedback, make the requested changes, commit, and push.'
1533
1518
  );
1534
1519
  lines.push(
1535
- '- "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.'
1520
+ '- "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.'
1536
1521
  );
1537
1522
  return lines.join("\n");
1538
1523
  }
@@ -1575,10 +1560,11 @@ var FILES_BY_AGENT = {
1575
1560
  var MAX_CHARS_PER_FILE = 48e3;
1576
1561
  function loadAgentInstructions(worktreePath, agent) {
1577
1562
  const candidates = FILES_BY_AGENT[agent] ?? FILES_BY_AGENT.claude;
1578
- const seen = /* @__PURE__ */ new Set();
1563
+ const seenPaths = /* @__PURE__ */ new Set();
1564
+ const seenBodies = /* @__PURE__ */ new Set();
1579
1565
  const out = [];
1580
1566
  for (const rel of candidates) {
1581
- if (seen.has(rel)) continue;
1567
+ if (seenPaths.has(rel)) continue;
1582
1568
  const abs = join(worktreePath, rel);
1583
1569
  if (!existsSync(abs)) continue;
1584
1570
  try {
@@ -1590,7 +1576,10 @@ function loadAgentInstructions(worktreePath, agent) {
1590
1576
 
1591
1577
  \u2026(truncated)`;
1592
1578
  }
1593
- seen.add(rel);
1579
+ const body = content.trim().replace(/\r\n/g, "\n");
1580
+ if (seenBodies.has(body)) continue;
1581
+ seenPaths.add(rel);
1582
+ seenBodies.add(body);
1594
1583
  out.push({ relativePath: rel, content });
1595
1584
  } catch {
1596
1585
  }
@@ -1672,9 +1661,9 @@ function matchSimpleGlob(pattern, name) {
1672
1661
  return new RegExp(`^${escaped}$`).test(name);
1673
1662
  }
1674
1663
  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("#"));
1664
+ const path2 = join2(repoPath, ".worktreeinclude");
1665
+ if (!existsSync2(path2)) return [];
1666
+ return readFileSync2(path2, "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
1678
1667
  }
1679
1668
  function resolveFilesToCopy(repoPath) {
1680
1669
  const fromInclude = readWorktreeInclude(repoPath);
@@ -1749,8 +1738,20 @@ async function captureLoginEnv() {
1749
1738
  cachedLoginEnv = { ...process.env };
1750
1739
  return { ...cachedLoginEnv };
1751
1740
  }
1741
+ var NESTED_ELECTRON_ENV_PREFIXES = ["ELECTRON_", "CHROME_"];
1742
+ function stripNestedElectronEnv(env) {
1743
+ const out = { ...env };
1744
+ for (const key of Object.keys(out)) {
1745
+ if (NESTED_ELECTRON_ENV_PREFIXES.some((prefix) => key.startsWith(prefix))) {
1746
+ delete out[key];
1747
+ }
1748
+ }
1749
+ return out;
1750
+ }
1752
1751
  function buildWorkspaceScriptEnv(opts, baseEnv) {
1753
- const env = { ...baseEnv ?? process.env };
1752
+ const env = stripNestedElectronEnv({
1753
+ ...baseEnv ?? process.env
1754
+ });
1754
1755
  const name = opts.workspaceName ?? basename(opts.worktreePath);
1755
1756
  const ports = opts.ports ?? [];
1756
1757
  const primary = ports[0];
@@ -1975,10 +1976,10 @@ import { join as join3 } from "path";
1975
1976
  import { execa as execa3 } from "execa";
1976
1977
  import { createInterface as createInterface3 } from "readline";
1977
1978
  function loadCursorWorktreesJson(rootPath) {
1978
- const path = join3(rootPath, ".cursor", "worktrees.json");
1979
- if (!existsSync3(path)) return null;
1979
+ const path2 = join3(rootPath, ".cursor", "worktrees.json");
1980
+ if (!existsSync3(path2)) return null;
1980
1981
  try {
1981
- return JSON.parse(readFileSync3(path, "utf8"));
1982
+ return JSON.parse(readFileSync3(path2, "utf8"));
1982
1983
  } catch {
1983
1984
  return null;
1984
1985
  }
@@ -2079,9 +2080,9 @@ function parseNameStatus(stdout) {
2079
2080
  const out = [];
2080
2081
  for (const line of stdout.split("\n").filter(Boolean)) {
2081
2082
  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 });
2083
+ const path2 = pathParts[pathParts.length - 1];
2084
+ if (!status || !path2) continue;
2085
+ out.push({ status: status[0] ?? status, path: path2 });
2085
2086
  }
2086
2087
  return out;
2087
2088
  }
@@ -2089,12 +2090,12 @@ function parseNumstat(stdout) {
2089
2090
  const map = /* @__PURE__ */ new Map();
2090
2091
  for (const line of stdout.split("\n").filter(Boolean)) {
2091
2092
  const [addRaw, delRaw, ...pathParts] = line.split(" ");
2092
- const path = pathParts[pathParts.length - 1];
2093
- if (!path) continue;
2093
+ const path2 = pathParts[pathParts.length - 1];
2094
+ if (!path2) continue;
2094
2095
  const additions = addRaw === "-" ? 0 : Number(addRaw);
2095
2096
  const deletions = delRaw === "-" ? 0 : Number(delRaw);
2096
2097
  if (!Number.isFinite(additions) && !Number.isFinite(deletions)) continue;
2097
- map.set(path, {
2098
+ map.set(path2, {
2098
2099
  additions: Number.isFinite(additions) ? additions : 0,
2099
2100
  deletions: Number.isFinite(deletions) ? deletions : 0
2100
2101
  });
@@ -2171,70 +2172,70 @@ function splitCombinedDiff(stdout) {
2171
2172
  }
2172
2173
  return map;
2173
2174
  }
2174
- function syntheticAddPatch(path, content, maxHunk) {
2175
+ function syntheticAddPatch(path2, content, maxHunk) {
2175
2176
  const lines = content.split("\n");
2176
2177
  const body = lines.map((l) => `+${l}`).join("\n");
2177
- const header = `diff --git a/${path} b/${path}
2178
+ const header = `diff --git a/${path2} b/${path2}
2178
2179
  new file mode 100644
2179
2180
  --- /dev/null
2180
- +++ b/${path}
2181
+ +++ b/${path2}
2181
2182
  @@ -0,0 +1,${lines.length} @@
2182
2183
  `;
2183
2184
  return {
2184
- path,
2185
+ path: path2,
2185
2186
  status: "A",
2186
2187
  patch: capPatch(`${header}${body}`, maxHunk),
2187
2188
  additions: lines.length,
2188
2189
  deletions: 0
2189
2190
  };
2190
2191
  }
2191
- async function untrackedPatch(worktreePath, path, maxHunk) {
2192
- const abs = join4(worktreePath, path);
2192
+ async function untrackedPatch(worktreePath, path2, maxHunk) {
2193
+ const abs = join4(worktreePath, path2);
2193
2194
  try {
2194
2195
  const st = statSync2(abs);
2195
2196
  if (st.isFile() && st.size > maxHunk) {
2196
2197
  const buf = readFileSync4(abs).subarray(0, maxHunk);
2197
- return syntheticAddPatch(path, buf.toString("utf8"), maxHunk);
2198
+ return syntheticAddPatch(path2, buf.toString("utf8"), maxHunk);
2198
2199
  }
2199
2200
  } catch {
2200
2201
  }
2201
2202
  const { stdout: patch } = await git(
2202
- ["diff", "--no-ext-diff", "--no-color", "--no-index", "--", "/dev/null", path],
2203
+ ["diff", "--no-ext-diff", "--no-color", "--no-index", "--", "/dev/null", path2],
2203
2204
  worktreePath,
2204
2205
  { reject: false, timeoutMs: 4e3 }
2205
2206
  );
2206
- const normalized = patch.replace(/^diff --git a\/.+? b\/.+?$/m, `diff --git a/${path} b/${path}`).replace(/^--- .*$/m, "--- /dev/null").replace(/^\+\+\+ .*$/m, `+++ b/${path}`);
2207
+ const normalized = patch.replace(/^diff --git a\/.+? b\/.+?$/m, `diff --git a/${path2} b/${path2}`).replace(/^--- .*$/m, "--- /dev/null").replace(/^\+\+\+ .*$/m, `+++ b/${path2}`);
2207
2208
  const addLines = normalized.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
2208
2209
  return {
2209
- path,
2210
+ path: path2,
2210
2211
  status: "A",
2211
2212
  patch: capPatch(normalized, maxHunk),
2212
2213
  additions: addLines,
2213
2214
  deletions: 0
2214
2215
  };
2215
2216
  }
2216
- async function listUntrackedPaths(worktreePath, path) {
2217
+ async function listUntrackedPaths(worktreePath, path2) {
2217
2218
  const args = ["ls-files", "-z", "--others", "--exclude-standard"];
2218
- if (path) args.push("--", path);
2219
+ if (path2) args.push("--", path2);
2219
2220
  const { stdout } = await git(args, worktreePath, { reject: false });
2220
2221
  return stdout.split("\0").filter(Boolean).filter((p) => !isSideboardScratchPath(p));
2221
2222
  }
2222
2223
  async function listUntrackedFiles(worktreePath, maxHunk, paths) {
2223
2224
  const list = paths ?? await listUntrackedPaths(worktreePath);
2224
2225
  if (list.length === 0) return [];
2225
- return Promise.all(list.map((path) => untrackedPatch(worktreePath, path, maxHunk)));
2226
+ return Promise.all(list.map((path2) => untrackedPatch(worktreePath, path2, maxHunk)));
2226
2227
  }
2227
2228
  function untrackedStubs(paths) {
2228
- return paths.map((path) => ({
2229
- path,
2229
+ return paths.map((path2) => ({
2230
+ path: path2,
2230
2231
  status: "A",
2231
2232
  patch: "",
2232
2233
  additions: 0,
2233
2234
  deletions: 0
2234
2235
  }));
2235
2236
  }
2236
- function pathspec(path) {
2237
- return path ? ["--", path] : [];
2237
+ function pathspec(path2) {
2238
+ return path2 ? ["--", path2] : [];
2238
2239
  }
2239
2240
  function gitDiff(args, cwd, opts) {
2240
2241
  return git(["diff", "--no-ext-diff", "--no-color", ...args], cwd, {
@@ -2242,7 +2243,7 @@ function gitDiff(args, cwd, opts) {
2242
2243
  timeoutMs: opts?.timeoutMs
2243
2244
  });
2244
2245
  }
2245
- function fileFromPatch(path, patch, maxHunk) {
2246
+ function fileFromPatch(path2, patch, maxHunk) {
2246
2247
  let status = "M";
2247
2248
  if (/^new file mode /m.test(patch) || /^--- \/dev\/null/m.test(patch)) {
2248
2249
  status = "A";
@@ -2254,15 +2255,15 @@ function fileFromPatch(path, patch, maxHunk) {
2254
2255
  const additions = patch.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).length;
2255
2256
  const deletions = patch.split("\n").filter((l) => l.startsWith("-") && !l.startsWith("---")).length;
2256
2257
  return {
2257
- path,
2258
+ path: path2,
2258
2259
  status,
2259
2260
  patch: capPatch(patch, maxHunk),
2260
2261
  additions,
2261
2262
  deletions
2262
2263
  };
2263
2264
  }
2264
- async function gitDiffParts(worktreePath, diffArgs, includePatches, path) {
2265
- const extra = pathspec(path);
2265
+ async function gitDiffParts(worktreePath, diffArgs, includePatches, path2) {
2266
+ const extra = pathspec(path2);
2266
2267
  if (includePatches) {
2267
2268
  const diff = await gitDiff([...diffArgs, ...extra], worktreePath, {
2268
2269
  timeoutMs: 4e3
@@ -2300,17 +2301,17 @@ function filesFromDiff(nameStatus, numstat, combinedDiff, maxHunk) {
2300
2301
  const patches = splitCombinedDiff(combinedDiff);
2301
2302
  if (!nameStatus.trim() && patches.size > 0) {
2302
2303
  return [...patches.entries()].map(
2303
- ([path, patch]) => fileFromPatch(path, patch, maxHunk)
2304
+ ([path2, patch]) => fileFromPatch(path2, patch, maxHunk)
2304
2305
  );
2305
2306
  }
2306
2307
  const counts = parseNumstat(numstat);
2307
2308
  const files = [];
2308
- for (const { status, path } of parseNameStatus(nameStatus)) {
2309
- const n = counts.get(path);
2309
+ for (const { status, path: path2 } of parseNameStatus(nameStatus)) {
2310
+ const n = counts.get(path2);
2310
2311
  files.push({
2311
- path,
2312
+ path: path2,
2312
2313
  status,
2313
- patch: capPatch(patches.get(path) ?? "", maxHunk),
2314
+ patch: capPatch(patches.get(path2) ?? "", maxHunk),
2314
2315
  additions: n?.additions,
2315
2316
  deletions: n?.deletions
2316
2317
  });
@@ -2394,7 +2395,7 @@ function emptyPathDiff(scope, base, hasLastTurnBase, commitSha = null) {
2394
2395
  };
2395
2396
  }
2396
2397
  async function getPathDiff(worktreePath, repoPath, opts) {
2397
- const { path, scope, commitSha, lastTurnBase, hasLastTurnBase, includePatches, maxHunk } = opts;
2398
+ const { path: path2, scope, commitSha, lastTurnBase, hasLastTurnBase, includePatches, maxHunk } = opts;
2398
2399
  if (scope === "last_turn" && !lastTurnBase) {
2399
2400
  return emptyPathDiff(scope, "last turn", false);
2400
2401
  }
@@ -2433,7 +2434,7 @@ async function getPathDiff(worktreePath, repoPath, opts) {
2433
2434
  labelBase = "HEAD";
2434
2435
  }
2435
2436
  const diff = await gitDiff(
2436
- includePatches ? [...diffArgs, "--", path] : ["--name-status", ...diffArgs, "--", path],
2437
+ includePatches ? [...diffArgs, "--", path2] : ["--name-status", ...diffArgs, "--", path2],
2437
2438
  worktreePath,
2438
2439
  { timeoutMs: 4e3 }
2439
2440
  );
@@ -2441,8 +2442,8 @@ async function getPathDiff(worktreePath, repoPath, opts) {
2441
2442
  if (includePatches) {
2442
2443
  const parsed = filesFromDiff("", "", diff.stdout, maxHunk);
2443
2444
  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));
2445
+ if (diff.stdout.trim() && !filesMap.has(path2)) {
2446
+ filesMap.set(path2, fileFromPatch(path2, diff.stdout, maxHunk));
2446
2447
  }
2447
2448
  } else {
2448
2449
  for (const file of filesFromDiff(diff.stdout, "", "", maxHunk)) {
@@ -2450,7 +2451,7 @@ async function getPathDiff(worktreePath, repoPath, opts) {
2450
2451
  }
2451
2452
  }
2452
2453
  if (filesMap.size === 0) {
2453
- const maybe = await listUntrackedPaths(worktreePath, path);
2454
+ const maybe = await listUntrackedPaths(worktreePath, path2);
2454
2455
  if (maybe.length > 0) {
2455
2456
  const extra = includePatches ? await listUntrackedFiles(worktreePath, maxHunk, maybe) : untrackedStubs(maybe);
2456
2457
  for (const file of extra) filesMap.set(file.path, file);
@@ -3473,8 +3474,8 @@ function splitForCompaction(messages, thresholds = {}) {
3473
3474
  function formatToolPart(t, detail) {
3474
3475
  if (detail === "summary") {
3475
3476
  const label = t.description || t.detail || t.name;
3476
- const path = t.filePath ? ` (${t.filePath})` : "";
3477
- return `- ${t.name}: ${label}${path}`;
3477
+ const path2 = t.filePath ? ` (${t.filePath})` : "";
3478
+ return `- ${t.name}: ${label}${path2}`;
3478
3479
  }
3479
3480
  const lines = [`#### Tool: ${t.name}`, `Status: ${t.status}`];
3480
3481
  if (t.description) lines.push(`Description: ${t.description}`);
@@ -3812,7 +3813,7 @@ async function createThread(input, _onSetupLine) {
3812
3813
  return readThread(thread.id) ?? thread;
3813
3814
  }
3814
3815
  async function listLinearIssues(agent, repoPath) {
3815
- const { getAdapter: getAdapter2 } = await import("./agents-XJV6J3K2.js");
3816
+ const { getAdapter: getAdapter2 } = await import("./agents-UQ7D6U6L.js");
3816
3817
  await requireAgent(agent, { requireLinear: true });
3817
3818
  const adapter = getAdapter2(agent);
3818
3819
  if (!adapter.listLinearIssues) {
@@ -4355,7 +4356,7 @@ async function adoptThread(input) {
4355
4356
  messages: input.messages ?? []
4356
4357
  });
4357
4358
  writeThread(thread);
4358
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-O3U5BENH.js");
4359
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-QLRCOXAY.js");
4359
4360
  await ensureWorkspace2(repoPath);
4360
4361
  return thread;
4361
4362
  }
@@ -4606,10 +4607,10 @@ function storePath() {
4606
4607
  return join8(appDataDir(), "slack-reply-to.json");
4607
4608
  }
4608
4609
  function readStore() {
4609
- const path = storePath();
4610
- if (!existsSync10(path)) return {};
4610
+ const path2 = storePath();
4611
+ if (!existsSync10(path2)) return {};
4611
4612
  try {
4612
- const parsed = isSecureFileEncrypted(path) ? readSecureJson(path) : JSON.parse(readFileSync8(path, "utf8"));
4613
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync8(path2, "utf8"));
4613
4614
  return parsed?.targets && typeof parsed.targets === "object" ? parsed.targets : {};
4614
4615
  } catch {
4615
4616
  return {};
@@ -4632,12 +4633,12 @@ function storePath2() {
4632
4633
  }
4633
4634
  function readStore2() {
4634
4635
  try {
4635
- const path = storePath2();
4636
- const wasEncrypted = isSecureFileEncrypted(path);
4637
- const parsed = readSecureJson(path);
4636
+ const path2 = storePath2();
4637
+ const wasEncrypted = isSecureFileEncrypted(path2);
4638
+ const parsed = readSecureJson(path2);
4638
4639
  const workspaces = Array.isArray(parsed?.workspaces) ? parsed.workspaces : [];
4639
4640
  if (workspaces.length > 0 && !wasEncrypted && resolveVaultKey()) {
4640
- writeSecureJson(path, { workspaces });
4641
+ writeSecureJson(path2, { workspaces });
4641
4642
  }
4642
4643
  return workspaces;
4643
4644
  } catch {
@@ -4773,10 +4774,10 @@ function tsNewer(a, b) {
4773
4774
  return Number(a) > Number(b);
4774
4775
  }
4775
4776
  function readStore3() {
4776
- const path = storePath3();
4777
- if (!existsSync11(path)) return [];
4777
+ const path2 = storePath3();
4778
+ if (!existsSync11(path2)) return [];
4778
4779
  try {
4779
- const parsed = isSecureFileEncrypted(path) ? readSecureJson(path) : JSON.parse(readFileSync9(path, "utf8"));
4780
+ const parsed = isSecureFileEncrypted(path2) ? readSecureJson(path2) : JSON.parse(readFileSync9(path2, "utf8"));
4780
4781
  return Array.isArray(parsed?.watches) ? parsed.watches : [];
4781
4782
  } catch {
4782
4783
  return [];
@@ -5140,8 +5141,8 @@ function shouldAutoArchiveOnPrMerge(opts) {
5140
5141
  // src/git/orphan-cleanup.ts
5141
5142
  import { existsSync as existsSync12, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
5142
5143
  import { join as join11 } from "path";
5143
- function isSideboardWorktreePath(path) {
5144
- return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
5144
+ function isSideboardWorktreePath(path2) {
5145
+ return path2.includes("/.sideboard/worktrees/") || path2.includes("/sideboard/workspaces/");
5145
5146
  }
5146
5147
  async function findOrphanWorktrees(repoPaths) {
5147
5148
  const threads = listThreads({ includeArchived: true });
@@ -5166,18 +5167,18 @@ async function findOrphanWorktrees(repoPaths) {
5166
5167
  try {
5167
5168
  const wts = await listWorktrees(repoPath);
5168
5169
  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);
5170
+ const path2 = wt.path.replace(/\/$/, "");
5171
+ if (!isSideboardWorktreePath(path2)) continue;
5172
+ if (known.has(path2)) continue;
5173
+ if (seen.has(path2)) continue;
5174
+ seen.add(path2);
5174
5175
  let mtimeMs = 0;
5175
5176
  try {
5176
- mtimeMs = statSync5(path).mtimeMs;
5177
+ mtimeMs = statSync5(path2).mtimeMs;
5177
5178
  } catch {
5178
5179
  mtimeMs = 0;
5179
5180
  }
5180
- orphans.push({ path, repoPath, mtimeMs });
5181
+ orphans.push({ path: path2, repoPath, mtimeMs });
5181
5182
  }
5182
5183
  } catch {
5183
5184
  }
@@ -5186,17 +5187,17 @@ async function findOrphanWorktrees(repoPaths) {
5186
5187
  if (existsSync12(root)) {
5187
5188
  for (const entry of readdirSync4(root, { withFileTypes: true })) {
5188
5189
  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);
5190
+ const path2 = join11(root, entry.name).replace(/\/$/, "");
5191
+ if (known.has(path2) || seen.has(path2)) continue;
5192
+ if (!existsSync12(join11(path2, ".git"))) continue;
5193
+ seen.add(path2);
5193
5194
  let mtimeMs = 0;
5194
5195
  try {
5195
- mtimeMs = statSync5(path).mtimeMs;
5196
+ mtimeMs = statSync5(path2).mtimeMs;
5196
5197
  } catch {
5197
5198
  mtimeMs = Date.now();
5198
5199
  }
5199
- orphans.push({ path, repoPath, mtimeMs });
5200
+ orphans.push({ path: path2, repoPath, mtimeMs });
5200
5201
  }
5201
5202
  }
5202
5203
  } catch {
@@ -5579,9 +5580,9 @@ function ensureReviewRequestFile(worktreePath) {
5579
5580
  const localAbs = join13(worktreePath, REVIEW_REQUEST_PATH);
5580
5581
  const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent(join13(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
5581
5582
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
5582
- const path = existsSync14(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
5583
+ const path2 = existsSync14(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
5583
5584
  return {
5584
- path,
5585
+ path: path2,
5585
5586
  name: REVIEW_REQUEST_NAME,
5586
5587
  content: localContent,
5587
5588
  source: "local"
@@ -5597,13 +5598,13 @@ function ensureReviewRequestFile(worktreePath) {
5597
5598
  };
5598
5599
  }
5599
5600
  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);
5601
+ const path2 = opts?.path ?? REVIEW_REQUEST_PATH;
5602
+ const name = opts?.name ?? (path2 === REPO_REVIEW_PATH ? REPO_REVIEW_NAME : REVIEW_REQUEST_NAME);
5602
5603
  return {
5603
5604
  id: randomUUID4(),
5604
5605
  name,
5605
5606
  kind: "file",
5606
- path,
5607
+ path: path2,
5607
5608
  content
5608
5609
  };
5609
5610
  }
@@ -5872,7 +5873,7 @@ var Orchestrator = class {
5872
5873
  orphans: orphans.map((o) => ({ path: o.path, repoPath: o.repoPath }))
5873
5874
  });
5874
5875
  }
5875
- const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
5876
+ const { autoCleanupOrphansEnabled: autoCleanupOrphansEnabled2 } = await import("./app-settings-6IOQYGBK.js");
5876
5877
  if (autoCleanupOrphansEnabled2() && shouldRunWorktreeCleanup() && orphans.length > 0) {
5877
5878
  await cleanupOrphanWorktrees({ repoPaths });
5878
5879
  }
@@ -6029,7 +6030,7 @@ var Orchestrator = class {
6029
6030
  }
6030
6031
  async finishCreateThread(threadId, prompt) {
6031
6032
  await this.runSetupAfterCreate(threadId);
6032
- const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
6033
+ const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-6IOQYGBK.js");
6033
6034
  if (autoRunAfterSetupEnabled2()) {
6034
6035
  try {
6035
6036
  await this.startDev(threadId);
@@ -6307,14 +6308,16 @@ var Orchestrator = class {
6307
6308
  }
6308
6309
  const isBrightsy = fresh.agent === "brightsy";
6309
6310
  const isOrchestration = isOrchestratorThread(fresh);
6311
+ const { autoRenameBranchEnabled: autoRenameBranchEnabled2, getGithubGitAuthMode: getGithubGitAuthMode2 } = await import("./app-settings-6IOQYGBK.js");
6312
+ const gitAuthMode = getGithubGitAuthMode2();
6310
6313
  const worktreeDirective = isBrightsy || isOrchestration ? null : formatWorktreeDirective(fresh, {
6311
6314
  githubSlug: await resolveGithubRepoSlug(fresh.worktreePath).catch(
6312
6315
  () => null
6313
- )
6316
+ ),
6317
+ gitAuthMode
6314
6318
  });
6315
6319
  const artifactDirective = isBrightsy ? null : formatArtifactDirective();
6316
6320
  const settings = loadWorkspaceSettings(fresh.worktreePath, fresh.repoPath);
6317
- const { autoRenameBranchEnabled: autoRenameBranchEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
6318
6321
  const renameBranchDirective = !isBrightsy && !isOrchestration && autoRenameBranchEnabled2() ? formatRenameBranchDirective(fresh, {
6319
6322
  customPrompt: settings?.prompts?.renameBranch
6320
6323
  }) : null;
@@ -7020,7 +7023,7 @@ var Orchestrator = class {
7020
7023
  updateThread(thread.id, { title: meta.title });
7021
7024
  }
7022
7025
  }
7023
- const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
7026
+ const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await import("./app-settings-6IOQYGBK.js");
7024
7027
  const latest = this.requireThread(thread.id);
7025
7028
  if (!shouldAutoArchiveOnPrMerge({
7026
7029
  previousPrState: prevState || null,
@@ -7151,8 +7154,9 @@ var Orchestrator = class {
7151
7154
  return tab;
7152
7155
  }
7153
7156
  /**
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.
7157
+ * Desktop git buttons + MCP `ask_git`.
7158
+ * When the worktree is clean, push / open the PR here (HTTPS via `gh` if SSH
7159
+ * is missing). When dirty, queue the worktree agent to commit first.
7156
7160
  */
7157
7161
  async askGit(threadRef, action) {
7158
7162
  if (!AGENT_GIT_ACTIONS.includes(action)) {
@@ -7170,6 +7174,10 @@ var Orchestrator = class {
7170
7174
  "No pull request linked. Ask the worktree agent to open a draft PR first (ask_git create-draft)."
7171
7175
  );
7172
7176
  }
7177
+ if ((action === "commit-push" || action === "create-draft" || action === "create-web") && thread.worktreePath?.trim() && !await isDirty(thread.worktreePath)) {
7178
+ await this.pushAndMaybeOpenPr(thread, action);
7179
+ return this.requireThread(threadRef);
7180
+ }
7173
7181
  let prBase;
7174
7182
  if (action === "resolve-conflicts") {
7175
7183
  try {
@@ -7180,6 +7188,34 @@ var Orchestrator = class {
7180
7188
  }
7181
7189
  return this.send(threadRef, agentGitPrompt(action, { prBase }));
7182
7190
  }
7191
+ /** Push origin (gh HTTPS fallback) and create/update the PR when requested. */
7192
+ async pushAndMaybeOpenPr(thread, action) {
7193
+ const cwd = thread.worktreePath;
7194
+ const branch = await currentBranch(cwd);
7195
+ await pushBranch(cwd, branch);
7196
+ if (action === "commit-push") return;
7197
+ const base = await resolveDefaultBranch(thread.repoPath);
7198
+ const meta = await suggestPrMetadata(cwd, {
7199
+ base,
7200
+ fallbackTitle: thread.prTitle ?? thread.title
7201
+ });
7202
+ const url = await createOrUpdatePr(cwd, {
7203
+ title: meta.title,
7204
+ body: meta.body,
7205
+ base,
7206
+ head: branch,
7207
+ draft: action === "create-draft",
7208
+ web: action === "create-web"
7209
+ });
7210
+ if (!url) return;
7211
+ const patch = { prUrl: url, prTitle: meta.title };
7212
+ try {
7213
+ const fetched = await getPrMeta(cwd, url);
7214
+ if (fetched?.title) patch.prTitle = fetched.title;
7215
+ } catch {
7216
+ }
7217
+ updateThread(thread.id, patch);
7218
+ }
7183
7219
  setThreadOptions(threadRef, patch) {
7184
7220
  const thread = this.requireThread(threadRef);
7185
7221
  const next = {};
@@ -7275,7 +7311,7 @@ var Orchestrator = class {
7275
7311
  this.emit({ type: "status_changed", threadId: archived.id, status: "archived" });
7276
7312
  if (thread.repoPath && !isGlobalRepoPath(thread.repoPath)) {
7277
7313
  try {
7278
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-O3U5BENH.js");
7314
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-QLRCOXAY.js");
7279
7315
  await ensureWorkspace2(thread.repoPath);
7280
7316
  } catch {
7281
7317
  }
@@ -7297,7 +7333,7 @@ var Orchestrator = class {
7297
7333
  await runArchiveScript(thread.repoPath, thread.worktreePath);
7298
7334
  } catch {
7299
7335
  }
7300
- const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-3TLA2EJ7.js");
7336
+ const { deleteBranchOnPurgeEnabled: deleteBranchOnPurgeEnabled2 } = await import("./app-settings-6IOQYGBK.js");
7301
7337
  const deleteBranch = opts?.deleteBranch ?? deleteBranchOnPurgeEnabled2();
7302
7338
  await removeWorktree(thread.repoPath, thread.worktreePath, {
7303
7339
  deleteBranch: deleteBranch ? thread.branchName : void 0
@@ -7318,7 +7354,7 @@ var Orchestrator = class {
7318
7354
  return restored2;
7319
7355
  }
7320
7356
  if (!existsSync15(thread.worktreePath)) {
7321
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-VOCH3WS3.js");
7357
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-5VLLFI5W.js");
7322
7358
  const { execa: execa6 } = await import("execa");
7323
7359
  const slug = thread.worktreePath.split("/").pop();
7324
7360
  const dest = thread.worktreePath;
@@ -7427,7 +7463,7 @@ async function startOrchestration(opts) {
7427
7463
  sourceRef: "default",
7428
7464
  ...createOpts
7429
7465
  }).catch(async () => {
7430
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-VOCH3WS3.js");
7466
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-5VLLFI5W.js");
7431
7467
  const repo = await resolveRepoRoot2(repoPath);
7432
7468
  const def = await resolveDefaultBranch2(repo);
7433
7469
  return createThread({
@@ -7745,9 +7781,9 @@ function labelGithubUrl(url) {
7745
7781
  return null;
7746
7782
  }
7747
7783
  if (!/(^|\.)github\.com$/i.test(parsed.hostname)) return null;
7748
- const path = parsed.pathname;
7784
+ const path2 = parsed.pathname;
7749
7785
  const hash = parsed.hash || "";
7750
- const pr = path.match(/\/([^/]+)\/([^/]+)\/pull\/(\d+)/i);
7786
+ const pr = path2.match(/\/([^/]+)\/([^/]+)\/pull\/(\d+)/i);
7751
7787
  if (pr) {
7752
7788
  const n = pr[3];
7753
7789
  if (/#discussion_r\d+/i.test(hash) || /#pullrequestreview-\d+/i.test(hash)) {
@@ -7758,11 +7794,11 @@ function labelGithubUrl(url) {
7758
7794
  }
7759
7795
  return { kind: "pr", label: `PR #${n}`, url: raw };
7760
7796
  }
7761
- const issueComment = path.match(/\/([^/]+)\/([^/]+)\/issues\/(\d+)/i);
7797
+ const issueComment = path2.match(/\/([^/]+)\/([^/]+)\/issues\/(\d+)/i);
7762
7798
  if (issueComment && /#issuecomment-\d+/i.test(hash)) {
7763
7799
  return { kind: "comment", label: `Issue #${issueComment[3]} comment`, url: raw };
7764
7800
  }
7765
- const blob = path.match(/\/([^/]+)\/([^/]+)\/blob\/[^/]+\/(.+)$/i);
7801
+ const blob = path2.match(/\/([^/]+)\/([^/]+)\/blob\/[^/]+\/(.+)$/i);
7766
7802
  if (blob) {
7767
7803
  const filePath = decodeURIComponent(blob[3]);
7768
7804
  const fileName = filePath.split("/").pop() || filePath;
@@ -8179,7 +8215,7 @@ function withTimeout(promise, ms, label) {
8179
8215
  async function startMcpServer() {
8180
8216
  const orch = getOrchestrator();
8181
8217
  try {
8182
- const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-3TLA2EJ7.js");
8218
+ const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-6IOQYGBK.js");
8183
8219
  orch.setMaxConcurrent(maxConcurrentAgents2());
8184
8220
  } catch {
8185
8221
  }
@@ -8324,10 +8360,10 @@ async function startMcpServer() {
8324
8360
  const t = orch.getThread(thread_id.trim());
8325
8361
  if (t?.worktreePath?.trim()) root = t.worktreePath;
8326
8362
  }
8327
- const path = writePlanFile2(root, content);
8363
+ const path2 = writePlanFile2(root, content);
8328
8364
  const payload = {
8329
8365
  ok: true,
8330
- path,
8366
+ path: path2,
8331
8367
  title: title?.trim() || "Plan",
8332
8368
  message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
8333
8369
  };
@@ -8389,7 +8425,7 @@ async function startMcpServer() {
8389
8425
  registerLinearTools(server);
8390
8426
  if (sideboardMcpProfile() !== "worktree") {
8391
8427
  const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await import("./caffeinate-hold-EAZNWJBE.js");
8392
- const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-3TLA2EJ7.js");
8428
+ const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-6IOQYGBK.js");
8393
8429
  const accountDefaults = resolveThreadDefaults2();
8394
8430
  const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
8395
8431
  server.tool(
@@ -8547,7 +8583,7 @@ async function startMcpServer() {
8547
8583
  );
8548
8584
  server.tool(
8549
8585
  "send_to_thread",
8550
- "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.",
8586
+ '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.',
8551
8587
  {
8552
8588
  ref: z3.string(),
8553
8589
  prompt: z3.string(),
@@ -8643,7 +8679,7 @@ async function startMcpServer() {
8643
8679
  );
8644
8680
  server.tool(
8645
8681
  "archive_thread",
8646
- "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).",
8682
+ "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.",
8647
8683
  { ref: z3.string() },
8648
8684
  async ({ ref }) => {
8649
8685
  const t = orch.getThread(ref);
@@ -8751,7 +8787,7 @@ async function startMcpServer() {
8751
8787
  );
8752
8788
  server.tool(
8753
8789
  "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.",
8790
+ "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.",
8755
8791
  {
8756
8792
  ref: z3.string().describe("Worktree thread id/ref"),
8757
8793
  action: z3.enum(AGENT_GIT_ACTIONS).describe(
@@ -9052,7 +9088,7 @@ async function startMcpServer() {
9052
9088
  );
9053
9089
  server.tool(
9054
9090
  "get_pr_stack",
9055
- "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.",
9091
+ "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).",
9056
9092
  { ref: z3.string() },
9057
9093
  async ({ ref }) => {
9058
9094
  const stack = await orch.getPrStack(ref);
@@ -9219,9 +9255,9 @@ var BrightsySideboardApi = class {
9219
9255
  get endpoint() {
9220
9256
  return (this.cfg.endpoint || "https://brightsy.ai").replace(/\/$/, "");
9221
9257
  }
9222
- async request(path, init = {}) {
9258
+ async request(path2, init = {}) {
9223
9259
  await this.refreshIfNeeded();
9224
- const url = `${this.endpoint}${path}`;
9260
+ const url = `${this.endpoint}${path2}`;
9225
9261
  const headers = {
9226
9262
  Authorization: `Bearer ${this.cfg.access_token}`,
9227
9263
  Accept: "application/json"
@@ -10809,6 +10845,83 @@ var SlackRelayHub = class {
10809
10845
  // src/slack/relay-server.ts
10810
10846
  import { createServer as createServer3 } from "http";
10811
10847
  import { WebSocketServer } from "ws";
10848
+
10849
+ // src/slack/relay-static.ts
10850
+ import { createReadStream } from "fs";
10851
+ import { stat } from "fs/promises";
10852
+ import path from "path";
10853
+ var TYPES = {
10854
+ ".css": "text/css; charset=utf-8",
10855
+ ".html": "text/html; charset=utf-8",
10856
+ ".ico": "image/x-icon",
10857
+ ".jpeg": "image/jpeg",
10858
+ ".jpg": "image/jpeg",
10859
+ ".js": "text/javascript; charset=utf-8",
10860
+ ".json": "application/json; charset=utf-8",
10861
+ ".png": "image/png",
10862
+ ".svg": "image/svg+xml",
10863
+ ".txt": "text/plain; charset=utf-8",
10864
+ ".webp": "image/webp",
10865
+ ".woff2": "font/woff2"
10866
+ };
10867
+ function requestHostname(req) {
10868
+ const raw = req.headers.host ?? "";
10869
+ return raw.split(":")[0]?.toLowerCase() ?? "";
10870
+ }
10871
+ function hostnameAllowed(host, allowed) {
10872
+ if (!allowed || allowed.length === 0) return true;
10873
+ return allowed.includes(host);
10874
+ }
10875
+ function resolveStaticPath(root, requestUrl) {
10876
+ let pathname;
10877
+ try {
10878
+ pathname = decodeURIComponent(new URL(requestUrl, "http://relay.local").pathname);
10879
+ } catch {
10880
+ return null;
10881
+ }
10882
+ if (!pathname.startsWith("/") || pathname.includes("\0")) return null;
10883
+ const rootResolved = path.resolve(root);
10884
+ const candidate = path.resolve(rootResolved, `.${pathname}`);
10885
+ if (candidate !== rootResolved && !candidate.startsWith(rootResolved + path.sep)) {
10886
+ return null;
10887
+ }
10888
+ return candidate;
10889
+ }
10890
+ async function fileSize(file) {
10891
+ try {
10892
+ const st = await stat(file);
10893
+ return st.isFile() ? st.size : null;
10894
+ } catch {
10895
+ return null;
10896
+ }
10897
+ }
10898
+ function sendFile(req, res, file, size) {
10899
+ const ext = path.extname(file).toLowerCase();
10900
+ res.writeHead(200, {
10901
+ "Content-Type": TYPES[ext] ?? "application/octet-stream",
10902
+ "Content-Length": size,
10903
+ "Cache-Control": ext === ".html" ? "no-cache" : "public, max-age=3600"
10904
+ });
10905
+ if (req.method === "HEAD") {
10906
+ res.end();
10907
+ return true;
10908
+ }
10909
+ createReadStream(file).pipe(res);
10910
+ return true;
10911
+ }
10912
+ async function tryServeStatic(req, res, root) {
10913
+ if (req.method !== "GET" && req.method !== "HEAD") return false;
10914
+ const candidate = resolveStaticPath(root, req.url || "/");
10915
+ if (!candidate) return false;
10916
+ const direct = await fileSize(candidate);
10917
+ if (direct != null) return sendFile(req, res, candidate, direct);
10918
+ const asIndex = path.join(candidate, "index.html");
10919
+ const indexSize = await fileSize(asIndex);
10920
+ if (indexSize != null) return sendFile(req, res, asIndex, indexSize);
10921
+ return false;
10922
+ }
10923
+
10924
+ // src/slack/relay-server.ts
10812
10925
  function sendHtml(res, status, title, body) {
10813
10926
  res.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
10814
10927
  res.end(slackOAuthHtmlPage(title, body));
@@ -10907,7 +11020,26 @@ async function startSlackRelayServer(opts) {
10907
11020
  const reqUrl = req.url || "/";
10908
11021
  if (await handleCallback(reqUrl, res)) return;
10909
11022
  if (handleResult(reqUrl, res)) return;
10910
- if (req.url === "/health" || req.url === "/") {
11023
+ if (req.url === "/health") {
11024
+ sendJson(res, 200, {
11025
+ ok: true,
11026
+ service: "sideboard-slack-relay",
11027
+ sessions: hub.listSessions().length,
11028
+ oauth: Boolean(clientSecret)
11029
+ });
11030
+ return;
11031
+ }
11032
+ const hostname = requestHostname(req);
11033
+ const canonical = (opts.canonicalSiteHost ?? "www.sideboard.cloud").toLowerCase();
11034
+ if (opts.redirectHosts?.includes(hostname) && hostname !== canonical) {
11035
+ res.writeHead(301, { Location: `https://${canonical}${reqUrl}` });
11036
+ res.end();
11037
+ return;
11038
+ }
11039
+ if (opts.staticRoot && hostnameAllowed(hostname, opts.staticHosts) && await tryServeStatic(req, res, opts.staticRoot)) {
11040
+ return;
11041
+ }
11042
+ if (req.url === "/") {
10911
11043
  sendJson(res, 200, {
10912
11044
  ok: true,
10913
11045
  service: "sideboard-slack-relay",
@@ -11016,6 +11148,7 @@ export {
11016
11148
  CONTEXT_MIN_MESSAGES,
11017
11149
  COORDINATOR_TOOL_PLAYBOOK,
11018
11150
  FAMOUS_SOCCER_TEAMS,
11151
+ GITHUB_GIT_AUTH_MODES,
11019
11152
  GLOBAL_WORKSPACE_ID,
11020
11153
  HARNESS_ENV_KEYS,
11021
11154
  LEGACY_ATTACHMENTS_DIR,
@@ -11065,10 +11198,12 @@ export {
11065
11198
  allocateTeamSlug,
11066
11199
  appDataDir,
11067
11200
  appSettingsPath,
11201
+ appendIndexedGitConfig,
11068
11202
  appendMessage,
11069
11203
  applyAgentEvent,
11070
11204
  applyAppEnvironment,
11071
11205
  applyCompaction,
11206
+ applyGithubGitAuthEnv,
11072
11207
  applyThreadIntoMain,
11073
11208
  applyTurnUsage,
11074
11209
  assertOrchestratorCapableAgent,
@@ -11183,6 +11318,7 @@ export {
11183
11318
  formatBrightsyFetchError,
11184
11319
  formatFetchError,
11185
11320
  formatGhLandError,
11321
+ formatGitAuthModeDirective,
11186
11322
  formatIpcInvokeError,
11187
11323
  formatMessagesAsTranscript,
11188
11324
  formatPlanQuestionAnswers,
@@ -11197,6 +11333,7 @@ export {
11197
11333
  formatWorkspaceInventory,
11198
11334
  formatWorktreeDirective,
11199
11335
  formatWorktreeReminder,
11336
+ fromInclusiveInputUsage,
11200
11337
  getAdapter,
11201
11338
  getAgentSetupInfo,
11202
11339
  getBrightsySession,
@@ -11209,6 +11346,8 @@ export {
11209
11346
  getDiff,
11210
11347
  getDiffSummary,
11211
11348
  getGitHubStatus,
11349
+ getGithubGitAuthMode,
11350
+ getGithubPat,
11212
11351
  getIssueSource,
11213
11352
  getLinearApiKey,
11214
11353
  getLinearAuthToken,
@@ -11227,6 +11366,7 @@ export {
11227
11366
  ghHeadRef,
11228
11367
  ghRepoSelectArgs,
11229
11368
  git,
11369
+ githubAgentGitEnv,
11230
11370
  globalAgentCwd,
11231
11371
  handleSlackInbound,
11232
11372
  harnessEnvKey,
@@ -11449,6 +11589,7 @@ export {
11449
11589
  startSlackOAuth,
11450
11590
  startSlackRelayServer,
11451
11591
  stripBrightsyNdjsonNoise,
11592
+ stripNestedElectronEnv,
11452
11593
  submitPrStack,
11453
11594
  suggestSlug,
11454
11595
  summarizeConversation,