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.
@@ -3,9 +3,10 @@
3
3
  * Allows ralph to receive commands and send notifications via chat services.
4
4
  */
5
5
  import { existsSync, readFileSync, writeFileSync } from "fs";
6
- import { join, basename } from "path";
6
+ import { join, basename, extname } from "path";
7
7
  import { spawn } from "child_process";
8
- import { loadConfig, getRalphDir, isRunningInContainer } from "../utils/config.js";
8
+ import YAML from "yaml";
9
+ import { loadConfig, getRalphDir, isRunningInContainer, getPrdFiles } from "../utils/config.js";
9
10
  import { createTelegramClient } from "../providers/telegram.js";
10
11
  import { createSlackClient } from "../providers/slack.js";
11
12
  import { createDiscordClient } from "../providers/discord.js";
@@ -53,18 +54,40 @@ function getOrCreateProjectId() {
53
54
  function getProjectName() {
54
55
  return basename(process.cwd());
55
56
  }
57
+ /**
58
+ * Parse PRD file content based on file extension.
59
+ */
60
+ function parsePrdContent(filePath, content) {
61
+ const ext = extname(filePath).toLowerCase();
62
+ try {
63
+ let parsed;
64
+ if (ext === ".yaml" || ext === ".yml") {
65
+ parsed = YAML.parse(content);
66
+ }
67
+ else {
68
+ parsed = JSON.parse(content);
69
+ }
70
+ if (Array.isArray(parsed)) {
71
+ return parsed;
72
+ }
73
+ return null;
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
56
79
  /**
57
80
  * Get PRD status (completed/total tasks).
58
81
  */
59
82
  function getPrdStatus() {
60
- const ralphDir = getRalphDir();
61
- const prdPath = join(ralphDir, "prd.json");
62
- if (!existsSync(prdPath)) {
83
+ const prdFiles = getPrdFiles();
84
+ if (prdFiles.none) {
63
85
  return { complete: 0, total: 0, incomplete: 0 };
64
86
  }
65
87
  try {
88
+ const prdPath = prdFiles.primary;
66
89
  const content = readFileSync(prdPath, "utf-8");
67
- const items = JSON.parse(content);
90
+ const items = parsePrdContent(prdPath, content);
68
91
  if (!Array.isArray(items)) {
69
92
  return { complete: 0, total: 0, incomplete: 0 };
70
93
  }
@@ -81,14 +104,14 @@ function getPrdStatus() {
81
104
  * Returns unique categories that have at least one incomplete task.
82
105
  */
83
106
  function getOpenCategories() {
84
- const ralphDir = getRalphDir();
85
- const prdPath = join(ralphDir, "prd.json");
86
- if (!existsSync(prdPath)) {
107
+ const prdFiles = getPrdFiles();
108
+ if (prdFiles.none) {
87
109
  return [];
88
110
  }
89
111
  try {
112
+ const prdPath = prdFiles.primary;
90
113
  const content = readFileSync(prdPath, "utf-8");
91
- const items = JSON.parse(content);
114
+ const items = parsePrdContent(prdPath, content);
92
115
  if (!Array.isArray(items)) {
93
116
  return [];
94
117
  }
@@ -109,14 +132,14 @@ function getOpenCategories() {
109
132
  * Add a new task to the PRD.
110
133
  */
111
134
  function addPrdTask(description) {
112
- const ralphDir = getRalphDir();
113
- const prdPath = join(ralphDir, "prd.json");
114
- if (!existsSync(prdPath)) {
135
+ const prdFiles = getPrdFiles();
136
+ if (prdFiles.none) {
115
137
  return false;
116
138
  }
117
139
  try {
140
+ const prdPath = prdFiles.primary;
118
141
  const content = readFileSync(prdPath, "utf-8");
119
- const items = JSON.parse(content);
142
+ const items = parsePrdContent(prdPath, content);
120
143
  if (!Array.isArray(items)) {
121
144
  return false;
122
145
  }
@@ -126,7 +149,14 @@ function addPrdTask(description) {
126
149
  steps: [],
127
150
  passes: false,
128
151
  });
129
- writeFileSync(prdPath, JSON.stringify(items, null, 2) + "\n");
152
+ // Write back in the same format as the source file
153
+ const ext = extname(prdPath).toLowerCase();
154
+ if (ext === ".yaml" || ext === ".yml") {
155
+ writeFileSync(prdPath, YAML.stringify(items));
156
+ }
157
+ else {
158
+ writeFileSync(prdPath, JSON.stringify(items, null, 2) + "\n");
159
+ }
130
160
  return true;
131
161
  }
132
162
  catch {
@@ -1,7 +1,8 @@
1
1
  import { existsSync, readFileSync, copyFileSync } from "fs";
2
- import { join, isAbsolute } from "path";
3
- import { getPaths, getRalphDir } from "../utils/config.js";
4
- import { validatePrd, attemptRecovery, createBackup, findLatestBackup, createTemplatePrd, readPrdFile, writePrd, } from "../utils/prd-validator.js";
2
+ import { join, isAbsolute, extname } from "path";
3
+ import { getPaths, getRalphDir, getPrdFiles } from "../utils/config.js";
4
+ import { validatePrd, attemptRecovery, createBackup, findLatestBackup, createTemplatePrd, readPrdFile, writePrdAuto, } from "../utils/prd-validator.js";
5
+ import YAML from "yaml";
5
6
  /**
6
7
  * Resolves a backup path - can be absolute, relative, or just a filename.
7
8
  */
@@ -17,8 +18,21 @@ function resolveBackupPath(backupArg) {
17
18
  // Otherwise treat as relative to cwd
18
19
  return join(process.cwd(), backupArg);
19
20
  }
21
+ /**
22
+ * Parses a backup file content based on file extension.
23
+ * Supports both JSON and YAML formats.
24
+ */
25
+ function parseBackupContent(backupPath, content) {
26
+ const ext = extname(backupPath).toLowerCase();
27
+ if (ext === ".yaml" || ext === ".yml") {
28
+ return YAML.parse(content);
29
+ }
30
+ return JSON.parse(content);
31
+ }
20
32
  /**
21
33
  * Restores PRD from a specific backup file.
34
+ * Supports restoring from both .json and .yaml/.yml backup files.
35
+ * Writes output in the same format as the target PRD file.
22
36
  */
23
37
  function restoreFromBackup(prdPath, backupPath) {
24
38
  if (!existsSync(backupPath)) {
@@ -27,7 +41,7 @@ function restoreFromBackup(prdPath, backupPath) {
27
41
  }
28
42
  try {
29
43
  const backupContent = readFileSync(backupPath, "utf-8");
30
- const backupParsed = JSON.parse(backupContent);
44
+ const backupParsed = parseBackupContent(backupPath, backupContent);
31
45
  const validation = validatePrd(backupParsed);
32
46
  if (!validation.valid) {
33
47
  console.error("Error: Backup file contains invalid PRD structure:");
@@ -41,7 +55,8 @@ function restoreFromBackup(prdPath, backupPath) {
41
55
  const currentBackup = createBackup(prdPath);
42
56
  console.log(`Created backup of current PRD: ${currentBackup}`);
43
57
  }
44
- writePrd(prdPath, validation.data);
58
+ // Write in the same format as the target PRD file
59
+ writePrdAuto(prdPath, validation.data);
45
60
  console.log(`\x1b[32m✓ PRD restored from: ${backupPath}\x1b[0m`);
46
61
  console.log(` Restored ${validation.data.length} entries.`);
47
62
  return true;
@@ -52,13 +67,16 @@ function restoreFromBackup(prdPath, backupPath) {
52
67
  }
53
68
  }
54
69
  /**
55
- * Handles the case where the PRD file contains invalid JSON.
70
+ * Handles the case where the PRD file contains invalid JSON/YAML.
56
71
  * Attempts to restore from backup or reset to template.
72
+ * Preserves the original file format (JSON or YAML).
57
73
  */
58
74
  function handleBrokenPrd(prdPath) {
59
- // Create backup of the broken file (preserving raw content)
75
+ // Create backup of the broken file (preserving raw content and extension)
60
76
  const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
61
- const backupPath = prdPath.replace("prd.json", `backup.prd.${timestamp}.json`);
77
+ const ext = extname(prdPath).toLowerCase();
78
+ const backupExt = ext === ".yaml" || ext === ".yml" ? ext : ".json";
79
+ const backupPath = join(getRalphDir(), `backup.prd.${timestamp}${backupExt}`);
62
80
  copyFileSync(prdPath, backupPath);
63
81
  console.log(`Created backup of broken file: ${backupPath}\n`);
64
82
  // Try to restore from a previous valid backup
@@ -67,10 +85,10 @@ function handleBrokenPrd(prdPath) {
67
85
  console.log(`Found previous backup: ${latestBackup}`);
68
86
  try {
69
87
  const backupContent = readFileSync(latestBackup, "utf-8");
70
- const backupParsed = JSON.parse(backupContent);
88
+ const backupParsed = parseBackupContent(latestBackup, backupContent);
71
89
  const backupValidation = validatePrd(backupParsed);
72
90
  if (backupValidation.valid) {
73
- writePrd(prdPath, backupValidation.data);
91
+ writePrdAuto(prdPath, backupValidation.data);
74
92
  console.log("\x1b[32m✓ PRD restored from backup!\x1b[0m");
75
93
  console.log(` Restored ${backupValidation.data.length} entries.`);
76
94
  console.log("\x1b[33m Note: Recent changes may have been lost.\x1b[0m");
@@ -89,7 +107,7 @@ function handleBrokenPrd(prdPath) {
89
107
  }
90
108
  // Reset to template as last resort - with instructions to recover from backup
91
109
  console.log("Resetting PRD to recovery template...");
92
- writePrd(prdPath, createTemplatePrd(backupPath));
110
+ writePrdAuto(prdPath, createTemplatePrd(backupPath));
93
111
  console.log("\x1b[33m✓ PRD reset with recovery task.\x1b[0m");
94
112
  console.log(" Next 'ralph run' will instruct the LLM to recover entries from backup.");
95
113
  console.log(` Backup location: ${backupPath}`);
@@ -113,20 +131,26 @@ export async function fixPrd(args = []) {
113
131
  const success = restoreFromBackup(paths.prd, resolvedPath);
114
132
  process.exit(success ? 0 : 1);
115
133
  }
116
- if (!existsSync(paths.prd)) {
117
- console.error("Error: .ralph/prd.json not found. Run 'ralph init' first.");
134
+ // Detect which PRD files exist
135
+ const prdFiles = getPrdFiles();
136
+ if (prdFiles.none) {
137
+ console.error("Error: No PRD file found (.ralph/prd.yaml or .ralph/prd.json). Run 'ralph init' first.");
118
138
  process.exit(1);
119
139
  }
120
- console.log("Checking PRD structure...\n");
140
+ // Use the primary PRD path (YAML preferred over JSON)
141
+ const prdPath = paths.prd;
142
+ const isYamlFile = extname(prdPath).toLowerCase() === ".yaml" || extname(prdPath).toLowerCase() === ".yml";
143
+ const fileFormatName = isYamlFile ? "YAML" : "JSON";
144
+ console.log(`Checking PRD structure (${fileFormatName})...\n`);
121
145
  // Step 1: Try to read and parse the file
122
- const parsed = readPrdFile(paths.prd);
146
+ const parsed = readPrdFile(prdPath);
123
147
  if (!parsed) {
124
- // JSON parsing failed - file is completely broken
125
- console.log("\x1b[31m✗ PRD file contains invalid JSON.\x1b[0m\n");
148
+ // Parsing failed - file is completely broken
149
+ console.log(`\x1b[31m✗ PRD file contains invalid ${fileFormatName}.\x1b[0m\n`);
126
150
  if (verifyOnly) {
127
151
  process.exit(1);
128
152
  }
129
- handleBrokenPrd(paths.prd);
153
+ handleBrokenPrd(prdPath);
130
154
  return;
131
155
  }
132
156
  // Step 2: Validate the structure
@@ -149,7 +173,7 @@ export async function fixPrd(args = []) {
149
173
  process.exit(1);
150
174
  }
151
175
  // Step 3: Create backup before any modifications
152
- const backupPath = createBackup(paths.prd);
176
+ const backupPath = createBackup(prdPath);
153
177
  console.log(`Created backup: ${backupPath}\n`);
154
178
  // Step 4: Attempt recovery strategies
155
179
  console.log("Attempting recovery...\n");
@@ -159,7 +183,7 @@ export async function fixPrd(args = []) {
159
183
  // Validate the recovered data
160
184
  const recoveredValidation = validatePrd(recovered);
161
185
  if (recoveredValidation.valid) {
162
- writePrd(paths.prd, recovered);
186
+ writePrdAuto(prdPath, recovered);
163
187
  console.log("\x1b[32m✓ PRD recovered successfully!\x1b[0m");
164
188
  console.log(` Recovered ${recovered.length} entries by unwrapping/remapping fields.`);
165
189
  return;
@@ -167,15 +191,15 @@ export async function fixPrd(args = []) {
167
191
  }
168
192
  console.log(" Direct recovery failed.\n");
169
193
  // Strategy 2: Restore from backup
170
- const latestBackup = findLatestBackup(paths.prd);
194
+ const latestBackup = findLatestBackup(prdPath);
171
195
  if (latestBackup && latestBackup !== backupPath) {
172
196
  console.log(`Found previous backup: ${latestBackup}`);
173
197
  try {
174
198
  const backupContent = readFileSync(latestBackup, "utf-8");
175
- const backupParsed = JSON.parse(backupContent);
199
+ const backupParsed = parseBackupContent(latestBackup, backupContent);
176
200
  const backupValidation = validatePrd(backupParsed);
177
201
  if (backupValidation.valid) {
178
- writePrd(paths.prd, backupValidation.data);
202
+ writePrdAuto(prdPath, backupValidation.data);
179
203
  console.log("\x1b[32m✓ PRD restored from backup!\x1b[0m");
180
204
  console.log(` Restored ${backupValidation.data.length} entries.`);
181
205
  console.log("\x1b[33m Note: Recent changes may have been lost.\x1b[0m");
@@ -194,7 +218,7 @@ export async function fixPrd(args = []) {
194
218
  }
195
219
  // Strategy 3: Reset to recovery template - LLM will fix it on next run
196
220
  console.log("Resetting PRD to recovery template...");
197
- writePrd(paths.prd, createTemplatePrd(backupPath));
221
+ writePrdAuto(prdPath, createTemplatePrd(backupPath));
198
222
  console.log("\x1b[33m✓ PRD reset with recovery task.\x1b[0m");
199
223
  console.log(" Next 'ralph run' will instruct the LLM to recover entries from backup.");
200
224
  console.log(` Backup location: ${backupPath}`);
@@ -10,9 +10,10 @@ COMMANDS:
10
10
  run [n] [opts] Run automation iterations (default: all tasks)
11
11
  add Add a new PRD entry (interactive)
12
12
  list [opts] List all PRD entries
13
- status Show PRD completion status
13
+ status [--head] Show PRD completion status
14
14
  toggle <n> Toggle passes status for entry n
15
15
  clean Remove all passing entries from the PRD
16
+ reset Reset all entries to incomplete (passes=false)
16
17
  fix-prd [opts] Validate and recover corrupted PRD file
17
18
  fix-config [opts] Validate and recover corrupted config.json
18
19
  prompt [opts] Display resolved prompt (for testing in Claude Code)
@@ -45,8 +46,12 @@ LIST OPTIONS:
45
46
  --passes Show only completed items (passes=true)
46
47
  --no-passes Show only incomplete items (passes=false)
47
48
 
49
+ STATUS OPTIONS:
50
+ --head Show only status summary without task headlines
51
+
48
52
  TOGGLE OPTIONS:
49
53
  <n> [n2] [n3]... Toggle one or more entries by number
54
+ <start>-<end> Toggle a range of entries (e.g., 1-18)
50
55
  --all, -a Toggle all PRD entries
51
56
 
52
57
  FIX-PRD OPTIONS:
@@ -105,11 +110,14 @@ EXAMPLES:
105
110
  ralph list -c feature # Show only feature entries
106
111
  ralph list --passes # Show only completed entries
107
112
  ralph list --no-passes # Show only incomplete entries
108
- ralph status # Show completion summary
113
+ ralph status # Show completion summary with task headlines
114
+ ralph status --head # Show completion summary without task headlines
109
115
  ralph toggle 1 # Toggle entry #1
110
116
  ralph toggle 1 2 3 # Toggle multiple entries
117
+ ralph toggle 1-5 # Toggle entries 1 through 5
111
118
  ralph toggle --all # Toggle all entries
112
119
  ralph clean # Remove passing entries
120
+ ralph reset # Reset all entries to incomplete
113
121
  ralph fix-prd # Validate/recover corrupted PRD file
114
122
  ralph fix-prd --verify # Check PRD format without fixing
115
123
  ralph fix-prd backup.prd.2024-01-15.json # Restore from specific backup
@@ -133,7 +141,7 @@ CONFIGURATION:
133
141
  .ralph/
134
142
  ├── config.json Project configuration (language, commands, cli)
135
143
  ├── prompt.md Prompt template with $variables ($language, $checkCommand, etc.)
136
- ├── prd.json Product requirements document
144
+ ├── prd.yaml Product requirements document
137
145
  └── progress.txt Progress tracking file
138
146
 
139
147
  CLI CONFIGURATION:
@@ -1,7 +1,7 @@
1
1
  import { existsSync, writeFileSync, mkdirSync, copyFileSync, chmodSync } from "fs";
2
2
  import { join, basename, dirname } from "path";
3
3
  import { fileURLToPath } from "url";
4
- import { getLanguages, generatePromptTemplate, DEFAULT_PRD, DEFAULT_PROGRESS, getCliProviders, getSkillsForLanguage, } from "../templates/prompts.js";
4
+ import { getLanguages, generatePromptTemplate, DEFAULT_PRD_YAML, DEFAULT_PROGRESS, getCliProviders, getSkillsForLanguage, } from "../templates/prompts.js";
5
5
  import { generateGenXcodeScript, hasSwiftUI, hasFastlane, generateFastfile, generateAppfile, generateFastlaneReadmeSection, } from "../templates/macos-scripts.js";
6
6
  import { promptSelectWithArrows, promptConfirm, promptInput, promptMultiSelectWithArrows, } from "../utils/prompt.js";
7
7
  import { dockerInit } from "./docker.js";
@@ -13,7 +13,7 @@ const PACKAGE_ROOT = join(__dirname, "..", ".."); // dist/commands -> dist -> pa
13
13
  const RALPH_DIR = ".ralph";
14
14
  const CONFIG_FILE = "config.json";
15
15
  const PROMPT_FILE = "prompt.md";
16
- const PRD_FILE = "prd.json";
16
+ const PRD_FILE = "prd.yaml";
17
17
  const PROGRESS_FILE = "progress.txt";
18
18
  const PRD_GUIDE_FILE = "HOW-TO-WRITE-PRDs.md";
19
19
  export async function init(args) {
@@ -339,14 +339,15 @@ export async function init(args) {
339
339
  writeFileSync(promptPath, prompt + "\n");
340
340
  console.log(`${existsSync(promptPath) ? "Updated" : "Created"} ${RALPH_DIR}/${PROMPT_FILE}`);
341
341
  }
342
- // Create PRD if not exists
342
+ // Create PRD if not exists (check for both yaml and json)
343
343
  const prdPath = join(ralphDir, PRD_FILE);
344
- if (!existsSync(prdPath)) {
345
- writeFileSync(prdPath, DEFAULT_PRD + "\n");
344
+ const prdJsonPath = join(ralphDir, "prd.json");
345
+ if (!existsSync(prdPath) && !existsSync(prdJsonPath)) {
346
+ writeFileSync(prdPath, DEFAULT_PRD_YAML);
346
347
  console.log(`Created ${RALPH_DIR}/${PRD_FILE}`);
347
348
  }
348
349
  else {
349
- console.log(`Skipped ${RALPH_DIR}/${PRD_FILE} (already exists)`);
350
+ console.log(`Skipped ${RALPH_DIR}/${PRD_FILE} (PRD already exists)`);
350
351
  }
351
352
  // Create progress file if not exists
352
353
  const progressPath = join(ralphDir, PROGRESS_FILE);
@@ -453,7 +454,7 @@ docker/.config-hash
453
454
  console.log("Created .ralph/docker/ files");
454
455
  console.log("\nRalph initialized successfully!");
455
456
  console.log("\nNext steps:");
456
- console.log(" 1. Edit .ralph/prd.json to add your project requirements");
457
+ console.log(" 1. Edit .ralph/prd.yaml to add your project requirements");
457
458
  console.log(" 2. Run 'ralph docker run' to start (auto-builds image on first run)");
458
459
  console.log("\nSee .ralph/HOW-TO-WRITE-PRDs.md for guidance on writing PRDs");
459
460
  console.log("To regenerate Docker files: ralph docker init");
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Converts prd.json to prd.yaml format.
3
+ * - Reads .ralph/prd.json
4
+ * - Converts to YAML format
5
+ * - Writes to .ralph/prd.yaml
6
+ * - Renames original prd.json to prd.json.pre-yaml
7
+ */
8
+ export declare function prdConvert(args: string[]): Promise<void>;
9
+ export declare function convert(args: string[]): Promise<void>;
@@ -0,0 +1,108 @@
1
+ import { existsSync, readFileSync, renameSync, writeFileSync } from "fs";
2
+ import { join } from "path";
3
+ import YAML from "yaml";
4
+ import { getRalphDir } from "../utils/config.js";
5
+ const PRD_JSON_FILE = "prd.json";
6
+ const PRD_YAML_FILE = "prd.yaml";
7
+ const PRD_BACKUP_SUFFIX = ".pre-yaml";
8
+ function getPrdJsonPath() {
9
+ return join(getRalphDir(), PRD_JSON_FILE);
10
+ }
11
+ function getPrdYamlPath() {
12
+ return join(getRalphDir(), PRD_YAML_FILE);
13
+ }
14
+ /**
15
+ * Converts prd.json to prd.yaml format.
16
+ * - Reads .ralph/prd.json
17
+ * - Converts to YAML format
18
+ * - Writes to .ralph/prd.yaml
19
+ * - Renames original prd.json to prd.json.pre-yaml
20
+ */
21
+ export async function prdConvert(args) {
22
+ const force = args.includes("--force") || args.includes("-f");
23
+ const dryRun = args.includes("--dry-run") || args.includes("-n");
24
+ const jsonPath = getPrdJsonPath();
25
+ const yamlPath = getPrdYamlPath();
26
+ const backupPath = jsonPath + PRD_BACKUP_SUFFIX;
27
+ // Check if prd.json exists
28
+ if (!existsSync(jsonPath)) {
29
+ console.error("Error: .ralph/prd.json not found.");
30
+ console.error("Run 'ralph init' first to create a project.");
31
+ process.exit(1);
32
+ }
33
+ // Check if prd.yaml already exists
34
+ if (existsSync(yamlPath) && !force) {
35
+ console.error("Error: .ralph/prd.yaml already exists.");
36
+ console.error("Use --force to overwrite the existing YAML file.");
37
+ process.exit(1);
38
+ }
39
+ // Check if backup already exists (previous conversion)
40
+ if (existsSync(backupPath) && !force) {
41
+ console.error("Error: .ralph/prd.json.pre-yaml already exists.");
42
+ console.error("It appears a previous conversion was performed.");
43
+ console.error("Use --force to overwrite existing files.");
44
+ process.exit(1);
45
+ }
46
+ // Read and parse JSON
47
+ let prdEntries;
48
+ try {
49
+ const jsonContent = readFileSync(jsonPath, "utf-8");
50
+ prdEntries = JSON.parse(jsonContent);
51
+ if (!Array.isArray(prdEntries)) {
52
+ throw new Error("PRD content is not an array");
53
+ }
54
+ }
55
+ catch (err) {
56
+ const message = err instanceof Error ? err.message : "Unknown error";
57
+ console.error(`Error reading .ralph/prd.json: ${message}`);
58
+ console.error("Run 'ralph fix-prd' to attempt repair.");
59
+ process.exit(1);
60
+ }
61
+ // Convert to YAML
62
+ const yamlContent = YAML.stringify(prdEntries, {
63
+ indent: 2,
64
+ lineWidth: 0, // Don't wrap lines
65
+ });
66
+ if (dryRun) {
67
+ console.log("Dry run - no files will be modified.\n");
68
+ console.log("Would convert .ralph/prd.json to .ralph/prd.yaml:\n");
69
+ console.log("--- YAML Output ---");
70
+ console.log(yamlContent);
71
+ console.log("-------------------\n");
72
+ console.log(`Entries: ${prdEntries.length}`);
73
+ return;
74
+ }
75
+ // Write YAML file
76
+ writeFileSync(yamlPath, yamlContent);
77
+ console.log(`\x1b[32m✓\x1b[0m Created .ralph/prd.yaml`);
78
+ // Rename original JSON to backup
79
+ renameSync(jsonPath, backupPath);
80
+ console.log(`\x1b[32m✓\x1b[0m Renamed .ralph/prd.json to .ralph/prd.json.pre-yaml`);
81
+ // Success message
82
+ console.log(`\n\x1b[32mConversion complete!\x1b[0m`);
83
+ console.log(` Converted ${prdEntries.length} PRD entries to YAML format.\n`);
84
+ console.log("Next steps:");
85
+ console.log(" 1. Your PRD is now in .ralph/prd.yaml");
86
+ console.log(" 2. The original JSON is preserved as .ralph/prd.json.pre-yaml");
87
+ console.log(" 3. Ralph will automatically use the YAML file going forward");
88
+ console.log("");
89
+ console.log("To revert, simply rename the files back:");
90
+ console.log(" mv .ralph/prd.json.pre-yaml .ralph/prd.json");
91
+ console.log(" rm .ralph/prd.yaml");
92
+ }
93
+ function showHelp() {
94
+ console.log("Usage: ralph prd convert [options]\n");
95
+ console.log("Convert .ralph/prd.json to .ralph/prd.yaml format.\n");
96
+ console.log("Options:");
97
+ console.log(" --force, -f Overwrite existing prd.yaml and backup files");
98
+ console.log(" --dry-run, -n Show what would be converted without making changes");
99
+ console.log(" --help, -h Show this help message\n");
100
+ console.log("The original prd.json will be renamed to prd.json.pre-yaml as a backup.");
101
+ }
102
+ export async function convert(args) {
103
+ if (args.includes("--help") || args.includes("-h")) {
104
+ showHelp();
105
+ return;
106
+ }
107
+ await prdConvert(args);
108
+ }
@@ -1,8 +1,9 @@
1
1
  export declare function prdAdd(): Promise<void>;
2
2
  export declare function prdList(category?: string, passesFilter?: boolean): void;
3
- export declare function prdStatus(): void;
3
+ export declare function prdStatus(headOnly?: boolean): void;
4
4
  export declare function prdToggle(args: string[]): void;
5
5
  export declare function prdClean(): void;
6
+ export declare function prdReset(): Promise<void>;
6
7
  export declare function parseListArgs(args: string[]): {
7
8
  category?: string;
8
9
  passesFilter?: boolean;