spec-and-loop 2.0.0 → 2.0.1

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/QUICKSTART.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Quick Start Guide
2
2
 
3
+ > **Version Compatibility:** OpenSpec 1.2.0 | spec-and-loop 2.0.0
4
+
3
5
  Get up and running with **spec-and-loop** in 5 minutes!
4
6
 
5
7
  ## Prerequisites
@@ -7,8 +9,8 @@ Get up and running with **spec-and-loop** in 5 minutes!
7
9
  Install these tools (one-time setup):
8
10
 
9
11
  ```bash
10
- # 1. Install openspec (OpenSpec CLI)
11
- npm install -g @fission-ai/openspec@latest
12
+ # 1. Install openspec (OpenSpec CLI) - pinned to version 1.2.0
13
+ npm install -g @fission-ai/openspec@1.2.0
12
14
 
13
15
  # 2. Install opencode (agentic coding assistant)
14
16
  npm install -g opencode-ai
@@ -24,7 +26,8 @@ brew install jq
24
26
  git init
25
27
  ```
26
28
 
27
- > **Note:** No external `ralph` CLI needed `spec-and-loop` includes its own internal
29
+ > **Note:** This guide is for OpenSpec 1.2.0 and spec-and-loop 2.0.0.
30
+ > No external `ralph` CLI needed — `spec-and-loop` includes its own internal
28
31
  > mini Ralph loop engine. Just install `opencode` and you're ready to go. The
29
32
  > runtime prompt is self-contained and does not depend on editor-specific slash
30
33
  > commands or local-only skills.
@@ -32,7 +35,7 @@ git init
32
35
  ## Installation
33
36
 
34
37
  ```bash
35
- npm install -g spec-and-loop
38
+ npm install -g spec-and-loop@2.0.0
36
39
  ```
37
40
 
38
41
  ## Quick Demo (5 Minutes)
@@ -47,10 +50,13 @@ git init
47
50
  openspec init
48
51
 
49
52
  # 3. Create a new change
50
- openspec new add-hello-world
53
+ openspec new change add-hello-world
51
54
 
52
- # 4. Fast-forward through artifact creation
53
- openspec ff
55
+ # 4. Review and complete the OpenSpec artifacts
56
+ # (openspec/changes/add-hello-world/proposal.md)
57
+ # (openspec/changes/add-hello-world/design.md)
58
+ # (openspec/changes/add-hello-world/specs/*/spec.md)
59
+ # (openspec/changes/add-hello-world/tasks.md)
54
60
 
55
61
  # 5. Run the ralph loop (executes tasks with opencode)
56
62
  ralph-run --change add-hello-world
@@ -101,12 +107,27 @@ ralph-run --status
101
107
  ### OpenSpec Commands
102
108
 
103
109
  ```bash
104
- openspec init # Initialize in current directory
105
- openspec new <name> # Start a new change
106
- openspec continue <name> # Continue working on change
107
- openspec ff <name> # Fast-forward artifact creation
108
- openspec apply <name> # Apply change (implementation)
109
- openspec archive <name> # Archive completed change
110
+ # Core workflow
111
+ openspec init # Initialize OpenSpec in current directory
112
+ openspec new change <name> # Create a new change directory
113
+ openspec show <item-name> # Show a change or spec in detail
114
+ openspec archive <change-name> # Archive a completed change
115
+
116
+ # Information and status
117
+ openspec list # List all active changes (use --specs to list specs)
118
+ openspec status --change <name> # Display artifact completion status for a change
119
+ openspec validate <item-name> # Validate changes and specs
120
+
121
+ # View and manage
122
+ openspec view # Display interactive dashboard of specs and changes
123
+ openspec update # Update OpenSpec instruction files
124
+ openspec config # View and modify global OpenSpec configuration
125
+
126
+ # Advanced
127
+ openspec spec # Manage and view OpenSpec specifications
128
+ openspec instructions # Output enriched instructions for creating artifacts
129
+ openspec templates # Show resolved template paths for artifacts
130
+ openspec schemas # List available workflow schemas
110
131
  ```
111
132
 
112
133
  ### Ralph Loop Commands
@@ -133,7 +154,7 @@ git init
133
154
  openspec init
134
155
 
135
156
  # 2. Create a feature
136
- openspec new user-authentication
157
+ openspec new change user-authentication
137
158
 
138
159
  # 3. Go through the workflow
139
160
  # - Create proposal: Why add auth?
@@ -141,77 +162,257 @@ openspec new user-authentication
141
162
  # - Create design: Use JWT, store hashed passwords
142
163
  # - Create tasks: 15 checkboxes for implementation
143
164
 
144
- # 4. Fast-forward to create all artifacts
145
- openspec ff user-authentication
146
-
147
- # 5. Execute the implementation
165
+ # 4. Execute the implementation
148
166
  ralph-run --change user-authentication
149
167
 
150
- # 6. Watch the magic happen!
168
+ # 5. Watch the magic happen!
151
169
  # [INFO] Found 15 tasks to execute
152
170
  # [INFO] Executing task 1/15: Create User model
153
171
  # [INFO] Executing task 2/15: Implement password hashing
