ChatGPT/Tool/Idea/Script/To Clean Up Output of Canvas/Report/Deterministic Markdown Formatters
Properties
- tags
- AI Deep Research, CGPT Deep Research
Tools for Deterministic Markdown Formatting (Black-Style)
- Modern "Black-style" Markdown formatters automatically reformat markdown files into a consistent, opinionated style – much like the Black formatter does for Python code. Below we catalogue the major open-source (and a few commercial) tools across ecosystems (Python, Node.js, Go, Rust) that provide deterministic Markdown formatting. For each, we note their release status, license, maintenance, popularity, and feature support. We then compare their capabilities (e.g. smart punctuation fixes, heading normalization, link reference handling, list numbering, table formatting) and identify gaps (like accessibility heading-order enforcement or advanced table constraints). Finally, we recommend top candidates and how to integrate them into a Python pre-commit workflow.
Python Ecosystem Formatters
mdformat (Python)
- Description: mdformat is an opinionated Markdown formatter available as a CLI and Python library. It targets CommonMark (with optional extensions for GitHub-Flavored MD via plugins) and emphasizes deterministic output (it even validates that the Markdown AST is unchanged after formatting).
- Status & Popularity: The project is open-source (MIT license) and was actively maintained up to 2023. The latest tagged release was v0.7.17 in Aug 2023 (development has slowed since). It has ~600 GitHub stars and is used in some CI/pre-commit setups.
- Key Features:
- Whitespace & Line Breaks: mdformat enforces consistent whitespace (single newline at EOF, one blank line between blocks, no trailing spaces). It preserves "semantic line breaks" by default – i.e. it does not re-wrap paragraphs unless configured, to respect deliberate line breaks. You can override this with a wrap width setting.
- Headings: All headings are output in ATX style (
#notation). Any Setext-style headings (===or---underlines) are converted to#or##forms. (It does not alter heading levels beyond style conversion, so it will not auto-fix a skipped level hierarchy issue.) A space after the#is always enforced for valid syntax. - Lists: By default, ordered lists are renumbered to use
1.for every item (a "non-numbering" style to minimize diffs). This means mdformat will output:1. Item A 1. Item B 1. Item Cfor any ordered list, regardless of the original numbers. This approach avoids renumbering all lines when an item is inserted. However, consecutive numbering is available via a
--numberflag or config if you prefer actual 1,2,3 numbering. Bullet lists are standardized to use-(and will alternate-/*if you have adjacent bullet lists). - Links and References: mdformat cleans up link syntax. It removes unnecessary angle brackets in inline links (e.g.
[text](<http://...>)->[text](http://...)). It also collates and sorts all reference link definitions to the bottom of the document, removing duplicates and unused definitions. This means if your Markdown uses reference-style links, all the[]: URLdefinitions will be moved to the end and sorted by label. - Code Blocks & Spans: Indented code blocks are converted to fenced code blocks for consistency. It also normalizes the fencing (removing superfluous backticks and trimming spaces in inline code spans).
- Tables and Frontmatter: Base mdformat (CommonMark) doesn't support tables or YAML front-matter by default, but plugins are available. For example,
mdformat-gfmandmdformat-tablesadd GitHub table support (auto-aligning columns). Similarly,mdformat-frontmatterpreserves YAML front-matter. With the GFM plugin, tables are formatted with consistent column spacing. - Other: It converts hard line breaks to the visible
\syntax (avoiding trailing double-spaces). It formats horizontal rules as a 70-character line of underscores for consistency.
- CLI & Integration: The CLI supports
--check(to verify formatting without writing) and in-place formatting by default. It also has an official pre-commit hook configuration, making it easy to add to a Python project's pre-commit workflow. As a library, you can callmdformat.text()to format Markdown strings. - Notable Gaps: mdformat does not modify punctuation like quotes or dashes – it deliberately does not do "smart quote" or typography fixes. It focuses on structural whitespace and syntax. It also does not enforce any particular heading level ordering (no checks for skipping levels – that would be up to a linter). These could be considered feature gaps for certain use cases (accessibility or typographic consistency).
Person/Joshua Levy/GitHub/flowmark Flowmark (Python)
- Description: Flowmark is a newer Markdown formatter (pure Python) introduced in 2024-2025, aimed at "better auto-formatting and line wrapping" especially for AI/LLM-generated content and version control workflows. It emphasizes clean diffs and Markdown optimized for both editing and rendering. The design philosophy is to be conservative (no content changes) but highly configurable, essentially a "Black for Markdown" with additional smart features.
- Status: Flowmark is MIT-licensed (author: Joshua Levy). It's actively maintained – version 0.5.0 was released July 8, 2025 – and is in beta but quickly evolving. It's on PyPI and GitHub (currently ~100 stars, as it's fairly new).
- Key Features:
- Paragraph Wrapping & Semantic Line Breaks: Flowmark's marquee feature is advanced line wrapping. By default it wraps text at 88 characters (the "90-ish columns" guideline popularized by Black). It can also perform "semantic line breaks", meaning it prefers to break lines at sentence or clause boundaries when possible. This significantly improves diff readability (small edits don't reflow entire paragraphs). You can enable this with
--semanticor--autoflags. (Traditional formatters either don't wrap at all or wrap at a fixed width without semantic awareness.) - Smart Typography (Quotes/Dashes): Flowmark offers optional "smart quotes" conversion. With
--smartquotes, it will convert straight quotes and apostrophes into "curly" typographic quotes where appropriate, and likewise for apostrophes, while avoiding code blocks and other unaffected areas. This yields more professional typography in rendered output. (It does this conservatively, only in normal text paragraphs.) It can also handle other special characters: for example, it normalizes triple-dot ellipsis, em dashes, etc., similar to a "smartypants" tool. All of this is optional (off by default unless using--auto). - Headings: Flowmark, like others, uses ATX
#headings. It will ensure a space after the `. It does not automatically re-level headings that skip levels (it assumes the author's structure is intentional). However, it will ensure that headings are properly spaced (e.g., blank line before headings as needed) and any standardized style choices. - Links and Footnotes: The tool parses and normalizes links, footnotes, and horizontal rules. Standard reference links and footnote syntax are supported (via the Marko CommonMark/GFM parser). It doesn't appear to move reference definitions like mdformat does; rather, it will keep links as-is but ensure consistent formatting (e.g., spacing, angle brackets removal similar to mdformat). Unused references or duplicates are not explicitly mentioned as being removed, so likely not.
- Lists: Flowmark will preserve the author's list numbering by default. It doesn't force all
1.like mdformat – if your list is1., 2., 3.it will keep those numbers (and if you had all1.it would leave them as all1.). There's no specific mention of an option to renumber or style change, so it's effectively "don't change content unnecessarily". It will clean up indentation of list items and sublists for consistency. - Tables: It has "just works" support for GFM tables out-of-the-box (no extra plugin needed, since it uses the Marko parser which supports tables). Flowmark will format tables by aligning columns, adjusting cell padding, and ensuring the table fits within the set line width. It handles wide tables gracefully by wrapping cell content if needed (one user noted it handles tables where Prettier might awkwardly wrap lines). This addresses some advanced table scenarios.
- Front Matter: YAML front-matter (between
---lines at the top) is preserved exactly – Flowmark will not alter it (no reordering or reformatting of YAML). This is important for static site generators and tools like Logseq. - Misc: It normalizes horizontal rules and other syntax elements similarly to mdformat. It's also designed to be small and fast (depends only on the Marko parser,
regexandstriflibs).
- Paragraph Wrapping & Semantic Line Breaks: Flowmark's marquee feature is advanced line wrapping. By default it wraps text at 88 characters (the "90-ish columns" guideline popularized by Black). It can also perform "semantic line breaks", meaning it prefers to break lines at sentence or clause boundaries when possible. This significantly improves diff readability (small edits don't reflow entire paragraphs). You can enable this with
- CLI & Integration: Flowmark provides a CLI (
flowmark) with flags like--width,--semantic,--smartquotes, and--check. It currently doesn't have an official pre-commit hook in its repo, but it can be easily added to pre-commit as a generic Python package (since it has a standard CLI entry point). In VSCode or other editors, you can configure it as a format-on-save command. The project's README even gives tips for editor integration and using it as a library in pipelines (e.g. autoformatting LLM outputs). - Notable Gaps: Because Flowmark focuses on not altering semantics, it does not enforce heading level order (no automatic "fix" if you jump from
#to###). It assumes that's a logical document structure issue, which would be better caught by a linter. Also, while it formats tables nicely, it doesn't impose "advanced table constraints" like consistent column widths across multiple tables or a specific max table width (beyond wrapping to the overall line width). Those remain manual concerns. Overall, Flowmark's feature set is quite comprehensive, and its emphasis on diff-friendly output (semantic wraps and minimal changes) is unique. It's a strong candidate if you need those advanced features.
pymarkdown / markdownlint (Python)
- (Note: The user specifically isn't looking for pure linters, but for completeness, we mention this Python-based tool with some fix capabilities.)
- PyMarkdown by Jack De Winter (also known as
markdownlint-cli2in Node form) is primarily a Markdown linter with an extensive rule set. It can detect issues like improper spacing, inconsistent headings, etc. While it's mainly for reporting, it does have some auto-fix functionality for simple issues (similar to how ESLint can fix some problems). For example, it can fix trailing spaces, add missing blank lines, etc. However, it is not a full auto-formatter – many rules are warnings that must be fixed manually or with an external formatter. Since the question focuses on automated formatting, PyMarkdown/markdownlint is not a top solution here (it's complementary: you might run a formatter, then a linter for anything the formatter can't fix). For reference, PyMarkdown is actively maintained (as of 2024) and has a pre-commit hook. But it does not reformat entire documents deterministically in the way Black or the tools above do. We'll focus on true formatters below.
Node.js / JavaScript Ecosystem
Prettier (Node.js)
- Description: Prettier is a very popular opinionated code formatter that supports Markdown (and MDX) in addition to many programming languages. While primarily known for code, Prettier's Markdown formatting was introduced in version 1.8 (2017) and it's widely used via editors and CI.
- Status & Popularity: Prettier is actively maintained (as of mid-2025 it's at version 3.6.x with regular releases). It's MIT-licensed and often the default formatter in JS/TS projects – many developers use it to format Markdown files in docs and repos. It's considered very stable.
- Key Features:
- Headings: Prettier will convert Setext headings to ATX by default. It enforces a space after the
. All heading levels are preserved as-is (it does not change a###` to##, for example, even if out of order). Essentially, it normalizes the syntax but not the structure. - Lists: Prettier keeps the numbering you wrote. If your list is
1. 2. 3., it will remain1. 2. 3.; if you used all1.s, it will leave them as all1.. It does not renumber lists or force a particular style – the philosophy is to preserve meaning and not introduce large diffs. It does, however, standardize list indentation and can enforce a consistent bullet symbol. For example, Prettier by default uses*for unordered list bullets (configurable in some forks). For ordered lists, Prettier will add two spaces after the period for single-digit list numbers (to align multi-digit numbers nicely). - Links: Prettier mostly leaves link syntax alone (aside from breaking long link URLs across lines if needed). It does not consolidate reference definitions or sort them – if you have reference-style links, it will keep the definitions where they are. Its focus is line-breaking long links for readability.
- Wrapping: By default, Prettier's
proseWrapoption is set to "preserve", meaning it will not re-flow paragraphs – it preserves your manual line breaks in text. This is to avoid conflicts with services that care about Markdown line breaks (and to respect intentional formatting). You can setproseWrap: "always"to have Prettier wrap prose at the print width (usually 80 characters), but this is optional. Many users keep "preserve" and manually enforce semantic line breaks. - Tables: Prettier has full support for GFM tables. It will automatically align the pipes and pad cells so that the table is neatly formatted. If a table cell has a long word or URL, Prettier might wrap the cell content to a new line within the cell, which sometimes looks odd in raw form. There is no option to disable table formatting; it always tries to make the table readable in source. This often means expanding the table to fit the longest cell (up to the print width).
- Other: It converts HTML entities to lowercase hex (
stays – Prettier doesn't alter them). It doesn't perform typographic replacements (no quote or dash changes). It will consistently format blockquotes, code fences, etc., with standardized spacing. Prettier also supports MDX (Markdown with JSX, used in React docs), if that's relevant – it can format JSX code inside MD files.
- Headings: Prettier will convert Setext headings to ATX by default. It enforces a space after the
- CLI & Integration: Prettier's CLI (
prettier --write) can format files in place. In a pre-commit context, it's often integrated via thepretty-quicktool or a Prettier pre-commit hook (there's apre-commitmirror repository for Prettier). It requires Node.js, which is a consideration if you prefer a pure Python toolchain. But in terms of ecosystem popularity, Prettier is ubiquitous. Many editors (VS Code, JetBrains, etc.) have built-in Prettier format-on-save for Markdown. - Notable Gaps: Prettier does not enforce any heading level progression rules (no accessibility checks). It also does not have configurable "style" options for Markdown beyond the global print width and bullet style – it's intentionally unconfigurable to reduce bikeshedding. For example, you cannot tell Prettier to use all
1.for lists or to move references; those are out of scope. It also doesn't do "smart" punctuation; that would require a separate tool (like remark-smartypants) if you wanted curly quotes or similar.
Remark (unified ecosystem) (Node.js)
- Description: Remark is part of the unified.js suite – it's essentially a Markdown AST processor with an extensive plugin ecosystem (linting rules, transformers, etc.). There isn't a single "remark formatter" that people use widely, but you can build one by combining remark plugins. For example,
remark-toccan generate a table of contents,remark-footnotescan normalize footnote syntax,remark-lintrules can check spacing, andremark-stringifywill serialize the AST back to markdown in a consistent way. - Use Case: If you need highly custom formatting or want to enforce a specific style guide, remark can likely do it via plugins. However, as noted by developers, "there isn't one [auto-formatter] that's broadly used as a CLI tool" in the remark ecosystem. Instead, people tend to use remark-lint (with
--fixto auto-fix trivial issues) and/or custom remark scripts. For example, you could write a remark plugin to enforce heading level increments or table column width limits – but you'd be writing custom code or configuration. - Status: Remark (and unified) are well-maintained (MIT license) and very powerful. If other tools fall short, a custom remark setup could fill the gap. For instance, remark-smartypants can do the smart punctuation that Prettier lacks, remark-frontmatter can help with front-matter, etc. But using remark requires more work to configure, whereas the other formatters work out-of-the-box. In this survey, we focus on out-of-the-box tools, so we won't delve deeply into remark. It's worth noting as an option for extension if you have highly specific needs not met by existing formatters.
- Description: Remark is part of the unified.js suite – it's essentially a Markdown AST processor with an extensive plugin ecosystem (linting rules, transformers, etc.). There isn't a single "remark formatter" that people use widely, but you can build one by combining remark plugins. For example,
tidy-markdown (Node.js)
- Description: tidy-markdown is a CLI tool (written in CoffeeScript) that was an early attempt at an automatic Markdown formatter. It was inspired by the idea of a canonical style (similar to how
gofmtis for Go code) with minimal configuration. - Status: Unfortunately, tidy-markdown is no longer maintained – the latest release was v2.0.4 in September 2016. It has only ~74 stars on GitHub. It's GPL-3.0 licensed. While not active, it's notable for some unique features that even newer tools haven't fully replicated, especially in terms of enforcing document structure.
- Notable Features:
- Heading Order Enforcement: tidy-markdown will automatically correct heading level jumps. For example, if you write an
# H1followed immediately by an#### H4, tidy would change thatH4into an##to ensure the hierarchy has no gaps. It effectively enforces that after an H1, the next sub-heading must be H2, then H3, etc., in sequence. This is an accessibility and consistency guideline (commonly recommended, but rarely auto-fixed by tools). Tidy is one of the few that tried to do this in an automated way. - Unicode punctuation to ASCII: It will "decode" certain Unicode characters to their Markdown equivalents. For instance, an ellipsis character
…becomes..., and an em dash—becomes--(since many Markdown processors interpret--or---as en/em dashes). This is essentially the inverse of smart quotes – tidy focuses on ensuring plain ASCII punctuation, likely for compatibility. (It's unclear if it touched curly quotes; it might leave them or also convert to straight quotes, but the docs explicitly mention ellipses and em-dash.) - Standardized List Markers: All unordered list items are converted to use
-as the bullet (no mixing*or+). Ordered lists are renumbered sequentially (1, 2, 3, …) regardless of input. So unlike mdformat's default of all1.s, tidy prefers "natural" numbering starting at 1 for each list. Both approaches have merit; tidy's choice makes the raw markdown easier to read, at the cost of diffs when inserting items. - Reference Link Ordering: Tidy will renumber reference-style links if they use numeric labels. It notes that reference links "do the same (based on first occurrence)" for numbering. This implies if you have
[1]: http...and[2]: http...out of order or missing numbers, it will reorder/renumber them in the order they appear in the text. (It might not affect non-numeric reference labels, only numeric ones, treating them like footnote IDs.) - HTML to Markdown: It can convert simple HTML tags to Markdown syntax (e.g.
<em>text</em>->_text_). It helps to "purify" Markdown. - Tables and Front-matter: It claims to format Markdown tables (likely aligning pipes and spacing) and handle YAML front-matter (probably just ensuring it remains at top and is separated by blank lines).
- Heading Order Enforcement: tidy-markdown will automatically correct heading level jumps. For example, if you write an
- These features made tidy-markdown quite ambitious. However, given its age, it doesn't know about newer CommonMark or GFM extensions (like task lists or fancy tables) and could choke on newer syntax. Its unmaintained status means it's risky to use on modern Markdown. The structural fixes (heading leveling, etc.) stand out as a gap not filled by later formatters.
- Description: tidy-markdown is a CLI tool (written in CoffeeScript) that was an early attempt at an automatic Markdown formatter. It was inspired by the idea of a canonical style (similar to how
Go Ecosystem
markdownfmt (Go)
- Description: markdownfmt is a formatter originally written by Dmitri Shuralyov (shurcooL) in Go, released in 2014 as "like gofmt, but for Markdown". It reads Markdown and outputs a consistently formatted version. This was one of the first markdown auto-formatters.
- Status: The original shurcooL/markdownfmt is MIT-licensed and reached v1.1 in June 2014. It has ~800 stars (many from its initial release). However, it's essentially frozen – no significant updates in years (it doesn't support CommonMark extensions like front-matter or tables).
- Active Fork – markdownfmt v3: A fork by GitHub user Kunde21 modernized this tool. Kunde21/markdownfmt v3 (also MIT-licensed) updated the parser to use Goldmark (CommonMark compliant) instead of the old Blackfriday parser. It added features. The latest version v3.1.0 was released Jan 6, 2023. This fork has ~50 stars (lower visibility, but it's used in some Go projects and documentation pipelines). We'll refer to this updated version's features:
- Key Features (Kunde21 markdownfmt v3):
- GitHub Flavored Markdown support: It supports GFM syntax (task lists, tables, etc.) fully. Notably, front matter is supported (the fork mentions alternatives for front matter were a reason to fork). If a file has YAML front matter, the tool will detect and preserve it.
- Headings: ATX-style headings by default. It will convert any Setext headings to
#/##. You can pass a flag-u("underline") to instead use Setext for levels 1 and 2 if desired. But by default it sticks to#for consistency. It ensures there's exactly one space after the#and that heading text is properly spaced. Like others, it doesn't change heading levels beyond style. - Lists: It will renumber ordered lists sequentially by default (1, 2, 3, …). It also provides a flag
-list-indent-styleto control sub-list indentation style ("aligned" vs "uniform"). This helps control whether multiline list items align under the text or under the bullet. - Code Fences: It automatically uses fenced code blocks (with triple backticks). There's a neat option
-gofmtwhich will run Go's code formatter on any Go code blocks in the Markdown. Similarly, it can integrate with other formatters for other languages if configured (just as mdformat can use plugins to format code inside Markdown). - Line Wrapping: By default, it does not reflow paragraph text (it preserves line breaks). There is an option
-soft-wrapswhich will treat soft line breaks as hard breaks, effectively wrapping all paragraphs at the print width. However, this isn't commonly used by default. - Tables: The original markdownfmt did not support tables (it would leave them as-is). The Kunde21 fork, using Goldmark, can parse tables. It will format GFM tables by aligning columns. However, some have noted it lacks advanced options (it might not auto-break long cell content). It covers basic table formatting.
- Other: It trims or adds blank lines as needed (ensuring one blank line between top-level elements, etc.). It's generally conservative like gofmt – the output is meant to be semantically unchanged.
- CLI & Integration: Being a Go binary, you can install it via
go install. It offers flags for diff mode (-dto show a unified diff of changes instead of overwriting files) and list mode (-lto list files that would change), similar to gofmt. There's also-wto write in place. This makes it easy to integrate into pre-commit: you can have a hook callmarkdownfmt -dto fail if changes are needed, or-wto auto-fix. There are Vim/Emacs plugins available for on-save formatting. - Popularity: Outside Go circles, markdownfmt isn't as widely adopted as Prettier or mdformat. But it's used in some documentation projects (even integrated into CI for projects on GitHub to ensure consistent docs). If you like the simplicity of a single binary with no runtime dependencies, it's a solid choice.
- Notable Gaps: markdownfmt (even the fork) does not handle "smart" punctuation (no curly quote handling or special char conversions – it's mostly about spacing and syntax). It also doesn't enforce heading level ordering (like tidy did). If using the original or fork without the GFM enhancements, support for tables or footnotes may be limited (the fork improved this, though). The fork's adoption is not huge, so minor quirks could exist. But overall, it achieves basic deterministic formatting.
mdfmt (Go)
- To avoid confusion: mdfmt is another Go tool (created by moorereason) which was actually a fork of the original markdownfmt to add front matter support. It saw very few commits (mostly just skipping YAML front matter then calling markdownfmt). The Kunde21 fork supersedes it by including front matter support and many improvements. So while mdfmt (moorereason) exists, it's not actively maintained (only ~6 commits, last around 2017) and has ~67 stars. We mention it for completeness, but one should use the Kunde21 markdownfmt for an actively maintained Go formatter.
Rust / Multi-language
Dprint (Rust) + Markdown Plugin
- Description: Dprint is a pluggable code formatting platform written in Rust. It's like an engine that can host multiple formatter plugins (including officially supported ones for JSON, TypeScript, HTML, etc.). There is an official dprint-plugin-markdown which formats Markdown files. Dprint's goal is to be extremely fast and easily integrable (plugins can run as WASM).
- Status: Dprint is actively maintained (3.5k stars) and MIT-licensed. The Markdown plugin is also active – as of mid-2025, v0.18.0 was released Feb 27, 2025 and updates continue (the crate was updated "16 days ago" from July 15, 2025). This indicates ongoing maintenance and improvements. Many users in the Rust community use dprint for combined formatting (e.g., Rust + Markdown in one go).
- Key Features of dprint Markdown:
- CommonMark Compliance: The plugin uses the Pulldown-Cmark parser, ensuring CommonMark compliance. It likely supports GFM features as well (since Pulldown-Cmark has extensions for tables, task lists, etc. and dprint mentions table support).
- Configuration Options: Dprint allows configuring certain formatting decisions in a JSON config. For Markdown, you can set options like lineWidth (to wrap prose), whether to always add a blank line at end, etc. By default, dprint will wrap prose to 80 characters (if I recall correctly, dprint tends to wrap text where possible). However, it may also have a "preserve line breaks" option. This is configurable via the "markdown.textWrap" setting (
auto,always,never). So you have flexibility similar to Prettier's proseWrap. - Headings and Spacing: Dprint will ensure ATX headings are consistently formatted (it will convert Setext to ATX as well). It enforces spacing after the `. It also ensures blank lines around headings and other elements as needed. It does not adjust heading levels (no re-leveling).
- Lists: Dprint by default follows an approach similar to Prettier – it does not renumber lists; it will keep whatever numbering scheme is present. (Dprint's philosophy is generally to minimize semantic changes.) It does, however, ensure consistent indentation for list items and will likely unify bullet marker style (possibly defaulting to
-). If desired, one could imagine a plugin or setting to enforce all1.numbering, but it's not default. - Link References: The dprint Markdown plugin currently does not move reference definitions to the bottom or sort them. It leaves them as-is. Its focus is layout (spacing, wrapping) rather than reordering content.
- Tables: Yes, dprint formats tables, aligning columns neatly. This is handled within the plugin. It will add/remove padding spaces so that all columns line up under the header separators. Like Prettier, it will break a cell's text if it exceeds the line width. You can configure markdown.tableColumnAlignment in dprint's config if needed.
- Other: Dprint supports special HTML comments to ignore sections (
<!-- dprint-ignore -->). It can be useful if you don't want a certain part of your markdown (say a complex table or ASCII art) to be reformatted. Few other formatters have this ignore capability.
- CLI & Integration: You'd install dprint (a single binary). To use the Markdown formatter, you add the markdown plugin in dprint.json (it can automatically fetch the plugin WASM). Then running
dprint fmtwill format all configured file types. Dprint integrates with pre-commit via the dprint hook (or simply as an entry: dprint fmt). It's cross-platform and doesn't require Node or Python at runtime. - Notable Gaps: Out of the box, dprint's Markdown plugin doesn't do "smart" punctuation replacement – you'd need a separate step if you wanted to convert quotes or dashes. It also doesn't enforce heading level rules. Essentially, it covers similar ground to Prettier in terms of output, with some configurability. One gap is that if you require strict control over table column widths (say all tables should be max 80 chars wide even if content is wider), that's not directly enforced – dprint will try to fit within lineWidth, but very long words (like long URLs) may still extend the table. You might need to manually handle extreme cases or use link shorteners.
Others / Commercial
- Beyond the above, there aren't many dedicated commercial Markdown formatters. Most commercial tools incorporate open-source formatters: for example, Trunk (a commercial linter/formatter aggregator) includes Prettier and remark under the hood. Mega-Linter and other CI services do similarly. These aren't new formatters themselves.
- One could consider Grammarly or LanguageTool as "content fixers" – they can suggest changes in Markdown text (including heading capitalization, consistent punctuation, etc.), but they're not deterministic formatters, and they often require manual approval.
- There have been some AI-based writing tools (OpenAI's GPT, etc.) that can output nicely formatted Markdown given instructions, but at present no known LLM-powered CLI formatter exists that guarantees deterministic style fixes. (An LLM could hallucinate changes or make stylistic choices that aren't consistent run-to-run, which goes against the deterministic requirement.) Some people have written GPT prompts or small scripts to reformat Markdown via an API, but these are experimental and not robust for automation. In short: no mainstream formatter relies on an API key or LLM – all the tools listed above run offline with predefined rules. (Flowmark's mention of "LLM workflows" is about formatting text for LLM usage, not using an LLM to format it.)
Feature Comparison Matrix
- To summarize the capabilities, here is a comparison of how the major tools support specific formatting features:
Tool Last Release (active?) License Smart Punctuation (quotes, dashes) Heading Style (Setext → ATX) Heading Level Order Fix Link Ref Collation (sorting, deduping) List Numbering Style Table Formatting & Alignment Prettier (Node) 3.6.2 in Jun 2025 (Yes) MIT No (does not alter quotes or ellipses) Yes (forces ATX headings) No (skips not corrected) No (leaves references in place) Preserve (keeps your numbering as-is) Yes (formats GFM tables with aligned columns) mdformat (Py) 0.7.17 in Aug 2023 (mostly) MIT No (no smart quote conversion) Yes (ATX only) No (doesn't adjust levels) Yes (moves all ref defs to bottom) Option (default all "1.") Yes (with mdformat-tablesplugin for GFM tables)Flowmark (Py) 0.5.0 in Jul 2025 (Yes) MIT Yes (optional smart quotes --smartquotes)Yes (ATX output) No (does not re-level headings) Partial (normalizes links, but does not re-order ref blocks) Preserve (does not renumber lists by default) Yes (built-in support for GFM tables) markdownfmt (Go) v3.1.0 Jan 2023 (Yes fork) MIT No Yes (ATX by default) No No (does not move reference links) Yes (sequential numbering 1..n) Limited (formats tables basically, but no advanced wrapping) tidy-markdown (Node) v2.0.4 Sep 2016 (No) GPL-3.0 Yes (converts …→...,—→--etc.)Yes (ATX output) Yes (auto-corrects heading jumps) Partial (renumbers numeric link references) Yes (renumbers lists 1,2,3…) Yes (formats tables, ensures pipes align) dprint (Rust) v0.18.0 Feb 2025 (Yes) MIT No (no built-in smart punctuation) Yes (ATX only) No No (does not rearrange references) Configurable (defaults to preserve input numbering) Yes (aligns and pads tables, respects lineWidth)- Notes: "Preserve" means the tool keeps whatever style the input had (unless it's invalid). Option means there's a flag or config to change behavior. For "active?" we mark the primary project status – e.g., mdformat is mostly active (though fewer recent commits, it's considered stable).
- From the matrix, you can see that no single tool covers all desired features out-of-the-box. For example, tidy-markdown was the only one that tried to enforce heading level order (but it's unmaintained). No actively maintained formatter will automatically ensure your headings increment by one level at a time – that remains a gap (addressable by a linter like markdownlint's rule MD001, but not by auto-fix). Similarly, smart punctuation is only done by Flowmark (optional) and partially by tidy (in the opposite direction, turning them into ASCII). If having typographic quotes or enforcing ASCII punctuation is important, you'll need to choose the right tool or add a secondary tool (e.g., use remark's
smartypantsplugin alongside Prettier). - Accessibility Heading-Order Enforcement – Gap: As noted, no modern formatter fully enforces heading hierarchy. This is a known accessibility recommendation (e.g., don't jump from H1 to H3) and some linters warn about it. But auto-correcting it is tricky (could alter meaning). Tidy did a simple promotion/demotion, but that might not always be what the author intended. Currently, you'd have to rely on a linter to flag this and then fix manually. This is a gap for those who want an end-to-end automated solution with "no skipped headings."
- Advanced Table Constraints – Gap: All tools format tables to be pretty, but none allow specifying a maximum column width or consistent column widths across different tables. For example, if you require that tables wrap at 50 characters for readability, or that all tables have exactly 3 columns of equal width, there's no general-purpose formatter to enforce that. You might address this by post-processing (a custom script) or by structuring your Markdown differently (splitting wide tables). Some tools like Pandoc could rearrange tables (convert to CSV and back), but that's beyond typical formatter usage. So advanced control over table layout remains largely manual.
Recommendations for a Python Pre-Commit Workflow
- Given the tools above, here are recommendations for integrating the top candidates into a Python-based development workflow (e.g., using pre-commit hooks):
- Choose a Primary Formatter: For most users working in a Python environment, mdformat or Flowmark will be the go-to, since they're Python packages easily installed with pip.
- If you want stability and alignment with common standards: mdformat with the GFM plugin is a great choice. It's stable, used in many projects, and fast. In your
.pre-commit-config.yaml, you can add:- repo: https://github.com/executablebooks/mdformat rev: 0.7.17 *# or latest stable* hooks: - id: mdformat additional_dependencies: [mdformat-gfm, mdformat-frontmatter]This will run mdformat on your Markdown files, supporting tables and front-matter. Mdformat will automatically fix spacing, list style, etc., and fail CI if something wasn't formatted (so developers remember to run it). One thing to note: if you prefer actual numbering in ordered lists, you might set the
MDFORMAT_NUMBERenvironment variable or use a.mdformat-config.yamlto enable consecutive numbering. Otherwise, developers might be surprised that all list items become "1." – though this is by design to minimize diffs. Many teams adopt the "all 1." style; if you don't want that, configure it. - If you want more advanced formatting (semantic breaks, smart quotes): Flowmark is a compelling option. Since it's new, you'd add it as a local hook in pre-commit (there isn't an official hook repo yet). For example:
- repo: local hooks: - id: flowmark name: Flowmark Markdown Formatter entry: flowmark --width 88 --semantic --smartquotes language: python types: [markdown]This assumes
flowmarkis in your environment (you might addadditional_dependencies: [flowmark]under that hook to have pre-commit install it from PyPI). This will run Flowmark with your chosen options (here we enabled semantic line breaks and smart quotes). Flowmark will then reformat markdown files accordingly. Keep an eye on its repository for any changes in usage or new options as it develops. - It's generally not recommended to run multiple formatters on the same files (they could conflict). Pick one and stick to it for consistency. Both mdformat and Flowmark aim for CommonMark compliance, so they shouldn't break each other's output, but their styles differ (especially on list numbering and wrapping). For example, Flowmark might reflow lines that mdformat would leave, causing churn. So use one at a time.
- If you want stability and alignment with common standards: mdformat with the GFM plugin is a great choice. It's stable, used in many projects, and fast. In your
- Supplement with Linters if needed: As discussed, no formatter does everything. You might include markdownlint (or its Python equivalent PyMarkdown) in pre-commit in –check mode to catch things like heading order (MD001) or other rules that aren't auto-fixable. For instance:
- repo: https://github.com/DavidAnson/markdownlint-cli2 rev: v0.8.0 hooks: - id: markdownlint-cli2This will run markdownlint on your files. It won't fix, but it will warn you about any remaining issues (like missing ALT text in images, or heading levels). You can disable any rules you don't care about via a config file. This linter pairs well with a formatter: the formatter does spacing and wrapping; the linter does content/style checks that require human decision (e.g., "Is this heading out of order?").
- Prettier in a Python Project: If you already have Node.js in your toolchain or are using Prettier for frontend code, you can absolutely use Prettier for markdown in pre-commit. There's a convenient mirror:
- repo: https://github.com/pre-commit/mirrors-prettier rev: v3.6.2 hooks: - id: prettier args: [--parser=markdown]This ensures Prettier formats your Markdown. It's a fine choice if you're okay with Node and want consistency with developers who might already use Prettier in editors. Prettier won't give you semantic line breaks or smart quotes, but it's very reliable for basic formatting.
- Dprint for Polyglot Repos: If your repo has multiple languages (say Rust code, docs in Markdown, maybe JSON, etc.), adopting dprint can be efficient – one formatter config for everything. You can add a pre-commit hook for dprint:
- repo: local hooks: - id: dprint name: dprint formatter entry: dprint fmt --check language: system types: [markdown, json, yaml, rust]Here
language: systemmeans it expects thedprintbinary installed (you can also manage it via a cargo install or provide it in the environment). Dprint'sdprint fmt --checkwill check formatting anddprint fmt(without --check) will write fixes. You might rundprint fmtlocally and use--checkin CI. Just ensure yourdprint.jsonis configured for Markdown (you'd rundprint config add markdownonce to set it up). Dprint is a bit more involved to configure initially than Prettier or mdformat, but it pays off with speed and one-tool integration. - Logseq and Others: The user specifically mentioned Logseq – a tool that uses Markdown for note-taking (with some variations, like
[[Wiki Links]]and:collapsed:markers). No dedicated Logseq markdown formatter exists at the moment. However, you could use Flowmark or mdformat on Logseq pages (.md files) – just be cautious that Logseq has some expectations (e.g., it might not like if you reorder certain sections). Always keep backups when auto-formatting personal notes. You might disable rewrapping if it interferes with your style of writing in Logseq (since those notes are often manually wrapped for readability). In summary, while there isn't a Logseq-specific formatter, the general tools here can be applied to those Markdown files with success. - Final Advice: Determine your highest priorities – e.g., diff friendliness (then Flowmark or mdformat with all-
1.lists is helpful), typographic precision (Flowmark with smart quotes), or simplicity and convention (Prettier or mdformat defaults). Then adopt the tool that best matches, and configure it in pre-commit so that every commit has beautifully formatted Markdown. Each of the top tools has documentation and active communities if you run into edge cases. After initial setup, you'll enjoy consistent Markdown styling without any manual effort – just like Black does for your Python code. - Sources: The information above is drawn from official tool docs and repositories, as well as developer discussions. Key references include the mdformat documentation, the Flowmark README and author's notes, Prettier's documentation and issues, the tidy-markdown README, and others as cited throughout. Each tool's feature set and limitations were verified against these sources to ensure accuracy.
- Citations
- Choose a Primary Formatter: For most users working in a Python environment, mdformat or Flowmark will be the go-to, since they're Python packages easily installed with pip.
- Given the tools above, here are recommendations for integrating the top candidates into a Python-based development workflow (e.g., using pre-commit hooks):
Footnotes
- [1] GitHub - hukkin/mdformat: CommonMark compliant Markdown formatter: https://github.com/hukkin/mdformat
- [2] Formatting style - mdformat 0.7.22 documentation: https://mdformat.readthedocs.io/en/stable/users/style.html
- [3] Markdown linters and formatters - Varac's documentation: https://www.varac.net/docs/markup/markdown/linting-formatting.html
- [4] Configureable markdown formatting · Issue 331 · hukkin/mdformat: https://github.com/executablebooks/mdformat/issues/331
- [5] flowmark·PyPI: https://pypi.org/project/flowmark/
- [6] flowmark: A better auto-formatter for Markdown : r/Python: https://www.reddit.com/r/Python/comments/1lwr4g1/flowmark_a_better_autoformatter_for_markdown/
- [7] GitHub - notslang/tidy-markdown: Beautify Markdown, fixing formatting mistakes and standardizing syntax: https://github.com/notslang/tidy-markdown
- [8] GitHub - shurcooL/markdownfmt: Like gofmt, but for Markdown.: https://github.com/shurcooL/markdownfmt
- [9] GitHub - Kunde21/markdownfmt: Like gofmt, but for Markdown.: https://github.com/Kunde21/markdownfmt
- [10] dprint-plugin-markdown - crates.io: Rust Package Registry: https://crates.io/crates/dprint-plugin-markdown/range/=0.18.0
- [11] Markdown Plugin - dprint - Code Formatter: https://dprint.dev/plugins/markdown/
- [12] Setext vs atx Header Styles in Markdown | Brianary: http://webcoder.info/markdown-headers.html
- [13] How to config prettier not to auto-wrap markdown table?: https://stackoverflow.com/questions/69872327/how-to-config-prettier-not-to-auto-wrap-markdown-table/71554135
- [14] Fix markdown tables with prettier - Stack Overflow: https://stackoverflow.com/questions/66239138/fix-markdown-tables-with-prettier
- [15] Options - Prettier: https://prettier.io/docs/options
- [16] Prettier 1.8: Markdown Support: https://prettier.io/blog/2017/11/07/1.8.0.html
- [17] Releases · prettier/prettier - GitHub: https://github.com/prettier/prettier/releases
- [18] Better way to indent ordered lists in Markdown – Matias Kinnunen: https://mtsknn.fi/blog/indenting-ordered-lists-in-markdown/
- [19] Markdown Unordered List Item Symbol · Issue #4251 - GitHub: https://github.com/prettier/prettier/issues/4251
- [20] flowmark·PyPI: https://pypi.org/project/flowmark/
- [21] flowmark: A better auto-formatter for Markdown : r/Python: https://www.reddit.com/r/Python/comments/1lwr4g1/flowmark_a_better_autoformatter_for_markdown/
- [22] GitHub - moorereason/mdfmt: Like gofmt, but for Markdown with front matter.: https://github.com/moorereason/mdfmt
- [23] Dependencies | github.com/hashicorp/terraform-plugin-docs | Go ...: https://deps.dev/go/github.com%2Fhashicorp%2Fterraform-plugin-docs/v0.21.0/dependencies?filter=license%3A%22MIT%22
- [24] Open Source Licenses - Spaceti: https://landing.spaceti.com/support/license/