From 13defd6c89b36e76671f272608f0d8ed276b338e Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Fri, 16 Jan 2026 15:26:57 +0000 Subject: [PATCH] feat(cli): add reversed numbering to search results display Search results now display with reversed numbering where the most relevant result (shown last, closest to the command prompt) is numbered "1", while less relevant results appear earlier with higher numbers. This complements the reversed display order introduced in cb73f15. Assisted-by: Claude Code --- src/logsqueak/cli.py | 9 +- tests/integration/test_cli_search.py | 61 ++++++++++ .../test_cli_display_reversed_numbering.py | 109 ++++++++++++++++++ 3 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_cli_display_reversed_numbering.py diff --git a/src/logsqueak/cli.py b/src/logsqueak/cli.py index 18abef5..c294330 100644 --- a/src/logsqueak/cli.py +++ b/src/logsqueak/cli.py @@ -414,12 +414,17 @@ def _display_search_results(results: list[dict], graph_path: Path): Display search results in terminal-friendly format. Uses OSC 8 escape codes for clickable links and color coding for readability. + Results are displayed in reverse relevance order (least to most relevant), + but numbered in reverse (most relevant = 1). """ for idx, result in enumerate(results, 1): page_name = result["page_name"] confidence = result["confidence"] snippet = result["snippet"] + # Reverse numbering so most relevant result (shown last) gets number "1" + display_idx = len(results) - idx + 1 + # Create clickable logseq:// link using OSC 8 escape codes clickable_link = _create_clickable_link(page_name, graph_path) @@ -444,8 +449,8 @@ def _display_search_results(results: list[dict], graph_path: Path): # Fallback if no bullet content (shouldn't happen) snippet_display = "(no content preview)" - # Display result - click.echo(f"{idx}. {clickable_link}") + # Display result with reversed numbering + click.echo(f"{display_idx}. {clickable_link}") click.echo(f" Relevance: {confidence}%") click.echo(f" {snippet_display}") click.echo() # Blank line between results diff --git a/tests/integration/test_cli_search.py b/tests/integration/test_cli_search.py index a35e781..d9b9bee 100644 --- a/tests/integration/test_cli_search.py +++ b/tests/integration/test_cli_search.py @@ -229,3 +229,64 @@ def test_search_respects_top_k_config(temp_config, temp_graph_with_pages, use_sh # Should have at most 2 results result_count = result.output.count("\n1.") + result.output.count("\n2.") + result.output.count("\n3.") assert result_count <= 2 + + +def test_search_displays_reversed_numbering(temp_config, temp_graph_with_pages, use_shared_encoder, monkeypatch): + """Test that search displays results in reverse order with reversed numbering. + + Most relevant result should appear last (closest to command prompt) and be numbered "1". + Less relevant results should appear earlier with higher numbers. + """ + # Monkeypatch config path + monkeypatch.setenv("HOME", str(temp_config.parent.parent)) + config_home = temp_config.parent.parent / ".config" / "logsqueak" + config_home.mkdir(parents=True, exist_ok=True) + (config_home / "config.yaml").write_text(temp_config.read_text()) + (config_home / "config.yaml").chmod(0o600) + + runner = CliRunner() + result = runner.invoke(cli, ["search", "programming"]) + + # Print output for debugging + print(f"\n=== CLI OUTPUT ===") + print(result.output) + print(f"=== EXIT CODE: {result.exit_code} ===\n") + + if result.exit_code != 0: + print(f"=== EXCEPTION ===") + if result.exception: + import traceback + traceback.print_exception(type(result.exception), result.exception, result.exception.__traceback__) + print("=================\n") + + assert result.exit_code == 0 + + # Parse the output to find result numbers and their relevance scores + lines = result.output.split('\n') + results = [] + + for i, line in enumerate(lines): + # Look for numbered results (e.g., "1. PageName" or "2. PageName") + if line and line[0].isdigit() and '. ' in line: + number = int(line.split('.')[0]) + # Next line should have "Relevance: XX%" + if i + 1 < len(lines) and "Relevance:" in lines[i + 1]: + relevance_str = lines[i + 1].split("Relevance:")[1].strip().rstrip('%') + relevance = int(relevance_str) + results.append((number, relevance)) + + # Should have at least 2 results for meaningful test + assert len(results) >= 2, f"Expected at least 2 results, got {len(results)}" + + # Most relevant result should be last in the list + last_result = results[-1] + first_result = results[0] + + # The last result (most relevant) should have number "1" + assert last_result[0] == 1, f"Expected last result to be numbered 1, got {last_result[0]}" + + # The first result (least relevant) should have the highest number + assert first_result[0] == len(results), f"Expected first result to be numbered {len(results)}, got {first_result[0]}" + + # Relevance should decrease as we go from last to first (reversed display) + assert last_result[1] >= first_result[1], f"Expected last result ({last_result[1]}%) to be more relevant than first ({first_result[1]}%)" diff --git a/tests/unit/test_cli_display_reversed_numbering.py b/tests/unit/test_cli_display_reversed_numbering.py new file mode 100644 index 0000000..d71f287 --- /dev/null +++ b/tests/unit/test_cli_display_reversed_numbering.py @@ -0,0 +1,109 @@ +"""Unit test for reversed numbering in search results display.""" + +from pathlib import Path +from unittest.mock import patch, MagicMock +from io import StringIO + + +def test_display_search_results_reversed_numbering(): + """Test that search results are numbered in reverse (most relevant = 1).""" + from logsqueak.cli import _display_search_results + + # Create mock results (already in reversed order - least to most relevant) + results = [ + { + "page_name": "LeastRelevant", + "block_id": "id1", + "confidence": 50, + "snippet": "- Some content" + }, + { + "page_name": "MiddleRelevant", + "block_id": "id2", + "confidence": 75, + "snippet": "- Some other content" + }, + { + "page_name": "MostRelevant", + "block_id": "id3", + "confidence": 100, + "snippet": "- The best content" + } + ] + + graph_path = Path("/fake/graph") + + # Capture output + output_lines = [] + + def mock_echo(msg=''): + output_lines.append(msg) + + # Mock click.echo and the clickable link function + with patch('logsqueak.cli.click.echo', side_effect=mock_echo): + with patch('logsqueak.cli._create_clickable_link', side_effect=lambda name, path: name): + _display_search_results(results, graph_path) + + # Join output for easier analysis + output = '\n'.join(output_lines) + + # Parse the output to find numbered results + result_numbers = [] + for i, line in enumerate(output_lines): + if '. LeastRelevant' in line or '. MiddleRelevant' in line or '. MostRelevant' in line: + # Extract the number before the dot + number = int(line.split('.')[0]) + page_name = line.split('. ')[1] + result_numbers.append((number, page_name)) + + # Verify we found all 3 results + assert len(result_numbers) == 3, f"Expected 3 results, got {len(result_numbers)}" + + # Verify numbering + # First result shown (least relevant) should have highest number (3) + assert result_numbers[0] == (3, "LeastRelevant"), \ + f"Expected (3, 'LeastRelevant'), got {result_numbers[0]}" + + # Middle result should have number 2 + assert result_numbers[1] == (2, "MiddleRelevant"), \ + f"Expected (2, 'MiddleRelevant'), got {result_numbers[1]}" + + # Last result shown (most relevant) should have number 1 + assert result_numbers[2] == (1, "MostRelevant"), \ + f"Expected (1, 'MostRelevant'), got {result_numbers[2]}" + + print("✓ Numbering is correctly reversed: most relevant result numbered as 1") + + +def test_display_search_results_reversed_numbering_with_two_results(): + """Test reversed numbering with only 2 results.""" + from logsqueak.cli import _display_search_results + + results = [ + {"page_name": "Less", "block_id": "id1", "confidence": 60, "snippet": "- Content"}, + {"page_name": "More", "block_id": "id2", "confidence": 90, "snippet": "- Better content"} + ] + + graph_path = Path("/fake/graph") + output_lines = [] + + def mock_echo(msg=''): + output_lines.append(msg) + + with patch('logsqueak.cli.click.echo', side_effect=mock_echo): + with patch('logsqueak.cli._create_clickable_link', side_effect=lambda name, path: name): + _display_search_results(results, graph_path) + + # Find numbered results + result_numbers = [] + for line in output_lines: + if '. Less' in line or '. More' in line: + number = int(line.split('.')[0]) + page_name = line.split('. ')[1] + result_numbers.append((number, page_name)) + + # Verify + assert result_numbers[0] == (2, "Less"), f"Expected (2, 'Less'), got {result_numbers[0]}" + assert result_numbers[1] == (1, "More"), f"Expected (1, 'More'), got {result_numbers[1]}" + + print("✓ Two results correctly numbered: 2 (less relevant), 1 (more relevant)")