Class: Rubino::Tools::EditTool
- Inherits:
-
Base
- Object
- Base
- Rubino::Tools::EditTool
show all
- Defined in:
- lib/rubino/tools/edit_tool.rb
Overview
Tool for performing exact string replacements in files. Replaces a specific old string with a new string - more precise than full file writes.
Instance Attribute Summary
Attributes inherited from Base
#cancel_token, #read_tracker, #stream_chunk, #stream_kind
Instance Method Summary
collapse
Methods inherited from Base
#cancellation_requested?, #config_key, #emit_chunk, #risky?, #to_tool_definition, workspace_root, workspace_roots
Instance Method Details
#call(arguments) ⇒ Object
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
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
111
112
113
114
115
116
117
118
119
120
|
# File 'lib/rubino/tools/edit_tool.rb', line 48
def call(arguments)
file_path, old_string, new_string, replace_all = parse_args(arguments)
if (guard = guard_args(old_string, new_string))
return guard
end
expanded = expand_workspace_path(file_path)
return workspace_violation_message(file_path) unless within_workspace?(expanded)
return "Error: File not found: #{file_path}" unless File.exist?(expanded)
if (gate = read_gate_error(expanded, file_path, verb: "edit"))
return gate
end
content = read_for_edit(expanded)
old_bytes = to_match_bytes(old_string)
new_bytes = to_match_bytes(new_string)
unless content.include?(old_bytes)
@read_tracker&.note_edit_failure(expanded)
return "Error: old_string not found in file content. " \
"Make sure the text matches exactly including whitespace."
end
count = content.scan(old_bytes).size
if count > 1 && !replace_all
return "Error: Found #{count} matches for old_string. " \
"Provide more surrounding context to make it unique, " \
"or set replace_all: true to replace all occurrences."
end
new_content = replace_literal(content, old_bytes, new_bytes, replace_all)
Util::AtomicFile.write_atomic(expanded, new_content)
@read_tracker&.note_write(expanded, new_content)
replaced_count = replace_all ? count : 1
added = new_string.to_s.lines.size
removed = old_string.to_s.lines.size
{ output: "Edit applied: #{replaced_count} replacement(s) in #{file_path}",
metrics: "#{replaced_count} replacement#{"s" if replaced_count != 1} · " \
"+#{added * replaced_count} −#{removed * replaced_count}",
body: build_diff_preview(old_string, new_string, replaced_count),
body_kind: :diff }
rescue StandardError => e
"Error editing #{file_path}: #{e.message}"
end
|
#description ⇒ Object
12
13
14
15
16
17
|
# File 'lib/rubino/tools/edit_tool.rb', line 12
def description
"Perform exact string replacement in a file. " \
"Specify the old text to find and the new text to replace it with. " \
"The old text must match exactly (including whitespace/indentation). " \
"Use replace_all to replace all occurrences."
end
|
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
# File 'lib/rubino/tools/edit_tool.rb', line 19
def input_schema
{
type: "object",
properties: {
file_path: {
type: "string",
description: "The path to the file to edit"
},
old_string: {
type: "string",
description: "The exact text to find and replace"
},
new_string: {
type: "string",
description: "The text to replace it with"
},
replace_all: {
type: "boolean",
description: "Replace all occurrences (default: false, replaces first only)"
}
},
required: %w[file_path old_string new_string]
}
end
|
#name ⇒ Object
8
9
10
|
# File 'lib/rubino/tools/edit_tool.rb', line 8
def name
"edit"
end
|
#risk_level ⇒ Object
44
45
46
|
# File 'lib/rubino/tools/edit_tool.rb', line 44
def risk_level
:medium
end
|