Reference
Plugin API
Every public method a plugin author touches, with signatures, defaults, and short examples.
Signatures are copied from elelem 0.13.0. Arguments shown with = or : and a value are optional, with that default. For the ideas behind these methods, read Plugins and Tools and commands first.
Elelem #
Elelem.configure #
Elelem.configure { |config| ... }Yields the registration object, described below. Call it at the top level of a plugin file or gem.
Elelem.start #
Elelem.start(provider: "stub", toolbox: Toolbox.new, provider_options: {}, template: nil, system_prompt: nil)Builds a session and runs the REPL until input ends. This is what elelem chat calls.
Elelem.ask #
Elelem.ask(prompt, provider: "stub", toolbox: Toolbox.new, output: NullOutput.new, provider_options: {},
template: nil, system_prompt: nil)Builds a session with an input that never prompts, runs one turn, and returns the reply as a string.
require "elelem"
require "elelem/builtins"
reply = Elelem.ask("Summarize README.md in one line.", provider: "anthropic")Note:
NullOutputonly silencessay. Thinking text and anything a tool sends tooutput.print, such as the output ofexecute, still go to stdout. Passoutput: Elelem::Output.new(stream: $stderr)to move them.
Elelem.build #
Elelem.build(provider: "stub", toolbox: Toolbox.new, input: nil, output: nil, provider_options: {},
template: nil, system_prompt: nil)Returns a Session with setup blocks applied, without running anything. nil input or output means the registered factory, or the core Input or Output.
| Keyword | Meaning |
|---|---|
provider: | Provider name, resolved as in Providers. |
toolbox: | Starting toolbox. Setup blocks add to it. |
provider_options: | Keyword arguments for the provider factory, such as { model: "..." }. |
template: | ERB template replacing the default system prompt. |
system_prompt: | Role text placed before the rendered template. Not rendered as ERB. |
Elelem.logger #
Elelem.logger # => LoggerA Logger on stderr. Its level comes from ELELEM_LOG_LEVEL (debug, info, warn, error, fatal), default warn.
Elelem::VERSION #
The core version string, "0.13.0".
Registration #
The object Elelem.configure yields. Every registering method returns the registration, so calls chain.
config.provider(name) { |model: nil, **| provider } # name: String or Symbol
config.setup(name) { |session| ... } # name: String or Symbol
config.output { output }
config.input { |commands:| input }| Method | Notes |
|---|---|
provider(name, &factory) | The factory runs on every build and receives the provider options. Same name replaces. |
setup(name, &block) | Runs against each new session, in registration order. Same name replaces in place. |
output(&factory) | Called with no arguments. Last one wins. |
input(&factory) | Called with commands:. Last one wins. |
providers | Hash of name to factory. |
setups | Hash of name to block. |
factories | Hash with :input and :output keys when registered. |
clear_setups! | Removes every setup. Used by reload. |
Elelem::Config #
Module functions on the shared registry.
Elelem::Config.build_provider(name, **options)
Elelem::Config.build_session(provider, toolbox: Toolbox.new, input: nil, output: nil, provider_options: {},
template: nil, system_prompt: nil)
Elelem::Config.apply(session)
Elelem::Config.reload(session)
Elelem::Config.names| Method | Returns |
|---|---|
build_provider | A new provider. Loads plugin files first, and the elelem-NAME gem if needed. |
build_session | A Session with setups applied. Elelem.build calls this. |
apply | Runs every setup block against the session. |
reload | Clears setups, tools, hooks, and commands, reloads plugin files, and applies again. See Reloading. |
names | Registered provider names, as strings. |
session.provider = Elelem::Config.build_provider("openai", model: "gpt-4o")Session #
What setup blocks receive.
Elelem::Session.new(agent, input: NullInput.new, commands: Commands.new)| Method | Returns |
|---|---|
agent | The Agent. |
input | The input object. |
commands | The Commands. |
provider, provider= | The agent’s provider. Assign to swap it. |
toolbox | The agent’s Toolbox. |
output | The agent’s output object. |
conversation | The agent’s Transcript. |
context | The messages the provider would get now, system prompt first. |
turn(prompt) | Runs a turn and returns the reply. |
Agent #
Elelem::Agent.new(provider, toolbox: Toolbox.new, output: NullOutput.new, template: nil, system_prompt: nil)| Method | Returns |
|---|---|
turn(prompt) | The reply string. Adds the prompt and the reply to the conversation. |
context | The current messages, system prompt first. |
conversation | The Transcript. |
provider, provider= | The provider. |
toolbox, output | As given. |
A second agent that shares the provider, tools, and output but has its own conversation and template is a sub-agent. elelem-tools builds its task tool this way:
sub = Elelem::Agent.new(session.provider, toolbox: session.toolbox, output: session.output,
template: "Research agent. Search, analyze, report. Be concise.")
sub.turn("Find where plugins are loaded.")Transcript #
session.conversation. The conversation plus the system prompt.
Elelem::Transcript.new(template: nil, system_prompt: nil)| Method | Notes |
|---|---|
add(role:, content:, **extra) | role must be "user", "assistant", or "tool", or it raises ArgumentError. Keys with nil values are dropped. |
last | The last message hash, or nil. |
clear! | Removes every message. The system prompt stays. |
to_a(extra = []) | System message, then the conversation, then extra. Renders the template each call. |
session.conversation.add(role: "user", content: "Context: the build is red on main.")Elelem::Conversation is the plain list underneath, with add, last, length, clear!, and to_a(system_prompt: nil).
Toolbox #
session.toolbox.
toolbox.add(name, description:, params: {}, required: [], aliases: [], &handler)
toolbox.before(tool_name = :*) { |args| ... } # global: { |args, tool_name:| }
toolbox.after(tool_name = :*) { |args, result| ... } # global: { |args, result, tool_name:| }
toolbox.run(name, args) # => Elelem::Result
toolbox.tool_for(name) # => Tool, following aliases, or a stand-in for unknown names
toolbox.to_a # => tool definitions for the provider
toolbox.tools # => { "name" => Tool }
toolbox.aliases # => { "alias" => "name" }
toolbox.hooks # => { before: { name => [block] }, after: { name => [block] } }
toolbox.clear! # removes every tool, alias, and hooksession.toolbox.add("now", description: "Current time in UTC") { |_args| { time: Time.now.utc.to_s } }
session.toolbox.after("write") { |args, result| system("rubocop", "-a", args["path"]) unless result[:error] }Handlers get string keys and must return a hash. Details in Tools and commands.
Tool and Schema #
toolbox.add builds these for you. You need them only to inspect a tool or validate arguments yourself.
Elelem::Tool.new(name, description:, schema: Schema.new, aliases: [], &handler)
Elelem::Schema.new(params: {}, required: [])| Method | Returns |
|---|---|
tool.name, tool.description, tool.aliases | As given. |
tool.call(args) | The handler’s return value. No validation or hooks. |
tool.validate(args) | An array of error strings, empty when valid. |
tool.to_h | { type: "function", function: { name:, description:, parameters: } } |
schema.to_h | { type: "object", properties: params, required: required } |
schema.validate(args) | An array of error strings. nil args are treated as {}. |
Result #
What toolbox.run returns.
Elelem::Result.success(payload) # payload.merge(ok: true)
Elelem::Result.failure(payload) # payload.merge(ok: false)| Method | Returns |
|---|---|
ok? | true when :ok is truthy. |
error? | true when :error is present. |
error | The :error value. |
[], each, key?, merge, to_h, to_json | Delegated to the payload hash. |
result = session.toolbox.run("execute", { "command" => "bin/test" })
session.output.say("tests failed", as: :error) unless result[:exit_status].to_i.zero?Commands #
session.commands.
commands.register(name, description: "", completions: nil) { |args| ... }
commands.run(name, args = nil) # => true if found, false if not
commands.names # => ["/name", ...]
commands.each { |slash_name, description| ... }
commands.completions_for(name, partial = "")
commands.clear!args is the text after the command name, or nil. completions: is an array or a lambda returning one. Commands includes Enumerable.
session.commands.register("model", description: "Show the provider class") do
session.output.say(session.provider.class.name)
endOutput #
The core terminal output. Subclass it to make your own.
Elelem::Output.new(stream: $stdout)| Method | Behavior in core |
|---|---|
say(text, as: nil) | Prints text and a newline. as: :markdown wraps it in a code fence. as: :error prefixes error: . Blank text prints nothing. |
print(text) | Prints without a newline. Blank text prints nothing. |
thinking(text) | Prints dimmed italic text. |
doing(name, args, state: "+") | Prints + name(args), with the name in cyan. |
waiting | Does nothing. Called before each provider request. |
display_file(path, fallback: nil) | Says fallback, or path when there is none. |
Elelem::NullOutput is an Output whose say does nothing. The other methods still write to its stream.
elelem-builtins registers its own output with the same methods. It renders Markdown with glow, shows files with bat, and prints dots while waiting.
Input #
Elelem::Input.new(commands: [])| Method | Returns |
|---|---|
ask(prompt) | The line you typed, stripped, or nil at end of input. Uses Reline, with history. |
interactive? | true when stdin is a terminal. |
Elelem::NullInput returns nil from ask and false from interactive?. Check session.input.interactive? before asking a question, as the confirmation hook in elelem-tools does.
SystemPrompt #
Elelem::SystemPrompt.new(template = nil)| Member | Notes |
|---|---|
render | The template rendered with ERB, trim_mode: "-". |
template, template= | The ERB source. Defaults to DEFAULT. |
DEFAULT | The contents of lib/elelem/prompts/default.erb. |
USER_AGENTS_MD | ~/.agents/AGENTS.md as a Pathname. |
Inside a template, agents_md returns the global and project AGENTS.md joined by a blank line, or nil.
StubProvider #
Elelem::StubProvider.new.fetch(messages, tools = [], &block)The built-in stub provider. Useful in tests: it calls the first tool once and then stops. See Installation.
Repl and CLI #
For building your own executable.
Elelem::Repl.new(session).run
Elelem::CLI.new(ARGV, provider: "stub").runCLI.new takes the default provider, which -p overrides. Every provider executable is three lines around it:
#!/usr/bin/env ruby
require "elelem/cli"
Signal.trap("INT") { exit 1 }
Elelem::CLI.new(ARGV, provider: "anthropic").runPlugins and Registry #
Rarely needed outside core.
Elelem::Plugins.new(load_paths: Elelem::Plugins::LOAD_PATHS).load!(force: false)
Elelem::Registry.new(plugins: Plugins.new)Plugins::LOAD_PATHS is ["~/.agents/plugins", ".agents/plugins"]. load! runs once unless force: true. Registry is the class behind Elelem::Config; Elelem::Config.default returns the shared instance.