Local development setup¶
This page covers running the bot on your laptop against a real GitHub App. For first-time GitHub App creation, see github-app.md. For production deployment, see deployment.md.
Prerequisites¶
| Tool | Version | Purpose |
|---|---|---|
| Bun | from .tool-versions (currently 1.3.13) |
Runtime and package manager. |
| Git | any | Repository checkout during agent execution. |
| Docker | any recent | Local Postgres + Valkey via docker-compose.dev.yml. |
| GitHub account | — | Admin access to the org or personal account where the App is registered. |
| Tunnelling tool | — | ngrok or smee.io to expose localhost:3000 to GitHub. |
| AI provider credentials | — | One of: Anthropic API key, Claude Code OAuth token, AWS credentials for Bedrock. |
First run¶
git clone https://github.com/chrisleekr/github-app-playground.git
cd github-app-playground
bun install
# Start Postgres + Valkey in the background
bun run dev:deps
# Copy and fill .env
cp .env.example .env
# Edit .env — see configuration.md for every variable.
# Run database migrations
bun run db:migrate
# Run in watch mode
bun run dev
The HTTP server binds to PORT (default 3000). Hit http://localhost:3000/healthz to confirm it's up; http://localhost:3000/readyz confirms the data layer is reachable.
Expose the local server¶
GitHub must reach your webhook URL over the internet.
# Wrapped script: ngrok on port 3000
bun run dev:ngrok
# Copy the https://....ngrok.io URL into the GitHub App's webhook URL field.
Alternative — smee.io:
The local trigger phrase is conventionally @chrisleekr-bot-dev (set TRIGGER_PHRASE in .env) so the dev installation does not collide with the production bot's mention.
Common dev commands¶
bun run dev # Watch mode
bun run start # Production binary (after bun run build)
bun run build # Compile to dist/
bun run check # Unified gate: typecheck + lint + format + tests + no-destructive
bun run typecheck # tsc --noEmit
bun run lint # ESLint
bun run lint:fix # ESLint auto-fix
bun run format # Prettier check
bun run format:fix # Prettier auto-fix
bun test # Run tests via scripts/test-isolated.sh (Bun mock isolation)
bun run test:fast # Direct bun test (no isolation)
bun run test:watch # Watch mode
bun run test:coverage # With coverage report
bun run audit:ci # Severity-gated bun audit (used by CI)
bun run db:migrate # Run migrations against DATABASE_URL
bun run dev:deps # Up Postgres + Valkey
bun run dev:deps:down # Tear down
bun run dev:daemon # Run a daemon locally against the running orchestrator
bun run docs:install # Install MkDocs Python deps (one-time)
bun run docs:serve # Live-reload preview at http://localhost:8000
bun run docs:build # Strict build (CI also runs this)
bun run check is the single command to run before opening a PR.
Running a daemon locally¶
The webhook server embeds an orchestrator that talks to daemons over WebSocket. To exercise the full pipeline locally:
# Terminal 1 — orchestrator (webhook server)
bun run dev
# Terminal 2 — local daemon
bun run dev:daemon
The daemon connects back to ws://localhost:3002 (WS_PORT) using DAEMON_AUTH_TOKEN from .env. From there, every @chrisleekr-bot-dev mention exercises the full webhook → orchestrator → daemon → pipeline path.
Testing webhook delivery¶
- Open an issue or PR in a repository where your dev App is installed.
- Comment
@chrisleekr-bot-dev triage this. - The bot creates a tracking comment within ~2 s.
If nothing happens, check:
| Symptom | Likely cause |
|---|---|
| 401 / 403 in tunnel logs | GITHUB_WEBHOOK_SECRET mismatch with the App settings (no trailing newline). |
| 200 OK but no comment | ALLOWED_OWNERS excludes the repo owner; or MAX_CONCURRENT_REQUESTS is saturated. |
GITHUB_APP_PRIVATE_KEY parse error |
Set the full PEM including -----BEGIN… / -----END… lines (literal \n is normalised). |
ANTHROPIC_API_KEY is required |
CLAUDE_PROVIDER defaults to anthropic; set the key or switch to bedrock. |
| Bot mention ignored locally | TRIGGER_PHRASE is unchanged from the default @chrisleekr-bot; the dev App expects @chrisleekr-bot-dev. |