A btw.md
file is a Markdown file that saves how btw should set up a
chat, so each chat in your project starts already set up: the right
model, the right tools, and instructions you’d otherwise repeat.
A btw.md file has two parts. The YAML front matter at
the top chooses the model and tools, and the Markdown body below it
gives the model instructions about your project. btw_app()
and btw_client() find the file in your project or home
directory and apply it automatically.
If you use other coding agents, you may already keep project
instructions in an AGENTS.md or CLAUDE.md
file. A btw.md file plays the same role for btw, and btw
falls back to those files when a project doesn’t have one (see Where btw looks for btw.md).
Using btw.md gives you a few things those files don’t:
CLAUDE.md, and the other tools that read
AGENTS.md don’t expect front matter there.btw.md in your home directory, such as
~/.btw/btw.md, with each project’s file. btw doesn’t look
for AGENTS.md or CLAUDE.md in your home
directory.In this vignette, we’ll set up a chat by hand, save that setup in a
btw.md file, and then learn how btw decides which file to
use. Custom agents are written in the same format, so this vignette is
also the groundwork for Custom agents.
To follow along, you’ll need btw, an ellmer chat provider you can
use, and shinychat
to run the chat app.
btw_app() starts a chat app in your R session. Its
client argument takes an ellmer chat object, which chooses
the provider and model. This example uses
ellmer::chat_posit(); substitute the chat_*()
function for the provider you use.
Inside the app, you can customize which tools the model can use in the chat. The tool menu lists all available tools, and you can turn each one on or off. Similarly, you can also edit the system prompt (the set of instructions sent to the model at the start of a chat). Together, the model, the tools, and the system prompt make up the chat’s configuration.
Those choices last only as long as the app is open, but you can use a
btw.md file to save those choices so that every btw chat in
the project starts from the same place.
btw.mdSuppose you’re working on a research project and want the chat to
read your files, look up R documentation, check installed packages, and
run R code. Create a file named btw.md at the root of the
project with this content:
---
client: posit
tools: [files, docs, sessioninfo, run]
---
You are a research data science assistant.
Inspect the project files before answering.
The two --- lines mark the start and end of the front
matter. The front matter configures the chat: client: posit
chooses the provider, the same way ellmer::chat_posit() did
above, and tools chooses which btw tools the chat can use.
The body gives the model instructions to steer its behavior. In short,
the YAML configures btw and the markdown configures the model.
Once you’ve created btw.md, you can start
btw_app() without any arguments from the project
directory:
btw finds btw.md, creates the client and the tools it
describes, and adds the body to the system prompt. If you open the
system prompt in the app, you’ll find the body under a “Project Context”
heading, after btw’s own instructions about your R session and tools.
Because the setup now lives in a file, you can read it, edit it, commit
it, and share it with collaborators.
You can write btw.md by hand, or let btw start it for
you.
use_btw_md() creates a btw.md file from a
template at the project root, and in an R package it also adds the file
to .Rbuildignore. use_btw_md("user") creates
the user-level file. edit_btw_md() opens the file that
btw_client() would use, so you can check which one is in
effect.
To draft the instructions for an existing project,
btw_task_create_btw_md() starts a chat in which the model
explores the project and writes a btw.md with you.
The rest of this vignette covers each part of the file in more detail.
clientThe client field takes the same provider or
provider/model strings as ellmer::chat().
client: posit uses the provider’s default model; add a
model name after a slash to choose a specific one:
---
client: posit/claude-sonnet-5-5
---
For more control, write client as a mapping.
provider chooses the ellmer::chat_*()
function, model becomes its model argument,
and any other keys are passed to that function as arguments. This
example points ellmer::chat_ollama() at a server on another
machine:
---
client:
provider: ollama
model: "gpt-oss:20b"
base_url: "http://my-company.example.com:11434"
---
You can also list several clients. When you start
btw_client() interactively, btw shows a menu of the
choices; in non-interactive code, it uses the first one. If you name the
entries, you can pass a name to the client argument, as in
btw_client(client = "local"):
---
client:
sonnet: posit/claude-sonnet-5-5
local:
provider: ollama
model: "gpt-oss:20b"
---
btw_app()
#>
#> ── Select a client ──
#>
#> 1. sonnet: posit/claude-sonnet-5-5 [default]
#> 2. local: ollama/gpt-oss:20b
#>
#> Enter choice [1-2, alias name, or 0 to exit]: sonnetThe client field is a default, not a requirement. A
client argument to btw_app() or
btw_client() takes priority over it, and so does the
btw.client R option.
toolsThe tools field lists tool groups, individual tool
names, or both. A group name such as files includes every
tool in that group. To see which tools a group contains, pass the same
names to btw_tools():
names(btw_tools("files"))
#> [1] "btw_tool_files_edit" "btw_tool_files_list" "btw_tool_files_patch"
#> [4] "btw_tool_files_read" "btw_tool_files_replace" "btw_tool_files_search"
#> [7] "btw_tool_files_write"The files group includes tools that write and edit
files, not only ones that read them. When the chat only needs to read,
name the individual tools instead. You can drop the
btw_tool_ prefix from tool names in
btw.md:
---
tools: [docs, files_read, files_search, files_list]
---
If you leave tools out, the chat gets btw’s default set
of tools. The R code tool in the run group isn’t part of
that default, because it runs code in your R session without asking you
first. Naming run in tools turns it on for
that chat; see ?btw_tool_run_r before you do.
As with client, a tools argument to
btw_app() or btw_client(), or the
btw.tools R option, takes priority over the field in
btw.md.
optionsSome btw settings are R options whose names start with
btw., and the options field sets them for
chats in this project. Write each option name without the
btw. prefix, nesting one level for each remaining part of
the name. For example, btw.subagent.client chooses the
model for subagents, the separate chat sessions that
your chat can start through a tool call:
---
client: posit/claude-sonnet-5-5
options:
subagent:
client: posit/zai-org/GLM-5.3-Flash
---
With this file, the main chat uses Claude Sonnet and its subagents
use the smaller, faster GLM 5.3 Flash model. You can learn more about
custom agents in vignette("custom-agents"). Because options
are tool-specific, they are listed in each tool’s help page, such as
?btw_tool_agent_subagent.
Everything after the front matter becomes part of the system prompt. Use the body for what you’d otherwise repeat at the start of every conversation: your coding style, terms that mean something specific in your field, the goals of the project, or where to find important files. Write it in whatever structure suits the project; btw doesn’t require particular headings.
Sometimes the file holds notes that you want to keep but don’t want
the model to read, such as finished tasks in a project plan. Wrap those
lines in <!-- HIDE --> and
<!-- /HIDE --> comments:
## Current task
Add a sensitivity analysis to `analysis.qmd`.
<!-- HIDE -->
## Done
- Cleaned the survey data.
<!-- /HIDE -->
btw removes the hidden lines before adding the body to the system
prompt. A <!-- HIDE --> without a closing
<!-- /HIDE --> hides everything to the end of the
file.
btw.mdWhen you start a chat, btw looks for a project file first. It starts
in the working directory and moves up through its parents, stopping at
the project root, which btw recognizes by a file such as
DESCRIPTION, .git, or an .Rproj
file. btw uses the closest btw.md it finds. If there isn’t
one, it uses an AGENTS.md
file, and then a CLAUDE.md file. btw reads the front matter
in AGENTS.md like btw.md, but ignores the
front matter in CLAUDE.md. btw reads only one of these
files, so if your project has both btw.md and
AGENTS.md, btw uses btw.md and ignores
AGENTS.md.
btw also reads a user-level btw.md that
applies to all of your projects, such as ~/.btw/btw.md. The
two files are combined:
client and tools fields are
used when they’re present; otherwise the user-level fields are
used.options from both files are combined, and the
project’s value wins when both set the same option.This lets you keep your general preferences, such as a default model
or coding style, in the user-level file and keep project details in the
project file. See ?btw-config for every location btw
checks.
To use a particular file instead of searching, pass its path to
path_btw. To start a chat without any btw.md
file, use path_btw = FALSE:
You now have a btw.md file that chooses the model, the
tools, and the instructions for the chat you talk to, and you know how
btw finds and combines these files.
A custom agent file uses the same format. Add a name and
a description to the front matter and save the file in
.btw/agents/, and btw turns it into a tool your chat can
call. Each call starts a subagent session configured by that file.
vignette("custom-agents") builds three of these agents,
starting from the btw.md file in this vignette.