From markdown
Integrates markdownlint into workflows for linting Markdown files using CLI commands, Node.js programmatic API, CI/CD pipelines, pre-commit hooks, and editor plugins.
How this skill is triggered — by the user, by Claude, or both
Slash command
/markdown:markdownlint-integrationThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Master integrating markdownlint into development workflows including CLI usage, programmatic API (sync/async/promise), CI/CD pipelines, pre-commit hooks, and editor integration.
Master integrating markdownlint into development workflows including CLI usage, programmatic API (sync/async/promise), CI/CD pipelines, pre-commit hooks, and editor integration.
Markdownlint can be integrated into various parts of your development workflow to ensure consistent markdown quality. This includes command-line tools, programmatic usage in Node.js, continuous integration pipelines, Git hooks, and editor plugins.
npm install -g markdownlint-cli
# or as dev dependency
npm install --save-dev markdownlint-cli
# Lint all markdown files in current directory
markdownlint '**/*.md'
# Lint specific files
markdownlint README.md CONTRIBUTING.md
# Lint with configuration file
markdownlint -c .markdownlint.json '**/*.md'
# Ignore specific files
markdownlint '**/*.md' --ignore node_modules
# Fix violations automatically
markdownlint -f '**/*.md'
# Output to file
markdownlint '**/*.md' -o linting-results.txt
# Use custom config
markdownlint --config config/markdown-lint.json docs/
# Ignore patterns from file
markdownlint --ignore-path .gitignore '**/*.md'
# Use multiple ignore patterns
markdownlint --ignore node_modules --ignore dist '**/*.md'
# Enable specific rules only
markdownlint --rules MD001,MD003,MD013 '**/*.md'
# Disable specific rules
markdownlint --disable MD013 '**/*.md'
# Show output in JSON format
markdownlint --json '**/*.md'
# Quiet mode (exit code only)
markdownlint -q '**/*.md'
# Verbose output
markdownlint --verbose '**/*.md'
.markdownlint-cli.json:
{
"config": {
"default": true,
"MD013": {
"line_length": 100
}
},
"files": ["**/*.md"],
"ignores": [
"node_modules/**",
"dist/**",
"build/**"
]
}
Use with:
markdownlint --config .markdownlint-cli.json
const markdownlint = require('markdownlint');
const options = {
files: ['good.md', 'bad.md'],
config: {
default: true,
'line-length': {
line_length: 100
}
}
};
markdownlint(options, (err, result) => {
if (!err) {
console.log(result.toString());
} else {
console.error(err);
}
});
import { lint as lintPromise } from 'markdownlint/promise';
const options = {
files: ['README.md', 'docs/**/*.md'],
config: {
default: true,
'no-inline-html': {
allowed_elements: ['br', 'img']
}
}
};
try {
const results = await lintPromise(options);
console.dir(results, { colors: true, depth: null });
} catch (err) {
console.error(err);
}
import { lint as lintSync } from 'markdownlint/sync';
const options = {
files: ['README.md'],
strings: {
'inline-content': '# Test\n\nContent here.'
},
config: {
default: true
}
};
const results = lintSync(options);
console.log(results.toString());
const markdownlint = require('markdownlint');
const options = {
strings: {
'content-1': '# Heading\n\nParagraph text.',
'content-2': '## Another heading\n\nMore content.'
},
config: {
default: true,
'first-line-heading': {
level: 1
}
}
};
markdownlint(options, (err, result) => {
if (!err) {
const resultString = result.toString();
console.log(resultString);
}
});
import { lint } from 'markdownlint/promise';
const results = await lint({
files: ['docs/**/*.md'],
config: { default: true }
});
// Results is an object keyed by filename
Object.keys(results).forEach(file => {
const fileResults = results[file];
fileResults.forEach(result => {
console.log(`${file}:${result.lineNumber} ${result.ruleNames.join('/')} ${result.ruleDescription}`);
if (result.errorDetail) {
console.log(` Detail: ${result.errorDetail}`);
}
if (result.errorContext) {
console.log(` Context: ${result.errorContext}`);
}
});
});
const markdownlint = require('markdownlint');
const { readConfigSync } = require('markdownlint/sync');
// Read configuration from file
const config = readConfigSync('.markdownlint.json');
const options = {
files: ['**/*.md'],
config: config
};
const results = markdownlint.sync(options);
console.log(results.toString());
const markdownlint = require('markdownlint');
const customRule = require('./custom-rules/heading-capitalization');
const options = {
files: ['README.md'],
config: {
default: true,
'heading-capitalization': true
},
customRules: [customRule]
};
markdownlint(options, (err, result) => {
if (!err) {
console.log(result.toString());
}
});
const { applyFix } = require('markdownlint');
const line = ' Text with extra spaces ';
const fixInfo = {
editColumn: 1,
deleteCount: 2,
insertText: ''
};
const fixed = applyFix(line, fixInfo);
console.log(fixed); // 'Text with extra spaces '
const { applyFixes } = require('markdownlint');
const input = '# Heading\n\n\nParagraph';
const errors = [
{
lineNumber: 3,
ruleNames: ['MD012'],
ruleDescription: 'Multiple blank lines',
fixInfo: {
lineNumber: 3,
deleteCount: -1
}
}
];
const fixed = applyFixes(input, errors);
console.log(fixed); // '# Heading\n\nParagraph'
const fs = require('fs');
const markdownlint = require('markdownlint');
const { applyFixes } = require('markdownlint');
const file = 'README.md';
const content = fs.readFileSync(file, 'utf8');
const options = {
strings: {
[file]: content
},
config: {
default: true
}
};
markdownlint(options, (err, result) => {
if (!err) {
const errors = result[file] || [];
if (errors.length > 0) {
const fixed = applyFixes(content, errors);
fs.writeFileSync(file, fixed, 'utf8');
console.log(`Fixed ${errors.length} issues in ${file}`);
}
}
});
.github/workflows/markdownlint.yml:
name: Markdownlint
user-invocable: false
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run markdownlint
run: npx markdownlint '**/*.md' --ignore node_modules
- name: Annotate PR with results
if: failure()
run: |
npx markdownlint '**/*.md' --ignore node_modules -o markdownlint-results.txt
cat markdownlint-results.txt
name: Markdownlint
user-invocable: false
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run markdownlint-cli2
uses: DavidAnson/markdownlint-cli2-action@v9
with:
paths: '**/*.md'
.gitlab-ci.yml:
markdownlint:
image: node:18-alpine
stage: test
before_script:
- npm install -g markdownlint-cli
script:
- markdownlint '**/*.md' --ignore node_modules
only:
- merge_requests
- main
artifacts:
when: on_failure
paths:
- markdownlint-results.txt
.circleci/config.yml:
version: 2.1
jobs:
markdownlint:
docker:
- image: cimg/node:18.0
steps:
- checkout
- run:
name: Install markdownlint
command: npm install -g markdownlint-cli
- run:
name: Run linter
command: markdownlint '**/*.md' --ignore node_modules
workflows:
version: 2
build_and_test:
jobs:
- markdownlint
Jenkinsfile:
pipeline {
agent any
stages {
stage('Lint Markdown') {
steps {
sh 'npm install -g markdownlint-cli'
sh 'markdownlint "**/*.md" --ignore node_modules'
}
}
}
post {
always {
cleanWs()
}
failure {
echo 'Markdownlint found issues!'
}
}
}
azure-pipelines.yml:
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
inputs:
versionSpec: '18.x'
displayName: 'Install Node.js'
- script: |
npm install -g markdownlint-cli
displayName: 'Install markdownlint'
- script: |
markdownlint '**/*.md' --ignore node_modules
displayName: 'Run markdownlint'
Install Husky:
npm install --save-dev husky
npx husky install
Create pre-commit hook:
npx husky add .husky/pre-commit "npm run lint:md"
Add script to package.json:
{
"scripts": {
"lint:md": "markdownlint '**/*.md' --ignore node_modules",
"lint:md:fix": "markdownlint -f '**/*.md' --ignore node_modules"
}
}
Install lint-staged:
npm install --save-dev lint-staged
Configure in package.json:
{
"lint-staged": {
"*.md": [
"markdownlint --fix",
"git add"
]
}
}
Update pre-commit hook:
npx husky add .husky/pre-commit "npx lint-staged"
.pre-commit-config.yaml:
repos:
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.37.0
hooks:
- id: markdownlint
args: ['--fix']
- repo: https://github.com/DavidAnson/markdownlint-cli2
rev: v0.10.0
hooks:
- id: markdownlint-cli2
args: ['--fix']
Install and use:
pip install pre-commit
pre-commit install
pre-commit run --all-files
Install the markdownlint extension:
Configure in .vscode/settings.json:
{
"markdownlint.config": {
"default": true,
"MD013": {
"line_length": 100
}
},
"markdownlint.ignore": [
"node_modules/**",
"dist/**"
],
"editor.codeActionsOnSave": {
"source.fixAll.markdownlint": true
}
}
Using ALE (Asynchronous Lint Engine):
" In .vimrc or init.vim
let g:ale_linters = {
\ 'markdown': ['markdownlint'],
\}
let g:ale_fixers = {
\ 'markdown': ['markdownlint'],
\}
let g:ale_markdown_markdownlint_options = '-c .markdownlint.json'
" Enable fixing on save
let g:ale_fix_on_save = 1
Install via Package Control:
Configure in preferences:
{
"linters": {
"markdownlint": {
"args": ["-c", ".markdownlint.json"]
}
}
}
Install packages:
apm install linter-markdownlint
Configure in Atom settings or .atom/config.cson:
"linter-markdownlint":
configPath: ".markdownlint.json"
{
"scripts": {
"lint": "npm run lint:md",
"lint:md": "markdownlint '**/*.md' --ignore node_modules",
"lint:md:fix": "markdownlint -f '**/*.md' --ignore node_modules",
"lint:md:ci": "markdownlint '**/*.md' --ignore node_modules -o markdownlint-report.txt",
"test": "npm run lint && npm run test:unit",
"precommit": "lint-staged"
}
}
Using cross-env for environment variables:
{
"scripts": {
"lint:md": "cross-env NODE_ENV=development markdownlint '**/*.md'"
},
"devDependencies": {
"cross-env": "^7.0.3"
}
}
FROM node:18-alpine
WORKDIR /app
# Install markdownlint globally
RUN npm install -g markdownlint-cli
# Copy markdown files
COPY . .
# Run linter
CMD ["markdownlint", "**/*.md", "--ignore", "node_modules"]
version: '3.8'
services:
markdownlint:
image: node:18-alpine
working_dir: /app
volumes:
- .:/app
command: >
sh -c "npm install -g markdownlint-cli &&
markdownlint '**/*.md' --ignore node_modules"
Run with:
docker-compose run markdownlint
Root .markdownlint.json:
{
"default": true,
"line-length": {
"line_length": 100
}
}
Root package.json:
{
"scripts": {
"lint:md": "markdownlint '**/*.md' --ignore node_modules",
"lint:md:packages": "lerna run lint:md",
"lint:md:all": "npm run lint:md && npm run lint:md:packages"
}
}
Package-level packages/api/package.json:
{
"scripts": {
"lint:md": "markdownlint '**/*.md'"
}
}
{
"scripts": {
"lint:md": "lerna run lint:md --stream"
}
}
turbo.json:
{
"pipeline": {
"lint:md": {
"outputs": []
}
}
}
Using a custom script:
const fs = require('fs');
const markdownlint = require('markdownlint');
const options = {
files: ['**/*.md'],
config: { default: true }
};
markdownlint(options, (err, results) => {
if (!err) {
const html = generateHtmlReport(results);
fs.writeFileSync('markdownlint-report.html', html);
}
});
function generateHtmlReport(results) {
let html = '<html><head><title>Markdownlint Report</title></head><body>';
html += '<h1>Markdownlint Results</h1>';
Object.keys(results).forEach(file => {
html += `<h2>${file}</h2>`;
html += '<ul>';
results[file].forEach(result => {
html += `<li>Line ${result.lineNumber}: ${result.ruleDescription}</li>`;
});
html += '</ul>';
});
html += '</body></html>';
return html;
}
markdownlint '**/*.md' --json > results.json
Process with jq:
markdownlint '**/*.md' --json | jq '.[] | select(length > 0)'
npx claudepluginhub thebushidocollective/han --plugin markdownGuides markdown linting with markdownlint-cli2: run checks, fix MD0XX errors, configure .markdownlint-cli2.jsonc rules/ignores, set up VS Code extension and GitHub Actions. Supports GFM/CommonMark for validation and workflows.
Configures markdownlint rules and options via JSON files, inline comments, and inheritance for consistent Markdown linting in Node.js projects.
Validates commit messages against Conventional Commits using commitlint configs. Detects and explains rules from .commitlintrc or commitlint.config.js for setup, CI/CD enforcement, LLM prompts, and debugging.