Material and card management for OpenAlgernon. Use when the user runs any of: `/algernon install`, `list`, `info`, `update`, `remove`, `import`, `audio`, `ingest`, or says "instalar material", "listar materiais", "gerar cards", "instalar novo conteudo", "remover material", "importar PDF", "gerar podcast NotebookLM", or "criar flashcards para [material]". Also handles card generation (all three modes: generate new cards, promote N1/N2/N3, create correction cards).
You handle all material management and card operations for OpenAlgernon. This covers installing and removing materials, generating cards at all depth levels, and creating NotebookLM audio scripts.
ALGERNON_HOME="${ALGERNON_HOME:-$HOME/.openalgernon}"
DB="${ALGERNON_HOME}/data/study.db"
MATERIALS="${ALGERNON_HOME}/materials"
sqlite3 "$DB" \
"SELECT slug, name, author, version, installed_at
FROM materials ORDER BY installed_at DESC;"
Format as a readable list. If empty: "No materials installed yet."
sqlite3 "$DB" \
"SELECT slug, name, author, version, repo_url, local_path, installed_at
FROM materials WHERE slug = 'SLUG';"
git clone --depth 1 https://github.com/ORG/REPO "$MATERIALS/SLUG"
$MATERIALS/SLUG/algernon.yaml for: name, author, version, content files,
card distribution, focus concepts.sqlite3 "$DB" \
"INSERT INTO materials (slug, name, author, version, repo_url, local_path, algernonspec)
VALUES ('SLUG','NAME','AUTHOR','VERSION',
'https://github.com/ORG/REPO',
'$MATERIALS/SLUG',
'v1');"
$ALGERNON_HOME/files/PATH (supports .md, .txt, .pdf).LOCAL_PATH=$(sqlite3 "$DB" "SELECT local_path FROM materials WHERE slug = 'SLUG';")
git -C "$LOCAL_PATH" pull --ff-only
Confirm: "Material SLUG updated."
Confirm with user before proceeding (this cannot be undone).
If confirmed:
LOCAL_PATH=$(sqlite3 "$DB" "SELECT local_path FROM materials WHERE slug = 'SLUG';")
sqlite3 "$DB" "DELETE FROM materials WHERE slug = 'SLUG';"
rm -rf "$LOCAL_PATH"
(The DELETE cascades to decks, cards, card_state, and reviews via foreign keys.)
Read LOCAL_PATH/algernon.yaml to get:
content: list of content filescards.distribution: [flashcard%, dissertative%, argumentative%] (default [50, 30, 20])cards.focus: list of priority conceptsFor a default batch of 20 cards:
Card rules:
[N1].cards.focus.Create a deck first:
sqlite3 "$DB" \
"INSERT INTO decks (material_id, name, topic)
VALUES (MATERIAL_ID, 'DECK_NAME', 'TOPIC');
SELECT last_insert_rowid();"
Insert each card and its initial card_state:
sqlite3 "$DB" \
"INSERT INTO cards (deck_id, type, front, back, tags, source_file, source_title)
VALUES (DECK_ID, 'TYPE', 'FRONT', 'BACK', '[\"[N1]\",\"TAG\"]', 'FILE', 'TITLE');
INSERT INTO card_state (card_id, due_date)
VALUES (last_insert_rowid(), date('now'));"
Escape all single quotes in card text as '' (two single quotes) for SQLite.
Input: card_id, target_level
Read the original card, then generate a deeper version:
sqlite3 "$DB" \
"INSERT INTO cards (deck_id, type, front, back, tags, source_file, source_title)
VALUES (DECK_ID, 'TYPE', 'FRONT', 'DEEP_BACK', '[\"[N2]\",\"TAG\"]', 'FILE', 'TITLE');
INSERT INTO card_state (card_id, due_date)
VALUES (last_insert_rowid(), date('now'));"
Input: deck_id, misconception, correct_explanation
sqlite3 "$DB" \
"INSERT INTO cards (deck_id, type, front, back, tags)
VALUES (DECK_ID, 'flashcard',
'CORRECTION: MISCONCEPTION_AS_QUESTION',
'CORRECT_EXPLANATION',
'[\"[correction]\",\"[N1]\"]');
INSERT INTO card_state (card_id, due_date)
VALUES (last_insert_rowid(), date('now'));"
Generates a podcast-style learning script from the material.
HOST A: [question or framing]
HOST B: [explanation using N1 definitions]
HOST A: [follow-up or analogy request]
HOST B: [deeper explanation or example]
...
$MATERIALS/SLUG/podcast-script.md.$ALGERNON_HOME/files/PATH).--title flag.$MATERIALS/SLUG/content/01-main.md.algernon.yaml pointing at the content file.