add-to-library
Skill4 starsChanged 5 months agoMITView on GitHub
Add ONE document (a PDF) directly to the shared master library — outside the project ingest flow. Auto-discovers the bibliographic metadata, VERIFIES it against Crossref/OpenAlex (never guessing), derives keywords cheaply from the PDF's docinfo + first-page abstract (no full read), computes the canonical bibkey, places the PDF at <library>/pdf/<bibkey>.pdf and appends the entry to <library>/references.bib. Stops and asks when metadata cannot be verified. Triggers on "add this PDF to the library", "add to the master bibliography", "put this straight in the library", "füge das Dokument der Bibliothek hinzu", "nimm das PDF in die Bibliothek auf". NOT ingest-source (which reads a source into a project's wiki), NOT acquire-sources (which downloads a review's worklist).
In this repository16 other files it gives its agents.
- acquire-sourcesSkill
- brainstorming-researchSkill
- drafting-manuscriptSkill
- drift-reportSkill
- executing-research-planSkill
- finishing-a-research-projectSkill
- grant-finderSkill
- ingest-sourceSkill
- literature-reviewSkill
- requesting-peer-reviewSkill
- scaffold-research-projectSkill
- semantic-wiki-reviewSkill
- using-research-powersSkill
- wiki-graphSkill
- wiki-lintSkill
- writing-research-planSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 35
Use it when a colleague sends a PDF, or you download one, and you want it in the collection now without ingesting it.
- Very specific: line 53
If a condition is unmet: name it, ask for a one-line reason, write it to knowledge/_meta/gate-overrides.log, and close.
- Distinctive: line 60
Create a task-list item for each:
- Very specific: line 69
⛔ Stop-and-ask: if has_text_layer is false and no DOI was found: a scan with no verifiable identifier cannot be added cheaply — OCR it …
- Very specific: line 81
Without a DOI: search search_crossref / search_openalex by the title + author from docinfo, and require a confident single match.
- Distinctive: line 102
if the work is already in the library (matched by DOI), unions your keywords into the existing entry instead of duplicating the key.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Boundary: where this sits
add-to-library is the direct-to-library route, deliberately outside the project pipeline:
ingest-sourcereads an acquired source into a project's wiki (source page + entities + a project-local BibTeX entry) with a full read. It is project-scoped.acquire-sourcesdownloads the PDFs aliterature-reviewguide lists.add-to-librarytakes one PDF you already have and adds it to the shared master so it surfaces inbib-search.pyfor every future project — no wiki content, no full read. Later, if a project actually cites it,ingest-sourcereads it as usual; the master entry is already there.
Use it when a colleague sends a PDF, or you download one, and you want it in the collection now without ingesting it.
Add a document directly to the master library
Announce at start: "Using add-to-library to add <pdf> to the shared master library — I'll verify the metadata before writing anything."
The deterministic mechanics (compute the bibkey, place the PDF, append the entry) live in scripts/add-to-library.py. Your job is the judgement the script cannot do: verify the metadata against a real record, and curate the keywords. Never let the script mint a key from data you have not verified.
<SOFT-GATE>
Before closing, check:
(1) the metadata was **verified** against Crossref/OpenAlex (or a manual Crossref lookup) — not taken from the PDF's docinfo alone,
(2) the PDF is placed at `<library>/pdf/<bibkey>.pdf` and the bibkey follows `autor-jahr-kurztitel`,
(3) the entry is in `<library>/references.bib` with that exact key,
(4) `keywords` carries 3–8 curated terms (canonical name + aliases),
(5) `bib-search.py index` was re-run and the fused self-test (step 7: canonical name `--q` alias `--key <bibkey>`) surfaced the entry, keyword hit first,
(6) `knowledge/_meta/log.md` has an `add-to-library` line,
(7) the PDF is not egregiously oversized — `optimize-pdf.py check <pdf>` reports it unflagged, or it was optimised (`optimize-pdf.py optimize --replace`) and re-indexed. Publishers ship figures at absurd resolutions; the whole library syncs to everyone (and every LFS version is kept forever), so a bloated file is a standing cost. The shrink is reading-lossless and self-verifying (page count + text layer); the pristine file stays re-fetchable by DOI.If a condition is unmet: name it, ask for a one-line reason, write it to knowledge/_meta/gate-overrides.log, and close. Exception — never soft-gate past (1): if the metadata cannot be verified, do not write; stop and ask.
(1) is enforced by the script, not by your resolve. add-to-library.py commit refuses without --doi unless you pass --unverified-reason "…", and that reason is written into the entry's note field as UNVERIFIED: … — visible to everyone who ever reads that record, in every project. You cannot write an unverified entry silently.
</SOFT-GATE>
Checklist
Create a task-list item for each:
- Resolve the library.
python scripts/add-to-library.py inspect <pdf>resolves it viascripts/library.py. If it reports the library is not configured, that is the machine-local.research-libraryproblem — surface the message, do not proceed. - Inspect the PDF (cheap).
Returns docinfo (python scripts/add-to-library.py inspect <pdf> --jsonTitle/Author/Keywords/Subject), the first-page text (where the abstract lives), any DOI printed on the front pages, andhas_text_layer. ⛔ Stop-and-ask ifhas_text_layeris false and no DOI was found: a scan with no verifiable identifier cannot be added cheaply — OCR it first, or add it viaingest-source. - Discover and VERIFY the metadata (this is the load-bearing step). Prefer the
dao-paper-search-mcp(seedocs/recommended-mcps.md); fall back to a manual Crossref lookup if it is absent.- With a DOI (from step 2 or the
doiinput):
or manually:dao-paper-search-mcp.search_crossref(query=<doi>) → exactly one hit; check that its identifiers.doi IS <doi> (a DOI query is a direct lookup from dao-paper-search-mcp 1.2.0; older versions search and can return a different work) → hit.inline_citation.authoritative_bibliography_line (verbatim)curl -s https://api.crossref.org/works/<doi>. - Without a DOI: search
search_crossref/search_openalexby the title + author from docinfo, and require a confident single match. - Cross-check the record against the PDF — title, first author, year must match the docinfo and first page. A DOI can resolve to the wrong record; a title search can return a namesake.
- ⛔ Stop-and-ask on any mismatch, low confidence, or no confident match after the manual fallback. Never guess bibliographic data (the project's hard rule). Present the candidate(s) and ask.
- Choose the entry type:
article,incollection(book chapter — use--booktitle),book,inproceedings, … Chapter-vs-whole-book is a judgement; if the record is ambiguous, ask.
- With a DOI (from step 2 or the
Derive keywords cheaply. From the docinfo
Keywords/Subjectfields (often empty or boilerplate — treat as hints) plus the first-page abstract text from step 2, curate 3–8 terms: the canonical name plus known synonyms and aliases across spellings and disciplines (the same discipline asdrafting-manuscript's "Searching for a concept, not a string"). No full read. Thin keywords are acceptable — they accrete later atingest-sourcetime. Never let weak keywords justify skipping step 3.Where the value sits: the abstract's own vocabulary is already in the full-text index — a later rank-fused alias search (
bib-search.py --q, the ranking arm) will match those words with or without you. What only this step can add is the recall arm: terms the abstract does not use — the other disciplines' names, the spelling the field you come from doesn't — because fusion re-ranks what some alias literally matches and adds zero recall. One alias the abstract omits is worth more than three it repeats.- Choose the kurztitel. One to three significant title words — a judgement, not the first three words of the title ("The Low Chronology and the Problem of…" →
low chronology, notlow-chronology-problem).add-to-library.pyproposes one vialibrary.propose_shorttitle; refine it. - Commit — dry-run first, then write.
Review the proposed bibkey and, if it reports a disambiguation letter (python scripts/add-to-library.py commit --pdf <pdf> \ --author "<first author, as in BibTeX>" --year <YYYY> --title "<full title>" \ --etype <type> --kurztitel "<chosen>" \ [--journal … --booktitle … --pages … --volume … --number … --doi … --url …] \ --keywords "term one; term two; term three"mazar-2011b-iron-age), confirm the new document is genuinely a different work from the existingmazar-2011-…before writing. Then re-run with--write. The commit step:- computes the bibkey from your verified fields (refuses, rather than inventing, if a slot is empty);
- if the work is already in the library (matched by DOI), unions your keywords into the existing entry instead of duplicating the key;
- places the PDF (refuses to overwrite a different file at the target path without
--force); - appends the entry to
<library>/references.bib.
- Re-index so the source is immediately searchable:
Then close the loop with the search a later drafter would run — canonical name plus your aliases, rank-fused:python scripts/bib-search.py index
The keyword hit should print first (ahead of any page hit,python scripts/bib-search.py '"<canonical name>"' --q '<alias>' --key <bibkey>page: null). If nothing comes back, the entry is invisible to exactly the query it exists for — fix the keywords now, while the abstract is still in front of you. - Log. Append one line to
knowledge/_meta/log.md:- YYYY-MM-DD · add-to-library · <bibkey> · verified via <crossref|openalex> - Soft-gate (above) and report: the bibkey, where the PDF landed, the keywords written.
BibTeX & keyword convention
Same as ingest-source's BibTeX Entry Convention. keywords is semicolon-separated, 3–8 terms, canonical name plus synonyms/aliases — it is what bib-search.py matches when a source describes a method in prose without naming it; the rank-fused --q alias search changes ranking only, never recall, so this field is the sole non-text-derived path into a search result (see ingest-source's keyword step for the full division of labour). The bibkey is autor-jahr-kurztitel, all lowercase ASCII (umlauts → ae/oe/ue, ß → ss; Turkish ı → i, Polish ł → l; particles/spaces removed), letter suffix after the year for a genuine clash. bibkey == PDF filename stem is the cross-project join key — the script enforces the shape.
Red flags
| Tempting shortcut | Why it's wrong |
| "The docinfo Title is the citation — use it." | Docinfo is frequently wrong, stale, or a template placeholder. Verify against Crossref/OpenAlex (step 3). |
| "curl returned 200, so the DOI is right." | A 200 only means the DOI resolves. Check the returned record matches the PDF's title/author/year. |
| "No DOI, but I'll infer the year from the filename." | That is guessing bibliographic data. Stop and ask (step 3). |
| "The keywords look thin, so I'll skip verification and just file it." | Keywords are never a reason to skip step 3. Verification gates the write; keywords do not. |
"The abstract names the method — --q fusion will find this entry, keywords are redundant." | The abstract's words are in the index either way; fusion only re-ranks what an alias literally matches. Keywords earn their keep as the aliases the abstract does not use — and without a full read, nobody has recorded those yet. |
| "The base bibkey already exists — I'll just overwrite it." | If it's the same work, keywords union in automatically. If it's different, take the disambiguation letter. Never overwrite a live key. |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

