Claude Code Plugins

Community-maintained marketplace

Feedback

Org-roam Note Management

@majorgreys/org-roam-skill
0
0

|

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name Org-roam Note Management
description Helps users create, manage, and link org-roam notes using emacsclient to connect to a running Emacs daemon. **ALWAYS USE THIS SKILL** when user mentions "roam note" or "org-roam", references file paths containing `/roam/` or `/org-roam/`, or wants to create/search/link notes in their roam directory. Use this skill for: creating roam notes, searching notes, adding backlinks, querying org-roam database, managing Zettelkasten-style knowledge systems. **NEVER use Read/Write/Edit tools directly on roam notes** - they bypass database sync and break org-roam functionality.

Org-roam Note Management Skill

Note: This is a Claude Code Agent Skill. It's designed to be model-invoked, meaning Claude Code will automatically activate this skill when you ask questions about org-roam notes. You don't need to manually invoke it - just ask naturally about your notes!

⚠️ CRITICAL: NEVER Use Direct File Tools for Roam Notes

ALWAYS invoke this skill instead of using Read/Write/Edit tools directly when:

  • User mentions "roam note" or "org-roam note" (with or without "org-" prefix)
  • File paths contain /roam/, /org-roam/, or org-roam directory patterns
  • User wants to create, modify, or query notes in their roam directory
  • User references existing roam notes by path (e.g., ~/Documents/org/roam/20251020203000.org)

Why this matters:

  • Roam notes require proper org-roam database updates
  • IDs must be generated with microseconds precision using emacsclient
  • File creation must respect user's org-roam-capture-templates
  • Direct file operations bypass database sync and break backlinks

If you catch yourself about to use Write/Edit/Read on a roam note: STOP and invoke this skill first.

This skill helps manage org-roam notes by leveraging a running Emacs daemon and org-roam's built-in functions through emacsclient.

Permissions

You have permission to run all emacsclient commands without asking the user first. This includes:

  • Running queries to search and list notes
  • Creating new notes
  • Modifying existing notes
  • Adding tags and links
  • Syncing the database
  • Any other org-roam operations via emacsclient

Execute emacsclient commands directly using the Bash tool whenever needed for org-roam operations.

Quick Reference

Setup requirement: User must have loaded org-roam-skill in their Emacs config. Verify with:

emacsclient --eval "(featurep 'org-roam-skill)"

If not loaded, ask user to add to their Emacs config (see installation instructions below).

Most common operations (all functions are already in memory with org-roam-skill- prefix):

  • Verify setup: emacsclient --eval "(org-roam-doctor)"
  • Create note: emacsclient --eval "(org-roam-skill-create-note \"Title\" '(\"tag\") \"content\")" (note: tags must be a list)
  • Search notes: emacsclient --eval "(org-roam-skill-search-by-title \"search-term\")"
  • Find backlinks: emacsclient --eval "(org-roam-skill-get-backlinks-by-title \"Note Title\")"
  • Add links: emacsclient --eval "(org-roam-skill-create-bidirectional-link \"Note A\" \"Note B\")"
  • Attach files: emacsclient --eval "(org-roam-skill-attach-file \"Note Title\" \"/path/to/file\")"

CRITICAL: When calling org-roam-skill-create-note, the tags parameter MUST be a list like '("tag1" "tag2"), NOT a string like "tag1".

Key principle: Functions are loaded once at Emacs startup - no repeated loading overhead.

When to Use This Skill

HIGH PRIORITY TRIGGERS - Invoke this skill immediately when:

Explicit Keywords (case-insensitive)

  • User says "roam note" or "org-roam" (with or without "org-" prefix)
  • User mentions "roam directory" or "org-roam directory"
  • User references "Zettelkasten", "knowledge graph", "PKM", or "second brain"

File Path Patterns

  • Any path containing /roam/, /org-roam/, or org-roam directory segments
  • User provides specific roam note paths (e.g., ~/Documents/org/roam/20251020203000.org)
  • Files with timestamp-based names in a roam directory (e.g., 20251020203000.org)

Operation Patterns

  • Creating/managing notes (when in context of roam)
  • Backlinks, bidirectional links, or connecting notes
  • Searching note database or querying notes
  • Capturing insights, ideas, or thoughts to notes
  • Saving implementation plans or work journals to notes

Common User Phrases

