Skip to content

Repository files navigation

zero-md-formatter

GitHub Release npm version npm downloads CI

Zero-dependency GFM and MDX formatter with structural guardrails — trailing whitespace removal, table alignment, fence normalization, pipe-safety checks, column-count enforcement, and drift detection.

Designed for AI-agent workflows but works anywhere Node.js >=24 runs.

npm install -g zero-md-formatter
mdfmt --fix README.md

The CLI and formatter module have zero npm runtime dependencies. Installable on any system with Node.js >=24.


Quick start

Install from npm

npm install -g zero-md-formatter
mdfmt --fix README.md

Requires Node.js >=24. Zero runtime npm dependencies — no config file, no plugin system.

With pnpm:

pnpm add -g zero-md-formatter
mdfmt --fix README.md

Update an existing global pnpm installation with pnpm -g update zero-md-formatter. If the global dependency is pinned to an exact version, use pnpm add -g zero-md-formatter@latest to advance it explicitly.

Node.js support policy

The package supports Node.js 24 and newer. CI validates Node.js 24.x and the current Node.js 25.x line; Node.js versions below 24 are not supported. The .node-version file remains the local development baseline.

Use via npx (no install)

npx zero-md-formatter --fix README.md

Use programmatically

import { formatContent } from 'zero-md-formatter';

const result = formatContent(rawMarkdown);
console.log(result);

Run from source

git clone https://github.com/CodeSigils/zero-md-formatter.git
cd zero-md-formatter
node src/index.js --fix --guard README.md

What it does

Formatter-owned behavior:

  • Remove trailing whitespace
  • Ensure a final newline
  • Normalize leading-tab indentation outside fenced code blocks
  • Align GFM table columns when the table has no empty-cell ambiguity
  • Normalize tilde fences to backtick fences, escalating the backtick count when nested content requires it; existing backtick fences are never modified

Guard-owned behavior:

  • Fence closure and malformed fence info strings
  • Table column counts (header vs delimiter vs data row alignment)
  • Unescaped inline-code pipes in table rows
  • Adjacent-pipe table hazards (||| |)
  • Pre/post structural drift detection and rollback when --guard is used

What it doesn't do

  • No formatting config file — no .prettierrc, .markdownlintrc, or similar. No plugin system. Zero runtime dependencies means no extension points.
  • No dialect extensions — no Obsidian wiki-links, Mermaid, Pandoc, or frontmatter semantics.
  • No JSX/MDX validation — formats Markdown containers only; JSX inside is passed through unchecked.

CLI reference

mdfmt [options] <path...>
Flag Description
--check Read-only pipe-safety and format check (exit 0 if clean)
--fix Format files in-place after pipe-safety preflight (default)
--all Process directories recursively
--guard Pre/post structural check; rollback on drift; clean snapshots
--verify Run formatting, idempotence, and structural checks without writing
--fences Validate fenced code block info strings
--validate Run all structural validations
--doctor Check runtime prerequisites without modifying files
--dry-run, -n Run pipe-safety preflight, preview changes without writing
--audit-tables Print table row cell counts and pipe hazards without writing
--no-repair Report repairable table issues instead of modifying them
--version Print version number and exit
--help, -h Display help message

File exclusion

Create .mdfmtignore in the project root to exclude files from --all and explicit path processing. One pattern per line; # for comments. Patterns ending with / match directories; glob characters are honored there too (build*/, **/tmp/).

  • * matches any characters within a single path segment
  • ** matches across any number of path segments (docs/**/*.md, **/generated/)
  • Everything else matches literally or as a path prefix
  • Patterns without a / are matched against the project root only (*.md does not match docs/a.md)
  • ? and [...] have no special meaning and match literally; ! negation is not supported
# Skip vendored docs and generated output
vendor/
docs/generated/
*.generated.md
docs/**/*.draft.md
**/internal/

Dot-directories are scanned by default as of v1.5.0. Previously hidden folders like .github/ were always skipped; add them to .mdfmtignore to preserve that behavior:

.github/
.agents/
.opencode/

Examples

# Check formatting (read-only, CI-safe)
mdfmt --check README.md

# Format with rollback-safe structural guards
mdfmt --fix --guard docs/

# Validate structure across a directory
mdfmt --validate --all docs/

# Diagnose installed readiness
mdfmt --doctor

Table and pipe safety

