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:
- Resolved relative to the project root
- Read from disk
- 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.
Summary
Types
Completion state for the @-mention popup.
A single extracted mention: the file path and its character range in the text.
Functions
Extracts @path mentions from prompt text.
Returns true if the path has an image file extension.
Creates a new completion state from the current file list.
Resolves all @path mentions in the text and returns an augmented prompt.
Resolves mentions with options.
Moves selection down (wraps around).
Moves selection up (wraps around).
Returns the currently selected candidate path, or nil if none.
Updates the prefix and re-filters candidates.
Types
@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.
@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.
Functions
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")
[]
Returns true if the path has an image file extension.
@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.
@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
@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
@spec select_next(completion()) :: completion()
Moves selection down (wraps around).
@spec select_prev(completion()) :: completion()
Moves selection up (wraps around).
@spec selected_path(completion()) :: String.t() | nil
Returns the currently selected candidate path, or nil if none.
@spec update_prefix(completion(), String.t()) :: completion()
Updates the prefix and re-filters candidates.