Configuring tutti.toml
tutti.toml in the root of a repository tells Tutti how to give every agent a working copy of your app:
what to install, which dev servers to run, which ports they need, and who's on the team. Commit it, and everyone on
your team gets the same setup.
Create one
- In the app: choose Set up on the project. Tutti detects your stack (Django, FastAPI, Flask, Next.js, Vite, Nest, Rails, Laravel, Go, Rust, Spring, monorepos, Docker Compose, databases) and proposes a file to review and save.
- From a terminal:
tutti initprints a suggestion for the current repository;tutti init --writesaves it. - By describing it: in project settings, Change with Claude takes a sentence ("add a Redis service, run migrations on setup") and shows a checked diff to accept. Nothing is saved until you do.
Saving tutti.toml from Tutti approves its commands. A tutti.toml that arrives with the
repository, or changes later, runs only after you review it; see Trust.
A full example
A Django API and a Vite storefront, each conversation with its own database:
[project]
base_branch = "main"
default_agent = "claude-code"
team = "full-stack"
[setup]
copy = [".env"] # copied from your checkout into each worktree
symlink = ["frontend/node_modules/.cache"]
script = """
createdb shop_$TUTTI_SESSION
cd backend && uv sync && uv run python manage.py migrate && uv run python manage.py loaddata demo
cd ../frontend && pnpm install
"""
[teardown]
script = "dropdb --if-exists shop_$TUTTI_SESSION"
[run]
api = { cmd = "uv run python manage.py runserver 127.0.0.1:$TUTTI_PORT", cwd = "backend", url = "http://localhost:$TUTTI_PORT" }
web = { cmd = "pnpm dev --port $TUTTI_PORT_1", cwd = "frontend", url = "http://localhost:$TUTTI_PORT_1" }
[ports]
extra = 1
[env]
DATABASE_URL = "postgres://localhost/shop_$TUTTI_SESSION"
VITE_API_URL = "http://localhost:$TUTTI_PORT"
[checks]
test = "cd backend && uv run pytest -q"
lint = "cd frontend && pnpm lint"
[[team.members]]
id = "sarah"
name = "Sarah"
role = "lead"
[[team.members]]
id = "tom"
name = "Tom"
role = "backend"
strengths = "APIs, data models and payments; careful with migrations"
agent = "claude-code"
[[team.members]]
id = "ada"
name = "Ada"
role = "frontend"
agent = "codex"
Editor support
Point your editor at the JSON Schema for completion and checks:
https://tutti.build/schemas/tutti.schema.json. With the Even
Better TOML extension, add this as the first line:
#:schema https://tutti.build/schemas/tutti.schema.json
Unknown keys are reported as warnings, not errors, so older and newer versions of Tutti can share a file.
Reference
Every key Tutti reads, from the current schema.
[project]
Basics for the project.
| Key | Type | What it does |
|---|---|---|
base_branch
|
string | Branch new sessions start from (defaults to the repo's current default branch). |
branch_prefix
|
string | Prefix for session branches (default tutti/). |
default_agent
|
string | Agent used when none is chosen. |
name
|
string | Display name (defaults to the repository folder name). |
team
|
string | The team Tutti suggests for this project, by template id (e.g. feature-squad, full-stack, campaign, outreach). Without it Tutti picks one from the contents. |
[setup] and [teardown]
Runs once after a session's worktree is created. [teardown]: Runs when a session is archived (before its worktree is removed).
| Key | Type | What it does |
|---|---|---|
copy
|
array of strings | Files copied from the main checkout into the worktree (e.g. .env). Missing files are skipped. |
script
|
string | Shell script (run with sh -c on Unix, PowerShell on Windows unless shell is set). |
shell
|
array of strings | Override the shell, e.g. ["bash", "-lc"]. |
symlink
|
array of strings | Paths symlinked from the main checkout (shared caches). Falls back to copying where symlinks are unavailable. |
[run.<name>]
Dev servers / long-running processes, started on demand per session.
| Key | Type | What it does |
|---|---|---|
cmd
|
string | Command line (shell), e.g. pnpm dev --port $TUTTI_PORT. |
cwd
|
string | Working directory relative to the worktree root. |
env
|
table | Extra environment for this service only. $TUTTI_* variables are expanded. |
url
|
string | URL to open, e.g. http://localhost:$TUTTI_PORT. |
[ports]
Ports per conversation. $TUTTI_PORT is always there.
| Key | Type | What it does |
|---|---|---|
extra
|
number | Extra ports per session beyond TUTTI_PORT (exposed as TUTTI_PORT_1..N, max 9). |
[env]
Extra environment for setup, run, teardown and agents. $TUTTI_* variables are expanded. Keys are variable names, values are strings.
[checks]
Named checks (tests, lint) used by recipes, testers and arena scoring. Keys are names, values are shell commands, e.g. test = "uv run pytest -q".
[agents.<id>]
Per-agent overrides, keyed by adapter id (claude-code, codex, …) or a custom name.
| Key | Type | What it does |
|---|---|---|
args
|
array of strings | Extra command-line arguments. |
cmd
|
string | For custom agents (generic terminal adapter): the command to run. |
env
|
table | Extra environment for this agent only. $TUTTI_* variables are expanded. |
idle_seconds
|
number | Generic adapter: seconds without output before the session counts as idle. |
model
|
string | Model id passed to the agent (e.g. claude-opus-5-5). |
prompt_regex
|
string | Generic adapter: regex matching the agent's input prompt, to detect "waiting for input". |
[[team.members]]
The project's team, shared through the repository. Each teammate's notes and history stay on each person's machine.
| Key | Type | What it does |
|---|---|---|
agent
|
string | Agent (claude-code, codex, …); omitted = picked from the role. |
id
|
string | Stable id: lowercase letters, digits and dashes (e.g. ada). |
model
|
string | Model; omitted = the agent's default from settings. |
name
|
string | Display name. |
photo
|
string | Photo, relative to the repository root (e.g. .tutti/team/ada.png). |
role
|
string | Role: lead, developer, backend, frontend, tester, reviewer, security, docs, investigator, upgrader, researcher, copywriter, editor, seo, sdr, analyst. |
strengths
|
string | What they're good at and how they should work. |
Agent ids
Built in: claude-code, codex, gemini, opencode, aider.
Any other name with a cmd is a custom agent that runs in a terminal:
[agents.my-agent]
cmd = "my-agent --model local"
prompt_regex = "^> $" # how Tutti sees it's waiting for input
idle_seconds = 20
Environment variables Tutti sets
TUTTI_PORT, TUTTI_PORT_1…, TUTTI_WORKTREE, TUTTI_ROOT,
TUTTI_BRANCH, TUTTI_SESSION, TUTTI_SESSION_ID and TUTTI_PROJECT.
See Worktrees and ports.
Something missing or unclear? Ask us, and we'll answer and improve this page.