Claude-skill-registry fix-markdown-fences
Repair malformed markdown code fence closings. Use when markdown files have closing fences with language identifiers (```text instead of ```) or when generating markdown with code blocks to ensure proper fence closure.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/fix-markdown-fences" ~/.claude/skills/majiayu000-claude-skill-registry-fix-markdown-fences && rm -rf "$T"
manifest:
skills/data/fix-markdown-fences/SKILL.mdsource content
Fix Markdown Code Fence Closings
Problem
When generating markdown with code blocks, closing fences sometimes include language identifiers:
<!-- Wrong --> ```python def hello(): print("world") ```python <!-- Should be just ``` -->
The closing fence should never have a language identifier. This breaks markdown parsers and causes rendering issues.
When to Use
- After generating markdown with multiple code blocks
- When fixing existing markdown files with rendering issues
- When code blocks appear to "bleed" into surrounding content
- As a validation step before committing markdown documentation
Algorithm
Track fence state while scanning line by line:
-
Opening fence: Line matches
and not inside a block. Record indent level. Enter "inside block" state.^\s*```\w+ -
Malformed closing fence: Line matches
while inside a block. This is a closing fence with a language identifier. Fix by inserting proper closing fence before this line.^\s*```\w+ -
Valid closing fence: Line matches
. Exit "inside block" state.^\s*```\s*$ -
End of file: If still inside a block, append closing fence.
Implementation
Python (Recommended)
import re from pathlib import Path def fix_markdown_fences(content: str) -> str: """Fix malformed code fence closings in markdown content.""" lines = content.splitlines() result = [] in_code_block = False block_indent = "" opening_pattern = re.compile(r'^(\s*)```(\w+)') closing_pattern = re.compile(r'^(\s*)```\s*$') for line in lines: opening_match = opening_pattern.match(line) closing_match = closing_pattern.match(line) if opening_match: if in_code_block: # Malformed closing fence with language identifier # Insert proper closing fence before this line result.append(f"{block_indent}```") # Start new block result.append(line) block_indent = opening_match.group(1) in_code_block = True elif closing_match: result.append(line) in_code_block = False block_indent = "" else: result.append(line) # Handle file ending inside code block if in_code_block: result.append(f"{block_indent}```") return '\n'.join(result) def fix_markdown_files(directory: Path, pattern: str = "**/*.md") -> list[str]: """Fix all markdown files in directory. Returns list of fixed files.""" fixed = [] for file_path in directory.glob(pattern): content = file_path.read_text() fixed_content = fix_markdown_fences(content) if content != fixed_content: file_path.write_text(fixed_content) fixed.append(str(file_path)) return fixed
Bash (Quick Check)
# Find files with potential issues (opening fence pattern at end of block) grep -rn '```[a-zA-Z]' --include="*.md" | grep -v "^[^:]*:[0-9]*:\s*```[a-zA-Z]*$"
PowerShell
$directories = @('docs', 'src') foreach ($dir in $directories) { Get-ChildItem -Path $dir -Filter '*.md' -Recurse | ForEach-Object { $file = $_.FullName $content = Get-Content $file -Raw $lines = $content -split "`r?`n" $result = @() $inCodeBlock = $false $codeBlockIndent = "" for ($i = 0; $i -lt $lines.Count; $i++) { $line = $lines[$i] if ($line -match '^(\s*)```(\w+)') { if ($inCodeBlock) { $result += $codeBlockIndent + '```' $result += $line $codeBlockIndent = $Matches[1] } else { $result += $line $codeBlockIndent = $Matches[1] $inCodeBlock = $true } } elseif ($line -match '^(\s*)```\s*$') { $result += $line $inCodeBlock = $false $codeBlockIndent = "" } else { $result += $line } } if ($inCodeBlock) { $result += $codeBlockIndent + '```' } $newContent = $result -join "`n" Set-Content -Path $file -Value $newContent -NoNewline Write-Host "Fixed: $file" } }
Usage
Fix Files in Directory
# Python python -c " from pathlib import Path exec(open('fix_fences.py').read()) fixed = fix_markdown_files(Path('docs')) for f in fixed: print(f'Fixed: {f}') " # PowerShell pwsh fix-fences.ps1
Fix Single String (In-Memory)
content = """ ```python def example(): pass ```python """ fixed = fix_markdown_fences(content) print(fixed)
Verification
# Check what changed git diff --stat # Review specific file git diff docs/example.md # Validate markdown renders correctly # (use your preferred markdown preview tool)
Edge Cases Handled
- Nested indentation: Preserves indent level from opening fence
- Multiple consecutive blocks: Each block tracked independently
- File ending inside block: Automatically closes unclosed blocks
- Mixed line endings: Handles both
and\n\r\n
Prevention
When generating markdown with code blocks:
- Always use plain ``` for closing fences
- Never copy the opening fence line to close
- Track block state when programmatically generating markdown