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

Tool permission patterns for `--allowed-tools` / `--disallowed-tools`.

The Claude CLI accepts three pattern shapes in its tool lists:

  * Bare name: `Bash`, `Read`, `Write`.
  * Name with argument glob: `Bash(git log:*)`, `Write(src/*.ex)`.
  * MCP pattern: `mcp__server__tool` or `mcp__server__*`.

This struct models all three. The typed constructors (`tool/1`,
`tool_with_args/2`, `all/1`, `mcp/2`) always produce well-formed
output. `parse/1` validates the shape of a raw string and returns a
`t:pattern_error/0` on malformed input.

For back-compat, the loose constructors (and the string clauses on
`ClaudeWrapper.Query.allowed_tool/2` / `disallowed_tool/2`) accept any
string and store it verbatim, so callers passing raw CLI strings keep
working without changes. Use `parse/1` directly when you want to catch
typos before the CLI invocation.

This is the Elixir port of the Rust crate's `ToolPattern` /
`PatternError` (`../claude-wrapper/src/tool_pattern.rs`). Validation
is shape-level only: tool names are not checked against any allowlist
because the CLI's tool inventory evolves independently.

## Example

    iex> ClaudeWrapper.ToolPattern.tool_with_args("Bash", "git log:*") |> to_string()
    "Bash(git log:*)"

    iex> ClaudeWrapper.ToolPattern.all("Write") |> to_string()
    "Write(*)"

    iex> ClaudeWrapper.ToolPattern.mcp("my-server", "*") |> to_string()
    "mcp__my-server__*"

# `problem`

```elixir
@type problem() ::
  :empty
  | :missing_name
  | {:unbalanced_parens, String.t()}
  | {:illegal_char, String.t()}
```

The specific validation problem behind a `parse/1` failure, carried in
the `:reason` field of the returned `t:ClaudeWrapper.Error.t/0` (kind
`:invalid_tool_pattern`).

  * `:empty` -- input was empty or all whitespace.
  * `:missing_name` -- the tool-name part was empty (e.g. `(args)`
    with nothing before the `(`).
  * `{:unbalanced_parens, original}` -- parentheses were unbalanced or
    appeared out of order. Carries the trimmed input.
  * `{:illegal_char, original}` -- contained a comma (splits the argv)
    or a control char (can break the shell). Carries the trimmed
    input.

# `t`

```elixir
@type t() :: %ClaudeWrapper.ToolPattern{value: String.t()}
```

# `all`

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

Shorthand for `tool_with_args/2` with `*` as the argument pattern --
"any args to this tool."

    iex> ClaudeWrapper.ToolPattern.all("Write") |> to_string()
    "Write(*)"

# `mcp`

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

An MCP pattern: `mcp__{server}__{tool}`. Pass `"*"` as the tool to
match any tool from the server.

    iex> ClaudeWrapper.ToolPattern.mcp("my-server", "do_thing") |> to_string()
    "mcp__my-server__do_thing"

    iex> ClaudeWrapper.ToolPattern.mcp("my-server", "*") |> to_string()
    "mcp__my-server__*"

# `parse`

```elixir
@spec parse(String.t()) :: {:ok, t()} | {:error, ClaudeWrapper.Error.t()}
```

Parse and validate a raw CLI-format pattern string.

Validation is shape-level only (non-empty, balanced parens, no comma
or control chars). Tool names are not checked against any allowlist
because the CLI's tool inventory evolves independently. Surrounding
whitespace is trimmed before validation, so leading/trailing control
characters do not trip `{:illegal_char, _}`.

Returns `{:ok, t}` or `{:error, %ClaudeWrapper.Error{kind:
:invalid_tool_pattern}}` whose `:reason` is the specific
`t:problem/0`.

    iex> ClaudeWrapper.ToolPattern.parse("Bash(git log:*)")
    {:ok, %ClaudeWrapper.ToolPattern{value: "Bash(git log:*)"}}

    iex> ClaudeWrapper.ToolPattern.parse("")
    {:error, %ClaudeWrapper.Error{kind: :invalid_tool_pattern, reason: :empty}}

    iex> ClaudeWrapper.ToolPattern.parse("(args)")
    {:error, %ClaudeWrapper.Error{kind: :invalid_tool_pattern, reason: :missing_name}}

# `to_string`

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

Render the pattern as the string it will become in the CLI arg.

The `String.Chars` protocol is also implemented, so the kernel
`to_string/1` and string interpolation work too.

    iex> ClaudeWrapper.ToolPattern.to_string(ClaudeWrapper.ToolPattern.tool("Read"))
    "Read"

# `tool`

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

A bare tool name, e.g. `tool("Bash")` -> `Bash`.

No validation beyond trimming whitespace; the CLI is the source of
truth for which tool names exist. Use `parse/1` when you want shape
validation.

    iex> ClaudeWrapper.ToolPattern.tool("  Bash  ") |> to_string()
    "Bash"

# `tool_with_args`

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

A tool with an argument glob, rendered `Name(args)`.

    iex> ClaudeWrapper.ToolPattern.tool_with_args("Bash", "git log:*") |> to_string()
    "Bash(git log:*)"

---

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