Class: RubyLLM::Toolbox::Tools::ReplaceInFiles

Inherits:
Base
  • Object
show all
Defined in:
lib/ruby_llm/toolbox/tools/replace_in_files.rb

Overview

EXEC. Find-and-replace across the files in fs_root that match a glob. Literal by default; set regex: true for a pattern (with backreferences in the replacement). Binary files and ignored directories are skipped, the pattern runs under a ReDoS timeout, and every path is jailed to fs_root. Use dry_run: true to preview the impact without writing.

Constant Summary collapse

MAX_BYTES =
5 * 1024 * 1024
MAX_LISTED =
100

Instance Attribute Summary

Attributes inherited from Base

#config

Instance Method Summary collapse

Methods inherited from Base

#call, exec_tool!, exec_tool?, #initialize, #name

Constructor Details

This class inherits a constructor from RubyLLM::Toolbox::Base

Instance Method Details

#execute(pattern:, replacement: "", glob: "**/*", regex: false, ignore_case: false, dry_run: false) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/ruby_llm/toolbox/tools/replace_in_files.rb', line 44

def execute(pattern:, replacement: "", glob: "**/*", regex: false, ignore_case: false, dry_run: false)
  return error("pattern must not be empty", code: :empty_pattern) if pattern.to_s.empty?

  matcher = build_matcher(pattern, regex, ignore_case)
  jail = Safety::PathJail.new(config.fs_root)
  root = File.realpath(config.fs_root)

  changed = []
  total = 0
  candidate_files(glob, jail, root).each do |abs, rel|
    content = File.read(abs)
    next unless text?(content)

    count, updated = substitute(content, matcher, replacement.to_s, regex)
    next if count.zero?

    total += count
    changed << [rel, count]
    File.write(abs, updated) unless dry_run
  end

  format_summary(changed, total, dry_run)
rescue Safety::PathJail::Jailbreak => e
  error(e.message, code: :path_denied)
rescue Regexp::TimeoutError
  error("pattern timed out (possible ReDoS); narrow the pattern", code: :regex_timeout)
rescue RegexpError => e
  error("invalid regular expression: #{e.message}", code: :bad_regex)
end