Markdown Author
Overview
Proactively enforce markdown linting rules and spelling standards during writing, preventing violations before they're committed. Auto-fix formatting issues, block for structural decisions requiring context.
Git hooks remain as safety net for non-skill markdown changes.
Prerequisites
.markdownlint.jsonconfiguration (create with defaults if missing - see Configuration Verification)cspell.jsonspelling configuration (create with defaults if missing - see Configuration Verification)- markdownlint-cli2 and cspell installed (via package.json)
When to Use
- Writing new markdown files (.md)
- Editing existing markdown files
- Creating documentation, README files, plans
- Writing skill specifications
Do not use for:
- Non-markdown files
- Generated markdown (tool output)
- Third-party markdown (node_modules, etc.)
Configuration Verification (First Use)
On first use in repository, verify configurations exist:
-
Check
.markdownlint.json:-
If exists: Parse and validate JSON
-
If missing: Use Write tool to create with defaults:
{ "default": true, "MD013": { "line_length": 120, "code_blocks": false, "tables": false }, "MD024": { "siblings_only": true }, "MD033": false, "MD041": false } -
If malformed: Error with message to fix or delete
-
-
Check
cspell.json:-
If exists: Parse and validate JSON
-
If missing: Use Write tool to create with defaults:
{ "version": "0.2", "language": "en-US", "words": [], "ignorePaths": ["node_modules/**", "package-lock.json"] } -
If malformed: Error with message to fix or delete
-
-
Load rule configuration:
- Read enabled rules from
.markdownlint.json - Read spelling locale and custom dictionary from
cspell.json - Cache for session (don't re-read for each file)
- Read enabled rules from
Core Workflow
Before Writing Markdown
- Load markdown-author skill if not already loaded
- Verify configurations exist (first use only)
- Read
.markdownlint.jsonto determine enabled rules - Read
cspell.jsonfor spelling configuration
During Writing
For each markdown content section:
-
Analyze document structure:
- Identify existing heading levels
- Determine appropriate next heading level
- Note any lists, blockquotes, tables
-
Compose content with validation:
- Check line length as writing (120 char default)
- Validate heading hierarchy
- Check spelling against cspell configuration
- Detect code fences without language
-
Apply auto-fixes silently:
- Break long lines at word boundaries
- Adjust heading levels to maintain hierarchy
- Strip trailing whitespace
- Normalize list spacing
- Fix emphasis/link syntax
-
Block for structural decisions:
- Code fence missing language → Prompt for language
- Spelling error → Fix or add to dictionary
- Duplicate heading → Rename or confirm
-
Log summary after writing:
✓ Markdown validated and auto-fixed: <!-- markdownlint-enable MD040 --> - Adjusted 3 lines exceeding 120 characters - Fixed heading hierarchy (h3 → h2) - Added 1 term to cspell dictionary
After Writing
- Write validated markdown to file
- Git hooks run as safety net (markdownlint-cli2, cspell)
- If hooks fail, investigate why skill didn't catch violation
Validation Rules
See Validation Rules Reference for complete list.
Auto-Fixable Rules
Applied silently without interruption:
- MD001: Heading hierarchy - Auto-adjust levels to maintain proper structure
- MD013: Line length - Break at word boundaries, respect code/table exclusions
- MD009: Trailing spaces - Strip automatically
- MD010: Hard tabs - Replace with spaces
- MD012: Multiple blank lines - Collapse to single
- MD030/MD032: List spacing - Normalize
- All formatting rules: Applied per .markdownlint.json configuration
Blocking Rules
Require agent decision:
- MD040: Code fence language - Agent must specify (bash, python, csharp, text, etc.)
- MD024: Duplicate headings - Agent must rename or confirm
- MD045: Image alt text - Agent must provide
- Spelling errors: Agent must fix or add to cspell.json dictionary
Error Handling
Configuration Errors
Malformed JSON:
Error: .markdownlint.json is invalid JSON
Line 5: Unexpected token }
Fix syntax or delete file to regenerate with defaults.
```text
**Missing required fields:**
```text
Error: cspell.json missing 'version' field
Add field or delete file to regenerate with defaults.
```text
### Blocking Violations
**Code fence without language:**
```text
⚠ Markdown validation error:
Line 45: Code fence requires language specification
Current:
```text
npm install
```text
Options:
- bash (recommended for shell commands)
- text (for plain text)
- json, yaml, markdown, etc.
What language should this code fence use?
```text
**Spelling error:**
```text
⚠ Spelling error detected:
Line 12: Unknown word 'agentskills'
Options:
1. Add to cspell.json custom dictionary (recommended for project terms)
2. Fix spelling
3. Ignore (not recommended)
How should this be handled?
```text
**Duplicate heading:**
```text
⚠ Duplicate heading detected:
Line 67: "## Implementation" already exists at line 34
This may cause navigation issues. Options:
1. Rename to "## Implementation Details"
2. Rename to "## Implementation (Phase 2)"
3. Keep as-is (not recommended)
How should this be resolved?
```text
### Graceful Degradation
If configuration error prevents full validation:
```text
⚠ markdown-author skill error: Unable to parse .markdownlint.json
Falling back to basic validation (line length, code fences only)
Full linting will run in pre-commit hook.
Consider fixing configuration: .markdownlint.json
```text
## Example
### Writing New Documentation
```markdown
User: "Create architecture documentation"
Agent: [Loads markdown-author skill]
Agent: [Verifies .markdownlint.json exists]
Agent: [Verifies cspell.json exists]
Agent: [Begins composing architecture.md]
Content composed:
# Architecture Overview
This document describes the system architecture...
[Spell check detects: architecture → architecture]
⚠ Spelling error: 'architecture'. Did you mean: 'architecture'?
Agent: [Corrects to 'architecture']
The application uses a microservices approach with the following components:
[Spell check detects: components → components]
⚠ Spelling error: 'components'. Did you mean: 'components'?
Agent: [Corrects to 'components']
### API Gateway
[Heading hierarchy: h1 → h3, auto-corrects to h2]
## API Gateway
Handles all incoming requests and routes to appropriate services. The gateway validates authentication tokens, applies rate limiting, and logs all requests for monitoring purposes.
[Line length: 165 chars, auto-breaks at word boundary]
Handles all incoming requests and routes to appropriate services. The gateway
validates authentication tokens, applies rate limiting, and logs all requests
for monitoring purposes.
```python
def validate_token(token):
return jwt.decode(token)
```text
[Code fence has language: python ✓]
✓ Markdown validated and auto-fixed:
- Fixed 2 spelling errors
- Adjusted heading hierarchy (h3 → h2)
- Broke 1 line exceeding 120 characters
```text
### Editing Existing Markdown
```markdown
Agent: [Loads markdown-author skill]
Agent: [Reads existing document structure: h1, h2, h2, h3]
Agent: [Plans to add new section at same level as last h2]
Adding new content:
## New Feature
[Heading level: h2 - correct for sibling section ✓]
Implementation details follow...
[Validation passes, writes to file]
```text
## Common Mistakes
- Writing markdown without loading skill first
- Ignoring blocking violations (spelling, code fence language)
- Proceeding when configuration malformed
- Not adding project-specific terms to cspell.json dictionary
- Disabling rules in config without understanding impact
- Using --no-verify to bypass pre-commit hooks (skill should catch issues first)
## Red Flags - STOP
- "I'll skip the code fence language, it's obvious"
- "Spelling check is too strict, I'll ignore it"
- "Line length doesn't matter, I'll disable the rule"
- "I'll fix the linting errors later"
- "The configuration is broken but I'll write anyway"
- "--no-verify is faster, I'll use that"
## Rationalizations (and Reality)
| Excuse | Reality |
| ------------------------------------------- | -------------------------------------------------------- |
| "Line length doesn't affect readability" | 120 chars is readability standard, pre-commit will fail |
| "Code fence language is obvious from code" | Syntax highlighting requires explicit language |
| "Spelling errors are minor" | Breaks cspell pre-commit hook, blocks entire commit |
| "I'll fix linting issues in next commit" | Creates broken window, forces --no-verify bypasses |
| "Configuration is too strict" | Rules exist for consistency, modify config if needed |
| "Pre-commit hooks will catch it" | Hooks are safety net, skill prevents issues proactively |
## Reference Templates
Choose a template based on project needs:
| Template | Use Case | Line Length | HTML |
| ----------------------------------------------------------------- | ---------------------------- | ----------- | --------- |
| [markdownlint-default.json](templates/markdownlint-default.json) | Most projects | 120 | Allowed |
| [markdownlint-strict.json](templates/markdownlint-strict.json) | Documentation-heavy projects | 80 | Limited |
| [markdownlint-minimal.json](templates/markdownlint-minimal.json) | Simple projects | Unlimited | Allowed |
| [cspell-default.json](templates/cspell-default.json) | All projects | N/A | N/A |
### Quick Setup
```bash
# Default configuration (recommended)
cp templates/markdownlint-default.json.template .markdownlint.json
cp templates/cspell-default.json.template cspell.json
# Strict configuration for docs-heavy projects
cp templates/markdownlint-strict.json.template .markdownlint.json
# Minimal configuration for simple projects
cp templates/markdownlint-minimal.json.template .markdownlint.json
```
## See Also
- [Validation Rules Reference](references/validation-rules.md) - Complete markdownlint rule list
- [Spelling Configuration](references/spelling-configuration.md) - cspell setup and custom dictionary
- [Editor Integration](references/editor-integration.md) - VS Code and other editor setup
- Pre-commit hooks: `.husky/pre-commit` and `package.json` lint-staged configuration
- markdownlint rules: <https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md>
## Before/After Examples
### Line Length (MD013)
**Before (violation):**
```markdown
This is a very long line that exceeds the configured line length limit of 120 characters and will cause the markdownlint check to fail when committed.
```
**After (fixed):**
```markdown
This is a very long line that exceeds the configured line length limit of 120
characters and will cause the markdownlint check to fail when committed.
```
### Heading Hierarchy (MD001)
**Before (violation):**
```markdown
# Main Title
### Skipped Level
Content here jumps from h1 to h3.
```
**After (fixed):**
```markdown
# Main Title
## Proper Level
Content here follows h1 → h2 hierarchy.
```
### Code Fence Language (MD040)
**Before (violation):**
````markdown
```
npm install
npm run build
```
After (fixed):
```bash
npm install
npm run build
```
Spelling Error
Before (violation):
The agentskills module provides reusable behaviours.
After (fixed - add to dictionary):
// cspell.json
{
"words": ["agentskills"]
}
The agentskills module provides reusable behaviours.
Validation Commands
Quick Validation
# Validate single file
npx markdownlint-cli2 "path/to/file.md"
# Validate with auto-fix
npx markdownlint-cli2 --fix "path/to/file.md"
# Check spelling
npx cspell "path/to/file.md"
# Validate all markdown files
npx markdownlint-cli2 "**/*.md"
npx cspell "**/*.md"
Pre-Commit Validation
# Run what pre-commit will run
npx lint-staged --dry-run
# Run lint-staged for real
npx lint-staged
CI Validation
# GitHub Actions example
- name: Lint Markdown
run: npx markdownlint-cli2 "**/*.md"
- name: Check Spelling
run: npx cspell "**/*.md" --no-progress
VS Code Integration
// .vscode/settings.json
{
"editor.formatOnSave": true,
"markdownlint.config": {
"extends": "./.markdownlint.json"
},
"cSpell.enabled": true
}
Validation Script
#!/bin/bash
# validate-markdown.sh
echo "=== Markdown Validation ==="
# Count files
FILES=$(find . -name "*.md" -not -path "./node_modules/*" | wc -l)
echo "Found $FILES markdown files"
# Run markdownlint
echo ""
echo "Running markdownlint..."
if npx markdownlint-cli2 "**/*.md" --ignore node_modules; then
echo "✓ Markdownlint passed"
else
echo "✗ Markdownlint failed"
exit 1
fi
# Run cspell
echo ""
echo "Running cspell..."
if npx cspell "**/*.md" --no-progress; then
echo "✓ Spelling check passed"
else
echo "✗ Spelling check failed"
exit 1
fi
echo ""
echo "=== All checks passed ==="
