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.
Fixes malformed markdown code block closings to ensure proper rendering and prevent content bleed.
/plugin marketplace add rjmurillo/ai-agents/plugin install project-toolkit@ai-agentsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
| Trigger Phrase | Operation |
|---|---|
fix markdown fences | Scan and repair malformed fence closings |
repair code block closings | Fix closing fences with language identifiers |
markdown rendering broken | Diagnose and fix fence issues |
code blocks bleeding into content | Fix unclosed or malformed fences |
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.
Use this skill when:
```python instead of ```)Use manual editing instead when:
Track fence state while scanning line by line:
Opening fence: Line matches ^\s*```\w+ and not inside a block. Record indent level. Enter "inside block" state.
Malformed closing fence: Line matches ^\s*```\w+ while inside a block. This is a closing fence with a language identifier. Fix by inserting proper closing fence before this line.
Valid closing fence: Line matches ^\s*```\s*$. Exit "inside block" state.
End of file: If still inside a block, append closing fence.
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
# 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]*$"
$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"
}
}
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}')
"
content = """
```python
def example():
pass
```python
"""
fixed = fix_markdown_fences(content)
print(fixed)
After execution:
git diff shows only fence-closing changes, no content modifications\n and \r\n| Avoid | Why | Instead |
|---|---|---|
| Manually searching for bad fences | Error-prone in large files | Use the algorithm or grep pattern |
| Copying opening fence line to close a block | Creates the exact bug this skill fixes | Always use plain ``` for closing |
| Fixing fences without tracking block state | Misidentifies nested vs sequential blocks | Use the stateful line-by-line algorithm |
When generating markdown with code blocks:
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.