GenAgentClaude

CIHex.pmDocs

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"}
  ]
end

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

Query:

Backend-only:

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.