@bastani/atomic 0.5.2-0 → 0.5.3-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/dist/chunk-1gb5qxz9.js +1 -0
- package/dist/chunk-fdk7tact.js +417 -0
- package/dist/chunk-mn870nrv.js +982 -0
- package/dist/sdk/components/color-utils.d.ts +3 -0
- package/dist/sdk/components/connectors.d.ts +14 -0
- package/dist/sdk/components/connectors.test.d.ts +1 -0
- package/dist/sdk/components/edge.d.ts +3 -0
- package/dist/sdk/components/error-boundary.d.ts +22 -0
- package/dist/sdk/components/graph-theme.d.ts +16 -0
- package/dist/sdk/components/header.d.ts +2 -0
- package/dist/sdk/components/layout.d.ts +26 -0
- package/dist/sdk/components/layout.test.d.ts +1 -0
- package/dist/sdk/components/node-card.d.ts +8 -0
- package/dist/sdk/components/orchestrator-panel-contexts.d.ts +15 -0
- package/dist/sdk/components/orchestrator-panel-store.d.ts +35 -0
- package/dist/sdk/components/orchestrator-panel-store.test.d.ts +1 -0
- package/dist/sdk/components/orchestrator-panel-types.d.ts +16 -0
- package/dist/sdk/components/orchestrator-panel.d.ts +51 -0
- package/dist/sdk/components/session-graph-panel.d.ts +6 -0
- package/dist/sdk/components/status-helpers.d.ts +5 -0
- package/dist/sdk/components/statusline.d.ts +6 -0
- package/dist/sdk/define-workflow.d.ts +77 -0
- package/dist/sdk/define-workflow.test.d.ts +1 -0
- package/dist/sdk/errors.d.ts +21 -0
- package/dist/sdk/index.d.ts +12 -0
- package/dist/sdk/index.js +52 -0
- package/dist/sdk/providers/claude.d.ts +159 -0
- package/dist/sdk/providers/copilot.d.ts +14 -0
- package/dist/sdk/providers/opencode.d.ts +14 -0
- package/dist/sdk/runtime/discovery.d.ts +31 -0
- package/dist/sdk/runtime/executor-entry.d.ts +10 -0
- package/dist/sdk/runtime/executor.d.ts +61 -0
- package/dist/sdk/runtime/executor.test.d.ts +1 -0
- package/dist/sdk/runtime/graph-inference.d.ts +34 -0
- package/dist/sdk/runtime/loader.d.ts +72 -0
- package/dist/sdk/runtime/panel.d.ts +8 -0
- package/dist/sdk/runtime/theme.d.ts +27 -0
- package/dist/sdk/runtime/tmux.d.ts +191 -0
- package/dist/sdk/types.d.ts +214 -0
- package/dist/sdk/workflows/builtin/ralph/claude/index.d.ts +13 -0
- package/dist/sdk/workflows/builtin/ralph/claude/index.js +95 -0
- package/dist/sdk/workflows/builtin/ralph/copilot/index.d.ts +13 -0
- package/dist/sdk/workflows/builtin/ralph/copilot/index.js +118 -0
- package/dist/sdk/workflows/builtin/ralph/helpers/git.d.ts +16 -0
- package/dist/sdk/workflows/builtin/ralph/helpers/prompts.d.ts +118 -0
- package/dist/sdk/workflows/builtin/ralph/helpers/review.d.ts +19 -0
- package/dist/sdk/workflows/builtin/ralph/opencode/index.d.ts +13 -0
- package/dist/sdk/workflows/builtin/ralph/opencode/index.js +147 -0
- package/dist/sdk/workflows/index.d.ts +24 -0
- package/dist/sdk/workflows/index.js +94 -0
- package/package.json +33 -6
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
buildDebuggerReportPrompt,
|
|
4
|
+
buildOrchestratorPrompt,
|
|
5
|
+
buildPlannerPrompt,
|
|
6
|
+
buildReviewPrompt,
|
|
7
|
+
extractMarkdownBlock,
|
|
8
|
+
hasActionableFindings,
|
|
9
|
+
parseReviewResult,
|
|
10
|
+
safeGitStatusS
|
|
11
|
+
} from "../../../../../chunk-fdk7tact.js";
|
|
12
|
+
import"../../../../../chunk-1gb5qxz9.js";
|
|
13
|
+
import {
|
|
14
|
+
defineWorkflow
|
|
15
|
+
} from "../../../../../chunk-mn870nrv.js";
|
|
16
|
+
|
|
17
|
+
// src/sdk/workflows/builtin/ralph/claude/index.ts
|
|
18
|
+
var MAX_LOOPS = 10;
|
|
19
|
+
var CONSECUTIVE_CLEAN_THRESHOLD = 2;
|
|
20
|
+
function asAgentCall(agentName, prompt) {
|
|
21
|
+
return `@"${agentName} (agent)" ${prompt}`;
|
|
22
|
+
}
|
|
23
|
+
var claude_default = defineWorkflow({
|
|
24
|
+
name: "ralph",
|
|
25
|
+
description: "Plan \u2192 orchestrate \u2192 review \u2192 debug loop with bounded iteration"
|
|
26
|
+
}).run(async (ctx) => {
|
|
27
|
+
let consecutiveClean = 0;
|
|
28
|
+
let debuggerReport = "";
|
|
29
|
+
for (let iteration = 1;iteration <= MAX_LOOPS; iteration++) {
|
|
30
|
+
const plannerName = `planner-${iteration}`;
|
|
31
|
+
await ctx.stage({ name: plannerName }, {}, {}, async (s) => {
|
|
32
|
+
await s.session.query(asAgentCall("planner", buildPlannerPrompt(s.userPrompt, {
|
|
33
|
+
iteration,
|
|
34
|
+
debuggerReport: debuggerReport || undefined
|
|
35
|
+
})));
|
|
36
|
+
s.save(s.sessionId);
|
|
37
|
+
});
|
|
38
|
+
const orchName = `orchestrator-${iteration}`;
|
|
39
|
+
await ctx.stage({ name: orchName }, {}, {}, async (s) => {
|
|
40
|
+
await s.session.query(asAgentCall("orchestrator", buildOrchestratorPrompt(s.userPrompt)));
|
|
41
|
+
s.save(s.sessionId);
|
|
42
|
+
});
|
|
43
|
+
let gitStatus = await safeGitStatusS();
|
|
44
|
+
const reviewerName = `reviewer-${iteration}`;
|
|
45
|
+
const review = await ctx.stage({ name: reviewerName }, {}, {}, async (s) => {
|
|
46
|
+
const result = await s.session.query(asAgentCall("reviewer", buildReviewPrompt(s.userPrompt, { gitStatus, iteration })));
|
|
47
|
+
s.save(s.sessionId);
|
|
48
|
+
return result.output;
|
|
49
|
+
});
|
|
50
|
+
let reviewRaw = review.result;
|
|
51
|
+
let parsed = parseReviewResult(reviewRaw);
|
|
52
|
+
if (!hasActionableFindings(parsed, reviewRaw)) {
|
|
53
|
+
consecutiveClean += 1;
|
|
54
|
+
if (consecutiveClean >= CONSECUTIVE_CLEAN_THRESHOLD)
|
|
55
|
+
break;
|
|
56
|
+
gitStatus = await safeGitStatusS();
|
|
57
|
+
const confirmName = `reviewer-${iteration}-confirm`;
|
|
58
|
+
const confirm = await ctx.stage({ name: confirmName }, {}, {}, async (s) => {
|
|
59
|
+
const result = await s.session.query(asAgentCall("reviewer", buildReviewPrompt(s.userPrompt, {
|
|
60
|
+
gitStatus,
|
|
61
|
+
iteration,
|
|
62
|
+
isConfirmationPass: true
|
|
63
|
+
})));
|
|
64
|
+
s.save(s.sessionId);
|
|
65
|
+
return result.output;
|
|
66
|
+
});
|
|
67
|
+
reviewRaw = confirm.result;
|
|
68
|
+
parsed = parseReviewResult(reviewRaw);
|
|
69
|
+
if (!hasActionableFindings(parsed, reviewRaw)) {
|
|
70
|
+
consecutiveClean += 1;
|
|
71
|
+
if (consecutiveClean >= CONSECUTIVE_CLEAN_THRESHOLD)
|
|
72
|
+
break;
|
|
73
|
+
} else {
|
|
74
|
+
consecutiveClean = 0;
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
consecutiveClean = 0;
|
|
78
|
+
}
|
|
79
|
+
if (hasActionableFindings(parsed, reviewRaw) && iteration < MAX_LOOPS) {
|
|
80
|
+
const debuggerName = `debugger-${iteration}`;
|
|
81
|
+
const debugger_ = await ctx.stage({ name: debuggerName }, {}, {}, async (s) => {
|
|
82
|
+
const result = await s.session.query(asAgentCall("debugger", buildDebuggerReportPrompt(parsed, reviewRaw, {
|
|
83
|
+
iteration,
|
|
84
|
+
gitStatus
|
|
85
|
+
})));
|
|
86
|
+
s.save(s.sessionId);
|
|
87
|
+
return result.output;
|
|
88
|
+
});
|
|
89
|
+
debuggerReport = extractMarkdownBlock(debugger_.result);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}).compile();
|
|
93
|
+
export {
|
|
94
|
+
claude_default as default
|
|
95
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ralph workflow for Copilot — plan → orchestrate → review → debug loop.
|
|
3
|
+
*
|
|
4
|
+
* Each sub-agent invocation spawns its own visible session in the graph,
|
|
5
|
+
* so users can see each iteration's progress in real time. The loop
|
|
6
|
+
* terminates when:
|
|
7
|
+
* - {@link MAX_LOOPS} iterations have completed, OR
|
|
8
|
+
* - Two consecutive reviewer passes return zero findings.
|
|
9
|
+
*
|
|
10
|
+
* Run: atomic workflow -n ralph -a copilot "<your spec>"
|
|
11
|
+
*/
|
|
12
|
+
declare const _default: import("../../../index.ts").WorkflowDefinition<"copilot">;
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
buildDebuggerReportPrompt,
|
|
4
|
+
buildOrchestratorPrompt,
|
|
5
|
+
buildPlannerPrompt,
|
|
6
|
+
buildReviewPrompt,
|
|
7
|
+
extractMarkdownBlock,
|
|
8
|
+
hasActionableFindings,
|
|
9
|
+
parseReviewResult,
|
|
10
|
+
safeGitStatusS
|
|
11
|
+
} from "../../../../../chunk-fdk7tact.js";
|
|
12
|
+
import"../../../../../chunk-1gb5qxz9.js";
|
|
13
|
+
import {
|
|
14
|
+
defineWorkflow
|
|
15
|
+
} from "../../../../../chunk-mn870nrv.js";
|
|
16
|
+
|
|
17
|
+
// src/sdk/workflows/builtin/ralph/copilot/index.ts
|
|
18
|
+
var MAX_LOOPS = 10;
|
|
19
|
+
var CONSECUTIVE_CLEAN_THRESHOLD = 2;
|
|
20
|
+
var AGENT_SEND_TIMEOUT_MS = 30 * 60 * 1000;
|
|
21
|
+
function getAssistantText(messages) {
|
|
22
|
+
return messages.filter((m) => m.type === "assistant.message" && !m.data.parentToolCallId).map((m) => m.data.content).filter((c) => c.length > 0).join(`
|
|
23
|
+
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
var copilot_default = defineWorkflow({
|
|
27
|
+
name: "ralph",
|
|
28
|
+
description: "Plan \u2192 orchestrate \u2192 review \u2192 debug loop with bounded iteration"
|
|
29
|
+
}).run(async (ctx) => {
|
|
30
|
+
let consecutiveClean = 0;
|
|
31
|
+
let debuggerReport = "";
|
|
32
|
+
for (let iteration = 1;iteration <= MAX_LOOPS; iteration++) {
|
|
33
|
+
const plannerName = `planner-${iteration}`;
|
|
34
|
+
const planner = await ctx.stage({ name: plannerName }, {}, { agent: "planner" }, async (s) => {
|
|
35
|
+
await s.session.sendAndWait({
|
|
36
|
+
prompt: buildPlannerPrompt(s.userPrompt, {
|
|
37
|
+
iteration,
|
|
38
|
+
debuggerReport: debuggerReport || undefined
|
|
39
|
+
})
|
|
40
|
+
}, AGENT_SEND_TIMEOUT_MS);
|
|
41
|
+
const messages = await s.session.getMessages();
|
|
42
|
+
s.save(messages);
|
|
43
|
+
return getAssistantText(messages);
|
|
44
|
+
});
|
|
45
|
+
const orchName = `orchestrator-${iteration}`;
|
|
46
|
+
await ctx.stage({ name: orchName }, {}, { agent: "orchestrator" }, async (s) => {
|
|
47
|
+
await s.session.sendAndWait({
|
|
48
|
+
prompt: buildOrchestratorPrompt(s.userPrompt, {
|
|
49
|
+
plannerNotes: planner.result
|
|
50
|
+
})
|
|
51
|
+
}, AGENT_SEND_TIMEOUT_MS);
|
|
52
|
+
s.save(await s.session.getMessages());
|
|
53
|
+
});
|
|
54
|
+
let gitStatus = await safeGitStatusS();
|
|
55
|
+
const reviewerName = `reviewer-${iteration}`;
|
|
56
|
+
const review = await ctx.stage({ name: reviewerName }, {}, { agent: "reviewer" }, async (s) => {
|
|
57
|
+
await s.session.sendAndWait({
|
|
58
|
+
prompt: buildReviewPrompt(s.userPrompt, {
|
|
59
|
+
gitStatus,
|
|
60
|
+
iteration
|
|
61
|
+
})
|
|
62
|
+
}, AGENT_SEND_TIMEOUT_MS);
|
|
63
|
+
const messages = await s.session.getMessages();
|
|
64
|
+
s.save(messages);
|
|
65
|
+
return getAssistantText(messages);
|
|
66
|
+
});
|
|
67
|
+
let reviewRaw = review.result;
|
|
68
|
+
let parsed = parseReviewResult(reviewRaw);
|
|
69
|
+
if (!hasActionableFindings(parsed, reviewRaw)) {
|
|
70
|
+
consecutiveClean += 1;
|
|
71
|
+
if (consecutiveClean >= CONSECUTIVE_CLEAN_THRESHOLD)
|
|
72
|
+
break;
|
|
73
|
+
gitStatus = await safeGitStatusS();
|
|
74
|
+
const confirmName = `reviewer-${iteration}-confirm`;
|
|
75
|
+
const confirm = await ctx.stage({ name: confirmName }, {}, { agent: "reviewer" }, async (s) => {
|
|
76
|
+
await s.session.sendAndWait({
|
|
77
|
+
prompt: buildReviewPrompt(s.userPrompt, {
|
|
78
|
+
gitStatus,
|
|
79
|
+
iteration,
|
|
80
|
+
isConfirmationPass: true
|
|
81
|
+
})
|
|
82
|
+
}, AGENT_SEND_TIMEOUT_MS);
|
|
83
|
+
const messages = await s.session.getMessages();
|
|
84
|
+
s.save(messages);
|
|
85
|
+
return getAssistantText(messages);
|
|
86
|
+
});
|
|
87
|
+
reviewRaw = confirm.result;
|
|
88
|
+
parsed = parseReviewResult(reviewRaw);
|
|
89
|
+
if (!hasActionableFindings(parsed, reviewRaw)) {
|
|
90
|
+
consecutiveClean += 1;
|
|
91
|
+
if (consecutiveClean >= CONSECUTIVE_CLEAN_THRESHOLD)
|
|
92
|
+
break;
|
|
93
|
+
} else {
|
|
94
|
+
consecutiveClean = 0;
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
consecutiveClean = 0;
|
|
98
|
+
}
|
|
99
|
+
if (hasActionableFindings(parsed, reviewRaw) && iteration < MAX_LOOPS) {
|
|
100
|
+
const debuggerName = `debugger-${iteration}`;
|
|
101
|
+
const debugger_ = await ctx.stage({ name: debuggerName }, {}, { agent: "debugger" }, async (s) => {
|
|
102
|
+
await s.session.sendAndWait({
|
|
103
|
+
prompt: buildDebuggerReportPrompt(parsed, reviewRaw, {
|
|
104
|
+
iteration,
|
|
105
|
+
gitStatus
|
|
106
|
+
})
|
|
107
|
+
}, AGENT_SEND_TIMEOUT_MS);
|
|
108
|
+
const messages = await s.session.getMessages();
|
|
109
|
+
s.save(messages);
|
|
110
|
+
return getAssistantText(messages);
|
|
111
|
+
});
|
|
112
|
+
debuggerReport = extractMarkdownBlock(debugger_.result);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}).compile();
|
|
116
|
+
export {
|
|
117
|
+
copilot_default as default
|
|
118
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic working-tree probes used by the Ralph loop.
|
|
3
|
+
*
|
|
4
|
+
* The reviewer and debugger sub-agents both benefit from knowing exactly which
|
|
5
|
+
* files were touched in the current iteration. Asking an LLM to figure that
|
|
6
|
+
* out via tool calls is expensive and lossy, so we capture `git status -s`
|
|
7
|
+
* directly from the workflow runner and inject it into prompts.
|
|
8
|
+
*
|
|
9
|
+
* Failures (no git binary, not a repo, command non-zero) collapse to "" so
|
|
10
|
+
* the prompts can fall back to the "working tree clean" branch.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Run `git status -s` from the given cwd. Returns trimmed stdout, or "" on
|
|
14
|
+
* any error. Never throws.
|
|
15
|
+
*/
|
|
16
|
+
export declare function safeGitStatusS(cwd?: string): Promise<string>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ralph Prompt Utilities
|
|
3
|
+
*
|
|
4
|
+
* Prompts used by the Ralph plan → orchestrate → review → debug loop:
|
|
5
|
+
* - buildPlannerPrompt: initial planning OR re-planning from a debugger report
|
|
6
|
+
* - buildOrchestratorPrompt: spawn workers to execute the task list
|
|
7
|
+
* - buildReviewPrompt: structured code review with injected git status
|
|
8
|
+
* - buildDebuggerReportPrompt: diagnose review findings, produce a re-plan brief
|
|
9
|
+
*
|
|
10
|
+
* Plus parsing helpers for the reviewer JSON output and the debugger markdown
|
|
11
|
+
* report.
|
|
12
|
+
*
|
|
13
|
+
* Zero-dependency: no imports from the Atomic runtime.
|
|
14
|
+
*/
|
|
15
|
+
export interface PlannerContext {
|
|
16
|
+
/** 1-indexed loop iteration. Iteration 1 = initial plan; >1 = re-plan. */
|
|
17
|
+
iteration: number;
|
|
18
|
+
/** Markdown report from the previous iteration's debugger sub-agent. */
|
|
19
|
+
debuggerReport?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Build the planner prompt. The first iteration decomposes the original spec;
|
|
23
|
+
* subsequent iterations decompose the work needed to resolve the debugger
|
|
24
|
+
* report from the previous loop iteration.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildPlannerPrompt(spec: string, context?: PlannerContext): string;
|
|
27
|
+
export interface OrchestratorContext {
|
|
28
|
+
/**
|
|
29
|
+
* Trailing commentary from the planner's last assistant message, if any.
|
|
30
|
+
* The Copilot and OpenCode workflows create a fresh session for each
|
|
31
|
+
* sub-agent, so the planner's in-session output is NOT automatically
|
|
32
|
+
* visible to the orchestrator — only what the planner persisted via
|
|
33
|
+
* `TaskCreate`. Forward the planner's final text here so the orchestrator
|
|
34
|
+
* sees any caveats, risks, or execution hints that didn't fit into task
|
|
35
|
+
* bodies.
|
|
36
|
+
*/
|
|
37
|
+
plannerNotes?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build the orchestrator prompt. The orchestrator retrieves the planner's
|
|
41
|
+
* task list, validates the dependency graph, and spawns parallel workers.
|
|
42
|
+
*
|
|
43
|
+
* @param spec - The original user specification. Required because the
|
|
44
|
+
* orchestrator runs in a fresh session on Copilot/OpenCode and needs the
|
|
45
|
+
* end-user goal to resolve ambiguous tasks.
|
|
46
|
+
* @param context - Optional planner handoff context (trailing commentary).
|
|
47
|
+
*/
|
|
48
|
+
export declare function buildOrchestratorPrompt(spec: string, context?: OrchestratorContext): string;
|
|
49
|
+
/** A single finding from the reviewer sub-agent. */
|
|
50
|
+
export interface ReviewFinding {
|
|
51
|
+
title: string;
|
|
52
|
+
body: string;
|
|
53
|
+
confidence_score?: number;
|
|
54
|
+
priority?: number;
|
|
55
|
+
code_location?: {
|
|
56
|
+
absolute_file_path: string;
|
|
57
|
+
line_range: {
|
|
58
|
+
start: number;
|
|
59
|
+
end: number;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Parsed reviewer JSON output. */
|
|
64
|
+
export interface ReviewResult {
|
|
65
|
+
findings: ReviewFinding[];
|
|
66
|
+
overall_correctness: string;
|
|
67
|
+
overall_explanation: string;
|
|
68
|
+
overall_confidence_score?: number;
|
|
69
|
+
}
|
|
70
|
+
export interface ReviewContext {
|
|
71
|
+
/** Output of `git status -s` captured immediately before the review. */
|
|
72
|
+
gitStatus: string;
|
|
73
|
+
/** 1-indexed loop iteration, used in the prompt header. */
|
|
74
|
+
iteration?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Whether this is the second consecutive review pass within the same loop
|
|
77
|
+
* iteration (i.e. the previous pass had zero findings and we are
|
|
78
|
+
* confirming before counting two clean reviews in a row).
|
|
79
|
+
*/
|
|
80
|
+
isConfirmationPass?: boolean;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build the reviewer prompt. Injects deterministic `git status -s` so the
|
|
84
|
+
* reviewer doesn't have to re-discover what changed.
|
|
85
|
+
*/
|
|
86
|
+
export declare function buildReviewPrompt(spec: string, context: ReviewContext): string;
|
|
87
|
+
export interface DebuggerContext {
|
|
88
|
+
/** 1-indexed loop iteration the debugger is investigating. */
|
|
89
|
+
iteration: number;
|
|
90
|
+
/** Output of `git status -s` from immediately before the review. */
|
|
91
|
+
gitStatus: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Build a prompt asking the debugger sub-agent to investigate a set of review
|
|
95
|
+
* findings and produce a structured report. The debugger MUST NOT apply
|
|
96
|
+
* fixes — its only deliverable is the report, which the next iteration's
|
|
97
|
+
* planner consumes.
|
|
98
|
+
*/
|
|
99
|
+
export declare function buildDebuggerReportPrompt(review: ReviewResult | null, rawReview: string, context: DebuggerContext): string;
|
|
100
|
+
/**
|
|
101
|
+
* Parse the reviewer's JSON output. Tries, in order:
|
|
102
|
+
* 1. Direct JSON.parse on the entire content.
|
|
103
|
+
* 2. The LAST fenced ```json (or unlabelled) code block.
|
|
104
|
+
* 3. The LAST balanced object containing a "findings" key in surrounding prose.
|
|
105
|
+
*
|
|
106
|
+
* Filters out P3 (minor/style) findings — only P0/P1/P2 count as actionable.
|
|
107
|
+
* Returns null when no parse strategy succeeds.
|
|
108
|
+
*/
|
|
109
|
+
export declare function parseReviewResult(content: string): ReviewResult | null;
|
|
110
|
+
/**
|
|
111
|
+
* Extract the LAST fenced ```markdown block from a piece of text. Used for
|
|
112
|
+
* parsing the debugger's structured report out of a long Claude pane
|
|
113
|
+
* scrollback or any other output that may include extra prose.
|
|
114
|
+
*
|
|
115
|
+
* Falls back to the trimmed full input when no fenced block is present, so
|
|
116
|
+
* the planner still receives the debugger's content even if formatting drifts.
|
|
117
|
+
*/
|
|
118
|
+
export declare function extractMarkdownBlock(content: string): string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review analysis helpers for the Ralph workflow.
|
|
3
|
+
*
|
|
4
|
+
* Simplified versions of the internal conductor-based helpers,
|
|
5
|
+
* operating on direct values instead of StageOutput maps.
|
|
6
|
+
*/
|
|
7
|
+
import type { ReviewResult } from "./prompts.js";
|
|
8
|
+
/**
|
|
9
|
+
* Check whether the reviewer produced actionable findings.
|
|
10
|
+
*
|
|
11
|
+
* Returns true when:
|
|
12
|
+
* 1. The parsed ReviewResult has one or more findings, OR
|
|
13
|
+
* 2. The review could not be parsed (null) but the raw response
|
|
14
|
+
* text is non-empty (treat unparseable output as actionable).
|
|
15
|
+
*
|
|
16
|
+
* @param review - Parsed ReviewResult, or null if parsing failed.
|
|
17
|
+
* @param rawText - The raw reviewer response text.
|
|
18
|
+
*/
|
|
19
|
+
export declare function hasActionableFindings(review: ReviewResult | null, rawText: string): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ralph workflow for OpenCode — plan → orchestrate → review → debug loop.
|
|
3
|
+
*
|
|
4
|
+
* Each sub-agent invocation spawns its own visible session in the graph,
|
|
5
|
+
* so users can see each iteration's progress in real time. The loop
|
|
6
|
+
* terminates when:
|
|
7
|
+
* - {@link MAX_LOOPS} iterations have completed, OR
|
|
8
|
+
* - Two consecutive reviewer passes return zero findings.
|
|
9
|
+
*
|
|
10
|
+
* Run: atomic workflow -n ralph -a opencode "<your spec>"
|
|
11
|
+
*/
|
|
12
|
+
declare const _default: import("../../../index.ts").WorkflowDefinition<"opencode">;
|
|
13
|
+
export default _default;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
buildDebuggerReportPrompt,
|
|
4
|
+
buildOrchestratorPrompt,
|
|
5
|
+
buildPlannerPrompt,
|
|
6
|
+
buildReviewPrompt,
|
|
7
|
+
extractMarkdownBlock,
|
|
8
|
+
hasActionableFindings,
|
|
9
|
+
parseReviewResult,
|
|
10
|
+
safeGitStatusS
|
|
11
|
+
} from "../../../../../chunk-fdk7tact.js";
|
|
12
|
+
import"../../../../../chunk-1gb5qxz9.js";
|
|
13
|
+
import {
|
|
14
|
+
defineWorkflow
|
|
15
|
+
} from "../../../../../chunk-mn870nrv.js";
|
|
16
|
+
|
|
17
|
+
// src/sdk/workflows/builtin/ralph/opencode/index.ts
|
|
18
|
+
var MAX_LOOPS = 10;
|
|
19
|
+
var CONSECUTIVE_CLEAN_THRESHOLD = 2;
|
|
20
|
+
function extractResponseText(parts) {
|
|
21
|
+
return parts.filter((p) => p.type === "text").map((p) => p.text).join(`
|
|
22
|
+
`);
|
|
23
|
+
}
|
|
24
|
+
var opencode_default = defineWorkflow({
|
|
25
|
+
name: "ralph",
|
|
26
|
+
description: "Plan \u2192 orchestrate \u2192 review \u2192 debug loop with bounded iteration"
|
|
27
|
+
}).run(async (ctx) => {
|
|
28
|
+
let consecutiveClean = 0;
|
|
29
|
+
let debuggerReport = "";
|
|
30
|
+
for (let iteration = 1;iteration <= MAX_LOOPS; iteration++) {
|
|
31
|
+
const plannerName = `planner-${iteration}`;
|
|
32
|
+
const planner = await ctx.stage({ name: plannerName }, {}, { title: `planner-${iteration}` }, async (s) => {
|
|
33
|
+
const result = await s.client.session.prompt({
|
|
34
|
+
sessionID: s.session.id,
|
|
35
|
+
parts: [
|
|
36
|
+
{
|
|
37
|
+
type: "text",
|
|
38
|
+
text: buildPlannerPrompt(s.userPrompt, {
|
|
39
|
+
iteration,
|
|
40
|
+
debuggerReport: debuggerReport || undefined
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
agent: "planner"
|
|
45
|
+
});
|
|
46
|
+
s.save(result.data);
|
|
47
|
+
return extractResponseText(result.data.parts);
|
|
48
|
+
});
|
|
49
|
+
const orchName = `orchestrator-${iteration}`;
|
|
50
|
+
await ctx.stage({ name: orchName }, {}, { title: `orchestrator-${iteration}` }, async (s) => {
|
|
51
|
+
const result = await s.client.session.prompt({
|
|
52
|
+
sessionID: s.session.id,
|
|
53
|
+
parts: [
|
|
54
|
+
{
|
|
55
|
+
type: "text",
|
|
56
|
+
text: buildOrchestratorPrompt(s.userPrompt, {
|
|
57
|
+
plannerNotes: planner.result
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
agent: "orchestrator"
|
|
62
|
+
});
|
|
63
|
+
s.save(result.data);
|
|
64
|
+
});
|
|
65
|
+
let gitStatus = await safeGitStatusS();
|
|
66
|
+
const reviewerName = `reviewer-${iteration}`;
|
|
67
|
+
const review = await ctx.stage({ name: reviewerName }, {}, { title: `reviewer-${iteration}` }, async (s) => {
|
|
68
|
+
const result = await s.client.session.prompt({
|
|
69
|
+
sessionID: s.session.id,
|
|
70
|
+
parts: [
|
|
71
|
+
{
|
|
72
|
+
type: "text",
|
|
73
|
+
text: buildReviewPrompt(s.userPrompt, {
|
|
74
|
+
gitStatus,
|
|
75
|
+
iteration
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
agent: "reviewer"
|
|
80
|
+
});
|
|
81
|
+
s.save(result.data);
|
|
82
|
+
return extractResponseText(result.data.parts);
|
|
83
|
+
});
|
|
84
|
+
let reviewRaw = review.result;
|
|
85
|
+
let parsed = parseReviewResult(reviewRaw);
|
|
86
|
+
if (!hasActionableFindings(parsed, reviewRaw)) {
|
|
87
|
+
consecutiveClean += 1;
|
|
88
|
+
if (consecutiveClean >= CONSECUTIVE_CLEAN_THRESHOLD)
|
|
89
|
+
break;
|
|
90
|
+
gitStatus = await safeGitStatusS();
|
|
91
|
+
const confirmName = `reviewer-${iteration}-confirm`;
|
|
92
|
+
const confirm = await ctx.stage({ name: confirmName }, {}, { title: `reviewer-${iteration}-confirm` }, async (s) => {
|
|
93
|
+
const result = await s.client.session.prompt({
|
|
94
|
+
sessionID: s.session.id,
|
|
95
|
+
parts: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: buildReviewPrompt(s.userPrompt, {
|
|
99
|
+
gitStatus,
|
|
100
|
+
iteration,
|
|
101
|
+
isConfirmationPass: true
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
agent: "reviewer"
|
|
106
|
+
});
|
|
107
|
+
s.save(result.data);
|
|
108
|
+
return extractResponseText(result.data.parts);
|
|
109
|
+
});
|
|
110
|
+
reviewRaw = confirm.result;
|
|
111
|
+
parsed = parseReviewResult(reviewRaw);
|
|
112
|
+
if (!hasActionableFindings(parsed, reviewRaw)) {
|
|
113
|
+
consecutiveClean += 1;
|
|
114
|
+
if (consecutiveClean >= CONSECUTIVE_CLEAN_THRESHOLD)
|
|
115
|
+
break;
|
|
116
|
+
} else {
|
|
117
|
+
consecutiveClean = 0;
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
consecutiveClean = 0;
|
|
121
|
+
}
|
|
122
|
+
if (hasActionableFindings(parsed, reviewRaw) && iteration < MAX_LOOPS) {
|
|
123
|
+
const debuggerName = `debugger-${iteration}`;
|
|
124
|
+
const debugger_ = await ctx.stage({ name: debuggerName }, {}, { title: `debugger-${iteration}` }, async (s) => {
|
|
125
|
+
const result = await s.client.session.prompt({
|
|
126
|
+
sessionID: s.session.id,
|
|
127
|
+
parts: [
|
|
128
|
+
{
|
|
129
|
+
type: "text",
|
|
130
|
+
text: buildDebuggerReportPrompt(parsed, reviewRaw, {
|
|
131
|
+
iteration,
|
|
132
|
+
gitStatus
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
agent: "debugger"
|
|
137
|
+
});
|
|
138
|
+
s.save(result.data);
|
|
139
|
+
return extractResponseText(result.data.parts);
|
|
140
|
+
});
|
|
141
|
+
debuggerReport = extractMarkdownBlock(debugger_.result);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}).compile();
|
|
145
|
+
export {
|
|
146
|
+
opencode_default as default
|
|
147
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* atomic/workflows
|
|
3
|
+
*
|
|
4
|
+
* Workflow SDK for defining dynamic agent workflows.
|
|
5
|
+
* Workflows use defineWorkflow().run().compile() with ctx.stage()
|
|
6
|
+
* for spawning agent sessions using native TypeScript control flow.
|
|
7
|
+
*/
|
|
8
|
+
export { defineWorkflow, WorkflowBuilder } from "../define-workflow.js";
|
|
9
|
+
export type { AgentType, Transcript, SavedMessage, SaveTranscript, SessionContext, SessionRef, SessionHandle, SessionRunOptions, WorkflowContext, WorkflowOptions, WorkflowDefinition, StageClientOptions, StageSessionOptions, ProviderClient, ProviderSession, CopilotClient, CopilotClientOptions, CopilotSession, CopilotSessionConfig, OpencodeClient, OpencodeSession, ClaudeClientWrapper, ClaudeSessionWrapper, ClaudeQueryDefaults, } from "../types.js";
|
|
10
|
+
export type { SessionEvent as CopilotSessionEvent } from "@github/copilot-sdk";
|
|
11
|
+
export type { SessionPromptResponse as OpenCodePromptResponse } from "@opencode-ai/sdk/v2";
|
|
12
|
+
export type { SessionMessage as ClaudeSessionMessage } from "@anthropic-ai/claude-agent-sdk";
|
|
13
|
+
export { createClaudeSession, claudeQuery, clearClaudeSession, validateClaudeWorkflow } from "../providers/claude.js";
|
|
14
|
+
export type { ClaudeSessionOptions, ClaudeQueryOptions, ClaudeQueryResult, ClaudeValidationWarning } from "../providers/claude.js";
|
|
15
|
+
export { validateCopilotWorkflow } from "../providers/copilot.js";
|
|
16
|
+
export type { CopilotValidationWarning } from "../providers/copilot.js";
|
|
17
|
+
export { validateOpenCodeWorkflow } from "../providers/opencode.js";
|
|
18
|
+
export type { OpenCodeValidationWarning } from "../providers/opencode.js";
|
|
19
|
+
export { isTmuxInstalled, getMuxBinary, resetMuxBinaryCache, isInsideTmux, createSession, createWindow, createPane, sendLiteralText, sendSpecialKey, sendKeysAndSubmit, capturePane, capturePaneVisible, capturePaneScrollback, killSession, killWindow, sessionExists, attachSession, switchClient, getCurrentSession, attachOrSwitch, selectWindow, waitForOutput, tmuxRun, normalizeTmuxCapture, normalizeTmuxLines, paneLooksReady, paneHasActiveTask, paneIsIdle, waitForPaneReady, attemptSubmitRounds, } from "../runtime/tmux.js";
|
|
20
|
+
export { AGENTS, discoverWorkflows, findWorkflow, WORKFLOWS_GITIGNORE, } from "../runtime/discovery.js";
|
|
21
|
+
export type { DiscoveredWorkflow } from "../runtime/discovery.js";
|
|
22
|
+
export { WorkflowLoader } from "../runtime/loader.js";
|
|
23
|
+
export { executeWorkflow } from "../runtime/executor.js";
|
|
24
|
+
export type { WorkflowRunOptions } from "../runtime/executor.js";
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import"../../chunk-1gb5qxz9.js";
|
|
3
|
+
import {
|
|
4
|
+
AGENTS,
|
|
5
|
+
WORKFLOWS_GITIGNORE,
|
|
6
|
+
WorkflowBuilder,
|
|
7
|
+
WorkflowLoader,
|
|
8
|
+
attachOrSwitch,
|
|
9
|
+
attachSession,
|
|
10
|
+
attemptSubmitRounds,
|
|
11
|
+
capturePane,
|
|
12
|
+
capturePaneScrollback,
|
|
13
|
+
capturePaneVisible,
|
|
14
|
+
claudeQuery,
|
|
15
|
+
clearClaudeSession,
|
|
16
|
+
createClaudeSession,
|
|
17
|
+
createPane,
|
|
18
|
+
createSession,
|
|
19
|
+
createWindow,
|
|
20
|
+
defineWorkflow,
|
|
21
|
+
discoverWorkflows,
|
|
22
|
+
executeWorkflow,
|
|
23
|
+
findWorkflow,
|
|
24
|
+
getCurrentSession,
|
|
25
|
+
getMuxBinary,
|
|
26
|
+
isInsideTmux,
|
|
27
|
+
isTmuxInstalled,
|
|
28
|
+
killSession,
|
|
29
|
+
killWindow,
|
|
30
|
+
normalizeTmuxCapture,
|
|
31
|
+
normalizeTmuxLines,
|
|
32
|
+
paneHasActiveTask,
|
|
33
|
+
paneIsIdle,
|
|
34
|
+
paneLooksReady,
|
|
35
|
+
resetMuxBinaryCache,
|
|
36
|
+
selectWindow,
|
|
37
|
+
sendKeysAndSubmit,
|
|
38
|
+
sendLiteralText,
|
|
39
|
+
sendSpecialKey,
|
|
40
|
+
sessionExists,
|
|
41
|
+
switchClient,
|
|
42
|
+
tmuxRun,
|
|
43
|
+
validateClaudeWorkflow,
|
|
44
|
+
validateCopilotWorkflow,
|
|
45
|
+
validateOpenCodeWorkflow,
|
|
46
|
+
waitForOutput,
|
|
47
|
+
waitForPaneReady
|
|
48
|
+
} from "../../chunk-mn870nrv.js";
|
|
49
|
+
export {
|
|
50
|
+
waitForPaneReady,
|
|
51
|
+
waitForOutput,
|
|
52
|
+
validateOpenCodeWorkflow,
|
|
53
|
+
validateCopilotWorkflow,
|
|
54
|
+
validateClaudeWorkflow,
|
|
55
|
+
tmuxRun,
|
|
56
|
+
switchClient,
|
|
57
|
+
sessionExists,
|
|
58
|
+
sendSpecialKey,
|
|
59
|
+
sendLiteralText,
|
|
60
|
+
sendKeysAndSubmit,
|
|
61
|
+
selectWindow,
|
|
62
|
+
resetMuxBinaryCache,
|
|
63
|
+
paneLooksReady,
|
|
64
|
+
paneIsIdle,
|
|
65
|
+
paneHasActiveTask,
|
|
66
|
+
normalizeTmuxLines,
|
|
67
|
+
normalizeTmuxCapture,
|
|
68
|
+
killWindow,
|
|
69
|
+
killSession,
|
|
70
|
+
isTmuxInstalled,
|
|
71
|
+
isInsideTmux,
|
|
72
|
+
getMuxBinary,
|
|
73
|
+
getCurrentSession,
|
|
74
|
+
findWorkflow,
|
|
75
|
+
executeWorkflow,
|
|
76
|
+
discoverWorkflows,
|
|
77
|
+
defineWorkflow,
|
|
78
|
+
createWindow,
|
|
79
|
+
createSession,
|
|
80
|
+
createPane,
|
|
81
|
+
createClaudeSession,
|
|
82
|
+
clearClaudeSession,
|
|
83
|
+
claudeQuery,
|
|
84
|
+
capturePaneVisible,
|
|
85
|
+
capturePaneScrollback,
|
|
86
|
+
capturePane,
|
|
87
|
+
attemptSubmitRounds,
|
|
88
|
+
attachSession,
|
|
89
|
+
attachOrSwitch,
|
|
90
|
+
WorkflowLoader,
|
|
91
|
+
WorkflowBuilder,
|
|
92
|
+
WORKFLOWS_GITIGNORE,
|
|
93
|
+
AGENTS
|
|
94
|
+
};
|