ralph.rb 1.2.4355354345 → 2.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/gem-push.yml +2 -2
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +53 -0
  5. data/lib/ralph/cli.rb +67 -186
  6. data/lib/ralph/display.rb +105 -0
  7. data/lib/ralph/events.rb +117 -0
  8. data/lib/ralph/loop.rb +113 -170
  9. data/lib/ralph/metrics.rb +88 -0
  10. data/lib/ralph/opencode.rb +66 -0
  11. data/lib/ralph/version.rb +1 -1
  12. data/lib/ralph.rb +0 -3
  13. data/plans/00-complete-implementation.md +120 -0
  14. data/plans/01-cli-implementation.md +53 -0
  15. data/plans/02-loop-implementation.md +78 -0
  16. data/plans/03-agents-implementation.md +76 -0
  17. data/plans/04-metrics-implementation.md +98 -0
  18. data/plans/README.md +63 -0
  19. data/specs/README.md +4 -15
  20. data/specs/__templates__/API_TEMPLATE.md +0 -0
  21. data/specs/__templates__/AUTOMATION_ACTION_TEMPLATE.md +0 -0
  22. data/specs/__templates__/AUTOMATION_TRIGGER_TEMPLATE.md +0 -0
  23. data/specs/__templates__/CONTROLLER_TEMPLATE.md +32 -0
  24. data/specs/__templates__/INTEGRATION_TEMPLATE.md +0 -0
  25. data/specs/__templates__/MODEL_TEMPLATE.md +0 -0
  26. data/specs/agents.md +426 -120
  27. data/specs/cli.md +11 -218
  28. data/specs/lib/todo_item.rb +144 -0
  29. data/specs/log +15 -0
  30. data/specs/loop.md +42 -0
  31. data/specs/metrics.md +51 -0
  32. metadata +23 -39
  33. data/lib/ralph/agents/base.rb +0 -132
  34. data/lib/ralph/agents/claude_code.rb +0 -24
  35. data/lib/ralph/agents/codex.rb +0 -25
  36. data/lib/ralph/agents/open_code.rb +0 -30
  37. data/lib/ralph/agents.rb +0 -24
  38. data/lib/ralph/config.rb +0 -40
  39. data/lib/ralph/git/file_snapshot.rb +0 -60
  40. data/lib/ralph/helpers.rb +0 -76
  41. data/lib/ralph/iteration.rb +0 -220
  42. data/lib/ralph/output/active_loop_error.rb +0 -13
  43. data/lib/ralph/output/banner.rb +0 -29
  44. data/lib/ralph/output/completion_deferred.rb +0 -12
  45. data/lib/ralph/output/completion_detected.rb +0 -17
  46. data/lib/ralph/output/config_summary.rb +0 -31
  47. data/lib/ralph/output/context_consumed.rb +0 -11
  48. data/lib/ralph/output/iteration.rb +0 -45
  49. data/lib/ralph/output/max_iterations_reached.rb +0 -16
  50. data/lib/ralph/output/no_plugin_warning.rb +0 -14
  51. data/lib/ralph/output/nonzero_exit_warning.rb +0 -11
  52. data/lib/ralph/output/plugin_error.rb +0 -12
  53. data/lib/ralph/output/status.rb +0 -176
  54. data/lib/ralph/output/struggle_warning.rb +0 -18
  55. data/lib/ralph/output/task_completion.rb +0 -12
  56. data/lib/ralph/output/tasks_file_created.rb +0 -11
  57. data/lib/ralph/prompt_template.rb +0 -183
  58. data/lib/ralph/storage/context.rb +0 -58
  59. data/lib/ralph/storage/history.rb +0 -117
  60. data/lib/ralph/storage/state.rb +0 -178
  61. data/lib/ralph/storage/tasks.rb +0 -244
  62. data/lib/ralph/threads/heartbeat.rb +0 -44
  63. data/lib/ralph/threads/stream_reader.rb +0 -50
  64. data/original/bin/ralph.js +0 -13
  65. data/original/ralph.ts +0 -1706
  66. data/specs/iteration.md +0 -173
  67. data/specs/output.md +0 -104
  68. data/specs/storage/local-data-structure.md +0 -246
  69. data/specs/tasks.md +0 -295
