quill: add md-heading-normalize v1 with passing verification test

This commit is contained in:
quill authored and ledger committed 2026-10-31 14:11:35 +00:00
1 parent 1633ac3521
commit 4ccc642253
3 files changed
+114

No files matched your search

+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env python3
"""Normalize markdown heading styles to consistent ATX format."""
import re
import sys
_ATX_RE = re.compile(r'^(#{1,})(\s+)(.+)$')
def normalize(text: str) -> str:
lines = text.splitlines()
out = []
i = 0
while i < len(lines):
line = lines[i]
stripped = line.rstrip()
next_line = lines[i + 1].rstrip() if i + 1 < len(lines) else ''
# Setext h1/h2: only if current line is NOT already an ATX heading
if not _ATX_RE.match(stripped):
if re.fullmatch(r'=+\s*$', next_line):
out.append(f'# {stripped}')
i += 2
continue
if re.fullmatch(r'-+\s*$', next_line):
out.append(f'## {stripped}')
i += 2
continue
# ATX heading: cap level at 6
m = _ATX_RE.match(stripped)
if m:
hashes, space, content = m.group(1), m.group(2), m.group(3)
level = min(len(hashes), 6)
out.append(f'{"#" * level}{space}{content}')
i += 1
continue
out.append(stripped)
i += 1
return '\n'.join(out) + ('\n' if text.endswith('\n') else '')
def main() -> int:
if len(sys.argv) < 2:
print('usage: md-heading-normalize.py <file>', file=sys.stderr)
return 2
path = sys.argv[1]
with open(path, 'r', encoding='utf-8') as f:
src = f.read()
sys.stdout.write(normalize(src))
return 0
if __name__ == '__main__':
raise SystemExit(main())
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""Verification test: md-heading-normalize produces correct ATX output."""
import subprocess
import sys
import tempfile
import os
SCRIPT = '/root/.hermes/wizard-outputs/quill/work/md-heading-normalize.py'
CASES = [
# (input, expected_output)
("Title\n=====\n\nBody.\n", "# Title\n\nBody.\n"),
("Section\n-------\n", "## Section\n"),
("# H1\n## H2\n### H3\n#### H4\n##### H5\n###### H6\n####### H7\n",
"# H1\n## H2\n### H3\n#### H4\n##### H5\n###### H6\n###### H7\n"),
("plain text\n# not setext\n===\n", "plain text\n# not setext\n===\n"), # only setext if *next* line is ===
("# H1\nsome text\n## H2\n", "# H1\nsome text\n## H2\n"),
]
passed = 0
failed = 0
for idx, (src, expected) in enumerate(CASES):
with tempfile.NamedTemporaryFile('w', suffix='.md', delete=False) as f:
f.write(src)
path = f.name
try:
result = subprocess.run([sys.executable, SCRIPT, path], capture_output=True, text=True)
got = result.stdout
if got == expected:
passed += 1
else:
failed += 1
print(f'FAIL case {idx}:\n expected={expected!r}\n got={got!r}', file=sys.stderr)
finally:
os.unlink(path)
print(f'PASS={passed} FAIL={failed}')
if failed:
sys.exit(1)
@@ -0,0 +1,26 @@
# Verification Report — md-heading-normalize v1
## Artifact
- Script: `md-heading-normalize.py`
- Test: `test_md_heading_normalize.py`
## Test Results
```
PASS=5 FAIL=0
```
## Cases Covered
1. Setext h1 (`Title` + `=====` → `# Title`)
2. Setext h2 (`Section` + `-------` → `## Section`)
3. ATX cap: `####### H7` → `###### H7`
4. Setext skipped when current line is ATX heading
5. Round-trip preservation of already-normalized ATX headings
## Command
```
python3 test_md_heading_normalize.py
```
## Observed
- Exit code: 0
- 5 tests passed, 0 failed