# `ClaudeWrapper`
[🔗](https://github.com/genagent/claude_wrapper_ex/blob/main/lib/claude_wrapper.ex#L1)

Elixir wrapper for the Claude Code CLI.

Two ways to drive `claude` from Elixir:

1. `ClaudeWrapper.DuplexSession` -- a `GenServer` that holds **one**
   `claude` subprocess open for the lifetime of a conversation.
   Streams partial tokens as they arrive, supports clean mid-turn
   cancellation, and routes tool-permission prompts back to the
   host. The right fit for chat UIs, agent runtimes, Phoenix
   servers, and any long-running OTP host.
2. The convenience `ClaudeWrapper.query/2` and `ClaudeWrapper.stream/2`
   functions in this module -- one subprocess per turn, simple
   request/response. The right fit for `mix` tasks, escripts, batch
   jobs, and anything else that runs and exits.

Both modes are first-class. Pick whichever matches the lifecycle
of the host using `claude_wrapper`.

## Long-lived chat-style sessions (DuplexSession)

    config = ClaudeWrapper.Config.new(working_dir: ".")
    {:ok, pid} = ClaudeWrapper.DuplexSession.start_link(config: config)

    ClaudeWrapper.DuplexSession.subscribe(pid)
    {:ok, result} = ClaudeWrapper.DuplexSession.send(pid, "Explain this codebase.")

    # Subscriber inbox now holds streaming events:
    #   {:claude, {:assistant, msg}}
    #   {:claude, {:stream_event, msg}}    -- partial token deltas
    #   {:claude, {:result, %ClaudeWrapper.Result{}}}

    ClaudeWrapper.DuplexSession.close(pid)

See `ClaudeWrapper.DuplexSession` for the permission callback,
interrupt API, and full event vocabulary. `ClaudeWrapper.DuplexIEx`
wraps it with REPL helpers that stream tokens to stdout live.

## One-shot queries

    # One-shot query (convenience -- uses default config)
    {:ok, result} = ClaudeWrapper.query("Explain this error: ...")

    # With options
    {:ok, result} = ClaudeWrapper.query("Fix the bug in lib/foo.ex",
      model: "sonnet",
      working_dir: "/path/to/project",
      max_turns: 5,
      permission_mode: :bypass_permissions
    )

    # Streaming
    ClaudeWrapper.stream("Implement the feature described in issue #42",
      working_dir: "/path/to/project"
    )
    |> Stream.each(fn event -> IO.inspect(event.type) end)
    |> Stream.run()

    # Full control via Query builder
    config = ClaudeWrapper.Config.new(working_dir: "/path/to/project")

    ClaudeWrapper.Query.new("Fix the tests")
    |> ClaudeWrapper.Query.model("sonnet")
    |> ClaudeWrapper.Query.dangerously_skip_permissions()
    |> ClaudeWrapper.Query.max_turns(10)
    |> ClaudeWrapper.Query.execute(config)

## Multi-turn continuity for one-shot mode (Session)

`ClaudeWrapper.Session` threads `--resume <session_id>` across
one-shot calls so you can have a multi-turn conversation without
holding a subprocess open. Use it when you want a struct-passing
API rather than a process API, or when turns are far enough apart
that cold-start cost doesn't matter.

## Other modules

  * `ClaudeWrapper.Query` -- the query builder
  * `ClaudeWrapper.SessionServer` -- supervised wrapper for `Session`
  * `ClaudeWrapper.McpConfig` -- programmatic `.mcp.json` builder
  * `ClaudeWrapper.Retry` -- exponential backoff
  * `ClaudeWrapper.Telemetry` -- `:telemetry` spans for exec/stream/session
  * `ClaudeWrapper.Commands.{Auth, Mcp, Plugin, Marketplace, Doctor, Version}` -- CLI subcommand wrappers

## Binary discovery

The `claude` binary is found via (in order):

1. `:binary` option passed directly
2. `CLAUDE_CLI` environment variable
3. System PATH lookup

# `agents`

```elixir
@spec agents(keyword()) :: {:ok, [map()]} | {:error, term()}
```

List active background agent sessions (`claude agents --json`).

Returns the decoded JSON array of active sessions (each a raw, string-keyed
map). See `ClaudeWrapper.Commands.Agents`.

## Options

  * `:all` - include completed sessions (`--all`)
  * `:setting_sources` - comma-separated setting sources (e.g. `"user,project"`)

# `auth_status`

```elixir
@spec auth_status(keyword()) :: {:ok, map()} | {:error, term()}
```

Check authentication status.

# `doctor`

```elixir
@spec doctor(keyword()) :: {:ok, String.t()} | {:error, term()}
```

Run `claude doctor`.

# `query`

```elixir
@spec query(
  String.t(),
  keyword()
) :: {:ok, ClaudeWrapper.Result.t()} | {:error, term()}
```

Execute a one-shot query and return the result.

Convenience wrapper that builds a `Config` and `Query` from keyword options.
Returns `{:ok, %Result{}}` on success or `{:error, reason}` on failure.

## Options

Config options (passed to `ClaudeWrapper.Config.new/1`):
  * `:binary` - Path to claude binary
  * `:working_dir` - Working directory
  * `:env` - Environment variables
  * `:timeout` - Timeout in ms
  * `:verbose` - Enable verbose output
  * `:debug` - Enable debug output

Query options (passed to `Query` builder):
  * `:model` - Model name
  * `:system_prompt` - System prompt override
  * `:max_turns` - Max turns
  * `:max_budget_usd` - Budget limit
  * `:permission_mode` - Permission mode atom
  * `:dangerously_skip_permissions` - Bypass permissions (boolean)
  * `:session_id` - Session ID
  * `:continue_session` - Continue recent session (boolean)
  * `:resume` - Resume session ID
  * `:hermetic` - Seal the ambient `~/.claude` config for a reproducible
    surface: `true` or `:full` (drops user + project + local ambient),
    `:project` (keeps the user's global config). Does not change auth.
    See `ClaudeWrapper.Query.hermetic/2`.

The list above is the common subset. Any other option accepted by
`ClaudeWrapper.Query.apply_opts/2` also works here (for example `:effort`,
`:agent`, `:agents_json`, `:fallback_model`, `:output_format`,
`:allowed_tools`, `:disallowed_tools`, `:add_dir`, `:files`, `:mcp_config`,
`:plugin_dirs`, `:worktree`, `:fork_session`, `:setting_sources`,
`:json_schema`, `:betas`) -- see `ClaudeWrapper.Query.apply_opts/2` for the
authoritative full list.

# `raw`

```elixir
@spec raw(
  [String.t()],
  keyword()
) :: {:ok, String.t()} | {:error, term()}
```

Run an arbitrary CLI command that isn't wrapped by a dedicated module.

This is the escape hatch for new or experimental CLI subcommands.

## Examples

    ClaudeWrapper.raw(["config", "list"])
    ClaudeWrapper.raw(["plugin", "install", "my-plugin"], working_dir: "/tmp")

# `stream`

```elixir
@spec stream(
  String.t(),
  keyword()
) :: Enumerable.t()
```

Execute a query and return a lazy stream of `%StreamEvent{}` structs.

The subprocess starts when the stream is consumed. Accepts the same options as
`query/2`, except `:timeout`: streaming is bounded only by a per-frame idle
deadline, not a whole-run timeout. A truncated run (idle timeout, non-zero
exit, spawn failure) ends with a terminal
`%StreamEvent{type: "error", data: %{"error" => "stream_truncated"}}` rather
than a silent stop; see `ClaudeWrapper.Query.stream/2` for details and the
Forcola-runner note.

# `version`

```elixir
@spec version(keyword()) :: {:ok, map()} | {:error, term()}
```

Get the CLI version.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
