Module: Crimson::Tools::EditFile
- Defined in:
- lib/crimson/tools/edit_file.rb
Overview
Edit files by replacing strings, with single and multi-edit modes. Handles BOM, CRLF/LF line endings, and produces diffs.
Constant Summary collapse
- TOOL_NAME =
"edit_file"- PARAMS =
Tool parameter definitions.
{ path: { type: "string", description: "The path to the file to edit" }, old_string: { type: "string", description: "The exact string to find and replace (single edit mode)" }, new_string: { type: "string", description: "The string to replace it with (single edit mode)" }, replace_all: { type: "boolean", description: "Replace all occurrences (default: false)" }, edits: { type: "array", description: "Array of edits for multiple replacements in one call. Each edit has old_string, new_string, and optional replace_all.", items: { type: "object", properties: { old_string: { type: "string", description: "The exact string to find" }, new_string: { type: "string", description: "The replacement string" }, replace_all: { type: "boolean", description: "Replace all occurrences (default: false)" } }, required: %w[old_string new_string] } } }.freeze
- MUTATION_QUEUE =
FileMutationQueue.new
Class Method Summary collapse
-
.anthropic_definition ⇒ Hash
Anthropic-compatible tool definition.
-
.call(path:, old_string: nil, new_string: nil, replace_all: false, edits: nil) ⇒ String
Execute the tool.
-
.definition ⇒ Hash
OpenAI-compatible tool definition.
- .prepare_arguments(args) ⇒ Object private
Class Method Details
.anthropic_definition ⇒ Hash
Returns Anthropic-compatible tool definition.
48 49 50 |
# File 'lib/crimson/tools/edit_file.rb', line 48 def self.anthropic_definition Schema.build_anthropic(name: TOOL_NAME, description: "Replace strings in a file. Supports single edit or multiple edits in one call.", parameters: PARAMS, required: ["path"]) end |
.call(path:, old_string: nil, new_string: nil, replace_all: false, edits: nil) ⇒ String
Execute the tool.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/crimson/tools/edit_file.rb', line 59 def self.call(path:, old_string: nil, new_string: nil, replace_all: false, edits: nil) return "Error: No path provided" if path.nil? || path.strip.empty? = File.(path) MUTATION_QUEUE.with_file() do return "Error: File not found: #{path}" unless File.exist?() return "Error: Not a file: #{path}" unless File.file?() content = File.binread() has_bom = content.start_with?("\xEF\xBB\xBF") content = content.byteslice(3..) if has_bom content = content.force_encoding("UTF-8") line_ending = detect_line_ending(content) content = content.gsub("\r\n", "\n") if line_ending == :crlf old_content = content.dup if edits.is_a?(Array) && !edits.empty? count = 0 edits.each do |e| result = apply_edit(content, e["old_string"], e["new_string"], e["replace_all"]) return result[:error] if result[:error] content = result[:content] count += result[:count] end elsif old_string return "Error: No old_string provided" if old_string.nil? || old_string.empty? result = apply_edit(content, old_string, new_string, replace_all) return result[:error] if result[:error] content = result[:content] count = result[:count] else return "Error: Provide either old_string/new_string or edits array" end content = content.gsub("\n", "\r\n") if line_ending == :crlf content = "\xEF\xBB\xBF#{content}" if has_bom File.binwrite(, content) clean_old = old_content clean_new = has_bom ? content.byteslice(3..) : content diff = DiffUtil.format_diff(clean_old, clean_new, path) "Successfully edited #{path} (#{count} replacement#{'s' if count != 1})\n#{diff}" end rescue => e "Error editing file: #{e.}" end |
.definition ⇒ Hash
Returns OpenAI-compatible tool definition.
43 44 45 |
# File 'lib/crimson/tools/edit_file.rb', line 43 def self.definition Schema.build(name: TOOL_NAME, description: "Replace strings in a file. Supports single edit or multiple edits in one call.", parameters: PARAMS, required: ["path"]) end |
.prepare_arguments(args) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
34 35 36 37 38 39 40 |
# File 'lib/crimson/tools/edit_file.rb', line 34 def self.prepare_arguments(args) if args["edits"].is_a?(Array) args["edits"].each { |e| e["replace_all"] = !!e["replace_all"] if e.key?("replace_all") } end args["replace_all"] = !!args["replace_all"] if args.key?("replace_all") args end |