From learning-loop
Checks Obsidian vault health for ghost duplicates, near-duplicates, orphans, stale inbox, embedding gaps, broken wikilinks. Light mode default; --deep uses note-scorer; --auto fixes safe issues; --provenance for observability.
npx claudepluginhub robinslange/learning-loop --plugin learning-loopThis skill uses the workspace's default tool permissions.
Quick-check command that surfaces vault hygiene issues: ghost duplicates, near-duplicate pairs, orphan notes, stale inbox entries, embedding gaps, and broken wikilinks. Fast by default, deep on demand, with optional auto-fix for safe operations.
Guides Next.js Cache Components and Partial Prerendering (PPR): 'use cache' directives, cacheLife(), cacheTag(), revalidateTag() for caching, invalidation, static/dynamic optimization. Auto-activates on cacheComponents: true.
Processes PDFs: extracts text/tables/images, merges/splits/rotates pages, adds watermarks, creates/fills forms, encrypts/decrypts, OCRs scans. Activates on PDF mentions or output requests.
Share bugs, ideas, or general feedback.
Quick-check command that surfaces vault hygiene issues: ghost duplicates, near-duplicate pairs, orphan notes, stale inbox entries, embedding gaps, and broken wikilinks. Fast by default, deep on demand, with optional auto-fix for safe operations.
/health or /health --light: quick vault status check (default)/health --deep: full diagnostic with note-scorer analysis/health --auto: auto-fix safe issues (combinable with either mode)/reflect sessions/inbox to see what needs attention| Input | Mode | Auto-fix |
|---|---|---|
/health | light | no |
/health --light | light | no |
/health --deep | deep | no |
/health --auto | light | yes |
/health --deep --auto | deep | yes |
/health --provenance | provenance | no |
/health --librarian | librarian | no |
Use AskUserQuestion when no arguments are provided to help users discover modes.
No arguments (/health):
Run light mode immediately (fast, no prompting needed: it's the default and completes in seconds).
But after presenting results, if issues were found, mention available modes:
Found N issues. Options:
/health --deep: full analysis with note scoring/health --auto: auto-fix safe issues (ghost dupes, broken links)/health --provenance: pipeline observability (fabrication rates, agent stats)/health --deep --auto: both
This teaches the modes through use rather than upfront prompting.
If --provenance flag is present, skip all vault health checks and run:
node PLUGIN/scripts/provenance-report.mjs
Display the output directly.
If the report includes a Recommendations section, present each recommendation and ask:
Which of these would you like to act on? Options:
- "N" to act on recommendation N
- "pattern N" to create a learned pattern from recommendation N
- "all" to review all recommendations
- "done" to finish
When user selects "pattern N", draft a positive behavior-based pattern following the format in PLUGIN_DATA/provenance/learned-patterns.md (where PLUGIN_DATA = CLAUDE_PLUGIN_DATA env or ~/.claude/plugins/data/learning-loop) and present for approval before writing.
After the local report, check for peer provenance data:
PLUGIN_DATA/federation/provenance-peers.json (where PLUGIN_DATA = CLAUDE_PLUGIN_DATA env or ~/.claude/plugins/data/learning-loop)Network (last 7 days):
peer-a: 12 sessions, 47 notes, 3 fixes
peer-b: 5 sessions, 12 notes, 1 fix
vault-search.mjs sync to fetch."Then stop (do not proceed to Step 1).
--librarian)If --librarian flag is present, skip all vault health checks and enter librarian review mode.
Step L1: Load Queue
Read PLUGIN_DATA/librarian/queue.jsonl. Parse all lines. Filter to status === 'pending'. If no pending items, report "No pending librarian observations." and stop.
Step L2: Phase 1: Advisory Review
Group pending items into link suggestions, tag suggestions, voice flags, and duplicate flags. Each subsection is independent: present and resolve one at a time.
Link suggestions: Present in a table grouped by confidence:
High confidence (N):
Orphan → Suggested link Reason
3-permanent/cadences-are-harmonic... → 3-permanent/chord-progressions-are... Both discuss harmonic function
...
Review (N):
Orphan → Suggested link Reason
...
Ask user: "Apply approved links? Enter numbers to approve (e.g., '1,3,5'), 'all-high' for all high-confidence, or 'skip'."
For each approved link suggestion:
\n\n[[suggested-note-slug]] to the note body using Edit toolapprovedFor rejected items, update status to rejected.
Tag suggestions: Present as a table:
Tag suggestions (N):
Target Existing tags Suggested tags
3-permanent/ginkgo-biloba-acute-pk... nootropic pharmacology, neuroscience
...
Ask user: "Apply tag suggestions? Enter numbers, 'all', or 'skip'."
For each approved tag suggestion:
suggested_tags into the existing tags: list (dedupe)approvedFor rejected items, update status to rejected.
Voice flags: Present as a list:
Voice flags (N):
0-inbox/gmail-multi-daemon-pull-dedup... "gmail multi daemon pull deduplication": Names a topic without stating a claim
...
These are advisory: present them for awareness. Ask: "Acknowledge voice flags? (y/n)": on yes, update all to acknowledged.
Duplicate flags: Present as a list:
Duplicate flags (N):
1. 0-inbox/foo-claim.md ↔ 3-permanent/foo-claim-original.md (similarity 0.93)
...
For each, ask the user to choose one of: merge (read both, decide which to keep, the user does the merge), link (add a wikilink between them: drop a [[other]] reference into the newer note's body), dismiss. Update queue item status to merged, linked, or dismissed.
Step L3: Phase 2: Staleness Suspects
Present staleness suspects:
Staleness suspects (N):
3-permanent/react-compiler-memoizes... 90 days old, matched: v1.0, October 2025
...
Ask: "Investigate staleness suspects? Enter numbers (e.g., '1,2'), 'all', or 'skip'."
For each selected suspect, Claude (the active model) investigates using available tools:
After investigation, ask user what to do with each: "update", "dismiss", or "flag for /deepen".
Step L4: Cleanup
After both phases:
node -e "import('./scripts/lib/librarian-queue.mjs').then(m => m.expireStaleItems('VAULT_PATH'))"node -e "import('./scripts/lib/librarian-queue.mjs').then(m => m.resetState())"Then stop (do not proceed to Step 1).
Collect the raw data needed for all checks. Run these in parallel:
Glob pattern *.md in {{VAULT}}/0-inbox/Glob pattern *.md in {{VAULT}}/1-fleeting/Glob pattern *.md in {{VAULT}}/3-permanent/Glob pattern *.md in {{VAULT}}/2-literature/Glob pattern *.md in {{VAULT}}/_system/node PLUGIN/scripts/vault-search.mjs cluster --threshold 0.85node PLUGIN/scripts/vault-search.mjs listnode PLUGIN/scripts/check-deps.mjsll-search binary via node -e "import { binaryVersion } from 'PLUGIN/scripts/lib/binary.mjs'; console.log(binaryVersion());" -- returns version string or nullParse the check-deps output from Step 1.
For each dependency:
This check runs in both light and deep modes -- there's no deeper analysis needed.
Compare inbox filenames against filenames in 1-fleeting/ and 3-permanent/. A ghost duplicate exists when the same filename appears in inbox AND a promoted folder.
Light: List each ghost duplicate with its promoted location. Deep: Read both versions of each ghost duplicate. If content is identical or the inbox version is a subset, confirm as true duplicate. If content has diverged, flag as "diverged copy: review before deleting".
Parse the cluster output from Step 1. Filter to pairs with similarity > 0.85 that are NOT ghost duplicates (same filename in different folders: already caught in Step 2).
Light: List each pair with similarity score. Deep: Read both notes in each pair. Compare content. Recommend which to keep (prefer the more mature version) or merge.
For each note across all content folders (0-inbox, 1-fleeting, 3-permanent), grep for \[\[ outgoing wikilinks. Notes with zero outgoing links are orphans. Exclude _system/ and 2-literature/ from orphan checks (system docs and literature notes don't need outlinks).
Light: List orphan filenames with their folder.
Deep: For each orphan, run node PLUGIN/scripts/vault-search.mjs similar "<note-path>" --top 3 to suggest link targets.
For each inbox note, check file modification time using Bash: node -e "console.log(require('fs').statSync('FILE').mtimeMs)". Flag notes older than 14 days.
Light: List stale notes with age in days.
Deep: Launch note-scorer agent(s) with stale note paths. Report maturity tier and recommend action: promote (if deep/medium), /deepen (if shallow but promising), or delete candidate (if shallow and empty).
Batching: If > 10 stale notes, split into batches of ~10 and launch parallel note-scorer agents.
Compare the full vault file list (all .md files in content folders) against the output of vault-search.mjs list. Notes present in the vault but missing from the embedding index are stale.
Light and Deep: List missing notes. No difference between modes: there's nothing deeper to analyze.
Grep all \[\[...\]\] wikilink references across all vault notes. For each unique link target, check if a matching .md file exists anywhere in the vault (case-insensitive filename match). Broken links are references to non-existent notes.
Light: List each broken link with the source note that contains it. Deep: For each broken link, find the closest matching vault filename using fuzzy/substring match and suggest it as a correction.
Read PLUGIN_DATA/librarian/queue.jsonl (where PLUGIN_DATA = CLAUDE_PLUGIN_DATA env or ~/.claude/plugins/data/learning-loop). Parse each line as JSON. Filter to items where status === 'pending'. Also read PLUGIN_DATA/librarian/state.json for visited count.
If the queue file doesn't exist or is empty, skip this step silently.
Group pending items by task field:
link_suggestion: link suggestions (orphan notes that should be linked)tag_suggestion: tag suggestions (under-tagged notes with proposed vocabulary tags)voice_flag: voice flags (topic-style titles)duplicate_flag: duplicate flags (notes that make the same claim as a near-neighbour)staleness_suspect: staleness suspects (Claude investigates)Add to the dashboard output:
Librarian: N pending observations (visited M/T notes)
Link suggestions: N (X high, Y review)
Tag suggestions: N
Voice flags: N
Duplicate flags: N
Staleness suspects: N (for Claude to investigate)
Queue: P% full (N/CAP cap)
Where CAP is read from config.json's librarian.queue_cap (default 200).
If the queue has pending items, add recommendation:
Run
/health --librarianto review and act on librarian suggestions.
Output the summary dashboard:
Vault Health: YYYY-MM-DD
Binary: ll-search vX.Y.Z (installed) | not installed
Dependencies: N satisfied, M missing
Ghost dupes: N inbox notes already promoted
Near-dupes: N pairs (>0.85 similarity)
Orphans: N notes with no outlinks
Stale inbox: N notes older than 14 days
Embeddings: N notes not indexed
Broken links: N dead [[wikilinks]]
Status: [total] issues [run /health --deep for full analysis]
The "run --deep" hint only appears in light mode. In deep mode, replace with a summary of findings.
Then output per-category details:
If --auto flag is set:
Bash: rm {{VAULT}}/0-inbox/<filename>[[ and ]] brackets from broken wikilinks using Edit tool, leaving the display text as plain text (e.g., [[missing-note]] becomes missing-note, [[missing|displayed]] becomes displayed)If --auto flag is NOT set:
/inbox, /verify, /deepen, or "re-index in Obsidian")Output a one-line summary of actions taken:
Fixed: N ghost dupes removed, N broken links cleaned. Remaining: N issues: see recommendations above.
If nothing was fixed (no --auto, user declined, or nothing fixable):
No fixes applied. N issues found: see recommendations above.
Reuses the note-scorer agent. Only invoked for stale inbox notes in --deep mode.
Launch pattern:
Spawn note-scorer subagent(s) (subagent_type: learning-loop:note-scorer) with this prompt:
Score these notes for inbox staleness assessment.
notes: <file-path-1>, <file-path-2>, ...
vault_path: {{VAULT}}/
scope: stale inbox triage (health --deep)
Return per-note: dimension scores + maturity tier (shallow/medium/deep) + specific issues found.
Batching: One agent per ~10 notes. Parallel for larger sets.
Output contract: Each agent returns a list of objects:
- file: <path>
tier: shallow | medium | deep
issues: [<string>, ...]
gate: N/6
claim_specificity: 0-2
source_grounded: 0-2
Mapping gate pass count to summary labels (for dashboard display):
--deep, give them the full picture. Use note-scorer, read content, diff duplicates.--auto only touches ghost dupes (inbox copy of promoted note) and broken links (references to nothing). Never auto-merge, auto-delete non-duplicate notes, or auto-promote./verify, /inbox, or /deepen. Recommend the right tool for each issue.0-inbox/ without asking. Broken link fixes edit the source note, which may be in any folder: always ask unless --auto.