Documentation menu

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: NullOutput only silences say. Thinking text and anything a tool sends to output.print, such as the output of execute, still go to stdout. Pass output: 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.

KeywordMeaning
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 # => Logger

A 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 }
MethodNotes
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.
providersHash of name to factory.
setupsHash of name to block.
factoriesHash 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
MethodReturns
build_providerA new provider. Loads plugin files first, and the elelem-NAME gem if needed.
build_sessionA Session with setups applied. Elelem.build calls this.
applyRuns every setup block against the session.
reloadClears setups, tools, hooks, and commands, reloads plugin files, and applies again. See Reloading.
namesRegistered 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)
MethodReturns
agentThe Agent.
inputThe input object.
commandsThe Commands.
provider, provider=The agent’s provider. Assign to swap it.
toolboxThe agent’s Toolbox.
outputThe agent’s output object.
conversationThe agent’s Transcript.
contextThe 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)
MethodReturns
turn(prompt)The reply string. Adds the prompt and the reply to the conversation.
contextThe current messages, system prompt first.
conversationThe Transcript.
provider, provider=The provider.
toolbox, outputAs 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)
MethodNotes
add(role:, content:, **extra)role must be "user", "assistant", or "tool", or it raises ArgumentError. Keys with nil values are dropped.
lastThe 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 hook
session.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: [])
MethodReturns
tool.name, tool.description, tool.aliasesAs 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)
MethodReturns
ok?true when :ok is truthy.
error?true when :error is present.
errorThe :error value.
[], each, key?, merge, to_h, to_jsonDelegated 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)
end

Output #

The core terminal output. Subclass it to make your own.

Elelem::Output.new(stream: $stdout)
MethodBehavior 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.
waitingDoes 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: [])
MethodReturns
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)
MemberNotes
renderThe template rendered with ERB, trim_mode: "-".
template, template=The ERB source. Defaults to DEFAULT.
DEFAULTThe 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").run

CLI.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").run

Plugins 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.