Plugins / Core
elelem-tools
More tools. Permission prompts.
Adds search, edit, git, sub-agent, and verification tools, three slash commands, and a permissions hook that asks before a tool runs.
gem install elelem-toolselelem-tools adds 9 tools and 3 slash commands on top of
elelem-builtins, and a permissions hook that can
allow, deny, or ask before any tool runs. Use it when you want targeted
edits, code search, sub-agents, and a say in what the model runs.
Install #
gem install elelem-toolsThe elelem command does not load this gem on its own. Add a plugin file
that requires it, in ~/.agents/plugins/ for every project or
.agents/plugins/ for one:
mkdir -p ~/.agents/plugins
echo 'require "elelem/tools"' > ~/.agents/plugins/tools.rbStart elelem chat and run /tools to check that the new tools are
listed.
Configure #
Permissions are read from 3 JSON files, merged in this order, later files overriding earlier ones:
permissions.jsonbundled with the gem~/.agents/permissions.json.agents/permissions.jsonin the current directory
The bundled file:
{
"execute": "ask",
"read": "allow",
"write": "allow"
}Each key is a tool name and each value is a policy:
| Policy | Effect |
|---|---|
allow | The tool runs. |
deny | The tool does not run. The model gets the error Permission denied: NAME. |
ask | elelem prints Allow? [Y/n] > and waits. Typing n (or N) refuses, and the model gets User denied permission: NAME. Any other answer, including an empty line or no, allows the call. |
Rules that matter:
- A tool that is not listed is treated as
ask. With the defaults, that is every tool in this gem, plus any tool from another plugin, such as MCP tools. - Any value other than the three above allows the call. A misspelled
"dney"is an allow. - Keys are canonical tool names. A model calling the
bashalias is checked asexecute. askonly prompts when stdin is a terminal. Inelelem ask, or with piped stdin,askallows without prompting. Onlydenyblocks there.- A file that is not valid JSON is ignored.
- Rules are read once, when the session starts. Restart elelem after editing them.
A setup that trusts searches but never evaluates Ruby:
{
"glob": "allow",
"grep": "allow",
"list": "allow",
"eval": "deny"
}Use #
Tools #
| Tool | What it does | Params |
|---|---|---|
edit | Replaces the first occurrence of old with new in a file, then saves it through the write tool. | path, old, new (all required) |
eval | Evaluates Ruby inside the elelem process and returns the result. The code can reach agent, so the model can add tools at runtime. | ruby (required) |
git | Runs git COMMAND ARGS.... Arguments are shell-escaped. | command (required), args (array of strings) |
glob | Finds files with fd --glob PATTERN PATH. | pattern (required), path (default .) |
grep | Searches contents with rg -n PATTERN PATH, adding -g GLOB when given. | pattern (required), path (default .), glob |
interview | Shows the user a question and returns their answer. | question (required) |
list | Runs ls -la PATH, or ls -laR when recursive is true. Aliases: ls. | path (default .), recursive (boolean) |
task | Runs a prompt in a sub-agent and returns its final reply. | prompt (required) |
verify | Checks a file’s syntax by extension, then runs bin/test or script/test if one is executable. Stops at the first failure. | path (required) |
git, glob, grep, list, and verify run their commands through the
execute tool from elelem-builtins, so output streams to your terminal
and ELELEM_CMD_TIMEOUT applies.
verify uses these syntax checks:
| Extension | Command |
|---|---|
.rb | ruby -c PATH |
.erb | erb -x PATH | ruby -c |
.py | python -m py_compile PATH |
.go | go vet PATH |
.rs | cargo check --quiet |
.ts | npx tsc --noEmit PATH |
.js | node --check PATH |
The write tool in elelem-builtins calls verify after every write when
this gem is loaded. edit saves through write, so edits are verified
too. In a project with bin/test, that means the test suite runs after
each change.
task starts a sub-agent with the same provider, tools, and output, but
an empty conversation and its own system prompt: “Research agent. Search,
analyze, report. Be concise.” It does not see AGENTS.md or the parent
conversation. Use it to keep long searches out of the main context.
Slash commands #
| Command | What it does |
|---|---|
/compact | Asks the model to summarize accomplishments, state, and next steps, then replaces the conversation with that summary. |
/init | Runs a sub-agent instructed to read the project (README, manifests, test scripts, linter configs) and write a short AGENTS.md to the project root. |
/shell | Opens an interactive shell with script. When you exit, the session, with color codes removed, is added to the conversation as a user message. |
/shell is a way to show the model what you did: run the failing
command yourself, exit, and ask about it.
How it works #
Each tool and command is a setup block in its own file under
lib/elelem/tools/. Permissions are one global before hook on the
toolbox, so they apply to every tool call, including calls one tool makes
to another. The hook either returns or raises; raising stops the call, and
the toolbox turns the error into a failure result for the model.
task and /init use Elelem::Tools.fork, which builds a new
Elelem::Agent that shares the parent’s provider, toolbox, and output
with a different system prompt template.
Limitations #
- Nested calls are checked again. With the default rules,
git,glob,grep,list, andverifyask once for themselves and once more for eachexecutecall they make. The second prompt does not show the command. After everywrite, you are asked aboutverifyand then about each command it runs. Set those tools toallowif this is too much. - Denying
executealso disablesgit,glob,grep,list, andverify. - The
Allow?prompt does not print the arguments itself. The tool call line printed immediately before it does, for calls the model makes directly. globandgrepcontain a fallback tofindandgrepwhenfdorrgfails, butexecutereports success for any exit status, so the fallback never runs. Installfdandrg.editdoes not fail whenoldis not found; it rewrites the file unchanged and reports success.newgoes through Ruby’sString#sub, so backslash sequences such as\0in it are expanded.evalruns arbitrary Ruby in the elelem process. It defaults toask; considerdeny.- The model does not see
verifyresults that follow awrite. Only you do. - Like
elelem-builtins, these setup blocks are registered once, when the gem is required, so/reloadremoves them. Restart elelem instead.
Development #
git clone https://git.mokhan.ca/elelem/tools.git
cd tools
bin/setup
bin/test
bin/lintThe specs cover the permission policies.