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.
Install command
npx @skill-hub/cli install jeongsk-langchain-academy-obsidian-moc-creator
Repository
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 repositoryBest 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
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()
```