ralph-cli-sandboxed 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/chat.js +45 -15
- package/dist/commands/fix-prd.js +48 -24
- package/dist/commands/help.js +11 -3
- package/dist/commands/init.js +8 -7
- package/dist/commands/prd-convert.d.ts +9 -0
- package/dist/commands/prd-convert.js +108 -0
- package/dist/commands/prd.d.ts +2 -1
- package/dist/commands/prd.js +206 -37
- package/dist/commands/prompt.js +1 -1
- package/dist/commands/run.js +42 -43
- package/dist/index.js +6 -2
- package/dist/templates/prompts.d.ts +1 -0
- package/dist/templates/prompts.js +8 -1
- package/dist/utils/config.d.ts +19 -0
- package/dist/utils/config.js +85 -5
- package/dist/utils/prd-validator.d.ts +21 -2
- package/dist/utils/prd-validator.js +61 -8
- package/docs/HOW-TO-WRITE-PRDs.md +90 -108
- package/docs/PRD-GENERATOR.md +166 -96
- package/docs/RALPH-SETUP-TEMPLATE.md +1 -1
- package/package.json +3 -2
package/dist/utils/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "fs";
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
|
-
import { getCliProviders } from "../templates/prompts.js";
|
|
3
|
+
import { getCliProviders, DEFAULT_PRD_YAML, DEFAULT_PROGRESS } from "../templates/prompts.js";
|
|
4
4
|
export const DEFAULT_CLI_CONFIG = {
|
|
5
5
|
command: "claude",
|
|
6
6
|
args: [],
|
|
@@ -41,8 +41,70 @@ export function getCliConfig(config) {
|
|
|
41
41
|
const RALPH_DIR = ".ralph";
|
|
42
42
|
const CONFIG_FILE = "config.json";
|
|
43
43
|
const PROMPT_FILE = "prompt.md";
|
|
44
|
-
const
|
|
44
|
+
const PRD_FILE_JSON = "prd.json";
|
|
45
|
+
const PRD_FILE_YAML = "prd.yaml";
|
|
45
46
|
const PROGRESS_FILE = "progress.txt";
|
|
47
|
+
/**
|
|
48
|
+
* Gets the PRD file path(s) that exist.
|
|
49
|
+
* Returns an object with:
|
|
50
|
+
* - primary: The main PRD file path to use (yaml preferred over json)
|
|
51
|
+
* - secondary: The secondary PRD file path if both exist (for merging)
|
|
52
|
+
* - jsonOnly: True if only prd.json exists (shows migration notice)
|
|
53
|
+
* - yamlOnly: True if only prd.yaml exists (happy path)
|
|
54
|
+
* - both: True if both files exist (merge mode)
|
|
55
|
+
* - none: True if no PRD file exists
|
|
56
|
+
*/
|
|
57
|
+
export function getPrdFiles() {
|
|
58
|
+
const ralphDir = getRalphDir();
|
|
59
|
+
const jsonPath = join(ralphDir, PRD_FILE_JSON);
|
|
60
|
+
const yamlPath = join(ralphDir, PRD_FILE_YAML);
|
|
61
|
+
const hasJson = existsSync(jsonPath);
|
|
62
|
+
const hasYaml = existsSync(yamlPath);
|
|
63
|
+
if (hasYaml && hasJson) {
|
|
64
|
+
// Both exist - merge mode (YAML is primary)
|
|
65
|
+
return {
|
|
66
|
+
primary: yamlPath,
|
|
67
|
+
secondary: jsonPath,
|
|
68
|
+
jsonOnly: false,
|
|
69
|
+
yamlOnly: false,
|
|
70
|
+
both: true,
|
|
71
|
+
none: false,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
else if (hasYaml) {
|
|
75
|
+
// Only YAML exists - happy path
|
|
76
|
+
return {
|
|
77
|
+
primary: yamlPath,
|
|
78
|
+
secondary: null,
|
|
79
|
+
jsonOnly: false,
|
|
80
|
+
yamlOnly: true,
|
|
81
|
+
both: false,
|
|
82
|
+
none: false,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
else if (hasJson) {
|
|
86
|
+
// Only JSON exists - show migration notice
|
|
87
|
+
return {
|
|
88
|
+
primary: jsonPath,
|
|
89
|
+
secondary: null,
|
|
90
|
+
jsonOnly: true,
|
|
91
|
+
yamlOnly: false,
|
|
92
|
+
both: false,
|
|
93
|
+
none: false,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// No PRD file exists
|
|
98
|
+
return {
|
|
99
|
+
primary: null,
|
|
100
|
+
secondary: null,
|
|
101
|
+
jsonOnly: false,
|
|
102
|
+
yamlOnly: false,
|
|
103
|
+
both: false,
|
|
104
|
+
none: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
46
108
|
export function getRalphDir() {
|
|
47
109
|
return join(process.cwd(), RALPH_DIR);
|
|
48
110
|
}
|
|
@@ -66,20 +128,38 @@ export function checkFilesExist() {
|
|
|
66
128
|
if (!existsSync(ralphDir)) {
|
|
67
129
|
throw new Error(".ralph/ directory not found. Run 'ralph init' first.");
|
|
68
130
|
}
|
|
69
|
-
|
|
131
|
+
// Check config and prompt files (these are critical and must exist)
|
|
132
|
+
const requiredFiles = [CONFIG_FILE, PROMPT_FILE];
|
|
70
133
|
for (const file of requiredFiles) {
|
|
71
134
|
if (!existsSync(join(ralphDir, file))) {
|
|
72
135
|
throw new Error(`.ralph/${file} not found. Run 'ralph init' first.`);
|
|
73
136
|
}
|
|
74
137
|
}
|
|
138
|
+
// Create progress.txt if it doesn't exist (user may have cleaned up)
|
|
139
|
+
const progressPath = join(ralphDir, PROGRESS_FILE);
|
|
140
|
+
if (!existsSync(progressPath)) {
|
|
141
|
+
writeFileSync(progressPath, DEFAULT_PROGRESS);
|
|
142
|
+
console.log(`Created ${progressPath}`);
|
|
143
|
+
}
|
|
144
|
+
// Create prd.yaml if no PRD file exists (user may have cleaned up)
|
|
145
|
+
const prdFiles = getPrdFiles();
|
|
146
|
+
if (prdFiles.none) {
|
|
147
|
+
const prdPath = join(ralphDir, PRD_FILE_YAML);
|
|
148
|
+
writeFileSync(prdPath, DEFAULT_PRD_YAML);
|
|
149
|
+
console.log(`Created ${prdPath}`);
|
|
150
|
+
}
|
|
75
151
|
}
|
|
76
152
|
export function getPaths() {
|
|
77
153
|
const ralphDir = getRalphDir();
|
|
154
|
+
const prdFiles = getPrdFiles();
|
|
155
|
+
// Use the primary PRD file path (yaml preferred over json, fallback to json for backwards compat)
|
|
156
|
+
const prdPath = prdFiles.primary || join(ralphDir, PRD_FILE_JSON);
|
|
78
157
|
return {
|
|
79
158
|
dir: ralphDir,
|
|
80
159
|
config: join(ralphDir, CONFIG_FILE),
|
|
81
160
|
prompt: join(ralphDir, PROMPT_FILE),
|
|
82
|
-
prd:
|
|
161
|
+
prd: prdPath,
|
|
162
|
+
prdSecondary: prdFiles.secondary, // Second PRD file if merging
|
|
83
163
|
progress: join(ralphDir, PROGRESS_FILE),
|
|
84
164
|
};
|
|
85
165
|
}
|
|
@@ -40,11 +40,13 @@ export declare function smartMerge(original: PrdEntry[], corrupted: unknown): Me
|
|
|
40
40
|
export declare function attemptRecovery(corrupted: unknown): PrdEntry[] | null;
|
|
41
41
|
/**
|
|
42
42
|
* Creates a timestamped backup of the PRD file.
|
|
43
|
+
* Preserves the original file extension (.json or .yaml/.yml).
|
|
43
44
|
* Returns the backup path.
|
|
44
45
|
*/
|
|
45
46
|
export declare function createBackup(prdPath: string): string;
|
|
46
47
|
/**
|
|
47
48
|
* Finds the most recent backup file.
|
|
49
|
+
* Searches for both .json and .yaml/.yml backup files.
|
|
48
50
|
* Returns the path or null if no backups exist.
|
|
49
51
|
*/
|
|
50
52
|
export declare function findLatestBackup(prdPath: string): string | null;
|
|
@@ -55,7 +57,16 @@ export declare function findLatestBackup(prdPath: string): string | null;
|
|
|
55
57
|
*/
|
|
56
58
|
export declare function createTemplatePrd(backupPath?: string): PrdEntry[];
|
|
57
59
|
/**
|
|
58
|
-
* Reads and parses a PRD file
|
|
60
|
+
* Reads and parses a YAML PRD file.
|
|
61
|
+
* Returns the parsed content or null if it couldn't be parsed.
|
|
62
|
+
*/
|
|
63
|
+
export declare function readYamlPrdFile(prdPath: string): {
|
|
64
|
+
content: unknown;
|
|
65
|
+
raw: string;
|
|
66
|
+
} | null;
|
|
67
|
+
/**
|
|
68
|
+
* Reads and parses a PRD file, handling potential JSON/YAML errors.
|
|
69
|
+
* Detects file format based on extension (.yaml/.yml uses YAML, .json uses JSON).
|
|
59
70
|
* Returns the parsed content or null if it couldn't be parsed.
|
|
60
71
|
*/
|
|
61
72
|
export declare function readPrdFile(prdPath: string): {
|
|
@@ -63,9 +74,17 @@ export declare function readPrdFile(prdPath: string): {
|
|
|
63
74
|
raw: string;
|
|
64
75
|
} | null;
|
|
65
76
|
/**
|
|
66
|
-
* Writes a PRD to file.
|
|
77
|
+
* Writes a PRD to file in JSON format.
|
|
67
78
|
*/
|
|
68
79
|
export declare function writePrd(prdPath: string, entries: PrdEntry[]): void;
|
|
80
|
+
/**
|
|
81
|
+
* Writes a PRD to file in YAML format.
|
|
82
|
+
*/
|
|
83
|
+
export declare function writePrdYaml(prdPath: string, entries: PrdEntry[]): void;
|
|
84
|
+
/**
|
|
85
|
+
* Writes a PRD to file, detecting format from file extension.
|
|
86
|
+
*/
|
|
87
|
+
export declare function writePrdAuto(prdPath: string, entries: PrdEntry[]): void;
|
|
69
88
|
/**
|
|
70
89
|
* Expands @{filepath} patterns in a string with actual file contents.
|
|
71
90
|
* Similar to curl's @ syntax for including file contents.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync, readdirSync } from "fs";
|
|
2
|
-
import { join, dirname } from "path";
|
|
2
|
+
import { join, dirname, extname } from "path";
|
|
3
|
+
import YAML from "yaml";
|
|
3
4
|
const VALID_CATEGORIES = ["ui", "feature", "bugfix", "setup", "development", "testing", "docs"];
|
|
4
5
|
/**
|
|
5
6
|
* Validates that a PRD structure is correct.
|
|
@@ -340,18 +341,23 @@ function attemptArrayRecovery(items) {
|
|
|
340
341
|
}
|
|
341
342
|
/**
|
|
342
343
|
* Creates a timestamped backup of the PRD file.
|
|
344
|
+
* Preserves the original file extension (.json or .yaml/.yml).
|
|
343
345
|
* Returns the backup path.
|
|
344
346
|
*/
|
|
345
347
|
export function createBackup(prdPath) {
|
|
346
348
|
const content = readFileSync(prdPath, "utf-8");
|
|
347
349
|
const dir = dirname(prdPath);
|
|
348
350
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
349
|
-
const
|
|
351
|
+
const ext = extname(prdPath).toLowerCase();
|
|
352
|
+
// Preserve original extension, default to .json if unknown
|
|
353
|
+
const backupExt = ext === ".yaml" || ext === ".yml" ? ext : ".json";
|
|
354
|
+
const backupPath = join(dir, `backup.prd.${timestamp}${backupExt}`);
|
|
350
355
|
writeFileSync(backupPath, content);
|
|
351
356
|
return backupPath;
|
|
352
357
|
}
|
|
353
358
|
/**
|
|
354
359
|
* Finds the most recent backup file.
|
|
360
|
+
* Searches for both .json and .yaml/.yml backup files.
|
|
355
361
|
* Returns the path or null if no backups exist.
|
|
356
362
|
*/
|
|
357
363
|
export function findLatestBackup(prdPath) {
|
|
@@ -361,7 +367,8 @@ export function findLatestBackup(prdPath) {
|
|
|
361
367
|
}
|
|
362
368
|
const files = readdirSync(dir);
|
|
363
369
|
const backups = files
|
|
364
|
-
.filter((f) => f.startsWith("backup.prd.") &&
|
|
370
|
+
.filter((f) => f.startsWith("backup.prd.") &&
|
|
371
|
+
(f.endsWith(".json") || f.endsWith(".yaml") || f.endsWith(".yml")))
|
|
365
372
|
.sort()
|
|
366
373
|
.reverse();
|
|
367
374
|
if (backups.length === 0) {
|
|
@@ -384,7 +391,7 @@ export function createTemplatePrd(backupPath) {
|
|
|
384
391
|
description: "Fix the PRD entries",
|
|
385
392
|
steps: [
|
|
386
393
|
`Recreate PRD entries based on this corrupted backup content:\n\n@{${absolutePath}}`,
|
|
387
|
-
"Write valid entries to .ralph/prd.
|
|
394
|
+
"Write valid entries to .ralph/prd.yaml with format: category (string), description (string), steps (array of strings), passes (boolean)",
|
|
388
395
|
],
|
|
389
396
|
passes: false,
|
|
390
397
|
},
|
|
@@ -395,7 +402,7 @@ export function createTemplatePrd(backupPath) {
|
|
|
395
402
|
category: "setup",
|
|
396
403
|
description: "Add PRD entries",
|
|
397
404
|
steps: [
|
|
398
|
-
"Add requirements using 'ralph add' or edit .ralph/prd.
|
|
405
|
+
"Add requirements using 'ralph add' or edit .ralph/prd.yaml directly",
|
|
399
406
|
"Verify format: category (string), description (string), steps (array of strings), passes (boolean)",
|
|
400
407
|
],
|
|
401
408
|
passes: false,
|
|
@@ -403,13 +410,37 @@ export function createTemplatePrd(backupPath) {
|
|
|
403
410
|
];
|
|
404
411
|
}
|
|
405
412
|
/**
|
|
406
|
-
* Reads and parses a PRD file
|
|
413
|
+
* Reads and parses a YAML PRD file.
|
|
414
|
+
* Returns the parsed content or null if it couldn't be parsed.
|
|
415
|
+
*/
|
|
416
|
+
export function readYamlPrdFile(prdPath) {
|
|
417
|
+
try {
|
|
418
|
+
const raw = readFileSync(prdPath, "utf-8");
|
|
419
|
+
const content = YAML.parse(raw);
|
|
420
|
+
return { content, raw };
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Reads and parses a PRD file, handling potential JSON/YAML errors.
|
|
428
|
+
* Detects file format based on extension (.yaml/.yml uses YAML, .json uses JSON).
|
|
407
429
|
* Returns the parsed content or null if it couldn't be parsed.
|
|
408
430
|
*/
|
|
409
431
|
export function readPrdFile(prdPath) {
|
|
410
432
|
try {
|
|
411
433
|
const raw = readFileSync(prdPath, "utf-8");
|
|
412
|
-
const
|
|
434
|
+
const ext = extname(prdPath).toLowerCase();
|
|
435
|
+
// Parse based on file extension
|
|
436
|
+
let content;
|
|
437
|
+
if (ext === ".yaml" || ext === ".yml") {
|
|
438
|
+
content = YAML.parse(raw);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
// Default to JSON for .json or any other extension
|
|
442
|
+
content = JSON.parse(raw);
|
|
443
|
+
}
|
|
413
444
|
return { content, raw };
|
|
414
445
|
}
|
|
415
446
|
catch {
|
|
@@ -417,17 +448,39 @@ export function readPrdFile(prdPath) {
|
|
|
417
448
|
}
|
|
418
449
|
}
|
|
419
450
|
/**
|
|
420
|
-
* Writes a PRD to file.
|
|
451
|
+
* Writes a PRD to file in JSON format.
|
|
421
452
|
*/
|
|
422
453
|
export function writePrd(prdPath, entries) {
|
|
423
454
|
writeFileSync(prdPath, JSON.stringify(entries, null, 2) + "\n");
|
|
424
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* Writes a PRD to file in YAML format.
|
|
458
|
+
*/
|
|
459
|
+
export function writePrdYaml(prdPath, entries) {
|
|
460
|
+
writeFileSync(prdPath, YAML.stringify(entries));
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Writes a PRD to file, detecting format from file extension.
|
|
464
|
+
*/
|
|
465
|
+
export function writePrdAuto(prdPath, entries) {
|
|
466
|
+
const ext = extname(prdPath).toLowerCase();
|
|
467
|
+
if (ext === ".yaml" || ext === ".yml") {
|
|
468
|
+
writePrdYaml(prdPath, entries);
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
writePrd(prdPath, entries);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
425
474
|
/**
|
|
426
475
|
* Expands @{filepath} patterns in a string with actual file contents.
|
|
427
476
|
* Similar to curl's @ syntax for including file contents.
|
|
428
477
|
* Paths are resolved relative to the .ralph directory.
|
|
429
478
|
*/
|
|
430
479
|
export function expandFileReferences(text, baseDir) {
|
|
480
|
+
// Handle null/undefined text
|
|
481
|
+
if (typeof text !== "string") {
|
|
482
|
+
return text ?? "";
|
|
483
|
+
}
|
|
431
484
|
// Match @{filepath} patterns
|
|
432
485
|
const pattern = /@\{([^}]+)\}/g;
|
|
433
486
|
return text.replace(pattern, (match, filepath) => {
|
|
@@ -4,19 +4,16 @@ This guide explains how to write Product Requirement Documents (PRDs) that Ralph
|
|
|
4
4
|
|
|
5
5
|
## PRD Structure
|
|
6
6
|
|
|
7
|
-
Each PRD
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
],
|
|
18
|
-
"passes": false
|
|
19
|
-
}
|
|
7
|
+
Each PRD entry is a YAML object with four fields:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
- category: feature
|
|
11
|
+
description: Short imperative description of what to implement
|
|
12
|
+
steps:
|
|
13
|
+
- First concrete action to take
|
|
14
|
+
- Second concrete action to take
|
|
15
|
+
- Verification step to confirm completion
|
|
16
|
+
passes: false
|
|
20
17
|
```
|
|
21
18
|
|
|
22
19
|
## Categories
|
|
@@ -74,90 +71,80 @@ Steps tell the AI agent exactly **how** to verify or implement the requirement.
|
|
|
74
71
|
### Step Patterns
|
|
75
72
|
|
|
76
73
|
**For features:**
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
]
|
|
74
|
+
```yaml
|
|
75
|
+
steps:
|
|
76
|
+
- Implement X in src/path/file.ts
|
|
77
|
+
- Add Y functionality that does Z
|
|
78
|
+
- Run `command` and confirm expected output
|
|
83
79
|
```
|
|
84
80
|
|
|
85
81
|
**For bug fixes:**
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
]
|
|
82
|
+
```yaml
|
|
83
|
+
steps:
|
|
84
|
+
- Identify the cause of X in src/path/file.ts
|
|
85
|
+
- Fix by doing Y
|
|
86
|
+
- Run `command` and verify the bug is resolved
|
|
92
87
|
```
|
|
93
88
|
|
|
94
89
|
**For documentation:**
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
]
|
|
90
|
+
```yaml
|
|
91
|
+
steps:
|
|
92
|
+
- Add section 'X' to README.md
|
|
93
|
+
- Include explanation of Y
|
|
94
|
+
- Include example showing Z
|
|
101
95
|
```
|
|
102
96
|
|
|
103
97
|
**For releases:**
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
]
|
|
98
|
+
```yaml
|
|
99
|
+
steps:
|
|
100
|
+
- Update version in package.json to 'X.Y.Z'
|
|
101
|
+
- Run `npm run build` to verify no errors
|
|
102
|
+
- Run `command --version` and confirm it shows X.Y.Z
|
|
110
103
|
```
|
|
111
104
|
|
|
112
105
|
## Anti-Patterns to Avoid
|
|
113
106
|
|
|
114
107
|
### Vague Steps
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
"Run `ralph init` with missing config and verify helpful error message"
|
|
128
|
-
]
|
|
108
|
+
```yaml
|
|
109
|
+
# Bad
|
|
110
|
+
steps:
|
|
111
|
+
- Make it work
|
|
112
|
+
- Test it
|
|
113
|
+
- Verify it's good
|
|
114
|
+
|
|
115
|
+
# Good
|
|
116
|
+
steps:
|
|
117
|
+
- Add error handling for null input in parseConfig()
|
|
118
|
+
- Run `npm test` and confirm all tests pass
|
|
119
|
+
- Run `ralph init` with missing config and verify helpful error message
|
|
129
120
|
```
|
|
130
121
|
|
|
131
122
|
### Steps That Require Human Judgment
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
"Run `npm test` and verify retry tests pass"
|
|
145
|
-
]
|
|
123
|
+
```yaml
|
|
124
|
+
# Bad
|
|
125
|
+
steps:
|
|
126
|
+
- Understand the codebase
|
|
127
|
+
- Decide the best approach
|
|
128
|
+
- Implement your solution
|
|
129
|
+
|
|
130
|
+
# Good
|
|
131
|
+
steps:
|
|
132
|
+
- Add retry logic with exponential backoff to fetchData() in src/api.ts
|
|
133
|
+
- Set max retries to 3 with initial delay of 1000ms
|
|
134
|
+
- Run `npm test` and verify retry tests pass
|
|
146
135
|
```
|
|
147
136
|
|
|
148
137
|
### Missing Verification
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
"Run `ralph --version` and confirm output shows '1.2.3'"
|
|
160
|
-
]
|
|
138
|
+
```yaml
|
|
139
|
+
# Bad
|
|
140
|
+
steps:
|
|
141
|
+
- Update the version number
|
|
142
|
+
|
|
143
|
+
# Good
|
|
144
|
+
steps:
|
|
145
|
+
- Update version in package.json to '1.2.3'
|
|
146
|
+
- Run `npm run build` to verify no errors
|
|
147
|
+
- Run `ralph --version` and confirm output shows '1.2.3'
|
|
161
148
|
```
|
|
162
149
|
|
|
163
150
|
## Priority Through Ordering
|
|
@@ -176,39 +163,34 @@ Recommended ordering:
|
|
|
176
163
|
|
|
177
164
|
Break large features into smaller, independently completable items. Each item should be achievable in a single Ralph iteration.
|
|
178
165
|
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
"steps": [...]
|
|
198
|
-
}
|
|
166
|
+
```yaml
|
|
167
|
+
# Too large
|
|
168
|
+
- description: Implement user authentication system
|
|
169
|
+
steps:
|
|
170
|
+
- Add login, logout, registration, password reset, OAuth...
|
|
171
|
+
|
|
172
|
+
# Better: Split into multiple items
|
|
173
|
+
- description: Add user registration endpoint POST /api/register
|
|
174
|
+
steps:
|
|
175
|
+
- ...
|
|
176
|
+
|
|
177
|
+
- description: Add user login endpoint POST /api/login
|
|
178
|
+
steps:
|
|
179
|
+
- ...
|
|
180
|
+
|
|
181
|
+
- description: Add JWT token generation and validation
|
|
182
|
+
steps:
|
|
183
|
+
- ...
|
|
199
184
|
```
|
|
200
185
|
|
|
201
186
|
## Quick Reference
|
|
202
187
|
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
],
|
|
212
|
-
"passes": false
|
|
213
|
-
}
|
|
188
|
+
```yaml
|
|
189
|
+
- category: setup|feature|bugfix|refactor|docs|test|release|config|ui|integration
|
|
190
|
+
description: Imperative verb + specific what + where (context)
|
|
191
|
+
steps:
|
|
192
|
+
- Concrete action with `commands` and file paths
|
|
193
|
+
- Another specific action
|
|
194
|
+
- Verification: Run `command` and confirm expected result
|
|
195
|
+
passes: false
|
|
214
196
|
```
|