Class: RubynCode::CLI::MentionExpander

Inherits:
Object
  • Object
show all
Defined in:
lib/rubyn_code/cli/mention_expander.rb

Overview

Expands @path/to/file mentions in user input into inline file content, mirroring Claude Code / Codex @-mentions. The original text is kept and the referenced files are appended as labeled, fenced blocks so the agent sees both the request and the files it points at.

Conservative by design: only existing, readable, reasonably-sized regular files inside the project are expanded. Unresolved mentions (and things that merely look like mentions, e.g. email addresses) are left untouched.

Constant Summary collapse

MENTION =

A mention is an "@" that doesn't follow a word char or another "@" (so emails like foo@bar.com don't match), followed by a path-ish run.

/(?<![\w@])@([^\s@]+)/
TRAILING_PUNCT =
/[).,;:!?'"]+\z/
MAX_FILE_BYTES =
64 * 1024
MAX_FILES =
10

Instance Method Summary collapse

Constructor Details

#initialize(project_root:) ⇒ MentionExpander

Returns a new instance of MentionExpander.

Parameters:

  • project_root (String)


24
25
26
# File 'lib/rubyn_code/cli/mention_expander.rb', line 24

def initialize(project_root:)
  @project_root = project_root
end

Instance Method Details

#expand(input) ⇒ Array(String, Array<String>)

Returns expanded text + resolved rel paths.

Parameters:

  • input (String)

    raw user input

Returns:

  • (Array(String, Array<String>))

    expanded text + resolved rel paths



30
31
32
33
34
35
36
37
38
# File 'lib/rubyn_code/cli/mention_expander.rb', line 30

def expand(input)
  return [input, []] unless input.is_a?(String) && input.include?('@')

  resolved = scan(input)
  return [input, []] if resolved.empty?

  blocks = resolved.map { |rel, abs| file_block(rel, abs) }
  ["#{input}\n\n#{blocks.join("\n\n")}", resolved.map(&:first)]
end

#expand_images(input) ⇒ Array<LLM::ImageBlock>

Extract image references from input and return them as a list of LLM::ImageBlock instances. Image files (.png/.jpg/.jpeg/.gif/.webp) are NOT included in expand because they're sent as image content blocks, not as text fances. Non-image files are returned as nil.

Parameters:

  • input (String)

Returns:



47
48
49
50
51
52
53
54
55
# File 'lib/rubyn_code/cli/mention_expander.rb', line 47

def expand_images(input)
  return [] unless input.is_a?(String) && input.include?('@')

  scan(input).filter_map do |rel, abs|
    next unless LLM::ImageReader.image_extension?(rel)

    LLM::ImageReader.for_path(abs)
  end
end