litresearch 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ # At least one LLM provider key is required.
2
+ OPENAI_API_KEY=
3
+ ANTHROPIC_API_KEY=
4
+ OPENROUTER_API_KEY=
5
+
6
+ # Optional: higher Semantic Scholar rate limits.
7
+ S2_API_KEY=
@@ -0,0 +1,13 @@
1
+ name: CI
2
+ on:
3
+ push:
4
+ branches: [main]
5
+ pull_request:
6
+ branches: [main]
7
+ jobs:
8
+ check:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: astral-sh/setup-uv@v5
13
+ - run: uv run nox
@@ -0,0 +1,16 @@
1
+ name: Release
2
+ on:
3
+ push:
4
+ tags: ["v*"]
5
+ jobs:
6
+ publish:
7
+ runs-on: ubuntu-latest
8
+ permissions:
9
+ id-token: write
10
+ contents: write
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: astral-sh/setup-uv@v5
14
+ - run: uv run nox
15
+ - run: uv build
16
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,39 @@
1
+ # OS
2
+ .DS_Store
3
+ Thumbs.db
4
+ *.swp
5
+ *.swo
6
+ *~
7
+
8
+ # IDEs
9
+ .vscode/
10
+ .idea/
11
+ *.sublime-project
12
+ *.sublime-workspace
13
+
14
+ # Python
15
+ __pycache__/
16
+ *.py[cod]
17
+ *$py.class
18
+ *.so
19
+ .Python
20
+ build/
21
+ dist/
22
+ *.egg-info/
23
+ *.egg
24
+ .env
25
+ .venv/
26
+
27
+ # Tool caches
28
+ .ruff_cache/
29
+ .pyright/
30
+ .pytest_cache/
31
+ .coverage
32
+ htmlcov/
33
+ .nox/
34
+ .uv_cache/
35
+
36
+ # Agent runtime artifacts
37
+ .pi/plans/
38
+ .pi/reviews/
39
+ .pi/LESSONS.md
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,38 @@
1
+ # litresearch
2
+
3
+ ## Overview
4
+ CLI-first Python package for automated literature research workflows. The scaffold
5
+ supports a Typer CLI, importable package code, and standard OSS tooling.
6
+
7
+ ## Tech Stack
8
+ - Language: Python 3.12
9
+ - Framework: Typer
10
+ - Core dependencies: LiteLLM, Semantic Scholar client, Pydantic, httpx, pypdf, Rich
11
+ - Tooling: uv, Ruff, Pyright, Pytest, Nox, GitHub Actions
12
+
13
+ ## Directory Structure
14
+ src/
15
+ litresearch/ Python package and CLI entrypoints
16
+
17
+ tests/
18
+ unit/ Fast unit tests
19
+ integration/ Integration-level CLI checks
20
+
21
+ docs/
22
+ decisions/ ADRs and design notes
23
+
24
+ ## Conventions
25
+ - Keep scaffold files minimal until /plan defines implementation work.
26
+ - Use `src/` layout for all package code.
27
+ - Prefer small, typed modules with fail-fast validation.
28
+ - Keep CLI wiring thin; business logic belongs in separate modules.
29
+
30
+ ## Architecture Decisions
31
+ - Typer provides the public CLI surface.
32
+ - Pydantic settings handle environment-backed configuration.
33
+ - Nox is the single entrypoint for lint, typecheck, and test workflows.
34
+ - GitHub Actions should mirror local `uv run nox` behavior.
35
+
36
+ ## Known Constraints
37
+ - This repo is scaffold-only at init time. Pipeline stages and prompts are not implemented here.
38
+ - Semantic Scholar and LLM provider details stay in configuration, not hard-coded in the scaffold.
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-03-09
9
+
10
+
11
+ ### Added
12
+
13
+ - **ci:** Add oss release workflows
14
+
15
+
16
+ ### Maintenance
17
+
18
+ - Initial project setup
19
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SilasPignotti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,45 @@
1
+ Metadata-Version: 2.4
2
+ Name: litresearch
3
+ Version: 0.1.0
4
+ Summary: CLI tool for automated literature research workflows.
5
+ Project-URL: Homepage, https://github.com/spignotti/litresearch
6
+ Project-URL: Repository, https://github.com/spignotti/litresearch
7
+ Author-email: Silas Pignotti <pignottisilas@gmail.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Requires-Python: >=3.12
11
+ Requires-Dist: httpx>=0.28.1
12
+ Requires-Dist: litellm>=1.82.0
13
+ Requires-Dist: pydantic-settings>=2.13.1
14
+ Requires-Dist: pydantic>=2.12.5
15
+ Requires-Dist: pypdf>=6.7.5
16
+ Requires-Dist: rich>=14.3.3
17
+ Requires-Dist: semanticscholar>=0.11.0
18
+ Requires-Dist: typer>=0.24.1
19
+ Description-Content-Type: text/markdown
20
+
21
+ # litresearch
22
+ [![CI](https://github.com/spignotti/litresearch/actions/workflows/ci.yml/badge.svg)](https://github.com/spignotti/litresearch/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/litresearch.svg)](https://pypi.org/project/litresearch/)
23
+
24
+ CLI tool that automates literature research from research questions to curated,
25
+ ranked, and exported paper sets with structured reports.
26
+
27
+ ## Overview
28
+ - Automates discovery, screening, analysis, ranking, and export steps.
29
+ - Targets a CLI-first workflow with an importable Python API.
30
+ - Uses Semantic Scholar for paper metadata and LiteLLM for provider-agnostic LLM access.
31
+
32
+ ## Planned Architecture
33
+ - Framework: Typer CLI with package modules under `src/litresearch/`
34
+ - Core dependencies: LiteLLM, Semantic Scholar client, Pydantic, httpx, pypdf, Rich
35
+ - Outputs: Markdown reports, bibliographic exports, downloaded PDFs, and JSON pipeline state
36
+
37
+ ## Development
38
+ ```bash
39
+ uv sync
40
+ uv run nox
41
+ uv run litresearch --help
42
+ ```
43
+
44
+ ## Status
45
+ This repository currently contains project scaffolding only. Product logic will be added in later planning and task steps.
@@ -0,0 +1,142 @@
1
+ # litresearch
2
+
3
+ ## Identity
4
+ - **What**: CLI tool that automates literature research — from research questions to a curated, ranked, and exported set of relevant papers with structured reports.
5
+ - **Why**: Manual literature research (Elicit → PDF download → LLM summarization → manual ranking) is tedious and repetitive. This pipeline automates the entire flow end-to-end.
6
+ - **Type**: cli
7
+ - **Python**: 3.12
8
+
9
+ ## Architecture
10
+ - **Framework**: Typer (CLI) + importable Python API
11
+ - **Dependencies**:
12
+ - typer: CLI framework with type hints and auto-generated help
13
+ - litellm: Multi-provider LLM abstraction (100+ providers via unified interface)
14
+ - semanticscholar: Typed Python client for Semantic Scholar API (paper search, metadata, pagination)
15
+ - pypdf: PDF text extraction (pure Python, BSD license — MIT-compatible)
16
+ - pydantic / pydantic-settings: Config validation, typed models, TOML/ENV support
17
+ - httpx: Async HTTP client for PDF downloads
18
+ - rich: Terminal output formatting (progress bars, tables, status)
19
+ - **Secrets**:
20
+ - LLM API key (provider-dependent, e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY) — required
21
+ - S2_API_KEY — optional (higher rate limits with key, works without)
22
+
23
+ ### Data Flow
24
+ Research Questions → [Query Generation] → [Paper Discovery] → [Metadata Enrichment] → [LLM Analysis] → [Ranking] → [Export]
25
+ - **Trigger**: Manual (CLI command)
26
+ - **Volume**: ~50–200 candidate papers per run, ~20–50 analyzed in depth, Top-N exported
27
+
28
+ ### External Services
29
+ | Service | Auth | Rate Limit | Purpose |
30
+ |---------|------|------------|---------|
31
+ | Semantic Scholar API | API Key (optional) | 1 RPS authenticated, shared pool unauthenticated | Paper search, metadata, OA-PDF links |
32
+ | LLM Provider (via LiteLLM) | API Key (required) | Provider-dependent | Query generation, abstract screening, paper analysis, synthesis |
33
+
34
+ ### Data Model
35
+ - **Input**: List of research questions (strings)
36
+ - **Intermediate State** (JSON, resumable):
37
+ - Generated facets and search queries
38
+ - Candidate papers with metadata (title, abstract, authors, year, citations, venue, DOI, PDF URL)
39
+ - Screening scores (Stage A)
40
+ - Full analysis results (Stage B)
41
+ - **Output**:
42
+ - report.md — Main report (questions, search strategy, top-N papers with analysis, synthesis with research gaps)
43
+ - paper_analyses.md — Detailed analysis of all screened papers
44
+ - references.bib — BibTeX for top-N papers
45
+ - references.ris — RIS format (Zotero, Mendeley, Citavi import)
46
+ - papers/ — Downloaded PDFs of top-N open access papers
47
+ - data.json — Machine-readable export of all metadata + analyses
48
+
49
+ ### Components
50
+ | Component | Purpose | Priority |
51
+ |-----------|---------|----------|
52
+ | cli.py | Typer CLI with `run`, `resume`, `config` commands | MVP |
53
+ | config.py | Pydantic Settings — all parameters configurable via TOML file + CLI flags + ENV | MVP |
54
+ | models.py | Pydantic models: Paper, Analysis, SearchQuery, Facet, PipelineState | MVP |
55
+ | pipeline.py | Orchestrator — runs stages sequentially, saves state after each stage | MVP |
56
+ | stages/query_gen.py | Stage 1: LLM generates facets + search queries from research questions | MVP |
57
+ | stages/discovery.py | Stage 2: Semantic Scholar API search, deduplication by DOI/Corpus-ID | MVP |
58
+ | stages/enrichment.py | Stage 3: Fetch full metadata for all candidates via S2 API | MVP |
59
+ | stages/analysis.py | Stage 4: Two-phase LLM analysis (A: abstract screening, B: extended with PDF text) | MVP |
60
+ | stages/ranking.py | Stage 5: Multi-criteria ranking (relevance score → citations → year) | MVP |
61
+ | stages/export.py | Stage 6: Generate report.md, paper_analyses.md, references.bib, references.ris, data.json, download PDFs | MVP |
62
+ | pdf.py | PDF text extraction — page heuristic: first N + last M pages | MVP |
63
+ | prompts/query_gen.md | Prompt template for facet + query generation | MVP |
64
+ | prompts/screening.md | Prompt template for abstract screening (Stage A) | MVP |
65
+ | prompts/analysis.md | Prompt template for extended analysis (Stage B) | MVP |
66
+ | prompts/synthesis.md | Prompt template for literature synthesis in report | MVP |
67
+
68
+ ### Pipeline Stages Detail
69
+
70
+ **Stage 1 — Query Generation**
71
+ LLM receives research questions, derives thematic facets, generates 2–3 targeted search queries per facet. Output: list of Facet objects with queries.
72
+
73
+ **Stage 2 — Paper Discovery**
74
+ Each query hits Semantic Scholar relevance search endpoint. Results deduplicated by paperId/DOI. Output: candidate pool (typically 50–200 papers).
75
+
76
+ **Stage 3 — Metadata Enrichment**
77
+ Batch fetch via S2 API: title, abstract, authors, year, citationCount, venue, openAccessPdf, externalIds (DOI). Output: enriched Paper objects.
78
+
79
+ **Stage 4A — Abstract Screening**
80
+ Each paper's abstract + metadata sent to LLM. Returns relevance score (0–100). Papers below threshold (default: 40) are filtered out. Fast, cheap (short prompts).
81
+
82
+ **Stage 4B — Extended Analysis**
83
+ For papers passing screening: download PDF, extract first 4 + last 2 pages via pypdf, send to LLM with full analysis prompt. Returns: summary, key_findings, methodology, relevance_score (refined), relevance_rationale.
84
+
85
+ **Stage 5 — Ranking**
86
+ Sort by: relevance_score (desc) → citationCount (desc) → year (desc). Apply top_n cutoff.
87
+
88
+ **Stage 6 — Export**
89
+ Generate all output files. report.md includes a final LLM-generated synthesis section analyzing consensus, contradictions, and research gaps across top-N papers.
90
+
91
+ ## Objectives
92
+ ### MVP (v0.1)
93
+ - [ ] Full pipeline runs end-to-end: questions in → report + PDFs + BibTeX out
94
+ - [ ] All 6 stages implemented and working
95
+ - [ ] Configuration via TOML file + CLI flags
96
+ - [ ] Resume interrupted runs via --resume flag
97
+ - [ ] Works with at least OpenAI and Anthropic via LiteLLM
98
+ - [ ] Installable via pip install (proper pyproject.toml with entry point)
99
+ - [ ] README with quickstart, usage examples, configuration reference
100
+ - [ ] Tests for core logic (models, ranking, export formatting)
101
+ - [ ] CI via GitHub Actions (nox: lint + typecheck + test)
102
+
103
+ ### Non-Goals
104
+ - GUI / Web interface
105
+ - NotebookLM integration
106
+ - Citation graph analysis
107
+ - Full-text PDF analysis (MVP uses page heuristic)
108
+ - Configurable analysis schema (MVP uses fixed schema)
109
+ - Section-aware PDF parsing (GROBID)
110
+ - Multi-source search (OpenAlex) — S2 only for MVP
111
+
112
+ ## Constraints
113
+ - Semantic Scholar API: 1 RPS with key, respect rate limits. Use async batch where possible.
114
+ - LLM costs: Two-phase analysis minimizes tokens. Abstract screening is cheap (~200 tokens/paper). Extended analysis only for papers above threshold.
115
+ - PDF downloads: Only open access papers. Fail gracefully if PDF unavailable.
116
+ - License compatibility: All dependencies must be MIT/BSD/Apache-2.0 compatible. No AGPL.
117
+
118
+ ## Setup
119
+ - **Category**: Open Source
120
+ - **Git Remote**: https://github.com/SilasPignotti/litresearch
121
+ - **License**: MIT
122
+ - **Release**: PyPI via GitHub Actions (Trusted Publisher, tag-triggered)
123
+ - **Versioning**: SemVer, start at 0.1.0, manual bumping + git-cliff for changelogs
124
+ - **Python**: 3.12
125
+ - **Package Manager**: uv
126
+ - **Quality**: nox (sessions: lint, typecheck, test) + ruff + pyright + pytest
127
+ - **CI**: GitHub Actions running nox on push/PR
128
+
129
+ ## Context
130
+ - **Key Docs**:
131
+ - Semantic Scholar API: https://api.semanticscholar.org/api-docs/
132
+ - semanticscholar Python client: https://semanticscholar.readthedocs.io/
133
+ - LiteLLM: https://docs.litellm.ai/docs/
134
+ - Typer: https://typer.tiangolo.com/
135
+ - pypdf: https://pypdf.readthedocs.io/
136
+ - **Decisions**:
137
+ - LiteLLM over direct API calls — provider-agnostic is critical for open source adoption
138
+ - pypdf over PyMuPDF — AGPL license incompatible with MIT project
139
+ - JSON state over SQLite — simpler for MVP, SQLite as future enhancement
140
+ - Page heuristic (first 4 + last 2 pages) over section-aware parsing — no external services needed
141
+ - Prompts as markdown files — editable without code changes, version-controlled
142
+ - Dual interface (CLI + Python API) — Typer for CLI, underlying functions importable as library
@@ -0,0 +1,25 @@
1
+ # litresearch
2
+ [![CI](https://github.com/spignotti/litresearch/actions/workflows/ci.yml/badge.svg)](https://github.com/spignotti/litresearch/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/litresearch.svg)](https://pypi.org/project/litresearch/)
3
+
4
+ CLI tool that automates literature research from research questions to curated,
5
+ ranked, and exported paper sets with structured reports.
6
+
7
+ ## Overview
8
+ - Automates discovery, screening, analysis, ranking, and export steps.
9
+ - Targets a CLI-first workflow with an importable Python API.
10
+ - Uses Semantic Scholar for paper metadata and LiteLLM for provider-agnostic LLM access.
11
+
12
+ ## Planned Architecture
13
+ - Framework: Typer CLI with package modules under `src/litresearch/`
14
+ - Core dependencies: LiteLLM, Semantic Scholar client, Pydantic, httpx, pypdf, Rich
15
+ - Outputs: Markdown reports, bibliographic exports, downloaded PDFs, and JSON pipeline state
16
+
17
+ ## Development
18
+ ```bash
19
+ uv sync
20
+ uv run nox
21
+ uv run litresearch --help
22
+ ```
23
+
24
+ ## Status
25
+ This repository currently contains project scaffolding only. Product logic will be added in later planning and task steps.
@@ -0,0 +1,49 @@
1
+ [changelog]
2
+ header = """
3
+ # Changelog
4
+
5
+ All notable changes to this project will be documented in this file.
6
+
7
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
8
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
9
+ """
10
+ body = """
11
+ {% if version %}
12
+ ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
13
+ {% else %}
14
+ ## [Unreleased]
15
+ {% endif %}
16
+ {% for group, commits in commits | group_by(attribute="group") %}
17
+ ### {{ group }}
18
+ {% for commit in commits %}
19
+ - {% if commit.scope %}**{{ commit.scope }}:** {% endif %}{{ commit.message | upper_first }}
20
+ {% endfor %}
21
+ {% endfor %}
22
+ """
23
+ trim = true
24
+
25
+ [git]
26
+ conventional_commits = true
27
+ filter_unconventional = true
28
+ tag_pattern = "v[0-9]*"
29
+ split_commits = false
30
+
31
+ commit_preprocessors = [
32
+ { pattern = "\\((#\\d+)\\)", replace = "" },
33
+ ]
34
+
35
+ commit_parsers = [
36
+ { message = "^feat", group = "Added" },
37
+ { message = "^fix", group = "Fixed" },
38
+ { message = "^refactor", group = "Changed" },
39
+ { message = "^perf", group = "Changed" },
40
+ { message = "^docs", group = "Documentation" },
41
+ { message = "^test", group = "Tests" },
42
+ { message = "^chore", group = "Maintenance" },
43
+ { body = ".*security", group = "Security" },
44
+ ]
45
+
46
+ protect_breaking_commits = false
47
+ filter_commits = false
48
+ toposort = false
49
+ sort_commits = "oldest"
File without changes
@@ -0,0 +1,51 @@
1
+ import nox
2
+
3
+ nox.options.sessions = ["lint", "typecheck", "test"]
4
+ nox.options.stop_on_first_error = False
5
+
6
+
7
+ @nox.session
8
+ def lint(session: nox.Session) -> None:
9
+ """Check code with ruff."""
10
+ session.run("uv", "run", "ruff", "check", "src", "tests", external=True)
11
+ session.run("uv", "run", "ruff", "format", "--check", "src", "tests", external=True)
12
+
13
+
14
+ @nox.session
15
+ def format(session: nox.Session) -> None:
16
+ """Format code with ruff."""
17
+ session.run("uv", "run", "ruff", "format", "src", "tests", external=True)
18
+
19
+
20
+ @nox.session
21
+ def typecheck(session: nox.Session) -> None:
22
+ """Type check with pyright."""
23
+ session.run("uv", "run", "pyright", external=True)
24
+
25
+
26
+ @nox.session
27
+ def fix(session: nox.Session) -> None:
28
+ """Auto-fix lint issues and format code."""
29
+ session.run("uv", "run", "ruff", "check", "--fix", "src", "tests", external=True)
30
+ session.run("uv", "run", "ruff", "format", "src", "tests", external=True)
31
+
32
+
33
+ @nox.session
34
+ def test(session: nox.Session) -> None:
35
+ """Run tests."""
36
+ session.run("uv", "run", "pytest", "-v", external=True)
37
+
38
+
39
+ @nox.session
40
+ def coverage(session: nox.Session) -> None:
41
+ """Run tests with coverage."""
42
+ session.run("uv", "run", "pytest", "--cov=src", "--cov-report=term", "-v", external=True)
43
+
44
+
45
+ @nox.session
46
+ def ci(session: nox.Session) -> None:
47
+ """Run the full CI pipeline."""
48
+ session.run("uv", "run", "ruff", "check", "src", "tests", external=True)
49
+ session.run("uv", "run", "ruff", "format", "--check", "src", "tests", external=True)
50
+ session.run("uv", "run", "pyright", external=True)
51
+ session.run("uv", "run", "pytest", "--cov=src", "-v", external=True)
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "litresearch"
3
+ version = "0.1.0"
4
+ description = "CLI tool for automated literature research workflows."
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ authors = [
8
+ { name = "Silas Pignotti", email = "pignottisilas@gmail.com" },
9
+ ]
10
+ license = { text = "MIT" }
11
+ dependencies = [
12
+ "httpx>=0.28.1",
13
+ "litellm>=1.82.0",
14
+ "pydantic>=2.12.5",
15
+ "pydantic-settings>=2.13.1",
16
+ "pypdf>=6.7.5",
17
+ "rich>=14.3.3",
18
+ "semanticscholar>=0.11.0",
19
+ "typer>=0.24.1",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/spignotti/litresearch"
24
+ Repository = "https://github.com/spignotti/litresearch"
25
+
26
+ [project.scripts]
27
+ litresearch = "litresearch.cli:main"
28
+
29
+ [build-system]
30
+ requires = ["hatchling"]
31
+ build-backend = "hatchling.build"
32
+
33
+ [dependency-groups]
34
+ dev = [
35
+ "nox>=2026.2.9",
36
+ "pyright>=1.1.408",
37
+ "pytest>=9.0.2",
38
+ "pytest-cov>=7.0.0",
39
+ "ruff>=0.15.5",
40
+ ]
41
+
42
+ [tool.ruff]
43
+ line-length = 100
44
+ target-version = "py312"
45
+
46
+ [tool.ruff.lint]
47
+ select = ["E", "F", "I", "B"]
48
+
49
+ [tool.pyright]
50
+ include = ["src", "tests"]
51
+ pythonVersion = "3.12"
52
+ venvPath = "."
53
+ venv = ".venv"
54
+ typeCheckingMode = "standard"
55
+
56
+ [tool.pytest.ini_options]
57
+ addopts = "-q"
58
+ pythonpath = ["src"]
59
+ testpaths = ["tests"]
@@ -0,0 +1,5 @@
1
+ """litresearch package."""
2
+
3
+ __all__ = ["__version__"]
4
+
5
+ __version__ = "0.1.0"
@@ -0,0 +1,6 @@
1
+ """Module entrypoint for `python -m litresearch`."""
2
+
3
+ from litresearch.cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,30 @@
1
+ """CLI entrypoints for litresearch."""
2
+
3
+ import typer
4
+ from rich.console import Console
5
+
6
+ from litresearch import __version__
7
+ from litresearch.config import Settings
8
+
9
+ app = typer.Typer(help="Automated literature research workflow CLI.")
10
+ console = Console()
11
+
12
+
13
+ @app.command()
14
+ def version() -> None:
15
+ """Print the installed version."""
16
+ console.print(__version__)
17
+
18
+
19
+ @app.command()
20
+ def config() -> None:
21
+ """Show basic configuration status without exposing secrets."""
22
+ settings = Settings()
23
+ console.print(f"default_model={settings.default_model}")
24
+ console.print(f"s2_api_key_configured={bool(settings.s2_api_key)}")
25
+ console.print(f"llm_api_key_configured={settings.has_llm_api_key}")
26
+
27
+
28
+ def main() -> None:
29
+ """Run the Typer application."""
30
+ app()
@@ -0,0 +1,28 @@
1
+ """Application settings for litresearch."""
2
+
3
+ from pydantic import computed_field
4
+ from pydantic_settings import BaseSettings, SettingsConfigDict
5
+
6
+
7
+ class Settings(BaseSettings):
8
+ """Environment-backed settings."""
9
+
10
+ model_config = SettingsConfigDict(env_file=".env", extra="ignore")
11
+
12
+ openai_api_key: str | None = None
13
+ anthropic_api_key: str | None = None
14
+ openrouter_api_key: str | None = None
15
+ s2_api_key: str | None = None
16
+ default_model: str = "gpt-4o-mini"
17
+
18
+ @computed_field
19
+ @property
20
+ def has_llm_api_key(self) -> bool:
21
+ """Return whether any supported LLM provider key is configured."""
22
+ return any(
23
+ [
24
+ self.openai_api_key,
25
+ self.anthropic_api_key,
26
+ self.openrouter_api_key,
27
+ ]
28
+ )
@@ -0,0 +1,9 @@
1
+ """Shared data models for the litresearch scaffold."""
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class ResearchRequest(BaseModel):
7
+ """Minimal input model for future pipeline work."""
8
+
9
+ questions: list[str] = Field(default_factory=list)
File without changes
@@ -0,0 +1,21 @@
1
+ import os
2
+ import subprocess
3
+ import sys
4
+ from pathlib import Path
5
+
6
+
7
+ def test_module_entrypoint_shows_help() -> None:
8
+ env = os.environ.copy()
9
+ src_path = str(Path(__file__).resolve().parents[2] / "src")
10
+ env["PYTHONPATH"] = src_path if not env.get("PYTHONPATH") else f"{src_path}:{env['PYTHONPATH']}"
11
+
12
+ result = subprocess.run(
13
+ [sys.executable, "-m", "litresearch", "--help"],
14
+ capture_output=True,
15
+ text=True,
16
+ check=False,
17
+ env=env,
18
+ )
19
+
20
+ assert result.returncode == 0
21
+ assert "Automated literature research workflow CLI" in result.stdout
File without changes
@@ -0,0 +1,12 @@
1
+ from typer.testing import CliRunner
2
+
3
+ from litresearch.cli import app
4
+
5
+ runner = CliRunner()
6
+
7
+
8
+ def test_version_command() -> None:
9
+ result = runner.invoke(app, ["version"])
10
+
11
+ assert result.exit_code == 0
12
+ assert "0.1.0" in result.stdout