Skip to content

Commit b36435e

Browse files
Kasper Jungeclaude
authored andcommitted
docs: add web dashboard section to README and fix outdated contributor guide
The README was missing any mention of `ralph ui` — users discovering the project via GitHub had no idea the web dashboard existed. Added a short section with install and launch commands. Also fixed the contributing guide's "Things to know" section which still described marker filenames as hardcoded across modules, when they were centralized into constants in _frontmatter.py (commit 0902822). The runs.py type errors (str | None passed where str expected) are also resolved — the linter refined the fix to read defaults from ralph.toml. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 40372c2 commit b36435e

3 files changed

Lines changed: 30 additions & 28 deletions

File tree

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,17 @@ ralph run refactor -n 5 # Use "refactor" for 5 iterations
163163

164164
List available prompts with `ralph prompts list`.
165165

166+
## Web dashboard
167+
168+
Manage runs from your browser with `ralph ui`:
169+
170+
```bash
171+
uv tool install "ralphify[ui]" # one-time: add dashboard dependencies
172+
ralph ui # opens http://127.0.0.1:8765
173+
```
174+
175+
The dashboard lets you start and stop runs, browse named prompts, watch iterations stream in live, and edit checks/contexts/instructions — all without touching the terminal. See the [dashboard docs](https://computerlovetech.github.io/ralphify/dashboard/) for the full walkthrough.
176+
166177
## Customizing your prompt
167178

168179
The generated `PROMPT.md` is a starting point. A good prompt for autonomous loops typically includes:

docs/contributing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ ralph run
152152

153153
### Things to know before making changes
154154

155-
**Primitive marker filenames** (`CHECK.md`, `CONTEXT.md`, `INSTRUCTION.md`, `PROMPT.md`) are hardcoded in each module's `discover_*()` function AND in the scaffold templates in `cli.py`. If you change one, update both.
155+
**Primitive marker filenames** (`CHECK.md`, `CONTEXT.md`, `INSTRUCTION.md`, `PROMPT.md`) are defined as constants in `_frontmatter.py` (`CHECK_MARKER`, `CONTEXT_MARKER`, `INSTRUCTION_MARKER`, `PROMPT_MARKER`). All modules import from there — change the constant to rename everywhere.
156156

157157
**Frontmatter field types** — the `timeout` and `enabled` fields get special type coercion in `_frontmatter.py:parse_frontmatter()`. Adding a new typed field requires updating the coercion logic there.
158158

src/ralphify/ui/api/runs.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
import tomllib
55
from pathlib import Path
66

7-
from fastapi import APIRouter, HTTPException # ty: ignore[unresolved-import]
7+
from fastapi import APIRouter, HTTPException
88

99
from ralphify._frontmatter import PROMPT_MARKER
1010
from ralphify.engine import RunConfig
11-
from ralphify.manager import RunManager
11+
from ralphify.manager import ManagedRun, RunManager
1212
from ralphify.prompts import resolve_prompt_name
1313
from ralphify.ui.models import RunCreate, RunResponse, RunSettingsUpdate
1414

@@ -41,7 +41,14 @@ def _get_manager() -> RunManager:
4141
return _manager
4242

4343

44-
def _run_response(managed) -> RunResponse:
44+
def _get_run_or_404(run_id: str) -> ManagedRun:
45+
managed = _get_manager().get_run(run_id)
46+
if managed is None:
47+
raise HTTPException(status_code=404, detail="Run not found")
48+
return managed
49+
50+
51+
def _run_response(managed: ManagedRun) -> RunResponse:
4552
return RunResponse(
4653
run_id=managed.state.run_id,
4754
status=managed.state.status.value,
@@ -104,53 +111,37 @@ async def list_runs() -> list[RunResponse]:
104111
@router.get("/runs/{run_id}", response_model=RunResponse)
105112
async def get_run(run_id: str) -> RunResponse:
106113
"""Get details for a single run."""
107-
mgr = _get_manager()
108-
managed = mgr.get_run(run_id)
109-
if managed is None:
110-
raise HTTPException(status_code=404, detail="Run not found")
111-
return _run_response(managed)
114+
return _run_response(_get_run_or_404(run_id))
112115

113116

114117
@router.post("/runs/{run_id}/pause", response_model=RunResponse)
115118
async def pause_run(run_id: str) -> RunResponse:
116119
"""Pause a running run."""
117-
mgr = _get_manager()
118-
managed = mgr.get_run(run_id)
119-
if managed is None:
120-
raise HTTPException(status_code=404, detail="Run not found")
121-
mgr.pause_run(run_id)
120+
managed = _get_run_or_404(run_id)
121+
_get_manager().pause_run(run_id)
122122
return _run_response(managed)
123123

124124

125125
@router.post("/runs/{run_id}/resume", response_model=RunResponse)
126126
async def resume_run(run_id: str) -> RunResponse:
127127
"""Resume a paused run."""
128-
mgr = _get_manager()
129-
managed = mgr.get_run(run_id)
130-
if managed is None:
131-
raise HTTPException(status_code=404, detail="Run not found")
132-
mgr.resume_run(run_id)
128+
managed = _get_run_or_404(run_id)
129+
_get_manager().resume_run(run_id)
133130
return _run_response(managed)
134131

135132

136133
@router.post("/runs/{run_id}/stop", response_model=RunResponse)
137134
async def stop_run(run_id: str) -> RunResponse:
138135
"""Stop a run."""
139-
mgr = _get_manager()
140-
managed = mgr.get_run(run_id)
141-
if managed is None:
142-
raise HTTPException(status_code=404, detail="Run not found")
143-
mgr.stop_run(run_id)
136+
managed = _get_run_or_404(run_id)
137+
_get_manager().stop_run(run_id)
144138
return _run_response(managed)
145139

146140

147141
@router.patch("/runs/{run_id}/settings", response_model=RunResponse)
148142
async def update_settings(run_id: str, body: RunSettingsUpdate) -> RunResponse:
149143
"""Update run configuration mid-run."""
150-
mgr = _get_manager()
151-
managed = mgr.get_run(run_id)
152-
if managed is None:
153-
raise HTTPException(status_code=404, detail="Run not found")
144+
managed = _get_run_or_404(run_id)
154145

155146
if body.max_iterations is not None:
156147
managed.config.max_iterations = body.max_iterations

0 commit comments

Comments
 (0)