ralph-cli-sandboxed 0.2.6 → 0.2.8
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/README.md +185 -3
- package/dist/commands/docker.js +103 -2
- package/dist/commands/help.js +2 -1
- package/dist/commands/init.js +30 -2
- package/dist/commands/once.js +174 -15
- package/dist/commands/run.js +189 -18
- package/dist/config/cli-providers.json +28 -3
- package/dist/config/skills.json +12 -0
- package/dist/templates/prompts.d.ts +13 -0
- package/dist/templates/prompts.js +17 -0
- package/dist/utils/config.d.ts +6 -0
- package/dist/utils/config.js +5 -1
- package/dist/utils/notification.d.ts +28 -0
- package/dist/utils/notification.js +69 -0
- package/dist/utils/stream-json.d.ts +132 -0
- package/dist/utils/stream-json.js +662 -0
- package/docs/SECURITY.md +21 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,6 +83,57 @@ After running `ralph init`, you'll have:
|
|
|
83
83
|
└── ...
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
### Notifications
|
|
87
|
+
|
|
88
|
+
Ralph can send notifications when events occur during automation. Configure a notification command in `.ralph/config.json`:
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"notifyCommand": "ntfy pub mytopic"
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The message is appended as the last argument to your command. Supported notification tools include:
|
|
97
|
+
|
|
98
|
+
| Tool | Example Command | Description |
|
|
99
|
+
|------|----------------|-------------|
|
|
100
|
+
| [ntfy](https://ntfy.sh/) | `ntfy pub mytopic` | Push notifications to phone/desktop |
|
|
101
|
+
| notify-send (Linux) | `notify-send Ralph` | Desktop notifications on Linux |
|
|
102
|
+
| terminal-notifier (macOS) | `terminal-notifier -title Ralph -message` | Desktop notifications on macOS |
|
|
103
|
+
| Custom script | `/path/to/notify.sh` | Your own notification script |
|
|
104
|
+
|
|
105
|
+
#### Notification Events
|
|
106
|
+
|
|
107
|
+
Ralph sends notifications for these events:
|
|
108
|
+
|
|
109
|
+
| Event | Message | When |
|
|
110
|
+
|-------|---------|------|
|
|
111
|
+
| PRD Complete | "Ralph: PRD Complete! All tasks finished." | All PRD tasks are marked as passing |
|
|
112
|
+
| Iteration Complete | "Ralph: Iteration complete." | Single `ralph once` iteration finishes |
|
|
113
|
+
| Run Stopped | "Ralph: Run stopped..." | `ralph run` stops due to no progress or max failures |
|
|
114
|
+
| Error | "Ralph: An error occurred." | CLI fails repeatedly |
|
|
115
|
+
|
|
116
|
+
#### Example: ntfy Setup
|
|
117
|
+
|
|
118
|
+
[ntfy](https://ntfy.sh/) is a simple HTTP-based pub-sub notification service:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# 1. Install ntfy CLI
|
|
122
|
+
pip install ntfy
|
|
123
|
+
|
|
124
|
+
# 2. Subscribe to your topic on your phone (ntfy app) or browser
|
|
125
|
+
# Visit: https://ntfy.sh/your-unique-topic
|
|
126
|
+
|
|
127
|
+
# 3. Configure ralph
|
|
128
|
+
# In .ralph/config.json:
|
|
129
|
+
{
|
|
130
|
+
"notifyCommand": "ntfy pub your-unique-topic"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# 4. Run ralph - you'll get notifications on completion
|
|
134
|
+
ralph docker run
|
|
135
|
+
```
|
|
136
|
+
|
|
86
137
|
### Supported Languages
|
|
87
138
|
|
|
88
139
|
Ralph supports 18 programming languages with pre-configured build/test commands:
|
|
@@ -116,8 +167,9 @@ Ralph supports multiple AI CLI tools. Select your provider during `ralph init`:
|
|
|
116
167
|
|-----|--------|----------------------|-------|
|
|
117
168
|
| [Claude Code](https://github.com/anthropics/claude-code) | Working | `ANTHROPIC_API_KEY` | Default provider. Also supports ~/.claude OAuth credentials |
|
|
118
169
|
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Working | `GEMINI_API_KEY`, `GOOGLE_API_KEY` | |
|
|
119
|
-
| [OpenCode](https://github.com/anomalyco/opencode) | Working | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY` | Requires [PR #9073](https://github.com/anomalyco/opencode/pull/9073) |
|
|
170
|
+
| [OpenCode](https://github.com/anomalyco/opencode) | Working | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY` | No autonomous/yolo mode yet. Requires [PR #9073](https://github.com/anomalyco/opencode/pull/9073) |
|
|
120
171
|
| [Aider](https://github.com/paul-gauthier/aider) | Working | `OPENAI_API_KEY`, `ANTHROPIC_API_KEY` | |
|
|
172
|
+
| [Goose](https://github.com/block/goose) | Working | `OPENAI_API_KEY`, `ANTHROPIC_API_KEY` | Block's AI coding agent |
|
|
121
173
|
| [Codex CLI](https://github.com/openai/codex) | Testers wanted | `OPENAI_API_KEY` | Sponsors welcome |
|
|
122
174
|
| [AMP](https://ampcode.com/) | Testers wanted | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY` | Sponsors welcome |
|
|
123
175
|
| Custom | - | User-defined | Configure your own CLI |
|
|
@@ -130,7 +182,7 @@ Ralph can be configured to use different AI CLI tools. By default, it uses Claud
|
|
|
130
182
|
{
|
|
131
183
|
"cli": {
|
|
132
184
|
"command": "claude",
|
|
133
|
-
"args": [
|
|
185
|
+
"args": [],
|
|
134
186
|
"modelArgs": ["--model"],
|
|
135
187
|
"promptArgs": ["-p"]
|
|
136
188
|
}
|
|
@@ -144,6 +196,102 @@ Ralph can be configured to use different AI CLI tools. By default, it uses Claud
|
|
|
144
196
|
|
|
145
197
|
The prompt content and `--dangerously-skip-permissions` (in containers) are added automatically at runtime.
|
|
146
198
|
|
|
199
|
+
### Stream-JSON Output
|
|
200
|
+
|
|
201
|
+
Ralph supports stream-json output mode for real-time streaming of AI responses. This feature provides cleaner terminal output and enables recording of raw JSON logs for debugging or replay.
|
|
202
|
+
|
|
203
|
+
#### Enabling Stream-JSON
|
|
204
|
+
|
|
205
|
+
Configure stream-json in `.ralph/config.json`:
|
|
206
|
+
|
|
207
|
+
```json
|
|
208
|
+
{
|
|
209
|
+
"docker": {
|
|
210
|
+
"asciinema": {
|
|
211
|
+
"enabled": true,
|
|
212
|
+
"autoRecord": true,
|
|
213
|
+
"outputDir": ".recordings",
|
|
214
|
+
"streamJson": {
|
|
215
|
+
"enabled": true,
|
|
216
|
+
"saveRawJson": true
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Configuration options:
|
|
224
|
+
- `enabled`: Enable stream-json output mode (default: `false`)
|
|
225
|
+
- `saveRawJson`: Save raw JSON output to `.jsonl` files (default: `true` when enabled)
|
|
226
|
+
- `outputDir`: Directory for recordings and logs (default: `.recordings`)
|
|
227
|
+
|
|
228
|
+
#### Provider Compatibility
|
|
229
|
+
|
|
230
|
+
Not all CLI providers support stream-json output. Here's the compatibility matrix:
|
|
231
|
+
|
|
232
|
+
| CLI Provider | Stream-JSON Support | Arguments Used |
|
|
233
|
+
|--------------|---------------------|----------------|
|
|
234
|
+
| Claude Code | ✅ Yes | `--output-format stream-json --verbose --print` |
|
|
235
|
+
| Gemini CLI | ✅ Yes | `--output-format json` |
|
|
236
|
+
| OpenCode | ✅ Yes | `--format json` |
|
|
237
|
+
| Codex CLI | ✅ Yes | `--json` |
|
|
238
|
+
| Goose | ✅ Yes | `--output-format stream-json` |
|
|
239
|
+
| Aider | ❌ No | - |
|
|
240
|
+
| AMP | ❌ No | - |
|
|
241
|
+
| Custom | ❌ No* | *Add `streamJsonArgs` to your custom config |
|
|
242
|
+
|
|
243
|
+
Each provider uses different command-line arguments and output formats. Ralph automatically selects the correct parser based on your configured provider.
|
|
244
|
+
|
|
245
|
+
#### Output Files
|
|
246
|
+
|
|
247
|
+
When stream-json is enabled, Ralph creates the following files in the `.recordings/` directory (configurable via `outputDir`):
|
|
248
|
+
|
|
249
|
+
| File Type | Pattern | Description |
|
|
250
|
+
|-----------|---------|-------------|
|
|
251
|
+
| `.jsonl` | `ralph-run-YYYYMMDD-HHMMSS.jsonl` | Raw JSON Lines log from `ralph run` |
|
|
252
|
+
| `.jsonl` | `ralph-once-YYYYMMDD-HHMMSS.jsonl` | Raw JSON Lines log from `ralph once` |
|
|
253
|
+
| `.cast` | `session-YYYYMMDD-HHMMSS.cast` | Asciinema terminal recording (when asciinema enabled) |
|
|
254
|
+
|
|
255
|
+
The `.jsonl` files contain one JSON object per line with the raw streaming events from the AI provider. These files are useful for:
|
|
256
|
+
- Debugging AI responses
|
|
257
|
+
- Replaying sessions
|
|
258
|
+
- Analyzing tool calls and outputs
|
|
259
|
+
- Building custom post-processing pipelines
|
|
260
|
+
|
|
261
|
+
#### Troubleshooting Stream-JSON
|
|
262
|
+
|
|
263
|
+
**Stream-JSON not working:**
|
|
264
|
+
1. Verify your CLI provider supports stream-json (see compatibility matrix above)
|
|
265
|
+
2. Check that `streamJson.enabled` is set to `true` in config
|
|
266
|
+
3. Ensure your CLI provider is correctly installed and accessible
|
|
267
|
+
|
|
268
|
+
**No output appearing:**
|
|
269
|
+
- Stream-json parsing extracts human-readable text from JSON events
|
|
270
|
+
- Some providers emit different event types; Ralph handles the most common ones
|
|
271
|
+
- Use `--debug` flag with ralph commands to see raw parsing output: `[stream-json]` prefixed lines go to stderr
|
|
272
|
+
|
|
273
|
+
**Missing .jsonl files:**
|
|
274
|
+
- Verify `saveRawJson` is `true` (or not set, as it defaults to `true`)
|
|
275
|
+
- Check that the `outputDir` directory is writable
|
|
276
|
+
- Files are created at command start; check for permission errors
|
|
277
|
+
|
|
278
|
+
**Parser not recognizing events:**
|
|
279
|
+
- Each provider has a specific parser (ClaudeStreamParser, GeminiStreamParser, etc.)
|
|
280
|
+
- Unknown event types are handled by a default parser that extracts common fields
|
|
281
|
+
- If you see raw JSON in output, the parser may not support that event type yet
|
|
282
|
+
|
|
283
|
+
**Custom CLI provider:**
|
|
284
|
+
To add stream-json support for a custom CLI provider, add `streamJsonArgs` to your CLI config:
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"cli": {
|
|
288
|
+
"command": "my-cli",
|
|
289
|
+
"promptArgs": ["-p"],
|
|
290
|
+
"streamJsonArgs": ["--json-output"]
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
147
295
|
## PRD Format
|
|
148
296
|
|
|
149
297
|
The PRD (`prd.json`) is an array of requirements:
|
|
@@ -181,11 +329,45 @@ File paths are resolved relative to the project root. Absolute paths are also su
|
|
|
181
329
|
|
|
182
330
|
Ralph includes automatic PRD protection to handle cases where the LLM corrupts the PRD structure:
|
|
183
331
|
|
|
184
|
-
- **Automatic backup**: Before each run, the PRD is backed up
|
|
332
|
+
- **Automatic backup**: Before each run, the PRD is backed up to `.ralph/backups/`
|
|
185
333
|
- **Validation**: After each iteration, the PRD structure is validated
|
|
186
334
|
- **Smart recovery**: If corrupted, ralph attempts to extract `passes: true` flags from the corrupted PRD and merge them into the backup
|
|
187
335
|
- **Manual recovery**: Use `ralph fix-prd` to validate, auto-fix, or restore from a specific backup
|
|
188
336
|
|
|
337
|
+
### When PRD Corruption Happens
|
|
338
|
+
|
|
339
|
+
LLMs sometimes modify the PRD file incorrectly, such as:
|
|
340
|
+
- Converting the array to an object
|
|
341
|
+
- Adding invalid JSON syntax
|
|
342
|
+
- Changing the structure entirely
|
|
343
|
+
|
|
344
|
+
If you see an error like:
|
|
345
|
+
```
|
|
346
|
+
Error: prd.json is corrupted - expected an array of items.
|
|
347
|
+
The file may have been modified incorrectly by an LLM.
|
|
348
|
+
|
|
349
|
+
Run ralph fix-prd to diagnose and repair the file.
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### Using fix-prd
|
|
353
|
+
|
|
354
|
+
The `ralph fix-prd` command diagnoses and repairs corrupted PRD files:
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
ralph fix-prd # Auto-diagnose and fix
|
|
358
|
+
ralph fix-prd --verify # Check structure without modifying
|
|
359
|
+
ralph fix-prd backup.json # Restore from a specific backup file
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**What fix-prd does:**
|
|
363
|
+
1. Validates JSON syntax and structure
|
|
364
|
+
2. Checks that all required fields exist (category, description, steps, passes)
|
|
365
|
+
3. Attempts to recover `passes: true` flags from corrupted files
|
|
366
|
+
4. Falls back to the most recent backup if recovery fails
|
|
367
|
+
5. Creates a fresh template PRD as a last resort
|
|
368
|
+
|
|
369
|
+
**Backups are stored in:** `.ralph/backups/backup.prd.YYYY-MM-DD-HHMMSS.json`
|
|
370
|
+
|
|
189
371
|
### Dynamic Iteration Limits
|
|
190
372
|
|
|
191
373
|
To prevent runaway loops, `ralph run` limits iterations to `incomplete_tasks + 3`. This limit adjusts dynamically if new tasks are added during execution.
|
package/dist/commands/docker.js
CHANGED
|
@@ -113,6 +113,7 @@ RUN ${gitCommands.join(' \\\n && ')}
|
|
|
113
113
|
// Build asciinema installation section if enabled
|
|
114
114
|
let asciinemaInstall = '';
|
|
115
115
|
let asciinemaDir = '';
|
|
116
|
+
let streamScriptCopy = '';
|
|
116
117
|
if (dockerConfig?.asciinema?.enabled) {
|
|
117
118
|
const outputDir = dockerConfig.asciinema.outputDir || '.recordings';
|
|
118
119
|
asciinemaInstall = `
|
|
@@ -123,6 +124,14 @@ RUN apt-get update && apt-get install -y asciinema && rm -rf /var/lib/apt/lists/
|
|
|
123
124
|
# Create asciinema recordings directory
|
|
124
125
|
RUN mkdir -p /workspace/${outputDir} && chown node:node /workspace/${outputDir}
|
|
125
126
|
`;
|
|
127
|
+
// Add stream script if streamJson is enabled
|
|
128
|
+
if (dockerConfig.asciinema.streamJson?.enabled) {
|
|
129
|
+
streamScriptCopy = `
|
|
130
|
+
# Copy ralph stream wrapper script for clean JSON output
|
|
131
|
+
COPY ralph-stream.sh /usr/local/bin/ralph-stream.sh
|
|
132
|
+
RUN chmod +x /usr/local/bin/ralph-stream.sh
|
|
133
|
+
`;
|
|
134
|
+
}
|
|
126
135
|
}
|
|
127
136
|
return `# Ralph CLI Sandbox Environment
|
|
128
137
|
# Based on Claude Code devcontainer
|
|
@@ -218,7 +227,7 @@ ENV EDITOR=nano
|
|
|
218
227
|
# Add bash aliases and prompt (fallback if using bash)
|
|
219
228
|
RUN echo 'alias ll="ls -la"' >> /etc/bash.bashrc && \\
|
|
220
229
|
echo 'PS1="\\[\\033[43;30m\\][ralph]\\w\\[\\033[0m\\]\\$ "' >> /etc/bash.bashrc
|
|
221
|
-
${rootBuildCommands}${asciinemaInstall}
|
|
230
|
+
${rootBuildCommands}${asciinemaInstall}${streamScriptCopy}
|
|
222
231
|
# Switch to non-root user
|
|
223
232
|
USER node
|
|
224
233
|
${gitConfigSection}${nodeBuildCommands}
|
|
@@ -364,11 +373,21 @@ function generateDockerCompose(imageName, dockerConfig) {
|
|
|
364
373
|
}
|
|
365
374
|
// Build command section if configured
|
|
366
375
|
let commandSection = '';
|
|
376
|
+
let streamJsonNote = '';
|
|
367
377
|
if (dockerConfig?.asciinema?.enabled && dockerConfig?.asciinema?.autoRecord) {
|
|
368
378
|
// Wrap with asciinema recording
|
|
369
379
|
const outputDir = dockerConfig.asciinema.outputDir || '.recordings';
|
|
370
380
|
const innerCommand = dockerConfig.startCommand || 'zsh';
|
|
371
381
|
commandSection = ` command: bash -c "mkdir -p /workspace/${outputDir} && asciinema rec -c '${innerCommand}' /workspace/${outputDir}/session-$$(date +%Y%m%d-%H%M%S).cast"\n`;
|
|
382
|
+
// Add note about stream-json if enabled
|
|
383
|
+
if (dockerConfig.asciinema.streamJson?.enabled) {
|
|
384
|
+
streamJsonNote = `
|
|
385
|
+
# Stream JSON mode enabled - use ralph-stream.sh for clean Claude output:
|
|
386
|
+
# ralph-stream.sh -p "your prompt here"
|
|
387
|
+
# This formats stream-json output for readable terminal display.
|
|
388
|
+
# Raw JSON is saved to ${outputDir}/session-*.jsonl for later analysis.
|
|
389
|
+
`;
|
|
390
|
+
}
|
|
372
391
|
}
|
|
373
392
|
else if (dockerConfig?.startCommand) {
|
|
374
393
|
commandSection = ` command: ${dockerConfig.startCommand}\n`;
|
|
@@ -394,7 +413,7 @@ ${environmentSection} working_dir: /workspace
|
|
|
394
413
|
tty: true
|
|
395
414
|
cap_add:
|
|
396
415
|
- NET_ADMIN # Required for firewall
|
|
397
|
-
${commandSection}
|
|
416
|
+
${streamJsonNote}${commandSection}
|
|
398
417
|
volumes:
|
|
399
418
|
${imageName}-history:
|
|
400
419
|
`;
|
|
@@ -405,6 +424,82 @@ dist
|
|
|
405
424
|
.git
|
|
406
425
|
*.log
|
|
407
426
|
`;
|
|
427
|
+
// Generate stream wrapper script for clean asciinema recordings
|
|
428
|
+
function generateStreamScript(outputDir, saveRawJson) {
|
|
429
|
+
const saveJsonSection = saveRawJson ? `
|
|
430
|
+
# Save raw JSON for later analysis
|
|
431
|
+
JSON_LOG="$OUTPUT_DIR/session-$TIMESTAMP.jsonl"
|
|
432
|
+
TEE_CMD="tee \\"$JSON_LOG\\""` : `
|
|
433
|
+
TEE_CMD="cat"`;
|
|
434
|
+
return `#!/bin/bash
|
|
435
|
+
# Ralph stream wrapper - formats Claude stream-json output for clean terminal display
|
|
436
|
+
# Generated by ralph-cli
|
|
437
|
+
|
|
438
|
+
set -e
|
|
439
|
+
|
|
440
|
+
OUTPUT_DIR="\${RALPH_RECORDING_DIR:-/workspace/${outputDir}}"
|
|
441
|
+
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
|
442
|
+
|
|
443
|
+
# Ensure output directory exists
|
|
444
|
+
mkdir -p "$OUTPUT_DIR"
|
|
445
|
+
${saveJsonSection}
|
|
446
|
+
|
|
447
|
+
# jq filter to extract and format content from stream-json
|
|
448
|
+
# Handles text, tool calls, tool results, file operations, and commands
|
|
449
|
+
JQ_FILTER='
|
|
450
|
+
if .type == "content_block_delta" then
|
|
451
|
+
(if .delta.type == "text_delta" then .delta.text // empty
|
|
452
|
+
elif .delta.text then .delta.text
|
|
453
|
+
else empty end)
|
|
454
|
+
elif .type == "content_block_start" then
|
|
455
|
+
(if .content_block.type == "tool_use" then "\\n── Tool: " + (.content_block.name // "unknown") + " ──\\n"
|
|
456
|
+
elif .content_block.type == "text" then .content_block.text // empty
|
|
457
|
+
else empty end)
|
|
458
|
+
elif .type == "tool_result" then
|
|
459
|
+
"\\n── Tool Result ──\\n" + ((.content // .output // "") | tostring) + "\\n"
|
|
460
|
+
elif .type == "assistant" then
|
|
461
|
+
([.message.content[]? | select(.type == "text") | .text] | join(""))
|
|
462
|
+
elif .type == "message_start" then
|
|
463
|
+
"\\n"
|
|
464
|
+
elif .type == "message_delta" then
|
|
465
|
+
(if .delta.stop_reason then "\\n[" + .delta.stop_reason + "]\\n" else empty end)
|
|
466
|
+
elif .type == "file_edit" or .type == "file_write" then
|
|
467
|
+
"\\n── Writing: " + (.path // .file // "unknown") + " ──\\n"
|
|
468
|
+
elif .type == "file_read" then
|
|
469
|
+
"── Reading: " + (.path // .file // "unknown") + " ──\\n"
|
|
470
|
+
elif .type == "bash" or .type == "command" then
|
|
471
|
+
"\\n── Running: " + (.command // .content // "") + " ──\\n"
|
|
472
|
+
elif .type == "bash_output" or .type == "command_output" then
|
|
473
|
+
(.output // .content // "") + "\\n"
|
|
474
|
+
elif .type == "result" then
|
|
475
|
+
(if .result then "\\n── Result ──\\n" + (.result | tostring) + "\\n" else empty end)
|
|
476
|
+
elif .type == "error" then
|
|
477
|
+
"\\n[Error] " + (.error.message // (.error | tostring)) + "\\n"
|
|
478
|
+
elif .type == "system" then
|
|
479
|
+
(if .message then "[System] " + .message + "\\n" else empty end)
|
|
480
|
+
elif .text then
|
|
481
|
+
.text
|
|
482
|
+
elif (.content | type) == "string" then
|
|
483
|
+
.content
|
|
484
|
+
else
|
|
485
|
+
empty
|
|
486
|
+
end
|
|
487
|
+
'
|
|
488
|
+
|
|
489
|
+
# Pass all arguments to claude with stream-json output
|
|
490
|
+
# Filter JSON lines, optionally save raw JSON, and display formatted text
|
|
491
|
+
claude \\
|
|
492
|
+
--output-format stream-json \\
|
|
493
|
+
--verbose \\
|
|
494
|
+
--print \\
|
|
495
|
+
"\$@" 2>&1 \\
|
|
496
|
+
| grep --line-buffered '^{' \\
|
|
497
|
+
| eval $TEE_CMD \\
|
|
498
|
+
| jq --unbuffered -rj "$JQ_FILTER"
|
|
499
|
+
|
|
500
|
+
echo "" # Ensure final newline
|
|
501
|
+
`;
|
|
502
|
+
}
|
|
408
503
|
// Generate .mcp.json content for Claude Code MCP servers
|
|
409
504
|
function generateMcpJson(mcpServers) {
|
|
410
505
|
return JSON.stringify({ mcpServers }, null, 2);
|
|
@@ -432,6 +527,12 @@ async function generateFiles(ralphDir, language, imageName, force = false, javaV
|
|
|
432
527
|
{ name: "docker-compose.yml", content: generateDockerCompose(imageName, dockerConfig) },
|
|
433
528
|
{ name: ".dockerignore", content: DOCKERIGNORE },
|
|
434
529
|
];
|
|
530
|
+
// Add stream script if streamJson is enabled
|
|
531
|
+
if (dockerConfig?.asciinema?.enabled && dockerConfig.asciinema.streamJson?.enabled) {
|
|
532
|
+
const outputDir = dockerConfig.asciinema.outputDir || '.recordings';
|
|
533
|
+
const saveRawJson = dockerConfig.asciinema.streamJson.saveRawJson !== false; // default true
|
|
534
|
+
files.push({ name: "ralph-stream.sh", content: generateStreamScript(outputDir, saveRawJson) });
|
|
535
|
+
}
|
|
435
536
|
for (const file of files) {
|
|
436
537
|
const filePath = join(dockerDir, file.name);
|
|
437
538
|
if (existsSync(filePath) && !force) {
|
package/dist/commands/help.js
CHANGED
|
@@ -93,7 +93,7 @@ CLI CONFIGURATION:
|
|
|
93
93
|
{
|
|
94
94
|
"cli": {
|
|
95
95
|
"command": "claude",
|
|
96
|
-
"args": [
|
|
96
|
+
"args": [],
|
|
97
97
|
"yoloArgs": ["--dangerously-skip-permissions"]
|
|
98
98
|
},
|
|
99
99
|
"cliProvider": "claude"
|
|
@@ -104,6 +104,7 @@ CLI CONFIGURATION:
|
|
|
104
104
|
- aider: AI pair programming
|
|
105
105
|
- codex: OpenAI Codex CLI
|
|
106
106
|
- gemini: Google Gemini CLI
|
|
107
|
+
- goose: Block's Goose AI coding agent
|
|
107
108
|
- opencode: Open source AI coding agent
|
|
108
109
|
- amp: Sourcegraph AMP CLI
|
|
109
110
|
- custom: Configure your own CLI
|
package/dist/commands/init.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, writeFileSync, mkdirSync, copyFileSync } 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 } from "../templates/prompts.js";
|
|
4
|
+
import { getLanguages, generatePromptTemplate, DEFAULT_PRD, DEFAULT_PROGRESS, getCliProviders, getSkillsForLanguage } from "../templates/prompts.js";
|
|
5
5
|
import { promptSelectWithArrows, promptConfirm, promptInput, promptMultiSelectWithArrows } from "../utils/prompt.js";
|
|
6
6
|
import { dockerInit } from "./docker.js";
|
|
7
7
|
// Get package root directory (works for both dev and installed package)
|
|
@@ -39,6 +39,7 @@ export async function init(args) {
|
|
|
39
39
|
let cliConfig;
|
|
40
40
|
let selectedKey;
|
|
41
41
|
let selectedTechnologies = [];
|
|
42
|
+
let selectedSkills = [];
|
|
42
43
|
let checkCommand;
|
|
43
44
|
let testCommand;
|
|
44
45
|
if (useDefaults) {
|
|
@@ -115,6 +116,29 @@ export async function init(args) {
|
|
|
115
116
|
console.log("\nNo technologies selected.");
|
|
116
117
|
}
|
|
117
118
|
}
|
|
119
|
+
// Step 4: Select skills if available for this language
|
|
120
|
+
const availableSkills = getSkillsForLanguage(selectedKey);
|
|
121
|
+
if (availableSkills.length > 0) {
|
|
122
|
+
const skillOptions = availableSkills.map(s => `${s.name} - ${s.description}`);
|
|
123
|
+
const selectedSkillNames = await promptMultiSelectWithArrows("Select AI coding rules/skills to enable (optional):", skillOptions);
|
|
124
|
+
// Convert selected display names to SkillConfig objects
|
|
125
|
+
selectedSkills = selectedSkillNames.map(sel => {
|
|
126
|
+
const idx = skillOptions.indexOf(sel);
|
|
127
|
+
const skill = availableSkills[idx];
|
|
128
|
+
return {
|
|
129
|
+
name: skill.name,
|
|
130
|
+
description: skill.description,
|
|
131
|
+
instructions: skill.instructions,
|
|
132
|
+
userInvocable: skill.userInvocable,
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
if (selectedSkills.length > 0) {
|
|
136
|
+
console.log(`\nSelected skills: ${selectedSkills.map(s => s.name).join(", ")}`);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
console.log("\nNo skills selected.");
|
|
140
|
+
}
|
|
141
|
+
}
|
|
118
142
|
// Allow custom commands for "none" language
|
|
119
143
|
checkCommand = config.checkCommand;
|
|
120
144
|
testCommand = config.testCommand;
|
|
@@ -164,6 +188,10 @@ export async function init(args) {
|
|
|
164
188
|
enabled: false,
|
|
165
189
|
autoRecord: false,
|
|
166
190
|
outputDir: ".recordings",
|
|
191
|
+
streamJson: {
|
|
192
|
+
enabled: false,
|
|
193
|
+
saveRawJson: true,
|
|
194
|
+
},
|
|
167
195
|
},
|
|
168
196
|
firewall: {
|
|
169
197
|
allowedDomains: [],
|
|
@@ -172,7 +200,7 @@ export async function init(args) {
|
|
|
172
200
|
// Claude-specific configuration (MCP servers and skills)
|
|
173
201
|
claude: {
|
|
174
202
|
mcpServers: {},
|
|
175
|
-
skills:
|
|
203
|
+
skills: selectedSkills,
|
|
176
204
|
},
|
|
177
205
|
};
|
|
178
206
|
const configPath = join(ralphDir, CONFIG_FILE);
|