Select Markdown sections by heading path
Project description
mdselect
Select Markdown sections by heading path.
mdselect is a small, zero-runtime-dependency command-line tool for inspecting
a Markdown outline and extracting complete sections without losing their
original formatting.
Why mdselect?
Line-oriented tools such as grep can find a heading, but they do not know
where that section ends. A full Markdown AST library understands much more of
the format, but usually requires writing a program and choosing rendering
semantics.
mdselect fills the gap:
- query the heading hierarchy directly from the shell;
- return a matched heading together with its body and descendants;
- preserve the original Markdown content without parsing and re-rendering it;
- use regular expressions in a compact heading path expression;
- run with only the Python standard library.
Installation
mdselect requires Python 3.10 or newer.
pipx install mdselect
# or
uv tool install mdselect
# or
python -m pip install mdselect
To install a development checkout:
python -m pip install -e ".[test]"
Codex plugin
This repository also provides the recall-markdown-memory skill as an
installable Codex plugin. The skill uses the mdselect executable from the
task environment; installing the plugin does not install Python or the PyPI
package automatically.
Install the CLI first and verify that it is on PATH:
pipx install mdselect
mdselect --version
Then add the repository marketplace and install the plugin:
codex plugin marketplace add stevenybw/mdselect
codex plugin add mdselect@mdselect
Start a new Codex session after installation. Invoke the skill explicitly with
$mdselect:recall-markdown-memory, or ask Codex to recall a decision, preference,
or other context from Markdown memory and let the skill trigger automatically.
The first release targets shell-enabled Codex CLI, IDE, and local desktop tasks. For a Codex Cloud environment, add CLI installation to its setup script, for example:
python -m pip install mdselect
The skill is read-only. It searches only user-specified or project-configured
memory locations, plus the conventional MEMORY.md, memory/, and
.agents/memory/ paths; it does not scan every Markdown file by default.
Quick start
Given handbook.md:
# Engineering Handbook
## Getting Started
Introductory material.
### Local Setup
Create a virtual environment.
## Operations
Operational notes.
Print its outline:
mdselect outline handbook.md
# Engineering Handbook
## Getting Started
### Local Setup
## Operations
Extract the complete Getting Started subtree:
mdselect select --mpath '/Engineering/Getting Started' handbook.md
## Getting Started
Introductory material.
### Local Setup
Create a virtual environment.
Add a trailing slash to omit the matched heading line:
mdselect select --mpath '/Engineering/Getting Started/' handbook.md
mpath syntax
An mpath is a slash-separated heading path:
| Form | Meaning |
|---|---|
/Guide/Install |
Absolute query: the first segment only matches level-one headings. |
Install |
Relative query: the first segment matches headings at any level. |
/^Guide$/^Install$/ |
Each segment is a Python regular expression; anchors make matches exact. The trailing slash omits the matched heading. |
/ |
Return the original document unchanged. |
The complete rules are:
- Each non-empty segment is compiled as a Python regular expression and
matched with
re.search. - A leading slash makes the query absolute: its first segment is tested only
against level-one (
#) headings. - Without a leading slash, the first segment is tested against every heading.
- Every later segment is tested against descendants of the current matches, at any depth.
- A trailing slash excludes the final matched heading line while keeping its body and descendants.
/,//, and an empty expression are no-ops that return the original document.- A slash cannot appear inside a segment.
- Multiple matches retain document order and are separated by one blank line.
- When both an ancestor and its descendant match, only the ancestor is rendered as a result because its subtree already contains the descendant.
mdselect select -h prints these rules without requiring the README.
Exit status
| Situation | Status |
|---|---|
| Successful outline or selection | 0 |
| No matching section | 0, with empty standard output |
| Invalid arguments or regular expression | 2 |
| File cannot be read | non-zero |
The no-match behavior is intentional so selection can be used in shell pipelines without treating an optional section as a command failure.
Supported Markdown and boundaries
- ATX headings from
#through######are supported when the marker starts in column one and is followed by whitespace. - Optional closing hash markers are removed from the heading title used for matching.
- Backtick and tilde fenced code blocks are recognized; heading-like lines inside a fence are ignored.
- Selected content is rendered from the original lines, preserving tables, blockquotes, blank lines, inline markup, and fences.
- Files are read as UTF-8.
- Setext headings are not supported.
mdselectdoes not interpret front matter, links, inline Markdown, or HTML as an AST.
Comparison
| Tool | Best for | Structural section extraction |
|---|---|---|
grep / rg |
Finding matching lines across files | No |
| Markdown AST library | Full parsing, transformation, and custom rendering | Yes, with application code |
mdselect |
Shell-friendly outline and heading-subtree selection | Yes, directly from the CLI |
Use grep or rg to discover candidate files, then use mdselect when
the unit you need is a Markdown section rather than a line.
Python API
from mdselect import outline, select
print(outline(markdown_text))
section = select(markdown_text, "/Guide/Install")
The package also exports Section, parse, render_subtree, and
InvalidPattern.
Contributing and security
See CONTRIBUTING.md for development and pull request guidance. Report security issues privately as described in SECURITY.md.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mdselect-0.2.0.tar.gz.
File metadata
- Download URL: mdselect-0.2.0.tar.gz
- Upload date:
- Size: 18.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7be1b3201403683032948949c5f369694f8dc3afc9d2ee76a56adb2e5ed6d46d
|
|
| MD5 |
c5c5c1d2c60472a82c8aba221cc3ffb0
|
|
| BLAKE2b-256 |
5ecc3d08e52969a3d3ac9e98f24b2d8752e834971ad8d7d7776b3c0dcf2f2bdc
|
Provenance
The following attestation bundles were made for mdselect-0.2.0.tar.gz:
Publisher:
release.yml on stevenybw/mdselect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mdselect-0.2.0.tar.gz -
Subject digest:
7be1b3201403683032948949c5f369694f8dc3afc9d2ee76a56adb2e5ed6d46d - Sigstore transparency entry: 2135966042
- Sigstore integration time:
-
Permalink:
stevenybw/mdselect@7821517fb2b865b3b50d98915524c91f2b1cae84 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/stevenybw
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7821517fb2b865b3b50d98915524c91f2b1cae84 -
Trigger Event:
release
-
Statement type:
File details
Details for the file mdselect-0.2.0-py3-none-any.whl.
File metadata
- Download URL: mdselect-0.2.0-py3-none-any.whl
- Upload date:
- Size: 10.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e3e0783d321bc54844305f1f2e565ff257a3267bfd004da78b8a8fb80a36034
|
|
| MD5 |
0b28a3cb0b65da58d60f07ede19fcbf4
|
|
| BLAKE2b-256 |
acf5c047618faeccb91f12fbc54a725c4ac347ddb4fe67c3a654ced2c4c0344f
|
Provenance
The following attestation bundles were made for mdselect-0.2.0-py3-none-any.whl:
Publisher:
release.yml on stevenybw/mdselect
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mdselect-0.2.0-py3-none-any.whl -
Subject digest:
2e3e0783d321bc54844305f1f2e565ff257a3267bfd004da78b8a8fb80a36034 - Sigstore transparency entry: 2135966050
- Sigstore integration time:
-
Permalink:
stevenybw/mdselect@7821517fb2b865b3b50d98915524c91f2b1cae84 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/stevenybw
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7821517fb2b865b3b50d98915524c91f2b1cae84 -
Trigger Event:
release
-
Statement type: