Skip to content

Commit 2c6c34f

Browse files
Kasper Jungeclaude
authored andcommitted
docs: document command parsing rules for users who use shell features in check/context commands
The cookbook had a context example using pipes and redirections (2>&1 | tail -20) which silently fails because commands are run via subprocess.run without shell=True. Fixed the example and added: - Command parsing section in primitives reference - Troubleshooting entry for the common gotcha - Cross-links between all three pages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6bede8f commit 2c6c34f

File tree

3 files changed

+41
-4
lines changed

3 files changed

+41
-4
lines changed

docs/cookbook.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,17 @@ Do not modify tests unless they are incorrect.
155155

156156
```markdown
157157
---
158-
command: uv run pytest --tb=line -q 2>&1 | tail -20
158+
command: uv run pytest --tb=line -q
159159
timeout: 60
160160
enabled: true
161161
---
162162
## Current test status
163163
```
164164

165-
This context gives the agent a snapshot of which tests are failing before it starts, so it can pick the most important one without running the full suite first.
165+
This context gives the agent a snapshot of which tests are failing before it starts, so it can pick the most important one without running the full suite first. Ralphify automatically truncates output to 5,000 characters, so you don't need to limit it yourself.
166+
167+
!!! note "Need pipes or redirections?"
168+
Commands are parsed with `shlex` and run directly — not through a shell. If you need pipes (`|`), redirections (`2>&1`), or other shell features, use a [`run.sh` script](primitives.md#using-a-script-instead-of-a-command) instead.
166169

167170
### Setup commands
168171

docs/primitives.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,20 @@ The body text below the frontmatter is the **failure instruction** — it gets i
4747

4848
| Field | Type | Default | Description |
4949
|---|---|---|---|
50-
| `command` | string || Shell command to run |
50+
| `command` | string || Command to run (see [command parsing](#command-parsing) below) |
5151
| `timeout` | int | `60` | Max seconds before the check is killed |
5252
| `enabled` | bool | `true` | Set to `false` to skip without deleting |
5353

54+
### Command parsing
55+
56+
Commands are split with Python's `shlex.split()` and executed **directly** — not through a shell. This means:
57+
58+
- Simple commands work as expected: `uv run pytest -x`, `npm test`, `ruff check .`
59+
- Shell features like **pipes** (`|`), **redirections** (`2>&1`, `>`), **chaining** (`&&`, `||`), and **variable expansion** (`$VAR`) do **not** work
60+
- Arguments with spaces need quoting: `pytest "tests/my dir/"` works correctly
61+
62+
If you need shell features, use a [script](#using-a-script-instead-of-a-command) instead.
63+
5464
### Using a script instead of a command
5565

5666
Instead of a `command` in frontmatter, you can place an executable script named `run.*` (e.g. `run.sh`, `run.py`) in the check directory:
@@ -142,7 +152,7 @@ A context can also be purely static (no command) — just omit the `command` fie
142152

143153
| Field | Type | Default | Description |
144154
|---|---|---|---|
145-
| `command` | string || Shell command whose stdout is captured |
155+
| `command` | string || Command whose stdout is captured (see [command parsing](#command-parsing)) |
146156
| `timeout` | int | `30` | Max seconds before the command is killed |
147157
| `enabled` | bool | `true` | Set to `false` to skip without deleting |
148158

docs/troubleshooting.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,30 @@ This is different from `PROMPT.md`, which **is** re-read every iteration. See [W
108108

109109
## Check issues
110110

111+
### Command with pipes or redirections not working
112+
113+
Commands in frontmatter are parsed with `shlex` and run **directly** — not through a shell. Shell features like pipes (`|`), redirections (`2>&1`), chaining (`&&`), and variable expansion (`$VAR`) silently fail or produce unexpected results.
114+
115+
**Won't work:**
116+
117+
```yaml
118+
command: pytest --tb=line -q 2>&1 | tail -20
119+
```
120+
121+
**Fix:** Use a `run.sh` script instead:
122+
123+
```bash
124+
#!/bin/bash
125+
# .ralph/checks/my-check/run.sh
126+
pytest --tb=line -q 2>&1 | tail -20
127+
```
128+
129+
```bash
130+
chmod +x .ralph/checks/my-check/run.sh
131+
```
132+
133+
See [command parsing](primitives.md#command-parsing) for details.
134+
111135
### Checks always failing
112136

113137
Run the check command manually to see if it works:

0 commit comments

Comments
 (0)