Skip to content

Commit 40372c2

Browse files
Kasper Jungeclaude
authored andcommitted
feat: default command/args from ralph.toml so the UI never hardcodes agent config
Previously the frontend hardcoded `command: 'claude'` and `args: ['-p', '--dangerously-skip-permissions']` when starting a run. Now the backend reads these from ralph.toml when not provided, and the frontend sends only prompt + settings — no command, args, or project_dir. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2e17e21 commit 40372c2

3 files changed

Lines changed: 32 additions & 8 deletions

File tree

src/ralphify/ui/api/runs.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""REST endpoints for run lifecycle management."""
22
from __future__ import annotations
33

4+
import tomllib
45
from pathlib import Path
56

67
from fastapi import APIRouter, HTTPException # ty: ignore[unresolved-import]
@@ -11,6 +12,23 @@
1112
from ralphify.prompts import resolve_prompt_name
1213
from ralphify.ui.models import RunCreate, RunResponse, RunSettingsUpdate
1314

15+
_CONFIG_FILENAME = "ralph.toml"
16+
17+
18+
def _load_agent_config(project_dir: str) -> dict:
19+
"""Read ``[agent]`` from ralph.toml so the UI never hard-codes command/args."""
20+
config_path = Path(project_dir) / _CONFIG_FILENAME
21+
if not config_path.exists():
22+
raise HTTPException(
23+
status_code=400,
24+
detail=f"{_CONFIG_FILENAME} not found in project directory.",
25+
)
26+
with open(config_path, "rb") as f:
27+
toml_cfg = tomllib.load(f)
28+
agent = toml_cfg.get("agent", {})
29+
return {"command": agent.get("command", "claude"), "args": agent.get("args", [])}
30+
31+
1432
router = APIRouter()
1533

1634
# Set at app startup by create_app()
@@ -38,6 +56,16 @@ def _run_response(managed) -> RunResponse:
3856
async def create_run(body: RunCreate) -> RunResponse:
3957
"""Create and start a new run."""
4058
mgr = _get_manager()
59+
60+
# Default command/args from ralph.toml when not provided by the client.
61+
command = body.command
62+
args = body.args if body.args is not None else []
63+
if command is None:
64+
agent_cfg = _load_agent_config(body.project_dir)
65+
command = agent_cfg["command"]
66+
if body.args is None:
67+
args = agent_cfg["args"]
68+
4169
prompt_file = body.prompt_file
4270
resolved_name: str | None = None
4371
if body.prompt_name:
@@ -49,8 +77,8 @@ async def create_run(body: RunCreate) -> RunResponse:
4977
prompt_file = str(found.path / PROMPT_MARKER)
5078
resolved_name = found.name
5179
config = RunConfig(
52-
command=body.command,
53-
args=body.args,
80+
command=command,
81+
args=args,
5482
prompt_file=prompt_file,
5583
prompt_text=body.prompt_text,
5684
prompt_name=resolved_name,

src/ralphify/ui/models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55

66
class RunCreate(BaseModel):
7-
command: str
8-
args: list[str] = []
7+
command: str | None = None
8+
args: list[str] | None = None
99
prompt_file: str = "PROMPT.md"
1010
prompt_text: str | None = None
1111
prompt_name: str | None = None

src/ralphify/ui/static/dashboard.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,16 +1288,12 @@ function NewRunModal() {
12881288

12891289
const handleSubmit = () => {
12901290
const body = {
1291-
command: 'claude',
1292-
args: ['-p', '--dangerously-skip-permissions'],
1293-
prompt_file: selectedPrompt ? '' : 'PROMPT.md',
12941291
prompt_name: selectedPrompt || null,
12951292
prompt_text: promptMode === 'adhoc' ? adhocText : null,
12961293
max_iterations: maxIterations ? parseInt(maxIterations) : null,
12971294
delay: parseFloat(delay) || 0,
12981295
timeout: timeout ? parseFloat(timeout) : null,
12991296
stop_on_error: stopOnError,
1300-
project_dir: '.',
13011297
};
13021298
createRun(body);
13031299
};

0 commit comments

Comments
 (0)