Changelog¶
All notable changes to this project will be documented in this file.
Format follows Keep a Changelog, versioning follows Semantic Versioning.
[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)