mcpField
L
lit-sync

lit-sync

github.com/aperivue生产力353108 次安装1 天前更新
代码仓库

关于 lit-sync

lit-sync 将 BibTeX 文献同步到 Zotero,并在 Obsidian 中创建文献笔记;适用于项目负责人管理研究引用的工作流。它通过 Better BibTeX 刷新引用快照,并可按需检索全文。

主要功能

  1. 01解析 BibTeX 条目
  2. 02去重后同步文献到 Zotero
  3. 03验证 Better BibTeX 引用快照
  4. 04在 Obsidian 创建文献笔记
  5. 05按需检索全文并生成报告

适用场景

  1. 01将检索到的文献加入项目 Zotero 集合
  2. 02检查 refs.bib 快照是否更新
  3. 03把文献整理为 Obsidian 笔记
  4. 04按需获取并汇总论文全文

MCP 相关

可使用 Zotero MCP 服务搜索、添加文献并管理集合;未连接时会跳过该同步阶段。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/aperivue/medsci-skills --skill lit-sync

安装后在 Claude Code 中输入/lit-sync确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

skill.yml
references/
bbt_lookup.md
concept_note_template.md
fulltext_report.md
references/locale/ko/
note_templates.md
scripts/
check_citekey_provenance.py
tests/
citekey_provenance_challenge_selftest.sh
citekey_provenance_challenge.sh
test_poll_logic.sh

详细介绍

文献同步:Zotero + Obsidian 流水线

获取 /search-lit 的 .bib 输出(或用户指定的任意 .bib 文件),将参考文献同步到 Zotero 文库和 Obsidian 文献笔记中,并在文献笔记积累到足够数量后提取跨主题概念笔记。

前置条件

  • 仅限项目所有者。 协作者只能以只读方式使用已提交的 manuscript/_src/refs.bib 快照。若当前用户是协作者(根据 SSOT.yaml 中的 reference_manager.required_for,其无权访问 Zotero),则中止操作,并说明应在稿件中标记 [@NEW:topic] 占位符并通知所有者。
  • Zotero 桌面版 7.x + Better BibTeX 插件,并已将 Better BibTeX 的“Keep updated”自动导出配置为 <project>/manuscript/_src/refs.bib。
  • Zotero MCP 服务(若未连接,则跳过阶段 2;重新打开 Zotero 后,自动导出仍会刷新文件)。
  • Obsidian CLI 或直接写入保管库文件;保管库路径取自用户环境(例如 $OBSIDIAN_VAULT)。

产物约定

/lit-sync 是 manuscript/_src/refs.bib(通过触发 Better BibTeX 自动导出)、references/zotero_collection.json 和 references/fulltext_retrieval.json(阶段 2.7)的唯一写入者。切勿直接写入 refs.bib——即使是为了弥补 Zotero 连接缺失也不行,因为只有 Better BibTeX 导出才能确保文件与文库保持同步;若自动导出出现故障,请修复 Zotero 设置。直接手动编辑 refs.bib 会造成偏离,发现后立即还原。


阶段 1:解析 BibTeX

使用正则表达式解析用户指定的 .bib 文件,或 /search-lit 刚生成的 references/library.bib。逐条提取:citekey、doi、pmid、标题、作者(至少提取名和姓)、期刊、年份,以及存在时的卷号、期号和页码。记录解析失败的条目并跳过。后续阶段中的所有书目信息均来自这些条目或 API 响应。

解析出的 citekey 还不是文库键:/search-lit 的候选键(Kim_2024_Validation)只是临时键,阶段 2 添加条目时,Better BibTeX 才会生成真正的键。切勿自行拼造键;步骤 3.2 的 §Citekey provenance 说明了笔记键的来源。


阶段 2:Zotero 同步

若 Zotero MCP 未连接,则跳过此阶段(仍需写入步骤 2.3 的文件,并设置 status: "skipped"),然后继续阶段 3。

步骤 2.1:确定项目集合

