{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreigymzsrmga2zp3j3o5qmgeqoaodvqnbssdnozue63xcwgeafzeasi",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3moh6h4i2a7q2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreicx7i6al3nzag3bvlyaetib66tzzdhrh6bsztitgtsyvkslsfreha"
    },
    "mimeType": "image/webp",
    "size": 114400
  },
  "path": "/_06a3df6b50aec966668fb/your-docs-have-dead-links-i-built-a-zero-dependency-cli-that-catches-the-local-ones-no-network-5a39",
  "publishedAt": "2026-06-17T01:10:48.000Z",
  "site": "https://dev.to",
  "tags": [
    "showdev",
    "markdown",
    "cli",
    "opensource",
    "https://www.npmjs.com/package/linkbust",
    "https://pypi.org/project/linkbust",
    "https://github.com/jjdoor/linkbust"
  ],
  "textContent": "You rename a file, restructure a folder, tweak a heading — and quietly leave a trail of dead links across your README and docs. Nobody notices until a reader clicks `./old-guide.md` and gets a 404.\n\nThere are tools for this, but each asks for something:\n\n  * **markdown-link-check** works, but it pulls in ~9 dependencies and its headline feature is hitting **external URLs over HTTP** — slow, rate-limited, and flaky in CI (your build shouldn't go red because someone's blog was down).\n  * **lychee** is fast and great, but it's a **Rust binary** to install.\n  * The old Python option, `mlc`, has been **unmaintained since 2021**.\n\n\n\nI wanted the deterministic part — **local links** — to be instant and dependency-free. So I built **linkbust** :\n\n\n\n    npx linkbust\n\n\n\n    README.md:8      ✗  ./setup.md           (file not found)\n    docs/api.md:14   ✗  #usage               (no anchor in this file)\n    guide.md:3       ✗  ./api.md#missing     (no anchor in ./api.md)\n\n    ✗ 3 broken · 142 local links checked · 38 external skipped (never fetched)\n\n\n##  What it checks (and skips)\n\n  * **Relative file links and image sources** resolve on disk.\n  * **Anchors** — both `[x](#section)` in the same file and `[x](other.md#section)` across files — match a real heading (GitHub-style slug) or an explicit `<a id=\"…\">`.\n  * **Reference links** `[text][ref]` have a matching `[ref]: …` definition.\n  * Links inside fenced/inline **code blocks are ignored** , so examples don't cause false alarms.\n\n\n\nIt deliberately **never makes a network request** — `http(s)`, `mailto:`, and `/absolute` paths are classified and skipped. That's the whole design: because it's offline, it's instant and can't flake. Which makes it ideal as a **pre-commit hook** :\n\n\n\n    - repo: local\n      hooks:\n        - id: linkbust\n          name: linkbust\n          entry: npx linkbust\n          language: system\n          pass_filenames: false\n\n\nExit code is `1` if anything's broken, so CI fails cleanly.\n\n##  Zero dependencies, both ecosystems\n\nPure standard library — a small Markdown parser (code masking, reference links, GitHub anchor slugs) plus filesystem checks. The Node and Python ports are behavior-identical, byte-for-byte (same output, same exit codes).\n\n\n\n    npx linkbust              # Node >= 18\n    pip install linkbust      # Python >= 3.8\n\n\n  * npm: https://www.npmjs.com/package/linkbust\n  * PyPI: https://pypi.org/project/linkbust\n  * GitHub: https://github.com/jjdoor/linkbust\n\n\n\nDo you check links in CI, or only find out when someone reports a 404? And for _external_ URLs — worth the flakiness to catch them, or do you treat those as out of scope too?",
  "title": "Your docs have dead links. I built a zero-dependency CLI that catches the local ones — no network"
}