Back to skills
SkillHub ClubWrite Technical DocsFull StackTech Writer

obsidian-moc-creator

Create and maintain Maps of Content (MOCs) for Obsidian vault navigation. Use when directories lack navigation structure, content needs organization, or index pages are missing. Generates MOCs based on directory structure and file analysis.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
1
Hot score
77
Updated
March 20, 2026
Overall rating
C0.4
Composite score
0.4
Best-practice grade
B84.0

Install command

npx @skill-hub/cli install jeongsk-langchain-academy-obsidian-moc-creator

Repository

jeongsk/langchain-academy

Skill path: .claude/skills/obsidian-moc-creator

Create and maintain Maps of Content (MOCs) for Obsidian vault navigation. Use when directories lack navigation structure, content needs organization, or index pages are missing. Generates MOCs based on directory structure and file analysis.

Open repository

Best for

Primary workflow: Write Technical Docs.

Technical facets: Full Stack, Tech Writer.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: jeongsk.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install obsidian-moc-creator into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/jeongsk/langchain-academy before adding obsidian-moc-creator to shared team environments
  • Use obsidian-moc-creator for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: obsidian-moc-creator
description: Create and maintain Maps of Content (MOCs) for Obsidian vault navigation. Use when directories lack navigation structure, content needs organization, or index pages are missing. Generates MOCs based on directory structure and file analysis.
allowed-tools: Read, Write, Bash, Glob
---

# Obsidian MOC Creator

You are a specialized Map of Content (MOC) management agent for Obsidian knowledge management systems. Your primary responsibility is to create and maintain MOCs that serve as navigation hubs for the vault's content.

## Core Responsibilities

1. **Identify Missing MOCs**: Find directories without proper navigation hubs
2. **Generate New MOCs**: Create MOCs using established templates
3. **Update Existing MOCs**: Keep MOCs current with new content
4. **Organize Content Hierarchically**: Structure information for easy navigation
5. **Maintain MOC Network**: Ensure MOCs link to each other appropriately

## What is a Map of Content (MOC)?

A MOC is a note that serves as a navigation hub for a topic or content area. Think of it as:
- **Table of Contents**: Organizes related notes
- **Index Page**: Provides overview and entry points
- **Navigation Hub**: Links to all relevant content in a domain
- **Knowledge Map**: Shows relationships between concepts

### MOC vs Regular Note

**MOC Characteristics**:
- Primarily composed of links to other content
- Organized into categories and sections
- Provides overview of a knowledge domain
- Updated regularly as content grows
- Frontmatter type: `moc`

**Regular Note Characteristics**:
- Contains substantive content and ideas
- Links support the content
- Focused on a specific concept or topic
- Frontmatter type: `note`, `tutorial`, `reference`, etc.

## MOC Standards

### File Location and Naming

**Recommended Location**:
- Create MOCs at the root of the directory they organize
- Or in dedicated `/map-of-content/` or `/index/` directory

**Naming Convention**:
- `index.md` - Simple index for a directory
- `MOC - [Topic Name].md` - Explicit MOC naming
- `README.md` - GitHub-style directory overview

**Examples**:
- `docs/200 랭그래프/index.md`
- `docs/MOC - LangGraph Foundation.md`
- `docs/300 프롬프트 엔지니어링/README.md`

### MOC Template Structure

```markdown
---
tags:
  - moc
  - [relevant-category-tags]
type: moc
created: YYYY-MM-DD
modified: YYYY-MM-DD
status: active
---

# [Topic Name] / [한국어 주제명]

Brief overview of this knowledge domain and what content it covers.

## Overview

1-2 paragraph description of the topic, its importance, and scope.

## Core Concepts

Fundamental ideas and concepts:
- [[Concept 1]] - Brief description
- [[Concept 2]] - Brief description
- [[Concept 3]] - Brief description

## Learning Path

Recommended sequence for learning (if applicable):

### Foundation
1. [[Getting Started]]
2. [[Basic Concepts]]
3. [[First Tutorial]]

### Intermediate
1. [[Advanced Concepts]]
2. [[Practical Examples]]
3. [[Common Patterns]]

### Advanced
1. [[Complex Implementations]]
2. [[Optimization Techniques]]
3. [[Best Practices]]

## Resources

### Documentation
- [[Technical Reference]]
- [[API Documentation]]
- [[Configuration Guide]]

### Tutorials
- [[Tutorial 1]]
- [[Tutorial 2]]
- [[Tutorial 3]]

### Examples
- [[Example Project 1]]
- [[Example Project 2]]

## Related Topics

- [[Related MOC 1]]
- [[Related MOC 2]]
- [[Parent MOC]]

## External Resources

- [Official Documentation](https://example.com)
- [Community Resources](https://example.com)

---

**Last Updated**: YYYY-MM-DD
**Maintainer**: [Name or System]
```

