Skip to content

Commit c6ea061

Browse files
feat(cli): add enhanced pretty format for webset-list
Replace JSON pretty-print with human-readable text format for webset-list --output-format pretty. The new format shows: - Header with count and pagination info - Each webset with aligned labels (ID, Status, Title, etc.) - Searches with status indicators (✓/→/✗) - Enrichments, monitors, imports counts Text format remains minimal, pretty format now provides detailed readable output matching pattern from webset-search-get. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 0565dc5 commit c6ea061

3 files changed

Lines changed: 128 additions & 1 deletion

File tree

lib/exa/cli/formatters/webset_formatter.rb

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def self.format_collection(collection, output_format)
2424
when "json"
2525
JSON.generate(collection.to_h)
2626
when "pretty"
27-
JSON.pretty_generate(collection.to_h)
27+
format_collection_as_pretty(collection)
2828
when "text"
2929
format_collection_as_text(collection)
3030
when "toon"
@@ -66,6 +66,66 @@ def self.format_collection_as_text(collection)
6666
lines.join("\n")
6767
end
6868
private_class_method :format_collection_as_text
69+
70+
def self.format_collection_as_pretty(collection)
71+
lines = []
72+
73+
# Header with count and pagination info
74+
header = "Websets (#{collection.data.length} items)"
75+
header += " - Page #{collection.has_more ? '1 of many' : '1 of 1'}" if collection.data.any?
76+
lines << header
77+
78+
if collection.has_more
79+
lines << "Next Cursor: #{collection.next_cursor}"
80+
end
81+
82+
lines << ""
83+
84+
# Format each webset
85+
collection.data.each_with_index do |ws, idx|
86+
lines << "" if idx > 0 # Blank line between websets
87+
88+
lines << "Webset ID: #{ws['id']}"
89+
lines << "Status: #{ws['status']}"
90+
lines << "Title: #{ws['title']}" if ws['title']
91+
lines << "External ID: #{ws['externalId']}" if ws['externalId']
92+
lines << "Created: #{ws['createdAt']}" if ws['createdAt']
93+
lines << "Updated: #{ws['updatedAt']}" if ws['updatedAt']
94+
95+
# Searches
96+
if ws['searches'] && !ws['searches'].empty?
97+
lines << ""
98+
lines << "Searches (#{ws['searches'].length}):"
99+
ws['searches'].each do |search|
100+
status_indicator = case search['status']
101+
when 'completed' then '✓'
102+
when 'running' then '→'
103+
when 'failed' then '✗'
104+
else '•'
105+
end
106+
lines << " #{status_indicator} #{search['query']} (#{search['status']})" if search['query']
107+
end
108+
end
109+
110+
# Enrichments
111+
if ws['enrichments'] && !ws['enrichments'].empty?
112+
lines << "Enrichments: #{ws['enrichments'].length}"
113+
end
114+
115+
# Monitors
116+
if ws['monitors'] && !ws['monitors'].empty?
117+
lines << "Monitors: #{ws['monitors'].length}"
118+
end
119+
120+
# Imports
121+
if ws['imports'] && !ws['imports'].empty?
122+
lines << "Imports: #{ws['imports'].length}"
123+
end
124+
end
125+
126+
lines.join("\n")
127+
end
128+
private_class_method :format_collection_as_pretty
69129
end
70130
end
71131
end

test/cli/formatters/webset_formatter_test.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,49 @@ def test_format_collection_toon_format_returns_toon_string
6464
json_output = Exa::CLI::Formatters::WebsetFormatter.format_collection(collection, "json")
6565
assert output.length < json_output.length
6666
end
67+
68+
def test_formats_collection_as_pretty
69+
webset_data = {
70+
"id" => "ws_123",
71+
"object" => "webset",
72+
"status" => "idle",
73+
"title" => "Test Webset",
74+
"searches" => [
75+
{"id" => "search_1", "query" => "test query", "status" => "completed"}
76+
],
77+
"enrichments" => [{"id" => "enrich_1"}],
78+
"monitors" => [],
79+
"imports" => [],
80+
"externalId" => "ext_123",
81+
"createdAt" => "2025-01-01T00:00:00Z",
82+
"updatedAt" => "2025-01-01T00:00:00Z"
83+
}
84+
85+
collection = Exa::Resources::WebsetCollection.new(
86+
data: [webset_data],
87+
has_more: true,
88+
next_cursor: "cursor_123"
89+
)
90+
91+
output = Exa::CLI::Formatters::WebsetFormatter.format_collection(collection, "pretty")
92+
93+
# Verify it's NOT JSON (should not be parseable as JSON)
94+
assert_raises(JSON::ParserError) { JSON.parse(output) }
95+
96+
# Verify presence of key fields
97+
assert_includes output, "ws_123"
98+
assert_includes output, "idle"
99+
assert_includes output, "Test Webset"
100+
assert_includes output, "test query"
101+
assert_includes output, "completed"
102+
assert_includes output, "2025-01-01T00:00:00Z"
103+
104+
# Verify pagination info
105+
assert_includes output, "1 items"
106+
assert_includes output, "cursor_123"
107+
108+
# Verify it has structured formatting (not just plain text dump)
109+
assert_includes output, "Webset ID:"
110+
assert_includes output, "Status:"
111+
end
67112
end

test/integration/websets_cli_integration_test.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,28 @@ def test_webset_list_pagination
301301
assert result2["data"].is_a?(Array)
302302
end
303303

304+
# Test webset-list with pretty format
305+
def test_webset_list_pretty_format
306+
skip_if_no_api_key
307+
308+
command = "bundle exec exe/exa-ai webset-list --limit 2 --output-format pretty"
309+
stdout, _stderr, status = run_command(command)
310+
311+
assert status.success?, "webset-list with pretty format should succeed"
312+
313+
# Verify it's NOT JSON (should not be parseable)
314+
assert_raises(JSON::ParserError) { parse_json_output(stdout) }
315+
316+
# Verify presence of expected human-readable format
317+
assert_includes stdout, "Websets"
318+
assert_includes stdout, "items"
319+
assert_includes stdout, "Webset ID:"
320+
assert_includes stdout, "Status:"
321+
322+
# Should have at least one webset listed
323+
assert_match(/webset_\w+/, stdout)
324+
end
325+
304326
# Test webset-update command
305327
def test_webset_update
306328
skip_if_no_api_key

0 commit comments

Comments
 (0)