diff --git a/README.md b/README.md index d79d8b62..531d391e 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,23 @@ Ralph only works if there are feedback loops: Frontend stories must include "Verify in browser using dev-browser skill" in acceptance criteria. Ralph will use the dev-browser skill to navigate to the page, interact with the UI, and confirm changes work. +### Per-Story Model Selection (Claude Code) + +When using `--tool claude`, you can specify a model per story by adding a `model` field: + +```json +{ + "id": "US-005", + "title": "Complex table reconstruction", + "model": "opus", + "notes": "Requires opus for complex multi-column table logic" +} +``` + +Ralph reads the `model` field from the next incomplete story and passes `--model` to the Claude CLI. Valid values: `sonnet`, `opus`, `haiku`, or full model IDs like `claude-opus-4-6`. Stories without a `model` field use the default model. + +This lets you use a cheaper model (sonnet) for straightforward stories and reserve opus for complex ones, optimizing cost across a run. + ### Stop Condition When all stories have `passes: true`, Ralph outputs `COMPLETE` and the loop exits. diff --git a/prd.json.example b/prd.json.example index fbc40668..13148748 100644 --- a/prd.json.example +++ b/prd.json.example @@ -14,7 +14,8 @@ ], "priority": 1, "passes": false, - "notes": "" + "notes": "", + "model": "" }, { "id": "US-002", diff --git a/ralph.sh b/ralph.sh index baff052a..6ec7826b 100755 --- a/ralph.sh +++ b/ralph.sh @@ -87,12 +87,28 @@ for i in $(seq 1 $MAX_ITERATIONS); do echo " Ralph Iteration $i of $MAX_ITERATIONS ($TOOL)" echo "===============================================================" + # Detect per-story model override from prd.json + # Reads the "model" field from the first story where passes is false + STORY_MODEL="" + STORY_ID="" + if [ -f "$PRD_FILE" ] && [[ "$TOOL" == "claude" ]]; then + STORY_MODEL=$(jq -r '[.userStories[] | select(.passes == false)] | sort_by(.priority) | first | .model // empty' "$PRD_FILE" 2>/dev/null || echo "") + STORY_ID=$(jq -r '[.userStories[] | select(.passes == false)] | sort_by(.priority) | first | .id // empty' "$PRD_FILE" 2>/dev/null || echo "") + if [ -n "$STORY_MODEL" ] && [ -n "$STORY_ID" ]; then + echo " Story $STORY_ID requests model: $STORY_MODEL" + fi + fi + # Run the selected tool with the ralph prompt if [[ "$TOOL" == "amp" ]]; then OUTPUT=$(cat "$SCRIPT_DIR/prompt.md" | amp --dangerously-allow-all 2>&1 | tee /dev/stderr) || true else # Claude Code: use --dangerously-skip-permissions for autonomous operation, --print for output - OUTPUT=$(claude --dangerously-skip-permissions --print < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || true + MODEL_FLAG="" + if [ -n "$STORY_MODEL" ]; then + MODEL_FLAG="--model $STORY_MODEL" + fi + OUTPUT=$(claude --dangerously-skip-permissions --print $MODEL_FLAG < "$SCRIPT_DIR/CLAUDE.md" 2>&1 | tee /dev/stderr) || true fi # Check for completion signal