brute

A framework-agnostic coding agent for Ruby — Rack-style middleware pipelines for agent turns, a full set of coding tools, and session persistence. Bring your own LLM library.

Brute treats an agent turn the way Rack treats an HTTP request: an env hash flowing through a middleware stack toward a terminal app. The middleware handles the agentic machinery — the tool loop, session persistence, the system prompt, iteration guards. The terminal app is a proc you write with whatever LLM library you prefer: ruby_llm, llm.rb, the official openai or anthropic gems, or raw HTTP. Brute depends on none of them.

Quick start

require "brute"
require "ruby_llm"

agent = Brute.agent
  .use(Brute::Middleware::SessionLog, path: "tmp/session.jsonl")
  .use(Brute::Middleware::SystemPrompt)
  .use(Brute::Middleware::Loop::ToolResult)
  .use(Brute::Middleware::MaxIterations)
  .use(Brute::Middleware::ToolPipeline, tools: Brute::Tools::ALL)
  .run do |env|
    # The LLM call — yours, with your library. A MessageTransport
    # translates between Brute's message format and the library's.
    response = provider.complete(
      Brute::MessageTransport::RubyLLM.dump_all(env[:messages]),
      tools: rubyllm_tools(env[:tools]), model: model,
    )
    Brute::MessageTransport::RubyLLM.wrap_each(response) { |m| env[:messages] << m }
  end

agent.start("What files are in the current directory?")

The proc makes ONE completion per pass; the ToolPipeline middleware executes whatever tools the model called, and Loop::ToolResult sends the results back down the stack until the model answers with text. Brute is the turn manager — the LLM library is just a client.

Head to Getting Started for a complete runnable walkthrough.

What’s here

Design principles

  1. No LLM dependency. brute.gemspec names no LLM library. The terminal run proc owns provider, model, and credentials.
  2. Plain data. The conversation log is an Array of Brute::Message — an immutable Data value with role, content, tool_calls, tool_call_id. Anything that duck-types those methods rides along.
  3. Middleware all the way down. Turns are pipelines; tools can be pipelines; even the event stream is a stack of handlers. If you know Rack, you know Brute.

This site uses Just the Docs, a documentation theme for Jekyll.