orbitr 0.1.1__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.
Files changed (79) hide show
  1. orbitr-0.1.1/.env.example +33 -0
  2. orbitr-0.1.1/.envrc +1 -0
  3. orbitr-0.1.1/.github/workflows/ci.yml +95 -0
  4. orbitr-0.1.1/.gitignore +23 -0
  5. orbitr-0.1.1/AGENTS.md +114 -0
  6. orbitr-0.1.1/CHANGELOG.md +108 -0
  7. orbitr-0.1.1/LICENSE +21 -0
  8. orbitr-0.1.1/PKG-INFO +735 -0
  9. orbitr-0.1.1/README.md +701 -0
  10. orbitr-0.1.1/flake.lock +61 -0
  11. orbitr-0.1.1/flake.nix +44 -0
  12. orbitr-0.1.1/justfile +122 -0
  13. orbitr-0.1.1/pyproject.toml +75 -0
  14. orbitr-0.1.1/specs/implementation.md +475 -0
  15. orbitr-0.1.1/specs/planning.md +190 -0
  16. orbitr-0.1.1/specs/progress.md +276 -0
  17. orbitr-0.1.1/src/orbitr/__init__.py +3 -0
  18. orbitr-0.1.1/src/orbitr/_async.py +27 -0
  19. orbitr-0.1.1/src/orbitr/cli.py +132 -0
  20. orbitr-0.1.1/src/orbitr/clients/__init__.py +1 -0
  21. orbitr-0.1.1/src/orbitr/clients/arxiv.py +151 -0
  22. orbitr-0.1.1/src/orbitr/clients/base.py +228 -0
  23. orbitr-0.1.1/src/orbitr/clients/semantic_scholar.py +258 -0
  24. orbitr-0.1.1/src/orbitr/commands/__init__.py +1 -0
  25. orbitr-0.1.1/src/orbitr/commands/author.py +146 -0
  26. orbitr-0.1.1/src/orbitr/commands/cache.py +147 -0
  27. orbitr-0.1.1/src/orbitr/commands/doctor.py +128 -0
  28. orbitr-0.1.1/src/orbitr/commands/export.py +176 -0
  29. orbitr-0.1.1/src/orbitr/commands/init.py +196 -0
  30. orbitr-0.1.1/src/orbitr/commands/paper.py +350 -0
  31. orbitr-0.1.1/src/orbitr/commands/query.py +202 -0
  32. orbitr-0.1.1/src/orbitr/commands/recommend.py +172 -0
  33. orbitr-0.1.1/src/orbitr/commands/search.py +360 -0
  34. orbitr-0.1.1/src/orbitr/commands/zotero.py +266 -0
  35. orbitr-0.1.1/src/orbitr/config.py +173 -0
  36. orbitr-0.1.1/src/orbitr/core/__init__.py +1 -0
  37. orbitr-0.1.1/src/orbitr/core/cache.py +221 -0
  38. orbitr-0.1.1/src/orbitr/core/deduplication.py +184 -0
  39. orbitr-0.1.1/src/orbitr/core/export.py +223 -0
  40. orbitr-0.1.1/src/orbitr/core/models.py +57 -0
  41. orbitr-0.1.1/src/orbitr/core/query.py +214 -0
  42. orbitr-0.1.1/src/orbitr/core/ranking.py +157 -0
  43. orbitr-0.1.1/src/orbitr/display/__init__.py +119 -0
  44. orbitr-0.1.1/src/orbitr/display/detail.py +140 -0
  45. orbitr-0.1.1/src/orbitr/display/json_fmt.py +24 -0
  46. orbitr-0.1.1/src/orbitr/display/panels.py +91 -0
  47. orbitr-0.1.1/src/orbitr/display/table.py +76 -0
  48. orbitr-0.1.1/src/orbitr/exceptions.py +35 -0
  49. orbitr-0.1.1/src/orbitr/zotero/__init__.py +1 -0
  50. orbitr-0.1.1/src/orbitr/zotero/client.py +134 -0
  51. orbitr-0.1.1/tests/__init__.py +0 -0
  52. orbitr-0.1.1/tests/conftest.py +31 -0
  53. orbitr-0.1.1/tests/fixtures/.gitkeep +0 -0
  54. orbitr-0.1.1/tests/fixtures/arxiv_get_by_id.xml +47 -0
  55. orbitr-0.1.1/tests/fixtures/arxiv_search.xml +100 -0
  56. orbitr-0.1.1/tests/fixtures/ss_citations.json +145 -0
  57. orbitr-0.1.1/tests/fixtures/ss_get_by_id.json +56 -0
  58. orbitr-0.1.1/tests/fixtures/ss_recommendations.json +3 -0
  59. orbitr-0.1.1/tests/fixtures/ss_search.json +111 -0
  60. orbitr-0.1.1/tests/smoke_test.sh +257 -0
  61. orbitr-0.1.1/tests/test_arxiv.py +305 -0
  62. orbitr-0.1.1/tests/test_author.py +153 -0
  63. orbitr-0.1.1/tests/test_base_client.py +203 -0
  64. orbitr-0.1.1/tests/test_cache.py +212 -0
  65. orbitr-0.1.1/tests/test_cache_cmd.py +200 -0
  66. orbitr-0.1.1/tests/test_deduplication.py +293 -0
  67. orbitr-0.1.1/tests/test_display_phase4.py +265 -0
  68. orbitr-0.1.1/tests/test_doctor.py +209 -0
  69. orbitr-0.1.1/tests/test_export.py +281 -0
  70. orbitr-0.1.1/tests/test_init.py +344 -0
  71. orbitr-0.1.1/tests/test_models.py +152 -0
  72. orbitr-0.1.1/tests/test_paper.py +334 -0
  73. orbitr-0.1.1/tests/test_query.py +204 -0
  74. orbitr-0.1.1/tests/test_ranking.py +169 -0
  75. orbitr-0.1.1/tests/test_recommend.py +150 -0
  76. orbitr-0.1.1/tests/test_search.py +441 -0
  77. orbitr-0.1.1/tests/test_semantic_scholar.py +250 -0
  78. orbitr-0.1.1/tests/test_zotero.py +274 -0
  79. orbitr-0.1.1/uv.lock +1011 -0
