Skip to content

Latest commit

 

History

History
878 lines (634 loc) · 22.6 KB

File metadata and controls

878 lines (634 loc) · 22.6 KB
name ralph-dashboard
description Monitor and control Ralph Loop AI agent sessions via the Ralph Dashboard REST API. Use to check project status, view iterations/stats, start/stop/pause loops, inject instructions, manage plans/specs, browse git history, and run notification-driven recovery workflows before escalating to a human/user.

Ralph Dashboard API Skill

Interact with a Ralph Dashboard instance to monitor and control Ralph Loop sessions.

First-Time Installation

If the dashboard isn't running yet, follow these steps. If it's already deployed, skip to Setup.

# 1. Clone and install
git clone https://github.com/Endogen/ralph-dashboard.git
cd ralph-dashboard

# 2. Install dashboard + CLI
./scripts/install.sh

# If the CLI is not found, add the wrapper directory to PATH
export PATH="$HOME/.local/bin:$PATH"

# 3. Create runtime config and first user (interactive)
ralph-dashboard init

# 4. Validate environment
ralph-dashboard doctor

# 5. Start the dashboard (choose one)
# 5a. Linux/systemd: install + start user service
ralph-dashboard service install --user --start

# 5b. macOS/manual: run uvicorn directly
set -a
source ~/.config/ralph-dashboard/env
set +a
cd backend
.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port "$RALPH_PORT"

For production deployment with systemd and nginx, see the README.

Setup

To use the API, you need:

  • Base URL of the dashboard (e.g. https://ralph.example.com or http://localhost:8420)
  • Username and password for JWT authentication

Store credentials somewhere the agent can access them (e.g. TOOLS.md, env vars, or a config file). Never hardcode passwords in scripts.

Authentication

All API endpoints (except /api/health, /api/auth/login, /api/auth/refresh) require a Bearer JWT token.

Login

curl -s -X POST "$BASE_URL/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"USER","password":"PASS"}' | jq

Response:

{
  "access_token": "eyJ...",
  "refresh_token": "eyJ...",
  "token_type": "bearer"
}

Use access_token in all subsequent requests as Authorization: Bearer <access_token>.

Refresh

When the access token expires, refresh it:

curl -s -X POST "$BASE_URL/api/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{"refresh_token":"eyJ..."}' | jq

Returns a new access_token.

Auth header for all requests

TOKEN="<access_token>"
AUTH="Authorization: Bearer $TOKEN"

API Reference

Health Check (no auth)

curl -s "$BASE_URL/api/health" | jq
# {"status": "ok"}

Projects

List all projects

curl -s -H "$AUTH" "$BASE_URL/api/projects" | jq

Response: array of project summaries:

[
  {
    "id": "my-project",
    "name": "my-project",
    "path": "/home/user/projects/my-project",
    "status": "running"
  }
]

Status values: running, paused, stopped, complete.

The id is a slug derived from the directory name. Use it in all project-specific endpoints.

Get project detail

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}" | jq

Register a project manually

curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects" \
  -d '{"path":"/path/to/project"}' | jq

The project directory must contain a .ralph/ subdirectory.

Unregister a project

curl -s -X DELETE -H "$AUTH" "$BASE_URL/api/projects/{id}"

Iterations

List iterations

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/iterations?status=all&limit=50&offset=0" | jq

Query params:

  • status: all (default), success, error
  • limit: 1–500 (default 50)
  • offset: pagination offset

Response:

{
  "iterations": [
    {
      "number": 5,
      "max_iterations": 50,
      "start_timestamp": "2026-02-08T01:23:41+01:00",
      "end_timestamp": "2026-02-08T01:26:00+01:00",
      "duration_seconds": 139,
      "tokens_used": 62.698,
      "status": "success",
      "has_errors": false,
      "errors": [],
      "tasks_completed": ["1.5"],
      "commit": "abc1234",
      "test_passed": true
    }
  ],
  "total": 15
}

Get iteration detail (includes log output)

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/iterations/{number}" | jq

Returns the same fields as above plus log_output (string with full terminal output for that iteration).


Stats

Get aggregated project stats

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/stats" | jq

Response:

