Author high-quality Markdown documentation with deterministic structure, lint compliance, and CI integration. Use when writing README files, creating docs pages, fixing markdownlint failures, defining style rules, or wiring markdown checks into pre-commit and pipelines. Keywords: markdown, markdownlint, readme, docs, headings, lists, code fences, links, images, lint config, ci, documentation style.
Overall
score
100%
Does it follow best practices?
Validation for skill structure
npm install markdownlintconst markdownlint = require('markdownlint');
const options = {
files: ['README.md', 'docs/**/*.md'],
config: {
default: true,
MD013: { line_length: 120 }
}
};
markdownlint(options, (err, result) => {
if (err) {
console.error(err);
return;
}
console.log(result.toString());
// README.md: 23: MD013/line-length Line length [Expected: 120; Actual: 145]
});const { markdownlint } = require('markdownlint/promise');
async function lintFiles() {
const options = {
files: ['README.md', 'docs/**/*.md'],
config: {
default: true,
MD013: { line_length: 120 }
}
};
try {
const result = await markdownlint(options);
console.log(result.toString());
} catch (err) {
console.error(err);
}
}
lintFiles();const { markdownlintSync } = require('markdownlint/sync');
const options = {
files: ['README.md'],
config: {
default: true
}
};
const result = markdownlintSync(options);
console.log(result.toString());const options = {
// Files to lint (glob patterns supported)
files: ['**/*.md'],
// Or strings to lint
strings: {
'content1': '# Heading\n\nContent here',
'content2': '# Another\n\nMore content'
},
// Configuration object
config: {
default: true,
MD013: { line_length: 120 }
},
// Custom rules
customRules: [
require('./custom-rule')
],
// Directories to search for rules
markdownItPlugins: [
[require('markdown-it-emoji')]
]
};const options = {
files: ['README.md'],
config: {
default: true,
MD013: false
},
// Front matter pattern (RegExp or array of RegExp)
frontMatter: /^---[\s\S]*?---$/,
// Enable/disable inline configuration
noInlineConfig: false,
// Results version for compatibility
resultVersion: 3,
// Handle results
handleRuleFailures: true
};const result = await markdownlint(options);
// result is an object with filename keys:
// {
// 'README.md': [
// {
// lineNumber: 23,
// ruleNames: ['MD013', 'line-length'],
// ruleDescription: 'Line length',
// ruleInformation: 'https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md013',
// errorDetail: 'Expected: 120; Actual: 145',
// errorContext: 'This is a very long line that...',
// errorRange: [1, 145],
// fixInfo: null
// }
// ]
// }const result = await markdownlint(options);
// Check if any errors found
const hasErrors = Object.keys(result).some(
file => result[file].length > 0
);
if (hasErrors) {
console.error('Linting errors found:');
Object.keys(result).forEach(file => {
const errors = result[file];
if (errors.length > 0) {
console.error(`\n${file}:`);
errors.forEach(error => {
console.error(
` ${error.lineNumber}: ${error.ruleNames[0]} ` +
`${error.ruleDescription} ${error.errorDetail || ''}`
);
});
}
});
process.exit(1);
}const result = await markdownlint(options);
let totalErrors = 0;
const errorsByRule = {};
Object.values(result).forEach(fileErrors => {
totalErrors += fileErrors.length;
fileErrors.forEach(error => {
const ruleName = error.ruleNames[0];
errorsByRule[ruleName] = (errorsByRule[ruleName] || 0) + 1;
});
});
console.log(`Total errors: ${totalErrors}`);
console.log('Errors by rule:', errorsByRule);const { markdownlint } = require('markdownlint/promise');
async function lintString(content) {
const result = await markdownlint({
strings: {
'content': content
},
config: {
default: true
}
});
return result.content || [];
}
const errors = await lintString('# Heading\n\nSome content');const result = await markdownlint({
strings: {
'file1': '# First\n\nContent',
'file2': '# Second\n\nMore content',
'file3': '# Third\n\nEven more'
},
config: {
default: true
}
});
// Access results by key
console.log(result.file1);
console.log(result.file2);
console.log(result.file3);const { readConfig } = require('markdownlint/sync');
// Read .markdownlint.json
const config = readConfigSync('.markdownlint.json');
const result = await markdownlint({
files: ['README.md'],
config: config
});const baseConfig = readConfigSync('.markdownlint.json');
const overrides = {
MD013: { line_length: 150 }
};
const config = { ...baseConfig, ...overrides };
const result = await markdownlint({
files: ['README.md'],
config: config
});const result = await markdownlint(options);
Object.keys(result).forEach(file => {
result[file].forEach(error => {
if (error.fixInfo) {
console.log(`${file}:${error.lineNumber} can be auto-fixed`);
}
});
});const { markdownlint } = require('markdownlint/promise');
const { applyFixes } = require('markdownlint');
const fs = require('fs').promises;
async function lintAndFix(filePath) {
// Read file
const content = await fs.readFile(filePath, 'utf8');
// Lint
const result = await markdownlint({
strings: {
[filePath]: content
},
config: {
default: true
}
});
// Apply fixes
const fixed = applyFixes(content, result[filePath]);
// Write back
await fs.writeFile(filePath, fixed, 'utf8');
console.log(`Fixed ${filePath}`);
}
await lintAndFix('README.md');const { markdownlint } = require('markdownlint/promise');
const { applyFixes } = require('markdownlint');
const fs = require('fs').promises;
const glob = require('glob');
async function fixAllFiles(pattern) {
const files = glob.sync(pattern);
for (const file of files) {
const content = await fs.readFile(file, 'utf8');
const result = await markdownlint({
strings: { [file]: content },
config: { default: true }
});
if (result[file] && result[file].length > 0) {
const fixed = applyFixes(content, result[file]);
if (fixed !== content) {
await fs.writeFile(file, fixed, 'utf8');
console.log(`Fixed ${file}`);
}
}
}
}
await fixAllFiles('docs/**/*.md');// custom-no-lorem.js
module.exports = {
names: ['no-lorem'],
description: 'Disallow lorem ipsum text',
tags: ['test'],
function: (params, onError) => {
params.tokens.forEach(token => {
if (token.type === 'inline') {
const content = token.content.toLowerCase();
if (content.includes('lorem ipsum')) {
onError({
lineNumber: token.lineNumber,
detail: 'Found "lorem ipsum" placeholder text',
context: token.line
});
}
}
});
}
};const customRule = require('./custom-no-lorem');
const result = await markdownlint({
files: ['README.md'],
config: {
default: true
},
customRules: [customRule]
});const express = require('express');
const { markdownlint } = require('markdownlint/promise');
const app = express();
app.use(express.json());
app.post('/api/lint', async (req, res) => {
const { content } = req.body;
try {
const result = await markdownlint({
strings: { 'input': content },
config: { default: true }
});
res.json({
errors: result.input || [],
success: (result.input || []).length === 0
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
app.listen(3000);// gulpfile.js
const gulp = require('gulp');
const { markdownlint } = require('markdownlint/promise');
const through2 = require('through2');
function lintMarkdown() {
return gulp.src('**/*.md')
.pipe(through2.obj(async (file, enc, callback) => {
const result = await markdownlint({
strings: {
[file.path]: file.contents.toString()
},
config: { default: true }
});
const errors = result[file.path] || [];
if (errors.length > 0) {
console.error(`${file.path}:`);
errors.forEach(err => {
console.error(` ${err.lineNumber}: ${err.ruleDescription}`);
});
}
callback(null, file);
}));
}
gulp.task('lint:md', lintMarkdown);Install with Tessl CLI
npx tessl i pantheon-ai/markdown-authoring@0.1.1