154
172
  # ...
155
173
 
156
- # 7. Add context mid-run if needed (from another terminal)
174
+ # 6. Add context mid-run if needed (from another terminal)
157
175
  ralph-run --add-context "Prefer bcrypt over argon2 for password hashing"
158
176
 
159
- # 8. Check status
177
+ # 7. Check status
160
178
  ralph-run --status
161
179
 
162
- # 9. Verify the implementation
180
+ # 8. Verify the implementation
163
181
  git log --oneline # 15 commits, one per task
164
182
  git diff HEAD~15 # See full implementation
165
183
  ```
166
184
 
167
185
  ## Troubleshooting
168
186
 
169
- ### "openspec CLI not found" or "opencode CLI not found"
187
+ ### "openspec: command not found"
188
+
189
+ **Problem:** OpenSpec CLI is not installed or not in PATH
190
+
191
+ **Solution:**
192
+ ```bash
193
+ # Install with pinned version (recommended)
194
+ npm install -g @fission-ai/openspec@1.2.0
195
+
196
+ # Verify installation
197
+ openspec --version
198
+
199
+ # If command still not found, add npm global bin to PATH
200
+ export PATH="$PATH:$(npm root -g)/.bin"
201
+ ```
202
+
203
+ ### "opencode: command not found"
170
204
 
205
+ **Problem:** opencode CLI is not installed or not in PATH
206
+
207
+ **Solution:**
171
208
  ```bash
172
- npm install -g @fission-ai/openspec@latest opencode-ai
209
+ # Install opencode
210
+ npm install -g opencode-ai
211
+
212
+ # Verify installation
213
+ opencode --version
214
+
215
+ # If command still not found, add npm global bin to PATH
216
+ export PATH="$PATH:$(npm root -g)/.bin"
173
217
  ```
174
218
 
175
219
  ### "jq CLI not found"
176
220
 
221
+ **Problem:** jq (JSON processor) is not installed
222
+
223
+ **Solution:**
177
224
  ```bash
178
225
  # Ubuntu/Debian
179
226
  sudo apt install jq
180
227
 
181
228
  # macOS
182
229
  brew install jq
230
+
231
+ # Verify installation
232
+ jq --version
183
233
  ```
184
234
 
185
235
  ### "Not a git repository"
186
236
 
237
+ **Problem:** You're not in a git repository
238
+
239
+ **Solution:**
187
240
  ```bash
241
+ # Initialize git in current directory
188
242
  git init
243
+
244
+ # Verify
245
+ git status
189
246
  ```
190
247
 
191
248
  ### "command not found: ralph-run"
192
249
 
193
- **Problem:** npm bin directory not in PATH
250
+ **Problem:** spec-and-loop npm bin directory not in PATH
194
251
 
195
252
  **Solution:**
196
253
  ```bash
197
- # Add to ~/.bashrc or ~/.zshrc
198
- export PATH="$PATH:$(npm root -g)/.bin"
254
+ # Add npm global bin directory to PATH
255
+ echo 'export PATH="$PATH:$(npm root -g)/.bin"' >> ~/.bashrc
256
+ # Or for zsh:
257
+ echo 'export PATH="$PATH:$(npm root -g)/.bin"' >> ~/.zshrc
199
258
 
200
259
  # Reload shell
201
260
  source ~/.bashrc
261
+ # or
262
+ source ~/.zshrc
263
+
264
+ # Verify
265
+ ralph-run --help
266
+ ```
267
+
268
+ ### "Internal mini Ralph runtime not found"
269
+
270
+ **Problem:** spec-and-loop installation is incomplete or node is missing
271
+
272
+ **Solution:**
273
+ ```bash
274
+ # Ensure spec-and-loop is properly installed
275
+ npm uninstall -g spec-and-loop
276
+ npm install -g spec-and-loop
277
+
278
+ # Ensure Node.js is installed (version 24.0.0 or higher)
279
+ node --version
280
+
281
+ # If node is not installed, install from https://nodejs.org
282
+ ```
283
+
284
+ ### "OpenSpec changes directory not found"
285
+
286
+ **Problem:** OpenSpec has not been initialized or no changes exist
287
+
288
+ **Solution:**
289
+ ```bash
290
+ # Initialize OpenSpec
291
+ openspec init
292
+
293
+ # Create a new change
294
+ openspec new change my-feature
295
+
296
+ # Verify directory exists
297
+ ls -la openspec/changes/
298
+ ```
299
+
300
+ ### "No changes found with tasks.md"
301
+
302
+ **Problem:** No OpenSpec changes with tasks files exist
303
+
304
+ **Solution:**
305
+ ```bash
306
+ # List available changes
307
+ openspec list
308
+
309
+ # Create a new change if needed
310
+ openspec new change my-new-feature
311
+
312
+ # Ensure tasks.md exists in your change directory
313
+ ls -la openspec/changes/my-new-feature/tasks.md
202
314
  ```
203
315
 
204
316
  ### "No tasks to execute"
205
317
 
206
- **Problem:** All tasks already complete (or tasks.md has no unchecked items)
318
+ **Problem:** All tasks in tasks.md are already marked complete
319
+
320
+ **Solution:**
321
+ ```bash
322
+ # Check tasks.md for incomplete tasks
323
+ grep "^\- \[ \]" openspec/changes/my-new-feature/tasks.md
324
+
325
+ # If no incomplete tasks, create a new change
326
+ openspec new change another-feature
327
+
328
+ # Or uncheck a task by editing tasks.md manually:
329
+ # Change: - [x] This task is done
330
+ # To: - [ ] This task needs work
331
+ ```
332
+
333
+ ### "Required artifact not found"
334
+
335
+ **Problem:** OpenSpec artifacts (proposal.md, design.md, tasks.md) are missing
207
336
 
208
337
  **Solution:**
209
338
  ```bash
