Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:48:47 +08:00
commit 36566db138
12 changed files with 1320 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
{
"name": "mr-sparkle",
"description": "Automated code quality enforcement with linting and formatting.",
"version": "0.1.0",
"author": {
"name": "racurry",
"email": "zhongweili@tubi.tv"
},
"skills": [
"./skills"
],
"commands": [
"./commands"
],
"hooks": [
"./hooks"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# mr-sparkle
Automated code quality enforcement with linting and formatting.

20
commands/fix-md.md Normal file
View File

@@ -0,0 +1,20 @@
---
description: Automatically fix markdown linting issues
argument-hint: [path]
allowed-tools: Bash(markdownlint-cli2:*)
model: haiku
---
Fix markdown files at path `$1` (defaults to current directory if not provided).
Run `markdownlint-cli2 --fix` to automatically correct markdown formatting issues according to the markdown-quality skill rules.
## Usage examples
- `/fix-md` - Fix all markdown files in current directory
- `/fix-md docs/` - Fix markdown files in the docs folder
- `/fix-md README.md` - Fix a specific markdown file
After fixing, the tool will report which files were processed and any issues that couldn't be auto-fixed.
For details on the linting rules and configuration, see the markdown-quality skill.

22
commands/lint-md.md Normal file
View File

@@ -0,0 +1,22 @@
---
description: Lint markdown files and report issues with line numbers
argument-hint: [path]
allowed-tools: Bash(markdownlint-cli2:*)
model: haiku
---
Run `markdownlint-cli2` to check markdown files for formatting issues.
Usage examples:
- `/lint-md` - Lint all markdown files in current directory
- `/lint-md docs/` - Lint files in the docs directory
- `/lint-md guides/getting-started.md` - Lint a specific file
The command reports:
- Which files have issues
- Line numbers where issues occur
- Rule violations and descriptions
For guidance on rule interpretations and fixes, refer to the markdown-quality skill.
Do NOT automatically fix issues - this command is for inspection only. Use `/mr-sparkle:fix-md` to apply automatic fixes.

16
hooks/hooks.json Normal file
View File

@@ -0,0 +1,16 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/lint-on-write.py",
"timeout": 30
}
]
}
]
}
}

249
hooks/lint-on-write.py Executable file
View File

