diff --git a/lib/roast/cogs/agent/providers/claude/tool_use.rb b/lib/roast/cogs/agent/providers/claude/tool_use.rb index 0d6404a5..f6354f2d 100644 --- a/lib/roast/cogs/agent/providers/claude/tool_use.rb +++ b/lib/roast/cogs/agent/providers/claude/tool_use.rb @@ -166,6 +166,33 @@ def format_write "WRITE #{file_path} \"#{preview}\" (+#{count} #{line_label})" end + # Formats an Edit tool-use line. + # + # Input fields: + # :file_path (String) – absolute path to edit [required] + # :old_string (String) – text being replaced [required] + # :new_string (String) – replacement text [required] + # :replace_all (bool) – replace every occurrence [optional] + # + # Output: "EDIT (- + lines)", where and + # are the line counts of :old_string and :new_string. The + # count is always labeled " lines"; " · replace all" is appended + # when :replace_all is set. + # + # Examples: + # EDIT /app/models/user.rb (-6 +6 lines) + # EDIT /config/routes.rb (-1 +2 lines · replace all) + # + #: () -> String + def format_edit + file_path, old_string, new_string = input.values_at(:file_path, :old_string, :new_string) + old_count = old_string.to_s.lines.length + new_count = new_string.to_s.lines.length + details = "-#{old_count} +#{new_count} lines" + details = "#{details} · replace all" if input[:replace_all] + "EDIT #{file_path} (#{details})" + end + #: () -> String def format_unknown "UNKNOWN [#{name}] #{input.inspect}" diff --git a/test/roast/cogs/agent/providers/claude/tool_use_test.rb b/test/roast/cogs/agent/providers/claude/tool_use_test.rb index 905ac5a6..1af18b43 100644 --- a/test/roast/cogs/agent/providers/claude/tool_use_test.rb +++ b/test/roast/cogs/agent/providers/claude/tool_use_test.rb @@ -193,6 +193,41 @@ class ToolUseTest < ActiveSupport::TestCase assert_equal "WRITE /a.rb \"#{truncated}\" (+1 line)", output end + # format_edit + + test "format_edit shows the line counts of a single-line replacement" do + tool_use = ToolUse.new(name: :edit, input: { file_path: "/a.rb", old_string: "foo", new_string: "bar" }) + + output = tool_use.format + + assert_equal "EDIT /a.rb (-1 +1 lines)", output + end + + test "format_edit counts the lines spanned by each string" do + tool_use = ToolUse.new(name: :edit, input: { file_path: "/a.rb", old_string: "a\nb", new_string: "x" }) + + output = tool_use.format + + assert_equal "EDIT /a.rb (-2 +1 lines)", output + end + + test "format_edit appends replace all when the flag is set" do + input = { file_path: "/a.rb", old_string: "foo", new_string: "bar", replace_all: true } + tool_use = ToolUse.new(name: :edit, input: input) + + output = tool_use.format + + assert_equal "EDIT /a.rb (-1 +1 lines · replace all)", output + end + + test "format_edit shows zero counts when the strings are absent" do + tool_use = ToolUse.new(name: :edit, input: { file_path: "/a.rb" }) + + output = tool_use.format + + assert_equal "EDIT /a.rb (-0 +0 lines)", output + end + test "format calls format_unknown for unknown tool" do tool_use = ToolUse.new(name: :unknown_tool, input: { arg: "value" })