@@ -0,0 +1,33 @@
1
+ # lumen environment variables
2
+ # Copy to .env and fill in values, or run `lumen init` for guided setup.
3
+ # These override settings in ~/.config/lumen/config.toml.
4
+
5
+ # Semantic Scholar API key (optional — increases rate limits)
6
+ # Get one at: https://www.semanticscholar.org/product/api
7
+ SEMANTIC_SCHOLAR_API_KEY=
8
+
9
+ # Zotero credentials (required for `lumen zotero` commands)
10
+ # Get your User ID and API key at: https://www.zotero.org/settings/keys
11
+ ZOTERO_USER_ID=
12
+ ZOTERO_API_KEY=
13
+
14
+ # Override default search sources (comma-separated)
15
+ # LUMEN_SOURCES=arxiv,semantic_scholar
16
+
17
+ # Override default max results
18
+ # LUMEN_MAX_RESULTS=10
19
+
20
+ # Override default output format (table, list, json)
21
+ # LUMEN_FORMAT=table
22
+
23
+ # Override cache directory
24
+ # LUMEN_CACHE_DIR=~/.cache/lumen
25
+
26
+ # Disable caching entirely
27
+ # LUMEN_NO_CACHE=1
28
+
29
+ # Disable pager for long output
30
+ # LUMEN_NO_PAGER=1
31
+
32
+ # Disable all color output (also respected: NO_COLOR=1)
33
+ # NO_COLOR=1
orbitr-0.1.1/.envrc ADDED
@@ -0,0 +1 @@
1
+ use flake .
@@ -0,0 +1,95 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ release:
9
+ types: [published]
10
+
11
+ jobs:
12
+ lint:
13
+ name: Lint
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v5
18
+ with:
19
+ enable-cache: true
20
+ - run: uv sync --group dev
21
+ - run: uv run ruff format --check src/ tests/
22
+ - run: uv run ruff check src/ tests/
23
+
24
+ typecheck:
25
+ name: Type check
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: astral-sh/setup-uv@v5
30
+ with:
31
+ enable-cache: true
32
+ - run: uv sync --group dev
33
+ - run: uv run pyright src/
34
+
35
+ test:
36
+ name: Test (Python ${{ matrix.python-version }})
37
+ runs-on: ubuntu-latest
38
+ strategy:
39
+ fail-fast: false
40
+ matrix:
41
+ python-version: ["3.10", "3.11", "3.12"]
42
+ steps:
43
+ - uses: actions/checkout@v4
44
+ - uses: astral-sh/setup-uv@v5
45
+ with:
46
+ enable-cache: true
47
+ python-version: ${{ matrix.python-version }}
48
+ - run: uv sync --group dev
49
+ - run: uv run pytest --tb=short -q
50
+
51
+ coverage:
52
+ name: Coverage
53
+ runs-on: ubuntu-latest
54
+ steps:
55
+ - uses: actions/checkout@v4
56
+ - uses: astral-sh/setup-uv@v5
57
+ with:
58
+ enable-cache: true
59
+ - run: uv sync --group dev
60
+ - run: uv run pytest --cov=orbitr --cov-report=term-missing --cov-fail-under=80 -q
61
+
62
+ build:
63
+ name: Build
64
+ runs-on: ubuntu-latest
65
+ needs: [lint, typecheck, test]
66
+ steps:
67
+ - uses: actions/checkout@v4
68
+ - uses: astral-sh/setup-uv@v5
69
+ with:
70
+ enable-cache: true
71
+ - run: uv build
72
+ - name: Check wheel installs and --version works
73
+ run: |
74
+ uv tool install dist/*.whl
75
+ orbitr --version
76
+ - uses: actions/upload-artifact@v4
77
+ with:
78
+ name: dist
79
+ path: dist/
80
+
81
+ publish:
82
+ name: Publish to PyPI
83
+ runs-on: ubuntu-latest
84
+ needs: [build]
85
+ if: github.event_name == 'release'
86
+ environment: pypi
87
+ permissions:
88
+ id-token: write
89
+ steps:
90
+ - uses: actions/download-artifact@v4
91
+ with:
92
+ name: dist
93
+ path: dist/
94
+ - uses: astral-sh/setup-uv@v5
95
+ - run: uv publish --trusted-publishing always
@@ -0,0 +1,23 @@
1
+ dist/
2
+ build/
3
+ *.egg-info/
4
+ __pycache__/
5
+ .venv/
6
+ .env
7
+ .env.local
8
+ *.pem
9
+ credentials.json
10
+ .DS_Store
11
+ .idea/
12
+ .vscode/
13
+ *.swp
14
+ *.pyc
15
+ *.pyo
16
+ .pytest_cache/
17
+ .ruff_cache/
18
+ .pyright/
19
+ htmlcov/
20
+ .coverage
21
+ coverage.xml
22
+ .direnv/
23
+ .aider*
orbitr-0.1.1/AGENTS.md ADDED
@@ -0,0 +1,114 @@
1
+ # orbitr — Agent Project Context
2
+
3
+ ## Software Purpose
4
+
5
+ `orbitr` is a Python CLI tool for academic literature search and reference management. It queries arXiv, Semantic Scholar, and Google Scholar concurrently, deduplicates and ranks results, and integrates with the Zotero reference manager. Target users: researchers, academics, and students who prefer terminal-based workflows.
6
+
7
+ ## Architecture Overview
8
+
9
+ CLI pipeline: command dispatch → concurrent async API clients → core processing (dedup, ranking, cache) → display layer (Rich or JSON).
10
+
11
+ **Key components:**
12
+
13
+ - `src/orbitr/cli.py` — Typer root app; global flags; injects config into context
14
+ - `src/orbitr/config.py` — layered config: CLI flags > env vars > `~/.config/orbitr/config.toml` > defaults
15
+ - `src/orbitr/commands/` — one module per command (`search`, `paper`, `cite`, `author`, `recommend`, `export`, `query`, `zotero`, `cache`, `init`, `doctor`)
16
+ - `src/orbitr/clients/` — async httpx clients: `arxiv.py`, `semantic_scholar.py`; all extend `base.py` (retry, rate limiting, circuit break); Google Scholar deferred to v1.1
17
+ - `src/orbitr/core/` — `models.py` (Pydantic), `deduplication.py`, `ranking.py`, `cache.py` (SQLite), `export.py` (BibTeX/RIS/CSL-JSON)
18
+ - `src/orbitr/zotero/client.py` — pyzotero wrapper
19
+ - `src/orbitr/display/` — `table.py`, `list.py`, `detail.py`, `json_fmt.py`
20
+
21
+ ## Dev Environment
22
+
23
+ The project uses a **Nix flake** to pin the dev environment (Python 3.12, uv, ruff,
24
+ pyright) and **direnv** to activate it automatically on `cd`. A **justfile** provides
25
+ all common workflow recipes.
26
+
27
+ Entry point:
28
+
29
+ ```bash
30
+ cd orbitr # direnv runs `use flake .` — activates pinned shell
31
+ just setup # uv sync inside the flake environment
32
+ ```
33
+
34
+ Without direnv: `nix develop` then `just setup`.
35
+
36
+ ## Build, Test, and Run
37
+
38
+ ```bash
39
+ just setup # uv sync — install deps and editable package
40
+ just run -- --help # run orbitr via uv run
41
+ just build # uv build — produces dist/ wheel + sdist
42
+
43
+ just fmt # ruff format src/ tests/
44
+ just lint # ruff check --fix src/ tests/
45
+ just check # fmt + lint, no writes (use in CI)
46
+ just types # pyright src/
47
+ just qa # check + types
48
+
49
+ just test # pytest — full suite
50
+ just test-unit # pytest -m "not integration"
51
+ just cov # pytest --cov with term-missing report
52
+ just test-mod core/test_deduplication # single module
53
+
54
+ just clean # remove build artifacts and caches
55
+ just reset # wipe .venv and reinstall from scratch
56
+ ```
57
+
58
+ Run `just` with no arguments to list all recipes.
59
+
60
+ ## Language and Stack
61
+
62
+ - **Python** ≥ 3.10; `uv` for deps and packaging; `hatchling` build backend
63
+ - **CLI:** Typer + Click
64
+ - **Terminal output:** Rich
65
+ - **HTTP:** httpx (async)
66
+ - **Models:** Pydantic v2
67
+ - **Cache:** SQLite (stdlib `sqlite3`)
68
+ - **Zotero:** pyzotero
69
+ - **Code style:** `ruff format` (88 chars), `ruff check` (rules: E, F, UP, B, SIM, I), `pyright` basic
70
+
71
+ ## Directory Structure
72
+
73
+ ```
74
+ orbitr/
75
+ ├── pyproject.toml
76
+ ├── flake.nix
77
+ ├── CLAUDE.md
78
+ ├── README.md
79
+ ├── .env.example
80
+ ├── specs/ # planning.md, progress.md, implementation.md
81
+ ├── logs/ # session and weekly review logs
82
+ ├── src/orbitr/ # source package
83
+ ├── tests/ # pytest suite + fixtures/
84
+ └── .gitignore
85
+ ```
86
+
87
+ ## Development Workflow
88
+
89
+ - Conventional commits: `type(scope): message` (feat, fix, chore, docs, test, refactor)
90
+ - All commands must have complete `--help` text with a usage example before merging
91
+ - Errors go to stderr; data goes to stdout — enforce at every command boundary
92
+ - All new commands: add entry to `cli.py`, stub in `commands/`, unit test in `tests/test_<command>.py`
93
+ - Run `just check` before committing; run `just qa` before opening a PR
94
+ - Check `specs/planning.md` for the current phase and open tasks
95
+ - Update `specs/progress.md` after completing a milestone or feature
96
+
97
+ ## Key Conventions
98
+
99
+ - **Exit codes:** 0 success, 1 general error, 2 usage error, 3 config error, 4 no results
100
+ - **Config path:** `~/.config/orbitr/config.toml` (XDG); cache at `~/.cache/orbitr/`
101
+ - **Credentials file permissions:** `0600` — enforced in `orbitr init`
102
+ - **TTY detection:** when stdout is not a TTY, default `--format` to `json` automatically
103
+ - **`NO_COLOR`** env var disables all Rich color; `--no-color` flag does the same
104
+ - **Async in Typer:** wrap async work in `asyncio.run()` at the command level; no persistent event loop
105
+ - **Google Scholar:** deferred to v1.1 — do not implement in v1
106
+ - **Deduplication threshold:** 85% fuzzy title similarity (configurable internally)
107
+
108
+ ## How to Use Claude Effectively Here
109
+
110
+ - **Before implementing a command:** read `specs/implementation.md` for the module structure and key interfaces; check `specs/progress.md` for current status
111
+ - **Error messages:** always include what failed, why, and a fix suggestion — see `specs/implementation.md` § Error Handling for the pattern
112
+ - **Adding a new command:** follow the pattern in existing `commands/` modules; register in `cli.py`; add `--help` text with a real example
113
+ - **Tests:** use `tests/fixtures/` for API responses; mock with `respx`; never make live API calls in tests
114
+ - **Display:** add new renderers in `display/`; always accept `format: Literal["table","list","detail","json"]` and honor `NO_COLOR`
@@ -0,0 +1,108 @@
1
+ # Changelog
2
+
3
+ All notable changes are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
+ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ---
8
+
9
+ ## [0.1.0] — 2026-04-06
10
+
11
+ Initial release.
12
+
13
+ ### Added
14
+
15
+ **Core pipeline**
16
+
17
+ - Multi-source concurrent search across arXiv (Atom feed) and Semantic Scholar
18
+ (Graph API v1) via `orbitr search`
19
+ - Intelligent deduplication: exact DOI match → exact arXiv ID match → fuzzy
20
+ title similarity (rapidfuzz, 85% threshold) with author-overlap gating
21
+ - Five ranking criteria: `relevance` (TF-IDF), `citations` (log-scaled),
22
+ `date` (recency), `impact` (citations × date), `combined` (weighted blend)
23
+ - SQLite TTL cache with three independent tiers:
24
+ `search` (1 h), `paper` (24 h), `citations` (6 h)
25
+
26
+ **Commands**
27
+
28
+ - `orbitr search` — keyword + `field:value` syntax, field flags (`--title`,
29
+ `--author`, `--venue`), year range (`--from`, `--to`), sort, format
30
+ - `orbitr paper` — fetch by arXiv ID, DOI, or Semantic Scholar ID; auto-detects
31
+ ID type; accepts `arxiv:`, `abs/`, URL, and bare ID forms
32
+ - `orbitr cite` — citing papers via Semantic Scholar
33
+ - `orbitr author` — author search via Semantic Scholar two-step (search → papers)
34
+ - `orbitr recommend` — content/citation/hybrid recommendations via SS
35
+ - `orbitr export` — BibTeX, RIS, CSL-JSON; reads ndjson from stdin or `--query`
36
+ - `orbitr query` — heuristic NL-to-query translator with `--run` flag
37
+ - `orbitr zotero add/collections/new` — Zotero Web API integration via pyzotero
38
+ - `orbitr cache stats/clean/clear` — cache inspection and management
39
+ - `orbitr init` — interactive credential and defaults setup (writes `config.toml`
40
+ with mode `0600`)
41
+ - `orbitr doctor` — async connectivity checks for arXiv, SS, and Zotero
42
+
43
+ **Display layer**
44
+
45
+ - Four output formats: `table` (Rich Table), `list` (Rich Panels), `detail`
46
+ (full single-paper layout with abstract, metadata, links), `json` (ndjson)
47
+ - TTY auto-detection: `--format` defaults to `json` when stdout is not a TTY
48
+ - Pager integration: long output routed through `$PAGER` (`less -R`) on TTY;
49
+ disabled with `LUMEN_NO_PAGER=1`
50
+
51
+ **Error handling**
52
+
53
+ - `LumenError` hierarchy with exit codes: 1 (source), 2 (usage), 3 (config),
54
+ 4 (no results)
55
+ - HTTP errors converted to clean `SourceError` in `BaseClient._get`; no raw
56
+ httpx messages surface to users
57
+ - 5xx errors retried up to 3× with exponential backoff; 403 directs to
58
+ `orbitr init` for API key setup
59
+ - Dim suggestion lines on all error messages
60
+
61
+ **Infrastructure**
62
+
63
+ - Layered config: CLI flags > env vars > `~/.config/orbitr/config.toml` > defaults
64
+ - Nix flake dev environment (Python 3.12, uv, ruff, pyright)
65
+ - `justfile` with test, lint, format, coverage, build, and install recipes
66
+ - GitHub Actions CI: lint, typecheck, test (Python 3.10–3.12), coverage, build,
67
+ publish-on-release
68
+
69
+ ### Test coverage
70
+
71
+ - 343 tests; `core/` at 100%, `display/` at 97% overall
72
+ - Offline API tests via `respx` fixtures (no live network calls in CI)
73
+ - Smoke test script (`tests/smoke_test.sh`) for pre-release live-API validation
74
+
75
+ ### Known limitations
76
+
77
+ - Google Scholar support deferred to v1.1 (scraping fragility)
78
+ - `orbitr recommend --method` flag is accepted but all methods use the same
79
+ SS endpoint; method distinctions planned for v1.1
80
+ - `display/detail.py` falls back to list view for multi-paper input in some
81
+ edge cases
82
+
83
+ ---
84
+
85
+ ## [0.1.1] — 2026-04-06
86
+
87
+ ### Fixed
88
+
89
+ - **`orbitr init` — env-var credential protection** (`commands/init.py`, `config.py`):
90
+ - Credentials supplied via `SEMANTIC_SCHOLAR_API_KEY`, `ZOTERO_USER_ID`, or
91
+ `ZOTERO_API_KEY` environment variables are now detected at init time.
92
+ - A clear dim note is shown for each active env var: *"Already set via
93
+ ENV_VAR — leave blank to keep using the env var."*
94
+ - Prompts for env-var-sourced credentials default to blank instead of
95
+ pre-filling with the resolved (env) value, preventing accidental plain-text
96
+ exposure in `config.toml`.
97
+ - If the user leaves a credential blank and an env var is active, the
98
+ existing `config.toml` value for that field is preserved rather than
99
+ overwritten with an empty string.
100
+ - Entering a new value at the prompt always writes it to `config.toml`,
101
+ regardless of whether an env var is also set.
102
+ - Config loading (`load_config`) was already correct (env vars take
103
+ precedence over `config.toml` at runtime); this fix closes the init-time
104
+ loophole.
105
+
106
+ ## Unreleased
107
+
108
+ _Nothing yet._
orbitr-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jerid Francom
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.