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

Query command -- the primary interface for executing prompts.

Wraps `claude -p <prompt>` with the full set of CLI flags.

## Usage

    config = ClaudeWrapper.Config.new(working_dir: "/path/to/project")

    # Build a query
    query = ClaudeWrapper.Query.new("Fix the failing test")
      |> ClaudeWrapper.Query.model("sonnet")
      |> ClaudeWrapper.Query.max_turns(5)
      |> ClaudeWrapper.Query.permission_mode(:bypass_permissions)

    # Execute (one-shot, returns full result)
    {:ok, result} = ClaudeWrapper.Query.execute(query, config)

    # Or stream events
    ClaudeWrapper.Query.stream(query, config)
    |> Stream.each(&IO.inspect/1)
    |> Stream.run()

# `effort`

```elixir
@type effort() :: :low | :medium | :high | :xhigh | :max
```

# `hermetic`

```elixir
@type hermetic() :: :full | :project
```

# `input_format`

```elixir
@type input_format() :: :text | :stream_json
```

# `output_format`

```elixir
@type output_format() :: :text | :json | :stream_json
```

# `permission_mode`

```elixir
@type permission_mode() ::
  :default | :accept_edits | :bypass_permissions | :dont_ask | :plan | :auto
```

# `t`

```elixir
@type t() :: %ClaudeWrapper.Query{
  add_dir: [String.t()],
  agent: String.t() | nil,
  agents_json: String.t() | nil,
  allowed_tools: [String.t()],
  append_system_prompt: String.t() | nil,
  append_system_prompt_file: String.t() | nil,
  bare: boolean(),
  betas: String.t() | [String.t()] | nil,
  brief: boolean(),
  continue_session: boolean(),
  dangerously_skip_permissions: boolean(),
  debug_file: String.t() | nil,
  debug_filter: String.t() | nil,
  disable_slash_commands: boolean(),
  disallowed_tools: [String.t()],
  effort: effort() | nil,
  exclude_dynamic_system_prompt_sections: boolean(),
  fallback_model: String.t() | nil,
  files: [String.t()],
  fork_session: boolean(),
  from_pr: String.t() | nil,
  hermetic: hermetic() | nil,
  include_hook_events: boolean(),
  include_partial_messages: boolean(),
  input_format: input_format() | nil,
  json_schema: String.t() | nil,
  max_budget_usd: float() | nil,
  max_thinking_tokens: pos_integer() | nil,
  max_turns: pos_integer() | nil,
  mcp_config: [String.t()],
  model: String.t() | nil,
  name: String.t() | nil,
  no_session_persistence: boolean(),
  output_format: output_format() | nil,
  permission_mode: permission_mode() | nil,
  permission_prompt_tool: String.t() | nil,
  plugin_dirs: [String.t()],
  plugin_urls: [String.t()],
  prompt: String.t(),
  prompt_suggestions: boolean(),
  replay_user_messages: boolean(),
  resume: String.t() | nil,
  safe_mode: boolean(),
  session_id: String.t() | nil,
  setting_sources: String.t() | nil,
  settings: String.t() | nil,
  strict_mcp_config: boolean(),
  system_prompt: String.t() | nil,
  system_prompt_file: String.t() | nil,
  tmux: boolean(),
  tools: [String.t()],
  worktree: boolean() | String.t()
}
```

# `add_dir`

```elixir
@spec add_dir(t(), String.t()) :: t()
```

Add a directory for tool access.

# `agent`

```elixir
@spec agent(t(), String.t()) :: t()
```

Set agent name.

# `agents_json`

```elixir
@spec agents_json(t(), String.t()) :: t()
```

Provide custom agents as JSON.

# `allowed_tool`

```elixir
@spec allowed_tool(t(), String.t() | ClaudeWrapper.ToolPattern.t()) :: t()
```

Add an allowed tool.

Accepts either a plain CLI-format string (`"Bash(git log:*)"`) or a
`t:ClaudeWrapper.ToolPattern.t/0`, which is rendered to its string
form. The stored field stays a list of strings.

# `append_system_prompt`

```elixir
@spec append_system_prompt(t(), String.t()) :: t()
```

Append to the system prompt.

# `append_system_prompt_file`

```elixir
@spec append_system_prompt_file(t(), String.t()) :: t()
```

Append to the system prompt from a file (`--append-system-prompt-file`). Verified against claude CLI 2.1.170 (hidden from --help).

# `apply_opts`

```elixir
@spec apply_opts(
  t(),
  keyword()
) :: t()
```

Apply a keyword list of options to a query, calling the equivalent
`Query` setter for each known key. Unknown keys are silently ignored.

This is the shared mapping used by `ClaudeWrapper.query/2`,
`ClaudeWrapper.stream/2`, and `ClaudeWrapper.Session.send/3`. It
exists so the opt-to-setter mapping lives in one place rather than
being duplicated across each surface.

Boolean opts (e.g. `:worktree`, `:dangerously_skip_permissions`)
are applied when the value is `true` and ignored when `false`. List
opts (e.g. `:allowed_tools`, `:add_dir`) reduce over their list,
applying the per-item setter for each entry.

