Write your agent rules, identity and skills once. Build them for any coding agent: AGENTS.md, Claude Code, Cursor and more. Layered, secret-safe, standard library only.
dotenv keeps configuration in one file and loads it into any program. agent-dotenv does the same for agent setup: you keep rules, identity and skills in one portable folder, and the tool builds the files that each coding agent reads.
- One source, many agents. Each agent gets its own output from the same folder.
- Layered. Content from an employer, client or team lives in a separate layer. Remove the layer and only that content goes away.
- Secret-safe. Files marked
sensitivenever reach an output. Every output is scanned for secrets before it is kept. - Small. Python 3.9 or newer, standard library only. A build uses no network and writes only under
dist/.
A 30-second overview of the layers, filters, adapters and the secret scan.
agent-dotenv.mp4
You need Python 3.9 or newer. There is no PyPI release in v1.
pipx install git+https://github.com/suryast/agent-dotenvThis installs the agent-dotenv command.
You can also run python3 -m agent_dotenv from a checkout of this repository. It works only when the checkout is the current folder or is on PYTHONPATH. Then pass your source folder with --source:
cd ~/src/agent-dotenvpython3 -m agent_dotenv build --source ~/my-agent-envThe diagram shows the whole workflow before the steps.
flowchart LR
I["agent-dotenv init"] --> E["edit base/ and local/ files"]
E --> B{"agent-dotenv build"}
B -- "error or secret finding, exit 1" --> E
B -- "ok" --> D{"agent-dotenv doctor"}
D -- "FAIL because dist is older than the sources" --> B
D -- "ok" --> U["use the files in dist/NAME/"]
Make a source folder. This is your own folder, separate from this repository. A private repository works well.
mkdir ~/my-agent-env && cd ~/my-agent-envCreate the starter files.
agent-dotenv initcreated ~/my-agent-env/base/rules.md
created ~/my-agent-env/base/identity.md
created ~/my-agent-env/local/paths.env.example
created ~/my-agent-env/secrets.ref
created ~/my-agent-env/.gitignore
created ~/my-agent-env/local/paths.env
next: edit the files in ~/my-agent-env
then run: agent-dotenv build --source ~/my-agent-env
then run: agent-dotenv doctor --source ~/my-agent-env
Edit base/rules.md and base/identity.md. Keep the lines between the --- fences. For example, base/rules.md:
---
scope: personal
---
# Rules
- Write short, direct answers.
- Ask before deleting files.
- Run the tests before you say a change is done.And base/identity.md:
---
scope: personal
---
# Identity
- I am a backend developer. I work mostly in Python.Build every adapter.
agent-dotenv buildbuilt agents-md: 1 file(s) in ~/my-agent-env/dist/agents-md
built claude-ai-project: 1 file(s) in ~/my-agent-env/dist/claude-ai-project
built claude-code: 8 file(s) in ~/my-agent-env/dist/claude-code
built cursor: 2 file(s) in ~/my-agent-env/dist/cursor
Read the result.
cat dist/agents-md/AGENTS.md# Rules
- Write short, direct answers.
- Ask before deleting files.
- Run the tests before you say a change is done.
# Identity
- I am a backend developer. I work mostly in Python.Copy dist/agents-md/AGENTS.md into a project and any tool that reads AGENTS.md uses it. Check the environment at any time with agent-dotenv doctor.
Each adapter writes to dist/<adapter>/ inside your source folder.
| Agent | Output | How to use it |
|---|---|---|
Any tool that reads AGENTS.md |
dist/agents-md/AGENTS.md, plus skills/<name>/SKILL.md copies |
Copy the files into a project. |
| Claude Code | dist/claude-code/, a plugin |
claude --plugin-dir dist/claude-code |
| Cursor | dist/cursor/.cursor/rules/*.mdc |
Copy the .cursor folder into a project. |
| claude.ai project | dist/claude-ai-project/project-instructions.md and docs/ |
Paste the instructions into the project and upload docs/. |
my-agent-env/
base/ rules.md, identity.md, skills/<name>/SKILL.md, any other .md
local/
paths.env.example committed
paths.env ignored by git
adapters/ optional drop-in adapters (*.py)
secrets.ref names and where each value lives, never values
dist/ build output, ignored by git
Every Markdown file starts with frontmatter:
---
scope: personal | external | sensitive required
mode: replace | append default replace
targets: [agents-md, cursor] default: all adapters
description: one line used in indexes
---
personal: your own content. Any adapter may use it.external: content that belongs to another party, such as an employer, client or team. Only adapters that accept external content receive it.sensitive: never written to any output.
Files under skills/ use the skill format and have no scope.
Three kinds of layer are merged. A later layer replaces an earlier one, file by file, by relative path.
flowchart LR
B["1. base/<br/>rules, identity, skills"] -- "replaced by" --> E["2. external layers<br/>AGENT_DOTENV_LAYERS, in listed order"]
E -- "replaced by" --> L["3. local/<br/>per-machine overrides"]
mode: appendadds the later body after the earlier body, separated by a blank line.- External layers are folders outside the source folder. Set
AGENT_DOTENV_LAYERSto a list of absolute paths separated by:, in the process environment or inlocal/paths.env. An empty value in the process environment falls through tolocal/paths.env. - Only
base/andlocal/hold skills. Askills/folder in an external layer is ignored with a warning. - Each layer is filtered before the layers merge. A dropped file never leaks into a later file through
mode: append. A dropped override falls back to the earlier file.
agent-dotenv init --layer /absolute/path/to/layer creates an external layer from a headings-only template and records it in local/paths.env.
flowchart LR
S["source folder<br/>base/, local/, external layers"] --> LD["load layers"]
LD --> EACH["for each adapter:<br/>filter each layer, resolve, build"]
A["built-in and drop-in adapters"] --> EACH
EACH --> SC{"secret scan"}
SC -- "clean" --> OK["dist/NAME/ written"]
SC -- "finding" --> X["all output deleted, exit 1"]
An adapter returns data and does not write files. The core scans that data, deletes the old dist/NAME/ and writes the new files. See docs/security.md for what the scan catches and what it does not.
build never prints a secret value. This includes the file paths in findings: a secrets.ref value in a path is shown as [redacted].
| Adapter | Writes | External content |
|---|---|---|
agents-md |
AGENTS.md (rules, then identity, then a ## Skills index section when skills exist) and skills/<name>/SKILL.md copies |
No. Refused, because AGENTS.md is meant to be committed to project repositories. |
claude-code |
A Claude Code plugin: manifest, skills, a session-start hook and a bundled loader | Index only. The hook prints one path: description line for each external file at session start. The plugin holds no external body text. |
cursor |
.cursor/rules/agent-dotenv-rules.mdc and agent-dotenv-identity.mdc, both alwaysApply: true |
No. Refused, because Cursor rule files are committed to project repositories. |
claude-ai-project |
project-instructions.md and docs/ for upload. It warns above 8,000 characters (--instructions-limit N). |
Yes. External files are written to docs/ as full bodies. |
Behaviour to know about:
- In
claude-ai-project, an external layer file namedrules.mdreplaces the base rules in the instructions, and it is also written todocs/rules.md. Layers replace by relative path. - The Claude Code plugin version is the tool version. After a rebuild with new content, an installed copy may not refresh. Reload the plugin or reinstall it.
- The Claude Code session-start hook runs
python3.python3must be onPATHfor the agent. - A drop-in adapter runs as ordinary Python with your permissions. It loads only from
<source>/adapters/. See docs/adapters.md.
| Command | What it does |
|---|---|
agent-dotenv init [--source DIR] [--layer DIR] |
Creates the starter files. It never overwrites a file. --layer needs an absolute path to an empty or new folder. |
agent-dotenv build [--target NAME]... [--source DIR] [--instructions-limit N] |
Builds the named adapters, or all of them. Repeat the flag for each adapter: --target agents-md --target cursor. --source defaults to AGENT_DOTENV_HOME, then the current directory. The source folder must have a base/ folder. |
agent-dotenv doctor [--source DIR] |
Checks the base/ folder, local/paths.env, external layer paths, the Python version, the names in secrets.ref, drop-in adapters and whether dist/ is newer than the sources. It exits 1 on any failure. |
If you change a source and then build only some adapters (build --target NAME), doctor fails with "dist/ is older than the sources" for the other adapters until you run a full build.
- docs/adapters.md: write your own adapter, with the
BuildContextreference and a worked example. - docs/security.md: scopes, the secret scan and its limits, and what is never written.
- examples/minimal/: a small source folder with one drop-in adapter.
The tests need pytest. Install it once:
python3 -m pip install pytestRun the tests from the checkout:
python3 -m pytest -qSee CLAUDE.md for the layout and the rules for contributors.