From co-researcher
Synthesizes existing knowledge, identifies research gaps, and traces scientific idea evolution using OpenAlex and systematic literature review methods.
How this skill is triggered — by the user, by Claude, or both
Slash command
/co-researcher:literature-reviewThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
<role>
references/arxiv/query_syntax.mdreferences/openalex/authors.mdreferences/openalex/geo_and_language.mdreferences/openalex/institutions.mdreferences/openalex/publishers_funders.mdreferences/openalex/sources.mdreferences/openalex/taxonomy.mdreferences/openalex/topics.mdreferences/openalex/type_values.mdreferences/openalex/works.mdscripts/build_corpus.pyscripts/europepmc_api.pyscripts/http_client.pyscripts/jats.pyscripts/openalex_cli.pyscripts/prisma_counts.pyscripts/read_paper.pyscripts/search_arxiv.pyscripts/verify_citations.py<search_backend>
This skill owns the command-line search backends in scripts/. They are not separate skills. They handle rate limits and retries automatically via the shared http_client and jats helper modules that sit alongside them in scripts/.
Invocation & workspace (read first): Invoke each script by its absolute path under this skill's base directory (shown to you above as "Base directory for this skill") — e.g. uv run <skill-dir>/scripts/openalex_cli.py …. Never cd into the skill directory. Stay in the directory where the user invoked the skill and anchor the review workspace there with an absolute path: compute WS="$(pwd)/review/{slug}" once at step 1 and pass $WS as --workspace everywhere. Relative review/{slug} resolves against the wrong directory and pollutes the installed plugin. In the command examples below, scripts/… is shorthand for <skill-dir>/scripts/….
Prerequisites:
uv must be installed. Verify with uv --version. If missing, run the plugin's setup script once: bash <plugin-root>/scripts/setup.sh. The setup script installs uv, prompts (optionally) for an OpenAlex API key, and warms the dependency cache. Fallback if setup is unreachable: curl -LsSf https://astral.sh/uv/install.sh | sh && export PATH="$HOME/.local/bin:$PATH".OPENALEX_API_KEY (optional, recommended). Without it, OpenAlex runs in the unauthenticated polite pool — $0.01/day budget, ~10 filter queries or 1 --search per day before throttling. With a free key, $1/day budget at ~10 req/s. The key lives in ~/.env. Never read, print, cat, echo, or otherwise inspect ~/.env — credentials must stay out of the agent's context. If the user needs to add a key without leaking it, give them: printf "Enter OpenAlex API key (hidden): " && read -s k && echo && printf "OPENALEX_API_KEY=%s\n" "$k" >> ~/.env && unset k && echo "Saved.".1. OpenAlex — scripts/openalex_cli.py (cross-disciplinary, ~250M works)
Use as the default broad search. Full reference: references/openalex/works.md, authors.md, topics.md, etc.
uv run scripts/openalex_cli.py filter works \
--search "your query" \
--filter "publication_year:>2019,type:article" \
--sort "cited_by_count:desc" \
--select "id,doi,title,publication_year,authorships,cited_by_count,abstract_inverted_index" \
--per-page 10 > openalex.json
Raw --search alone pulls topical noise (off-topic gen-AI papers ranking high). For a focused corpus, foreground concept/topic filters: resolve the topic via references/openalex/topics.md, then narrow with --filter "topics.id:T<id>" (or concepts.id:C<id>) and use --search only to rank within that slice. Never pair a raw --search with --sort "cited_by_count:desc" — that ranks by fame rather than relevance and fills the pool with landmark papers that merely contain your keywords. Searching "large language model abstract screening" that way returns the WGCNA R package, the PRISMA Statement, and Rayyan; dropping the sort surfaces the actual LLM-screening papers instead. Sort by citations only inside an already-narrow topic filter. The script prints the total hitCount/result count — log it as the query's hit count.
2. arXiv — scripts/search_arxiv.py (preprints: CS, physics, math, quant-bio, stat)
Use for very recent work, ML/CS topics, and physics. Query syntax: references/arxiv/query_syntax.md.
uv run scripts/search_arxiv.py \
--query "ti:\"your phrase\" AND cat:cs.LG" \
--sort_by relevance \
--max_results 10 > arxiv.json
Default to relevance for a review — it surfaces foundational work. Use --sort_by submittedDate --sort_order descending only when you specifically want the newest preprints; date-sort biases the pool to the most recent month and misses seminal papers. Emits one JSON object with a results_count field — log that as the hit count.
3. Europe PMC — scripts/europepmc_api.py (life-science open-access full text + citation graph)
Use for biomedical topics, full-text retrieval, and forward/backward citation chaining.
uv run scripts/europepmc_api.py search "your query AND HAS_FT:y" \
--sort "CITED desc" --max_results 10 --output europmc.json
uv run scripts/europepmc_api.py get_citations MED <PMID> --output citing.json
uv run scripts/europepmc_api.py get_references MED <PMID> --output refs.json
uv run scripts/europepmc_api.py get_fulltext <PMCID> --output fulltext.txt
4. Full-text acquisition — scripts/read_paper.py (any identifier → markdown)
One call per paper; resolves the best legal open-access route automatically.
uv run scripts/read_paper.py --doi 10.1038/s41586-021-03819-2 --workspace "$WS"
uv run scripts/read_paper.py --arxiv 1706.03762 --workspace "$WS"
uv run scripts/read_paper.py --pmcid PMC8371605 --workspace "$WS"
($WS is the absolute workspace from step 1 — $(pwd)/review/{slug}; never pass a relative path.) Prints one JSON line: {"status": "fulltext|abstract-only", "path": ..., "source": ..., "id": ...}. Files land in $WS/papers/{id}/ (paper.pdf, fulltext.md or abstract.md). A PDF the user drops at $WS/papers/{id}/paper.pdf is picked up before any network call. Paywalled papers return abstract-only — never scrape for them.
5. Corpus assembly — scripts/build_corpus.py (raw backend JSON → normalized, deduplicated corpus.json)
uv run scripts/build_corpus.py --openalex "$WS/openalex.json" \
--arxiv "$WS/arxiv.json" --epmc "$WS/epmc.json" --output "$WS/corpus.json"
Each flag is repeatable. Papers found by several backends are merged into one record with a joined found_via (openalex+epmc). Running it again against an existing corpus.json adds only new papers — screening decisions, fulltext, and role on records already there are left untouched, so later search rounds and snowballing never discard prior work.
6. Citation verification — scripts/verify_citations.py (bibliography → verified/mismatched/not_found/retracted)
uv run scripts/verify_citations.py --input "$WS/refs.json"
Input: JSON array ([{"doi", "title"}] or bare strings), BibTeX (.bib), or a text/markdown file with one citation per line (DOIs extracted automatically). Resolves each through OpenAlex, then Europe PMC for DOIs, then cross-checks every clean DOI against Crossref's Retraction Watch data (OpenAlex's own retraction flag misses some withdrawn papers). Set CO_RESEARCHER_USER_AGENT="your-tool (mailto:[email protected])" to use Crossref's faster polite pool. Retraction is checked down a ladder — OpenAlex, then Crossref for DOIs, then the paper's PubMed record when there is no DOI (or when Crossref cannot answer). Every result carries retraction_checked and retraction_source; a check that could not run is reported as unchecked, never as clean. Prints a JSON report; exit 0 only when every citation verifies. Run it on any bibliography before presenting it — a mismatched result means the DOI exists but the claimed title doesn't match it (the classic fabrication pattern), and retracted means the paper exists but has been withdrawn; fix or drop either before output.
7. PRISMA counts — scripts/prisma_counts.py (corpus.json → PRISMA 2020 flow numbers)
uv run scripts/prisma_counts.py --corpus "$WS/corpus.json"
Reports records by source, after-dedup, screened, excluded-by-reason, included, not-retrieved, and in-synthesis. Exits 1 if any excluded record lacks a reason. Used by systematic-review; useful in any review to sanity-check that the corpus bookkeeping matches reality.
Picking a backend:
For broad reviews, run all three in parallel and dedupe by DOI in step 3. </search_backend>
All state lives in a review workspace `review/{slug}/`: `protocol.md` (question, criteria, query log), `corpus.json` (candidate pool + screening decisions), `papers/{id}/` (full texts + notes), `synthesis.md`. Create it at step 1; on a restarted session, read `corpus.json` first and resume where screening left off.protocol.md. Pause for user approval of the criteria. Scoping searches may revise the question; append revisions, never overwrite.protocol.md with date and hit count. Save raw JSON in the workspace; do not load it into context wholesale.build_corpus.py on the raw backend files; never hand-merge them. It emits one record per paper — key (normalized DOI, else normalized title, else a source identifier), ids, title, year, cited_by (highest any backend reported), found_via, screening: {status, stage, reason}, fulltext, role — deduplicated across backends, and re-running it after a later search round preserves every screening decision already made.uv run scripts/build_corpus.py --openalex "$WS/openalex.json" \
--arxiv "$WS/arxiv.json" --epmc "$WS/epmc.json" --output "$WS/corpus.json"
Title/abstract screening — Set screening.status (included/excluded) and reason per record. Exclusion reasons are mandatory. If the pool exceeds ~50, pilot-screen a random ~20 first and surface borderline calls to the user before bulk screening.
Acquire & read — For each included paper, run read_paper.py. Write the script's status value ("fulltext" or "abstract-only") into that paper's fulltext field in corpus.json immediately — prisma_counts.py reads this field to compute not_retrieved and in_synthesis, and a record left at null is counted as not retrieved. Classify each as evidence (bears directly on the question) or background. Evidence papers require notes.md written from the full text — methods and results actually read via Read/Grep on fulltext.md, one paper at a time, never multiple full texts in context. Background papers may be cited at abstract level. notes.md format: citation, read depth, design, N, key effects, claims relevant to the question (with section anchors), limitations, theme tags. Do not trust extracted tables — multi-column tables come through as scrambled line fragments; re-read the source PDF for any tabular data.
Navigating fulltext.md depends on the route, given by the source field of the script's JSON line. source: "epmc" (JATS) yields real markdown headings — find sections with grep -n "^#". Every PDF route (arxiv_pdf, oa_pdf, user_pdf, cached) yields no # headings at all; section titles appear as bold lines, so use grep -n "^\*\*" instead. A grep "^#" returning nothing on a PDF-route paper means you used the wrong pattern, not that the document is unstructured. If neither pattern finds a section you need, cite by content rather than a section anchor.
Snowball — For core evidence papers, run get_references/get_citations (Europe PMC) or follow OpenAlex referenced_works. Fold the new candidates into the same corpus.json with build_corpus.py --epmc citing.json --found-via snowball:citations --output "$WS/corpus.json" (it adds only what's new, tags their provenance, and preserves decisions already made), then screen them at step 4. One round by default; stop when a round adds nothing. If included papers reveal vocabulary the original queries missed, run one adapted search round and log it.
Synthesize — Write synthesis.md from notes.md files only. Tag any citation whose fulltext is abstract-only with [abstract-only] inline. End with a retrieval summary listing papers not retrieved and the papers/{id}/paper.pdf path where the user can drop a legally obtained PDF for a re-run.
Verify bibliography — Write the final citation list to $WS/refs.json and run verify_citations.py --input "$WS/refs.json". Exit 0 is required before presenting; correct or remove any mismatched/not_found entry. (Papers screened through corpus.json will pass — this gate catches citations that entered the synthesis from memory rather than from the corpus.)
<output_format>
Research question: [Stated] Inclusion criteria: [Population / methods / date / language] Searches executed:
<exact query> → N hits<exact query> → N hits<exact query> → N hitsThematic synthesis:
Research gaps:
Annotated bibliography:
Retrieval summary: [N full-text / N abstract-only; drop-in paths for missing PDFs] </output_format>
Pause for the user only at: (1) end of Scope — criteria approval; (2) pilot screening — borderline calls; (3) before Synthesize, if the included set is unexpectedly large (>40) or small (<3). Otherwise run the funnel autonomously.npx claudepluginhub poemswe/co-researcher --plugin co-researcherRuns a systematic literature review workflow: scope definition, multi-source search (arXiv, Semantic Scholar, Google Scholar), screening, extraction, synthesis, and gap analysis.
Conducts systematic literature reviews across PubMed, arXiv, bioRxiv, and Semantic Scholar, producing markdown and PDF output with verified citations. Use for meta-analysis, research synthesis, or broad literature searches in biomedical and scientific domains.
Conducts systematic literature reviews across PubMed, arXiv, bioRxiv, Semantic Scholar and other academic databases. Generates professionally formatted markdown and PDF documents with verified citations in APA, Nature, Vancouver and other styles.