Docs

Run it locally

Local dev runs the harness on your host (so you get hot reload) but spawns every agent task in a real per-phase sandbox, exactly like production. This is deliberate: it's the only honest way to develop against the bot's actual execution model.

The default sandbox backend is gondolin — agentic-pi's QEMU micro-VM, using HVF on macOS or KVM on Linux. No Docker needed. Switch to LASTLIGHT_SANDBOX=docker if you'd rather run sibling Docker containers (useful for prod-like smoke testing), or none for in-process dev runs.

1. Clone and install

git clone https://github.com/cliftonc/lastlight.git
cd lastlight
npm install

2. Create .env

cp .env.example .env

Edit .env and fill in the four values you collected in the previous step:

GITHUB_APP_ID=123456
GITHUB_APP_PRIVATE_KEY_PATH=./your-app.private-key.pem
GITHUB_APP_INSTALLATION_ID=789012
WEBHOOK_SECRET=your-webhook-secret

Drop the .pem file into the project root (or wherever GITHUB_APP_PRIVATE_KEY_PATH points). It's already in .gitignore, so you won't accidentally commit it.

3. (Docker mode only) Build the sandbox image

docker compose --profile build-only build sandbox

Only needed if you set LASTLIGHT_SANDBOX=docker. The default gondolin sandbox runs from the harness directly and needs no image build. Rebuild when sandbox.Dockerfile changes.

4. Set a model API key

The agent runtime is agentic-pi — provider-agnostic. Drop the matching key into .env:

# Default model is anthropic/claude-sonnet-4-6
ANTHROPIC_API_KEY=sk-ant-...

# Or, if you'd rather route to OpenAI
OPENAI_API_KEY=sk-...
LASTLIGHT_MODEL=openai/gpt-5.5

# Or use OpenRouter — one key, many models
OPENROUTER_API_KEY=sk-or-v1-...
LASTLIGHT_MODEL=openrouter/anthropic/claude-sonnet-4.5

# Sandbox backend — gondolin (default), docker, or none
# LASTLIGHT_SANDBOX=gondolin

No interactive login step. pi-ai reads the API key from the harness env and forwards it into each sandbox.

5. Start the harness

# Server + dashboard, with hot reload
npm run dev

# Or individually
npm run dev:server
npm run dev:dashboard

Under the hood, npm run dev:server calls scripts/dev-local.sh, which:

  • When LASTLIGHT_SANDBOX=docker, verifies Docker is running and the sandbox image exists.
  • Copies your GITHUB_APP_PRIVATE_KEY_PATH into ./data/sandbox-data/secrets/app.pem (mode 600) so sandboxes can authenticate to GitHub.
  • Sets safe defaults: LASTLIGHT_LOCAL_DEV=1, STATE_DIR=./data, LASTLIGHT_SESSIONS_DIR=./data/agent-sessions.
  • Launches the harness with tsx watch src/index.ts.
LASTLIGHT_LOCAL_DEV=1 tells the harness not to touch your personal ~/.gitconfig or keychain. Everything sandbox-related stays inside ./data/. Safe to run on a dev laptop.

6. Trigger work via the CLI

The CLI is a thin HTTP client — it POSTs to the running server. Open a second terminal:

# Cheap defaults — single agent invocation
npx tsx src/cli.ts owner/repo#42                          # triage one issue
npx tsx src/cli.ts https://github.com/owner/repo/pull/99  # review one PR
npx tsx src/cli.ts triage owner/repo                       # scan repo for new issues
npx tsx src/cli.ts review owner/repo                       # scan repo for PRs to review
npx tsx src/cli.ts health owner/repo                       # weekly-style health report

# Expensive, opt-in — full Architect → Executor → Reviewer → PR cycle
npx tsx src/cli.ts build owner/repo#42

The default action for a single-issue or single-PR shorthand is the cheap one (triage / review). Build cycles are always opt-in via the explicit build subcommand.

7. Open the dashboard

The admin dashboard is served at http://localhost:8644/admin. It has four tabs: Home, Workflows, Sandbox Sessions, and Chat Sessions. You can watch sessions stream live, drill into any run, and resolve approval gates.

If you set ADMIN_PASSWORD in .env, the dashboard will prompt for it. Otherwise it's open.

Optional: receive real webhooks

To receive real GitHub webhooks in local dev, expose port 8644 with ngrok or cloudflared and point your GitHub App's webhook URL at the public tunnel. Without webhooks you can still trigger every workflow via the CLI.