Overlays
The configuration is a base overlay plus opt-in overlays. The base is what
init lays down — the AGENTS.md managed block, with no opinions. Everything
opinionated ships as a named overlay you add explicitly.
The overlay model
An overlay is a directory (optionally carrying an overlay.toml manifest) that
installs as a unit into <store>/overlays/<name>/ — ~/.agents/overlays/<name>/ for
the user store, <project>/.agents/overlays/<name>/ for a project. Its files keep
their relative paths inside that directory, and content refers to them through the
overlay's root variable ($<NAME>_OVERLAY_ROOT/kb/X.md, see
below). dotagents overlays resolves
each overlay by name against the repos you name (--repo, $AGENTS_OVERLAYS_REPO,
a dotagents.{json,toml,yaml} in a store — see "Where overlays come from" below), and
installs it into the scope's overlays directory. Installed overlays are then
discovered by their presence there.
overlays remove deletes <store>/overlays/<name>/ outright, so never author an
overlay inside that directory: keep your source elsewhere and install it from there.
add and sync refuse a source that lies inside <store>/overlays/.
Overlays are additive. add / sync never clobber a file you hand-edited inside
an installed overlay — a file that differs from the source is kept and reported.
Overlay names
A name starts with an ASCII letter, continues with letters, digits, _, . or -,
and ends in a letter or digit. It is case-insensitive, and _ and . count as -:
My_Overlay, my.overlay and my-overlay are one overlay, installed as
overlays/my-overlay/. The scope level names (user, project, system,
project-root, default, overlay) and Windows device names (con, nul, com1,
… with or without an extension) are refused.
Example overlays
The example overlays below live on this repo's separate
repo branch, not in main —
they are payloads riding on dotagents, not part of the tool, so you swap them for
your own. Name the branch as a repo to install any of them:
dotagents overlays add <name> --repo "https://github.com/jose-pr/dotagents.git@repo#overlays",
or --repo <checkout>/overlays for a local checkout of the branch.
| Overlay | What it carries |
|---|---|
engineering |
One opinionated engineering process. rules/ENGINEERING.md — always-on discipline merged into AGENTS.md (commit shape, release-tag consent, benchmark-as-evidence, token discipline, draft follow-ups). flows/ — PLAN.md (a strong model writes precise, autonomous plans), EXEC.md (a cheaper model executes them without re-deriving context), REVIEW.md (file-threaded multi-agent plan review), REPO.md (the repo standard); kb/MODELS.md (executor/model selection). references/ — language-neutral repo-file templates (README, CHANGELOG, LICENSE, .gitignore, docs-index, the plan template). cmds/ — plans, leak-check, eol, summarize-run (command → verdict) and compare-bench (diff benchmark JSONs), each a dotagents command. The language overlays require it. |
python, node, rust |
Per-language kb/ conventions + manifest templates + CI workflow templates. |
release |
A host-agnostic release helper: an agent-driven commit-plan loop plus tag/CI monitoring across GitHub (gh) and GitLab. |
private-sync |
The one-private-repo, per-project .agents model (kb/PRIVATE_SYNC.md + cloud hooks) — see Private sync. |
net |
HTTP tooling: a drop-in curl launcher (curlike), an OS-trust-store certifi shim, and syshttp (the system proxy and the OS verifier by default), both packages installed from the overlay's requirements.txt; settings are AGENTS_* (AGENTS_PROXY and kin). |
recovery |
A config-recovery playbook for reconstructing a lost ~/.agents. |
Managing overlays
dotagents overlays add python engineering # install into the scope, publish skills, merge rules/routing
dotagents overlays list # installed (discovered) + available (from the repos)
dotagents overlays sync 'py*' # refresh installed overlays matching a glob
dotagents overlays remove python # delete the overlay dir, unpublish its skills, un-merge its rules
dotagents overlays show python # describe one (manifest, requires, setup, skills, source)
Scope is project by default, or user with -g / --global (the configurable
store). Each overlay:
- installs as a directory (kept, discoverable), after what its manifest
requires; - records the repo it came from and the digest of every file it installed, in
<store>/overlays/<name>/.dotagents-install.json; - has its
routing/rulesmerged intoAGENTS.md's managed block, which is recomposed from the pristine base over every installed overlay in priority order; - has its skills published into the scope's shared skills dir, so every agent that reads that dir sees the same skills.
sync refreshes each overlay from the repo it was installed from (--repo replaces
that source for the run) and makes the install exactly the source: changed files are
replaced and files the source does not ship are removed, anything edited there backed
up under <store>/install_backup/ first. What the .gitignore / .ignore at the
overlay's root match (an ignore file in a subdirectory is just a file it ships), and
tool caches, belong to the install (a setup script's output, local
state) and are left alone; --prune clears them too. A requires added upstream is
installed. An installed overlay is upstream's copy, not a place for your edits.
Your additions to an overlay's files
Put your own text for an overlay file at the same path under the store root: an
addition to the rust overlay's kb/RUST.md goes in ~/.agents/kb/RUST.md (or
<project>/.agents/kb/RUST.md for one project). sync never touches it, and agents
read it alongside the overlay's file, winning on conflict: the base AGENTS.md tells
them to, and dotagents context lists every addition it finds (for each .md an
overlay ships in a subdirectory other than skills/). Private names, local tool paths
and links into your own notes belong there, never in the overlay.
Removing an overlay deletes only its directory and unpublishes only the skills it
published (a copy the user edited is left alone). Its lines in AGENTS.md's managed
block leave with it: the block is recomposed from the pristine base over the overlays
that remain. remove refuses an overlay another installed overlay requires, unless
--force; list and show flag a requirement no installed overlay provides.
Where overlays come from: repos
add, list and show resolve a name against a list of repos, and for add the
first repo that offers the name wins. (sync goes back to the repo each overlay was
installed from.) A repo is always a collection of overlays, in one of two shapes:
- a directory of overlays — each subdirectory is an overlay,
<dir>/<name>/(a checkout of therepobranch has them underoverlays/, any folder); - a registry — a JSON, TOML or YAML file mapping
<name-or-alias>to the source of that one overlay (the whole document, or itsoverlayskey). TOML usestomlibelow Python 3.11 (installed with the package, bundled in the.pyz); YAML needspip install 'dotagents-cli[yaml]'.
Both the repo and each source are written as a spec, <location>[@<ref>][#<path>]:
<location> is a local path, an http(s):// URL, or a git repository
(https://…/x.git, git@host:org/x.git, ssh://…, a local …/x.git; prefix git+ to
force git); <ref> is a branch, tag or commit; <path> is inside it. The two differ
in what the path names:
- for a repo, the collection: a directory (of overlays) or a registry file in the
checkout, the checkout root with no path —
--repo …/dotagents.git@repo#overlaysnames theoverlays/directory of therepobranch; - for a source (a registry value), the overlay: the path is the overlay's root
directory, and with no path the repository root is the overlay —
…/dotagents.git@repo#overlays/netis thenetoverlay itself. Given as--repo, that form would listnet's own subdirectories as overlays.
A scheme:// location that is not git is a uripath path (pathapi's URI layer, the
pathapi-uri distribution). With the uri extra (pip install 'dotagents-cli[uri]';
[sftp], [s3] and [repo] add sftp:, s3: and github: / gitlab: / repo:)
any scheme uripath speaks works like a local path: an http(s)://
directory listing, an sftp, s3, dav or github tree is a directory of overlays
as a repo or one overlay as a source, a file is a registry; an archive is a tree too —
zip:<archive-uri>!/<inner path> (tar:, or archive: to auto-detect), the archive
itself local (file:) or at any URL: zip:https://h/overlays.zip!/overlays. Remote content is
materialized into <user store>/.cache/overlays/uri/, synced once per run, and used
from there; file:// is a local path, no copy. Without the extra, an http(s)://
location can still be a registry file (fetched with the standard library), and
anything else says which extra it needs. The .pyz vendors none of this unless built
with build-pyz --extras uri.
A source written as a relative path (./python, ../shared/net, overlays/rust)
is relative to the registry it is in: the registry file's directory for a local file,
and for a registry inside a git checkout the same repository at the same ref, with
the path joined onto the registry file's directory (overlays/reg.toml saying ./rust
means <repo>@<ref>#overlays/rust), and for a registry at a URL the URL beside it
(https://h/cfg/reg.json saying ./rust means https://h/cfg/rust).
Git checkouts are cached under <user store>/.cache/overlays/, one per repository and
ref, and fetched again once per run. That directory carries a .gitignore of *, so
a store kept in git never records them. Credentials in a repo URL never reach a log
line, a cache directory name or a checkout's .git/config, and an install record
keeps the URL without them (so sync of such an overlay needs the repo configured
again, or --repo).
The repos, in order:
--repo <spec>(repeatable);$AGENTS_OVERLAYS_REPO_<KEY>=<spec>, sorted byKEY, then$AGENTS_OVERLAYS_REPO(the default repo);<project store>/dotagents.{json,toml,yaml,yml}, then the user store's.
No build bundles overlays; with none of these configured, add fails with an error
that names them.
# ~/.agents/dotagents.toml
[overlays]
engineering = "https://github.com/you/dotagents.git@repo#overlays/engineering"
python = "https://github.com/you/dotagents.git@repo#overlays/python"
mytool = "git@github.com:you/mytool-overlay.git@v2" # no path: the repo root is the overlay
local = "~/src/overlays/local" # a local overlay directory
mine = "./my-overlays/mine" # relative to this file: ~/.agents/my-overlays/mine
dotagents overlays add engineering -g # from the registry above
dotagents overlays add net -g --repo https://github.com/you/dotagents.git@repo#overlays
dotagents overlays sync -g # refetches from each recorded repo
What dotagents env wires for every overlay
Nothing in an overlay has to set up its own paths. For every installed overlay,
dotagents env prepends bin/ to PATH and an existing lib/ to PYTHONPATH
(listing the libs in AGENTS_PYTHONPATH too), puts the launchers of its scripts
(bin/*.py and [scripts], in <venv>/overlays/ of the agents' venv) first on
PATH, and exports $<NAME>_OVERLAY_ROOT — the overlay's
installed directory (NAME is the overlay's name upper-cased, - → _:
private-sync → PRIVATE_SYNC_OVERLAY_ROOT).
That variable is how overlay content refers to itself and to other overlays: a
routing line reads $ENGINEERING_OVERLAY_ROOT/flows/PLAN.md, never ~/.agents/flows/PLAN.md
— overlays install under overlays/<name>/, and the store itself can live anywhere
($AGENTS_HOME). dotagents context expands both forms, $ENGINEERING_OVERLAY_ROOT and
<ENGINEERING_OVERLAY_ROOT>, at assembly time for its stdout and JSON output; a file
written by context --write-agent keeps the variable.
An overlay that needs to export something of its own ships an env.py (or a plain
env file) at its root. dotagents env runs it as env.py --level <overlay name>
(plus --global when the project tiers are skipped) with the environment assembled
so far, and reads its changes from stdout as JSON — one object, or one object per line,
merged in order. Values must be strings, and null unsets a variable. The full
contract is under Commands → env.
overlay.toml
The manifest is read by the overlays command and the context assembler, with
tomllib (Python 3.11+) or tomli when either is installed, and otherwise with a
small built-in reader that covers these keys. Keys:
| Key | Meaning |
|---|---|
name |
The overlay's canonical name. |
description |
One-line summary shown by overlays list. |
requires |
Other overlays this one depends on; add installs them first. |
routing |
Lines appended to the core's "Load on demand" routing table. |
rules |
Overlay-relative markdown paths whose rule bullets append to "Always-on rules". |
priority |
Merge order (lower sorts earlier; unprioritized default is 500). |
setup_timeout |
Seconds the setup script may run before it is stopped (default 300; 0 = no limit). |
[scripts] |
Launchers on PATH: name = "path/in/overlay.py" adds or replaces one, name = false drops the one for a bin/<name>.py. See Authoring → Commands on PATH. |
overlay.toml is the only manifest. A leading --- frontmatter block in a
CONTEXT.md (the priority: / scope: / dependencies: an older overlay format
kept there) is not read, and context leaves it out of what it assembles.
Skills
An overlay may ship skills/<skill-name>/ directories. Publishing symlinks each (or
copies, where symlinks are unavailable) into the scope's shared skills dir, so every
agent that reads that dir picks up the same skills. Removing the overlay unpublishes
only the skills it published, then sweeps any now-broken symlinks.
Python dependencies and the scope venv
Every store has a Python venv, <store>/.pyvenv/<X.Y.Z>-<os>-<arch>/ (for example
~/.agents/.pyvenv/3.14.7-nt-arm64), made from the Python that runs dotagents.
A store's .pyvenv/ holds these agents' venvs only: skills, overlay helpers and
setup scripts run there. A project's own code gets its venvs beside the store, in
the project root's .pyvenv/ (<project>/.pyvenv/<version>-<os>-<arch>/, which the
python overlay's dotagents pyvenv creates); only a venv dotagents itself synced is
ever used as an agents' venv. init and every
overlays add / sync / remove keep it current:
- The user store's venv always has duho, so any skill can build a command line
on it, plus the packages each overlay installed there lists in a
requirements.txtat its root (pip's format). They are installed in one pip run, so pip sees every constraint at once, and only when a file changed. - A project gets its own venv only when one of its overlays ships a
requirements.txtor alib/; otherwise a project session uses the user venv. - The venvs layer like everything else. A
.pthfile in each venv lists, highest precedence first: the store's own overlay libs, then the user venv's packages and libs, then the system store's. dotagents itself comes last. So in a project venv, the project's packages and libs win, then the user's, then the system's. dotagents envpointsAGENTS_PYTHONat the active venv (and names it inAGENTS_PYVENV). Overlaysetup.pyandenv.pyrun under it, and the managed block tells agents to run skill scripts with it. Libs the venv layers stay offPYTHONPATH, so no other Python in the session, such as a project's own venv, imports them by accident.
No ensurepip, no network? pip runs from its wheel, pinned by version (25.3, the
last for Python 3.9) and hash, found in this order: the store's cache, a copy
vendored into the .pyz (build-pyz --with-pip), AGENTS_PIP_SOURCE (a directory
of wheels, or any URI pathapi-uri reads: file:, s3:, sftp:, ...), pip's own
find-links, then your index (PIP_INDEX_URL or the mirror in pip.ini /
pip.conf). AGENTS_PIP_SOURCE is also handed to pip as --find-links for the
requirements themselves.
Opt out per run with --no-venv, or for good with AGENTS_NO_PYVENV=1.
Setup scripts
An overlay may ship an idempotent setup.py at its root — the recommended,
OS-agnostic form: it runs under AGENTS_PYTHON (the scope venv above, with the
overlay's requirements installed first), so it works on every platform. After
add / sync copies the overlay in, dotagents runs the script automatically. Reserve it for real install-time work:
PATH, PYTHONPATH and $<NAME>_OVERLAY_ROOT are dotagents env's job
(above), and an overlay's own env vars belong in its env.py, not in a script that
writes one into the store — none of the example overlays ship a setup script any more.
Presence of a script is the opt-in; skip it with --no-setup. The author contract:
- Idempotent — safe on every
add/sync; check-then-act, never blindly append. - cwd is the installed overlay dir, so reference your own files by relative path.
- Environment carries
AGENTS_HOME(the user store),AGENTS_SCOPE_ROOT(the store the overlay is installed into),AGENTS_SCOPE(userorproject) andAGENTS_OVERLAY_DIR(the overlay's own installed dir) — never hardcode a home path. - Environment is also the scope's assembled
dotagents env: every overlay'sbin/onPATH, and every overlay'slib/importable (through the venv's layering, orPYTHONPATHwithout a venv). - Time limit: 300 seconds, or the manifest's
setup_timeout, or the command's--setup-timeout(which wins);0means no limit. A script still running then is stopped, and that counts as a failure (exit 124). - A non-zero exit fails the install with a clear error, not a silent skip, and a
fresh
addis rolled back. Any outward or irreversible action must be confirmed by the script itself.
See Authoring an overlay to build your own.