Development setup
The repository layout, the toolchain, and how to install, run and change each part of SAT locally.
SAT is a monorepo: a Python API, a React web app, a TypeScript CLI and runner, and this docs site, with Bun workspaces and Turborepo for the JavaScript side. This page gets you from a fresh clone to every part running, and covers the conventions for branches, commits and generated code. For the shortest path to a running app, see Run SAT locally.
Repository layout
| Path | What it is |
|---|---|
apps/api | FastAPI backend. main.py is the entry point (main:app); api/main_api.py builds the app. The company domain is in company/, models in database/, auth and email in services/, migrations in alembic/. scripts/seed_demo.py creates the demo company; scripts/export_openapi.py writes the OpenAPI schema. |
apps/web | React 19 control plane (Vite 8, Tailwind 4, SWR, zustand). Workspace package @sat/web. |
apps/cli | The sat CLI, runner and scheduler (Bun, commander). Workspace package @sat/cli. |
apps/docs | This site (Next.js, Fumadocs, static export). Workspace package @sat/docs. |
libs/api-client | @sat/api-client: the typed client, generated types (src/schema.d.ts) from openapi.json, the SSE subscriber, and the shared status vocabulary. Used by the web app, the CLI and the docs. |
libs/shared | @sat/shared: older shared constants and types. Neither the web app nor the CLI imports it. |
scripts/02-local-dev | bootstrap.sh (bring up everything) and stop.sh. |
scripts/03-testing | cli-smoke.sh, the end-to-end CLI and runner test against PostgreSQL. |
scripts/04-deployment | One-time infrastructure setup for the hosted Yarlis environments. |
tests/e2e | Playwright suite for the web app. |
rollout.yaml | The release manifest for the hosted Yarlis environments. |
Other folders (apps/sat_console_flutter, apps/api/archived, apps/api/src, most of scripts/01-setup and docs/) are from earlier versions of SAT and are not built, tested or deployed.
Toolchain
| Tool | Version | Used for |
|---|---|---|
| Bun | 1.4.0 (packageManager in package.json) | JavaScript installs, scripts, the CLI runtime and its tests |
| Node.js | 22 or later (engines) | Vite, Next.js, Playwright |
| Python | 3.12 (.python-version: 3.12.7) | The API |
| uv | any recent | Optional; creates the API venv faster |
| PostgreSQL | 16 | The API's database. SQLite does not work; see Database. |
| Docker | any recent | Optional; runs PostgreSQL and Redis from docker-compose.db.yml |
| Turborepo | 2.10.12 | Runs build, typecheck and test across workspaces |
| Biome | 2.5.11 | Lint and format for TypeScript, JSON and CSS |
| TypeScript | 6.0.3 | Type checking |
Optional, for running real agents: claude (Claude Code) or codex on your PATH, and git.
Install
Authenticate to npm for @yarlisai/ui
The web app depends on @yarlisai/ui, a restricted package. Run npm login with an account that can read the @yarlisai scope, or put a read-only token in ~/.npmrc:
echo "//registry.npmjs.org/:_authToken=<token>" > ~/.npmrcInstall JavaScript dependencies
bun installCreate the API virtual environment
cd apps/api
uv venv -p 3.12 .venv
uv pip install -p .venv/bin/python -r requirements-production.txt
uv pip install -p .venv/bin/python pytest pytest-asyncio # for the test suite
cd ../..Without uv: python3.12 -m venv apps/api/.venv && apps/api/.venv/bin/pip install -r apps/api/requirements-production.txt. If .venv/bin/python is a dangling symlink, see Troubleshooting.
Start PostgreSQL
Either a local cluster on a free port:
initdb -D ~/.sat-pg -U sat -A trust
pg_ctl -D ~/.sat-pg -o "-p 5433" -l ~/.sat-pg.log start
createdb -h 127.0.0.1 -p 5433 -U sat sat_main
export DATABASE_URL=postgresql://sat@127.0.0.1:5433/sat_mainOr a throwaway container matching production:
docker run -d --name sat-pg -p 5433:5432 \
-e POSTGRES_USER=sat -e POSTGRES_PASSWORD=sat -e POSTGRES_DB=sat_main postgres:16
export DATABASE_URL=postgresql://sat:sat@127.0.0.1:5433/sat_mainAbout docker-compose.db.yml
scripts/02-local-dev/bootstrap.sh starts docker-compose.db.yml, which runs postgres:15-alpine on port 5432 (user sat_admin, database sat_main) plus Redis, Redis Commander and pgAdmin. It also loads scripts/db/*.sql into a new volume. Those SQL files predate Alembic and create tables that do not match the migrations. Prefer an empty PostgreSQL 16 database as above, and let the API create the schema.
Run each part
API (port 8080)
cd apps/api
DATABASE_URL=postgresql://sat@127.0.0.1:5433/sat_main DEBUG=true \
.venv/bin/uvicorn main:app --reload --port 8080Or, from the repository root, start_api_local.py applies development defaults (ENVIRONMENT=development, DEBUG=true, ALLOWED_ORIGINS for ports 4200 and 3000, PORT=8080) and leaves any variable you set alone:
DATABASE_URL=postgresql://sat@127.0.0.1:5433/sat_main apps/api/.venv/bin/python start_api_local.pyOn first start the API runs every migration. With DEBUG=true, Swagger UI is at http://localhost:8080/docs. Check it with curl localhost:8080/health.
Seed the demo company, NoteFlow (prefix NF), with agents Ada, Linus, Grace, Ken, Maya, Margaret and Toni:
cd apps/api
DATABASE_URL=postgresql://sat@127.0.0.1:5433/sat_main .venv/bin/python scripts/seed_demo.pyThe seed script calls the API in-process (it imports the app), so it needs DATABASE_URL but not a running server.
The demo login is demo@example.com / DemoPassword123!. Use it only locally.
The root script bun run dev:api runs uvicorn from your PATH, not from apps/api/.venv. Activate the venv first, or use the commands above.
Web app (port 4200)
bun run dev # turbo run dev --filter=@sat/webVite proxies /api and /health to SAT_API_URL (default http://localhost:8080). Open http://localhost:4200.
CLI and runner
bun run sat -- --help # run from source
bun run sat -- login # profile "local" → http://localhost:8080
bun run sat -- company use NoteFlow
bun run sat -- apikey create dev-runner --store
bun run sat -- runner start --simulate # built-in echo adapter, no model calls
bun run --cwd apps/cli build # single binary at apps/cli/dist/sat--simulate executes runs with the echo adapter, so you can exercise the whole loop without Claude Code or Codex. See sat runner.
Docs site (port 4300)
bun run dev:docsSee Writing docs.
Everything at once
scripts/02-local-dev/bootstrap.sh installs dependencies, starts docker-compose.db.yml, the API (through start_api_local.py) and the web dev server, and writes logs to .local-run/. It kills whatever listens on the API and web ports first. Stop with scripts/02-local-dev/stop.sh. Flags: --skip-deps, --skip-db, --only api|web|db.
Regenerate the API client
The web app, the CLI and the docs are typed against libs/api-client/openapi.json. After any change to an endpoint, a request or response model, or a docstring, regenerate it:
bun run --cwd libs/api-client generateThis runs apps/api/scripts/export_openapi.py with apps/api/.venv/bin/python (so the venv must exist), writes libs/api-client/openapi.json, then runs openapi-typescript to produce libs/api-client/src/schema.d.ts. Commit both files with the API change. Then:
bunx turbo run typecheck # web, CLI and docs against the new types
bun run --cwd apps/cli docs # if CLI commands changed: regenerate the CLI referenceThe API reference on this site is built from the same openapi.json, so endpoint docs come from FastAPI docstrings and Pydantic models.
Lint and format
bun run lint # biome check .
bun run lint:fix # biome check --write .
bunx turbo run typecheckBiome covers apps/web, apps/cli, apps/docs and libs: 2-space indent, single quotes, semicolons, trailing commas (ES5), 100-column lines. Generated files (schema.d.ts, openapi.json) are excluded. Python code is not linted in CI; follow the style of the surrounding file.
Branches, commits and pull requests
- Branch from
mainfor every change:feat/<topic>,fix/<topic>,chore/<topic>,docs/<topic>. Do not commit tomaindirectly. - Commit messages follow Conventional Commits:
type(scope): summary, with typesfeat,fix,docs,refactor,test,chore,ci,build, and scopes such asapi,web,cli,docs,deploy. Examples from history:feat(cli): sat CLI, agent runner and scheduler (apps/cli),fix(api): timezone-safe refresh-token and lockout checks on Postgres. - Pull requests run CI and
tollgate-conformance. Keep the API client, the CLI reference and the docs in the same pull request as the change that needs them. - Merging to
mainbuilds the API image. Deploys of the hosted Yarlis environments are separate, through the Yarlis release system. See Architecture.