# `MingaAgent.FileMention`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga_agent/file_mention.ex#L1)

Handles `@file` mentions in chat input.

Users type `@path/to/file.ex` in the chat input to attach file
content as context to their prompt. This module extracts those
references and resolves them to content blocks prepended to the
prompt text.

## Mention format

A mention is `@` followed by a file path. The `@` must appear at the
start of the input or after whitespace (not mid-word). The path
extends until the next whitespace or end of string.

## Resolution

Each `@path` is:
1. Resolved relative to the project root
2. Read from disk
3. Prepended to the prompt as a fenced code block with the file path

If any mentioned file does not exist, resolution fails with an error
message listing the missing files.

# `completion`

```elixir
@type completion() :: %{
  prefix: String.t(),
  all_files: [String.t()],
  candidates: [String.t()],
  selected: non_neg_integer(),
  anchor_line: non_neg_integer(),
  anchor_col: non_neg_integer()
}
```

Completion state for the @-mention popup.

# `mention`

```elixir
@type mention() :: %{
  path: String.t(),
  start: non_neg_integer(),
  stop: non_neg_integer()
}
```

A single extracted mention: the file path and its character range in the text.

# `extract_mentions`

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

Extracts `@path` mentions from prompt text.

Returns a list of mention maps with the file path and its character
position range (for potential highlighting or removal).

## Examples

    iex> MingaAgent.FileMention.extract_mentions("@lib/foo.ex what does this do?")
    [%{path: "lib/foo.ex", start: 0, stop: 14}]

    iex> MingaAgent.FileMention.extract_mentions("look at @a.ex and @b.ex")
    [%{path: "a.ex", start: 8, stop: 14}, %{path: "b.ex", start: 19, stop: 24}]

    iex> MingaAgent.FileMention.extract_mentions("no mentions here")
    []

    iex> MingaAgent.FileMention.extract_mentions("email@example.com is not a mention")
    []

# `image_path?`

```elixir
@spec image_path?(String.t()) :: boolean()
```

Returns true if the path has an image file extension.

# `new_completion`

```elixir
@spec new_completion([String.t()], non_neg_integer(), non_neg_integer()) ::
  completion()
```

Creates a new completion state from the current file list.

`anchor_line` and `anchor_col` mark where the `@` was typed so the
completion popup knows where to render and where to insert the result.

# `resolve_prompt`

```elixir
@spec resolve_prompt(String.t(), String.t()) ::
  {:ok, String.t()}
  | {:ok, [ReqLLM.Message.ContentPart.t()]}
  | {:error, String.t()}
```

Resolves all `@path` mentions in the text and returns an augmented prompt.

Each mentioned text file is prepended as a fenced code block.
Image files (PNG, JPEG, GIF, WebP) are returned as ContentPart structs
for multi-modal API requests.

Returns:
- `{:ok, String.t()}` when only text files are mentioned
- `{:ok, [ContentPart.t()]}` when images are present (mixed text + image parts)
- `{:error, message}` if any file doesn't exist or can't be read

# `resolve_prompt`

```elixir
@spec resolve_prompt(String.t(), String.t(), keyword()) ::
  {:ok, String.t()}
  | {:ok, [ReqLLM.Message.ContentPart.t()]}
  | {:error, String.t()}
```

Resolves mentions with options.

Options:
  - `:model` — the model string for vision capability checking

# `select_next`

```elixir
@spec select_next(completion()) :: completion()
```

Moves selection down (wraps around).

# `select_prev`

```elixir
@spec select_prev(completion()) :: completion()
```

Moves selection up (wraps around).

# `selected_path`

```elixir
@spec selected_path(completion()) :: String.t() | nil
```

Returns the currently selected candidate path, or nil if none.

# `update_prefix`

```elixir
@spec update_prefix(completion(), String.t()) :: completion()
```

Updates the prefix and re-filters candidates.

---

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