---
title: "Agent tools"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Agent tools}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
eval = FALSE
)
```
hal connects to a coding agent with built-in tools for reading code,
editing files, searching your codebase, and running commands. You can
also register your own R functions as tools.
## Built-in tools
Always available — no registration needed:
| Tool | What it does |
|---|---|
| view / Read | Read file contents |
| grep / Grep | Search file contents |
| glob / Glob | Find files by pattern |
| edit / Edit | Edit an existing file |
| create / Write | Create a new file |
| bash / Bash | Run shell commands |
The agent decides when to use them — you just describe what you want:
```r
hal("Find all functions that call the database and list them")
hal("Add error handling to the process_data function")
hal("Run the tests and fix any failures")
```
## eval_r — the R bridge
hal registers `eval_r` in every session so the agent can run R code in
your live session, not a subprocess copy:
```r
df <- mtcars
hal("Which rows in df have mpg above the median?", use_env = TRUE)
```
`use_env = TRUE` injects names + types from your environment into the
prompt; the agent reads actual values via `eval_r`. The default
(`use_env = NULL`) auto-detects when prompt tokens match env objects.
## Plot vision
When `eval_r` code draws a plot, hal captures it as a PNG and attaches
it to the tool result as an image — the model sees the rendered chart,
not just the code that made it:
```r
df <- mtcars
hal("Plot mpg vs wt and tell me what stands out")
#> i hal: plot captured for the model.
```
| Backend | Plot vision |
|---|---|
| vscode | Yes (bundled hal-bridge >= 0.1.4; if the selected model rejects images, the bridge falls back to text automatically) |
| claude | Yes (MCP image content blocks) |
| copilot | No — text-only until the CLI's image forwarding is verified |
Details worth knowing:
- Returned `ggplot`/`lattice` objects are printed to your device first,
so they appear in your plots pane as usual — and a ggplot that fails
to render reports the error to the model instead of failing silently.
- One image per eval: the final page (a `par(mfrow = ...)` grid is one
page and is captured whole).
- Plots written to file devices your code opens itself (`png()`,
`pdf()`) are not echoed.
- Disable globally with `hal_configure(plot_vision = FALSE)`.
## Register your own tools
Turn any R function into a tool:
```r
hal_register_tool(
fun = function(city) paste("Sunny, 72F in", city),
name = "get_weather",
description = "Get current weather for a city",
types = list(city = "string")
)
hal("What's the weather in Austin?")
```
Register tools **before** the first `hal()` / `$chat()` call — they're
passed to the CLI at startup.
Bulk registration:
```r
hal_register_package_tools("dplyr")
hal_register_tool_specs(winston::timelog_tool_specs())
```
ellmer `ToolDef` objects are accepted directly:
```r
chat <- hal_chat()
chat$register_tool(my_ellmer_tool)
```
## Permissions, in brief
Read tools auto-allow. Writes and shell commands trigger a permission
request. The default policy auto-allows everything; switch to
auto-deny for read-only behavior:
```r
hal_configure(permission_policy = "auto-deny")
```
For custom logic (logging, interactive approval, selective allow), pass
a function. See `?hal_configure` for the full reference.
## Next
- `?hal_register_tool` — full tool builder reference
- `?hal_configure` — governance settings (policies, denylist, timeout)