210
- # Check tasks.md
211
- grep "^\- \[ \]" openspec/changes/my-feature/tasks.md
339
+ # Check what artifacts exist in your change directory
340
+ ls -la openspec/changes/my-new-feature/
341
+
342
+ # Create missing artifacts manually or use openspec new change
343
+ openspec new change my-new-feature
344
+
345
+ # Or manually create the required files:
346
+ # - openspec/changes/my-new-feature/proposal.md
347
+ # - openspec/changes/my-new-feature/design.md
348
+ # - openspec/changes/my-new-feature/specs/spec-name/spec.md
349
+ # - openspec/changes/my-new-feature/tasks.md
350
+ ```
351
+
352
+ ### "Required directory not found: specs/"
353
+
354
+ **Problem:** The specs directory is missing from your change
355
+
356
+ **Solution:**
357
+ ```bash
358
+ # Create the specs directory
359
+ mkdir -p openspec/changes/my-new-feature/specs
360
+
361
+ # Create at least one spec file
362
+ mkdir -p openspec/changes/my-new-feature/specs/main-feature
363
+ echo "# Main Feature Spec" > openspec/changes/my-new-feature/specs/main-feature/spec.md
364
+
365
+ # Verify
366
+ ls -la openspec/changes/my-new-feature/specs/
367
+ ```
368
+
369
+ ### "opencode CLI not found"
370
+
371
+ **Problem:** opencode is not installed globally
372
+
373
+ **Solution:**
374
+ ```bash
375
+ # Install opencode
376
+ npm install -g opencode-ai
377
+
378
+ # Verify installation
379
+ opencode --version
380
+
381
+ # Add to PATH if needed
382
+ export PATH="$PATH:$(npm root -g)/.bin"
383
+ ```
384
+
385
+ ### "Node.js version too old"
386
+
387
+ **Problem:** Node.js version is below the required version (24.0.0+)
388
+
389
+ **Solution:**
390
+ ```bash
391
+ # Check current Node.js version
392
+ node --version
393
+
394
+ # If version is below 24.0.0, upgrade Node.js:
395
+ # Using nvm (recommended):
396
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
397
+ nvm install 24
398
+ nvm use 24
399
+
400
+ # Or install from https://nodejs.org
401
+ ```
402
+
403
+ ### "npm: command not found"
404
+
405
+ **Problem:** npm is not installed or not in PATH
406
+
407
+ **Solution:**
408
+ ```bash
409
+ # npm comes with Node.js. Install Node.js from https://nodejs.org
410
+
411
+ # After installing Node.js, verify:
412
+ npm --version
413
+ node --version
212
414
 
213
- # Or create a new change
214
- openspec new another-feature
415
+ # If still not found, restart your terminal or add to PATH
215
416
  ```
216
417
 
217
418
  ## Features at a Glance
package/README.md CHANGED
@@ -4,7 +4,9 @@ OpenSpec + Ralph Loop integration for iterative development with opencode.
4
4
 
5
5
  ![CI Status](https://img.shields.io/github/actions/workflow/status/ncheaz/spec-and-loop/test.yml)
6
6
  ![Coverage](https://img.shields.io/badge/coverage-0%25-red)
7
- [![npm version](https://badge.fury.io/js/spec-and-loop.svg)](https://badge.fury.io/js/spec-and-loop)
7
+ [![npm version](https://badge.fury.io/js/spec-and-loop.svg)](https://badge.fury.io/js/spec-and-loop.svg)
8
+
9
+ **Version:** spec-and-loop 2.0.0 + OpenSpec 1.2.0
8
10
 
9
11
  **[Quick Start Guide](./QUICKSTART.md)** - Get up and running in 5 minutes!
10
12
 
@@ -14,16 +16,18 @@ OpenSpec provides excellent structure for planning (proposal → specs → desig
14
16
 
15
17
  The runtime prompt is self-contained: it does not depend on Cursor-only slash commands or editor-local skills.
16
18
 
19
+ **Version Requirements:** This documentation applies to OpenSpec 1.2.0 and spec-and-loop 2.0.0.
20
+
17
21
  ## Installation
18
22
 
19
23
  ```bash
20
- npm install -g spec-and-loop
24
+ npm install -g spec-and-loop@2.0.0
21
25
  ```
22
26
 
23
27
  **Prerequisites:** You need OpenSpec and the OpenCode AI agent installed:
24
28
 
25
29
  ```bash
