From 621fd973538e3dd46f6096180fe6169ea618ae6b Mon Sep 17 00:00:00 2001 From: Lasmar Khalifa Date: Thu, 2 Jul 2026 12:15:05 -0400 Subject: [PATCH] add find tool formatters --- .../pi/messages/tool_call_message.rb | 26 +++++++++++++++ .../pi/messages/tool_result_message.rb | 18 ++++++++++ .../pi/messages/tool_call_message_test.rb | 24 ++++++++++++++ .../pi/messages/tool_result_message_test.rb | 33 +++++++++++++++++++ 4 files changed, 101 insertions(+) diff --git a/lib/roast/cogs/agent/providers/pi/messages/tool_call_message.rb b/lib/roast/cogs/agent/providers/pi/messages/tool_call_message.rb index b4423bb9..e0ddcaf7 100644 --- a/lib/roast/cogs/agent/providers/pi/messages/tool_call_message.rb +++ b/lib/roast/cogs/agent/providers/pi/messages/tool_call_message.rb @@ -169,6 +169,32 @@ def format_grep details.any? ? "#{base} (#{details.join(", ")})" : base end + # Formats a find tool call. + # + # Input fields: + # :pattern (String) – filename glob to match [required] + # :path (String) – directory to search in [optional] + # :limit (Integer) – max number of results [optional] + # + # Output: "FIND (path: , limit: )" – the "path:" + # and "limit:" details are each included only when present and joined + # with ", " inside a single trailing "(...)". + # + # Examples: + # FIND *.rb (path: lib, limit: 50) + # FIND *.rb (limit: 50) + # FIND *.rb + # + #: () -> String + def format_find + pattern, path, limit = arguments.values_at(:pattern, :path, :limit) + details = [ + ("path: #{path}" if path.present?), + ("limit: #{limit}" if limit.present?), + ].compact + details.any? ? "FIND #{pattern} (#{details.join(", ")})" : "FIND #{pattern}" + end + # Formats a tool call for which Roast has no dedicated formatter. # # Output: " : , ..." – the upcased tool name, then each diff --git a/lib/roast/cogs/agent/providers/pi/messages/tool_result_message.rb b/lib/roast/cogs/agent/providers/pi/messages/tool_result_message.rb index cb99c5bd..100b4f31 100644 --- a/lib/roast/cogs/agent/providers/pi/messages/tool_result_message.rb +++ b/lib/roast/cogs/agent/providers/pi/messages/tool_result_message.rb @@ -147,6 +147,24 @@ def format_grep ok_line("#{count} #{"match".pluralize(count)}", note) end + # Formats a find tool result. + # + # Content: matching paths, one per line. + # + # Output: "FIND OK " – is the number of non-blank + # lines (pluralized). + # + # Examples: + # FIND OK 12 paths + # FIND OK 1 path + # FIND OK 0 paths + # + #: () -> String + def format_find + count = content.to_s.lines.map(&:strip).reject(&:empty?).length + ok_line("#{count} #{"path".pluralize(count)}") + end + # Formats a result for which Roast has no dedicated formatter. # # Content: the tool's output text. diff --git a/test/roast/cogs/agent/providers/pi/messages/tool_call_message_test.rb b/test/roast/cogs/agent/providers/pi/messages/tool_call_message_test.rb index 64c9aa39..4113178f 100644 --- a/test/roast/cogs/agent/providers/pi/messages/tool_call_message_test.rb +++ b/test/roast/cogs/agent/providers/pi/messages/tool_call_message_test.rb @@ -154,6 +154,30 @@ class ToolCallMessageTest < ActiveSupport::TestCase assert_equal "GREP \"#{"x" * (ToolCallMessage::TRUNCATE_LIMIT - 3)}...\" #{long}", msg.format end + test "format renders FIND with the search path but no limit" do + msg = ToolCallMessage.new( + id: "1", + name: "find", + arguments: { pattern: "*.rb", path: "lib" }, + ) + assert_equal "FIND *.rb (path: lib)", msg.format + end + + test "format renders FIND with the path and limit joined in one parenthetical" do + msg = ToolCallMessage.new(id: "1", name: "find", arguments: { pattern: "*.rb", path: "lib", limit: 50 }) + assert_equal "FIND *.rb (path: lib, limit: 50)", msg.format + end + + test "format renders FIND with the limit but no path" do + msg = ToolCallMessage.new(id: "1", name: "find", arguments: { pattern: "*.rb", limit: 50 }) + assert_equal "FIND *.rb (limit: 50)", msg.format + end + + test "format renders FIND with only the pattern" do + msg = ToolCallMessage.new(id: "1", name: "find", arguments: { pattern: "*.rb" }) + assert_equal "FIND *.rb", msg.format + end + test "format renders an unhandled tool as NAME key: value, ..." do msg = ToolCallMessage.new( id: "1", diff --git a/test/roast/cogs/agent/providers/pi/messages/tool_result_message_test.rb b/test/roast/cogs/agent/providers/pi/messages/tool_result_message_test.rb index 35e76019..a5d5534c 100644 --- a/test/roast/cogs/agent/providers/pi/messages/tool_result_message_test.rb +++ b/test/roast/cogs/agent/providers/pi/messages/tool_result_message_test.rb @@ -170,6 +170,39 @@ def setup assert_equal "GREP OK 1 match · NOTE results truncated", msg.format(@context) end + test "format summarizes find output with a path count" do + msg = ToolResultMessage.new( + tool_call_id: "1", + tool_name: "find", + content: "lib/roast.rb\nlib/roast/version.rb\nlib/roast/cog.rb", + is_error: false, + ) + + assert_equal "FIND OK 3 paths", msg.format(@context) + end + + test "format pluralizes a single find path" do + msg = ToolResultMessage.new( + tool_call_id: "1", + tool_name: "find", + content: "lib/roast.rb", + is_error: false, + ) + + assert_equal "FIND OK 1 path", msg.format(@context) + end + + test "format reports zero find paths when there is no output" do + msg = ToolResultMessage.new( + tool_call_id: "1", + tool_name: "find", + content: nil, + is_error: false, + ) + + assert_equal "FIND OK 0 paths", msg.format(@context) + end + test "format renders NAME ERROR with the message for an error result" do msg = ToolResultMessage.new( tool_call_id: "1",