From cc-obsidian-mem
Normalizes long filenames and consolidates semantic duplicate notes in Obsidian _claude-mem projects using AI. For verbose names, topic duplicates, post-migration, or maintenance.
npx claudepluginhub z-m-huang/cc-obsidian-memThis skill is limited to using the following tools:
Two-phase knowledge base cleanup: normalize verbose filenames, then consolidate semantic duplicates.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
Two-phase knowledge base cleanup: normalize verbose filenames, then consolidate semantic duplicates.
/mem-consolidate
/mem-consolidate project:my-project
/mem-consolidate project:my-project dryRun:true
/mem-consolidate project:my-project normalizeOnly:true # Skip duplicate merging
Phase 1: Normalize Long Filenames
├── Find files with names > 40 chars
├── AI suggests short generic titles (2-4 words)
├── Rename files, add original as alias
└── Handle collisions (merge or suffix)
Phase 2: Consolidate Semantic Duplicates
├── AI groups notes by topic similarity
├── Merge groups into single files
├── Archive source files
└── Update aliases for future matching
Before any file operations, you MUST read the config:
Read user config: ~/.cc-obsidian-mem/config.json
C:\Users\{username}\.cc-obsidian-mem\config.json~/.cc-obsidian-mem/config.jsonExtract settings from config:
{
"vault": {
"path": "/path/to/obsidian/vault",
"memFolder": "_claude-mem"
},
"ai": {
"enabled": true,
"model": "sonnet",
"timeout": 30000
}
}
Construct project paths:
{vault.path}/{vault.memFolder}{vault.path}/{vault.memFolder}/projects{vault.path}/{vault.memFolder}/projects/{project-name}Note AI settings for semantic matching:
ai.enabled: Whether to use AI (default: true)ai.model: "sonnet", "haiku", or "opus" (default: sonnet)Important: Never hardcode or assume vault paths. Always read from config first.
Consolidation is always scoped to a single project. Notes from different projects are never merged together.
.git root)For each category (decisions, patterns, errors, research, knowledge):
List all files in {project}/{category}/ folder
decisions/decisions.md).archive/ subfolderBuild notes list with title, category, and filename length:
[
{ "title": "Stop hook non-blocking", "category": "decisions", "path": "...", "filenameLength": 24 },
{ "title": "QA: How to prevent stop hook from blocking", "category": "research", "path": "...", "filenameLength": 53 },
...
]
Before finding duplicates, normalize verbose filenames to short generic titles.
Find all files with filename length > 40 characters (excluding .md extension):
Long filenames found:
1. [decisions] use-writeifchanged-pattern-to-avoid-unnecessary-fi.md (50 chars)
2. [research] qa-how-to-prevent-stop-hook-from-blocking-claude-s.md (53 chars)
3. [patterns] atomic-file-operations-with-collision-handling.md (47 chars)
Send long filenames to AI for generic title suggestions:
Suggest short generic titles (2-4 words) for these verbose filenames.
FILES:
0. [decisions] "use-writeifchanged-pattern-to-avoid-unnecessary-fi"
1. [research] "qa-how-to-prevent-stop-hook-from-blocking-claude-s"
2. [patterns] "atomic-file-operations-with-collision-handling"
Respond with JSON:
{"titles": ["WriteIfChanged pattern", "Stop hook blocking", "Atomic file operations"]}
Rules:
- Keep titles short: 2-4 words maximum
- Capture the core concept, not implementation details
- Remove prefixes like "qa-", "how-to-", "pattern-for-"
- Use title case
For each long filename:
write-if-changed-pattern.md)Example transformation:
Before: use-writeifchanged-pattern-to-avoid-unnecessary-fi.md
After: write-if-changed-pattern.md
aliases: ["use writeifchanged pattern to avoid unnecessary fi"]
If the generic filename already exists:
write-if-changed-pattern-2.mdUse AI semantic matching to identify groups of notes within the current project that discuss the same topic.
Send this prompt to Claude CLI using the model from config:
Group these notes by semantic similarity (same topic, different wording).
NOTES (0-indexed):
0. [decisions] "Stop hook non-blocking"
1. [research] "QA: How to prevent stop hook from blocking"
2. [research] "QA: Stop hook blocking Claude session"
3. [patterns] "Two-phase locking"
...
Respond with JSON using 0-based indices:
{"groups": [[0,1,2], [3]], "genericTitles": ["Stop hook blocking", "Two-phase locking"]}
Rules:
- Only group notes that are semantically about the SAME topic
- Notes in different categories CAN be grouped if same topic
- Each note appears in at most one group
- Single-note groups can be omitted
- Suggest a short generic title (2-4 words) for each group
echo '<prompt>' | claude -p - --model {config.ai.model} --no-session-persistence --output-format text
AI returns groups like:
{
"groups": [[0,1,2], [5,8,12]],
"genericTitles": ["Stop hook blocking", "Windows path normalization"]
}
For each group with 2+ notes:
Show the group for confirmation (unless auto-mode):
Found duplicate group: "Stop hook blocking"
1. [decisions] stop-hook-non-blocking.md
2. [research] qa-how-to-prevent-stop-hook-from-blocking-claude-s.md
3. [research] qa-stop-hook-blocking-claude-session.md
Merge into: decisions/stop-hook-blocking.md ?
[Yes / Skip / Custom target]
Choose target file:
Merge content:
created date (oldest first)## Entry: YYYY-MM-DD HH:MMentry_countWrite merged file to target path
Archive source files (except target) to .archive/
When merging multiple files into one:
Sort files by created date (oldest first)
created field (ISO8601 format)created missingPrepare merged content:
created date## Entry: YYYY-MM-DD HH:MM
{content from this file}
entry_count from all ## Entry headersWrite merged file:
Archive source files (except the target file):
.archive/ subfolderWhen user requests automatic merging (no prompts):
Trust AI groupings: Auto-merge all groups identified by AI
Category priority for target: When group spans categories:
Still prompt for:
After processing all files:
# Consolidation Summary
**Scanned**: {N} files across {M} categories
**AI Model**: {config.ai.model}
## Phase 1: Filename Normalization
| Before | After | Category |
|--------|-------|----------|
| use-writeifchanged-pattern-to-avoid-unnecessary-fi.md | write-if-changed-pattern.md | decisions |
| qa-how-to-prevent-stop-hook-from-blocking-claude-s.md | stop-hook-blocking.md | research |
**Normalized**: {X} files renamed to shorter titles
## Phase 2: Duplicate Consolidation
| Group | Files | Target | Aliases Added |
|-------|-------|--------|---------------|
| Stop hook blocking | 3 | decisions/stop-hook-blocking.md | 2 |
| Windows path normalization | 4 | research/windows-path-normalization.md | 3 |
**Merged**: {Y} duplicate groups
## Skipped
- `{filename}`: {reason}
## Final Stats
- Files before: {N}
- Normalized: {X} long filenames → short titles
- Merged: {Y} duplicate groups
- Files after: {M}
- Total reduction: {N-M} files ({percentage}%)
---
Archived files are in `.archive/` folders.
Aliases added will improve future Jaccard matching.
Filename-based matching (Jaccard word similarity) misses semantic duplicates:
AI understands that these are the same topic even with different wording.
When AI identifies duplicates and they're merged:
If project has many notes (50+), batch the AI call:
Problem: Category index files like decisions/decisions.md have the same slug as their folder name.
Solution: Explicitly exclude files matching ${category}.md pattern.
Problem: sessions/ folder contains auto-generated session notes.
Solution: Exclude sessions category from consolidation.
Problem: Some files may not have created field in frontmatter.
Solution: Fall back to filesystem mtime and warn user.
Problem: Some generic files may already have multiple entries from previous consolidations.
Solution: Append new entries, merge aliases, update entry_count.
Problem: File already exists in .archive/ from previous consolidation.
Solution: Append timestamp to filename: auth-bug_20260115-103000.md
.archive/ for easy recoveryAfter consolidation, suggest:
.archive/ folders/mem-audit to verify vault structure is clean