GenAgentClaude
Claude backend for GenAgent, built on top of claude_wrapper.
Provides GenAgent.Backends.Claude, which wraps the claude CLI and
translates its stream-json output into the normalized GenAgent.Event
values the state machine consumes.
Prerequisites
The claude CLI must be installed and on your PATH (or set CLAUDE_CLI
to point at it). See the Claude Code docs
for install instructions.
Installation
def deps do
[
{:gen_agent, "~> 0.2.0"},
{:gen_agent_claude, "~> 0.1.0"}
]
endQuick start
defmodule MyApp.Coder do
use GenAgent
defmodule State do
defstruct [:path, responses: []]
end
@impl true
def init_agent(opts) do
path = Keyword.fetch!(opts, :cwd)
backend_opts = [
cwd: path,
system_prompt: "You are a coding assistant.",
permission_mode: :accept_edits
]
{:ok, backend_opts, %State{path: path}}
end
@impl true
def handle_response(_ref, response, state) do
{:noreply, %{state | responses: state.responses ++ [response.text]}}
end
end
{:ok, _pid} = GenAgent.start_agent(MyApp.Coder,
name: "my-coder",
backend: GenAgent.Backends.Claude,
cwd: "/path/to/project"
)
{:ok, response} = GenAgent.ask("my-coder", "What does lib/foo.ex do?")
IO.puts(response.text)Session continuation
Claude CLI tracks multi-turn state via a server-side session_id. The
backend captures it from the terminal :result event and threads it
through --resume on subsequent turns -- transparently, no caller code
required.
# Turn 1: fresh conversation
{:ok, r1} = GenAgent.ask("my-coder", "Remember the number 42")
# Turn 2: same thread, same agent
{:ok, r2} = GenAgent.ask("my-coder", "What number did I ask you to remember?")
# r2.text == "42"Backend options
start_session/1 accepts any option supported by ClaudeWrapper.stream/2:
Config:
:binary,:working_dir(aliased as:cwd),:env,:timeout,:verbose,:debug
Query:
:model,:system_prompt,:append_system_prompt,:max_turns,:max_budget_usd,:permission_mode,:dangerously_skip_permissions,:effort,:json_schema,:agent,:brief
Backend-only:
:stream_fn-- a 2-arity function(prompt, opts) -> Enumerable.t()that replaces the default&ClaudeWrapper.stream/2. Intended for tests that want to stub out the subprocess.
See GenAgent.Backends.Claude for the full module docs.
Event translation
Claude CLI's stream-json output is translated into GenAgent.Event
values by GenAgent.Backends.Claude.EventTranslator:
| Claude event | GenAgent event |
|---|---|
"system" | filtered |
"assistant" | :text (from text content blocks) |
"content_block_delta" | :text (from delta text) |
"tool_use" | :tool_use |
"tool_result" | :tool_result |
"result" | :usage + terminal :result |
"error" |
terminal :error |
| anything else | filtered |
Token counts from data["usage"] are pulled out into a separate
:usage event so GenAgent.Response.usage is populated.
Testing
# Unit tests only (default, no CLI invocation)
mix test
# Include live integration tests that actually call the claude CLI
mix test --only integration
Integration tests are tagged :integration so they do not run by
default. They burn real tokens -- keep them cheap.
License
MIT. See LICENSE.