Agent tools

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:

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:

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:

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:

Register your own tools

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:

hal_register_package_tools("dplyr")
hal_register_tool_specs(winston::timelog_tool_specs())

ellmer ToolDef objects are accepted directly:

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:

hal_configure(permission_policy = "auto-deny")

For custom logic (logging, interactive approval, selective allow), pass a function. See ?hal_configure for the full reference.