Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 

README.md

id MDS021
name include
status ready
description Include section content must match the referenced file.
category directive
nature directive
maintainability
signal fix for-diagnostic
near-duplicate sections that drift across files
adopt `<?include?>` so shared content has one source of truth
false
markdownlint
rumdl
mado
panache
obsidian-linter
gomarklint

MDS021: include

Include section content must match the referenced file.

Marker Syntax

<?include
file: path/to/file.md
strip-frontmatter: "true"
wrap: markdown
heading-level: "absolute"
?>
...included content...
<?/include?>

Do not use YAML folded scalars (>, >-) in the YAML body. Markdown parsers interpret > at the start of a line as a blockquote marker, which breaks the processing instruction content. Use literal block scalars (|, |-, |+) or quoted strings instead. See the generated-section concept for details.

Parameters

Parameter Required Default Description
file yes -- Relative path to include
extract no -- Dotted path through the extract projection of a kind-typed file:. Splices one leaf value
strip-frontmatter no "true" Remove YAML frontmatter (incompatible with extract:)
wrap no -- Wrap in code fence (value = language)
heading-level no -- "absolute" or 1-6: nest under parent, or pin shallowest heading to that level
heading-offset no -- Signed integer -6 to 6: shift every heading by N (incompatible with heading-level, extract:)

Link Adjustment

Relative link and image targets in included content are automatically rewritten so they resolve from the including file's directory. Absolute URLs, anchor-only links (#foo), and protocol links (http://, https://, mailto:) are not modified.

For example, DEVELOPMENT.md contains [rules](internal/rules/). When included from docs/guide.md, the link becomes [rules](../internal/rules/).

Heading-Level Adjustment

heading-level takes "absolute" or an integer 1-6.

"absolute" is parent-relative: included headings shift so the shallowest one becomes a child of the enclosing section. The marker sits under ## Project (level 2). The included file has ## Build (2) and ### Sub (3). The shift is 2 - 2 + 1 = 1: ### Build (3) and #### Sub (4). At the document root no shift is applied.

An integer pins the shallowest included heading to that level, whatever the source starts at, so it also works at the document root. heading-level: "2" renders a source # Title / ## Sub as ## Title / ### Sub. A deeper source is promoted; levels are capped at 1-6.

heading-level cannot combine with heading-offset or extract:.

Heading-Offset Adjustment

When heading-offset: "N" is set, every heading in the included content shifts by N levels. N is a signed integer from -6 to 6. A positive value demotes a heading (adds a #); a negative value promotes it. Levels are capped at the 1-6 range.

Unlike heading-level: "absolute", the shift is fixed. It does not read a preceding heading, so it also applies at the document root. heading-offset cannot combine with heading-level or extract:.

Cycle Detection

Include chains are tracked during check and fix. A diagnostic is emitted when:

  • A file includes itself (direct cycle).
  • A file includes B which includes A (indirect cycle, detected by scanning nested includes).
  • The include chain exceeds 10 levels deep.

The cycle message shows the full chain:

cyclic include: a.md -> b.md -> a.md

Config

rules:
  include: true

Disable:

rules:
  include: false

Examples

Basic Include

<?include
file: data.md
?>
Hello world
<?/include?>

With Code Fence Wrapping

<?include
file: config.yml
wrap: yaml
?>
```yaml
key: value
```
<?/include?>

With Frontmatter Kept

<?include
file: data.md
strip-frontmatter: "false"
?>
---
title: My Doc
---
Content here.
<?/include?>

With Heading-Level Shift

Given DEVELOPMENT.md contains ## Build and ### Sub, including under ## Project shifts headings one level down:

## Project

<?include
file: DEVELOPMENT.md
heading-level: "absolute"
?>
### Build

Steps here.

#### Sub

Details.
<?/include?>

With Heading Offset

Demote every heading in the included file by one level, even when no heading precedes the marker:

<?include
file: features.md
heading-offset: "1"
?>
## Was An H1 In The Source
<?/include?>

With Link Rewriting

Given DEVELOPMENT.md in the repo root contains [rules](internal/rules/), including it from docs/guide.md rewrites the link:

<?include
file: DEVELOPMENT.md
?>
See [rules](../internal/rules/) for details.
<?/include?>

Extract a Typed Value

extract: walks the JSON projection mdsmith extract would produce for a kind-typed file: target. The value is a dotted path. The directive splices the leaf:

<?include
file: docs/brand/messaging.md
extract: tagline.text
?>
Markdown, fast.
<?/include?>

A wrapper object with one content key (text, code, items, or rows) resolves to the inner value. So extract: tagline is the same as extract: tagline.text. Multi-key wrappers are ambiguous: the directive surfaces a lint error listing the keys. Frontmatter scalars sit under the frontmatter key: extract: frontmatter.title.

extract: is incompatible with strip-frontmatter:, heading-level:, and heading-offset:. The projection returns one scalar, so those parameters do not apply.

Bad — Outdated Content

<?include
file: data.md
?>
Outdated content
<?/include?>

Diagnostics

Condition Message
content mismatch generated section is out of date
missing file include file "x.md" not found
no file param include directive missing required "file" parameter
absolute path include directive has absolute file path
escapes root include file path escapes project root
no root for dotdot include file path contains ".." but project root is not configured
invalid heading-level include directive "heading-level" must be "absolute" or an integer between 1 and 6
invalid heading-offset include directive "heading-offset" must be an integer between -6 and 6
offset + level include directive "heading-offset" cannot be combined with "heading-level"
empty extract include directive "extract" value is empty
extract + sfm include directive "extract" cannot be combined with "strip-frontmatter"
extract + hl include directive "extract" cannot be combined with "heading-level"
extract + offset include directive "extract" cannot be combined with "heading-offset"
extract miss extract: missing key "x" in extract projection
extract no kind extract: "x.md" has no resolved kind; cannot project a typed value
extract not conformant extract: target file does not conform to its schema: ...
cyclic include cyclic include: a.md -> b.md -> a.md
depth exceeded include depth exceeds maximum (10)

Pattern

The bad pattern is a section duplicated across two files. The good pattern is one canonical source plus <?include?> references. The canonical source files live in pattern/bad/ and pattern/good/; the snippets below mirror those files for quick reference. The markdown-audit skill reads the folders directly.

Without the directive

README.md:

# Project

## Build

Run `make build` to compile the project. The
binary lands in `dist/`.

INSTALL.md:

# Install

## Build

Run `make build` to compile the project. The
binary lands in `dist/`.

With the directive

snippets/build.md:

Run `make build` to compile the project. The
binary lands in `dist/`.

README.md:

# Project

## Build

<?include
file: snippets/build.md
?>
Run `make build` to compile the project. The
binary lands in `dist/`.
<?/include?>

INSTALL.md follows the same shape.

Meta-Information