根据当前工作目录或用户明确指定的覆盖值识别项目。若已记录集合键,则重复使用;否则检查 Zotero 中是否已有该项目的集合,若没有,则使用 zotero_create_collection 创建集合。记录集合键;若创建了新集合,则向用户报告该键。

步骤 2.2:去重并添加

对每个条目:

  1. 使用 zotero_search_items 按 DOI 或标题搜索——若条目已存在,则跳过。 先搜索再操作是防止重复的关键;zotero_add_by_doi 本身不会去重(它会从 CrossRef 获取信息并创建条目),因此切勿跳过搜索。
  2. 否则,调用 zotero_add_by_doi(有 DOI 时)或 zotero_add_by_url(只有 PMID 时,使用 PubMed URL)。既没有 DOI 也没有 PMID 的条目不会被添加:将其计为失败,并请用户手动添加。
    • zotero_add_by_doi 接受 attach_mode 参数,用于控制添加时尝试附加 OA 子级 PDF的行为(已安装的服务会将 linked_url 视为“为 PDF URL 创建书签”;其他值则会下载/导入)。具体接受值因服务版本而异,请对照已连接的服务进行确认。不要使用 zotero_add_from_file 为此处添加的条目附加 PDF:它没有父条目参数,会创建重复的父条目。
  3. 使用 zotero_manage_collections 将条目放入项目集合。
步骤 2.3:结果报告
Zotero Sync:
  Added:     8 papers (new)
  Skipped:   3 papers (already in library)
  Failed:    1 paper (no DOI/PMID)
  Collection: RFA-Meta (TZQEP4NH)

始终在项目工作区中写入 references/zotero_collection.json:

{
  "schema_version": 1,
  "status": "synced",
  "collection": "RFA-Meta",
  "collection_key": "TZQEP4NH",
  "added": 8,
  "skipped": 3,
  "failed": 1
}

如果 Zotero 不可用,则写入同一文件,并将 status 设为 "skipped",同时提供人类可读的 reason。


阶段 2.5:刷新 refs.bib 快照

Better BibTeX 的“保持更新”自动导出通常会在 Zotero 更改后的几秒内刷新 manuscript/_src/refs.bib。此阶段会验证快照是否确实已更新,以免后续技能使用过期内容。

步骤 2.5.1:解析路径

读取 SSOT.yaml → truth.refs_bib。默认值:manuscript/_src/refs.bib。如果不存在(旧版项目),则回退到 manuscript/_src/refs.bib,并发出 WARN,建议迁移 SSOT。

步骤 2.5.1b:前置条件断言(提前退出,不要轮询)

在进入步骤 2.5.2 的 10 秒轮询循环之前,验证两个前置条件。如果任一条件失败,则中止阶段 2.5 并提供设置说明,而不是等待永远无法解决的超时。

  1. **Better BibTeX 正在响应。**探测正在运行的插件,而不是磁盘上的文件:

    curl -s -m 5 -o /dev/null -w "%{http_code}" \
      http://127.0.0.1:23119/better-bibtex/json-rpc    # expect 200
    

    如果返回非 200,说明 Zotero 已关闭,或 BBT 尚未完成启动。等 Zotero 窗口出现后重试一次;BBT 会在应用启动几秒后注册其端点。

    ⚠️ **不要将 ~/Zotero/better-bibtex/read-only.json 作为判断条件。**当前版本的 BBT 会在自己的存储中保留自动导出注册信息,因此在正常安装中该文件通常为 [];将其视为“未配置”会导致此阶段在正常运行的设置中被跳过,进而使过期的 refs.bib 和虚构的 citekey 被带入稿件。

    失败时打印:

    阶段 2.5 已跳过:Better BibTeX 未在 127.0.0.1:23119 上响应(HTTP <code>)。打开 Zotero,等待其完成加载,然后重新运行 /lit-sync。

  2. **目标 refs.bib 存在。**步骤 2.5.1 中解析出的 truth.refs_bib 路径必须在磁盘上存在(即使为空也可以——BBT 会覆盖它)。失败时打印:

    阶段 2.5 已跳过:未找到目标快照 <path>。将 BBT 自动导出配置为使用“更改时”导出到 SSOT 路径,然后重新运行。

