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.
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:
hal registers eval_r in every session so the agent can
run R code in your live session, not a subprocess copy:
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.
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:
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:
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.par(mfrow = ...)
grid is one page and is captured whole).png(), pdf()) are not echoed.hal_configure(plot_vision = FALSE).Turn any R function into a tool:
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:
ellmer ToolDef objects are accepted directly:
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:
For custom logic (logging, interactive approval, selective allow),
pass a function. See ?hal_configure for the full
reference.
?hal_register_tool — full tool builder reference?hal_configure — governance settings (policies,
denylist, timeout)