Zero-dependency GFM and MDX formatting for AI-agent-authored Markdown, with table, pipe, and fence guards.
This repository builds a Hermes-compatible GitHub-Flavored Markdown (GFM) and MDX formatter skill. The formatter is a zero-dependency Node.js module that normalizes Markdown container syntax and leaves fenced code content opaque. Guard scripts enforce table, pipe, and fence safety before formatting and can roll back writes when structure drifts.
Install for Hermes Agent:
hermes skills install CodeSigils/agents-markdown-formatter/markdown-formatter --yesFormat one file safely from an installed skill:
node ~/.hermes/skills/markdown-formatter/src/index.js --fix --guard README.mdVerify a docs directory without writing changes:
node ~/.hermes/skills/markdown-formatter/src/index.js --verify --all docs/Check installed runtime readiness:
node ~/.hermes/skills/markdown-formatter/src/index.js --doctorTo catch table formatting issues (like || double pipes) automatically after
every write_file or patch call, add this to your Hermes config.yaml:
hooks:
post_tool_call:
- command: node ~/.hermes/skills/markdown-formatter/src/index.js --check
matcher: write_fileThis runs --check (read-only) on every written file. It blocks pipe hazards,
fence errors, and formatting drift before they reach git. For auto-repair
instead of blocking, use --fix instead of --check (but --fix modifies
files during write — use with care).
AI agents write a lot of Markdown: READMEs, plans, runbooks, notes, review comments, and MDX documentation. That output often has the same failure modes: trailing whitespace, missing final newlines, inconsistent indentation, fragile tables, and fenced code blocks that should not be reformatted as production source files.
This repository keeps that scope narrow. The formatter handles low-risk presentation normalization, while guard scripts handle structure and safety policy.
Formatter-owned behavior:
- trailing whitespace removal
- final newline insertion
- leading-tab indentation normalization outside fences
- GFM table alignment when tables have no empty-cell ambiguity
- tilde-fence to backtick-fence normalization, with backtick-count escalation when needed
Guard-owned behavior:
- fence closure and malformed fence info strings
- table column counts
- unescaped inline-code pipes in table rows
- adjacent-pipe table hazards
- pre/post structural drift detection and rollback for
--guard
The shipped skill payload has no npm runtime dependencies.
From a source checkout:
node skills/markdown-formatter/src/index.js [options] <path...>Hermes is the first packaged skill target: SKILL.md, the install path, and shipped metadata are Hermes-compatible. The
CLI itself does not require Hermes at runtime.
| Flag | Description |
|---|---|
--check |
Check pipe safety and formatting without writing changes |
--fix |
Format files in-place after pipe-safety preflight; auto-repairs adjacent pipes and column-count mismatches; default behavior |
--all |
Process directory inputs recursively; accepts multiple paths |
--guard |
Enable structural pre/post guard; rolls back on drift and cleans snapshots |
--verify |
Check formatting, idempotence, and structural integrity read-only |
--fences |
Validate fence structure with check-fences.js |
--validate |
Run structural, fence, table, and pipe validations |
--doctor |
Check Node.js and payload readiness without modifying files |
--dry-run, -n |
Run pipe-safety preflight, then show what would change without writing |
--audit-tables |
Print table row cell counts and pipe hazards without writing; use before/after agent table edits |
--no-repair |
In write modes, report repairable table issues instead of modifying them |
--help, -h |
Display help message |
Reference spec for users and agents: GitHub Flavored Markdown Spec.
check-tables.jsenforces formatter-safe table column counts and pipe consistency, including unescaped pipes inside inline code spans. It is stricter than GFM body-row parsing because autonomous formatting should not guess table intent.check-pipes.jsdetects adjacent pipes (||) in table rows, which create valid empty cells per GFM. Write modes (--fix,--guard, default) repair them by inserting a space between the pipes (| |), preserving empty-cell semantics. Read-only modes (--check,--dry-run,--validate) block with a clear error.- Empty-cell tables that remain ambiguous, including no-leading-pipe rows with empty edge cells, are preserved by
skipping the full formatter pass after safety repairs. The delimiter row is still normalized to
3 dashes (
----→---,:-----→:---, etc.) during spacing normalization. - Table validation, structural table snapshots, pipe-safety checks, and automatic table repair ignore table-shaped text inside fenced code blocks.
--guardrestores the original file content if post-format structure changes.- All CLI modes detect unclosed fences before table/pipe checks. When an unclosed fence is found, the CLI warns that table and pipe checks are unreliable and skips them while continuing with fence validation and formatting.
The installed skill payload contains only these files on the user's disk:
~/.hermes/skills/markdown-formatter/
├── SKILL.md
├── src/
│ ├── format-content.mjs
│ └── index.js
└── scripts/
├── check-structure.js
├── check-fences.js
├── check-tables.js
└── check-pipes.js
Repository-only files (README.md, test/, package.json, etc.) are excluded from the shipped payload.
The formatter requires Node.js >=24. For repository development:
npm ciTo diagnose an installed skill without modifying files:
node ~/.hermes/skills/markdown-formatter/src/index.js --doctor--doctor exits 0 when Node.js and required runtime payload files are ready.
test/fixtures/current/— real-world docs that should format cleanlytest/fixtures/format-edge-cases/— formatter edge cases retained from the Oxfmt spiketest/fixtures/pipe-safety/— valid GFM that requires guard behaviortest/fixtures/violations/— structural violations the guard must detecttest/unit/— isolated component tests for formatter and guardstest/integration/— CLI and pipeline end-to-end tests
The skill follows a strict runtime allowlist:
- Shipped: only files under
skills/markdown-formatter/needed at runtime - Excluded: planning docs, tests, fixtures, development tooling, and repository-only metadata
- Verification:
bash scripts/staged-install-verify.shstages and tests the exact runtime payload - Dependency boundary: root
package.jsonandpackage-lock.jsonare repository-only
Recommended release practice:
-
Keep runtime changes and version bumps in separate commits.
-
Before tagging a runtime release, verify the exact commit with:
node scripts/check-consistency.js npm test npm run format:check bash scripts/staged-install-verify.sh -
Push the version-bump commit to main and confirm GitHub Actions is green.
-
Run the release script:
npm run release
The release script validates a clean tree, tag availability, isolated version metadata changes, pushed HEAD, and green CI before tagging. GitHub Release notes are generated from commit subjects since the previous version tag.
skills/markdown-formatter/SKILL.md— packaged skill instructionsskills/markdown-formatter/src/index.js— CLI entrypointskills/markdown-formatter/src/format-content.mjs— formatterscripts/check-consistency.js— repository drift checksscripts/staged-install-verify.sh— staged runtime payload verification