{
  "total_iterations": 15,
  "total_tokens": 940.5,
  "total_cost_usd": 5.64,
  "total_duration_seconds": 2100,
  "avg_iteration_duration_seconds": 140,
  "avg_tokens_per_iteration": 62.7,
  "tasks_done": 12,
  "tasks_total": 18,
  "errors_count": 2,
  "projected_completion": "2026-02-08T04:00:00+01:00",
  "projected_total_cost_usd": 8.46,
  "velocity": {
    "tasks_per_hour": 3.4,
    "tasks_remaining": 6,
    "hours_remaining": 1.76
  },
  "health_breakdown": {
    "productive": 12,
    "partial": 1,
    "failed": 2
  },
  "tokens_by_phase": [
    {"phase": "Phase 1: Core", "tokens": 450.2}
  ]
}

Get project report (markdown summary)

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/report" | jq -r '.content'

Returns a human-readable markdown report of the project's progress.


System Metrics

Get system and process metrics

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/system" | jq

Response:

{
  "process": {
    "pid": 12345,
    "rss_mb": 85.2,
    "children_rss_mb": 312.4,
    "total_rss_mb": 397.6,
    "cpu_percent": 12.5,
    "child_count": 3
  },
  "system": {
    "ram_total_mb": 16384.0,
    "ram_used_mb": 8192.0,
    "ram_available_mb": 8192.0,
    "ram_percent": 50.0,
    "cpu_load_1m": 1.2,
    "cpu_load_5m": 0.8,
    "cpu_load_15m": 0.6,
    "cpu_core_count": 8,
    "disk_total_gb": 500.0,
    "disk_used_gb": 120.0,
    "disk_free_gb": 380.0,
    "disk_percent": 24.0,
    "uptime_seconds": 864000.0
  }
}

Process metrics are gathered from the PID in .ralph/ralph.pid. If the loop isn't running, process.pid is null and memory/CPU values are 0.


Implementation Plan

Get parsed plan

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/plan" | jq

Returns the parsed IMPLEMENTATION_PLAN.md with phases, tasks, completion status.

Update plan

curl -s -X PUT -H "$AUTH" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects/{id}/plan" \
  -d '{"content":"# Implementation Plan\n\n## Phase 1\n- [ ] Task 1\n- [x] Task 2\n"}' | jq

Loop Control

Start a loop

curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects/{id}/start" \
  -d '{"max_iterations":50,"cli":"codex","flags":"--full-auto","test_command":"pytest"}' | jq

All fields are optional — defaults come from .ralph/config.json. Set max_iterations to 0 for unlimited iterations.

Response:

{
  "project_id": "my-project",
  "pid": 12345,
  "command": ["./scripts/ralph.sh", "50"]
}

Stop a loop

curl -s -X POST -H "$AUTH" "$BASE_URL/api/projects/{id}/stop" | jq
# {"stopped": true}

Pause a loop

curl -s -X POST -H "$AUTH" "$BASE_URL/api/projects/{id}/pause" | jq
# {"paused": true}

Resume a loop

curl -s -X POST -H "$AUTH" "$BASE_URL/api/projects/{id}/resume" | jq
# {"resumed": true}

Inject instructions into next iteration

curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects/{id}/inject" \
  -d '{"message":"Use async SQLAlchemy sessions instead of sync"}' | jq

The message is written to .ralph/inject.md and automatically appended to AGENTS.md at the start of the next iteration.


Loop Configuration

Read config

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/config" | jq

Response:

{
  "cli": "codex",
  "flags": "--full-auto",
  "max_iterations": 20,
  "test_command": "pytest",
  "model_pricing": {"codex": 0.006, "claude": 0.015}
}

Update config

curl -s -X PUT -H "$AUTH" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects/{id}/config" \
  -d '{"cli":"claude","flags":"--dangerously-skip-permissions","max_iterations":30,"test_command":"pytest -q","model_pricing":{"claude":0.015}}' | jq

max_iterations: 0 in config means unlimited loop iterations.


Project Files

Read AGENTS.md or PROMPT.md

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/files/agents" | jq
curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/files/prompt" | jq

Response:

{"name": "AGENTS.md", "content": "# AGENTS.md\n..."}

Update AGENTS.md or PROMPT.md

curl -s -X PUT -H "$AUTH" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects/{id}/files/agents" \
  -d '{"content":"# AGENTS.md\n\nUpdated content..."}' | jq

Spec Files

List specs

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/specs" | jq

Response:

[
  {"name": "overview.md", "size": 1234, "modified": "2026-02-08T01:00:00"}
]

