# `Agentic.AgentProtocol.CLI`

Behaviour for CLI-based local agent protocols.

Extends AgentProtocol with CLI-specific lifecycle, configuration,
and availability checking.

## CLI Configuration

The configuration maps to OpenClaw's `CliBackendConfig` with these key fields:

```elixir
%{
  command: "claude",           # CLI binary name or path
  args: ["-p", "--output-format", "stream-json"],  # base args
  env: %{...},                 # extra env vars
  clear_env: [...],            # env vars to remove
  session_mode: :always,       # :always | :existing | :none
  session_id_fields: ["session_id"],  # where to find session ID in output
  session_args: [...],         # extra args for resuming
  system_prompt_mode: :append, # :append | :replace
  system_prompt_when: :first,  # :first | :always | :never
  reliability: %{
    watchdog: %{
      fresh: %{no_output_timeout_ms: 120_000},
      resume: %{no_output_timeout_ms: 300_000}
    }
  }
}
```

## Usage

Use this behaviour in your protocol implementation:

```elixir
defmodule MyAgentProtocol do
  use Agentic.AgentProtocol.CLI

  @impl true
  def cli_name, do: "my-agent"

  @impl true
  def build_config(profile_config) do
    %{
      command: "my-agent",
      args: ["-p", "--output-format", "stream-json"],
      session_mode: :always,
      session_id_fields: ["session_id"]
    }
  end
end
```

# `cli_config`

```elixir
@type cli_config() :: %{
  :command =&gt; String.t(),
  optional(:args) =&gt; [String.t()],
  optional(:env) =&gt; %{required(String.t()) =&gt; String.t()},
  optional(:clear_env) =&gt; [String.t()],
  optional(:session_mode) =&gt; :always | :existing | :none,
  optional(:session_id_fields) =&gt; [String.t()],
  optional(:session_args) =&gt; [String.t()],
  optional(:resume_args) =&gt; [String.t()],
  optional(:system_prompt_arg) =&gt; String.t(),
  optional(:system_prompt_mode) =&gt; :append | :replace,
  optional(:system_prompt_when) =&gt; :first | :always | :never,
  optional(:model_arg) =&gt; String.t(),
  optional(:model_aliases) =&gt; %{required(String.t()) =&gt; String.t()},
  optional(:image_arg) =&gt; String.t(),
  optional(:image_mode) =&gt; :repeat | :list,
  optional(:serialize) =&gt; boolean(),
  optional(:reliability) =&gt; map(),
  optional(:sandbox_wrapper) =&gt; String.t()
}
```

# `build_config`

```elixir
@callback build_config(profile_config :: map()) :: cli_config()
```

Build CLI-specific configuration from profile config.

Merges default CLI config with profile-specific overrides.

# `cli_name`

```elixir
@callback cli_name() :: String.t()
```

Return the CLI binary name for this protocol.

Used for availability checking and logging.

# `cli_version`

```elixir
@callback cli_version() :: String.t() | nil
```

Get CLI version string.

Useful for debugging and compatibility checking.

# `default_args`

```elixir
@callback default_args() :: [String.t()]
```

Default args for fresh session (non-resume).

Override to customize base CLI arguments.

# `extract_session_id`

```elixir
@callback extract_session_id(response :: map(), cli_config :: cli_config()) ::
  String.t() | nil
```

Extract session ID from CLI output.

Searches the response metadata for session ID using configured field names.

# `format_session_arg`

```elixir
@callback format_session_arg(session_id :: String.t(), cli_config :: cli_config()) :: [
  String.t()
]
```

Format the session ID argument for CLI.

Default: `--session-id {sessionId}`

# `format_system_prompt`

```elixir
@callback format_system_prompt(
  system_prompt :: String.t(),
  is_first :: boolean(),
  cli_config :: cli_config()
) :: [String.t()] | nil
```

Format system prompt for CLI.

Handles the different modes (append vs replace, first vs always).

# `merge_env`

```elixir
@callback merge_env(cli_config :: cli_config(), extra_env :: map()) :: [
  {String.t(), String.t()}
]
```

Merge environment variables for CLI execution.

Combines host-managed env vars with protocol-specific vars.

# `resume_args`

```elixir
@callback resume_args() :: [String.t()]
```

Args used when resuming an existing session.

Use `{sessionId}` placeholder for session ID injection.

# `cli_name`

---

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