# markdownlint (https://openknowledge.ai/docs/advanced/content-rules/markdownlint)

The markdownlint linter for content rules — VS Code-parity defaults, your project's native `.markdownlint.*` config, and a no-code browser for all 53 rules.

markdownlint is the content-rules linter for markdown style — the standard [markdownlint](https://github.com/DavidAnson/markdownlint) engine, the same one behind `markdownlint-cli2` and the popular editor extensions.

This page covers its defaults, how it reads your project's config, and the built-in rule browser. For the linter-agnostic parts — where problems appear, the Problems panel, `ok lint`, and agent advisories — see the [content rules overview](https://openknowledge.ai/docs/advanced/content-rules/overview). For validating frontmatter rather than markdown style, see [Frontmatter schemas](https://openknowledge.ai/docs/advanced/content-rules/frontmatter).

## Default rules

The rules aren't stored in OK config — they live in your project's own native markdownlint file (see [below](https://openknowledge.ai/docs/advanced/content-rules/markdownlint#your-markdownlint-file)). With **no file**, OK uses the same default as the [VS Code markdownlint extension](https://github.com/DavidAnson/vscode-markdownlint): every rule on, except `MD013` (line-length). Nothing else is pre-disabled — so, exactly as in VS Code:

- `MD033` flags inline HTML, including MDX/JSX components like `<Callout>`,
- `MD041` flags docs that don't open with a top-level heading,
- `MD025` flags a frontmatter `title` beside an H1.

Turn off whichever don't fit your project — the first change [creates the file for you](https://openknowledge.ai/docs/advanced/content-rules/markdownlint#the-rule-browser).

## Your `.markdownlint.*` file

The rules live in your project's own native markdownlint file — `.markdownlint.jsonc`, `.markdownlint.json`, `.markdownlint.yaml`, or `.markdownlint.yml` (`.markdownlintrc` is recognized too; the first that exists wins). It's a plain, portable markdownlint config: the same file `markdownlint-cli2` and editor extensions read, with nothing OK-specific in it.

- **A file governs wholesale.** When a native file exists it's honored exactly as `markdownlint-cli2` would honor it — OK layers nothing underneath.
- **Per-folder cascade.** The nearest `.markdownlint.*` file on the walk from a doc's folder up to the project root governs that doc, matching `markdownlint-cli2` conventions. Subfolders inherit shared config explicitly via `extends` with a relative path (npm-package `extends` isn't resolved).
- **Aliases and severities.** Rule aliases are valid keys (`line-length` ≡ `MD013`, matched case-insensitively), and `"error"` / `"warning"` severity strings are honored — either as the rule's value (`"MD010": "error"`) or as a `severity` key in its options object. Everything else reports as a warning.
- **Executable configs.** A `.markdownlint.cjs` / `.mjs` is detected but **not executed**: you get a loud configuration warning, linting falls back to OK's defaults, and OK will never rewrite the file. Convert it to JSON, JSONC, or YAML to have it govern.

## The rule browser

**Settings ▸ Plugins ▸ markdownlint** (markdownlint appears there once the plugin is enabled) lists the full catalog — all 53 rules, generated from the installed engine's own config schema, so it always matches what actually runs. Search by id, alias, or name; browse by category (Headings, Lists, Whitespace, Code, Links & images, Style); or check **Only modified** to see just the rules your config changes. Expanding a rule reveals a link to its upstream documentation and typed editors for each of its options.

The same browser also opens **directly on the config file**. Open your project's root `.markdownlint.json` or `.markdownlint.jsonc` in the editor (reveal hidden files to find it) and use the **Source / Rules** toggle: **Source**, the default, shows the raw file read-only; **Rules** is the same no-code browser. Your choice is remembered separately from the document editor's own Visual/Markdown mode.

That toggle appears for JSON and JSONC configs only — a `.markdownlint.yaml`, `.markdownlint.yml`, or `.markdownlintrc` opens in the read-only file preview instead. And because rule edits target the project's governing root config, **Rules** is enabled only for that file; open a nested JSON config and **Rules** is disabled with a tooltip while **Source** still shows it.

Every edit writes back to your native `.markdownlint.*` file, and a rule you configured under an alias stays under that alias. JSON and JSONC files get minimal text edits, so comments and formatting survive; a YAML config is re-serialized, which drops its comments. Severity strings are shown as a read-only badge. If the project has no file yet, the first change creates a `.markdownlint.json` seeded with the defaults — from then on that file is the whole story for OK and every other markdownlint tool.

## What a finding looks like

A markdownlint finding carries the rule id as its `code` under source `markdownlint` — rendered as `markdownlint/MD010` in reports — with the upstream engine's message. In `ok lint` text output:

```text
docs/guide.md
  7:5     warning  Hard tabs: Column: 5  markdownlint/MD010
```

And as a diagnostic in `ok lint --json` (or the MCP `lint` tool's structured content), where an auto-fixable rule also carries its `fixes` — the exact text edits `--fix` or the editor's **Fix** action would apply:

```json
{
  "range": { "start": { "line": 6, "character": 4 }, "end": { "line": 6, "character": 5 } },
  "severity": "warning",
  "source": "markdownlint",
  "code": "MD010",
  "message": "Hard tabs: Column: 5",
  "fixes": [
    { "range": { "start": { "line": 6, "character": 4 }, "end": { "line": 6, "character": 5 } }, "newText": " " }
  ]
}
```

JSON ranges are 0-based and end-exclusive; the text report is 1-based. A rule that can't be auto-fixed simply has no `fixes` key. The shared report shapes — file grouping, counts, configuration warnings — are on the [overview](https://openknowledge.ai/docs/advanced/content-rules/overview#what-it-returns).

## Auto-fix and severities

- **Auto-fix covers only some rules.** markdownlint can mechanically fix problems like hard tabs, trailing whitespace, list-marker style, and blank-line spacing — but not ones that need a human decision, such as inline HTML (`MD033`) or a missing top-level heading (`MD041`). In source mode the **Fix** action appears only on a problem markdownlint can fix; the Problems panel offers **Fix all** to apply every fixable problem in the current scope at once; and `ok lint --fix` does the same across the whole project. Whatever's left is still reported, untouched — no affordance resolves the rules that need a human decision.
- **Severities.** A rule reports as a warning unless your config promotes it to `"error"`. Set `"error"` as the rule's value or via a `severity` key, then gate CI on just those rules with `ok lint --errors-only`.

## See also

- [Content rules overview](https://openknowledge.ai/docs/advanced/content-rules/overview): where problems show up, the Problems panel, `ok lint`, and agents
- [Frontmatter schemas](https://openknowledge.ai/docs/advanced/content-rules/frontmatter): the sibling linter for frontmatter validation
- [Configuration reference](https://openknowledge.ai/docs/reference/configuration): `config.yml` and what's shared via git
- [markdownlint rules](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md): the upstream rule reference