rank
¶
Full name: tenets.cli.commands.rank
rank¶
Rank command - show ranked files without content.
This module provides the rank command for the tenets CLI, which allows users to see which files are most relevant to their query without displaying the actual content of those files. This is useful for previewing what would be included in a full distill operation or for generating file lists for automation.
Classes¶
Functions¶
rank¶
rank(prompt: str = typer.Argument(..., help='Your query or task to rank files against'), path: Path = typer.Argument(Path(), help='Path to analyze (directory or files)'), format: str = typer.Option('markdown', '--format', '-f', help='Output format: markdown, json, xml, html, tree'), output: Optional[Path] = typer.Option(None, '--output', '-o', help='Save output to file instead of stdout'), mode: str = typer.Option('balanced', '--mode', '-m', help='Ranking mode: fast (keyword only), balanced (TF-IDF + structure), thorough (deep analysis)'), top: Optional[int] = typer.Option(None, '--top', '-t', help='Show only top N files'), min_score: Optional[float] = typer.Option(None, '--min-score', help='Minimum relevance score (0.0-1.0)'), max_files: Optional[int] = typer.Option(None, '--max-files', help='Maximum number of files to show'), tree_view: bool = typer.Option(False, '--tree', help='Show results as directory tree'), show_scores: bool = typer.Option(True, '--scores/--no-scores', help='Show relevance scores'), show_factors: bool = typer.Option(False, '--factors', help='Show ranking factor breakdown'), show_path: str = typer.Option('relative', '--path-style', help='Path display: relative, absolute, name'), include: Optional[str] = typer.Option(None, '--include', '-i', help="Include file patterns (e.g., '*.py,*.js')"), exclude: Optional[str] = typer.Option(None, '--exclude', '-e', help="Exclude file patterns (e.g., 'test_*,*.backup')"), include_tests: bool = typer.Option(False, '--include-tests', help='Include test files'), exclude_tests: bool = typer.Option(False, '--exclude-tests', help='Explicitly exclude test files'), no_git: bool = typer.Option(False, '--no-git', help='Disable git signals in ranking'), session: Optional[str] = typer.Option(None, '--session', '-s', help='Use session for stateful ranking'), show_stats: bool = typer.Option(False, '--stats', help='Show ranking statistics'), verbose: bool = typer.Option(False, '--verbose', '-v', help='Show detailed debug information'), copy: bool = typer.Option(False, '--copy', help='Copy file list to clipboard')) -> None
Rank files by relevance without showing their content.
This command runs the same intelligent ranking as 'distill' but only shows the list of relevant files, their scores, and optionally the ranking factors. Useful for understanding what files would be included in context or for feeding file lists to other tools.
PARAMETER | DESCRIPTION |
---|---|
prompt | The query or task to rank files against. TYPE: |
path | Path to analyze (directory or files). TYPE: |
format | Output format (markdown, json, xml, html, tree). TYPE: |
output | Optional file path to save output. TYPE: |
mode | Ranking algorithm mode (fast, balanced, thorough). TYPE: |
top | Show only top N files. TYPE: |
min_score | Minimum relevance score threshold (0.0-1.0). TYPE: |
max_files | Maximum number of files to display. TYPE: |
tree_view | Whether to show results as directory tree. TYPE: |
show_scores | Whether to display relevance scores. TYPE: |
show_factors | Whether to show ranking factor breakdown. TYPE: |
show_path | Path display style (relative, absolute, name). TYPE: |
include | Include file patterns (comma-separated). TYPE: |
exclude | Exclude file patterns (comma-separated). TYPE: |
include_tests | Whether to include test files. TYPE: |
exclude_tests | Whether to explicitly exclude test files. TYPE: |
no_git | Whether to disable git signals in ranking. TYPE: |
session | Optional session name for stateful ranking. TYPE: |
show_stats | Whether to show ranking statistics. TYPE: |
verbose | Whether to show detailed debug information. TYPE: |
copy | Whether to copy file list to clipboard. TYPE: |
RETURNS | DESCRIPTION |
---|---|
None | None |
RAISES | DESCRIPTION |
---|---|
SystemExit | On error with exit code 1. |
Examples:
Show top 10 most relevant files¶
tenets rank "implement OAuth2" --top 10
Show files above a score threshold¶
tenets rank "fix bug" . --min-score 0.3
Tree view with ranking factors¶
tenets rank "add caching" --tree --factors
Export as JSON for automation¶
tenets rank "review API" --format json -o ranked_files.json
Quick file list to clipboard¶
tenets rank "database queries" --top 20 --copy --no-scores
Source code in tenets/cli/commands/rank.py
def rank(
prompt: str = typer.Argument(..., help="Your query or task to rank files against"),
path: Path = typer.Argument(Path(), help="Path to analyze (directory or files)"),
# Output options
format: str = typer.Option(
"markdown", "--format", "-f", help="Output format: markdown, json, xml, html, tree"
),
output: Optional[Path] = typer.Option(
None, "--output", "-o", help="Save output to file instead of stdout"
),
# Ranking options
mode: str = typer.Option(
"balanced", # Use same default as distill command for consistency
"--mode",
"-m",
help="Ranking mode: fast (keyword only), balanced (TF-IDF + structure), thorough (deep analysis)",
),
top: Optional[int] = typer.Option(None, "--top", "-t", help="Show only top N files"),
min_score: Optional[float] = typer.Option(
None, "--min-score", help="Minimum relevance score (0.0-1.0)"
),
max_files: Optional[int] = typer.Option(
None, "--max-files", help="Maximum number of files to show"
),
# Display options
tree_view: bool = typer.Option(False, "--tree", help="Show results as directory tree"),
show_scores: bool = typer.Option(True, "--scores/--no-scores", help="Show relevance scores"),
show_factors: bool = typer.Option(False, "--factors", help="Show ranking factor breakdown"),
show_path: str = typer.Option(
"relative", "--path-style", help="Path display: relative, absolute, name"
),
# Filtering
include: Optional[str] = typer.Option(
None, "--include", "-i", help="Include file patterns (e.g., '*.py,*.js')"
),
exclude: Optional[str] = typer.Option(
None, "--exclude", "-e", help="Exclude file patterns (e.g., 'test_*,*.backup')"
),
include_tests: bool = typer.Option(False, "--include-tests", help="Include test files"),
exclude_tests: bool = typer.Option(
False, "--exclude-tests", help="Explicitly exclude test files"
),
# Features
no_git: bool = typer.Option(False, "--no-git", help="Disable git signals in ranking"),
session: Optional[str] = typer.Option(
None, "--session", "-s", help="Use session for stateful ranking"
),
# Info options
show_stats: bool = typer.Option(False, "--stats", help="Show ranking statistics"),
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed debug information"),
copy: bool = typer.Option(False, "--copy", help="Copy file list to clipboard"),
) -> None:
"""Rank files by relevance without showing their content.
This command runs the same intelligent ranking as 'distill' but only shows
the list of relevant files, their scores, and optionally the ranking factors.
Useful for understanding what files would be included in context or for
feeding file lists to other tools.
Args:
prompt: The query or task to rank files against.
path: Path to analyze (directory or files).
format: Output format (markdown, json, xml, html, tree).
output: Optional file path to save output.
mode: Ranking algorithm mode (fast, balanced, thorough).
top: Show only top N files.
min_score: Minimum relevance score threshold (0.0-1.0).
max_files: Maximum number of files to display.
tree_view: Whether to show results as directory tree.
show_scores: Whether to display relevance scores.
show_factors: Whether to show ranking factor breakdown.
show_path: Path display style (relative, absolute, name).
include: Include file patterns (comma-separated).
exclude: Exclude file patterns (comma-separated).
include_tests: Whether to include test files.
exclude_tests: Whether to explicitly exclude test files.
no_git: Whether to disable git signals in ranking.
session: Optional session name for stateful ranking.
show_stats: Whether to show ranking statistics.
verbose: Whether to show detailed debug information.
copy: Whether to copy file list to clipboard.
Returns:
None
Raises:
SystemExit: On error with exit code 1.
Examples:
# Show top 10 most relevant files
tenets rank "implement OAuth2" --top 10
# Show files above a score threshold
tenets rank "fix bug" . --min-score 0.3
# Tree view with ranking factors
tenets rank "add caching" --tree --factors
# Export as JSON for automation
tenets rank "review API" --format json -o ranked_files.json
# Quick file list to clipboard
tenets rank "database queries" --top 20 --copy --no-scores
"""
# Initialize timer
is_json_quiet: bool = format.lower() == "json" and not output
timer: CommandTimer = CommandTimer(console, is_json_quiet)
try:
timer.start("Initializing ranking...")
# Initialize tenets with same distiller pipeline
tenets_instance: Tenets = Tenets()
# Use the same distiller pipeline that the distill command uses
# This ensures consistent ranking behavior
# Show progress only for non-JSON formats
if format.lower() != "json" or output:
console.print(f"[yellow]Ranking files for: {prompt[:50]}...[/yellow]")
# Use distiller's ranking pipeline by calling rank_files directly
# This ensures we get the same sophisticated ranking as distill
result: Any = tenets_instance.rank_files(
prompt=prompt,
paths=[path] if path else None,
mode=mode,
include_patterns=include.split(",") if include else None,
exclude_patterns=exclude.split(",") if exclude else None,
include_tests=include_tests if include_tests else None,
exclude_tests=exclude_tests if exclude_tests else False,
explain=show_factors,
)
ranked_files: List[FileAnalysis] = result.files
# Apply threshold filtering if min_score is set
if min_score:
ranked_files = [
f for f in ranked_files if getattr(f, "relevance_score", 0) >= min_score
]
# Apply limits
if top:
ranked_files = ranked_files[:top]
if max_files:
ranked_files = ranked_files[:max_files]
# Format output
output_content: str = _format_ranked_files(
ranked_files,
format=format,
tree_view=tree_view,
show_scores=show_scores,
show_factors=show_factors,
show_path=show_path,
prompt=prompt,
stats=None, # Stats not available from rank_files yet
)
# Output results
if output:
output.write_text(output_content, encoding="utf-8")
console.print(f"[green]OK[/green] Saved ranking to {output}")
# Offer to open HTML in browser
if format == "html" and sys.stdin.isatty():
if click.confirm("\nWould you like to open it in your browser now?", default=False):
import webbrowser
file_path: Path = output.resolve()
webbrowser.open(file_path.as_uri())
console.print("[green]OK[/green] Opened in browser")
elif format in ["html", "xml", "json"]:
# For HTML/XML/JSON, auto-save to a default file like distill does
if sys.stdin.isatty(): # Interactive mode
import re
from datetime import datetime
# Create filename from prompt
safe_prompt: str = re.sub(r"[^\w\s-]", "", prompt[:30]).strip()
safe_prompt = re.sub(r"[-\s]+", "-", safe_prompt)
timestamp: str = datetime.now().strftime("%Y%m%d_%H%M%S")
# Determine file extension
ext: str = format.lower()
if ext == "html":
ext = "html"
elif ext == "xml":
ext = "xml"
else: # json
ext = "json"
default_file: Path = Path(f"tenets_rank_{safe_prompt}_{timestamp}.{ext}")
default_file.write_text(output_content, encoding="utf-8")
console.print(
f"[green]OK[/green] {format.upper()} output saved to [cyan]{default_file}[/cyan]"
)
console.print(f"[dim]File size:[/dim] {len(output_content):,} bytes")
# Offer to open in browser for HTML, or folder for XML/JSON
if format == "html":
if click.confirm(
"\nWould you like to open it in your browser now?", default=False
):
import webbrowser
file_path = default_file.resolve()
webbrowser.open(file_path.as_uri())
console.print("[green]OK[/green] Opened in browser")
else:
console.print(
"[cyan]Tip:[/cyan] Open the file in a browser or use --output to specify a different path"
)
# For XML/JSON, offer to open the folder
elif click.confirm(
f"\nWould you like to open the folder containing the {format.upper()} file?",
default=False,
):
import platform
import webbrowser
folder: Path = default_file.parent.resolve()
if platform.system() == "Windows":
import os
os.startfile(folder)
elif platform.system() == "Darwin": # macOS
import subprocess
subprocess.run(["open", folder], check=False)
else: # Linux
import subprocess
subprocess.run(["xdg-open", folder], check=False)
console.print(f"[green]OK[/green] Opened folder: {folder}")
else:
# Non-interactive mode: print raw output
print(output_content)
elif format == "markdown" or format == "tree":
console.print(output_content)
else:
print(output_content)
# Check if we should copy to clipboard
do_copy: bool = copy
try:
# Check config flag for auto-copy (similar to distill command)
cfg: Any = getattr(tenets_instance, "config", None)
if cfg and getattr(getattr(cfg, "output", None), "copy_on_rank", False):
do_copy = True
except Exception:
pass
# Copy to clipboard if requested or config enabled
if do_copy and pyperclip:
# Create simple file list for clipboard
clip_content: str
if show_scores:
clip_content = "\n".join(
f"{f.path} ({f.relevance_score:.3f})" for f in ranked_files
)
else:
clip_content = "\n".join(str(f.path) for f in ranked_files)
pyperclip.copy(clip_content)
console.print("[green]OK[/green] Copied file list to clipboard")
# Show stats if requested
if show_stats:
# Stats not available from rank_files yet
console.print("[yellow]Stats not available yet[/yellow]")
timer.stop()
except Exception as e:
console.print(f"[red]Error:[/red] {e!s}")
if verbose:
console.print_exception()
sys.exit(1)