在任一提前退出情况下,在步骤 2.5.3 的 JSON 中将 refs_bib_refreshed: false 和 reason: "precondition:<which>" 设为相应值,告知用户,并将控制权返回给调用方。后续没有任何步骤会强制检查此标志,因此,告知用户是防止过期的 refs.bib 进入稿件的关键。

步骤 2.5.2:验证刷新

阶段 2 添加条目后:

  1. 在 Zotero 写入之前,捕获 stat -f "%m" manuscript/_src/refs.bib 的结果。
  2. 最多等待 10 秒(Better BibTeX 防抖时间)。轮询 mtime。
  3. 如果 10 秒后 mtime 仍未变化:
    • 提示用户检查 Zotero 是否正在运行,以及 BBT 导出是否设置为“保持更新”。
    • 如果 BBT 自动导出路径有误,打印预期路径(<project>/manuscript/_src/refs.bib)。
    • 作为最后手段,提供手动导出方式:File → Export Library → Better BibTeX → target path。
  4. mtime 更新后,搜索新添加的 citekey。所有 citekey 都必须存在;如果缺少任何一个,则报告失败(不要伪造条目)。
步骤 2.5.3:记录到 zotero_collection.json

追加到步骤 2.3 写入的 JSON 中:

{
  "refs_bib_path": "manuscript/_src/refs.bib",
  "refs_bib_mtime": "2026-04-24T14:32:11Z",
  "refs_bib_refreshed": true,
  "citekeys_verified": ["smithDeepLearningRadiology2024", "..."]
}

如果刷新失败,将 refs_bib_refreshed: false 设为相应值,并填写 reason。


阶段 2.7:全文检索(选择加入,仅限所有者)

仅在用户要求获取全文时运行(例如“下载 PDF”“获取全文”,或用户提供了明确要求获取全文的工作清单)。默认情况下,/lit-sync 仅处理元数据且尽量减少网络访问——不要自动运行此阶段。此阶段在条目加入 Zotero(阶段 2)且快照已验证(阶段 2.5)之后、Obsidian 笔记(阶段 3)之前运行。

提供以下两种途径,并在一份报告中汇总结果。只能通过这两种途径获取全文: 不要自动化已认证的浏览器会话,不要绕过付费墙或访问控制,也不要在此技能中硬编码机构代理、凭据或主机。对于这两种途径都无法获取的内容,转向机构访问、馆际互借或联系作者。

路线 A — 磁盘上的 OA PDF(供下游技能使用)

委托 /fulltext-retrieval 引擎(不要重新实现 OA 检索流程,也不要导入其代码;按路径调用):

ENGINE="${CLAUDE_SKILL_DIR}/../fulltext-retrieval/fetch_oa.py"
python3 "$ENGINE" <worklist> -o pdfs/ -e <contact-email> --report pdfs/retrieval_report.json

