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-tools

elelem-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-tools

The 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.rb

Start 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:

  1. permissions.json bundled with the gem
  2. ~/.agents/permissions.json
  3. .agents/permissions.json in the current directory

The bundled file:

{
  "execute": "ask",
  "read": "allow",
  "write": "allow"
}

Each key is a tool name and each value is a policy:

PolicyEffect
allowThe tool runs.
denyThe tool does not run. The model gets the error Permission denied: NAME.
askelelem 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 bash alias is checked as execute.
  • ask only prompts when stdin is a terminal. In elelem ask, or with piped stdin, ask allows without prompting. Only deny blocks 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 #

ToolWhat it doesParams
editReplaces the first occurrence of old with new in a file, then saves it through the write tool.path, old, new (all required)
evalEvaluates Ruby inside the elelem process and returns the result. The code can reach agent, so the model can add tools at runtime.ruby (required)
gitRuns git COMMAND ARGS.... Arguments are shell-escaped.command (required), args (array of strings)
globFinds files with fd --glob PATTERN PATH.pattern (required), path (default .)
grepSearches contents with rg -n PATTERN PATH, adding -g GLOB when given.pattern (required), path (default .), glob
interviewShows the user a question and returns their answer.question (required)
listRuns ls -la PATH, or ls -laR when recursive is true. Aliases: ls.path (default .), recursive (boolean)
taskRuns a prompt in a sub-agent and returns its final reply.prompt (required)
verifyChecks 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:

ExtensionCommand
.rbruby -c PATH
.erberb -x PATH | ruby -c
.pypython -m py_compile PATH
.gogo vet PATH
.rscargo check --quiet
.tsnpx tsc --noEmit PATH
.jsnode --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 #

CommandWhat it does
/compactAsks the model to summarize accomplishments, state, and next steps, then replaces the conversation with that summary.
/initRuns a sub-agent instructed to read the project (README, manifests, test scripts, linter configs) and write a short AGENTS.md to the project root.
/shellOpens 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, and verify ask once for themselves and once more for each execute call they make. The second prompt does not show the command. After every write, you are asked about verify and then about each command it runs. Set those tools to allow if this is too much.
  • Denying execute also disables git, glob, grep, list, and verify.
  • The Allow? prompt does not print the arguments itself. The tool call line printed immediately before it does, for calls the model makes directly.
  • glob and grep contain a fallback to find and grep when fd or rg fails, but execute reports success for any exit status, so the fallback never runs. Install fd and rg.
  • edit does not fail when old is not found; it rewrites the file unchanged and reports success. new goes through Ruby’s String#sub, so backslash sequences such as \0 in it are expanded.
  • eval runs arbitrary Ruby in the elelem process. It defaults to ask; consider deny.
  • The model does not see verify results that follow a write. Only you do.
  • Like elelem-builtins, these setup blocks are registered once, when the gem is required, so /reload removes them. Restart elelem instead.

Development #

git clone https://git.mokhan.ca/elelem/tools.git
cd tools
bin/setup
bin/test
bin/lint

The specs cover the permission policies.