Tools¶
Registry and base types¶
_base
¶
Base types and registry for the tools package.
SearchResult
dataclass
¶
A single hit returned by a tool.
line_number
class-attribute
instance-attribute
¶
Line number of the match inside the file (content searches only).
snippet
class-attribute
instance-attribute
¶
Matching text excerpt (content searches only).
score
class-attribute
instance-attribute
¶
Optional relevance score (higher = more relevant).
ToolResult
dataclass
¶
Aggregated output from a single tool invocation.
truncated
class-attribute
instance-attribute
¶
True when the result list was clipped to max_results.
to_text
¶
Format the result as a human-readable string for the AI/UI.
Source code in src/tools/_base.py
Tool
¶
Abstract base for all built-in tools.
schema_text
¶
Return a compact description for inclusion in the system prompt.
Source code in src/tools/_base.py
ToolPermissions
¶
Encapsulates the allow/disallow/approval rules for tool dispatch.
Resolution order (highest to lowest precedence):
1. disallowed — tool is always blocked, no override.
2. allowed — if non-empty, only tools in this set may run.
3. requires_approval — tool may run, but only after the user confirms.
Parameters¶
allowed:
Whitelist of tool names the AI may call. An empty set means all
registered tools are allowed (subject to the disallowed list).
disallowed:
Blacklist of tool names that are completely blocked. Takes precedence
over allowed.
requires_approval:
Tool names that need explicit user confirmation before executing.
The approval callback receives the tool name and the argument dict and
must return True to proceed.
approve_callback:
Called as approve_callback(tool_name, args) -> bool when a tool
in requires_approval is invoked. Defaults to a terminal prompt.
Source code in src/tools/_base.py
is_allowed
¶
Return True if name passes the whitelist check.
If no whitelist is configured (empty set) every tool passes.
check
¶
Enforce permissions for tool_name with args.
Returns¶
ToolResult | None
A ToolResult with an error message if the tool is blocked or
the user declines. None means the tool may proceed.
Source code in src/tools/_base.py
visible_names
¶
Return the subset of all_names that are advertised to the AI.
Disallowed tools and tools outside the whitelist are hidden from the system prompt so the AI doesn't even try to call them.
Source code in src/tools/_base.py
ToolDescriptor
dataclass
¶
Metadata + factory for a single tool — the instance is created lazily.
The descriptor holds everything the :class:ToolRegistry needs to:
- Advertise the tool to the AI (name, description, schema) without
instantiating it.
- Create the tool instance on first use via factory.
- Release the instance after use when unload_after_use is True.
Lifecycle¶
.. code-block:: text
UNLOADED ──(get_instance)──► LOADED ──(release)──► UNLOADED
│
run() called here
The transition back to UNLOADED is triggered automatically by
:meth:ToolRegistry.dispatch when unload_after_use is True, or
manually via :meth:ToolRegistry.unload / :meth:ToolRegistry.unload_all.
get_instance
¶
Return the live tool instance, creating it on first call.
release
¶
Release the tool instance so it can be garbage-collected.
schema_text
¶
Return a compact one-line description for the AI system prompt.
Reads only from the descriptor fields — never touches the instance.
Source code in src/tools/_base.py
ToolRegistry
¶
Registry of all available tools.
Tools are stored as :class:ToolDescriptor objects and instantiated
lazily — only when dispatch() actually calls them. After each
call the instance can optionally be released (unloaded) so it is
garbage-collected, keeping memory usage at a minimum.
Key properties¶
- Zero startup cost: registering tools is free; nothing is imported
or constructed until the first
dispatch()for that tool. - Selective unloading: set
unload_after_use=Trueper tool (or globally via config) to release instances between calls. - System-prompt generation uses descriptor metadata only — no tool is ever instantiated just to build the prompt.
- Permission enforcement via :class:
ToolPermissionshappens before the tool instance is even loaded, so blocked tools cost nothing.
Source code in src/tools/_base.py
register_lazy
¶
Register a tool using a factory function (primary API).
The factory is only called on the first dispatch() for this tool.
Subsequent calls reuse the cached instance unless unload_after_use
is True, in which case the instance is released after every call.
Parameters¶
name:
Tool name used in [TOOL: name {...}] markers.
description:
One-line description shown to the AI in the system prompt.
schema:
JSON Schema dict for the tool's parameters.
factory:
Zero-argument callable that returns a fresh Tool instance.
unload_after_use:
Override the registry's default unload policy for this tool.
None → inherit the registry default.
Source code in src/tools/_base.py
register
¶
Register an already-constructed tool instance (convenience API).
The instance is wrapped in a descriptor so it participates in the
same lazy-load / unload lifecycle. The instance is considered
pre-loaded (is_loaded == True) immediately after registration.
Source code in src/tools/_base.py
get
¶
Return the tool instance for name, loading it if needed.
get_descriptor
¶
names
¶
loaded_names
¶
unload
¶
Release the instance for tool name.
Returns True if the tool was loaded and is now released,
False if the tool is unknown or was already unloaded.
Source code in src/tools/_base.py
unload_all
¶
Release all currently loaded tool instances.
Returns the list of tool names that were unloaded.
Source code in src/tools/_base.py
system_prompt_section
¶
Return the block injected into the AI system prompt.
Only lists tools the AI is permitted to call. No tool instance is created — reads descriptor metadata only.
Source code in src/tools/_base.py
dispatch
¶
Parse and execute a [TOOL: name {...}] call from the AI.
Lifecycle per call¶
- Parse call_text — return
Noneif no marker found. - Permission check (allow/disallow/approval) — return error result if blocked. No instance is created for blocked tools.
- Lazily load the tool instance via
descriptor.get_instance(). - Run
tool.run(args)and collect the result. - If
descriptor.unload_after_useisTrue, release the instance immediately so memory is reclaimed.
Parameters¶
call_text:
Text containing a [TOOL: name {...}] marker.
Returns¶
ToolResult | None
Result of the tool call, or None if no marker was found.
Source code in src/tools/_base.py
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 | |
Available tools¶
find_files
¶
Find files tool — search for files by name or glob pattern.
FindFilesTool
¶
Bases: Tool
Search for files by name or glob pattern.
Backend priority¶
plocate– fastest; uses an mmap-based index updated by a daemon.locate– compatible but slightly slower index format.find– always available; slower because it walks the filesystem.
The tool automatically chooses the fastest backend present on the system.
Source code in src/tools/find_files.py
search_in_files
¶
Search in files tool — find text patterns inside files.
SearchInFilesTool
¶
Bases: Tool
Search for text inside files.
Backend priority¶
rg(ripgrep) – fastest; respects.gitignore; written in Rust.grep -r– always available; slower on large trees.
Source code in src/tools/search_in_files.py
web_fetch
¶
Web fetch tool — retrieve text content from a URL.
WebFetchTool
¶
WebFetchTool(max_response_chars=8000, allowed_content_types=None, domain_allowlist=None, domain_blocklist=None, max_redirects=5, connect_timeout=5.0, read_timeout=15.0)
Bases: Tool
Fetch a URL from the internet and return its text content.
Security model¶
- TLS always verified —
verify=Trueis non-negotiable; there is no way to disable certificate checking through config. - SSRF protection — requests to loopback (
127.x,::1,localhost) and link-local addresses are blocked so the AI cannot use this tool to probe internal services. - Domain filtering — optional allow-list and block-list. If
domain_allowlistis non-empty, only those domains are reachable. - Content-type whitelist — only
text/*and safeapplication/*responses are returned; binary files and executables are rejected. - Size cap — responses larger than
max_response_charsare truncated; the raw body is never buffered beyond that limit. - No session / cookie persistence — each request uses a fresh
requests.Sessiondestroyed immediately after the call. - Connection: close — the TCP socket is released right after the response is consumed.
This tool is disabled by default. Enable it via config.yaml:
.. code-block:: yaml
tools: web_fetch: enabled: true
And ensure it stays in requires_approval so every fetch is confirmed
by the user before it happens.
Source code in src/tools/web_fetch.py
web_search
¶
Web search tool — open the user's browser with a search query.
WebSearchTool
¶
Bases: Tool
Open the user's default browser to search the web.
Privacy model¶
This tool never makes any HTTP request itself. It builds a search URL
and hands it to xdg-open, which opens the URL in whatever browser the
user has set as their default. The assistant has no visibility into what
the browser does after that point.
The tool is useful for: - Letting the AI suggest search queries based on conversation context. - Giving the user a one-click way to look something up without the assistant having to access the internet itself.
Source code in src/tools/web_search.py
man_reader
¶
Man page reader tool — read Linux manual pages.
ManPageTool
¶
Bases: Tool
Read the Linux manual page for a command.
The tool runs man <command> locally with plain-text output (no pager,
no ANSI codes), optionally extracts specific sections, and truncates the
result to keep it within the model's context window.
This lets the AI look up the exact flags, syntax, and examples for any installed command before crafting a shell instruction — ensuring accuracy especially across distros where option names differ.
The tool is entirely offline: it reads man pages from the local system and never contacts any network resource.
Parameters¶
max_chars:
Maximum characters returned per call. Defaults to 8 000, which is
enough for SYNOPSIS + OPTIONS of most commands.
default_sections:
Section names to extract when the caller doesn't specify any. Use
[] to return the full man page (up to max_chars).
Source code in src/tools/man_reader.py
system_control
¶
System control tool — control audio, bluetooth, wifi, brightness, etc.
SystemControlTool
¶
Bases: Tool
Control system resources: audio, microphone, Bluetooth, Wi-Fi, power mode, and display brightness.
Backend detection¶
For each resource the tool tries available backends in order:
- Audio / Microphone: WirePlumber (
wpctl) → PulseAudio (pactl) → ALSA (amixer) - Bluetooth:
bluetoothctl→rfkill - Wi-Fi:
nmcli(NetworkManager) →rfkill - Power mode:
powerprofilesctl(power-profiles-daemon) →tuned-adm(TuneD) - Brightness:
brightnessctl→xbacklight→light
The first backend whose executable is on $PATH is used. All
subprocess imports and shutil.which checks are deferred to the
first run() call so loading this module is free.
Safety¶
This tool is in requires_approval by default. The user sees exactly
which resource and action the AI is requesting before anything changes.
Read-only get queries skip approval.
system_info
¶
System information tool — answer questions about the user's system.
Queries answered¶
time Current local date and time.
uptime How long the system has been running.
battery Battery percentage, charging status, and time remaining.
battery_health Battery health (design capacity vs actual capacity).
gpu Graphics card model(s).
cpu CPU model, core/thread count, current frequency.
memory RAM total, used, available.
disk Disk usage for mounted filesystems.
os Distro name, version, kernel, package manager.
network Active network interfaces and their IP addresses.
all A concise summary covering every topic above.
Data sources (in priority order)¶
/proc/*and/sys/*— direct kernel interfaces, fastest and most accurate. No subprocess, no extra packages.- Command-line tools (
upower,lspci,lscpu,ip) — used when the proc/sys files don't give enough detail.
All subprocess and heavy-stdlib imports are deferred inside run() so
importing this module is free.
SystemInfoTool
¶
Answer questions about the user's system hardware and software.
Reads from /proc and /sys wherever possible (no subprocess
overhead) and falls back to CLI tools for richer detail.
app
¶
Application tool — open, search, and install apps.
Actions¶
open
Launch an installed application by name. Tries (in order):
gtk-launch → desktop file Exec= field → plain binary exec.
search
Search for packages matching a query using the distro's package manager
(apt, dnf, pacman, …). Also searches local .desktop files for
installed applications.
install
Open the user's terminal pre-filled with the correct install command for
this distro (e.g. sudo apt install <pkg>). The user must press Enter
to confirm — the assistant never runs installs itself.
All subprocess imports are deferred to the first run() call (lazy loading).
AppTool
¶
Open, search, and install applications.