install.packages('ellmer')
# or the dev version
pak::pak('tidyverse/ellmer')30 🏗 btw
In the previous chapters, we used the chores package to develop helpers—pre-written prompts—for repetitive tasks such as creating roxygen2 documentation and updating testthat tests.1 In the gander chapter, we used the addin to send code and environment context to the LLM.2
chores and gander assist us in automating or enhancing the prompts we submit to the LLM. The addins and shortcuts streamline interactions with the model, providing an experience that resembles a browser interface (as opposed to chatting with live_console(chat) or live_browser()).
In this chapter we’ll focus on the btw package, which is fundamentally different than chores and gander because it,
“provides a default set of tools to to peruse the documentation of packages you have installed, check out the objects in your global environment, and retrieve metadata about your session and platform.”3
I’ll cover using btw to add documentation and improve the contents of the downloadable report in our sap application.
30.1 Configuration
Install ellmer and btw:
install.packages("btw")
# or the dev version
# install.packages("pak")
pak::pak("posit-dev/btw")We can place the btw configuration options in the .Rprofile (similar to other ellmer configurations).4 Recall that the .Rprofile file can exist at the user and/or the project-level.
For example, I’ve added a project-level .Rprofile file this branch of the sap package and included a system_prompt and model:
if (interactive()) {
require(ellmer, quietly = TRUE)
}
if (interactive()) {
require(btw, quietly = TRUE)
}
options(
btw.client = ellmer::chat_anthropic(
system_prompt =
"You are an expert R/Python programmer who loves explaining complex topics to non-technical audiences.
...",
model = "claude-sonnet-4-5-20250929"
)
)- 1
-
Ensure
ellmerpackage
- 2
-
Ensure
btwpackage
- 3
-
btwconfig
- 4
-
System prompt for all conversations with chat
- 5
-
modelargument for most current Claude model
I’ve included the full system prompt below in an easier to read format:
Launch app with the shinypak package:
launch('30_llm-btw')30.2 btw tools
btw comes with a set of pre-defined tools for examining package documentation, environments, directories and files, git and GitHub, our development environment, CRAN packages, R sessions, and general web searches.5
We can use btw to work interactively in RStudio
console by creating a btw-enhanced chat client and passing messages directly.6
chat <- btw_client()We can view the btw enhancements by examining the chat object in the console:
chatbtw has added ‘System and Session Context’ and ‘Tools’ sections to the chat client.
<Chat Anthropic/claude-sonnet-4-5-20250929 turns=1 input=0 output=0 cost=$0.00>
── system ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
# System and Session Context
Please account for the following R session and system settings in all responses.
<system_info>
R_VERSION: R version 4.5.2 (2025-10-31)
OS: macOS Tahoe 26.4
SYSTEM: x86_64, darwin20
UI: RStudio
LANGUAGE: (EN)
LOCALE: en_US.UTF-8
ENCODING: en_US.UTF-8
TIMEZONE: America/Phoenix
DATE: Thursday, April 2, 2026 (2026-04-02)
RSTUDIO: 2026.04.0-daily+381 Globemaster Allium (desktop)
</system_info>
# Tools
You have access to tools that help you interact with the user's R session and workspace. Use these tools when they are helpful and appropriate to complete the user's request. These tools are available to augment your ability to help the user, but you are smart and capable and can answer many things on your own. It is okay to answer the user without relying on these tools.
## Skills
You have access to specialized skills that provide detailed guidance for specific tasks. Skills are loaded on-demand to provide domain-specific expertise without consuming context until needed.
### Using Skills
1. **Check available skills**: Review the `<available_skills>` listing below
2. **Load when relevant**: When you recognize that a task matches a skill's description, call `btw_tool_skill(name)` to load the full skill instructions
3. **Don't reload**: If a skill has already been loaded in this conversation, follow its instructions directly without loading it again
4. **Access resources**: After loading, use file read tools to access references
Skills may include bundled resources:
- **Scripts**: Code bundled with the skill. Scripts are not directly executable by btw; read them for reference or adapt their logic into R code for use with the R code execution tool.
- **References**: Additional documentation to consult as needed
- **Assets**: Templates and files for use in outputs
<available_skills>
<skill>
<name>skill-creator</name>
<description>Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.</description>
<location>/Users/mjfrigaard/Library/Caches/org.R-project.R/R/renv/cache/v5/macos/R-4.5/x86_64-apple-darwin20/btw/1.2.1/9d2abfeb6859f7d079b14da1f8d84ad8/btw/skills/skill-creator/SKILL.md</location>
</skill>
</available_skills>We can see the model will have some additional instructions and methods at it’s disposal (beyond what we’ve provided in the system_prompt argument of ellmer::chat_anthropic()).
30.3 The btw.md context file
As we learned in the ellmer chapter, project prompts should be stored in inst/prompts/. btw also provides the use_btw_md() function, which creates a project ‘context file.’
use_btw_md(scope = "project")ℹ See btw::btw_client for format details
ℹ See btw::btw_tools for available tools
ℹ Call `btw::btw_task_create_btw_md()` to use an LLM to help you initialize the project context.The btw.md file comes with some default content for project context:
“Use
btw.mdto inform the LLM of your preferred code style, to provide domain-specific terminology or definitions, to establish project documentation, goals and constraints, to include reference materials such or technical specifications, or more. Storing this kind of information inbtw.mdmay help you avoid repeating yourself and can be used to maintain coherence across many chat sessions.”
client
The YAML header in our newly created btw.md is where can specify the client (along with the provider and model).
---
client: claude/claude-sonnet-4-5-20250929
---The default values in btw.md will automatically use the latest Claude model from Anthropic. The YAML values above are similar to using ellmer’s chat_* functions.7
tools
The tools section of the YAML header contains a list of the groups from btw_tools(). Each of these groups contains a collection of functions “that allow the chat to interface with your computational environment.”
---
tools:
- docs
- env
- files
- git
- github
- ide
- search
- session
- web
---Additional instructions are also provided on code style:
Follow these important style rules when writing R code:
* Prefer solutions that use {tidyverse}
* Always use `<-` for assignment
* Always use the native base-R pipe `|>` for piped expressionsNow that we have a btw.md file for project context, we’ll start an interactive chat session to help us write our context file.
30.4 Console chat
We can use the btw tools to add more project context to the btw.md by calling btw_task_create_btw_md() and setting the mode to "console".
btw_task_create_btw_md(mode = "console", client = "anthropic")This opens an interactive chat in the console:
30.5 How tool calling works
LLMs can’t execute R code, but if we’ve registered tools (i.e., R functions) with the model, they can be used to help provide additional information.
As mentioned above, btw has a collection of tools at our disposal, so when we start the interactive chat, the model informs us it will be examining the sap package contents for more information.
>>> "Let's get started."The model tells us it’s intentions for the btw.md file:
I'll begin by exploring the project structure to understand
what we're working with.To perform this exploration, the model will use the registered tools from btw.
30.5.1 Registered tools
When registering a tool with an LLM, we include the name of the function, a description of what the function does, and a list of function arguments with their type (boolean, integer, number, etc.). 8
We can view all the tools registered with the LLM using:
chat$get_tools()The output from chat$get_tools() is rather lengthy, but we can use Ctrl/Cmd + F to locate btw_tool_files_list(), which is the first tool called by the LLM.
$btw_tool_files_list
# <ellmer::ToolDef> btw_tool_files_list(path, type, regexp, `_intent`)
# @name: btw_tool_files_list
# @description: List files or directories in the project.
WHEN TO USE:
* Use this tool to discover the file structure of a project.
* When you want to understand the project structure, use `type = "directory"`
to list all directories.
* When you want to find a specific file, use `type = "file"` and `regexp` to
filter files by name or extension.
CAUTION: Do not list all files in a project, instead prefer listing files in
a specific directory with a `regexp` to filter to files of interest.View the entire output from chat$get_tools() in inst/prompts/btw-chat-get-tools.md.
30.5.2 Tool calls
Recall the model was going to start by ‘exploring your project structure’, and we can see this aligns with the’ WHEN TO USE section of btw_tool_files_list().
WHEN TO USE:
Use this tool to discover the file structure of a project.
When you want to understand the project structure, use
type = "directory"to list all directories.When you want to find a specific file, use
type = "file"andregexpto filter files by name or extension.
In the console, the tool call and a preview of it’s results are printed. Below is the first call to btw_tool_files_list():
◯ [tool call] btw_tool_files_list(path = ".",
`_intent` = "List root directory to identify project type and structure")The function includes an _intent argument, where the model, “explain[s] why it called the tool.”9
30.5.3 Tool results
The results are a markdown formatted table of the project contents (file/folder names, their size, and when they were last changed):10
● #> | path | type | size | modification_time |
#> |-------------|-----------|-------|---------------------|
#> | DESCRIPTION | file | 533 | 2026-04-01 08:47:33 |
#> | NAMESPACE | file | 977 | 2026-04-01 08:47:33 |
#> | R | directory | 1.03K | 2026-04-01 08:47:33 |
#> …30.5.4 Model response
The table output is sent to the LLM, which them provides a summary of the contents:
Great! This is clearly an **R package** for building a Shiny application. I’ve created an overview of this tool call in the diagram below.11
The model is equipped with the supplemented system prompt–which informs it of the tools at it’s disposal–and when we begin, it starts by attempting to understand the project structure.
This objective matches the btw registered tool description for the btw_tool_files_list() function, which returns a markdown-formatted table of the project contents. The results from the tool are sent to the LLM, which provides a summary in the console.
The entire PHASE 1: PROJECT EXPLORATION is in the callout box below (expand the callout box to view).
As we can see, the model called btw_tool_files_read() and btw_tool_docs_available_vignettes() to gather context on the project.
The PPHASE 1 COMPLETE - Summary of Findings is what the model learned about the sap package using the btw tools (expand the callout box to view):
After the project summary, the model starts PHASE 2: QUESTIONS FOR CONTEXT, which includes a series of questions from the LLM regarding the context of the package/project (expand the callout box to view).
I included some instructions to the model on building mermaid diagrams for the btw.md file. The PHASE 2: NARRATIVE CONSTRUCTION returned after the questions and answers is below (expand the callout box to view).
30.7 Updated bwd.md file
When Phase 2 is complete, we’re told we have a “comprehensive btw.md file that captures the essential information about [our] Shiny App-Packages project.” The full conversation with the model is stored in inst/prompts/btw-md-conversation.md.
View the complete updated btw.md file on GitHub.
30.8 btw App
Now that we have a project context file, we will use btw to improve our downloadable report. Instead of using the console to chat with the model, we’ll use the btw app:12
btw_app()If we expand the sidebar, we can see the registered tools provided by btw:
By default, the app includes all of the tools available from btw (with the exception of Subagent, Load skill, and Package Tools).13
30.8.1 Agents
If you’re new to LLMs, btw somewhat drops you in the deep end of the pool. As we can see from the App interface, we’re able to add a Subagent to the available tools.
What is the difference between an agent and a subagent?
Generally speaking, an Agent is an LLM chat session that uses tools autonomously to complete tasks. A Subagent is a specialized tool the Agent can delegate subtasks to—like a manager (Agent) assigning work to a specialist (Subagent).
%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"16px"}}}%%
flowchart TD
User(User) -->|prompt| Agt("Agent<br>(LLM + tools)")
Agt -->|delegates subtask| SubAgt("Subagent<br>(tool-shaped agent)")
SubAgt -->|result| Agt
Agt -->|final response| User
The documentation for the subagent is available in the btw documentation,14 but at a high level, it’s instructed to “work efficiently; deliver complete, actionable answers; use available tools effectively; handle problems clearly; and build on previous work.”
30.8.2 Skills
30.8.3 Package Tools
30.8.4 Using the app
The btw_app() functions like any LLM chat interface.
The prompt directory is where
choresstores the helpers used in the addin.↩︎This is explained in What is gander actually doing?↩︎
This description of
btwactually comes from themcptoolsdocumentation.↩︎You can easily open this file with
usethis::edit_r_profile().↩︎Read a complete list of available tools from
btwin the package documentation.↩︎An
ellmer::Chatclient which provides a “sequence of user and assistant Turns sent to a specific Provider.”↩︎For more information on
clientvalues, read the Chat Settings documentation.↩︎We can register tools using
ellmer’screate_tool_def()andtool()functions.↩︎The
_intentargument is “An optional string describing the intent of the tool use. When the tool is used by an LLM, the model will use this argument to explain why it called the tool.”↩︎I’ve cleaned up the formatting on this markdown table so it’s easier to read.↩︎
This image was inspired by the tool calling overview presented in Garrick Aden-Buie’s genAI 2025: Using LLMs in Shiny presentation.↩︎
To run the
btw_app(), make sure you have the latest version ofshinychat.↩︎Subagent uses SOURCE, Load skill uses SOURCE, Package Tools uses SOURCE↩︎
See the
btw-subagent.mdfile for the full set of instructions.↩︎





