Configuration
yottacode does not ship with built-in defaults for the required provider
settings. If --model or --base-url is missing, startup fails immediately
with a clear error.
Flags And Environment Variables
Every flag below works for both yottacode and yottacode run.
The same provider flags also apply to yottacode doctor.
| Flag | Env var | Required | Notes |
|---|---|---|---|
--model, -m | YOTTACODE_MODEL | yes | Provider-specific model id (run /model list to see what your account allows) |
--base-url | YOTTACODE_BASE_URL | yes | OpenAI-compatible base URL such as http://localhost:11434/v1 or https://api.openai.com/v1 |
--api-key | YOTTACODE_API_KEY | no | Bearer token for authenticated providers |
--provider | YOTTACODE_PROVIDER | no | Provider profile name or provider kind hint |
--reasoning-effort | YOTTACODE_REASONING_EFFORT | no | Reasoning effort for providers that support it: low, medium, or high (unset = provider default). Applies to OpenAI, Anthropic, Gemini, and xAI via each one’s native knob โ see providers.md. Change it mid-session with /effort. |
--enable-web-search | YOTTACODE_ENABLE_WEB_SEARCH | no | Enable provider-native web search when supported |
--disable-web-search | YOTTACODE_DISABLE_WEB_SEARCH | no | Disable provider-native web search even when OpenAI/xAI would enable it by default |
--enable-x-search | YOTTACODE_ENABLE_X_SEARCH | no | Enable xAI x_search when supported |
--enable-code-interpreter | YOTTACODE_ENABLE_CODE_INTERPRETER | no | Enable provider-native code interpreter when supported |
--search-allowed-domains | YOTTACODE_SEARCH_ALLOWED_DOMAINS | no | Comma-separated allowlist for provider-native web search |
--search-excluded-domains | YOTTACODE_SEARCH_EXCLUDED_DOMAINS | no | Comma-separated blocklist for provider-native web search |
--x-search-allowed-handles | YOTTACODE_X_SEARCH_ALLOWED_HANDLES | no | Comma-separated X handle allowlist for xAI x_search |
--x-search-excluded-handles | YOTTACODE_X_SEARCH_EXCLUDED_HANDLES | no | Comma-separated X handle blocklist for xAI x_search |
--x-search-from-date | YOTTACODE_X_SEARCH_FROM_DATE | no | Inclusive lower bound for xAI x_search in YYYY-MM-DD form |
--x-search-to-date | YOTTACODE_X_SEARCH_TO_DATE | no | Inclusive upper bound for xAI x_search in YYYY-MM-DD form |
--system | โ | no | Override the default system prompt |
--resume | โ | no | Resume a session by id or name |
--continue / -c | โ | no | Resume the most recent session whose cwd matches the current directory. Mirrors Claude Code’s --continue. Mutually exclusive with --resume. |
--yolo | โ | no | DANGEROUS: auto-approve every tool call without prompting and remove the iteration cap (deny rules in permissions.json still apply). Launch-only; cannot be toggled mid-run โ restart yottacode without the flag to recover. |
--max-iterations | โ | no | Tool-call cap per turn; defaults to 50. Auto mode raises the effective cap to 4ร (200). --yolo removes it entirely. |
--allow-paths | YOTTACODE_ALLOW_PATHS | no | Comma-separated extra write roots in addition to the current working directory |
--permission-mode | โ | no | Startup permission mode: default (no startup mode), plan (read-only research; describe the task as your first message), or auto (edits auto-allow; bash & commits still prompt). Mirrors Claude Code’s --permission-mode. No-op for yottacode run. |
--plan-resume | โ | no | Resume an existing plan by slug or substring (matched against ~/.yottacode/plans/, newest-first). Implies --permission-mode plan. No-op for yottacode run. |
Precedence is:
- Explicit flags
- Environment variables
- Matching provider profile in
~/.yottacode/config.toml - Error for missing required values
Resolution lives in internal/cli/options.go.
Examples
# Local Ollama (no API key) โ flags form
yottacode --provider ollama --model <your-model-id> --base-url http://localhost:11434/v1
# Same thing through env vars
export YOTTACODE_PROVIDER=ollama
export YOTTACODE_MODEL=<your-model-id>
export YOTTACODE_BASE_URL=http://localhost:11434/v1
yottacode
# OpenAI API key
export YOTTACODE_PROVIDER=openai
export YOTTACODE_MODEL=<your-model-id>
export YOTTACODE_BASE_URL=https://api.openai.com/v1
export YOTTACODE_API_KEY=sk-...
yottacode
# ChatGPT OAuth (first run: yottacode openai-auth login)
export YOTTACODE_PROVIDER=openai-auth
export YOTTACODE_MODEL=<your-model-id> # /model list shows what your account allows
export YOTTACODE_BASE_URL=https://chatgpt.com/backend-api/codex
yottacode
# GitHub Copilot (first run: yottacode copilot-auth login)
export YOTTACODE_PROVIDER=copilot
export YOTTACODE_MODEL=claude-haiku-4.5
export YOTTACODE_BASE_URL=https://api.githubcopilot.com
yottacode
# Anthropic
export YOTTACODE_PROVIDER=anthropic
export YOTTACODE_MODEL=<your-model-id>
export YOTTACODE_BASE_URL=https://api.anthropic.com
export YOTTACODE_API_KEY=sk-ant-...
yottacode
# xAI with default web_search + explicit x_search
export YOTTACODE_PROVIDER=xai
export YOTTACODE_MODEL=<your-model-id>
export YOTTACODE_BASE_URL=https://api.x.ai/v1
export YOTTACODE_API_KEY=xai-...
export YOTTACODE_SEARCH_ALLOWED_DOMAINS=docs.x.ai,arxiv.org
export YOTTACODE_ENABLE_X_SEARCH=1
export YOTTACODE_X_SEARCH_ALLOWED_HANDLES=xai
yottacode
# Opt out of default hosted web search
export YOTTACODE_DISABLE_WEB_SEARCH=1
yottacode
# Allow writes into sibling repos too
export YOTTACODE_ALLOW_PATHS=/home/me/shared-configs,/home/me/other-repo
yottacodeIsolation
There is no in-process sandbox, and there will not be one. yottacode
keeps its core small and does not ship bwrap/firejail/landlock
backends. run_bash and every other tool run directly on the host.
For real isolation, run yottacode inside a container or
devcontainer โ that protects every tool (not just shell commands)
and is portable across host distros.
Provider Diagnostics
At startup, yottacode resolves a provider profile from the configured
endpoint, model, and feature flags. That profile drives:
- adapter routing (
chat.completionsvs Responses API) - which provider-native tools are actually enabled
- static diagnostics shown in the startup card, footer, and
/provider
Examples of diagnostics:
- unsupported built-in tool flags on the selected provider
x_searchfilters used on a non-xAI endpoint- empty API keys for remote providers
- suspicious provider/model mismatches such as
grok-*onopenai
Use /provider to inspect the resolved static state, or /doctor to run an
active /models probe against the configured endpoint.
Default Hosted Web Search
yottacode enables provider-native web_search by default for:
- OpenAI
- xAI
That default can be disabled with --disable-web-search or
YOTTACODE_DISABLE_WEB_SEARCH=1.
For Ollama and generic OpenAI-compatible endpoints, hosted provider tools stay
off by default. Those models can use the local fetch_url tool instead.
For shell and CI usage:
yottacode doctor
yottacode doctor --jsonyottacode doctor exits non-zero when issues are found. --json emits a
stable machine-readable payload.
doctor --json shape
Top-level fields:
profilebase_urlmodelhttp_statusendpoint_reachableauth_okmodel_visibleavailable_modelsissueswarnings
The nested profile object includes:
provideruses_responses_apisupports_reasoningsupports_web_searchsupports_x_searchsupports_code_interpreterenabled_builtin_toolsissueswarnings
Example:
{
"profile": {
"provider": "openai",
"uses_responses_api": true,
"supports_reasoning": true,
"supports_web_search": true,
"supports_x_search": false,
"supports_code_interpreter": true,
"enabled_builtin_tools": ["web_search"],
"warnings": ["API key is empty for a remote provider"]
},
"base_url": "https://api.openai.com/v1",
"model": "<your-model-id>",
"http_status": 200,
"endpoint_reachable": true,
"auth_ok": true,
"model_visible": true,
"available_models": ["<model-a>", "<model-b>"],
"warnings": ["API key is empty for a remote provider"]
}On-Disk State
Most state lives under ~/.yottacode/:
- openai-auth.json
- openai-auth-models.json
- copilot.json
- copilot-models.json
- <id>.json
- <name>.md
- MEMORY.md
- index.sqlite
- USER.md
- config.toml
auth/โ provider OAuth token stores plus per-account model caches (*-models.json) foropenai-authandcopilot(0600; denied to model tools).sessions/โ saved conversations;index.sqliteis the FTS5 index that powers/recall.checkpoints/โ/checkpoints+Esc Escsnapshot store.memory/โ agent-managed user-scope memories (MEMORY.mdis the auto-generated index).projects/<slug>/memory/โ agent-managed project-scope memories (per-user).USER.mdโ optional global user memory (human-only).config.tomlโ tunables (context watermarks, retrieval, checkpoints).
Checkpoints retention
/checkpoints and Esc Esc capture a per-prompt snapshot under ~/.yottacode/checkpoints/. By default, snapshots expire 30 days after creation; the sweep runs opportunistically when a session opens. Override the window in ~/.yottacode/config.toml:
[checkpoints]
retention_days = 30 # set to 0 to fall back to the 30-day default; smaller values prune more aggressivelySee tui-slash-commands.md for the full feature.
Theme
/theme switches the TUI color palette and persists the choice. Pin a non-default theme in ~/.yottacode/config.toml:
[theme]
name = "catppuccin"
# one of: terminal, catppuccin, dimmed, gruvbox, high-contrast,
# low-contrast, no-color, nord, one-dark, solarized-dark,
# tokyo-nightOmit the section to ride the default (terminal). Unknown names are rejected at load time โ see themes.md for the full palette catalog.
Per-repo state lives under <repo>/.yottacode/:
<repo>/.yottacode/
YOTTACODE.md optional per-repo project memory (human-seeded;
the agent keeps it fresh through approval-gated
writes)
permissions.json committable team-shared permission rules
permissions.local.json gitignored personal additions (where the
modal's [a]lways-allow path writes to)See memory.md for how the agent-managed memory layer
works and how to inspect or prune it.
USER.md, YOTTACODE.md, and both permissions*.json files are safe to
edit directly โ opening either memory file via /memory (yottacode
suspends to vim) reloads on exit, and external edits to either
permissions*.json are picked up automatically on the next tool call
(run /permissions to print the two paths). Session files and the FTS
index are application-managed.
USER.md is in the agent’s write-deny list (global preferences are
out of scope for a project-scoped agent to curate). YOTTACODE.md is
not in the deny list: the agent updates it through edit_file /
write_file with the standard approval modal gating each change. To
make YOTTACODE.md human-only on a specific project, add a deny rule
to <repo>/.yottacode/permissions.json:
{ "permissions": { "deny": ["Edit(.yottacode/YOTTACODE.md)", "Write(.yottacode/YOTTACODE.md)"] } }Add **/.yottacode/permissions.local.json to your .gitignore so
personal allow rules don’t leak into the team-shared file.
A missing, empty, or whitespace-only permissions.json /
permissions.local.json is treated as “no rules” โ yottacode no longer
fails to start when either file exists but has no content. Opening
either path from /permissions seeds the file with the full
{allow, ask, deny} skeleton before vim launches, so you always edit a
fully-shaped file instead of an empty buffer. Files that already have
content are never overwritten.
Rule Prefixes
Each rule has the shape <Tool>(<pattern>). Supported tool prefixes:
| Prefix | Applies to | Pattern matches against |
|---|---|---|
Bash | run_bash | Full shell command text |
Read | read_file, read_many_files | cwd-relative path (doublestar) |
Write | write_file | cwd-relative path (doublestar) |
Edit | edit_file, apply_diff | cwd-relative path (doublestar) |
Mkdir | mkdir | cwd-relative path (doublestar) |
Copy / Move / Delete | the same-named tools | path or src -> dst (string) |
List | list_dir, list_project_structure | cwd-relative path (doublestar) |
Glob / Grep | the same-named tools | pattern string |
Fetch | fetch_url | URL (string) |
Git | unified git + discrete git_* helpers | joined args (string) |
Github | every gh_* tool (PR + issue surface) | canonical verb name (string) |
Memory | memory_save / memory_forget | op scope:name (string) |
Tests / Rollback | the same-named tools | empty descriptor (binary allow/deny) |
Github(...) descriptors are the canonical verb name extracted from
the tool name (independent of the resource-first tool naming so the
roadmap’s Github(read_*) style works):
| Tool | Verb |
|---|---|
gh_pr_read | read_pr |
gh_pr_review_context | read_pr_review_context |
gh_pr_create | create_pr |
gh_pr_update | update_pr |
gh_pr_add_comment | add_pr_comment |
gh_issue_read | read_issue |
gh_issue_list | list_open_issues |
Wildcards work as in any other rule, so:
Github(read_*)covers every read verbGithub(*_pr)covers every PR-targeting verbGithub(*)is the catch-all (use sparingly โAllowit and writes auto-approve)
Owner/repo scoping (Github(create_pr owner/repo)) is not yet
implemented โ every call currently resolves against the cwd’s git
remote. The roadmap tracks per-repo scoping for the cloud bot work
(SaaS Phase 2).
Starter Rule Set
The default skeleton ships with empty arrays โ yottacode is unopinionated
about which rules a project wants. The set below is a curated starting
point you can paste into <repo>/.yottacode/permissions.json and prune
to taste. Decision precedence is Deny > Allow > Ask > Default, so the
deny block always wins even if a broader allow is added later.
{
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git status *)",
"Bash(git diff)",
"Bash(git diff *)",
"Bash(git log)",
"Bash(git log *)",
"Bash(git show *)",
"Bash(git branch)",
"Bash(git branch -*)",
"Bash(git remote -v)",
"Bash(ls)",
"Bash(ls *)",
"Bash(pwd)",
"Bash(echo *)",
"Bash(which *)",
"Bash(head *)",
"Bash(tail *)",
"Bash(wc *)",
"Github(read_*)",
"Github(list_open_issues)"
],
"ask": [
"Bash(git push *)",
"Bash(git reset --hard *)",
"Bash(git rebase *)",
"Bash(gh pr create *)",
"Bash(gh pr merge *)",
"Bash(gh pr close *)",
"Github(create_pr)",
"Github(update_pr)",
"Github(add_pr_comment)",
"Bash(gh release *)",
"Bash(npm publish*)",
"Bash(cargo publish*)",
"Bash(docker push *)",
"Read(**/.env)",
"Read(**/.env.*)",
"Read(**/*.pem)",
"Read(**/id_rsa)",
"Read(**/credentials*)"
],
"deny": [
"Bash(rm -rf /*)",
"Bash(rm -rf ~*)",
"Bash(sudo rm -rf *)",
"Bash(curl * | sh)",
"Bash(curl * | bash)",
"Bash(wget * | sh)",
"Bash(wget * | bash)",
"Bash(* | sudo sh)",
"Bash(* | sudo bash)",
"Bash(dd if=* of=/dev/*)",
"Bash(mkfs.*)",
"Bash(chmod -R 777 /)",
"Bash(chmod -R 777 /*)",
"Edit(/etc/**)",
"Edit(/usr/**)",
"Edit(/bin/**)",
"Edit(/sbin/**)",
"Edit(/boot/**)",
"Write(/etc/**)",
"Write(/usr/**)",
"Delete(/etc/**)",
"Delete(/usr/**)"
]
}
}Pattern semantics that catch new authors out:
*matches the empty sequence too, soBash(rm -rf /*)covers bothrm -rf /andrm -rf /home/userwith one rule โ you don’t need a separaterm -rf /entry.- The space before
*is literal:Bash(git status *)matchesgit status -sbut not the baregit status. Add both forms when you want to cover the command with and without arguments. - Path-typed rules (
Read,Write,Edit,Delete, โฆ) use doublestar;Read(**/.env)matches both top-level.envand nestedservices/api/.env.
Personal additions go in permissions.local.json (gitignored). A
common starter for a Go project:
{
"permissions": {
"allow": [
"Bash(go *)",
"Bash(make *)",
"Bash(gofmt *)",
"Bash(goimports *)"
],
"ask": [],
"deny": []
}
}MCP servers
yottacode is a client for Anthropic’s Model Context Protocol. Each [[mcp_servers]] block in ~/.yottacode/config.toml launches a subprocess at session start and registers its tools under the mcp/<name>/<tool> namespace.
[[mcp_servers]]
name = "filesystem"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/home/me/workspace"]
[[mcp_servers]]
name = "github"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
env = { GITHUB_PERSONAL_ACCESS_TOKEN = "$GITHUB_PAT" }env values support $VAR substitution from yottacode’s process environment so secrets stay out of the config file. v1 supports stdio transport only. See mcp.md for the full reference, including permission rules (MCP(...)), the /mcp slash command, and a curated server list.
Model routing
The [router] block hosts two independent, opt-in features.
Cache-safe task routing runs isolated work (subagents, history compaction) on a cheap model while your main conversation stays on your chosen model โ a pure cost saving with no prompt-cache churn:
[router]
mode = "auto" # off | manual | auto (default off)
fast_model = "anthropic:claude-haiku-4-5"
smart_model = "anthropic:claude-opus-4-6"mode = "off"(or absent) โ disabled; fully backward compatible.mode = "manual"โ only routes subagents that declare an explicitmodel:.mode = "auto"โ also routes read-only/search subagents and summarization tofast_model.
fast_model / smart_model are required when mode is not off and
use the "<provider>" or "<provider>:<model>" grammar; the model must
exist in that provider’s models. See models.md
for the cost rationale and the auto heuristic.
Multi-provider failover (separate feature, same block) dispatches each main-thread turn across an ordered candidate list, falling through on early failure:
[router]
enabled = true
policy = "fallback-chain" # fallback-chain | cheap-first
candidates = ["anthropic:claude-haiku-4-5", "openai:gpt-4o"]
health_window_seconds = 60
health_failure_threshold = 3The two are orthogonal: enabled/candidates control failover across
providers; mode/fast_model/smart_model control task routing. You
can set either, both, or neither.
Runtime Reconfiguration
The TUI supports changing the active session configuration without restarting:
/model <name>/provider(use/provider use <name>to swap endpoint + key in one step)/doctor/permissions(prints the shared + local rule file paths; edit either file directly)/memory(editUSER.md/YOTTACODE.md, browse user-scope and project-scope memories)
These changes apply to the current session only. They do not rewrite your shell configuration or future launch defaults.