jeevesagent.tools.builtin¶
Built-in tools for filesystem + shell agents.
Four tools that any Agent can register to
gain the canonical “Claude-Code-shaped” capability set:
read_tool()— read a text filewrite_tool()— create or overwrite a fileedit_tool()— find-and-replace inside an existing filebash_tool()— run a shell command, capture output
All four are factory functions: they take a workdir (and
in the case of bash_tool a few safety knobs) and return a
ready-to-register Tool instance. The closure captures the
workdir, so the resulting tool is workdir-scoped — the model can’t
escape it via ../ traversal or absolute paths.
Usage:
from jeevesagent import (
Agent, read_tool, write_tool, edit_tool, bash_tool,
)
agent = Agent(
"You are a research agent.",
model="gpt-4.1-mini",
tools=[
read_tool(workdir="/tmp/agent_work"),
write_tool(workdir="/tmp/agent_work"),
edit_tool(workdir="/tmp/agent_work"),
bash_tool(workdir="/tmp/agent_work", timeout=30.0),
],
)
Or as a bundle:
from jeevesagent import filesystem_tools, bash_tool
agent = Agent(
"...",
model="...",
tools=filesystem_tools("/tmp/agent_work") + [bash_tool("/tmp/agent_work")],
)
Safety¶
Workdir-scoped by default. Read / write / edit refuse paths that resolve outside the workdir.
bash_toolruns commands with the workdir ascwd.Timeout on bash. Default 30 seconds; override via the
timeoutkwarg. Commands that exceed the timeout are killed.Destructive-command denylist.
bash_toolrejects a small set of obviously-dangerous patterns (rm -rf /,sudo,mkfs, etc.) by default. Override via theallow_patterncallable for advanced use.Edit requires unique match.
edit_tool’sold_stringmust appear EXACTLY once in the file (unlessreplace_all=Trueis passed in the call) — forces the model to provide enough context for unambiguous edits, the same approach Claude Code takes.
These will be the foundation of the upcoming Deep Agent architecture (planner + filesystem state + subagent registry).
Exceptions¶
Raised when a tool argument resolves outside its workdir. |
Functions¶
|
Build a |
|
Return the framework's default workdir for built-in tools, |
|
Build a |
|
Return all three filesystem tools (read + write + edit) |
|
Build a |
|
Build a |
Module Contents¶
- exception jeevesagent.tools.builtin.PathEscapeError[source]¶
Bases:
ValueErrorRaised when a tool argument resolves outside its workdir.
Initialize self. See help(type(self)) for accurate signature.
- jeevesagent.tools.builtin.bash_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'bash', timeout: float = 30.0, allow_pattern: collections.abc.Callable[[str], bool] | None = None, extra_env: dict[str, str] | None = None) jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat runs a shell command with the workdir as the current working directory.Default safety:
Commands matching the built-in destructive patterns (
rm -rf /,sudo,mkfs, fork bombs, …) are rejected before being executed.Commands run with a default
timeoutof 30 seconds; the subprocess is killed on timeout.The shell is invoked via
/bin/sh -c <command>, so pipelines + redirections work the way you’d expect.
Knobs:
allow_pattern— a callable that takes the command string and returns True if the command should run. When provided, it OVERRIDES the default deny list — you take full responsibility.extra_env— extra environment variables merged into the subprocess env.timeout— seconds before the command is killed.
workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools).
- jeevesagent.tools.builtin.default_workdir() pathlib.Path[source]¶
Return the framework’s default workdir for built-in tools, creating it lazily on first call.
The directory is a fresh tempdir under
$TMPDIR/jeeves_agent_*, created once per process. All built-in tool factories share it when called without an explicitworkdirargument, so an Agent that registersread_tool()andwrite_tool()(no args) sees the same place.The directory is NOT auto-cleaned at process exit — leave that to the OS’s tempdir cleanup so debug data survives a crash.
- jeevesagent.tools.builtin.edit_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'edit') jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat does find-and-replace inside an existing file underworkdir.- The tool’s signature seen by the model:
- ``edit(path: str, old_string: str, new_string: str,
replace_all: bool = False)``
Behaviour matches Claude Code’s Edit tool:
old_stringmust be EXACTLY present in the file. Mismatch (whitespace, indentation, line breaks) → error.old_stringmust appear EXACTLY once in the file unlessreplace_all=Trueis passed — forces the model to give enough surrounding context for unambiguous matches.new_stringreplacesold_string(or every occurrence ifreplace_all=True).
workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools).
- jeevesagent.tools.builtin.filesystem_tools(workdir: pathlib.Path | str | None = None) list[jeevesagent.tools.registry.Tool][source]¶
Return all three filesystem tools (read + write + edit) bound to a single workdir.
bash_toolis excluded — pair them only when you want shell access too.workdiris optional;Noneuses the framework’s default tempdir (shared with bash_tool() called the same way).
- jeevesagent.tools.builtin.read_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'read', line_limit: int = _DEFAULT_READ_LINE_LIMIT) jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat reads a text file underworkdir.- The tool’s signature seen by the model:
read(path: str, offset: int = 0, limit: int | None = None)
Returns the file’s text with line numbers prefixed (one line per output line), in the same format Claude Code’s Read tool uses — that lets the
edittool work without ambiguity later. Long files are truncated toline_limitlines per call; passoffset/limitto read further chunks.Errors (file-not-found, path-escape) are returned as a string starting with
"ERROR: "rather than raising — the model sees them as a tool result and can adjust.workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools called without a workdir).
- jeevesagent.tools.builtin.write_tool(workdir: pathlib.Path | str | None = None, *, name: str = 'write', create_parents: bool = True) jeevesagent.tools.registry.Tool[source]¶
Build a
Toolthat writes / overwrites a text file underworkdir.- The tool’s signature seen by the model:
write(path: str, content: str)
Overwrites existing files. With
create_parents=True(the default), missing parent directories are created automatically.Returns a confirmation string with the byte count, or an
"ERROR: "-prefixed message on failure.workdiris optional;Noneuses the framework’s default tempdir (shared with the other built-in tools).