/ref:audit
phase 11PubMed MCP
Re-check retraction and erratum status across the library, or record and show dated cited-by observations.
/ref:audit [selector]
/ref:audit --citations [selector]
/ref:audit show-citations [--stale-days N] [selector]
Arguments
- [selector]
- Optional selector (
<pmid…>, --project, --study, --search, --from-file, …) to scope the sweep. Unlike other selector commands, no selector means the whole library, not an error: this is a maintenance sweep, not a saved set.
- --citations
- Record citation observations instead of retraction checks. Run the two modes separately.
- show-citations
- An action, not a flag. Print each PMID's latest real observation and whether it is stale, without checking anything new.
- --stale-days N
- For
show-citations: age threshold in days (default 180).
What happens
Default mode (retraction status):
- For each resolved PMID, Claude calls
get_article_metadata and checks article_types for a retraction, erratum or correction marker. This is the same check /ref:extract runs at ingestion.
- A successful check updates
retraction_status in meta.json and reports whether it changed. A failed check leaves the prior status untouched and prints a diagnostic. It never guesses "none" or downgrades an earlier "retracted".
- If any status changed,
propagate lists saved /ref:brief, /ref:compare and /ref:summarize artifacts that used those papers. They are not refreshed automatically; you run --refresh on them, and their earlier frozen versions are kept.
--citations mode: for each PMID, a paper with no PMCID gets a no_pmcid marker, a failed lookup gets a check_failed marker, and a real count is stored with its source, query, coverage note and retrieval date. Every call appends to citations.json; earlier entries are never overwritten, and a failure never becomes a zero.
No citation-count source is wired in yet. None of the PubMed MCP tools available return a PMC cited-by count, and related-article similarity is not citation. The storage and display machinery is built and tested, but until a real source exists, --citations has nothing to record and the command says so instead of inventing counts.
show-citations: skips check_failed and no_pmcid markers and shows the most recent real observation. A PMID with none shows null, which means "no observation available", not zero.
Reads & writes
Retraction mode updates papers/<pmid>/meta.json (retraction_status). --citations appends to papers/<pmid>/citations.json. propagate and show-citations only read: saved artifact manifests under tables/, summaries/, briefs/ (library-wide and per project), and citations.json.
Example
output from a scratch library (check results written by hand)
› /ref:audit
31452104: checked (none -> retracted) [CHANGED]
30679568: check_failed -- prior status retained (unknown); get_article_metadata timed out
# followed by the same results as JSON, ending in:
"changed_pmids": [
"31452104"
]
# propagate: which saved artifacts used the changed paper
{
"changed_pmids": [
"31452104"
],
"potentially_stale_artifacts": []
}
› /ref:audit show-citations 31452104
[
{
"pmid": "31452104",
"latest_observation": null,
"stale": null
}
]
Under the hood
audit.py retraction --repo <library_root> --results-file <json> [selector], then audit.py propagate --repo <library_root> --results-file <changed-pmids-json>; audit.py citations --repo … --results-file … [selector]; audit.py show-citations --repo … [--stale-days N] [selector]. The script never calls PubMed; Claude supplies the check results as JSON.
See also
/ref:report (--citations) · /ref:brief · /ref:compare · /ref:summarize · /ref:index
/ref:index
phase 0refresh: phase 8
Rebuild the SQLite search catalog from committed records and flag graph relations whose evidence has changed.
/ref:index --rebuild
Arguments
- --rebuild
- Required. It is currently the only supported mode.
What happens
- Deletes
index/catalog.sqlite and recreates it from scratch. Files are the authority and the catalog is only a projection, so rebuilding beats migrating in place.
- Indexes one row per paper from
meta.json (including retraction status), paragraphs of each paper's current source.md into passages_fts (headings become section labels), and every claim in claim_registry.json, whether active or superseded, into claims and claims_fts.
- Only what
current.json points at is read; incomplete staging directories are ignored. Running it twice gives the same result.
- If
graph/relations.jsonl exists, a relation refresh sets stale: true on any relation whose supporting claims are no longer active. A review decision is never cleared, only flagged for re-confirmation, and the stale relation IDs are reported.
Reads & writes
Reads papers/*/meta.json, current.json, versions/<v>/source.md, claim_registry.json. Writes index/catalog.sqlite and, when present, updates graph/relations.jsonl (stale, stale_reason).
Example
output from a scratch library
› /ref:index --rebuild
catalog rebuilt: 2 paper(s), 0 passage(s), 1 claim(s) indexed
# passages come only from source.md in the version current.json names;
# here /ref:extract committed last, and its version holds claims, not source.md
# with a graph present, the relation refresh then prints JSON of the form
# {"relations_checked": N, "went_stale": [relation IDs]}
Under the hood
catalog.py rebuild --repo <library_root>, then, if graph/relations.jsonl exists, relation.py refresh --repo <library_root>.
See also
/ref:status · /ref:search · /ref:weave · Concepts
/ref:status
phase 0read-only
Show which library is configured, catalog counts, extraction-tier breakdown, source completeness, project summaries, and a few recent papers. Read-only.
/ref:status
Arguments
None.
What happens
- Reads the config file. If none exists, it errors and points you at
/ref:init; it never guesses a path.
- Prints the library root, and errors if that directory no longer exists.
- Prints the number of papers in the catalog (or a hint to run
/ref:index --rebuild if the catalog hasn't been built), an extraction-tier breakdown, and source/completeness counts.
- Prints per-project summaries and, where useful, a short list of recent papers.
- Each recent paper carries a concise source badge —
full-text, pdf-backed, oa-pending, abstract-only, or metadata-only — so you can see acquisition state at a glance without opening records. Claude keeps this compact; the goal is quick orientation, not a substitute for full search.
Reads & writes
Reads ~/.config/ref-manager/config.json, index/catalog.sqlite, projects/, and papers' meta.json for source/completeness state. Writes nothing.
Example
output from a scratch library (paths shortened)
› /ref:status
library: /private/tmp/…/scratch/lib
catalog: not built yet (run /ref:index --rebuild)
projects: 0
› /ref:index --rebuild
› /ref:status
library: /private/tmp/…/scratch/lib
papers indexed: 2
by extraction tier: {"abstract": 1, "full": 1, "unavailable": 0}
by source completeness: {"full-text": 1, "abstract-only": 1}
projects: 0
recent papers:
10000001 rivera2019cortical (full-text)
10000002 chen2019editorial (abstract-only)
Under the hood
status.py (no arguments).
See also
/ref:init · /ref:index
/ref:open
phase 3Papers.app
Open a paper's acquired PDF in Papers.app for reading and annotating.
/ref:open <pmid>
Arguments
- <pmid>
- Required. The paper must be in the library.
What happens
- Finds the paper's acquired PDF under
raw/<sha256>/.
- If there is none, reports
no_pdf_acquired and points at /ref:fetch or /ref:attach. This is not an error.
- Otherwise runs
open -a Papers <pdf>, just like double-clicking the file in Finder, and reports opened with the path.
This is the only ref-manager command that touches Papers.app itself, and it never touches the Papers database. Highlights you make there come back with /ref:pull-annotations.
Reads & writes
Reads papers/<pmid>/meta.json and the acquired PDF. Writes nothing in the library; Papers.app receives the file.
Example
output from a scratch library
› /ref:open 31452104
{
"pmid": "31452104",
"status": "no_pdf_acquired",
"message": "no acquired PDF for this paper yet — run /ref:fetch or /ref:attach first"
}
illustrative output
› /ref:open 31452104
{
"pmid": "31452104",
"status": "opened",
"path": "<library_root>/papers/31452104/raw/<sha256>/source.pdf"
}
Under the hood
open_in_papers.py <pmid> --repo <library_root>; PDF lookup shared with export_papers.py.
See also
/ref:pull-annotations · /ref:export-papers · Papers handoff tutorial
/ref:pull-annotations
phase 4Papers.app
Copy your Papers.app highlights and margin notes for one paper into its record, one-way and without loss.
/ref:pull-annotations <pmid>
Arguments
- <pmid>
- Required. The paper must already exist (
/ref:add first).
What happens
- Claude looks for
~/Library/Application Support/Papers/*.db and passes the first match. If there is none, the result is no_papers_db_configured, not a failure.
- The live database is copied with SQLite's backup API from a read-only connection, and all reads happen on that copy. If no consistent copy can be taken, the result is
snapshot_unavailable.
- The Papers item is matched by PMID, falling back to DOI. No match gives
no_matching_papers_item, meaning the paper isn't in your Papers library.
- Annotations are upserted by their own Papers
id, into added, updated or unchanged. A repeat pull with no upstream changes reports everything unchanged and never duplicates.
- An annotation deleted in Papers since the last pull is tombstoned (
deleted_upstream: true with a timestamp), not deleted locally, and listed under tombstoned. An accidental deletion or sync hiccup in Papers never silently erases evidence you already had.
Your free-text notes.md (from /ref:note) is never touched; annotations and notes are stored separately.
Reads & writes
Reads papers/<pmid>/meta.json and a snapshot of the Papers database. Writes papers/<pmid>/annotations.json (with papers_item_id and last_pulled_at). Never writes to Papers.
Example
output from a scratch library
› /ref:pull-annotations 31452104
# no Papers database on this machine
{
"pmid": "31452104",
"status": "no_papers_db_configured"
}
illustrative output
› /ref:pull-annotations 31452104
{
"pmid": "31452104",
"status": "ok",
"added": ["<annotation id>"],
"updated": [],
"tombstoned": ["<annotation id>"],
"unchanged": ["<annotation id>", "<annotation id>"]
}
Under the hood
pull_annotations.py --repo <library_root> --pmid <pmid> [--papers-db <path>]; snapshot access via papers_snapshot.py.
See also
/ref:open · /ref:note · /ref:export-papers · Papers handoff tutorial