Changelog¶
All notable changes to this project will be documented in this file.
Format follows Keep a Changelog, versioning follows Semantic Versioning.
[Unreleased]¶
[0.13.0] - 2026-08-17¶
Changed¶
- The
workspaceconcept is gone — it lasted one release as a synonym for "collection", and two names for one thing was one name too many. Scoped ranked retrieval now lives inzot search --rankedandzot ask --collection.
Added¶
zot search --ranked— relevance-ranked results (scores + snippets) from the index-free engine introduced in 0.12.0, with optional--collection COLLscope (name or key).zot collection remove ITEM COLL— remove an item from a collection without deleting it from the library, completing the soft-link membership loop withcollection move. MCP: newcollection_removetool; thesearchtool gained arankedparameter (39 tools total).
Removed¶
zot workspacecommand group and the--workspaceflag onzot ask(renamed--collection). MCP: theworkspace_querytool (usesearchwithranked=True); theasktool'sworkspaceparameter was renamedcollection.
[0.12.0] - 2026-08-17¶
Changed¶
- A workspace is now just a Zotero collection —
zot workspace queryandzot askresolve--workspaceto a collection (name or key; omit it to search the whole library). Manage membership in the Zotero app or viazot collection *; zot stores nothing locally. - Retrieval is now index-free — a two-stage ranker replaces the pre-built
RAG index: stage 1 scores items with idf-weighted term coverage from
Zotero's own full-text index tables, fused via reciprocal rank fusion with
metadata matching (title/abstract/creators/tags/notes); stage 2 (
ask) extracts PDF passages on the fly around query terms (pdfium, cached). Results are always fresh — there is no index to build or rebuild. zot ask's--workspaceflag is now optional.
Removed¶
zot workspacecuration and indexing subcommands (new,delete,add,remove,list,show,export,import,search,index) — superseded by Zotero collections. Local workspaces under~/.config/zot/workspaces/(JSON and*.idx.sqlite) are no longer read and can be deleted; recreate the same sets as collections in Zotero.- Embedding support — the
[embedding]config section, theZOT_EMBEDDING_URL/ZOT_EMBEDDING_KEY/ZOT_EMBEDDING_MODEL/ZOT_EMBEDDING_PROVIDERenvironment variables, and the aliyun/jina providers. The unusedopenaidependency was dropped from the package. - MCP tools:
workspace_new,workspace_delete,workspace_add,workspace_remove,workspace_list,workspace_show,workspace_export,workspace_import,workspace_search,workspace_index(39 tools remain).workspace_querywas reworked (name→ optionalworkspace, returns ranked items with snippets) and a newasktool mirrors the CLI.
[0.11.0] - 2026-08-17¶
Changed¶
- PyPI distribution renamed
zotero-cli-cc→zotero-cli-ai. Install or upgrade withuv tool install zotero-cli-ai(orpipx/pip). Thezotcommand, thezotero_cli_ccPython module, the Zotero bridge plugin ID, and all config/cache paths are unchanged.zotero-cli-ccon PyPI receives no further updates;zotero-cliis an unrelated older project. - Project branding unified as "zotero-cli — A Zotero CLI for Any AI Agent" (was positioned as Claude Code-only).
Fixed¶
zot config profile setwas a silent no-op on configs written byzotitself (single-quoted TOML values weren't matched);save_confignow emits properly escaped double-quoted TOML, andconfig.tomlis written0600with the config dir at0700(#89).update-statusandpdfbroke the JSON agent contract — prose mixed into stdout. All machine output now goes through the JSON envelope, prose to stderr (#89).- Library-scale reads could exceed
SQLITE_MAX_VARIABLE_NUMBER— the remainingIN (...)clauses are batched, with a Python-side sort fallback for very large sorted queries; database paths containing?,#or%no longer corrupt the SQLite URI (#89). zot openon Windows usesos.startfileinstead ofshell=True; MinerU split chunks go to a temporary directory instead of polluting Zotero storage (#89).
[0.10.0] - 2026-07-15¶
Added¶
- OpenAI-compatible embedding endpoints — new
provider = "openai"(ZOT_EMBEDDING_PROVIDER=openai) routes any OpenAI-compatible/v1/embeddingsendpoint (Aliyun Bailian workspace URLs, LiteLLM, Ollama, vLLM, ...) with configurable base URL, API key, and model. Thealiyunprovider now also honorsZOT_EMBEDDING_URL/[embedding] urlas a custom base URL — needed since Bailian moved to per-workspace endpoints — and falls back to the dashscope default when unset. Both base URLs and full.../embeddingsURLs are accepted (#84, #86).
Fixed¶
zot workspace querycrashed on large indices withsqlite3.OperationalError: too many SQL variables— BM25 term lookup bound every chunk id into a singleIN (...)clause, exceeding SQLite'sSQLITE_MAX_VARIABLE_NUMBER. Now queried in batches of 900 ids (#83, #85).
[0.9.0] - 2026-07-14¶
Added¶
zot attachment path KEY --all(-a) — list every PDF attachment of an item, one local path per line, instead of just the first. Built for items that now routinely ship an appendix or supplementary PDF alongside the main article. In JSON mode it returns{item_key, count, attachments: [...]}with each entry carryingattachment_key,path,filename,exists, andmime_type. Attachments with no local file are skipped;not_foundis returned only when the item has no PDF at all, or no PDF has been synced to local storage. Default (no flag) behaviour is unchanged — still prints the first PDF's path.- New reader API
ZoteroReader.get_pdf_attachments(key)returning all PDFs;get_pdf_attachment(first PDF) is now a thin wrapper over it. - Tokenized multi-word search —
zot searchsplits multi-word queries into independent word matches: each word may hit title, creator, tag, or full text separately, and results are the intersection across words (#76). - Group-library support for
zot attach --via-bridge— the bridge maps the Web-API group id to the desktop's internallibraryID; requires bridge plugin v0.4.0 (the CLI checks the plugin version and fails withbridge_missingrather than importing into the personal library) (#72). zot attachauto-detects the bridge — imports through the running Zotero desktop when reachable, otherwise falls back to the Web API; the cloud path now reports an honestdata.result(createdvsexists) (#71).--idempotency-keyonzot enrichandzot renameso agent retries are safe on those commands too (#79).
Changed¶
- Schema version bumped to
1.9.0(additive: new--alloption onattachment path).
Fixed¶
- Migrated remaining
print_errorcall sites toemit_errorso failures exit non-zero with typed codes;cite,export,stats,summarize, andtagnow emit proper JSON envelopes (#78). - MCP server no longer re-creates the writer connection per call (caching);
workspace-index errors are now logged instead of swallowed;
minerufallback reports the underlying error;save_configpreserves other profiles; removed an N+1 query inget_collection_items; workspace search is tokenized; duplicate detection scores all pairs (#77).
[0.8.0] - 2026-06-03¶
Added¶
zot orphans— find and clean attachments whose stored file is missing from localstorage/(Zotero's "the attached file could not be found").orphans listclassifies each asdead(no copy anywhere — safe to remove),recoverable(server still has it — fix by file-sync), orunknown;orphans cleandeletes the dead ones via the Web API (--dry-run/--yes/--idempotency-key;--include-recoverableto also drop server-held ones). Mirrored as the read-onlyfind_orphansMCP tool.zot attach --via-bridge— import a file through the running Zotero desktop (thezot-cli-bridgeplugin's newPOST /zot-cli/import-fileendpoint →Zotero.Attachments.importFromFile) so the binary lands in local storage immediately instead of cloud-only, cooperating with attachment movers like zotero-attanger. Bridge plugin bumped to v0.3.0; also exposed asattach(via_bridge=True)over MCP.- GROBID extractor (
pdf.extractor = "grobid"/ZOT_GROBID_URL): a references/structure tier backed by a running GROBID service (defaulthttp://localhost:8070). Addsextract_references()to the extractor interface and azot pdf KEY --referencesflag that returns the parsed reference list (title / authors / year / journal / DOI). Lighter than the vision-model extractors; intended for citation verification and metadata completion. GROBID is not bundled — the user runs the service. referencesMCP tool inzot mcp serve: exposes the GROBID-parsed reference list (title / authors / year / journal / DOI) over MCP, so an agent can verify citations without shelling out. Returnserror+hintwhen no GROBID service is reachable.pdfplumbertable extractor (zotero-cli-cc[pdfplumber]): pure-Python table extraction (no ML / GPU / network). Addsextract_tables()to the extractor interface, azot pdf KEY --tablesflag, and atablesMCP tool. All-empty tables (spurious grids pdfplumber detects on figure-heavy pages) are filtered out so only tables with real cell content are returned.pymupdf4llmbundled into the[pymupdf]extra: installing the extra now also enables higher-quality local Markdown output (no API / network — thePyMuPdfExtractoralready uses it when present).
Changed¶
- License: relicensed from CC-BY-NC-4.0 to a dual license —
AGPL-3.0-or-later for open-source use plus a separate commercial
license (see
LICENSE/LICENSE-COMMERCIAL). The base install ships no AGPL runtime code (defaultpdfiumextractor;pymupdfis opt-in). zot attachnow reports where the file landed viadata.stored("cloud"for the default Web-API upload,"local"for--via-bridge), and warns that a cloud upload only reaches localstorage/after a desktop file-sync.schema_version1.6.0 → 1.7.0.
[0.7.0] - 2026-05-29¶
Added¶
zot ask "QUESTION" --workspace NAMEretrieves a citation-keyed evidence pack from a workspace's RAG index (hybrid BM25 + embedding retrieval via reciprocal rank fusion) and returns it withanswer_instructions, so the calling agent can synthesize a grounded, cited answer. Unlikeworkspace query(which dumps ranked chunks), each evidence entry is tagged with its Zotero item key as thecite_keyand carries per-method scores. Following the same contract assummarize,zotprepares the context but calls no generative LLM itself — the agent is the model. Options:--evidence-k(default 12) and--mode auto|bm25|semantic|hybrid.- Schema version bumped to
1.6.0.
Changed¶
- Default PDF extractor is now
pdfium(pypdfium2, BSD/Apache-licensed) instead ofpymupdf. PyMuPDF is AGPL/Artifex-licensed, so it is no longer a core dependency — a plainpip install zotero-cli-ccnow ships no AGPL code, making it usable in commercial/closed-source products without an Artifex license. PyMuPDF moves to an optional extra:
Install the extra to enable the pymupdf extractor, which adds PDF
annotation/highlight extraction and higher-quality markdown. The default
pdfium extractor covers text and DOI extraction; it returns an empty list
for annotations. The MinerU fallback now falls back to pdfium. Select an
extractor explicitly with extractor = "..." in config or
ZOT_PDF_EXTRACTOR.
[0.6.0] - 2026-05-28¶
Added¶
zot enrich KEY...writes journal metrics (impact factor, JCR/CAS quartile, core-journal flags, …) into an item's Extra field. Deliberately source-neutral: values come from inline--set "Label=value"flags or a user-maintained--from-mapTOML table (matched by journal name) —zotships no journal data and calls no third-party API, so it stays independent of any external product. Metrics are written in a<!-- zot:metrics -->block, so re-running replaces only that block (idempotent) and preserves other Extra content. Supports--dry-run.zot rename KEY...renames an item's PDF attachment files from its metadata via the bridge plugin. The default template is{journal}_{year}_{title}(tokens{journal} {year} {title} {fulltitle} {shorttitle} {author};{title}prefers the Short Title field when set).{journal}is resolved from aJab/#tag or an item-type-aware abbreviation (arXiv →Pre, single-word venues kept whole soNaturestaysNature). Empty tokens are collapsed (noPre__x) and names are truncated to a filesystem-safe length. Non-PDF attachments (Excel/Word/snapshots) are filtered out by content type; supplementary PDFs are detected by filename and get an_SIsuffix so names never collide. Supports--dry-run,--main-only,--force,--template, and--attachment/--namefor explicit single-file renames. Requires thezot-cli-bridgeplugin v0.2.0+ (re-runzot bridge install).meta.schema_versionis bumped 1.4.0 → 1.5.0.
[0.5.0] - 2026-05-28¶
Added¶
zot find-pdf KEYtriggers Zotero desktop's "Find Full Text" over a local bridge plugin, so the CLI can fetch and attach PDFs that the Zotero Web API cannot reach (paywalled content behind the desktop's configured resolvers, authenticated sessions, and institutional proxies). Ships withzot bridge install/status/uninstallto package the bundledzot-cli-bridgeplugin into an.xpiand guide installation. Both commands are also exposed over MCP.meta.schema_versionis bumped 1.3.0 → 1.4.0 (#43).zot workspace index --skip-tagexcludes attachments carrying a given tag from the RAG index (defaultskip-index), so large or irrelevant PDFs can be kept out of the index. Also available on the MCPworkspace_indextool (#44, #46).
Fixed¶
- On Windows with a CJK (GBK/CP936) system locale,
zotcrashed withUnicodeEncodeErrorwhenever output contained characters outside the GBK range (e.g. emoji). stdout/stderr are now reconfigured to UTF-8 at startup on Windows when the encoding is not already UTF-8 (#48).
Changed¶
- The bundled Claude Code skill was split from a single
SKILL.mdinto a concise entry point plus on-demandreferences/files (commands, workspaces, workflows, windows-encoding), and now documents thefind-pdf/bridgecommands andworkspace index --skip-tag(#49).
[0.4.4] - 2026-05-14¶
Fixed¶
zot add --doicreated empty items because the Zotero Web API does not auto-resolve DOIs the way the desktop translator does. The CLI now fetches metadata from Crossref (title, creators, journal, volume/issue/ pages, date, ISSN, abstract, publisher, language) and merges it into the item template before posting, so created items are populated, not bare shells. Same fix applies to the MCPadd/add_from_pdfhandlers. Pass--no-resolveto opt out, setZOT_CROSSREF_MAILTOto join Crossref's polite pool.meta.schema_versionis bumped 1.1.0 → 1.2.0 for the additive envelope slot (data.resolved/data.resolve_warning) (#41, #42).
[0.4.3] - 2026-05-11¶
Fixed¶
- Update-available banner hard-coded
uv tool upgrade zotero-cli-cc, which is wrong for users who installed via pip / conda / pipx. The suggested command is now detected fromsys.executable(uv tool / pipx) with apip install -Ufallback that works for pip, conda, and system installs (#31).
[0.4.2] - 2026-05-10¶
Fixed¶
- Update-available nag fired indefinitely after upgrading because
__version__was hardcoded in__init__.pyand missed in the 0.4.0 / 0.4.1 bumps, so installed copies of 0.4.1 reported themselves as 0.3.0. Version is now sourced from package metadata (importlib.metadata), makingpyproject.tomlthe single source of truth (#30).
[0.4.1] - 2026-05-05¶
Embedding configuration cleanup. The provider-specific Aliyun key
(aliyun_api_key / ZOT_EMBEDDING_ALIYUN_KEY) and the implicit
provider="auto" mode were leaking the multi-provider routing
implementation into the user-facing config without a symmetrical
counterpart for Jina. Single-provider, single-key surface is cleaner.
Changed¶
[embedding] providerdefault is now"jina"(was"auto"). Setprovider = "aliyun"(orZOT_EMBEDDING_PROVIDER=aliyun) to use Aliyun DashScope.
Removed (Breaking, very rare)¶
[embedding] aliyun_api_keyconfig key — use the unified[embedding] api_keyinstead.ZOT_EMBEDDING_ALIYUN_KEYenv var — useZOT_EMBEDDING_KEYinstead.provider = "auto"mode — pick a provider explicitly.
If you upgraded to 0.4.0 within the past hour and were already using
the Aliyun-specific key, rename it to api_key /
ZOT_EMBEDDING_KEY and set provider = "aliyun".
[0.4.0] - 2026-05-05¶
PDF extraction overhaul, envelope routing for the rest of the --json surface,
typed exit codes wired up across all command error paths, and a CI repair pass.
schema_version bumps to 1.1.0.
Added¶
- MinerU PDF extractor alongside the existing pymupdf-based extractor, with
a new
BasePdfExtractorabstract class and automatic fallback when MinerU fails (zot pdf KEY --extractor mineru). Configure via[pdf] extractor,[pdf] mineru_token, orMINERU_TOKEN/ZOT_PDF_EXTRACTORenv vars. zot pdf --outline— list every heading in the document as a numbered outline so agents can navigate without dumping the full text.zot pdf --section N— extract just the content under the N-th heading from--outline. Useful for "show me the methods section" workflows.zot workspace index --extractor— choose the PDF extractor used during RAG indexing.- Embedding provider router with first-class support for Aliyun
(DashScope, OpenAI-compatible) and Jina endpoints. Routes via the new
[embedding] providerconfig key /ZOT_EMBEDDING_PROVIDERenv var. (0.4.1 simplified the surface — see below.) - Attachment resolver that handles
storage:paths,file://URLs, Zotero'sattachments:paths, Windows drive letters, and base-attachment prefs. PDFs in non-default storage directories now resolve correctly. progress_callbackplumbing through the PDF extraction path so MinerU batch operations and per-PDF extraction surface progress to the caller.
Changed¶
- Envelope routing extended to the remaining
--jsoncommands:zot pdf(incl.--outline/--section),zot workspace list,zot workspace query, andzot config cache listnow emit the standard{ok, data, meta}envelope.workspace querydatabecomes{mode, results}rather than the bare results list. schema_version1.0.0 → 1.1.0 to reflect the envelope-coverage extension and the typed-exit-code parity.docs/agent-interface.mdupdated.- Typed exit codes wired across all command error paths. Previously many
error paths called
print_error(...); return, printing the error message but silently exiting 0. They now useemit_error(...)with the appropriate typed code: not_found(4): item / PDF / workspace / collection / profile / index / section missing — affectscite,export,summarize,open,pdf,workspace delete/add/remove/show/export/import/search/index/query,config profile_set.validation_error(3): bad page range inpdf, missing required source flag inworkspace import, invalid workspace name.auth_missing(2): alltag/trash restore/collectionwrite commands when API credentials aren't configured.conflict(6):workspace newwhen the workspace already exists, andzot duplicatesnow exits 6 when duplicates are found so agents can branch onif zot duplicates …; then …; else act_on_dups; fi.runtime_error(1): caughtPdfExtractionErrorinpdfandZoteroWriteErrorincollection move/delete/rename.zot relate KEYwith no related items is now a normal exit-0 outcome (matchingzot searchon no matches) rather than an error message.config cache listrobustness: graceful fallback when the cache DB is unreachable; closes the connection in afinallyblock.
Fixed¶
- 20 pre-existing test failures on
mainrepaired (some were envelope-shape drift between tests and production; the rest were genuine exit-code regressions covered by the migration above). Theci.ymlpytest run goes green again. tests/test_extracts_textno longer breaks on hosts without~/.config/zot/config.toml. The previous over-broadPath.existsmock also patchedload_pdf_config's file-existence check; tightened to a targetedload_pdf_configmock.
Breaking¶
- Tools / agents parsing
--jsonoutput fromzot pdf,zot workspace list,zot workspace query, orzot config cache listneed to unwrap the standard envelope (result["data"]). Other commands were already enveloped; this brings the rest of the surface into line. - Error paths that previously exited 0 with a printed message now exit
with their typed code (1, 2, 3, 4, or 6). Scripts that ran
zot cite NONEXIST && echo okand similar will now correctly fail. zot duplicatesexits 6 (CONFLICT) when duplicates are detected. Scripts that ignored the exit code or usedif zot duplicates; thenwill need to invert the branch.
[0.3.0] - 2026-04-15¶
Agent-native CLI interface. zot now serves humans, AI agents (Claude Code,
Codex), and orchestrators from a single surface. See docs/agent-interface.md
for the full contract.
Added¶
- Stable JSON envelope for every command:
{"ok": true, "data": ..., "meta": {...}}on success,{"ok": false, "error": {"code", "message", "retryable"}, "meta": {...}}on failure,{"ok": "partial", "data": {"succeeded", "failed"}}for batch operations. - TTY auto-detection:
--jsonis now implicit when stdout is not a TTY. Agents pipingzotoutput always get parseable JSON without remembering a flag. Override withZOT_FORMAT=json|table|text. - Typed exit codes: 0 success, 1 runtime error, 2 auth error, 3 validation error, 4 not-found, 5 network error, 6 conflict. Orchestrators can route failures deterministically.
zot schema [command...]— machine-readable introspection for the full CLI tree. Each entry carriesname,params(typed),safety_tier,since,deprecated, and nestedsubcommands. Agents can discover every command without a README.- Safety tiers in
--help: top-level help groups commands into Read / Write (MUTATES LIBRARY) / Destructive sections. Destructive command help carries a "MUTATES LIBRARY" warning. --dry-runon all mutating commands:add,update,note --add,attach,delete,trash restore. Preview shape:{"ok": true, "dry_run": true, "data": {"would": ...}}.--idempotency-keyonadd,update,note --add,attach,delete. SQLite-backed cache at$ZOT_CACHE_DIR/idempotency.db(default~/.cache/zotero-cli-cc) with 24h TTL. Retried calls carrying the same key return the original envelope and never duplicate the upstream mutation.metaslot on every envelope:request_id(uuid),latency_ms,schema_version,cli_version. Mutating commands also setsync_required: true.nexthints in success envelopes:add,update,delete,note --add,attachsuggest plausible follow-up commands so the agent saves a planning turn.retryablefield on every error: network / 5xx / rate-limit →retryable: true; not-found / validation / 4xx →retryable: false.ZoteroWriteErrorcarriescode,retryable,retry_after_seconds.--streammode onsearch,list,recent— emits NDJSON (one item per line) plus a summary line. Agents can process long result sets incrementally.- Structured stderr progress events for long-running commands (
add --from-file,summarize-all): NDJSON{event, phase, done, total, elapsed_ms, request_id}so agents can detect liveness without blocking on the final stdout envelope. - Confirmation-required guard on destructive commands:
zot delete K1with non-interactive stdin and no--yes/--dry-runreturns a structuredconfirmation_requirederror instead of blocking. - New
exit_codes.py,core/idempotency.pymodules. - 43 new tests across
test_agent_interface.py,test_agent_p1.py,test_agent_p2.py.
Changed¶
format_error/format_items/format_item_detail/format_collections/format_notes/format_duplicatesnow wrap JSON output in the envelope. Callers that parsed raw arrays must unwrap viaenv["data"].- Human error messages moved from stdout to stderr via the new
print_errorhelper. ErrorInfodataclass gainscodeandretryablefields.- Top-level CLI group uses a custom
TieredGrouphelp renderer.
Breaking¶
- JSON output contract: callers parsing bare arrays or dicts must now read from
env["data"]. Error responses now nest underenv["error"]withcode/message/retryablefields instead of a flat{"error": "..."}. - Exit codes: previously
1for all failures; now distinct codes per failure class. Scripts checking for any non-zero exit remain valid.
[0.1.6] - 2026-03-24¶
Added¶
zot duplicates [--by doi|title|both] [--threshold 0.85]— find duplicate items by DOI match or fuzzy title similarityzot trash list— view trashed itemszot trash restore KEY [KEY ...]— restore item(s) from trash via Zotero APIzot attach KEY --file paper.pdf— upload file attachments to existing itemszot add --pdf paper.pdf— extract DOI from PDF, create item, and attach file--library group:<id>— global option for group library support across all commandsDuplicateGroupmodel for structured duplicate detection resultsresolve_library_id()helper for group library resolution- All 5 new features available as MCP tools (
duplicates,trash_list,trash_restore,attach,add_from_pdf) libraryparameter added to all existing MCP tools for group library access- 43 new tests (314 total)
Changed¶
ZoteroReaderacceptslibrary_idparameter for multi-library filteringZoteroWriteracceptslibrary_typeparameter for group library writes- MCP server uses per-library reader cache instead of global singleton
[0.1.5] - 2026-03-24¶
Added¶
zot search --type journalArticle— filter search/list results by item typezot search --sort dateAdded --direction desc— sort results by date, title, or creatorzot recent --days 7— show recently added or modified itemszot update KEY --title/--date/--field— update item metadata via Zotero APIzot pdf KEY --annotations— extract PDF annotations (highlights, notes, comments)--detail fullnow shows journal, volume, issue, pages, ISSN, publisher, citation keysummarizenow shows URL, tags, source info, abstract, and notes- All 5 new features available as MCP tools (
search,list_items,recent,update,annotations) - 37 new tests (271 total)
Fixed¶
--detail fulloutput was identical to standard detail levelsummarizecommand only showed basic metadata without abstract or source info
[0.1.3] - 2026-03-23¶
Added¶
zot citecommand — format citations in APA, Nature, or Vancouver style and copy to clipboardzot add --from-file— batch import DOIs/URLs from a text file (one per line, supports#comments)- RIS export format (
zot export KEY --format ris) with 11 Zotero type mappings - Usage examples in
--helptext for 13 commands - PyPI/CI/Python/License badges in README
pipxas install option- Shell completion install instructions (zsh/bash/fish)
[0.1.2] - 2026-03-22¶
Added¶
--dry-runflag fordelete,collection delete, andtagcommands--offsetpagination forsummarize-allandreader.search()PdfExtractionErrorwith graceful handling of corrupted/password-protected PDFs- Page range validation — error when requested pages exceed document length
- API timeout (30s) on ZoteroWriter to prevent hanging on unresponsive servers
_excluded_filter()method returning parameterized SQL placeholdersmarkdownifydependency for proper HTML-to-Markdown conversion- 19 new tests covering dry-run, offset, PDF errors, timeouts, and write error handling (199 total)
Changed¶
- Exception handling narrowed from
except Exceptiontoexcept ZoteroWriteErrorin all write commands - HTML-to-Markdown conversion replaced from naive regex to
markdownifylibrary - WAL lock fallback uses
TemporaryDirectoryinstead of manualmkdtemp/rmtree __enter__/__exit__type annotations fixed, removedtype: ignore- Search queries use parameterized SQL (
?placeholders) instead of string interpolation
Fixed¶
- Unguarded writer calls in
add,delete,tag,notecommands now catchZoteroWriteError httpx.TimeoutExceptionnow caught alongsideConnectErrorin all writer methods
[0.1.1] - 2026-03-22¶
Added¶
zot statscommand for library statisticszot opencommand for launching PDFs and URLs- CSL-JSON export format
- Shared MCP reader instance with
atexitcleanup note_updateMCP tool- Collection key filter for search
- Unified Zotero skill routing between
zotandrak
Fixed¶
- Excluded type IDs looked up dynamically instead of hardcoding
- Fulltext search routed to
rakfor semantic search - Version sync, CI workflow, temp file leak, BibTeX escaping, search N+1
[0.1.0] - 2026-03-21¶
Added¶
- Initial release
- SQLite-based read operations (search, list, read, export, relate, notes, collections, attachments, PDF extraction)
- Web API write operations via pyzotero (add, delete, tag, note, collection CRUD)
- MCP server with 17 tools (11 read + 6 write)
summarize-allandcollection reorganizefor AI classification- PDF text extraction with SQLite-backed caching
- Rich table + JSON output formatting
- TOML-based configuration with profile support
- WAL lock handling with automatic fallback
- Batch query optimization (N+1 prevention)
- BibTeX and CSL-JSON citation export
- Related items discovery (explicit relations + implicit via shared tags/collections)