Reference

The config file, explained

hexiel's settings live in one plain-text file, config.toml. You can change most of them from inside the app, but if you'd rather edit the file by hand — or you're setting up a new machine — this is every setting, what it does, its default, and examples you can paste.

The basics

Where it is

~/.config/hexiel/config.toml (on Windows, .config\hexiel\config.toml in your user folder). hexiel writes a starter file the first time it runs. Set the HEXIEL_HOME environment variable to keep it somewhere else.

Two ways to change it

In the app: /config shows every common setting and changes it for this session or saves it. /model picks a model. By hand: edit the file, then restart hexiel. Saves from the app change only the one line they touch, so your comments and layout survive; the first save keeps a copy as config.toml.bak.

The file is TOML: key = value lines grouped under [tables], # starts a comment, text goes in quotes, true/false are unquoted. It has four kinds of content: one top-level key (default), a [settings] table, one [models.NAME] table per model, and optional [permissions] and [mcp] tables.

Since version 0.3.20, a mistake in the file stops hexiel with a one-line message that says which table and key is wrong and what is allowed — not a Python traceback. Keys that hexiel doesn't know inside [settings] are ignored without a message, so check spelling if a setting seems to do nothing.

default

The model profile hexiel starts with. It must name one of your [models.*] tables. Override it for one run with hexiel -m NAME, or switch inside the app with /model NAME.

default = "local"

[settings]

How hexiel behaves, regardless of which model it uses. Every key is optional.

Everyday behaviour

KeyDefaultWhat it does
auto_approvefalsefalse: ask before file edits and shell commands. "except-commits": run everything without asking except anything that commits or publishes to git or svn — those still ask. true: never ask, including commits, pushes and deletes (risky). Change it live with /auto — it cycles through the three, or name one: /auto off, /auto commits, /auto all. Deny rules in [permissions] always apply. Details.
notifytrueDesktop notification when hexiel needs your approval or a long turn finishes.
auto_commitfalseAfter each finished turn, git-commit exactly the files hexiel changed — never anything else in your working tree.
toolsets"auto"Which optional tool groups to load: "auto" (load a group when the project looks like it needs it), "all", or a list such as ["data", "research"]. Groups: data (spreadsheets, SQL, charts), schedule (recurring jobs), docs (questions over PDFs and Word files), research (deep web research). Fewer groups means a shorter prompt.

Auto-approve: how much to trust it

SettingBehaviourStatus line shows
false (default)Asks before every file edit and shell command (read-only tools never ask).approving writes
"except-commits"Runs edits, commands, installs, deletes and so on without asking — but stops and asks before anything that would commit or publish history.auto-approve · asks before commits
trueNever asks. Commits, pushes, rm -rf — all run. Only your deny rules stop anything.AUTO-APPROVE ALL (red)

