Loom’s AI assistant uses a suite of built-in tools to interact with your codebase. These tools enable file manipulation, code search, command execution, and more.
Read Tools Safe, read-only operations:
file_read
content_search
file_search
directory_list
Write Tools File modification operations:
file_read
Reads a file and returns its contents with line numbers.
Path to the file relative to project root
Line number to start reading from (1-based)
Maximum number of lines to return
Example usage :
# Read entire file
file_read ( file_path: "lib/user.ex" )
# Read lines 50-100
file_read ( file_path: "lib/user.ex" , offset: 50 , limit: 50 )
Output format :
lib/user.ex (150 lines total, showing 50)
50 def authenticate(email, password) do
51 # implementation
52 end
Line numbers are formatted with consistent width for easy reference.
content_search
Searches file contents using regular expressions.
Regex pattern to search for
File pattern to filter (e.g., “.ex”, ” .”)
Directory to search in (defaults to project root)
Example usage :
# Find all TODO comments in Elixir files
content_search (
pattern: "TODO:" ,
include: "*.ex"
)
# Find function definitions
content_search (
pattern: "def \\ s+ \\ w+" ,
path: "lib/core"
)
Output format :
lib/user.ex:42: # TODO: Add validation
lib/auth.ex:18: # TODO: Implement refresh tokens
Results show file path, line number, and matching line content.
file_search
Finds files by name pattern using glob matching.
Glob pattern to match files (e.g., ”**/*.ex”)
Example usage :
# Find all test files
file_search ( pattern: "**/*_test.exs" )
# Find JavaScript files in specific directory
file_search (
pattern: "**/*.js" ,
path: "assets"
)
Output : List of matching file paths sorted by modification time.
directory_list
Lists directory contents.
Directory path to list (relative to project root)
List subdirectories recursively
Example usage :
# List files in lib/
directory_list ( path: "lib" )
# Recursive listing
directory_list (
path: "lib" ,
recursive: true
)
file_write
Creates or overwrites a file with new content.
Path to the file (relative to project root)
Content to write to the file
This tool overwrites existing files. Use with caution.
Example usage :
file_write (
file_path: "lib/new_module.ex" ,
content: """
defmodule MyApp.NewModule do
def hello, do: :world
end
"""
)
file_edit
Performs exact string replacements in a file.
Exact text to find and replace (must match exactly including whitespace)
Replace all occurrences. If false, old_string must appear exactly once.
Example usage :
file_edit (
file_path: "lib/user.ex" ,
old_string: "def authenticate(email, password) do \n # TODO \n end" ,
new_string: "def authenticate(email, password) do \n User.verify_credentials(email, password) \n end"
)
Validation :
If old_string not found: Error with helpful message
If multiple matches and replace_all: false: Error asking for more context
Successful: Returns count of replacements made
Indentation matters! Make sure your old_string matches the exact indentation in the file.
shell
Executes shell commands in the project directory.
Commands run with your user permissions. Be careful with destructive operations.
Example usage :
# Run tests
shell ( command: "mix test" )
# Check git status
shell ( command: "git status" )
# Install dependencies
shell ( command: "mix deps.get" )
Output format :
Exit code: 0
[command stdout/stderr output]
Output is truncated at 10,000 characters. Full output is available in the terminal tab.
git
Executes git commands (convenience wrapper around shell).
Git subcommand and arguments
Example usage :
# View changes
git ( command: "diff" )
# Create commit
git ( command: "commit -m 'Add feature'" )
# Check branch
git ( command: "branch --show-current" )
decision_log
Logs important decisions during code generation.
Description of the decision made
Explanation of why this decision was made
Example usage :
decision_log (
decision: "Use GenServer for user cache" ,
rationale: "Need process isolation and state management for cache invalidation"
)
Decisions are stored and can be queried later.
decision_query
Queries logged decisions.
Search query for decisions
Example usage :
decision_query ( query: "cache" )
Returns all decisions matching the query.
sub_agent
Spawns a read-only search sub-agent for complex searches.
Description of what to search for
Directory to scope the search to
Example usage :
sub_agent (
task: "Find all authentication-related functions" ,
scope: "lib/core"
)
The sub-agent:
Uses a weaker/faster model (claude-haiku-4-5)
Has access only to read tools
Performs multiple search iterations
Returns consolidated findings
Sub-agents are great for exploratory searches when you don’t know exact file locations.
Max iterations : 10
Available to sub-agent :
file_read
file_search
content_search
directory_list
lsp_diagnostics
Retrieves diagnostics from Language Server Protocol clients.
Specific file to get diagnostics for
Example usage :
# Get all diagnostics
lsp_diagnostics ()
# Get diagnostics for specific file
lsp_diagnostics ( file_path: "lib/user.ex" )
Output format :
lib/user.ex:42:5: warning: unused variable "result"
lib/auth.ex:18:12: error: undefined function validate/1
Requires LSP client to be configured in .loom.toml.
All tools are registered in Loom.Tools.Registry:
Loom . Tools . Registry . all ()
# Returns: [Loom.Tools.FileRead, Loom.Tools.FileEdit, ...]
Loom . Tools . Registry . definitions ()
# Returns: [%ReqLLM.Tool{name: "file_read", ...}, ...]
Loom . Tools . Registry . find ( "file_read" )
# Returns: {:ok, Loom.Tools.FileRead}
Loom . Tools . Registry . execute (
"file_read" ,
%{ file_path: "lib/user.ex" },
%{ project_path: "/path/to/project" }
)
Tools are categorized for permission management:
Read tools : Auto-approved by default
Write tools : Require explicit permission
Execute tools : Require explicit permission
Configure in .loom.toml:
[ permissions ]
auto_approve = [
"file_read" ,
"file_search" ,
"content_search" ,
"directory_list"
]
See Permissions for details.
Path Safety
All tools validate paths to prevent directory traversal:
# ✓ Valid
file_read ( file_path: "lib/user.ex" )
# ✗ Rejected (outside project)
file_read ( file_path: "../../etc/passwd" )
Paths must be:
Relative to project root
Within project boundaries
No .. traversal allowed outside project
Error Handling
Tools return structured errors:
{ :error , "File not found: lib/missing.ex" }
{ :error , "old_string not found in file" }
{ :error , "Command timed out after 30000ms" }
The AI assistant receives these errors and can adjust its approach.
You can extend Loom with custom tools using the Jido.Action behavior:
defmodule MyApp . CustomTool do
use Jido . Action ,
name: "my_tool" ,
description: "Does something useful" ,
schema: [
param: [ type: :string , required: true , doc: "Parameter description" ]
]
@impl true
def run (params, context) do
# Implementation
{ :ok , %{ result: "Success" }}
end
end
Register in the tool registry:
# In your config or application startup
Loom . Tools . Registry . register ( MyApp . CustomTool )
Next Steps
Permissions Configure which tools require approval
MCP Connect external tools via Model Context Protocol
LSP Integrate language servers for diagnostics
CLI Use tools from the command line