{"id":"5a9fc0a6-6062-4664-b99c-2c28b3a3350d","slug":"zero-md-formatter","kind":"skill","name":"zero-md-formatter","summary":"Zero-dependency GFM and MDX formatter with table, pipe, and fence guards for AI-agent-authored Markdown","owner":"CodeSigils","repoUrl":"https://github.com/CodeSigils/zero-md-formatter","license":"MIT","licenseTier":"permissive","trust":"community","stars":0,"match":null,"fewStars":true,"format":"skill","createdAt":"2026-05-20T06:49:55.000Z","addedAt":"2026-10-21T11:01:09.593Z","logoUrl":"https://avatars.githubusercontent.com/u/2639571?u=608040dd8c412a7463fc613b18e7eaa71beaa5e6&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["files-documents","developer-tools","writing"],"tags":["ai-agents","developer-tools","documentation","formatter","gfm","hermes-agent","markdown","mdx"],"runtime":"node","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T11:42:41.824Z","sample":false,"url":"https://conduits.dev/tools/zero-md-formatter","language":"JavaScript","transports":["stdio"],"install":{"npm":"zero-md-formatter"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# zero-md-formatter\n\n[![skills.sh](https://skills.sh/b/codesigils/zero-md-formatter)](https://skills.sh/codesigils/zero-md-formatter)\n[![GitHub Release](https://img.shields.io/github/v/release/CodeSigils/zero-md-formatter?display_name=tag&sort=semver)](https://github.com/CodeSigils/zero-md-formatter/releases/latest)\n[![npm version](https://img.shields.io/npm/v/zero-md-formatter)](https://www.npmjs.com/package/zero-md-formatter)\n[![npm downloads](https://img.shields.io/npm/dw/zero-md-formatter)](https://www.npmjs.com/package/zero-md-formatter)\n[![CI](https://github.com/CodeSigils/zero-md-formatter/actions/workflows/ci.yml/badge.svg)](https://github.com/CodeSigils/zero-md-formatter/actions/workflows/ci.yml)\n\nZero-dependency [GFM] and MDX formatter with structural guardrails — trailing\nwhitespace removal, table alignment, fence normalization, pipe-safety checks,\ncolumn-count enforcement, and drift detection.\n\nDesigned for AI-agent workflows but works anywhere Node.js >=24 runs.\n\n```\nnpm install -g zero-md-formatter\nmdfmt --fix README.md\n```\n\nThe CLI and formatter module have zero npm runtime dependencies. Installable on\nany system with Node.js >=24.\n\n---\n\n## Quick start\n\n### Install from npm\n\n```bash\nnpm install -g zero-md-formatter\nmdfmt --fix README.md\n```\n> Requires Node.js >=24. Zero *runtime* npm dependencies — no config file, no plugin system.\n\nWith pnpm:\n\n```bash\npnpm add -g zero-md-formatter\nmdfmt --fix README.md\n```\n\nUpdate an existing global pnpm installation with `pnpm -g update\nzero-md-formatter`. If the global dependency is pinned to an exact version,\nuse `pnpm add -g zero-md-formatter@latest` to advance it explicitly.\n\n### Node.js support policy\n\nThe package supports Node.js 24 and newer. CI validates the Node.js 24.x\nsupport floor and the current Node.js 26.x line; Node.js versions below 24 are\nnot supported. The\n[`.node-version`](.node-version) file remains the local development baseline.\n\n### Use via npx (no install)\n\n```bash\nnpx zero-md-formatter --fix README.md\n```\n\n### Use programmatically\n\n```js\nimport { formatContent } from 'zero-md-formatter';\n\nconst result = formatContent(rawMarkdown);\nconsole.log(result);\n```\n\n### Run from source\n\n```bash\ngit clone https://github.com/CodeSigils/zero-md-formatter.git\ncd zero-md-formatter\nnode src/index.js --fix --guard README.md\n```\n\n---\n\n## What it does\n\n**Formatter-owned behavior:**\n\n- Remove trailing whitespace\n- Ensure a final newline\n- Normalize leading-tab indentation outside fenced code blocks\n- Align [GFM] table columns when the table has no empty-cell ambiguity\n- Normalize tilde fences to backtick fences, escalating the backtick count when\n  nested content requires it; existing backtick fences are never modified\n\n**Guard-owned behavior:**\n\n- Fence closure and malformed fence info strings\n- Table column counts (header vs delimiter vs data row alignment)\n- Unescaped inline-code pipes in table rows\n- Adjacent-pipe table hazards (`||` → `| |`)\n- Pre/post structural drift detection and rollback when `--guard` is used\n\n---\n\n### What it doesn't do\n\n- **No formatting config file** — no `.prettierrc`, `.markdownlintrc`, or similar. No plugin system. Zero runtime dependencies means no extension points.\n- **No dialect extensions** — no Obsidian wiki-links, Mermaid, Pandoc, or frontmatter semantics.\n- **No JSX/MDX validation** — formats Markdown containers only; JSX inside is passed through unchecked.\n\n---\n\n## CLI reference\n\n```bash\nmdfmt [options] <path...>\n```\n\n| Flag              | Description                                                        |\n| ----------------- | ------------------------------------------------------------------ |\n| `--check`         | Read-only pipe-safety and format check (exit 0 if clean)           |\n| `--fix`           | Format files in-place after pipe-safety preflight (default)        |\n| `--all`           | Process directories recursively                                    |\n| `--guard`         | Pre/post structural check; rollback on drift; clean snapshots      |\n| `--verify`        | Run formatting, idempotence, and structural checks without writing |\n| `--fences`        | Validate fenced code block info strings                            |\n| `--validate`      | Run all structural validations                                     |\n| `--doctor`        | Check runtime prerequisites without modifying files                |\n| `--dry-run`, `-n` | Run pipe-safety preflight, preview changes without writing         |\n| `--audit-tables`  | Print table row cell counts and pipe hazards without writing       |\n| `--no-repair`     | Report repairable table issues instead of modifying them           |\n| `--version`       | Print version number and exit                                      |\n| `--help`, `-h`    | Display help message                                               |\n\n### File exclusion\n\nCreate `.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/`).\n\n- `*` matches any characters within a single path segment\n- `**` matches across any number of path segments (`docs/**/*.md`, `**/generated/`)\n- Everything else matches literally or as a path prefix\n- Patterns without a `/` are matched against the project root only (`*.md` does not match `docs/a.md`)\n- `?` and `[...]` have no special meaning and match literally; `!` negation is not supported\n\n```\n# Skip vendored docs and generated output\nvendor/\ndocs/generated/\n*.generated.md\ndocs/**/*.draft.md\n**/internal/\n```\n\n> 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:\n>\n> ```\n> .github/\n> .agents/\n> .opencode/\n> ```\n\n### Examples\n\n```bash\n# Check formatting (read-only, CI-safe)\nmdfmt --check README.md\n\n# Format with rollback-safe structural guards\nmdfmt --fix --guard docs/\n\n# Validate structure across a directory\nmdfmt --validate --all docs/\n\n# Diagnose installed readiness\nmdfmt --doctor\n```\n\n---\n\n## Table and pipe safety\n\n[GFM] tables are notoriously fragile in agent-generated Markdown. This formatter\nincludes guard scripts that catch the most common failure modes before\nformatting:\n\n- **Adjacent pipes** (`||`) create empty cells per [GFM]. Write modes\n  automatically insert a space (`| |`), preserving empty-cell semantics.\n  Read-only modes block with a clear error.\n- **Inline-code pipes** (`| `cmd \\| opt` | title |`) look like extra columns to\n  naive formatters. Guard scripts detect them and block formatting before\n  corruption.\n- **Column drift** — rows with mismatched column counts are detected and, in\n  write mode, repaired by padding short rows or rolling back on structural\n  drift.\n- **Empty-cell tables** that remain ambiguous are preserved by skipping the\n  full formatter pass. The delimiter row is still normalized to [GFM]-canonical\n  width.\n- **Unclosed-fence preflight** — all modes detect unclosed fences before\n  running table/pipe checks and skip validation that cannot be trusted while a\n  fence is open. Read-only and guarded modes fail without modifying the file;\n  unguarded write modes warn and continue formatting around the open fence.\n\nTable-shaped content inside fenced code blocks is always left untouched.\n\n---\n\n## Agent skill usage\n\nThe formatter ships as a standard agentskills-compatible skill via\n[`SKILL.md`](SKILL.md). It works with any agent that supports\nagentskills.io-formatted skills.\n\nThe optional [Codex behavioral regression][codex-regression] checks that an\nagent both performs guarded formatting and respects a blocking pipe hazard.\nNormal CI runs the deterministic behavior self-tests as part of `npm test`.\n\n### Install as a skill\n\nWith the standard skills CLI:\n\n```bash\nnpx skills add CodeSigils/zero-md-formatter --skill markdown-formatter\n```\n\n<details>\n<summary><b>Hermes Agent</b></summary>\n\n**Recommended for development — clone the repo and add to `external_dirs`:**\n```yaml\nskills:\n  external_dirs:\n    - /path/to/zero-md-formatter/skills\n```\nEvery commit is immediately reflected without reinstalling.\n\n**For end users — install from hub:**\n\n```bash\n# Add repo as skill tap (one-time), then install\nhermes skills tap add CodeSigils/zero-md-formatter\nhermes skills install CodeSigils/zero-md-formatter/markdown-formatter --yes\n```\n\nThen use the formatter via npm (recommended — gives `mdfmt` binary):\n\n```bash\nnpm install -g zero-md-formatter\nmdfmt --fix --guard README.md\n```\n\nOr run from source (no npm install):\n\n```bash\nnode src/index.js --fix --guard README.md\n```\n\nFor auto-wiring on every `write_file` or `patch` call — the hook script ships with the\nskill. You just need to register it:\n\n```bash\n# The script is already at:\n#   ~/.hermes/skills/markdown-formatter/scripts/check-markdown.sh\n# No download needed.\n```\n\nThen add the hook to `config.yaml`:\n\n```yaml\nhooks:\n  post_tool_call:\n    - command: ~/.hermes/skills/markdown-formatter/scripts/check-markdown.sh\n      matcher: write_file\n    - command: ~/.hermes/skills/markdown-formatter/scripts/check-markdown.sh\n      matcher: patch\n```\n\nThis runs `--fix --guard` on every written Markdown file — formatting, repairing\nadjacent pipes, normalizing fences, aligning tables, and rolling back on\nstructural drift before they reach git.\n</details>\n\n<details>\n<summary><b>Codex CLI</b></summary>\n\nFor a repo-specific Codex skill, copy the tap payload into `.agents/skills`:\n\n```bash\nmkdir -p .agents/skills\ncp -R skills/markdown-formatter .agents/skills/markdown-formatter\n```\n\nFor a user-wide Codex skill, copy it to `$HOME/.agents/skills` instead.\nCodex also works directly with the CLI:\n\n```bash\nnpm install -g zero-md-formatter\nmdfmt --fix --guard README.md\n```\n</details>\n\n<details>\n<summary><b>Claude Code / OpenCode / Gemini CLI</b></summary>\n\nAll three can run the formatter as a normal shell CLI:\n\n```bash\nnpm install -g zero-md-formatter\nmdfmt --fix --guard README.md\n```\n\nOr clone the source and run the bundled CLI directly:\n\n```bash\ngit clone https://github.com/CodeSigils/zero-md-formatter.git\nnode zero-md-formatter/src/index.js --fix --guard README.md\n```\n\nFor native Agent Skills support, copy the tap payload to the runtime's\ndocumented skill directory:\n\n```bash\n# Claude Code\nmkdir -p .claude/skills\ncp -R skills/markdown-formatter .claude/skills/markdown-formatter\n\n# OpenCode\nmkdir -p .opencode/skills\ncp -R skills/markdown-formatter .opencode/skills/markdown-formatter\n\n# Gemini CLI\nmkdir -p .gemini/skills\ncp -R skills/markdown-formatter .gemini/skills/markdown-formatter\n```\n\nOpenCode and Gemini CLI also discover `.agents/skills/markdown-formatter/`.\nClaude Code also supports `$HOME/.claude/skills/markdown-formatter/` for\nuser-wide installs.\n</details>\n\n### Portability\n\n| Component              | Portable?                               |\n| ---------------------- | --------------------------------------- |\n| CLI (`src/index.js`)   | Pure Node.js, no agent runtime required |\n| SKILL.md               | agentskills.io base frontmatter         |\n| Guard modules          | Node.js, no agent tools referenced      |\n| Post-write hook config | Hermes-specific (platform feature)      |\n\n---\n\n## Safety policy\n\nReference spec: [GitHub Flavored Markdown Spec](https://github.github.com/gfm/).\n\n- `check-tables.js` enforces formatter-safe table column counts and pipe\n  consistency, including unescaped pipes inside inline code spans. Stricter\n  than [GFM] body-row parsing because autonomous formatting should not guess\n  table intent.\n- `check-pipes.js` detects adjacent pipes in table rows, which create valid\n  empty cells per [GFM]. Write modes repair them by inserting a space between\n  the pipes. Read-only modes block with a clear error.\n- All CLI modes run pipe-safety preflight checks before table operations. When\n  an unclosed fence is detected, the CLI warns that table and pipe checks are\n  unreliable and skips them. Read-only modes and write mode with `--guard`\n  fail fence validation without modifying the file. Unguarded write modes\n  continue formatting around the open fence.\n- Write-mode `--guard` runs structural snapshots before and after formatting.\n  If post-format structure doesn't match the pre-format snapshot, the original\n  content is restored.\n\n---\n\n## Supported file types\n\n- `.md`\n- `.markdown`\n- `.mdx`\n\n---\n\n## Prerequisites\n\n- Node.js >=24\n- `jq` (Hermes shell hook only)\n\nRun `mdfmt --doctor` to verify runtime readiness.\n\n---\n\n## Install payload\n\nThe shipped runtime payload contains:\n\n```\nzero-md-formatter/\n  SKILL.md\n  src/index.js\n  src/repairs.js\n  src/format-content.mjs\n  guard/check-structure.js\n  guard/check-fences.js\n  guard/check-tables.js\n  guard/check-pipes.js\n  guard/fence-utils.js\n  scripts/check-markdown.sh\n```\n\nThe **npm tarball** also includes package metadata, README.md, and LICENSE.\nRepository-only files (test/, .github/) are excluded via the `files`\nfield in package.json — `scripts/` is not shipped with npm, except\n`scripts/check-markdown.sh` which is included in the Hermes tap payload\n(skill install) but not the npm package.\n\n---\n\n## Project files\n\n- [`SKILL.md`](SKILL.md) — packaged skill instructions\n- [`src/index.js`](src/index.js) — CLI entrypoint\n- [`src/format-content.mjs`](src/format-content.mjs) — formatter module\n- [`scripts/check-consistency.js`](scripts/check-consistency.js) — repository\n  drift checks\n\n---\n\n## Maintaining\n\n### Everyday changes\n\nAfter changing runtime code, guards, tests, or documentation:\n\n```bash\nnpm test\nnpm run format:check\nbash scripts/staged-install-verify.sh\n```\n\n`npm test` covers structural fixtures, unit tests, integration tests,\nconsistency checks, and deterministic behavior tests. The pre-commit hook runs\nthe same test suite plus the formatting check. Runtime files listed by\n[`scripts/runtime-payload.js`](scripts/runtime-payload.js) must be synchronized\nwith `skills/markdown-formatter/`; run this after runtime edits:\n\n```bash\nnode scripts/sync-tap-payload.js\n```\n\n### Dependency and action freshness\n\nDependabot checks npm metadata and pinned GitHub Actions weekly. It groups\ncompatible minor/patch npm updates and action updates into focused pull\nrequests; CI remains the merge gate. Review major npm updates separately for\nruntime or formatting behavior changes.\n\n### CI and pull requests\n\nEvery pull request must pass the deterministic `lint` gate and both runtime\nmatrix jobs (`test (24.x)` and `test (26.x)`). The lint gate runs formatting,\npayload synchronization, dependency audit, offline link checks, and builds the\nsingle npm tarball used by publishing. It also installs that tarball into a\nclean temporary prefix and exercises the packaged `mdfmt` binary; the runtime\njobs focus on the test suite across supported Node versions. Publishing is\nallowed only after all three gates succeed.\n\nLive HTTP checks are intentionally not required for pull requests. The\n`external-contracts` job runs weekly and on demand from the Actions tab, so an\nupstream outage does not make an otherwise deterministic change unmergeable.\n\n### Evidence URLs\n\n[`docs/evidence-urls.json`](docs/evidence-urls.json) records the external\nreferences used by the README and skill. Unit tests enforce that every\n`last_verified` value is no more than 30 days old. The scheduled/manual\n`external-contracts` job performs live HTTP verification.\n\nAfter checking the links live, refresh the timestamps with:\n\n```bash\nnode scripts/verify-urls.mjs --update\n```\n\nDo not use `--update` without a successful live verification.\n\nFor local relative Markdown links, run:\n\n```bash\nnpm run check:links\n```\n\nThis offline check validates links such as `[regression guide](docs/codex-regression.md)`; external\nURLs remain covered by `verify:urls`.\n\n### Release process\n\nThe npm and GitHub Release badges above show the current published version.\nEvery release tag and npm package is published from the same CI-verified\ntarball. GitHub Releases are the canonical source for automatically generated\nrelease notes; use the\n[latest release](https://github.com/CodeSigils/zero-md-formatter/releases/latest)\nor the [full release history](https://github.com/CodeSigils/zero-md-formatter/releases).\nPublishing uses npm trusted publishing (OIDC) with provenance; no long-lived\nnpm token is stored. The npm package must have a trusted publisher configured\nfor `ci.yml` in this repository before the tag-triggered publish job will\nsucceed.\n\nRuntime changes must be merged before the isolated version-bump commit.\n`release.sh` requires a clean tree, synchronized skill metadata, a stable\n`x.y.z` version, an isolated version commit, a pushed `main`, and successful\nCI. It creates the tag and GitHub Release; the `publish` job publishes the exact\nnpm tarball built and tested by CI.\n\n```bash\nnpm version patch --no-git-tag-version   # or minor/major\ngit add package.json package-lock.json SKILL.md skills/markdown-formatter\ngit commit -m \"chore(release): bump version to X.Y.Z\"\ngit push origin main\nDRY_RUN=1 bash scripts/release.sh\nbash scripts/release.sh\n```\n\nThe dry run validates all release preconditions without creating a tag,\npushing, or publishing. Do not run regular `npm version` here because it\ncreates a tag before `release.sh` can perform its checks.\n\n#### Release troubleshooting\n\n- If CI fails before publishing, fix the failing commit, push it, and rerun\n  the release preflight.\n- If a tag was created but npm publication failed, confirm the version is not\n  present with `npm view zero-md-formatter versions --json`. Remove the\n  unpublished GitHub Release and remote tag, then rerun `release.sh` from a\n  clean, tested commit.\n- Verify the result from both clients:\n\n  ```bash\n  npm view zero-md-formatter version\n  pnpm -g update zero-md-formatter\n  mdfmt --version\n  ```\n\nNever reuse a tag for a version that has already been published to npm.\n\n### Behavior harness\n\n`npm run test:behavior` runs deterministic fixture and grader self-tests. The\nlive Codex evaluation is optional and manual; its commands and artifact policy\nare documented in [`docs/codex-regression.md`](docs/codex-regression.md).\n\n---\n\n## License\n\nMIT\n\n[GFM]: https://github.github.com/gfm/\n[codex-regression]: https://github.com/CodeSigils/zero-md-formatter/blob/main/docs/codex-regression.md\n"}