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.
@@ -1,36 +1,109 @@
1
- import { existsSync, readFileSync, writeFileSync } from "fs";
2
- import { join } from "path";
3
- import { promptInput, promptSelect } from "../utils/prompt.js";
4
- import { getRalphDir } from "../utils/config.js";
5
- const PRD_FILE = "prd.json";
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
2
+ import { extname, join } from "path";
3
+ import { promptInput, promptSelect, promptConfirm } from "../utils/prompt.js";
4
+ import { getRalphDir, getPrdFiles } from "../utils/config.js";
5
+ import { convert as prdConvert } from "./prd-convert.js";
6
+ import { DEFAULT_PRD_YAML } from "../templates/prompts.js";
7
+ import YAML from "yaml";
8
+ const PRD_FILE_JSON = "prd.json";
9
+ const PRD_FILE_YAML = "prd.yaml";
6
10
  const CATEGORIES = ["ui", "feature", "bugfix", "setup", "development", "testing", "docs"];
11
+ // Track whether we've shown the migration notice in this session
12
+ let migrationNoticeShown = false;
7
13
  function getPrdPath() {
8
- return join(getRalphDir(), PRD_FILE);
9
- }
10
- function loadPrd() {
11
- const path = getPrdPath();
12
- if (!existsSync(path)) {
13
- throw new Error(".ralph/prd.json not found. Run 'ralph init' first.");
14
+ const prdFiles = getPrdFiles();
15
+ if (prdFiles.primary) {
16
+ return prdFiles.primary;
14
17
  }
18
+ // Fallback to json path for backwards compatibility
19
+ return join(getRalphDir(), PRD_FILE_JSON);
20
+ }
21
+ /**
22
+ * Parses a PRD file based on its extension.
23
+ * Supports both JSON and YAML formats.
24
+ * Returns empty array if file is empty or parses to null.
25
+ */
26
+ function parsePrdFile(path) {
15
27
  const content = readFileSync(path, "utf-8");
28
+ const ext = extname(path).toLowerCase();
16
29
  try {
17
- return JSON.parse(content);
30
+ let result;
31
+ if (ext === ".yaml" || ext === ".yml") {
32
+ result = YAML.parse(content);
33
+ }
34
+ else {
35
+ result = JSON.parse(content);
36
+ }
37
+ // Handle empty files or null content
38
+ return result ?? [];
18
39
  }
19
40
  catch (err) {
20
- const message = err instanceof SyntaxError ? err.message : "Invalid JSON";
21
- console.error(`Error parsing .ralph/prd.json: ${message}`);
41
+ const format = ext === ".yaml" || ext === ".yml" ? "YAML" : "JSON";
42
+ const message = err instanceof Error ? err.message : `Invalid ${format}`;
43
+ console.error(`Error parsing ${path}: ${message}`);
22
44
  console.error("");
23
45
  console.error("Common issues:");
24
- console.error(" - Trailing comma before ] or }");
25
- console.error(" - Missing comma between entries");
26
- console.error(" - Unescaped quotes in strings");
46
+ if (format === "JSON") {
47
+ console.error(" - Trailing comma before ] or }");
48
+ console.error(" - Missing comma between entries");
49
+ console.error(" - Unescaped quotes in strings");
50
+ }
51
+ else {
52
+ console.error(" - Incorrect indentation");
53
+ console.error(" - Missing colons after keys");
54
+ console.error(" - Unquoted special characters");
55
+ }
27
56
  console.error("");
28
57
  console.error("Run 'ralph fix-prd' to attempt automatic repair.");
29
58
  process.exit(1);
30
59
  }
31
60
  }
61
+ /**
62
+ * Loads PRD entries from prd.yaml and/or prd.json.
63
+ * - If neither exists, creates prd.yaml with default content
64
+ * - If both exist, merges them (no deduplication)
65
+ * - If only prd.json exists, shows migration notice
66
+ * - If only prd.yaml exists, uses it (happy path)
67
+ */
68
+ function loadPrd() {
69
+ const prdFiles = getPrdFiles();
70
+ if (prdFiles.none) {
71
+ // Create .ralph directory if it doesn't exist
72
+ const ralphDir = getRalphDir();
73
+ if (!existsSync(ralphDir)) {
74
+ mkdirSync(ralphDir, { recursive: true });
75
+ }
76
+ // Create default prd.yaml
77
+ const prdPath = join(ralphDir, PRD_FILE_YAML);
78
+ writeFileSync(prdPath, DEFAULT_PRD_YAML);
79
+ console.log(`Created ${prdPath}`);
80
+ return parsePrdFile(prdPath);
81
+ }
82
+ // If only JSON exists, show migration notice (once per session)
83
+ if (prdFiles.jsonOnly && !migrationNoticeShown) {
84
+ console.log("\x1b[33mNote: Consider migrating to YAML format with 'ralph prd convert'\x1b[0m");
85
+ console.log("");
86
+ migrationNoticeShown = true;
87
+ }
88
+ // Load primary file
89
+ const primary = parsePrdFile(prdFiles.primary);
90
+ // If both files exist, merge them
91
+ if (prdFiles.both && prdFiles.secondary) {
92
+ const secondary = parsePrdFile(prdFiles.secondary);
93
+ // Merge without deduplication - primary (YAML) first, then secondary (JSON)
94
+ return [...primary, ...secondary];
95
+ }
96
+ return primary;
97
+ }
32
98
  function savePrd(entries) {
33
- writeFileSync(getPrdPath(), JSON.stringify(entries, null, 2) + "\n");
99
+ const path = getPrdPath();
100
+ const ext = extname(path).toLowerCase();
101
+ if (ext === ".yaml" || ext === ".yml") {
102
+ writeFileSync(path, YAML.stringify(entries));
103
+ }
104
+ else {
105
+ writeFileSync(path, JSON.stringify(entries, null, 2) + "\n");
106
+ }
34
107
  }
35
108
  export async function prdAdd() {
36
109
  console.log("Add new PRD entry\n");
@@ -114,7 +187,7 @@ export function prdList(category, passesFilter) {
114
187
  console.log();
115
188
  });
116
189
  }
