diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-31 12:19:34 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-31 12:19:34 -0500 |
| commit | ddf48dc7ac780da1aacdff4e03f1d7da255b8f39 (patch) | |
| tree | 99926b681a9ea6d4210d0dcd1bd8e8a6d47d7d9e /.ai/scripts/flashcard-sync | |
| parent | b46619cd17ed4e36f2e59c1b600078521b2049ef (diff) | |
| download | rulesets-ddf48dc7ac780da1aacdff4e03f1d7da255b8f39.tar.gz rulesets-ddf48dc7ac780da1aacdff4e03f1d7da255b8f39.zip | |
feat: add rename-ai-artifact tool and rename the drill-deck family to flashcard
Renaming an .ai artifact by hand is the kind of mechanical job that gets done incompletely: the canonical copy moves but the mirror doesn't, a reference in the INDEX is missed, a trigger phrase points at the old name. I'd also assumed a rename was costly because references scatter, when the index update is trivial and the drift check already guards it. So I built the discipline into a script instead of re-deriving it each time.
scripts/rename-ai-artifact.sh takes old and new basenames, moves the file in both the canonical and mirror trees, and rewrites every reference repo-wide on a token boundary so renaming "foo" can't corrupt "foobar" or "foo-bar". It rewrites the underscore module-name variant too (a hyphenated script imported as foo_bar via importlib), leaves the archived session records under sessions/ alone because they're history, and runs workflow-integrity + sync-check at the end to prove no drift. rename-artifact.org documents it and indexes the triggers.
Then I used the tool to do the rename that prompted it: the org-drill deck workflow and its helpers are now flashcard-named, since "flashcard" is the word you'd actually search for. The renamed set is flashcard-review.org plus flashcard-stats.py, flashcard-sync, flashcard-to-anki.py, and flashcard-diff-ids.py, with their tests, every reference, and the INDEX entry updated. The deck is still an org-drill deck under the hood, so the ":drill:" tag handling and the "drill deck" trigger phrases stay. I added "review/update the flashcards" alongside them.
Tests: 9 bats for the rename tool (including the prefix-collision and history-preservation edges), and the renamed script suites all pass under make test.
Diffstat (limited to '.ai/scripts/flashcard-sync')
| -rwxr-xr-x | .ai/scripts/flashcard-sync | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/.ai/scripts/flashcard-sync b/.ai/scripts/flashcard-sync new file mode 100755 index 0000000..f5ba7fb --- /dev/null +++ b/.ai/scripts/flashcard-sync @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# flashcard-sync: stats check + regenerate Anki apkg + place at ~/sync/phone/anki/ +# +# Wraps flashcard-stats.py + flashcard-to-anki.py (and optionally +# flashcard-diff-ids.py) for the canonical "rewrote the deck, now ship +# it" step in the flashcard-review workflow. +# +# Usage: +# flashcard-sync <source.org> +# flashcard-sync <source.org> --diff-against <previous-version.org> +# +# Exits non-zero when the stats check warns, when --diff-against shows +# any disappeared / appeared IDs, or when flashcard-to-anki.py fails. The +# Anki apkg is not written when any gate fails. + +set -euo pipefail + +usage() { + cat >&2 <<'EOF' +usage: flashcard-sync <source.org> [--diff-against <previous-version.org>] +EOF + exit 2 +} + +if [[ $# -lt 1 ]]; then + usage +fi + +SOURCE="$1" +shift + +DIFF_AGAINST="" +while [[ $# -gt 0 ]]; do + case "$1" in + --diff-against) + [[ $# -ge 2 ]] || usage + DIFF_AGAINST="$2" + shift 2 + ;; + -h|--help) + usage + ;; + *) + echo "unknown arg: $1" >&2 + usage + ;; + esac +done + +if [[ ! -f "$SOURCE" ]]; then + echo "error: $SOURCE not found" >&2 + exit 2 +fi + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +STATS="$SCRIPT_DIR/flashcard-stats.py" +DIFF_IDS="$SCRIPT_DIR/flashcard-diff-ids.py" +TO_ANKI="$SCRIPT_DIR/flashcard-to-anki.py" + +for helper in "$STATS" "$DIFF_IDS" "$TO_ANKI"; do + if [[ ! -f "$helper" ]]; then + echo "error: helper $helper not found" >&2 + exit 2 + fi +done + +echo "=== flashcard-sync: $SOURCE ===" +echo +echo "--- stats ---" +if ! python3 "$STATS" "$SOURCE"; then + echo + echo "stats check failed — fix warnings before sync, or call flashcard-to-anki.py directly to override" >&2 + exit 1 +fi +echo + +if [[ -n "$DIFF_AGAINST" ]]; then + if [[ ! -f "$DIFF_AGAINST" ]]; then + echo "error: $DIFF_AGAINST not found" >&2 + exit 2 + fi + echo "--- ID preservation ---" + if ! python3 "$DIFF_IDS" "$DIFF_AGAINST" "$SOURCE"; then + echo + echo "ID preservation check failed — SRS state may have been lost" >&2 + exit 1 + fi + echo +fi + +BASENAME="$(basename "$SOURCE" .org)" +OUTPUT="$HOME/sync/phone/anki/${BASENAME}.apkg" + +echo "--- regenerate apkg ---" +mkdir -p "$(dirname "$OUTPUT")" +"$TO_ANKI" "$SOURCE" --output "$OUTPUT" +echo +echo "deck synced to $OUTPUT" |