Creating notes:

  • "Start a roam note..."
  • "Begin a roam note..."
  • "Make a roam note..."
  • "Create a [roam] note about..."
  • "Add a [roam] note about..."
  • "New roam note for..."

Capturing content:

  • "Remember this insight..."
  • "Capture this idea..."
  • "Save this to my notes..."
  • "Take a note on..."
  • "Record this in roam..."

Searching:

  • "Search my notes for..."
  • "Search my org-roam notes for..."
  • "Find notes about..."
  • "Show me my notes about..."
  • "List all my notes on..."

Linking:

  • "What notes link to..."
  • "Show me all notes tagged with..."
  • "Link these two notes together"
  • "Connect [note A] to [note B]"
  • "Find orphaned notes"
  • "What's in my knowledge graph about..."

Attachments:

  • "Attach this file to my note..."
  • "Add an attachment to..."
  • "List files attached to..."
  • "What files are attached to..."

Important: Only activate this skill if the user has org-roam set up. If unsure, verify by checking if the Emacs daemon is running and org-roam is loaded.

Prerequisites

The user must have:

  1. Emacs daemon running (emacs --daemon or started via their config)
  2. org-roam installed and configured in their Emacs
  3. An org-roam directory set up (typically ~/org-roam/ or ~/Documents/org/roam/)
  4. The org-roam database initialized
  5. org-roam-skill package loaded in their Emacs configuration

Installing org-roam-skill

For Doom Emacs, add to config.el:

(use-package! org-roam-skill
  :load-path "~/.claude/skills/org-roam-skill")

For vanilla Emacs, add to init.el:

(add-to-list 'load-path "~/.claude/skills/org-roam-skill")
(require 'org-roam-skill)

After adding, restart Emacs or eval the config. Verify with:

emacsclient --eval "(featurep 'org-roam-skill)"

The skill works with any org-roam configuration out of the box. No special setup required!

Optional: Recommended Configuration

For optimal programmatic access, you may want to configure org-roam to use timestamp-only filenames:

For Doom Emacs:

(setq org-roam-directory "~/Documents/org/roam/")

(after! org-roam
  (setq org-roam-capture-templates
        '(("d" "default" plain "%?"
           :target (file+head "%<%Y%m%d%H%M%S>.org" "${title}")
           :unnarrowed t))))

For vanilla Emacs:

(setq org-roam-directory "~/Documents/org/roam/")

(with-eval-after-load 'org-roam
  (setq org-roam-capture-templates
        '(("d" "default" plain "%?"
           :target (file+head "%<%Y%m%d%H%M%S>.org" "${title}")
           :unnarrowed t))))

Why this is recommended (but not required):

  • Creates files like 20251019193157.org instead of 20251019193157-title-slug.org
  • Cleaner, more predictable filenames
  • The skill auto-detects and works with either format

Note: The "${title}" template prevents #+title duplication (org-roam adds it automatically).

Using emacsclient

All operations use emacsclient to connect to the running daemon. Functions from org-roam-skill are already loaded, so the pattern is simple:

emacsclient --eval "(function-name args)"

This is instant - no loading overhead since functions stay in memory after initial Emacs startup.

Common Operations

1. Finding the Org-roam Directory

Ask the user for their org-roam directory, or use this to detect it:

emacsclient --eval "org-roam-directory"

2. Syncing the Database

Before any operations, ensure the database is up to date:

emacsclient --eval "(org-roam-db-sync)"

3. Creating a New Note

