tenets
What botfile is
1
botfile manages your agents' config and context lifecycle components like skills or instructions. It is just like dotfiles for your other dev tools.
3
botfile is declarative. You declare desired state in config (38); botfile reconciles the filesystem to match it. Following Stow, this is a two-phase discipline: compute a plan, then apply it.
4
botfile is recursive. Its CLI serves human users and, with structured JSON output (
--format json), is easy for an agent to drive. With botfile installed, an agent can manage botfiles itself.5
botfile lets you share, sync, and develop components with others (like your team) and with yourself (across devices, and across different agents on the same device).
Vocabulary
6
agent = harness + model.
7
component = context or config that is injected or persisted across the harness lifecycle: automatically (like instructions), manually (like skills or slash commands), or both (like mcp configs or hooks).
8
source = a curated repository or directory of components (for example a private personal Codeberg repo of secret skills, or a public team repo of coding standards and best practices).
9
plugin = a bundle of components.
10
botfile adopts the hierarchy source > plugin > component.
11
selection = a declaration of which components are registered with which agents (see the struct in 39).
12
registration = a thin layer that makes a selection discoverable using an agent's native discovery mechanism (defined precisely by the rubric, 22-25, 51, and disciplined by 26-27).
Core features
13
Multiple sources. You can have many (for example a private personal repo and a public team repo).
14
A source maps to all agents on the system, or to a subset (15, 39).
15
botfile supports emergent workflows in which different agents see and use different components.
Supported agents and components
16
botfile supports most popular agents:
codex-cli, claude-code, copilot-cli, copilot-vscode, crush, opencode, pi.dev.17
botfile supports skills for most agents. Skills are the most rigorously specified component, at agentskills.io.
18
botfile supports instructions: the user-scope standing guidance you want version-controlled and synced across machines, shared across agents or scoped to a subset as the selection (11) directs. "memory,"
rules/, AGENTS.md, CLAUDE.md, and copilot-instructions.md are vendor labels for this one concept, so botfile models one kind, instruction. What decides how botfile manages it is the shape the agent exposes, and there are two, both no-clobber:
- A drop-in directory of one file per instruction, discovered by presence (tier 1, 22). botfile distributes into it. claude-code (
~/.claude/rules/<name>.md) and copilot-vscode (~/.copilot/instructions/<name>.instructions.md) do this today; the install leaf takes the agent's extension, so the same source<name>.mdserves both. - A single fixed file at a per-agent path (codex-cli
~/.codex/AGENTS.md, opencode~/.config/opencode/AGENTS.md, pi.dev~/.pi/agent/AGENTS.md, copilot-cli~/.copilot/copilot-instructions.md, crush~/.config/crush/CRUSH.md, and claude-code's ownCLAUDE.md). botfile installs the symlink here like any other target, so an adopted instruction syncs to every machine, but it never clobbers: it creates the link only where the path is free or already its own, and where a user-authored file sits there it reports a conflict and refuses (35), never overwriting and never splicing bytes into the file (26, 28, 33). The user reconciles that conflict out of band, normally by adopting (50) the file they already wrote, which moves it into a source so it is then managed like any symlink. Because the file usually pre-exists, adoption is the entry point; distribution is how it propagates afterward.
botfile does not point a setting or environment variable at a managed directory to manufacture a per-file surface where the agent ships none (opencode's instructions: glob, copilot-cli's COPILOT_CUSTOM_INSTRUCTIONS_DIRS). That registration path (tier 3, 51) is retired for instructions: it mutates an agent's config, the magic this design avoids (28). Every agent already reads some instruction file, a drop-in directory or a singleton, so a plain symlink reaches all of them and registration is redundant (see callouts, 44).
19
botfile supports a component only where the agent treats it in a way that is manageable by symlinks. As the other components (hooks, mcp config, subagents) tend toward standardization, especially toward symlink-friendly Linux conventions like XDG or the drop-in-directory idiom, they can be supported. Where agents adopt idiosyncratic context injection, they cannot be managed by botfile.
20
By favoring these conventions, botfile strives to guide agents toward consensus and shared structure (see callouts, 44-45).
The support rubric
21
Shape-preserving rule. botfile supports a component kind only when the user's source component can stay portable and shape-preserving across agents. The ideal path is direct: one source file or directory becomes one installed file or directory in the agent's native per-kind namespace, and the source bytes remain plain and agent-neutral. If the agent does not natively discover that namespace, botfile may add a small registration (12) that points the harness at botfile-managed files, but never rewrites the bytes (26).
22
Tier 1 - Native directory. The harness natively scans a drop-in directory by presence (claude-code's
~/.claude/rules/, every agent's skills/). botfile symlinks one file per artifact into it and the harness finds them. Many artifacts per agent, additive, multiple sources blend, no registration. Shared-first. When an agent reads several such directories for a kind at user scope (crush reads four for skills), botfile installs to the single shared cross-agent namespace (~/.agents/skills), so one symlink serves every reader, accepting the coarser visibility of 49. botfile records where it installs, not every directory an agent reads.23
Tier 2 - Native fixed file. The harness natively reads a single file at a known fixed path by presence (codex-cli
~/.codex/AGENTS.md, copilot-cli ~/.copilot/copilot-instructions.md, claude-code's own CLAUDE.md). botfile symlinks that one file. This shares tier 1's no-registration property; the only difference is cardinality and naming, one fixed-named file rather than a scanned directory of many. It is a singleton, so source precedence (35) picks the occupant and a user's existing file is a reported conflict, never a clobber (the entry ritual for a pre-existing file is adoption, 50).51
Tier 3 - Registration. The harness exposes no presence-based surface, so botfile would have to make its files discoverable with a one-time settings or env-var write pointing the harness at a managed directory (the shape opencode's
instructions: glob or copilot-cli's COPILOT_CUSTOM_INSTRUCTIONS_DIRS would take). That mutates the agent's config, the magic this design avoids, so it is defined but used by no kind today: instructions reach every agent through tier 1 or tier 2, so registration is redundant (see callouts, 44). It is kept for a future kind that genuinely has no native surface, to be weighed then on its own merits against the no-magic tenet (19, 25-27).24
If tier 1 (22), tier 2 (23), or tier 3 (51) applies for a given component and agent, that component can be supported for that agent. Otherwise it cannot.
25
botfile never monkeypatches context (for example a SessionStart hook that loads a directory of "core instructions"). An agent that would require such monkeypatching is recorded in the callouts directory (44), not supported.
Registration discipline
26
Mechanism, never bytes. A registration (12, 51) writes only mechanism (a glob, an env var, a settings value), never artifact bytes. Component content is always symlinked, never inlined into a host file. This is the line that separates registration from vendoring content into a file the user also edits.
27
Structural, not textual. When a registration must touch a shared config file (tier 3, 51), botfile treats that file as structured data: it parses the native format, adds the smallest botfile-owned entry, and preserves the user's unrelated entries. It never introduces marker-delimited text regions.
Boundaries
28
Authored, not learned. Consumed, not managed. botfile distributes artifacts a human deliberately authors and wants identical across machines, agents, and teammates. Content flows one direction, source to agents. botfile does not capture, replace, or round-trip the state an agent generates for itself at runtime (for example a model's consolidated, learned memories). The same line separates what an agent consumes from what it manages: botfile installs the content an agent reads (skills, instructions) and never takes ownership of a file the agent itself writes, such as its config or settings (which it rewrites, so owning it is both fragile and a round-trip). Where an agent's only instruction surface is a single file it also owns, botfile does not splice into it; the user adopts (50) their own file instead, so botfile manages the symlink, never the bytes.
29
Mechanism, not policy. botfile never seeds or authors component content; it is a mechanism (reconcile, symlink, resolve conflicts), not a policy about how a source comes to exist. The dotfiles analogy is exact: Stow does not
git init your dotfiles repo or seed it with a blank .zshrc. You curate the repo with the tools you already have; botfile takes over once curated state exists. botfile creates symlinks (its job) and its own config (38); it does not create source content.30
Not a package manager. botfile has no lockfiles, no provenance or trust prompts, no version policy, and no transitive resolution. Opening a project must never alter your agent environment.
31
botfile manages user-owned agent dotfiles. It does not make a project repository into a botfile dependency root, and it does not write managed content into project files.
32
A repo may still contain native agent files (
CLAUDE.md, AGENTS.md, .claude/skills/, and so on), but those are ordinary project content: authored, copied, reviewed, and committed with the user's normal git workflow.33
botfile owns only the symlink it creates. It may create a missing parent scan directory when that directory is the documented user-scope scan root, but it never claims ownership of the parent directory and never edits or deletes user-authored sibling entries.
34
botfile does not impose ordering among sibling components in a target directory; ordering is the host's concern. Any numeric prefix is part of the user-authored component name, never generated by botfile.
Conflict, precedence, and adoption
35
At most one symlink may occupy a given agent path, and botfile resolves a contested path deterministically rather than guessing:
- Two different sources contributing to the same path are resolved by source precedence, which is declaration order (38): the earlier-declared source wins the single slot and the others are reported as shadowed, never silently dropped.
- A single source contributing more than one destination to the same path is ambiguous: precedence cannot choose, because precedence is between sources. botfile reports it as an error in that source's layout and leaves the target uninstalled, rather than picking one by chance.
- An unmanaged non-symlink already present at the path is a conflict botfile reports and never clobbers (29, 33).
36
botfile can adopt an existing on-disk component (a skill, an instruction, or any other component an agent or you created in place) into a source, replacing the original with a symlink (Stow's
--adopt). Adoption is explicit and discovery-driven: botfile reports the unmanaged components it finds in an agent's namespaces (the content the agent consumes, 28, not the config it manages), and you choose which to adopt and into which source and plugin. It brings the component fully under management: the files move into the source, the symlink takes their place, and a selection is ensured (so the component is both stored and selected, and a later sync is a no-op). It never authors new content (29) and never overwrites existing content, neither a name already used in the target source nor anything at the destination (33).50
Adoption is how botfile keeps pace with an emerging field. The agent ecosystem changes faster than any fixed support matrix (16-25): new component kinds, new conventions, and new on-disk artifacts keep appearing. botfile keeps pace by adding kinds as they emerge, in tool releases. It does not adopt a kind it does not yet recognize: an unknown artifact has no place in the source grammar (46) and no agent target to recompute on another machine, so there is nothing to absorb it into. What adoption adds is reach beyond the support rubric (22-25, 51) for the kinds botfile does recognize: such a kind can be adopted even when it cannot clear any tier to be distributed to that agent, because an artifact already on disk has proven the agent discovers its path, so symlinking the user's own file back needs no tier qualification. The moment a botfile release recognizes a kind, your existing files of that kind become manageable, even if full fan-out distribution never follows. A single fixed instruction file (a
CLAUDE.md, an AGENTS.md) is the case in point: it can never clear the rubric for fan-out (it is one merged file, 18), yet once botfile recognizes the instruction kind it can adopt the one you already wrote, because its presence proves the path and its target is known, and sync it across your machines without ever authoring or clobbering it (28, 33).Configuration
37
botfile manages the mapping between sources and agents using an XDG-compliant
config.toml at $XDG_CONFIG_HOME/botfile/config.toml.38
botfile's configuration is the declared desired state (3): the set of sources and the selections (39) that map them onto agents.
39
A selection (11) is declared as:
type Selection struct {
SourceName string // matches Source.Name
PluginName string // "*" for all plugins in the source
ComponentID string // "<kind>/<name>"; "*" for all in the plugin
Agents []string // agent IDs
}
Architecture
40
botfile is written in Go.
41
botfile builds for Linux, macOS, and Windows. Where Windows requires Developer Mode (for symlink creation), botfile calls it out.
42
botfile is functional, preferring the Elm pattern in all its internals and to model state. Unidirectional dataflow makes botfile easy to reason about, understand, and develop; debugging and error handling are easy. The recurring engineering patterns that realize this (the explicit outcome algebra, the normalize-then-plan pipeline, parse-don't-validate domain types, and the rest) are catalogued in
reviews/patterns.md, the pattern companion to this manifesto and the review rubric.43
botfile uses the charm.land ecosystem for TUI apps that sparkle, leaning heavily on the Elm pattern (42).
Callouts
44
botfile includes a callouts directory used to steer agents across the ecosystem toward a shared specification for components, across a changing field (19, 20).
45
The callouts directory is intended to host a small blog.
Source layout
46
Source grammar. A source is a directory tree with a fixed grammar:
<source>/<plugin>/<kind>/<component>. The first level under the source root is always a plugin (9), the second is always a component kind (7), the third is the component. botfile requires an explicit plugin level and never infers an implicit or default plugin from files lying at the source root. This follows Stow, whose unit is a named package directory, and keeps the grammar unambiguous: the first level can never be mistaken for a kind directory.47
Plural kind directories, singular kind tokens. On disk a kind groups its components under a plural directory (
skills/, instructions/) to match the agents' native namespaces (21, 22). The kind token in a component id (39) and in config stays singular (skill, instruction); botfile maps between the two.48
Skills are directories, instructions are files. A skill follows agentskills.io (17): a directory named for the skill containing a required
SKILL.md plus any resources it references, installed as one directory symlink. An instruction (18) is a single file <name>.md, installed as one file symlink. Either way a component installs as exactly one symlink; botfile never folds or unfolds trees (33, 34).Selection granularity
49
Selection granularity follows how a component enters the context (7). Auto-injected components (instructions) load into every session, so a selection's agents scope them precisely: each named agent receives exactly the instructions selected for it, and no others (their surfaces are per-agent isolated, so precise scoping is always honored). Because that scoping is worth preserving, when an agent exposes both an isolated and a shared instruction surface botfile targets the isolated one (crush reads both
~/.config/crush/CRUSH.md and the generic cross-tool ~/.config/AGENTS.md; botfile uses the former, so selecting an instruction for crush reaches crush and nothing else). Invoked components (skills, commands) cost nothing until invoked, so botfile installs them into each selected agent's namespace without trying to hide them from neighbors, and where several agents share a skills namespace it installs to the shared one (22) rather than fight a costless overlap. The agents on a selection still scope which agents botfile touches (so it never materializes a namespace for an agent you do not use), but where agents share a skills namespace (35), botfile does not subdivide it: the shared visibility is harmless by design, since an uninvoked skill does nothing. botfile never routes or replaces an agent's own scan directory to force sharing (33); it installs one symlink per agent namespace.