117
- export function prdStatus() {
190
+ export function prdStatus(headOnly = false) {
118
191
  const prd = loadPrd();
119
192
  if (prd.length === 0) {
120
193
  console.log("No PRD entries found.");
@@ -146,7 +219,7 @@ export function prdStatus() {
146
219
  if (passing === total) {
147
220
  console.log("\n \x1b[32m\u2713 All requirements complete!\x1b[0m");
148
221
  }
149
- else {
222
+ else if (!headOnly) {
150
223
  const remaining = prd.filter((e) => !e.passes);
151
224
  console.log(`\n Remaining (${remaining.length}):`);
152
225
  remaining.forEach((entry) => {
@@ -154,6 +227,71 @@ export function prdStatus() {
154
227
  });
155
228
  }
156
229
  }
230
+ /**
231
+ * Parses a range string like "1-18" into an array of numbers [1, 2, ..., 18].
232
+ * Returns null if the string is not a valid range.
233
+ */
234
+ function parseRange(str) {
235
+ const match = str.match(/^(\d+)-(\d+)$/);
236
+ if (!match)
237
+ return null;
238
+ const start = parseInt(match[1]);
239
+ const end = parseInt(match[2]);
240
+ if (isNaN(start) || isNaN(end) || start < 1 || end < 1)
241
+ return null;
242
+ if (start > end)
243
+ return null;
244
+ const result = [];
245
+ for (let i = start; i <= end; i++) {
246
+ result.push(i);
247
+ }
248
+ return result;
249
+ }
250
+ /**
251
+ * Expands arguments to handle range syntax.
252
+ * Supports:
253
+ * - "1-18" (single arg with dash) → [1, 2, ..., 18]
254
+ * - "1", "-", "18" (three args with dash separator) → [1, 2, ..., 18]
255
+ * - "1", "18" (separate numbers) → [1, 18]
256
+ */
257
+ function expandRangeArgs(args) {
258
+ const indices = [];
259
+ for (let i = 0; i < args.length; i++) {
260
+ const arg = args[i];
261
+ // Check if this arg is a range like "1-18"
262
+ const range = parseRange(arg);
263
+ if (range) {
264
+ indices.push(...range);
265
+ continue;
266
+ }
267
+ // Check if this is part of a "start - end" pattern (with spaces)
268
+ if (arg === "-" && i > 0 && i + 1 < args.length) {
269
+ // Look back to see if previous was a number and forward to see if next is a number
270
+ const prevNum = parseInt(args[i - 1]);
271
+ const nextNum = parseInt(args[i + 1]);
272
+ if (!isNaN(prevNum) && !isNaN(nextNum) && prevNum >= 1 && nextNum >= 1) {
273
+ // Remove the previous number we already added (it's the start of a range)
274
+ indices.pop();
275
+ if (prevNum > nextNum) {
276
+ return null; // Invalid range
277
+ }
278
+ // Add the full range
279
+ for (let j = prevNum; j <= nextNum; j++) {
280
+ indices.push(j);
281
+ }
282
+ i++; // Skip the next number since we already processed it
283
+ continue;
284
+ }
285
+ }
286
+ // Otherwise, parse as a single number
287
+ const num = parseInt(arg);
288
+ if (isNaN(num) || num < 1) {
289
+ return null; // Invalid argument
290
+ }
291
+ indices.push(num);
292
+ }
293
+ return indices.length > 0 ? indices : null;
294
+ }
157
295
  export function prdToggle(args) {
158
296
  const arg = args[0];
159
297
  // Check for --all flag
@@ -170,19 +308,11 @@ export function prdToggle(args) {
170
308
  console.log(`Toggled all ${prd.length} PRD entries.`);
171
309
  return;
172
310
  }
173
- // Parse all numeric arguments
174
- const indices = [];
175
- for (const a of args) {
176
- const index = parseInt(a);
177
- if (!index || isNaN(index)) {
178
- console.error("Usage: ralph prd toggle <number> [number2] [number3] ...");
179
- console.error(" ralph prd toggle --all");
180
- process.exit(1);
181
- }
182
- indices.push(index);
183
- }
184
- if (indices.length === 0) {
311
+ // Parse arguments with range support
312
+ const indices = expandRangeArgs(args);
313
+ if (!indices) {
185
314
  console.error("Usage: ralph prd toggle <number> [number2] [number3] ...");
315
+ console.error(" ralph prd toggle <start>-<end>");
186
316
  console.error(" ralph prd toggle --all");
187
317
  process.exit(1);
188
318
  }
@@ -194,8 +324,10 @@ export function prdToggle(args) {
194
324
  process.exit(1);
195
325
  }
196
326
  }
327
+ // Remove duplicates and sort
328
+ const uniqueIndices = [...new Set(indices)].sort((a, b) => a - b);
197
329
  // Toggle each entry
198
- for (const index of indices) {
330
+ for (const index of uniqueIndices) {
199
331
  const entry = prd[index - 1];
200
332
  entry.passes = !entry.passes;
201
333
  const statusText = entry.passes ? "PASSING" : "NOT PASSING";
@@ -216,6 +348,29 @@ export function prdClean() {
216
348
  console.log(`Removed ${removed} passing ${removed === 1 ? "entry" : "entries"}.`);
217
349
  console.log(`${filtered.length} ${filtered.length === 1 ? "entry" : "entries"} remaining.`);
218
350
  }
351
+ export async function prdReset() {
352
+ const prd = loadPrd();
353
+ if (prd.length === 0) {
354
+ console.log("No PRD entries to reset.");
355
+ return;
356
+ }
357
+ const alreadyPassing = prd.filter((e) => e.passes).length;
358
+ if (alreadyPassing === 0) {
359
+ console.log("All PRD entries are already incomplete (passes=false).");
360
+ return;
361
+ }
362
+ const confirmed = await promptConfirm(`Are you sure you want to reset ${alreadyPassing} ${alreadyPassing === 1 ? "entry" : "entries"} to incomplete?`, false);
363
+ if (!confirmed) {
364
+ console.log("Reset cancelled.");
365
+ return;
366
+ }
367
+ prd.forEach((entry) => {
368
+ entry.passes = false;
369
+ });
370
+ savePrd(prd);
371
+ console.log(`Reset ${alreadyPassing} ${alreadyPassing === 1 ? "entry" : "entries"} to incomplete.`);
372
+ console.log(`All ${prd.length} PRD ${prd.length === 1 ? "entry is" : "entries are"} now passes=false.`);
373
+ }
219
374
  export function parseListArgs(args) {
220
375
  let category;
221
376
  let passesFilter;
@@ -260,29 +415,43 @@ export async function prd(args) {
260
415
  prdList(category, passesFilter);
261
416
  break;
262
417
  }
263
- case "status":
264
- prdStatus();
418
+ case "status": {
419
+ const headOnly = args.slice(1).includes("--head");
420
+ prdStatus(headOnly);
265
421
  break;
422
+ }
266
423
  case "toggle":
267
424
  prdToggle(args.slice(1));
268
425
  break;
269
426
  case "clean":
270
427
  prdClean();
271
428
  break;
429
+ case "reset":
430
+ await prdReset();
431
+ break;
432
+ case "convert":
433
+ await prdConvert(args.slice(1));
434
+ break;
272
435
  default:
273
- console.error("Usage: ralph prd <add|list|status|toggle|clean>");
436
+ console.error("Usage: ralph prd <add|list|status|toggle|clean|reset|convert>");
274
437
  console.error("\nSubcommands:");
275
438
  console.error(" add Add a new PRD entry");
276
439
  console.error(" list [options] List all PRD entries");
277
- console.error(" status Show completion status");
440
+ console.error(" status [--head] Show completion status");
278
441
  console.error(" toggle <n> ... Toggle passes status for entry n (accepts multiple)");
442
+ console.error(" toggle <start>-<end> Toggle a range of entries (e.g., 1-18)");
279
443
  console.error(" toggle --all Toggle all PRD entries");
280
444
  console.error(" clean Remove all passing entries from the PRD");
445
+ console.error(" reset Reset all entries to incomplete (passes=false)");
446
+ console.error(" convert [options] Convert prd.json to prd.yaml format");
281
447
  console.error("\nList options:");
282
448
  console.error(" --category, -c <cat> Filter by category");
283
449
  console.error(" --passes Show only completed items");
284
450
  console.error(" --no-passes Show only incomplete items");
285
451
  console.error(" --stats Show statistics instead of entries");
452
+ console.error("\nConvert options:");
453
+ console.error(" --force, -f Overwrite existing files");
454
+ console.error(" --dry-run, -n Preview without making changes");
286
455
  console.error(`\nValid categories: ${CATEGORIES.join(", ")}`);
287
456
  process.exit(1);
288
457
  }
@@ -13,7 +13,7 @@ USAGE:
13
13
 
14
14
  DESCRIPTION:
15
15
  Prints the complete prompt that gets sent to Claude Code, including
16
- the @.ralph/prd.json and @.ralph/progress.txt file references.
16
+ the @.ralph/prd.yaml and @.ralph/progress.txt file references.
17
17
  This is useful for testing the prompt manually in Claude Code.
18
18
 
19
19
  TEMPLATE VARIABLES:
@@ -1,9 +1,9 @@
1
1
  import { spawn } from "child_process";
2
2
  import { existsSync, readFileSync, writeFileSync, unlinkSync, appendFileSync, mkdirSync } from "fs";
3
- import { join } from "path";
3
+ import { extname, join } from "path";
4
4
  import { checkFilesExist, loadConfig, loadPrompt, getPaths, getCliConfig, requireContainer, } from "../utils/config.js";
5
5
  import { resolvePromptVariables, getCliProviders } from "../templates/prompts.js";
6
- import { validatePrd, smartMerge, readPrdFile, writePrd, expandPrdFileReferences, } from "../utils/prd-validator.js";
6
+ import { validatePrd, smartMerge, readPrdFile, writePrd, writePrdAuto, expandPrdFileReferences, } from "../utils/prd-validator.js";
7
7
  import { getStreamJsonParser } from "../utils/stream-json.js";
8
8
  import { sendNotificationWithDaemonEvents } from "../utils/notification.js";
9
9
  const CATEGORIES = ["ui", "feature", "bugfix", "setup", "development", "testing", "docs"];
@@ -14,24 +14,23 @@ const CATEGORIES = ["ui", "feature", "bugfix", "setup", "development", "testing"
14
14
  * Returns the path to the temp file, or null if all items pass.
15
15
  */
16
16
  function createFilteredPrd(prdPath, baseDir, category) {
17
- const content = readFileSync(prdPath, "utf-8");
18
- let parsed;
19
- try {
20
- parsed = JSON.parse(content);
21
- }
22
- catch {
23
- console.error("\x1b[31mError: prd.json contains invalid JSON.\x1b[0m");
17
+ // Use readPrdFile to handle both JSON and YAML formats
18
+ const parsed = readPrdFile(prdPath);
19
+ if (!parsed) {
20
+ const ext = extname(prdPath).toLowerCase();
21
+ const format = ext === ".yaml" || ext === ".yml" ? "YAML" : "JSON";
22
+ console.error(`\x1b[31mError: PRD file contains invalid ${format}.\x1b[0m`);
24
23
  console.error("The file may have been corrupted by an LLM.\n");
25
24
  console.error("Run \x1b[36mralph fix-prd\x1b[0m to diagnose and repair the file.");
26
25
  process.exit(1);
27
26
  }
28
- if (!Array.isArray(parsed)) {
29
- console.error("\x1b[31mError: prd.json is corrupted - expected an array of items.\x1b[0m");
27
+ if (!Array.isArray(parsed.content)) {
28
+ console.error("\x1b[31mError: PRD is corrupted - expected an array of items.\x1b[0m");
30
29
  console.error("The file may have been modified incorrectly by an LLM.\n");
31
30
  console.error("Run \x1b[36mralph fix-prd\x1b[0m to diagnose and repair the file.");
32
31
  process.exit(1);
33
32
  }
34
- const items = parsed;
33
+ const items = parsed.content;
35
34
  let filteredItems = items.filter((item) => item.passes === false);
36
35
  // Apply category filter if specified
37
36
  if (category) {
@@ -48,9 +47,9 @@ function createFilteredPrd(prdPath, baseDir, category) {
48
47
  };
49
48
  }
50
49
  /**
51
- * Syncs passes flags from prd-tasks.json back to prd.json.
50
+ * Syncs passes flags from prd-tasks.json back to the main PRD file.
52
51
  * If the LLM marked any item as passes: true in prd-tasks.json,
53
- * find the matching item in prd.json and update it.
52
+ * find the matching item in the PRD and update it.
54
53
  * Returns the number of items synced and their names.
55
54
  */
56
55
  function syncPassesFromTasks(tasksPath, prdPath) {
@@ -66,22 +65,21 @@ function syncPassesFromTasks(tasksPath, prdPath) {
66
65
  return { count: 0, taskNames: [] };
67
66
  }
68
67
  const tasks = tasksParsed;
69
- const prdContent = readFileSync(prdPath, "utf-8");
70
- let prdParsed;
71
- try {
72
- prdParsed = JSON.parse(prdContent);
73
- }
74
- catch {
75
- console.warn("\x1b[33mWarning: prd.json contains invalid JSON - skipping sync.\x1b[0m");
68
+ // Use readPrdFile to handle both JSON and YAML formats
69
+ const prdParsed = readPrdFile(prdPath);
70
+ if (!prdParsed) {
71
+ const ext = extname(prdPath).toLowerCase();
72
+ const format = ext === ".yaml" || ext === ".yml" ? "YAML" : "JSON";
73
+ console.warn(`\x1b[33mWarning: PRD contains invalid ${format} - skipping sync.\x1b[0m`);
76
74
  console.warn("Run \x1b[36mralph fix-prd\x1b[0m after this session to repair.\n");
77
75
  return { count: 0, taskNames: [] };
78
76
  }
79
- if (!Array.isArray(prdParsed)) {
80
- console.warn("\x1b[33mWarning: prd.json is corrupted - skipping sync.\x1b[0m");
77
+ if (!Array.isArray(prdParsed.content)) {
78
+ console.warn("\x1b[33mWarning: PRD is corrupted - skipping sync.\x1b[0m");
81
79
  console.warn("Run \x1b[36mralph fix-prd\x1b[0m after this session to repair.\n");
82
80
  return { count: 0, taskNames: [] };
83
81
  }
84
- const prd = prdParsed;
82
+ const prd = prdParsed.content;
85
83
  let synced = 0;
86
84
  const syncedTaskNames = [];
87
85
  // Find tasks that were marked as passing
@@ -98,10 +96,11 @@ function syncPassesFromTasks(tasksPath, prdPath) {
98
96
  }
99
97
  }
100
98
  }
101
- // Write back if any items were synced
99
+ // Write back if any items were synced (using format-aware write)
102
100
  if (synced > 0) {
103
- writeFileSync(prdPath, JSON.stringify(prd, null, 2) + "\n");
104
- console.log(`\x1b[32mSynced ${synced} completed item(s) from prd-tasks.json to prd.json\x1b[0m`);
101
+ writePrdAuto(prdPath, prd);
102
+ const prdFileName = prdPath.split("/").pop() || "PRD";
103
+ console.log(`\x1b[32mSynced ${synced} completed item(s) from prd-tasks.json to ${prdFileName}\x1b[0m`);
105
104
  }
106
105
  return { count: synced, taskNames: syncedTaskNames };
107
106
  }
@@ -277,24 +276,23 @@ function formatElapsedTime(startTime, endTime) {
277
276
  * Optionally filters by category if specified.
278
277
  */
279
278
  function countPrdItems(prdPath, category) {
280
- const content = readFileSync(prdPath, "utf-8");
281
- let parsed;
282
- try {
283
- parsed = JSON.parse(content);
284
- }
285
- catch {
286
- console.error("\x1b[31mError: prd.json contains invalid JSON.\x1b[0m");
279
+ // Use readPrdFile to handle both JSON and YAML formats
280
+ const parsed = readPrdFile(prdPath);
281
+ if (!parsed) {
282
+ const ext = extname(prdPath).toLowerCase();
283
+ const format = ext === ".yaml" || ext === ".yml" ? "YAML" : "JSON";
284
+ console.error(`\x1b[31mError: PRD contains invalid ${format}.\x1b[0m`);
287
285
  console.error("The file may have been corrupted by an LLM.\n");
288
286
  console.error("Run \x1b[36mralph fix-prd\x1b[0m to diagnose and repair the file.");
289
287
  process.exit(1);
290
288
  }
291
- if (!Array.isArray(parsed)) {
292
- console.error("\x1b[31mError: prd.json is corrupted - expected an array of items.\x1b[0m");
289
+ if (!Array.isArray(parsed.content)) {
290
+ console.error("\x1b[31mError: PRD is corrupted - expected an array of items.\x1b[0m");
293
291
  console.error("The file may have been modified incorrectly by an LLM.\n");
294
292
  console.error("Run \x1b[36mralph fix-prd\x1b[0m to diagnose and repair the file.");
295
293
  process.exit(1);
296
294
  }
297
- const items = parsed;
295
+ const items = parsed.content;
298
296
  let filteredItems = items;
299
297
  if (category) {
300
298
  filteredItems = items.filter((item) => item.category === category);
@@ -347,15 +345,16 @@ function validateAndRecoverPrd(prdPath, validPrd) {
347
345
  * Returns the validated PRD entries.
348
346
  */
349
347
  function loadValidPrd(prdPath) {
350
- const content = readFileSync(prdPath, "utf-8");
351
- try {
352
- return JSON.parse(content);
353
- }
354
- catch {
355
- console.error("\x1b[31mError: prd.json contains invalid JSON.\x1b[0m");
348
+ // Use readPrdFile to handle both JSON and YAML formats
349
+ const parsed = readPrdFile(prdPath);
350
+ if (!parsed || !Array.isArray(parsed.content)) {
351
+ const ext = extname(prdPath).toLowerCase();
352
+ const format = ext === ".yaml" || ext === ".yml" ? "YAML" : "JSON";
353
+ console.error(`\x1b[31mError: PRD contains invalid ${format}.\x1b[0m`);
356
354
  console.error("Run \x1b[36mralph fix-prd\x1b[0m to diagnose and repair the file.");
357
355
  process.exit(1);
358
356
  }
357
+ return parsed.content;
359
358
  }
360
359
  export async function run(args) {
361
360
  // Parse flags
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { help } from "./commands/help.js";
6
6
  import { init } from "./commands/init.js";
7
7
  import { once } from "./commands/once.js";
8
8
  import { run } from "./commands/run.js";
9
- import { prd, prdAdd, prdList, prdStatus, prdToggle, prdClean, parseListArgs, } from "./commands/prd.js";
9
+ import { prd, prdAdd, prdList, prdStatus, prdToggle, prdClean, prdReset, parseListArgs, } from "./commands/prd.js";
10
10
  import { docker } from "./commands/docker.js";
11
11
  import { prompt } from "./commands/prompt.js";
12
12
  import { fixPrd } from "./commands/fix-prd.js";
@@ -51,9 +51,13 @@ const commands = {
51
51
  const { category, passesFilter } = parseListArgs(args);
52
52
  prdList(category, passesFilter);
53
53
  },
54
- status: () => prdStatus(),
54
+ status: (args) => {
55
+ const headOnly = args.includes("--head");
56
+ prdStatus(headOnly);
57
+ },
55
58
  toggle: (args) => prdToggle(args),
56
59
  clean: () => prdClean(),
60
+ reset: () => prdReset(),
57
61
  };
58
62
  async function main() {
59
63
  const args = process.argv.slice(2);
@@ -75,5 +75,6 @@ export declare function resolvePromptVariables(template: string, config: {
75
75
  }): string;
76
76
  export declare function generatePrompt(config: LanguageConfig, technologies?: string[]): string;
77
77
  export declare const DEFAULT_PRD = "[\n {\n \"category\": \"setup\",\n \"description\": \"Example: Project builds successfully\",\n \"steps\": [\n \"Run the build command\",\n \"Verify no errors occur\"\n ],\n \"passes\": false\n }\n]";
78
+ export declare const DEFAULT_PRD_YAML = "- category: setup\n description: \"Example: Project builds successfully\"\n steps:\n - Run the build command\n - Verify no errors occur\n passes: false\n";
78
79
  export declare const DEFAULT_PROGRESS = "# Progress Log\n";
79
80
  export {};
@@ -105,7 +105,7 @@ INSTRUCTIONS:
105
105
  3. Verify your changes work by running:
106
106
  - Type/build check: $checkCommand
107
107
  - Tests: $testCommand
108
- 4. Update .ralph/prd.json to set "passes": true for the completed feature
108
+ 4. Update .ralph/prd.yaml to set "passes": true for the completed feature
109
109
  5. Append a brief note about what you did to .ralph/progress.txt
110
110
  6. Create a git commit with a descriptive message for this feature
111
111
  7. Only work on ONE feature per execution
@@ -152,4 +152,11 @@ export const DEFAULT_PRD = `[
152
152
  "passes": false
153
153
  }
154
154
  ]`;
155
+ export const DEFAULT_PRD_YAML = `- category: setup
156
+ description: "Example: Project builds successfully"
157
+ steps:
158
+ - Run the build command
159
+ - Verify no errors occur
160
+ passes: false
161
+ `;
155
162
  export const DEFAULT_PROGRESS = `# Progress Log\n`;
@@ -193,6 +193,24 @@ export interface RalphConfig {
193
193
  }
194
194
  export declare const DEFAULT_CLI_CONFIG: CliConfig;
195
195
  export declare function getCliConfig(config: RalphConfig): CliConfig;
196
+ /**
197
+ * Gets the PRD file path(s) that exist.
198
+ * Returns an object with:
199
+ * - primary: The main PRD file path to use (yaml preferred over json)
200
+ * - secondary: The secondary PRD file path if both exist (for merging)
201
+ * - jsonOnly: True if only prd.json exists (shows migration notice)
202
+ * - yamlOnly: True if only prd.yaml exists (happy path)
203
+ * - both: True if both files exist (merge mode)
204
+ * - none: True if no PRD file exists
205
+ */
206
+ export declare function getPrdFiles(): {
207
+ primary: string | null;
208
+ secondary: string | null;
209
+ jsonOnly: boolean;
210
+ yamlOnly: boolean;
211
+ both: boolean;
212
+ none: boolean;
213
+ };
196
214
  export declare function getRalphDir(): string;
197
215
  export declare function loadConfig(): RalphConfig;
198
216
  export declare function loadPrompt(): string;
@@ -202,6 +220,7 @@ export declare function getPaths(): {
202
220
  config: string;
203
221
  prompt: string;
204
222
  prd: string;
223
+ prdSecondary: string | null;
205
224
  progress: string;
206
225
  };
207
226
  /**