Skip to main content

Instructions

Put standing instructions in AGENTS.md and they apply to every task in that directory.

# AGENTS.md

Run `make check` before saying a change is done.
Prefer `edit_file` over rewriting a file.
Commit subjects are imperative; the body explains why, never what.

Instructions tell the planner how work is done here. To run a command of your own when something happens, rather than to ask the planner for it, see Hooks. For a whole procedure the planner loads only when the task calls for it, see Skills. For a kind of delegate the planner can hand a sub-task to, see Delegate definitions.

The six sources​

FileApplies to
~/.bravebot/AGENTS.mdevery project
~/.bravebot/skills/<name>/SKILL.mdevery project
~/.bravebot/agents/<name>.mdevery project
<workspace>/AGENTS.md, else CLAUDE.md, else .claude/CLAUDE.mdthis project
<workspace>/.bravebot/skills/<name>/SKILL.mdthis project
<workspace>/.bravebot/agents/<name>.mdthis project

And no others. There is no search of parent directories and no nested instructions file. A rule that walked upwards would pick up instructions from whatever happened to be above a project on this machine, which is a different set of instructions on the next machine. A file at any other path is an ordinary file, read only when something asks for it by name, or when the source points at it.

The project's file is looked for under three names, and the first that exists is the one. Not all three: a repository holding two of them holds one set of instructions under two names, and reading both would say everything twice in a system prompt that goes out afresh every request.

~/.bravebot is .bravebot inside the home directory the environment gives, and there is no fallback. When there is no home, or the name is empty, everything kept there is absent. Nothing is guessed and no other location is tried. Daemons and containers run without a home, and everything kept there is optional, so absence is a case to do without rather than a reason to refuse to start.

HOME is what names it. Stock Windows sets no HOME, so there USERPROFILE is read after it and the first of the two that is set wins: that is the platform stating where the profile is, the same thing HOME does on Unix, rather than a guess past an answer. See Configuration for the rest of what lives in that directory.

Note the two roots are spelled differently. Your own skills and delegate definitions sit directly beneath ~/.bravebot; a project's sit under a dotted .bravebot directory rather than at the root where AGENTS.md sits. A skill is a directory because it has other material to keep beside its instructions; a delegate definition is one file, so agents/ is flat.

A file that only names another is followed​

An instructions file under 500 bytes that names another file is followed to that file, and that file is what reaches the planner. Repositories supporting several agents often keep one real document and point the other names at it. An AGENTS.md holding nothing but

Refer to canonical agent instructions in `.claude/CLAUDE.md`.

loads .claude/CLAUDE.md.

Length is the whole test. Anything past 500 bytes is a document that happens to cite other files, so it is read as itself and its citations are left alone. Following the first name in a real conventions file would swap your instructions for whatever they mentioned in passing.

Once, not twice: what the named file names in turn is not followed. The pointer is opened by the same route as any other path, so confinement and the trust map decide whether it may be read at all, and a pointer naming something outside the workspace is refused there.

Words from the command line​

--append-system-prompt adds its words as a seventh source that is not a file. It is read last, after the project's AGENTS.md, so where the two disagree the words win. It is held by the running process, so it is in every turn of the run and in the stored session of none: resuming without the flag runs without it.

--system-prompt replaces the opening of the system prompt and nothing after it. The instructions for reading a tool's output, the facts about where you are working, the mode and the goal are still sent, because other guarantees rest on them. Neither flag is read through the trust gate, since there is no file behind them. --append-system-prompt "$(cat x)" therefore gives the file's bytes your authority.

What wins​

The project has the last word. Sources are read least specific first: your own directory before the project, both AGENTS.md files reaching the planner in that order, and a project skill replacing a global one of the same name. It is the same "most specific wins" rule the trust map uses for paths. Shadowing by name rather than merging is what lets a project override one skill without restating the rest.

A directory opened with /add-dir during a session adds no standing instructions and no skills, whatever it contains. Opening a directory to read one file out of it should not change how every later turn behaves.

Where they end up​

What is resolved goes into the system prompt, never into the conversation. A session running many turns carries one copy of its instructions however long it runs, rather than a copy per turn crowding out the task.

Sources are resolved afresh every turn, so editing AGENTS.md mid-session takes effect on the next thing you send. A source that is not there is not an error. No AGENTS.md, no skills directory, no user directory at all: each is the ordinary case and offers nothing.

Where you are working​

The system prompt also states a handful of facts about your machine, so the planner does not have to run a command to learn them:

