defmodule Omni.Tools do @moduledoc """ ![License](https://img.shields.io/github/license/aaronrussell/omni_tools?color=informational) **Ready-to-use tools for Omni-powered agents** — filesystem, shell, REPL, and web fetch. Built on [Omni](https://github.com/aaronrussell/omni). Omni Tools is a small set of reference `Omni.Tool` implementations that ship ready-to-use and serve as worked examples of how to build tools for LLMs. Each tool validates configuration at construction time and works with any Omni provider that supports tool use. ## Installation Add Omni Tools to your dependencies: {:omni_tools, "~> 0.1"} Omni Tools depends on `omni`, which provides the LLM API layer. Configure your provider API keys as described in the [Omni README](https://github.com/aaronrussell/omni#setup). ## The tools | Module | What it does | | --- | --- | | `Omni.Tools.FileSystem` | CRUD over a scoped directory with read-only and flat modes | | `Omni.Tools.Bash` | Executes shell commands with timeout and output capture | | `Omni.Tools.Repl` | Evaluates Elixir code in a sandboxed peer node | | `Omni.Tools.WebFetch` | Fetches URLs, simplifies content for LLM consumption | Each tool is created with `new/1` and returns an `%Omni.Tool{}` struct ready to pass into an Omni context: fs = Omni.Tools.FileSystem.new(base_dir: "/data/workspace") bash = Omni.Tools.Bash.new(dir: "/app") repl = Omni.Tools.Repl.new() web = Omni.Tools.WebFetch.new() ### FileSystem Read, write, patch, list, and delete files within a scoped directory. Configuration controls read-only access and whether subdirectories are allowed: # Full access with nested paths Omni.Tools.FileSystem.new(base_dir: "/data/workspace") # Read-only, flat (no subdirectories) Omni.Tools.FileSystem.new(base_dir: "/data/docs", read_only: true, nested: false) See `Omni.Tools.FileSystem` for all options. The underlying operations are also available standalone via `Omni.Tools.FileSystem.FS`. ### Bash Executes shell commands in a configured working directory with environment variables, timeout, and output truncation: Omni.Tools.Bash.new(dir: "/app", timeout: 60_000, env: [{"NODE_ENV", "test"}]) See `Omni.Tools.Bash` for all options. The command runner is also available standalone via `Omni.Tools.Bash.Runner`. ### Repl Evaluates Elixir code in a fresh peer node — clean slate per execution, with IO capture and configurable extensions: alias Omni.Tools.Repl.Extension Omni.Tools.Repl.new( extensions: [ {MyApp.ReplExtension, api_key: "sk-..."}, Extension.new(description: "Req and Jason are available.") ] ) See `Omni.Tools.Repl` for all options and `Omni.Tools.Repl.Extension` for the extension API. ### WebFetch Fetches one or more URLs and extracts content appropriate for LLM consumption — HTML to Markdown, JSON to pretty-printed, plain text passthrough: Omni.Tools.WebFetch.new(max_output: 30_000, timeout: 10_000) Three strategies are always active — GitHub (blob URLs to raw content), Reddit (JSON API to formatted Markdown), and a default catch-all. Custom strategies are prepended and matched first, so they can override or extend the built-ins: Omni.Tools.WebFetch.new(strategies: [{MyApp.WikiStrategy, []}]) See `Omni.Tools.WebFetch` for all options and `Omni.Tools.WebFetch.Strategy` for the strategy behaviour. ## Using tools in a conversation Pass tools to `Omni.generate_text/3` or `Omni.stream_text/3` — the tool loop executes uses automatically and feeds results back to the model: fs = Omni.Tools.FileSystem.new(base_dir: "/data/workspace") bash = Omni.Tools.Bash.new(dir: "/data/workspace") context = Omni.context( system: "You are a coding assistant with access to a project workspace.", messages: [Omni.message("List all Elixir files and count the lines in each.")], tools: [fs, bash] ) {:ok, response} = Omni.generate_text({:anthropic, "claude-sonnet-4-6"}, context) Tools work the same way with `Omni.Agent` — pass them as start options or set them in your agent's `init/1` callback: {:ok, agent} = Omni.Agent.start_link( model: {:anthropic, "claude-sonnet-4-6"}, tools: [fs, bash], subscribe: true ) ## Configuration Every tool supports a three-layer configuration merge: module defaults → application config → explicit opts Explicit opts to `new/1` always win. Application config provides per-environment defaults. Module defaults are sensible out of the box. # config/runtime.exs config :omni_tools, Omni.Tools.Bash, timeout: 60_000, max_output: 100_000 # At call site — :dir is required, :timeout overrides the app config Omni.Tools.Bash.new(dir: "/app", timeout: 10_000) No application config is required — every tool works with zero config and sensible defaults. ## Writing custom tools These tools implement the `Omni.Tool` behaviour from the `omni` package. To build your own, see `Omni.Tool` — or read any of the tools in this package as worked examples. """ end