Check Markdown for broken LOCAL links — relative file paths and #heading anchors. No network, zero dependencies, fast enough for a pre-commit hook.
Project description
linkbust
Find broken local links in your Markdown — relative file paths that don't
exist and #anchors that don't resolve to a heading. No network, zero
dependencies, fast enough to drop in a pre-commit hook.
linkbust
docs/setup.md:14 ✗ ./install.md (file not found)
README.md:8 ✗ #quick-start (no anchor "#quick-start" in this file)
guide.md:23 ✗ ./api.md#missing (no anchor "#missing" in ./api.md)
✗ 3 broken · 142 links checked in 26 file(s) (38 external skipped)
Why
Reorganize your docs, rename a file, tweak a heading — and quietly leave a trail of dead links behind. The checkers that catch this come with baggage:
- markdown-link-check works, but it pulls in ~9 dependencies and its main job is hitting external URLs over HTTP — slow, flaky in CI, and rate-limited.
- lychee is fast and thorough, but it's a Rust binary to install.
- The old Python option (
mlc) has been unmaintained since 2021.
linkbust does the part that breaks most often and is fully deterministic:
local links. It never makes a network request — so it's instant, works
offline, and won't flake your CI because someone's blog was down. That makes it
ideal for a pre-commit hook or a fast docs lint.
What it checks
- Relative file links —
[x](../docs/api.md), image sources — resolve on disk. - Anchors —
[x](#section)and[x](other.md#section)match a real heading (GitHub-style slug) or an explicit<a id="...">/id="...". - Reference links —
[text][ref]has a matching[ref]: …definition. - Links inside fenced/inline code are ignored, so examples don't trip it up.
It skips (never fetches) http(s), mailto:, protocol-relative //…, and
/absolute paths — checking those is a different, networked job.
Usage
linkbust # every .md under the current directory
linkbust README.md # a single file
linkbust docs/ CHANGELOG.md # files and/or directories (recursive)
| Option | |
|---|---|
--json |
machine-readable results |
-q, --quiet |
print nothing when everything's fine (great for hooks) |
--no-color |
exit 0 all local links resolve
exit 1 one or more broken links
exit 2 usage / read error
As a pre-commit hook
# .pre-commit-config.yaml
- repo: local
hooks:
- id: linkbust
name: linkbust
entry: linkbust
language: system
pass_filenames: false
Install
pip install linkbust # Python >= 3.8
npx linkbust # Node >= 18 (byte-for-byte port)
- PyPI: https://pypi.org/project/linkbust/
- npm: https://www.npmjs.com/package/linkbust
- GitHub: https://github.com/jjdoor/linkbust-py · linkbust (Node)
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 linkbust-0.1.0.tar.gz.
File metadata
- Download URL: linkbust-0.1.0.tar.gz
- Upload date:
- Size: 10.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7774c28a797f903c956b451ccbcbe6fb210faa27283ca97bca4d1062a3b6cb51
|
|
| MD5 |
4bcc74147aacde2746aa902e7690f0ac
|
|
| BLAKE2b-256 |
835ef1dc26990a55f6ccee06eca14844a68e3e2860abd028a0ce1ef671d14e72
|
File details
Details for the file linkbust-0.1.0-py3-none-any.whl.
File metadata
- Download URL: linkbust-0.1.0-py3-none-any.whl
- Upload date:
- Size: 9.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a14ca61444525dff2812f18486e8c9a7fb7078a6327766d2098abd4f03317fc
|
|
| MD5 |
8b44d1468cca08c7dfe03ad46b9a1689
|
|
| BLAKE2b-256 |
31b0aa96009dcdf49dff14db668b1b409d12c14b8a8a48c7f9a59a1951846bc6
|