feat: Add mermaid-fixer skill for automatic Mermaid syntax error correction

- Add skill that uses check-mermaid.js to validate Mermaid diagrams
- Skill guides Claude to parse error reports and apply intelligent fixes
- Fixes common Mermaid parser bugs (parentheses, brackets, braces in labels)
- Include demo scripts with test Markdown file for validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Misaka_Company
2026-04-08 13:32:55 +08:00
parent 57b797196c
commit 1351371bd2
5 changed files with 911 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
---
name: mermaid-fixer
description: Fix Mermaid diagram syntax errors in Markdown files. Use when: (1) Mermaid diagrams aren't rendering or show parse errors, (2) User mentions "mermaid syntax error", "mermaid not working", "diagram broken", (3) CI/CD fails on Mermaid validation, (4) Documentation contains Mermaid code blocks that need fixing.
---
# Mermaid Diagram Fixer
Automatically detects and fixes Mermaid diagram syntax errors in Markdown files using the `check-mermaid.js` validation script.
## How It Works
1. **Run `check-mermaid.js`** to get detailed error reports
2. **Parse error information** (line numbers, error types, code snippets)
3. **Apply intelligent fixes** based on the specific error
4. **Verify** by running the check again
## Core Script
The `scripts/check-mermaid.js` script is the heart of this skill. It:
- Scans Markdown files for ```` ```mermaid ```` code blocks
- Validates each block using the Mermaid CLI (`mmdc`)
- Returns detailed error reports with line numbers and error messages
## Workflow
### Step 1: Run the Check Script
```bash
node scripts/check-mermaid.js <path/to/file.md>
```
**Expected output format:**
- **No errors**: Returns exit code 0 with success message
- **Has errors**: Returns exit code 1 with structured error report:
```markdown
## 🚨 Mermaid 语法检查报告
**检查文件:** `example.md`
**检查结果:** ❌ 发现 2 处语法错误 (共检测到 5 个代码块)
---
### ❌ 错误 #1 (代码块 #2)
- **文档位置:** 第 `51` 行至第 `86`
#### 核心错误详情
\`\`\`text
Error: Parse error on line 13: ...
Expecting 'SQE', 'DOUBLECIRCLEEND', ... got 'PS'
\`\`\`
#### 代码内容片段
\`\`\`text
[shows the problematic code]
\`\`\`
```
### Step 2: Parse and Understand Errors
Key information from each error:
- **文档位置**: Which line numbers contain the error
- **核心错误详情**: The parser error message
- **代码内容片段**: The actual Mermaid code causing the issue
### Step 3: Apply Fixes Based on Error Type
#### Error Type: "got 'PS'" (Parentheses Issue)
**Cause**: Parentheses `()` in node labels or arrow labels within subgraphs
**Fix**: Quote the label
```mermaid
# Before (causes error)
NodeA[Label (with parens)]
-->|Label (parens)| NodeB
# After (fixed)
NodeA["Label (with parens)"]
-->"|Label (parens)|" NodeB
```
#### Error Type: "got 'SQS'" (Square Brackets Issue)
**Cause**: Square brackets `[]` in node labels
**Fix**: Quote the label
```mermaid
# Before
NodeA[Result[]<br/>text]
# After
NodeA["Result[]<br/>text"]
```
#### Error Type: "got 'DIAMOND_START'" or similar
**Cause**: Curly braces `{}` or other special characters in labels
**Fix**: Quote the label
```mermaid
# Before
NodeA[Label {variable}]
# After
NodeA["Label {variable}"]
```
### Step 4: Apply the Fix to the File
1. Read the Markdown file
2. Locate the problematic code block using the reported line numbers
3. Apply the appropriate fix (quote labels with special characters)
4. Save the file
### Step 5: Verify
Run `check-mermaid.js` again to confirm all errors are resolved.
## Common Fix Patterns
| Error Pattern | Fix Strategy |
|---------------|--------------|
| `NodeID[label (text)]` | Change to `NodeID["label (text)"]` |
| `-->|label (text)|` | Change to `-->|"label (text)"\|` |
| `NodeID[label[]text]` | Change to `NodeID["label[]text"]` |
| `NodeID[label{text}]` | Change to `NodeID["label{text}"]` |
## Important Notes
1. **Always quote labels** containing: `()`, `[]`, `{}`, `<`, `>`, `#`, or Chinese characters
2. **Preserve the original structure** - only modify the problematic labels
3. **Check all reported errors** - don't stop after fixing just one
4. **Re-verify** after each fix round
## Example Usage
```
User: "My Mermaid diagrams in docs/architecture.md aren't rendering"
Claude:
1. Run: node scripts/check-mermaid.js docs/architecture.md
2. Parse error output
3. Identify errors (e.g., "got 'PS'" on line 45)
4. Read file, locate line 45, find the Mermaid block
5. Apply fix: quote labels with parentheses
6. Save and verify
```

View File