26
- npm install -g @fission-ai/openspec@latest opencode-ai
30
+ npm install -g @fission-ai/openspec@1.2.0 opencode-ai
27
31
  ```
28
32
 
29
33
  Alternative OpenCode install methods:
@@ -43,10 +47,13 @@ brew install anomalyco/tap/opencode
43
47
  openspec init
44
48
 
45
49
  # 2. Create a new change
46
- openspec new add-user-auth
50
+ openspec new change add-user-auth
47
51
 
48
- # 3. Fast-forward through artifact creation
49
- openspec ff add-user-auth
52
+ # 3. Review and complete the OpenSpec artifacts
53
+ # (openspec/changes/add-user-auth/proposal.md)
54
+ # (openspec/changes/add-user-auth/design.md)
55
+ # (openspec/changes/add-user-auth/specs/*/spec.md)
56
+ # (openspec/changes/add-user-auth/tasks.md)
50
57
 
51
58
  # 4. Run the ralph loop (executes tasks with opencode)
52
59
  ralph-run --change add-user-auth
@@ -56,6 +63,8 @@ For detailed step-by-step instructions, see [QUICKSTART.md](./QUICKSTART.md).
56
63
 
57
64
  ## Testing
58
65
 
66
+ *Testing suite for spec-and-loop 2.0.0*
67
+
59
68
  Spec-and-loop includes a comprehensive test suite to ensure reliability and cross-platform compatibility.
60
69
 
61
70
  **[Testing Guide](./TESTING.md)** - Detailed instructions for running tests
@@ -89,17 +98,19 @@ All tests are run automatically via GitHub Actions on every push and pull reques
89
98
 
90
99
  ## Prerequisites
91
100
 
101
+ *Required for spec-and-loop 2.0.0 with OpenSpec 1.2.0*
102
+
92
103
  Before using spec-and-loop, ensure you have:
93
104
 
94
- 1. **Node.js** - For package installation
95
- ```bash
96
- node --version # Should be >=24
97
- ```
105
+ 1. **Node.js** - For package installation (requires >=24.0.0)
106
+ ```bash
107
+ node --version # Should be >=24.0.0
108
+ ```
98
109
 
99
- 2. **openspec** - OpenSpec CLI for specification workflow
100
- ```bash
101
- npm install -g @fission-ai/openspec@latest
102
- ```
110
+ 2. **openspec** - OpenSpec CLI for specification workflow (requires 1.2.0)
111
+ ```bash
112
+ npm install -g @fission-ai/openspec@1.2.0
113
+ ```
103
114
 
104
115
  3. **opencode** - Agentic coding assistant
105
116
  ```bash
@@ -124,14 +135,13 @@ For complete installation instructions, see [QUICKSTART.md](./QUICKSTART.md).
124
135
 
125
136
  ## Commands
126
137
 
138
+ *Documentation applies to OpenSpec 1.2.0 and spec-and-loop 2.0.0*
139
+
127
140
  ### OpenSpec Commands
128
141
 
129
142
  - `openspec init` - Initialize OpenSpec in current directory
130
- - `openspec new <name>` - Start a new change
131
- - `openspec ff <name>` - Fast-forward artifact creation
132
- - `openspec continue <name>` - Continue working on change
133
- - `openspec apply <name>` - Apply change (implementation)
134
- - `openspec archive <name>` - Archive a completed change
143
+ - `openspec new change <name>` - Create a new change with artifact templates
144
+ - `openspec --help` - View all available commands and their syntax
135
145
 
136
146
  ### Ralph Loop Commands
137
147
 
@@ -139,24 +149,26 @@ For complete installation instructions, see [QUICKSTART.md](./QUICKSTART.md).
139
149
  ralph-run [OPTIONS]
140
150
 
141
151
  OPTIONS:
142
- --change <name> OpenSpec change to execute (default: auto-detect)
143
- --max-iterations <n> Maximum iterations (default: 50)
144
- --no-commit Suppress automatic git commits
145
- --verbose, -v Enable verbose output
152
+ --change <name> Specify the OpenSpec change to execute (default: auto-detect)
153
+ --max-iterations <n> Maximum iterations for Ralph loop (default: 50)
154
+ --no-commit Suppress automatic git commits during the loop
155
+ --verbose, -v Enable verbose mode for debugging
156
+ --help, -h Show this help message
146
157
 
147
158
  OBSERVABILITY AND CONTROL:
148
- --status Print loop status dashboard and exit
149
- --add-context <text> Add context to inject into the next iteration
150
- --clear-context Clear any pending context
159
+ --status Print the current loop status dashboard and exit
160
+ --add-context <text> Add pending context to inject into the next iteration and exit
161
+ --clear-context Clear any pending context and exit
151
162
  ```
152
163
 
153
164
  ## How It Works
154
165
 
166
+ *Workflow for OpenSpec 1.2.0 + spec-and-loop 2.0.0*
167
+
155
168
  ### Step 1: Create Spec with OpenSpec
156
169
 
157
170
  ```bash