<worklist> 是 DOI/PMID(/标题)列表——Phase-1 .bib 中的 DOI、Standalone Modes 中提供的工作列表,或项目文献集的 DOI。输出:供 /meta-analysis 和 pdf_to_md.py 使用的 pdfs/*.pdf,以及 pdfs/retrieval_report.json(schema 2:检索 status/source、source_identity 和 file_sha256)。请区分文件是否存在与其身份是否已评估。

路线 B — 文献库中的 PDF(Zotero 原生方式,检索率更高,支持代理)

输出 ${CLAUDE_SKILL_DIR}/../fulltext-retrieval/references/find_available_pdf.js,供用户在选中项目文献集后粘贴到 Zotero(工具 → 开发者 → 运行 JavaScript)。此脚本会触发 Zotero 自带的 addAvailablePDF/addAvailablePDFs,复用用户自己的 OpenURL 解析器 / 机构代理——因此通常比仅检索 OA 的方式获取更多 PDF,同时此技能不会接触任何凭据或机构标识符。无需编写代码的等效操作是右键 →“查找可用 PDF”。记录打印出的 JSON 中 {attached, missing} 摘要。

报告

将路线 A 的 pdfs/retrieval_report.json(以及用户报告的路线 B 摘要)合并到 references/fulltext_retrieval.json,并在 references/zotero_collection.json 中添加一个简短的 fulltext 计数块。写入前请阅读 ${CLAUDE_SKILL_DIR}/references/fulltext_report.md——其中包含 schema 和身份依据规则(检索到的文件不等于已验证的论文;consistent 状态仅供参考;未评估的条目应列入 identity_review_needed)。


第 3 阶段:Obsidian 文献笔记

创建笔记前,先检测库中已有的目录结构。如果库已采用某种文件夹结构(包括韩语结构),请遵循现有结构——切勿擅自重命名用户的文件夹。对于新建或结构不明确的库,默认使用英文文件夹 Literature/ 和 Concepts/,以及英文模板(步骤 3.2、步骤 4.3)。对于韩语结构的库,或偏好韩语笔记的用户,请使用韩语文件夹布局和韩语标题模板,路径为 ${CLAUDE_SKILL_DIR}/references/locale/ko/note_templates.md(以及库自身的中心笔记名称)。

步骤 3.1:检查现有文献笔记
# 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
步骤 3.2:创建文献笔记

为每个 .bib 条目创建 Literature/{citekey}.md(或使用库中现有的文献文件夹)。 如果文件已存在,则跳过——切勿覆盖,因为用户可能已添加高亮或个人笔记。

引文键溯源——笔记文件名代表对文献库的断言

笔记的文件名和 citekey: 字段表明 Zotero 中有一条使用该键的条目。手稿中的 [@key]、笔记之间的 [[key]],以及 Zotero Integration 插件的 {{citekey}}.md 都依赖这个键,而一个无法解析到任何条目的键看起来仍然正确。因此,键只能读取,不能编造:

  1. 从 Better BibTeX 导出的 refs.bib 条目中获取,或通过 Better BibTeX 查询(json-rpc 上的 item.search——参见 ${CLAUDE_SKILL_DIR}/references/bbt_lookup.md)。
  2. 如果 Zotero 中没有这篇论文,先添加(zotero_add_by_doi),再由 BBT 生成键。第二阶段负责这一步是有原因的:在文献库条目之前写好笔记,就无法确定键是否正确。
  3. 如果无法添加(没有 DOI、处于离线状态),将笔记的 citekey: "" 留空,并添加标签 _needs-citekey。空字段可以补全;编造的键则不行,因为下游无法区分它和真实键。

完成前请验证:

python3 "${CLAUDE_SKILL_DIR}/scripts/check_citekey_provenance.py" --vault "$VAULT" --bib "$REFS_BIB"

INVENTED 表示笔记中的键不存在,但其 DOI 能解析到真实键;请在此处修正。 MISMATCH 表示键是真实的,但笔记的 DOI 是文献库中属于另一键的 DOI,因此引用了另一篇论文;请将文件重命名为建议的键(已知限制:如果笔记 DOI 的拼写与文献库不同,例如带引号或花括号,则不会触发此提示)。UNPARSED 表示文献笔记的 frontmatter 没有闭合,因此未读取其键。使用 --strict 时,这三种情况都会以状态码 1 退出;如果扫描未找到任何文献笔记,则以状态码 2 退出。始终按 DOI(而非键)将笔记与论文匹配(AMBIGUOUS、UNUSABLE、NO_IDENTIFIER:参见脚本的 --help),并在导入任何报告为缺失的内容前,搜索完整文献库(--live)。

模板
---
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]]
-
-

规则:

  • notetype: literature——与 Zotero Integration 模板兼容。
  • _unread 标签——用户之后在 Zotero 中阅读 PDF 并添加高亮后,将其改为 _read。
  • 将 ## Key points 和 ## My thoughts 留空——由用户自行填写。
  • ## Related notes 包含 2 个中心笔记链接和 2 个空位(留待之后链接概念笔记)。
  • 如果有 PMID,请添加 PubMed 链接。
步骤 3.3:结果报告
Obsidian Literature Notes:
  Created:   8 notes (new)
  Skipped:   3 notes (already exist)
  Location:  Literature/
  Total in vault: 12 literature notes

阶段 4:概念提取(条件执行)

触发条件

仅当知识库中有 ≥10 篇文献笔记时运行此阶段。如果不足,请打印类似“N 篇文献笔记——概念提取需达到 ≥10 篇”的状态消息,然后停止。

步骤 4.1:跨主题概念扫描

读取 Literature/*.md 下的所有文件(或知识库现有的文献文件夹),从每篇论文的标题、期刊和标签中提取关键词,并从 .bib 条目标题中提取主要概念,然后识别至少在 3 篇文献笔记中共同出现的概念。

步骤 4.2:筛选(5 条排除规则)

从概念候选项中排除:模型名称(GPT-4、Claude 等)、数据集名称(MedQA、ImageNet 等)、期刊名称、机构名称,以及泛化的技术名称(过于宽泛)。其余内容成为概念笔记候选项。

步骤 4.3:起草概念笔记

根据 ${CLAUDE_SKILL_DIR}/references/concept_note_template.md 中的模板创建 Concepts/{concept name}.md(或使用知识库现有的概念笔记文件夹;采用韩语结构的知识库请使用 references/locale/ko/note_templates.md 中的概念模板)。

关键规则:

  • 绝不自动填写 ## Definition——保留 > TODO 标记;只有用户用自己的话写下定义后,第二层笔记才有意义。
  • status 始终以 🌱Seedling 开始。
  • ## Related notes 下至少包含 4 个维基链接(遵循知识库惯例)。
步骤 4.4:向用户提出建议
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)

自动起草,但只有在用户确认后才创建。


独立模式

此技能无需新的 .bib 文件即可运行。

仅提取概念

收到明确的概念提取请求时,扫描现有的 Literature/*.md(或知识库中现有的文献文件夹),并且只运行 Phase 4。

整理参考文献

收到“整理此项目的参考文献”请求时,在工作区内查找 .bib 文件并运行 Phase 1–3。

仅同步 Zotero

收到“同步 Zotero”请求时,将 Zotero 文献集与 .bib 文件进行比对,并补充缺失的内容。

导入 PMID 列表(无 .bib)

用户提供 PMID 列表时(例如来自 HANDOFF 或同事),先通过 PubMed esummary 将 PMID 解析为 DOI,然后使用这些 DOI 进入 Phase 2:

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)"'

对于已在文献库中的条目(通过 Step 2.2 搜索发现),使用 zotero_manage_collections 将其添加到项目文献集中,不要重新添加——通过 URL/PubMed-URL 重新添加会绕过搜索去重,造成重复。将 added 和 existing 条目都记录到 references/zotero_collection.json 中。如果 PMID 在 PubMed 中没有 DOI(较早发表或未被索引的论文),则使用 PubMed URL 调用 zotero_add_by_url,并将条目标记为 no_doi: true。

导入工作列表(DOI/PMID/标题;无 .bib)

用户提供工作列表文件时(包含 DOI 列以及可选的 PMID/Title 的 .tsv/.csv/.md 表格,或每行一个 DOI 的纯文本列表——例如 SR 纳入集合),直接从该文件进入 Phase 2:先将仅包含 PMID 的行解析为 DOI(使用上文的 esummary),然后运行先搜索去重再添加的循环。同一工作列表文件也可用于 Phase 2.7 Route A(fetch_oa.py 可原生读取 .tsv/.csv/.md/纯文本文件),因此无需重新格式化。

评论

支持 Markdown