Skip to main content

Split and recombine markdown files by ATX headers

Project description

mdstruct

Split and recombine markdown files by ATX headers.

Similar Projects

  • Inspired by gron - make markdown greppable by splitting into hierarchical files, then recombine them.
  • mdsplit - Python CLI for splitting markdown files (focuses on flat output structure), but has no easy reversible join.

Installation

uv pip install mdstruct
# or
pip install mdstruct

Tip: For convenience, consider adding an alias to your shell:

# Add to ~/.bashrc, ~/.zshrc, or equivalent
alias md='mdstruct'

# Then use:
md split foo
md join foo

Usage

Auto-detection (recommended)

Use mdstruct directly to automatically detect whether to split or join based on what exists:

# Auto-detects: splits if foo.md exists, joins if foo/ exists
mdstruct foo

# Works with explicit extensions too
mdstruct foo.md    # splits foo.md → foo/
mdstruct foo/      # joins foo/ → foo.md

# With options
mdstruct foo --level 3    # split with custom level
mdstruct foo --force      # skip overwrite confirmation

Tip: Create an alias for even shorter commands:

alias md='mdstruct'
# Then use: md foo

Split markdown file

# Auto-detect and split (uses smart default level)
mdstruct foo

# Explicit split command
mdstruct split foo

# Split up to H3 level
mdstruct split foo --level 3

Example:

example.md:

# First Section

Content before subsections.

## Subsection A

Content A.

## Subsection B

Content B.

After mdstruct example (or mdstruct split example):

example/
├── first-section/
│   ├── README.md         # "# First Section\n\nContent before subsections."
│   ├── subsection-a.md   # "# Subsection A\n\nContent A."
│   └── subsection-b.md   # "# Subsection B\n\nContent B."

Alphanumeric Prefixes with Fractional Indexing:

Prefixes are only added when sections are not in alphabetical order or when there are duplicates.

Automatic Indexing (during split):

  • Uses rescaling based on total item count
  • ≤36 items: 1-char indexes (0, 1, ..., 9, a, ..., z)
  • 37-702 items: 2-char indexes (aa, ab, ..., zz)
  • 703+ items: 3-char indexes (aaa, aab, ..., zzz)

Manual Insertion (between splits):

  • Add fractional characters after the index to insert between items
  • Between 0 and 1: create 0m.new-section/ (index=0, fraction=m)
  • Between z and aa: create z5.new-section/ (index=z, fraction=5)
  • Between aa and ab: create aam.new-section/ (index=aa, fraction=m)
  • Can nest deeper: 0mm., 0mmm., etc. for very tight spaces

How it works:

  • Format: <index><fraction>.slug where fraction is optional
  • During join, detects most common prefix length to determine index width
  • Everything beyond that length is treated as manual fractional insertion
  • Preserves lexicographic sorting automatically

Examples:

# Alphabetical headers → no prefixes needed
apple/
banana/
cherry/

# Non-alphabetical (3 items) → single-char indexes
0.zebra/
1.apple/
2.banana/

# Manual insertion between 0 and 1
0.zebra/
03.manually-inserted/    # index=0, fraction=3
1.apple/
2.banana/

# Many items (50) → two-char indexes for all
00.first/
...
03.second/    # index=3, fraction=0
...
bx.fiftieth/

# Rebalancing: just run join + split
mdstruct join foo
mdstruct split foo
# Removes all fractional insertions, resets to clean sequential indexes

Join split files

# Auto-detect and join (checks if foo/ exists)
mdstruct foo

# Explicit join command
mdstruct join foo

# Join with explicit output
mdstruct join foo --output combined.md

# Force overwrite without confirmation
mdstruct foo --force

Manual insertion workflow

After splitting, you can manually insert new sections between existing ones:

# Split into directories
mdstruct split doc.md

# Filesystem shows:
# doc/
# ├── 0.intro/
# ├── 1.main-content/
# └── 2.conclusion/

# Manually create new section between intro and main-content
mkdir doc/0m.background
echo "# Background" > doc/0m.background/README.md
echo "Historical context..." >> doc/0m.background/README.md

# Join back - manual insertion is preserved in order
mdstruct join doc -o doc.md

# Result preserves order:
# - Intro
# - Background (inserted)
# - Main Content
# - Conclusion

Development

Setup

# Clone repository
git clone https://github.com/DJRHails/mdstruct
cd mdstruct

# Install dependencies with uv
uv pip install -e ".[dev]"

Testing

# Run tests
uv run pytest -v

# Run tests with coverage
uv run pytest --cov=mdstruct --cov-report=term-missing

# Run linting
uv run ruff check .

# Format code
uv run ruff format .

Version Management

This project uses bumpver for version management:

# Show current version
uv run bumpver show

# Bump patch version (1.0.0 → 1.0.1)
uv run bumpver update --patch

# Bump minor version (1.0.0 → 1.1.0)
uv run bumpver update --minor

# Bump major version (1.0.0 → 2.0.0)
uv run bumpver update --major

# Dry run to preview changes
uv run bumpver update --patch --dry

Publishing

# Build package
uv build

# Publish to PyPI
uv publish

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mdstruct-1.2.1.tar.gz (51.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mdstruct-1.2.1-py3-none-any.whl (11.7 kB view details)

Uploaded Python 3

File details

Details for the file mdstruct-1.2.1.tar.gz.

File metadata

  • Download URL: mdstruct-1.2.1.tar.gz
  • Upload date:
  • Size: 51.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for mdstruct-1.2.1.tar.gz
Algorithm Hash digest
SHA256 5d0905047bc1f90bf92f505e23e98a284b50e4ea423186165bdfb6f721485b19
MD5 e97323229eb719405e9b6f9771f2519b
BLAKE2b-256 3a006089a0ad27ec10319812a7b48c4a51d8bd5d02ff8fe774c48725e8fa30b4

See more details on using hashes here.

File details

Details for the file mdstruct-1.2.1-py3-none-any.whl.

File metadata

  • Download URL: mdstruct-1.2.1-py3-none-any.whl
  • Upload date:
  • Size: 11.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for mdstruct-1.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 86ecbe4e5b831ebad276d322912a13491e30607e0155065cfde6d9d7a39b9268
MD5 8c9e60bb00e6083a4f568a36d2a9d996
BLAKE2b-256 bd3bcab87b400770ac44220a97ff465a5f87cba819b2841a1aed3f3cc05ae07c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page