## Examples

    iex> q = ClaudeWrapper.Query.new("hi")
    iex> q = ClaudeWrapper.Query.apply_opts(q, model: "sonnet", max_turns: 3, worktree: true)
    iex> q.model
    "sonnet"
    iex> q.max_turns
    3
    iex> q.worktree
    true

# `bare`

```elixir
@spec bare(t()) :: t()
```

Minimal mode: skip hooks, LSP, plugins, and settings (`--bare`).

# `betas`

```elixir
@spec betas(t(), String.t() | [String.t()]) :: t()
```

Set beta feature header(s).

Accepts a single string or a list -- the CLI's `--betas` is variadic, so a
list emits one `--betas` flag followed by every value (a single string is
still accepted, unchanged).

# `brief`

```elixir
@spec brief(t()) :: t()
```

Enable the SendUserMessage tool for agent-to-user communication (`--brief`).

# `continue_session`

```elixir
@spec continue_session(t()) :: t()
```

Continue the most recent session.

# `dangerously_skip_permissions`

```elixir
@spec dangerously_skip_permissions(t()) :: t()
```

Bypass all permission checks. Use in isolated worktrees.

# `debug_file`

```elixir
@spec debug_file(t(), String.t()) :: t()
```

Set debug log file.

# `debug_filter`

```elixir
@spec debug_filter(t(), String.t()) :: t()
```

Set debug filter.

# `disable_slash_commands`

```elixir
@spec disable_slash_commands(t()) :: t()
```

Disable all slash commands / skills (`--disable-slash-commands`).

# `disallowed_tool`

```elixir
@spec disallowed_tool(t(), String.t() | ClaudeWrapper.ToolPattern.t()) :: t()
```

Add a disallowed tool.

Accepts either a plain CLI-format string or a
`t:ClaudeWrapper.ToolPattern.t/0`, mirroring `allowed_tool/2`.

# `effort`

```elixir
@spec effort(t(), effort()) :: t()
```

Set effort level.

# `exclude_dynamic_system_prompt_sections`

```elixir
@spec exclude_dynamic_system_prompt_sections(t()) :: t()
```

Exclude dynamic system-prompt sections (`--exclude-dynamic-system-prompt-sections`).

# `execute`

```elixir
@spec execute(t(), ClaudeWrapper.Config.t()) ::
  {:ok, ClaudeWrapper.Result.t()} | {:error, ClaudeWrapper.Error.t()}
```

Execute the query synchronously, returning a parsed `%Result{}`.

Automatically sets `--output-format json` for parsing.

# `fallback_model`

```elixir
@spec fallback_model(t(), String.t()) :: t()
```

Set a fallback model.

# `file`

```elixir
@spec file(t(), String.t()) :: t()
```

Add a file resource.

# `fork_session`

```elixir
@spec fork_session(t()) :: t()
```

Fork to a new session.

# `from_pr`

```elixir
@spec from_pr(t(), String.t()) :: t()
```

Resume a session linked to a PR (`--from-pr <number|url>`).

Analogous to `resume/2` but keyed on a pull request rather than a
session id. The value is a PR number or URL.

# `hermetic`

```elixir
@spec hermetic(t(), hermetic()) :: t()
```

Seal the ambient `~/.claude` config so a programmatic run's surface is
exactly what was provided explicitly.

A preset over three flags, expanded at flag-build time (never here), so
the seal is order-independent and an explicit `setting_sources/2` always
wins over the scope's default:

  * `--setting-sources` (the ambient-config seal)
  * `--strict-mcp-config` (only servers from `--mcp-config`)
  * `--exclude-dynamic-system-prompt-sections` (reproducibility)

The scope sets the seal level:

  * `:full` -> `--setting-sources ""` -- drops user + project + local
    ambient config; only claude's built-in agents remain.
  * `:project` -> `--setting-sources user` -- seals project + local
    ambient config, keeps the user's global `~/.claude`.

Hermetic seals the promptspace WITHOUT changing auth: it never emits
`--bare` (which would force `ANTHROPIC_API_KEY` billing). OAuth /
keychain / subscription auth is untouched.

# `include_hook_events`

```elixir
@spec include_hook_events(t()) :: t()
```

Include hook lifecycle events in the output stream (`--include-hook-events`).

# `include_partial_messages`

```elixir
@spec include_partial_messages(t()) :: t()
```

Include partial messages in streaming output.

# `input_format`

```elixir
@spec input_format(t(), input_format()) :: t()
```

Set input format.

# `json_schema`

```elixir
@spec json_schema(t(), String.t()) :: t()
```

Set JSON schema for structured output.

# `max_budget_usd`

```elixir
@spec max_budget_usd(t(), float()) :: t()
```

Set maximum budget in USD.

# `max_thinking_tokens`

```elixir
@spec max_thinking_tokens(t(), pos_integer()) :: t()
```

Cap the thinking-token budget (`--max-thinking-tokens`), a finer control than `--effort`. Verified against claude CLI 2.1.170 (hidden from --help).

# `max_turns`

```elixir
@spec max_turns(t(), pos_integer()) :: t()
```