data/specs/tasks.md DELETED
@@ -1,295 +0,0 @@
1
- # Task Management Specification
2
-
3
- Task management lets Ralph break large projects into discrete units of work tracked in a markdown file. Instead of sending the entire project goal every iteration, Ralph focuses the agent on one task at a time, advancing through the list as each task is completed. This reduces per-iteration context size, improves agent focus, and lowers cost.
4
-
5
- ## File Format
6
-
7
- Tasks are stored in `.ralph/ralph-tasks.md`. The file uses markdown checkbox syntax with three status characters.
8
-
9
- ### Status Characters
10
-
11
- | Character | Symbol | Status | Meaning |
12
- |-----------|--------|--------|---------|
13
- | ` ` (space) | `[ ]` | `:todo` | Not started |
14
- | `/` | `[/]` | `:in_progress` | Currently being worked on |
15
- | `x` | `[x]` | `:complete` | Finished |
16
-
17
- ### Structure
18
-
19
- ```markdown
20
- # Ralph Tasks
21
-
22
- - [ ] First task description
23
- - [/] Second task (in progress)
24
- - [ ] Subtask A
25
- - [x] Subtask B
26
- - [x] Third task (complete)
27
- ```
28
-
29
- **Rules:**
30
-
31
- - Top-level tasks start at column 0 with `- [<status>] <text>`.
32
- - Subtasks are indented with two spaces followed by the same `- [<status>] <text>` pattern.
33
- - Subtasks are **single-level only** — no nesting subtasks under subtasks.
34
- - The `# Ralph Tasks` header is written on save but not required for parsing.
35
- - Lines that do not match the task pattern are ignored during parsing.
36
-
37
- ### Round-Trip Preservation
38
-
39
- Each `Task` stores its `original_line` (the raw line from disk). This is used for diagnostics and debugging. On save, the canonical `- [<status>] <text>` format is always written, so whitespace and formatting are normalized.
40
-
41
- ## Data Models
42
-
43
- ### `Ralph::Storage::Task`
44
-
45
- Represents a single task (top-level or subtask).
46
-
47
- | Attribute | Type | Description |
48
- |-----------|------|-------------|
49
- | `text` | `String` | Task description text |
50
- | `status` | `Symbol` | One of `:todo`, `:in_progress`, `:complete` |
51
- | `subtasks` | `Array<Task>` | Child tasks (empty for subtasks themselves) |
52
- | `original_line` | `String` or `nil` | Raw line from disk, for diagnostics |
53
-
54
- **Status query methods:**
55
-
56
- - `todo?` — true when status is `:todo`
57
- - `in_progress?` — true when status is `:in_progress`
58
- - `complete?` — true when status is `:complete`
59
-
60
- **Status mutation methods:**
61
-
62
- - `mark_todo!` — sets status to `:todo`
63
- - `mark_in_progress!` — sets status to `:in_progress`
64
- - `mark_complete!` — sets status to `:complete`
65
- - `toggle_status` — cycles `:todo` → `:in_progress` → `:complete` → `:todo`
66
-
67
- **Serialization:**
68
-
69
- `to_s` returns the canonical line: `- [<status_char>] <text>`
70
-
71
- ### `Ralph::Storage::TasksCollection`
72
-
73
- An `Enumerable` collection of top-level `Task` objects.
74
-
75
- | Method | Signature | Description |
76
- |--------|-----------|-------------|
77
- | `each` | `(&block)` | Yields each top-level task |
78
- | `empty?` | `-> Boolean` | True when collection has no tasks |
79
- | `length` | `-> Integer` | Number of top-level tasks |
80
- | `any?` | `-> Boolean` | True when collection has at least one task |
81
- | `add` | `(Task) -> self` | Appends a task to the collection |
82
- | `remove_at` | `(Integer) -> Task` | Removes task at 1-based index; raises `IndexError` if out of range |
83
- | `current` | `-> Task or nil` | First task with `:in_progress` status |
84
- | `next` | `-> Task or nil` | First task with `:todo` status |
85
- | `all_complete?` | `-> Boolean` | True when non-empty and every task is `:complete` |
86
-
87
- **Class methods:**
88
-
89
- - `TasksCollection.parse(content)` — Parses a markdown string into a `TasksCollection`. Handles top-level tasks and single-level subtasks. Returns a new collection (empty if no tasks found).
90
-
91
- **Display:**
92
-
93
- - `display_with_indices` — Prints a numbered list of tasks with status icons to stdout. Used by the `--list-tasks` subcommand.
94
-
95
- **Status icons** (for display only):
96
-
97
- | Status | Icon |
98
- |--------|------|
99
- | `:complete` | `✅` |
100
- | `:in_progress` | `🔄` |
101
- | `:todo` | `⏸️` |
102
-
103
- ## Storage
104
-
105
- ### `Ralph::Storage::Tasks`
106
-
107
- Manages the task file on disk. Always instantiate with `Tasks.new`.
108
-
109
- | Method | Signature | Description |
110
- |--------|-----------|-------------|
111
- | `load_tasks` | `-> TasksCollection or nil` | Reads and parses `.ralph/ralph-tasks.md`. Returns `nil` if the file does not exist or cannot be parsed. |
112
- | `save_tasks` | `(TasksCollection) -> void` | Writes the collection to `.ralph/ralph-tasks.md` with the `# Ralph Tasks` header. Writes top-level tasks and their subtasks. |
113
- | `clear_tasks` | `-> void` | Deletes the tasks file if it exists. Silently ignores errors. |
114
- | `tasks_exist?` | `-> Boolean` | True when `.ralph/ralph-tasks.md` exists on disk. |
115
- | `add_task` | `(String) -> Task` | Loads existing tasks (or creates a new collection), appends a new `:todo` task, saves, and returns the new task. |
116
- | `remove_task` | `(Integer) -> Task` | Removes the task at the given 1-based index. Raises `RuntimeError` if no tasks file exists. Raises `IndexError` if the index is out of range. |
117
- | `initialize_tasks_file` | `-> String` | Creates a starter tasks file with a header and usage hint. Returns the file path. |
118
-
119
- **Class methods:**
120
-
121
- - `Tasks.dir` — Returns the `.ralph/` directory path (relative to `Dir.pwd`).
122
- - `Tasks.path` — Returns the full path to `ralph-tasks.md`.
123
-
124
- **Directory management:** The constructor creates `.ralph/` if it does not exist.
125
-
126
- ## Task Lifecycle in the Loop
127
-
128
- When `--tasks` (or `-t`) is enabled, the loop coordinates task progression across iterations.
129
-
130
- ### Task Selection
131
-
132
- Each iteration, the prompt builder determines the current focus:
133
-
134
- 1. **Find in-progress task:** Look for the first task with status `[/]` (`:in_progress`). If found, this is the current task.
135
- 2. **Find next todo task:** If no in-progress task, look for the first task with status `[ ]` (`:todo`). This becomes the next task to start.
136
- 3. **All complete:** If every task is `[x]` (`:complete`), instruct the agent to output the completion promise.
137
- 4. **No tasks:** If the collection is empty, instruct the agent to add tasks.
138
-
139
- ### Status Transitions
140
-
141
- The agent is responsible for updating task statuses in `.ralph/ralph-tasks.md` directly. The prompt instructs the agent to follow this workflow:
142
-
143
- ```
144
- [ ] todo → [/] in progress → [x] complete
145
- ```
146
-
147
- 1. Before starting work: mark the task as `[/]` in the file.
148
- 2. After verifying the task is done: mark it as `[x]` in the file.
149
- 3. Output `<promise>READY_FOR_NEXT_TASK</promise>` to signal task completion.
150
-
151
- Ralph does not programmatically modify task statuses between iterations. The agent owns the file.
152
-
153
- ### Task Promise Detection
154
-
155
- When the loop detects `<promise>READY_FOR_NEXT_TASK</promise>` (or the value of `--task-promise`) in agent output:
156
-
157
- - The loop prints a task completion message via `Output::TaskCompletion`.
158
- - The next iteration's prompt will pick up the updated task file, find the next `[ ]` task, and continue.
159
-
160
- The task promise does **not** stop the loop. It is informational — the loop continues to the next iteration.
161
-
162
- ### Completion Promise in Tasks Mode
163
-
164
- The completion promise (`<promise>COMPLETE</promise>` by default) is only accepted when the agent has signaled that all tasks are done. The prompt instructs the agent to only output the completion promise when every task in the file is `[x]`.
165
-
166
- ### Tasks File Creation
167
-
168
- When `--tasks` is enabled and no tasks file exists:
169
-
170
- - The prompt builder includes instructions telling the agent to create the file or use `ralph --add-task`.
171
- - Alternatively, the user can pre-populate the file with `ralph --add-task "description"` before starting the loop.
172
- - `initialize_tasks_file` creates a starter file with a header and usage hint.
173
-
174
- ## Prompt Integration
175
-
176
- The `Prompt` class builds task-aware prompts when `state.tasks_mode` is true.
177
-
178
- ### Task-Mode Prompt Structure
179
-
180
- ```
181
- # Ralph Wiggum Loop - Iteration N
182
-
183
- You are in an iterative development loop working through a task list.
184
-
185
- [Additional Context section, if present]
186
-
187
- ## TASKS MODE: Working through task list
188
-
189
- Current tasks from .ralph/ralph-tasks.md:
190
- ```markdown
191
- [full contents of ralph-tasks.md]
192
- ```
193
-
194
- [Task instructions — one of four states]
195
-
196
- ### Task Workflow
197
- 1. Find any task marked [/] (in progress). If none, pick the first [ ] task.
198
- 2. Mark the task as [/] in ralph-tasks.md before starting.
199
- 3. Complete the task.
200
- 4. Mark as [x] when verified complete.
201
- 5. Output <promise>READY_FOR_NEXT_TASK</promise> to move to the next task.
202
- 6. Only output <promise>COMPLETE</promise> when ALL tasks are [x].
203
-
204
- ---
205
-
206
- ## Your Main Goal
207
-
208
- [user's prompt text]
209
-
210
- ## Critical Rules
211
-
212
- [rules about one task at a time, promise usage, honesty]
213
-
214
- ## Current Iteration: N / M (min: K)
215
-
216
- Tasks Mode: ENABLED - Work on one task at a time from ralph-tasks.md
217
- ```
218
-
219
- ### Four Task Instruction States
220
-
221
- The prompt builder selects one instruction block based on the current task state:
222
-
223
- | State | Condition | Instruction |
224
- |-------|-----------|-------------|
225
- | Current task exists | `tasks.current` returns a task | Focus on completing the in-progress task. When done, mark as `[x]` and output the task promise. |
226
- | Next task available | No in-progress task, `tasks.next` returns a task | Mark the next task as `[/]` before starting. When done, mark as `[x]` and output the task promise. |
227
- | All tasks complete | `tasks.all_complete?` is true | All tasks done. Output the completion promise to finish. |
228
- | No tasks found | Collection is empty | Add tasks to the file or use `ralph --add-task`. |
229
-
230
- ### Error State
231
-
232
- If the tasks file cannot be read (parse error, permissions, etc.), the prompt builder falls back to a short error message:
233
-
234
- ```
235
- ## TASKS MODE: Error reading tasks file
236
-
237
- Unable to read .ralph/ralph-tasks.md
238
- ```
239
-
240
- ## CLI Subcommands
241
-
242
- These subcommands are fire-and-exit — they perform their action and exit without starting the loop. See also [cli.md](./cli.md) for the full CLI specification.
243
-
244
- ### `--list-tasks`
245
-
246
- Loads and displays tasks from `.ralph/ralph-tasks.md` with numbered indices and status icons.
247
-
248
- - If no tasks file exists, prints a message and exits.
249
- - Uses `TasksCollection#display_with_indices`.
250
-
251
- ### `--add-task DESC`
252
-
253
- Appends a new `- [ ] DESC` task to the file.
254
-
255
- - Creates the file with a `# Ralph Tasks` header if it does not exist.
256
- - Requires a non-empty description argument.
257
-
258
- ### `--remove-task N`
259
-
260
- Removes the task at 1-based index N, including its subtasks.
261
-
262
- - Requires an integer argument.
263
- - Validates the index is in range `1..task_count`.
264
- - Raises if no tasks file exists.
265
-
266
- ## Consumers
267
-
268
- | Consumer | What it uses | How |
269
- |----------|-------------|-----|
270
- | `Loop#run` | `check_completion` with `task_promise` | Detects `<promise>READY_FOR_NEXT_TASK</promise>` in agent output and prints task completion message |
271
- | `Prompt#task_mode_prompt` | `Storage::Tasks`, `TasksCollection.parse` | Reads task file, determines current/next task, builds task-aware prompt |
272
- | `Prompt#build_tasks_section` | `TasksCollection#current`, `#next`, `#all_complete?` | Selects the appropriate task instruction block |
273
- | `CLI` | `Storage::Tasks#add_task`, `#remove_task`, `#load_tasks` | Implements `--add-task`, `--remove-task`, `--list-tasks` subcommands |
274
- | `Output::Status` | `Storage::Tasks#load_tasks` | Displays task progress in the `--status` dashboard |
275
- | `Output::TaskCompletion` | — | Prints task completion message when task promise is detected |
276
- | `Output::TasksFileCreated` | — | Prints confirmation when the tasks file is initialized |
277
-
278
- ## Testing Strategy
279
-
280
- ### Unit Tests
281
-
282
- - **Parsing:** Test `TasksCollection.parse` with:
283
- - Empty string, header-only, single task, multiple tasks, subtasks, mixed statuses
284
- - Lines that do not match the task pattern (ignored)
285
- - Malformed status characters (treated as todo)
286
- - **Models:** Test `Task` status queries, mutations, `toggle_status`, `to_s`
287
- - **Collection:** Test `add`, `remove_at` (valid index, out of range), `current`, `next`, `all_complete?`, `empty?`
288
- - **Storage:** Test `load_tasks`, `save_tasks` round-trip, `add_task`, `remove_task`, `clear_tasks`, `tasks_exist?`, `initialize_tasks_file`
289
-
290
- ### Integration Tests
291
-
292
- - **Loop integration:** Test that task promise detection in agent output triggers `Output::TaskCompletion` and continues the loop
293
- - **Prompt integration:** Test that the prompt builder includes the correct task instructions for each of the four task states
294
- - **CLI subcommands:** Test `--list-tasks`, `--add-task`, `--remove-task` end-to-end with a real tasks file
295
- - **File creation:** Test that `--tasks` mode works when no tasks file exists (agent or user creates it)