GFM tables are notoriously fragile in agent-generated Markdown. This formatter includes guard scripts that catch the most common failure modes before formatting:

  • Adjacent pipes (||) create empty cells per GFM. Write modes automatically insert a space (| |), preserving empty-cell semantics. Read-only modes block with a clear error.
  • Inline-code pipes (| cmd | opt | title |) look like extra columns to naive formatters. Guard scripts detect them and block formatting before corruption.
  • Column drift — rows with mismatched column counts are detected and, in write mode, repaired by padding short rows or rolling back on structural drift.
  • Empty-cell tables that remain ambiguous are preserved by skipping the full formatter pass. The delimiter row is still normalized to GFM-canonical width.
  • Unclosed-fence preflight — all modes detect unclosed fences before running table/pipe checks and skip validation that cannot be trusted while a fence is open. Read-only and guarded modes fail without modifying the file; unguarded write modes warn and continue formatting around the open fence.

Table-shaped content inside fenced code blocks is always left untouched.


Agent skill usage

The formatter ships as a standard agentskills-compatible skill via SKILL.md. It works with any agent that supports agentskills.io-formatted skills.

The optional Codex behavioral regression checks that an agent both performs guarded formatting and respects a blocking pipe hazard. Normal CI runs the deterministic behavior self-tests as part of npm test.

Install as a skill

With the standard skills CLI:

npx skills add CodeSigils/zero-md-formatter --skill markdown-formatter
Hermes Agent

Recommended for development — clone the repo and add to external_dirs:

skills:
  external_dirs:
    - /path/to/zero-md-formatter/skills

Every commit is immediately reflected without reinstalling.

For end users — install from hub:

# Add repo as skill tap (one-time), then install
hermes skills tap add CodeSigils/zero-md-formatter
hermes skills install CodeSigils/zero-md-formatter/markdown-formatter --yes

Then use the formatter via npm (recommended — gives mdfmt binary):

npm install -g zero-md-formatter
mdfmt --fix --guard README.md

Or run from source (no npm install):

node src/index.js --fix --guard README.md

For auto-wiring on every write_file or patch call — the hook script ships with the skill. You just need to register it:

# The script is already at:
#   ~/.hermes/skills/markdown-formatter/scripts/check-markdown.sh
# No download needed.

Then add the hook to config.yaml:

hooks:
  post_tool_call:
    - command: ~/.hermes/skills/markdown-formatter/scripts/check-markdown.sh
      matcher: write_file
    - command: ~/.hermes/skills/markdown-formatter/scripts/check-markdown.sh
      matcher: patch

This runs --fix --guard on every written Markdown file — formatting, repairing adjacent pipes, normalizing fences, aligning tables, and rolling back on structural drift before they reach git.

Codex CLI

For a repo-specific Codex skill, copy the tap payload into .agents/skills:

mkdir -p .agents/skills
cp -R skills/markdown-formatter .agents/skills/markdown-formatter

For a user-wide Codex skill, copy it to $HOME/.agents/skills instead. Codex also works directly with the CLI:

npm install -g zero-md-formatter
mdfmt --fix --guard README.md
Claude Code / OpenCode / Gemini CLI

All three can run the formatter as a normal shell CLI:

npm install -g zero-md-formatter
mdfmt --fix --guard README.md

Or clone the source and run the bundled CLI directly:

git clone https://github.com/CodeSigils/zero-md-formatter.git
node zero-md-formatter/src/index.js --fix --guard README.md

For native Agent Skills support, copy the tap payload to the runtime's documented skill directory:

# Claude Code
mkdir -p .claude/skills
cp -R skills/markdown-formatter .claude/skills/markdown-formatter

# OpenCode
mkdir -p .opencode/skills
cp -R skills/markdown-formatter .opencode/skills/markdown-formatter

# Gemini CLI
mkdir -p .gemini/skills
cp -R skills/markdown-formatter .gemini/skills/markdown-formatter

OpenCode and Gemini CLI also discover .agents/skills/markdown-formatter/. Claude Code also supports $HOME/.claude/skills/markdown-formatter/ for user-wide installs.

Portability

Component Portable?
CLI (src/index.js) Pure Node.js, no agent runtime required
SKILL.md agentskills.io base frontmatter
Guard modules Node.js, no agent tools referenced
Post-write hook config Hermes-specific (platform feature)

Safety policy

