@bastani/atomic 0.8.17-0 → 0.8.18-0
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/CHANGELOG.md +16 -0
- package/dist/builtin/intercom/CHANGELOG.md +5 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +5 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +5 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/CHANGELOG.md +5 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +25 -0
- package/dist/builtin/workflows/README.md +62 -3
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +555 -537
- package/dist/builtin/workflows/builtin/goal.ts +5 -0
- package/dist/builtin/workflows/builtin/open-claude-design.ts +3 -3
- package/dist/builtin/workflows/builtin/ralph.ts +737 -713
- package/dist/builtin/workflows/builtin/shared-prompts.ts +11 -0
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/discovery.ts +61 -22
- package/dist/builtin/workflows/src/extension/index.ts +2 -0
- package/dist/builtin/workflows/src/extension/runtime.ts +4 -0
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +4 -0
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +96 -6
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +2 -0
- package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +7 -0
- package/dist/builtin/workflows/src/runs/shared/worktree.ts +214 -1
- package/dist/builtin/workflows/src/sdk-surface.ts +2 -0
- package/dist/builtin/workflows/src/shared/types.ts +32 -3
- package/dist/builtin/workflows/src/workflows/define-workflow.ts +18 -1
- package/dist/core/agent-session-services.d.ts +2 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +1 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +3 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +16 -5
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +40 -28
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/core/sdk.d.ts +9 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +2 -2
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/system-prompt.d.ts +2 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +22 -13
- package/dist/core/system-prompt.js.map +1 -1
- package/docs/quickstart.md +13 -5
- package/docs/sdk.md +20 -5
- package/docs/workflows.md +44 -17
- package/examples/sdk/05-tools.ts +22 -1
- package/examples/sdk/README.md +7 -3
- package/package.json +1 -1
|
@@ -9,10 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
12
|
-
import { dirname, extname, join } from "node:path";
|
|
12
|
+
import { dirname, extname, isAbsolute, join, relative } from "node:path";
|
|
13
13
|
import { defineWorkflow } from "../src/index.js";
|
|
14
14
|
import type {
|
|
15
15
|
WorkflowOutputMode,
|
|
16
|
+
WorkflowRunContext,
|
|
16
17
|
WorkflowTaskResult,
|
|
17
18
|
WorkflowTaskStep,
|
|
18
19
|
} from "../src/shared/types.js";
|
|
@@ -73,12 +74,13 @@ function positiveInteger(value: number | undefined, fallback: number): number {
|
|
|
73
74
|
: fallback;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
function countCodebaseLines(): number {
|
|
77
|
+
function countCodebaseLines(cwd = process.cwd()): number {
|
|
77
78
|
try {
|
|
78
79
|
const gitFiles = Bun.spawnSync({
|
|
79
80
|
cmd: ["git", "ls-files", "--cached", "--others", "--exclude-standard"],
|
|
80
81
|
stdout: "pipe",
|
|
81
82
|
stderr: "pipe",
|
|
83
|
+
cwd,
|
|
82
84
|
});
|
|
83
85
|
const files =
|
|
84
86
|
gitFiles.success && gitFiles.stdout
|
|
@@ -98,6 +100,7 @@ function countCodebaseLines(): number {
|
|
|
98
100
|
cmd: ["wc", "-l", "--", ...batch],
|
|
99
101
|
stdout: "pipe",
|
|
100
102
|
stderr: "pipe",
|
|
103
|
+
cwd,
|
|
101
104
|
});
|
|
102
105
|
if (!wc.stdout) continue;
|
|
103
106
|
|
|
@@ -150,7 +153,10 @@ function slugifyResearchTopic(prompt: string): string {
|
|
|
150
153
|
|
|
151
154
|
function defaultResearchDocPath(prompt: string, now = new Date()): string {
|
|
152
155
|
const date = now.toISOString().slice(0, 10);
|
|
153
|
-
return join(
|
|
156
|
+
return join(
|
|
157
|
+
DEFAULT_RESEARCH_DOC_DIR,
|
|
158
|
+
`${date}-${slugifyResearchTopic(prompt)}.md`,
|
|
159
|
+
);
|
|
154
160
|
}
|
|
155
161
|
|
|
156
162
|
function sanitizeRunId(value: string): string {
|
|
@@ -172,7 +178,10 @@ function suffixedPath(path: string, suffix: number): string {
|
|
|
172
178
|
}
|
|
173
179
|
|
|
174
180
|
function isFileExistsError(error: unknown): boolean {
|
|
175
|
-
return
|
|
181
|
+
return (
|
|
182
|
+
error instanceof Error &&
|
|
183
|
+
(error as { readonly code?: string }).code === "EEXIST"
|
|
184
|
+
);
|
|
176
185
|
}
|
|
177
186
|
|
|
178
187
|
interface DeepResearchArtifactRoot {
|
|
@@ -180,13 +189,17 @@ interface DeepResearchArtifactRoot {
|
|
|
180
189
|
readonly artifactRoot: string;
|
|
181
190
|
}
|
|
182
191
|
|
|
183
|
-
async function createArtifactRoot(
|
|
184
|
-
|
|
192
|
+
async function createArtifactRoot(
|
|
193
|
+
startedAt: Date,
|
|
194
|
+
cwd = process.cwd(),
|
|
195
|
+
): Promise<DeepResearchArtifactRoot> {
|
|
196
|
+
const researchDocDir = join(cwd, DEFAULT_RESEARCH_DOC_DIR);
|
|
197
|
+
await mkdir(researchDocDir, { recursive: true });
|
|
185
198
|
const baseRunId = timestampRunId(startedAt);
|
|
186
199
|
for (let suffix = 0; ; suffix += 1) {
|
|
187
200
|
const runId = suffix === 0 ? baseRunId : `${baseRunId}-${suffix + 1}`;
|
|
188
201
|
const artifactRoot = join(
|
|
189
|
-
|
|
202
|
+
researchDocDir,
|
|
190
203
|
`${DEEP_RESEARCH_RUN_DIR_PREFIX}${runId}`,
|
|
191
204
|
);
|
|
192
205
|
try {
|
|
@@ -208,11 +221,17 @@ interface DeepResearchManifest {
|
|
|
208
221
|
readonly artifacts: Record<string, string>;
|
|
209
222
|
}
|
|
210
223
|
|
|
211
|
-
async function writeManifest(
|
|
224
|
+
async function writeManifest(
|
|
225
|
+
path: string,
|
|
226
|
+
manifest: DeepResearchManifest,
|
|
227
|
+
): Promise<void> {
|
|
212
228
|
await writeFile(path, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
213
229
|
}
|
|
214
230
|
|
|
215
|
-
async function writeResearchDoc(
|
|
231
|
+
async function writeResearchDoc(
|
|
232
|
+
path: string,
|
|
233
|
+
content: string,
|
|
234
|
+
): Promise<string> {
|
|
216
235
|
await mkdir(dirname(path), { recursive: true });
|
|
217
236
|
|
|
218
237
|
for (let suffix = 0; ; suffix += 1) {
|
|
@@ -227,7 +246,10 @@ async function writeResearchDoc(path: string, content: string): Promise<string>
|
|
|
227
246
|
}
|
|
228
247
|
}
|
|
229
248
|
|
|
230
|
-
async function readArtifactText(
|
|
249
|
+
async function readArtifactText(
|
|
250
|
+
path: string | undefined,
|
|
251
|
+
fallback: string,
|
|
252
|
+
): Promise<string> {
|
|
231
253
|
if (path === undefined) return fallback;
|
|
232
254
|
try {
|
|
233
255
|
return await readFile(path, "utf8");
|
|
@@ -270,12 +292,13 @@ async function specialistHandoffFromArtifacts(
|
|
|
270
292
|
function manifestArtifactPaths(
|
|
271
293
|
artifactPathsByStage: ReadonlyMap<string, string>,
|
|
272
294
|
manifestPath: string,
|
|
295
|
+
display: (path: string) => string,
|
|
273
296
|
): Record<string, string> {
|
|
274
297
|
const artifacts: Record<string, string> = {};
|
|
275
298
|
for (const [stage, path] of artifactPathsByStage) {
|
|
276
|
-
artifacts[stage] =
|
|
299
|
+
artifacts[stage] = display(path);
|
|
277
300
|
}
|
|
278
|
-
artifacts.manifest =
|
|
301
|
+
artifacts.manifest = display(manifestPath);
|
|
279
302
|
return artifacts;
|
|
280
303
|
}
|
|
281
304
|
|
|
@@ -292,596 +315,591 @@ function displayPath(path: string): string {
|
|
|
292
315
|
return path.replace(/\\/g, "/");
|
|
293
316
|
}
|
|
294
317
|
|
|
318
|
+
function displayPathFrom(cwd: string, path: string): string {
|
|
319
|
+
const relativePath = relative(cwd, path);
|
|
320
|
+
if (relativePath.length === 0) return ".";
|
|
321
|
+
if (!relativePath.startsWith("..") && !isAbsolute(relativePath)) {
|
|
322
|
+
return displayPath(relativePath);
|
|
323
|
+
}
|
|
324
|
+
return displayPath(path);
|
|
325
|
+
}
|
|
326
|
+
|
|
295
327
|
function displayPaths(paths: readonly string[]): string {
|
|
296
328
|
return paths.map(displayPath).join(", ");
|
|
297
329
|
}
|
|
298
330
|
|
|
299
|
-
export
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
.
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
331
|
+
export async function runDeepResearchCodebaseWorkflow(
|
|
332
|
+
ctx: WorkflowRunContext<Record<string, unknown>>,
|
|
333
|
+
workflowStartCwd = process.cwd(),
|
|
334
|
+
): Promise<DeepResearchCodebaseResult> {
|
|
335
|
+
const inputs = ctx.inputs as {
|
|
336
|
+
prompt?: string;
|
|
337
|
+
max_partitions?: number;
|
|
338
|
+
max_concurrency?: number;
|
|
339
|
+
};
|
|
340
|
+
const prompt = inputs.prompt ?? "";
|
|
341
|
+
const requestedMaxPartitions = positiveInteger(
|
|
342
|
+
inputs.max_partitions,
|
|
343
|
+
DEFAULT_MAX_PARTITIONS,
|
|
344
|
+
);
|
|
345
|
+
const maxConcurrency = positiveInteger(
|
|
346
|
+
inputs.max_concurrency,
|
|
347
|
+
DEFAULT_MAX_CONCURRENCY,
|
|
348
|
+
);
|
|
349
|
+
const startedAt = new Date();
|
|
350
|
+
const finalResearchDocPath = join(
|
|
351
|
+
workflowStartCwd,
|
|
352
|
+
defaultResearchDocPath(prompt),
|
|
353
|
+
);
|
|
354
|
+
const codebaseLines = countCodebaseLines(workflowStartCwd);
|
|
355
|
+
const partitionCap = calculatePartitionCap(
|
|
356
|
+
requestedMaxPartitions,
|
|
357
|
+
codebaseLines,
|
|
358
|
+
);
|
|
359
|
+
const { runId, artifactRoot } = await createArtifactRoot(
|
|
360
|
+
startedAt,
|
|
361
|
+
workflowStartCwd,
|
|
362
|
+
);
|
|
363
|
+
const artifactPathsByStage = new Map<string, string>();
|
|
364
|
+
const addArtifact = (stage: string, path: string) => {
|
|
365
|
+
artifactPathsByStage.set(stage, path);
|
|
366
|
+
return path;
|
|
367
|
+
};
|
|
368
|
+
const fileOnlyOutput = (
|
|
369
|
+
output: string,
|
|
370
|
+
): {
|
|
371
|
+
output: string;
|
|
372
|
+
outputMode: WorkflowOutputMode;
|
|
373
|
+
} => ({
|
|
374
|
+
output,
|
|
375
|
+
outputMode: FILE_ONLY_OUTPUT,
|
|
376
|
+
});
|
|
377
|
+
const displayWorkflowPath = (path: string): string =>
|
|
378
|
+
displayPathFrom(workflowStartCwd, path);
|
|
379
|
+
const displayWorkflowPaths = (paths: readonly string[]): string =>
|
|
380
|
+
paths.map(displayWorkflowPath).join(", ");
|
|
381
|
+
|
|
382
|
+
const scoutPath = addArtifact(
|
|
383
|
+
"codebase-scout",
|
|
384
|
+
join(artifactRoot, "00-codebase-scout.md"),
|
|
385
|
+
);
|
|
386
|
+
const partitionPlanPath = addArtifact(
|
|
387
|
+
"partition",
|
|
388
|
+
join(artifactRoot, "01-partition-plan.md"),
|
|
389
|
+
);
|
|
390
|
+
const historyLocatorPath = addArtifact(
|
|
391
|
+
"history-locator",
|
|
392
|
+
join(artifactRoot, "01-history-locator.md"),
|
|
393
|
+
);
|
|
394
|
+
const historyAnalyzerPath = addArtifact(
|
|
395
|
+
"history-analyzer",
|
|
396
|
+
join(artifactRoot, "02-history-analyzer.md"),
|
|
397
|
+
);
|
|
355
398
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
"
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
399
|
+
const plannerModelConfig = {
|
|
400
|
+
model: "openai/gpt-5.5",
|
|
401
|
+
fallbackModels: [
|
|
402
|
+
"openai-codex/gpt-5.5",
|
|
403
|
+
"github-copilot/gpt-5.5",
|
|
404
|
+
"anthropic/claude-opus-4-7",
|
|
405
|
+
"github-copilot/claude-opus-4.7",
|
|
406
|
+
],
|
|
407
|
+
thinkingLevel: "high" as const,
|
|
408
|
+
excludeTools: ["ask_user_question"],
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const explorerModelConfig = {
|
|
412
|
+
model: "openai/gpt-5.4-mini",
|
|
413
|
+
fallbackModels: [
|
|
414
|
+
"openai-codex/gpt-5.4-mini",
|
|
415
|
+
"github-copilot/gpt-5.4-mini",
|
|
416
|
+
"anthropic/claude-haiku-4-5",
|
|
417
|
+
"github-copilot/claude-haiku-4.5",
|
|
418
|
+
],
|
|
419
|
+
thinkingLevel: "low" as const,
|
|
420
|
+
excludeTools: ["ask_user_question"],
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const initialDiscovery = await ctx.parallel(
|
|
424
|
+
[
|
|
425
|
+
{
|
|
426
|
+
name: "codebase-scout",
|
|
427
|
+
task: taggedPrompt([
|
|
428
|
+
[
|
|
429
|
+
"role",
|
|
430
|
+
"You are a senior codebase research scout preparing work for specialist agents.",
|
|
431
|
+
],
|
|
432
|
+
["objective", `Map the repository. Research question: ${prompt}`],
|
|
433
|
+
[
|
|
434
|
+
"codebase_skills",
|
|
435
|
+
codebaseSkillGuidance("locator", "analyzer", "patternFinder"),
|
|
436
|
+
],
|
|
437
|
+
[
|
|
438
|
+
"instructions",
|
|
439
|
+
[
|
|
440
|
+
"Identify the subsystems, files, tests, docs, and runtime/configuration areas most likely to answer the question.",
|
|
441
|
+
`Propose at most ${partitionCap} independent investigation partitions that can be assigned to parallel specialists.`,
|
|
442
|
+
"Ground codebase claims in concrete paths, symbols, commands, or docs when possible.",
|
|
443
|
+
"If evidence is missing or uncertain, say so explicitly instead of guessing.",
|
|
444
|
+
].join("\n"),
|
|
445
|
+
],
|
|
446
|
+
[
|
|
447
|
+
"output_format",
|
|
448
|
+
[
|
|
449
|
+
"Markdown with these headings:",
|
|
450
|
+
"1. Executive orientation",
|
|
451
|
+
"2. Key paths and why they matter",
|
|
452
|
+
"3. Suggested partitions",
|
|
453
|
+
"4. Known unknowns / risks",
|
|
454
|
+
].join("\n"),
|
|
455
|
+
],
|
|
456
|
+
]),
|
|
457
|
+
...fileOnlyOutput(scoutPath),
|
|
458
|
+
...plannerModelConfig,
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
name: "history-locator",
|
|
462
|
+
task: taggedPrompt([
|
|
463
|
+
["role", "You locate prior project research and decision history."],
|
|
464
|
+
[
|
|
465
|
+
"objective",
|
|
466
|
+
"Find existing docs, specs, ADRs, issues/PR notes, TODOs, and research artifacts relevant to the task.",
|
|
467
|
+
],
|
|
468
|
+
["task", "{task}"],
|
|
469
|
+
["codebase_skills", codebaseSkillGuidance("researchLocator")],
|
|
470
|
+
[
|
|
471
|
+
"instructions",
|
|
472
|
+
[
|
|
473
|
+
"Search broadly before narrowing.",
|
|
474
|
+
"Prefer exact file paths, section names, and short relevance notes.",
|
|
475
|
+
"Separate strong evidence from weak/possibly stale evidence.",
|
|
476
|
+
"If no prior research exists, state that plainly and list where you looked.",
|
|
477
|
+
].join("\n"),
|
|
478
|
+
],
|
|
479
|
+
[
|
|
480
|
+
"output_format",
|
|
481
|
+
"A markdown table with columns: Path, Evidence, Relevance, Confidence.",
|
|
482
|
+
],
|
|
483
|
+
]),
|
|
484
|
+
...fileOnlyOutput(historyLocatorPath),
|
|
485
|
+
...explorerModelConfig,
|
|
486
|
+
},
|
|
487
|
+
],
|
|
488
|
+
{ task: prompt, concurrency: maxConcurrency },
|
|
489
|
+
);
|
|
372
490
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
491
|
+
const scout =
|
|
492
|
+
findResult(initialDiscovery, "codebase-scout") ?? initialDiscovery[0]!;
|
|
493
|
+
const historyLocator =
|
|
494
|
+
findResult(initialDiscovery, "history-locator") ?? initialDiscovery[1]!;
|
|
495
|
+
await ctx.chain(
|
|
496
|
+
[
|
|
497
|
+
{
|
|
498
|
+
name: "history-analyzer",
|
|
499
|
+
task: taggedPrompt([
|
|
500
|
+
[
|
|
501
|
+
"role",
|
|
502
|
+
"You synthesize prior project research for downstream investigators.",
|
|
503
|
+
],
|
|
504
|
+
[
|
|
505
|
+
"objective",
|
|
506
|
+
`Extract reusable historical context. Research question: ${prompt}`,
|
|
507
|
+
],
|
|
508
|
+
["prior_research_locator_output", "{previous}"],
|
|
509
|
+
["codebase_skills", codebaseSkillGuidance("researchAnalyzer")],
|
|
510
|
+
[
|
|
511
|
+
"instructions",
|
|
512
|
+
[
|
|
513
|
+
"Cluster related prior decisions and unresolved questions.",
|
|
514
|
+
"Identify which findings are still likely valid and which may be stale.",
|
|
515
|
+
"Quote or cite paths from the locator output for every important claim.",
|
|
516
|
+
"Do not invent history that is not supported by the locator output.",
|
|
517
|
+
].join("\n"),
|
|
518
|
+
],
|
|
519
|
+
[
|
|
520
|
+
"output_format",
|
|
521
|
+
[
|
|
522
|
+
"Markdown with headings:",
|
|
523
|
+
"1. Prior decisions",
|
|
524
|
+
"2. Relevant research artifacts",
|
|
525
|
+
"3. Open questions",
|
|
526
|
+
"4. How this should steer the new investigation",
|
|
527
|
+
].join("\n"),
|
|
528
|
+
],
|
|
529
|
+
]),
|
|
530
|
+
previous: historyLocator,
|
|
531
|
+
reads: [historyLocatorPath],
|
|
532
|
+
...fileOnlyOutput(historyAnalyzerPath),
|
|
533
|
+
...plannerModelConfig,
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
{ task: prompt },
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
const partitionPlan = await ctx.task("partition", {
|
|
540
|
+
prompt: taggedPrompt([
|
|
541
|
+
["role", "You turn scout research into clean work partitions."],
|
|
542
|
+
[
|
|
543
|
+
"objective",
|
|
544
|
+
`Return at most ${partitionCap} independent partitions for this research question: ${prompt}`,
|
|
394
545
|
],
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const explorerModelConfig = {
|
|
400
|
-
model: "openai/gpt-5.4-mini",
|
|
401
|
-
fallbackModels: [
|
|
402
|
-
"openai-codex/gpt-5.4-mini",
|
|
403
|
-
"github-copilot/gpt-5.4-mini",
|
|
404
|
-
"anthropic/claude-haiku-4-5",
|
|
405
|
-
"github-copilot/claude-haiku-4.5",
|
|
546
|
+
["scout_output", "{previous}"],
|
|
547
|
+
[
|
|
548
|
+
"codebase_skills",
|
|
549
|
+
codebaseSkillGuidance("locator", "analyzer", "patternFinder"),
|
|
406
550
|
],
|
|
407
|
-
thinkingLevel: "low" as const,
|
|
408
|
-
tools: noAskQuestionToolSet,
|
|
409
|
-
};
|
|
410
|
-
|
|
411
|
-
const initialDiscovery = await ctx.parallel(
|
|
412
551
|
[
|
|
552
|
+
"instructions",
|
|
553
|
+
[
|
|
554
|
+
"Each partition must be concrete enough for one specialist to investigate independently.",
|
|
555
|
+
"Prefer boundaries based on files, subsystems, runtime layers, or documented concepts.",
|
|
556
|
+
"Do not include bullets, numbering, markdown fences, explanations, or duplicate partitions.",
|
|
557
|
+
].join("\n"),
|
|
558
|
+
],
|
|
559
|
+
["output_format", "Plain text only: one partition per line."],
|
|
560
|
+
]),
|
|
561
|
+
previous: scout,
|
|
562
|
+
output: partitionPlanPath,
|
|
563
|
+
reads: [scoutPath],
|
|
564
|
+
...plannerModelConfig,
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
const partitions = parsePartitions(partitionPlan.text, partitionCap);
|
|
568
|
+
const locatorArtifactPaths = new Map<number, string>();
|
|
569
|
+
|
|
570
|
+
const wave1Steps: WorkflowTaskStep[] = partitions.flatMap(
|
|
571
|
+
(partition, index) => {
|
|
572
|
+
const i = index + 1;
|
|
573
|
+
const locatorPath = addArtifact(
|
|
574
|
+
`locator-${i}`,
|
|
575
|
+
join(artifactRoot, `locator-${i}.md`),
|
|
576
|
+
);
|
|
577
|
+
const patternFinderPath = addArtifact(
|
|
578
|
+
`pattern-finder-${i}`,
|
|
579
|
+
join(artifactRoot, `pattern-finder-${i}.md`),
|
|
580
|
+
);
|
|
581
|
+
locatorArtifactPaths.set(i, locatorPath);
|
|
582
|
+
return [
|
|
413
583
|
{
|
|
414
|
-
name:
|
|
584
|
+
name: `locator-${i}`,
|
|
415
585
|
task: taggedPrompt([
|
|
586
|
+
["role", "You are a codebase locator specialist."],
|
|
587
|
+
["assignment", `Partition ${i}/${partitions.length}: ${partition}`],
|
|
588
|
+
["research_question", prompt],
|
|
416
589
|
[
|
|
417
|
-
"
|
|
418
|
-
|
|
419
|
-
],
|
|
420
|
-
["objective", `Map the repository. Research question: ${prompt}`],
|
|
421
|
-
[
|
|
422
|
-
"codebase_skills",
|
|
423
|
-
codebaseSkillGuidance("locator", "analyzer", "patternFinder"),
|
|
590
|
+
"scout_context",
|
|
591
|
+
`Read the scout artifact before making evidence claims: ${displayWorkflowPath(scoutPath)}\nCompact saved-output reference: {previous}`,
|
|
424
592
|
],
|
|
593
|
+
["codebase_skills", codebaseSkillGuidance("locator")],
|
|
425
594
|
[
|
|
426
595
|
"instructions",
|
|
427
596
|
[
|
|
428
|
-
"
|
|
429
|
-
|
|
430
|
-
"
|
|
431
|
-
"
|
|
597
|
+
"Find the highest-signal files, tests, docs, commands, configs, and symbols for this partition.",
|
|
598
|
+
"Explain why each path matters for the research question.",
|
|
599
|
+
"Prioritize exact paths and symbol names over broad descriptions.",
|
|
600
|
+
"Flag areas that look relevant but could not be verified.",
|
|
432
601
|
].join("\n"),
|
|
433
602
|
],
|
|
434
603
|
[
|
|
435
604
|
"output_format",
|
|
436
605
|
[
|
|
437
|
-
"Markdown with
|
|
438
|
-
"1.
|
|
439
|
-
"2.
|
|
440
|
-
"3.
|
|
441
|
-
"4.
|
|
606
|
+
"Markdown with headings:",
|
|
607
|
+
"1. Must-read paths",
|
|
608
|
+
"2. Supporting paths",
|
|
609
|
+
"3. Entry points / symbols",
|
|
610
|
+
"4. Gaps or uncertainty",
|
|
442
611
|
].join("\n"),
|
|
443
612
|
],
|
|
444
613
|
]),
|
|
445
|
-
|
|
446
|
-
|
|
614
|
+
previous: scout,
|
|
615
|
+
reads: [scoutPath],
|
|
616
|
+
...fileOnlyOutput(locatorPath),
|
|
617
|
+
...explorerModelConfig,
|
|
447
618
|
},
|
|
448
619
|
{
|
|
449
|
-
name:
|
|
620
|
+
name: `pattern-finder-${i}`,
|
|
450
621
|
task: taggedPrompt([
|
|
451
|
-
["role", "You
|
|
622
|
+
["role", "You are a codebase pattern-finding specialist."],
|
|
623
|
+
["assignment", `Partition ${i}/${partitions.length}: ${partition}`],
|
|
624
|
+
["research_question", prompt],
|
|
452
625
|
[
|
|
453
|
-
"
|
|
454
|
-
|
|
626
|
+
"scout_context",
|
|
627
|
+
`Read the scout artifact before making evidence claims: ${displayWorkflowPath(scoutPath)}\nCompact saved-output reference: {previous}`,
|
|
455
628
|
],
|
|
456
|
-
["
|
|
457
|
-
["codebase_skills", codebaseSkillGuidance("researchLocator")],
|
|
629
|
+
["codebase_skills", codebaseSkillGuidance("patternFinder")],
|
|
458
630
|
[
|
|
459
631
|
"instructions",
|
|
460
632
|
[
|
|
461
|
-
"
|
|
462
|
-
"
|
|
463
|
-
"
|
|
464
|
-
"
|
|
633
|
+
"Identify recurring implementation patterns, abstractions, naming conventions, and anti-patterns in this partition.",
|
|
634
|
+
"Use concrete examples with paths, symbols, or test names.",
|
|
635
|
+
"Distinguish established conventions from one-off implementation details.",
|
|
636
|
+
"Avoid generic advice that is not grounded in the repository.",
|
|
465
637
|
].join("\n"),
|
|
466
638
|
],
|
|
467
639
|
[
|
|
468
640
|
"output_format",
|
|
469
|
-
|
|
641
|
+
[
|
|
642
|
+
"Markdown with headings:",
|
|
643
|
+
"1. Established patterns",
|
|
644
|
+
"2. Variations / exceptions",
|
|
645
|
+
"3. Anti-patterns or risks",
|
|
646
|
+
"4. Evidence index",
|
|
647
|
+
].join("\n"),
|
|
470
648
|
],
|
|
471
649
|
]),
|
|
472
|
-
|
|
650
|
+
previous: scout,
|
|
651
|
+
reads: [scoutPath],
|
|
652
|
+
...fileOnlyOutput(patternFinderPath),
|
|
473
653
|
...explorerModelConfig,
|
|
474
654
|
},
|
|
475
|
-
]
|
|
476
|
-
|
|
477
|
-
|
|
655
|
+
];
|
|
656
|
+
},
|
|
657
|
+
);
|
|
478
658
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
659
|
+
const wave1 = await ctx.parallel(wave1Steps, {
|
|
660
|
+
task: prompt,
|
|
661
|
+
concurrency: maxConcurrency,
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
const wave2Steps: WorkflowTaskStep[] = partitions.flatMap(
|
|
665
|
+
(partition, index) => {
|
|
666
|
+
const i = index + 1;
|
|
667
|
+
const locator = findResult(wave1, `locator-${i}`);
|
|
668
|
+
const locatorPath =
|
|
669
|
+
locator === undefined ? undefined : locatorArtifactPaths.get(i);
|
|
670
|
+
const analyzerReads =
|
|
671
|
+
locatorPath === undefined ? [scoutPath] : [scoutPath, locatorPath];
|
|
672
|
+
const onlineResearcherReads =
|
|
673
|
+
locatorPath === undefined ? [scoutPath] : [locatorPath];
|
|
674
|
+
const onlineResearcherLocalContext =
|
|
675
|
+
locatorPath === undefined
|
|
676
|
+
? `Read scout context before researching: ${displayWorkflowPath(scoutPath)}\nCompact saved-output reference: {previous}`
|
|
677
|
+
: `Read local artifact context before researching: ${displayWorkflowPath(locatorPath)}\nCompact saved-output reference: {previous}`;
|
|
678
|
+
const analyzerPath = addArtifact(
|
|
679
|
+
`analyzer-${i}`,
|
|
680
|
+
join(artifactRoot, `analyzer-${i}.md`),
|
|
681
|
+
);
|
|
682
|
+
const onlineResearcherPath = addArtifact(
|
|
683
|
+
`online-${i}`,
|
|
684
|
+
join(artifactRoot, `online-${i}.md`),
|
|
685
|
+
);
|
|
686
|
+
return [
|
|
485
687
|
{
|
|
486
|
-
name:
|
|
688
|
+
name: `analyzer-${i}`,
|
|
487
689
|
task: taggedPrompt([
|
|
690
|
+
["role", "You are a codebase behavior and architecture analyzer."],
|
|
691
|
+
["assignment", `Partition ${i}/${partitions.length}: ${partition}`],
|
|
692
|
+
["research_question", prompt],
|
|
488
693
|
[
|
|
489
|
-
"
|
|
490
|
-
|
|
491
|
-
],
|
|
492
|
-
[
|
|
493
|
-
"objective",
|
|
494
|
-
`Extract reusable historical context. Research question: ${prompt}`,
|
|
694
|
+
"context",
|
|
695
|
+
`Read these artifacts before analyzing: ${displayWorkflowPaths(analyzerReads)}\nCompact saved-output reference: {previous}`,
|
|
495
696
|
],
|
|
496
|
-
["
|
|
497
|
-
["codebase_skills", codebaseSkillGuidance("researchAnalyzer")],
|
|
697
|
+
["codebase_skills", codebaseSkillGuidance("analyzer")],
|
|
498
698
|
[
|
|
499
699
|
"instructions",
|
|
500
700
|
[
|
|
501
|
-
"
|
|
502
|
-
"
|
|
503
|
-
"
|
|
504
|
-
"
|
|
701
|
+
"Analyze behavior, control flow, data flow, lifecycle, error handling, and test coverage for this partition.",
|
|
702
|
+
"Build on the locator output; do not repeat file discovery except where needed as evidence.",
|
|
703
|
+
"Call out edge cases, invariants, and coupling to other partitions.",
|
|
704
|
+
"If evidence is incomplete, explain what remains unknown and how to verify it.",
|
|
505
705
|
].join("\n"),
|
|
506
706
|
],
|
|
507
707
|
[
|
|
508
708
|
"output_format",
|
|
509
709
|
[
|
|
510
710
|
"Markdown with headings:",
|
|
511
|
-
"1.
|
|
512
|
-
"2.
|
|
513
|
-
"3.
|
|
514
|
-
"4.
|
|
711
|
+
"1. Behavioral model",
|
|
712
|
+
"2. Key flows and invariants",
|
|
713
|
+
"3. Tests / validation",
|
|
714
|
+
"4. Risks, unknowns, and verification steps",
|
|
515
715
|
].join("\n"),
|
|
516
716
|
],
|
|
517
717
|
]),
|
|
518
|
-
previous:
|
|
519
|
-
reads:
|
|
520
|
-
...fileOnlyOutput(
|
|
521
|
-
...
|
|
718
|
+
previous: locator === undefined ? scout : [scout, locator],
|
|
719
|
+
reads: analyzerReads,
|
|
720
|
+
...fileOnlyOutput(analyzerPath),
|
|
721
|
+
...explorerModelConfig,
|
|
522
722
|
},
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
"codebase_skills",
|
|
537
|
-
codebaseSkillGuidance("locator", "analyzer", "patternFinder"),
|
|
538
|
-
],
|
|
539
|
-
[
|
|
540
|
-
"instructions",
|
|
541
|
-
[
|
|
542
|
-
"Each partition must be concrete enough for one specialist to investigate independently.",
|
|
543
|
-
"Prefer boundaries based on files, subsystems, runtime layers, or documented concepts.",
|
|
544
|
-
"Do not include bullets, numbering, markdown fences, explanations, or duplicate partitions.",
|
|
545
|
-
].join("\n"),
|
|
546
|
-
],
|
|
547
|
-
["output_format", "Plain text only: one partition per line."],
|
|
548
|
-
]),
|
|
549
|
-
previous: scout,
|
|
550
|
-
output: partitionPlanPath,
|
|
551
|
-
reads: [scoutPath],
|
|
552
|
-
...plannerModelConfig,
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
const partitions = parsePartitions(partitionPlan.text, partitionCap);
|
|
556
|
-
const locatorArtifactPaths = new Map<number, string>();
|
|
557
|
-
|
|
558
|
-
const wave1Steps: WorkflowTaskStep[] = partitions.flatMap(
|
|
559
|
-
(partition, index) => {
|
|
560
|
-
const i = index + 1;
|
|
561
|
-
const locatorPath = addArtifact(
|
|
562
|
-
`locator-${i}`,
|
|
563
|
-
join(artifactRoot, `locator-${i}.md`),
|
|
564
|
-
);
|
|
565
|
-
const patternFinderPath = addArtifact(
|
|
566
|
-
`pattern-finder-${i}`,
|
|
567
|
-
join(artifactRoot, `pattern-finder-${i}.md`),
|
|
568
|
-
);
|
|
569
|
-
locatorArtifactPaths.set(i, locatorPath);
|
|
570
|
-
return [
|
|
571
|
-
{
|
|
572
|
-
name: `locator-${i}`,
|
|
573
|
-
task: taggedPrompt([
|
|
574
|
-
["role", "You are a codebase locator specialist."],
|
|
575
|
-
[
|
|
576
|
-
"assignment",
|
|
577
|
-
`Partition ${i}/${partitions.length}: ${partition}`,
|
|
578
|
-
],
|
|
579
|
-
["research_question", prompt],
|
|
580
|
-
[
|
|
581
|
-
"scout_context",
|
|
582
|
-
`Read the scout artifact before making evidence claims: ${displayPath(scoutPath)}\nCompact saved-output reference: {previous}`,
|
|
583
|
-
],
|
|
584
|
-
["codebase_skills", codebaseSkillGuidance("locator")],
|
|
585
|
-
[
|
|
586
|
-
"instructions",
|
|
587
|
-
[
|
|
588
|
-
"Find the highest-signal files, tests, docs, commands, configs, and symbols for this partition.",
|
|
589
|
-
"Explain why each path matters for the research question.",
|
|
590
|
-
"Prioritize exact paths and symbol names over broad descriptions.",
|
|
591
|
-
"Flag areas that look relevant but could not be verified.",
|
|
592
|
-
].join("\n"),
|
|
593
|
-
],
|
|
594
|
-
[
|
|
595
|
-
"output_format",
|
|
596
|
-
[
|
|
597
|
-
"Markdown with headings:",
|
|
598
|
-
"1. Must-read paths",
|
|
599
|
-
"2. Supporting paths",
|
|
600
|
-
"3. Entry points / symbols",
|
|
601
|
-
"4. Gaps or uncertainty",
|
|
602
|
-
].join("\n"),
|
|
603
|
-
],
|
|
604
|
-
]),
|
|
605
|
-
previous: scout,
|
|
606
|
-
reads: [scoutPath],
|
|
607
|
-
...fileOnlyOutput(locatorPath),
|
|
608
|
-
...explorerModelConfig,
|
|
609
|
-
},
|
|
610
|
-
{
|
|
611
|
-
name: `pattern-finder-${i}`,
|
|
612
|
-
task: taggedPrompt([
|
|
613
|
-
["role", "You are a codebase pattern-finding specialist."],
|
|
614
|
-
[
|
|
615
|
-
"assignment",
|
|
616
|
-
`Partition ${i}/${partitions.length}: ${partition}`,
|
|
617
|
-
],
|
|
618
|
-
["research_question", prompt],
|
|
619
|
-
[
|
|
620
|
-
"scout_context",
|
|
621
|
-
`Read the scout artifact before making evidence claims: ${displayPath(scoutPath)}\nCompact saved-output reference: {previous}`,
|
|
622
|
-
],
|
|
623
|
-
["codebase_skills", codebaseSkillGuidance("patternFinder")],
|
|
624
|
-
[
|
|
625
|
-
"instructions",
|
|
626
|
-
[
|
|
627
|
-
"Identify recurring implementation patterns, abstractions, naming conventions, and anti-patterns in this partition.",
|
|
628
|
-
"Use concrete examples with paths, symbols, or test names.",
|
|
629
|
-
"Distinguish established conventions from one-off implementation details.",
|
|
630
|
-
"Avoid generic advice that is not grounded in the repository.",
|
|
631
|
-
].join("\n"),
|
|
632
|
-
],
|
|
633
|
-
[
|
|
634
|
-
"output_format",
|
|
635
|
-
[
|
|
636
|
-
"Markdown with headings:",
|
|
637
|
-
"1. Established patterns",
|
|
638
|
-
"2. Variations / exceptions",
|
|
639
|
-
"3. Anti-patterns or risks",
|
|
640
|
-
"4. Evidence index",
|
|
641
|
-
].join("\n"),
|
|
642
|
-
],
|
|
643
|
-
]),
|
|
644
|
-
previous: scout,
|
|
645
|
-
reads: [scoutPath],
|
|
646
|
-
...fileOnlyOutput(patternFinderPath),
|
|
647
|
-
...explorerModelConfig,
|
|
648
|
-
},
|
|
649
|
-
];
|
|
650
|
-
},
|
|
651
|
-
);
|
|
652
|
-
|
|
653
|
-
const wave1 = await ctx.parallel(wave1Steps, {
|
|
654
|
-
task: prompt,
|
|
655
|
-
concurrency: maxConcurrency,
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
const wave2Steps: WorkflowTaskStep[] = partitions.flatMap(
|
|
659
|
-
(partition, index) => {
|
|
660
|
-
const i = index + 1;
|
|
661
|
-
const locator = findResult(wave1, `locator-${i}`);
|
|
662
|
-
const locatorPath =
|
|
663
|
-
locator === undefined ? undefined : locatorArtifactPaths.get(i);
|
|
664
|
-
const analyzerReads =
|
|
665
|
-
locatorPath === undefined ? [scoutPath] : [scoutPath, locatorPath];
|
|
666
|
-
const onlineResearcherReads =
|
|
667
|
-
locatorPath === undefined ? [scoutPath] : [locatorPath];
|
|
668
|
-
const onlineResearcherLocalContext =
|
|
669
|
-
locatorPath === undefined
|
|
670
|
-
? `Read scout context before researching: ${displayPath(scoutPath)}\nCompact saved-output reference: {previous}`
|
|
671
|
-
: `Read local artifact context before researching: ${displayPath(locatorPath)}\nCompact saved-output reference: {previous}`;
|
|
672
|
-
const analyzerPath = addArtifact(
|
|
673
|
-
`analyzer-${i}`,
|
|
674
|
-
join(artifactRoot, `analyzer-${i}.md`),
|
|
675
|
-
);
|
|
676
|
-
const onlineResearcherPath = addArtifact(
|
|
677
|
-
`online-${i}`,
|
|
678
|
-
join(artifactRoot, `online-${i}.md`),
|
|
679
|
-
);
|
|
680
|
-
return [
|
|
681
|
-
{
|
|
682
|
-
name: `analyzer-${i}`,
|
|
683
|
-
task: taggedPrompt([
|
|
684
|
-
[
|
|
685
|
-
"role",
|
|
686
|
-
"You are a codebase behavior and architecture analyzer.",
|
|
687
|
-
],
|
|
688
|
-
[
|
|
689
|
-
"assignment",
|
|
690
|
-
`Partition ${i}/${partitions.length}: ${partition}`,
|
|
691
|
-
],
|
|
692
|
-
["research_question", prompt],
|
|
693
|
-
[
|
|
694
|
-
"context",
|
|
695
|
-
`Read these artifacts before analyzing: ${displayPaths(analyzerReads)}\nCompact saved-output reference: {previous}`,
|
|
696
|
-
],
|
|
697
|
-
["codebase_skills", codebaseSkillGuidance("analyzer")],
|
|
698
|
-
[
|
|
699
|
-
"instructions",
|
|
700
|
-
[
|
|
701
|
-
"Analyze behavior, control flow, data flow, lifecycle, error handling, and test coverage for this partition.",
|
|
702
|
-
"Build on the locator output; do not repeat file discovery except where needed as evidence.",
|
|
703
|
-
"Call out edge cases, invariants, and coupling to other partitions.",
|
|
704
|
-
"If evidence is incomplete, explain what remains unknown and how to verify it.",
|
|
705
|
-
].join("\n"),
|
|
706
|
-
],
|
|
707
|
-
[
|
|
708
|
-
"output_format",
|
|
709
|
-
[
|
|
710
|
-
"Markdown with headings:",
|
|
711
|
-
"1. Behavioral model",
|
|
712
|
-
"2. Key flows and invariants",
|
|
713
|
-
"3. Tests / validation",
|
|
714
|
-
"4. Risks, unknowns, and verification steps",
|
|
715
|
-
].join("\n"),
|
|
716
|
-
],
|
|
717
|
-
]),
|
|
718
|
-
previous: locator === undefined ? scout : [scout, locator],
|
|
719
|
-
reads: analyzerReads,
|
|
720
|
-
...fileOnlyOutput(analyzerPath),
|
|
721
|
-
...explorerModelConfig,
|
|
722
|
-
},
|
|
723
|
-
{
|
|
724
|
-
name: `online-researcher-${i}`,
|
|
725
|
-
task: taggedPrompt([
|
|
726
|
-
[
|
|
727
|
-
"role",
|
|
728
|
-
"You are an ecosystem and documentation research specialist.",
|
|
729
|
-
],
|
|
730
|
-
[
|
|
731
|
-
"assignment",
|
|
732
|
-
`Partition ${i}/${partitions.length}: ${partition}`,
|
|
733
|
-
],
|
|
734
|
-
["research_question", prompt],
|
|
735
|
-
[
|
|
736
|
-
"local_context",
|
|
737
|
-
onlineResearcherLocalContext,
|
|
738
|
-
],
|
|
739
|
-
["codebase_skills", codebaseSkillGuidance("onlineResearcher")],
|
|
723
|
+
{
|
|
724
|
+
name: `online-researcher-${i}`,
|
|
725
|
+
task: taggedPrompt([
|
|
726
|
+
[
|
|
727
|
+
"role",
|
|
728
|
+
"You are an ecosystem and documentation research specialist.",
|
|
729
|
+
],
|
|
730
|
+
["assignment", `Partition ${i}/${partitions.length}: ${partition}`],
|
|
731
|
+
["research_question", prompt],
|
|
732
|
+
["local_context", onlineResearcherLocalContext],
|
|
733
|
+
["codebase_skills", codebaseSkillGuidance("onlineResearcher")],
|
|
734
|
+
[
|
|
735
|
+
"instructions",
|
|
740
736
|
[
|
|
741
|
-
"
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
737
|
+
"Identify external library/framework behavior, standards, or docs that materially affect the local interpretation.",
|
|
738
|
+
"Cite sources, package names, API names, versions, or documentation titles when available.",
|
|
739
|
+
"Explain how each external fact applies to this repository.",
|
|
740
|
+
"If external research is unnecessary or unavailable, say so and focus on local implications.",
|
|
741
|
+
].join("\n"),
|
|
742
|
+
],
|
|
743
|
+
[
|
|
744
|
+
"output_format",
|
|
749
745
|
[
|
|
750
|
-
"
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
},
|
|
767
|
-
);
|
|
746
|
+
"Markdown with headings:",
|
|
747
|
+
"1. Relevant external facts",
|
|
748
|
+
"2. Local implications",
|
|
749
|
+
"3. Version/API assumptions",
|
|
750
|
+
"4. Unverified or unnecessary research",
|
|
751
|
+
].join("\n"),
|
|
752
|
+
],
|
|
753
|
+
]),
|
|
754
|
+
previous: locator === undefined ? scout : locator,
|
|
755
|
+
reads: onlineResearcherReads,
|
|
756
|
+
...fileOnlyOutput(onlineResearcherPath),
|
|
757
|
+
...explorerModelConfig,
|
|
758
|
+
},
|
|
759
|
+
];
|
|
760
|
+
},
|
|
761
|
+
);
|
|
768
762
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
],
|
|
804
|
-
[
|
|
805
|
-
"context_artifacts",
|
|
806
|
-
[
|
|
807
|
-
`Read the scout artifact at ${displayPath(scoutPath)}.`,
|
|
808
|
-
`Read the partition plan artifact at ${displayPath(partitionPlanPath)}.`,
|
|
809
|
-
historyOverview === ""
|
|
810
|
-
? "No prior research overview artifact is available."
|
|
811
|
-
: `Read the prior research overview artifact at ${displayPath(historyAnalyzerPath)}.`,
|
|
812
|
-
].join("\n"),
|
|
813
|
-
],
|
|
763
|
+
const wave2 = await ctx.parallel(wave2Steps, {
|
|
764
|
+
task: prompt,
|
|
765
|
+
concurrency: maxConcurrency,
|
|
766
|
+
});
|
|
767
|
+
const historyOverview = await readArtifactText(historyAnalyzerPath, "");
|
|
768
|
+
const explorerPaths = await Promise.all(
|
|
769
|
+
partitions.map(async (partition, index) => {
|
|
770
|
+
const i = index + 1;
|
|
771
|
+
const explorerPath = addArtifact(
|
|
772
|
+
`explorer-${i}`,
|
|
773
|
+
join(artifactRoot, `explorer-${i}.md`),
|
|
774
|
+
);
|
|
775
|
+
const explorer = await specialistHandoffFromArtifacts(
|
|
776
|
+
partition,
|
|
777
|
+
index,
|
|
778
|
+
artifactPathsByStage,
|
|
779
|
+
);
|
|
780
|
+
await writeFile(explorerPath, explorer, "utf8");
|
|
781
|
+
return explorerPath;
|
|
782
|
+
}),
|
|
783
|
+
);
|
|
784
|
+
const aggregatorReadPaths = [
|
|
785
|
+
scoutPath,
|
|
786
|
+
partitionPlanPath,
|
|
787
|
+
...(historyOverview === "" ? [] : [historyAnalyzerPath]),
|
|
788
|
+
...explorerPaths,
|
|
789
|
+
];
|
|
790
|
+
|
|
791
|
+
const aggregate = await ctx.task("aggregator", {
|
|
792
|
+
prompt: taggedPrompt([
|
|
793
|
+
["role", "You are the final deep-research aggregator."],
|
|
794
|
+
["objective", `Answer the research question comprehensively: ${prompt}`],
|
|
795
|
+
[
|
|
796
|
+
"context_artifacts",
|
|
814
797
|
[
|
|
815
|
-
|
|
798
|
+
`Read the scout artifact at ${displayWorkflowPath(scoutPath)}.`,
|
|
799
|
+
`Read the partition plan artifact at ${displayWorkflowPath(partitionPlanPath)}.`,
|
|
816
800
|
historyOverview === ""
|
|
817
|
-
? "
|
|
818
|
-
: `Read the prior research overview artifact at ${
|
|
819
|
-
],
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
801
|
+
? "No prior research overview artifact is available."
|
|
802
|
+
: `Read the prior research overview artifact at ${displayWorkflowPath(historyAnalyzerPath)}.`,
|
|
803
|
+
].join("\n"),
|
|
804
|
+
],
|
|
805
|
+
[
|
|
806
|
+
"prior_research_overview",
|
|
807
|
+
historyOverview === ""
|
|
808
|
+
? "(no prior research found)"
|
|
809
|
+
: `Read the prior research overview artifact at ${displayWorkflowPath(historyAnalyzerPath)}.`,
|
|
810
|
+
],
|
|
811
|
+
[
|
|
812
|
+
"specialist_reports",
|
|
813
|
+
`Read the complete explorer handoff artifact(s) at ${displayWorkflowPaths(explorerPaths)}. They preserve every partition's Locator, Pattern Finder, Analyzer, and Online Researcher output from the original inline specialist handoff while keeping this prompt bounded.`,
|
|
814
|
+
],
|
|
815
|
+
[
|
|
816
|
+
"codebase_skills",
|
|
817
|
+
codebaseSkillGuidance(
|
|
818
|
+
"analyzer",
|
|
819
|
+
"researchAnalyzer",
|
|
820
|
+
"onlineResearcher",
|
|
821
|
+
),
|
|
822
|
+
],
|
|
823
|
+
[
|
|
824
|
+
"instructions",
|
|
832
825
|
[
|
|
833
|
-
"
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
826
|
+
"Synthesize; do not merely concatenate specialist reports.",
|
|
827
|
+
"Use the supplied input files as the source of detailed scout, partition, history, and specialist evidence instead of relying on inline transcripts.",
|
|
828
|
+
"Prioritize claims supported by concrete paths, symbols, tests, docs, or cited external references.",
|
|
829
|
+
"Resolve contradictions explicitly and preserve important uncertainty.",
|
|
830
|
+
"Avoid inventing facts not supported by the supplied reports; state unknowns instead.",
|
|
831
|
+
"End with actionable next steps for a developer who will use this research.",
|
|
832
|
+
].join("\n"),
|
|
833
|
+
],
|
|
834
|
+
[
|
|
835
|
+
"output_format",
|
|
843
836
|
[
|
|
844
|
-
"
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
837
|
+
"Markdown with headings:",
|
|
838
|
+
"1. Executive answer",
|
|
839
|
+
"2. Architecture / behavior findings",
|
|
840
|
+
"3. Evidence by partition",
|
|
841
|
+
"4. Risks and unknowns",
|
|
842
|
+
"5. Recommended next steps",
|
|
843
|
+
].join("\n"),
|
|
844
|
+
],
|
|
845
|
+
]),
|
|
846
|
+
reads: aggregatorReadPaths,
|
|
847
|
+
...explorerModelConfig,
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
const writtenResearchDocPath = await writeResearchDoc(
|
|
851
|
+
finalResearchDocPath,
|
|
852
|
+
aggregate.text,
|
|
853
|
+
);
|
|
854
|
+
const manifestPath = join(artifactRoot, "manifest.json");
|
|
855
|
+
const completedAt = new Date();
|
|
856
|
+
await writeManifest(manifestPath, {
|
|
857
|
+
runId,
|
|
858
|
+
startedAt: startedAt.toISOString(),
|
|
859
|
+
completedAt: completedAt.toISOString(),
|
|
860
|
+
researchQuestion: prompt,
|
|
861
|
+
finalAsset: displayWorkflowPath(writtenResearchDocPath),
|
|
862
|
+
artifacts: manifestArtifactPaths(
|
|
863
|
+
artifactPathsByStage,
|
|
864
|
+
manifestPath,
|
|
865
|
+
displayWorkflowPath,
|
|
866
|
+
),
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
const result: DeepResearchCodebaseResult = {
|
|
870
|
+
findings: aggregate.text,
|
|
871
|
+
research_doc_path: displayWorkflowPath(writtenResearchDocPath),
|
|
872
|
+
artifact_dir: displayWorkflowPath(artifactRoot),
|
|
873
|
+
manifest_path: displayWorkflowPath(manifestPath),
|
|
874
|
+
partitions,
|
|
875
|
+
explorer_count: partitions.length,
|
|
876
|
+
specialist_count: wave1.length + wave2.length,
|
|
877
|
+
max_concurrency: maxConcurrency,
|
|
878
|
+
history: historyOverview,
|
|
879
|
+
};
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
873
882
|
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
883
|
+
export default defineWorkflow("deep-research-codebase")
|
|
884
|
+
.description(
|
|
885
|
+
"Scout + research-history chain → parallel specialist waves → aggregator for deep codebase research.",
|
|
886
|
+
)
|
|
887
|
+
.input("prompt", {
|
|
888
|
+
type: "text",
|
|
889
|
+
required: true,
|
|
890
|
+
description: "Research question or investigation focus for the codebase.",
|
|
891
|
+
})
|
|
892
|
+
.input("max_partitions", {
|
|
893
|
+
type: "number",
|
|
894
|
+
default: DEFAULT_MAX_PARTITIONS,
|
|
895
|
+
description:
|
|
896
|
+
"Maximum number of codebase partitions to explore in parallel. Actual partitions scale by one per 10K LoC, capped by this value.",
|
|
897
|
+
})
|
|
898
|
+
.input("max_concurrency", {
|
|
899
|
+
type: "number",
|
|
900
|
+
default: DEFAULT_MAX_CONCURRENCY,
|
|
901
|
+
description:
|
|
902
|
+
"Maximum number of workflow stages to run concurrently during deep research.",
|
|
886
903
|
})
|
|
904
|
+
.run(async (ctx) => runDeepResearchCodebaseWorkflow(ctx, ctx.cwd))
|
|
887
905
|
.compile();
|