Markdown Style and Lint
Help agents follow one Markdown style when writing or bulk-fixing docs, aligned with markdownlint, Prettier, or the repo’s .markdownlint.json.
In the SKILL, document heading rules (single H1, logical H2/H3 progression), blank lines around lists, fenced code language tags, readable link/image syntax, and table alignment—reducing CI churn and manual ping-pong.
Separate “must fix” from “nice to have”: MD041 (first-line H1) may be waived for some templates; line width and list marker style should follow team config so models do not improvise.
markdownlint Complete Configuration File
A project-root .markdownlint.json configuration covering the most common rules; set individual rules to false (disabled) or provide specific parameters as needed:
// .markdownlint.json
{
"default": true,
"MD001": true, // Heading levels increment one at a time (H1→H2→H3, no skips)
"MD003": { "style": "atx" }, // Use # ## ### style consistently
"MD004": { "style": "dash" }, // Unordered list markers use - consistently
"MD007": { "indent": 2 }, // List indent: 2 spaces
"MD010": { "code_blocks": false }, // No tabs (except code blocks)
"MD013": false, // Disable line length (handled by Prettier)
"MD022": true, // Blank line above and below headings
"MD024": { "siblings_only": true }, // Sibling headings may not share the same text
"MD025": true, // Only one H1 per document
"MD031": true, // Blank lines around fenced code blocks
"MD032": true, // Blank lines around lists
"MD033": false, // Allow HTML (e.g. <details><summary>)
"MD040": true, // Fenced code blocks must have a language tag
"MD041": true, // First line must be an H1 (may set to false for some templates)
"MD051": true, // Link fragment must exist
"MD055": { "style": "consistent" } // Consistent table separator style
}
Dead link check: markdown-link-check configuration:
// .mlc-config.json (markdown-link-check configuration)
{
"ignorePatterns": [
{ "pattern": "^https://localhost" },
{ "pattern": "^https://internal\.example\.com" }
],
"replacementPatterns": [
{ "pattern": "^/", "replacement": "{{BASEURL}}/" }
],
"httpHeaders": [
{ "urls": ["https://github.com"], "headers": { "Accept-Encoding": "zstd,br,gzip,deflate" } }
],
"timeout": "20s",
"retryOn429": true,
"retryCount": 3,
"aliveStatusCodes": [200, 206]
}
// package.json scripts
// "check:links": "find docs -name '*.md' | xargs markdown-link-check --config .mlc-config.json"
CI integration (GitHub Actions):
# .github/workflows/docs-lint.yml
name: Docs Lint
on:
pull_request:
paths: ['**/*.md', 'docs/**']
jobs:
markdownlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm ci
- run: npx markdownlint-cli2 "**/*.md" "#node_modules"
link-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm i -g markdown-link-check
- run: find docs -name '*.md' | xargs markdown-link-check -c .mlc-config.json
- Match repo config: read
.markdownlint.jsonfirst; do not encode conflicting rules in the SKILL. - With Prettier: clarify whether prose wrap and list styling are Prettier's or markdownlint's responsibility.
Rules mapped to SKILL
The table links common markdownlint rules to what the SKILL should spell out; enablement still follows repo config.
| Rule | Summary | SKILL should state |
|---|---|---|
MD001 |
Heading levels increase one step at a time—no skips. | Whether H2 → H4 jumps are allowed; if H1 is generated by the site, say so. |
MD003 / MD004 |
Consistent heading style and unordered list markers. | Standardize on - or *, aligned with Prettier. |
MD013 |
Line length cap (if enabled). | Max columns or “handled by Prettier prose wrap”. |
MD022 / MD031 / MD032 |
Blank lines around headings and lists. | Require blank lines above/below headings and lists to reduce render jitter. |
MD040 |
Fenced code blocks need language tags. | Whether a default like text is acceptable. |
MD041 |
First line should be a level-one heading. | If README H1 comes from a template, document the waiver. |
MD051 |
Link fragment existence (some configs). | Anchor naming and whether in-repo links are validated. |
CI integration
Run markdownlint alongside TypeScript/ESLint in PR checks so agents can iterate under a green-CI constraint.
- markdownlint-cli2: add
markdownlint-cli2 "**/*.md"to package.json scripts with config in.markdownlint-cli2.jsonc; fits npm-centric repos. - GitHub Actions: dedicated job or a serial step inside
lint; cachenode_modulesfor speed. - pre-commit: run the same command locally to catch failures before push.
- With Prettier: in CI, run Prettier first (if it formats md) then markdownlint, or document that only one tool owns whitespace/newlines.
Agent fix workflow
Encode “read config → summarize → batch fix → verify” in the SKILL so models execute in order.
[ Read .markdownlint* / Prettier / CI script names ]
│
▼
[ Scan: violation summary + group by file ]
│
┌────────┴────────┐
▼ ▼
[ Auto-fixable batch ] [ Human-review items listed separately ]
│ │
└────────┬──────────┘
▼
[ Apply patches / small commits ]
│
▼
[ Local or CI: markdownlint + agreed commands ]
│
┌────────┴────────┐
▼ ▼
[ Pass ] [ Fail: revert that batch ]
│
▼
[ Adjust against rule IDs, retry ]
Heading level check (ignores fences)
Paste draft Markdown: count lines, detect ATX headings (# … ######) for skipped levels (MD001-style intuition); # inside ``` fences does not count.
SKILL snippet
Copy as a skeleton, then substitute real paths and scripts from your repo.
---
name: markdown-lint
description: Normalize Markdown to project rules and integrate with markdownlint
---
# Rules
- Headings: single H1, H2/H3 do not skip levels (MD001/MD025)
- Lists: use - marker consistently, blank lines before and after (MD004/MD032)
- Code blocks: all fenced with language tag (```lang); no untagged fences (MD040)
- Links: internal links use relative paths; anchor IDs use lowercase kebab-case
- Line width: handled by Prettier; disable MD013
- Tables: aligned column separators, consistent column count per row
# Steps
1. Read .markdownlint.json / .markdownlint-cli2.jsonc (project config takes precedence)
2. Run scan: npx markdownlint-cli2 "**/*.md" "#node_modules"
3. Output violation summary grouped by file (rule ID + line number + description)
4. Auto-fix: markdownlint-cli2 --fix (safe auto-fixable items only)
5. Manual review: skipped heading levels, code blocks missing language tags, broken links
6. Dead link check: find docs -name '*.md' | xargs markdown-link-check -c .mlc-config.json
7. Small commits: each batch of fixes in its own commit for easy revert