Get Forge running in ~5 minutes.
All commands below assume your current working directory is the repo root (
forge/).
- Python 3.12+ (
python3 --version) - Git
- One LLM provider credential:
- Amazon Bedrock API key or AWS credentials (default provider), or
- OpenRouter API key (used by
./run-forever.shand the benchmark harness)
- OpenCode on
PATH— Forge installs it inside the worker image automatically; only needed on the host for ad-hoc tooling - Docker Desktop (required since R9.E) — Forge runs every worker inside a container built from
Dockerfile.worker. Build the image once withmake worker-image. - (Optional) nono — kernel-level sandbox invoked by
./run.sh
git clone https://github.com/sjqtentacles/forge.git
cd forge
python3 -m venv .venv
source .venv/bin/activate
pip install -e 'platform[dev]'Forge defaults to Amazon Bedrock (Claude Sonnet 4.6 for primary/secondary/reasoning, Claude Opus 4.6 for the adversary reviewer).
cp .env.example .env
# Edit .env and uncomment the credentials you want to use.Credential precedence (first non-empty wins):
BEDROCK_API_KEY— direct Bedrock API keyAWS_ACCESS_KEY_ID+AWS_SECRET_ACCESS_KEY(+ optionalAWS_SESSION_TOKEN,AWS_REGION)OPENROUTER_API_KEY— OpenRouter provider
./run.sh will also extract AWS credentials from your ~/.aws/credentials profile if they are not in .env. See Launcher scripts for details.
If you want to run Forge on a fresh codebase rather than the bundled defaults:
PYTHONPATH=platform python3 -m forge.cli init --name my-project --out /path/to/output --no-llmForge-on-Forge — to dogfood Forge against its own monorepo, select the
forge_on_forgeprofile (forge run --profile forge_on_forge). Seedocs/PROFILES.mdandstudio/profiles/forge_on_forge.yaml.
This creates:
studio/config.yaml— project configuration (paths, commands, features)studio/models.yaml— model aliases and rates (the only file that names model IDs)studio/agents/— agent prompt templates.forge/— runtime data directory
Two files. studio/config.yaml describes the project shape; studio/models.yaml pins which models to use. The split is a hard policy — see docs/MODELS.md.
studio/config.yaml:
project:
name: "my-project"
language: "python"
source_root: "."
module_name: "myproject"
epic_prefix: "PROJ"
layout:
packages_dir: ""
test_suffix: "test_*.py"
source_glob: "*.py"
commands:
test: "python3 -m pytest -x -q"
build: "python3 -m py_compile myproject/__init__.py"
vet: "python3 -m py_compile myproject/__init__.py"
docs:
design: ""
taskboard: "docs/TASKBOARD.md"
sandbox:
extra_allow: []
extra_read: []
worker_runtime: docker # only supported value since R9.E
# NOTE: model selection lives in studio/models.yaml -- not here.
# See docs/MODELS.md for the policy.
features:
best_of_n: true
adaptive_search: true
mcp_tools: truestudio/models.yaml (the only place model IDs are allowed):
aliases:
primary: "<your-primary-model-id>"
secondary: "<your-secondary-model-id>"
reasoning: "<your-reasoning-model-id>"
embedding: "<your-embedding-model-id>"
rates: {} # optional; per-model USD/1M-token costs for the dashboardSee docs/MODELS.md for the schema, the provider-routing
rules, and the four required aliases. See docs/CONFIG-REFERENCE.md
for every key in config.yaml.
Forge reads epics from docs/TASKBOARD.md (or wherever project.docs.taskboard points). Note: this is the taskboard format — a per-project backlog your epic prefix keys into. The Forge repo itself also ships docs/TASKBOARD.md documenting internal SWE-bench tracking; the format is the same, the scope is different.
Format:
### PROJ-001: User authentication
**Priority:** P0
**Description:** Add JWT-based user authentication.
### PROJ-002: Forgot-password flow
**Description:** Email reset link + token verification.Or use inline checkboxes:
- [ ] PROJ-003: Rate-limit login endpoint
- [x] PROJ-004: Add /healthz route <!-- already done, skipped -->A typical small epic (2-4 subtasks, ~200 lines of code) costs $3-8 in LLM API calls and takes 10-20 minutes. Larger epics (6+ subtasks, cross-package refactors) can cost $15-30 and take 30-60 minutes.
Forge uses Sonnet 4.6 for most work and automatically routes the adversarial reviewer to Opus 4.6 when the blast radius exceeds a configurable threshold. Opus calls are 5x more expensive per token but only fire on high-risk changes.
The dashboard (http://127.0.0.1:8420) shows live cost tracking per epic and per session.
The canonical invocation is:
PYTHONPATH=platform .venv/bin/python3 -m forge.cli run --dashboardOther common modes:
PYTHONPATH=platform .venv/bin/python3 -m forge.cli run --once --max-tasks 3 # one-shot
PYTHONPATH=platform .venv/bin/python3 -m forge.cli run --profile go-game # named profileDashboard is served at http://127.0.0.1:8420 while the engine is running.
Two launchers live at the repo root. They are not interchangeable — they target different providers:
| Script | Provider | Sandbox | Mode |
|---|---|---|---|
./run.sh |
Amazon Bedrock via AWS profile | nono (kernel-level) |
Single run |
./run-forever.sh |
OpenRouter (OPENROUTER_API_KEY) |
None | Continuous daemon |
Notes before using them:
run.shreads~/.aws/credentialsand extracts the profile named by$AWS_PROFILE(defaults todefaultif unset). To use a non-default profile, setAWS_PROFILE=<your-profile>in your shell or in.envbefore invoking the script.run.shrequiresnonoto be installed and onPATH.run-forever.shcallspython3(system Python) directly. Either keep your venv activated or edit the script to use.venv/bin/python3.
python3 -m pytest platform/tests/ -p no:xdist -o "addopts=" -vThe -p no:xdist -o "addopts=" overrides the parallel defaults from platform/pyproject.toml so test output is deterministic. For a fast parallel run, just use python3 -m pytest platform/tests/.
R9.E's cassette-gated end-to-end canary replays both the LLM tape
(<scenario>.ndjson) and the OpenCode subprocess tape
(<scenario>.opencode.ndjson) so the test runs hermetically with
zero paid calls and zero live opencode invocations. To bootstrap
or refresh the canary cassettes locally (once, paying real Bedrock
cost + spinning up the worker container):
make worker-image
FORGE_EVAL_LIVE=1 scripts/capture_eval_cassette.sh canaryThe script refuses to run if either cassette already exists; remove
both before recapturing so they always come from the same live run.
See docs/eval-runs/R9/README.md
for the full record/replay contract and drift-handling rules.
PYTHONPATH=platform python3 -m forge.cli status --costs # status + cost summary
PYTHONPATH=platform python3 -m forge.cli config validate # YAML + structural check
PYTHONPATH=platform python3 -m forge.cli doctor # preflight: config, PATH, MCP, SQLite
PYTHONPATH=platform python3 -m forge.cli canary # replay the frozen canary epics
PYTHONPATH=platform python3 -m forge.cli sleeptime # sleep-time meta-evolution consolidation
PYTHONPATH=platform python3 -m forge.cli task https://github.com/you/repo \
--description "Add OAuth login"See examples/ for profile templates — each is a studio/config.yaml + studio/agents/ + design/taskboard that you point at your own project tree. They are not self-contained runnable apps. See examples/README.md for how to pair them with a real project.
examples/go-game/— Go colony-sim profile (expects a Go module rooted atgame/)examples/python-api/— FastAPI profile (expects a Python package rooted atsrc/)
| Symptom | Likely cause | Fix |
|---|---|---|
ModuleNotFoundError: forge |
PYTHONPATH not set |
Prefix commands with PYTHONPATH=platform, or activate the editable install (pip install -e 'platform[dev]') |
No credentials found / Bedrock auth error |
Neither .env nor AWS profile is populated |
Set BEDROCK_API_KEY in .env, or configure an AWS profile that the launcher can read |
opencode: command not found |
OpenCode not on PATH |
Install from https://github.com/opencode-ai/opencode and ensure which opencode succeeds |
nono: command not found |
run.sh expects nono |
Install from https://nono.sh, or invoke the CLI directly instead of ./run.sh |
Cannot connect to the Docker daemon |
Docker Desktop isn't running (Forge requires it since R9.E) | Start Docker Desktop, then re-run. There is no in-process fallback runtime. |
UnsupportedRuntimeError: Forge is Docker-only since R9.E |
Config still sets worker_runtime: local (or claude_code/auto) |
Set worker_runtime: docker in studio/config.yaml (or remove the key to pick up the default). |
Address already in use (127.0.0.1:8420) |
Prior Forge run still holds the dashboard port | lsof -iTCP:8420 -sTCP:LISTEN then kill <pid>. If you need a different port, invoke forge.engine.main --dashboard --dashboard-port 8421 directly (the forge run subcommand does not currently forward a custom port). |
SyntaxError on match / str | None at import |
Python < 3.12 | Install Python 3.12+ and recreate the venv |
| Worker container exits with code 137 | Docker OOM-killed the container | Increase Docker Desktop memory limit (Settings > Resources) to at least 4 GB. Run docker system prune -f to reclaim disk space from dangling images and build cache. |
No such container / RWLayer unexpectedly nil |
Docker storage layer under pressure | Run docker system prune -a to remove unused images, then docker system df to verify disk is healthy. Restart Docker Desktop if errors persist. |
| Worker keeps committing compiled binaries (Go) | Missing .gitignore in the game repo |
Add the binary name (e.g. /ironhold) and *.test, *.out to your game's .gitignore. |