Development Guide
Complete developer documentation for contributing to Agor.
Quick start
git clone https://github.com/preset-io/agor
cd agor
docker compose up
# Visit http://localhost:5173 → Login: admin@agor.live / adminThat gives you SQLite, single-user, no RBAC — the fastest path to a running daemon. For Postgres, multiplayer + RBAC, or the docs site, see the variant system below.
.agor.yml — the variant system
Agor’s repo ships .agor.yml — a declarative schema describing how to spawn an environment for this codebase. The same schema you’d write for any repo you load into Agor: Agor uses it to develop itself. Each variant is a Handlebars-templated start / stop / nuke / logs / health / app block.
| Variant | What you get | Base ports |
|---|---|---|
sqlite (default) | SQLite, single-user, no RBAC. Fastest to boot. | daemon: 3000 + worktree.unique_id, UI: 5000 + … |
postgres | Postgres backend (closer to multi-user prod). RBAC off. | same as sqlite |
full | Postgres + worktree RBAC + strict Unix-user impersonation. Requires the sudoers config under docker/sudoers/. | same as sqlite |
docs | The Nextra docs site (apps/agor-docs) in its own self-contained container. Standalone — no daemon, no DB. | 7000 + worktree.unique_id |
Ports derive from worktree.unique_id so multiple worktrees run side-by-side without colliding. The docs variant uses {{host.ip_address}} in its app URL so the dev server is reachable from your laptop, not just localhost on the daemon host.
Pick a variant by setting AGOR_VARIANT in your environment or selecting it in the worktree’s environment settings — or render it manually with the appropriate docker compose invocation from the file.
A representative variant block, abridged from the file:
sqlite:
description: Single-user dev with SQLite. No RBAC. Fastest to boot.
start: >-
DAEMON_PORT={{add 3000 worktree.unique_id}}
UI_PORT={{add 5000 worktree.unique_id}}
docker compose -p agor-{{worktree.name}} up -d
stop: docker compose -p agor-{{worktree.name}} down
nuke: docker compose -p agor-{{worktree.name}} down -v
logs: docker compose -p agor-{{worktree.name}} logs --tail=100
health: http://localhost:{{add 3000 worktree.unique_id}}/health
app: http://localhost:{{add 5000 worktree.unique_id}}Other variants extends: sqlite to inherit the unchanged blocks — the schema enforces single-level extension only (no chains).
Agor in Agor (recommended)
The whole point of .agor.yml is that Agor can develop itself. The natural workflow:
- Run a “host” Agor (any way you like —
npm i -g agor-live,docker compose up, anything). - Register the agor repo in your host Agor — clone or point it at your local checkout.
- Create a worktree per feature. Each worktree gets a unique
worktree.unique_id→ unique ports → fully isolated dev stack. - Pick a variant in the worktree’s environment settings (sqlite for most things, postgres if you’re touching DB code, full if you’re testing RBAC, docs for documentation work).
- Spawn an agent session. The session has Agor MCP access, so it can spin up environments, monitor health and logs, run tasks against the running stack, and tear it down — without you ever leaving the canvas.
This is also the only sane way to develop multiple Agor features in parallel: each worktree’s variant picks its own ports, mounts source for HMR, and persists node_modules in a named volume.
When to use pnpm dev instead
Skip Docker if you’re working on the environment-spawning system itself — running Agor in Docker while it tries to spawn docker compose for environments creates docker-in-docker entanglements that aren’t worth fighting.
git clone https://github.com/preset-io/agor && cd agor && pnpm installTwo terminals:
# Terminal 1: Daemon (watches @agor/core + daemon, auto-restarts)
cd apps/agor-daemon && pnpm dev
# Terminal 2: UI dev server (Vite HMR)
cd apps/agor-ui && pnpm dev
# Visit http://localhost:5173Custom builds
To test the packaged artifact end-to-end — the way users actually install Agor — build the agor-live npm package locally:
cd packages/agor-live
./build.sh
npm i -g . # or: npm i -g ./agor-live-<version>.tgzbuild.sh produces a single self-contained package: the UI is built as static assets and bundled into agor-live so the daemon serves it directly. After npm i -g ., the global agor and agor-live commands run your local build instead of the published version.
Useful when you want to:
- Verify the production-style flow (no Vite dev server, no separate UI process)
- Test CLI behavior against a built package
- Hand a colleague a tarball without publishing to npm
Run npm uninstall -g agor-live to remove your local build and fall back to whatever you had installed before.
Committing from inside Docker
When the daemon runs in Docker, pnpm install populates node_modules/ with Linux binaries (turbo-linux-arm64, eslint-linux, etc.). If you commit from the host, Husky pre-commit hooks try to execute those Linux binaries and fail.
Two fixes:
- Commit inside the container (recommended) —
docker compose exec agor-dev git add . && docker compose exec agor-dev git commit -m "msg". Hooks run in Linux. - Reinstall on host — run
pnpm installfrom the host once to get host-OS binaries; commit normally afterward. (You’ll end up with both binary sets innode_modules, which is harmless but bloats the tree.)
Checking which database is active
Open Settings → About (admin only):
- 💾 SQLite — shows the database file path
- 🐘 PostgreSQL — shows the connection URL (password masked)
Or hit the daemon directly:
curl http://localhost:3030/healthProject Structure
agor/
├── apps/
│ ├── agor-daemon/ # FeathersJS backend (REST + WebSocket)
│ ├── agor-cli/ # CLI tool (oclif-based)
│ ├── agor-ui/ # React UI (Ant Design + React Flow)
│ └── agor-docs/ # Documentation website (Nextra)
├── packages/
│ ├── core/ # Shared @agor/core package
│ │ ├── types/ # TypeScript types (Session, Task, Worktree, etc.)
│ │ ├── db/ # Drizzle ORM + repositories + schema
│ │ ├── git/ # Git utils (simple-git only, no subprocess)
│ │ ├── claude/ # Claude Code session loading utilities
│ │ └── api/ # FeathersJS client utilities
│ └── agor-live/ # Published npm package
└── context/ # 📚 Architecture documentation (READ THIS!)
├── concepts/ # Core design docs
└── explorations/ # Experimental designsMonorepo & Development Tooling
Agor is a pnpm workspace monorepo with several automation tools:
pnpm Workspaces
Structure:
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'Workspace protocol:
- Packages reference each other via
workspace:* - Example:
@agor/clidepends on@agor/core@workspace:* - pnpm symlinks workspace packages (no need to rebuild on every change)
Turbo
Parallel builds and task orchestration:
# Build all packages in dependency order
pnpm build
# Run typecheck across all packages in parallel
pnpm typecheck
# Run dev servers (daemon + UI)
pnpm devHow it works:
- Reads
turbo.jsonfor task definitions - Understands package dependencies
- Runs tasks in parallel when possible
- Caches build outputs for speed
Git Hooks (Husky + lint-staged)
Pre-commit checks:
- Runs automatically before
git commit - Only checks staged files (fast!)
- Runs: biome (linting), prettier (formatting), typecheck
Setup:
pnpm prepare # Installs git hooksCode Quality
Linting:
- biome - Fast linter and formatter
- Config:
biome.json - Run:
pnpm lintorpnpm lint:fix
Formatting:
- prettier - Code formatter
- Config:
.prettierrc - Run:
pnpm format
Root Scripts
Common commands from root directory:
# Development
pnpm dev # Start daemon + UI
pnpm docs:dev # Start docs site
# Code quality
pnpm typecheck # Type check all packages
pnpm lint # Lint all packages
pnpm lint:fix # Lint and auto-fix
pnpm format # Format all files
pnpm check # typecheck + lint + build
pnpm check:fix # lint:fix + typecheck + build
# Building
pnpm build # Build all packages
pnpm clean # Clean all build artifacts
# CLI (from root)
pnpm agor <command> # Run CLI without global installTech Stack
See the Architecture Guide for the complete tech stack (FeathersJS, Drizzle, React, Ant Design, etc.).
Development Patterns
Code Standards
- Type-driven - Use branded types for IDs, strict TypeScript
- Centralize types - ALWAYS import from
packages/core/src/types/(never redefine) - Read before edit - Always read files before modifying
- Prefer Edit over Write - Modify existing files when possible
- Git operations - ALWAYS use
simple-git(NEVER subprocessexecSync,spawn, etc.) - Error handling - Clean user-facing errors, no stacktraces in CLI
Important Rules
Git Library:
- ✅ Use
simple-gitfor ALL git operations - ❌ NEVER use
execSync,spawn, or bash for git commands - Location:
packages/core/src/git/index.ts
Watch Mode:
- User runs
pnpm devin daemon (watches core + daemon) - DO NOT run builds unless explicitly asked or you see compilation errors
- DO NOT start background processes
Type Reuse:
- Import types from
packages/core/src/types/ - Sessions, Tasks, Worktrees, Messages, Repos, Boards, Users, etc.
- Never redefine canonical types
Worktree-Centric Architecture:
- Boards display Worktrees as primary cards (NOT Sessions)
- Sessions reference worktrees via required FK
- Read
context/concepts/worktrees.mdbefore touching boards
Key Documentation
Before diving into code, familiarize yourself with the architecture:
- AGENTS.md - Development patterns and project structure
- CONTRIBUTING.md - Complete contribution guide
- context/README.md - Architecture documentation index
- context/concepts/core.md - Core primitives and design philosophy
Testing
Database Operations
SQLite:
# Query database directly
sqlite3 ~/.agor/agor.db "SELECT COUNT(*) FROM messages"
sqlite3 ~/.agor/agor.db "SELECT * FROM sessions LIMIT 5"PostgreSQL:
# Connect to postgres container
docker compose exec postgres psql -U agor -d agor
# Example queries
docker compose exec postgres psql -U agor -d agor -c "SELECT COUNT(*) FROM messages"
docker compose exec postgres psql -U agor -d agor -c "SELECT * FROM sessions LIMIT 5"Health Checks
# Daemon health
curl http://localhost:3030/health
# Check which database is active (admin auth required)
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" http://localhost:3030/healthCLI Commands
# Test CLI (ensure clean exit, no hanging)
pnpm agor session list
pnpm agor repo list
# CLI auto-detects database from environment
# Works with both SQLite and PostgreSQLTroubleshooting
Build fails with ERR_WORKER_OUT_OF_MEMORY / JS heap out of memory
@agor/core exports ~40 entry points and emits TypeScript declarations for
both CJS and ESM. tsup’s DTS pipeline peaks around 3 GB of memory, which
exceeds Node’s default heap ceiling on smaller hosts (4 GB RAM containers,
Raspberry Pis, etc.).
Agor’s standard build paths already raise the ceiling to 4 GB. If you wrap
the build in your own script (or invoke tsup directly) and hit the OOM, set
it yourself:
export NODE_OPTIONS="--max-old-space-size=4096"
pnpm --filter @agor/core buildIf 4 GB isn’t enough on a particularly large feature branch, raise to 8192.
”Method is not a function” after editing @agor/core
Should NOT happen with new 2-process workflow (daemon watches core and auto-restarts).
If it still happens:
cd packages/core && pnpm build
cd apps/agor-daemon && pnpm devtsx watch not picking up changes
cd apps/agor-daemon
rm -rf node_modules/.tsx
pnpm devDaemon hanging
lsof -ti:3030 | xargs kill -9
cd apps/agor-daemon && pnpm devWhat to Contribute
Browse the roadmap issues for contribution ideas, or propose your own!
Getting Help
- Discord - Join our Discord community for support and discussion
- GitHub Discussions - Ask questions, share ideas
- GitHub Issues - Report bugs, request features
Next Steps
- Architecture - System design and internals
- Run
agor --helpfor complete CLI documentation - API Reference - REST endpoints and WebSocket events
- AGENTS.md - Development patterns and project structure