@pageai/ralph-loop 1.12.0 → 1.14.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 CHANGED
@@ -1,10 +1,14 @@
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
+ [![@pageai/ralph-loop version](https://img.shields.io/npm/v/@pageai/ralph-loop?label=npm&style=flat)](https://github.com/pageai-pro/ralph-loop)
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
+ 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.
6
6
 
7
- ![Ralph Wiggum Loop](https://github.com/user-attachments/assets/052d5290-7e83-4bfb-a6b5-6be761cbe890)
7
+ 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).
8
+
9
+ #### 👉 [Watch the video](https://www.youtube.com/watch?v=3TL8Ez66I3o) for an in-depth walkthrough.
10
+
11
+ [![Ralph Wiggum Loop](https://github.com/user-attachments/assets/be94b8ba-b073-489d-b07e-d11db975a907)](https://www.youtube.com/watch?v=3TL8Ez66I3o)
8
12
 
9
13
  - [Getting Started](#getting-started)
10
14
  - [(Optional) Set up code base](#optional-set-up-code-base)
@@ -17,7 +21,6 @@ This is an implementation that actually works, containing a hackable script so y
17
21
  - [How It Works](#how-it-works)
18
22
  - [How Is This Different from Other Ralphs?](#how-is-this-different-from-other-ralphs)
19
23
  - [Steering the Agent](#steering-the-agent)
20
- - [Features](#features)
21
24
  - [Support](#support)
22
25
  - [Promise Tags](#promise-tags)
23
26
  - [Exit Codes](#exit-codes)
@@ -41,7 +44,7 @@ This is an implementation that actually works, containing a hackable script so y
41
44
  I recommend using a CLI to bootstrap your project with the necessary tools and dependencies, e.g.:
42
45
 
43
46
  ```bash
44
- npx @tanstack/cli create lib --add-ons shadcn,eslint,form,tanstack-query,nitro --no-git
47
+ npx @tanstack/cli create lib --add-ons eslint,form,tanstack-query,nitro --no-git
45
48
  ```
46
49
 
47
50
  > If you must start from a blank slate, which is not recommended, see [Starting from scratch](#starting-from-scratch). You can also go for a more barebone start by running `npx create-vite@latest src --template react-ts`
@@ -84,15 +87,20 @@ Requirements:
84
87
  // etc.
85
88
  ```
86
89
 
87
- Pro tips:
90
+ <details>
91
+ <summary><strong>✨ Pro tips</strong></summary>
92
+
88
93
  - mention libraries and frameworks you want to use
89
- - mention env variables, e.g. for database, 3rd party API keys, etc. Store them in a .env file that you add to **.gitignore**
94
+ - mention env variables, e.g. for DB, 3rd party API keys, etc. Store them in `.env` and add it to **.gitignore**
90
95
  - describe user flows and journeys
91
96
  - add relevant docs and UI references if applicable inside `/docs` and mention them in the requirements
92
97
  - be as descriptive as possible
93
98
  - *it's fine to write in your own language*
94
99
 
95
- Then follow the Skill's instructions and verify the PRD and then tasks.<br/>
100
+ </details>
101
+ <br/>
102
+
103
+ Then, follow the Skill's instructions and verify the PRD and then tasks.<br/>
96
104
  **It is highly recommended that you review individual task requirements before starting the loop. Review EACH TASK INDIVIDUALLY.**
97
105
 
98
106
  ### 3️⃣ Step 3: Set up the agent inside Docker sandbox
@@ -162,7 +170,7 @@ Each iteration, Ralph will:
162
170
  1. Find the highest-priority incomplete task from `.agent/tasks.json`
163
171
  2. Work through the task steps defined in `.agent/tasks/TASK-{ID}.json`
164
172
  3. Run tests, linting, and type checking
165
- 4. Update task status and commit changes
173
+ 4. Complete task, take screenshot, update task status and commit changes
166
174
  5. Repeat until all tasks pass or max iterations reached
167
175
 
168
176
  ## How Is This Different from Other Ralphs?
@@ -172,10 +180,27 @@ The script follows the original concepts of the Ralph Wiggum Loop, working with
172
180
 
173
181
  It also works generically with any task set.
174
182
 
183
+ <details>
184
+ <summary><strong>✨ Features</strong></summary>
185
+
186
+ - **PRD generation** - Creates a PRD and task list from requirements
187
+ - **Task lookup table generation** - Creates a task lookup table from the PRD
188
+ - **Task breakdown + step generation** - Breaks down each task into manageable steps
189
+ - **Iteration tracking** - Shows progress through iterations with timing
190
+ - **Stream preview** - Shows live output from the Agent
191
+ - **Step detection** - Identifies current activity (Thinking, Implementing, Testing, etc.)
192
+ - **Screenshot capture** - Captures a screenshot of the current screen
193
+ - **Notifications** - Alerts when human input is needed
194
+ - **History logging** - Saves clean output from each iteration
195
+ - **Timing** - Shows timing metrics for each iteration and total time
196
+ - **Steering** - Allows prioritizing critical work that needs to be done before the loop can continue
197
+ </details>
198
+
199
+ <br/>
175
200
  Besides that:
176
201
 
177
202
  - 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 -> more scalable as you get 100s of tasks done.
203
+ - it uses a task lookup table with individual detailed steps more scalable as you get 100s of tasks done.
179
204
  - it's sandboxed and more secure
180
205
  - it shows progress and stats so you can keep an eye on what's been done
181
206
  - it instructs the agent to write and run automated tests and screenshots per task
@@ -189,19 +214,6 @@ While the loop is running, you can edit the `.agent/STEERING.md` file to add cri
189
214
 
190
215
  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
216
 
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
217
  ## Support
206
218
 
207
219
  The `ralph.sh` script is designed to be hackable.
@@ -258,6 +270,8 @@ Skills are reusable agent capabilities that provide specialized knowledge and wo
258
270
  | `prd-creator` | Create PRDs and task breakdowns for Ralph |
259
271
  | `skill-creator` | Create new skills |
260
272
  | `vercel-react-best-practices` | React/Next.js performance patterns |
273
+ | `mysql` | MySQL/InnoDB schema, indexing, query tuning, and ops |
274
+ | `postgres` | PostgreSQL best practices and query optimization |
261
275
  | `web-design-guidelines` | UI/UX design principles |
262
276
 
263
277
  ### Skills Directory Structure
@@ -269,18 +283,14 @@ Skills are symlinked from `.agent/skills/` to multiple locations for cross-tool
269
283
  .agent/skills/
270
284
  ├── component-refactoring/
271
285
  ├── e2e-tester/
272
- ├── frontend-code-review/
273
- ├── frontend-testing/
274
- ├── prd-creator/
275
- ├── skill-creator/
276
- ├── vercel-react-best-practices/
277
- └── web-design-guidelines/
286
+ ├── postgres/
287
+ ├── ...
278
288
 
279
289
  # Symlinks -> .agent/skills/*
280
- .agents/skills/
281
- .claude/skills/
282
- .codex/skills/
283
- .cursor/skills/
290
+ .agents/skills/*
291
+ .claude/skills/*
292
+ .codex/skills/*
293
+ .cursor/skills/*
284
294
  ```
285
295
 
286
296
  ## Reference
@@ -324,29 +334,28 @@ export default defineConfig({
324
334
  If you are using Vitest, here is a recommended configuration:
325
335
 
326
336
  ```typescript:vitest.config.ts
327
- import { defineConfig } from 'vitest/config'
328
- import react from '@vitejs/plugin-react'
329
- import path from 'path'
337
+ import { defineConfig } from "vitest/config";
338
+ import react from "@vitejs/plugin-react";
339
+ import path from "path";
330
340
 
331
341
  export default defineConfig({
332
342
  plugins: [react()],
333
343
  test: {
334
- environment: 'jsdom',
344
+ environment: "node",
335
345
  globals: true,
336
- setupFiles: ['./vitest.setup.ts'],
337
- include: ['**/*.test.{ts,tsx}'],
338
- exclude: ['node_modules', '.next', 'tests'],
346
+ include: ["lib/**/*.test.ts", "lib/**/*.test.tsx"],
347
+ // setupFiles: ['./vitest.setup.ts'], // Include this if using Next.js
339
348
  },
340
349
  resolve: {
341
350
  alias: {
342
- '@': path.resolve(__dirname, './'),
351
+ "@": path.resolve(__dirname),
343
352
  },
344
353
  },
345
- })
354
+ });
346
355
 
347
356
  ```
348
357
 
349
- And:
358
+ 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
359
 
351
360
  ```typescript:vitest.setup.ts
352
361
  import '@testing-library/jest-dom/vitest'
@@ -388,8 +397,8 @@ docker sandbox run codex . # for Codex CLI
388
397
  docker sandbox run gemini . # for Gemini CLI
389
398
  ```
390
399
 
391
- Docker currently supports: `claude`, `codex`, `gemini`, `cagent`, `kiro`.
392
- See more in [Docker's docs](https://docs.docker.com/ai/sandboxes/migration/).
400
+ Docker currently supports: `claude`, `codex`, `opencode`,`copilot`, `gemini`, `cagent`, `kiro` and more.
401
+ See all supported agentic AI CLIs in [Docker's docs](https://docs.docker.com/ai/sandboxes/agents/).
393
402
 
394
403
  ### Starting from scratch
395
404
 
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 — Dev server address
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
- console.log();
345
- display.printStep('🎭', 'Playwright setup');
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
- // Install Playwright browsers
357
- const s = clack.spinner();
358
- s.start('Installing Playwright browsers (chromium)...');
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,9 @@
1
+ /**
2
+ * Shared constants for Ralph Loop CLI
3
+ */
4
+
5
+ const DEFAULT_APP_DIR = 'lib';
6
+
7
+ module.exports = {
8
+ DEFAULT_APP_DIR,
9
+ };
@@ -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
+ };
package/bin/lib/shadcn.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * shadcn/ui module for Ralph Loop CLI
3
- * Detects shadcn config and installs all components
3
+ * Detects React projects, initializes shadcn if needed, and installs all components
4
4
  */
5
5
 
6
6
  const fs = require('fs');
@@ -15,7 +15,7 @@ const SHADCN_SCHEMA = 'https://ui.shadcn.com/schema.json';
15
15
  * @param {string} dir - Directory path to check
16
16
  * @returns {boolean}
17
17
  */
18
- function isShadcnProject(dir) {
18
+ function hasShadcnConfig(dir) {
19
19
  const configPath = path.join(dir, 'components.json');
20
20
  if (!exists(configPath)) return false;
21
21
 
@@ -28,10 +28,55 @@ function isShadcnProject(dir) {
28
28
  }
29
29
 
30
30
  /**
31
- * Installs all shadcn/ui components in the given directory
31
+ * Checks if a directory is a React project by looking for react in package.json
32
+ * @param {string} dir - Directory path to check
33
+ * @returns {boolean}
34
+ */
35
+ function isReactProject(dir) {
36
+ const pkgPath = path.join(dir, 'package.json');
37
+ if (!exists(pkgPath)) return false;
38
+
39
+ try {
40
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
41
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
42
+ return 'react' in allDeps;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Returns true if shadcn components should be installed.
50
+ * Either the project already has a shadcn config, or it's a React project
51
+ * where we can init shadcn first.
52
+ * @param {string} dir - Directory path to check
53
+ * @returns {boolean}
54
+ */
55
+ function isShadcnProject(dir) {
56
+ return hasShadcnConfig(dir) || isReactProject(dir);
57
+ }
58
+
59
+ /**
60
+ * Initializes shadcn in the given directory with default settings.
61
+ * @param {string} dir - Directory path to run the init in
62
+ */
63
+ function initShadcn(dir) {
64
+ execSync('npx shadcn@latest init -y -d 2>&1', {
65
+ cwd: dir,
66
+ stdio: 'pipe',
67
+ });
68
+ }
69
+
70
+ /**
71
+ * Installs all shadcn/ui components in the given directory.
72
+ * Runs init first if no components.json exists.
32
73
  * @param {string} dir - Directory path to run the install in
33
74
  */
34
75
  function installAllComponents(dir) {
76
+ if (!hasShadcnConfig(dir)) {
77
+ initShadcn(dir);
78
+ }
79
+
35
80
  execSync('npx shadcn@latest add --all --yes --overwrite 2>&1', {
36
81
  cwd: dir,
37
82
  stdio: 'pipe',
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pageai/ralph-loop",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -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
+ });