@@ -0,0 +1,249 @@
#!/usr/bin/env -S uv run --quiet --script
# /// script
# dependencies = []
# ///
"""
Generic PostToolUse linting hook for Claude Code.
Reads stdin JSON to extract file paths, detects file type by extension,
and runs appropriate linter commands. Designed for extensibility to support
multiple languages (markdown, JavaScript, Python, Go, etc.).
Exit codes:
- 0: Success or silently skipped (file formatted or no issues)
- 1: Non-blocking error (unfixable linting issues, linter errors)
"""
import json
import subprocess
import sys
from pathlib import Path
from typing import Dict, List, Optional, Tuple
# Configuration: Map file extensions to linter configurations
# Each config includes: command, extensions, success messages, error handling
LINTER_CONFIG = {
"markdown": {
"extensions": [".md", ".markdown"],
"command": ["markdownlint-cli2", "--fix"],
"check_installed": "markdownlint-cli2",
"success_message": "Markdown formatted: {file_path}",
"unfixable_message": "Markdown linting found unfixable issues in {file_path}",
"unfixable_hint": "Run: markdownlint-cli2 {file_path} to see details",
"unfixable_exit_code": 1, # markdownlint-cli2 returns 1 for unfixable issues
},
# Example: Future JavaScript/TypeScript support
# "javascript": {
# "extensions": [".js", ".jsx", ".ts", ".tsx"],
# "command": ["eslint", "--fix"],
# "check_installed": "eslint",
# "success_message": "JavaScript formatted: {file_path}",
# "unfixable_message": "ESLint found unfixable issues in {file_path}",
# "unfixable_hint": "Run: eslint {file_path} to see details",
# "unfixable_exit_code": 1,
# },
# Example: Future Python support
# "python": {
# "extensions": [".py"],
# "command": ["ruff", "check", "--fix"],
# "check_installed": "ruff",
# "success_message": "Python formatted: {file_path}",
# "unfixable_message": "Ruff found unfixable issues in {file_path}",
# "unfixable_hint": "Run: ruff check {file_path} to see details",
# "unfixable_exit_code": 1,
# },
}
def read_stdin_json() -> Dict:
"""Read and parse JSON from stdin (hook input)."""
try:
return json.load(sys.stdin)
except json.JSONDecodeError:
# Invalid JSON - exit silently (non-blocking)
sys.exit(0)
def extract_file_path(hook_input: Dict) -> Optional[str]:
"""Extract file_path from tool_input in hook JSON."""
tool_input = hook_input.get("tool_input", {})
file_path = tool_input.get("file_path")
# Handle both string and null/missing values
if not file_path or not isinstance(file_path, str):
return None
return file_path
def get_linter_config(file_path: str) -> Optional[Tuple[str, Dict]]:
"""
Determine linter config based on file extension.
Returns:
Tuple of (language_name, config_dict) or None if no match
"""
file_ext = Path(file_path).suffix.lower()
for language, config in LINTER_CONFIG.items():
if file_ext in config["extensions"]:
return (language, config)
return None
def check_tool_installed(command_name: str) -> bool:
"""Check if a command-line tool is installed and available."""
try:
subprocess.run(
["command", "-v", command_name],
check=True,
capture_output=True,
shell=False
)
return True
except subprocess.CalledProcessError:
return False
def run_linter(file_path: str, config: Dict) -> Tuple[int, str]:
"""
Run linter command on file.
Returns:
Tuple of (exit_code, output)
"""
command = config["command"] + [file_path]
try:
result = subprocess.run(
command,
capture_output=True,
text=True,
timeout=60 # Safety timeout
)
# Combine stdout and stderr for complete output
output = result.stdout + result.stderr
return (result.returncode, output.strip())
except subprocess.TimeoutExpired:
return (1, f"Linter timed out after 60 seconds")
except Exception as e:
return (1, f"Linter execution error: {str(e)}")
def format_message(template: str, **kwargs) -> str:
"""Format message template with provided variables."""
return template.format(**kwargs)
def output_json_response(system_message: Optional[str] = None, additional_context: Optional[str] = None, decision: Optional[str] = None, reason: Optional[str] = None):
"""Output JSON response to stdout for Claude to process."""
response = {}
if decision:
response["decision"] = decision
if reason:
response["reason"] = reason
if system_message:
response["systemMessage"] = system_message
if additional_context:
response["hookSpecificOutput"] = {
"hookEventName": "PostToolUse",
"additionalContext": additional_context
}
print(json.dumps(response), flush=True)
def main():
"""Main hook execution logic."""
# Debug: log that hook was triggered
import datetime
with open("/tmp/hook-test.log", "a") as f:
f.write(f"{datetime.datetime.now()}: Hook triggered\n")
# Read hook input from stdin
hook_input = read_stdin_json()
# Extract file path from tool_input
file_path = extract_file_path(hook_input)
# Exit silently if no file path (not a file write/edit operation)
if not file_path:
sys.exit(0)
# Verify file exists
if not Path(file_path).is_file():
sys.exit(0)
# Get linter configuration for this file type
linter_info = get_linter_config(file_path)
# Exit silently if no linter configured for this file type
if not linter_info:
sys.exit(0)
_language, config = linter_info
# Check if linter tool is installed
if not check_tool_installed(config["check_installed"]):
# Not installed - exit silently (non-blocking)
# User can install the tool if desired
sys.exit(0)
# Run the linter
exit_code, output = run_linter(file_path, config)
# Handle linter results
if exit_code == 0:
# Success - file was clean or successfully fixed
success_msg = format_message(
config["success_message"],
file_path=file_path
)
output_json_response(system_message=success_msg)
sys.exit(0)
elif exit_code == config.get("unfixable_exit_code", 1):
# Linter found issues that couldn't be auto-fixed
unfixable_msg = format_message(
config["unfixable_message"],
file_path=file_path
)
# Build complete context message
context_parts = [unfixable_msg]
if output:
context_parts.append(output)
if "unfixable_hint" in config:
hint = format_message(
config["unfixable_hint"],
file_path=file_path
)
context_parts.append(hint)
full_context = "\n".join(context_parts)
output_json_response(system_message=full_context)
sys.exit(0)
else:
# Unexpected exit code - report error
error_msg = f"Linter error (exit code {exit_code}) in {file_path}:"
context_parts = [error_msg]
if output:
context_parts.append(output)
full_context = "\n".join(context_parts)
output_json_response(system_message=full_context)
sys.exit(0)
if __name__ == "__main__":
main()

