Skip to main content

Anki ↔ Markdown, with bidirectional sync, custom note types, and LLM integration

Project description

AnkiOps

Tests License: MIT PyPI version

AnkiOps is a bidirectional Anki ↔ Markdown bridge where each deck becomes a Markdown file. Edit in plain text, version with Git, enhance with LLMs, and sync changes both ways.

Features

  • Full Anki support: Safe, performant bidirectional syncing of notes with custom note types, (sub-)decks, and media files
  • Markdown-first: Edit in your favourite editor and render Markdown features in Anki (including syntax highlighting)
  • Simple CLI interface for importing and exporting between Anki and the filesystem
  • LLM-integration: Run programmable tasks such as content review, grammar fixes, or translations on your collection
  • Git-based: Share your decks and collaborate via fully-synchronized Github repositories (nyi)

[!NOTE] AnkiOps only acts on note types defined within the note_types/ folder. You can add note types from Anki using ankiops note-types --add <name>.

Installation

  1. Install AnkiOps via pipx: Pipx will make AnkiOps globally available in your terminal.
pipx install ankiops
  1. Initialize AnkiOps: Make sure that Anki is running, with the AnkiConnect add-on enabled. Initialize AnkiOps in any empty directory of your choosing. The command creates a database file for synchronization, an llm/ directory for custom LLM tasks, and a note_types/ directory for the note types AnkiOps will act on (following Infrastructure as Code principles). The additional tutorial flag creates a sample Markdown deck you can experiment with.
ankiops init --tutorial
  1. Execute AnkiOps: Import the tutorial deck into Anki using:
ankiops ma # alias for markdown-to-anki (import)
  1. Keep everything in sync: When editing your Markdown files, sync Markdown → Anki (and vice versa), as each sync makes one side match the other. After reviewing and editing your cards in Anki, you can sync Anki → Markdown using the following command:
ankiops am # alias for anki-to-markdown (export)

FAQ

How is this different from other Markdown tools?

Most available tools are one-way importers: you write in Markdown and push to Anki, but edits in Anki don't sync back. AnkiOps is bidirectional: you can edit in either Anki or Markdown and sync in both directions. It uses a one-file-per-deck structure, making your collection easier to navigate than approaches that use one file per card. Further, custom note types are supported while maintaining a clear working environment. This essentially lets you manage your entire Anki collection from your favorite text editor.

Is it safe to use?

Yes, AnkiOps will never modify notes that are not defined within the note_types/ folder. Your existing collection won't be affected and you can safely mix managed and unmanaged notes within one deck. Further, AnkiOps only syncs if the activated profiles matches the one it was initialized with. Concerning the Markdown files, AnkiOps automatically creates a Git commit of your collection folder before every sync, so you can always roll your files back if needed.

How do I create new notes?

Create a new Markdown file in your initialized AnkiOps folder. For the first import, the file name acts as the deck name. Subdecks use __ (for example, Anatomy::Heart --> Anatomy__Heart.md). Notes must be separated by a new line, three dashes ---, and another new line. You can add new notes anywhere in an existing file.

<!-- note_key: 123487556abc -->
<!-- tags: AnkiOps -->
Q: Question text here
A: Answer text here
E: Extra information (optional)
M: Content behind a "more" button (optional)

---

<!-- note_key: 123474567def -->
T: Text with the {{c1::first}} cloze.
Text with the {{c1::second}} cloze.
E: Some *formatted* extra info.

![image with set width](im.png){width=700}

---

Q: What is this?
C1: A multiple choice note
C2: with
C3: automatically randomized answers.
A: 1, 3

In this example, the last note is a new note which will get a note_key comment assigned on the next import.

How are different note types handled?

AnkiOps reads note types exclusively from your local note_types/ directory. ankiops init ejects default note types as bootstrap files; those local files are then the only source of truth and can be modified as needed. Each note type is identified by a unique set of field labels. These labels are defined innote_types/name/note_type.yaml and can be customized as needed. The set operations of each unique note type are defined by the identifying fields in the yamls. For an overview of the current configuration, use ankiops note-types.

How can I migrate my existing notes into AnkiOps?

There are three ways to migrate your existing collection. You can create new note types configuration files in the note_types/ folder that match your existing note types in Anki by hand, use the ankiops note-types --add <name> command to copy note types from Anki, or convert your existing notes to the default AnkiOps note types using Change Note Type… in the Anki browser.

For the last option specifically, the recommended workflow is:

  1. Convert your existing notes to the matching AnkiOps note types via Change Note Type… in the Anki browser.
  2. Export your notes from Anki to Markdown using ankiops am.
  3. In the first re-import, some formatting may change because the original HTML from Anki may not follow the CommonMark standard. Formatting of your cards can be done automatically at a low cost using the included JSON serializer and AI tooling.

How does it work?

