link-suggestions
Use when user wants wikilink suggestions for a note, wants to find connections between notes, or asks to improve linking in rough notes or source material
Install
mkdir -p .claude/skills/link-suggestions && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/13650" && unzip -o skill.zip -d .claude/skills/link-suggestions && rm skill.zipInstalls to .claude/skills/link-suggestions
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use when user wants wikilink suggestions for a note, wants to find connections between notes, or asks to improve linking in rough notes or source materialAbout this skill
Link Suggestions
Suggest wikilinks for any note based on vault content. Searches Main Note titles, tag notes, and note content to find meaningful connections. Presents ranked suggestions with context for where to add links.
When to Use
Use when user:
- Asks for link suggestions: "suggest links for this note" or "what should I link?"
- Wants to improve connections: "find related notes" or "link this rough note"
- Requests batch linking: "link all my rough notes" or "suggest links for recent notes"
- Is writing/editing a note and wants connection ideas
Invocation Pattern
/link-suggestions
# User then provides:
# - Note name or path (optional): "Rough - 2026-01-15 - RT-2 deployment"
# - Batch mode: --batch-rough (all rough notes from last 7 days)
# - Conservative mode: --conservative (only high-confidence)
# - If no note specified → ask user which note to analyze
Workflow
Step 1: Identify Target Note
1. If note name/path provided → locate it
2. If --batch-rough → collect rough notes (Step 7)
3. If nothing specified → ask user:
"Which note should I suggest links for?
Provide note name or path."
Locate the note:
# Try exact match first
target = Glob("**/{note_name}.md")
# If not found, try partial match
if not target:
target = Glob("**/*{note_name}*.md")
# If multiple matches, ask user to pick
if len(target) > 1:
"Found multiple matches:"
- list each
"Which one?"
Step 2: Read and Parse Target Note
note_content = Read(target_path)
# Extract:
# 1. Existing wikilinks (don't re-suggest these)
existing_links = find_all("[[...]]" patterns in note_content)
# 2. Key terms and phrases (for matching)
# - Multi-word noun phrases (2-4 words)
# - Technical terms, proper nouns
# - Capitalized phrases
# - Quoted terms
# Skip stopwords: the, a, is, are, was, with, for, on, in, to, of, and, that, this
# 3. Count existing wikilinks
link_count = len(existing_links)
# If link_count >= 8, warn: "Note already has 8+ links. Suggest anyway? [y/n]"
Step 3: Build Vault Index (cache for batch)
Load searchable vault content (do this ONCE per session, reuse for batch):
# 1. Main Note titles (highest value matches)
main_notes = Glob("06 Main Notes/*.md")
main_note_titles = [filename without .md for each]
# 2. Tag note titles
tag_notes = Glob("03 Tags/*.md")
tag_note_titles = [filename without .md for each]
# 3. Source Material titles (lower priority)
source_notes = Glob("02 Source Material/**/*.md")
source_note_titles = [filename without .md for each]
Step 4: Score Candidates
For each key term/phrase extracted from the target note, score against vault:
candidate_scores = {}
# IMPORTANT: Exclude the target note itself from all candidate lists
# Remove target_title from main_note_titles before scoring
for term in key_terms:
term_lower = term.lower()
# Check Main Note titles (+10 for exact match in title)
for title in main_note_titles:
if term_lower in title.lower():
candidate_scores[title] = candidate_scores.get(title, 0) + 10
# Check Tag note titles (+8 for tag match)
for tag in tag_note_titles:
if term_lower == tag.lower() or term_lower in tag.lower().split('-'):
candidate_scores[tag] = candidate_scores.get(tag, 0) + 8
# Key term extraction guidance:
# - Multi-word phrases (2-4 words) are preferred: "domain randomization", "friction coefficients"
# - Compound phrases containing a filtered word ARE valid if the full phrase is meaningful:
# "policy serving" ✅, "policy" alone ❌
# - Only extract EXPLICIT terms from the text, not implied concepts
# - When in doubt, prefer precision over recall
# For top candidates (score >= 5), do deeper search:
for candidate_name in top_candidates:
# Read candidate note content
candidate_content = Read(candidate_path)
# Check if target note's key terms appear in candidate (+5)
for term in key_terms:
if term.lower() in candidate_content.lower():
candidate_scores[candidate_name] += 5
# Check tag overlap between target and candidate (+2 per shared tag)
shared_tags = target_tags & candidate_tags
candidate_scores[candidate_name] += 2 * len(shared_tags)
# Remove already-linked notes (-100, effectively removes)
for existing_link in existing_links:
if existing_link in candidate_scores:
candidate_scores[existing_link] -= 100
# Remove reverse links (-100, absolute prohibition)
# If candidate already links TO target, don't suggest target→candidate
# Check Related Ideas section of top candidates
for candidate_name in top_candidates:
if target_title in candidate_content: # target note name appears in candidate
candidate_scores[candidate_name] -= 100
Confidence levels:
- High (score >= 10): Strong match - title match or multiple content matches
- Medium (score 5-9): Probable match - content or tag overlap
- Low (score < 5): Weak match - skip unless
--allflag
Step 5: Contextualize Suggestions
For each suggestion (max 5 unless --all), identify WHERE in the note to add the link:
# Find the line in target note where the term appears
# Show surrounding context
🔗 Link Suggestions for "{note_name}"
High confidence:
1. Line {N}: "{...matching text...}"
→ Add: [[{Suggested Note}]]
Reason: {Why this is relevant - e.g., "Exact title match", "Discusses same concept"}
2. Line {M}: "{...matching text...}"
→ Add: [[{Suggested Note}]]
Reason: {explanation}
Medium confidence:
3. Line {P}: "{...matching text...}"
→ Add: [[{Suggested Note}]]
Reason: {explanation}
---
Options:
1. Apply all high-confidence links ({N})
2. Review each suggestion individually
3. Apply all suggestions ({M} total)
4. Cancel
Step 6: Apply Links
When user approves suggestions:
For each approved link:
# Read current note content
current = Read(target_path)
# Find the exact line/phrase to modify
# Choose placement strategy:
# OPTION A: Inline link - use when the exact note title appears in the text
# Before: "tried deploying RT-2 on warehouse robot"
# After: "tried deploying RT-2 on warehouse robot → see [[VLA Policy Serving Architecture]]"
# OPTION B: Parenthetical - use when a key term matches but not the exact title
# Before: "GPU memory issues on 80GB A100"
# After: "GPU memory issues on 80GB A100 ([[GPU Architecture Compatibility Patterns]])"
# OPTION C: Related Ideas section - use when the connection is conceptual
# (no specific line in the body matches well)
# Append: "- [[Suggested Note]] - {brief context}"
# Decision heuristic:
# 1. Does the exact note title appear in the text? → OPTION A
# 2. Does a key term match a specific line? → OPTION B
# 3. Connection is conceptual / no good inline spot? → OPTION C
# Show diff preview before applying:
"Preview:"
- {old line}
+ {new line}
"Apply? [y/n]"
Edit(target_path, old=old_line, new=new_line)
IMPORTANT: After each Edit, re-read the file before making another Edit.
Step 6b: Suggest New Main Notes
If a key concept appears 3+ times in the note with no existing Main Note:
💡 Potential New Main Note:
You mention "{concept}" {N} times in this note.
No Main Note exists for this concept.
Consider: /extract-main-note "{concept}"
Step 7: Batch Mode (--batch-rough)
Process rough notes from last 7 days:
# Find recent rough notes
rough_notes = Glob("01 Rough Notes/Rough - *.md")
# Filter by date in filename (last 7 days)
# Or use --days N to customize
# Build vault index ONCE (Step 3)
# Process each note:
📝 Batch Link Suggestions ({N} rough notes)
{Note 1 name}
→ {X} high-confidence, {Y} medium-confidence suggestions
{Note 2 name}
→ {X} high-confidence, {Y} medium-confidence suggestions
...
Total: {N} high-confidence, {M} medium-confidence
Options:
1. Apply all high-confidence links
2. Review note by note
3. Cancel
Step 8: Final Summary
✅ Link Suggestions Applied
📄 Note: {note_name}
🔗 Links added: {N}
- {N} high-confidence
- {N} medium-confidence (user-approved)
Changes:
✏️ {note_name}: added {N} wikilinks
💡 Suggested {N} potential Main Notes for extraction
Next steps:
1. Review added links in Obsidian
2. Consider extracting suggested concepts:
→ /extract-main-note "{concept}"
Critical Requirements
DO NOT:
- Suggest links already present in the note
- Suggest more than 5 links per note (unless
--all) - Suggest links if note already has 8+ wikilinks (warn first)
- Auto-apply without showing diff preview
- Suggest recursive links (if B already links to A, don't suggest A→B)
- Suggest links to the note itself
ALWAYS:
- Show WHERE in the note to add each link (line number + context)
- Show WHY each suggestion is relevant (reason)
- Present diff preview before applying changes
- Group by confidence level (high/medium)
- Build vault index once and reuse for batch mode
- Use wikilinks:
[[Note Name]]not[Note Name]
Scoring Reference
| Signal | Points |
|---|---|
| Exact term in Main Note title | +10 |
| Exact term in Tag note title | +8 |
| Term in candidate note content | +5 |
| Synonym/variant match | +3 |
| Shared tag between notes | +2 per tag |
| Already linked in current note | -100 |
| Reverse link exists (candidate → target) | -100 |
Edge Cases
Over-linked note (8+ existing links):
⚠️ This note already has {N} wikilinks.
Adding more may reduce readability.
Suggest anyway? [y/n]
No suggestions found:
ℹ️ No link suggestions found for this note.
The note may be well-linked already, or cover topics not yet in the vault.
Consider: /extract-main-note to create Main Notes for new concepts.
Already well-linked (most candidates eliminated by reverse links):
ℹ️ This note is already well-connected.
Most related notes already link to it:
- [[{Note A}]] ↔ bidirectional
- [[{Note B}]] ↔ bidirectio
---
*Content truncated.*