This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
arXiv Explorer is a personalized paper recommendation and management system with a CLI interface. It uses TF-IDF-based content filtering combined with category priorities, keyword matching, and recency scoring to recommend papers from arXiv.
uv sync # Install dependencies
uv run axp --help # Test CLIuv run axp daily --days 7 --limit 10 # Get daily papers
uv run axp search "quantum computing" # Search papers
uv run axp prefs add-category hep-ph # Add category preferenceuv run pytest # Run all tests
uv run pytest tests/test_recommendation.py # Run specific test
uv run pytest --cov # Run with coverageThe codebase follows a clean 3-layer architecture:
Core Layer (src/arxiv_explorer/core/):
models.py: Immutable dataclasses for all entities (Paper, PreferredCategory, ReadingList, etc.)database.py: SQLite schema and connection management with context managersconfig.py: Global configuration with XDG-compliant paths
Services Layer (src/arxiv_explorer/services/):
arxiv_client.py: arXiv API client with rate limiting (3s delays, HTTPS required)recommendation.py: TF-IDF recommendation engine (singleton pattern viaget_recommendation_engine())preference_service.py: User preference CRUD operationspaper_service.py: Orchestrates arxiv_client and recommendation enginesummarization.py: Gemini CLI integration with SQLite cachingreading_list_service.py,notes_service.py: Feature-specific services
CLI Layer (src/arxiv_explorer/cli/):
main.py: Typer app entry point, registers all commands- Individual command modules:
daily.py,search.py,preferences.py,lists.py,notes.py,export.py - All CLI modules use
invoke_without_command=Truepattern for smart defaults
Recommendation Algorithm (services/recommendation.py):
- Builds user profile from liked papers using TF-IDF vectorization
- Scores papers using weighted combination:
- Content similarity: 0.5 (cosine similarity to user profile)
- Category matching: 0.2 (normalized by priority)
- Keyword matching: 0.1 (weighted keyword presence)
- Recency bonus: 0.05 (papers < 30 days old)
- Configurable weights in
core/config.py
Smart CLI Defaults:
axp prefs→ shows preferences (no subcommand needed)axp list→ shows all reading listsaxp note→ shows all notesaxp show→ shows recently liked papers- Implemented via
@app.callback()checkingctx.invoked_subcommand
Database Design:
- Single SQLite DB at
~/.config/arxiv-explorer/explorer.db - Optional read-only access to arxivterminal DB at
~/.local/share/arxivterminal/papers.db - Tables: preferred_categories, paper_interactions, paper_summaries, reading_lists, reading_list_papers, paper_notes, keyword_interests
- Uses sqlite3.Row for dict-like access
Caching Strategy:
- Gemini summaries cached in
paper_summariestable to avoid redundant API calls - TF-IDF vectorizer fitted once per session, reused for all scoring
- MUST use HTTPS:
https://export.arxiv.org/api/query(HTTP redirects) - Rate limiting required: 3-second delays between requests
- Disable proxy:
httpx.Client(trust_env=False)to avoid socks:// proxy errors - Parsing: Uses feedparser for Atom feed parsing
When adding new CLI commands:
- Use
typer.Argument(None, ...)for optional args with helpful defaults - Add
invoke_without_command=Truefor command groups - Implement
@app.callback()to handle no-subcommand case - Provide helpful error messages with example commands
- Services instantiate their own dependencies (e.g.,
PaperServicecreatesArxivClient) - Use context managers for database connections:
with get_connection() as conn: - Return domain models (dataclasses), not raw database rows
- Keep services stateless except for caching (recommendation engine)
New paper interaction type:
- Add enum to
InteractionTypeincore/models.py - Add methods to
PreferenceService - Add CLI command in appropriate module
New recommendation factor:
- Modify scoring weights in
Configdataclass - Update
score_papers()inRecommendationEngine - Weight should sum to ≤1.0 with existing weights
New export format:
- Add elif branch in
export.pycommand functions - Follow existing pattern: fetch data → format → output/save
- Config/DB:
~/.config/arxiv-explorer/explorer.db - Integration:
~/.local/share/arxivterminal/papers.db(read-only, optional) - No cloud sync: All data local-only
Gemini CLI:
- Called via subprocess:
gemini -p <prompt> - Expects JSON output with
summary_shortandkey_findingsfields - Handles both plain JSON and markdown code blocks (```json)
arxivterminal:
- Can read from existing arxivterminal database (if present)
- Connection via
get_arxivterminal_connection()with read-only mode
arxiv-doc-builder:
- Integration in
export.py→export_markdowncommand - Calls conversion script via
uv runsubprocess