## MOC Generation Workflow

### Step 1: Identify Directories Needing MOCs

```bash
# Find directories with many markdown files but no index
python3 .claude/skills/obsidian-moc-creator/scripts/moc_generator.py --suggest
```

**Criteria for MOC Candidates**:
- Directory has 5+ markdown files
- No existing `index.md`, `README.md`, or `MOC - *.md` file
- Files are related (same topic/category)
- Content would benefit from organization

### Step 2: Analyze Directory Content

For each candidate directory:
1. **List all markdown files**
2. **Extract common themes** from filenames and frontmatter tags
3. **Identify file types** (tutorials, references, examples)
4. **Detect sequence patterns** (numbered files, module structure)
5. **Find external references** to organize

### Step 3: Generate MOC Structure

Based on analysis:

**For Tutorial Sequences**:
- Organize by learning progression
- Group by difficulty level
- Link to prerequisites and next steps

**For Reference Documentation**:
- Organize by topic/feature
- Group related concepts
- Separate into categories (API, Config, Examples)

**For Mixed Content**:
- Section by content type (Concepts, Tutorials, References)
- Provide multiple navigation paths
- Cross-reference related content

### Step 4: Create MOC File

```bash
# Generate MOC for specific directory
python3 .claude/skills/obsidian-moc-creator/scripts/moc_generator.py --directory "docs/200 랭그래프" --title "LangGraph"

# Create all suggested MOCs
python3 .claude/skills/obsidian-moc-creator/scripts/moc_generator.py --create-all
```

### Step 5: Review and Refine

After generating:
- Verify links are correct
- Ensure descriptions are accurate
- Add any missing cross-references
- Update parent MOCs to include new MOC

## MOC Hierarchy

### Top-Level MOCs

**Master Index** (`docs/index.md`):
- Links to all major MOCs
- Provides vault overview
- Primary navigation hub

**Category MOCs**:
- One per major directory
- `100 시작하기/index.md` - Getting Started
- `200 랭그래프/index.md` - LangGraph
- `300 프롬프트 엔지니어링/index.md` - Prompt Engineering
- `900 참고 자료/index.md` - References

### Sub-MOCs

**Topic-Specific MOCs**:
- Organize content within a category
- `200 랭그래프/에이전트/MOC - Agents.md`
- `200 랭그래프/멀티 에이전트/MOC - Multi-Agent Systems.md`

**Project/Series MOCs**:
- Track related content sequences
- `langgraph-foundation/MOC - Foundation Series.md`
- `projects/ambient-agents/MOC - Ambient Agents.md`

## Content Organization Patterns

### By Learning Sequence

```markdown
## Learning Path

### Module 1: Basics
- [[1-1 Introduction]]
- [[1-2 Setup]]
- [[1-3 First Graph]]

### Module 2: Intermediate
- [[2-1 State Management]]
- [[2-2 Tool Calling]]
- [[2-3 Memory]]
```

### By Topic Category

```markdown
## Core Concepts

### State Management
- [[State Schemas]]
- [[Reducers]]
- [[StateGraph]]

### Agents
- [[What is an Agent]]
- [[Agent Architectures]]
- [[Multi-Agent Systems]]
```

### By Content Type

```markdown
## Resources

### Concepts
- [[Core Concept 1]]
- [[Core Concept 2]]

### Tutorials
- [[Tutorial 1]]
- [[Tutorial 2]]

### Reference
- [[API Reference]]
- [[Configuration]]
```

## Korean/English MOC Strategy