77
plugin.lock.json Normal file
View File

@@ -0,0 +1,77 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:racurry/neat-little-package:plugins/mr-sparkle",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "f248c18db7862675380a41980e261ce61aadfeb5",
"treeHash": "d9eca1dc2f9990e5ca9cfb3c7ec0f37f4a77fe7495979820056473eb33f0bfca",
"generatedAt": "2025-11-28T10:27:42.880995Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "mr-sparkle",
"description": "Automated code quality enforcement with linting and formatting.",
"version": "0.1.0"
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "124355aecd6ac5003307da3bc9a8b6e3acd5581af7b80979c9054f87c6937790"
},
{
"path": "hooks/hooks.json",
"sha256": "68139e1c6a4069eeadd5d2883e9db309cc7b506172810365b82d3a538e65e657"
},
{
"path": "hooks/lint-on-write.py",
"sha256": "b39886689a8b6910e769cedcbab735828d2b8766f0a1718345d141491a330e3b"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "fa945e85598b991b5d14eb7a41dde2de92704440b8cff5be2e6149424733ca47"
},
{
"path": "commands/lint-md.md",
"sha256": "828c76c959ffa777b6f57d7eb1e65ab42d098ec94475526c490da0e8d5e85440"
},
{
"path": "commands/fix-md.md",
"sha256": "3c9178b1a3ef79447600611891629b352e61cf539f9e82229dac2c8684d06710"
},
{
"path": "skills/markdown-quality/troubleshooting.md",
"sha256": "96e599faab2eca258b10c55b63e9fde0c4db770f857db2aea9b1ad95b018f329"
},
{
"path": "skills/markdown-quality/rules-reference.md",
"sha256": "4daed4e73d5fcfb0a6ef2ab1ed6d61d5bbbe58f629beb4952e559f6fcd3ff57f"
},
{
"path": "skills/markdown-quality/example-config.jsonc",
"sha256": "f2399af51d33235dbf957ee6dd9e513092f6d77ec51f881b5a03034bb81fc3b0"
},
{
"path": "skills/markdown-quality/SKILL.md",
"sha256": "3279c72a94475ef4e605b30c13b1a9c909ca4739659ce08a8cb313aa37a3771e"
},
{
"path": "skills/markdown-quality/pitfalls-reference.md",
"sha256": "c0e48a9872280ff4be48af37f0f4872923e4015a922b3c7ce7afdb3d80073587"
}
],
"dirSha256": "d9eca1dc2f9990e5ca9cfb3c7ec0f37f4a77fe7495979820056473eb33f0bfca"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,412 @@
---
name: markdown-quality
description: Interpretive guidance for applying markdownlint rules using our opinionated configuration. Use when creating or editing markdown files, configuring markdownlint, troubleshooting linting issues, or reviewing markdown quality.
---
# Markdown Quality Skill
This skill teaches how to interpret and apply markdownlint rules effectively using our opinionated configuration. It provides guidance on what the rules mean in practice, when exceptions are appropriate, and how our configuration balances strictness with flexibility.
## Required Reading Before Editing Markdown
Fetch official markdownlint documentation every time:
- **https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md** - Complete rule reference with examples
- **https://github.com/DavidAnson/markdownlint-cli2** - CLI tool documentation and configuration options
**Why fetch-first:** Rules and best practices evolve. This skill interprets the rules; official docs define them.
## Core Understanding
### Our Markdown Philosophy
**Key principle:** Prioritize readability and practical modern usage over rigid formatting constraints.
**What this means:**
- **No line length limits** - Write naturally; let content breathe
- **Specific HTML allowed** - Markdown can't do everything; HTML fills gaps
- **Flexible document structure** - First line can be badges, images, or other metadata
- **Strict code formatting** - Always fenced blocks with language specification
**Decision test:** Does this rule enhance readability and consistency, or does it create unnecessary friction?
### Markdownlint Architecture
**How markdownlint works:**
1. Parses markdown into AST (Abstract Syntax Tree)
2. Applies configured rules to AST nodes
3. Reports violations with line numbers
4. Auto-fixes simple formatting issues (when possible)
**Important:** Not all violations are auto-fixable. Structural issues (heading hierarchy, link format) often require manual intervention.
## Configuration Approach (Best Practices)
### Layer 1: Default Rules (Official Specification)
**Official behavior:** When `"default": true`, all rules are enabled unless explicitly overridden.
**From official docs:** This is the recommended baseline - comprehensive coverage of common markdown issues.
### Layer 2: Opinionated Overrides (Best Practices)
**Our philosophy-driven configuration:**
```jsonc
{
"config": {
"default": true, // Start with all rules
// Readability over rigid constraints
"MD013": false, // No line length limits
// Practical HTML usage
"MD033": {
"allowed_elements": ["br", "details", "summary", "img", "kbd", "sub", "sup"]
},
// Modern document patterns
"MD041": false, // First line doesn't need to be H1
// Flexible but organized
"MD024": {
"siblings_only": true // Duplicate headings OK if not siblings
},
// Strict code formatting
"MD046": {
"style": "fenced" // Always fenced, never indented
}
}
}
```
**Why these overrides work:**
- **MD013 disabled** - Modern editors handle long lines; artificial line breaks harm readability
- **MD033 selective** - Allows semantic HTML that markdown can't express
- **MD041 disabled** - Enables badges, shields.io, and metadata-first documents
- **MD024 siblings_only** - Allows "Usage" sections under different features
- **MD046 strict** - Fenced blocks are explicit, syntax-highlighting compatible
## Document Structure (Official Specification)
**From markdownlint docs:**
- **MD001** - Heading increment by one level at a time (no skipping)
- **MD022** - Headings surrounded by blank lines
- **MD025** - Single H1 per document
- **MD032** - Lists surrounded by blank lines
**Syntax:**
```markdown
# Document Title
Introductory paragraph.
## Section Heading
Content here.
### Subsection
More content.
```
## Document Structure (Best Practices)
**When hierarchy matters:**
- ✅ Use proper nesting for table of contents generation
- ✅ One H1 per document (document title)
- ✅ Sections flow logically (H2 → H3, never H2 → H4)
**When to bend rules:**
- First line can be badges/images (MD041 disabled)
- Duplicate H2 headings OK if under different H1s (MD024 siblings_only)
**Anti-pattern:**
```markdown
# Title
#### Subsection <!-- ❌ Skipped levels -->
```
**Better:**
```markdown
# Title
## Section
### Subsection <!-- ✅ Proper hierarchy -->
```
## Code Blocks (Official Specification)
**Required behavior (MD046):**
- MUST use fenced code blocks (triple backticks)
- MUST NOT use indented code blocks (4 spaces)
- SHOULD specify language for syntax highlighting
## Code Blocks (Best Practices)
**Always specify language:**
```markdown
<!-- ❌ No language specified -->
```
code here
```
<!-- ✅ Language specified -->
```javascript
code here
```
```
**Why:** Enables syntax highlighting, makes intent clear, helps tools process content.
**Language identifiers:**
- `javascript`, `typescript`, `python`, `bash`, `json`, `yaml`, `markdown`
- Use `text` or `plaintext` when no language applies
## Allowed HTML Elements (Best Practices)
**Our configuration allows specific HTML when markdown is insufficient:**
| Element | Use Case | Example |
|---------|----------|---------|
| `<br>` | Line breaks within paragraphs | `Line one<br>Line two` |
| `<details>` + `<summary>` | Collapsible sections | See below |
| `<img>` | Advanced image attributes | `<img src="..." width="100">` |
| `<kbd>` | Keyboard input styling | Press `<kbd>Ctrl</kbd>+<kbd>C</kbd>` |
| `<sub>` / `<sup>` | Subscript/superscript | H<sub>2</sub>O, x<sup>2</sup> |
**Collapsible section pattern:**
```markdown
<details>
<summary>Click to expand</summary>
Content here (can include markdown).
</details>
```
**Anti-pattern:**
```markdown
<!-- ❌ Arbitrary HTML -->
<div class="custom">Content</div>
<!-- ✅ Use allowed elements only -->
<details>
<summary>Custom Section</summary>
Content
</details>
```
## Links and Emphasis (Official Specification)
**From markdownlint rules:**
- **MD034** - No bare URLs (must be in angle brackets or link syntax)
- **MD037** - No spaces inside emphasis markers
- **MD049/MD050** - Consistent emphasis style
## Links and Emphasis (Best Practices)
**Link formatting:**
```markdown
<!-- ❌ Bare URL -->
Check https://example.com for details
<!-- ✅ Proper link syntax -->
Check <https://example.com> for details
Check [documentation](https://example.com) for details
```
**Emphasis consistency:**
```markdown
<!-- ✅ Consistent style -->
Use **bold** for strong emphasis
Use *italic* for emphasis
<!-- ❌ Inconsistent -->
Use **bold** and __bold__ mixed
```
**Spacing:**
```markdown
<!-- ❌ Spaces inside markers -->
** bold text **
<!-- ✅ No spaces -->
**bold text**
```
## Common Pitfalls
### Pitfall #1: Heading Hierarchy Violations
**Problem:** Skipping heading levels breaks document outline and navigation.
```markdown
# Main Title
### Subsection <!-- ❌ MD001: Skipped H2 level -->
```
**Why it fails:** Screen readers, table-of-contents generators, and navigation tools expect proper hierarchy.
**Better:**
```markdown
# Main Title
## Section
### Subsection <!-- ✅ Proper progression -->
```
### Pitfall #2: Indented Code Blocks
**Problem:** Using 4-space indentation instead of fenced blocks.
```markdown
<!-- ❌ Indented code block (MD046) -->
const x = 1;
console.log(x);
```
**Why it fails:** No language specification, less explicit, harder to process.
**Better:**
```markdown
<!-- ✅ Fenced code block with language -->
```javascript
const x = 1;
console.log(x);
```
```
### Pitfall #3: Missing Blank Lines
**Problem:** No spacing around structural elements.
```markdown
# Heading
Content immediately after heading
- List item <!-- ❌ MD032: No blank line before list -->
```
**Why it fails:** Reduces readability, can cause parsing ambiguities.
**Better:**
```markdown
# Heading
Content with proper spacing.
- List item <!-- ✅ Blank lines around structural elements -->
```
### Pitfall #4: Trailing Whitespace
**Problem:** Invisible spaces at end of lines.
**Why it fails:** Causes unnecessary diff noise, some markdown parsers interpret as hard breaks.
**Detection:** Most editors can highlight trailing whitespace.
**Fix:** Configure editor to strip trailing whitespace on save, or run `markdownlint-cli2 --fix`.
## Automatic Hook Behavior
The mr-sparkle plugin includes a PostToolUse hook that:
1. Triggers after Write and Edit operations
2. Detects markdown files (`.md`, `.markdown`)
3. Runs `markdownlint-cli2 --fix` automatically
4. Reports unfixable issues (non-blocking)
5. Silently skips if markdownlint-cli2 not installed
**What this means:** Most formatting issues auto-fix on save. Pay attention to reported unfixable issues.
## Quality Checklist
**Before finalizing markdown:**
**From official rules:**
- ✓ Proper heading hierarchy (no skipped levels)
- ✓ Blank lines around headings, lists, code blocks
- ✓ Code blocks are fenced with language specified
- ✓ No trailing whitespace
- ✓ Links use proper syntax (not bare URLs)
- ✓ Single H1 per document
**Best practices:**
- ✓ Only allowed HTML elements used
- ✓ Consistent emphasis style (`**bold**`, `*italic*`)
- ✓ Language specified for all code blocks
- ✓ Collapsible sections use `<details>` + `<summary>`
- ✓ Natural line length (no artificial breaks)
## Commands
### `/mr-sparkle:lint-md [path]`
**Use when:**
- Reviewing existing markdown files
- Checking documentation before commit
- Identifying issues without making changes
- Learning which rules apply to your content
**Example:** `/mr-sparkle:lint-md docs/` or `/mr-sparkle:lint-md README.md`
### `/mr-sparkle:fix-md [path]`
**Use when:**
- Cleaning up formatting issues
- Preparing markdown for commit
- Batch-fixing multiple files
- After configuration changes
**Note:** Not all issues can be auto-fixed. Structural problems require manual intervention.
**Example:** `/mr-sparkle:fix-md docs/` or `/mr-sparkle:fix-md .`
## Reference Documentation
**Detailed guides** (loaded on-demand for progressive disclosure):
- `rules-reference.md` - Complete table of markdownlint rules enforced
- `pitfalls-reference.md` - Common mistakes with detailed examples
- `troubleshooting.md` - Debugging guide for common issues
- `example-config.jsonc` - Full configuration file example
**Official documentation to fetch:**
- https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md - Complete rule reference
- https://github.com/DavidAnson/markdownlint-cli2 - CLI tool and configuration
- https://commonmark.org/ - CommonMark standard
- https://github.github.com/gfm/ - GitHub Flavored Markdown
**Remember:** This skill interprets markdownlint rules and our configuration philosophy. Always fetch official docs for current rule definitions and syntax details.

