Write a tool plugin
Add a tool the model can call, in one Ruby file, then package it as a gem.
At the end of this guide the model will have a new tool, wc, that counts
the lines, words, and bytes in a file. You will write it as one Ruby file
in your project, reload it without restarting, and then package it as a gem
named elelem-wc that any project can use.
Before you start #
- elelem and
elelem-builtinsinstalled, and a provider that works. See your first session. - A project directory to work in.
1. Create the plugin file #
elelem loads every *.rb file in two directories when it starts:
~/.agents/plugins/ for plugins you want everywhere, and .agents/plugins/
in the current directory for one project. A plugin is any file that calls
Elelem.configure.
mkdir -p .agents/pluginsCreate .agents/plugins/wc.rb:
Elelem.configure do |config|
config.setup(:wc) do |agent|
agent.toolbox.add("wc",
description: "Count lines, words, and bytes in a file",
params: { path: { type: "string", description: "Path to the file" } },
required: ["path"]) do |args|
path = File.expand_path(args["path"])
next { error: "not found: #{args["path"]}" } unless File.file?(path)
text = File.read(path)
{ path: args["path"], lines: text.lines.count, words: text.split.count, bytes: text.bytesize }
end
end
endWhat each part does:
config.setup(:wc)registers a setup block. elelem runs it once per session and passes the session asagent. The name identifies the block; registering the same name again replaces it.agent.toolbox.addadds the tool. Thedescriptionis what the model reads when it decides whether to call the tool, so write it for the model.paramsis the JSON Schemapropertiesof the tool’s input, andrequiredlists the keys the model must send. elelem validates every call against this schema before your code runs. A call withoutpathnever reaches the block; the model gets backobject at root is missing required properties: path.- The block receives the arguments as a hash with string keys, as
parsed from the model’s JSON. Use
args["path"], notargs[:path]. - The block returns a hash. elelem adds
ok: trueand sends it to the model as JSON. Return a hash with an:errorkey to report a failure; elelem marks itok: false. Usenext, notreturn, to leave the block early.
If the block raises, elelem catches it and sends the model the error message, so one broken tool does not end the session.
2. Check that elelem sees it #
elelem chat -p ollama> /tools wc## wc
Count lines, words, and bytes in a file
### Parameters
- `path` (string, required) - Path to the fileAsk for it:
> how long is the README? use the wc tool.+ wc({"path" => "README.md"})If the file has a syntax error, elelem prints
elelem: failed to load plugin /full/path/to/.agents/plugins/wc.rb: ...
and carries on without it.
3. Show the result with an after hook #
The model sees the tool’s result. You only see the call line. An after
hook runs after the tool and can print something for you. Add this inside
the setup block, after toolbox.add:
agent.toolbox.after("wc") do |_args, result|
next if result[:error]
agent.output.say " #{result[:lines]} lines, #{result[:words]} words, #{result[:bytes]} bytes"
endThe hook gets the arguments and the hash your tool returned. It runs only
when the tool ran: not when validation failed, and not when the block
raised. agent.output.say prints a line in the terminal. It does not
change what the model sees.
There is also agent.toolbox.before("wc") { |args| ... }, which runs
before the tool. Leave out the tool name to hook every tool:
agent.toolbox.after { |args, result, tool_name:| ... }.
4. Reload without restarting #
> /reload/reload clears the registered setup blocks, re-reads every file in
~/.agents/plugins/ and .agents/plugins/, and runs their setup blocks
again. Ask for a word count and the new line appears under the call.
Note: In elelem 0.13.0,
/reloadalso drops the tools and commands that come from gems, includingread,write,execute, and/reloaditself, because gems are not re-read. Your plugin’s tools stay. Use/reloadto iterate on a plugin, then restart elelem to get everything back.
5. Package it as a gem #
A plugin file works for one machine. To share it, make a gem named
elelem-wc, with the plugin at lib/elelem/wc.rb.
mkdir -p elelem-wc/lib/elelemCreate elelem-wc/lib/elelem/wc.rb: require "elelem", then the same
Elelem.configure block as before.
require "elelem"
Elelem.configure do |config|
config.setup(:wc) do |agent|
# the toolbox.add and toolbox.after calls from steps 1 and 3
end
endCreate elelem-wc/elelem-wc.gemspec:
Gem::Specification.new do |spec|
spec.name = "elelem-wc"
spec.version = "0.1.0"
spec.authors = ["Your Name"]
spec.summary = "A wc tool for elelem."
spec.license = "MIT"
spec.required_ruby_version = ">= 4.0.0"
spec.files = ["lib/elelem/wc.rb"]
spec.require_paths = ["lib"]
spec.add_dependency "elelem", "~> 0.13"
endBuild and install it:
cd elelem-wc
gem build elelem-wc.gemspec
gem install ./elelem-wc-0.1.0.gemgem push elelem-wc-0.1.0.gem publishes it to RubyGems.
6. Load the gem #
elelem does not load tool gems on its own. The only gem it requires at
startup is elelem-builtins, and provider gems load when you pick them
with -p. Load yours from a plugin file. Replace the project file with a
one-line loader in your home directory, so every project gets the tool:
rm .agents/plugins/wc.rb
mkdir -p ~/.agents/plugins
echo 'require "elelem/wc"' > ~/.agents/plugins/wc.rbStart elelem and run /tools. wc is in the list. elelem-tools,
elelem-mcp, and elelem-skills load the same way.
What you built #
A tool with a validated schema, an after hook that reports to you, and
the same code packaged as elelem-wc, loaded from one line in
~/.agents/plugins/.
Next steps #
- Write a provider plugin to connect a model elelem does not support yet.
- Read the elelem-builtins source for more examples: each tool and command there is one short file.
- The plugin API reference lists every method a setup block can call.