junco
contents

Configuration

junco reads one JSON file: ~/.junco/config.json, resolved from the environment only — the working directory never matters, and the deprecated --config flag is ignored. A legacy ~/.config/junco/config.json (respects XDG_CONFIG_HOME) keeps working until junco data migrate moves it. Every field is optional — omit a key and its default applies. The guided way to produce the file is the setup walkthrough (junco dashboard on a first run); the headless way is junco config init.

Levers marked live apply on the daemon's next poll after the file changes — no restart. Levers marked restart need junco restart; junco status lists any pending ones. Inspect and edit from the command line:

junco config list
junco config get worker.pollIntervalSeconds
junco config set worker.dailyBudgetUsd 5

A minimal config is the empty object {} — local endpoint, sandbox on, GitHub bridge off. A typical hosted-catalog setup touches three things:

{
  "model": { "id": "<provider>/<model-name>" },
  "worker": { "dailyBudgetUsd": 5 },
  "github": {
    "enabled": true,
    "repos": [{ "nwo": "acme/reef-api", "path": "~/code/reef-api" }]
  }
}
note — catalog model ids are provider-prefixed; the walkthrough and junco config list print the real ones. An unset model.baseUrl means the local default, http://127.0.0.1:1234/v1 — any OpenAI-compatible endpoint works.

Deprecated path keys (vaultRoot, juncoSubdir, observability.stateDir, git.worktreeRoot, github.externalReposRoot) still work, warn once at startup, and are flagged by junco doctor; junco data migrate unifies them under dataDir. TOML configs are no longer read — a nearby config.toml is a startup error, not a silent ignore.

Environment variables

JUNCO_LOG_JSON=1
Force JSON log lines even on a TTY.
GH_CONFIG_DIR
Injected by the daemon for bot-account GitHub traffic; not yours to set.
$VISUAL / $EDITOR
Editor opened by junco analyze edit.
XDG_CONFIG_HOME
Relocates the default config directory.
provider key vars
Hosted catalog models read their provider's conventional env var when model.apiKey is unset; model.apiKey also accepts a literal or a "$ENV_VAR" reference.

Every lever

Generated from junco config list at the version stamped below — names, types, defaults, and descriptions are junco's own. read-only marks structured levers you edit in the file directly rather than via junco config set.

top-level

dataDir string
Unified data root — queue, reviews, outbox, mirror, clones, worktrees, transcripts. default undefined · restart

Everything junco keeps on disk resolves under this one root (default ~/.local/state/junco):

<dataDir>/
  queue/            inbox · processing · done · failed
  review/           parked assess findings · parked analyze drafts
  outbox/           queued GitHub writes (+ dead/ after 3 attempts)
  clones/           watched/ and external/ managed checkouts
  worktrees/        ephemeral per-ticket build worktrees
  transcripts/      <ticket-id>.jsonl per-run event streams
  watchlist.json · spend.json · worker.log · migrated.json

The root writes itself a .gitignore (*), so pointing dataDir inside a git checkout can never dirty a commit. junco data prints the live tree with counts and provenance; junco data migrate folds any legacy-key layout into it.

vaultRoot string
Root directory Junco keeps its ticket queue under. default undefined · restart
juncoSubdir string
Subdirectory under vaultRoot holding inbox/processing/done/failed. default "Junco" · restart
tools structured, read-only
Tool allowlist granted to the coding agent. default ["read","bash","edit","write","grep","find","ls"] · live
updateCheck boolean
Enable periodic update checks for new Junco releases. default true · live

model.*