View File

@@ -0,0 +1,58 @@
{
// Configuration for markdownlint-cli2
// See https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md for rule details
"config": {
// Default state for all rules
"default": true,
// MD013/line-length - Line length (disabled)
"MD013": false,
// MD033/no-inline-html - Inline HTML
"MD033": {
"allowed_elements": [
"br",
"details",
"summary",
"img",
"kbd",
"sub",
"sup"
]
},
// MD041/first-line-heading - First line in file should be a top level heading
"MD041": false,
// MD024/no-duplicate-heading - Multiple headings with the same content
"MD024": {
"siblings_only": true
},
// MD046/code-block-style - Code block style
"MD046": {
"style": "fenced"
}
},
// Globs to ignore
"ignores": [
"node_modules/**",
"**/node_modules/**",
"vendor/**",
".git/**",
"**/.git/**",
"dist/**",
"build/**",
".next/**",
"coverage/**",
".venv/**",
"venv/**"
],
// Enable glob matching for .gitignore files
"gitignore": true,
"noProgress": true
}

View File

@@ -0,0 +1,198 @@
# Common Markdown Pitfalls
Detailed examples of common mistakes and how to fix them.
## Pitfall #1: Inconsistent Heading Styles
**Problem:** Mixing ATX and setext heading styles
```markdown
# Heading 1
Heading 2
---------
```
**Why it fails:** MD003 requires consistent heading style (we use ATX)
**Better:**
```markdown
# Heading 1
## Heading 2
```
## Pitfall #2: Missing Code Block Languages
**Problem:** Fenced code blocks without language specifiers
````markdown
```
code here
```
````
**Why it fails:** MD040 requires language specification for syntax highlighting
**Better:**
````markdown
```javascript
code here
```
````
## Pitfall #3: Using Indented Code Blocks
**Problem:** Using indentation instead of fences
```markdown
Some text
code here
more code
```
**Why it fails:** MD046 requires fenced code blocks (our style setting)
**Better:**
````markdown
Some text
```javascript
code here
more code
```
````
## Pitfall #4: Trailing Whitespace
**Problem:** Invisible spaces at end of lines (hard to spot)
**Why it fails:** MD009 - trailing spaces can cause rendering issues
**Better:** Configure editor to show/remove trailing whitespace automatically
## Pitfall #5: No Blank Lines Around Elements
**Problem:**
```markdown
# Heading
Content immediately after
- List item
More content
```
**Why it fails:** MD022, MD032 require spacing for readability
**Better:**
```markdown
# Heading
Content with proper spacing
- List item
More content
```
## Pitfall #6: Disallowed Inline HTML
**Problem:** Using HTML elements not in our allowed list
```markdown
This is <span style="color: red">red text</span>
```
**Why it fails:** MD033 - only specific elements are allowed
**Better:** Use markdown emphasis or allowed HTML elements:
```markdown
This is **important text**
Use <kbd>Ctrl</kbd>+<kbd>C</kbd> to copy
```
## Pitfall #7: Skipping Heading Levels
**Problem:**
```markdown
# Main Heading
### Subheading (skipped H2)
```
**Why it fails:** MD001 requires proper heading hierarchy
**Better:**
```markdown
# Main Heading
## Section Heading
### Subheading
```
## Pitfall #8: Multiple Top-Level Headings
**Problem:**
```markdown
# Introduction
Some content
# Another Top-Level Heading
```
**Why it fails:** MD025 requires single H1 per document
**Better:**
```markdown
# Introduction
## Another Section Heading
```
## Pitfall #9: Bare URLs
**Problem:**
```markdown
Check out https://example.com for more info
```
**Why it fails:** MD034 requires proper link syntax
**Better:**
```markdown
Check out <https://example.com> or [Example Site](https://example.com) for more info
```
## Pitfall #10: Inconsistent List Markers
**Problem:**
```markdown
- Item one
* Item two
- Item three
```
**Why it fails:** MD004 requires consistent list markers
**Better:**
```markdown
- Item one
- Item two
- Item three
```

