Obsidian-vault-agent recall
git clone https://github.com/tuan3w/obsidian-vault-agent
T=$(mktemp -d) && git clone --depth=1 https://github.com/tuan3w/obsidian-vault-agent "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/recall" ~/.claude/skills/tuan3w-obsidian-vault-agent-recall && rm -rf "$T"
skills/recall/SKILL.md<Use_When>
- User wants a review session ("what should I review?", "quiz me", "recall practice")
- User wants to resurface notes they haven't seen in a while
- User wants to triage their inbox
- User wants to discover connections across domains
- User asks "what do I remember about X?"
- Periodic knowledge maintenance (daily, weekly reviews) </Use_When>
<Do_Not_Use_When>
- User wants to deeply process a specific note (use /process)
- User wants vault structure analysis (use /vault-graph)
- User wants to synthesize across a cluster (use /synthesize)
- User is actively reading or adding a new source note </Do_Not_Use_When>
<Execution_Policy>
- INTERACTIVE — never skip ahead. Each card requires the user to respond before the answer is shown. The friction IS the learning.
- Never show note content before the user attempts recall
- Prefer notes with updated_date older than 14 days (spacing effect)
- MUST interleave domains — draw from at least 3 different topic folders
- Use MCP tools (search_notes, get_frontmatter) when available; fall back to Grep/Glob
- Session size: default 5 retrieval cards, 2 inbox items, 1 surprise connection (adjust to user's argument if provided)
- After each reveal, offer to update the note if it's stale </Execution_Policy>
Stage 1: BUILD THE QUEUE
1a. Retrieval Practice Cards (default 3–5)
Candidate selection using MCP:
search_notes(query="processing_status: processed OR processing_status: evergreen", limit=50) search_notes(query="type: term OR type: note", limit=50)
Or fall back to Grep:
Grep(pattern="processing_status: (processed|evergreen)", path="notes/", glob="*.md") Grep(pattern="^type: (term|note)$", path="notes/", glob="*.md")
Filter and rank candidates:
- Prefer notes with
older than 14 days — compute from today (2026-03-20)updated_date - Prefer notes with the
anchor (marked for spaced repetition)[ ](#anki-card) - Prefer notes with
overprocessing_status: processed
(evergreen notes are maintained; processed ones need re-testing)evergreen - Enforce domain interleaving: collect the topic folder for each candidate
(
,notes/ml/
,notes/psychology/
, etc.) and ensure the final selection spans at least 3 different foldersnotes/startup/ - If fewer than 3 domains are available, accept 2 minimum
Read the frontmatter to confirm
updated_date and type:
get_frontmatter(path="notes/PATH/filename.md")
1b. Inbox Triage Items (1–2)
Find the oldest unprocessed source notes:
search_notes(query="processing_status: inbox", limit=20)
Or:
Grep(pattern="processing_status: inbox", path="notes/", glob="*.md")
Filter to types:
paper, post, book, lecture, course.
Sort by created_date ascending — oldest first.
Pick 1–2.
1c. Surprise Connection (1)
Goal: find two notes from DIFFERENT domain folders that share a concept keyword but have NO wikilink between them.
Algorithm:
- Pick a retrieval card already in the queue (it has topic keywords in its body)
- Extract 2–3 key concept words from that note's title or content
- Search a DIFFERENT domain folder for notes containing those keywords:
Grep(pattern="KEYWORD", path="notes/OTHER_DOMAIN/", glob="*.md") - Check that the two notes do NOT link to each other:
Grep(pattern="\\[\\[.*TITLE.*\\]\\]", path="notes/SOURCE_NOTE.md") - If no match, try a second keyword or a second candidate note
- Present both notes with the shared concept as the "bridge"
Non-obvious pairings to prioritize (cross-domain > within-domain):
- ml/ ↔ psychology/ (learning algorithms ↔ cognitive science)
- startup/ ↔ design/ (product strategy ↔ UX)
- finance/ ↔ ml/ (optimization ↔ market dynamics)
- psychology/ ↔ startup/ (behavioral economics ↔ business)
- computer science/ ↔ logic/ (formal systems ↔ reasoning)
Stage 2: PRESENT QUEUE OVERVIEW
Before starting, show the session plan:
Review session — [N] cards queued Retrieval practice ([count]): 1. [Note title] — [domain] 2. [Note title] — [domain] ... Inbox triage ([count]): • [Note title] ([type], [age] days old) ... Surprise connection: [teaser — e.g., "a bridge between ML and psychology"] Ready? I'll show you one card at a time. Type anything to start, or "skip [N]" to skip a card.
Stage 3: RUN THE SESSION
For each retrieval practice card:
Turn 1 — Prompt recall:
───────────────────────────────────── Card [N/total] · [domain folder] [[ (Type) Note Title ]] What do you remember about this concept? (Type your answer, or "skip" to reveal) ─────────────────────────────────────
Wait for user response.
Turn 2 — Reveal: Read the full note content:
read_note(path="notes/PATH/filename.md")
Or:
Read(file_path="/absolute/path/to/note.md")
Show the note content (truncate at 400 words if very long — show key sections).
Then ask:
How did you do? Is this note still accurate? [u] Update note [n] Next card [e] Evergreen (promote status)
If user wants to update: help them edit inline, then update
updated_date
in frontmatter using update_frontmatter.
If user types "e" or "evergreen": update
processing_status to evergreen.
For each inbox triage item:
───────────────────────────────────── Inbox triage · [type] · [age] days old [[ (Type) Note Title ]]
Read the note, show the first 5–7 bullets (or Abstract section for papers).
Then ask:
Worth processing deeply, or archive? [p] Process (I'll run /process next) [a] Archive [s] Skip for now
→ note it as a recommendation; optionally updatepprocessing_status: processing
→ updateaprocessing_status: archived
→ leave unchangeds
For the surprise connection:
───────────────────────────────────── Surprise connection These two notes share the concept "[KEYWORD]" but don't link to each other: [[ (Type) Note A ]] (domain: X) [[ (Type) Note B ]] (domain: Y) [Brief excerpt from each showing the shared concept] Do you see a meaningful connection? What would the link say? ─────────────────────────────────────
Wait for user response. If they see a connection:
- Help them decide which note gets the wikilink (or both)
- Offer to add the link:
into Note A's Related links section[[ (Type) Note B ]] - Update
on both notesupdated_date
Stage 4: SESSION SUMMARY
───────────────────────────────────── Session complete Reviewed: [N] cards Updated: [N] notes Promoted to evergreen: [list] Archived: [list] Queued for /process: [list] New connections made: [count] Oldest unreviewed term: [title] ([N] days) ─────────────────────────────────────
Optionally suggest: "Run /process on [oldest inbox item] next."
</Steps> <Examples> <Good> User: "review session" 1. Build queue: 4 term notes (ml/, psychology/, startup/, finance/), 2 inbox papers, 1 surprise 2. Show overview → user starts 3. Card 1: "[[ (Term) Attention Mechanism ]]" — user recalls, sees note, updates a stale bullet 4. Card 2: "[[ (Term) Loss Aversion ]]" — user blanks, reads note, promotes to evergreen 5. Inbox: "(Paper) Chinchilla Scaling Laws" — 60 days old → user says archive 6. Surprise: "(Term) Gradient Descent" ↔ "(Term) Operant Conditioning" — shared keyword "reward signal" → user says "oh, both use incremental feedback to converge on a target behavior" → adds link 7. Summary: 6 reviewed, 2 updated, 1 evergreen, 1 archived, 1 connection made </Good> <Good> User: "quiz me on 3 cards" → Session with 3 retrieval cards (not 5), 1 inbox item, 1 surprise connection </Good> <Bad> Showing note content immediately without prompting recall first. → This destroys the retrieval practice effect. ALWAYS show title first, wait for response. </Bad> <Bad> Picking 4 cards all from notes/ml/. → Violates interleaving. Domains must be mixed. If ml/ only has enough candidates, pull from any other folder before repeating a domain. </Bad> </Examples><Escalation_And_Stop_Conditions>
- Not enough processed notes (<3): Inform user, offer to run /process first on top inbox items
- All notes recently reviewed (updated within 7 days): Report "vault is fresh — nothing due for spacing"
- No inbox items: Skip inbox triage stage, replace with an extra retrieval card
- Surprise connection not found after 3 attempts: Skip it, note "no unlinked cross-domain pairs found today"
- User types "stop" or "quit": End session immediately, show partial summary
- MCP unavailable: Fall back to Grep/Glob for all searches; behavior is identical </Escalation_And_Stop_Conditions>
$ARGUMENTS