Skip to content

Commit d191e2d

Browse files
Kasper Jungeclaude
authored andcommitted
refactor: rename context.* placeholders to ralph.*
{{ ralph.name }}, {{ ralph.iteration }}, and {{ ralph.max_iterations }} read more naturally in RALPH.md files than the context.* prefix. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 84f3b87 commit d191e2d

10 files changed

Lines changed: 42 additions & 42 deletions

File tree

docs/changelog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ All notable changes to ralphify are documented here.
1212

1313
### Added
1414

15-
- **Context placeholders** — ralphs can now access runtime metadata via `{{ context.name }}` (ralph directory name), `{{ context.iteration }}` (current iteration, 1-based), and `{{ context.max_iterations }}` (total iterations if `-n` was set, empty otherwise). No frontmatter configuration needed.
15+
- **Ralph placeholders** — ralphs can now access runtime metadata via `{{ ralph.name }}` (ralph directory name), `{{ ralph.iteration }}` (current iteration, 1-based), and `{{ ralph.max_iterations }}` (total iterations if `-n` was set, empty otherwise). No frontmatter configuration needed.
1616

1717
---
1818

docs/contributing/codebase-map.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ src/ralphify/ # All source code
2222
├── cli.py # CLI commands (run, new, init) — delegates to engine for the loop
2323
├── engine.py # Core run loop orchestration with structured event emission
2424
├── manager.py # Multi-run orchestration (concurrent runs via threads)
25-
├── _resolver.py # Template placeholder resolution ({{ commands.* }}, {{ args.* }}, {{ context.* }})
25+
├── _resolver.py # Template placeholder resolution ({{ commands.* }}, {{ args.* }}, {{ ralph.* }})
2626
├── _agent.py # Run agent subprocesses (streaming + blocking modes, log writing)
2727
├── _run_types.py # RunConfig, RunState, RunStatus, Command — shared data types
2828
├── _runner.py # Execute shell commands with timeout and capture output

docs/quick-reference.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,12 @@ Your instructions here. Use {{ args.dir }} for user arguments.
107107
- `--` ends flag parsing: `ralph run my-ralph -- --verbose ./src` treats `--verbose` as a positional value
108108
- Missing args resolve to empty string
109109

110-
### Context placeholders
110+
### Ralph placeholders
111111

112112
```markdown
113-
{{ context.name }} # Ralph directory name (e.g. "my-ralph")
114-
{{ context.iteration }} # Current iteration number (1-based)
115-
{{ context.max_iterations }} # Total iterations if -n was set, empty otherwise
113+
{{ ralph.name }} # Ralph directory name (e.g. "my-ralph")
114+
{{ ralph.iteration }} # Current iteration number (1-based)
115+
{{ ralph.max_iterations }} # Total iterations if -n was set, empty otherwise
116116
```
117117

118118
- Automatically available — no frontmatter configuration needed
@@ -124,7 +124,7 @@ Each iteration:
124124

125125
1. Re-read `RALPH.md` from disk
126126
2. Run all commands in order, capture output
127-
3. Resolve `{{ commands.* }}`, `{{ args.* }}`, and `{{ context.* }}` placeholders
127+
3. Resolve `{{ commands.* }}`, `{{ args.* }}`, and `{{ ralph.* }}` placeholders
128128
4. Pipe assembled prompt to agent via stdin
129129
5. Wait for agent to exit
130130
6. Repeat

docs/writing-prompts.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ Rules of thumb:
341341
- **Commands:** Pick the 2-3 most useful signals. Don't add commands whose output the agent doesn't need.
342342
- **Command output:** Can be long. If your commands produce verbose output, consider using scripts that filter to the relevant lines.
343343
- **User args:** Use `{{ args.name }}` to make ralphs reusable — pass project-specific values from the CLI instead of hardcoding them in the prompt. Args also work in command `run` strings (e.g., `run: gh issue view {{ args.issue }}`).
344-
- **Context placeholders:** Use `{{ context.name }}`, `{{ context.iteration }}`, and `{{ context.max_iterations }}` to access runtime metadata — the ralph's directory name, which iteration this is, and the total number if `--n` was set.
344+
- **Ralph placeholders:** Use `{{ ralph.name }}`, `{{ ralph.iteration }}`, and `{{ ralph.max_iterations }}` to access runtime metadata — the ralph's directory name, which iteration this is, and the total number if `--n` was set.
345345
- **Working directory:** Commands run from the project root by default. Commands starting with `./` run from the ralph directory — handy for bundling helper scripts next to your `RALPH.md`.
346346

347347
## Next steps

src/ralphify/_frontmatter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
FIELD_COMMANDS = "commands"
2626
FIELD_ARGS = "args"
2727
FIELD_CREDIT = "credit"
28-
FIELD_CONTEXT = "context"
28+
FIELD_RALPH = "ralph"
2929