View File

@@ -0,0 +1,55 @@
# Markdownlint Rules Reference
Complete reference of markdownlint rules enforced in our configuration.
## Rules Enforced
| Rule | Description | Fix |
|------|-------------|-----|
| MD001 | Heading levels increment by one | Don't skip from H1 to H3 |
| MD003 | Heading style (ATX vs setext) | Use `#` syntax consistently |
| MD004 | List marker style | Use consistent markers (all `-` or all `*`) |
| MD007 | List indentation | Use 2 or 4 spaces consistently |
| MD009 | No trailing spaces | Remove spaces at line ends |
| MD012 | No multiple blank lines | Use single blank lines |
| MD018 | Space after hash in heading | `# Heading` not `#Heading` |
| MD019 | Multiple spaces after hash | Use single space after `#` |
| MD022 | Headings surrounded by blank lines | Add blank lines before/after |
| MD024 | Duplicate headings (siblings only) | OK if not siblings |
| MD025 | Single H1 per document | Only one top-level heading |
| MD026 | No trailing punctuation in headings | Remove `!?.` from headings |
| MD030 | Space after list marker | `- Item` not `-Item` |
| MD031 | Code blocks surrounded by blank lines | Add spacing around code |
| MD032 | Lists surrounded by blank lines | Add spacing around lists |
| MD033 | Inline HTML (specific elements allowed) | Use allowed elements only |
| MD037 | No spaces inside emphasis | `**text**` not `** text **` |
| MD040 | Code blocks should specify language | Add language to ``` blocks |
| MD046 | Code block style (fenced only) | Use ``` not indentation |
| MD047 | Single trailing newline | Files must end with newline |
| MD049 | Emphasis style consistency | Use consistent `*` or `_` style |
| MD050 | Strong emphasis style consistency | Use consistent `**` or `__` style |
## Disabled Rules
| Rule | Description | Why Disabled |
|------|-------------|--------------|
| MD013 | Line length | Write naturally, no arbitrary limits |
| MD041 | First line heading | Badges, images, etc. are fine at top |
## Rule Categories
**Heading rules:** MD001, MD003, MD018, MD019, MD022, MD024, MD025, MD026
**List rules:** MD004, MD007, MD030, MD032
**Code block rules:** MD031, MD040, MD046
**Whitespace rules:** MD009, MD012, MD047
**Inline rules:** MD033, MD034, MD037, MD049, MD050
## Complete Rule Documentation
For detailed explanations of each rule:
<https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md>