158
- openspec new my-feature
159
- openspec ff my-feature
171
+ openspec new change my-feature
160
172
  ```
161
173
 
162
174
  This creates:
@@ -165,6 +177,8 @@ This creates:
165
177
  - **design.md**: Technical decisions and architecture
166
178
  - **tasks.md**: Implementation tasks as checkboxes
167
179
 
180
+ After creating the change, manually complete the OpenSpec artifacts by filling in proposal.md, design.md, specs/*/spec.md, and tasks.md.
181
+
168
182
  **Example tasks.md:**
169
183
  ```markdown
170
184
  ## Implementation
@@ -185,12 +199,16 @@ ralph-run --change my-feature
185
199
 
186
200
  1. **Validation**: Checks for required OpenSpec artifacts and git repository
187
201
  2. **PRD Generation**: Converts proposal + specs + design → PRD format for internal use
188
- 3. **Task Execution**: For each incomplete task:
202
+ 3. **Setup**: Creates .ralph directory, syncs tasks symlink, and sets up output capture
203
+ 4. **Task Execution**: For each incomplete task:
189
204
  - Generates context-rich prompt (full OpenSpec artifacts + a fresh task snapshot + recent loop signals)
190
205
  - Runs `opencode` with the prompt via the internal mini Ralph engine
206
+ - Captures output to temp directory for review and debugging
207
+ - Logs any errors to `.ralph/errors.md` with timestamps
191
208
  - Creates git commit with task description (unless `--no-commit`)
192
209
  - Marks task complete in tasks.md
193
- 4. **Completion**: All tasks done
210
+ 5. **Cleanup**: Automatically removes old output directories (older than 7 days)
211
+ 6. **Completion**: All tasks done
194
212
 
195
213
  ### Step 3: Monitor Progress
196
214
 
@@ -210,12 +228,21 @@ ralph-run --add-context "Prefer async/await over callbacks"
210
228
 
211
229
  ## Example Workflow
212
230
 
231
+ *Example workflow for OpenSpec 1.2.0 and spec-and-loop 2.0.0*
232
+
213
233
  ```bash
214
- # 1. Plan feature with OpenSpec
215
- openspec new user-auth
216
- openspec ff user-auth
234
+ # 1. Initialize OpenSpec in your project
235
+ cd my-web-app
236
+ git init
237
+ openspec init
217
238
 
218
- # 2. Execute with Ralph
239
+ # 2. Create a new change
240
+ openspec new change user-auth
241
+
242
+ # 3. Complete OpenSpec artifacts manually or use opencode skills
243
+ # (review and fill in proposal.md, design.md, specs/*/spec.md, tasks.md)
244
+
245
+ # 4. Execute with Ralph
219
246
  ralph-run --change user-auth
220
247
 
221
248
  # Output:
@@ -224,7 +251,7 @@ ralph-run --change user-auth
224
251
  # [INFO] Executing task 2/15: Implement password hashing
225
252
  # ...
226
253
 
227
- # 3. Verify implementation
254
+ # 5. Verify implementation
228
255
  git log --oneline # 15 commits, one per task
229
256
  git diff HEAD~15 # See full implementation
230
257
  ```
@@ -241,10 +268,15 @@ git diff HEAD~15 # See full implementation
241
268
  | **Auto-Resume** | Interrupted? Run again — picks up where left off |
242
269
  | **Context Injection** | `--add-context` injects guidance into the next iteration |
243
270
  | **Loop Status** | `--status` shows active state, history, and struggle indicators |
271
+ | **Error Tracking** | Automatic error logging and archiving for debugging |
272
+ | **Output Capture** | Loop output captured to temp directories for review |
273
+ | **Cross-Platform** | Full support for Linux and macOS with portable operations |
244
274
  | **No External Ralph** | Self-contained mini Ralph engine — no external `ralph` CLI needed |
245
275
 
246
276
  ## Features
247
277
 
278
+ *Features available in spec-and-loop 2.0.0 with OpenSpec 1.2.0*
279
+
248
280
  ### Mini Ralph Loop Engine
249
281
 
250
282
  `spec-and-loop` includes a first-party mini Ralph implementation (`lib/mini-ralph/`) that
@@ -272,11 +304,17 @@ this repository's OpenSpec-first workflow (multi-agent rotation, plugin toggles,
272
304
  - **Auto-resume**: Interrupted? Run again — picks up where left off
273
305
  - **Context injection**: `--add-context` / `--clear-context` via each change's `.ralph/ralph-context.md`
274
306
  - **Error recovery**: Recent loop signals help guide subsequent tasks
307
+ - **Error tracking**: Automatic error logging to `.ralph/errors.md` with timestamps and archiving
275
308
  - **Task synchronization**: `tasks.md` and the per-change `.ralph/ralph-tasks.md` symlink stay in sync
309
+ - **Output capture**: Loop output captured to temp directories for review and debugging
310
+ - **Cross-platform**: Portable operations for Linux and macOS (stat, md5sum, realpath)
311
+ - **Cleanup**: Automatic cleanup of old output directories (older than 7 days)
276
312
  - **Idempotent**: Run multiple times safely
277
313
 
278
314
  ## Advanced Usage
279
315
 
316
+ *Advanced features for spec-and-loop 2.0.0*
317
+
280
318
  ### Context Injection
281
319
 