LineValue
Working directoryThe absolute path of the workspace root
Is a git repositoryWhether this tree or a directory above it holds a .git
GitHub CLI (gh) on PATHWhether gh is installed, looked up on $PATH the way a run would
Platformmacos, linux, or whatever this build runs on
OS versionThe kernel release string on Unix, as uname reports it. On Windows, the three numbers a build is named by
Shell$SHELL, or /bin/sh when that is unset or empty
Today's dateThe current UTC date, as YYYY-MM-DD
Scratch directoryThe directory this session has to itself, on the sessions that have one
These lines are sent to the model with every request

The working directory is an absolute path, so on most machines it contains your username, and the scratch path may too. The OS version names your build. All of it is part of every request this session sends, including the first one, and there is no setting that withholds any of it.

Nothing else about your machine is added. No environment variables beyond $SHELL, no hostname, no username on its own, no file contents, no directory listing.

They are labelled as facts about the machine rather than as instructions. One of them asks something of the planner as well, and only because a probe found what the request names: the scratch directory is stated with what to put in it. Nothing is asked on the account of the rest. What an installed gh is for is said too, just outside this block, and below is what it says.

The date is stated because a model's sense of it comes from its training and is wrong by however long ago that was. The rest is stated because discovering any of it otherwise costs a run, and a run costs two approvals rather than one: you approve the command, then its output comes back quarantined and the planner has to ask to be shown it. A planner that does not know its own working directory reaches for pwd and spends that whole exchange on a value already on your screen at every run prompt.

This block is composed afresh every turn like the sources are, so /cd is followed and the next turn states where the session went.

The scratch directory​

The last line names a directory this session has to itself, created as the session opens in the system temporary directory rather than anywhere in your project. It is where a file the work needs on disk but nobody is asking to keep belongs: output to grep through, an archive to look inside. Written into the project instead, that file is one a build, a test run, a git add -A and a reviewer each have to deal with, and one somebody has to remember to delete.

It is removed when the session ends, with everything written in it, and a session that carries on from another is given its own. A program started by run reads the same path from BRAVEBOT_SCRATCH_DIR. A session that could not be given a directory says so and runs without one, and the line is then absent rather than naming somewhere that is not there.

It is stated because no run could discover it: nothing names that directory but this program, and a planner never told of it puts an intermediate file in the project instead. Reaching it grants nothing. A file there is read and written by its absolute path, but it prompts exactly when a file in the workspace would, and neither /add-dir nor /cd will take it. See Trusted directories.

The GitHub CLI​

Where gh is on your $PATH, the block says so and adds the commands that read a GitHub URL: gh pr view, gh pr diff and gh issue view, through run, with --comments for what a review said. Paste the URL of a pull request and that is one approval for exactly the diff, where fetch_url fetches the page around it. Both come back quarantined, so either way a processor reads the answer out; the difference is that one of them carried the page to say it. The .diff address of a pull request is worse than that: it redirects to another host, and an approval for github.com does not carry to wherever a fetch went next, so it is refused unless a rule names that host too.

Whether gh is installed is all this looks at. Whether you have logged in with it cannot be told without a request, and none is made before your first turn, so the paragraph names fetch_url as what to fall back to when gh fails for that reason.

A delegate is told the fact and not this paragraph. It has no fetch_url at all, and run only if its kind reaches programs, so a sentence choosing between the two would send it after a tool it was not given.

Trust​

~/.bravebot is trusted by provenance: it is your own directory, on the same footing as the configuration that picks the model and the endpoint. Putting a file there is the grant, and an empty directory offers nothing.

A project's own AGENTS.md is different. It is workspace content, so it is read through the trust map like any other file. It loads when you vouched for the directory and is left out when you did not:

AGENTS.md was not loaded: this directory is not trusted
2 skills in .bravebot/skills were not loaded: this directory is not trusted
1 delegate definition in .bravebot/agents was not loaded: this directory is not trusted

A source that fails the trusted-content gate is dropped entirely, never quarantined. A reference to an instruction is no use to anyone: an instruction is either followed or absent, and one from a directory nobody vouched for has to be absent.

What was skipped is counted, never named. A directory in an untrusted project can be given a name that reads like an instruction, and that name would otherwise be on your screen as though the agent had written it.

The notice is said when it is learned, before the first request goes out, rather than when the turn ends. A turn that fails or is cancelled has already told you what it was working without.

The environment lines do not go through this gate, and cannot be refused by it. There is no file behind any of them: the working directory is where you pointed the session, the scratch directory is one this program created empty, and the rest comes from the kernel and this process's own environment, which is the same provenance a command you typed rests on. Nothing read out of the workspace may join that block, which is the whole reason it can skip the gate.