@pageai/ralph-loop 1.12.0 β 1.13.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 +51 -44
- package/bin/cli.js +20 -28
- package/bin/lib/consts.js +9 -0
- package/bin/lib/playwright.js +48 -0
- package/bin/lib/vitest.js +59 -0
- package/package.json +1 -1
- package/scripts/assets/vitest.config.ts +17 -0
package/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# A Ralph Wiggum Loop implementation that worksβ’
|
|
2
2
|
|
|
3
|
-
Ralph is a long-running AI agent loop. Ralph automates software development tasks by iteratively working through a task list until completion.
|
|
3
|
+
Ralph is a long-running AI agent loop. Ralph automates software development tasks by iteratively working through a task list until completion. This allows for long running agent loops, effectively enabling AI to code for days at a time.
|
|
4
4
|
|
|
5
|
-
This is an implementation that actually works, containing a hackable script so you can configure it to your env and favorite agentic AI CLI. It's set up by default to use Claude Code in a Docker sandbox.
|
|
5
|
+
This is an implementation that actually works, containing a hackable script so you can configure it to your env and favorite agentic AI CLI. It's set up by default to use Claude Code in a Docker sandbox, but supports [many other agentic AI CLIs](#running-with-a-different-agentic-cli).
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
#### π [Watch the video](https://www.youtube.com/watch?v=3TL8Ez66I3o) for an in-depth walkthrough.
|
|
8
|
+
|
|
9
|
+
[](https://www.youtube.com/watch?v=3TL8Ez66I3o)
|
|
8
10
|
|
|
9
11
|
- [Getting Started](#getting-started)
|
|
10
12
|
- [(Optional) Set up code base](#optional-set-up-code-base)
|
|
@@ -17,7 +19,6 @@ This is an implementation that actually works, containing a hackable script so y
|
|
|
17
19
|
- [How It Works](#how-it-works)
|
|
18
20
|
- [How Is This Different from Other Ralphs?](#how-is-this-different-from-other-ralphs)
|
|
19
21
|
- [Steering the Agent](#steering-the-agent)
|
|
20
|
-
- [Features](#features)
|
|
21
22
|
- [Support](#support)
|
|
22
23
|
- [Promise Tags](#promise-tags)
|
|
23
24
|
- [Exit Codes](#exit-codes)
|
|
@@ -84,15 +85,20 @@ Requirements:
|
|
|
84
85
|
// etc.
|
|
85
86
|
```
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
<details>
|
|
89
|
+
<summary><strong>β¨ Pro tips</strong></summary>
|
|
90
|
+
|
|
88
91
|
- mention libraries and frameworks you want to use
|
|
89
|
-
- mention env variables, e.g. for
|
|
92
|
+
- mention env variables, e.g. for DB, 3rd party API keys, etc. Store them in `.env` and add it to **.gitignore**
|
|
90
93
|
- describe user flows and journeys
|
|
91
94
|
- add relevant docs and UI references if applicable inside `/docs` and mention them in the requirements
|
|
92
95
|
- be as descriptive as possible
|
|
93
96
|
- *it's fine to write in your own language*
|
|
94
97
|
|
|
95
|
-
|
|
98
|
+
</details>
|
|
99
|
+
<br/>
|
|
100
|
+
|
|
101
|
+
Then, follow the Skill's instructions and verify the PRD and then tasks.<br/>
|
|
96
102
|
**It is highly recommended that you review individual task requirements before starting the loop. Review EACH TASK INDIVIDUALLY.**
|
|
97
103
|
|
|
98
104
|
### 3οΈβ£ Step 3: Set up the agent inside Docker sandbox
|
|
@@ -162,7 +168,7 @@ Each iteration, Ralph will:
|
|
|
162
168
|
1. Find the highest-priority incomplete task from `.agent/tasks.json`
|
|
163
169
|
2. Work through the task steps defined in `.agent/tasks/TASK-{ID}.json`
|
|
164
170
|
3. Run tests, linting, and type checking
|
|
165
|
-
4.
|
|
171
|
+
4. Complete task, take screenshot, update task status and commit changes
|
|
166
172
|
5. Repeat until all tasks pass or max iterations reached
|
|
167
173
|
|
|
168
174
|
## How Is This Different from Other Ralphs?
|
|
@@ -172,10 +178,27 @@ The script follows the original concepts of the Ralph Wiggum Loop, working with
|
|
|
172
178
|
|
|
173
179
|
It also works generically with any task set.
|
|
174
180
|
|
|
181
|
+
<details>
|
|
182
|
+
<summary><strong>β¨ Features</strong></summary>
|
|
183
|
+
|
|
184
|
+
- **PRD generation** - Creates a PRD and task list from requirements
|
|
185
|
+
- **Task lookup table generation** - Creates a task lookup table from the PRD
|
|
186
|
+
- **Task breakdown + step generation** - Breaks down each task into manageable steps
|
|
187
|
+
- **Iteration tracking** - Shows progress through iterations with timing
|
|
188
|
+
- **Stream preview** - Shows live output from the Agent
|
|
189
|
+
- **Step detection** - Identifies current activity (Thinking, Implementing, Testing, etc.)
|
|
190
|
+
- **Screenshot capture** - Captures a screenshot of the current screen
|
|
191
|
+
- **Notifications** - Alerts when human input is needed
|
|
192
|
+
- **History logging** - Saves clean output from each iteration
|
|
193
|
+
- **Timing** - Shows timing metrics for each iteration and total time
|
|
194
|
+
- **Steering** - Allows prioritizing critical work that needs to be done before the loop can continue
|
|
195
|
+
</details>
|
|
196
|
+
|
|
197
|
+
<br/>
|
|
175
198
|
Besides that:
|
|
176
199
|
|
|
177
200
|
- it allows you to dump unstructured requirements and have the agent create a PRD and task list for you.
|
|
178
|
-
- it uses a task lookup table with individual detailed steps
|
|
201
|
+
- it uses a task lookup table with individual detailed steps β more scalable as you get 100s of tasks done.
|
|
179
202
|
- it's sandboxed and more secure
|
|
180
203
|
- it shows progress and stats so you can keep an eye on what's been done
|
|
181
204
|
- it instructs the agent to write and run automated tests and screenshots per task
|
|
@@ -189,19 +212,6 @@ While the loop is running, you can edit the `.agent/STEERING.md` file to add cri
|
|
|
189
212
|
|
|
190
213
|
The agent will check this file each iteration and if it finds any critical work, it will skip tasks and complete the critical work first.
|
|
191
214
|
|
|
192
|
-
## Features
|
|
193
|
-
|
|
194
|
-
- **PRD generation** - Creates a PRD and task list from requirements
|
|
195
|
-
- **Task lookup table generation** - Creates a task lookup table from the PRD
|
|
196
|
-
- **Task breakdown + step generation** - Breaks down each task into manageable steps
|
|
197
|
-
- **Iteration tracking** - Shows progress through iterations with timing
|
|
198
|
-
- **Stream preview** - Shows live output from the Agent
|
|
199
|
-
- **Step detection** - Identifies current activity (Thinking, Implementing, Testing, etc.)
|
|
200
|
-
- **Screenshot capture** - Captures a screenshot of the current screen
|
|
201
|
-
- **Notifications** - Alerts when human input is needed
|
|
202
|
-
- **History logging** - Saves clean output from each iteration
|
|
203
|
-
- **Timing** - Shows timing metrics for each iteration and total time
|
|
204
|
-
|
|
205
215
|
## Support
|
|
206
216
|
|
|
207
217
|
The `ralph.sh` script is designed to be hackable.
|
|
@@ -258,6 +268,8 @@ Skills are reusable agent capabilities that provide specialized knowledge and wo
|
|
|
258
268
|
| `prd-creator` | Create PRDs and task breakdowns for Ralph |
|
|
259
269
|
| `skill-creator` | Create new skills |
|
|
260
270
|
| `vercel-react-best-practices` | React/Next.js performance patterns |
|
|
271
|
+
| `mysql` | MySQL/InnoDB schema, indexing, query tuning, and ops |
|
|
272
|
+
| `postgres` | PostgreSQL best practices and query optimization |
|
|
261
273
|
| `web-design-guidelines` | UI/UX design principles |
|
|
262
274
|
|
|
263
275
|
### Skills Directory Structure
|
|
@@ -269,18 +281,14 @@ Skills are symlinked from `.agent/skills/` to multiple locations for cross-tool
|
|
|
269
281
|
.agent/skills/
|
|
270
282
|
βββ component-refactoring/
|
|
271
283
|
βββ e2e-tester/
|
|
272
|
-
βββ
|
|
273
|
-
βββ
|
|
274
|
-
βββ prd-creator/
|
|
275
|
-
βββ skill-creator/
|
|
276
|
-
βββ vercel-react-best-practices/
|
|
277
|
-
βββ web-design-guidelines/
|
|
284
|
+
βββ postgres/
|
|
285
|
+
βββ ...
|
|
278
286
|
|
|
279
287
|
# Symlinks -> .agent/skills/*
|
|
280
|
-
.agents/skills
|
|
281
|
-
.claude/skills
|
|
282
|
-
.codex/skills
|
|
283
|
-
.cursor/skills
|
|
288
|
+
.agents/skills/*
|
|
289
|
+
.claude/skills/*
|
|
290
|
+
.codex/skills/*
|
|
291
|
+
.cursor/skills/*
|
|
284
292
|
```
|
|
285
293
|
|
|
286
294
|
## Reference
|
|
@@ -324,29 +332,28 @@ export default defineConfig({
|
|
|
324
332
|
If you are using Vitest, here is a recommended configuration:
|
|
325
333
|
|
|
326
334
|
```typescript:vitest.config.ts
|
|
327
|
-
import { defineConfig } from
|
|
328
|
-
import react from
|
|
329
|
-
import path from
|
|
335
|
+
import { defineConfig } from "vitest/config";
|
|
336
|
+
import react from "@vitejs/plugin-react";
|
|
337
|
+
import path from "path";
|
|
330
338
|
|
|
331
339
|
export default defineConfig({
|
|
332
340
|
plugins: [react()],
|
|
333
341
|
test: {
|
|
334
|
-
environment:
|
|
342
|
+
environment: "node",
|
|
335
343
|
globals: true,
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
exclude: ['node_modules', '.next', 'tests'],
|
|
344
|
+
include: ["lib/**/*.test.ts", "lib/**/*.test.tsx"],
|
|
345
|
+
// setupFiles: ['./vitest.setup.ts'], // Include this if using Next.js
|
|
339
346
|
},
|
|
340
347
|
resolve: {
|
|
341
348
|
alias: {
|
|
342
|
-
|
|
349
|
+
"@": path.resolve(__dirname),
|
|
343
350
|
},
|
|
344
351
|
},
|
|
345
|
-
})
|
|
352
|
+
});
|
|
346
353
|
|
|
347
354
|
```
|
|
348
355
|
|
|
349
|
-
|
|
356
|
+
If you are using Next.js, you'll also need a `vitest.setup.ts` file to mock the `next/image` and `next/link` components.
|
|
350
357
|
|
|
351
358
|
```typescript:vitest.setup.ts
|
|
352
359
|
import '@testing-library/jest-dom/vitest'
|
|
@@ -388,8 +395,8 @@ docker sandbox run codex . # for Codex CLI
|
|
|
388
395
|
docker sandbox run gemini . # for Gemini CLI
|
|
389
396
|
```
|
|
390
397
|
|
|
391
|
-
Docker currently supports: `claude`, `codex`, `gemini`, `cagent`, `kiro
|
|
392
|
-
See
|
|
398
|
+
Docker currently supports: `claude`, `codex`, `opencode`,`copilot`, `gemini`, `cagent`, `kiro` and more.
|
|
399
|
+
See all supported agentic AI CLIs in [Docker's docs](https://docs.docker.com/ai/sandboxes/agents/).
|
|
393
400
|
|
|
394
401
|
### Starting from scratch
|
|
395
402
|
|
package/bin/cli.js
CHANGED
|
@@ -8,15 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
|
-
const { execSync } = require('child_process');
|
|
12
11
|
const display = require('./lib/display');
|
|
13
12
|
const { copyFile, copyDir, mergeDir, exists, ensureDir } = require('./lib/copy');
|
|
14
13
|
const { isGitRepo, initGitRepo } = require('./lib/git');
|
|
15
14
|
const { isShadcnProject, installAllComponents } = require('./lib/shadcn');
|
|
15
|
+
const { setupPlaywright } = require('./lib/playwright');
|
|
16
|
+
const { setupVitest } = require('./lib/vitest');
|
|
17
|
+
const { DEFAULT_APP_DIR } = require('./lib/consts');
|
|
16
18
|
|
|
17
19
|
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
18
20
|
const TARGET_DIR = process.cwd();
|
|
19
|
-
const DEFAULT_APP_DIR = 'lib';
|
|
20
21
|
|
|
21
22
|
// Directories to ensure exist (created even if source doesn't exist)
|
|
22
23
|
const DIRS_TO_ENSURE = [
|
|
@@ -161,7 +162,18 @@ async function main() {
|
|
|
161
162
|
process.exit(0);
|
|
162
163
|
}
|
|
163
164
|
|
|
164
|
-
// Prompt 3 β
|
|
165
|
+
// Prompt 3 β Install Vitest
|
|
166
|
+
const installVitest = await clack.confirm({
|
|
167
|
+
message: 'Set up Vitest for unit testing?',
|
|
168
|
+
initialValue: true,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
if (clack.isCancel(installVitest)) {
|
|
172
|
+
clack.cancel('Setup cancelled.');
|
|
173
|
+
process.exit(0);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Prompt 4 β Dev server address
|
|
165
177
|
const devServerRaw = await clack.text({
|
|
166
178
|
message: 'Where does your dev server run?',
|
|
167
179
|
placeholder: 'localhost:3000',
|
|
@@ -341,32 +353,12 @@ async function main() {
|
|
|
341
353
|
|
|
342
354
|
// Playwright setup
|
|
343
355
|
if (installPlaywright) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
// Copy playwright config to app directory
|
|
348
|
-
const playwrightSrc = path.join(TARGET_DIR, 'scripts/assets/playwright.config.ts');
|
|
349
|
-
const playwrightDest = path.join(TARGET_DIR, appDir, 'playwright.config.ts');
|
|
350
|
-
|
|
351
|
-
if (exists(playwrightSrc)) {
|
|
352
|
-
copyFile(playwrightSrc, playwrightDest);
|
|
353
|
-
display.printSuccess(`playwright.config.ts β ${appDir}/`);
|
|
354
|
-
}
|
|
356
|
+
setupPlaywright(clack, TARGET_DIR, appDir);
|
|
357
|
+
}
|
|
355
358
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
try {
|
|
360
|
-
execSync('npx playwright install --with-deps chromium', {
|
|
361
|
-
cwd: path.join(TARGET_DIR, appDir),
|
|
362
|
-
stdio: 'pipe',
|
|
363
|
-
});
|
|
364
|
-
s.stop('Playwright browsers installed');
|
|
365
|
-
} catch (err) {
|
|
366
|
-
s.stop('Playwright browser install failed');
|
|
367
|
-
display.printWarning('Could not install Playwright browsers. Run manually:');
|
|
368
|
-
display.printWarning(` cd ${appDir} && npx playwright install --with-deps chromium`);
|
|
369
|
-
}
|
|
359
|
+
// Vitest setup
|
|
360
|
+
if (installVitest) {
|
|
361
|
+
setupVitest(clack, TARGET_DIR, appDir);
|
|
370
362
|
}
|
|
371
363
|
|
|
372
364
|
// Final setup steps (git init, shadcn install)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright module for Ralph Loop CLI
|
|
3
|
+
* Copies config and installs Playwright browsers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const { copyFile, exists } = require('./copy');
|
|
9
|
+
const display = require('./display');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Copies playwright.config.ts into the app directory and installs Chromium.
|
|
13
|
+
* @param {object} clack - @clack/prompts module (passed in because it's ESM)
|
|
14
|
+
* @param {string} targetDir - Project root (TARGET_DIR)
|
|
15
|
+
* @param {string} appDir - Relative app source directory
|
|
16
|
+
*/
|
|
17
|
+
function setupPlaywright(clack, targetDir, appDir) {
|
|
18
|
+
console.log();
|
|
19
|
+
display.printStep('π', 'Playwright setup');
|
|
20
|
+
|
|
21
|
+
// Copy playwright config to app directory
|
|
22
|
+
const playwrightSrc = path.join(targetDir, 'scripts/assets/playwright.config.ts');
|
|
23
|
+
const playwrightDest = path.join(targetDir, appDir, 'playwright.config.ts');
|
|
24
|
+
|
|
25
|
+
if (exists(playwrightSrc)) {
|
|
26
|
+
copyFile(playwrightSrc, playwrightDest);
|
|
27
|
+
display.printSuccess(`playwright.config.ts β ${appDir}/`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Install Playwright browsers
|
|
31
|
+
const s = clack.spinner();
|
|
32
|
+
s.start('Installing Playwright browsers (chromium)...');
|
|
33
|
+
try {
|
|
34
|
+
execSync('npx playwright install --with-deps chromium', {
|
|
35
|
+
cwd: path.join(targetDir, appDir),
|
|
36
|
+
stdio: 'pipe',
|
|
37
|
+
});
|
|
38
|
+
s.stop('Playwright browsers installed');
|
|
39
|
+
} catch (err) {
|
|
40
|
+
s.stop('Playwright browser install failed');
|
|
41
|
+
display.printWarning('Could not install Playwright browsers. Run manually:');
|
|
42
|
+
display.printWarning(` cd ${appDir} && npx playwright install --with-deps chromium`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = {
|
|
47
|
+
setupPlaywright,
|
|
48
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vitest module for Ralph Loop CLI
|
|
3
|
+
* Copies config and installs Vitest dependencies
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
const { copyFile, exists } = require('./copy');
|
|
10
|
+
const display = require('./display');
|
|
11
|
+
const { DEFAULT_APP_DIR } = require('./consts');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Copies vitest.config.ts into the app directory, patches the include
|
|
15
|
+
* paths to match appDir, and installs vitest + react plugin.
|
|
16
|
+
* @param {object} clack - @clack/prompts module (passed in because it's ESM)
|
|
17
|
+
* @param {string} targetDir - Project root (TARGET_DIR)
|
|
18
|
+
* @param {string} appDir - Relative app source directory
|
|
19
|
+
*/
|
|
20
|
+
function setupVitest(clack, targetDir, appDir) {
|
|
21
|
+
console.log();
|
|
22
|
+
display.printStep('β‘', 'Vitest setup');
|
|
23
|
+
|
|
24
|
+
// Copy vitest config to app directory
|
|
25
|
+
const vitestSrc = path.join(targetDir, 'scripts/assets/vitest.config.ts');
|
|
26
|
+
const vitestDest = path.join(targetDir, appDir, 'vitest.config.ts');
|
|
27
|
+
|
|
28
|
+
if (exists(vitestSrc)) {
|
|
29
|
+
copyFile(vitestSrc, vitestDest);
|
|
30
|
+
|
|
31
|
+
// Patch include paths to match the user's app directory
|
|
32
|
+
if (appDir !== DEFAULT_APP_DIR) {
|
|
33
|
+
const content = fs.readFileSync(vitestDest, 'utf8');
|
|
34
|
+
const updated = content.replaceAll('lib/**/', `${appDir}/**/`);
|
|
35
|
+
fs.writeFileSync(vitestDest, updated, 'utf8');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
display.printSuccess(`vitest.config.ts β ${appDir}/`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Install Vitest dependencies
|
|
42
|
+
const s = clack.spinner();
|
|
43
|
+
s.start('Installing Vitest dependencies...');
|
|
44
|
+
try {
|
|
45
|
+
execSync('npm install --save-dev vitest @vitejs/plugin-react', {
|
|
46
|
+
cwd: path.join(targetDir, appDir),
|
|
47
|
+
stdio: 'pipe',
|
|
48
|
+
});
|
|
49
|
+
s.stop('Vitest dependencies installed');
|
|
50
|
+
} catch (err) {
|
|
51
|
+
s.stop('Vitest install failed');
|
|
52
|
+
display.printWarning('Could not install Vitest dependencies. Run manually:');
|
|
53
|
+
display.printWarning(` cd ${appDir} && npm install --save-dev vitest @vitejs/plugin-react`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = {
|
|
58
|
+
setupVitest,
|
|
59
|
+
};
|
package/package.json
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
import react from "@vitejs/plugin-react";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [react()],
|
|
7
|
+
test: {
|
|
8
|
+
environment: "node",
|
|
9
|
+
globals: true,
|
|
10
|
+
include: ["lib/**/*.test.ts", "lib/**/*.test.tsx"],
|
|
11
|
+
},
|
|
12
|
+
resolve: {
|
|
13
|
+
alias: {
|
|
14
|
+
"@": path.resolve(__dirname),
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|