{"id":34370892,"url":"https://github.com/eudoxia0/hashcards","last_synced_at":"2026-03-03T13:39:02.527Z","repository":{"id":318090528,"uuid":"1034788649","full_name":"eudoxia0/hashcards","owner":"eudoxia0","description":"A plain text-based spaced repetition system.","archived":false,"fork":false,"pushed_at":"2025-12-15T00:31:39.000Z","size":774,"stargazers_count":785,"open_issues_count":18,"forks_count":33,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-12-17T15:09:57.591Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eudoxia0.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.xml","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-09T01:52:11.000Z","updated_at":"2025-12-17T14:44:29.000Z","dependencies_parsed_at":"2025-10-05T03:31:49.769Z","dependency_job_id":"1b3f018a-5012-457f-b47f-88a06f3044d7","html_url":"https://github.com/eudoxia0/hashcards","commit_stats":null,"previous_names":["eudoxia0/hashcards"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/eudoxia0/hashcards","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eudoxia0%2Fhashcards","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eudoxia0%2Fhashcards/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eudoxia0%2Fhashcards/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eudoxia0%2Fhashcards/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eudoxia0","download_url":"https://codeload.github.com/eudoxia0/hashcards/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eudoxia0%2Fhashcards/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27976918,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-23T02:00:07.087Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-18T10:00:31.883Z","updated_at":"2025-12-23T12:00:58.169Z","avatar_url":"https://github.com/eudoxia0.png","language":"Rust","funding_links":[],"categories":["Rust","others"],"sub_categories":[],"readme":"# hashcards\n\n[![Test](https://github.com/eudoxia0/hashcards/actions/workflows/test.yaml/badge.svg)](https://github.com/eudoxia0/hashcards/actions/workflows/test.yaml)\n[![Release](https://github.com/eudoxia0/hashcards/actions/workflows/release.yaml/badge.svg?branch=master)](https://github.com/eudoxia0/hashcards/actions/workflows/release.yaml)\n[![codecov](https://codecov.io/gh/eudoxia0/hashcards/branch/master/graph/badge.svg?token=GDV3CYZMHQ)](https://codecov.io/gh/eudoxia0/hashcards)\n[![dependency status](https://deps.rs/repo/github/eudoxia0/hashcards/status.svg)](https://deps.rs/repo/github/eudoxia0/hashcards)\n\n![Screenshot of the app, showing a front/back flashcard.](screenshot.webp)\n\nA plain text-based spaced repetition system. Features:\n\n- **Plain Text:** all your flashcards are stored as plain text files, so you can\n  operate on them with standard tools, write with your editor of choice, and\n  track changes in a VCS.\n- **Content Addressable:** cards are identified by the hash of their text. This\n  means a card's progress is reset when the card is edited.\n- **Low Friction:** you create flashcards by typing into a text file, using a\n  lightweight notation to denote flashcard sides and cloze deletions.\n- **Simple:** the only card types are front-back and cloze cards. More complex\n  workflows (e.g.: Anki-style note types, card templates, automation) be can\n  implemented using a Makefile and some scripts.\n- **Efficient:** uses [FSRS] for scheduling reviews, maximizing learning while\n  minimizing time spent reviewing.\n\nAnnouncement blog post: [Hashcards: A Plain-Text Spaced Repetition System][blog].\n\n## Example\n\nThe following Markdown file is a valid hashcards deck:\n\n```\nQ: How many neurons are there in the human brain?\nA: ~80 billion.\n\nC: An [agonist] is a ligand that binds to a receptor and [activates it].\n\nQ: How many synapses are there in a human brain?\nA: ~100 trillion\n\nC: In the nervous system, [chemical] communication happens [between] neurons.\n```\n\nFor a larger example, see [my personal flashcards repo][fc].\n\n## Installation\n\nYou can download a binary from the the [releases](https://github.com/eudoxia0/hashcards/releases) page.\n\nIf you have [cargo] installed, you can run:\n\n```\n$ cargo install hashcards\n```\n\nOn Arch Linux, you can install it from [AUR](https://aur.archlinux.org/packages/hashcards-bin).\n\n## Building\n\nYou need [cargo] installed. You can get it through [rustup]. Then:\n\n```\n$ git clone https://github.com/eudoxia0/hashcards.git\n$ cd hashcards\n$ make\n$ sudo make install\n```\n\nTo drill flashcards in a directory, run:\n\n```\n$ hashcards drill $DIRNAME\n```\n\n## Tutorial\n\nCreate a directory for your flashcards, and add a Markdown file with some cards:\n\n```bash\n$ mkdir cards\n$ cd cards\n$ cat \u003e Geography.md \u003c\u003c 'EOF'\nQ: What is Coulomb's constant?\nA: The proportionality constant of the electric force.\n\nQ: What is an object with zero net charge called?\nA: Neutral.\nEOF\n```\n\nA Markdown file is called a \"deck\", and the name of the file, sans extension, is\nthe name of the deck. This will be shown on top of the flashcard during reviews,\nthis saves you from having to specify the context in each of the flashcards.\n\nStart drilling:\n\n```bash\n$ hashcards drill\n```\n\nThis opens a web interface at `http://localhost:8000` where you can review your\ncards. The interface is simple: you read the question, mentally recall the\nanswer, and click reveal (or press space). Then you grade yourself on how you\ndid, with one of four choices:\n\n1. Forgot (shortcut: `1`)\n2. Hard (shortcut: `2`)\n3. Good (shortcut: `3`)\n4. Easy (shortcut: `4`)\n\nBe honest. If you got the answer almost right, press \"Forgot\". If you mis-grade\nsomething, you can undo (shortcut: `u`). The session ends when every card has\nbeen graded \"Good\" or higher. You can end the session prematurely by clicking\n\"End\", this will save your changes.\n\nTo learn how to write good flashcards, read [Effective Spaced Repetition][esr].\n\n## Commands\n\nThis section documents the hashcards command line interface.\n\n### `drill`\n\nStart a drilling session.\n\n```bash\n$ hashcards drill [DIRECTORY]\n```\n\nNote: your progress is not saved until the session ends, either when you run out\nof cards, or when you click \"End\".\n\nOptions:\n\n- `--card-limit=\u003cN\u003e`: Limit the session to at most N cards.\n- `--new-card-limit=\u003cN\u003e`: Limit the number of new cards in the session.\n- `--port=\u003cPORT\u003e`: Use a specific port (default: 8000).\n- `--from-deck=\u003cNAME\u003e`: Only drill cards from a deck with the given name.\n- `--open-browser=\u003ctrue|false\u003e`: Whether or not to open the browser after the\n  server starts (default: true).\n\n### `stats`\n\nPrint collection statistics to standard output.\n\n```bash\n$ hashcards stats [DIRECTORY]\n```\n\nOptions:\n\n- `--format=\u003cFORMAT\u003e`: Output format (`html` or `json`)\n\nAt present, only JSON output is supported.\n\n### `check`\n\nCheck the integrity of a collection.\n\n```bash\n$ hashcards check [DIRECTORY]\n```\n\n### `orphans`\n\nManage orphan cards (cards that exist in the database, but not in the\ncollection, i.e., cards that were deleted from the collection).\n\n```bash\n$ hashcards orphans list [DIRECTORY]\n$ hashcards orphans delete [DIRECTORY]\n```\n\nExample:\n\n```\n$ hashcards orphans list Cards\n04effc035b71692b66a90a622559479516526e7720c41afa22b29562915d58af\n059e4e0fd5c3d0ab7ef0cc902cdc402a555ec4152b842fe584109de6c8082ce3\n061b8c27e0f437d0c6ae735e829b39cc3bf0ad8218cb16387dcb4271c20b244d\n$ hashcards orphans delete Cards\n04effc035b71692b66a90a622559479516526e7720c41afa22b29562915d58af\n059e4e0fd5c3d0ab7ef0cc902cdc402a555ec4152b842fe584109de6c8082ce3\n061b8c27e0f437d0c6ae735e829b39cc3bf0ad8218cb16387dcb4271c20b244d\n$ hashcards orphans list Cards\n# no output\n```\n\n### `export`\n\nExport a collection to a JSON file.\n\n```bash\n$ hashcards export [DIRECTORY]\n```\n\nOptions:\n\n- `--output=\u003cPATH\u003e`: The path to the output. By default, the export is printed\n  to stdout.\n\n## Format\n\nThis section describes the text format used by hashcards.\n\n### Basic Cards\n\nQuestion-answer flashcards are written like this:\n\n```\nQ: What are the possible values of electric charge?\nA: Any integer multiple of the fundamental charge.\n```\n\nBoth the question and the answer can span multiple lines:\n\n```\nQ: List the elements of the Platinum group.\nA:\n\n- ruthenium\n- rhodium\n- palladium\n- osmium\n- iridium\n- platinum\n```\n\n### Cloze Cards\n\nCloze cards start with the `C:` tag, and use square brackets to denote cloze\ndeletions:\n\n```\nC: The [order] of a group is [the cardinality of its underlying set].\n```\n\nAgain, cloze cards can span multiple lines:\n\n```\nC:\nBetter is the sight of the eyes than the wandering of the\ndesire: this is also vanity and vexation of spirit.\n\n— [Ecclesiastes] [6]:[9]\n```\n\n## Features\n\nThis section documents specific hashcards features.\n\n### LaTeX Support\n\nCards support LaTeX math via KaTeX.\n\nUse `$...$` for inline math:\n\n```\nQ: What is the combinatorial meaning of $\\binom{n}{k}$?\nA: From a set of size $n$, we can choose $\\binom{n}{k}$ subsets of size $k$.\n```\n\nAnd `$$...$$` for display math:\n\n```\nC: The [amount of substance] of a sample, denoted $n$, is defined as:\n\n$$\nn = \\frac{N}{N_A}\n$$\n\nwhere $N$ is [the number of elementary entities] and $N_A$ is [Avogadro's constant].\n```\n\nYou can define custom LaTeX macros by creating a `macros.tex` file in your\ncollection root:\n\n```\n\\C \\mathbb{C}\n\\R \\mathbb{R}\n```\n\nMacro definitions can refer to arguments: `#1` for the first, `#2` for the\nsecond and so on.\n\n### Images\n\nOrdinary Markdown image syntax works:\n\n```\nQ: Identify this painting:\n\n![](art/diagram.png)\n\nA: _The Siren_, by John William Waterhouse.\n```\n\nBy default, image paths are resolved relative to the deck (the Markdown file)\nthat contains the flashcard. For example, if you have:\n\n```\ncards/\n  Art Theory/\n    Art.md\n    Images/\n      TheMermaid.jpg\n      Circe.jpg\n      Odysseus.jpg\n```\n\nThen flashcards in `Art.md` can reference images with paths like\n`Images/Circe.jpg`.\n\nBy prefixing a path with `@/`, you can point to images relative to the\ncollection root directory, e.g., a path like `@/Art Theory/Images/Circe.jpg`\nwill always resolve to the same path, even if the deck is moved around within\nthe collection.\n\n### Audio\n\nWorks like images:\n\n```\nQ: How do you pronounce \"پرنده\" in Persian?\nA: ![](audio/parande.mp3)\n```\n\n### Deck Names\n\nBy default, the filename of a deck is the name of a deck, e.g. a file\n`Medicine.md` will be parsed as a deck called `Medicine`. It is possible to\noverride the name using [TOML](https://toml.io/en/) frontmatter, like so:\n\n```\n---\nname = \"Medicine\"\n---\n\nC: The mitochondria is the [powerhouse] of the cell.\n```\n\nRegardless of the filename, cards in this deck will have `Medicine` as their\ndeck name. This is particularly useful when you want to organize a large number\nof cards into different files, while keeping their deck name the same. For\nexample, when taking notes from a textbook, you might have something like so:\n\n```\nPrinciples of Neural Science/\n  Ch1.md\n  Ch2.md\n  ...\n```\n\nBut you don't want the cards in those Markdown files to have `Ch1`, `Ch2`, etc.\nas their deck name. TOML frontmatter allows you to give each chapter deck the same\ndeck name.\n\n## Database\n\nhashcards stores card performance data and the review history in an SQLite3\ndatabase. The file is called `hashcards.db` and is found in the root of the card\ndirectory (i.e., the path you pass to the `drill` command).\n\nThe `cards` table has the following schema:\n\n| Column             | Type               | Description                                                                                                                         |\n|--------------------|--------------------|-------------------------------------------------------------------------------------------------------------------------------------|\n| `card_hash`        | `text primary key` | The hash of the card.                                                                                                               |\n| `added_at`         | `text not null`    | The timestamp when the card was first added to the database, in timestamp format.                                                   |\n| `last_reviewed_at` | `text`             | The timestamp when the card was most recently reviewed. `null` if the card is new.                                                  |\n| `stability`        | `real`             | The card's stability. `null` if the card is new.                                                                                    |\n| `difficulty`       | `real`             | The card's difficulty. `null` if the card is new.                                                                                   |\n| `interval_raw`     | `real`             | The FSRS-calculated interval, before rounding and clamping. A real number of days until the next review. `null` if the card is new. |\n| `interval_days`    | `real`             | The interval as an integer number of days, after rounding and clamping. `null` if the card is new.                                  |\n| `due_date`         | `text`             | The date when the card is next due, in `YYYY-MM-DD` format. `null` if the card is new.                                              |\n| `review_count`     | `integer not null` | The number of times the card has been reviewed.                                                                                     |\n\nThe `sessions` table has the following schema:\n\n| Column       | Type                  | Description                                                  |\n|--------------|-----------------------|--------------------------------------------------------------|\n| `session_id` | `integer primary key` | The ID of the session.                                       |\n| `started_at` | `text not null`       | The timestamp when the session started, in timestamp format. |\n| `ended_at`   | `text not null`       | The timestamp when the session ended, in timestamp format.   |\n\nThe `reviews` table has the following schema:\n\n| Column          | Type                  | Description                                                                                                                        |\n|-----------------|-----------------------|------------------------------------------------------------------------------------------------------------------------------------|\n| `review_id`     | `integer primary key` | The review ID.                                                                                                                     |\n| `session_id`    | `integer not null`    | The ID of the session this review was performed in, a foreign key.                                                                 |\n| `card_hash`     | `text not null`       | The hash of the card that was reviewed, a foreign key.                                                                             |\n| `reviewed_at`   | `text not null`       | The timestamp when the review was performed (i.e., when the user submitted a grade).                                               |\n| `grade`         | `text not null`       | One of `forgot`, `hard`, `good`, or `easy`.                                                                                        |\n| `stability`     | `real not null`       | The card's stability after this review.                                                                                            |\n| `difficulty`    | `real not null`       | The card's difficulty after this review.                                                                                           |\n| `interval_raw`  | `real`                | The FSRS-calculated interval, before rounding and clamping. A real number of days until the next review `null` if the card is new. |\n| `interval_days` | `real`                | The interval as an integer number of days, after rounding and clamping. `null` if the card is new.                                 |\n| `due_date`      | `text not null`       | The date, in the user's local time, when the card is next due, in `YYYY-MM-DD` format.                                             |\n\nNote: \"timestamp format\" is `YYYY-MM-DDTHH:MM:SS.MMM`, e.g. `2025-10-04T17:09:51.517`.\n\n## Prior Art\n\n- [org-fc](https://github.com/l3kn/org-fc)\n- [org-drill](https://orgmode.org/worg/org-contrib/org-drill.html)\n- [hascard](https://hackage.haskell.org/package/hascard)\n- [carddown](https://github.com/martintrojer/carddown)\n- [My implementation of a personal mnemonic medium](https://notes.andymatuschak.org/My_implementation_of_a_personal_mnemonic_medium)\n\n[FSRS]: https://github.com/open-spaced-repetition/fsrs4anki\n[blog]: https://borretti.me/article/hashcards-plain-text-spaced-repetition\n[cargo]: https://doc.rust-lang.org/cargo/\n[esr]: https://borretti.me/article/effective-spaced-repetition\n[fc]: https://github.com/eudoxia0/flashcards\n[rustup]: https://rustup.rs/\n\n## License\n\n© 2025 by [Fernando Borretti][fb]. Licensed under the [Apache 2.0][apache2] license.\n\n[fb]: https://borretti.me/\n[apache2]: https://www.apache.org/licenses/LICENSE-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feudoxia0%2Fhashcards","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feudoxia0%2Fhashcards","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feudoxia0%2Fhashcards/lists"}