Read a spec

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/specs/{name}" | jq

Create a spec

curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects/{id}/specs" \
  -d '{"name":"auth.md","content":"# Auth Spec\n..."}' | jq

Update a spec

curl -s -X PUT -H "$AUTH" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects/{id}/specs/{name}" \
  -d '{"content":"# Updated spec\n..."}' | jq

Delete a spec

curl -s -X DELETE -H "$AUTH" "$BASE_URL/api/projects/{id}/specs/{name}"

Git History

Commit log

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/git/log?limit=20&offset=0" | jq

Commit diff

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/git/diff/{commit_hash}" | jq

Notifications

Get notification history

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/notifications" | jq

Response:

[
  {
    "timestamp": "2026-02-08T02:30:00+01:00",
    "prefix": "DONE",
    "message": "All tasks complete.",
    "status": "delivered",
    "iteration": 15,
    "details": null,
    "source": "pending-notification.txt"
  }
]

Notification-Driven Orchestration

Use this section when an orchestration agent sits between Ralph and a human/user.

Goal:

  • react automatically to loop notifications
  • attempt safe recovery steps
  • escalate to a human/user only when automation cannot safely resolve the issue

Trigger Source

Ralph writes .ralph/pending-notification.txt, and the dashboard records notification history in .ralph/notifications/events.jsonl.

Treat the dashboard notification history endpoint as the source of truth:

curl -s -H "$AUTH" "$BASE_URL/api/projects/{id}/notifications" | jq

Triage Workflow

When a new notification arrives:

  1. Identify project_id and latest notification (prefix, message, iteration, details).
  2. Fetch current state:
    • GET /api/projects/{id}
    • GET /api/projects/{id}/stats
    • GET /api/projects/{id}/iterations?status=all&limit=20
    • GET /api/projects/{id}/iterations/{iteration} (if iteration is known)
  3. Apply prefix-based policy:
    • PROGRESS or DONE: record status, no intervention required.
    • DECISION: ask the human/user for a decision, then POST /inject with approved guidance.
    • ERROR or BLOCKED: attempt bounded auto-recovery (below), then re-check health.
  4. If recovery fails or confidence is low, escalate to a human/user with concise context and recommended next action.

Bounded Auto-Recovery Policy

For ERROR / BLOCKED notifications, use conservative limits:

  • max 3 automated recovery attempts per incident
  • max 1 restart/resume action per attempt
  • require improved signal before next attempt (e.g. no new error in next iteration)

Suggested attempt sequence:

  1. If project status is paused, call POST /api/projects/{id}/resume.
  2. If project status is stopped and work is incomplete, call POST /api/projects/{id}/start.
  3. Inject focused remediation guidance:
    • POST /api/projects/{id}/inject
    • include explicit constraints (reproduce failure, isolate root cause, patch minimally, rerun tests, report if still blocked).
  4. Wait for next iteration/notification and evaluate:
    • if healthy progression resumes, clear incident
    • if repeated failure persists, escalate to a human/user

Escalation Standard (Human/User)

When escalating, include:

  • project id and current status
  • last failing iteration number
  • short error summary
  • actions already attempted by automation
  • clear request for human/user input or decision

Example Incident Snapshot Commands

PROJECT_ID="my-project"

curl -s -H "$AUTH" "$BASE_URL/api/projects/$PROJECT_ID" | jq
curl -s -H "$AUTH" "$BASE_URL/api/projects/$PROJECT_ID/stats" | jq
curl -s -H "$AUTH" "$BASE_URL/api/projects/$PROJECT_ID/notifications" | jq '.[0]'
curl -s -H "$AUTH" "$BASE_URL/api/projects/$PROJECT_ID/iterations?status=all&limit=5" | jq

Use these outputs to decide whether to resume/restart, inject guidance, or escalate.


From Idea to Running Loop

This section covers the complete workflow for turning a project description into a running Ralph loop monitored by the dashboard. Follow these steps in order.

Step 1: Create the project directory

mkdir -p ~/projects/my-project
cd ~/projects/my-project
git init

The directory must be under one of the paths in RALPH_PROJECT_DIRS (default: ~/projects).

Step 2: Write specs from the description

Create a specs/ directory with one or more markdown files describing what to build. Start with an overview, then break down backend and frontend if applicable.

mkdir -p specs

specs/overview.md — the main spec:

# Project Name

## Goal