282
320
  Inject custom instructions into the next iteration:
@@ -322,8 +360,33 @@ ralph-run --verbose --change my-feature
322
360
  cat openspec/changes/my-feature/.ralph/PRD.md
323
361
  ```
324
362
 
363
+ ### Review Loop Output
364
+
365
+ ```bash
366
+ # Find the latest output directory path
367
+ cat openspec/changes/my-feature/.ralph/.output_dir
368
+
369
+ # View stdout and stderr logs
370
+ cat openspec/changes/my-feature/.ralph/.output_dir/ralph-stdout.log
371
+ cat openspec/changes/my-feature/.ralph/.output_dir/ralph-stderr.log
372
+ ```
373
+
374
+ Output is captured to temporary directories for debugging. Old output directories are automatically cleaned up after 7 days.
375
+
376
+ ### View Error Logs
377
+
378
+ ```bash
379
+ # View recent errors
380
+ cat openspec/changes/my-feature/.ralph/errors.md
381
+
382
+ # Archived errors are saved with timestamps
383
+ ls openspec/changes/my-feature/.ralph/errors_*.md
384
+ ```
385
+
325
386
  ## Architecture
326
387
 
388
+ *Architecture for spec-and-loop 2.0.0 with OpenSpec 1.2.0*
389
+
327
390
  This package integrates:
328
391
  - **OpenSpec**: Structured specification workflow
329
392
  - **opencode**: Agentic coding assistant for task execution
@@ -357,16 +420,41 @@ openspec/changes/<name>/
357
420
  │ └── api/
358
421
  │ └── spec.md
359
422
  └── .ralph/ # Internal loop state (auto-generated, per change)
360
- ├── PRD.md
361
- ├── ralph-tasks.md
362
- ├── ralph-context.md
363
- ├── ralph-history.json
364
- ├── ralph-loop.state.json
365
- └── prompt-template.md
423
+ ├── PRD.md # Generated product requirements document
424
+ ├── prompt-template.md # Template used for generating prompts
425
+ ├── ralph-history.json # Iteration history and state
426
+ ├── ralph-loop.state.json # Current loop state and iteration count
427
+ ├── ralph-tasks.md # Symlink to ../tasks.md (syncs task state)
428
+ ├── .output_dir # Path to latest output capture directory
429
+ ├── ralph-context.md # (Optional) Pending context for next iteration
430
+ ├── errors.md # (Optional) Error logs with timestamps
431
+ ├── errors_*.md # (Optional) Archived error logs
432
+ └── *.md # (Optional) Research artifacts created during task execution
366
433
  ```
367
434
 
435
+ **Note:** Files marked as (Optional) are created only when needed:
436
+ - `ralph-context.md`: Created when you use `--add-context`
437
+ - `errors.md` and `errors_*.md`: Created when errors occur during loop execution
438
+ - Additional `*.md` files: Research artifacts created by opencode during task execution (e.g., verification outputs, analysis documents)
439
+
440
+ ### Cross-Platform Support
441
+
442
+ *Cross-platform support verified for spec-and-loop 2.0.0*
443
+
444
+ `spec-and-loop` is designed to work seamlessly on both Linux and macOS. The script includes portable implementations for:
445
+
446
+ - **File modification times**: Uses `stat -f %m` on macOS and `stat -c %Y` on Linux
447
+ - **MD5 hashing**: Supports both `md5sum` (Linux) and `md5 -q` (macOS)
448
+ - **Path resolution**: Falls back from `realpath` to `readlink -f` to manual path construction
449
+ - **Temp directories**: Uses `TMPDIR` environment variable or `/tmp` as fallback
450
+ - **Cleanup**: Portable `find` and `rm` operations for old output directories
451
+
452
+ All features work identically on both platforms without requiring platform-specific configuration.
453
+
368
454
  ## Troubleshooting
369
455
 
