Skip to main content

Anki decks ↔ Markdown files, in perfect sync

Project description

AnkiOps

Tests License: MIT PyPI version

Anki decks ↔ Markdown files, in perfect sync

Editing flashcards in Anki's UI is tedious when you could be using your favorite text editor, AI tools, and Git. AnkiOps is a bi-directional Anki ↔ Markdown bridge. Each deck becomes a Markdown file. Work in either Anki or your text editor, and let changes flow both ways. This brings AI assistance, batch editing, and version control to your flashcards.

[!NOTE] AnkiOps is in early development and there will be breaking changes with each release. This readme might not always reflect the current state of the codebase.

Features

  • Simple CLI interface: after initialization, only two commands are needed for daily use
  • Fully round-trip, bi-directional sync that handles note creation, deletion, movements across decks, and conflicts
  • Markdown rendering with nearly all features (including syntax-highlighted code blocks, supported on desktop and mobile)
  • Support for all standard note types, plus Single & Multiple Choice and Cloze Hide All
  • Embed images via VS Code where they are directly copied into your Anki media folder (automatically set up)
  • Built-in Git integration with autocommit for tracking all changes
  • High-performance processing: handles thousands of cards across hundreds of decks in mere seconds
  • Thoroughly tested, bi-directional conversion between Markdown and Anki-compatible HTML
  • Serialize/deserialize entire collections to JSON format for backup, sharing, or automated AI processing

[!NOTE] Runtime note type config is IaC-only: 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.

[!NOTE] LLM task config is also IaC-only. AnkiOps reads from your local llm/ folder with this structure: llm/system_prompt.md, llm/tasks/*.yaml, and task prompt files referenced via prompt_file (for example llm/prompts/grammar.md).

Getting Started

  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. This is where your text-based decks will live. The additional tutorial flag creates a sample Markdown deck.
ankiops init --tutorial
  1. Execute AnkiOps: Import the tutorial deck into Anki using:
ankiops ma # 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 # anki to markdown (export)

FAQ

How is this different from other Markdown or Obsidian tools?

Most available tools are one-way importers: you write in Markdown or Obsidian and push to Anki, but edits in Anki don't sync back. AnkiOps is bi-directional: you can edit in either Anki or Markdown and sync in both directions. Additionally, AnkiOps uses a one-file-per-deck structure, making your collection easier to navigate and manage than approaches that use one file per card. 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 with non-AnkiOps note types. Your existing collection won't be affected and you can safely mix managed and unmanaged notes. Further, AnkiOps only syncs if the activated profiles matches the one it was initialized with. Concerning your 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, Biology::Cell -> Biology__Cell.md). If a deck name contains a literal __, AnkiOps escapes it in the filename so mapping stays reversible. 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 -->
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 {{c1::multiple}} {{c2::cloze deletions}}.
E: ![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 the different note types identified?

Each note type is identified by its field labels. E: (Extra) and M: (More, revealed on click) are optional fields shared across all note types.

Note Type Fields
AnkiOpsQA Q:, A:
AnkiOpsReversed F:, B:
AnkiOpsCloze T:
AnkiOpsInput Q:, I:
AnkiOpsChoice Q:, C1:``C8:, A:

How does it work?

On first import, 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. With note keys in place, we can track what is new, changed, moved between decks, or deleted, and AnkiOps syncs accordingly. Content is automatically converted between Anki's HTML format and Markdown during sync operations. One AnkiOps folder represents one Anki profile.

What is the recommended workflow?

We recommend using VS Code. It has excellent AI integration, a great add-on for Markdown previews, and supports image pasting (which will be saved in your Anki media folder by default).

How can I share my AnkiOps collection?

Use ankiops serialize to export your local AnkiOps collection to JSON. Recipients can import it with ankiops deserialize --input <path> into an initialized collection folder.

Alternatively, share your collection via native Anki export (.apkg) or by sharing Markdown files with your local media/ folder.

How can I migrate my existing notes into AnkiOps?

For standard note types, migration is straightforward:

  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.

If your existing note format doesn't map cleanly to the AnkiOps format (e.g., notes with additional or custom fields), you'll need to adapt the code accordingly. This should be fairly simple for most cases: define your note type with unique field labels (and unique field names within that note type) for automatic note type detection, and add your card's templates to ankiops/note_types.

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

What commands and flags are available in the CLI?

Global:

  • --debug - Enable debug logging
  • --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

serialize:

  • --output, -o - Output file path (default: <collection-name>.json)
  • --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: <collection-name>.json)
  • --overwrite - Overwrite existing markdown files

note-type:

  • ankiops note-type --info - Show taken labels and per-note-type label details
  • ankiops note-type <name> - Copy a note type from Anki into local note_types/ with interactive label/identifying prompts

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.4.tar.gz (82.9 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.4-py3-none-any.whl (100.3 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for ankiops-0.5.4.tar.gz
Algorithm Hash digest
SHA256 21646b55040b95af6bd4bf6839aaacd2e454a68b98904d7f4b7ff62080071dc7
MD5 add659f33649e822ddb21f8ea670a772
BLAKE2b-256 434d8abc763564fa5e83caf4fc9258db06e272e862310ca2b2ef0ee1aeef4e7f

See more details on using hashes here.

Provenance

The following attestation bundles were made for ankiops-0.5.4.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.4-py3-none-any.whl.

File metadata

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

File hashes

Hashes for ankiops-0.5.4-py3-none-any.whl
Algorithm Hash digest
SHA256 f7aba7c4ed1e390876921940a84fc03900a44e3cc408c9ab69dd9acb6bb77202
MD5 e74b487aa797e336050c2d3d65a38d82
BLAKE2b-256 33c0a089aba0d7a381f2a62f20ec17f49b7e2ce0560c22053a92f3b6fe9e887a

See more details on using hashes here.

Provenance

The following attestation bundles were made for ankiops-0.5.4-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