What counts as a commit. Commands containing git commit, push, merge, rebase, cherry-pick, revert, am or the ci alias; svn commit/ci/import; svnmucc; and svn copy/move/delete/mkdir/propset when aimed at a repository URL (against a local working copy those only schedule a change, so they don't ask). hexiel looks inside shell commands, Python code, container commands and MCP tools — including bash -c "…", sudo, and options such as git -C dir commit. When it can't tell, it asks. When it asks you can say yes once, or no; choosing Always approves that one call but never turns the guard off.

What it can't see. A shell alias or script that commits behind an innocent name (gc, ./release.sh) looks harmless from the outside, so keep it out of auto-approved sessions or add a deny rule for it. The separate auto_commit setting, if you've turned it on, still commits after each turn — that is you opting in to automatic commits. An explicit allow rule such as Bash(git commit:*) in [permissions] also lets that command through without asking. Delegated /delegate workers and plan checks follow the same mode.

Context and speed

KeyDefaultWhat it does
compact_threshold0.82When the conversation reaches this share of the model's context_window, hexiel summarizes the older part to free space. Lower it (0.5–0.7) for small local models that get less reliable on long prompts; the cost is more frequent summaries, and on a slow local model each one is a long wait — so pair a low threshold with a fast compact_profile.
compact_profile""The model profile that writes compaction summaries. Empty means the current model. On slow local hardware a summary is the slowest thing hexiel does — thousands of words written at a few words per second can take many minutes — so pointing this at a fast cloud model (for example "anthropic-sonnet") turns that wait into seconds. If that model is unreachable or has no API key, hexiel says so and summarizes with the current model instead. Summaries are kept under about 900 words either way. Shown as Summary model.
request_timeout900Seconds hexiel waits on the model server — for the start of a reply, and between streamed chunks. A slow local model reading a huge prompt can need minutes; a hung server is given up on after this long.

Asking a stronger model for advice

KeyDefaultWhat it does
workers"auto"How many /delegate steps may run at the same time. "auto" = every CPU core except one (so the machine stays usable); or a number, 1 = one at a time. Only steps that are independent run together: a step waits for the steps it depends_on, and for any earlier or running step that edits the same files; a step that doesn't name its files runs alone, in order. If one step fails, no new ones start. Approval questions are asked one at a time. Shown as Parallel steps.
worker_fallback_profile""A model used for a /delegate step while the worker model is busy. Before each step hexiel sends the worker a one-word request; if it isn't answered within 20 seconds (a local model serving another session, or one that is hung), the step goes to this model instead, and back to the worker once it answers again. Empty = always wait for the worker. This is also where extra parallel steps go when a single-request local server is already busy. Shown as Worker when busy.
escalate_mode"auto"When the working model keeps struggling, the planner model takes over as the working model for a short burst (8 steps), then the work goes back to the cheap model. "Struggling" is a score: failed tool calls, results with nothing in them, the same call repeated, a very long run of tool calls, and — the strongest signal — you correcting it ("no, it IS on the macmini", "stop arguing"). A moderate score gets advice first (see consult_mode); a high one hands the work over. At most two bursts per request, and the status line shows claude-… · escalated while it lasts. /cloud <request> hands one whole request to the cloud model on demand. "off" disables the automatic switch (/cloud still works). Needs a different, stronger model as planner_profile (or fallback_profile). Shown as Switch to cloud.
consult_mode"auto"Lets the working model get advice from the planner model (planner_profile, else fallback_profile). "auto": the model can ask for advice with its consult tool, and hexiel asks on its behalf when it is visibly stuck — three tool calls in a row that failed, or the very same call three times. "tool": only when the model asks. "off". At most three consultations per request; nothing happens unless a different, stronger model is configured. Shown as Ask for advice.

Rate limits and fallbacks

KeyDefaultWhat it does
auto_resumetrueWhen a provider says you've hit a rate or usage limit, wait until it resets and carry on by itself.
auto_resume_max_wait21600The longest wait (seconds, so six hours) before hexiel gives up waiting or switches to the fallback model.
fallback_profile""A profile to switch to when a limit won't reset soon. Today the per-model fallbacks chains are the better mechanism; this global setting is what profiles use when they declare no chain of their own. Empty means don't switch.
subagent_profile""The profile used by task subagents (read-only research helpers). Pick a cheaper or local model to save tokens; empty means the current model.

Delegation — a strong model plans, another does the work

See how delegation works. All five can also be changed in /config.

KeyDefaultWhat it does
planner_profile""The model that writes the plan for a big task. Empty means fallback_profile, and if that is empty too, the current model. Shown in the app as Planner model.
executor_profile""The model that gathers context and carries out each step. Empty means the current model. Shown as Worker model.
auto_delegate"off""off": never size up requests (you can still run /delegate). "ask": offer to delegate requests that look large. "auto": delegate them without asking. Anything else is treated as "off". Shown as Big-task check.
delegate_approvaltrueShow the plan and wait for a yes before any step runs. Shown as Approve plans.
delegate_review"auto"After every step has passed, the planner model reviews the combined diff and the results of the checks, looking for real defects (missed requirements, bugs, broken edge cases). Whatever it finds becomes extra fix steps for the worker, one round. "auto": review only when the planner is a different model from the worker (a second opinion); "always"; "off". Shown as Review plans.
delegate_retries2Fix attempts for a step whose check fails, before that step is handed to the planner model. Shown as Step retries.

Helper models for big tool outputs

KeyDefaultWhat it does
digest_min_chars3000Tool output shorter than this is never condensed by a helper model (minimum 500). Set per tool with min_chars in [tool_models].
digest_timeout90Seconds a helper model gets to condense one output; after that hexiel uses the raw output and says so (minimum 5). Set per tool with timeout.

Web search

KeyDefaultWhat it does
search_backend"duckduckgo""duckduckgo" needs no key. "searxng" uses your own SearXNG server.
searxng_url""Your SearXNG address, for example http://localhost:8888. Used when search_backend = "searxng".

[tool_models] — a small model for big tool outputs 0.4.1

Map a tool to a model profile and hexiel condenses that tool’s big outputs with it, for your current request, before the main model sees them. The main model can call the tool again with full: true for the raw text. Tools that can be mapped: read, grep, glob, list, map, web_fetch and web_search (alias search). write, edit and bash are never delegated. Guide with measurements: Helper models.

[tool_models]
read      = "small-local"                                   # a profile name
grep      = ["pi", "minipc"]                                # a list: the first one that isn't busy wins
web_fetch = { models = ["pi"], min_chars = 3000, max_input = 6500, timeout = 100, max_tokens = 220 }
search    = "minipc"                                        # alias for web_search
Table keyDefaultWhat it does
models (or model)—One profile name or a list, tried in order (one that isn't busy is preferred). Unknown profiles, ones whose API key is missing, and the model you are already using are skipped; if a model fails or times out the next one is tried, and a model that just failed is skipped for two minutes. After the last one, the raw output is used.
min_charsdigest_min_charsOutputs at or below this size pass through untouched.
max_input60000Outputs above this size are not sent to the helper; you get the raw output. Use a low value for slow helpers.
timeoutdigest_timeoutSeconds to wait before falling back to the raw output.
max_tokens500How long the digest may be. Writing is the slow part on a small helper, so a shorter digest is a faster one.

Check it with /toolmodels (state, busy, limits) and /toolmodels test (times each model); /usage shows what was kept out of context. hexiel also warns when another window or background run is already using the same local model.

[models.NAME]

One table per model you want to use; NAME is whatever you like and is what you give to default, /model, hexiel -m and the profile settings above. provider and model are required.

KeyDefaultWhat it does
providerrequired"openai" for anything that speaks the OpenAI-compatible API — Ollama, LM Studio, vLLM, OpenAI, Gemini, OpenRouter, DeepSeek and many more. "anthropic" for Claude's own API.
modelrequiredThe model's id as the server knows it, for example qwen3.6:35b-a3b-coding or claude-sonnet-5-5.
base_url""The server address. Required for provider = "openai" (for example http://localhost:11434/v1). For Anthropic it defaults to the official API.
api_key_env""The name of the environment variable holding the API key — not the key itself, so the file is safe to share. Leave empty for local servers.
context_window200000The model's context size in tokens. hexiel compacts relative to this, so for a local server set it to the server's real limit (for Ollama, its OLLAMA_CONTEXT_LENGTH).
max_output_tokens16384The longest single reply hexiel asks for.
extra_body{}Extra fields merged into every request, as an inline table. The usual use is the thinking level: extra_body = { reasoning_effort = "none" } makes slow local hardware answer in seconds instead of minutes.
echo_reasoningfalseSend the model's earlier reasoning back with the conversation. Some providers (DeepSeek, Together, Kimi) require it.
max_tokens_param""Which request field carries the output limit. Empty picks max_completion_tokens for OpenAI and Azure and max_tokens for everyone else. Set it only if a server rejects the default.
fallbacks[]Model profile names to try, in order, when this one is unreachable (server off), over its usage limit, or busy with another request — any string, or a list. A typical local model points at a cloud model first and a second cloud model next: fallbacks = ["sonnet", "glm-flash"]. A profile whose usage limit just ran out is skipped for 30 minutes (or until the provider's own reset time) so it isn't retried with every request, and when a session was pushed to a fallback, hexiel switches back to the primary automatically once its cooldown has run out. Profiles that declare nothing keep the old behaviour: fallback_profile is used on a long-term limit only.

Unknown keys are rejected with a message listing the allowed ones. Ready-made profiles for the major providers: run hexiel --presets, or /model add NAME inside the app.

Examples

# a local model on this machine
[models.local]
provider = "openai"
base_url = "http://localhost:11434/v1"
model = "qwen3.6:35b-a3b-coding"
context_window = 65536                 # match the server's context length
max_output_tokens = 8192
extra_body = { reasoning_effort = "none" }

# a model on another machine on your network
[models.minipc]
provider = "openai"
base_url = "http://192.168.3.10:11434/v1"
model = "qwen3.6:35b-a3b-coding"
context_window = 65536

# an Ollama cloud model, through your local Ollama
[models.kimi]
provider = "openai"
base_url = "http://localhost:11434/v1"
model = "kimi-k2.7-code:cloud"
context_window = 262144

# Claude
[models.claude]
provider = "anthropic"
model = "claude-sonnet-5-5"
api_key_env = "ANTHROPIC_API_KEY"

# many models behind one key
[models.openrouter]
provider = "openai"
base_url = "https://openrouter.ai/api/v1"
model = "anthropic/claude-opus-4.5"
api_key_env = "OPENROUTER_API_KEY"

[permissions]

Rules that decide, per tool call, whether hexiel runs it, asks you, or refuses. They use Claude Code's rule syntax, so existing rules work unchanged.

[permissions]
allow = ["Bash(git status:*)", "Bash(npm run *)", "Read(src/**)"]
ask   = ["Bash(git push:*)"]
deny  = ["Read(./.env)", "Edit(secrets/**)", "WebFetch(domain:example.com)"]

[mcp] — extra tools from MCP servers

MCP servers add tools — databases, browsers, issue trackers. Add a well-known one in one step with /mcp add NAME (filesystem, playwright, firebase, supabase, github, notion), or describe your own:

[mcp.servers.files]                    # a program hexiel starts and talks to
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "."]

[mcp.servers.github]                   # a remote server
url = "https://api.githubcopilot.com/mcp/"
headers = { Authorization = "Bearer ${GITHUB_TOKEN}" }
tools = ["search_issues"]              # optional: expose only these tools
# enabled = false                      # keep the entry but don't start it
KeyWhat it does
command, args, envStart a local server: the program, its arguments, and extra environment variables for it.
url, headersConnect to a remote (streamable HTTP) server instead. A server with a url is treated as remote.
toolsOnly expose these tools from the server. Omit it to expose them all.
enabledfalse turns an entry off without deleting it.

Text values can use ${VAR} or ${VAR:-default} to read environment variables, so secrets stay out of the file. Servers are also read from ~/.config/hexiel/mcp.json and the project's .mcp.json (Claude Code's format); on a name clash, config.toml wins. Each tool appears as mcp__SERVER__TOOL, and tools a server marks read-only skip the approval prompt.

Other files hexiel reads

File or folderWhat it's for
~/.config/hexiel/settings.json
.hexiel/settings.json
Hooks and permission rules, in Claude Code's format. Hooks run your own commands on events (PreToolUse, PostToolUse, UserPromptSubmit, Stop, SessionStart). The project's .claude/settings.json and settings.local.json are read too.
~/.config/hexiel/mcp.json
.mcp.json
MCP servers (see above).
~/.config/hexiel/commands/
.hexiel/commands/
Your own slash commands: a markdown file becomes /name. Claude Code's .claude/commands folders work too.
~/.local/share/hexiel/skills/
.hexiel/skills/
Skills — packaged instructions hexiel can load for a task. Claude Code skills in ~/.claude/skills and .claude/skills work unchanged.
~/.config/hexiel/auth.tomlYour GitLab token, if you use /setup-gitlab (kept private to your user).
~/.local/share/hexiel/What hexiel remembers across projects: sessions/ (saved conversations), memory/, jobs.json and inbox/ for scheduled jobs. Move it with HEXIEL_DATA.
.hexiel/ in a projecttodo.md (the task list that lets work resume), plans/ (delegated plans), memory/, and caches such as pastes/. You may want .hexiel/ in your .gitignore.

Environment variables

VariableWhat it does
HEXIEL_HOMEWhere config.toml and the other config files live. Default ~/.config/hexiel.
HEXIEL_DATAWhere sessions, memory and skills live. Default ~/.local/share/hexiel.
the name in api_key_envHolds a model's API key (for example ANTHROPIC_API_KEY, OPENAI_API_KEY). Anthropic profiles also fall back to ANTHROPIC_API_KEY.
HEXIEL_CONTAINERForce the container tool to use docker or podman instead of detecting one.
HEXIEL_NO_UPDATE_CHECKSet to anything to stop the check for a newer hexiel release at startup.
HEXIEL_GITLAB_TOKEN / GITLAB_TOKENA GitLab token for /ship, instead of saving one with /setup-gitlab.
HEXIEL_RELAYAdvanced: the Hexiel Remote relay address, for Hexiel Remote subscribers.

Command-line options

OptionWhat it does
hexiel [path]Start in a project folder (default: the current one).
-m, --model NAMEStart with this model profile instead of default.
-p, --print "PROMPT"Run one prompt without the interface, print the answer, and exit. Add -y to auto-approve tool calls in scripts.
-c, --continueResume the most recent conversation for this project.
--serve [--host, --port]Run the browser UI instead of the terminal one (default 127.0.0.1:8777).
--replA plain line-by-line prompt, for terminals that can't run the full-screen UI.
--presetsList ready-made model profiles for the major providers.
--doctor / --setupPreflight checks with copy-paste fixes / the first-run guided setup.

Two complete examples

Both are ready to paste — rename the models you'd rather use; every chain is declared in the file, so hexiel runs with whatever you have, whether that's Ollama, Anthropic, OpenAI, Gemini or anything else that speaks OpenAI's API. Each model needs its [models.NAME] table too (see above) — these examples assume the names they use are defined.

The basic setup

Use it when: you run hexiel on one machine, want almost nothing to configure, and care that a request always completes. One workhorse does the work and a cloud model covers it when the machine (or its model server) is off, busy or over its limit — including the reverse: when the cloud model is the one that hits its quota, hexiel slides back to the local model after a cooldown instead of hammering the API. No router, no reading helper, no delegation tiers — a config a dozen lines long that you never think about.

default = "local"

[settings]
request_timeout = 900
auto_delegate = "off"

[models.local]
provider = "openai"
base_url = "http://localhost:11434/v1"
model = "qwen3.6:35b-a3b-coding"     # or whatever your machine runs well
context_window = 65536
max_output_tokens = 8192
extra_body = { reasoning_effort = "none" }
fallbacks = ["sonnet"]               # the cloud model carries everything while the local one is out

[models.sonnet]
provider = "anthropic"
model = "claude-sonnet-5-5"
api_key_env = "ANTHROPIC_API_KEY"
context_window = 200000
max_output_tokens = 16384
fallbacks = ["local"]                # and back, if the cloud side is the one that fails

Flipped version: make a cloud model the default and give it fallbacks = ["local"] — same resilience, cloud-first speed, local as the free overflow. Purely a question of taste and of what runs fastest on your machine: hexiel picks the chain your config declares, for Anthropic, Ollama, Ollama Cloud, OpenAI-compatible endpoints and beyond.

The advanced setup

Use it when: you have several machines and a paid API key, and you want costs near zero on the routine work without giving up speed or resilience. Each part of a request gets its own small model reading big tool outputs on a Raspberry Pi, a strong local box doing the heavy work, cloud heavyweights planning and writing summaries — and every one of those models carries a fallbacks chain (Anthropic first, an Ollama Cloud model second), so an unreachable box, a busy server or a spent quota never takes a turn down with it: the chain picks up, the limited model cools off for 30 minutes, and the session returns to its primary as soon as it is available again.

default = "minipc"

[settings]
compact_threshold = 0.6          # compact earlier: the local model is sharper with a shorter prompt
auto_delegate = "ask"            # offer to delegate big requests
planner_profile = "anthropic-sonnet"     # /delegate: the strong model writes the plan...
executor_profile = "minipc"      # ...and the local model does each step
workers = "auto"                 # independent steps run at once: every core but one
request_timeout = 900
auto_delegate = "ask"            # offer to delegate big requests
delegate_review = "auto"         # the planner reviews the finished diff and has the worker fix what it finds
worker_fallback_profile = "glm-flash"   # a /delegate step goes here while minipc is busy
escalate_mode = "auto"           # the planner takes over for a few steps when the worker keeps struggling
compact_profile = "anthropic-sonnet"     # compaction summaries in seconds, not minutes
toolsets = ["data"]
# model routing (see the /delegate docs): a fast cloud model sorts every message
# router_profile = "anthropic-haiku"     # chat answered by haiku itself; work and big requests pass through
# overflow_profile = "anthropic-haiku"   # and chat-sized turns run on haiku while minipc is busy, up to a daily cap
# overflow_daily_tokens = 200000

# reading helper: the pi digests big tool outputs for free; when it is busy or slow, haiku, then glm-flash
[tool_models]
read       = { models = ["pi"], min_chars = 3000, max_input = 6500, timeout = 100, max_tokens = 220 }
grep       = { models = ["pi"], min_chars = 3000, max_input = 6500, timeout = 100, max_tokens = 220 }
web_fetch  = { models = ["pi"], min_chars = 3000, max_input = 6500, timeout = 100, max_tokens = 220 }
web_search = { models = ["pi"], min_chars = 3000, max_input = 6500, timeout = 100, max_tokens = 220 }

[models.pi]
provider = "openai"
base_url = "http://192.168.1.50:11434/v1"   # a Raspberry Pi 5 running Ollama on your network
model = "qwen2.5:1.5b"
context_window = 4096
max_output_tokens = 600
fallbacks = ["anthropic-haiku", "glm-flash"]

[models.minipc]
provider = "openai"
base_url = "http://192.168.3.10:11434/v1"
model = "qwen3.6:35b-a3b-coding"
context_window = 65536
max_output_tokens = 8192
extra_body = { reasoning_effort = "none" }
fallbacks = ["anthropic-sonnet", "glm-flash"]    # box off / limited / busy: sonnet, then glm-flash

[models.anthropic-sonnet]
provider = "anthropic"
model = "claude-sonnet-5-5"
api_key_env = "ANTHROPIC_API_KEY"
context_window = 200000
max_output_tokens = 16384
fallbacks = ["glm-flash", "kimi"]                # anthropic limited or down: ollama cloud, then another cloud model

[models.anthropic-haiku]
provider = "anthropic"
model = "claude-haiku-5-5"
api_key_env = "ANTHROPIC_API_KEY"
context_window = 200000
max_output_tokens = 16384

[models.glm-flash]
provider = "openai"
base_url = "http://localhost:11434/v1"
model = "glm-5.3-flash:cloud"               # Ollama Cloud, through your own Ollama
context_window = 200000
max_output_tokens = 16384
fallbacks = ["kimi", "anthropic-sonnet"]

[models.kimi]
provider = "openai"
base_url = "http://localhost:11434/v1"
model = "kimi-k2.7-code:cloud"
context_window = 262144
max_output_tokens = 16384
fallbacks = ["anthropic-sonnet"]

[permissions]
deny = ["Read(./.env)", "Bash(rm -rf *)"]
allow = ["Bash(git status:*)", "Bash(git diff:*)"]

When something's wrong

bad config … [models.x] has unknown setting(s)

A key in that table isn't one hexiel knows — usually a typo. The message lists the allowed keys: api_key_env, base_url, context_window, echo_reasoning, extra_body, fallbacks, max_output_tokens, max_tokens_param, model, provider.

bad config … [models.x] needs provider (or model)

Both provider and model are required in every model table.

bad config … default = 'x' is not one of your [models.*] profiles

The top-level default must match a table name exactly, as in default = "minipc" for [models.minipc].

a [settings] value has the wrong type or format

A number was written as text (compact_threshold = "high") or the reverse. Numbers and true/false are written without quotes; names and paths are in quotes.

A setting in [settings] does nothing

Unknown keys there are ignored silently — compare the spelling with the tables above. Also check that the line is under [settings] and not under a [models.*] table that comes before it, and restart hexiel after editing by hand.

The model "forgets" things or gets confused in long sessions

Match context_window to the server's real limit, and lower compact_threshold (try 0.6) so hexiel summarizes before the prompt gets long. For big tasks, delegate them so each step starts with a short, clean context.

Requests time out or hang on a local model

A long prompt can take minutes for a small machine to read. Raise request_timeout if that's normal for your hardware; if the server is genuinely stuck, Ctrl+C stops the turn and hexiel will say the model server isn't responding rather than blaming a rate limit.

Still stuck? Run hexiel --doctor, or open an issue.

Make it yours.