ralphflow 0.1.0 → 0.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/README.md +4 -0
- package/dist/ralphflow.js +106 -117
- package/package.json +2 -2
- package/src/templates/research/loops/00-discovery-loop/loop.md +21 -0
- package/src/templates/research/loops/00-discovery-loop/prompt.md +119 -0
- package/src/templates/research/loops/00-discovery-loop/topics.md +3 -0
- package/src/templates/research/loops/00-discovery-loop/tracker.md +15 -0
- package/src/templates/research/loops/01-research-loop/findings.md +3 -0
- package/src/templates/research/loops/01-research-loop/loop.md +60 -0
- package/src/templates/research/loops/01-research-loop/prompt.md +182 -0
- package/src/templates/research/loops/01-research-loop/tracker.md +16 -0
- package/src/templates/research/loops/02-story-loop/loop.md +21 -0
- package/src/templates/research/loops/02-story-loop/prompt.md +109 -0
- package/src/templates/research/loops/02-story-loop/stories.md +3 -0
- package/src/templates/research/loops/02-story-loop/tracker.md +13 -0
- package/src/templates/research/loops/03-document-loop/loop.md +30 -0
- package/src/templates/research/loops/03-document-loop/prompt.md +122 -0
- package/src/templates/research/loops/03-document-loop/tracker.md +9 -0
- package/src/templates/research/ralphflow.yaml +80 -0
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# RalphFlow
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="WiggumFlow.png" alt="RalphFlow" width="400" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
Multi-agent AI workflow orchestration for [Claude Code](https://docs.anthropic.com/en/docs/claude-code).
|
|
4
8
|
|
|
5
9
|
Define pipelines as loops, coordinate parallel agents via file-based trackers, and ship structured work — from single-agent interactive sessions to multi-agent autonomous execution.
|
package/dist/ralphflow.js
CHANGED
|
@@ -141,7 +141,7 @@ import { Command as Command2 } from "commander";
|
|
|
141
141
|
import chalk4 from "chalk";
|
|
142
142
|
|
|
143
143
|
// src/core/runner.ts
|
|
144
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
144
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync, readdirSync as readdirSync3, unlinkSync } from "fs";
|
|
145
145
|
import { join as join4 } from "path";
|
|
146
146
|
import chalk3 from "chalk";
|
|
147
147
|
|
|
@@ -150,12 +150,19 @@ import { readFileSync as readFileSync2, existsSync as existsSync3, readdirSync a
|
|
|
150
150
|
import { join as join3 } from "path";
|
|
151
151
|
import { parse as parseYaml } from "yaml";
|
|
152
152
|
var LOOP_ALIASES = {
|
|
153
|
+
// code-implementation aliases
|
|
153
154
|
story: "story-loop",
|
|
154
155
|
stories: "story-loop",
|
|
155
156
|
tasks: "tasks-loop",
|
|
156
157
|
task: "tasks-loop",
|
|
157
158
|
delivery: "delivery-loop",
|
|
158
|
-
deliver: "delivery-loop"
|
|
159
|
+
deliver: "delivery-loop",
|
|
160
|
+
// research aliases
|
|
161
|
+
discovery: "discovery-loop",
|
|
162
|
+
discover: "discovery-loop",
|
|
163
|
+
research: "research-loop",
|
|
164
|
+
document: "document-loop",
|
|
165
|
+
doc: "document-loop"
|
|
159
166
|
};
|
|
160
167
|
function listFlows2(cwd) {
|
|
161
168
|
const baseDir = join3(cwd, ".ralph-flow");
|
|
@@ -222,55 +229,23 @@ function resolveLoop(config, name) {
|
|
|
222
229
|
// src/core/claude.ts
|
|
223
230
|
import { spawn } from "child_process";
|
|
224
231
|
async function spawnClaude(options) {
|
|
225
|
-
const { prompt, model,
|
|
226
|
-
const args = [];
|
|
227
|
-
if (printMode) {
|
|
228
|
-
args.push("-p");
|
|
229
|
-
args.push("--dangerously-skip-permissions");
|
|
230
|
-
}
|
|
232
|
+
const { prompt, model, cwd } = options;
|
|
233
|
+
const args = ["--dangerously-skip-permissions", prompt];
|
|
231
234
|
if (model) {
|
|
232
|
-
args.
|
|
235
|
+
args.unshift("--model", model);
|
|
233
236
|
}
|
|
234
237
|
return new Promise((resolve, reject) => {
|
|
235
238
|
const child = spawn("claude", args, {
|
|
236
239
|
cwd,
|
|
237
|
-
stdio:
|
|
240
|
+
stdio: "inherit",
|
|
238
241
|
env: { ...process.env }
|
|
239
242
|
});
|
|
240
|
-
let output = "";
|
|
241
|
-
child.stdout?.on("data", (data) => {
|
|
242
|
-
const text = data.toString();
|
|
243
|
-
output += text;
|
|
244
|
-
if (agentName) {
|
|
245
|
-
const lines = text.split("\n");
|
|
246
|
-
for (const line of lines) {
|
|
247
|
-
if (line) {
|
|
248
|
-
process.stdout.write(`[${agentName}] ${line}
|
|
249
|
-
`);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
} else {
|
|
253
|
-
process.stdout.write(text);
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
if (printMode && child.stderr) {
|
|
257
|
-
child.stderr.on("data", (data) => {
|
|
258
|
-
const text = data.toString();
|
|
259
|
-
if (agentName) {
|
|
260
|
-
process.stderr.write(`[${agentName}] ${text}`);
|
|
261
|
-
} else {
|
|
262
|
-
process.stderr.write(text);
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
child.stdin?.write(prompt);
|
|
267
|
-
child.stdin?.end();
|
|
268
243
|
child.on("error", (err) => {
|
|
269
244
|
reject(new Error(`Failed to spawn claude: ${err.message}`));
|
|
270
245
|
});
|
|
271
246
|
child.on("close", (code, signal) => {
|
|
272
247
|
resolve({
|
|
273
|
-
output,
|
|
248
|
+
output: "",
|
|
274
249
|
exitCode: code,
|
|
275
250
|
signal
|
|
276
251
|
});
|
|
@@ -279,41 +254,104 @@ async function spawnClaude(options) {
|
|
|
279
254
|
}
|
|
280
255
|
|
|
281
256
|
// src/core/runner.ts
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
257
|
+
function agentsDir(flowDir, loop) {
|
|
258
|
+
const loopDir = join4(flowDir, loop.tracker, "..");
|
|
259
|
+
return join4(loopDir, ".agents");
|
|
260
|
+
}
|
|
261
|
+
function isProcessAlive(pid) {
|
|
262
|
+
try {
|
|
263
|
+
process.kill(pid, 0);
|
|
264
|
+
return true;
|
|
265
|
+
} catch {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function cleanStaleAgents(dir) {
|
|
270
|
+
if (!existsSync4(dir)) return;
|
|
271
|
+
for (const file of readdirSync3(dir)) {
|
|
272
|
+
if (!file.endsWith(".lock")) continue;
|
|
273
|
+
const pidStr = readFileSync3(join4(dir, file), "utf-8").trim();
|
|
274
|
+
const pid = parseInt(pidStr, 10);
|
|
275
|
+
if (isNaN(pid) || !isProcessAlive(pid)) {
|
|
276
|
+
unlinkSync(join4(dir, file));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function acquireAgentId(dir, maxAgents) {
|
|
281
|
+
mkdirSync2(dir, { recursive: true });
|
|
282
|
+
cleanStaleAgents(dir);
|
|
283
|
+
for (let n = 1; n <= maxAgents; n++) {
|
|
284
|
+
const lockFile = join4(dir, `agent-${n}.lock`);
|
|
285
|
+
if (!existsSync4(lockFile)) {
|
|
286
|
+
writeFileSync(lockFile, String(process.pid));
|
|
287
|
+
return `agent-${n}`;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
throw new Error(`All ${maxAgents} agent slots are occupied. Wait for one to finish or increase max_agents.`);
|
|
291
|
+
}
|
|
292
|
+
function releaseAgentId(dir, agentName) {
|
|
293
|
+
const lockFile = join4(dir, `${agentName}.lock`);
|
|
294
|
+
try {
|
|
295
|
+
unlinkSync(lockFile);
|
|
296
|
+
} catch {
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function checkTrackerForCompletion(flowDir, loop) {
|
|
300
|
+
const trackerPath = join4(flowDir, loop.tracker);
|
|
301
|
+
if (!existsSync4(trackerPath)) return false;
|
|
302
|
+
const content = readFileSync3(trackerPath, "utf-8");
|
|
303
|
+
return content.includes(`<promise>${loop.completion}</promise>`);
|
|
304
|
+
}
|
|
290
305
|
async function runLoop(loopName, options) {
|
|
291
306
|
const flowDir = resolveFlowDir(options.cwd, options.flow);
|
|
292
307
|
const config = loadConfig(flowDir);
|
|
293
308
|
const { key, loop } = resolveLoop(config, loopName);
|
|
294
|
-
|
|
309
|
+
let agentName;
|
|
310
|
+
let agentDir;
|
|
311
|
+
if (options.multiAgent) {
|
|
312
|
+
if (loop.multi_agent === false) {
|
|
313
|
+
throw new Error(`Loop "${loop.name}" does not support multi-agent mode.`);
|
|
314
|
+
}
|
|
315
|
+
const ma = loop.multi_agent;
|
|
316
|
+
agentDir = agentsDir(flowDir, loop);
|
|
317
|
+
agentName = acquireAgentId(agentDir, ma.max_agents);
|
|
318
|
+
}
|
|
295
319
|
console.log();
|
|
296
320
|
console.log(
|
|
297
|
-
chalk3.bold(` RalphFlow \u2014 ${loop.name}`) + (
|
|
321
|
+
chalk3.bold(` RalphFlow \u2014 ${loop.name}`) + (agentName ? chalk3.dim(` [${agentName}]`) : "")
|
|
298
322
|
);
|
|
299
323
|
console.log();
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
324
|
+
const cleanup = () => {
|
|
325
|
+
if (agentDir && agentName) {
|
|
326
|
+
releaseAgentId(agentDir, agentName);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
process.on("exit", cleanup);
|
|
330
|
+
process.on("SIGINT", () => {
|
|
331
|
+
cleanup();
|
|
332
|
+
process.exit(130);
|
|
333
|
+
});
|
|
334
|
+
process.on("SIGTERM", () => {
|
|
335
|
+
cleanup();
|
|
336
|
+
process.exit(143);
|
|
337
|
+
});
|
|
338
|
+
try {
|
|
339
|
+
await iterationLoop(loop, flowDir, options, agentName);
|
|
340
|
+
} finally {
|
|
341
|
+
cleanup();
|
|
304
342
|
}
|
|
305
343
|
}
|
|
306
|
-
async function
|
|
344
|
+
async function iterationLoop(loop, flowDir, options, agentName) {
|
|
307
345
|
for (let i = 1; i <= options.maxIterations; i++) {
|
|
308
|
-
|
|
309
|
-
|
|
346
|
+
const label = agentName ? chalk3.dim(` [${agentName}] Iteration ${i}/${options.maxIterations}`) : chalk3.dim(` Iteration ${i}/${options.maxIterations}`);
|
|
347
|
+
console.log(label);
|
|
348
|
+
const prompt = readPrompt(loop, flowDir, agentName);
|
|
310
349
|
const result = await spawnClaude({
|
|
311
350
|
prompt,
|
|
312
351
|
model: options.model,
|
|
313
|
-
printMode: false,
|
|
314
352
|
cwd: options.cwd
|
|
315
353
|
});
|
|
316
|
-
if (
|
|
354
|
+
if (checkTrackerForCompletion(flowDir, loop)) {
|
|
317
355
|
console.log();
|
|
318
356
|
console.log(chalk3.green(` Loop complete: ${loop.completion}`));
|
|
319
357
|
return;
|
|
@@ -323,64 +361,15 @@ async function runSingleAgent(loop, flowDir, options) {
|
|
|
323
361
|
console.log();
|
|
324
362
|
continue;
|
|
325
363
|
}
|
|
326
|
-
if (result.exitCode
|
|
327
|
-
console.log(chalk3.
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
console.log(chalk3.dim(` Iteration ${i} finished, continuing...`));
|
|
331
|
-
console.log();
|
|
332
|
-
}
|
|
333
|
-
console.log(chalk3.yellow(` Max iterations (${options.maxIterations}) reached.`));
|
|
334
|
-
}
|
|
335
|
-
async function runMultiAgent(loop, flowDir, options) {
|
|
336
|
-
const agentCount = options.agents;
|
|
337
|
-
let completed = false;
|
|
338
|
-
const agentRunners = Array.from({ length: agentCount }, (_, idx) => {
|
|
339
|
-
const agentNum = idx + 1;
|
|
340
|
-
const agentName = `agent-${agentNum}`;
|
|
341
|
-
const colorFn = AGENT_COLORS[idx % AGENT_COLORS.length];
|
|
342
|
-
return runAgentLoop(loop, flowDir, {
|
|
343
|
-
...options,
|
|
344
|
-
agentName
|
|
345
|
-
}, colorFn, () => completed, () => {
|
|
346
|
-
completed = true;
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
await Promise.allSettled(agentRunners);
|
|
350
|
-
console.log();
|
|
351
|
-
console.log(chalk3.green(` All agents finished.`));
|
|
352
|
-
}
|
|
353
|
-
async function runAgentLoop(loop, flowDir, options, colorFn, isCompleted, setCompleted) {
|
|
354
|
-
const agentName = options.agentName;
|
|
355
|
-
for (let i = 1; i <= options.maxIterations; i++) {
|
|
356
|
-
if (isCompleted()) {
|
|
357
|
-
console.log(colorFn(` [${agentName}] Stopping \u2014 completion detected.`));
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
console.log(colorFn(` [${agentName}] Iteration ${i}/${options.maxIterations}`));
|
|
361
|
-
const prompt = readPrompt(loop, flowDir, agentName);
|
|
362
|
-
const result = await spawnClaude({
|
|
363
|
-
prompt,
|
|
364
|
-
model: options.model,
|
|
365
|
-
printMode: true,
|
|
366
|
-
agentName,
|
|
367
|
-
cwd: options.cwd
|
|
368
|
-
});
|
|
369
|
-
if (result.output.includes(`<promise>${loop.completion}</promise>`)) {
|
|
370
|
-
console.log(colorFn(` [${agentName}] Loop complete: ${loop.completion}`));
|
|
371
|
-
setCompleted();
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
374
|
-
if (result.signal === "SIGINT" || result.exitCode === 130) {
|
|
375
|
-
console.log(colorFn(` [${agentName}] Iteration ${i} complete, restarting...`));
|
|
364
|
+
if (result.exitCode === 0 || result.exitCode === null) {
|
|
365
|
+
console.log(chalk3.dim(` Iteration ${i} finished, continuing...`));
|
|
366
|
+
console.log();
|
|
376
367
|
continue;
|
|
377
368
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
369
|
+
console.log(chalk3.red(` Claude exited with code ${result.exitCode}`));
|
|
370
|
+
return;
|
|
382
371
|
}
|
|
383
|
-
console.log(chalk3.yellow(`
|
|
372
|
+
console.log(chalk3.yellow(` Max iterations (${options.maxIterations}) reached.`));
|
|
384
373
|
}
|
|
385
374
|
function readPrompt(loop, flowDir, agentName) {
|
|
386
375
|
const promptPath = join4(flowDir, loop.prompt);
|
|
@@ -395,10 +384,10 @@ function readPrompt(loop, flowDir, agentName) {
|
|
|
395
384
|
}
|
|
396
385
|
|
|
397
386
|
// src/cli/run.ts
|
|
398
|
-
var runCommand = new Command2("run").description("Run a loop").argument("<loop>", "Loop to run (story, tasks, delivery)").option("-
|
|
387
|
+
var runCommand = new Command2("run").description("Run a loop").argument("<loop>", "Loop to run (story, tasks, delivery, discovery, research, document)").option("--multi-agent", "Run as a multi-agent instance (auto-assigns agent ID)").option("-m, --model <model>", "Claude model to use").option("-n, --max-iterations <n>", "Maximum iterations", "30").option("-f, --flow <name>", "Which flow to run (auto-detected if only one)").action(async (loop, opts) => {
|
|
399
388
|
try {
|
|
400
389
|
await runLoop(loop, {
|
|
401
|
-
|
|
390
|
+
multiAgent: !!opts.multiAgent,
|
|
402
391
|
model: opts.model,
|
|
403
392
|
maxIterations: parseInt(opts.maxIterations, 10),
|
|
404
393
|
flow: opts.flow,
|
|
@@ -418,7 +407,7 @@ import { Command as Command3 } from "commander";
|
|
|
418
407
|
import chalk6 from "chalk";
|
|
419
408
|
|
|
420
409
|
// src/core/status.ts
|
|
421
|
-
import { readFileSync as readFileSync4, existsSync as
|
|
410
|
+
import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
|
|
422
411
|
import { join as join5 } from "path";
|
|
423
412
|
import chalk5 from "chalk";
|
|
424
413
|
import Table from "cli-table3";
|
|
@@ -495,7 +484,7 @@ function parseTracker(trackerPath, flowDir, loopName) {
|
|
|
495
484
|
completed: 0,
|
|
496
485
|
total: 0
|
|
497
486
|
};
|
|
498
|
-
if (!
|
|
487
|
+
if (!existsSync5(fullPath)) {
|
|
499
488
|
return status;
|
|
500
489
|
}
|
|
501
490
|
const content = readFileSync4(fullPath, "utf-8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralphflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Multi-agent AI workflow orchestration framework for Claude Code. Define pipelines as loops, coordinate parallel agents, and ship structured work.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"license": "MIT",
|
|
41
41
|
"repository": {
|
|
42
42
|
"type": "git",
|
|
43
|
-
"url": "https://github.com/
|
|
43
|
+
"url": "https://github.com/rahulthakur319/ralph-flow"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=18.0.0"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Discovery Loop — Execution
|
|
2
|
+
|
|
3
|
+
## Using RalphFlow CLI
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx ralphflow run discovery
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Manual (without CLI)
|
|
10
|
+
|
|
11
|
+
### Option 1: ralph-loop slash command
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
/ralph-loop "$(cat .ralph-flow/00-discovery-loop/prompt.md)" --max-iterations 10 --completion-promise "ALL TOPICS DISCOVERED"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Option 2: while loop
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
while :; do cat .ralph-flow/00-discovery-loop/prompt.md | claude --dangerously-skip-permissions --model claude-opus-4-6; done
|
|
21
|
+
```
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Discovery Loop — Identify Research Topics
|
|
2
|
+
|
|
3
|
+
Read `.ralph-flow/00-discovery-loop/tracker.md` FIRST to determine where you are.
|
|
4
|
+
|
|
5
|
+
> **Map the territory before exploring it.** Your job is to take a broad research brief and decompose it into specific, researchable topics. Each topic should be independently investigable and contribute to the overall research goal.
|
|
6
|
+
|
|
7
|
+
> **WRITE ONLY TO:** `00-discovery-loop/topics.md`, `00-discovery-loop/tracker.md`, `01-research-loop/tracker.md`, `01-research-loop/findings.md`.
|
|
8
|
+
|
|
9
|
+
**Pipeline:** `user brief → YOU → topics.md → 01-research-loop → findings`
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## No Brief? Collect One
|
|
14
|
+
|
|
15
|
+
If `topics.md` has no unprocessed topics and the tracker queue is empty/all done:
|
|
16
|
+
1. Tell the user: *"No research brief found. Tell me what you want to research — describe questions, problems, or domains you want to understand."*
|
|
17
|
+
2. Use `AskUserQuestion` to prompt: "What do you want to research or understand?" (open-ended)
|
|
18
|
+
3. As the user narrates, capture the research brief in tracker log under `## Research Brief`
|
|
19
|
+
4. **Confirm scope** — present the brief back. Use `AskUserQuestion` (up to 5 questions) to validate: correct scope? right depth? any areas to include/exclude? target audience? desired output format (PDF, PPT, document)?
|
|
20
|
+
5. Apply corrections, finalize brief, proceed to normal flow
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## State Machine (3 stages)
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
SCOPE → Understand the research brief, define boundaries → stage: explore
|
|
28
|
+
EXPLORE → Search broadly for sub-domains, angles, key questions → stage: decompose
|
|
29
|
+
DECOMPOSE → Break into TOPIC entries, write to topics.md, seed research tracker → kill
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## First-Run Handling
|
|
33
|
+
|
|
34
|
+
If Topics Queue in tracker is empty and Research Brief exists: proceed to SCOPE. If Topics Queue is populated, check for remaining unprocessed items.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## STAGE 1: SCOPE
|
|
39
|
+
|
|
40
|
+
1. Read tracker → check if a Research Brief exists
|
|
41
|
+
2. Read any existing context: `CLAUDE.md`, project files, existing research
|
|
42
|
+
3. **Define research boundaries:**
|
|
43
|
+
- What is in scope vs. out of scope
|
|
44
|
+
- What depth is needed (surface survey vs. deep dive)
|
|
45
|
+
- Who is the audience (technical, executive, public)
|
|
46
|
+
- What output format is expected
|
|
47
|
+
4. Update tracker: `stage: explore`, log entry with scope decisions
|
|
48
|
+
|
|
49
|
+
## STAGE 2: EXPLORE
|
|
50
|
+
|
|
51
|
+
1. **Broad exploration** — use `WebSearch`, `WebFetch`, file reads to survey the domain:
|
|
52
|
+
- Search for 10+ different angles on the research question
|
|
53
|
+
- Identify key sub-domains, stakeholders, competing perspectives
|
|
54
|
+
- Note data sources, reports, standards, regulations that exist
|
|
55
|
+
- Find gaps — what's hard to find, what's controversial, what needs primary research
|
|
56
|
+
2. **Cluster findings** — group related areas into potential topic clusters
|
|
57
|
+
3. Update tracker: `stage: decompose`, log entry with exploration summary
|
|
58
|
+
|
|
59
|
+
## STAGE 3: DECOMPOSE
|
|
60
|
+
|
|
61
|
+
1. Find next TOPIC numbers (check existing in `00-discovery-loop/topics.md`)
|
|
62
|
+
2. Break the research space into **5-15 specific topics**, each:
|
|
63
|
+
- Independently researchable by a single agent
|
|
64
|
+
- Specific enough to produce focused findings (not "research everything about X")
|
|
65
|
+
- Clearly scoped with guiding questions
|
|
66
|
+
- Tagged with priority (high/medium/low) and estimated depth
|
|
67
|
+
3. Append to `00-discovery-loop/topics.md` (format below)
|
|
68
|
+
4. **Seed `01-research-loop/tracker.md`:**
|
|
69
|
+
1. Acquire `.ralph-flow/01-research-loop/.tracker-lock`:
|
|
70
|
+
- Exists + < 60s old → sleep 2s, retry up to 5 retries
|
|
71
|
+
- Exists + ≥ 60s old → stale, delete it
|
|
72
|
+
- Not exists → continue
|
|
73
|
+
- Write lock: `echo "discovery-loop $(date -u +%Y-%m-%dT%H:%M:%SZ)" > .ralph-flow/01-research-loop/.tracker-lock`
|
|
74
|
+
- Sleep 500ms, re-read lock, verify `discovery-loop` is in it
|
|
75
|
+
2. Add topics to `## Topics Queue` with metadata:
|
|
76
|
+
- `{agent: -, status: pending}` for topics with no dependencies
|
|
77
|
+
- `{agent: -, status: blocked}` for topics that depend on other topics
|
|
78
|
+
3. Add dependency entries to `## Dependencies` section
|
|
79
|
+
4. Release lock: `rm .ralph-flow/01-research-loop/.tracker-lock`
|
|
80
|
+
5. Update own tracker: mark complete, `stage: scope`, log entry
|
|
81
|
+
6. Exit: `kill -INT $PPID`
|
|
82
|
+
|
|
83
|
+
If all topics have been discovered and queue is empty: `<promise>ALL TOPICS DISCOVERED</promise>`
|
|
84
|
+
|
|
85
|
+
**Topic format:**
|
|
86
|
+
```markdown
|
|
87
|
+
## TOPIC-{N}: {Concise title}
|
|
88
|
+
|
|
89
|
+
**Priority:** {high | medium | low}
|
|
90
|
+
**Depth:** {surface | moderate | deep}
|
|
91
|
+
**Depends on:** {TOPIC-{M} or "None"}
|
|
92
|
+
|
|
93
|
+
### Research Question
|
|
94
|
+
{The specific question(s) this topic should answer. 1-3 sentences.}
|
|
95
|
+
|
|
96
|
+
### Scope
|
|
97
|
+
{What to include and exclude. Key angles to cover.}
|
|
98
|
+
|
|
99
|
+
### Suggested Sources
|
|
100
|
+
{Types of sources to look for: government data, academic papers, news, industry reports, etc.}
|
|
101
|
+
|
|
102
|
+
### Success Criteria
|
|
103
|
+
- [ ] {What constitutes a complete investigation of this topic}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Rules
|
|
109
|
+
|
|
110
|
+
- One research brief at a time. All 3 stages run in one iteration, one `kill` at the end.
|
|
111
|
+
- Read tracker first, update tracker last.
|
|
112
|
+
- Append to `topics.md` — never overwrite. Numbers globally unique and sequential.
|
|
113
|
+
- Topics must be self-contained — the research loop never reads the original brief directly.
|
|
114
|
+
- Each topic should take 1-3 research iterations to complete, not more.
|
|
115
|
+
- Mark inter-topic dependencies explicitly (some topics build on findings from others).
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
Read `.ralph-flow/00-discovery-loop/tracker.md` now and begin.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Research Loop — Execution
|
|
2
|
+
|
|
3
|
+
## Using RalphFlow CLI
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Single agent
|
|
7
|
+
npx ralphflow run research
|
|
8
|
+
|
|
9
|
+
# Multi-agent (3 parallel agents)
|
|
10
|
+
npx ralphflow run research --agents 3
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Manual (without CLI)
|
|
14
|
+
|
|
15
|
+
### Single Agent
|
|
16
|
+
|
|
17
|
+
#### Option 1: ralph-loop slash command
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
/ralph-loop "$(cat .ralph-flow/01-research-loop/prompt.md)" --max-iterations 50 --completion-promise "ALL TOPICS RESEARCHED"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
#### Option 2: while loop
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
while :; do cat .ralph-flow/01-research-loop/prompt.md | claude --dangerously-skip-permissions --model claude-opus-4-6; done
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
### Multi-Agent (2+ terminals)
|
|
32
|
+
|
|
33
|
+
Each terminal runs a named agent. `{{AGENT_NAME}}` is injected via `sed`.
|
|
34
|
+
|
|
35
|
+
#### Terminal 1
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
AGENT=agent-1 && while :; do sed "s/{{AGENT_NAME}}/$AGENT/g" .ralph-flow/01-research-loop/prompt.md | claude --dangerously-skip-permissions --model claude-opus-4-6; done
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### Terminal 2
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
AGENT=agent-2 && while :; do sed "s/{{AGENT_NAME}}/$AGENT/g" .ralph-flow/01-research-loop/prompt.md | claude --dangerously-skip-permissions --model claude-opus-4-6; done
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### Terminal 3 (optional)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
AGENT=agent-3 && while :; do sed "s/{{AGENT_NAME}}/$AGENT/g" .ralph-flow/01-research-loop/prompt.md | claude --dangerously-skip-permissions --model claude-opus-4-6; done
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Notes
|
|
56
|
+
|
|
57
|
+
- Single agent mode uses `cat` — no `{{AGENT_NAME}}` substitution needed (prompt defaults to `agent-1`)
|
|
58
|
+
- Multi-agent mode uses `sed` to inject the agent name into the prompt
|
|
59
|
+
- Each agent gets its own terminal — they coordinate via `tracker.md`
|
|
60
|
+
- Up to 4 agents can run concurrently depending on topic dependency chains
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Research Loop — Investigate Topics
|
|
2
|
+
|
|
3
|
+
**You are agent `{{AGENT_NAME}}`.** Multiple agents may work in parallel.
|
|
4
|
+
Coordinate via `tracker.md` — the single source of truth.
|
|
5
|
+
*(If you see the literal text `{{AGENT_NAME}}` above — i.e., it was not substituted — treat your name as `agent-1`.)*
|
|
6
|
+
|
|
7
|
+
Read `.ralph-flow/01-research-loop/tracker.md` FIRST to determine where you are.
|
|
8
|
+
|
|
9
|
+
> **Go deep, stay focused.** Each topic is a specific research question. Your job is to investigate thoroughly and produce structured findings. Use web search, file reading, and any available tools to gather evidence.
|
|
10
|
+
|
|
11
|
+
**Pipeline:** `topics.md → YOU → findings.md → 02-story-loop → narratives`
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Tracker Lock Protocol
|
|
16
|
+
|
|
17
|
+
Before ANY write to `tracker.md`, you MUST acquire the lock:
|
|
18
|
+
|
|
19
|
+
**Lock file:** `.ralph-flow/01-research-loop/.tracker-lock`
|
|
20
|
+
|
|
21
|
+
### Acquire Lock
|
|
22
|
+
1. Check if `.tracker-lock` exists
|
|
23
|
+
- Exists AND file is < 60 seconds old → sleep 2s, retry (up to 5 retries)
|
|
24
|
+
- Exists AND file is ≥ 60 seconds old → stale lock, delete it (agent crashed mid-write)
|
|
25
|
+
- Does not exist → continue
|
|
26
|
+
2. Write lock: `echo "{{AGENT_NAME}} $(date -u +%Y-%m-%dT%H:%M:%SZ)" > .ralph-flow/01-research-loop/.tracker-lock`
|
|
27
|
+
3. Sleep 500ms (`sleep 0.5`)
|
|
28
|
+
4. Re-read `.tracker-lock` — verify YOUR agent name (`{{AGENT_NAME}}`) is in it
|
|
29
|
+
- Your name → you own the lock, proceed to write `tracker.md`
|
|
30
|
+
- Other name → you lost the race, retry from step 1
|
|
31
|
+
5. Write your changes to `tracker.md`
|
|
32
|
+
6. Delete `.tracker-lock` immediately: `rm .ralph-flow/01-research-loop/.tracker-lock`
|
|
33
|
+
7. Never leave a lock held — if your write fails, delete the lock in your error handler
|
|
34
|
+
|
|
35
|
+
### When to Lock
|
|
36
|
+
- Claiming a topic (pending → in_progress)
|
|
37
|
+
- Completing a topic (in_progress → completed)
|
|
38
|
+
- Updating stage transitions
|
|
39
|
+
- Heartbeat updates (bundled with other writes, not standalone)
|
|
40
|
+
|
|
41
|
+
### When NOT to Lock
|
|
42
|
+
- Reading `tracker.md` — read-only access needs no lock
|
|
43
|
+
- Reading `topics.md` or `findings.md` — always read-only for topics, append-only for findings
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Topic Selection Algorithm
|
|
48
|
+
|
|
49
|
+
1. **Parse tracker** — read `completed_topics`, `## Dependencies`, Topics Queue metadata `{agent, status}`, Agent Status table
|
|
50
|
+
2. **Update blocked→pending** — for each topic with `status: blocked`, check if ALL its dependencies (from `## Dependencies`) are in `completed_topics`. If yes, acquire lock and update to `status: pending`
|
|
51
|
+
3. **Resume own work** — if any topic has `{agent: {{AGENT_NAME}}, status: in_progress}`, resume it (skip to the current stage)
|
|
52
|
+
4. **Find claimable** — filter topics where `status: pending` AND `agent: -`
|
|
53
|
+
5. **Apply priority** — prefer high-priority topics, then medium, then low
|
|
54
|
+
6. **Claim** — acquire lock, set `{agent: {{AGENT_NAME}}, status: in_progress}`, update your Agent Status row, update `last_heartbeat`, release lock, log the claim
|
|
55
|
+
7. **Nothing available:**
|
|
56
|
+
- All topics completed → emit `<promise>ALL TOPICS RESEARCHED</promise>`
|
|
57
|
+
- All remaining topics are blocked or claimed by others → log "{{AGENT_NAME}}: waiting — all topics blocked or claimed", exit: `kill -INT $PPID`
|
|
58
|
+
|
|
59
|
+
### New Topic Discovery
|
|
60
|
+
|
|
61
|
+
If you find a topic in the Topics Queue without `{agent, status}` metadata (e.g., added by the discovery loop while agents were running):
|
|
62
|
+
1. Read the topic's `**Depends on:**` field in `topics.md`
|
|
63
|
+
2. Add the dependency to `## Dependencies` section if not already there (skip if `Depends on: None`)
|
|
64
|
+
3. Set status to `pending` (all deps in `completed_topics`) or `blocked` (deps incomplete)
|
|
65
|
+
4. Set agent to `-`
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Anti-Hijacking Rules
|
|
70
|
+
|
|
71
|
+
1. **Never touch another agent's `in_progress` topic** — do not modify, complete, or reassign it
|
|
72
|
+
2. **Respect priority** — do not skip high-priority topics to grab low-priority ones unless high-priority are all blocked/claimed
|
|
73
|
+
3. **Note overlap** — if your topic overlaps with another agent's active topic, log a NOTE in the tracker and avoid duplicating their research angles
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Heartbeat Protocol
|
|
78
|
+
|
|
79
|
+
Every tracker write includes updating your `last_heartbeat` to current ISO 8601 timestamp in the Agent Status table. If another agent's heartbeat is **30+ minutes stale**, log a WARNING in the tracker log but do NOT auto-reclaim their topic — user must manually reset.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Crash Recovery (Self)
|
|
84
|
+
|
|
85
|
+
On fresh start, if your agent name has an `in_progress` topic but you have no memory of it:
|
|
86
|
+
- Findings already written for that topic → resume at SYNTHESIZE stage
|
|
87
|
+
- No findings found → restart from INVESTIGATE stage
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## State Machine (2 stages per topic)
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
INVESTIGATE → Search, read, gather evidence on the topic → stage: synthesize
|
|
95
|
+
SYNTHESIZE → Structure findings, write to findings.md, mark done → next topic
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
When ALL done: `<promise>ALL TOPICS RESEARCHED</promise>`
|
|
99
|
+
|
|
100
|
+
After completing ANY stage, exit: `kill -INT $PPID`
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## STAGE 1: INVESTIGATE
|
|
105
|
+
|
|
106
|
+
1. Read tracker → **run topic selection algorithm** (see above)
|
|
107
|
+
2. Read topic in `topics.md` — understand the research question, scope, suggested sources
|
|
108
|
+
3. If sibling topics are done, read their findings to avoid duplication and build on prior work
|
|
109
|
+
4. Acquire lock → update tracker: your Agent Status row `active_topic: TOPIC-{N}`, `stage: investigate`, `last_heartbeat`, log entry → release lock
|
|
110
|
+
5. **Deep research:**
|
|
111
|
+
- Use `WebSearch` for 5-10 different search queries covering different angles of the topic
|
|
112
|
+
- Use `WebFetch` to read key sources in detail (reports, articles, data pages)
|
|
113
|
+
- Read any relevant local files (project docs, data files, prior research)
|
|
114
|
+
- Cross-reference sources — look for consensus and disagreements
|
|
115
|
+
- Note data points, statistics, quotes, and source URLs
|
|
116
|
+
- If the topic requires it, explore primary sources (government sites, official reports)
|
|
117
|
+
6. **Organize raw notes** — keep structured scratch notes as you research
|
|
118
|
+
7. Acquire lock → update tracker: `stage: synthesize`, `last_heartbeat`, log entry → release lock
|
|
119
|
+
8. Exit: `kill -INT $PPID`
|
|
120
|
+
|
|
121
|
+
## STAGE 2: SYNTHESIZE
|
|
122
|
+
|
|
123
|
+
1. Read your raw notes and all gathered evidence
|
|
124
|
+
2. **Structure findings** — write a FINDING entry to `01-research-loop/findings.md`:
|
|
125
|
+
- Use the finding format below
|
|
126
|
+
- Include specific data points, statistics, and source citations
|
|
127
|
+
- Note confidence level for each key claim
|
|
128
|
+
- Flag gaps — what couldn't be found, what needs primary research
|
|
129
|
+
3. Acquire lock:
|
|
130
|
+
- Add topic to `completed_topics` list
|
|
131
|
+
- Check off topic in Topics Queue: `[x]`, set `{completed}`
|
|
132
|
+
- **Unblock dependents:** for each topic in `## Dependencies` that lists the just-completed topic, check if ALL its dependencies are now in `completed_topics`. If yes, update that topic's status from `blocked` → `pending`
|
|
133
|
+
- Update your Agent Status row: clear `active_topic`
|
|
134
|
+
- Update `last_heartbeat`
|
|
135
|
+
- Log entry
|
|
136
|
+
- Release lock
|
|
137
|
+
4. **Run topic selection algorithm again:**
|
|
138
|
+
- Claimable topic found → claim it, exit: `kill -INT $PPID`
|
|
139
|
+
- All topics completed → `<promise>ALL TOPICS RESEARCHED</promise>`
|
|
140
|
+
- All blocked/claimed → log "waiting", exit: `kill -INT $PPID`
|
|
141
|
+
|
|
142
|
+
**Finding format:**
|
|
143
|
+
```markdown
|
|
144
|
+
## FINDING-{N}: {Title matching TOPIC title}
|
|
145
|
+
|
|
146
|
+
**Source Topic:** TOPIC-{M}
|
|
147
|
+
**Researched by:** {{AGENT_NAME}}
|
|
148
|
+
**Date:** {ISO 8601 date}
|
|
149
|
+
**Confidence:** {high | medium | low}
|
|
150
|
+
|
|
151
|
+
### Key Findings
|
|
152
|
+
{3-7 bullet points summarizing the most important discoveries. Each should be specific and evidence-backed.}
|
|
153
|
+
|
|
154
|
+
### Detailed Analysis
|
|
155
|
+
{2-4 paragraphs of structured analysis. Include data points, statistics, comparisons.}
|
|
156
|
+
|
|
157
|
+
### Sources
|
|
158
|
+
{Numbered list of sources with URLs where available}
|
|
159
|
+
1. {Source title} — {URL or description}
|
|
160
|
+
|
|
161
|
+
### Gaps & Open Questions
|
|
162
|
+
{What couldn't be answered? What needs further investigation or primary research?}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## First-Run Handling
|
|
168
|
+
|
|
169
|
+
If Topics Queue in tracker is empty: read `topics.md`, scan `## TOPIC-{N}:` headers + `**Depends on:**` tags, populate queue with `{agent: -, status: pending|blocked}` metadata, then start.
|
|
170
|
+
|
|
171
|
+
## Rules
|
|
172
|
+
|
|
173
|
+
- One topic at a time per agent. One stage per iteration.
|
|
174
|
+
- Read tracker first, update tracker last. Always use lock protocol for writes.
|
|
175
|
+
- Append to `findings.md` — never overwrite. FINDING numbers match TOPIC numbers (FINDING-1 for TOPIC-1).
|
|
176
|
+
- Findings must be self-contained — the story loop never reads `topics.md`.
|
|
177
|
+
- Cite sources. Include URLs. Note confidence levels.
|
|
178
|
+
- **Multi-agent: never touch another agent's in_progress topic. Coordinate via tracker.md.**
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
Read `.ralph-flow/01-research-loop/tracker.md` now and begin.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Story Loop — Execution
|
|
2
|
+
|
|
3
|
+
## Using RalphFlow CLI
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx ralphflow run story
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Manual (without CLI)
|
|
10
|
+
|
|
11
|
+
### Option 1: ralph-loop slash command
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
/ralph-loop "$(cat .ralph-flow/02-story-loop/prompt.md)" --max-iterations 20 --completion-promise "ALL STORIES WRITTEN"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Option 2: while loop
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
while :; do cat .ralph-flow/02-story-loop/prompt.md | claude --dangerously-skip-permissions --model claude-opus-4-6; done
|
|
21
|
+
```
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Story Loop — Condense Findings into Narratives
|
|
2
|
+
|
|
3
|
+
Read `.ralph-flow/02-story-loop/tracker.md` FIRST to determine where you are.
|
|
4
|
+
|
|
5
|
+
> **Turn research into readable stories.** Your job is to take raw findings and synthesize them into coherent, well-structured narrative stories. Each story should stand on its own while contributing to the overall research picture.
|
|
6
|
+
|
|
7
|
+
> **WRITE ONLY TO:** `02-story-loop/stories.md`, `02-story-loop/tracker.md`.
|
|
8
|
+
|
|
9
|
+
**Pipeline:** `findings.md → YOU → stories.md → 03-document-loop → final output`
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## No Findings? Wait
|
|
14
|
+
|
|
15
|
+
If `findings.md` has no unprocessed findings and the tracker queue is empty/all done:
|
|
16
|
+
1. Check `01-research-loop/tracker.md` — is the research loop still running?
|
|
17
|
+
- Yes → tell user "Research is still in progress. Run story loop after research completes."
|
|
18
|
+
- No → `<promise>ALL STORIES WRITTEN</promise>`
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## State Machine (2 stages per story)
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
DRAFT → Read findings, identify themes, draft a narrative story → stage: refine
|
|
26
|
+
REFINE → Polish language, verify citations, ensure coherence → mark done, kill
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## First-Run Handling
|
|
30
|
+
|
|
31
|
+
If Stories Queue in tracker is empty:
|
|
32
|
+
1. Read ALL findings from `01-research-loop/findings.md`
|
|
33
|
+
2. **Identify story themes** — group related findings into story clusters:
|
|
34
|
+
- Look for natural narrative arcs (problem → evidence → insight)
|
|
35
|
+
- Group by theme, not by topic number
|
|
36
|
+
- A story may synthesize 2-5 findings
|
|
37
|
+
- Some findings may contribute to multiple stories
|
|
38
|
+
3. Create story entries in queue with titles and source findings
|
|
39
|
+
4. Proceed to first story
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## STAGE 1: DRAFT
|
|
44
|
+
|
|
45
|
+
1. Read tracker → pick next unprocessed story from queue
|
|
46
|
+
2. Read ALL source findings for this story from `findings.md`
|
|
47
|
+
3. Read completed stories from `stories.md` to maintain consistency and avoid repetition
|
|
48
|
+
4. **Draft the narrative:**
|
|
49
|
+
- Open with a compelling hook or framing question
|
|
50
|
+
- Build the argument/narrative logically
|
|
51
|
+
- Weave in specific data points, statistics, and evidence from findings
|
|
52
|
+
- Include source citations (inline references to finding sources)
|
|
53
|
+
- Address counterarguments or gaps where relevant
|
|
54
|
+
- Close with implications or key takeaways
|
|
55
|
+
5. Write draft to `stories.md` (format below)
|
|
56
|
+
6. Update tracker: `active_story: STORY-{N}`, `stage: refine`, log entry
|
|
57
|
+
|
|
58
|
+
## STAGE 2: REFINE
|
|
59
|
+
|
|
60
|
+
1. Re-read the draft from `stories.md`
|
|
61
|
+
2. **Polish and verify:**
|
|
62
|
+
- Check narrative flow — does each paragraph lead naturally to the next?
|
|
63
|
+
- Verify all cited data points match the source findings
|
|
64
|
+
- Ensure the story stands alone (reader doesn't need to read findings)
|
|
65
|
+
- Tighten language — remove filler, sharpen claims, strengthen evidence links
|
|
66
|
+
- Add section headers for readability if the story is long
|
|
67
|
+
- Verify the story doesn't duplicate content from other completed stories
|
|
68
|
+
3. Update the story in `stories.md` with refined version
|
|
69
|
+
4. Mark done in tracker: check off queue entry, update `active_story: none`, `stage: draft`, log entry
|
|
70
|
+
5. Exit: `kill -INT $PPID`
|
|
71
|
+
|
|
72
|
+
If all stories written: `<promise>ALL STORIES WRITTEN</promise>`
|
|
73
|
+
|
|
74
|
+
**Story format:**
|
|
75
|
+
```markdown
|
|
76
|
+
## STORY-{N}: {Compelling title}
|
|
77
|
+
|
|
78
|
+
**Source Findings:** FINDING-{A}, FINDING-{B}, ...
|
|
79
|
+
**Theme:** {1-sentence theme descriptor}
|
|
80
|
+
**Word Count:** {target: 500-1500 words}
|
|
81
|
+
|
|
82
|
+
### {Opening section header}
|
|
83
|
+
{Hook + context setting. 1-2 paragraphs.}
|
|
84
|
+
|
|
85
|
+
### {Evidence/analysis section header}
|
|
86
|
+
{Core argument with data points and citations. 2-4 paragraphs.}
|
|
87
|
+
|
|
88
|
+
### {Implications/takeaway section header}
|
|
89
|
+
{What this means, what to do about it. 1-2 paragraphs.}
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
**Sources:** {Consolidated list of sources cited in this story}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Rules
|
|
98
|
+
|
|
99
|
+
- One story at a time. Both stages run in one iteration, one `kill` at the end.
|
|
100
|
+
- Read tracker first, update tracker last.
|
|
101
|
+
- Stories must be self-contained — a reader should understand the story without reading the raw findings.
|
|
102
|
+
- Write for the audience identified in the discovery loop's research brief (check `00-discovery-loop/tracker.md` for audience context).
|
|
103
|
+
- Cite specific data points. Don't make vague claims — tie everything back to evidence.
|
|
104
|
+
- Each story should be 500-1500 words. Quality over quantity.
|
|
105
|
+
- Stories synthesize findings — they don't just summarize them. Add narrative structure, analysis, and implications.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
Read `.ralph-flow/02-story-loop/tracker.md` now and begin.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Document Loop — Execution
|
|
2
|
+
|
|
3
|
+
## Using RalphFlow CLI
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx ralphflow run document
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Manual (on-demand, single run)
|
|
10
|
+
|
|
11
|
+
### Option 1: ralph-loop slash command
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
/ralph-loop "$(cat .ralph-flow/03-document-loop/prompt.md)" --max-iterations 1 --completion-promise "DOCUMENT COMPLETE"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Option 2: Direct invocation (no while loop — runs once)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cat .ralph-flow/03-document-loop/prompt.md | claude --dangerously-skip-permissions --model claude-opus-4-6
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Notes
|
|
26
|
+
|
|
27
|
+
- This is an **on-demand** loop — it runs once to produce the final document, not in a recurring while loop
|
|
28
|
+
- Run this after the story loop has completed all stories
|
|
29
|
+
- The agent will ask for output format preferences if not specified in the research brief
|
|
30
|
+
- Output files are written to the project root directory
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Document Loop — Compile Stories into Final Output
|
|
2
|
+
|
|
3
|
+
Read `.ralph-flow/03-document-loop/tracker.md` FIRST to determine where you are.
|
|
4
|
+
|
|
5
|
+
> **This is an on-demand loop.** Unlike discovery, research, and story loops, this loop runs once to produce the final document. It reads all completed stories and compiles them into the requested output format.
|
|
6
|
+
|
|
7
|
+
> **WRITE ONLY TO:** `03-document-loop/tracker.md`, and the output file(s) in the project directory.
|
|
8
|
+
|
|
9
|
+
**Pipeline:** `stories.md → YOU → final document (PDF, PPT, Markdown, etc.)`
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## STAGE 1: COMPILE
|
|
14
|
+
|
|
15
|
+
1. **Read context:**
|
|
16
|
+
- Read `00-discovery-loop/tracker.md` → get the Research Brief (audience, scope, output format)
|
|
17
|
+
- Read `02-story-loop/stories.md` → all completed stories
|
|
18
|
+
- Read `02-story-loop/tracker.md` → verify all stories are complete
|
|
19
|
+
- Read `CLAUDE.md` for any project-specific context
|
|
20
|
+
|
|
21
|
+
2. **Determine output format** from the Research Brief. If not specified, use `AskUserQuestion`:
|
|
22
|
+
- "What format should the final document be? (markdown/pdf/ppt/html)" with options
|
|
23
|
+
- Also ask: "Any specific structure, branding, or style requirements?"
|
|
24
|
+
|
|
25
|
+
3. **Plan document structure:**
|
|
26
|
+
- Executive summary / abstract
|
|
27
|
+
- Table of contents
|
|
28
|
+
- Arrange stories in logical reading order (not necessarily story-number order)
|
|
29
|
+
- Add transitions between stories for narrative flow
|
|
30
|
+
- Create introduction and conclusion that frame the overall research
|
|
31
|
+
- Add appendices if needed (methodology notes, full source list, data tables)
|
|
32
|
+
|
|
33
|
+
4. **Write the document:**
|
|
34
|
+
|
|
35
|
+
**For Markdown (.md):**
|
|
36
|
+
- Write a single comprehensive markdown file to the project directory
|
|
37
|
+
- Include all sections, proper heading hierarchy, citations
|
|
38
|
+
|
|
39
|
+
**For HTML:**
|
|
40
|
+
- Write a styled HTML file with clean, professional CSS
|
|
41
|
+
- Include print-friendly styles for PDF conversion
|
|
42
|
+
- Responsive layout suitable for reading
|
|
43
|
+
|
|
44
|
+
**For PPT/Presentation:**
|
|
45
|
+
- Write a structured markdown file optimized for presentation
|
|
46
|
+
- Each major section = 1-3 slides
|
|
47
|
+
- Key findings as bullet points, not paragraphs
|
|
48
|
+
- Include speaker notes as HTML comments
|
|
49
|
+
- Create a companion script or instructions for converting to actual PPT (e.g., using marp, pandoc, or similar)
|
|
50
|
+
|
|
51
|
+
**For PDF:**
|
|
52
|
+
- Write a well-structured markdown or HTML file optimized for PDF conversion
|
|
53
|
+
- Include instructions for converting to PDF (pandoc, browser print, etc.)
|
|
54
|
+
|
|
55
|
+
5. **Compile source bibliography:**
|
|
56
|
+
- Gather all sources from all findings
|
|
57
|
+
- Deduplicate and format consistently
|
|
58
|
+
- Add as appendix
|
|
59
|
+
|
|
60
|
+
6. **Update tracker:**
|
|
61
|
+
- Record output file path, format, word count
|
|
62
|
+
- Log completion
|
|
63
|
+
- `<promise>DOCUMENT COMPLETE</promise>`
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Document Structure Template
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
# {Research Title}
|
|
71
|
+
|
|
72
|
+
**Prepared by:** {from CLAUDE.md or user context}
|
|
73
|
+
**Date:** {current date}
|
|
74
|
+
**Audience:** {from research brief}
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Executive Summary
|
|
79
|
+
{2-3 paragraphs synthesizing the entire research. Key findings, implications, recommendations.}
|
|
80
|
+
|
|
81
|
+
## Table of Contents
|
|
82
|
+
{Auto-generated from section headers}
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
{Stories arranged in logical order, with transitions}
|
|
87
|
+
|
|
88
|
+
## Story Section 1: {title}
|
|
89
|
+
{Story content, refined for document context}
|
|
90
|
+
|
|
91
|
+
## Story Section 2: {title}
|
|
92
|
+
{Story content}
|
|
93
|
+
|
|
94
|
+
...
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Conclusions & Recommendations
|
|
99
|
+
{Cross-cutting insights that emerge from the stories together}
|
|
100
|
+
|
|
101
|
+
## Appendix A: Methodology
|
|
102
|
+
{Brief description of research approach}
|
|
103
|
+
|
|
104
|
+
## Appendix B: Sources
|
|
105
|
+
{Consolidated, deduplicated bibliography}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Rules
|
|
111
|
+
|
|
112
|
+
- This runs ONCE, on-demand. It is not a recurring loop.
|
|
113
|
+
- Read ALL stories before writing — the document must be coherent as a whole.
|
|
114
|
+
- Respect the audience — adjust tone, depth, and jargon accordingly.
|
|
115
|
+
- The document should add value beyond just concatenating stories — add executive summary, transitions, cross-cutting analysis, and recommendations.
|
|
116
|
+
- Cite sources properly. Include a full bibliography.
|
|
117
|
+
- Write to the project root directory, not inside `.ralph-flow/`.
|
|
118
|
+
- If the user wants multiple formats, produce each one.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
Read `.ralph-flow/03-document-loop/tracker.md` now and begin.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: research
|
|
2
|
+
description: "Discovery → Research → Story → Document pipeline for research projects"
|
|
3
|
+
version: 1
|
|
4
|
+
dir: .ralph-flow
|
|
5
|
+
|
|
6
|
+
entities:
|
|
7
|
+
TOPIC:
|
|
8
|
+
prefix: TOPIC
|
|
9
|
+
data_file: 00-discovery-loop/topics.md
|
|
10
|
+
FINDING:
|
|
11
|
+
prefix: FINDING
|
|
12
|
+
data_file: 01-research-loop/findings.md
|
|
13
|
+
STORY:
|
|
14
|
+
prefix: STORY
|
|
15
|
+
data_file: 02-story-loop/stories.md
|
|
16
|
+
|
|
17
|
+
loops:
|
|
18
|
+
discovery-loop:
|
|
19
|
+
order: 0
|
|
20
|
+
name: "Discovery Loop"
|
|
21
|
+
prompt: 00-discovery-loop/prompt.md
|
|
22
|
+
tracker: 00-discovery-loop/tracker.md
|
|
23
|
+
data_files:
|
|
24
|
+
- 00-discovery-loop/topics.md
|
|
25
|
+
entities: [TOPIC]
|
|
26
|
+
stages: [scope, explore, decompose]
|
|
27
|
+
completion: "ALL TOPICS DISCOVERED"
|
|
28
|
+
feeds: [research-loop]
|
|
29
|
+
multi_agent: false
|
|
30
|
+
cadence: 0
|
|
31
|
+
|
|
32
|
+
research-loop:
|
|
33
|
+
order: 1
|
|
34
|
+
name: "Research Loop"
|
|
35
|
+
prompt: 01-research-loop/prompt.md
|
|
36
|
+
tracker: 01-research-loop/tracker.md
|
|
37
|
+
data_files:
|
|
38
|
+
- 01-research-loop/findings.md
|
|
39
|
+
entities: [TOPIC, FINDING]
|
|
40
|
+
stages: [investigate, synthesize]
|
|
41
|
+
completion: "ALL TOPICS RESEARCHED"
|
|
42
|
+
fed_by: [discovery-loop]
|
|
43
|
+
feeds: [story-loop]
|
|
44
|
+
multi_agent:
|
|
45
|
+
enabled: true
|
|
46
|
+
max_agents: 4
|
|
47
|
+
strategy: tracker-lock
|
|
48
|
+
agent_placeholder: "{{AGENT_NAME}}"
|
|
49
|
+
lock:
|
|
50
|
+
file: 01-research-loop/.tracker-lock
|
|
51
|
+
type: echo
|
|
52
|
+
stale_seconds: 60
|
|
53
|
+
cadence: 0
|
|
54
|
+
|
|
55
|
+
story-loop:
|
|
56
|
+
order: 2
|
|
57
|
+
name: "Story Loop"
|
|
58
|
+
prompt: 02-story-loop/prompt.md
|
|
59
|
+
tracker: 02-story-loop/tracker.md
|
|
60
|
+
data_files:
|
|
61
|
+
- 02-story-loop/stories.md
|
|
62
|
+
entities: [STORY]
|
|
63
|
+
stages: [draft, refine]
|
|
64
|
+
completion: "ALL STORIES WRITTEN"
|
|
65
|
+
fed_by: [research-loop]
|
|
66
|
+
feeds: [document-loop]
|
|
67
|
+
multi_agent: false
|
|
68
|
+
cadence: 0
|
|
69
|
+
|
|
70
|
+
document-loop:
|
|
71
|
+
order: 3
|
|
72
|
+
name: "Document Loop"
|
|
73
|
+
prompt: 03-document-loop/prompt.md
|
|
74
|
+
tracker: 03-document-loop/tracker.md
|
|
75
|
+
entities: [STORY]
|
|
76
|
+
stages: [compile]
|
|
77
|
+
completion: "DOCUMENT COMPLETE"
|
|
78
|
+
fed_by: [story-loop]
|
|
79
|
+
multi_agent: false
|
|
80
|
+
cadence: 0
|