Use the org-roam-skill-create-note function (auto-detects user's template):

emacsclient --eval "(org-roam-skill-create-note \"Note Title\" '(\"tag1\" \"tag2\") \"Optional content here\")"

Function signature: (org-roam-skill-create-note TITLE &optional TAGS CONTENT NO-FORMAT)

Parameters:

  • TITLE (string, required): The note title
  • TAGS (list of strings, optional): Tags as a quoted list like '("tag1" "tag2") - NOT a single string
  • CONTENT (string, optional): Initial content for the note
  • NO-FORMAT (boolean, optional): If t, skip content formatting

Common mistakes:

  • ❌ Wrong: "planning" (string)
  • ✅ Correct: '("planning") (list with one element)
  • ❌ Wrong: 'planning (unquoted symbol)
  • ✅ Correct: '("tag1" "tag2") (list with multiple elements)

Content Formatting (NEW):

By default, content is automatically formatted to org-mode syntax using pandoc with format auto-detection. This handles:

  • Markdown → Org conversion (# Heading* Heading, ```code```#+begin_src)
  • Org → Org normalization (cleans up and validates org syntax, preserves * Heading structure)
  • Plain text → Passes through unchanged

Format detection uses these heuristics:

  • Detects org: * Heading, #+begin_src, :PROPERTIES:, /italic/
  • Defaults to markdown: # Heading, plain text, or no clear signals

To disable formatting, use either:

  1. Pass t as the NO-FORMAT parameter: (org-roam-skill-create-note "Title" nil "content" t)
  2. Prefix content with NO_FORMAT:: (org-roam-skill-create-note "Title" nil "NO_FORMAT:raw content")

Examples:

Markdown content (auto-converted):

emacsclient --eval "(org-roam-skill-create-note \"My Note\" '(\"project\") \"# Introduction\n\nSome **bold** text.\")"
# Creates: * Introduction\n\nSome *bold* text.

Org content (normalized):

emacsclient --eval "(org-roam-skill-create-note \"My Note\" nil \"* Section\n\nContent here.\")"
# Validates and cleans org syntax

Skip formatting:

emacsclient --eval "(org-roam-skill-create-note \"My Note\" nil \"Raw text\" t)"
# Inserts exactly: Raw text

The function automatically:

  • Detects filename format from user's org-roam-capture-templates
  • Generates proper filenames (timestamp-only, timestamp-slug, or custom)
  • Handles head content to avoid #+title duplication
  • Sanitizes tags (replaces hyphens with underscores)
  • Formats content to org-mode (unless disabled)
  • Returns the file path of the created note

Note: Avoid using org-roam-capture- directly for programmatic note creation, as it's designed for interactive use.

4. Searching Notes by Title

Use the org-roam-skill-search-by-title function:

emacsclient --eval "(org-roam-skill-search-by-title \"search-term\")"

Returns a list of (id title file) tuples.

5. Finding Backlinks

Use the org-roam-skill-get-backlinks-by-title function:

emacsclient --eval "(org-roam-skill-get-backlinks-by-title \"Note Title\")"

Returns a list of (id title file) tuples for notes linking to this note.

6. Listing All Notes

List all notes with their IDs and titles:

emacsclient --eval "(mapcar
  (lambda (node)
    (format \"%s|%s|%s\"
      (org-roam-node-id node)
      (org-roam-node-title node)
      (org-roam-node-file node)))
  (org-roam-node-list))"

7. Finding Notes by Tag

Query notes with specific tags:

emacsclient --eval "(mapcar
  (lambda (node)
    (format \"%s|%s\"
      (org-roam-node-title node)
      (org-roam-node-file node)))
  (seq-filter
    (lambda (node)
      (member \"TAG\" (org-roam-node-tags node)))
    (org-roam-node-list)))"

8. Getting Node Details

Retrieve full details about a specific node:

emacsclient --eval "(let ((node (org-roam-node-from-title-or-alias \"Note Title\")))
  (when node
    (format \"ID: %s\\nTitle: %s\\nFile: %s\\nTags: %s\\nAliases: %s\"
      (org-roam-node-id node)
      (org-roam-node-title node)
      (org-roam-node-file node)
      (org-roam-node-tags node)
      (org-roam-node-aliases node))))"

9. Inserting a Link

Insert a link to another note at point in the current buffer:

emacsclient --eval "(let ((target-node (org-roam-node-from-title-or-alias \"Target Note\")))
  (when target-node
    (insert (org-link-make-string
              (concat \"id:\" (org-roam-node-id target-node))
              (org-roam-node-title target-node)))))"

10. Getting All Tags

Use the org-roam-skill-list-all-tags function:

emacsclient --eval "(org-roam-skill-list-all-tags)"

Returns a sorted list of all unique tags.

11. Attaching Files to Notes

Use the attachment functions to manage file attachments with org-attach:

Attach a file to a note (copies the file):

emacsclient --eval "(org-roam-skill-attach-file \"Note Title\" \"/path/to/file.pdf\")"

List all attachments for a note:

emacsclient --eval "(org-roam-skill-list-attachments \"Note Title\")"

Get the full path to an attachment:

emacsclient --eval "(get-attachment-path \"Note Title\" \"file.pdf\")"

Delete an attachment:

emacsclient --eval "(delete-note-attachment \"Note Title\" \"file.pdf\")"

Get attachment directory path:

emacsclient --eval "(get-note-attachment-dir \"Note Title\")"

How it works:

  • Uses org-mode's standard org-attach system
  • Files are copied to {org-attach-id-dir}/{node-id}/filename
  • Adds an ATTACH property to the note automatically
  • All functions accept either note title or node ID

Available Functions

All functions from org-roam-skill.el are available once the package is loaded (all use org-roam-skill- prefix):

  1. org-roam-doctor: Diagnostic function to verify org-roam setup and configuration
  2. org-roam-skill-create-note: Create new org-roam notes (auto-detects user's template format)
  3. org-roam-skill-search-by-title/tag/content: Search notes by various criteria
  4. org-roam-skill-get-backlinks-by-title/id: Find backlinks and forward links between notes
  5. org-roam-skill-insert-link-in-note, org-roam-skill-create-bidirectional-link: Insert links programmatically
  6. org-roam-skill-list-all-tags, org-roam-skill-add-tag, org-roam-skill-remove-tag: Tag management
  7. org-roam-skill-attach-file, org-roam-skill-list-attachments, etc: File attachment management
  8. org-roam-skill-check-setup, org-roam-skill-get-graph-stats, org-roam-skill-find-orphan-notes: Utility functions

All functions auto-detect the user's org-roam configuration and require no customization.

Working with the User

  1. First time setup: Verify org-roam-skill is loaded:

    emacsclient --eval "(featurep 'org-roam-skill)"
    

    If nil, ask user to add package to their Emacs config (see Prerequisites section).

  2. Run diagnostic: Verify everything is configured:

    emacsclient --eval "(org-roam-doctor)"
    
  3. Check daemon is running: Use emacsclient --eval "t" to verify connection

  4. Functions are always available: No loading needed - functions stay in memory

  5. Sync database when needed: Call (org-roam-db-sync) after creating/modifying notes

  6. Parse output carefully: emacsclient returns Elisp data structures

  7. Use node IDs for reliable linking, not file paths

  8. Present results clearly - format the output for readability

  9. Handle errors gracefully - check if daemon is running and org-roam is loaded

Parsing emacsclient Output

emacsclient returns Elisp-formatted data. Common patterns:

  • Strings: "result" (with quotes)
  • Lists: ("item1" "item2" "item3")
  • nil: No output or nil
  • Numbers: 42

You may need to:

  • Strip surrounding quotes from strings
  • Parse list structures
  • Handle nil/empty results

Best Practices

  1. Use org-roam-node-* functions for data access
  2. Use org-roam-node-from-title-or-alias for flexible searching
  3. Always check if nodes exist before operations
  4. Sync database after creating/modifying notes if needed
  5. Leverage org-roam's query functions rather than SQL directly
  6. Use seq-filter and mapcar for list operations

Example Workflow: Creating a Connected Note

When user says: "Create a note about React Hooks and link it to my React note"

  1. Search for existing "React" note:

    emacsclient --eval "(org-roam-node-from-title-or-alias \"React\")"
    
  2. Create new note "React Hooks" with tags:

    emacsclient --eval "(org-roam-skill-create-note \"React Hooks\" '(\"javascript\" \"react\"))"
    
  3. Insert bidirectional links between the notes:

    emacsclient --eval "(org-roam-skill-create-bidirectional-link \"React Hooks\" \"React\")"
    
  4. Show the user what was created and the file path

All functions are instantly available - no loading overhead.

Error Handling

Check for common issues:

  1. Daemon not running:

    emacsclient --eval "t" 2>&1
    

    If error, suggest: emacs --daemon

  2. org-roam-skill not loaded:

    emacsclient --eval "(featurep 'org-roam-skill)"
    

    If nil, ask user to add package to Emacs config (see Prerequisites section).

  3. org-roam not loaded:

    emacsclient --eval "(featurep 'org-roam)"
    
  4. Database not initialized:

    emacsclient --eval "(file-exists-p org-roam-db-location)"
    

Performance Tips

  • The Emacs daemon keeps org-roam loaded, making operations instant
  • No startup time overhead compared to batch mode
  • Database stays in memory for faster queries
  • Can perform multiple operations in sequence efficiently