Class: RubyLLM::Toolbox::Tools::EditFile
- Defined in:
- lib/ruby_llm/toolbox/tools/edit_file.rb
Overview
EXEC. The core editing primitive: replace an exact substring in a file.
By default old_string must match EXACTLY ONCE — if it’s missing the edit fails, and if it’s ambiguous (appears more than once) the edit also fails rather than guessing. Set replace_all to change every occurrence. This is the same contract coding agents rely on from a str_replace tool: it makes edits deterministic and refuses to silently do the wrong thing.
Constant Summary collapse
- MAX_BYTES =
10 * 1024 * 1024
Instance Attribute Summary
Attributes inherited from Base
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(path:, old_string:, new_string:, replace_all: false) ⇒ Object
39 40 41 42 43 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 |
# File 'lib/ruby_llm/toolbox/tools/edit_file.rb', line 39 def execute(path:, old_string:, new_string:, replace_all: false) old_s = old_string.to_s new_s = new_string.to_s return error("old_string must not be empty", code: :empty_match) if old_s.empty? return error("old_string and new_string are identical; nothing to do", code: :no_change) if old_s == new_s jail = Safety::PathJail.new(config.fs_root) real = jail.resolve(path) return error("not a file: #{path}", code: :not_a_file) unless File.file?(real) return error("file too large (> #{MAX_BYTES} bytes)", code: :too_large) if File.size(real) > MAX_BYTES original = File.read(real).scrub count = original.scan(old_string_regexp(old_s)).size return error("old_string not found in #{path}", code: :not_found) if count.zero? if count > 1 && !replace_all return error("old_string is ambiguous: #{count} matches in #{path}. " \ "Add surrounding context to make it unique, or set replace_all.", code: :ambiguous) end updated = if replace_all original.gsub(old_s) { new_s } else original.sub(old_s) { new_s } end File.write(real, updated) line = original[0...original.index(old_s)].count("\n") + 1 "Edited #{path} (#{count} replacement#{count == 1 ? '' : 's'}, " \ "#{original.bytesize} -> #{updated.bytesize} bytes, first change at line #{line})" rescue Safety::PathJail::Jailbreak => e error(e., code: :path_denied) end |