agents.md
This page is botfile's AGENTS.md: everything an AI agent needs to drive botfile on a user's machine. The CLI ships the same guide; with botfile installed, run botfile guide (or botfile guide --format markdown|json) to read it locally.
botfile manages AI-agent skills, instructions, and commands as symlinks from source repositories you control. Source lives on Codeberg (botfile/botfile), mirrored to GitHub (listfold/botfile); release binaries are published on Codeberg.
Install
Check first: botfile version succeeds if botfile is already on PATH. If it is not, install it (with the user's agreement):
curl -fsSL https://botfile.org/install.sh | sh
# symlink creation needs Developer Mode enabled
irm https://botfile.org/install.ps1 | iex
The installer is ~120 lines of plain shell with no obfuscation: it downloads a single static botfile binary from the Codeberg releases, verifies its sha256 against the release's checksums.txt, and installs it to a PATH directory; nothing else. Reviewing install.sh before running is fine and encouraged; set BOTFILE_DRY_RUN=1 to print the resolved version, URL, and install directory and stop before downloading. Once installed, botfile upgrade performs the same checksum-verified replacement.
To build from source instead: go install ./cmd/botfile from a checkout (see the README).
Model
| Term | Meaning |
|---|---|
source | A local directory, often a git checkout, holding curated components. botfile reads it in place; git does any fetching. |
plugin | A named bundle inside a source. Even a single-bundle source has an explicit plugin directory: <source>/<plugin>/. |
component | A typed artifact under a plugin. Kinds today: a skill (a directory with a SKILL.md), an instruction (a .md file), and a command (a .md file the agent exposes as a slash command). |
selection | A config rule mapping a source (and optionally one plugin or component) to one or more agents that should receive it. |
The same model on disk, in config, and on the agent after a sync; one component of each kind the whole way through:
~/botfiles/personal/ <- source: the location named in config
└── default/ <- plugin: the explicit bundle dir
├── skills/
│ └── review/
│ └── SKILL.md <- component skill/review
├── instructions/
│ └── house-style.md <- component instruction/house-style
└── commands/
└── changelog.md <- component command/changelog
[[sources]]
name = "personal"
location = "~/botfiles/personal"
[[selections]]
source = "personal"
agents = ["claude-code"]
~/.claude/skills/review -> ~/botfiles/personal/default/skills/review
~/.claude/rules/house-style.md -> ~/botfiles/personal/default/instructions/house-style.md
~/.claude/commands/changelog.md -> ~/botfiles/personal/default/commands/changelog.md
Config
One file declares everything: ~/.config/botfile/config.toml. A source's location is the leaf directory that is the repo, not a parent folder holding several repos. Two sources, each selected into agents:
[[sources]]
name = "personal"
location = "~/botfiles/personal"
[[sources]]
name = "team"
location = "~/botfiles/team"
[[selections]]
source = "personal"
agents = ["claude-code"]
[[selections]]
source = "team"
agents = ["claude-code", "codex-cli"]
Layout
~/botfiles/ is the conventional home for sources: a plain directory in which each source lives as its own git repo. When no source exists yet, offer to create two: ~/botfiles/personal, pushed to a private remote so it follows the user across devices, and ~/botfiles/team, pushed somewhere their team can reach. More sources are added later by cloning alongside, like ~/botfiles/ponytail from a public repo such as DietrichGebert/ponytail; each new source then gets its own [[sources]] entry and selections.
If the user already syncs a dotfiles repo, a source can live inside it instead: point location at a subdirectory like ~/dotfiles/botfile and the components ride along with what they already sync.
Sharing is plain git; botfile never fetches. To reach a second device or a teammate: git init and push the source to a remote they can reach, clone it there, add the same [[sources]] entry and selections on that machine, and botfile sync.
Scope
- botfile operates at user scope only: the per-user paths under your home directory. It never writes into a project checkout (a repo's
.claude/or an in-repoAGENTS.md); project-scoped components belong to the project. - Selections fan out: one component reaches every agent its selections name, one symlink per agent's native path, and agents reading the shared
~/.agents/skillspool are served by a single link. Symlinks, not copies, so an edit to the source is live through every agent at once. - The kinds differ in how they reach the model: an instruction is ambient (the harness injects it into every session), so it matters that instructions can be scoped to all, some, or one agent; a skill is on-demand: its name and description always sit in context and its body loads when the model matches it to the task or the user invokes it; a command is a prompt file the user runs explicitly (
/name). On-demand kinds are cheap when idle, not free. Not every agent supports commands; the agents table shows a dash where a kind has no native surface. - A selection picks any depth of source > plugin > component: omit plugin and component for the whole source, set plugin for one bundle, set both for a single component (component is
<kind>/<name>, likeskill/review). - An omitted plugin or component is a wildcard; an unknown config key is rejected rather than ignored, so a typo cannot silently widen a selection.
- botfile manages exactly the paths in the agents table below; ambient project files (a repo's
.claude/rules/, CLAUDE.md, an in-repo AGENTS.md) are project-scoped and out of scope.
Workflow
Run in this order. status and plan are read-only; only run sync after the user agrees.
botfile status: see what is managed, out of sync, and adoptable. Read-only, safe to run anytime.botfile plan: preview the exact symlinks a sync would create or remove. Read-only; changes nothing.- confirm with the user: show the plan and get the user's agreement before changing anything on disk.
botfile sync: apply the plan only after the user agrees: create and remove symlinks to match the config.botfile adopt <path> --into <source>/<plugin>: if sync reports a conflict (a real file where botfile wants a link), adopt that file into a source instead of overwriting it. botfile never clobbers.
Commands
| Command | Does |
|---|---|
botfile plan | show what a sync would change |
botfile sync | reconcile your agents to match your config |
botfile status | show what is managed, out of sync, and adoptable |
botfile adopt <path> --into <source>/<plugin> | bring an agent-created component under management |
botfile guide | print this guide (text, markdown, or json) |
botfile version | print the version |
botfile upgrade [--check] | replace this binary with the latest release, checksum-verified (--check: report only) |
Agents
| Agent | Skills | Instructions | Commands |
|---|---|---|---|
claude-code | ~/.claude/skills/<name>/ | ~/.claude/rules/<name>.md | ~/.claude/commands/<name>.md |
codex-cli | ~/.agents/skills/<name>/ | ~/.codex/AGENTS.md | ~/.codex/prompts/<name>.md |
copilot-cli | ~/.agents/skills/<name>/ | ~/.copilot/copilot-instructions.md | - |
copilot-vscode | ~/.agents/skills/<name>/ | ~/.copilot/instructions/<name>.instructions.md | - |
crush | ~/.agents/skills/<name>/ | ~/.config/crush/CRUSH.md | - |
opencode | ~/.agents/skills/<name>/ | ~/.config/opencode/AGENTS.md | ~/.config/opencode/commands/<name>.md |
pi.dev | ~/.agents/skills/<name>/ | ~/.pi/agent/AGENTS.md | ~/.pi/agent/prompts/<name>.md |
The same support, per agent version, is in the support matrix.
JSON for agents
- Every command accepts
--format json. Prefer it: parse the structured report rather than scraping text. - The JSON envelope carries
schemaVersion,command,phase,outcome,exitCode, plusops,notes,issues, and summary counts. exitCodeis authoritative: 0 ok, 1 blocked (a conflict or broken config refused the change), 2 a usage or effect error.planandstatusnever modify anything; onlysyncandadoptchange the filesystem, andupgradereplaces botfile's own binary (checksum-verified, never in the background).