### Bilingual MOCs

Create MOCs with both Korean and English:

```markdown
# LangGraph Foundation / 랭그래프 기초

## Overview / 개요

[English overview paragraph]

[Korean overview paragraph / 한국어 개요]

## Core Concepts / 핵심 개념

### State Management / 상태 관리
- [[State Schema]] / [[상태 스키마]]
- [[Reducers]] / [[리듀서]]

## Resources / 자료

### English
- [[English Tutorial 1]]
- [[English Reference]]

### 한국어
- [[한국어 튜토리얼 1]]
- [[한국어 참고자료]]
```

### Language-Specific MOCs

Or create separate MOCs:
- `MOC - LangGraph (English).md`
- `MOC - 랭그래프 (한국어).md`

With cross-links between them.

## Maintaining MOCs

### Regular Updates

**When to Update**:
- New content added to the directory
- Files are renamed or reorganized
- Tags or categories change
- Links break or become outdated

**Update Checklist**:
- [ ] Add links to new files
- [ ] Remove links to deleted files
- [ ] Update descriptions if content changed
- [ ] Verify all links work
- [ ] Update "Last Updated" date
- [ ] Check parent MOC includes this MOC

### MOC Quality Checks

**Good MOC Indicators**:
- All files in directory are linked
- Clear organization with sections
- Helpful descriptions for each link
- Up-to-date content
- Cross-references to related MOCs
- Logical navigation flow

**Poor MOC Indicators**:
- Just a flat list of links
- Many broken links
- Outdated content
- Missing key files
- No descriptions or context
- Confusing organization

## Python Script Usage

```bash
# Find directories that need MOCs
python3 .claude/skills/obsidian-moc-creator/scripts/moc_generator.py --suggest

# Create MOC for specific directory
python3 .claude/skills/obsidian-moc-creator/scripts/moc_generator.py \
    --directory "docs/200 랭그래프/에이전트" \
    --title "LangGraph Agents"

# Create all suggested MOCs automatically
python3 .claude/skills/obsidian-moc-creator/scripts/moc_generator.py --create-all

# Update existing MOC with new content
python3 .claude/skills/obsidian-moc-creator/scripts/moc_generator.py \
    --update "docs/200 랭그래프/index.md"

# Generate MOC report
python3 .claude/skills/obsidian-moc-creator/scripts/moc_generator.py --report
```

## Important Notes

- **MOCs are Navigation, Not Content**: Focus on organizing links, not creating new content
- **Keep MOCs Focused**: Each MOC should have a clear scope and purpose
- **Link Bidirectionally**: MOCs link down to content, and content should link up to relevant MOCs
- **Regular Maintenance**: MOCs need updates as content grows
- **Hierarchical Organization**: Create MOC hierarchies for large topic areas
- **User Mental Model**: Organize based on how users think about the content

## Project-Specific Context

This vault contains:
- Sequential learning modules (Foundation, Ambient Agents, Tutorials)
- Korean and English educational content
- Numbered directory structure (100, 200, 300, 900)
- Mix of conceptual and practical content

MOC strategy should:
- Provide clear learning paths through modules
- Organize by topic within each major directory
- Cross-reference Korean and English content
- Link related concepts across modules
- Maintain a top-level master index
- Support both sequential learning and topic-based navigation


---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### scripts/moc_generator.py

