ZPM — Zotero Project Manager
ZPM exports Zotero collections into clean, ordinary research folders. Zotero remains the source of truth: the project reads Zotero data and copies attachments outward without modifying the library, database, or original files.
Use the self-contained Zotero 9 plugin for native note links and interactive exports, or the Python CLI for batch operations, automation, verification, and safe pruning.
Choose an interface
| Capability | Zotero plugin | Python CLI |
|---|---|---|
| PDFs and optional non-PDF attachments | Yes | Yes |
| Metadata, annotations, notes, and cached annotation images | Yes | Yes |
| Recursive hierarchy, filename presets, and three annotation layouts | Yes | Yes |
| Incremental SHA-256 synchronization and legacy workspace migration | Yes | Yes |
| Multiple root collections in one operation | One at a time | Yes |
| Dry-run, status, pruning, full verification, and workspace adoption | No | Yes |
| Diagnostics, named projects, saved configuration, and automation | No | Yes |
| Runs without Python or direct SQLite access | Yes | No |
Both interfaces produce the same managed workspace format. The plugin stays focused and conservative; the CLI provides explicit administrative controls.
Zotero 9 plugin
Exports run inside Zotero and do not require Python, Homebrew, pipx, or an executable path. Exports create and update local workspaces without launching other apps, running AppleScript, or uploading files.
Download the XPI from the latest GitHub release, then open Zotero → Tools → Plugins → gear menu → Install Plugin From File…. Existing installations can be upgraded in place.
Menu names below use ZPM starting with 1.3.1. Versions through 1.3.0 display Zotero Project Manager instead.
Right-click a collection to use:
ZPM
Copy Link → Org / Markdown / Zotero URI
Export → Collection / Collection + Annotations
Settings…
Settings control:
- the default export folder;
- whether non-PDF attachments such as
README.md, text, images, and data are included; - attachment filename ordering;
separate,sidecar, orbundleannotation layout.
The first export asks for a destination if no valid default exists. Zotero can install future releases automatically when Update Add-ons Automatically is enabled in the Plugins gear menu; Check for Updates provides a manual check.
Native links (1.2.0)
Right-click a collection, paper record, or PDF attachment and choose ZPM → Copy Link. Copy Org links for Emacs, Markdown links for Obsidian, or plain Zotero URIs. The PDF reader also offers page and annotation links. Explicit Copy Multiple Links commands copy selected paper records and PDFs, one per line. Only My Library is supported; unsupported entries disable copying and batches never skip invalid entries. These commands use Zotero’s native clipboard and need neither Actions & Tags nor zotxt, bibliography styles, or an export folder.
See installation, Emacs setup, examples, and testing. Version 1.2.0 includes these native link commands.
Emacs collection picker (1.3.0)
From an Org note, run M-x zpm-insert-paper-link or M-x zpm-open-paper to browse
papers in the selected Zotero collection by author, year, and title. Remember a
collection per note with zpm-remember-collection. This local, read-only bridge
requires plugin 1.3.0 or newer and Zotero's local API permission. Existing native
links and exports remain unchanged. See the beginner picker guide.
Version 1.3.0 is the stable release of the picker.
One workspace, your choice of app
Export a collection once and use its standard folder wherever you need it. For example, selecting My-AI → Agentic_AI exports to Agentic_AI/ and preserves its descendants inside that workspace. Re-exporting updates the same workspace. Choose Export Collection + Annotations to include annotations and child notes.
Open the target app yourself and select the exported files you want to use. zpm
has no app-specific Send commands, stored notebook-link UI, automatic app indexing,
uploading, or separate Notebook export format. The hidden .zpm/ directory is
bookkeeping, not material to upload or import. You choose the files and compatible
types in the destination app.
Stable 1.1.0 keeps the public 1.0.0 feature set and fixes Settings/Choose while
adding concurrent-export protection. The experimental Gemini Notebook/DT4 preview
integrations are not included; standard exports and naming/layout settings remain.
Existing export folders (including old - NotebookLM folders), Google notebooks,
DT4 records, and Zotero originals are not deleted or migrated. Old notebook-link
preferences are left unused; the simplified plugin does not read or clear them.
The earlier implementation remains in Git history.
The CLI no longer accepts --to, --notebook-url, --devonthink-group,
--prepare-only, or --profile. Existing standard named projects still work.
A saved project with export_profile = "notebooklm" is rejected with a migration
message: review its output directory and layout, then remove that setting in its
TOML config only if you want standard export. zpm never silently switches the project
or rewrites the config on load.
See the Agentic_AI testing guide for installation and checks.
Python CLI
The CLI requires Python 3.11 or newer. Install it on macOS with Homebrew:
brew install sbilmis/tap/zpm
Or install the PyPI package with pipx:
brew install pipx
pipx install zotero-project-manager
Upgrade with brew upgrade zpm or pipx upgrade zotero-project-manager.
Quick start
List collections and export one recursively:
zpm list
zpm export "My-AI" --output ~/ResearchProjects
Include annotations and child notes:
zpm export "My-AI" --output ~/ResearchProjects --annotations
Preview changes, then safely prune files removed from Zotero:
zpm status "My-AI" --output ~/ResearchProjects --prune
zpm export "My-AI" --output ~/ResearchProjects --prune
Only manifest-owned files whose SHA-256 still matches are deleted.
Reusable projects and diagnostics
Save defaults or a named multi-collection project:
zpm config set --zotero-dir ~/Zotero --output ~/ResearchProjects
zpm project add ai "My-AI" "Claude" --annotations
zpm sync ai
Run a read-only readiness audit:
zpm doctor --output ~/ResearchProjects
Use zpm --help and zpm export --help for every command and option.
Workspace format
A typical export looks like this:
My-AI/
Curie - 2024 - Paper title.pdf
README.md
Books/
Author - 2023 - Book.pdf
Annotations/
Curie - 2024 - Paper title.md
.zpm/
manifest.json
metadata.json
INDEX.md
export-summary.md
Generated control data lives under .zpm/, leaving ordinary project names such as
README.md, INDEX.md, and metadata.json available for Zotero attachments.
The manifest supports incremental exports:
- unchanged files are left alone;
- new and changed attachments are copied;
- missing or removed attachments are recorded without silently deleting prior copies;
- identical files re-added under a new Zotero key are reconciled;
- root-level manifest v1–v4 workspaces migrate under
.zpm/after a successful export.
Annotations and layouts
Annotation export includes highlights, comments, page labels, tags, child notes, and available cached image or ink previews. It is opt-in because research notes may contain private material.
Choose one of three layouts:
separatekeeps generated Markdown under a parallelAnnotations/hierarchy;sidecarplacespaper.annotations.mdbeside each PDF;bundlecreates one folder per paper containing the attachment and annotations.
The selected filename preset and layout are recorded in the manifest. Existing workspaces retain those settings to prevent surprising reorganizations.
Safety model
- Zotero files, attachments, and annotation caches are never modified.
- The CLI opens
zotero.sqliteread-only with SQLite query-only protection. - The plugin reads through Zotero's in-process APIs and does not open SQLite directly.
- Output paths inside the Zotero data directory are rejected.
- Unmanaged workspaces and conflicting user files are not adopted silently.
- Generated annotation and control files carry ownership markers.
- Full verification and pruning require explicit CLI options.
Keep independent backups of important research data.
Development
git clone https://github.com/sbilmis/zotero-project-manager.git
cd zotero-project-manager
python -m venv .venv
.venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest
node --test zotero-plugin/tests/*.test.cjs
Build the release XPI and verify its update-feed entry with:
.venv/bin/python scripts/build_zotero_plugin.py
Install dist/zpm-zotero-1.2.0.xpi using Zotero's Tools → Plugins → gear →
Install Plugin From File…, or use the XPI from the matching GitHub release.
The installed Homebrew/pipx release does not change when this checkout changes;
use .venv/bin/python -m zotero_project_manager to run the CLI from this checkout.
For unpublished development versions only, pass --development to build without
a published feed entry. Release builds and CI verify the exact update-feed hash.
See CONTRIBUTING.md, PUBLISHING.md, the plugin guide, and CHANGELOG.md for focused development and release details.
ZPM is released under the MIT License.
For a paper inside a collection, ZPM → Copy Link in This Collection offers Org, Markdown, and Zotero URI formats that preserve that collection context. Ordinary item and PDF links remain unchanged.
Release files for zotero-project-manager 1.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| zotero_project_manager-1.3.1.tar.gz | 119.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zotero_project_manager-1.3.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 165.4 kB
Release files / zotero_project_manager-1.3.1.tar.gz
| Download URL | zotero_project_manager-1.3.1.tar.gz |
|---|---|
| Size | 119.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b1301fb6e5cf0afb2de7e341eb0c92460a845d5c60a4a7ed2e15d3ab9d991795
|
|
BLAKE2b-256 checksum How to use checksums |
2969ac841528d7f9ec57533e13e939c4ff0eb546cde649bb6576af01d32010e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency logRelease files / zotero_project_manager-1.3.1-py3-none-any.whl
| Download URL | zotero_project_manager-1.3.1-py3-none-any.whl |
|---|---|
| Size | 46.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9c87fdcd3665ab28bffb71fd269b88c5b9f6a1e7310d07aed15be43bc1baf0d3
|
|
BLAKE2b-256 checksum How to use checksums |
2facef1901ff5d0b790f0a7543f2520fb2fa604065ac2240e5e2aaedb45436fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.
Transparency log