provisioning/scripts/fix-markdown-lint.py

128 lines
4.4 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""Fix markdown linting errors: MD040 (code blocks), MD013 (line length), MD060 (tables)."""
import re
import sys
from pathlib import Path
def fix_code_block_languages(content):
"""Add language specifier to code blocks without one (MD040)."""
# Pattern: opening fence with no language specifier (``` followed by newline or whitespace)
pattern = r'(^```)\n'
# Replace with ```text\n (default language for unspecified blocks)
fixed = re.sub(pattern, r'\1text\n', content, flags=re.MULTILINE)
return fixed
def fix_line_length(content):
"""Break long lines to fit 150 character limit (MD013)."""
lines = content.split('\n')
fixed_lines = []
for line in lines:
# Skip code blocks, tables, and links (these have different rules)
if line.startswith('```') or line.startswith('|') or line.startswith('>'):
fixed_lines.append(line)
continue
# If line is longer than 150 chars and not a special case
if len(line) > 150:
# For paragraphs, try to break at word boundaries
if not line.startswith('#') and not line.startswith('-') and not line.startswith('*'):
words = line.split(' ')
current_line = ''
for word in words:
test_line = current_line + (' ' if current_line else '') + word
if len(test_line) <= 150:
current_line = test_line
else:
if current_line:
fixed_lines.append(current_line)
current_line = word
if current_line:
fixed_lines.append(current_line)
continue
fixed_lines.append(line)
return '\n'.join(fixed_lines)
def fix_table_formatting(content):
"""Fix table formatting: add spaces around pipes (MD060)."""
lines = content.split('\n')
fixed_lines = []
for line in lines:
# Check if this is a table line (contains pipes)
if '|' in line:
# Fix spacing: |text| -> | text |, but preserve already-correct spacing
# Match: | followed immediately by non-space or non-space followed immediately by |
fixed_line = line
# Fix cases like |column| -> | column |
fixed_line = re.sub(r'\|\s*([^\s|][^|]*?[^\s|])\s*\|', r'| \1 |', fixed_line)
# Fix edge case of single character: |a| -> | a |
fixed_line = re.sub(r'\|\s*([^\s|])\s*\|', r'| \1 |', fixed_line)
# Fix leading pipe spacing: |text -> | text
fixed_line = re.sub(r'^\|\s*([^\s|])', r'| \1', fixed_line)
# Fix trailing pipe spacing: text| -> text |
fixed_line = re.sub(r'([^\s|])\s*\|$', r'\1 |', fixed_line)
fixed_lines.append(fixed_line)
else:
fixed_lines.append(line)
return '\n'.join(fixed_lines)
def fix_file(filepath):
"""Fix all markdown linting errors in a single file."""
try:
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
# Apply fixes in order
content = fix_code_block_languages(content)
content = fix_line_length(content)
content = fix_table_formatting(content)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
return True
except Exception as e:
print(f"Error processing {filepath}: {e}", file=sys.stderr)
return False
def main():
"""Fix markdown linting errors in all AI documentation files."""
docs_root = Path('provisioning/docs/src/ai')
files_to_fix = [
'ai-assisted-forms.md',
'architecture.md',
'config-generation.md',
'configuration.md',
'cost-management.md',
'mcp-integration.md',
'natural-language-config.md',
'rag-system.md',
'security-policies.md',
]
success_count = 0
for filename in files_to_fix:
filepath = docs_root / filename
if filepath.exists():
if fix_file(filepath):
print(f"✓ Fixed {filename}")
success_count += 1
else:
print(f"✗ Failed to fix {filename}")
else:
print(f"⚠ File not found: {filename}")
print(f"\n✓ Fixed {success_count}/{len(files_to_fix)} files")
return 0 if success_count == len(files_to_fix) else 1
if __name__ == '__main__':
sys.exit(main())