Reference spec: GitHub Flavored Markdown Spec.

  • check-tables.js enforces formatter-safe table column counts and pipe consistency, including unescaped pipes inside inline code spans. Stricter than GFM body-row parsing because autonomous formatting should not guess table intent.
  • check-pipes.js detects adjacent pipes in table rows, which create valid empty cells per GFM. Write modes repair them by inserting a space between the pipes. Read-only modes block with a clear error.
  • All CLI modes run pipe-safety preflight checks before table operations. When an unclosed fence is detected, the CLI warns that table and pipe checks are unreliable and skips them. Read-only modes and write mode with --guard fail fence validation without modifying the file. Unguarded write modes continue formatting around the open fence.
  • Write-mode --guard runs structural snapshots before and after formatting. If post-format structure doesn't match the pre-format snapshot, the original content is restored.

Supported file types

  • .md
  • .markdown
  • .mdx

Prerequisites

  • Node.js >=24
  • jq (Hermes shell hook only)

Run mdfmt --doctor to verify runtime readiness.


Install payload

The shipped runtime payload contains:

zero-md-formatter/
  SKILL.md
  src/index.js
  src/repairs.js
  src/format-content.mjs
  guard/check-structure.js
  guard/check-fences.js
  guard/check-tables.js
  guard/check-pipes.js
  guard/fence-utils.js
  scripts/check-markdown.sh

The npm tarball also includes package metadata, README.md, and LICENSE. Repository-only files (test/, .github/) are excluded via the files field in package.json — scripts/ is not shipped with npm, except scripts/check-markdown.sh which is included in the Hermes tap payload (skill install) but not the npm package.


Project files


Maintaining

Everyday changes

After changing runtime code, guards, tests, or documentation:

npm test
npm run format:check
bash scripts/staged-install-verify.sh

npm test covers structural fixtures, unit tests, integration tests, consistency checks, and deterministic behavior tests. The pre-commit hook runs the same test suite plus the formatting check. Runtime files listed by scripts/runtime-payload.js must be synchronized with skills/markdown-formatter/; run this after runtime edits:

node scripts/sync-tap-payload.js

Dependency and action freshness

Dependabot checks npm metadata and pinned GitHub Actions weekly. It groups compatible minor/patch npm updates and action updates into focused pull requests; CI remains the merge gate. Review major npm updates separately for runtime or formatting behavior changes.

Evidence URLs

docs/evidence-urls.json records the external references used by the README and skill. Unit tests enforce that every last_verified value is no more than 30 days old; CI also performs live HTTP verification.

After checking the links live, refresh the timestamps with:

node scripts/verify-urls.mjs --update

Do not use --update without a successful live verification.

For local relative Markdown links, run:

npm run check:links

This offline check validates links such as [regression guide](docs/codex-regression.md); external URLs remain covered by verify:urls.

Release process

The npm and GitHub Release badges above show the current published version. Every release tag and npm package is published from the same CI-verified tarball. GitHub Releases are the canonical source for automatically generated release notes; use the latest release or the full release history. Publishing currently uses a protected npm token with provenance. The planned OIDC migration requires configuring the npm trusted publisher for ci.yml before removing that token.

Runtime changes must be merged before the isolated version-bump commit. release.sh requires a clean tree, synchronized skill metadata, a stable x.y.z version, an isolated version commit, a pushed main, and successful CI. It creates the tag and GitHub Release; the tag workflow publishes the exact npm tarball tested by CI.

npm version patch --no-git-tag-version   # or minor/major
git add package.json package-lock.json SKILL.md skills/markdown-formatter
git commit -m "chore(release): bump version to X.Y.Z"
git push origin main
DRY_RUN=1 bash scripts/release.sh
bash scripts/release.sh

The dry run validates all release preconditions without creating a tag, pushing, or publishing. Do not run regular npm version here because it creates a tag before release.sh can perform its checks.

Release troubleshooting

  • If CI fails before publishing, fix the failing commit, push it, and rerun the release preflight.

  • If a tag was created but npm publication failed, confirm the version is not present with npm view zero-md-formatter versions --json. Remove the unpublished GitHub Release and remote tag, then rerun release.sh from a clean, tested commit.

  • Verify the result from both clients:

    npm view zero-md-formatter version
    pnpm -g update zero-md-formatter
    mdfmt --version

Never reuse a tag for a version that has already been published to npm.

Behavior harness

npm run test:behavior runs deterministic fixture and grader self-tests. The live Codex evaluation is optional and manual; its commands and artifact policy are documented in docs/codex-regression.md.


License

MIT

About

Zero-dependency GFM and MDX formatter with table, pipe, and fence guards for AI-agent-authored Markdown

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages