@sideboard-ai/core 0.1.51 → 0.1.53

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 (40) hide show
  1. package/dist/agents/cursor-runner.cjs +43 -4
  2. package/dist/agents/cursor-runner.js +45 -6
  3. package/dist/{agents-HIEJL3UV.js → agents-KP7UJEHJ.js} +1 -1
  4. package/dist/{agents-5ROTZNCX.js → agents-KYACODJ3.js} +2 -2
  5. package/dist/{chunk-5ZPSH7VI.js → chunk-A6HVEMIB.js} +16 -7
  6. package/dist/chunk-B3SJXYIJ.js +24 -0
  7. package/dist/{chunk-7EUWSBWR.js → chunk-BZST4HMJ.js} +21 -6
  8. package/dist/{chunk-ZH5QZ4CR.js → chunk-DZFH2KLT.js} +280 -7
  9. package/dist/chunk-FKOIHGKV.js +21 -0
  10. package/dist/{chunk-E4VVEKAM.js → chunk-GNML24AW.js} +2 -2
  11. package/dist/{chunk-EOYDCKQC.js → chunk-HLEX5AQ6.js} +4 -0
  12. package/dist/{chunk-K3WMKGFY.js → chunk-LRLKJM3O.js} +2 -1
  13. package/dist/chunk-N5PM7HGQ.js +103 -0
  14. package/dist/chunk-QTUESPAW.js +101 -0
  15. package/dist/{chunk-O6W3P7V3.js → chunk-TSRXOSVD.js} +4 -0
  16. package/dist/{chunk-F4Q3IM6V.js → chunk-UEAHMGHW.js} +2 -1
  17. package/dist/{chunk-J5JTEJ5O.js → chunk-VG22SETP.js} +6 -0
  18. package/dist/{chunk-XRSAGVRW.js → chunk-XOU6HNQJ.js} +245 -7
  19. package/dist/{chunk-O5DOO7DP.js → chunk-XX5BB7NV.js} +3 -3
  20. package/dist/{chunk-QN7XNQAT.js → chunk-YDXQ72MD.js} +2 -2
  21. package/dist/{chunk-YFJ4FG2P.js → chunk-YOWIYAVA.js} +3 -3
  22. package/dist/{coordinator-prompt-7HHJRO7B.js → coordinator-prompt-6FXVTSFN.js} +4 -3
  23. package/dist/{coordinator-prompt-WD7FAMA2.js → coordinator-prompt-S6JZD5EF.js} +4 -3
  24. package/dist/{global-workspace-OJEPGDXA.js → global-workspace-EV4G2WMQ.js} +5 -4
  25. package/dist/{global-workspace-ECYN2MKL.js → global-workspace-MSX2K27Y.js} +5 -4
  26. package/dist/index.cjs +1383 -149
  27. package/dist/index.d.cts +389 -15
  28. package/dist/index.d.ts +389 -15
  29. package/dist/index.js +883 -91
  30. package/dist/mcp/run-stdio.cjs +1154 -141
  31. package/dist/mcp/run-stdio.js +709 -79
  32. package/dist/plan-file-6O7G4VPQ.js +23 -0
  33. package/dist/plan-file-PHVKUAEE.js +25 -0
  34. package/dist/{thread-store-XICUWFNM.js → thread-store-GHOADGL2.js} +1 -1
  35. package/dist/{thread-store-OV2X6PYO.js → thread-store-UJIGMI5J.js} +1 -1
  36. package/dist/{workspaces-MUU7RGVV.js → workspaces-3RQQZQRO.js} +6 -5
  37. package/dist/{workspaces-ZWOOFZUV.js → workspaces-AYTBR6KQ.js} +6 -5
  38. package/dist/{worktree-DVNDMWZ7.js → worktree-5KEQWSAF.js} +5 -2
  39. package/dist/{worktree-GDV56MX4.js → worktree-RWGL7FUV.js} +5 -2
  40. 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-E4VVEKAM.js";
