markdown-patch - v0.4.3

markdown-patch

Make targeted, structure-aware edits to Markdown documents — without sed.

Instead of treating a document as a blob of text, markdown-patch understands its structure (headings, block references, frontmatter) and lets you append, prepend, or replace content at a specific location within it.

Available as both a CLI tool (mdpatch) and a TypeScript/JavaScript library.

API docs: https://coddingtonbear.github.io/markdown-patch/

npm install markdown-patch

The mdpatch binary is included and available after install.

Given a document notes.md:

---
status: in-progress
---

# Meeting Notes

## Action Items

- Follow up with design team

Append a new item under Action Items:

echo "- Send the report" | mdpatch patch append heading "Meeting Notes::Action Items" notes.md

Replace the status frontmatter field:

echo '"done"' | mdpatch patch replace frontmatter status notes.md

Not sure what targets exist in a document? Use print-map:

mdpatch print-map notes.md

Apply a single patch operation.

mdpatch patch [options] <operation> <targetType> <target> <documentPath>
  • <operation>append, prepend, or replace
  • <targetType>heading, block, or frontmatter
  • <target> — the target address (see below)
  • <documentPath> — file to modify (patched in-place by default)

Options:

Flag Description
-i, --input <path> Read content from a file instead of stdin
-o, --output <path> Write result to a file instead of patching in-place; use - for stdout
-d, --delimiter <str> Heading path delimiter (default: ::)

Apply one or more patch instructions from a JSON patch file.

mdpatch apply [options] <documentPath> <patchFile>

The patch file should be a JSON object (single instruction) or JSON array (multiple instructions). Use - to read from stdin.

Extract the content of a specific target and write it to stdout (or a file).

mdpatch query [options] <targetType> <target> <documentPath>

Show all patchable targets discovered in a document, useful for finding the right target address.

mdpatch print-map <documentPath> [regex]

Address a section by its heading path, delimited by :: (or a custom delimiter). Nested headings use the full path:

# Target the top-level "Overview" section
mdpatch patch append heading "Overview" notes.md

# Target a nested heading
mdpatch patch append heading "Meeting Notes::Action Items" notes.md

Address a paragraph, table, or other block by its Obsidian block ID (e.g. ^abc123):

echo "New row content" | mdpatch patch append block "abc123" notes.md

When the target block is a Markdown table and content type is application/json, rows can be appended or prepended as JSON arrays.

Address a YAML frontmatter key by name. Content is treated as JSON:

# Set a scalar
echo '"done"' | mdpatch patch replace frontmatter status notes.md

# Append to a list
echo '"new-tag"' | mdpatch patch append frontmatter tags notes.md
import { applyPatch, getDocumentMap } from "markdown-patch";

const document = `# My Note\n\n## Tasks\n\n- Buy milk\n`;

const patched = applyPatch(document, {
operation: "append",
targetType: "heading",
target: ["My Note", "Tasks"],
content: "- Write tests\n",
});

getDocumentMap parses a document and returns its structure — useful for inspecting what headings, blocks, and frontmatter fields are available before patching.

Option Type Description
operation "append" | "prepend" | "replace" What to do
targetType "heading" | "block" | "frontmatter" What to target
target string | string[] Target address (array for heading paths)
content string Content to apply
contentType "text/markdown" | "application/json" Defaults to text/markdown
createTargetIfMissing boolean Create the heading or frontmatter field if it doesn't exist
applyIfContentPreexists boolean By default, patching fails if the content is already present; set this to override
trimTargetWhitespace boolean Trim whitespace from the target boundary before joining