Install
openclaw skills install @guoxh/zotero-enhancedZotero library management with PDF metadata auto-fetch (Crossref/arXiv), item search, file read, and note management. Supports cloud and WebDAV storage.
openclaw skills install @guoxh/zotero-enhancedThis skill provides a suite of scripts to interact with a Zotero library, covering the full document lifecycle: adding, searching, and reading. Includes enhanced metadata fetching for PDFs with DOI or arXiv IDs.
api.zotero.org), Crossref, and arXiv; WebDAV scripts additionally talk to your configured WebDAV server. read_universal.sh sends the API key only to https://*.zotero.org download URLs.ZOTERO_WEBDAV_URL must be https:// — plaintext http:// is refused so credentials never travel unencrypted.mktemp -d directories (mode 0700) and removed on exit; downloaded archives are checked for path‑traversal entries before extraction.delete_note.sh --no-confirm deletes without asking — intended for non‑interactive automation that has already obtained user consent; verify with --dry-run first. --backup saves note content to ~/.zotero-backup/ (directory 0700, files 0600) at the user's explicit request.The skill supports two storage configurations:
ZOTERO_USER_ID, ZOTERO_API_KEYimported_file (stored directly) and imported_url (referenced via WebDAV URL) attachment typesZOTERO_USER_ID, ZOTERO_API_KEY, ZOTERO_WEBDAV_URL (https:// enforced), ZOTERO_WEBDAV_USER, ZOTERO_WEBDAV_PASSAll scripts require Zotero API credentials. Get your API key from: https://www.zotero.org/settings/keys
Use scripts/search.sh to find items in the library by keyword.
# Ensure the script is executable
chmod +x scripts/search.sh
# Run the search
ZOTERO_USER_ID="<user_id>" \
ZOTERO_API_KEY="<api_key>" \
bash scripts/search.sh "your search query"
The script outputs a formatted list of matching items with their Key, needed for reading.
Use scripts/read_universal.sh to read documents from either storage mode.
chmod +x scripts/read_universal.sh
# For Zotero cloud storage:
ZOTERO_USER_ID="<user_id>" \
ZOTERO_API_KEY="<api_key>" \
bash scripts/read_universal.sh "ITEM_KEY"
# For WebDAV storage (add WebDAV variables; https:// required):
ZOTERO_USER_ID="<user_id>" \
ZOTERO_API_KEY="<api_key>" \
ZOTERO_WEBDAV_URL="https://<webdav-host>" \
ZOTERO_WEBDAV_USER="<user>" \
ZOTERO_WEBDAV_PASS="<pass>" \
bash scripts/read_universal.sh "ITEM_KEY"
Use scripts/read.sh for WebDAV storage only (legacy).
The skill now supports creating, reading, updating, and deleting notes in your Zotero library. Notes can be standalone or attached to parent items (documents).
Use scripts/create_note.sh to create a new note with plain text content.
chmod +x scripts/create_note.sh
# Create a standalone note:
ZOTERO_USER_ID="<user_id>" \
ZOTERO_API_KEY="<api_key>" \
bash scripts/create_note.sh "My important research notes"
# Create a note attached to a document:
bash scripts/create_note.sh --parent "ITEM_KEY" "Meeting notes about this paper"
# Create a note with tags:
bash scripts/create_note.sh --tag research --tag to-read "Follow up on this paper"
--parent KEY: Attach note to a parent item (document key)--tag TAG: Add a tag (can be used multiple times)--dry-run: Show steps without creating the noteThe script automatically converts plain text to HTML for Zotero storage.
Use scripts/read_note.sh to read a note and convert HTML back to plain text.
chmod +x scripts/read_note.sh
# Read as plain text (default):
ZOTERO_USER_ID="<user_id>" \
ZOTERO_API_KEY="<api_key>" \
bash scripts/read_note.sh "NOTE_KEY"
# Read as HTML:
bash scripts/read_note.sh --format html "NOTE_KEY"
# Read as JSON (full item data):
bash scripts/read_note.sh --format json "NOTE_KEY"
plain (default): Human-readable plain texthtml: Raw HTML contentjson: Full JSON item dataUse scripts/update_note.sh to update existing notes with new content or tags.
chmod +x scripts/update_note.sh
# Replace note content:
echo "New content" | \
ZOTERO_USER_ID="<user_id>" \
ZOTERO_API_KEY="<api_key>" \
bash scripts/update_note.sh --replace "NOTE_KEY"
# Append to existing content:
echo "Additional notes" | \
bash scripts/update_note.sh --append "NOTE_KEY"
# Add tags:
bash scripts/update_note.sh --tag important --tag to-read "NOTE_KEY"
# Remove tags:
bash scripts/update_note.sh --remove-tag obsolete "NOTE_KEY"
--replace: Replace note content (default)--append: Append new content to existing note--tag TAG: Add a tag (can be used multiple times)--remove-tag TAG: Remove a tag (can be used multiple times)--dry-run: Show steps without updatingThe script includes version checking to prevent update conflicts.
Use scripts/delete_note.sh to delete notes safely with confirmation and backup options.
chmod +x scripts/delete_note.sh
# Delete with confirmation:
ZOTERO_USER_ID="<user_id>" \
ZOTERO_API_KEY="<api_key>" \
bash scripts/delete_note.sh "NOTE_KEY"
# Delete without confirmation (use with caution):
bash scripts/delete_note.sh --no-confirm "NOTE_KEY"
# Backup before deleting:
bash scripts/delete_note.sh --backup "NOTE_KEY"
# Dry-run to see what would be deleted:
bash scripts/delete_note.sh --dry-run "NOTE_KEY"
--no-confirm skips the prompt for non‑interactive automation — the caller is responsible for having user consent; prefer --dry-run first~/.zotero-backup/ before deletion (directory 0700, backup files 0600)Use scripts/add_to_zotero_universal.sh for full metadata fetching and flexible storage.
--dry-run to see what would be uploaded without making changeschmod +x scripts/add_to_zotero_universal.sh
# Zotero cloud storage (no WebDAV needed):
ZOTERO_USER_ID="<user_id>" \
ZOTERO_API_KEY="<api_key>" \
bash scripts/add_to_zotero_universal.sh "/path/to/paper.pdf"
# WebDAV storage:
ZOTERO_USER_ID="<user_id>" \
ZOTERO_API_KEY="<api_key>" \
ZOTERO_WEBDAV_URL="https://<webdav-host>" \
ZOTERO_WEBDAV_USER="<user>" \
ZOTERO_WEBDAV_PASS="<pass>" \
bash scripts/add_to_zotero_universal.sh "/path/to/paper.pdf"
The universal script will:
10.1126/science.aec8352 from PDFUse scripts/add_to_zotero_enhanced.sh for WebDAV storage with metadata fetching (DOI/arXiv). Requires WebDAV — for Zotero cloud storage use add_to_zotero_universal.sh.
Use scripts/add_to_zotero.sh for WebDAV storage with title‑only extraction. Requires WebDAV — for Zotero cloud storage use add_to_zotero_universal.sh.
curl: HTTP requestsjq: JSON processing (for enhanced/universal scripts)pdftotext: PDF text extraction (from poppler-utils)zip: File compression (for WebDAV mode)All scripts are cross‑platform compatible (Linux and macOS). The universal scripts automatically detect platform‑specific commands (md5sum/md5, stat options).
sudo apt-get update
sudo apt-get install -y curl jq poppler-utils zip
brew install curl jq poppler zip
bash scripts/check_deps.sh
ZOTERO_USER_ID="1234567" \
ZOTERO_API_KEY="abc123def456" \
bash scripts/search.sh "artificial intelligence"
ZOTERO_USER_ID="1234567" \
ZOTERO_API_KEY="abc123def456" \
bash scripts/add_to_zotero_universal.sh "~/Downloads/paper.pdf"
add_to_zotero_universal.sh):
ZOTERO_WEBDAV_URL/ZOTERO_WEBDAV_USER/ZOTERO_WEBDAV_PASS must be set, or none.http:// URL can no longer leave an orphan item in the library before the script exits._meta.json version synced to the published version.delete_note.sh --backup: backup directory now created with mode 0700 and backup files set to 0600 (were default umask).add_to_zotero.sh / add_to_zotero_enhanced.sh: no longer claim “file stored in Zotero cloud” in cloud mode — these scripts never had a cloud upload path. They now fail fast with a clear error when WebDAV is not fully configured, before creating any item; use add_to_zotero_universal.sh for Zotero cloud storage.--no-confirm documented as a risk note (unattended deletion), added a Safety & Security Notes section, corrected the false “supports Zotero cloud” claims for both legacy adder scripts, and qualified the backup exception in SECURITY.md.ZOTERO_WEBDAV_URL must now be an https:// URL — all five WebDAV scripts (add_to_zotero.sh, add_to_zotero_enhanced.sh, add_to_zotero_universal.sh, read.sh, read_universal.sh) refuse plaintext http:// so credentials never travel unencrypted.read_universal.sh validates the attachment download URL against an allowlist (https://*.zotero.org) before sending the Zotero API key.mktemp -d, 0700) instead of predictable /tmp paths; archives are checked for path‑traversal entries before extraction (zip‑slip guard); API‑provided filenames are stripped of path components.--version strings synced to v1.3.12.WEBDAV_URL → ZOTERO_WEBDAV_URLWEBDAV_USER → ZOTERO_WEBDAV_USERWEBDAV_PASS → ZOTERO_WEBDAV_PASSadd_to_zotero.sh, add_to_zotero_enhanced.sh, add_to_zotero_universal.sh, read.sh, read_universal.sh, check_deps.sh) and SKILL.md.WEBDAV_BASE_URL, WEBDAV_TARGET_URL, WEBDAV_SOURCE_URL) are unchanged._filter_year + _filter_issue, missing _filter_abstract, _filter_keywords, and grep -v exclusions. Now applies the full filter set, matching the CJK branch (review #2).trap documented as known limitation (overwrites existing EXIT traps; acceptable for standalone script, review #1).grep '[一-龥]' is locale-sensitive — fails in C.UTF-8 (error) and silently matches nothing in zh_CN.UTF-8. Replaced with grep -P '[\x{4E00}-\x{9FFF}]' (codepoint-based, collation-independent) with BSD grep fallback probe.年.*期 / 第.*期 too greedy — killed legitimate titles like "青少年时期心理健康研究". Tightened to ^[0-90-9]{2,4}.*年 and 第[0-90-9一二三四五六七八九十]+[期卷].摘 要 (U+3000) / 关 键 词 not covered. Now using 摘[[:space:] ]*要 / 关[[:space:] ]*键.|| true to title=$(...) pipelines for explicit pipefail safety (was safe by accident due to function ending with echo).trap 'rm -f ...' EXIT for cleanup on failure/Ctrl-C.TMP_DIR now uses ${TMPDIR:-/tmp} instead of hardcoded /tmp.--version output.mktemp creates an empty 0-byte file, then zip -j treats it as an invalid existing archive and fails. Replaced mktemp with $TMP_DIR/${ATTACH_KEY}.prop and $TMP_DIR/${ATTACH_KEY}.zip (with rm -f guard), which also fixes a secondary bug where files were uploaded with random mktemp names instead of the required <ATTACH_KEY>.zip/<ATTACH_KEY>.prop filenames.extract_title_from_pdf() function with CJK-aware filtering: skips date/issue/volume markers, abstracts, keywords, author bios, and fund project notes; strips trailing footnote markers (*).-f.$WEBDAV_BASE_URL/${ATTACH_KEY}.prop) instead of relying on curl's trailing-slash filename appending behavior.add_to_zotero_universal.sh) — filename sanitized, all attachment JSON payloads use jq --arg instead of raw string interpolationupdate_note.sh — NOTE_KEY now passed via jq --argdelete_note.sh to prevent path traversal in backup filenamesupdate_note.sh text-to-HTML conversion — replaced for/IFS paragraph split with robust while read loopstat bug in read_universal.sh — now uses get_filesize() helper on all platformsdelete_note.sh backup now saves full decoded plain-text, not just 3-line preview--help and --version flags to all legacy scriptsadd_to_zotero_universal.sh (get_filesize function)create_note.sh HTML conversion: handles **bold**, bullet lists (-, *, numbered), and inline line breaks (<br>)read_note.sh HTML→plaintext conversion: decodes <br>, <strong>, <b>, <ul>/<li>, and HTML entitiescreate_note.sh: Create notes (plain text → HTML, optional parent, tags)read_note.sh: Read notes (HTML → text, with format options)update_note.sh: Update notes (append/replace, tag management, version checking)delete_note.sh: Delete notes (with confirmation, backup option)check_attachments.sh, find_duplicates.sh, analyze_tags.sh) per user request.imported_url support in read_universal.sh for WebDAV‑stored PDFs.--dry‑run mode for add_to_zotero_universal.sh.md5sum/md5 and stat variants.check_deps.sh).--help/--version flags.linkMode: "imported_file" (stored directly) or linkMode: "imported_url" (referenced via WebDAV)read_universal.sh script will search for both types automaticallycurl -u user:pass "https://your.webdav.server/"curl "https://api.crossref.org/works/10.1126/science.aec8352"ZOTERO_WEBDAV_URL (https:// required), ZOTERO_WEBDAV_USER, ZOTERO_WEBDAV_PASS variables