5.7 KiB
5.7 KiB
AGENTS.md
Guidelines for agentic coding agents working in this repository.
Project Overview
rp is a Python CLI tool for rich-printing files (markdown, JSON, code, etc.) in the terminal. It uses rich for rendering and typer for the CLI interface.
- Language: Python 3.12+
- Package Manager: uv
- Entry Point:
rp→rp.cli:app - Source Location:
src/rp/
Build/Lint/Test Commands
Setup
uv sync # Install dependencies
Running the CLI
uv run rp <file> # Rich-print a file
uv run rp --help # Show help
uv run rp --version # Show version
echo '{"a":1}' | uv run rp # Read from stdin
Testing
This project does not yet have automated tests. When adding tests:
# Run all tests
uv run pytest
# Run a single test file
uv run pytest tests/test_detect.py
# Run a single test function
uv run pytest tests/test_detect.py::test_detect_json_from_content
# Run with verbose output
uv run pytest -v
Linting & Type Checking (recommended)
uv run ruff check src/ # Lint
uv run ruff format src/ # Format
uv run mypy src/ # Type check
Code Style Guidelines
Imports
Order imports using standard library → third-party → local grouping:
from __future__ import annotations
import sys
from pathlib import Path
from typing import Annotated
import typer
from rich.console import Console
from rp import __version__
from rp.detect import detect_type
- Always use
from __future__ import annotationsat the top for modern type hint syntax - Use explicit imports (no
from module import *) - Import
Pathfrompathlib, notos.path
Formatting
- Line length: 88 characters (ruff default)
- Quotes: Prefer double quotes for strings, single quotes inside f-strings if needed
- Trailing commas: Use in multi-line collections and function arguments
Type Hints
- Use modern union syntax:
str | Noneinstead ofOptional[str] - Use
Annotated[Type, typer.Option(...)]for CLI parameters - Always annotate function parameters and return types
- Use
dataclassfor simple data containers
def detect_type(
path: str | None = None,
content: str | None = None,
explicit_type: str | None = None,
) -> str:
...
Naming Conventions
- Modules: lowercase with underscores (
detect.py,render.py) - Classes: PascalCase (
RenderOptions) - Functions/Variables: snake_case (
detect_type,file_path) - Constants: UPPER_SNAKE_CASE (
EXTENSION_MAP,_FILENAME_MAP) - Private module-level: prefix with underscore (
_MARKDOWN_TYPES) - CLI parameter names: Avoid Python builtins (use
file_typenottype)
Error Handling
- Use
rich.Consolefor user-facing error messages with[red]Error:[/red]prefix - Exit with
typer.Exit(code=1)for errors,typer.Exit()for success - Catch specific exceptions, not bare
except:
try:
content = file.read_text(encoding="utf-8")
except UnicodeDecodeError:
console.print(f"[red]Error:[/red] Cannot read binary file: {file}")
raise typer.Exit(code=1)
except (PermissionError, OSError) as e:
console.print(f"[red]Error:[/red] {e}")
raise typer.Exit(code=1)
- For fallback behavior (e.g., unknown lexer), catch broadly but document why:
try:
renderable = Syntax(content, file_type, ...)
except Exception:
# Unknown lexer — fall back to plain text
renderable = Syntax(content, "text", ...)
CLI Patterns
- Use
typer.Option()with--flag/--no-flagfor boolean toggles - Use
Annotated[Type, typer.Option(...)]for all options - Set
no_args_is_help=Falsewhen stdin input is valid - Use callbacks for
--versionwithis_eager=True
Module Structure
Each module should have:
- Module docstring (one line)
from __future__ import annotations- Standard library imports
- Third-party imports
- Local imports
- Constants (module-level)
- Classes
- Functions
File Type Detection
When adding new file types:
- Add extension to
EXTENSION_MAPindetect.py - Use Pygments lexer name as the value
- For special filenames (e.g.,
Dockerfile), add to_FILENAME_MAP
Rendering
markdownandmd→ userich.markdown.Markdownjson→ userich.json.JSONwith fallback toSyntax- All other types → use
rich.syntax.Syntax
Pager Behavior
The pager prefers less -R --mouse when available (version 543+) for mouse scrolling. Falls back to Rich's SystemPager otherwise.
Version Sync
Version metadata must stay in sync between pyproject.toml and src/rp/__init__.py.
Project Structure
rich-viewer/
├── pyproject.toml # Project config, dependencies, entry point
├── uv.lock # Lockfile (commit this)
├── .gitignore
├── README.md
├── AGENTS.md # This file
└── src/
└── rp/
├── __init__.py # Version metadata
├── cli.py # Typer CLI entry point
├── detect.py # File type detection
├── render.py # Rendering logic
├── pager.py # Pager integration
└── py.typed # PEP 561 marker
Dependencies
rich>=13.0— Terminal rendering (Markdown, JSON, Syntax)typer>=0.12— CLI framework
Runtime dependencies. Dev dependencies: ruff>=0.9, mypy>=1.14.
Git Conventions
- Write clear, imperative commit messages
- Reference issues/PRs when applicable
- Keep commits focused (one logical change per commit)
Notes for Agents
- This is a small, focused CLI tool — prefer simplicity over abstraction
- The
src/layout is intentional for proper packaging - The
py.typedmarker indicates type hints are available to consumers - When in doubt, follow patterns from existing code in the same file