3030
# Sub-field names within each command mapping.
3131
CMD_FIELD_NAME = "name"

src/ralphify/_resolver.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import re
1414

15-
from ralphify._frontmatter import CMD_NAME_RE, FIELD_ARGS, FIELD_COMMANDS, FIELD_CONTEXT
15+
from ralphify._frontmatter import CMD_NAME_RE, FIELD_ARGS, FIELD_COMMANDS, FIELD_RALPH
1616

1717

1818
# Pattern matching ``{{ args.<name> }}`` placeholders — used by resolve_args
@@ -37,26 +37,26 @@ def _replace(match: re.Match) -> str:
3737

3838
# Single pattern matching all placeholder kinds for single-pass resolution.
3939
_ALL_PATTERN = re.compile(
40-
rf"\{{\{{\s*({FIELD_COMMANDS}|{FIELD_ARGS}|{FIELD_CONTEXT})\.({CMD_NAME_RE.pattern})\s*\}}\}}"
40+
rf"\{{\{{\s*({FIELD_COMMANDS}|{FIELD_ARGS}|{FIELD_RALPH})\.({CMD_NAME_RE.pattern})\s*\}}\}}"
4141
)
4242

4343

4444
def resolve_all(
4545
prompt: str,
4646
command_outputs: dict[str, str],
4747
user_args: dict[str, str],
48-
context: dict[str, str] | None = None,
48+
ralph_context: dict[str, str] | None = None,
4949
) -> str:
5050
"""Resolve all placeholders in a single pass to prevent cross-contamination.
5151
5252
Resolves ``{{ commands.name }}``, ``{{ args.name }}``, and
53-
``{{ context.name }}`` in a single pass so values inserted by one
53+
``{{ ralph.name }}`` in a single pass so values inserted by one
5454
kind of placeholder are not re-processed as the other kind.
5555
"""
5656
lookups: dict[str, dict[str, str]] = {
5757
FIELD_COMMANDS: command_outputs,
5858
FIELD_ARGS: user_args,
59-
FIELD_CONTEXT: context or {},
59+
FIELD_RALPH: ralph_context or {},
6060
}
6161

6262
def _replace(match: re.Match) -> str:

