A personal literature surveillance pipeline for tracking new papers from top journals (Nature, Science, Cell, PNAS families and beyond) using OpenAlex, Crossref, and Semantic Scholar APIs.
Frontier Tracker automates the weekly literature monitoring workflow:
- Scan — Query OpenAlex for new papers from your configured journal watchlist
- Enrich — Fill missing abstracts via three-way fallback (OpenAlex → Crossref → Semantic Scholar)
- Screen — Classify papers into core / proxy / eco / noise tiers using your research profile keywords
- Render — Output results as Excel dashboards, interactive HTML tables, Obsidian notes, or inline previews
No API keys required — uses public OpenAlex, Crossref, and Semantic Scholar endpoints.
git clone https://github.com/yourusername/frontier-tracker.git
cd frontier-tracker
pip install openpyxl # optional, for Excel outputCopy the example profile and customize it:
cp references/example-profile.md references/my-profile.md
# Edit references/my-profile.md with your research keywordsSee references/research-profile-template.md for the full template with all fields.
The default watchlist covers Nature, Science, Cell, PNAS families and related top journals:
python -X utf8 scripts/build_top_family_watchlist.py --format json --output state/watchlist.jsonTo also resolve OpenAlex source IDs (recommended for first run):
python -X utf8 scripts/build_top_family_watchlist.py --format json --resolve-openalex --output state/watchlist.jsonpython -X utf8 scripts/scan_recent_papers.py --days 7 --update-state --output outputs/data/frontier_scan_$(date +%Y-%m-%d).jsonpython -X utf8 scripts/enrich_abstracts.py \
--input outputs/data/frontier_scan_$(date +%Y-%m-%d).json \
--output outputs/data/frontier_enriched_$(date +%Y-%m-%d).json \
--field newpython -X utf8 scripts/screen_by_profile.py \
--input outputs/data/frontier_enriched_$(date +%Y-%m-%d).json \
--profile references/my-profile.md \
--output outputs/data/frontier_screened_$(date +%Y-%m-%d).json \
--field both# Excel dashboard
python -X utf8 scripts/render_display_outputs.py --mode excel --scan-json outputs/data/frontier_screened_$(date +%Y-%m-%d).json
# Interactive HTML table
python -X utf8 scripts/render_display_outputs.py --mode app --scan-json outputs/data/frontier_screened_$(date +%Y-%m-%d).json
# Obsidian/Zotero notes
python -X utf8 scripts/render_display_outputs.py --mode notes --scan-json outputs/data/frontier_screened_$(date +%Y-%m-%d).json
# Quick inline preview
python -X utf8 scripts/render_display_outputs.py --mode codex --scan-json outputs/data/frontier_screened_$(date +%Y-%m-%d).jsonpython -X utf8 scripts/auto_weekly_report.py \
--input outputs/data/frontier_enriched_$(date +%Y-%m-%d).json \
--profile references/my-profile.md┌─────────────────────────────────────────────────────────┐
│ references/top-journal-families.md │
│ (journal watchlist seed) │
└────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ build_top_family_watchlist.py │
│ → state/watchlist.json │
└────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ scan_recent_papers.py │
│ → outputs/data/frontier_scan_<date>.json │
│ → state/reading_state.json (de-duplication) │
└────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ enrich_abstracts.py │
│ → outputs/data/frontier_enriched_<date>.json │
└────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ screen_by_profile.py │
│ (uses references/<your-profile>.md) │
│ → outputs/data/frontier_screened_<date>.json │
└────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ render_display_outputs.py / auto_weekly_report.py │
│ → outputs/excel/, outputs/app/, outputs/notes/ │
│ → outputs/reports/ │
└─────────────────────────────────────────────────────────┘
frontier-tracker/
├── scripts/
│ ├── scan_recent_papers.py # OpenAlex paper scanner
│ ├── enrich_abstracts.py # Multi-source abstract enrichment
│ ├── screen_by_profile.py # Keyword-based paper screening
│ ├── render_display_outputs.py # Multi-format output renderer
│ ├── build_top_family_watchlist.py # Watchlist builder from markdown
│ ├── auto_weekly_report.py # One-command weekly HTML report
│ └── read_local_chinese_journal_pool.py # Optional Chinese journal supplement
├── references/
│ ├── top-journal-families.md # Default journal watchlist (Nature/Science/Cell/PNAS)
│ ├── example-profile.md # Example research profile
│ ├── research-profile-template.md # Full profile template
│ ├── source-and-selection-rules.md # Journal selection rules
│ ├── output-templates.md # Digest/report templates
│ └── display-output-options.md # Output format guide
├── outputs/ # Generated outputs (gitignored)
│ ├── data/
│ ├── reports/
│ └── .gitkeep
├── state/ # Persistent state (gitignored)
│ └── .gitkeep
├── SKILL.md # Agent skill instructions
├── README.md
├── LICENSE
└── .gitignore
Your profile controls which papers are classified as core, proxy, or noise. Create it at references/<name>.md with these sections:
- Core keywords — Directly on-topic (classified as "core")
- Proxy keywords — Adjacent methods/themes (classified as "proxy")
- Eco-context keywords — Broader context (classified as "eco")
See references/example-profile.md for examples.
Edit references/top-journal-families.md to customize which journals to track. The file uses markdown tables organized by pool:
flagship-general-full-track— Nature, Science, Cell, PNAStop-family-full-track— High-signal family journalstop-family-relevant-only— Broad journals (filtered by relevance)
If you have CAS or JCR ranking data, you can enrich the watchlist:
- Add ranking columns to your watchlist
- Use
full-trackfor CAS Zone 1-2 or JCR Q1 - Use
relevant-onlyfor CAS Zone 3-4 or JCR Q2-Q4
For Chinese-language journals, use the optional supplement:
python -X utf8 scripts/read_local_chinese_journal_pool.py --workbook path/to/journal_list.xlsx --format jsonpython -X utf8 scripts/scan_recent_papers.py \
--days 7 \ # Look-back window
--update-state \ # Persist de-duplication state
--output outputs/data/scan.json \ # Output file
--pools flagship-general-full-track top-family-full-track # Journal poolspython -X utf8 scripts/enrich_abstracts.py \
--input scan.json \ # Input scan results
--output enriched.json \ # Output with abstracts
--field new \ # Which field to enrich (new, already_seen, both)
--force # Re-fetch even if abstract existspython -X utf8 scripts/screen_by_profile.py \
--input enriched.json \ # Input enriched data
--profile references/my-profile.md \ # Your research profile
--output screened.json \ # Output with tier classifications
--field both # Which field to screenpython -X utf8 scripts/render_display_outputs.py \
--mode excel \ # Output mode: excel, app, notes, codex
--scan-json screened.json \ # Input data
--output outputs/excel/ # Output pathpython -X utf8 scripts/auto_weekly_report.py \
--input enriched.json \ # Enriched data
--profile references/my-profile.md \ # Profile for screening
--output outputs/reports/weekly.html # HTML report outputpython -X utf8 scripts/build_top_family_watchlist.py \
--reference references/top-journal-families.md \ # Source markdown
--resolve-openalex \ # Also fetch OpenAlex source IDs
--format json \ # Output format: json or csv
--output state/watchlist.jsonAlways use python -X utf8 on Windows to avoid encoding errors with non-ASCII characters in journal names and paper titles.
- Python 3.10+
openpyxl(optional, for Excel output)
All API calls use Python's built-in urllib — no external HTTP libraries needed.
MIT License — see LICENSE.