[Paste or expand the project description into a clear goal statement]

## Tech Stack

### Backend
- Python 3.12+, FastAPI, SQLAlchemy 2.0 (async), SQLite, pytest

### Frontend
- React 19, TypeScript, Vite, Tailwind CSS 4, shadcn/ui

## Success Criteria
- [ ] Criterion 1
- [ ] Criterion 2

## Architecture

[Describe the high-level architecture: directory structure, API design, data models]

Add specs/backend.md and specs/frontend.md for detailed requirements if the project is large enough. Be specific about data models, API endpoints, UI components, and behavior.

Tip: The more detailed your specs, the better Ralph performs. Vague specs lead to vague implementations.

Step 3: Create the implementation plan

Create IMPLEMENTATION_PLAN.md in the project root. This is the task list Ralph works through — it picks the highest-priority incomplete task each iteration.

# Implementation Plan

STATUS: IN PROGRESS

## Phase 1: Project Setup
- [ ] 1.1: Initialize backend project (FastAPI, pyproject.toml, virtual env)
- [ ] 1.2: Create database models
- [ ] 1.3: Initialize frontend project (React, Vite, TypeScript, Tailwind)

## Phase 2: Core Backend
- [ ] 2.1: Implement user authentication
- [ ] 2.2: Implement CRUD endpoints for main resource
- [ ] 2.3: Add search and filtering

## Phase 3: Frontend UI
- [ ] 3.1: Create app layout with navigation
- [ ] 3.2: Build main list/grid view
- [ ] 3.3: Build detail view

## Phase 4: Testing & Polish
- [ ] 4.1: Write backend tests (≥80% coverage)
- [ ] 4.2: Write frontend tests (≥80% coverage)
- [ ] 4.3: Final UI polish and responsive fixes

Critical format rules:

  • Tasks MUST use - [ ] (unchecked) or - [x] (done) checkbox syntax
  • Task IDs MUST be numeric with dots: 1.1, 1.2, 2.1, etc. — Ralph uses these to track which tasks were completed in each iteration
  • Format: - [ ] 1.1: Description of the task
  • Group tasks into phases with ## Phase N: Name headers
  • Order tasks by dependency — Ralph works top-to-bottom
  • Keep tasks granular (30min–2hr of AI work each)

Step 4: Create AGENTS.md

This file gives the AI agent project context and the commands it needs to run for backpressure (lint, test, build).

# AGENTS.md

## Project

[One-paragraph project description]

## Commands

- **Install backend**: `cd backend && python3 -m venv .venv && source .venv/bin/activate && pip install -e ".[dev]"`
- **Install frontend**: `cd frontend && npm install --legacy-peer-deps`
- **Test backend**: `cd backend && .venv/bin/pytest tests/ -q --tb=short`
- **Test frontend**: `cd frontend && npx vitest run --reporter=verbose`
- **Build frontend**: `cd frontend && npm run build`
- **Lint backend**: `cd backend && .venv/bin/ruff check . --fix`

## Backpressure

Run after each implementation:
1. Lint (if backend changes)
2. Run tests (if backend changes)
3. Type check (if frontend changes)
4. Build (if frontend changes)

## Architecture Notes

[Key architectural decisions the agent should know about]

## Learnings

*(Agent appends operational learnings here during the loop)*

The Backpressure section is important — these are the commands Ralph's agent runs after each change to catch errors before committing. If tests fail, the agent retries before moving on.

Step 5: Create PROMPT.md

This is the prompt fed to the AI tool every iteration. It tells the agent what to do and how to behave.

# Ralph BUILDING Loop

## Goal

