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 our interactions with the model, allowing for a more intuitive and efficient experience that resembles a browser interface (like ChatGPT).
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.
- When writing R code, use base R functions, but follow the tidyverse style guide.
- Avoid using `for` loops and prefer functional programming patterns like `apply()` or `purrr`.
- When creating graphs/plots, use `ggplot2`.
- If writing R Shiny code, use `bslib` for all layout functions (unless explicitly instructed otherwise).
- If writing Python Shiny code, use shiny core (not express) to build apps and include explanations in comments. ",
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
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 the Positron
(or 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.
# 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.1 (2025-06-13)
OS: macOS Tahoe 26.0.1
SYSTEM: x86_64, darwin20
UI: Positron (a VS Code equivalent)
LANGUAGE: (EN)
LOCALE: en_US.UTF-8
ENCODING: en_US.UTF-8
TIMEZONE: America/Phoenix
DATE: Tuesday, October 28, 2025 (2025-10-28)
</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.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 Project context
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")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
---Now 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: btw.md context file
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 help you create a comprehensive project summary. Let me start by
exploring your 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_files(), which is the first tool called by the LLM.
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.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’ @description for btw_tool_files_list_files().
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_files():
◯ [tool call] btw_tool_files_list_files(path = ".", `_intent` = "Get initial
overview of project 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 | 736 | 2025-10-20 14:04:24 |
#> | NAMESPACE | file | 932 | 2025-10-22 08:01:23 |
#> | R | directory | 1K | 2025-10-22 08:01:23 |
#> …30.5.4 Model response
The table output is sent to the LLM, which them provides a summary of the contents:
Perfect! I can see this is an **R package** with a Shiny application. The structure shows:
- `DESCRIPTION` and `NAMESPACE` files (R package essentials)
- Extensive `R/` directory with many Shiny modules (`mod_*.R` files)
- `app.R` file (Shiny app entry point)
- `data/` directory with datasets
- `tests/testthat/` (comprehensive testing)
- `vignettes/` (package documentation)
- `renv.lock` (dependency management)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_files() 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_list_files() and btw_tool_files_read_text_file() to gather context on the project. The PHASE 1: SUMMARY 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: Narrative Construction, 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: SUMMARY returned after the questions and answers is below (expand the callout box to view).
30.5.5 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.6 App chat: downloadable report
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 Code Search).13
We can also specify specific tools, or categories of tools. Below are some groups of tools we might use exploring an app-package:
btw_app(tools = c("docs", "env", "files", "ide", "session"))30.6.1 Using the app
The btw_app() functions like any LLM chat interface.
What code could I add to the Counts section of
inst/rmd/report_template.Rmdto ensure all the graphs and tables from this tab are returned in the downloadable report?Only return R code (no the R Markdown syntax).
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.↩︎Code Search uses the
btw_tool_files_code_search()function.↩︎