Set maximum turns.

# `mcp_config`

```elixir
@spec mcp_config(t(), String.t()) :: t()
```

Add an MCP config file path.

# `model`

```elixir
@spec model(t(), String.t()) :: t()
```

Set the model (e.g. "sonnet", "opus", "haiku").

# `name`

```elixir
@spec name(t(), String.t()) :: t()
```

Set a display name for the session (`--name`).

# `new`

```elixir
@spec new(String.t()) :: t()
```

Create a new query with the given prompt.

# `no_session_persistence`

```elixir
@spec no_session_persistence(t()) :: t()
```

Disable session persistence.

# `output_format`

```elixir
@spec output_format(t(), output_format()) :: t()
```

Set output format.

# `permission_mode`

```elixir
@spec permission_mode(t(), permission_mode()) :: t()
```

Set permission mode.

# `permission_prompt_tool`

```elixir
@spec permission_prompt_tool(t(), String.t()) :: t()
```

Delegate permission decisions to a custom MCP tool (`--permission-prompt-tool <tool>`) for programmatic headless gating. Verified against claude CLI 2.1.170 (hidden from --help).

# `plugin_dir`

```elixir
@spec plugin_dir(t(), String.t()) :: t()
```

Add a plugin directory.

# `plugin_url`

```elixir
@spec plugin_url(t(), String.t()) :: t()
```

Fetch a plugin `.zip` from a URL (`--plugin-url`). Repeatable.

# `prompt_suggestions`

```elixir
@spec prompt_suggestions(t()) :: t()
```

Enable prompt suggestions (`--prompt-suggestions`).

# `replay_user_messages`

```elixir
@spec replay_user_messages(t()) :: t()
```

Re-emit user messages from stdin (`--replay-user-messages`). Mainly for stream/duplex use.

# `resume`

```elixir
@spec resume(t(), String.t()) :: t()
```

Resume a specific session by ID.

# `safe_mode`

```elixir
@spec safe_mode(t()) :: t()
```

Start with all customizations disabled (`--safe-mode`).

# `session_id`

```elixir
@spec session_id(t(), String.t()) :: t()
```

Use a specific session ID.

# `setting_sources`

```elixir
@spec setting_sources(t(), String.t()) :: t()
```

Set settings source list.

# `settings`

```elixir
@spec settings(t(), String.t()) :: t()
```

Set settings JSON.

# `spawn_args`

```elixir
@spec spawn_args(t()) :: [String.t()]
```

The spawn-time flag subset of a query, for a long-lived
`ClaudeWrapper.DuplexSession`.

Reuses `build_args/1` (the single source of flag emission, so the
duplex and one-shot paths cannot drift), then removes the flags a
duplex session owns itself: the transport formats
(`--output-format` / `--input-format` / `--include-partial-messages`,
which the session fixes to its stream-json protocol) and the leading
`--print <prompt>` pair (in duplex the prompt is a per-turn
stream-json message, not an argv). Everything else -- model, system
prompt, permission mode, tool allow/deny lists, mcp config, effort,
turn/budget caps, session continuity, and so on -- is spawn-safe and
passes through.

# `stream`

```elixir
@spec stream(t(), ClaudeWrapper.Config.t()) :: Enumerable.t()
```

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

Uses a Port with `:line` mode to read NDJSON output line-by-line.
The port is opened when the stream is consumed and closed when
the stream terminates.

A clean run ends with a terminal `%StreamEvent{type: "result"}`. If the stream
halts without one -- an idle timeout (`Config.timeout` is *not* a whole-run
bound here; only the per-frame idle deadline applies), a non-zero exit, or a
spawn failure -- the stream ends with a terminal
`%StreamEvent{type: "error", data: %{"error" => "stream_truncated"}}` so the
truncation is observable. Note the default runner orphans a stalled
subprocess; use the `ClaudeWrapper.Runner.Forcola` runner for leak-free
termination.

# `strict_mcp_config`

```elixir
@spec strict_mcp_config(t()) :: t()
```

Only use servers from --mcp-config.

# `system_prompt`

```elixir
@spec system_prompt(t(), String.t()) :: t()
```

Set the system prompt (overrides default).

# `system_prompt_file`

```elixir
@spec system_prompt_file(t(), String.t()) :: t()
```

Set the system prompt from a file (`--system-prompt-file`), avoiding ARG_MAX limits from inlining a large prompt as argv. Verified against claude CLI 2.1.170 (hidden from --help).

# `tmux`

```elixir
@spec tmux(t()) :: t()
```

Create tmux session.

# `to_command_string`

```elixir
@spec to_command_string(t(), ClaudeWrapper.Config.t()) :: String.t()
```

Build the shell command string (for debugging).

# `tool`

```elixir
@spec tool(t(), String.t()) :: t()
```

Add a tool.

# `worktree`

```elixir
@spec worktree(t()) :: t()
```

Create a git worktree for execution (`--worktree`). See `worktree/2` for a named worktree.

# `worktree`

```elixir
@spec worktree(t(), String.t()) :: t()
```

Create a named git worktree for execution (`--worktree <name>`).

---

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