[What you're building — 1-2 sentences]

## Context

- Read: specs/*.md for detailed requirements
- Read: IMPLEMENTATION_PLAN.md for the current task list
- Read: AGENTS.md for project context, commands, and learnings

## Rules

1. Pick the highest priority incomplete task from IMPLEMENTATION_PLAN.md
2. Investigate relevant code before changing
3. Implement the task fully
4. Run backpressure commands from AGENTS.md
5. If tests pass: commit with clear message, mark task done in IMPLEMENTATION_PLAN.md
6. If tests fail: try to fix (max 3 attempts), then notify
7. Update AGENTS.md with any operational learnings

## Tech Stack

[List exact versions so the agent doesn't guess]

## Notifications

When you need input or hit a blocker, write to .ralph/pending-notification.txt:
```json
{"prefix":"ERROR","message":"Brief description","details":"Full context..."}

Prefixes: DECISION, ERROR, BLOCKED, PROGRESS, DONE

Completion

When all tasks are done, add STATUS: COMPLETE to IMPLEMENTATION_PLAN.md.


### Step 6: Initial commit

```bash
git add -A
git commit -m "initial project setup with specs and plan"

Ralph needs a git repo — it commits after each successful iteration and tracks diffs.

Step 7: Start the loop

Option A: Via the dashboard API (if dashboard is running):

# Login first
TOKEN=$(curl -s -X POST "$BASE_URL/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"username":"USER","password":"PASS"}' | jq -r '.access_token')

# Start the loop
curl -s -X POST "$BASE_URL/api/projects/my-project/start" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"max_iterations": 0, "cli": "codex", "flags": "--full-auto"}' | jq

Option B: Directly via ralph.sh:

cd ~/projects/my-project
/path/to/ralph.sh --max-iterations 0 --cli codex --full-auto

The loop creates .ralph/ automatically on first run. The dashboard picks up the project within seconds.

Step 8: Monitor and intervene

Once running, use the dashboard API to monitor progress:

# Check stats
curl -s -H "Authorization: Bearer $TOKEN" "$BASE_URL/api/projects/my-project/stats" | \
  jq '{tasks: "\(.tasks_done)/\(.tasks_total)", iterations: .total_iterations, cost: .total_cost_usd}'

# Inject instructions if the agent needs guidance
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects/my-project/inject" \
  -d '{"message":"Use async SQLAlchemy sessions, not sync."}'

# Check for notifications (agent asking for help)
curl -s -H "Authorization: Bearer $TOKEN" "$BASE_URL/api/projects/my-project/notifications" | jq

Summary: minimum files needed

my-project/
├── specs/
│   └── overview.md          # What to build (detailed)
├── IMPLEMENTATION_PLAN.md   # Phased task checklist
├── AGENTS.md                # Build commands + context
├── PROMPT.md                # Loop prompt template
└── .git/                    # Must be a git repo

Ralph creates .ralph/ on first run. Everything else (iterations.jsonl, ralph.log, config.json) is generated automatically.


Common Workflows

Check if any loops are running

curl -s -H "$AUTH" "$BASE_URL/api/projects" | jq '[.[] | select(.status == "running")]'

Get a quick status overview of a project

# Project status + stats in two calls
PROJECT_ID="my-project"
curl -s -H "$AUTH" "$BASE_URL/api/projects/$PROJECT_ID" | jq
curl -s -H "$AUTH" "$BASE_URL/api/projects/$PROJECT_ID/stats" | jq '{tasks: "\(.tasks_done)/\(.tasks_total)", iterations: .total_iterations, cost: .total_cost_usd, velocity: .velocity.tasks_per_hour, errors: .errors_count}'

Inject a decision into a running loop

When the agent asks a question (DECISION/QUESTION notification), answer it:

curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  "$BASE_URL/api/projects/$PROJECT_ID/inject" \
  -d '{"message":"Decision: Use PostgreSQL instead of SQLite for the database layer."}' | jq

Get the latest iteration's log output

# Get total iterations, then fetch the last one
TOTAL=$(curl -s -H "$AUTH" "$BASE_URL/api/projects/$PROJECT_ID/iterations?limit=1" | jq '.total')
curl -s -H "$AUTH" "$BASE_URL/api/projects/$PROJECT_ID/iterations/$TOTAL" | jq -r '.log_output'

Generate a progress report

curl -s -H "$AUTH" "$BASE_URL/api/projects/$PROJECT_ID/report" | jq -r '.content'

Project Discovery

The dashboard automatically discovers projects by scanning configured directories (default: ~/projects) for any subdirectory containing .ralph/. No manual registration is needed — just ensure your project has a .ralph/ directory and lives under a scanned path.

Projects can also be registered manually via the API if they live outside the scanned directories.

Notes

  • Project IDs are slug-ified directory names (e.g. my-project from ~/projects/my-project/)
  • Token counts are in thousands (k-tokens) as reported by the CLI tools
  • WebSocket endpoint at /api/ws?token=<access_token> provides real-time events (iteration completions, plan updates, log appends, status changes) — useful for live monitoring but not covered here since agents typically poll
  • All timestamps are ISO 8601 with timezone