Initial commit
This commit is contained in:
18
.claude-plugin/plugin.json
Normal file
18
.claude-plugin/plugin.json
Normal 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
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# mr-sparkle
|
||||
|
||||
Automated code quality enforcement with linting and formatting.
|
||||
20
commands/fix-md.md
Normal file
20
commands/fix-md.md
Normal 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
22
commands/lint-md.md
Normal 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
16
hooks/hooks.json
Normal 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
249
hooks/lint-on-write.py
Executable 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
77
plugin.lock.json
Normal 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": []
|
||||
}
|
||||
}
|
||||
412
skills/markdown-quality/SKILL.md
Normal file
412
skills/markdown-quality/SKILL.md
Normal 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.
|
||||
58
skills/markdown-quality/example-config.jsonc
Normal file
58
skills/markdown-quality/example-config.jsonc
Normal 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
|
||||
}
|
||||
198
skills/markdown-quality/pitfalls-reference.md
Normal file
198
skills/markdown-quality/pitfalls-reference.md
Normal 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
|
||||
```
|
||||
55
skills/markdown-quality/rules-reference.md
Normal file
55
skills/markdown-quality/rules-reference.md
Normal 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>
|
||||
192
skills/markdown-quality/troubleshooting.md
Normal file
192
skills/markdown-quality/troubleshooting.md
Normal 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>
|
||||
Reference in New Issue
Block a user