aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-30 12:40:02 -0500
committerCraig Jennings <c@cjennings.net>2026-05-30 12:40:02 -0500
commit80e76cb91e645758d53755d2b7fbe94426d45a06 (patch)
treeb55945297dcd8fb9782d39683f03fdc8ec782a57 /.ai/scripts
parente3059241c61bd97e837f218f29eb657f02c2cddb (diff)
downloadrulesets-80e76cb91e645758d53755d2b7fbe94426d45a06.tar.gz
rulesets-80e76cb91e645758d53755d2b7fbe94426d45a06.zip
feat(workflows): add drill-deck-review + extend drill-to-anki script
I added a drill-deck-review workflow that walks an org-drill deck end-to-end: question-form audit on every heading (so the heading is the prompt, not the answer), content-accuracy audit via subagent against project source-of-truth, source rewrite preserving SRS state, regenerate to ~/sync/phone/anki/. The workflow covers three card families (acronym/concept, person, talking-point) and codifies the person-card pattern as "Who is X? Tell me about their Y." where X is a role descriptor that doesn't name the person and Y is the topical anchor from the answer body. The drill-to-anki.py script picks up a new strip_org_metadata helper that drops :PROPERTIES: drawers and SCHEDULED/DEADLINE/CLOSED planning lines from the rendered Anki back. Org-drill needs both in the source for SRS state and review scheduling; Anki cards shouldn't show them. INDEX entry under "On-demand utilities" wires the trigger phrases.
Diffstat (limited to '.ai/scripts')
-rwxr-xr-x.ai/scripts/drill-to-anki.py27
1 files changed, 27 insertions, 0 deletions
diff --git a/.ai/scripts/drill-to-anki.py b/.ai/scripts/drill-to-anki.py
index 50e1afd..543ccd8 100755
--- a/.ai/scripts/drill-to-anki.py
+++ b/.ai/scripts/drill-to-anki.py
@@ -100,6 +100,32 @@ def title_from_org(org_text: str) -> str | None:
return None
+def strip_org_metadata(body_lines: list[str]) -> list[str]:
+ """Drop :PROPERTIES: drawers and SCHEDULED/DEADLINE/CLOSED planning lines.
+
+ Org-drill needs these in the source file (SRS state lives in the
+ PROPERTIES drawer; SCHEDULED carries the next-review date), but they
+ are noise on the back of an Anki card.
+ """
+ cleaned: list[str] = []
+ in_drawer = False
+ planning_re = re.compile(r"^\s*(SCHEDULED|DEADLINE|CLOSED):\s")
+ drawer_start_re = re.compile(r"^\s*:PROPERTIES:\s*$")
+ drawer_end_re = re.compile(r"^\s*:END:\s*$")
+ for line in body_lines:
+ if in_drawer:
+ if drawer_end_re.match(line):
+ in_drawer = False
+ continue
+ if drawer_start_re.match(line):
+ in_drawer = True
+ continue
+ if planning_re.match(line):
+ continue
+ cleaned.append(line)
+ return cleaned
+
+
def parse(org_text: str) -> list[tuple[str, str, str]]:
"""Return [(front, back_html, tag), ...] for every :drill: card."""
cards: list[tuple[str, str, str]] = []
@@ -130,6 +156,7 @@ def parse(org_text: str) -> list[tuple[str, str, str]]:
break
body_lines.append(nxt)
i += 1
+ body_lines = strip_org_metadata(body_lines)
while body_lines and not body_lines[0].strip():
body_lines.pop(0)
while body_lines and not body_lines[-1].strip():