AnkiOps assigns a stable note_key to each managed note. It is represented by a single-line HTML tag (e.g., <!-- note_key: a1b2c3d4e5f6 -->) above a note in the Markdown. AnkiOps note keys are profile-independent, in contrast to Anki's note IDs. The .ankiops.dbdatabase stores the mapping between Anki's note IDs and AnkiOps note keys, along with other metadata. When syncing, AnkiOps uses these note keys to determine which notes to create, update, or delete in either Anki or Markdown. Media files are stored in a media/ folder with hashed file names to avoid conflicts.

What is the recommended workflow?

We recommend using VS Code. It has excellent AI integration, a native Markdown previewer, and supports image pasting from the clipboard directly into the /media folder (automatically set up).

How can I share my AnkiOps collection?

TODO

How do I upgrade AnkiOps to the latest version?

Use pipx upgrade ankiops to upgrade AnkiOps to the latest version. Delete all local AnkiOps files (except for your Markdown decks), re-initialize AnkiOps in the same folder using ankiops init, and sync from Anki via ankiops am. Since all files are git-tracked, you can easily spot any changes and roll back if needed.

How can I develop AnkiOps locally?

Fork this repository and initialize the tutorial in your root folder (make sure Anki is running). This will create a folder called collection with the sample Markdown in it. Paths will adapt automatically to the development environment. You can run AnkiOps locally using the main script.

git clone https://github.com/visserle/ankiops.git
cd ankiops
uv sync
uv run python -m main init --tutorial
uv run python -m main ma

Are Pull Requests welcome?

Yes! We welcome contributions of all kinds, including bug fixes, new features, documentation improvements, and more. Please open an issue or submit a pull request if you'd like to contribute.

What commands and flags are available in the CLI?

Global:

  • --debug - Enable debug logging
  • --version - Show installed AnkiOps version
  • --help - Show help message

init:

  • --tutorial - Create tutorial markdown file

anki-to-markdown / am:

  • --no-auto-commit, -n - Skip automatic git commit

markdown-to-anki / ma:

  • --no-auto-commit, -n - Skip automatic git commit

note-types:

  • ankiops note-types - Show note types, identifying labels, and the label registry
  • ankiops note-types --add <name> - Copy a note type from Anki into local note_types/ with interactive label/identifying prompts

serialize:

  • --output, -o - Output file path (default: AnkiCollection.json, <deck-stem>.json when --deck is set)
  • --deck - Serialize only one deck (includes subdecks by default)
  • --no-subdecks - With --deck, exclude subdecks (exact deck only)

deserialize:

  • --input, -i - Input file path (default: AnkiCollection.json)
  • --overwrite - Overwrite existing markdown files

fix-image-widths:

  • --deck - Fix only one deck (includes subdecks by default)
  • --no-subdecks - With --deck, exclude subdecks (exact deck only)
  • --tolerance - Pixel tolerance for near-equal width fixes (default: 5)
  • --width - Force all Markdown images in scope to this width
  • --no-auto-commit, -n - Skip automatic git commit

llm:

  • ankiops llm - Show LLM status dashboard (tasks + recent jobs)
  • ankiops llm <task_name> [--model <model>] [--deck <deck_name>] - Plan one configured task
  • ankiops llm <task_name> --run [--model <model>] [--deck <deck_name>] [--no-auto-commit] - Run one configured task job
  • ankiops llm --job <job_id|latest> - Show one LLM job in detail

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ankiops-0.5.10.tar.gz (107.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ankiops-0.5.10-py3-none-any.whl (128.5 kB view details)

Uploaded Python 3

File details

Details for the file ankiops-0.5.10.tar.gz.

File metadata

  • Download URL: ankiops-0.5.10.tar.gz
  • Upload date:
  • Size: 107.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ankiops-0.5.10.tar.gz
Algorithm Hash digest
SHA256 220cd82b666dfb82321955459f21731a5215ee38470f1e7787a770b3f98b664d
MD5 d2dd4b90cf9930870017f5589fe3f049
BLAKE2b-256 57e7123058329e00e067a23d3d98e8c799f7aab0a391f16c50c58bc09c8ac6f2

See more details on using hashes here.

Provenance

The following attestation bundles were made for ankiops-0.5.10.tar.gz:

Publisher: publish.yml on visserle/AnkiOps

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ankiops-0.5.10-py3-none-any.whl.

File metadata

  • Download URL: ankiops-0.5.10-py3-none-any.whl
  • Upload date:
  • Size: 128.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ankiops-0.5.10-py3-none-any.whl
Algorithm Hash digest
SHA256 b38fc03778811c3afc18b29759b450e3cc73ad0b7719e1d0a18dc93e941f7484
MD5 44dff8a64bd6dbbcaea5b946232c2b6c
BLAKE2b-256 192584b5d91cf2c3e1ab786561a8713f28023de65db2ef3032150e1876817e75

See more details on using hashes here.

Provenance

The following attestation bundles were made for ankiops-0.5.10-py3-none-any.whl:

Publisher: publish.yml on visserle/AnkiOps

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page