configuration
All configuration is TOML. ACE merges multiple layers into one effective config. Scalar, list, and map fields do not all use the same merge rule.
layer merge order
# 1. School defaults (read-only, from school.toml) # 2. User-global config ~/.config/ace/ace.toml # 3. Project config (checked into git, shared) ace.toml # 4. Project-local config (gitignored, per-machine) ace.local.toml
ace.toml / ace.local.toml
Project and local config use the same file format. Local config is gitignored and holds personal settings such as trust and resume; those settings belong in local or user config, and project ace.toml ignores them. Unknown or misplaced fields are reported and ignored.
# ace.toml example school = "ace-rs/school" backend = "claude" session_prompt = "Always run tests before committing." [env] RUSTFLAGS = "-D warnings"
| Key | Type / Values | Default | Description |
|---|---|---|---|
| school | string | (empty) | School specifier: "owner/repo", "owner/repo:subdir" for a school in a subdirectory, or "." for a school embedded in the project. The highest-priority configured value wins across layers. |
| backend | registered backend name | claude | Which AI backend to launch. Built-in names are claude, codex, opencode, and pi; names declared under [backends.<name>] are also accepted. The highest-priority configured value wins (local → project → user). |
| trust | default auto yolo | default | Requested backend permission mode. Personal-only: honored in user and local config, ignored in project ace.toml. An explicit trust = "default" overrides an inherited auto or yolo; omitting the key inherits. Support and exact behavior vary by backend; see trust modes. |
| session_prompt | string | unset | Additional text appended to the session prompt ACE injects. The highest-priority present value wins; an explicitly empty string clears inherited text. |
| env | map (string → string) | {} | Environment variables passed to the backend process. Merges additively across layers. |
| resume | bool | true | Auto-resume the previous backend session on launch. Unset configuration resolves to true. Personal-only: honored in ace.local.toml or user-global config, ignored in project ace.toml. |
| skip_update | bool | false | Disable automatic version check and background upgrade. Also overridden by ACE_SKIP_UPDATE=1. |
| [backends.<name>] | table | unset | Declare a custom backend or override a built-in one: kind, cmd, env, model, effort. See backend tables. |
| yolo | bool | false | Deprecated. Use trust = "yolo" instead. yolo = true still resolves to trust = "yolo"; writing trust to a layer removes a legacy yolo key from that layer. |
| exclude_mcp | list of strings | [] | MCP server names to skip from the school's [[mcp]] entries. Union across all scopes. Declining a server at the registration prompt appends its name here in ace.local.toml; ace mcp register <name> removes it. |
| skills | list of glob patterns | [] | Whitelist of skills to load. Last non-empty wins across layers; empty everywhere = all discovered skills. See skills selection. |
| include_skills | list of glob patterns | [] | Always-add patterns. Union across all scopes — exception to last-wins. See skills selection. |
| exclude_skills | list of glob patterns | [] | Remove patterns from the base set before include_skills is applied. Union across all scopes — exception to last-wins. See skills selection. |
backend tables
Each [backends.<name>] table declares one backend instance; the table key is the name that backend = "..." and --backend select. Any layer can declare one, including school.toml. The old [[backends]] array with a name field is rejected since 0.9.3: move each name into its table key.
# Override the model of the built-in claude backend [backends.claude] model = "opus" # Custom backend, selected with backend = "work-claude" [backends.work-claude] kind = "claude" effort = "high" [backends.work-claude.env] ANTHROPIC_BASE_URL = "https://proxy.example.com"
| Key | Type / Values | Default | Description |
|---|---|---|---|
| kind | claude codex opencode pi | inferred | Built-in backend this entry behaves as. A table keyed by a built-in name overrides that built-in. A custom name needs kind unless its cmd binary matches a built-in. |
| cmd | list of strings | built-in binary | Executable plus any wrapper prefix used to launch the backend. Supports path placeholders. |
| env | map (string → string) | {} | Environment variables for this backend. Supports path placeholders. |
| model | string | unset | Model passed to the backend's native model setting for sessions and ace -p. Opaque: passed through unchanged. Unset leaves the backend's own default. |
| effort | string | unset | Reasoning effort passed to the backend's native effort setting. Opaque and not validated by ACE; accepted values are whatever the backend accepts. Unset leaves the backend's own default. |
Custom backends inherit their kind's mapping of model and effort to native flags. Arguments passed after -- come last, so they can override either value for one run. See custom backends.
inspecting and editing
ace config # effective config as one TOML document ace config get <key> # resolved value of one key ace config set <key> <value> # write a key to a layer ace config explain [key] # provenance per layer
Keys: school, backend, trust, resume, skip_update, session_prompt, env.<NAME>, backends.<name>.model, and backends.<name>.effort. skills, include_skills, exclude_skills, and exclude_mcp are read-only here: get and explain show them, set rejects them. Pass --user, --project, or --local to pick the layer.
Bare ace config includes backend model/effort and the selection lists; school metadata is not included, so read it from the school's school.toml. ace config set validates before writing and keeps comments, unrelated fields, inline tables, and dotted keys, including fields ACE doesn't recognize. It says when a higher-precedence layer overrides the value you wrote.
skills selection
Three fields control which of the school's skills are loaded by the backend at session start. Each has one consistent rule across all scopes; the field name signals intent, the scope is mechanical.
| Field | Merge rule | Effect |
|---|---|---|
| skills | last-wins replace (local > project > user, first non-empty) | Sets the base set. Empty everywhere → base = all discovered skills. |
| include_skills | union across all scopes | Always added on top of the base. |
| exclude_skills | union across all scopes | Removed from the base before include_skills is added. |
Resolution: effective = (skills_base − exclude_skills) ∪ include_skills. Exclude is applied before include, so include_skills is authoritative when an item appears in both.
# user ace.toml — global "always-add" preferences include_skills = ["issue-*"] # project ace.toml — remove issue-* from the base selection skills = ["rust-coding", "issue-*"] exclude_skills = ["issue-*"] # Result: include_skills wins, so issue-* is still loaded.
Glob patterns use * as a wildcard (no ** or character classes). An invalid pattern is warned and skipped rather than failing the run. Foreign entries in <backend>/skills/ (real files or symlinks pointing outside the school clone) are left alone unless their name collides with a desired skill, in which case the link is skipped with a warning.
CLI
ace skills [--all] [--names] # list resolved skills ace skills include <pattern>... # append to include_skills ace skills exclude <pattern>... # append to exclude_skills ace skills reset [--include] [--exclude] # set list(s) back to empty ace explain <name> # provenance + trace for one skill
Mutating verbs default to project scope; pass --user or --local to target another layer. There is no ace skills set verb — the skills whitelist is config-only; edit ace.toml directly. The CLI exposes the union-merge fields because those are what users typically tweak per session.
user-global ace.toml
Same schema as project ace.toml. Holds your personal defaults across every ACE-managed repo — typically trust, resume, or a fallback school and backend. Project config overrides it; local config overrides project.
A user-level ace.toml resolves a project on its own: name a school here and a repo needs no ace.toml of its own. When the school comes from the user or local layer rather than the repo, bare ace announces which school it resolved, so a school that isn't the repo's own is never silent.
# ~/.config/ace/ace.toml school = "ace-rs/school" trust = "auto" resume = true
school.toml
Lives at the root of a school repository. Defines the school name, backend preference, backend tables, MCP servers, projects, and skill imports.
# school.toml example name = "acme-school" backend = "claude" session_prompt = "Follow ACME conventions." [env] EDITOR = "nvim" [[mcp]] name = "github" url = "https://mcp.example.com/github" instructions = "Use for all GitHub operations." [mcp.headers] Authorization = "Bearer {{ github_token }}" [[projects]] name = "ace" repo = "ace-rs/ace" description = "ACE CLI and backend integration code." [[imports]] skill = "docker" source = "ace-rs/school" [[imports]] skill = "*" source = "company/school" include_experimental = true
top-level keys
| Key | Type / Values | Default | Description |
|---|---|---|---|
| name | string | (empty) | School display name. Must be non-empty (validated after load). |
| backend | registered backend name | unset | Default backend for projects using this school. Built-in and custom names are accepted; ace.toml layers can override it. See custom backends. |
| session_prompt | string | (empty) | Prompt text injected into every session using this school. |
| env | map (string → string) | {} | Shared environment variables, not secrets. Merged with ace.toml env (ace.toml wins on conflict). |
| [backends.<name>] | table | unset | Same shape as in ace.toml; see backend tables. |
[[mcp]]
MCP (Model Context Protocol) server declarations. Each entry registers an MCP server for the backend to use.
| Key | Type | Default | Description |
|---|---|---|---|
| name | string | (empty) | Server identifier. Must be unique across all MCP entries. |
| url | string | (empty) | Remote MCP server URL. |
| headers | map (string → string) | {} | HTTP headers sent with requests. Values support {{ placeholder }} syntax for user-prompted secrets. |
| instructions | string | (empty) | Usage instructions passed to the backend for this server. |
[[projects]]
Optional project catalog entries that give the AI context about available repositories.
| Key | Type | Default | Description |
|---|---|---|---|
| name | string | (empty) | Short project identifier. |
| repo | string | (empty) | Git-cloneable repository URL or shorthand. |
| description | string | (empty) | Project description written for AI consumption. |
| env | map (string → string) | {} | Per-project environment variables exposed to the backend when this project is active. |
[[imports]]
Import skills from other school repositories. Skill discovery scans four tiers under skills/: the default skills/<name> and skills/.curated/<name> (both Curated), plus skills/.experimental/ and skills/.system/. Explicit names resolve from any tier. Glob patterns match only Curated by default, and skip skills marked internal: true unless include_internal is set. ace import writes these entries; ace school pull refreshes them.
| Key | Type | Default | Description |
|---|---|---|---|
| skills | names or glob patterns | (empty) | Skills to import, by name or glob (e.g. "*-coding", "*"). Written by ace import, which merges new picks into an existing entry. |
| skill | string | (empty) | Single skill name or glob pattern. An entry needs skills or skill. |
| exclude_skills | names or glob patterns | (empty) | Skills to leave out of this import. |
| source | string | (empty) | Where to import from — any git URL (SSH and self-hosted remotes included) or an owner/repo shorthand, e.g. "ace-rs/school". Sources cache under imports/{host}/{path}, so same-named repos on different hosts don't collide. |
| include_experimental | bool | false | For glob entries: also match skills under skills/.experimental/. |
| include_system | bool | false | For glob entries: also match skills under skills/.system/. |
| include_internal | bool | false | For glob entries: also match skills whose SKILL.md frontmatter sets internal: true. An explicit name imports them regardless. |
placeholder syntax
Several string fields support {{ placeholder }} substitution. Two placeholder families share the same syntax but resolve at different times.
| Where | Resolves to | When |
|---|---|---|
| [[mcp]] headers | User-supplied secret, prompted on first run and cached | runtime |
| [backends.<name>] cmd / env | Path placeholders — school_dir, project_dir, backend_dir, home | when ACE resolves the backend |
# MCP header — prompts user for "github_token" on first run Authorization = "Bearer {{ github_token }}" # Backend cmd — resolves school_dir when ACE resolves the backend [backends.codex-ace] kind = "codex" cmd = ["{{ school_dir }}/skills/ace-connect/scripts/codex.sh"] # Whitespace is flexible "{{name}}" "{{ name }}" "{{ name }}" # all equivalent # Names must be alphanumeric + underscore "{{ my_api_key_2 }}" # valid