7
+ } from "./chunk-GNML24AW.js";
8
8
  import {
9
9
  CLOUD_COORDINATOR_BUSY_REPLY,
10
10
  CLOUD_COORDINATOR_STOPPED_REPLY,
@@ -24,7 +24,7 @@ import {
24
24
  orchestratorSessionPoisonedByBuiltins,
25
25
  parseForceStopMessage,
26
26
  takenTeamSlugsForOrchestration
27
- } from "./chunk-O5DOO7DP.js";
27
+ } from "./chunk-XX5BB7NV.js";
28
28
  import {
29
29
  BRIGHTSY_MCP_ALLOWED_TOOLS,
30
30
  CLAUDE_MODEL_CATALOG,
@@ -70,7 +70,7 @@ import {
70
70
  resolveQuotaFallbackAgent,
71
71
  sanitizeMcpServerName,
72
72
  writeInjectedMcpConfig
73
- } from "./chunk-5ZPSH7VI.js";
73
+ } from "./chunk-A6HVEMIB.js";
74
74
  import {
75
75
  ORCHESTRATOR_AGENT_KINDS,
76
76
  assertOrchestratorCapableAgent,
@@ -89,16 +89,28 @@ import {
89
89
  saveBrightsyConfig,
90
90
  switchBrightsyAccount
91
91
  } from "./chunk-BXJ76RHF.js";
92
+ import {
93
+ LEGACY_PLAN_FILE_REL,
94
+ PLAN_FILE_NAME,
95
+ PLAN_FILE_REL,
96
+ extractPresentedPlan,
97
+ isPresentPlanToolName,
98
+ planFileAbs,
99
+ readPlanFile,
100
+ resolvePlanMarkdown,
101
+ writePlanFile
102
+ } from "./chunk-QTUESPAW.js";
92
103
  import {
93
104
  cursorSdkMessageToEvents,
94
105
  fallbackTurnFailDetail,
95
106
  formatTurnExitError,
96
107
  humanizeAgentFailDetail,
97
108
  looksLikeAgentFailureMessage,
109
+ looksLikeInvalidAgentSession,
98
110
  parseCursorRunnerLine,
99
111
  pushTurnStderr,
100
112
  summarizeTurnStderr
101
- } from "./chunk-J5JTEJ5O.js";
113
+ } from "./chunk-VG22SETP.js";
102
114
  import {
103
115
  HARNESS_ENV_KEYS,
104
116
  appSettingsPath,
@@ -144,17 +156,21 @@ import {
144
156
  enrichWorkspacesWithGithub,
145
157
  ensureGlobalCoordinatorCwd,
146
158
  formatWorkspaceInventory
147
- } from "./chunk-K3WMKGFY.js";
159
+ } from "./chunk-LRLKJM3O.js";
148
160
  import {
149
161
  FAMOUS_SOCCER_TEAMS,
162
+ addPrStackLayer,
150
163
  allocateTeamName,
151
164
  allocateTeamSlug,
152
165
  branchDisplayLabel,
166
+ checkoutPrStackLayer,
153
167
  collectTakenTeamSlugs,
154
168
  commitAll,
169
+ createExistingBranchWorktree,
155
170
  createOrUpdatePr,
156
171
  createThreadWorktree,
157
172
  currentBranch,
173
+ detectGhStack,
158
174
  detectLocalMergeConflicts,
159
175
  ensureGhPreferOrigin,
160
176
  extractGhErrorDetail,
@@ -166,10 +182,13 @@ import {
166
182
  getPrChecks,
167
183
  getPrDetails,
168
184
  getPrMeta,
185
+ getPrStack,
169
186
  ghHeadRef,
170
187
  ghRepoSelectArgs,
188
+ initPrStack,
171
189
  isDirty,
172
190
  isGhRateLimitError,
191
+ isInPrStack,
173
192
  isPlaceholderBranch,
174
193
  isSideboardScratchPath,
175
194
  listBranches,
@@ -177,11 +196,14 @@ import {
177
196
  listWorktrees,
178
197
  lookupSoccerTeam,
179
198
  mergePr,
199
+ mergePrStack,
180
200
  normalizeWorktreePath,
181
201
  originGhRepoEnv,
202
+ parseGhStackViewJson,
182
203
  parseGithubSlugFromRemoteUrl,
183
204
  pushBranch,
184
205
  removeWorktree,
206
+ resetGhStackDetectCache,
185
207
  resolveDefaultBranch,
186
208
  resolveDiffBaseRef,
187
209
  resolveGithubRepoSlug,
@@ -189,13 +211,21 @@ import {
189
211
  resolveRepoRoot,
190
212
  resolveWorktreeStartPoint,
191
213
  slugify,
214
+ stackMergeReadiness,
215
+ submitPrStack,
192
216
  suggestSlug,
193
217
  takenSlugsFromThread,
194
218
  threadDisplayLabel,
195
219
  worktreeDisplayLabel,
196
220
  worktreeDisplayLabelForGroup,
197
221
  worktreeNameFromPath
198
- } from "./chunk-ZH5QZ4CR.js";
222
+ } from "./chunk-DZFH2KLT.js";
223
+ import {
224
+ ATTACHMENTS_DIR,
225
+ LEGACY_ATTACHMENTS_DIR,
226
+ attachmentsGitignoreBody,
227
+ isWorkspaceScratchPath
228
+ } from "./chunk-FKOIHGKV.js";
199
229
  import {
200
230
  appendMessage,
201
231
  createEmptyThread,
@@ -209,7 +239,7 @@ import {
209
239
  updateThread,
210
240
  withThreadLock,
211
241
  writeThread
212
- } from "./chunk-O6W3P7V3.js";
242
+ } from "./chunk-TSRXOSVD.js";
213
243
  import {
214
244
  THINKING_EFFORTS,
215
245
  isThinkingEffort,
@@ -458,6 +488,9 @@ function toolDescription(name, input) {
458
488
  if (/present_artifact$/i.test(name)) {
459
489
  return str(input?.title) ? `Present ${str(input?.title)}` : "Present artifact";
460
490
  }
491
+ if (/present_plan$/i.test(name)) {
492
+ return str(input?.title) ? `Plan ${str(input?.title)}` : "Present plan";
493
+ }
461
494
  if (/present_schema$/i.test(name)) {
462
495
  return str(input?.title) ? `Schema ${str(input?.title)}` : "Present schema";
463
496
  }
@@ -657,9 +690,9 @@ async function spawnAgentTurn(thread, input, onEvent) {
657
690
  `Cannot spawn ${thread.agent}: thread ${thread.id} has no worktreePath`
658
691
  );
659
692
  }
660
- const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-ECYN2MKL.js");
693
+ const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-MSX2K27Y.js");
661
694
  if (isGlobalThread2(thread)) {
662
- const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-WD7FAMA2.js");
695
+ const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-S6JZD5EF.js");
663
696
  ensureGlobalCoordinatorCwd2();
664
697
  }
665
698
  if (isOrchestratorThread(thread)) {
@@ -2181,11 +2214,6 @@ var IMAGE_MIME_BY_EXT = {
2181
2214
  bmp: "image/bmp",
2182
2215
  ico: "image/x-icon"
2183
2216
  };
2184
- var ATTACHMENTS_DIR = ".sideboard/attachments";
2185
- var ATTACHMENTS_GITIGNORE = `# Sideboard review / composer attachments (local only)
2186
- *
2187
- !.gitignore
2188
- `;
2189
2217
  var MAX_INLINE_BYTES = 4e5;
2190
2218
  var MAX_PREVIEW_BYTES = 5e6;
2191
2219
  function fileExtension(filePath) {
@@ -2203,7 +2231,7 @@ function ensureAttachmentsDir(worktreePath) {
2203
2231
  mkdirSync3(dir, { recursive: true });
2204
2232
  const gi = join6(dir, ".gitignore");
2205
2233
  if (!existsSync6(gi)) {
2206
- writeFileSync2(gi, ATTACHMENTS_GITIGNORE, "utf8");
2234
+ writeFileSync2(gi, attachmentsGitignoreBody(), "utf8");
2207
2235
  }
2208
2236
  return dir;
2209
2237
  }
@@ -2853,7 +2881,7 @@ async function confirmLand(thread, opts) {
2853
2881
 
2854
2882
  // src/threads/create.ts
2855
2883
  import { existsSync as existsSync7 } from "fs";
2856
- async function createThread(input, onSetupLine) {
2884
+ async function createThread(input, _onSetupLine) {
2857
2885
  await requireAgent(input.agent);
2858
2886
  const repoPath = await resolveRepoRoot(input.repoPath);
2859
2887
  if (!existsSync7(repoPath)) {
@@ -2911,26 +2939,10 @@ async function createThread(input, onSetupLine) {
2911
2939
  });
2912
2940
  writeThread(thread);
2913
2941
  await ensureWorkspace(repoPath);
2914
- try {
2915
- let setup = await runSetupScript(repoPath, worktreePath, onSetupLine);
2916
- if (!setup.ran) {
2917
- setup = await runCursorWorktreeSetup(repoPath, worktreePath, onSetupLine);
2918
- }
2919
- if (setup.ran && setup.exitCode !== 0 && setup.exitCode !== null) {
2920
- updateThread(thread.id, {
2921
- lastError: `Setup exited ${setup.exitCode} (thread is still usable)`
2922
- });
2923
- }
2924
- } catch (err) {
2925
- const message = err instanceof Error ? err.message : String(err);
2926
- updateThread(thread.id, {
2927
- lastError: `Setup failed: ${message}`
2928
- });
2929
- }
2930
2942
  return readThread(thread.id) ?? thread;
2931
2943
  }
2932
2944
  async function listLinearIssues(agent, repoPath) {
2933
- const { getAdapter: getAdapter2 } = await import("./agents-5ROTZNCX.js");
2945
+ const { getAdapter: getAdapter2 } = await import("./agents-KYACODJ3.js");
2934
2946
  await requireAgent(agent, { requireLinear: true });
2935
2947
  const adapter = getAdapter2(agent);
2936
2948
  if (!adapter.listLinearIssues) {
@@ -2973,7 +2985,9 @@ function worktreeBindingFrom(from) {
2973
2985
  sourceIsFork: from.sourceIsFork,
2974
2986
  parentThreadId: from.parentThreadId,
2975
2987
  prUrl: from.prUrl,
2976
- prTitle: from.prTitle
2988
+ prTitle: from.prTitle,
2989
+ stackId: from.stackId,
2990
+ stackLayer: from.stackLayer
2977
2991
  };
2978
2992
  }
2979
2993
  function threadsSharingWorktree(worktreePath) {
@@ -3086,11 +3100,305 @@ async function forkThreadWorktree(input, onSetupLine) {
3086
3100
  return thread;
3087
3101
  }
3088
3102
 
3103
+ // src/threads/stack-layers.ts
3104
+ import { existsSync as existsSync8 } from "fs";
3105
+ function stackIdFrom(stack) {
3106
+ if (stack.stackNumber != null) return `gh-stack-${stack.stackNumber}`;
3107
+ const key = stack.layers.map((l) => l.branchName).join("|");
3108
+ if (!key) return null;
3109
+ return `gh-stack-local-${hashShort(key)}`;
3110
+ }
3111
+ function hashShort(s) {
3112
+ let h = 0;
3113
+ for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
3114
+ return Math.abs(h).toString(36);
3115
+ }
3116
+ function requireThread3(idOrRef) {
3117
+ const thread = findThreadByRef(idOrRef) ?? readThread(idOrRef);
3118
+ if (!thread) throw new Error(`Thread not found: ${idOrRef}`);
3119
+ return thread;
3120
+ }
3121
+ function sanitizeSlugPart(name) {
3122
+ return name.trim().replace(/^refs\/heads\//, "").replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "layer";
3123
+ }
3124
+ function layerSlug(stackId, layer) {
3125
+ const stackPart = stackId.replace(/^gh-stack-/, "s");
3126
+ return sanitizeSlugPart(`${stackPart}-L${layer.position}-${layer.branchName}`);
3127
+ }
3128
+ function findThreadForStackLayer(repoPath, stackId, layer) {
3129
+ const threads = listThreads({ includeArchived: false }).filter(
3130
+ (t) => t.repoPath === repoPath && t.stackId === stackId
3131
+ );
3132
+ const byLayer = threads.find((t) => t.stackLayer === layer.position);
3133
+ if (byLayer) return byLayer;
3134
+ return threads.find((t) => t.branchName === layer.branchName) ?? listThreads({ includeArchived: false }).find(
3135
+ (t) => t.repoPath === repoPath && t.branchName === layer.branchName
3136
+ ) ?? null;
3137
+ }
3138
+ async function openStackLayer(input, _onSetupLine) {
3139
+ await requireAgent(input.agent);
3140
+ const repoPath = await resolveRepoRoot(input.repoPath);
3141
+ const stackId = stackIdFrom(input.stack);
3142
+ if (!stackId) throw new Error("Cannot open stack layer without a stack id");
3143
+ const existing = findThreadForStackLayer(repoPath, stackId, input.layer);
3144
+ if (existing) {
3145
+ const patch = {};
3146
+ if (existing.stackId !== stackId) patch.stackId = stackId;
3147
+ if (existing.stackLayer !== input.layer.position) {
3148
+ patch.stackLayer = input.layer.position;
3149
+ }
3150
+ if (input.layer.prUrl && existing.prUrl !== input.layer.prUrl) {
3151
+ patch.prUrl = input.layer.prUrl;
3152
+ }
3153
+ if (input.layer.title && existing.prTitle !== input.layer.title) {
3154
+ patch.prTitle = input.layer.title;
3155
+ }
3156
+ if (Object.keys(patch).length > 0) updateThread(existing.id, patch);
3157
+ return {
3158
+ thread: readThread(existing.id) ?? existing,
3159
+ createdWorktree: false
3160
+ };
3161
+ }
3162
+ let worktreePath = null;
3163
+ let branchName = input.layer.branchName;
3164
+ let createdWorktree = false;
3165
+ const trees = await listWorktrees(repoPath);
3166
+ const checkedOut = trees.find((w) => w.branch === branchName);
3167
+ if (checkedOut?.path && existsSync8(checkedOut.path)) {
3168
+ if (input.reuseExistingWorktree !== false) {
3169
+ worktreePath = checkedOut.path;
3170
+ } else {
3171
+ throw new Error(
3172
+ `Branch ${branchName} is already checked out at ${checkedOut.path}`
3173
+ );
3174
+ }
3175
+ }
3176
+ if (!worktreePath) {
3177
+ const slug = layerSlug(stackId, input.layer);
3178
+ const created = await createExistingBranchWorktree({
3179
+ repoPath,
3180
+ branchName,
3181
+ slug
3182
+ });
3183
+ worktreePath = created.worktreePath;
3184
+ branchName = created.branchName;
3185
+ copyConfiguredFiles(repoPath, worktreePath);
3186
+ createdWorktree = true;
3187
+ }
3188
+ const title = input.layer.title?.trim() || (input.layer.prNumber != null ? `PR #${input.layer.prNumber}` : input.layer.branchName);
3189
+ const thread = createEmptyThread({
3190
+ title,
3191
+ userSetTitle: Boolean(input.layer.title?.trim()),
3192
+ sourceType: input.layer.prNumber != null ? "pr" : "branch",
3193
+ sourceRef: input.layer.prNumber != null ? String(input.layer.prNumber) : input.layer.branchName,
3194
+ branchName,
3195
+ worktreePath,
3196
+ repoPath,
3197
+ agent: input.agent,
3198
+ autonomy: input.autonomy ?? "default",
3199
+ model: input.model ?? null,
3200
+ effort: input.effort ?? "high",
3201
+ fast: Boolean(input.fast),
3202
+ planMode: Boolean(input.planMode),
3203
+ parentThreadId: input.parentThreadId ?? null,
3204
+ status: "idle",
3205
+ prUrl: input.layer.prUrl,
3206
+ prTitle: input.layer.title ?? null,
3207
+ stackId,
3208
+ stackLayer: input.layer.position
3209
+ });
3210
+ writeThread(thread);
3211
+ await ensureWorkspace(repoPath);
3212
+ return { thread: readThread(thread.id) ?? thread, createdWorktree };
3213
+ }
3214
+ async function openPrStackLayers(input, onSetupLine) {
3215
+ const from = requireThread3(input.threadRef);
3216
+ if (!from.worktreePath?.trim() || !from.repoPath?.trim()) {
3217
+ throw new Error("Thread has no worktree");
3218
+ }
3219
+ const stack = await getPrStack(from.worktreePath);
3220
+ if (!stack) throw new Error("Current branch is not part of a GitHub PR stack");
3221
+ const layers = input.layer != null ? stack.layers.filter((l) => l.position === input.layer) : stack.layers;
3222
+ if (!layers.length) {
3223
+ throw new Error(
3224
+ input.layer != null ? `No stack layer at position ${input.layer}` : "Stack has no layers"
3225
+ );
3226
+ }
3227
+ const threads = [];
3228
+ const createdThreadIds = [];
3229
+ for (const layer of layers) {
3230
+ const { thread, createdWorktree } = await openStackLayer(
3231
+ {
3232
+ repoPath: from.repoPath,
3233
+ stack,
3234
+ layer,
3235
+ agent: from.agent,
3236
+ autonomy: from.autonomy,
3237
+ model: from.model,
3238
+ effort: from.effort,
3239
+ fast: from.fast,
3240
+ planMode: from.planMode,
3241
+ parentThreadId: from.id
3242
+ },
3243
+ onSetupLine
3244
+ );
3245
+ threads.push(thread);
3246
+ if (createdWorktree) createdThreadIds.push(thread.id);
3247
+ }
3248
+ const stackId = stackIdFrom(stack);
3249
+ const current = stack.layers[stack.currentIndex];
3250
+ if (stackId) {
3251
+ updateThread(from.id, {
3252
+ stackId,
3253
+ stackLayer: current?.position ?? from.stackLayer
3254
+ });
3255
+ }
3256
+ return { stack, threads, createdThreadIds };
3257
+ }
3258
+ async function addStackLayerFromThread(input, onSetupLine) {
3259
+ const from = requireThread3(input.threadRef);
3260
+ if (!from.worktreePath?.trim() || !from.repoPath?.trim()) {
3261
+ throw new Error("Thread has no worktree");
3262
+ }
3263
+ const status = await detectGhStack(from.worktreePath);
3264
+ if (!status.available) throw new Error(status.reason);
3265
+ await addPrStackLayer(from.worktreePath, input.branchName);
3266
+ const stack = await getPrStack(from.worktreePath);
3267
+ if (!stack) throw new Error("Stack not found after adding layer");
3268
+ const layer = stack.layers.find((l) => l.branchName === input.branchName.trim()) ?? stack.layers[stack.layers.length - 1];
3269
+ if (!layer) throw new Error("New stack layer not found");
3270
+ if (input.title?.trim()) {
3271
+ layer.title = input.title.trim();
3272
+ }
3273
+ const thread = await openStackLayer(
3274
+ {
3275
+ repoPath: from.repoPath,
3276
+ stack,
3277
+ layer,
3278
+ agent: from.agent,
3279
+ autonomy: from.autonomy,
3280
+ model: from.model,
3281
+ effort: from.effort,
3282
+ fast: from.fast,
3283
+ planMode: from.planMode,
3284
+ parentThreadId: from.id
3285
+ },
3286
+ onSetupLine
3287
+ );
3288
+ return { stack, thread: thread.thread, createdWorktree: thread.createdWorktree };
3289
+ }
3290
+ async function initStackFromThread(input, onSetupLine) {
3291
+ const from = requireThread3(input.threadRef);
3292
+ if (!from.worktreePath?.trim() || !from.branchName?.trim() || !from.repoPath?.trim()) {
3293
+ throw new Error("Thread has no worktree/branch");
3294
+ }
3295
+ const status = await detectGhStack(from.worktreePath);
3296
+ if (!status.available) throw new Error(status.reason);
3297
+ const branches = [
3298
+ from.branchName,
3299
+ ...(input.additionalBranches ?? []).map((b) => b.trim()).filter(Boolean)
3300
+ ];
3301
+ await initPrStack(from.worktreePath, branches, {
3302
+ base: input.base ?? await resolveDefaultBranch(from.repoPath)
3303
+ });
3304
+ return openPrStackLayers({ threadRef: from.id }, onSetupLine);
3305
+ }
3306
+ async function createPrStack(input, onSetupLine) {
3307
+ await requireAgent(input.agent);
3308
+ const repoPath = await resolveRepoRoot(input.repoPath);
3309
+ if (!existsSync8(repoPath)) throw new Error(`Repo not found: ${repoPath}`);
3310
+ if (!input.branches.length) throw new Error("At least one branch name required");
3311
+ const status = await detectGhStack(repoPath);
3312
+ if (!status.available) throw new Error(status.reason);
3313
+ const team = allocateTeamSlug(repoPath);
3314
+ const base = input.base ?? await resolveDefaultBranch(repoPath);
3315
+ const bootstrap = await createThreadWorktree({
3316
+ repoPath,
3317
+ sourceRef: base,
3318
+ slug: `${team.slug}-stack-init`
3319
+ });
3320
+ copyConfiguredFiles(repoPath, bootstrap.worktreePath);
3321
+ try {
3322
+ await initPrStack(bootstrap.worktreePath, input.branches, { base });
3323
+ const bottom = input.branches[0];
3324
+ const co = await git(["checkout", bottom], bootstrap.worktreePath, {
3325
+ reject: false
3326
+ });
3327
+ if (co.exitCode !== 0) {
3328
+ throw new Error(
3329
+ co.stderr.trim() || co.stdout.trim() || `Could not check out ${bottom} after stack init`
3330
+ );
3331
+ }
3332
+ } catch (err) {
3333
+ try {
3334
+ await removeWorktree(repoPath, bootstrap.worktreePath, {
3335
+ deleteBranch: bootstrap.branchName
3336
+ });
3337
+ } catch {
3338
+ }
3339
+ throw err;
3340
+ }
3341
+ const stack = await getPrStack(bootstrap.worktreePath);
3342
+ if (!stack) {
3343
+ try {
3344
+ await removeWorktree(repoPath, bootstrap.worktreePath, {
3345
+ deleteBranch: bootstrap.branchName
3346
+ });
3347
+ } catch {
3348
+ }
3349
+ throw new Error("Stack init succeeded but gh stack view returned no stack");
3350
+ }
3351
+ const threads = [];
3352
+ const createdThreadIds = [];
3353
+ for (const layer of stack.layers) {
3354
+ const title = layer.position === 1 && input.title?.trim() ? input.title.trim() : layer.title;
3355
+ const opened = await openStackLayer(
3356
+ {
3357
+ repoPath,
3358
+ stack,
3359
+ layer: title ? { ...layer, title } : layer,
3360
+ agent: input.agent,
3361
+ autonomy: input.autonomy,
3362
+ model: input.model,
3363
+ effort: input.effort,
3364
+ fast: input.fast,
3365
+ planMode: input.planMode,
3366
+ reuseExistingWorktree: true
3367
+ },
3368
+ onSetupLine
3369
+ );
3370
+ threads.push(opened.thread);
3371
+ if (opened.createdWorktree || opened.thread.worktreePath === bootstrap.worktreePath) {
3372
+ createdThreadIds.push(opened.thread.id);
3373
+ }
3374
+ }
3375
+ const claimed = new Set(threads.map((t) => t.worktreePath));
3376
+ if (!claimed.has(bootstrap.worktreePath) && existsSync8(bootstrap.worktreePath)) {
3377
+ try {
3378
+ await removeWorktree(repoPath, bootstrap.worktreePath, {
3379
+ deleteBranch: bootstrap.branchName
3380
+ });
3381
+ } catch {
3382
+ }
3383
+ }
3384
+ return { stack, threads, createdThreadIds };
3385
+ }
3386
+ function stackAgentDefaultsFrom(input) {
3387
+ return {
3388
+ agent: input.agent,
3389
+ autonomy: input.autonomy,
3390
+ model: input.model,
3391
+ effort: input.effort,
3392
+ fast: input.fast,
3393
+ planMode: input.planMode
3394
+ };
3395
+ }
3396
+
3089
3397
  // src/threads/adopt.ts
3090
3398
  import { execFileSync } from "child_process";
3091
3399
  import {
3092
3400
  copyFileSync as copyFileSync3,
3093
- existsSync as existsSync8,
3401
+ existsSync as existsSync9,
3094
3402
  mkdtempSync,
3095
3403
  readdirSync as readdirSync3,
3096
3404
  readFileSync as readFileSync7,
@@ -3118,7 +3426,7 @@ function mapAgentType(raw) {
3118
3426
  return null;
3119
3427
  }
3120
3428
  function resolveConductorCursorAgentId(workspacePath) {
3121
- if (!workspacePath || !existsSync8(CURSOR_SDK_STORE)) return null;
3429
+ if (!workspacePath || !existsSync9(CURSOR_SDK_STORE)) return null;
3122
3430
  const normalized = workspacePath.replace(/\/$/, "");
3123
3431
  let best = null;
3124
3432
  let hashes;
@@ -3129,7 +3437,7 @@ function resolveConductorCursorAgentId(workspacePath) {
3129
3437
  }
3130
3438
  for (const hash of hashes) {
3131
3439
  const agentsFile = join7(CURSOR_SDK_STORE, hash, "agents.ndjson");
3132
- if (!existsSync8(agentsFile)) continue;
3440
+ if (!existsSync9(agentsFile)) continue;
3133
3441
  let text;
3134
3442
  try {
3135
3443
  text = readFileSync7(agentsFile, "utf8");
@@ -3156,7 +3464,7 @@ function resolveConductorCursorAgentId(workspacePath) {
3156
3464
  return best?.agentId ?? null;
3157
3465
  }
3158
3466
  async function adoptThread(input) {
3159
- if (!existsSync8(input.worktreePath)) {
3467
+ if (!existsSync9(input.worktreePath)) {
3160
3468
  throw new Error(`Worktree not found: ${input.worktreePath}`);
3161
3469
  }
3162
3470
  const repoPath = await resolveRepoRoot(input.worktreePath);
@@ -3175,7 +3483,7 @@ async function adoptThread(input) {
3175
3483
  messages: input.messages ?? []
3176
3484
  });
3177
3485
  writeThread(thread);
3178
- const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-ZWOOFZUV.js");
3486
+ const { ensureWorkspace: ensureWorkspace2 } = await import("./workspaces-AYTBR6KQ.js");
3179
3487
  await ensureWorkspace2(repoPath);
3180
3488
  return thread;
3181
3489
  }
@@ -3183,7 +3491,7 @@ function conductorDbPath() {
3183
3491
  return CONDUCTOR_DB;
3184
3492
  }
3185
3493
  function listConductorWorkspaces() {
3186
- if (!existsSync8(CONDUCTOR_DB)) {
3494
+ if (!existsSync9(CONDUCTOR_DB)) {
3187
3495
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
3188
3496
  }
3189
3497
  const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
@@ -3192,7 +3500,7 @@ function listConductorWorkspaces() {
3192
3500
  copyFileSync3(CONDUCTOR_DB, snapshot);
3193
3501
  for (const suffix of ["-wal", "-shm"]) {
3194
3502
  const src = `${CONDUCTOR_DB}${suffix}`;
3195
- if (existsSync8(src)) {
3503
+ if (existsSync9(src)) {
3196
3504
  try {
3197
3505
  copyFileSync3(src, `${snapshot}${suffix}`);
3198
3506
  } catch {
@@ -3274,7 +3582,7 @@ function listConductorWorkspaces() {
3274
3582
  }
3275
3583
  }
3276
3584
  function importConductorWorkspace(workspaceId) {
3277
- if (!existsSync8(CONDUCTOR_DB)) {
3585
+ if (!existsSync9(CONDUCTOR_DB)) {
3278
3586
  throw new Error(`Conductor DB not found at ${CONDUCTOR_DB}`);
3279
3587
  }
3280
3588
  const tmp = mkdtempSync(join7(tmpdir(), "sideboard-conductor-"));
@@ -3283,7 +3591,7 @@ function importConductorWorkspace(workspaceId) {
3283
3591
  copyFileSync3(CONDUCTOR_DB, snapshot);
3284
3592
  for (const suffix of ["-wal", "-shm"]) {
3285
3593
  const src = `${CONDUCTOR_DB}${suffix}`;
3286
- if (existsSync8(src)) {
3594
+ if (existsSync9(src)) {
3287
3595
  try {
3288
3596
  copyFileSync3(src, `${snapshot}${suffix}`);
3289
3597
  } catch {
@@ -3303,7 +3611,7 @@ function importConductorWorkspace(workspaceId) {
3303
3611
  ).get(workspaceId);
3304
3612
  if (!row) throw new Error(`Conductor workspace not found: ${workspaceId}`);
3305
3613
  const worktreePath = String(row.workspacePath);
3306
- if (!existsSync8(worktreePath)) {
3614
+ if (!existsSync9(worktreePath)) {
3307
3615
  throw new Error(`Conductor worktree missing on disk: ${worktreePath}`);
3308
3616
  }
3309
3617
  let sessionId = null;
@@ -3375,10 +3683,10 @@ async function importConductorWorkspaceAsync(workspaceId) {
3375
3683
 
3376
3684
  // src/orchestrator/orchestrator.ts
3377
3685
  import { EventEmitter } from "events";
3378
- import { existsSync as existsSync12 } from "fs";
3686
+ import { existsSync as existsSync13 } from "fs";
3379
3687
 
3380
3688
  // src/git/orphan-cleanup.ts
3381
- import { existsSync as existsSync9, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
3689
+ import { existsSync as existsSync10, readdirSync as readdirSync4, statSync as statSync5 } from "fs";
3382
3690
  import { join as join8 } from "path";
3383
3691
  function isSideboardWorktreePath(path) {
3384
3692
  return path.includes("/.sideboard/worktrees/") || path.includes("/sideboard/workspaces/");
@@ -3390,7 +3698,7 @@ async function findOrphanWorktrees(repoPaths) {
3390
3698
  repoPaths?.length ? repoPaths : threads.map((t) => t.repoPath).filter(Boolean)
3391
3699
  );
3392
3700
  const homeRoot = sideboardWorkspacesDir();
3393
- if (existsSync9(homeRoot)) {
3701
+ if (existsSync10(homeRoot)) {
3394
3702
  try {
3395
3703
  for (const entry of readdirSync4(homeRoot, { withFileTypes: true })) {
3396
3704
  if (!entry.isDirectory()) continue;
@@ -3402,7 +3710,7 @@ async function findOrphanWorktrees(repoPaths) {
3402
3710
  const orphans = [];
3403
3711
  const seen = /* @__PURE__ */ new Set();
3404
3712
  for (const repoPath of repos) {
3405
- if (!repoPath || !existsSync9(repoPath)) continue;
3713
+ if (!repoPath || !existsSync10(repoPath)) continue;
3406
3714
  try {
3407
3715
  const wts = await listWorktrees(repoPath);
3408
3716
  for (const wt of wts) {
@@ -3423,12 +3731,12 @@ async function findOrphanWorktrees(repoPaths) {
3423
3731
  }
3424
3732
  try {
3425
3733
  const root = worktreesRoot(repoPath);
3426
- if (existsSync9(root)) {
3734
+ if (existsSync10(root)) {
3427
3735
  for (const entry of readdirSync4(root, { withFileTypes: true })) {
3428
3736
  if (!entry.isDirectory()) continue;
3429
3737
  const path = join8(root, entry.name).replace(/\/$/, "");
3430
3738
  if (known.has(path) || seen.has(path)) continue;
3431
- if (!existsSync9(join8(path, ".git"))) continue;
3739
+ if (!existsSync10(join8(path, ".git"))) continue;
3432
3740
  seen.add(path);
3433
3741
  let mtimeMs = 0;
3434
3742
  try {
@@ -3577,7 +3885,7 @@ async function applyThreadIntoMain(thread, opts) {
3577
3885
  }
3578
3886
 
3579
3887
  // src/git/clone-repo.ts
3580
- import { existsSync as existsSync10 } from "fs";
3888
+ import { existsSync as existsSync11 } from "fs";
3581
3889
  import { basename as basename3, join as join9 } from "path";
3582
3890
  import { execa as execa5 } from "execa";
3583
3891
  async function cloneRepoIntoSideboard(opts) {
@@ -3590,7 +3898,7 @@ async function cloneRepoIntoSideboard(opts) {
3590
3898
  }
3591
3899
  name = name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "repo";
3592
3900
  const dest = join9(sideboardReposDir(), name);
3593
- if (existsSync10(dest)) {
3901
+ if (existsSync11(dest)) {
3594
3902
  const repoPath2 = await resolveRepoRoot(dest);
3595
3903
  const workspace2 = await ensureWorkspace(repoPath2);
3596
3904
  return { repoPath: repoPath2, workspace: workspace2 };
@@ -3608,7 +3916,7 @@ async function cloneRepoIntoSideboard(opts) {
3608
3916
 
3609
3917
  // src/review/request-review.ts
3610
3918
  import { randomUUID as randomUUID4 } from "crypto";
3611
- import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
3919
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
3612
3920
  import { dirname as dirname3, join as join10 } from "path";
3613
3921
 
3614
3922
  // src/review/review-request-template.ts
@@ -3733,13 +4041,10 @@ File: src/client/frontends/desktop/core/UserData.ts
3733
4041
  // src/review/request-review.ts
3734
4042
  var REPO_REVIEW_PATH = ".sideboard/review.md";
3735
4043
  var REPO_REVIEW_NAME = "review.md";
3736
- var REVIEW_REQUEST_PATH = ".sideboard/attachments/Review request.md";
4044
+ var REVIEW_REQUEST_PATH = `${ATTACHMENTS_DIR}/Review request.md`;
4045
+ var LEGACY_REVIEW_REQUEST_PATH = `${LEGACY_ATTACHMENTS_DIR}/Review request.md`;
3737
4046
  var REVIEW_REQUEST_NAME = "Review request.md";
3738
4047
  var REVIEW_REQUEST_PREFILL = "Review.";
3739
- var ATTACHMENTS_GITIGNORE2 = `# Sideboard review / composer attachments (local only)
3740
- *
3741
- !.gitignore
3742
- `;
3743
4048
  var LEGACY_REVIEW_TEMPLATE_MARKERS = [
3744
4049
  "You are acting as a reviewer for a proposed code change made by another engineer.",
3745
4050
  "HOW MANY FINDINGS TO RETURN:"
@@ -3753,7 +4058,7 @@ function shouldRefreshReviewRequestTemplate(content) {
3753
4058
  return LEGACY_REVIEW_TEMPLATE_MARKERS.every((m) => trimmed.includes(m));
3754
4059
  }
3755
4060
  function readTextIfPresent(abs) {
3756
- if (!existsSync11(abs)) return null;
4061
+ if (!existsSync12(abs)) return null;
3757
4062
  try {
3758
4063
  const content = readFileSync8(abs, "utf8");
3759
4064
  return content.trim() ? content : null;
@@ -3762,10 +4067,10 @@ function readTextIfPresent(abs) {
3762
4067
  }
3763
4068
  }
3764
4069
  function ensureAttachmentsGitignore(worktreePath) {
3765
- const gitignoreAbs = join10(worktreePath, ".sideboard", "attachments", ".gitignore");
3766
- if (existsSync11(gitignoreAbs)) return;
4070
+ const gitignoreAbs = join10(worktreePath, ATTACHMENTS_DIR, ".gitignore");
4071
+ if (existsSync12(gitignoreAbs)) return;
3767
4072
  mkdirSync4(dirname3(gitignoreAbs), { recursive: true });
3768
- writeFileSync3(gitignoreAbs, ATTACHMENTS_GITIGNORE2, "utf8");
4073
+ writeFileSync3(gitignoreAbs, attachmentsGitignoreBody(), "utf8");
3769
4074
  }
3770
4075
  function resolveReviewGuidelines(worktreePath) {
3771
4076
  const repoAbs = join10(worktreePath, REPO_REVIEW_PATH);
@@ -3788,6 +4093,16 @@ function resolveReviewGuidelines(worktreePath) {
3788
4093
  source: "local"
3789
4094
  };
3790
4095
  }
4096
+ const legacyAbs = join10(worktreePath, LEGACY_REVIEW_REQUEST_PATH);
4097
+ const legacyContent = readTextIfPresent(legacyAbs);
4098
+ if (legacyContent && !shouldRefreshReviewRequestTemplate(legacyContent)) {
4099
+ return {
4100
+ path: LEGACY_REVIEW_REQUEST_PATH,
4101
+ name: REVIEW_REQUEST_NAME,
4102
+ content: legacyContent,
4103
+ source: "local"
4104
+ };
4105
+ }
3791
4106
  ensureAttachmentsGitignore(worktreePath);
3792
4107
  mkdirSync4(dirname3(localAbs), { recursive: true });
3793
4108
  writeFileSync3(localAbs, REVIEW_REQUEST_TEMPLATE, "utf8");
@@ -3810,10 +4125,11 @@ function ensureReviewRequestFile(worktreePath) {
3810
4125
  };
3811
4126
  }
3812
4127
  const localAbs = join10(worktreePath, REVIEW_REQUEST_PATH);
3813
- const localContent = readTextIfPresent(localAbs);
4128
+ const localContent = readTextIfPresent(localAbs) ?? readTextIfPresent(join10(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
3814
4129
  if (localContent && !shouldRefreshReviewRequestTemplate(localContent)) {
4130
+ const path = existsSync12(localAbs) ? REVIEW_REQUEST_PATH : LEGACY_REVIEW_REQUEST_PATH;
3815
4131
  return {
3816
- path: REVIEW_REQUEST_PATH,
4132
+ path,
3817
4133
  name: REVIEW_REQUEST_NAME,
3818
4134
  content: localContent,
3819
4135
  source: "local"
@@ -3840,7 +4156,7 @@ function buildReviewRequestAttachment(content, opts) {
3840
4156
  };
3841
4157
  }
3842
4158
  function readExistingReviewRequestFile(worktreePath) {
3843
- return readTextIfPresent(join10(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent(join10(worktreePath, REVIEW_REQUEST_PATH));
4159
+ return readTextIfPresent(join10(worktreePath, REPO_REVIEW_PATH)) ?? readTextIfPresent(join10(worktreePath, REVIEW_REQUEST_PATH)) ?? readTextIfPresent(join10(worktreePath, LEGACY_REVIEW_REQUEST_PATH));
3844
4160
  }
3845
4161
  async function requestReview(threadRef, send) {
3846
4162
  const from = findThreadByRef(threadRef);
@@ -4084,7 +4400,7 @@ var Orchestrator = class {
4084
4400
  }
4085
4401
  continue;
4086
4402
  }
4087
- if (!existsSync12(thread.worktreePath)) {
4403
+ if (!existsSync13(thread.worktreePath)) {
4088
4404
  setStatus(thread.id, "broken", "Worktree missing on disk");
4089
4405
  this.emit({ type: "status_changed", threadId: thread.id, status: "broken" });
4090
4406
  continue;
@@ -4229,26 +4545,43 @@ var Orchestrator = class {
4229
4545
  return findThreadByRef(idOrRef) ?? readThread(idOrRef);
4230
4546
  }
4231
4547
  async createThread(input) {
4232
- let thread = await createThread(input, (line) => {
4233
- this.emit({
4234
- type: "turn_output",
4235
- threadId: "pending",
4236
- event: { type: "stdout", data: line }
4237
- });
4238
- });
4548
+ const thread = await createThread(input);
4239
4549
  this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
4550
+ void this.finishCreateThread(thread.id, input.prompt?.trim() || void 0);
4551
+ return thread;
4552
+ }
4553
+ async finishCreateThread(threadId, prompt) {
4554
+ await this.runSetupAfterCreate(threadId);
4240
4555
  const { autoRunAfterSetupEnabled: autoRunAfterSetupEnabled2 } = await import("./app-settings-LZP632KI.js");
4241
4556
  if (autoRunAfterSetupEnabled2()) {
4242
4557
  try {
4243
- await this.startDev(thread.id);
4558
+ await this.startDev(threadId);
4244
4559
  } catch {
4245
4560
  }
4246
4561
  }
4247
- const prompt = input.prompt?.trim();
4248
4562
  if (prompt) {
4249
- thread = await this.send(thread.id, prompt);
4563
+ try {
4564
+ await this.send(threadId, prompt);
4565
+ } catch (err) {
4566
+ const message = err instanceof Error ? err.message : String(err);
4567
+ updateThread(threadId, {
4568
+ lastError: `First prompt failed: ${message}`
4569
+ });
4570
+ }
4571
+ }
4572
+ }
4573
+ /** Run workspace setup after a new worktree is created (no-op if none configured). */
4574
+ async runSetupAfterCreate(threadId) {
4575
+ try {
4576
+ await this.runSetup(threadId);
4577
+ } catch (err) {
4578
+ const message = err instanceof Error ? err.message : String(err);
4579
+ if (/no setup script/i.test(message)) return;
4580
+ if (/already running/i.test(message)) return;
4581
+ updateThread(threadId, {
4582
+ lastError: `Setup failed: ${message}`
4583
+ });
4250
4584
  }
4251
- return thread;
4252
4585
  }
4253
4586
  listWorkspaces() {
4254
4587
  const fromThreads = listThreads({ includeArchived: false }).map((t) => t.repoPath);
@@ -4581,8 +4914,73 @@ var Orchestrator = class {
4581
4914
  }
4582
4915
  }
4583
4916
  }
4584
- const lastStderr = summarizeTurnStderr(stderrTail);
4585
- const detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
4917
+ let lastStderr = summarizeTurnStderr(stderrTail);
4918
+ let detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
4919
+ if (exitCode !== 0 && !assistantText && parts.length === 0 && !this.stoppedTurns.has(threadId) && looksLikeInvalidAgentSession(detail) && this.requireThread(threadId).sessionId && this.requireThread(threadId).agent !== "cursor" && this.requireThread(threadId).agent !== "brightsy") {
4920
+ updateThread(threadId, { sessionId: null });
4921
+ pushTurnStderr(
4922
+ stderrTail,
4923
+ "Agent session missing \u2014 starting a fresh session"
4924
+ );
4925
+ this.emit({
4926
+ type: "turn_output",
4927
+ threadId,
4928
+ event: {
4929
+ type: "stderr",
4930
+ data: "Agent session missing \u2014 starting a fresh session"
4931
+ }
4932
+ });
4933
+ const retryThread = this.requireThread(threadId);
4934
+ const prior = retryThread.messages.slice(0, -1);
4935
+ const retrySeed = buildSessionSeed(prior);
4936
+ const retryInstructions = retryThread.agent === "claude" ? null : formatAgentInstructions(
4937
+ loadAgentInstructions(retryThread.worktreePath, retryThread.agent)
4938
+ );
4939
+ const retryPrefix = [
4940
+ coordinatorDirective,
4941
+ worktreeDirective,
4942
+ artifactDirective,
4943
+ renameBranchDirective,
4944
+ retryInstructions,
4945
+ retrySeed
4946
+ ].filter(Boolean).join("\n\n---\n\n");
4947
+ const retryHandle = await spawnAgentTurn(
4948
+ retryThread,
4949
+ { cachedPrefix: retryPrefix, prompt: agentPrompt },
4950
+ (event) => {
4951
+ this.emit({ type: "turn_output", threadId, event });
4952
+ if (event.type === "session_id") {
4953
+ updateThread(threadId, { sessionId: event.data });
4954
+ }
4955
+ if (event.type === "stderr" && typeof event.data === "string") {
4956
+ pushTurnStderr(stderrTail, event.data);
4957
+ }
4958
+ }
4959
+ );
4960
+ this.activeTurns.set(threadId, retryHandle);
4961
+ if (typeof retryHandle.pid === "number" && retryHandle.pid > 0) {
4962
+ updateThread(threadId, { agentPid: retryHandle.pid });
4963
+ }
4964
+ this.processes.set(`${threadId}:agent`, {
4965
+ kind: "agent",
4966
+ pid: retryHandle.pid,
4967
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
4968
+ kill: retryHandle.kill
4969
+ });
4970
+ if (this.stoppedTurns.has(threadId)) {
4971
+ retryHandle.kill();
4972
+ }
4973
+ const retryResult = await retryHandle.done;
4974
+ if (retryResult.sessionId) {
4975
+ updateThread(threadId, { sessionId: retryResult.sessionId });
4976
+ }
4977
+ assistantText = retryResult.assistantText.trim();
4978
+ parts = retryResult.parts;
4979
+ usage = retryResult.usage ?? void 0;
4980
+ exitCode = retryResult.exitCode;
4981
+ lastStderr = summarizeTurnStderr(stderrTail);
4982
+ detail = lastStderr || (exitCode !== 0 ? fallbackTurnFailDetail(assistantText) : "");
4983
+ }
4586
4984
  let chatText = assistantText;
4587
4985
  if (exitCode !== 0 && !chatText && looksLikeAgentFailureMessage(detail)) {
4588
4986
  chatText = humanizeAgentFailDetail(detail);
@@ -4598,6 +4996,19 @@ var Orchestrator = class {
4598
4996
  });
4599
4997
  }
4600
4998
  const afterTurn = this.requireThread(threadId);
4999
+ if (afterTurn.planMode && afterTurn.worktreePath?.trim()) {
5000
+ const presented = extractPresentedPlan(parts);
5001
+ const exited = parts.some(
5002
+ (p) => p.type === "tool" && /exitplanmode/i.test(p.name)
5003
+ );
5004
+ if (presented?.content) {
5005
+ writePlanFile(afterTurn.worktreePath, presented.content);
5006
+ } else if (exited || chatText && chatText.trim().length >= 400) {
5007
+ if (!readPlanFile(afterTurn.worktreePath) && chatText?.trim()) {
5008
+ writePlanFile(afterTurn.worktreePath, chatText.trim());
5009
+ }
5010
+ }
5011
+ }
4601
5012
  if (afterTurn.planMode && afterTurn.agent === "claude" && parts.some(
4602
5013
  (p) => p.type === "tool" && /exitplanmode/i.test(p.name)
4603
5014
  )) {
@@ -5079,6 +5490,82 @@ var Orchestrator = class {
5079
5490
  }
5080
5491
  return meta;
5081
5492
  }
5493
+ async getPrStack(threadRef) {
5494
+ const thread = this.requireThread(threadRef);
5495
+ if (!thread.worktreePath?.trim()) return null;
5496
+ const stack = await getPrStack(thread.worktreePath);
5497
+ if (!stack) return null;
5498
+ const current = stack.currentIndex >= 0 ? stack.layers[stack.currentIndex] : null;
5499
+ const patch = {};
5500
+ if (stack.stackNumber != null) {
5501
+ const id = `gh-stack-${stack.stackNumber}`;
5502
+ if (thread.stackId !== id) patch.stackId = id;
5503
+ }
5504
+ if (current?.position != null && thread.stackLayer !== current.position) {
5505
+ patch.stackLayer = current.position;
5506
+ }
5507
+ if (current?.prUrl && current.prUrl !== thread.prUrl) patch.prUrl = current.prUrl;
5508
+ if (current?.title && current.title !== thread.prTitle) patch.prTitle = current.title;
5509
+ if (current?.branchName && current.branchName !== thread.branchName) {
5510
+ patch.branchName = current.branchName;
5511
+ }
5512
+ if (Object.keys(patch).length > 0) updateThread(thread.id, patch);
5513
+ return stack;
5514
+ }
5515
+ /** Open worktrees for all (or one) stack layers discovered from a thread. */
5516
+ async openPrStackLayers(threadRef, opts) {
5517
+ const result = await openPrStackLayers({ threadRef, layer: opts?.layer });
5518
+ for (const t of result.threads) {
5519
+ this.emit({ type: "status_changed", threadId: t.id, status: t.status });
5520
+ }
5521
+ for (const id of result.createdThreadIds) {
5522
+ await this.runSetupAfterCreate(id);
5523
+ }
5524
+ return { stack: result.stack, threads: result.threads };
5525
+ }
5526
+ /** Add a branch on top of the thread's stack and open its worktree. */
5527
+ async addStackLayer(threadRef, branchName, opts) {
5528
+ const result = await addStackLayerFromThread({
5529
+ threadRef,
5530
+ branchName,
5531
+ title: opts?.title
5532
+ });
5533
+ this.emit({
5534
+ type: "status_changed",
5535
+ threadId: result.thread.id,
5536
+ status: result.thread.status
5537
+ });
5538
+ if (result.createdWorktree) {
5539
+ await this.runSetupAfterCreate(result.thread.id);
5540
+ }
5541
+ return { stack: result.stack, thread: result.thread };
5542
+ }
5543
+ /** Initialize a stack from the current thread branch (optional extra layers). */
5544
+ async initStackFromThread(threadRef, opts) {
5545
+ const result = await initStackFromThread({
5546
+ threadRef,
5547
+ additionalBranches: opts?.additionalBranches,
5548
+ base: opts?.base
5549
+ });
5550
+ for (const t of result.threads) {
5551
+ this.emit({ type: "status_changed", threadId: t.id, status: t.status });
5552
+ }
5553
+ for (const id of result.createdThreadIds) {
5554
+ await this.runSetupAfterCreate(id);
5555
+ }
5556
+ return { stack: result.stack, threads: result.threads };
5557
+ }
5558
+ /** Create a new multi-layer stack with one worktree per layer. */
5559
+ async createPrStack(input) {
5560
+ const result = await createPrStack(input);
5561
+ for (const t of result.threads) {
5562
+ this.emit({ type: "status_changed", threadId: t.id, status: t.status });
5563
+ }
5564
+ for (const id of result.createdThreadIds) {
5565
+ await this.runSetupAfterCreate(id);
5566
+ }
5567
+ return { stack: result.stack, threads: result.threads };
5568
+ }
5082
5569
  async getPrDetails(threadRef) {
5083
5570
  const { thread, selector, cwd } = await this.withPrSelector(threadRef);
5084
5571
  if (!selector) return null;
@@ -5141,14 +5628,9 @@ var Orchestrator = class {
5141
5628
  return forkChatTab(input);
5142
5629
  }
5143
5630
  async forkThreadWorktree(input) {
5144
- const thread = await forkThreadWorktree(input, (line) => {
5145
- this.emit({
5146
- type: "turn_output",
5147
- threadId: "pending",
5148
- event: { type: "stdout", data: line }
5149
- });
5150
- });
5631
+ const thread = await forkThreadWorktree(input);
5151
5632
  this.emit({ type: "status_changed", threadId: thread.id, status: thread.status });
5633
+ await this.runSetupAfterCreate(thread.id);
5152
5634
  return thread;
5153
5635
  }
5154
5636
  renameThread(threadRef, title) {
@@ -5162,7 +5644,7 @@ var Orchestrator = class {
5162
5644
  }
5163
5645
  /**
5164
5646
  * Stage OS / worktree files into composer attachments (copies external files
5165
- * into `.sideboard/attachments/` so agents can Read images and binaries).
5647
+ * into `.context/attachments/` so agents can Read images and binaries).
5166
5648
  */
5167
5649
  attachComposerFiles(threadRef, opts) {
5168
5650
  const thread = this.requireThread(threadRef);
@@ -5236,8 +5718,8 @@ var Orchestrator = class {
5236
5718
  updateThread(thread.id, { worktreePath: globalAgentCwd2() });
5237
5719
  return setStatus(thread.id, "idle");
5238
5720
  }
5239
- if (!existsSync12(thread.worktreePath)) {
5240
- const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-GDV56MX4.js");
5721
+ if (!existsSync13(thread.worktreePath)) {
5722
+ const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-RWGL7FUV.js");
5241
5723
  const { execa: execa6 } = await import("execa");
5242
5724
  const slug = thread.worktreePath.split("/").pop();
5243
5725
  const dest = thread.worktreePath;
@@ -5322,7 +5804,7 @@ async function startOrchestration(opts) {
5322
5804
  sourceRef: "default",
5323
5805
  ...createOpts
5324
5806
  }).catch(async () => {
5325
- const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-GDV56MX4.js");
5807
+ const { resolveDefaultBranch: resolveDefaultBranch2, resolveRepoRoot: resolveRepoRoot2 } = await import("./worktree-RWGL7FUV.js");
5326
5808
  const repo = await resolveRepoRoot2(repoPath);
5327
5809
  const def = await resolveDefaultBranch2(repo);
5328
5810
  return createThread({
@@ -5332,7 +5814,7 @@ async function startOrchestration(opts) {
5332
5814
  repoPath: repo
5333
5815
  });
5334
5816
  });
5335
- const { updateThread: upd } = await import("./thread-store-OV2X6PYO.js");
5817
+ const { updateThread: upd } = await import("./thread-store-UJIGMI5J.js");
5336
5818
  const updated = upd(thread.id, {
5337
5819
  sourceType: "orchestration",
5338
5820
  sourceRef: goal
@@ -5343,6 +5825,104 @@ async function startOrchestration(opts) {
5343
5825
  return updated;
5344
5826
  }
5345
5827
 
5828
+ // src/plan/ask-user.ts
5829
+ function asRecord2(v) {
5830
+ return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
5831
+ }
5832
+ function parseOptions(raw) {
5833
+ if (!Array.isArray(raw)) return [];
5834
+ const out = [];
5835
+ for (const item of raw) {
5836
+ if (typeof item === "string" && item.trim()) {
5837
+ out.push({ label: item.trim() });
5838
+ continue;
5839
+ }
5840
+ const o = asRecord2(item);
5841
+ if (!o) continue;
5842
+ const label = typeof o.label === "string" ? o.label.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.title === "string" ? o.title.trim() : "";
5843
+ if (!label) continue;
5844
+ const description = typeof o.description === "string" && o.description.trim() ? o.description.trim() : void 0;
5845
+ out.push({ label, description });
5846
+ }
5847
+ return out;
5848
+ }
5849
+ function parseOneQuestion(raw) {
5850
+ const o = asRecord2(raw);
5851
+ if (!o) return null;
5852
+ const question = typeof o.question === "string" ? o.question.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.prompt === "string" ? o.prompt.trim() : "";
5853
+ if (!question) return null;
5854
+ const options = parseOptions(o.options);
5855
+ if (options.length < 1) return null;
5856
+ const header = typeof o.header === "string" && o.header.trim() ? o.header.trim() : void 0;
5857
+ const multiSelect = Boolean(o.multiSelect ?? o.multi_select);
5858
+ return { question, header, multiSelect, options };
5859
+ }
5860
+ function parsePlanQuestionsInput(input) {
5861
+ const root = asRecord2(input);
5862
+ if (!root) return [];
5863
+ const list = Array.isArray(root.questions) ? root.questions : Array.isArray(root.question) ? root.question : root.question || root.prompt ? [root] : [];
5864
+ const out = [];
5865
+ for (const item of list) {
5866
+ const q = parseOneQuestion(item);
5867
+ if (q) out.push(q);
5868
+ }
5869
+ return out;
5870
+ }
5871
+ var ASK_USER_TOOL_RE = /^(AskUserQuestion|ask_user|mcp__sideboard__ask_user)$/i;
5872
+ function isAskUserToolName(name) {
5873
+ if (!name) return false;
5874
+ const base = name.replace(/^mcp__sideboard__/i, "");
5875
+ return ASK_USER_TOOL_RE.test(name) || /^ask_user$/i.test(base);
5876
+ }
5877
+ function extractPendingPlanQuestions(parts) {
5878
+ if (!parts?.length) return null;
5879
+ for (let i = parts.length - 1; i >= 0; i--) {
5880
+ const p = parts[i];
5881
+ if (p.type !== "tool" || !isAskUserToolName(p.name)) continue;
5882
+ const questions = parsePlanQuestionsInput(p.input);
5883
+ if (!questions.length) continue;
5884
+ return {
5885
+ id: p.id || `ask-${i}`,
5886
+ questions,
5887
+ source: p.name || "ask_user"
5888
+ };
5889
+ }
5890
+ return null;
5891
+ }
5892
+ function formatPlanQuestionAnswers(questions, answers) {
5893
+ const lines = ["Answers to your questions:"];
5894
+ for (let i = 0; i < questions.length; i++) {
5895
+ const q = questions[i];
5896
+ const a = answers.find((x) => x.questionIndex === i);
5897
+ const header = q.header ? `**${q.header}** \u2014 ` : "";
5898
+ const parts = [];
5899
+ if (a?.selected.length) parts.push(a.selected.join(", "));
5900
+ if (a?.other?.trim()) parts.push(a.other.trim());
5901
+ const body = parts.length ? parts.join(" \xB7 ") : "(no answer)";
5902
+ lines.push(`${i + 1}. ${header}${q.question}`);
5903
+ lines.push(` \u2192 ${body}`);
5904
+ }
5905
+ return lines.join("\n");
5906
+ }
5907
+ function formatPlanQuestionsForChat(questions) {
5908
+ const lines = ["### Questions for you", ""];
5909
+ for (let i = 0; i < questions.length; i++) {
5910
+ const q = questions[i];
5911
+ const header = q.header ? `**${q.header}** \u2014 ` : "";
5912
+ lines.push(`${i + 1}. ${header}${q.question}`);
5913
+ for (let oi = 0; oi < q.options.length; oi++) {
5914
+ const opt = q.options[oi];
5915
+ const desc = opt.description?.trim();
5916
+ lines.push(
5917
+ desc ? ` - **${opt.label}** \u2014 ${desc}` : ` - **${opt.label}**`
5918
+ );
5919
+ }
5920
+ lines.push("");
5921
+ }
5922
+ lines.push("_Answer in the composer below._");
5923
+ return lines.join("\n").trimEnd();
5924
+ }
5925
+
5346
5926
  // src/mcp/server.ts
5347
5927
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5348
5928
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -5454,6 +6034,59 @@ async function startMcpServer() {
5454
6034
  return { content: [{ type: "text", text: JSON.stringify(payload) }] };
5455
6035
  }
5456
6036
  );
6037
+ server.tool(
6038
+ "ask_user",
6039
+ "Ask the user clarifying multiple-choice questions in Sideboard\u2019s composer (plan mode). Before calling, write a short chat message explaining the decision and what each option means. Include a description on every option. Use for approach forks and requirements \u2014 not for \u201Cis the plan ready?\u201D. After calling, stop and wait for their next message with answers.",
6040
+ {
6041
+ questions: z.array(
6042
+ z.object({
6043
+ question: z.string().describe("Full question text ending with ?"),
6044
+ header: z.string().max(24).optional().describe("Short label shown above the question"),
6045
+ multiSelect: z.boolean().optional().describe("Allow selecting multiple options"),
6046
+ options: z.array(
6047
+ z.object({
6048
+ label: z.string(),
6049
+ description: z.string().optional().describe("What this option means / when to choose it (strongly preferred)")
6050
+ })
6051
+ ).min(2).max(6).describe("2\u20136 choices (Sideboard also offers Other)")
6052
+ })
6053
+ ).min(1).max(4).describe("1\u20134 questions")
6054
+ },
6055
+ async ({ questions }) => {
6056
+ const payload = {
6057
+ ok: true,
6058
+ questions,
6059
+ message: "Questions shown in Sideboard\u2019s composer. Wait for the user\u2019s next message with their answers before continuing."
6060
+ };
6061
+ return { content: [{ type: "text", text: JSON.stringify(payload) }] };
6062
+ }
6063
+ );
6064
+ server.tool(
6065
+ "present_plan",
6066
+ "Save the implementation plan as markdown to .context/attachments/plan.md and show it in Sideboard chat for user approval (Copy / Hand off / Approve). Call this when the plan is ready \u2014 required in plan mode. Pass the full plan body in content. Then Claude should call ExitPlanMode.",
6067
+ {
6068
+ title: z.string().optional().describe("Short plan title (defaults to Plan)"),
6069
+ content: z.string().min(1).describe("Full plan markdown (headings, steps, risks, open questions)"),
6070
+ thread_id: z.string().optional().describe("Sideboard thread id when cwd is not the worktree")
6071
+ },
6072
+ async ({ title, content, thread_id }) => {
6073
+ const { writePlanFile: writePlanFile2 } = await import("./plan-file-6O7G4VPQ.js");
6074
+ let root = process.cwd();
6075
+ if (thread_id?.trim()) {
6076
+ const t = orch.getThread(thread_id.trim());
6077
+ if (t?.worktreePath?.trim()) root = t.worktreePath;
6078
+ }
6079
+ const path = writePlanFile2(root, content);
6080
+ const payload = {
6081
+ ok: true,
6082
+ path,
6083
+ title: title?.trim() || "Plan",
6084
+ content,
6085
+ message: "Plan saved to .context/attachments/plan.md and shown in Sideboard chat for approval."
6086
+ };
6087
+ return { content: [{ type: "text", text: JSON.stringify(payload) }] };
6088
+ }
6089
+ );
5457
6090
  server.tool(
5458
6091
  "present_schema",
5459
6092
  "Open Sideboard\u2019s schema-driven CMS side column (filterable table and/or form). Pass JSON Schema + schemaUi (Brightsy extensions supported). Use datasource=brightsy with resource_id (record type UUID) when logged into Brightsy; use datasource=inline with embedded resource/records for any other source.",
@@ -6032,6 +6665,126 @@ async function startMcpServer() {
6032
6665
  };
6033
6666
  }
6034
6667
  );
6668
+ server.tool(
6669
+ "get_pr_stack",
6670
+ "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 mergePr on stacked PRs.",
6671
+ { ref: z.string() },
6672
+ async ({ ref }) => {
6673
+ const stack = await orch.getPrStack(ref);
6674
+ return {
6675
+ content: [{ type: "text", text: JSON.stringify(stack, null, 2) }]
6676
+ };
6677
+ }
6678
+ );
6679
+ server.tool(
6680
+ "open_pr_stack_layers",
6681
+ "Materialize one worktree+thread per stack layer (or a single 1-based layer). Pass a thread ref already on the stack.",
6682
+ {
6683
+ ref: z.string(),
6684
+ layer: z.number().int().positive().optional()
6685
+ },
6686
+ async ({ ref, layer }) => {
6687
+ const result = await orch.openPrStackLayers(ref, { layer });
6688
+ return {
6689
+ content: [
6690
+ {
6691
+ type: "text",
6692
+ text: JSON.stringify(
6693
+ {
6694
+ stackNumber: result.stack.stackNumber,
6695
+ trunk: result.stack.trunk,
6696
+ threads: result.threads.map((t) => ({
6697
+ id: t.id,
6698
+ title: t.title,
6699
+ branchName: t.branchName,
6700
+ stackLayer: t.stackLayer,
6701
+ worktreePath: t.worktreePath,
6702
+ prUrl: t.prUrl,
6703
+ link: `sideboard://thread/${t.id}`
6704
+ }))
6705
+ },
6706
+ null,
6707
+ 2
6708
+ )
6709
+ }
6710
+ ]
6711
+ };
6712
+ }
6713
+ );
6714
+ server.tool(
6715
+ "add_stack_layer",
6716
+ "Add a branch on top of the current stack (`gh stack add`) and open a worktree+thread for it.",
6717
+ {
6718
+ ref: z.string(),
6719
+ branchName: z.string(),
6720
+ title: z.string().optional()
6721
+ },
6722
+ async ({ ref, branchName, title }) => {
6723
+ const result = await orch.addStackLayer(ref, branchName, { title });
6724
+ return {
6725
+ content: [
6726
+ {
6727
+ type: "text",
6728
+ text: JSON.stringify(
6729
+ {
6730
+ id: result.thread.id,
6731
+ title: result.thread.title,
6732
+ branchName: result.thread.branchName,
6733
+ stackLayer: result.thread.stackLayer,
6734
+ worktreePath: result.thread.worktreePath,
6735
+ link: `sideboard://thread/${result.thread.id}`
6736
+ },
6737
+ null,
6738
+ 2
6739
+ )
6740
+ }
6741
+ ]
6742
+ };
6743
+ }
6744
+ );
6745
+ server.tool(
6746
+ "create_pr_stack",
6747
+ "Create a new GitHub PR stack with one Sideboard worktree per layer (bottom\u2192top branch names). Requires `gh extension install github/gh-stack`.",
6748
+ {
6749
+ repoPath: z.string(),
6750
+ branches: z.array(z.string()).min(1),
6751
+ agent: z.enum(["claude", "codex", "opencode", "brightsy", "cursor"]),
6752
+ base: z.string().optional(),
6753
+ title: z.string().optional()
6754
+ },
6755
+ async (args) => {
6756
+ const result = await orch.createPrStack({
6757
+ repoPath: args.repoPath,
6758
+ branches: args.branches,
6759
+ agent: args.agent,
6760
+ base: args.base,
6761
+ title: args.title
6762
+ });
6763
+ return {
6764
+ content: [
6765
+ {
6766
+ type: "text",
6767
+ text: JSON.stringify(
6768
+ {
6769
+ stackNumber: result.stack.stackNumber,
6770
+ trunk: result.stack.trunk,
6771
+ threads: result.threads.map((t) => ({
6772
+ id: t.id,
6773
+ title: t.title,
6774
+ branchName: t.branchName,
6775
+ stackLayer: t.stackLayer,
6776
+ worktreePath: t.worktreePath,
6777
+ link: `sideboard://thread/${t.id}`
6778
+ }))
6779
+ },
6780
+ null,
6781
+ 2
6782
+ )
6783
+ }
6784
+ ]
6785
+ };
6786
+ }
6787
+ );
6035
6788
  server.tool(
6036
6789
  "list_issues",
6037
6790
  "List issues from Sideboard Account connections (Linear API or GitHub Issues; Linear\u2192GitHub fallback when Linear is not connected). Pass repoPath from list_workspaces \u2014 GitHub Issues are scoped to that repo. Then create_thread with sourceType=ticket.",
@@ -6385,6 +7138,7 @@ async function runCloudConnect(opts) {
6385
7138
  }
6386
7139
  }
6387
7140
  export {
7141
+ ATTACHMENTS_DIR,
6388
7142
  BRIGHTSY_MCP_ALLOWED_TOOLS,
6389
7143
  BrightsySideboardApi,
6390
7144
  CLAUDE_MODEL_CATALOG,
@@ -6400,11 +7154,16 @@ export {
6400
7154
  FAMOUS_SOCCER_TEAMS,
6401
7155
  GLOBAL_WORKSPACE_ID,
6402
7156
  HARNESS_ENV_KEYS,
7157
+ LEGACY_ATTACHMENTS_DIR,
7158
+ LEGACY_PLAN_FILE_REL,
7159
+ LEGACY_REVIEW_REQUEST_PATH,
6403
7160
  MAX_ANTHROPIC_CACHE_CONTROL_BLOCKS,
6404
7161
  ORCHESTRATOR_AGENT_KINDS,
6405
7162
  Orchestrator,
6406
7163
  PASTE_ATTACH_MIN_CHARS,
6407
7164
  PASTE_ATTACH_MIN_LINES,
7165
+ PLAN_FILE_NAME,
7166
+ PLAN_FILE_REL,
6408
7167
  PLAN_MODE_INSTRUCTION,
6409
7168
  REPO_REVIEW_NAME,
6410
7169
  REPO_REVIEW_PATH,
@@ -6414,6 +7173,8 @@ export {
6414
7173
  SIDEBOARD_FORCE_STOP,
6415
7174
  SIDEBOARD_MCP_ALLOWED_TOOLS,
6416
7175
  THINKING_EFFORTS,
7176
+ addPrStackLayer,
7177
+ addStackLayerFromThread,
6417
7178
  addWorkspace,
6418
7179
  adoptThread,
6419
7180
  allAdapters,
@@ -6432,6 +7193,7 @@ export {
6432
7193
  attachmentFromAbsolutePath,
6433
7194
  attachmentsFromBuffers,
6434
7195
  attachmentsFromWorktreePaths,
7196
+ attachmentsGitignoreBody,
6435
7197
  autoCleanupOrphansEnabled,
6436
7198
  autoRenameBranchEnabled,
6437
7199
  autoRunAfterSetupEnabled,
@@ -6454,6 +7216,7 @@ export {
6454
7216
  caffeinateWhileRunningEnabled,
6455
7217
  captureLoginEnv,
6456
7218
  captureTurnBaseline,
7219
+ checkoutPrStackLayer,
6457
7220
  childEnvWithAppSettings,
6458
7221
  claudeAdapter,
6459
7222
  claudeChromeEnabled,
@@ -6473,8 +7236,10 @@ export {
6473
7236
  countCacheControlBlocks,
6474
7237
  createChatTab,
6475
7238
  createEmptyThread,
7239
+ createExistingBranchWorktree,
6476
7240
  createGlobalChat,
6477
7241
  createOrUpdatePr,
7242
+ createPrStack,
6478
7243
  createThread,
6479
7244
  createThreadWorktree,
6480
7245
  currentBranch,
@@ -6484,6 +7249,7 @@ export {
6484
7249
  deleteBranchOnPurgeEnabled,
6485
7250
  deleteThreadRecord,
6486
7251
  detectAgents,
7252
+ detectGhStack,
6487
7253
  detectLocalMergeConflicts,
6488
7254
  disconnectBrightsyTeam,
6489
7255
  discoverSkills,
@@ -6499,12 +7265,15 @@ export {
6499
7265
  estimateThreadChars,
6500
7266
  expandComposerPrompt,
6501
7267
  extractGhErrorDetail,
7268
+ extractPendingPlanQuestions,
7269
+ extractPresentedPlan,
6502
7270
  extractiveSummary,
6503
7271
  fetchPrHead,
6504
7272
  finalizeParts,
6505
7273
  findInvalidCacheControlTtlOrder,
6506
7274
  findOrphanWorktrees,
6507
7275
  findThreadByRef,
7276
+ findThreadForStackLayer,
6508
7277
  flattenTurnInput,
6509
7278
  forkChatTab,
6510
7279
  forkMessageSlice,
@@ -6515,6 +7284,8 @@ export {
6515
7284
  formatGhLandError,
6516
7285
  formatIpcInvokeError,
6517
7286
  formatMessagesAsTranscript,
7287
+ formatPlanQuestionAnswers,
7288
+ formatPlanQuestionsForChat,
6518
7289
  formatRateLimitResetHint,
6519
7290
  formatRenameBranchDirective,
6520
7291
  formatTranscriptMarkdown,
@@ -6538,6 +7309,7 @@ export {
6538
7309
  getPrChecks,
6539
7310
  getPrDetails,
6540
7311
  getPrMeta,
7312
+ getPrStack,
6541
7313
  getRepoSetupInfo,
6542
7314
  getRunMode,
6543
7315
  getRunScript,
@@ -6554,9 +7326,12 @@ export {
6554
7326
  healOrchestrationSoccerTitles,
6555
7327
  importConductorWorkspace,
6556
7328
  importConductorWorkspaceAsync,
7329
+ initPrStack,
7330
+ initStackFromThread,
6557
7331
  initializeGitRepository,
6558
7332
  inspectGitWorktree,
6559
7333
  installAgent,
7334
+ isAskUserToolName,
6560
7335
  isBrightsyConnected,
6561
7336
  isBrightsyNdjsonLine,
6562
7337
  isCloudCoordinatorThread,
@@ -6566,14 +7341,17 @@ export {
6566
7341
  isGlobalRepoPath,
6567
7342
  isGlobalThread,
6568
7343
  isImageFilePath,
7344
+ isInPrStack,
6569
7345
  isLinearConnected,
6570
7346
  isOrchestratorCapableAgent,
6571
7347
  isOrchestratorThread,
6572
7348
  isPidAlive,
6573
7349
  isPlaceholderBranch,
7350
+ isPresentPlanToolName,
6574
7351
  isSessionQuotaLimit,
6575
7352
  isSideboardScratchPath,
6576
7353
  isThinkingEffort,
7354
+ isWorkspaceScratchPath,
6577
7355
  listAgentSetupInfo,
6578
7356
  listBranchCommits,
6579
7357
  listBranches,
@@ -6610,6 +7388,7 @@ export {
6610
7388
  mcpAllowTools,
6611
7389
  mcpAuthWarnings,
6612
7390
  mergePr,
7391
+ mergePrStack,
6613
7392
  mergeUsage,
6614
7393
  nextPastedTextName,
6615
7394
  nextThinkingEffort,
@@ -6619,6 +7398,8 @@ export {
6619
7398
  normalizeTurnInput,
6620
7399
  normalizeWorktreePath,
6621
7400
  openInSystemTerminal,
7401
+ openPrStackLayers,
7402
+ openStackLayer,
6622
7403
  opencodeAdapter,
6623
7404
  orchestrationQuotaFallbackAgent,
6624
7405
  orchestrationQuotaOnLimit,
@@ -6627,15 +7408,19 @@ export {
6627
7408
  originGhRepoEnv,
6628
7409
  parseCursorRunnerLine,
6629
7410
  parseForceStopMessage,
7411
+ parseGhStackViewJson,
6630
7412
  parseGithubSlugFromRemoteUrl,
6631
7413
  parseMcpList,
7414
+ parsePlanQuestionsInput,
6632
7415
  parseSessionQuotaResetAt,
6633
7416
  partsToAssistantText,
6634
7417
  pastedTextStats,
6635
7418
  permissionMode,
7419
+ planFileAbs,
6636
7420
  previewLand,
6637
7421
  pushBranch,
6638
7422
  readExistingReviewRequestFile,
7423
+ readPlanFile,
6639
7424
  readSkillBody,
6640
7425
  readThread,
6641
7426
  readWorktreeFile,
@@ -6647,6 +7432,7 @@ export {
6647
7432
  repoSlug,
6648
7433
  requestReview,
6649
7434
  requireAgent,
7435
+ resetGhStackDetectCache,
6650
7436
  resolveClaudeExecutable,
6651
7437
  resolveConductorCursorAgentId,
6652
7438
  resolveCursorModelId,
@@ -6656,6 +7442,7 @@ export {
6656
7442
  resolveFilesToCopy,
6657
7443
  resolveGhAuthToken,
6658
7444
  resolveGithubRepoSlug,
7445
+ resolvePlanMarkdown,
6659
7446
  resolvePrSelector,
6660
7447
  resolveQuotaFallbackAgent,
6661
7448
  resolveRepoRoot,
@@ -6683,12 +7470,16 @@ export {
6683
7470
  slugify,
6684
7471
  spawnAgentTurn,
6685
7472
  splitForCompaction,
7473
+ stackAgentDefaultsFrom,
7474
+ stackIdFrom,
7475
+ stackMergeReadiness,
6686
7476
  stageAbsolutePathsAsAttachments,
6687
7477
  stageBuffersAsAttachments,
6688
7478
  startDevServer,
6689
7479
  startMcpServer,
6690
7480
  startOrchestration,
6691
7481
  stripBrightsyNdjsonNoise,
7482
+ submitPrStack,
6692
7483
  suggestSlug,
6693
7484
  summarizeConversation,
6694
7485
  switchBrightsyAccount,
@@ -6724,6 +7515,7 @@ export {
6724
7515
  worktreeNameFromPath,
6725
7516
  worktreesRoot,
6726
7517
  writeInjectedMcpConfig,
7518
+ writePlanFile,
6727
7519
  writeThread,
6728
7520
  writeWorktreeFile
6729
7521
  };