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 e0ddcaf7..f848f2db 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 @@ -195,6 +195,23 @@ def format_find details.any? ? "FIND #{pattern} (#{details.join(", ")})" : "FIND #{pattern}" end + # Formats an ls tool call. + # + # Input fields: + # :path (String) – directory to list [optional] + # + # Output: "LS " – a missing path renders the bare "LS". + # + # Examples: + # LS lib/roast + # LS + # + #: () -> String + def format_ls + path = arguments[:path] + path.to_s.empty? ? "LS" : "LS #{path}" + 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 100b4f31..fadab033 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 @@ -165,6 +165,24 @@ def format_find ok_line("#{count} #{"path".pluralize(count)}") end + # Formats an ls tool result. + # + # Content: directory entries, one per line. + # + # Output: "LS OK " – is the number of non-blank + # lines (pluralized). + # + # Examples: + # LS OK 8 entries + # LS OK 1 entry + # LS OK 0 entries + # + #: () -> String + def format_ls + count = content.to_s.lines.map(&:strip).reject(&:empty?).length + ok_line("#{count} #{"entry".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 4113178f..25baabe0 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 @@ -178,6 +178,16 @@ class ToolCallMessageTest < ActiveSupport::TestCase assert_equal "FIND *.rb", msg.format end + test "format renders LS with the path" do + msg = ToolCallMessage.new(id: "1", name: "ls", arguments: { path: "lib/roast" }) + assert_equal "LS lib/roast", msg.format + end + + test "format renders a bare LS when the path is missing" do + msg = ToolCallMessage.new(id: "1", name: "ls", arguments: {}) + assert_equal "LS", 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 a5d5534c..a690ba78 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 @@ -203,6 +203,39 @@ def setup assert_equal "FIND OK 0 paths", msg.format(@context) end + test "format summarizes ls output with an entry count" do + msg = ToolResultMessage.new( + tool_call_id: "1", + tool_name: "ls", + content: "lib\ntest\nREADME.md", + is_error: false, + ) + + assert_equal "LS OK 3 entries", msg.format(@context) + end + + test "format pluralizes a single ls entry" do + msg = ToolResultMessage.new( + tool_call_id: "1", + tool_name: "ls", + content: "README.md", + is_error: false, + ) + + assert_equal "LS OK 1 entry", msg.format(@context) + end + + test "format reports zero ls entries when there is no output" do + msg = ToolResultMessage.new( + tool_call_id: "1", + tool_name: "ls", + content: nil, + is_error: false, + ) + + assert_equal "LS OK 0 entries", msg.format(@context) + end + test "format renders NAME ERROR with the message for an error result" do msg = ToolResultMessage.new( tool_call_id: "1",