src/ralphify/engine.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ def _run_commands(
120120
return results
121121

122122

123-
def _build_context(config: RunConfig, state: RunState) -> dict[str, str]:
124-
"""Build the context dict for ``{{ context.X }}`` placeholders."""
123+
def _build_ralph_context(config: RunConfig, state: RunState) -> dict[str, str]:
124+
"""Build the context dict for ``{{ ralph.X }}`` placeholders."""
125125
ctx: dict[str, str] = {
126126
"name": config.ralph_dir.name,
127127
"iteration": str(state.iteration),
@@ -143,8 +143,8 @@ def _assemble_prompt(
143143
"""
144144
raw = config.ralph_file.read_text(encoding="utf-8")
145145
_, prompt = parse_frontmatter(raw)
146-
context = _build_context(config, state)
147-
prompt = resolve_all(prompt, command_outputs, config.args, context)
146+
ralph_context = _build_ralph_context(config, state)
147+
prompt = resolve_all(prompt, command_outputs, config.args, ralph_context)
148148
if config.credit:
149149
prompt += _CREDIT_INSTRUCTION
150150
return prompt

src/ralphify/skills/new-ralph/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ If any tests are failing above, fix them before continuing.
8989
The body is the prompt. It supports three placeholder types:
9090
- `{{ commands.<name> }}` — replaced with command output each iteration
9191
- `{{ args.<name> }}` — replaced with CLI arguments
92-
- `{{ context.<name> }}` — replaced with runtime metadata (`name`, `iteration`, `max_iterations`)
92+
- `{{ ralph.<name> }}` — replaced with runtime metadata (`name`, `iteration`, `max_iterations`)
9393

9494
HTML comments (`<!-- ... -->`) are automatically stripped before the prompt is assembled. They never reach the agent. Use them for notes about why rules exist or TODOs for prompt maintenance.
9595

tests/test_engine.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -891,10 +891,10 @@ def test_arg_values_not_resolved_as_command_placeholders(self, tmp_path):
891891
assert "Filter: {{ commands.tests }}" in result
892892
assert "Tests: 5 passed" in result
893893

894-
def test_resolves_context_placeholders(self, tmp_path):
894+
def test_resolves_ralph_placeholders(self, tmp_path):
895895
config = make_config(
896896
tmp_path,
897-
"Name: {{ context.name }}, Iter: {{ context.iteration }}, Max: {{ context.max_iterations }}",
897+
"Name: {{ ralph.name }}, Iter: {{ ralph.iteration }}, Max: {{ ralph.max_iterations }}",
898898
max_iterations=5,
899899
credit=False,
900900
)
@@ -905,10 +905,10 @@ def test_resolves_context_placeholders(self, tmp_path):
905905

906906
assert result == "Name: my-ralph, Iter: 3, Max: 5"
907907

908-
def test_context_max_iterations_empty_when_unlimited(self, tmp_path):
908+
def test_ralph_max_iterations_empty_when_unlimited(self, tmp_path):
909909
config = make_config(
910910
tmp_path,
911-
"Max: {{ context.max_iterations }}",
911+
"Max: {{ ralph.max_iterations }}",
912912
max_iterations=None,
913913
credit=False,
914914
)
@@ -919,10 +919,10 @@ def test_context_max_iterations_empty_when_unlimited(self, tmp_path):
919919

920920
assert result == "Max: "
921921

922-
def test_context_name_is_ralph_dir_name(self, tmp_path):
922+
def test_ralph_name_is_ralph_dir_name(self, tmp_path):
923923
config = make_config(
924924
tmp_path,
925-
"Name: {{ context.name }}",
925+
"Name: {{ ralph.name }}",
926926
max_iterations=1,
927927
credit=False,
928928
)

tests/test_resolver.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -154,41 +154,41 @@ def test_hyphenated_arg_name(self):
154154
assert result == "/tmp"
155155

156156

157-
class TestResolveContext:
158-
"""Tests for {{ context.X }} placeholders passed through resolve_all."""
157+
class TestResolveRalphContext:
158+
"""Tests for {{ ralph.X }} placeholders passed through resolve_all."""
159159

160-
def test_resolves_context_name(self):
161-
result = resolve_all("Ralph: {{ context.name }}", {}, {}, {"name": "my-ralph"})
160+
def test_resolves_ralph_name(self):
161+
result = resolve_all("Ralph: {{ ralph.name }}", {}, {}, {"name": "my-ralph"})
162162
assert result == "Ralph: my-ralph"
163163

164-
def test_resolves_context_iteration(self):
165-
result = resolve_all("Iter: {{ context.iteration }}", {}, {}, {"iteration": "3"})
164+
def test_resolves_ralph_iteration(self):
165+
result = resolve_all("Iter: {{ ralph.iteration }}", {}, {}, {"iteration": "3"})
166166
assert result == "Iter: 3"
167167

168-
def test_resolves_context_max_iterations(self):
168+
def test_resolves_ralph_max_iterations(self):
169169
result = resolve_all(
170-
"Max: {{ context.max_iterations }}", {}, {}, {"max_iterations": "10"},
170+
"Max: {{ ralph.max_iterations }}", {}, {}, {"max_iterations": "10"},
171171
)
172172
assert result == "Max: 10"
173173

174-
def test_unknown_context_key_resolves_to_empty(self):
175-
result = resolve_all("{{ context.unknown }}", {}, {}, {"name": "test"})
174+
def test_unknown_ralph_key_resolves_to_empty(self):
175+
result = resolve_all("{{ ralph.unknown }}", {}, {}, {"name": "test"})
176176
assert result == ""
177177

178-
def test_no_context_clears_placeholders(self):
179-
result = resolve_all("{{ context.name }}", {}, {})
178+
def test_no_ralph_context_clears_placeholders(self):
179+
result = resolve_all("{{ ralph.name }}", {}, {})
180180
assert result == ""
181181

182-
def test_context_with_commands_and_args(self):
182+
def test_ralph_with_commands_and_args(self):
183183
result = resolve_all(
184-
"{{ commands.tests }} {{ args.dir }} {{ context.iteration }}",
184+
"{{ commands.tests }} {{ args.dir }} {{ ralph.iteration }}",
185185
{"tests": "ok"}, {"dir": "./src"}, {"iteration": "2"},
186186
)
187187
assert result == "ok ./src 2"
188188

189-
def test_context_value_not_resolved_as_command_placeholder(self):
189+
def test_ralph_value_not_resolved_as_command_placeholder(self):
190190
result = resolve_all(
191-
"Ctx: {{ context.name }}\nCmd: {{ commands.tests }}",
191+
"Ctx: {{ ralph.name }}\nCmd: {{ commands.tests }}",
192192
{"tests": "5 passed"},
193193
{},
194194
{"name": "{{ commands.tests }}"},
@@ -197,5 +197,5 @@ def test_context_value_not_resolved_as_command_placeholder(self):
197197
assert "Cmd: 5 passed" in result
198198

199199
def test_whitespace_tolerant(self):
200-
result = resolve_all("{{ context.name }}", {}, {}, {"name": "test"})
200+
result = resolve_all("{{ ralph.name }}", {}, {}, {"name": "test"})
201201
assert result == "test"

0 commit comments

Comments
 (0)