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.json first; 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; cache node_modules for 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.
SKILL hint: Require “after fixes, run the same lint command as main locally or in CI” and cite the exact script name from the repo—no invented tasks.

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.

Lines 0 Headings found 0

            

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

Back to skills More skills