@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.
- package/dist/agents/cursor-runner.cjs +43 -4
- package/dist/agents/cursor-runner.js +45 -6
- package/dist/{agents-HIEJL3UV.js → agents-KP7UJEHJ.js} +1 -1
- package/dist/{agents-5ROTZNCX.js → agents-KYACODJ3.js} +2 -2
- package/dist/{chunk-5ZPSH7VI.js → chunk-A6HVEMIB.js} +16 -7
- package/dist/chunk-B3SJXYIJ.js +24 -0
- package/dist/{chunk-7EUWSBWR.js → chunk-BZST4HMJ.js} +21 -6
- package/dist/{chunk-ZH5QZ4CR.js → chunk-DZFH2KLT.js} +280 -7
- package/dist/chunk-FKOIHGKV.js +21 -0
- package/dist/{chunk-E4VVEKAM.js → chunk-GNML24AW.js} +2 -2
- package/dist/{chunk-EOYDCKQC.js → chunk-HLEX5AQ6.js} +4 -0
- package/dist/{chunk-K3WMKGFY.js → chunk-LRLKJM3O.js} +2 -1
- package/dist/chunk-N5PM7HGQ.js +103 -0
- package/dist/chunk-QTUESPAW.js +101 -0
- package/dist/{chunk-O6W3P7V3.js → chunk-TSRXOSVD.js} +4 -0
- package/dist/{chunk-F4Q3IM6V.js → chunk-UEAHMGHW.js} +2 -1
- package/dist/{chunk-J5JTEJ5O.js → chunk-VG22SETP.js} +6 -0
- package/dist/{chunk-XRSAGVRW.js → chunk-XOU6HNQJ.js} +245 -7
- package/dist/{chunk-O5DOO7DP.js → chunk-XX5BB7NV.js} +3 -3
- package/dist/{chunk-QN7XNQAT.js → chunk-YDXQ72MD.js} +2 -2
- package/dist/{chunk-YFJ4FG2P.js → chunk-YOWIYAVA.js} +3 -3
- package/dist/{coordinator-prompt-7HHJRO7B.js → coordinator-prompt-6FXVTSFN.js} +4 -3
- package/dist/{coordinator-prompt-WD7FAMA2.js → coordinator-prompt-S6JZD5EF.js} +4 -3
- package/dist/{global-workspace-OJEPGDXA.js → global-workspace-EV4G2WMQ.js} +5 -4
- package/dist/{global-workspace-ECYN2MKL.js → global-workspace-MSX2K27Y.js} +5 -4
- package/dist/index.cjs +1383 -149
- package/dist/index.d.cts +389 -15
- package/dist/index.d.ts +389 -15
- package/dist/index.js +883 -91
- package/dist/mcp/run-stdio.cjs +1154 -141
- package/dist/mcp/run-stdio.js +709 -79
- package/dist/plan-file-6O7G4VPQ.js +23 -0
- package/dist/plan-file-PHVKUAEE.js +25 -0
- package/dist/{thread-store-XICUWFNM.js → thread-store-GHOADGL2.js} +1 -1
- package/dist/{thread-store-OV2X6PYO.js → thread-store-UJIGMI5J.js} +1 -1
- package/dist/{workspaces-MUU7RGVV.js → workspaces-3RQQZQRO.js} +6 -5
- package/dist/{workspaces-ZWOOFZUV.js → workspaces-AYTBR6KQ.js} +6 -5
- package/dist/{worktree-DVNDMWZ7.js → worktree-5KEQWSAF.js} +5 -2
- package/dist/{worktree-GDV56MX4.js → worktree-RWGL7FUV.js} +5 -2
- 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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
693
|
+
const { isGlobalThread: isGlobalThread2 } = await import("./global-workspace-MSX2K27Y.js");
|
|
661
694
|
if (isGlobalThread2(thread)) {
|
|
662
|
-
const { ensureGlobalCoordinatorCwd: ensureGlobalCoordinatorCwd2 } = await import("./coordinator-prompt-
|
|
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,
|
|
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,
|
|
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-
|
|
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
|
|
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 || !
|
|
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 (!
|
|
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 (!
|
|
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-
|
|
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 (!
|
|
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 (
|
|
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 (!
|
|
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 (
|
|
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 (!
|
|
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
|
|
3686
|
+
import { existsSync as existsSync13 } from "fs";
|
|
3379
3687
|
|
|
3380
3688
|
// src/git/orphan-cleanup.ts
|
|
3381
|
-
import { existsSync as
|
|
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 (
|
|
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 || !
|
|
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 (
|
|
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 (!
|
|
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
|
|
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 (
|
|
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
|
|
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 =
|
|
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 (!
|
|
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,
|
|
3766
|
-
if (
|
|
4070
|
+
const gitignoreAbs = join10(worktreePath, ATTACHMENTS_DIR, ".gitignore");
|
|
4071
|
+
if (existsSync12(gitignoreAbs)) return;
|
|
3767
4072
|
mkdirSync4(dirname3(gitignoreAbs), { recursive: true });
|
|
3768
|
-
writeFileSync3(gitignoreAbs,
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
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(
|
|
4558
|
+
await this.startDev(threadId);
|
|
4244
4559
|
} catch {
|
|
4245
4560
|
}
|
|
4246
4561
|
}
|
|
4247
|
-
const prompt = input.prompt?.trim();
|
|
4248
4562
|
if (prompt) {
|
|
4249
|
-
|
|
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
|
-
|
|
4585
|
-
|
|
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
|
|
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 `.
|
|
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 (!
|
|
5240
|
-
const { createThreadWorktree: createThreadWorktree2 } = await import("./worktree-
|
|
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-
|
|
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-
|
|
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
|
};
|