configuration

All configuration is TOML. ACE merges multiple layers into one effective config, with later layers overriding earlier ones.

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
Local overrides project, project overrides user-global, user-global overrides school defaults. For env, maps merge additively — later keys override earlier keys. For all other fields, last non-empty value wins.

ace.toml / ace.local.toml

Project and local config share the same schema. Local config is gitignored and holds machine-specific values like trust level.

# ace.toml example
school = "ace-rs/school"
backend = "claude"
trust = "auto"
session_prompt = "Always run tests before committing."

[env]
RUSTFLAGS = "-D warnings"
KeyType / ValuesDefaultDescription
schoolstring(empty)School specifier, e.g. "ace-rs/school". Last non-empty wins across layers.
backendclaude codex opencodeclaudeWhich AI backend to launch. Highest-priority Some wins (local → project → user).
trustdefault auto yolodefaultBackend permission mode. auto = auto-accept safe operations. yolo = bypass all permission prompts.
session_promptstring(empty)Additional prompt text prepended to the backend session. Last non-empty wins.
envmap (string → string){}Environment variables passed to the backend. Merges additively across layers.
resumeboolNoneAuto-resume the previous backend session on launch. Personal-only — set in ace.local.toml or user-global config, not checked in.
skip_updateboolfalseDisable automatic version check and background upgrade. Also overridden by ACE_SKIP_UPDATE=1.
yoloboolfalseDeprecated. Use trust = "yolo" instead. Kept for backwards compatibility; yolo = true resolves to trust = "yolo".
exclude_mcplist 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.
skillslist of glob patterns[]Whitelist of skills to load. Last non-empty wins across layers; empty everywhere = all discovered skills. See skills selection.
include_skillslist of glob patterns[]Always-add patterns. Union across all scopes — exception to last-wins. See skills selection.
exclude_skillslist of glob patterns[]Always-remove patterns. Union across all scopes — exception to last-wins. See skills selection.

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.

FieldMerge ruleEffect
skillslast-wins replace (local > project > user, first non-empty)Sets the base set. Empty everywhere → base = all discovered skills.
include_skillsunion across all scopesAlways added on top of the base.
exclude_skillsunion across all scopesAlways removed from the base.

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 — this repo only needs rust-coding
skills = ["rust-coding"]

# ace.local.toml — opt out of a global include just for this repo
exclude_skills = ["issue-*"]

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.

# ~/.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, 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

KeyType / ValuesDefaultDescription
namestring(empty)School display name. Must be non-empty (validated after load).
backendclaude codex opencodeNoneDefault backend for the school. Overridden by ace.toml layers.
session_promptstring(empty)Prompt text prepended to every session using this school.
envmap (string → string){}Environment variables. Merged with ace.toml env (ace.toml wins on conflict).

[[mcp]]

MCP (Model Context Protocol) server declarations. Each entry registers an MCP server for the backend to use.

KeyTypeDefaultDescription
namestring(empty)Server identifier. Must be unique across all MCP entries.
urlstring(empty)Server URL (SSE endpoint).
headersmap (string → string){}HTTP headers sent with requests. Values support {{ placeholder }} syntax for user-prompted secrets.
instructionsstring(empty)Usage instructions passed to the backend for this server.

[[projects]]

Optional project catalog entries that give the AI context about available repositories.

KeyTypeDefaultDescription
namestring(empty)Short project identifier.
repostring(empty)Git-cloneable repository URL or shorthand.
descriptionstring(empty)Project description written for AI consumption.
envmap (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.

KeyTypeDefaultDescription
skillstring(empty)Skill name or glob pattern (e.g. "*-coding", "*").
sourcestring(empty)School specifier to import from, e.g. "ace-rs/school".
include_experimentalboolfalseFor glob entries: also match skills under skills/.experimental/.
include_systemboolfalseFor glob entries: also match skills under skills/.system/.

placeholder syntax

Several string fields support {{ placeholder }} substitution. Two placeholder families share the same syntax but resolve at different times.

WhereResolves toWhen
[[mcp]] headersUser-supplied secret, prompted on first run and cachedruntime
[[backends]] cmd / envPath placeholders — school_dir, project_dir, backend_dir, homebind time
# MCP header — prompts user for "github_token" on first run
Authorization = "Bearer {{ github_token }}"

# Backend cmd — resolves school_dir at bind time
[[backends]]
name = "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
Path placeholders are a closed set — no shell-style $VAR or ~ expansion. Unknown names render to empty strings. See Backends › templated cmd and env for the full list.