Skip to main content

Muninn

Muninn (雾尼) - An Extensible Reciting CLI.

中文文档

What is Muninn?

Muninn is a highly extensible CLI application designed to help you memorize anything. Instead of hardcoding questions, Muninn relies on a Plugin Architecture. You can install "Reciting Packs" created by others (like chemistry elements, GRE vocabulary, or historical events) or develop your own packs using Python.

Muninn acts as a "host" that provides:

  1. A Smart Scheduling Algorithm (focuses on your weak-points).
  2. Persistent State Management (remembers your progress across sessions).
  3. A clean, distraction-free Terminal UI.

Installation & Usage

Install Muninn globally using uv (recommended) or pip:

# Using uv (Recommended)
uv tool install muninn-cli

# Or using pip
pip install muninn-cli

Available commands:

# List all installed packs
muninn list

# Install a pack from a directory, zip file, or GitHub URL
muninn install path/to/pack_or_zip
# e.g. muninn install example/chemistry

# Install from GitHub (any public repo with a manifest.json)
muninn install user/repo
muninn install user/repo@v1.0.0
muninn install https://github.com/user/repo
# e.g. muninn install a1fredbao/muninn-chemistry-plugin

# Uninstall a previously installed pack
muninn uninstall <pack_id>

# Run a specific pack by ID
muninn run <pack_id>

# Generate a new plugin template for development
muninn new <your_new_pack_id>

Plugin Development Guide

Muninn provides a layered API. Choose the level that fits your needs.

Quickstart: FlashcardPlugin (zero boilerplate)

For simple front/back flashcards (e.g. GRE words), create a CSV or JSON file with front and back columns, then write a 3-line plugin:

from core.helpers import FlashcardPlugin


class Plugin(FlashcardPlugin):
    DATA_FILE = "words.csv"

That's it. FlashcardPlugin handles rendering, answer-checking, and ID generation automatically.

File Description
manifest.json Pack metadata (name, author, version).
words.csv Data with front and back columns.
plugin.py The 3-line plugin above.

For structured data: DataPlugin + QuestionType

When each data record can be quizzed from multiple angles, use DataPlugin. Declare your question types and let Muninn generate all problem variants automatically.

Example — Chemistry elements quizzed from 4 directions (symbol → name, name → number, etc.):

import os, json
from core.helpers import DataPlugin, QuestionType, Matchers


class Plugin(DataPlugin):
    QUESTION_TYPES = [
        QuestionType(
            label="看序号背元素",
            statement=lambda el: f"原子序数: {el['num']}",
            answer=lambda el: f"{el['name']} {el['sym']}",
            matcher=Matchers.chinese_symbol_pair("name", "sym"),
        ),
        QuestionType(
            label="看元素背序号",
            statement=lambda el: f"元素: {el['name']} ({el['sym']})",
            answer=lambda el: str(el["num"]),
            matcher=Matchers.exact_integer("num"),
        ),
    ]

    def load_records(self) -> list:
        with open(
            os.path.join(self.workspace_dir, "elements.json"), encoding="utf-8"
        ) as f:
            return json.load(f)

    def filter(self, record, q_type):
        # Optional: skip certain question types for specific records
        return True

DataPlugin auto-generates problem IDs ({record_index}__{question_label}) and routes all five interface methods. You only supply data + question types.

Built-in Matchers

Instead of writing custom regex for every question type, use the built-in Matchers factories:

Matcher Behavior
Matchers.exact(key) Exact match after trimming whitespace.
Matchers.exact_integer(key) Extract digits, compare numerically.
Matchers.case_insensitive(key) Case-insensitive match.
Matchers.chinese_symbol_pair(key1, key2) Match "中文+符号" or "符号+中文" in any order.
Matchers.any_order(*keys) Match all field values appearing anywhere in the input.
Matchers.custom(fn) Pass your own (record, user_input) -> bool function.

Low-level: BaseRecitePlugin

For full control, implement the base interface directly:

from core.base_plugin import BaseRecitePlugin


class Plugin(BaseRecitePlugin):
    def load_data(self):
        # Load static data from self.workspace_dir
        pass

    def get_all_problem_ids(self) -> list[str]:
        """Return all unique problem IDs."""
        pass

    def render_statement(self, problem_id: str) -> str:
        """Return the question text to display."""
        pass

    def check_answer(self, problem_id: str, user_input: str) -> bool:
        """Return True if correct."""
        pass

    def get_expected_display(self, problem_id: str) -> str:
        """Return the correct answer to show on failure."""
        pass

    def get_expand_info(self, problem_id: str) -> str:
        """Optional: Return extra info to show on success."""
        return ""

Developing a pack from scratch

  1. Generate a template:

    muninn new my_cool_pack
    

    This creates a my_cool_pack/ directory with manifest.json and a skeleton plugin.py.

  2. Write your logic using one of the approaches above.

  3. Install and test:

    muninn install ./my_cool_pack
    muninn run my_cool_pack
    

Release files for muninn-cli 0.2.0

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

Source distribution (sdist)

Source distribution for muninn-cli 0.2.0
File Size Uploaded
muninn_cli-0.2.0.tar.gz 35.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for muninn-cli 0.2.0
File Interpreter ABI Platform
muninn_cli-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 51.9 kB

Release files / muninn_cli-0.2.0.tar.gz

Download URL muninn_cli-0.2.0.tar.gz
Size 35.6 kB
Tags Source
SHA-256 checksum
How to use checksums
b1429eae49bf37b9842cd54eada8f07c718a29ec3ed3c203da670a9db0e780e9
BLAKE2b-256 checksum
How to use checksums
9f65c6ae69c51e62438ffb7829f94dc922293c620d45983e65c34c15489e4b94
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","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 / muninn_cli-0.2.0-py3-none-any.whl

Download URL muninn_cli-0.2.0-py3-none-any.whl
Size 16.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
81a164bb827237fb2c5ece2740af3fcfb3b9099db7d1f9f87c8e9fa74c1bb775
BLAKE2b-256 checksum
How to use checksums
7992d1d3ef3c417f2b61fbb23991224dc57f3273d516929a2c557294f800b8c3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","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

1.0.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.1

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