Skip to content
YottaCode v0.2.0 is out! ๐ŸŽ‰ See the release notes โ†—
Configuration

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.

FlagEnv varRequiredNotes
--model, -mYOTTACODE_MODELyesProvider-specific model id (run /model list to see what your account allows)
--base-urlYOTTACODE_BASE_URLyesOpenAI-compatible base URL such as http://localhost:11434/v1 or https://api.openai.com/v1
--api-keyYOTTACODE_API_KEYnoBearer token for authenticated providers
--providerYOTTACODE_PROVIDERnoProvider profile name or provider kind hint
--reasoning-effortYOTTACODE_REASONING_EFFORTnoReasoning 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-searchYOTTACODE_ENABLE_WEB_SEARCHnoEnable provider-native web search when supported
--disable-web-searchYOTTACODE_DISABLE_WEB_SEARCHnoDisable provider-native web search even when OpenAI/xAI would enable it by default
--enable-x-searchYOTTACODE_ENABLE_X_SEARCHnoEnable xAI x_search when supported
--enable-code-interpreterYOTTACODE_ENABLE_CODE_INTERPRETERnoEnable provider-native code interpreter when supported
--search-allowed-domainsYOTTACODE_SEARCH_ALLOWED_DOMAINSnoComma-separated allowlist for provider-native web search
--search-excluded-domainsYOTTACODE_SEARCH_EXCLUDED_DOMAINSnoComma-separated blocklist for provider-native web search
--x-search-allowed-handlesYOTTACODE_X_SEARCH_ALLOWED_HANDLESnoComma-separated X handle allowlist for xAI x_search
--x-search-excluded-handlesYOTTACODE_X_SEARCH_EXCLUDED_HANDLESnoComma-separated X handle blocklist for xAI x_search
--x-search-from-dateYOTTACODE_X_SEARCH_FROM_DATEnoInclusive lower bound for xAI x_search in YYYY-MM-DD form
--x-search-to-dateYOTTACODE_X_SEARCH_TO_DATEnoInclusive upper bound for xAI x_search in YYYY-MM-DD form
--systemโ€”noOverride the default system prompt
--resumeโ€”noResume a session by id or name
--continue / -cโ€”noResume the most recent session whose cwd matches the current directory. Mirrors Claude Code’s --continue. Mutually exclusive with --resume.
--yoloโ€”noDANGEROUS: 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โ€”noTool-call cap per turn; defaults to 50. Auto mode raises the effective cap to 4ร— (200). --yolo removes it entirely.
--allow-pathsYOTTACODE_ALLOW_PATHSnoComma-separated extra write roots in addition to the current working directory
--permission-modeโ€”noStartup 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โ€”noResume an existing plan by slug or substring (matched against ~/.yottacode/plans/, newest-first). Implies --permission-mode plan. No-op for yottacode run.

Precedence is:

  1. Explicit flags
  2. Environment variables
  3. Matching provider profile in ~/.yottacode/config.toml
  4. 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
yottacode

Isolation

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.completions vs 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_search filters used on a non-xAI endpoint
  • empty API keys for remote providers
  • suspicious provider/model mismatches such as grok-* on openai

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 --json

yottacode doctor exits non-zero when issues are found. --json emits a stable machine-readable payload.

doctor --json shape

Top-level fields:

  • profile
  • base_url
  • model
  • http_status
  • endpoint_reachable
  • auth_ok
  • model_visible
  • available_models
  • issues
  • warnings

The nested profile object includes:

  • provider
  • uses_responses_api
  • supports_reasoning
  • supports_web_search
  • supports_x_search
  • supports_code_interpreter
  • enabled_builtin_tools
  • issues
  • warnings

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) for openai-auth and copilot (0600; denied to model tools).
      • sessions/ โ€” saved conversations; index.sqlite is the FTS5 index that powers /recall.
      • checkpoints/ โ€” /checkpoints + Esc Esc snapshot store.
      • memory/ โ€” agent-managed user-scope memories (MEMORY.md is 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 aggressively

      See 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-night

      Omit 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:

      PrefixApplies toPattern matches against
      Bashrun_bashFull shell command text
      Readread_file, read_many_filescwd-relative path (doublestar)
      Writewrite_filecwd-relative path (doublestar)
      Editedit_file, apply_diffcwd-relative path (doublestar)
      Mkdirmkdircwd-relative path (doublestar)
      Copy / Move / Deletethe same-named toolspath or src -> dst (string)
      Listlist_dir, list_project_structurecwd-relative path (doublestar)
      Glob / Grepthe same-named toolspattern string
      Fetchfetch_urlURL (string)
      Gitunified git + discrete git_* helpersjoined args (string)
      Githubevery gh_* tool (PR + issue surface)canonical verb name (string)
      Memorymemory_save / memory_forgetop scope:name (string)
      Tests / Rollbackthe same-named toolsempty 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):

      ToolVerb
      gh_pr_readread_pr
      gh_pr_review_contextread_pr_review_context
      gh_pr_createcreate_pr
      gh_pr_updateupdate_pr
      gh_pr_add_commentadd_pr_comment
      gh_issue_readread_issue
      gh_issue_listlist_open_issues

      Wildcards work as in any other rule, so:

      • Github(read_*) covers every read verb
      • Github(*_pr) covers every PR-targeting verb
      • Github(*) is the catch-all (use sparingly โ€” Allow it 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, so Bash(rm -rf /*) covers both rm -rf / and rm -rf /home/user with one rule โ€” you don’t need a separate rm -rf / entry.
      • The space before * is literal: Bash(git status *) matches git status -s but not the bare git 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 .env and nested services/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 explicit model:.
      • mode = "auto" โ€” also routes read-only/search subagents and summarization to fast_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 = 3

      The 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 (edit USER.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.