ralph-tool 1.0.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.
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { execSync } = require('child_process');
6
+
7
+ // ── Make all bash scripts executable ──
8
+
9
+ const scriptsDir = path.join(__dirname);
10
+ const helpersDir = path.join(__dirname, '..', 'helpers');
11
+
12
+ function makeExecutable(dir) {
13
+ if (!fs.existsSync(dir)) return;
14
+ const files = fs.readdirSync(dir);
15
+ for (const file of files) {
16
+ if (file.endsWith('.sh')) {
17
+ try {
18
+ fs.chmodSync(path.join(dir, file), '755');
19
+ } catch (e) {}
20
+ }
21
+ }
22
+ }
23
+
24
+ makeExecutable(scriptsDir);
25
+ makeExecutable(helpersDir);
26
+
27
+ // ── Dependency checks ──
28
+
29
+ const platform = process.platform; // 'darwin' | 'linux'
30
+
31
+ function hasBin(name) {
32
+ try {
33
+ execSync(`command -v ${name}`, { stdio: 'ignore' });
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ function tryInstall(name, { brew, apt, npm: npmPkg } = {}) {
41
+ // Try brew on macOS
42
+ if (platform === 'darwin' && brew && hasBin('brew')) {
43
+ try {
44
+ console.log(` Installing ${name} via brew...`);
45
+ execSync(`brew install ${brew}`, { stdio: 'inherit' });
46
+ return true;
47
+ } catch { return false; }
48
+ }
49
+ // Try apt on Linux
50
+ if (platform === 'linux' && apt && hasBin('apt-get')) {
51
+ try {
52
+ console.log(` Installing ${name} via apt...`);
53
+ execSync(`sudo apt-get install -y ${apt}`, { stdio: 'inherit' });
54
+ return true;
55
+ } catch { return false; }
56
+ }
57
+ // Try npm
58
+ if (npmPkg) {
59
+ try {
60
+ console.log(` Installing ${name} via npm...`);
61
+ execSync(`npm install -g ${npmPkg}`, { stdio: 'inherit' });
62
+ return true;
63
+ } catch { return false; }
64
+ }
65
+ return false;
66
+ }
67
+
68
+ // Dependencies: what ralph needs on the host for `ralph loop`
69
+ const deps = [
70
+ {
71
+ bin: 'jq',
72
+ label: 'jq (JSON processor)',
73
+ required: true,
74
+ install: { brew: 'jq', apt: 'jq' },
75
+ manual: 'brew install jq OR apt-get install jq',
76
+ },
77
+ {
78
+ bin: 'bc',
79
+ label: 'bc (calculator)',
80
+ required: true,
81
+ install: { brew: 'bc', apt: 'bc' },
82
+ manual: 'brew install bc OR apt-get install bc',
83
+ },
84
+ {
85
+ bin: 'claude',
86
+ label: 'Claude Code CLI',
87
+ required: true,
88
+ install: { npm: '@anthropic-ai/claude-code' },
89
+ manual: 'npm install -g @anthropic-ai/claude-code',
90
+ },
91
+ {
92
+ bin: 'gh',
93
+ label: 'GitHub CLI (for PR features)',
94
+ required: false,
95
+ install: { brew: 'gh', apt: 'gh' },
96
+ manual: 'brew install gh OR https://cli.github.com',
97
+ },
98
+ ];
99
+
100
+ console.log('');
101
+ console.log('Checking dependencies...');
102
+ console.log('');
103
+
104
+ const missing = [];
105
+
106
+ for (const dep of deps) {
107
+ if (hasBin(dep.bin)) {
108
+ console.log(` ✓ ${dep.label}`);
109
+ } else {
110
+ console.log(` ✗ ${dep.label} — not found, attempting install...`);
111
+ const installed = tryInstall(dep.bin, dep.install);
112
+ if (installed && hasBin(dep.bin)) {
113
+ console.log(` ✓ ${dep.label} — installed`);
114
+ } else {
115
+ missing.push(dep);
116
+ }
117
+ }
118
+ }
119
+
120
+ console.log('');
121
+
122
+ if (missing.length > 0) {
123
+ const required = missing.filter(d => d.required);
124
+ const optional = missing.filter(d => !d.required);
125
+
126
+ if (required.length > 0) {
127
+ console.log('⚠ Missing required dependencies (ralph loop will not work without these):');
128
+ console.log('');
129
+ for (const dep of required) {
130
+ console.log(` ${dep.label}`);
131
+ console.log(` → ${dep.manual}`);
132
+ }
133
+ console.log('');
134
+ }
135
+
136
+ if (optional.length > 0) {
137
+ console.log('ℹ Missing optional dependencies:');
138
+ console.log('');
139
+ for (const dep of optional) {
140
+ console.log(` ${dep.label}`);
141
+ console.log(` → ${dep.manual}`);
142
+ }
143
+ console.log('');
144
+ }
145
+ }
146
+
147
+ console.log('Ralph tool installed successfully!');
148
+ console.log('');
149
+ console.log('Usage:');
150
+ console.log(' ralph init - Initialize Ralph in current directory');
151
+ console.log(' ralph start - Start Ralph in Docker sandbox');
152
+ console.log(' ralph loop - Run Ralph loop directly');
153
+ console.log(' ralph status - Check task status');
154
+ console.log(' ralph --help - Show all commands');
155
+ console.log('');
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Ralph Review - PR Summary & Merge Automation
5
+ # Environment: PROJECT_RALPH_DIR
6
+
7
+ PROJECT_RALPH_DIR="${PROJECT_RALPH_DIR:?PROJECT_RALPH_DIR not set}"
8
+ PROMPT_FILE="${PROJECT_RALPH_DIR}/config/pr-review-prompt.md"
9
+
10
+ # Colors
11
+ RED='\033[0;31m'
12
+ GREEN='\033[0;32m'
13
+ YELLOW='\033[1;33m'
14
+ LIGHT_BLUE='\033[1;36m'
15
+ NC='\033[0m'
16
+
17
+ echo ""
18
+ echo -e "${LIGHT_BLUE}Ralph Review - PR Summary & Merge${NC}"
19
+ echo ""
20
+
21
+ # Verify prompt file exists
22
+ if [[ ! -f "$PROMPT_FILE" ]]; then
23
+ echo -e "${RED}Error: Prompt file not found at ${PROMPT_FILE}${NC}"
24
+ exit 1
25
+ fi
26
+
27
+ # 1. Get latest open PR
28
+ echo "[1/5] Checking for open PRs..."
29
+ PR_JSON=$(gh pr list --state open --limit 1 --json number,title,body)
30
+ PR_NUMBER=$(echo "$PR_JSON" | jq -r '.[0].number // empty')
31
+
32
+ if [[ -z "$PR_NUMBER" ]]; then
33
+ echo -e "${RED}No open PRs found${NC}"
34
+ exit 1
35
+ fi
36
+
37
+ PR_TITLE=$(echo "$PR_JSON" | jq -r '.[0].title')
38
+ PR_BODY=$(echo "$PR_JSON" | jq -r '.[0].body')
39
+ echo -e " ${GREEN}✓${NC} Found PR #${PR_NUMBER}: ${PR_TITLE}"
40
+
41
+ # 2. Get PR diff
42
+ echo "[2/5] Fetching PR diff..."
43
+ PR_DIFF=$(gh pr diff "$PR_NUMBER")
44
+ echo -e " ${GREEN}✓${NC} Diff fetched"
45
+
46
+ # 3. Generate summary via Claude
47
+ echo "[3/5] Generating PR summary via Claude..."
48
+ PR_CONTEXT="PR #${PR_NUMBER}: ${PR_TITLE}
49
+
50
+ ## PR Description
51
+ ${PR_BODY}
52
+
53
+ ## Diff
54
+ ${PR_DIFF}"
55
+
56
+ PROMPT=$(cat "$PROMPT_FILE")
57
+
58
+ # Call Claude and capture output
59
+ SUMMARY=$(echo "$PR_CONTEXT" | claude --dangerously-skip-permissions --print -p "$PROMPT" 2>&1) || {
60
+ echo -e "${RED}Claude failed to generate summary${NC}"
61
+ exit 1
62
+ }
63
+
64
+ echo ""
65
+ echo "==============================================================================="
66
+ echo -e "${LIGHT_BLUE}PR #${PR_NUMBER} Summary${NC}"
67
+ echo "==============================================================================="
68
+ echo ""
69
+ echo "$SUMMARY"
70
+ echo ""
71
+ echo "==============================================================================="
72
+ echo ""
73
+
74
+ # 4. Merge PR
75
+ echo "[4/5] Merging PR #${PR_NUMBER}..."
76
+ if ! gh pr merge "$PR_NUMBER" --squash --delete-branch; then
77
+ echo -e "${RED}Merge failed - check for conflicts${NC}"
78
+ exit 1
79
+ fi
80
+ echo -e " ${GREEN}✓${NC} PR merged and branch deleted"
81
+
82
+ # 5. Update local
83
+ echo "[5/5] Updating local main branch..."
84
+ git checkout main
85
+ git pull
86
+ echo -e " ${GREEN}✓${NC} Local main updated"
87
+
88
+ echo ""
89
+ echo -e "${GREEN}Review complete!${NC}"
90
+ echo ""
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ # Open interactive Docker sandbox environment
3
+ # Environment: RALPH_HOME, PROJECT_DIR
4
+
5
+ set -euo pipefail
6
+
7
+ RALPH_HOME="${RALPH_HOME:?RALPH_HOME not set}"
8
+ PROJECT_DIR="${PROJECT_DIR:?PROJECT_DIR not set}"
9
+
10
+ # Run interactive bash in Docker container
11
+ docker compose -f "${RALPH_HOME}/docker-compose.yaml" run --rm \
12
+ -v "${PROJECT_DIR}:/workspace:rw" \
13
+ -e PROJECT_DIR=/workspace \
14
+ -e PROJECT_RALPH_DIR=/workspace/.ralph \
15
+ ai bash
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bash
2
+ # Combined script to start sandbox and run Ralph loop inside container
3
+ # Environment: RALPH_HOME, PROJECT_DIR, PROJECT_RALPH_DIR
4
+
5
+ set -euo pipefail
6
+
7
+ MAX_ITERS="${1:-6}"
8
+ MODEL="${2:-}"
9
+
10
+ RALPH_HOME="${RALPH_HOME:?RALPH_HOME not set}"
11
+ PROJECT_DIR="${PROJECT_DIR:?PROJECT_DIR not set}"
12
+
13
+ # Build ralph command with optional model parameter
14
+ RALPH_CMD="ralph loop"
15
+ [[ -n "$MODEL" ]] && RALPH_CMD="$RALPH_CMD --model $MODEL"
16
+ RALPH_CMD="$RALPH_CMD --max ${MAX_ITERS}"
17
+ [[ "${RALPH_LOCAL:-}" == "1" ]] && RALPH_CMD="$RALPH_CMD --local"
18
+
19
+ # Run the ralph loop command inside the Docker container
20
+ # Mount the project directory to /workspace
21
+ docker compose -f "${RALPH_HOME}/docker-compose.yaml" run --rm \
22
+ -v "${PROJECT_DIR}:/workspace:rw" \
23
+ -e PROJECT_DIR=/workspace \
24
+ -e PROJECT_RALPH_DIR=/workspace/.ralph \
25
+ ai bash -c "${RALPH_CMD}"
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Ralph Status - Concise status for agent consumption
5
+ # Environment: PROJECT_RALPH_DIR
6
+
7
+ PROJECT_RALPH_DIR="${PROJECT_RALPH_DIR:?PROJECT_RALPH_DIR not set}"
8
+ PRD_FILE="${PROJECT_RALPH_DIR}/tasks/prd.json"
9
+
10
+ # Color support (only if TTY)
11
+ if [[ -t 1 ]]; then
12
+ BOLD="\033[1m"
13
+ CYAN="\033[36m"
14
+ GREEN="\033[32m"
15
+ YELLOW="\033[33m"
16
+ BLUE="\033[34m"
17
+ DIM="\033[2m"
18
+ RESET="\033[0m"
19
+ else
20
+ BOLD=""
21
+ CYAN=""
22
+ GREEN=""
23
+ YELLOW=""
24
+ BLUE=""
25
+ DIM=""
26
+ RESET=""
27
+ fi
28
+
29
+ # Check if prd.json exists
30
+ if [[ ! -f "$PRD_FILE" ]]; then
31
+ echo "ERROR: prd.json not found"
32
+ exit 1
33
+ fi
34
+
35
+ # Find current feature (first incomplete feature)
36
+ CURRENT_FEATURE_ID=$(jq -r '.features[] | select(.feature_completed == false) | .id' "$PRD_FILE" 2>/dev/null | head -1)
37
+
38
+ if [[ -z "$CURRENT_FEATURE_ID" ]]; then
39
+ echo "All features completed!"
40
+ echo ""
41
+ jq -r '.features[] | " [DONE] \(.id): \(.name)"' "$PRD_FILE" 2>/dev/null
42
+ exit 0
43
+ fi
44
+
45
+ # Get current feature details
46
+ CURRENT_FEATURE_NAME=$(jq -r --arg id "$CURRENT_FEATURE_ID" '.features[] | select(.id == $id) | .name' "$PRD_FILE" 2>/dev/null)
47
+ CURRENT_FEATURE_NUM=$(jq -r --arg id "$CURRENT_FEATURE_ID" '[.features[].id] | to_entries | .[] | select(.value == $id) | .key + 1' "$PRD_FILE" 2>/dev/null)
48
+
49
+ # Count tasks in current feature
50
+ TOTAL_TASKS=$(jq --arg id "$CURRENT_FEATURE_ID" '.features[] | select(.id == $id) | .tasks | length' "$PRD_FILE" 2>/dev/null)
51
+ COMPLETED_TASKS=$(jq --arg id "$CURRENT_FEATURE_ID" '.features[] | select(.id == $id) | [.tasks[] | select(.completed == true)] | length' "$PRD_FILE" 2>/dev/null)
52
+ PENDING_TASKS=$((TOTAL_TASKS - COMPLETED_TASKS))
53
+
54
+ # Count total features
55
+ TOTAL_FEATURES=$(jq '.features | length' "$PRD_FILE" 2>/dev/null)
56
+ COMPLETED_FEATURES=$(jq '[.features[] | select(.feature_completed == true)] | length' "$PRD_FILE" 2>/dev/null)
57
+
58
+ # Calculate border width
59
+ header="Current Feature: $CURRENT_FEATURE_NAME (Feature $CURRENT_FEATURE_NUM of $TOTAL_FEATURES)"
60
+ header_len=${#header}
61
+ border_len=$((header_len > 60 ? header_len + 4 : 64))
62
+
63
+ # Build border
64
+ border="╔"
65
+ for ((i=0; i<border_len-2; i++)); do border+="═"; done
66
+ border+="╗"
67
+
68
+ bottom_border="╚"
69
+ for ((i=0; i<border_len-2; i++)); do bottom_border+="═"; done
70
+ bottom_border+="╝"
71
+
72
+ # Human-readable output
73
+ printf "%b\n" "${BOLD}${CYAN}${border}${RESET}"
74
+ printf "%b\n" "${BOLD}${CYAN}║${RESET} ${BOLD}Current Feature:${RESET} ${YELLOW}${CURRENT_FEATURE_NAME}${RESET} ${DIM}(Feature ${CURRENT_FEATURE_NUM} of ${TOTAL_FEATURES})${RESET}"
75
+ printf "%b\n" "${BOLD}${CYAN}║${RESET} ${BOLD}Status:${RESET} ${GREEN}${COMPLETED_TASKS}${RESET}/${TOTAL_TASKS} tasks completed, ${YELLOW}${PENDING_TASKS}${RESET} pending"
76
+ printf "%b\n" "${BOLD}${CYAN}${bottom_border}${RESET}"
77
+ echo ""
78
+
79
+ # List all tasks in current feature
80
+ printf "%b\n" "${BOLD}Tasks:${RESET}"
81
+ jq -r --arg id "$CURRENT_FEATURE_ID" '.features[] | select(.id == $id) | .tasks[] | " \(if .completed then "[DONE]" else "[TODO]" end) \(.id): \(.name)"' "$PRD_FILE" 2>/dev/null | while IFS= read -r line; do
82
+ if [[ "$line" =~ ^\s*\[DONE\] ]]; then
83
+ printf "%b\n" "${DIM}${line}${RESET}"
84
+ else
85
+ printf "%b\n" "${BOLD}${line}${RESET}"
86
+ fi
87
+ done
88
+
89
+ # Show next incomplete task details
90
+ echo ""
91
+ NEXT_TASK=$(jq -r --arg id "$CURRENT_FEATURE_ID" '.features[] | select(.id == $id) | .tasks[] | select(.completed == false) | .id' "$PRD_FILE" 2>/dev/null | head -1)
92
+ if [[ -n "$NEXT_TASK" ]]; then
93
+ printf "%b\n" "${BOLD}${BLUE}Next task: ${NEXT_TASK}${RESET}"
94
+ jq -r --arg fid "$CURRENT_FEATURE_ID" --arg tid "$NEXT_TASK" '.features[] | select(.id == $fid) | .tasks[] | select(.id == $tid) | " Name: \(.name)\n Spec: \(.specFile // "none")\n Requirements: \(.requirements | join(", "))"' "$PRD_FILE" 2>/dev/null
95
+ else
96
+ printf "%b\n" "${BOLD}${GREEN}All tasks in current feature completed - ready for feature completion!${RESET}"
97
+ fi
98
+
99
+ # Show feature overview
100
+ echo ""
101
+ printf "%b\n" "${BOLD}All Features:${RESET}"
102
+ jq -r '.features[] | " \(if .feature_completed then "[DONE]" else "[TODO]" end) \(.id): \(.name)"' "$PRD_FILE" 2>/dev/null | while IFS= read -r line; do
103
+ if [[ "$line" =~ ^\s*\[DONE\] ]]; then
104
+ printf "%b\n" "${DIM}${line}${RESET}"
105
+ else
106
+ printf "%b\n" "${BOLD}${line}${RESET}"
107
+ fi
108
+ done
@@ -0,0 +1,27 @@
1
+ # Ralph Agent Knowledge Base
2
+
3
+ This file contains persistent learnings and patterns discovered across Ralph iterations.
4
+ Each iteration should update this file with new patterns, gotchas, and architectural insights
5
+ only if something new and relevant has been discovered.
6
+
7
+ **Progress Management:**
8
+ - Within feature: progress.txt accumulates task summaries
9
+ - Between features: progress.txt archives to `logs/progress/{feature-name}-progress-{timestamp}.txt`
10
+ - AGENT.md persists across all features (never cleared)
11
+ - NOTE: Do not create explainer documents or other documentation unless specifically asked to.
12
+
13
+ ## CRITICAL: Code Reuse Philosophy
14
+
15
+ **NEVER ASSUME FUNCTIONALITY DOESN'T EXIST**
16
+ - Always search for existing implementations before creating new ones
17
+ - Check for utility functions that might already do what you need
18
+ - Look for similar patterns in other parts of the codebase
19
+ - Reuse existing services, stores, and API endpoints when possible
20
+
21
+ ## Known Patterns
22
+
23
+ <!-- Add project-specific patterns here as you discover them -->
24
+
25
+ ## Known Gotchas
26
+
27
+ <!-- Add project-specific gotchas here as you discover them -->
@@ -0,0 +1,115 @@
1
+ # Feature Spec: [Feature Name]
2
+
3
+ ## Goal
4
+
5
+ **What user-visible change should exist when done?**
6
+
7
+ [1-2 lines describing the user-facing outcome]
8
+
9
+ **Success Criteria:**
10
+ - [How you'll know it worked - specific requirement that must be met]
11
+
12
+
13
+ ## Non-goals
14
+
15
+ **Explicitly list what NOT to do (prevents scope creep):**
16
+ - [Out of scope item 1]
17
+ - [Out of scope item 2]
18
+
19
+ ---
20
+
21
+ ## Implementation Tasks
22
+
23
+ **Subtask 1: [Layer/Component Name]**
24
+ - **Requirements:**
25
+ - [Specific requirement 1]
26
+ - [Specific requirement 2]
27
+ - **Done when:** [Completion criteria]
28
+ - **Files:** [List of files to modify/create]
29
+
30
+ **Subtask 2: [Layer/Component Name]**
31
+ - **Requirements:**
32
+ - [Specific requirement 1]
33
+ - [Specific requirement 2]
34
+ - **Done when:** [Completion criteria]
35
+ - **Files:** [List of files to modify/create]
36
+
37
+ ---
38
+
39
+ ## Current State
40
+
41
+ **Where this lives:**
42
+ - Files/folders: `[path/to/relevant/files]`
43
+ - Related domains: `[domain1, domain2]`
44
+
45
+ **How it works today:**
46
+ - [Brief description of current behavior]
47
+
48
+ ---
49
+
50
+ ## Proposed Behavior
51
+
52
+ **Write as bullet rules, not prose:**
53
+
54
+ **Primary flow:**
55
+ - If [condition X], then [action Y]
56
+ - If [condition Z], then [action W]
57
+
58
+ **Edge cases:**
59
+ - When [edge case 1], [behavior]
60
+ - Error handling: [what happens on errors]
61
+
62
+ ---
63
+
64
+ ## UX / UI (if relevant)
65
+
66
+ **Screens/components affected:**
67
+ - `[component/path/file.tsx]` - [what changes]
68
+
69
+ **States to handle:**
70
+ - Empty state: [what shows when no data]
71
+ - Loading state: [what shows while loading]
72
+ - Error state: [what shows on error]
73
+ - Success state: [what shows on success]
74
+
75
+ ---
76
+
77
+ ## Data + APIs (if relevant)
78
+
79
+ **API endpoints:**
80
+ - `POST /api/[path]` - [description]
81
+ - Request: `{ [field]: [type], ... }`
82
+ - Response: `{ [field]: [type], ... }`
83
+
84
+ ---
85
+
86
+ ## Files to Touch
87
+
88
+ **Most likely files (Ralph should start here):**
89
+ - `path/to/file1.ts`
90
+ - `path/to/file2.tsx`
91
+
92
+ ---
93
+
94
+ ## Patterns and helpers
95
+
96
+ Reuse existing files where possible. Follow existing patterns in the project.
97
+
98
+ ---
99
+
100
+ ## Notes
101
+
102
+ **Additional context for Ralph:**
103
+ - [Any special considerations]
104
+ - [Dependencies on other features]
105
+ - [Known gotchas]
106
+
107
+ ---
108
+
109
+ ## How to Use This Template
110
+
111
+ 1. Fill in all sections with specific requirements
112
+ 2. Organize subtasks logically
113
+ 3. Be specific in requirements and completion criteria
114
+ 4. Omit sections that don't apply
115
+ 5. Update prd.json with the task linking to this spec file
@@ -0,0 +1,61 @@
1
+ # Ralph Plan Cleanup - REVIEW & CONSOLIDATION MODE
2
+
3
+ ## YOUR ROLE
4
+
5
+ You are reviewing and cleaning up the planning output from previous iterations. Your job is to ensure quality, remove redundancy, fix ordering, and verify complete vision coverage.
6
+
7
+ ## Instructions
8
+
9
+ ### Step 1: Read Everything
10
+
11
+ Read ALL of these files:
12
+ 1. `.ralph/config/vision.md` — The project vision
13
+ 2. `.ralph/config/AGENT.md` — Accumulated learnings
14
+ 3. `.ralph/tasks/prd.json` — All planned features and tasks
15
+ 4. ALL spec files in `.ralph/tasks/1_new_tasks/` — Read every spec file referenced in prd.json
16
+ 5. `.ralph/tasks/progress.txt` — Planning history
17
+
18
+ ### Step 2: Audit Against Vision
19
+
20
+ For each section/area of the vision:
21
+ - Is there a corresponding feature in prd.json?
22
+ - Are the tasks specific enough for automated implementation?
23
+ - Do the spec files have actionable details (file paths, component names, behavior rules)?
24
+
25
+ Flag any gaps — areas of the vision not yet covered by features.
26
+
27
+ ### Step 3: Clean Up
28
+
29
+ Review all features and tasks for:
30
+ - **Duplicates**: Merge features/tasks that overlap or cover the same ground
31
+ - **Irrelevant tasks**: Remove tasks that don't align with the current vision
32
+ - **Ordering issues**: Reorder features so dependencies come first (e.g., data models before UI)
33
+ - **Spec quality**: Improve specs that are too vague for implementation — add specifics
34
+ - **Task granularity**: Split tasks that are too large (multi-day work), merge tasks that are too small (trivial one-liners)
35
+
36
+ Make changes directly to prd.json and spec files.
37
+
38
+ ### Step 4: Update Progress
39
+
40
+ Append to `.ralph/tasks/progress.txt`:
41
+ - What was cleaned up, reordered, or removed
42
+ - Any gaps identified that still need planning
43
+
44
+ Update `.ralph/config/AGENT.md` with any new learnings.
45
+
46
+ ### Step 5: Completion Check
47
+
48
+ After cleanup, assess vision coverage:
49
+ - If ALL areas of the vision have detailed features, well-ordered tasks, and actionable spec files: output `<promise>COMPLETE</promise>`
50
+ - If there are still gaps: output "Cleanup complete. Gaps remaining: [list areas]. Exiting for next iteration."
51
+
52
+ EXIT IMMEDIATELY after this assessment.
53
+
54
+ ## CLEANUP RULES
55
+
56
+ 1. **BE RUTHLESS**: Remove tasks that don't serve the vision
57
+ 2. **PRESERVE INTENT**: When merging/reordering, keep the original requirements intact
58
+ 3. **NO NEW FEATURES**: Only clean up existing plans — creating new features is for the plan prompt
59
+ 4. **FIX ORDERING**: Features should flow naturally for sequential implementation
60
+ 5. **IMPROVE SPECS**: If a spec is too vague, add details from your codebase analysis
61
+ 6. **ALWAYS EXIT**: After cleanup, exit for fresh iteration