456
+ *Troubleshooting guide for spec-and-loop 2.0.0 with OpenSpec 1.2.0*
457
+
370
458
  For common issues and solutions, see [QUICKSTART.md#troubleshooting](./QUICKSTART.md#troubleshooting).
371
459
 
372
460
  **Quick fixes:**
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const ERRORS_FILE = 'errors.md';
7
+
8
+ function errorsPath(ralphDir) {
9
+ return path.join(ralphDir, ERRORS_FILE);
10
+ }
11
+
12
+ function read(ralphDir, limit) {
13
+ const file = errorsPath(ralphDir);
14
+ if (!fs.existsSync(file)) return '';
15
+ const content = fs.readFileSync(file, 'utf8').trim();
16
+ if (!content) return '';
17
+ const entries = content.split(/^---$/m).filter(e => e.trim());
18
+ if (!entries.length) return '';
19
+ if (limit !== undefined && limit < entries.length) {
20
+ return entries.slice(-limit).join('\n---\n');
21
+ }
22
+ return entries.join('\n---\n');
23
+ }
24
+
25
+ function append(ralphDir, entry) {
26
+ _ensureDir(ralphDir);
27
+ const file = errorsPath(ralphDir);
28
+ const existing = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
29
+ const separator = existing && !existing.endsWith('\n') ? '\n' : '';
30
+ const timestamp = new Date().toISOString();
31
+ const text = [
32
+ '---',
33
+ `Timestamp: ${timestamp}`,
34
+ `Iteration: ${entry.iteration}`,
35
+ `Task: ${entry.task}`,
36
+ `Exit Code: ${entry.exitCode}`,
37
+ '',
38
+ '### stderr',
39
+ entry.stderr || '',
40
+ '',
41
+ '### stdout',
42
+ entry.stdout || '',
43
+ '',
44
+ ].join('\n');
45
+ fs.writeFileSync(file, `${existing}${separator}${text}`, 'utf8');
46
+ }
47
+
48
+ function clear(ralphDir) {
49
+ const file = errorsPath(ralphDir);
50
+ if (fs.existsSync(file)) {
51
+ fs.unlinkSync(file);
52
+ }
53
+ }
54
+
55
+ function archive(ralphDir) {
56
+ const file = errorsPath(ralphDir);
57
+ if (!fs.existsSync(file)) return null;
58
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
59
+ const archiveFile = path.join(ralphDir, `errors_${timestamp}.md`);
60
+ fs.copyFileSync(file, archiveFile);
61
+ return archiveFile;
62
+ }
63
+
64
+ function _ensureDir(ralphDir) {
65
+ if (!fs.existsSync(ralphDir)) {
66
+ fs.mkdirSync(ralphDir, { recursive: true });
67
+ }
68
+ }
69
+
70
+ module.exports = { errorsPath, read, append, clear, archive };
@@ -18,6 +18,7 @@ const runner = require('./runner');
18
18
  const state = require('./state');
19
19
  const history = require('./history');
20
20
  const context = require('./context');
21
+ const errors = require('./errors');
21
22
  const tasks = require('./tasks');
22
23
  const status = require('./status');
23
24
  const prompt = require('./prompt');
@@ -84,6 +85,7 @@ module.exports = {
84
85
  _state: state,
85
86
  _history: history,
86
87
  _context: context,
88
+ _errors: errors,
87
89
  _tasks: tasks,
88
90
  _prompt: prompt,
89
91
  _runner: runner,
@@ -67,6 +67,7 @@ async function invoke(opts) {
67
67
 
68
68
  return {
69
69
  stdout: result.stdout,
70
+ stderr: result.stderr,
70
71
  exitCode: result.exitCode,
71
72
  toolUsage: _extractToolUsage(result.stdout),
72
73
  filesChanged,
@@ -18,6 +18,7 @@ const context = require('./context');
18
18
  const tasks = require('./tasks');
19
19
  const prompt = require('./prompt');
20
20
  const invoker = require('./invoker');
21
+ const errors = require('./errors');
21
22
 
22
23
  const DEFAULTS = {
23
24
  minIterations: 1,
@@ -93,7 +94,8 @@ async function run(opts) {
93
94
 
94
95
  // Build the prompt for this iteration
95
96
  const renderedPrompt = await prompt.render(options, iterationCount);
96
- const iterationFeedback = _buildIterationFeedback(history.recent(ralphDir, 3));
97
+ const errorContent = errors.read(ralphDir, 3);
98
+ const iterationFeedback = _buildIterationFeedback(history.recent(ralphDir, 3), errorContent);
97
99
 
98
100
  // Inject any pending context
99
101
  const pendingContext = context.consume(ralphDir);
@@ -146,6 +148,17 @@ async function run(opts) {
146
148
  completedTasks: completedTasks.map((task) => task.fullDescription || task.description),
147
149
  });
148
150
 
151
+ if (result.exitCode !== 0) {
152
+ const currentTask = _getCurrentTaskDescription(tasksBefore);
153
+ errors.append(ralphDir, {
154
+ iteration: iterationCount,
155
+ task: currentTask,
156
+ exitCode: result.exitCode,
157
+ stderr: result.stderr || '',
158
+ stdout: result.stdout || '',
159
+ });
160
+ }
161
+
149
162
  // Auto-commit only for successful task/completion iterations.
150
163
  if (
151
164
  !options.noCommit &&
@@ -174,6 +187,14 @@ async function run(opts) {
174
187
  }
175
188
  }
176
189
 
190
+ if (completed) {
191
+ const archivePath = errors.archive(ralphDir);
192
+ if (archivePath && options.verbose) {
193
+ process.stderr.write(`[mini-ralph] errors archived to ${archivePath}\n`);
194
+ }
195
+ errors.clear(ralphDir);
196
+ }
197
+
177
198
  // Mark loop as inactive
178
199
  state.update(ralphDir, { active: false, completedAt: new Date().toISOString() });
179
200
 
@@ -321,7 +342,7 @@ function _formatAutoCommitMessage(iteration, completedTasks) {
321
342
  * @param {Array<object>} recentHistory
322
343
  * @returns {string}
323
344
  */
324
- function _buildIterationFeedback(recentHistory) {
345
+ function _buildIterationFeedback(recentHistory, errorContent) {
325
346
  if (!Array.isArray(recentHistory) || recentHistory.length === 0) {
326
347
  return '';
327
348
  }
@@ -344,7 +365,22 @@ function _buildIterationFeedback(recentHistory) {
344
365
  }
345
366
 
346
367
  if (issues.length > 0) {
347
- problemLines.push(`- Iteration ${entry.iteration}: ${issues.join('; ')}.`);
368
+ let line = `- Iteration ${entry.iteration}: ${issues.join('; ')}.`;
369
+
370
+ if (entry.exitCode !== 0 && errorContent) {
371
+ const errorDetails = _extractErrorForIteration(errorContent, entry.iteration);
372
+ if (errorDetails) {
373
+ line += '\n Error output:';
374
+ if (errorDetails.stderr) {
375
+ line += `\n ${errorDetails.stderr}`;
376
+ }
377
+ if (errorDetails.stdout) {
378
+ line += `\n stdout: ${errorDetails.stdout}`;
379
+ }
380
+ }
381
+ }
382
+
383
+ problemLines.push(line);
348
384
  }
349
385
  }
350
386
 
@@ -358,6 +394,39 @@ function _buildIterationFeedback(recentHistory) {
358
394
  ].join('\n');
359
395
  }
360
396
 
397
+ function _extractErrorForIteration(errorContent, iteration) {
398
+ if (!errorContent) return null;
399
+
400
+ const entries = errorContent.split(/^---$/m).filter((e) => e.trim());
401
+
402
+ for (const entry of entries) {
403
+ if (!entry.includes(`Iteration: ${iteration}`)) continue;
404
+
405
+ let stderr = '';
406
+ let stdout = '';
407
+
408
+ const stderrMatch = entry.match(/### stderr\n([\s\S]*?)(?=\n### stdout|$)/);
409
+ const stdoutMatch = entry.match(/### stdout\n([\s\S]*?)$/);
410
+
411
+ if (stderrMatch) stderr = stderrMatch[1].trim();
412
+ if (stdoutMatch) stdout = stdoutMatch[1].trim();
413
+
414
+ if (stderr.length > 2000) stderr = stderr.substring(0, 2000) + '...';
415
+ if (stdout.length > 500) stdout = stdout.substring(0, 500) + '...';
416
+
417
+ return { stderr, stdout };
418
+ }
419
+
420
+ return null;
421
+ }
422
+
423
+ function _getCurrentTaskDescription(tasksBefore) {
424
+ if (!Array.isArray(tasksBefore) || tasksBefore.length === 0) return 'N/A';
425
+ const incomplete = tasksBefore.find((t) => t.status !== 'completed');
426
+ if (incomplete) return incomplete.fullDescription || incomplete.description || 'N/A';
427
+ return 'N/A';
428
+ }
429
+
361
430
  function _taskIdentity(task) {
362
431
  return task.number
363
432
  ? `${task.number}|${task.fullDescription || task.description}`
@@ -412,4 +481,6 @@ module.exports = {
412
481
  _completedTaskDelta,
413
482
  _formatAutoCommitMessage,
414
483
  _buildIterationFeedback,
484
+ _extractErrorForIteration,
485
+ _getCurrentTaskDescription,
415
486
  };
@@ -12,6 +12,7 @@ const state = require('./state');
12
12
  const history = require('./history');
13
13
  const context = require('./context');
14
14
  const tasks = require('./tasks');
15
+ const errors = require('./errors');
15
16
 
16
17
  /**
17
18
  * Render a status dashboard string for the given .ralph/ directory.
@@ -99,6 +100,19 @@ function render(ralphDir, tasksFile) {
99
100
  lines.push('-'.repeat(50));
100
101
  }
101
102
 
103
+ // Error history
104
+ const errorContent = errors.read(ralphDir, 3);
105
+ if (errorContent) {
106
+ const entries = errorContent.split(/^---$/m).filter(e => e.trim());
107
+ const count = entries.length;
108
+ const preview = entries[entries.length - 1].substring(0, 200).trim();
109
+ lines.push('');
110
+ lines.push('--- Error History ---');
111
+ lines.push(` Errors: ${count}`);
112
+ lines.push(` Most recent: ${preview}`);
113
+ lines.push('-'.repeat(50));
114
+ }
115
+
102
116
  // Struggle indicators
103
117
  const struggles = _detectStruggles(recentHistory);
104
118
  if (struggles.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-and-loop",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "OpenSpec + Ralph Loop integration for iterative development with opencode",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -17,7 +17,8 @@
17
17
  "lint": "shellcheck scripts/*.sh"
18
18
  },
19
19
  "dependencies": {
20
- "@fission-ai/openspec": "1.2.0"
20
+ "@fission-ai/openspec": "1.2.0",
21
+ "spec-and-loop": "^2.0.0"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@types/jest": "^29.5.0",
@@ -496,6 +496,10 @@ check_tasks_modified() {
496
496
  return 1
497
497
  }
498
498
 
499
+ # DEPRECATED: The following functions are superseded by lib/mini-ralph/errors.js.
500
+ # Do not add new callers. These will be removed in a future cleanup.
501
+ # See: lib/mini-ralph/errors.js for the current implementation.
502
+
499
503
  format_error_entry() {
500
504
  local task_id="$1"
501
505
  local task_description="$2"