Help us improve
Share bugs, ideas, or general feedback.
From oh-my-claudecode-research
Verifies academic citations against CrossRef and OpenAlex, detects fabricated or wrong entries, and writes audit trail to a project summary table.
npx claudepluginhub youngeun1209/oh-my-claudecode-research --plugin oh-my-claudecode-researchHow this skill is triggered — by the user, by Claude, or both
Slash command
/oh-my-claudecode-research:verify-citationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<Purpose>
Audits citations in LaTeX files: checks \cite keys exist in .bib files and verifies entries via online literature search. Ideal for proofreading academic papers.
Verifies academic citations against CrossRef and BibTeX to catch AI-confabulated references, wrong DOIs, and fabricated co-authors. Run before commits that add or change citations in .md, .tex, .bib, or .ipynb files.
Verifies every citation in a manuscript by fetching cited works to detect ghost papers, wrong IDs, inverted claims, and dead links. Includes optional fix mode for bib corrections and claim rewrites.
Share bugs, ideas, or general feedback.
<Use_When>
literature-curator is about to add a new entry to the project BibTeX file.[CITE: ...] placeholder — verify the candidate before committing.verified_on / verify_status columns of the project summary table.
</Use_When><Do_Not_Use_When>
literature-curator's WebSearch task; this skill verifies once a candidate exists.| Variable | Default | Purpose |
|---|---|---|
BIB_FILE | references.bib | Path to project BibTeX. Used when verifying by citekey. |
SUMMARY_FILE | references.csv | Project literature summary table. When --summary-csv is set (or this is configured in CLAUDE.md), verified_on / verify_status are written back per citekey. |
CITATION_VERIFY_EMAIL | (unset) | Polite-pool email for CrossRef/OpenAlex. Strongly recommended — gives a higher rate limit and prioritizes your requests. |
CITATION_VERIFY_TIMEOUT | 15 | Request timeout in seconds. |
Resolution order on each invocation:
BibTeX file, Summary file, CrossRef email.If BIB_FILE is not configured on first use, ask the user once and offer to persist it.
Four modes:
# Verify a single DOI
python3 verify_citation.py --doi 10.1038/s41586-023-XXXXX-X
# Verify one citekey from the BibTeX file
python3 verify_citation.py --bib references.bib --key smith2023connectome
# Audit the whole BibTeX file
python3 verify_citation.py --bib references.bib
# Audit and also update the project summary table in place
python3 verify_citation.py --bib references.bib --summary-csv references.csv
Optional flags:
--claim "<one-line claim>" attaches the claim to the report so downstream logs show what was being supported. The skill does NOT decide whether the abstract supports it — that is the calling agent's call.--json emits structured JSON instead of the human report.python3 "$CLAUDE_PLUGIN_ROOT"/skills/verify-citation/verify_citation.py \
--bib "$BIB_FILE" \
--summary-csv "$SUMMARY_FILE"
The script:
api.crossref.org/works/<DOI>) for canonical metadata.api.openalex.org/works/doi:<DOI>) for the abstract.--summary-csv is set, writes verified_on (today, YYYY-MM-DD) and verify_status to the matching citekey row. Other columns (our_use, paper_says, bucket, cited_sections) are never overwritten.Human report (per entry):
[PASS|MISMATCH|NOT_FOUND] <citekey or DOI>
DOI: <doi> → CrossRef: <found / not found>
Title: crossref="..." match=<true/false>
Author: crossref="..." (+N more) match=<true/false>
Year: crossref=YYYY match=<true/false>
Abstract (first 500 chars):
"..."
Claim: <if --claim was provided>
→ Read the abstract above and confirm whether it supports the claim.
Note: <any flags>
Statuses:
PASS — DOI resolves AND title/authors/year all match (case-insensitive, normalized).MISMATCH — found at CrossRef but at least one metadata field disagrees with the BibTeX entry.NOT_FOUND — DOI does not resolve and title+author search returns no plausible match.NOT_VERIFIED — network error, surfaced separately; not treated as a pass.The script does NOT decide whether the abstract supports the claim — that requires reading. The calling agent reads the abstract from the report and answers the claim-fit question. The skill provides the evidence; the agent provides the judgment.
<Output_Contract>
stdout: human-readable report (one block per entry) — or JSON if --json was passed.
Exit codes:
0 — all entries PASS.1 — at least one entry MISMATCH or NOT_FOUND.2 — script error (network down on every retry, malformed BibTeX, missing file, etc.).When --summary-csv is set:
verified_on, verify_status (per-citekey).citekey, verified_on, verify_status filled and human-curated columns left blank for literature-curator to fill.our_use, paper_says, bucket, cited_sections, authors, year, title, venue, doi. These belong to the curator.</Output_Contract>
<Failure_Modes>
| Symptom | Cause | Action |
|---|---|---|
urllib.error.URLError / timeout | Network unreachable, CrossRef down | Retry once; if still failing, report affected entries as NOT_VERIFIED (network) and do NOT mark them PASS |
Title matches but DOI does not resolve | BibTeX has a typo in DOI | Report as MISMATCH; the curator agent fixes the DOI |
Author count differs | BibTeX truncates with "et al." | Soft MISMATCH — flagged in notes but does not necessarily fail (curator decides) |
Abstract empty in OpenAlex | Publisher does not deposit abstracts | Skill still PASSes on metadata; caller must verify claim-fit via manuscript download |
Summary file does not exist | First run, no summary table yet | Skill creates the file with the canonical header row |
</Failure_Modes>
- `SKILL.md` — this file. - `verify_citation.py` — the verifier. Stdlib-only (`urllib`, `json`, `csv`, `re`). Runnable as a script or importable.