model.id string
Provider-prefixed model id, e.g. <provider>/<model-name>. default "local/my-model" · live
model.source string
Model resolution mode: auto (catalog for non-local providers without an explicit baseUrl), catalog, or inline. default "auto" · live
model.modelsJson string
Path to a Pi-style models.json; when set, provider+model load from that file and the inline capability fields are ignored. default undefined · live
model.api string
Pi API style, e.g. openai-completions. default "openai-completions" · live
model.baseUrl string
OpenAI-compatible /v1 endpoint base URL. Unset = local default (http://127.0.0.1:1234/v1); setting it forces inline resolution. default undefined · live
model.apiKey secret
API key for the inference endpoint. Literal, "$ENV_VAR" reference, or unset to use the provider's env var (e.g. <PROVIDER>_API_KEY) for hosted catalog models. default undefined · live
model.retry.maxRetries number
SDK auto-retry attempts on transient provider errors; unset = SDK default (3). default undefined · live
model.retry.baseDelayMs number
Base delay for SDK auto-retry backoff in ms; unset = SDK default (2000). default undefined · live
model.reasoning boolean
Whether the model supports reasoning/thinking tokens. default true · live
model.input structured, read-only
Accepted input modalities for the model. default ["text","image"] · live
model.contextWindow number
Model context window size in tokens. default 131072 · live
model.maxTokens number
Max output tokens per model call. default 49152 · live
model.cost.input number
Cost per input token, for cost tracking/reporting. default 0 · live
model.cost.output number
Cost per output token, for cost tracking/reporting. default 0 · live
model.cost.cacheRead number
Cost per cache-read token, for cost tracking/reporting. default 0 · live
model.cost.cacheWrite number
Cost per cache-write token, for cost tracking/reporting. default 0 · live
model.thinkingLevel string
Worker default reasoning/thinking level. default "medium" · live
model.compat structured, read-only
Provider compatibility overrides (developer role, reasoning effort, thinking format, etc.), merged over junco's defaults. default {} · live

worker.*

worker.defaultTimeoutMinutes number
Default per-ticket execution timeout in minutes. default 30 · live
worker.pollIntervalSeconds number
Seconds between inbox polls when idle. default 15 · live
worker.startupPollSeconds number
Seconds the daemon waits/polls at startup before normal polling begins. default 30 · live
worker.startupWait boolean
Whether the daemon waits at startup for crash-recovery grace before claiming. default true · live
worker.endpointProbe auto | always | never
Endpoint probe policy: auto (probe local/inline, skip hosted catalog), always, or never. default "auto" · live
worker.maxTransientRetries number
Max retries for transient failures before a ticket is marked failed. default 2 · live
worker.retryBackoffSeconds number
Backoff seconds before a requeued ticket becomes eligible again. default 60 · live
worker.maxConcurrent number
Parallel ticket slots; same-repo tickets always serialize (restart to apply — the serial-vs-scheduler mode fork is chosen once at startup). default 1 · restart
worker.commitLeftovers boolean
Sweep uncommitted leftovers into a final commit at run end. default false · live
worker.dailyBudgetUsd number
Daily USD spend cap; claiming pauses once reached until local midnight (0 disables). default 0 · live
note — every agent session's cost lands in a per-day ledger (spend.json, surfaced on /health and the dashboard). At the cap, claiming pauses — loudly, without burning any ticket's retry budget — and resumes at local midnight or when you raise the cap. 0 disables the cap but keeps the ledger.

supervisor.*

supervisor.enabled boolean
Enable the supervisor (nudge/escalate/kill loop-guard). default true · live
supervisor.budgetPerKind number
Budget of guard interventions allowed per kind before escalation. default 1 · live
supervisor.escalationWindowTurns number
Turn window over which supervisor escalation is evaluated. default 3 · live
supervisor.outputBudgetPerTurn number
Max output budget per turn before the supervisor nudges. default 12000 · live
supervisor.outputBudgetPostCommit number
Max output budget per turn once a commit has landed. default 24000 · live

git.*

git.gitBin string
Path/name of the git binary to invoke. default "git" · live
git.ghBin string
Path/name of the GitHub CLI (gh) binary to invoke. default "gh" · live
git.defaultBaseBranch string
Default base branch PRs are opened against. default "main" · live
git.branchPrefix string
Prefix applied to branches Junco creates. default "junco/" · live
git.worktreeRoot string
Deprecated: overrides dataDir for per-ticket git worktrees only. default undefined · live
git.removeWorktreeOnSuccess boolean
Remove the git worktree after a successful run. default true · live
git.allowedRepoRoots structured, read-only
Containment rail: when non-empty, PR-flow tickets may only target repos under these roots ([] = anywhere). default [] · live
note — the containment rail for the folder inbox: with roots set, a PR-flow ticket whose repo: lies outside them fails plan-lint before any agent runs. An empty list means any path — fine for a single-user machine, worth narrowing wherever anything else can write tickets.

pr.*

pr.draftByDefault boolean
Open PRs as drafts by default. default true · live
pr.defaultLabels structured, read-only
Labels applied to every PR Junco opens. default [] · live

verify.*

verify.enabled boolean
Run the verify (build/test) phase before opening a PR. default true · live
verify.commandTimeout number
Timeout in seconds for the verify command. default 60 · live
verify.blockOnFail boolean
Block PR creation when verify fails (vs. warn and continue). default false · live

sandbox.*

Agent tool subprocesses run OS-confined: writes jailed to the worktree plus scratch, reads of secret paths denied, environment scrubbed, network egress denied unless a ticket sets network: true. Backends: Seatbelt on macOS (built in), bubblewrap on Linux (apt/dnf install bubblewrap).

warn — the sandbox fails closed. On Linux without bwrap the worker refuses to run tickets rather than run them unconfined; install bubblewrap or set sandbox.backend: "none" deliberately.
sandbox.enabled boolean
Wrap agent tool subprocesses in an OS sandbox (env scrub + path jail). default true · live
sandbox.backend auto | seatbelt | bwrap | none
Sandbox backend: auto (seatbelt on macOS, bwrap on Linux), a forced one, or none. default "auto" · live
sandbox.network deny | allow
Default network egress for agent tool subprocesses (per-ticket network:true widens it). default "deny" · live
sandbox.extraDenyRead structured, read-only
Extra absolute paths whose reads are denied (added to the built-in secret deny-list). default [] · live
sandbox.extraAllowWrite structured, read-only
Extra absolute paths where writes are permitted (added to worktree + scratch). default [] · live

critic.*

critic.enabled boolean
Run the post-session critic review pass before finalizing a PR. default true · live
critic.maxRetries number
Max critic-requested revision retries. default 1 · live
critic.thinking string
Thinking level used for critic review calls. default "minimal" · live

planLint.*

planLint.enabled boolean
Run plan-lint checks on ticket plans before execution. default true · live
planLint.blockOnError boolean
Block execution when plan-lint finds errors (vs. warn). default true · live
planLint.checkLabels boolean
Include label checks in plan-lint. default true · live

observability.*

observability.healthEnabled boolean
Enable the /health metrics HTTP server. default true · restart
observability.healthHost string
Bind address for /health; non-loopback exposes metrics (restart to rebind). default "127.0.0.1" · restart
observability.healthPort number
Port the /health metrics server binds (restart to rebind). default 8787 · restart
observability.logLevel debug | info | warn | error
Daemon-wide log threshold (applied live). default "info" · live
observability.stateDir string
Deprecated: overrides dataDir for the entire data root. default undefined · restart
observability.logToFile boolean
Write daemon logs to a file under dataDir. default true · restart
observability.transcripts boolean
Write per-ticket event transcripts under dataDir. default true · restart

github.*

github.enabled boolean
Enable the GitHub issues→inbox bridge (disabled by default: zero gh calls). default false · restart
github.triggerLabel string
Approval label that triggers the PR flow; lifecycle labels derive from it. Restart to apply — the reporter bakes in the label prefix at startup. default "junco" · restart
github.askLabel string
Issue label that routes to the read-only Q&A path (defaults to `<triggerLabel>:ask`). Restart to apply (see triggerLabel). default undefined · restart
github.pollIntervalSeconds number
GitHub bridge sweep cadence, independent of the worker poll interval. default 60 · live
github.requireApproval boolean
When false, a plan-ready GitHub ticket auto-executes on the next sweep. default true · live
warn — setting this false removes the human gate: any plan-ready issue executes on the next sweep. On a repo with other collaborators, someone with triage-only access could steer work they could never approve. Keep it true anywhere that isn't a private personal repo.
github.plannerModelId string
Planning-session model id override for GitHub tickets (same endpoint). default undefined · live
github.externalReposRoot string
Root directory for managed clones of unowned repos (fork-PR flow). default undefined · live
github.repos structured, read-only
Explicit list of watched GitHub repos (owner/repo name-with-owner + local path). default [] · live

botAccount.*

botAccount.enabled boolean
Act as a dedicated bot account for all daemon GitHub traffic (log it in with: junco auth login). default false · restart
botAccount.configDir string
Isolated gh config dir holding the bot login (GH_CONFIG_DIR for daemon gh/git). default "~/.junco/gh" · restart

assess.*

assess.maxIssuesPerRun number
Cap on issues filed per `junco assess` run. default 20 · live
assess.minSeverity critical | high | medium | low
Drop assessment findings below this severity. default "low" · live
assess.npmBin string
Binary for the dependency scan (npm audit --json). default "npm" · live
assess.fileAs me | bot
Identity `junco assess file` posts under: me (your gh login) or bot (the dedicated bot account; fails loud when its login is missing). default "me" · live

skills.*

skills.harnessDirs structured, read-only
Harness skills dirs that receive a junco-dispatch symlink (standing consent; managed by 'junco skill install' and the wizard). default [] · restart