View File

@@ -0,0 +1,192 @@
# Troubleshooting Guide
Common issues and solutions for markdownlint-cli2 and the code-quality plugin.
## Hook Not Running
**Symptoms:** Markdown files created but not linted
**Causes:**
1. markdownlint-cli2 not installed → Install with npm
2. Hook disabled in settings → Check `.claude/settings.json` or `.claude/hooks.json`
3. File extension not .md or .markdown → Rename file or update hook configuration
4. Hook script not executable → Check file permissions
5. Wrong working directory → Hook runs from project root
**Solution:**
```bash
# Install markdownlint-cli2 (using local/project npm)
npm install --save-dev markdownlint-cli2
# Or install globally (alternative)
npm install -g markdownlint-cli2
# Test manually
markdownlint-cli2 --fix yourfile.md
# Check hook configuration
cat .claude/hooks.json
```
## Unfixable Issues
**Symptoms:** Hook reports issues that remain after fixing
**Causes:**
1. Rule requires manual judgment (e.g., heading hierarchy, heading content)
2. Content violates multiple conflicting rules
3. Structural issues (missing language, wrong heading level)
4. Semantic issues (duplicate headings, improper nesting)
**Solution:** Run `/mr-sparkle:lint-md` to see detailed error messages, then fix manually
**Common unfixable issues:**
- Heading hierarchy violations (must restructure manually)
- Multiple H1 headings (decide which to demote)
- Missing code block language (must choose appropriate language)
- Duplicate heading content (reword one of them)
## Configuration Not Applied
**Symptoms:** Custom rules not taking effect
**Causes:**
1. Configuration file in wrong location (see file precedence below)
2. Invalid JSON syntax (use a JSON validator)
3. Wrong filename or precedence issue
4. Parent directory config overriding your settings
5. Cached configuration (stale)
6. Using markdownlint file instead of markdownlint-cli2 file
**Solution:**
```bash
# Validate JSON syntax
npx jsonlint .markdownlint-cli2.jsonc
# Check which config file is being used
markdownlint-cli2 --help # Shows search order
# Test with explicit config
markdownlint-cli2 --config .markdownlint-cli2.jsonc "**/*.md"
# Debug config loading
markdownlint-cli2 yourfile.md 2>&1 | grep -i config
```
**File precedence reminder:**
1. `.markdownlint-cli2.jsonc` (highest priority, recommended)
2. `.markdownlint-cli2.yaml`
3. `.markdownlint.jsonc`
4. `.markdownlint.json`
For complete configuration details, fetch the official documentation: https://github.com/DavidAnson/markdownlint-cli2
## Linting Errors on Valid Markdown
**Symptoms:** Markdownlint reports errors on markdown that renders correctly
**Cause:** Markdown renderers are permissive; linters enforce stricter standards
**Solution:** This is expected behavior - linting enforces best practices even if renderers are forgiving
**Example:**
```markdown
#No space after hash - renders fine but fails MD018
```
Better to follow the standard for consistency.
## Performance Issues
**Symptoms:** Linting is slow on large repositories
**Solutions:**
1. Add appropriate ignore patterns to `.markdownlint-cli2.jsonc`
2. Use `.gitignore` integration (already enabled in our config)
3. Lint specific directories instead of entire tree
4. Exclude large generated files
```json
{
"ignores": [
"node_modules/**",
"dist/**",
"build/**",
"**/*.min.md",
"vendor/**"
]
}
```
## Command Not Found
**Symptoms:** `markdownlint-cli2: command not found`
**Causes:**
1. Not installed globally
2. npm global bin directory not in PATH
3. Installed locally instead of globally
**Solution:**
```bash
# Install globally
npm install -g markdownlint-cli2
# Or use npx (no installation needed)
npx markdownlint-cli2 "**/*.md"
# Or install locally and use npm scripts
npm install --save-dev markdownlint-cli2
# Then add to package.json scripts
```
## False Positives
**Symptoms:** Linter flags correct markdown as errors
**Solution:** If a rule doesn't fit your workflow, disable it in config:
```json
{
"config": {
"MD013": false // Disable specific rule
}
}
```
Or disable inline for specific cases:
```markdown
<!-- markdownlint-disable MD013 -->
This very long line won't be flagged
<!-- markdownlint-enable MD013 -->
```
## Hook Returns Non-Zero Exit
**Symptoms:** Hook fails and blocks Claude operations
**Cause:** Unfixable linting errors return exit code 1 (by design, non-blocking)
**Expected behavior:** The hook should report errors but NOT block the operation
**If blocking occurs:** Check hook configuration - should use `exit 1` (report) not `exit 2` (block)
## Getting Help
1. Check `pitfalls-reference.md` for common mistakes
2. Check `rules-reference.md` for rule details
3. Run `/mr-sparkle:lint-md` for detailed error messages
4. Visit <https://github.com/DavidAnson/markdownlint-cli2/issues>
5. Check markdownlint rules: <https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md>