beflow reads its configuration from $XDG_CONFIG_HOME/beflow/config.json (fallback ~/.config/beflow/config.json). Running beflow doctor creates the file automatically on first run — open it, fill in your workspace details, and re-run.
The file holds the tracker connection, the workspace + project registry, the
agent definitions, and global defaults. The shipped config.example.json
includes a $schema pointing at the published JSON schema for editor
validation. API keys never live in this file — they are read from environment
variables (see Secrets).
| Key | Required | Description |
|---|---|---|
tracker |
yes | Active tracker: "plane" or "linear". |
trackers |
yes | Per-tracker connection settings (see Trackers). |
agent |
yes | Default agent name (a key under agents). See Run defaults. |
runMode |
yes | Default run mode: autonomous or supervised. See Run defaults. |
workspace |
yes | { id, slug } of the tracker workspace. |
projects |
yes | Map of project key → project config (see Projects). |
agents |
no | Map of agent name → agent config (see Agents). |
worktrees |
no | { dir } where --auto runs create per-issue git worktrees. ~ expands; default $XDG_DATA_HOME/beflow/worktrees (fallback ~/.local/share/beflow/worktrees). |
runs |
no | { dir } where run records persist for resume. Default $XDG_STATE_HOME/beflow/runs (fallback ~/.local/state/beflow/runs). |
decisions |
no | { dir, comment } — dir is the append-only policy decision log (default $XDG_STATE_HOME/beflow/decisions, fallback ~/.local/state/beflow/decisions); comment (default true) posts a receipt of each decision on the tracker issue, set false to opt out. The receipt body is the overridable decision-receipt.md template. |
tools |
no | { acpx } — the command array beflow spawns to run acpx. Default ["bunx", "acpx"]. |
prompts |
no | { dir } of user prompt-template overrides. See prompts. |
mcp |
no | { enabled } — inject a .mcp.json cascade into agent runs. Default off. See mcp. |
"trackers": {
"plane": {
"baseUrl": "https://api.plane.so",
"workspaceSlug": "your-workspace",
"apiKeyEnv": "PLANE_API_KEY"
},
"linear": {
"apiKeyEnv": "LINEAR_API_KEY"
}
}Only the active tracker (tracker) needs to be present. apiKeyEnv names the
environment variable that holds the API key.
These top-level keys set the defaults a run resolves against; every project may
override any of them under projects.<KEY>.<same-key>. agent and runMode are
the two required ones (listed in Top level); the rest are optional.
| Key | Required | Description |
|---|---|---|
assignee |
no | Tracker user id; beflow assigns the item to this user when it picks it up. |
onManualMove |
no | yield (default) lets a live run finish but skips writeback if a human moved the card; abort also cancels the agent. |
linkedContext |
no | Inline parent-epic + attachment context into the agent task. Default on. |
deadLetter |
no | { maxAttempts } — failed attempts (crash-resume + CI-rework combined) before an item is quarantined to Needs Input. Default 3. |
inputQuality |
no | { minBodyChars } — a fresh autonomous dispatch of a too-thin issue is parked to Needs Input instead of running. Off when 0/absent. |
qualityGate |
no | { commands, maxRework, baselineTestGlobs } — check command(s) run in the worktree before an implement done opens a PR. On red, beflow auto-reworks the agent up to maxRework times (integer, default 1; 0 = no auto-rework), then fails. baselineTestGlobs pins matching changed test files to the target branch so a change cannot self-grade against tests it weakened (beflow-owned runs only). |
review |
no | { enabled, postToPr } — opt-in PR review assist in watch. |
advisor |
no | { enabled, agents, maxNudges } — opt-in deputy reviewer for --auto runs. After each agent run it reviews the committed work against the ticket's contract and re-dispatches a correction on drift, escalating to Needs Input after maxNudges ignored corrections (default 3). agents names config.agents entries (v1 uses the first). Off by default. See Advisor. |
routing |
no | { triage, spec, implement } — route a job kind to a specific agent name. |
sla |
no | { needsInputMinutes, inReviewMinutes } — re-ping the escalation channel when an item ages past the threshold. |
telemetry |
no | { inComment } — inComment: true appends a compact token/cost line to the writeback comment. Default off. |
policy |
no | Global default policy gate (see PR ownership and policy). A present projects.<KEY>.policy replaces this block wholesale for that project. Sub-keys: evaluator ("globs" | "command" | "agentowners" | "off"; default "off"), rules, agentownersPath, command, onBlock ("comment"; default). Builtin: evaluator "off", onBlock "comment". |
Selectable job kinds: only
triage,spec, andimplementexist (JobKind = "triage" | "spec" | "implement"). Theroutingkeys map these same three values to agent names.reviewis a separate top-level command (beflow review <key>), not a job kind — it cannot be set via a label or meta block.taskandcontinuationdo not exist in the schema; they are not internal names either. When no CLI flag or per-issue meta setsjobKind,autoDetectJobKindderives it from the issue type and state group (see resolution).
Each entry maps a project key (e.g. MYAPP) to a tracker project (a Plane project
or a Linear team) and the local repos its work lands in.
"projects": {
"MYAPP": {
"name": "My App",
"plane_project_id": "00000000-0000-0000-0000-000000000000",
"root": "/path/to/your/project",
"default_repo": "main_repo",
"repos": {
"main_repo": "/path/to/your/project/main_repo",
"website": "/path/to/your/project/website"
},
"module_repo_map": {
"Backend": "main_repo",
"Frontend": "website"
},
"limits": { "inReview": 5, "inProgress": 3 }
}
}| Key | Required | Description |
|---|---|---|
name |
yes | Human-readable project name. |
default_repo |
yes | Repo key (from repos) used when a run resolves no specific repo. |
repos |
yes | Map of repo key → absolute path on disk. |
module_repo_map |
yes | Map of board module name → repo key. Modules become Plane modules; they route work to a repo. |
root |
yes | Absolute path to the project root. |
plane_project_id |
no | The Plane project UUID (Plane only; Linear maps the key to a team). |
limits |
no | WIP caps and a per-run time limit (see sub-keys below). |
agent |
no | Agent name overriding the global default for this project. |
runMode |
no | Run mode overriding the global default for this project. |
ci |
no | { autoReworkOnRed } — re-dispatch rework when an In-Review PR's CI goes red. |
routing |
no | Per-project job-kind → agent routing. |
scheduling |
no | { activeCycleOnly } — only dispatch Todo items in the active cycle. |
pr |
no | Project-level PR mechanics. Replaces the global pr block wholesale. Same sub-keys as the global pr (owner, baseBranch). See PR ownership and policy. |
policy |
no | Project-level policy gate. Replaces the global policy block wholesale. Same sub-keys as the global policy. See PR ownership and policy. |
| Sub-key | Type | Description |
|---|---|---|
inReview |
number | Maximum number of items allowed in In Review simultaneously for this project. Watch skips dispatch when the cap is reached. |
inProgress |
number | Maximum number of items allowed In Progress simultaneously. Watch skips dispatch when the cap is reached. |
maxRunMinutes |
number | Wall-clock limit per agent run in minutes. beflow terminates the run and parks the issue as failed if this threshold is exceeded. |
Per-project deadLetter, inputQuality, qualityGate, review, advisor, sla,
and telemetry mirror their Run defaults counterparts and override them.
A beflow project is a single board, but the work on it often lands in more
than one git repository — say a backend service, a marketing site, and a
shared library. repos is the map from a short repo key to that
repository's absolute path on disk; it is the set of repositories a run is
allowed to touch. (root is just the common parent directory; beflow doctor
checks that root and every repos path exist.)
When beflow runs a work item it resolves which repo the agent works in, then runs the agent in a git worktree of that repo:
--repo <key>on the command line wins, if given.- otherwise
module_repo_maproutes by the item's module — the board module maps to a repo key. - otherwise
default_repois the fallback.
See resolution for the full cascade.
"MYAPP": {
"name": "My App",
"root": "/home/you/projects/app",
"default_repo": "api",
"repos": {
"api": "/home/you/projects/app/api",
"web": "/home/you/projects/app/web",
"shared": "/home/you/projects/app/shared"
},
"module_repo_map": {
"Backend": "api",
"Frontend": "web",
"Shared Library": "shared"
}
}With the above, a work item filed under the Backend module runs in api, a
Frontend item in web, and a Shared Library item in shared. An item
with no module (or one not in the map) falls back to default_repo (api).
Override per run with beflow run <KEY>-42 --repo web.
When — and only when — you opt into the agentowners policy gate
(policy.evaluator = "agentowners"), beflow setup/update scaffold a starter
AGENTOWNERS file into each of these repos paths (at agentownersPath; existing
files are left untouched). With any other evaluator, including the default off,
beflow writes nothing into your repos. See
scaffolding the file.
Each entry defines how to launch one coding-agent CLI.
"agents": {
"claude": {
"command": "claude",
"args": ["--dangerously-skip-permissions"],
"acpCommand": "bunx",
"acpArgs": ["@agentclientprotocol/claude-agent-acp"],
"model": "claude-opus-4-8"
}
}| Key | Required | Description |
|---|---|---|
command |
yes | Interactive CLI binary used by --open (direct spawn). |
args |
no | Extra args for the --open spawn (before the task). |
acpCommand |
no | ACP-server binary for --auto/--attend via acpx. Defaults to command. |
acpArgs |
no | Args for the ACP server. beflow runs acpx --agent "<acpCommand ?? command> <acpArgs…>". |
model |
no | acpx --model for --auto/--attend. |
API keys are read from the environment, never from config.json. Set them in
your shell profile (~/.zshrc or ~/.bashrc) and reload:
# zsh:
echo 'export PLANE_API_KEY=...' >> ~/.zshrc && source ~/.zshrc
# bash:
echo 'export PLANE_API_KEY=...' >> ~/.bashrc && source ~/.bashrc
# Windows PowerShell:
[System.Environment]::SetEnvironmentVariable("PLANE_API_KEY","your_token","User")
# Use LINEAR_API_KEY instead if you are on Linear.The variable names are whatever each tracker's apiKeyEnv points at.