A zero-dependency Python markdown linter with auto-fix support. Checks heading structure, whitespace, links, code blocks, and more.
- 12 built-in lint rules
- Auto-fix mode for fixable issues
- Configurable via
.markdownlint.json - Reads from files or stdin
- Colored terminal output
- Exit code 1 on warnings (CI-friendly)
Copy markdown_lint.py into your project or add to PATH. No dependencies required (Python 3.7+).
chmod +x markdown_lint.pypython markdown_lint.py README.md docs/*.mdREADME.md
3:1 MD022 Missing blank line before heading
15:1 MD001 Heading level skipped: h2 -> h4
22:81 MD013 Line length: 95 > 80
3 warnings in 1 file
python markdown_lint.py --fix README.md docs/*.mdFixes trailing whitespace, hard tabs, code fence style, and blank lines around headings.
cat README.md | python markdown_lint.py -python markdown_lint.py --list-rules MD001 No skipped heading levels
MD003 Consistent heading style
MD007 Proper list indentation
MD009 No trailing whitespace
MD010 No hard tabs
MD013 Line length limit
MD022 Blank lines around headings
MD024 No duplicate headings
MD034 No bare URLs
MD036 No emphasis used as heading
MD042 Link validation
MD048 Consistent code fence style
Create .markdownlint.json in your project root:
{
"line_length": 100,
"heading_style": "atx",
"code_fence_style": "backtick",
"list_indent": 2,
"rules": {
"no_skip_heading_levels": true,
"consistent_heading_style": true,
"no_trailing_whitespace": true,
"blank_line_around_headings": true,
"no_duplicate_headings": true,
"line_length": true,
"no_hard_tabs": true,
"proper_list_indent": true,
"fenced_code_style": true,
"no_bare_urls": true,
"no_emphasis_as_heading": true,
"link_check": false
}
}Use a custom config path:
python markdown_lint.py -c .my-lint-config.json docs/*.mdOverride line length from CLI:
python markdown_lint.py --max-length 80 README.md| Code | Rule | Fixable | Description |
|---|---|---|---|
| MD001 | no_skip_heading_levels | No | Heading levels should increment by one (no h2 -> h4) |
| MD003 | consistent_heading_style | No | All headings should use the same style (atx # or setext) |
| MD007 | proper_list_indent | No | List items should be indented by the configured amount |
| MD009 | no_trailing_whitespace | Yes | No trailing spaces (except 2 spaces for line break) |
| MD010 | no_hard_tabs | Yes | No hard tab characters |
| MD013 | line_length | No | Lines should not exceed configured length |
| MD022 | blank_line_around_headings | Yes | Headings should have blank lines before and after |
| MD024 | no_duplicate_headings | No | No two headings with the same text |
| MD034 | no_bare_urls | No | URLs should be wrapped in <> or []() |
| MD036 | no_emphasis_as_heading | No | Bold text on its own line should be a heading |
| MD042 | link_check | No | Links should have text and valid URLs |
| MD048 | fenced_code_style | Yes | Code fences should use consistent style |
# .github/workflows/lint.yml
- name: Lint markdown
run: python markdown_lint.py docs/**/*.md README.mdThe linter exits with code 1 if any warnings are found.
MIT