@@ -0,0 +1,23 @@
{
"skill_name": "mermaid-fixer",
"evals": [
{
"id": 1,
"prompt": "The Mermaid diagrams in demo_scripts/demo.md have syntax errors. Please fix them.",
"expected_output": "The skill should run check-mermaid.js, identify the 3 syntax errors, and apply fixes by quoting labels with special characters. After fixes, all 9 Mermaid blocks should pass validation.",
"files": ["demo_scripts/demo.md"]
},
{
"id": 2,
"prompt": "My documentation file README.md has broken Mermaid diagrams. Can you check and fix them?",
"expected_output": "The skill runs check-mermaid.js on README.md, parses any error reports, applies appropriate fixes based on error types, and verifies the fixes by running the check again.",
"files": []
},
{
"id": 3,
"prompt": "CI/CD is failing with Mermaid validation errors on docs/api.md. Please fix the diagrams.",
"expected_output": "The skill identifies all Mermaid syntax errors using check-mermaid.js, applies fixes for each error type (parentheses, brackets, braces in labels), and ensures the file passes validation.",
"files": []
}
]
}

View File

@@ -0,0 +1,128 @@
#!/usr/bin/env node
const fs = require('fs');
const { execSync } = require('child_process');
const path = require('path');
const os = require('os');
// 获取命令行传入的 Markdown 文件路径
const fileArg = process.argv[2];
if (!fileArg) {
console.error('❌ 请提供 Markdown 文件路径。用法: node check-mermaid.js <file.md>');
process.exit(1);
}
const filePath = path.resolve(fileArg);
const fileName = path.basename(filePath);
if (!fs.existsSync(filePath)) {
console.error(`❌ 文件不存在: ${filePath}`);
process.exit(1);
}
const content = fs.readFileSync(filePath, 'utf-8');
// 正则匹配 ```mermaid ... ``` 代码块,兼容 Windows(\r\n) 和 Linux(\n)
const mermaidRegex = /```mermaid\r?\n([\s\S]*?)```/g;
let match;
let blockCount = 0;
let errorCount = 0;
// 用于收集所有格式化后的错误报告块
const errorReports = [];
while ((match = mermaidRegex.exec(content)) !== null) {
blockCount++;
const fullMatch = match[0];
const code = match[1];
// 1. 计算在 Markdown 文件中的绝对行号
const textBeforeMatch = content.substring(0, match.index);
const startLine = textBeforeMatch.split(/\r?\n/).length;
const endLine = startLine + fullMatch.split(/\r?\n/).length - 1;
// 创建临时文件存放单个 Mermaid 代码
const tmpFile = path.join(os.tmpdir(), `mermaid-check-${Date.now()}-${blockCount}.mmd`);
fs.writeFileSync(tmpFile, code.trim());
try {
// 调用 mmdc 进行静默渲染检查
execSync(`npx mmdc -i "${tmpFile}" -o "${tmpFile}.svg" -q`, { stdio: 'pipe' });
} catch (error) {
errorCount++;
// 2. 净化报错信息:剔除底层执行堆栈
const stderr = error.stderr ? error.stderr.toString() : error.message;
const errorLines = stderr.split(/\r?\n/);
const cleanErrorLines = [];
for (const line of errorLines) {
if (line.trim().startsWith('at ') ||
line.includes('Parser3.parseError') ||
line.includes('fromText')) {
break;
}
cleanErrorLines.push(line);
}
const cleanErrorOutput = cleanErrorLines.join('\n').trim();
// 3. 截断代码内容最少3行最多6行
const codeLines = code.split(/\r?\n/).filter(l => l.trim() !== '');
let displayCode = '';
if (codeLines.length <= 6) {
displayCode = codeLines.join('\n');
} else {
const head = codeLines.slice(0, 3).join('\n');
const tail = codeLines.slice(-3).join('\n');
displayCode = `${head}\n ...\n ... (中间省略 ${codeLines.length - 6} 行) ...\n ...\n${tail}`;
}
// 4. 组装单个错误的 Markdown 块
errorReports.push(`### ❌ 错误 #${errorCount} (代码块 #${blockCount})
- **文档位置:** 第 \`${startLine}\` 行至第 \`${endLine}\`
#### 核心错误详情
\`\`\`text
${cleanErrorOutput}
\`\`\`
#### 代码内容片段
\`\`\`text
${displayCode}
\`\`\``);
} finally {
// 清理临时文件
if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile);
if (fs.existsSync(`${tmpFile}.svg`)) fs.unlinkSync(`${tmpFile}.svg`);
}
}
// ------------------------------------------------------------------
// 生成最终的 Markdown 报告输出
// ------------------------------------------------------------------
if (errorCount > 0) {
// 存在错误的情况
console.log(`## 🚨 Mermaid 语法检查报告\n`);
console.log(`**检查文件:** \`${fileName}\``);
console.log(`**检查结果:** ❌ 发现 ${errorCount} 处语法错误 (共检测到 ${blockCount} 个代码块)\n`);
console.log(`---\n`);
console.log(errorReports.join('\n\n---\n\n'));
// 返回非零状态码,确保在 CI/CD 或 Git Hook 中能够阻断流程
process.exit(1);
} else {
// 全部正确或没有代码块的情况
console.log(`## 🎉 Mermaid 语法检查报告\n`);
console.log(`**检查文件:** \`${fileName}\``);
if (blockCount === 0) {
console.log(`**检查结果:** ⚠️ 未检测到 Mermaid 代码块`);
} else {
console.log(`**检查结果:** ✅ 全部通过 (共检测到 ${blockCount} 个代码块)`);
}
process.exit(0);
}