Skip to main content

Bookery

A CLI-first ebook library manager inspired by beets and Calibre. Fix your metadata, organize your library, sync to your Kobo — all from the terminal.

Bookery takes the "metadata-first" approach that made beets great for music and applies it to ebooks. It matches your EPUBs against online sources, lets you review and correct metadata interactively, and keeps your originals untouched.

Status

Active development — daily-driver usable. EPUB metadata extraction, Kindle (MOBI/AZW/AZW3) and PDF-to-EPUB conversion, multi-provider matching (Open Library + Google Books) with consensus merging, a SQLite catalog with per-field provenance, non-destructive write-back, Kobo device sync, a local web UI for browsing/editing, and Obsidian vault-export are all working. Plugin architecture is the main item still on the roadmap.

See docs/roadmap.md for the full plan.

⚠️ Back up your Kobo first

Bookery's library write-back is non-destructive, but Kobo sync rewrites EPUB files and the device database (.kobo/KoboReader.sqlite). Back up both the files on your Kobo and that database before syncing, and keep those backups. I daily-drive this tool, but you run it at your own risk — there is no warranty, and any data loss is on you, not the project. PRs to make it safer are always welcome.

Features

  • EPUB metadata extraction — reads title, author, ISBN, language, publisher, description, cover, and identifiers from any EPUB
  • Kindle-to-EPUB conversion — converts DRM-free MOBI, AZW, and AZW3 (KF8) files to EPUB, preserving metadata, images, cover art, and chapter structure (via NCX TOC). add, convert, and info all accept these suffixes in any case. DRM-protected files are rejected with a message naming the file.
  • PDF-to-EPUB conversion — bookery add detects text-based PDFs, extracts their structure with pdfplumber + a local LLM (LM Studio), and produces a reflowable EPUB. Scanned PDFs are refused (OCR not yet supported).
  • Kobo sync — bookery sync kobo walks the catalog, converts each EPUB to .kepub.epub via kepubify, and copies the result to a mounted Kobo. The library itself stays format-canonical (EPUB only); kepub is generated on demand at sync time and cached so re-syncs are free when nothing has changed. Books with series metadata group by series on device: sync writes Series/SeriesNumber into the Kobo database directly (the same mechanism Calibre's device driver uses — current firmware ignores series metadata inside sideloaded EPUBs). Newly copied books group on the sync after the device has indexed them.
  • Collections — group books into named lists, either static (hand-picked) or rule-based (membership derived live from a query like genre:"Science Fiction" or series:Dune, so it stays current as the library grows). See bookery collections.
  • Collection shelves on device — the same bookery sync kobo mirrors each collection to a Kobo shelf (Shelf/ShelfContent). Bookery owns only shelves whose InternalName is bookery-<collection_id>; a user-created shelf that shares a name is skipped, never overwritten. Unchanged shelves are skipped on re-sync (membership hash), and a shelf is removed once its collection is deleted. bookery collections show <id> --sync-status reports per-device shelf state.
  • Multi-provider metadata matching — Open Library and Google Books in a consensus merger that prefers values agreed on by ≥2 providers and falls back to a priority order otherwise. ISBN-10/13 lookups are normalized and provider responses are cached.
  • Per-field provenance & locking — every cataloged field records which provider supplied it and when. User edits are stamped as user and locked against rematch; individual fields can be locked/unlocked explicitly with bookery info --lock / --unlock.
  • Interactive review — presents candidates in a Rich table, lets you accept, compare details, look up by URL, or skip
  • Smart normalization — splits mangled filenames like SteveBerry-TheTemplarLegacy into clean search queries, detects embedded author names
  • SQLite catalog — imports books into a local database for querying, tagging, and integrity checks
  • Web UI — bookery serve launches a local browser UI for paginated/sortable browsing of the catalog, filter chips, cover thumbnails, a responsive mobile card layout, per-book detail/edit pages, collection create/edit (including raw rule strings, plus an append-only query builder for composing clauses without memorizing the syntax) with inline validation and non-persisting query match preview, search-active-providers, and "apply candidate metadata with diff" rematch flow.
  • Genre management — bookery genre auto-maps raw provider subjects to a canonical genre vocabulary, with assign / apply / auto / stats / unmatched for curation.
  • Obsidian vault-export — bookery vault-export turns an Obsidian vault into a single EPUB with a hierarchical folder/note TOC, A-Z buckets within each folder, leading-article-aware filing ("The Loop" files under L), resolved [[wiki-links]] and ![[image]] embeds, and an optional tag index. Can auto-catalog the export so it ships on the next Kobo sync.
  • Non-destructive — metadata writes always go to a copy; original file contents are never modified. add copies each source into your library by default (--move deletes the source after a successful catalog insert)

Installation

Requires Python 3.12+.

Install it (to use bookery)

Bookery is published on PyPI as bookery-cli (the bookery name was taken). The command it installs is still bookery.

uv tool install bookery-cli
# or
pipx install bookery-cli

uv tool install puts bookery in its own isolated environment and drops a bookery command on your PATH (in ~/.local/bin) — no virtualenv to create or activate. Just run bookery from anywhere. If the command isn't found after install, run uv tool update-shell once and restart your shell.

Upgrade later with uv tool upgrade bookery-cli; remove with uv tool uninstall bookery-cli. If you installed from GitHub before the PyPI release, run uv tool uninstall bookery first.

To run unreleased code from main, install straight from GitHub:

uv tool install git+https://github.com/joecotellese/bookery.git

Develop it (to hack on bookery)

git clone https://github.com/joecotellese/bookery.git
cd bookery
uv sync

uv sync installs into the project's .venv. Run the CLI with uv run bookery ..., or activate the venv first (source .venv/bin/activate) and call bookery directly.

Optional: PDF conversion

The PDF path in bookery add routes the document through a single semantic LLM call that reassembles articles/chapters into a clean EPUB. You'll need an OpenAI-compatible endpoint with a model strong enough to return structured JSON.

Local (default) — LM Studio with a long- context instruct model (known-good: Qwen 2.5 7B Instruct 1M). Load the model with ≥16k context, enable the local server, then point bookery at it via ~/.bookery/config.toml:

[convert.semantic]
provider = "lm-studio"
model = "qwen2.5-7b-instruct-1m"
base_url = "http://localhost:1234/v1"
api_key_env = ""             # empty for local; no key needed
prompt_version = 1
llm_max_retries = 2

Cloud — swap provider, model, base_url, and point api_key_env at the env var holding your key. Never write the key into config.toml:

[convert.semantic]
provider = "openai"
model = "gpt-5.4-nano"
base_url = "https://api.openai.com/v1"
api_key_env = "OPENAI_API_KEY"
prompt_version = 1

See docs/config.example.toml for a fully annotated config with LM Studio, Moonshot/Kimi, and OpenAI examples.

Semantic responses are cached under ~/.bookery/data/convert_cache.db so re-runs on the same PDF skip the LLM. Safe to delete at any time; bumping prompt_version invalidates only stale entries.

Quick Start

# Inspect a single EPUB (loose file on disk)
bookery info ~/Books/some-book.epub

# Scan a directory and report format coverage
bookery inventory ~/Books/

# Convert Kindle files (.mobi, .azw, .azw3) to EPUB
bookery convert ~/Books/ -o ~/Books-epub/

# Match EPUBs against Open Library and write corrected copies
bookery match ~/Books/ -o ~/Books-fixed/

# Auto-accept high-confidence matches (no prompts)
bookery match ~/Books/ -o ~/Books-fixed/ --yes

# Add a single EPUB to the catalog (copies into ~/.library/ by default)
bookery --db ~/library.db add ~/Books/dune.epub

# Add an entire directory of EPUBs (recursive)
bookery --db ~/library.db add ~/Books/

# Add and remove the sources after they land in the library
bookery --db ~/library.db add ~/Downloads/ --move

# Search and browse the catalog
bookery search "martian"
bookery ls --tag fiction
bookery info 42

Common options

  • --db PATH is a top-level option; put it before the subcommand so every command picks it up: bookery --db ~/library.db ls. Subcommand-level --db still works and overrides the global.

  • -y/--yes auto-accepts high-confidence matches without prompting (valid on add, match, rematch, convert). -q/--quiet is a deprecated alias.

  • -t/--threshold sets the confidence cutoff for auto-accept. The default comes from [matching].auto_accept_threshold in ~/.bookery/config.toml (default 0.8):

    [matching]
    auto_accept_threshold = 0.85
    cache_ttl_days = 30        # metadata response cache TTL (default 30)
    providers = ["openlibrary", "googlebooks", "hardcover"]   # priority order; default ["openlibrary"]
    min_request_interval = 0.1 # seconds between provider HTTP requests (default 0.1)
    fetch_covers = true        # download & embed candidate covers during matching (default true)
    
  • --no-cache on match/rematch bypasses the on-disk metadata response cache and forces fresh provider lookups. Cached responses live at {data_dir}/metadata_cache.db and expire after [matching].cache_ttl_days.

  • --no-covers on add/match/rematch skips downloading the accepted candidate's cover image (useful offline or for throttled batch runs). The default comes from [matching].fetch_covers (default true). When enabled, the cover is embedded in the same write as the text metadata; a failed download is non-fatal — the text still applies and one warning line is printed.

  • [matching].providers selects and orders metadata sources. With a single entry the named provider is used directly; with two or more, results are merged by a consensus step that prefers values agreed on by ≥2 providers and falls back to the priority order otherwise. Supported: openlibrary, googlebooks, hardcover. Exception: series, series_index, rating, and ratings_count prefer Hardcover's value regardless of provider order (it is the only source with reliable series positions); ≥2-provider agreement still wins the vote, but Hardcover's spelling of an agreed series name is used. Title-pattern heuristics fill series data that providers leave empty and are stamped source=heuristic, so they never outrank provider or user values.

  • Google Books API key — set the GOOGLE_BOOKS_API_KEY environment variable to authenticate Google Books requests. Without it, requests use the shared anonymous per-IP quota, which a full-library rematch exhausts quickly (HTTP 429). Create a free key in the Google Cloud Console: create/select a project, enable the Books API, then Credentials → Create credentials → API key. Then export GOOGLE_BOOKS_API_KEY=AIza.... The key is read from the environment only — never written to config.

  • Hardcover API key — set the HARDCOVER_API_KEY environment variable to enable the hardcover provider. Hardcover is the strongest source for series name/position and community ratings, but it requires a token: create a free account at hardcover.app, then copy the token from Account Settings → Hardcover API. Then export HARDCOVER_API_KEY=.... The key is read from the environment only — never written to config. Without it, a configured hardcover provider logs one warning and returns no results. Rate limit is 60 requests/minute; tokens expire January 1st each year.

  • [matching].min_request_interval is the minimum seconds between provider HTTP requests (default 0.1). Raise it to throttle a bulk rematch below a provider's rate limit; on a 429 the client also honors the response's Retry-After header (capped at 60s) before retrying.

  • Per-field provenance is recorded for every cataloged book in the book_field_provenance table. Use bookery info <id> --provenance to see which source supplied each field and when it was fetched. Use bookery info <id> --set field=value to hand-edit a value (it's stamped as user and locked against overwrite), and --lock field / --unlock field to gate fields against rematch.

Commands

Metadata & Matching

Command Description
match <path> -o <dir> Match metadata for loose EPUB files (not yet in the catalog) and write corrected copies
rematch [book_id] Re-run matching on cataloged books and update the database
series ls List series in the catalog with book counts and missing-position gaps
series backfill [book_id|--all|--tag] Fill missing series/series_index from providers (title-pattern heuristics as fallback) and rewrite library EPUBs whose series meta is stale (--dry-run to preview)

A provider's series is only taken from a candidate whose title actually corresponds to the book being backfilled. A match confidence is computed across title, author, ISBN and language, so a book whose own title carries little searchable signal (Book 17 - Remnant) could clear the threshold against an entirely unrelated book and inherit its series. Books matched by ISBN skip this check — the ISBN already settles identity. When a series is declined, the run says which candidate was rejected and why rather than reporting a bare "no series found". A series position that merely repeats a Book N prefix from the book's own title is dropped as well: a missing position is better than a wrong one.

Conversion

Command Description
convert <path> -o <dir> Convert Kindle files (MOBI/AZW/AZW3, DRM-free) to EPUB format (supports --match to chain into matching). Exits 1 if any file fails.
vault-export --vault <path> -o <file> Export an Obsidian vault to a single EPUB with clickable TOC, resolved wiki-links, and an optional tag index. Requires pandoc.

Library Catalog

Command Description
add <path> Add a single EPUB/PDF/Kindle file (MOBI, AZW, AZW3) or a directory of EPUBs to the library (copies into library_root, catalogs). Files default to --match; directories default to --no-match. Supports --move (ignored for PDF and Kindle input), --convert (also picks up Kindle files in a directory), --force-duplicates, -o/--output-dir. import is a deprecated alias.
remove <id>... Delete one or more books from the catalog and disk (-y skips prompt; --keep-file keeps the file)
prune Remove catalog rows whose underlying files are missing
ls List all books in the catalog (filter with --series or --tag)
info <id-or-path> Show metadata for a cataloged book by ID, or for a loose EPUB on disk. Catalog mode supports --provenance, --set field=value, --lock field, --unlock field. inspect is a deprecated alias.
search <query> Search the catalog by title, author, or description
inventory <path> Scan a directory tree and report ebook format coverage
reveal <query> Open the on-disk folder for a book (--print to print the path instead). folder is a deprecated alias.

Organization

Command Description
tag add <id> <tag> Add a tag to a book
tag rm <id> <tag> Remove a tag from a book
tag ls List all tags with book counts
genre assign <id> <genre> Assign a canonical genre to a book
genre auto-assign Auto-assign genres from subjects for cataloged books
genre auto Auto-map provider subjects to canonical genres across the catalog
genre ls List all canonical genres with book counts
genre stats Show the most common subjects that don't map to a canonical genre
genre unmatched Show books with subjects but no genre assigned
mark finished <id> Mark a book as finished (also mark reading <id>, mark unread <id>; supports --bulk-from FILE)
authors list List authors with book counts (--duplicates shows only multi-spelling clusters; --needs-review lists malformed names for manual merge)
authors normalize Reorder Surname, Given → Given Surname (dry-run by default; --apply to write; --include-reversed for names with no twin)
authors merge <forms…> --into <canonical> Merge arbitrary spellings into one canonical name (dry-run by default; --apply to write)
authors fix-sort Backfill opf:file-as so devices sort authors by surname (dry-run by default; --apply to write)
verify Check for missing or changed files (supports --check-hash)

Collections

Collections group books into named lists. A collection is either static (hand-picked membership) or rule-based (membership derived live from a query). The two are mutually exclusive — a rule-based collection holds no hand-picked rows, and its members are recomputed on every read, so it stays current automatically as the library changes.

Command Description
collections create <name> Create a static collection (-d/--description optional)
collections create <name> --query '<rule>' Create a rule-based collection, e.g. --query 'genre:"Science Fiction"'
collections ls List collections with live book counts
collections show <id> Show a collection's books; rule-based collections also show the rule and live match count (--sync-status for per-device shelf state)
collections add-books <id> <book_id>... Add books to a static collection
collections remove-books <id> <book_id>... Remove books from a static collection
collections edit <id> --query '<rule>' Convert a static collection to rule-based (discards hand-picked books)
collections edit <id> --clear-query Convert a rule-based collection to static, snapshotting current members
collections preview --query '<rule>' Show which books a rule matches, without saving
collections query-help Print the full query reference (fields, operators, dates, examples)
collections rename <id> <new_name> Rename a collection
collections rm <id> Delete a collection (books are not deleted)

Converting between the two kinds is one-way destructive in one direction: setting a rule on a static collection discards its hand-picked books, while clearing a rule snapshots the currently-matching books into a static list. In the web edit form these conversions are gated — when a static collection with hand-picked books gains a rule, or any rule is cleared, the form warns with the exact count of books affected and requires a second confirm before writing.

From the web UI (bookery serve), reach collections via the Collections link in the header. A book's detail page has a Collections section that lists the static collections it belongs to (each with a Remove), an Add to collection picker (static collections only — rule-based membership is derived), and a New collection from this book link that seeds a rule from the book's series or author. Deleting a collection from its detail page asks for confirmation first; your books are never deleted.

Rule query language

A rule query is a Lucene-style expression over a whitelisted set of fields, parsed permissively and validated restrictively — an unknown field, bad value, or unsupported shape is rejected with a message naming what's allowed. Run bookery collections query-help for the in-terminal reference.

Field Matches
id exact book id
title exact, phrase, or prefix* (left-anchored)
author substring (contains)
series exact
genre exact canonical genre
tag exact
language exact
publisher exact
subject substring (contains)
isbn exact
year publication year — =, range, or comparison
rating =, range, or comparison
added date added (ISO YYYY-MM-DD) — =, range, or comparison

Operators: AND, OR, NOT, grouping with ( ), and +/- prefixes (require/exclude). The numeric/date fields (year, rating, added) also accept ranges [a TO b] (inclusive), {a TO b} (exclusive), open-ended *, and comparisons >= <= > <. Fuzzy (~) and boost (^) are not supported.

series:Dune
genre:"Science Fiction" AND year:[2020 TO *]
rating:>=4
author:"Ursula K. Le Guin" NOT tag:reread

Web UI

Command Description
serve Launch the local web UI (--host, --port; default 127.0.0.1:5000)

The web UI provides paginated and sortable browsing, filter chips, cover thumbnails, a responsive mobile card layout, per-book detail and edit pages, a "search active providers" flow that lets you apply candidate metadata with a side-by-side diff, and inline delete. A Columns control on the book list toggles the optional Series, ISBN, Language, Publisher, Added, and Enriched columns (Title, Author, and cover always show); the choice is remembered per browser. The default view shows Title, Author, Added, and Enriched. The diff panel has a checkbox per changed field (all checked by default), so you can apply one, some, or all of a candidate's fields — including a cover-only apply; unchecked fields keep their current values. Subjects are among the applyable fields: applying them re-derives the book's genre(s) from the provider's subjects, the same auto-mapping bookery genre performs on the CLI. When you apply a candidate, its cover image (if the provider offers one and the cover row is checked) is fetched and embedded into the non-destructive EPUB copy alongside the text fields; a cover-fetch failure is non-fatal — the text metadata still applies and the result message notes the cover was skipped.

Series get their own read-only browsing surface: a Series item in the masthead leads to an index of every series with book counts (and a hint when positions are missing), each series page lists its books ordered by position, and a book's detail page shows its series as "Series #1 of 14" linking back to the series page. The book list's Series column is sortable, grouping a sorted library by series with unpositioned and unseriesed books sinking to the bottom. Series metadata comes from bookery series backfill (see Metadata & Matching above).

Collections can be created and edited directly from the web UI. "New collection" (on the collections page) opens a form for the name, an optional description, and an optional raw rule query (the same Lucene subset as collections create --query); leave the query blank for a hand-picked static collection. Each collection's detail page has an "Edit" affordance for changing its name, description, and (for an already rule-based collection) its rule. Invalid input — a blank or duplicate name, or an unparseable rule — is reported inline on the form with the field whitelist hint, never as an error page. A "Preview matches" button on the form resolves the rule without saving and shows the true match count plus a capped sample, so you can see exactly which books a rule selects before committing to it.

For beginners who don't want to memorize the query syntax, the form also has a collapsible Query builder (collapsed by default). Pick a field from the whitelist, type a value, and "Add condition (AND)" appends a correctly-quoted field:"value" clause to the rule textarea — quoting is handled server-side so the composed clause is always valid. The builder only ever appends; the raw textarea remains the single source of truth, so advanced users can keep hand-editing it (OR/NOT, grouping, ranges, comparisons) and ignore the builder entirely.

Device sync

Command Description
sync kobo Convert library EPUBs to .kepub.epub, copy to a mounted Kobo, and mirror collections to device shelves
sync kobo --target <path> Override auto-detection with an explicit mount point
sync kobo --dry-run Show what would be copied without touching the device
sync kobo --no-kepub Send plain EPUBs untouched — skip kepub conversion (and the kepubify dependency)
collections show <id> --sync-status Show per-device shelf sync state for a collection

Conversion to .kepub.epub requires the kepubify binary on PATH (brew install kepubify on macOS). Files are written to <kobo>/Bookery/Author/Title/Title.kepub.epub — the dedicated Bookery/ subdirectory keeps synced content visibly separate from Calibre sideloads, Kobo store purchases, and library borrows. Pass --no-kepub (or set kepub = false under [sync.kobo]) to copy plain EPUBs as Title.epub instead; kepub adds richer on-device progress/stats, but Kobo reads plain sideloaded EPUBs fine, and this path needs no kepubify. Sync is currently additive: existing files on the device are never deleted — including when you switch a book's format, so the old .kepub.epub/.epub is left in place. A SQLite cache at {data_dir}/kepub_cache.db keyed on the source EPUB hash plus the kepubify version makes re-syncs effectively free when nothing has changed.

[sync.kobo]
kepub = false          # default true; send plain EPUBs when false

Readers sort authors by the EPUB's opf:file-as key. If a device files an author under their given name (e.g. "Brandon" instead of "Sanderson"), the library copies are missing that key — run bookery authors fix-sort to backfill a surname-first file-as, then re-sync.

Some devices (Kobo) ignore file-as and sort by the raw dc:creator text instead, so a name stored as Cussler, Clive files under "C". Fix the catalog with bookery authors normalize (reorders Surname, Given to Given Surname) or bookery authors merge (collapses typos and duplicate spellings into one canonical name). Both are dry-run by default and back the database up before --apply. The corrected name flows into the EPUB's dc:creator on the next write-back and re-sync. Use bookery authors list --duplicates first to see what would change.

The vault-export workflow

Turn an Obsidian vault into a single EPUB — one chapter per note, with a clickable TOC, resolved [[wiki-links]] and ![[image]] embeds, and an optional tag index at the end. Requires pandoc (brew install pandoc on macOS).

Run with flags:

bookery vault-export --vault ~/obsidian-vault -o vault.epub \
  --include-folder "3_Permanent Notes" --include-folder "2_Literature Notes" \
  --index --exclude-tag type/meeting

--folder is accepted as a deprecated alias for --include-folder and will be removed in a future release.

Or set defaults once in ~/.bookery/config.toml and just run bookery vault-export -o vault.epub:

[vault_export]
vault_path = "~/obsidian-vault"
folders = ["3_Permanent Notes", "2_Literature Notes"]
include_index = true
index_exclude_prefixes = ["type/"]   # hide tags like `type/permanent` from index
index_min_count = 1
exclude_tags = ["type/meeting"]      # drop notes with these exact frontmatter tags
default_author = "Your Name"
uuid_mode = "stable"                 # "stable" keeps the same dc:identifier
                                     # across re-exports so Kobo updates in place.
                                     # Pass `--random-ids` on the command line
                                     # for a fresh identifier per export.
catalog = true                       # auto-add the EPUB to the bookery library
                                     # so it ships on the next `sync kobo`

exclude_tags matches the full tag string exactly (type/meeting skips notes tagged type/meeting but not type/permanent). Callouts, block references, note embeds (![[note]]), and Dataview queries are not resolved in this version.

With catalog = true (or --catalog on the command line), the export is imported into the library and then deployed to your reader on the next bookery sync kobo — no separate bookery add step. A vault export is a point-in-time snapshot, so re-running replaces the prior catalog row and EPUB rather than piling up a new copy each day.

The match workflow

When you run bookery match, Bookery will:

  1. Extract metadata from each EPUB
  2. Normalize mangled titles (CamelCase splitting, word segmentation)
  3. Search Open Library by ISBN first, then fall back to title/author
  4. Present you with scored candidates to review
  5. Write your chosen metadata to a copy of the file

During review, you can:

  • [1-N] Accept a candidate
  • [v1-vN] View a side-by-side comparison
  • [u] Look up a specific Open Library URL
  • [s] Skip this book
  • [k] Keep the original metadata

Project Structure

src/bookery/
  cli/            # Click commands
  core/           # Pipeline logic (non-destructive write-back, conversion)
  formats/        # Format handlers (EPUB via ebooklib, MOBI via KindleUnpack)
  metadata/       # Matching engine
    openlibrary.py    # Open Library API provider
    scoring.py        # Weighted confidence scoring
    normalizer.py     # Title/author normalization (CamelCase, wordninja)
    candidate.py      # MetadataCandidate dataclass
    provider.py       # MetadataProvider protocol
  db/             # SQLite catalog (schema, CRUD, search, migrations)

Development

# Install dev dependencies
uv sync

# Run tests
uv run pytest

# Run linter
uv run ruff check src/ tests/

# Run a specific test file
uv run pytest tests/unit/test_scoring.py -v

Tests are isolated from your real ~/.bookery/library.db by an autouse guardrail. If you previously ran the suite without this guardrail and your catalog now reports source missing: /private/var/folders/.../pytest-of-..., see "Recovering from test pollution" in CONTRIBUTING.md.

Roadmap

Bookery is being built in phases:

  1. EPUB metadata read/write + CLI skeleton
  2. Multi-provider matching (Open Library + Google Books) with consensus merger and interactive review
  3. SQLite catalog, import pipeline, per-field provenance, query commands
  4. MOBI-to-EPUB conversion
  5. PDF-to-EPUB conversion (semantic, local-LLM)
  6. Kobo device sync
  7. Obsidian vault-export to EPUB
  8. Web UI for browse/search/edit
  9. Next: Plugin architecture (format and provider plugins)
  10. Polish (covers cache, performance benchmarks, config polish)

See docs/roadmap.md for detailed checklists.

Design Principles

  • Metadata-first — get the data right before organizing files
  • Non-destructive — original file contents are never modified; metadata writes go to a copy in the library
  • CLI-first — every feature is a terminal command; no GUI required
  • Extensible — providers, formats, and devices will be pluggable
  • Respectful — rate-limited API usage, no scraping

Contributing

Bookery is AI-friendly and developed with the help of Claude Code. AI-assisted contributions are welcome alongside traditional ones.

See CONTRIBUTING.md for coding standards, testing requirements, and how to get started.

License

MIT

Metadata

Release files for bookery-cli 2026.10.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bookery-cli 2026.10.1
File Size Uploaded
bookery_cli-2026.10.1.tar.gz 375.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bookery-cli 2026.10.1
File Interpreter ABI Platform
bookery_cli-2026.10.1-py3-none-any.whl Python 3 none any Details

Total release size: 811.6 kB

Release files / bookery_cli-2026.10.1.tar.gz

Download URL bookery_cli-2026.10.1.tar.gz
Size 375.0 kB
Tags Source
SHA-256 checksum
How to use checksums
a93872b688f1539ad3e7ea05dea65a421a6f857b13b77b2bf16622e8d3b43c59
BLAKE2b-256 checksum
How to use checksums
eef9abf9661a79b0b547abab1a7445994574a92d367c90894be4e0ddc8b61d48
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / bookery_cli-2026.10.1-py3-none-any.whl

Download URL bookery_cli-2026.10.1-py3-none-any.whl
Size 436.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
196ed6aac350552a3914f32c6fd4f304d9a05b39c5a1c48329bdb93421418a24
BLAKE2b-256 checksum
How to use checksums
f5c09d9aefa34bc115eb51d6f7fc793edb375b3ac486bab67515c94a28ffbdf4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

2026.10.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page