From medsci-project
Syncs .bib references to Zotero library and generates Obsidian literature notes with cross-cutting concept extraction. Use after /search-lit or to bulk-register references.
How this skill is triggered — by the user, by Claude, or both
Slash command
/medsci-project:lit-syncinheritThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Takes the `.bib` output of `/search-lit` (or any user-specified .bib file) and
Takes the .bib output of /search-lit (or any user-specified .bib file) and
synchronizes the references into the Zotero library and Obsidian literature notes.
When enough literature notes accumulate, extracts cross-cutting concept notes.
02 연구/문헌/ and 02 연구/개념노트/), honor it — never silently
rename a user's folders. For a new or unclear vault, default to the English folders
Literature/ and Concepts/ with the English note templates below.references/locale/ko/note_templates.md — use it when the vault is Korean-structured or the
user prefers Korean notes./search-lit completes — sync the produced .bib into Zotero + Obsidian.references/ folder inside a project workspace./lit-sync is an owner-scoped operation per docs/zotero_policy.md. Collaborators consume the committed manuscript/_src/refs.bib snapshot read-only.<project>/manuscript/_src/refs.bib (owner setup checklist in docs/zotero_policy.md §Setup).$OBSIDIAN_VAULT).Per docs/artifact_contract.md, /lit-sync is the sole writer of:
| Artifact | Writer | Readers |
|---|---|---|
manuscript/_src/refs.bib | /lit-sync (via Better BibTeX auto-export trigger) | /write-paper, /verify-refs, /render |
references/zotero_collection.json | /lit-sync | /verify-refs, /sync-submission |
Direct hand edits to refs.bib are drift — revert on sight.
.bib file (or /search-lit output)
│
▼ Phase 1: Parse
Extract DOI, PMID, title, authors, journal, year
│
▼ Phase 2: Zotero Sync (owner)
Dedupe → zotero_add_by_doi → place in collection → pin citekey
│
▼ Phase 2.5: refs.bib snapshot refresh
Trigger Better BibTeX auto-export → verify manuscript/_src/refs.bib mtime updated
│
▼ Phase 3: Obsidian Literature Notes
Create Literature/{citekey}.md (empty note OK — fill later with highlights)
│
▼ Phase 4: Concept Extraction (conditional)
≥10 literature notes → scan for cross-cutting concepts → propose concept notes
The user-specified .bib file path, or the .bib just produced by /search-lit.
# Parse .bib entries with regex.
# Extract per entry:
# - citekey (e.g., Kim_2024_Validation)
# - doi
# - pmid
# - title
# - authors (first + last minimum)
# - journal
# - year
# - volume, number, pages (if present)
Log any parse failures and skip those entries.
Identify the project from the current working directory or from an explicit user override. Reuse an existing collection key if one is recorded; otherwise create a new collection.
Collection mapping: Check existing Zotero collections for the current project.
If no collection exists, create one with zotero_create_collection. Record the
collection key for future use.
For each entry:
zotero_search_items to search by DOI or title — if already present, skip.zotero_add_by_doi (when a DOI is available) or
zotero_add_by_url (falling back to the PubMed URL when no DOI is available).zotero_manage_collections to place the item in the project collection.Zotero Sync:
Added: 8 papers (new)
Skipped: 3 papers (already in library)
Failed: 1 paper (no DOI/PMID)
Collection: RFA-Meta (TZQEP4NH)
If the Zotero MCP is not connected, skip this entire phase and proceed to Phase 3.
Always write references/zotero_collection.json in the project workspace:
{
"schema_version": 1,
"status": "synced",
"collection": "RFA-Meta",
"collection_key": "TZQEP4NH",
"added": 8,
"skipped": 3,
"failed": 1
}
If Zotero is unavailable, write the same file with status: "skipped" and a
human-readable reason.
Better BibTeX "Keep updated" auto-export normally refreshes manuscript/_src/refs.bib within seconds of a Zotero change. This phase verifies the snapshot actually updated before downstream skills consume it.
Read SSOT.yaml → truth.refs_bib. Default: manuscript/_src/refs.bib. If absent (legacy project), fall back to manuscript/_src/refs.bib and emit a WARN recommending SSOT migration.
Before entering the 10s polling loop in Step 2.5.2, verify both preconditions. If either fails, abort Phase 2.5 with setup instructions instead of waiting for a timeout that will never resolve.
BBT auto-export registered. ~/Zotero/better-bibtex/read-only.json must be a non-empty JSON list. Check with:
python3 -c 'import json,sys,pathlib; p=pathlib.Path.home()/".zotero"/"zotero"/"Profiles"; \
f=pathlib.Path.home()/"Zotero"/"better-bibtex"/"read-only.json"; \
sys.exit(0 if f.exists() and json.loads(f.read_text() or "[]") else 1)'
Or equivalent shell: [ -s ~/Zotero/better-bibtex/read-only.json ] && [ "$(jq 'length' ~/Zotero/better-bibtex/read-only.json)" -gt 0 ].
On failure print:
Phase 2.5 skipped: BBT auto-export not configured (
~/Zotero/better-bibtex/read-only.jsonis empty or missing). Set up "Keep updated" auto-export perdocs/zotero_policy.md§Setup, then re-run/lit-sync.
Target refs.bib exists. The resolved truth.refs_bib path from Step 2.5.1 must exist on disk (even empty is OK — BBT will overwrite). On failure print:
Phase 2.5 skipped: target snapshot
<path>not found. Configure BBT auto-export with "On Change" to the SSOT path, then re-run.
In either early-exit, set refs_bib_refreshed: false + reason: "precondition:<which>" in the Step 2.5.3 JSON and return control to the caller. /verify-refs treats refs_bib_refreshed: false as an unverified snapshot — downstream skills (/write-paper, /render) block until the precondition is resolved.
Rationale (2026-04-24 Phase 1B-b dry-run): on a machine with BBT installed but no auto-export registered, the original Step 2.5.2 polled for 10s then emitted a generic "mtime unchanged" WARN that did not point at the actual cause. Findings: ~/.local/cache/phase1b_b_dryrun/findings.md.
After Phase 2 adds items:
stat -f "%m" manuscript/_src/refs.bib before Zotero writes.<project>/manuscript/_src/refs.bib) and refer to docs/zotero_policy.md §Setup.File → Export Library → Better BibTeX → target path.Append to the JSON written in Step 2.3:
{
"refs_bib_path": "manuscript/_src/refs.bib",
"refs_bib_mtime": "2026-04-24T14:32:11Z",
"refs_bib_refreshed": true,
"citekeys_verified": ["Kim_2024_Validation", "..."]
}
If refresh failed, set refs_bib_refreshed: false and include reason. /verify-refs uses this flag to decide whether the snapshot is trustworthy.
# Default English layout; substitute the vault's existing folder if one is present
# (e.g. "02 연구/문헌/" for a Korean-structured vault — see references/locale/ko/note_templates.md).
ls "$VAULT/Literature/" | grep -v "📊" | wc -l
For each .bib entry, create Literature/{citekey}.md (or the vault's existing literature folder).
Skip if the file already exists (never overwrite).
---
notetype: literature
citekey: "{citekey}"
title: "{title}"
authors: "{authors}"
journal: "{journal}"
year: {year}
doi: "{doi}"
pmid: "{pmid}"
created: "{today}"
tags:
- type/literature
- _unread
---
# {title}
## Bibliographic info
- **Authors**: {authors}
- **Journal**: {journal}{volume_issue_pages}
- **Year**: {year}
- **DOI**: [{doi}](https://doi.org/{doi})
{pmid_line}
## Key points (in my own words)
## My thoughts
## Related notes
- [[Research Hub]]
- [[Papers & Reviews]]
-
-
(For a Korean-structured vault, use the Korean-heading template in references/locale/ko/note_templates.md and the vault's own hub-note names.)
Rules:
notetype: literature — compatible with the Zotero Integration template._unread tag — change to _read later after the user reads the PDF in Zotero and adds highlights.## Key points and ## My thoughts blank — the user fills these in personally.## Related notes contains 2 hub links + 2 empty slots (reserved for later concept-note linking).Obsidian Literature Notes:
Created: 8 notes (new)
Skipped: 3 notes (already exist)
Location: Literature/
Total in vault: 12 literature notes
Run this phase only when there are ≥10 literature notes in the vault. If fewer exist, print a status message like "N literature notes — concept extraction unlocks at ≥10" and stop.
Read all files under Literature/*.md (or the vault's existing literature folder):
Exclude from concept candidates:
Whatever remains becomes a concept-note candidate.
Create Concepts/{concept name}.md (or the vault's existing concept-note folder):
---
title: "{concept name}"
type: concept
tags:
- concept
- {domain tag}
aliases:
- {alternative name}
related_papers:
- "[[{lit-note-1}]]"
- "[[{lit-note-2}]]"
- "[[{lit-note-3}]]"
status: 🌱Seedling
---
# {concept name}
## Definition (My Understanding)
> TODO: write in your own words
## Why it matters
{why the concept matters in this domain — AI supplies a draft}
## Per-paper perspectives
- **[[{lit-note-1}]]**: {this paper's angle}
- **[[{lit-note-2}]]**: {a different angle}
- **[[{lit-note-3}]]**: {comparison / complement}
## Related concepts
- [[{another concept}]]
## Open questions
- {open question 1}
- {open question 2}
## Related notes
- [[Research Hub]]
- [[{related project hub}]]
- [[{lit-note-1}]]
- [[{lit-note-2}]]
(For a Korean-structured vault, use the Korean-heading concept template in references/locale/ko/note_templates.md.)
Key rules:
## Definition section as a > TODO marker — the 2nd-layer note only becomes
meaningful once the user writes the definition in their own words.status always starts at 🌱Seedling.## Related notes (vault convention).Concept-note candidates (≥3 papers cross-referenced):
1. {Concept A} (4 papers)
2. {Concept B} (3 papers)
3. {Concept C} (5 papers)
Create? (all / selected / skip)
Create only after user confirmation. Auto-draft but always confirm.
This skill can run without a fresh .bib file.
On an explicit concept-extraction request, scan existing
Literature/*.md (or the vault's existing literature folder) and run only Phase 4.
On a "tidy this project's references" request, locate .bib files inside the
workspace and run Phase 1–3.
On a "sync Zotero" request, diff the Zotero collection against the .bib file
and add whatever is missing.
When the user supplies a list of PMIDs (e.g., from a HANDOFF or a colleague), resolve PMIDs to DOIs via PubMed esummary first, then enter Phase 2 with the DOIs:
PMIDS="12345,67890,..."
curl -s "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=pubmed&id=${PMIDS}&retmode=json" \
| jq -r '.result | to_entries[] | select(.key != "uids") | "\(.value.uid)\t\(.value.elocationid)\t\(.value.title)"'
For each resolved DOI call zotero_add_by_doi (auto-dedup by DOI). For items
already in the library (returned as "skipped" or detected via zotero_search_items
by DOI), use zotero_manage_collections to attach them to the project collection
without re-adding — re-adding by URL/PubMed-URL would bypass DOI dedup and
create duplicates. Record both added and existing items in
references/zotero_collection.json.
If a PMID has no DOI in PubMed (rare; older papers, non-indexed), fall back to
zotero_add_by_url with the PubMed URL and mark the entry as no_doi: true.
## Definition of a concept note — keep the TODO marker; the
essence of the 2nd-layer note is the user's own wording.refs.bib to compensate (violates artifact contract).refs.bib directly. Only Better BibTeX auto-export may write that file. If auto-export is broken, fix the Zotero setup rather than writing the file from this skill.SSOT.yaml reference_manager.required_for), abort with instructions to flag [@NEW:topic] placeholders in the manuscript and notify the owner.npx claudepluginhub aperivue/medsci-skills --plugin medsci-projectImports and synchronizes Zotero literature with an Obsidian vault. Searches Zotero library, inspects items/collections, imports notes with attachments and annotations, and batch-ingests collections.
Performs standalone Zotero library operations — adding abstracts, attaching PDFs, enriching metadata, deduplicating, and fixing BBT citation keys. Not for systematic review pipelines.
Builds Wikipedia-style Obsidian vaults from academic PDFs, extracting concepts into linked notes with atomic sentences and citations. Expands existing networks with new papers.