Built-in tools

Introduction

Overview

llm.rb ships with twelve ready-made tools that cover the operations a coding or system agent needs most: filesystem work, search, and shell commands. Each tool is a subclass of LLM::Tool with a name, description, and typed parameters, exactly like a tool you would write yourself. Load the whole catalog with require "llm/tools".

How it works

When you want to attach every built-in tool to an agent, require the catalog and pass the full set of subclasses as the tools: option. The runtime registers each tool’s schema and lets the model decide when to call it.

require "llm"
require "llm/tools"

llm   = LLM.deepseek(key: ENV["KEY"])
agent = LLM::Agent.new(llm, tools: LLM::Tool.subclasses)
agent.talk "List the files in this repository"

Why would I use it?

The built-in tools cover the operations a coding or system agent needs most, so you rarely need to write your own. They also handle edge cases correctly: subprocesses get timeouts, interrupts kill the child process, and failed calls return structured errors instead of crashing the conversation.

Notes

The tools that spawn subprocesses use the optional test-cmd.rb gem for process management and interrupt handling. The gem is required only when those tools load. Every tool returns a Hash with an ok: key that tells the model whether the call succeeded.

Filesystem

Overview

The filesystem tools let the model read, write, and edit files, list and create directories, and move around the working tree. The most distinctive is LLM::Tool::EditFile, which replaces an exact snippet and verifies the match count:

LLM::Tool::EditFile.new.call(
  path: "config.yml",
  before: "port: 3000",
  after:  "port: 4000"
)
# => {ok: true, replaced: 1}

How it works

Each filesystem tool takes a path and returns a result Hash. The read-file tool reads a whole file by default and accepts start: and stop: to read a range of lines instead. The edit-file tool counts occurrences of before and raises unless the count matches expected_count, which defaults to 1.

Tool Name Parameters Purpose
LLM::Tool::Pwd pwd none Report the current working directory
LLM::Tool::Ls ls path, glob List files and directories, optionally matching a glob
LLM::Tool::Chdir chdir path Change the current working directory
LLM::Tool::Mkdir mkdir path Create a tree of directories
LLM::Tool::ReadFile read-file path, start, stop Read a file, optionally a range of lines
LLM::Tool::WriteFile write-file path, content Write a string to a file
LLM::Tool::EditFile edit-file path, before, after, expected_count Replace an exact snippet in a file

Why would I use it?

Reading source files to answer questions, writing new files, and editing a snippet in place are the bread and butter of a coding agent. The filesystem tools give the model all of it without you writing a single tool.

Notes

The chdir tool changes the working directory for the whole process, so subsequent file operations see the new directory. The mkdir tool creates parent directories, like mkdir -p. The ls tool raises when the path does not exist.

Overview

The search tools find things without reading the whole tree: rg searches file contents, and which locates an executable on the PATH. When a method name or a term is needed, the model searches for it instead of guessing.

LLM::Tool::Rg.new.call(patterns: ["def talk"], path: "lib")
# => {ok: true, stdout: "lib/llm/context.rb:42:def talk", stderr: ""}

How it works

When you want to search for one or more patterns, call the LLM::Tool::Rg#call method with an Array of patterns, an optional path:, and a timeout: in seconds. When you want to check whether a command is installed, call the LLM::Tool::Which#call method with a name:.

Tool Name Parameters Purpose
LLM::Tool::Rg rg patterns, path, timeout Recursively search for lines matching patterns
LLM::Tool::Which which name Locate an executable on the system PATH

Why would I use it?

Search is how the model finds something without scanning files one by one. Asked where a method is defined, the agent searches for it. Before running a command, it checks with which that the binary exists, and falls back to another approach when it does not.

Notes

The rg tool runs the ripgrep binary, so rg must be installed and on the PATH. It refuses to search from the filesystem root and rejects the pattern . to keep the model from dumping the entire tree in one call. The which tool is pure Ruby: it scans the PATH in order and returns the first directory that contains an executable with the given name. When no match is found it returns {ok: false, path: nil}.

Shell

Overview

The shell tools run real subprocesses: arbitrary commands through shell, Ruby code through ruby, and a fixed set of git actions through git. All three accept a timeout: and kill the child process when the model interrupts the turn.

LLM::Tool::Shell.new.call(
  name: "bundle",
  arguments: ["exec", "rspec", "spec/llm"],
  timeout: 30
)

How it works

When you want to run a command and capture its output, call the LLM::Tool::Shell#call method with a name: and optional arguments:. The git tool accepts an action: from a fixed set, and the ruby tool runs its code in a fresh process.

Tool Name Parameters Purpose
LLM::Tool::Shell shell name, arguments, timeout Run a shell command
LLM::Tool::Git git action, arguments, timeout Run a fixed set of git actions
LLM::Tool::Ruby ruby code, timeout Run a string of Ruby code

Why would I use it?

Running tests, inspecting git history, and executing a snippet of Ruby are things a coding agent needs to do. The shell tools make those actions first-class, and the timeout keeps a hanging command from stalling the conversation.

Notes

LLM::Tool::Shell can be dangerous given a low-quality model. Gate it behind LLM::Agent#confirm or manage the tool loop manually through LLM::Context. On interrupt, the running child process is killed. The ruby tool uses the same Ruby that launched llm.rb. The git tool wraps the actions log, diff, commit, checkout, branch, and show.