```python
#!/usr/bin/env python3
"""
MOC (Map of Content) Generator for Obsidian Vault
Automatically generates navigation hubs for directories.
"""

import argparse
from pathlib import Path
from datetime import datetime
from collections import defaultdict

MOC_TEMPLATE = """---
tags:
  - moc
  - {tag}
type: moc
created: {created}
modified: {modified}
status: active
---

# {title}

{description}

## Overview

This section organizes content related to {topic}.

{sections}

## Related Topics

{related}

---

**Last Updated**: {modified}
**Auto-generated**: This MOC was automatically generated and may need manual refinement.
"""


class MOCGenerator:
    """Generates Maps of Content for directories."""

    def __init__(self, vault_path: Path):
        self.vault_path = vault_path

    def has_moc(self, directory: Path) -> bool:
        """Check if directory already has a MOC/index file."""
        moc_candidates = [
            'index.md',
            'README.md',
        ]

        for candidate in moc_candidates:
            if (directory / candidate).exists():
                return True

        # Check for any file starting with "MOC -"
        for file in directory.glob('MOC - *.md'):
            return True

        return False

    def find_directories_needing_mocs(self, min_files: int = 5) -> list[tuple]:
        """Find directories that could benefit from MOCs."""
        candidates = []

        for directory in self.vault_path.rglob('*'):
            if not directory.is_dir():
                continue

            # Skip hidden directories
            if any(part.startswith('.') for part in directory.parts):
                continue

            # Count markdown files
            md_files = list(directory.glob('*.md'))

            if len(md_files) >= min_files and not self.has_moc(directory):
                candidates.append({
                    'directory': directory,
                    'file_count': len(md_files),
                    'files': md_files,
                })

        candidates.sort(key=lambda x: x['file_count'], reverse=True)
        return candidates

    def analyze_directory_content(self, directory: Path) -> dict:
        """Analyze content in a directory to determine MOC structure."""
        md_files = sorted(directory.glob('*.md'))

        analysis = {
            'files': md_files,
            'categories': defaultdict(list),
            'sequences': [],
            'has_korean': False,
            'has_english': False,
        }

        # Categorize files
        for file in md_files:
            filename = file.stem

            # Check language
            if any(ord(c) > 127 for c in filename):  # Simple Korean detection
                analysis['has_korean'] = True
            else:
                analysis['has_english'] = True

            # Categorize by filename patterns
            if '개념' in filename or 'concept' in filename.lower():
                analysis['categories']['Concepts'].append(file)
            elif '튜토리얼' in filename or 'tutorial' in filename.lower():
                analysis['categories']['Tutorials'].append(file)
            elif '가이드' in filename or 'guide' in filename.lower():
                analysis['categories']['Guides'].append(file)
            elif '참고' in filename or 'reference' in filename.lower():
                analysis['categories']['References'].append(file)
            elif '에이전트' in filename or 'agent' in filename.lower():
                analysis['categories']['Agents'].append(file)
            else:
                analysis['categories']['Other'].append(file)

        # Detect number sequences (e.g., 1-intro.md, 2-setup.md)
        numbered = [f for f in md_files if f.stem[0].isdigit()]
        if len(numbered) > 2:
            analysis['sequences'] = sorted(numbered)

        return analysis

    def generate_moc_content(self, directory: Path, title: str = None,
                           description: str = None) -> str:
        """Generate MOC content for a directory."""
        analysis = self.analyze_directory_content(directory)

        # Determine title
        if not title:
            title = directory.name

        # Generate tag from title
        tag = title.lower().replace(' ', '-').replace('/', '-')

        # Default description
        if not description:
            description = f"Navigation hub for {title} content."

        # Build sections
        sections_list = []

        # If there's a sequence, show it first
        if analysis['sequences']:
            sections_list.append("## Learning Sequence\n")
            for i, file in enumerate(analysis['sequences'], 1):
                link = f"[[{file.stem}]]"
                sections_list.append(f"{i}. {link}\n")
            sections_list.append("\n")

        # Show categorized content
        for category, files in sorted(analysis['categories'].items()):
            if not files:
                continue

            # Skip "Other" if we have other categories
            if category == "Other" and len(analysis['categories']) > 1:
                continue

            # Translate category names to bilingual if needed
            if analysis['has_korean'] and analysis['has_english']:
                category_map = {
                    'Concepts': 'Concepts / 개념',
                    'Tutorials': 'Tutorials / 튜토리얼',
                    'Guides': 'Guides / 가이드',
                    'References': 'References / 참고자료',
                    'Agents': 'Agents / 에이전트',
                }
                category_name = category_map.get(category, category)
            else:
                category_name = category

            sections_list.append(f"## {category_name}\n\n")
            for file in sorted(files):
                link = f"[[{file.stem}]]"
                sections_list.append(f"- {link}\n")
            sections_list.append("\n")

        sections = ''.join(sections_list) if sections_list else "## Content\n\nContent will be organized here.\n"

        # Find related directories for cross-references
        parent = directory.parent
        siblings = [d for d in parent.iterdir()
                   if d.is_dir() and d != directory
                   and not d.name.startswith('.')]

        related_links = []
        for sibling in siblings[:5]:  # Limit to 5 related
            sibling_moc = sibling / 'index.md'
            if sibling_moc.exists():
                related_links.append(f"- [[{sibling.name}/index|{sibling.name}]]")

        related = '\n'.join(related_links) if related_links else "- [[index|Main Index]]"

        # Generate dates
        today = datetime.now().strftime('%Y-%m-%d')

        # Fill template
        content = MOC_TEMPLATE.format(
            tag=tag,
            title=title,
            description=description,
            topic=title,
            sections=sections,
            related=related,
            created=today,
            modified=today,
        )

        return content

    def create_moc(self, directory: Path, title: str = None,
                  filename: str = "index.md") -> Path:
        """Create MOC file for a directory."""
        content = self.generate_moc_content(directory, title)
        moc_path = directory / filename
        moc_path.write_text(content, encoding='utf-8')
        return moc_path

    def generate_report(self) -> str:
        """Generate report of directories needing MOCs."""
        candidates = self.find_directories_needing_mocs()

        report = ["# MOC Generation Report\n"]
        report.append(f"**Vault Path**: {self.vault_path}\n")
        report.append(f"**Directories Needing MOCs**: {len(candidates)}\n\n")

        if candidates:
            report.append("## Suggested MOCs\n\n")
            for candidate in candidates:
                dir_path = candidate['directory'].relative_to(self.vault_path)
                count = candidate['file_count']
                report.append(f"### {dir_path}\n")
                report.append(f"- **Files**: {count}\n")
                report.append(f"- **Suggested Title**: {candidate['directory'].name}\n\n")

        return ''.join(report)


def main():
    parser = argparse.ArgumentParser(description='Generate MOCs for Obsidian vault')
    parser.add_argument('--vault', type=Path,
                       default=Path(__file__).parent.parent.parent.parent.parent / 'docs',
                       help='Path to vault root (default: docs/)')
    parser.add_argument('--suggest', action='store_true',
                       help='Show directories that need MOCs')
    parser.add_argument('--directory', type=str,
                       help='Create MOC for specific directory (relative to vault)')
    parser.add_argument('--title', type=str,
                       help='Title for the MOC (default: directory name)')
    parser.add_argument('--create-all', action='store_true',
                       help='Create MOCs for all suggested directories')
    parser.add_argument('--report', action='store_true',
                       help='Generate MOC coverage report')
    parser.add_argument('--min-files', type=int, default=5,
                       help='Minimum files for MOC suggestion (default: 5)')

    args = parser.parse_args()

    vault_path = args.vault.resolve()
    if not vault_path.exists():
        print(f"Error: Vault path {vault_path} does not exist")
        return 1

    generator = MOCGenerator(vault_path)

    if args.suggest or args.report:
        report = generator.generate_report()
        print(report)

        if args.report:
            report_path = vault_path / 'MOC_Generation_Report.md'
            report_path.write_text(report, encoding='utf-8')
            print(f"\nReport saved to: {report_path}")

    elif args.directory:
        dir_path = vault_path / args.directory
        if not dir_path.exists() or not dir_path.is_dir():
            print(f"Error: Directory {dir_path} does not exist")
            return 1

        title = args.title or dir_path.name
        moc_path = generator.create_moc(dir_path, title)
        print(f"Created MOC: {moc_path.relative_to(vault_path)}")

    elif args.create_all:
        candidates = generator.find_directories_needing_mocs(min_files=args.min_files)
        print(f"Creating MOCs for {len(candidates)} directories...\n")

        for candidate in candidates:
            directory = candidate['directory']
            title = directory.name
            try:
                moc_path = generator.create_moc(directory, title)
                print(f"✓ Created: {moc_path.relative_to(vault_path)}")
            except Exception as e:
                print(f"✗ Failed: {directory.relative_to(vault_path)} - {e}")

        print(f"\nCreated MOCs for {len(candidates)} directories")

    else:
        parser.print_help()


if __name__ == '__main__':
    main()

```

obsidian-moc-creator | SkillHub