@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 +54 -45
- package/bin/cli.js +20 -28
- package/bin/lib/consts.js +9 -0
- package/bin/lib/playwright.js +48 -0
- package/bin/lib/shadcn.js +48 -3
- 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,14 @@
|
|
|
1
1
|
# A Ralph Wiggum Loop implementation that works™
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/pageai-pro/ralph-loop)
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
+
[](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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
├──
|
|
273
|
-
├──
|
|
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
|
|
328
|
-
import react from
|
|
329
|
-
import path from
|
|
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:
|
|
344
|
+
environment: "node",
|
|
335
345
|
globals: true,
|
|
336
|
-
|
|
337
|
-
|
|
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
|
-
|
|
351
|
+
"@": path.resolve(__dirname),
|
|
343
352
|
},
|
|
344
353
|
},
|
|
345
|
-
})
|
|
354
|
+
});
|
|
346
355
|
|
|
347
356
|
```
|
|
348
357
|
|
|
349
|
-
|
|
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
|
|
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 —
|
|
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
|
+
};
|
package/bin/lib/shadcn.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* shadcn/ui module for Ralph Loop CLI
|
|
3
|
-
* Detects shadcn
|
|
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
|
|
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
|
-
*
|
|
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
|
@@ -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
|
+
});
|