@bastani/atomic 0.8.22-0 → 0.8.23-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 +38 -0
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +6 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +13 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/CHANGELOG.md +6 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +35 -0
- package/dist/builtin/workflows/README.md +31 -12
- package/dist/builtin/workflows/builtin/goal.ts +139 -100
- package/dist/builtin/workflows/builtin/ralph.ts +137 -182
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.ts +2 -4
- package/dist/builtin/workflows/src/tui/stage-chat-view.ts +110 -13
- package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +2 -2
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +8 -4
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.d.ts.map +1 -1
- package/dist/core/tools/ask-user-question/ask-user-question.js +31 -11
- package/dist/core/tools/ask-user-question/ask-user-question.js.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.d.ts +8 -0
- package/dist/modes/interactive/components/chat-session-host.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-session-host.js +83 -2
- package/dist/modes/interactive/components/chat-session-host.js.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.d.ts +12 -1
- package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-transcript.js +140 -13
- package/dist/modes/interactive/components/chat-transcript.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -1
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/docs/workflows.md +66 -17
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Builtin workflow: ralph
|
|
3
3
|
*
|
|
4
4
|
* Re-implements the Atomic SDK Ralph design with the local workflow task
|
|
5
|
-
* primitives: bounded plan → orchestrate → simplify →
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* primitives: bounded plan → orchestrate → simplify → review iterations.
|
|
6
|
+
* Reviewer passes fan out with ctx.parallel(); each iteration feeds review
|
|
7
|
+
* findings into the next planner with ctx.task().
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
|
|
@@ -311,9 +311,7 @@ function parseReviewDecision(text: string): ReviewDecision | undefined {
|
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
-
function
|
|
315
|
-
const decision = parseReviewDecision(text);
|
|
316
|
-
if (decision === undefined) return false;
|
|
314
|
+
function reviewDecisionApproved(decision: ReviewDecision): boolean {
|
|
317
315
|
return (
|
|
318
316
|
decision.stop_review_loop === true &&
|
|
319
317
|
decision.overall_correctness === "patch is correct" &&
|
|
@@ -322,10 +320,8 @@ function reviewApproved(text: string): boolean {
|
|
|
322
320
|
);
|
|
323
321
|
}
|
|
324
322
|
|
|
325
|
-
function
|
|
326
|
-
|
|
327
|
-
): WorkflowTaskResult {
|
|
328
|
-
const decision: ReviewDecision = {
|
|
323
|
+
function reviewerErrorDecision(error: string): ReviewDecision {
|
|
324
|
+
return {
|
|
329
325
|
findings: [],
|
|
330
326
|
overall_correctness: "patch is incorrect",
|
|
331
327
|
overall_explanation:
|
|
@@ -339,33 +335,54 @@ function reviewerErrorResult(
|
|
|
339
335
|
"Model fallbacks were configured for the reviewer stage; continuing the bounded loop without approval.",
|
|
340
336
|
},
|
|
341
337
|
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function reviewerErrorResult(
|
|
341
|
+
error: string,
|
|
342
|
+
): WorkflowTaskResult {
|
|
342
343
|
return {
|
|
343
344
|
name: "reviewer-error",
|
|
344
345
|
stageName: "reviewer-error",
|
|
345
|
-
text: JSON.stringify(
|
|
346
|
+
text: JSON.stringify(reviewerErrorDecision(error), null, 2),
|
|
346
347
|
};
|
|
347
348
|
}
|
|
348
349
|
|
|
349
|
-
function
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
return
|
|
350
|
+
function artifactSafeName(value: string): string {
|
|
351
|
+
const safe = value
|
|
352
|
+
.toLowerCase()
|
|
353
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
354
|
+
.replace(/^-+|-+$/g, "");
|
|
355
|
+
return safe.length > 0 ? safe : "artifact";
|
|
355
356
|
}
|
|
356
357
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
358
|
+
type ReviewArtifact = {
|
|
359
|
+
readonly iteration: number;
|
|
360
|
+
readonly reviewer: string;
|
|
361
|
+
readonly decision: ReviewDecision;
|
|
362
|
+
readonly raw_text: string;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
type ReviewRoundArtifact = {
|
|
366
|
+
readonly iteration: number;
|
|
367
|
+
readonly reviews: readonly {
|
|
368
|
+
readonly reviewer: string;
|
|
369
|
+
readonly artifact_path: string;
|
|
370
|
+
readonly decision: ReviewDecision;
|
|
371
|
+
}[];
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
async function writeJsonArtifact(path: string, content: ReviewArtifact | ReviewRoundArtifact): Promise<string> {
|
|
375
|
+
await mkdir(dirname(path), { recursive: true });
|
|
376
|
+
await writeFile(path, `${JSON.stringify(content, null, 2)}\n`, {
|
|
377
|
+
encoding: "utf8",
|
|
378
|
+
});
|
|
379
|
+
return path;
|
|
363
380
|
}
|
|
364
381
|
|
|
365
|
-
function
|
|
366
|
-
return
|
|
367
|
-
|
|
368
|
-
|
|
382
|
+
function compactReviewReport(path: string | undefined): string {
|
|
383
|
+
return path === undefined
|
|
384
|
+
? "No reviewer artifact was produced."
|
|
385
|
+
: `Latest review round artifact: ${path}`;
|
|
369
386
|
}
|
|
370
387
|
|
|
371
388
|
type RalphInputs = {
|
|
@@ -391,6 +408,7 @@ type RalphWorkflowResult = {
|
|
|
391
408
|
readonly approved: boolean;
|
|
392
409
|
readonly iterations_completed: number;
|
|
393
410
|
readonly review_report: string;
|
|
411
|
+
readonly review_report_path?: string;
|
|
394
412
|
};
|
|
395
413
|
|
|
396
414
|
async function runRalphWorkflow(
|
|
@@ -399,7 +417,7 @@ async function runRalphWorkflow(
|
|
|
399
417
|
): Promise<RalphWorkflowResult> {
|
|
400
418
|
const { prompt, maxLoops, comparisonBaseBranch, workflowStartCwd } = options;
|
|
401
419
|
|
|
402
|
-
let
|
|
420
|
+
let latestReviewReportPath: string | undefined;
|
|
403
421
|
let finalPlan = "";
|
|
404
422
|
let finalPlanPath = "";
|
|
405
423
|
let finalResult = "";
|
|
@@ -409,6 +427,7 @@ async function runRalphWorkflow(
|
|
|
409
427
|
// worktree cwd so specs and stage writes land in the same checkout.
|
|
410
428
|
const workflowSpecPath = resolve(workflowStartCwd, defaultSpecPath(prompt));
|
|
411
429
|
const implementationNotesPath = await createImplementationNotesFile(prompt);
|
|
430
|
+
const artifactDir = await mkdtemp(join(tmpdir(), "atomic-ralph-run-"));
|
|
412
431
|
let approved = false;
|
|
413
432
|
let iterationsCompleted = 0;
|
|
414
433
|
|
|
@@ -461,18 +480,6 @@ async function runRalphWorkflow(
|
|
|
461
480
|
customTools: [reviewDecisionTool],
|
|
462
481
|
};
|
|
463
482
|
|
|
464
|
-
const explorerModelConfig = {
|
|
465
|
-
model: "openai/gpt-5.4-mini",
|
|
466
|
-
fallbackModels: [
|
|
467
|
-
"openai-codex/gpt-5.4-mini",
|
|
468
|
-
"github-copilot/gpt-5.4-mini",
|
|
469
|
-
"anthropic/claude-haiku-4-5",
|
|
470
|
-
"github-copilot/claude-haiku-4.5",
|
|
471
|
-
],
|
|
472
|
-
thinkingLevel: "low" as const,
|
|
473
|
-
excludedTools: ["ask_user_question"],
|
|
474
|
-
};
|
|
475
|
-
|
|
476
483
|
for (let iteration = 1; iteration <= maxLoops; iteration += 1) {
|
|
477
484
|
iterationsCompleted = iteration;
|
|
478
485
|
|
|
@@ -495,10 +502,14 @@ async function runRalphWorkflow(
|
|
|
495
502
|
`Plan iteration ${iteration}/${maxLoops} for this user specification:\n${prompt}`,
|
|
496
503
|
],
|
|
497
504
|
[
|
|
498
|
-
"
|
|
499
|
-
|
|
500
|
-
? "
|
|
501
|
-
:
|
|
505
|
+
"latest_review_artifact",
|
|
506
|
+
latestReviewReportPath === undefined
|
|
507
|
+
? "No prior review artifact; this is the first iteration."
|
|
508
|
+
: [
|
|
509
|
+
`Latest review round artifact: ${latestReviewReportPath}`,
|
|
510
|
+
"Read this JSON artifact incrementally and address only unresolved findings from the latest review round.",
|
|
511
|
+
"Do not rely on an injected review transcript or older review history unless the latest artifact explicitly points you there.",
|
|
512
|
+
].join("\n"),
|
|
502
513
|
],
|
|
503
514
|
[
|
|
504
515
|
"spec_revision_target",
|
|
@@ -568,16 +579,19 @@ async function runRalphWorkflow(
|
|
|
568
579
|
],
|
|
569
580
|
["rfc_template", PLANNER_RFC_TEMPLATE],
|
|
570
581
|
]),
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
:
|
|
574
|
-
|
|
582
|
+
reads: [
|
|
583
|
+
...(iteration > 1 ? [workflowSpecPath] : []),
|
|
584
|
+
...(latestReviewReportPath === undefined ? [] : [latestReviewReportPath]),
|
|
585
|
+
],
|
|
575
586
|
...plannerModelConfig,
|
|
576
587
|
});
|
|
577
588
|
finalPlan = planner.text;
|
|
578
589
|
const specPath = await writeSpecFile(workflowSpecPath, planner.text);
|
|
579
590
|
finalPlanPath = specPath;
|
|
580
591
|
|
|
592
|
+
const orchestratorReportPath = join(artifactDir, `orchestrator-${iteration}.md`);
|
|
593
|
+
const simplifierReportPath = join(artifactDir, `code-simplifier-${iteration}.md`);
|
|
594
|
+
|
|
581
595
|
const orchestrator = await ctx.task(`orchestrator-${iteration}`, {
|
|
582
596
|
prompt: taggedPrompt([
|
|
583
597
|
[
|
|
@@ -670,9 +684,11 @@ async function runRalphWorkflow(
|
|
|
670
684
|
],
|
|
671
685
|
]),
|
|
672
686
|
reads: [specPath, implementationNotesPath],
|
|
687
|
+
output: orchestratorReportPath,
|
|
688
|
+
outputMode: "file-only",
|
|
673
689
|
...orchestratorModelConfig,
|
|
674
690
|
});
|
|
675
|
-
finalResult = orchestrator.text
|
|
691
|
+
finalResult = orchestrator.text || `Orchestrator report artifact: ${orchestratorReportPath}`;
|
|
676
692
|
|
|
677
693
|
await ctx.task(`code-simplifier-${iteration}`, {
|
|
678
694
|
prompt: taggedPrompt([
|
|
@@ -688,7 +704,15 @@ async function runRalphWorkflow(
|
|
|
688
704
|
"objective",
|
|
689
705
|
`Refine recently modified code for this task while preserving exact behavior: ${prompt}`,
|
|
690
706
|
],
|
|
691
|
-
[
|
|
707
|
+
[
|
|
708
|
+
"artifact_handoff",
|
|
709
|
+
[
|
|
710
|
+
`Spec artifact: ${specPath}`,
|
|
711
|
+
`Implementation notes artifact: ${implementationNotesPath}`,
|
|
712
|
+
`Orchestrator report artifact: ${orchestratorReportPath}`,
|
|
713
|
+
"Read these artifacts incrementally only when needed to identify recently modified files and intent; do not depend on an injected planner/orchestrator transcript tail.",
|
|
714
|
+
].join("\n"),
|
|
715
|
+
],
|
|
692
716
|
[
|
|
693
717
|
"functionality_preservation",
|
|
694
718
|
[
|
|
@@ -767,131 +791,12 @@ async function runRalphWorkflow(
|
|
|
767
791
|
].join("\n"),
|
|
768
792
|
],
|
|
769
793
|
]),
|
|
770
|
-
|
|
794
|
+
reads: [specPath, implementationNotesPath, orchestratorReportPath],
|
|
795
|
+
output: simplifierReportPath,
|
|
796
|
+
outputMode: "file-only",
|
|
771
797
|
...simplifierModelConfig,
|
|
772
798
|
});
|
|
773
799
|
|
|
774
|
-
const discovery = await ctx.parallel(
|
|
775
|
-
[
|
|
776
|
-
{
|
|
777
|
-
name: `infra-locate-${iteration}`,
|
|
778
|
-
task: taggedPrompt([
|
|
779
|
-
[
|
|
780
|
-
"role",
|
|
781
|
-
"You locate project infrastructure needed for patch review.",
|
|
782
|
-
],
|
|
783
|
-
[
|
|
784
|
-
"objective",
|
|
785
|
-
`Find review-relevant infrastructure for the task: ${prompt}`,
|
|
786
|
-
],
|
|
787
|
-
[
|
|
788
|
-
"stage_contract",
|
|
789
|
-
[
|
|
790
|
-
"This is a repository-discovery stage. Do not answer from assumptions or common project layouts.",
|
|
791
|
-
"Before output, inspect the repository for each infrastructure category: package scripts, test configs, CI workflows, generated artifacts, lint/typecheck setup, and release gates.",
|
|
792
|
-
"The table is a compact handoff after discovery, not a substitute for discovery.",
|
|
793
|
-
].join("\n"),
|
|
794
|
-
],
|
|
795
|
-
[
|
|
796
|
-
"instructions",
|
|
797
|
-
[
|
|
798
|
-
"Locate package scripts, test configs, CI workflows, generated artifacts, lint/typecheck setup, and release gates.",
|
|
799
|
-
"Search/read relevant files such as package manifests, CI workflow directories, test configs, lint/typecheck configs, build scripts, release configs, and generated-artifact markers.",
|
|
800
|
-
"Prefer exact file paths and commands.",
|
|
801
|
-
"Explain how each item should influence review or validation.",
|
|
802
|
-
"If a category does not exist, report `not found` and briefly name the paths or patterns checked.",
|
|
803
|
-
].join("\n"),
|
|
804
|
-
],
|
|
805
|
-
[
|
|
806
|
-
"output_format",
|
|
807
|
-
"Markdown table: Area | Path/command | Why it matters | Confidence.",
|
|
808
|
-
],
|
|
809
|
-
]),
|
|
810
|
-
...explorerModelConfig,
|
|
811
|
-
},
|
|
812
|
-
{
|
|
813
|
-
name: `infra-analyze-${iteration}`,
|
|
814
|
-
task: taggedPrompt([
|
|
815
|
-
[
|
|
816
|
-
"role",
|
|
817
|
-
"You analyze integration risks in project infrastructure.",
|
|
818
|
-
],
|
|
819
|
-
[
|
|
820
|
-
"objective",
|
|
821
|
-
`Assess infrastructure and changed-code risks for the task: ${prompt}`,
|
|
822
|
-
],
|
|
823
|
-
[
|
|
824
|
-
"stage_contract",
|
|
825
|
-
[
|
|
826
|
-
"This stage analyzes actual repository coupling, not generic integration risks.",
|
|
827
|
-
"Before output, inspect the changed-code context plus relevant infrastructure/configuration files discovered or inferable from the repo.",
|
|
828
|
-
"Classify a risk as confirmed only when repository evidence shows the coupling; otherwise mark it speculative.",
|
|
829
|
-
].join("\n"),
|
|
830
|
-
],
|
|
831
|
-
[
|
|
832
|
-
"instructions",
|
|
833
|
-
[
|
|
834
|
-
"Identify hidden coupling with build, tests, linting, runtime config, release automation, or generated files.",
|
|
835
|
-
"Name the exact validations that would most efficiently detect regressions.",
|
|
836
|
-
"Separate confirmed risks from speculative risks.",
|
|
837
|
-
"Do not repeat generic review advice; ground findings in repository evidence.",
|
|
838
|
-
"Copy validation commands from actual repository scripts/configs when available; do not invent commands that are not supported by the repo.",
|
|
839
|
-
].join("\n"),
|
|
840
|
-
],
|
|
841
|
-
[
|
|
842
|
-
"evidence_expectations",
|
|
843
|
-
"Each confirmed risk must include concrete evidence: path, command, symbol, config key, script name, or file relationship.",
|
|
844
|
-
],
|
|
845
|
-
[
|
|
846
|
-
"output_format",
|
|
847
|
-
"Markdown with sections: Confirmed risks, Speculative risks, Validation commands, Evidence.",
|
|
848
|
-
],
|
|
849
|
-
]),
|
|
850
|
-
...explorerModelConfig,
|
|
851
|
-
},
|
|
852
|
-
{
|
|
853
|
-
name: `infra-patterns-${iteration}`,
|
|
854
|
-
task: taggedPrompt([
|
|
855
|
-
["role", "You find repository patterns that a patch must follow."],
|
|
856
|
-
[
|
|
857
|
-
"objective",
|
|
858
|
-
`Extract conventions relevant to reviewing this task: ${prompt}`,
|
|
859
|
-
],
|
|
860
|
-
[
|
|
861
|
-
"stage_contract",
|
|
862
|
-
[
|
|
863
|
-
"This is an evidence-gathering stage for repository conventions. Do not describe generic best practices.",
|
|
864
|
-
"Before output, find concrete examples in the repository that demonstrate conventions relevant to this task.",
|
|
865
|
-
"Read enough of each example to understand the convention before reporting it.",
|
|
866
|
-
].join("\n"),
|
|
867
|
-
],
|
|
868
|
-
[
|
|
869
|
-
"instructions",
|
|
870
|
-
[
|
|
871
|
-
"Find examples of build/test/style/release/architecture patterns the patch should mirror.",
|
|
872
|
-
"Search for nearby or analogous implementations, tests, configs, scripts, and docs.",
|
|
873
|
-
"Use concrete paths, commands, or symbols as evidence.",
|
|
874
|
-
"Highlight conventions that commonly cause subtle review failures.",
|
|
875
|
-
"If examples conflict, describe the conflict instead of forcing a single rule.",
|
|
876
|
-
"If no relevant example exists, state what was searched and that no pattern was found.",
|
|
877
|
-
].join("\n"),
|
|
878
|
-
],
|
|
879
|
-
[
|
|
880
|
-
"handoff_expectations",
|
|
881
|
-
"For every required convention or useful example, include the supporting path, command, symbol, or file relationship so reviewers can verify it quickly.",
|
|
882
|
-
],
|
|
883
|
-
[
|
|
884
|
-
"output_format",
|
|
885
|
-
"Markdown with sections: Required conventions, Useful examples, Exceptions, Review implications.",
|
|
886
|
-
],
|
|
887
|
-
]),
|
|
888
|
-
...explorerModelConfig,
|
|
889
|
-
},
|
|
890
|
-
],
|
|
891
|
-
{ task: prompt },
|
|
892
|
-
);
|
|
893
|
-
|
|
894
|
-
const discoveryContext = formatDiscovery(discovery);
|
|
895
800
|
const reviewPrompt = taggedPrompt([
|
|
896
801
|
[
|
|
897
802
|
"role",
|
|
@@ -910,7 +815,16 @@ async function runRalphWorkflow(
|
|
|
910
815
|
`Start with \`git status --short\`, then use working-tree-aware commands such as \`git diff ${comparisonBaseBranch}\` and \`git diff --cached ${comparisonBaseBranch}\` to identify changed tracked files; inspect untracked files from status directly.`,
|
|
911
816
|
].join("\n"),
|
|
912
817
|
],
|
|
913
|
-
[
|
|
818
|
+
[
|
|
819
|
+
"review_context_files",
|
|
820
|
+
[
|
|
821
|
+
`Spec artifact: ${specPath}`,
|
|
822
|
+
`Implementation notes artifact: ${implementationNotesPath}`,
|
|
823
|
+
`Orchestrator report artifact: ${orchestratorReportPath}`,
|
|
824
|
+
`Simplifier report artifact: ${simplifierReportPath}`,
|
|
825
|
+
"Read the files above incrementally when they help explain intent or recent changes, but verify the actual repository state directly before approving.",
|
|
826
|
+
].join("\n"),
|
|
827
|
+
],
|
|
914
828
|
[
|
|
915
829
|
"project_guidance",
|
|
916
830
|
[
|
|
@@ -998,7 +912,7 @@ async function runRalphWorkflow(
|
|
|
998
912
|
"The review loop decides whether to stop only by parsing the JSON object returned by this tool; invalid JSON, missing fields, reviewer_error, or stop_review_loop=false are treated as not approved for safety.",
|
|
999
913
|
"Set stop_review_loop=true only when findings is empty, overall_correctness is patch is correct, and reviewer_error is null/omitted.",
|
|
1000
914
|
"If you hit a reviewer/tool/validation error, still return the object with stop_review_loop=false and reviewer_error populated instead of pretending the patch is approved.",
|
|
1001
|
-
"The JSON
|
|
915
|
+
"The review_decision tool schema is authoritative; do not copy a hand-written JSON blob into the final response. Here is an example output:",
|
|
1002
916
|
"{",
|
|
1003
917
|
' "findings": [',
|
|
1004
918
|
" {",
|
|
@@ -1015,6 +929,9 @@ async function runRalphWorkflow(
|
|
|
1015
929
|
' "overall_correctness": "patch is correct" | "patch is incorrect",',
|
|
1016
930
|
' "overall_explanation": "<1-3 sentence explanation justifying the verdict>",',
|
|
1017
931
|
' "overall_confidence_score": <float 0.0-1.0>,',
|
|
932
|
+
' "goal_oracle_satisfied": <boolean>,',
|
|
933
|
+
' "receipt_assessment": "<how receipts/current evidence map to the verification oracle>",',
|
|
934
|
+
' "verification_remaining": "<oracle-relevant verification still missing, or none>",',
|
|
1018
935
|
' "stop_review_loop": <boolean>,',
|
|
1019
936
|
' "reviewer_error": null | {"kind": "validation_unavailable" | "dependency_unavailable" | "tool_failure" | "reviewer_failure", "message": "<what failed>", "attempted_recovery": "<what you tried>"}',
|
|
1020
937
|
"}",
|
|
@@ -1029,11 +946,23 @@ async function runRalphWorkflow(
|
|
|
1029
946
|
{
|
|
1030
947
|
name: "reviewer-a",
|
|
1031
948
|
task: reviewPrompt,
|
|
949
|
+
reads: [
|
|
950
|
+
specPath,
|
|
951
|
+
implementationNotesPath,
|
|
952
|
+
orchestratorReportPath,
|
|
953
|
+
simplifierReportPath,
|
|
954
|
+
],
|
|
1032
955
|
...reviewerModelConfig,
|
|
1033
956
|
},
|
|
1034
957
|
{
|
|
1035
958
|
name: "reviewer-b",
|
|
1036
959
|
task: reviewPrompt,
|
|
960
|
+
reads: [
|
|
961
|
+
specPath,
|
|
962
|
+
implementationNotesPath,
|
|
963
|
+
orchestratorReportPath,
|
|
964
|
+
simplifierReportPath,
|
|
965
|
+
],
|
|
1037
966
|
...reviewerModelConfig,
|
|
1038
967
|
},
|
|
1039
968
|
],
|
|
@@ -1044,10 +973,29 @@ async function runRalphWorkflow(
|
|
|
1044
973
|
reviews = [reviewerErrorResult(message)];
|
|
1045
974
|
}
|
|
1046
975
|
|
|
976
|
+
const reviewEntries = await Promise.all(reviews.map(async (review) => {
|
|
977
|
+
const reviewer = review.name ?? review.stageName;
|
|
978
|
+
const decision = parseReviewDecision(review.text) ??
|
|
979
|
+
reviewerErrorDecision(`Reviewer ${reviewer} returned invalid structured JSON.`);
|
|
980
|
+
const artifactPath = join(
|
|
981
|
+
artifactDir,
|
|
982
|
+
`review-${iteration}-${artifactSafeName(reviewer)}.json`,
|
|
983
|
+
);
|
|
984
|
+
await writeJsonArtifact(artifactPath, {
|
|
985
|
+
iteration,
|
|
986
|
+
reviewer,
|
|
987
|
+
decision,
|
|
988
|
+
raw_text: review.text,
|
|
989
|
+
});
|
|
990
|
+
return { reviewer, artifact_path: artifactPath, decision };
|
|
991
|
+
}));
|
|
1047
992
|
approved =
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
993
|
+
reviewEntries.length > 0 &&
|
|
994
|
+
reviewEntries.every((review) => reviewDecisionApproved(review.decision));
|
|
995
|
+
latestReviewReportPath = await writeJsonArtifact(
|
|
996
|
+
join(artifactDir, `review-round-${iteration}.json`),
|
|
997
|
+
{ iteration, reviews: reviewEntries },
|
|
998
|
+
);
|
|
1051
999
|
if (approved) break;
|
|
1052
1000
|
}
|
|
1053
1001
|
|
|
@@ -1070,6 +1018,9 @@ async function runRalphWorkflow(
|
|
|
1070
1018
|
? `Planner spec path: ${finalPlanPath}`
|
|
1071
1019
|
: "Planner spec path: unavailable",
|
|
1072
1020
|
`Implementation notes path: ${implementationNotesPath}`,
|
|
1021
|
+
latestReviewReportPath === undefined
|
|
1022
|
+
? "Latest review artifact: unavailable"
|
|
1023
|
+
: `Latest review artifact: ${latestReviewReportPath}`,
|
|
1073
1024
|
].join("\n"),
|
|
1074
1025
|
],
|
|
1075
1026
|
[
|
|
@@ -1109,9 +1060,11 @@ async function runRalphWorkflow(
|
|
|
1109
1060
|
].join("\n"),
|
|
1110
1061
|
],
|
|
1111
1062
|
]),
|
|
1112
|
-
reads:
|
|
1113
|
-
? [finalPlanPath
|
|
1114
|
-
|
|
1063
|
+
reads: [
|
|
1064
|
+
...(finalPlanPath ? [finalPlanPath] : []),
|
|
1065
|
+
implementationNotesPath,
|
|
1066
|
+
...(latestReviewReportPath === undefined ? [] : [latestReviewReportPath]),
|
|
1067
|
+
],
|
|
1115
1068
|
...orchestratorModelConfig,
|
|
1116
1069
|
});
|
|
1117
1070
|
finalPrReport = prResult.text;
|
|
@@ -1124,7 +1077,8 @@ async function runRalphWorkflow(
|
|
|
1124
1077
|
pr_report: finalPrReport,
|
|
1125
1078
|
approved,
|
|
1126
1079
|
iterations_completed: iterationsCompleted,
|
|
1127
|
-
review_report:
|
|
1080
|
+
review_report: compactReviewReport(latestReviewReportPath),
|
|
1081
|
+
...(latestReviewReportPath === undefined ? {} : { review_report_path: latestReviewReportPath }),
|
|
1128
1082
|
};
|
|
1129
1083
|
}
|
|
1130
1084
|
|
|
@@ -1157,7 +1111,8 @@ export default defineWorkflow("ralph")
|
|
|
1157
1111
|
.output("pr_report", Type.Optional(Type.String({ description: "Pull-request preparation report with diff review, PR status, commands, and follow-up steps." })))
|
|
1158
1112
|
.output("approved", Type.Optional(Type.Boolean({ description: "Whether the reviewer loop approved before PR handoff." })))
|
|
1159
1113
|
.output("iterations_completed", Type.Optional(Type.Number({ description: "Number of plan/orchestrate/review loops completed." })))
|
|
1160
|
-
.output("review_report", Type.Optional(Type.String({ description: "
|
|
1114
|
+
.output("review_report", Type.Optional(Type.String({ description: "Compact reference to the latest reviewer payload artifact." })))
|
|
1115
|
+
.output("review_report_path", Type.Optional(Type.String({ description: "JSON artifact path for the latest Ralph review round." })))
|
|
1161
1116
|
.run(async (ctx) => {
|
|
1162
1117
|
const workflowCtx = ctx;
|
|
1163
1118
|
const workflowStartCwd = workflowCtx.cwd ?? process.cwd();
|
|
@@ -125,6 +125,7 @@ export const WORKFLOW_TOOL_DESCRIPTION =
|
|
|
125
125
|
"Run named workflows or direct one-off task/tasks/chain workflows; " +
|
|
126
126
|
"discover with list/get/inputs, inspect status/stages/stage details, " +
|
|
127
127
|
"send prompt answers or steering, pause/resume/interrupt/kill runs, and reload workflow resources. " +
|
|
128
|
+
"For large stage handoffs, write context to files/artifacts, pass paths via reads, and prompt downstream agents to 'Read the file at <path>...' instead of injecting large previous text. " +
|
|
128
129
|
"For transcripts, prefer status/stages/stage to get sessionFile/transcriptPath, " +
|
|
129
130
|
"quote the exact path without rewriting separators (Windows backslashes are valid), " +
|
|
130
131
|
"search it with rg/grep, and read small ranges; transcript defaults to at most 5 recent entries and explicit tail/limit overrides that preview.";
|
|
@@ -3009,12 +3010,9 @@ function factory(pi: ExtensionAPI): void {
|
|
|
3009
3010
|
if (policy.allowInputPicker) {
|
|
3010
3011
|
overlay.open(runId, overlaySurfaceFromContext(ctx), stageId);
|
|
3011
3012
|
}
|
|
3012
|
-
const attachedStage = stageId ? run?.stages.find((s) => s.id === stageId) : undefined;
|
|
3013
3013
|
print(
|
|
3014
3014
|
stageId
|
|
3015
|
-
?
|
|
3016
|
-
? `Attached to ${runId.slice(0, 8)} stage ${stageId.slice(0, 8)}. ctrl+d close · esc close.`
|
|
3017
|
-
: `Attached to ${runId.slice(0, 8)} stage ${stageId.slice(0, 8)}. ctrl+d return to graph · esc close.`
|
|
3015
|
+
? `Attached to ${runId.slice(0, 8)} stage ${stageId.slice(0, 8)}. ctrl+d return to graph · esc close.`
|
|
3018
3016
|
: `Attached to ${runId.slice(0, 8)}. ↵ chat · ctrl+d detach.`,
|
|
3019
3017
|
);
|
|
3020
3018
|
return true;
|