Skip to content

About

Verify Markdown style links are valid

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

link_verifier

CLI tool to verify markdown-style local links such as [text](./path/to/file.md).

If a scanned file contains markdown-style links in docs or markdown-style comments, those paths must resolve to real files or link_verifier exits with an error.

Features

  • Resolves relative links from the source file's directory, not the working directory
  • Handles balanced parentheses in filenames, e.g. [sheet](Onboarding Sheet (1).xlsx)
  • Decodes percent-encoded URLs, e.g. Scope%20Doc.md resolves to Scope Doc.md, and Plan%20%E2%80%93%20Draft.md to Plan – Draft.md. A link whose filename literally contains % is also checked undecoded
  • Recognises directory links like [assessment](assessment/)
  • Skips any link carrying a URI scheme, so http://, https://, mailto:, tel: and application schemes such as obsidian:// or shortcutapp:// are left alone, along with bare anchors (#heading) and query-only links (?tab=...)
  • Treats a colon inside a filename as part of the path, so Meeting: notes.md and C:/Users/a.md are still checked
  • Strips #fragment suffixes before checking file paths
  • Exclude files or directories with --except / -x regex patterns (repeatable)
  • Skip links inside fenced code blocks and inline code with --no-code-links
  • Require reciprocal links with --assert-backlinks, so traversal works in both directions

Install

Download the latest release (native binary; no runtime dependency):

# Example: macOS arm64
gh release download --repo C-Sinclair/link_verifier --pattern 'link_verifier-macos-arm64' --output ~/.local/bin/link_verifier --clobber
chmod +x ~/.local/bin/link_verifier

Pick the asset matching your platform:

  • link_verifier-macos-arm64
  • link_verifier-linux-x86_64

Homebrew support is planned after 1.0; for now, GitHub release assets are the fastest path.

Build from source

make build

Usage

./link_verifier <target> [target ...]

Targets can be:

  • A file path (e.g. README.md)
  • A directory (recursively scans files under it)
  • A wildcard pattern (e.g. docs/*.md)
  • Multiple targets in one run

Options

./link_verifier --help
./link_verifier --version

Skipping code links

Use --no-code-links to ignore links inside fenced code blocks and inline code:

./link_verifier docs/ --no-code-links

Asserting backlinks

Use --assert-backlinks to require that a link is reciprocated. If a.md links to b.md, then b.md must link back to a.md:

./link_verifier docs/ --assert-backlinks

A missing backlink is reported as a.md:3: no backlink from b.md and exits 2, the same as a broken link. The flag is off by default, because one-way links are normal in most documentation.

Only edges where both files are in the scanned set are judged. If docs/a.md links to ../README.md and you scan only docs/, the tool has never parsed README.md and says nothing about it. Widen the target to include both ends.

A #fragment is stripped before comparison, and ./.. segments are collapsed, so ../a.md from docs/sub/b.md counts as a backlink to docs/a.md.

Excluding files

Use -x / --except to skip files matching a regex pattern. The flag can be repeated:

./link_verifier docs/ -x vendor/ -x "test/fixtures"

Exit codes

  • 0: all links valid
  • 1: usage/target/read error
  • 2: broken links found, or missing backlinks when --assert-backlinks is set

Benchmark

Run:

./scripts/benchmark.sh

The script builds the native binary via dune build and benchmarks the default _build/default/bin/main.exe unless BINARY is set.

Default dataset generated by the script:

  • depth: 3
  • files per level: 100
  • total markdown files: 300
  • randomized content with mixed valid/invalid markdown links (BROKEN_RATE=0.35)

Latest sample run:

single-file-mixed: min=2.59ms avg=3.39ms p95=3.73ms runs=15 exit=2
directory-recursive-mixed: min=8.82ms avg=9.58ms p95=10.32ms runs=15 exit=2
wildcard-pattern-mixed: min=5.25ms avg=5.84ms p95=7.21ms runs=15 exit=2

Environment used for this run (relevant to native binary execution):

  • OCaml 5.3.0
  • dune 3.21.1
  • macOS 15.7.1 (Darwin 24.6.0, arm64)

Hook integration

Claude Code hook example

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "./link_verifier docs/*.md"
          }
        ]
      }
    ]
  }
}

Git pre-commit example

#!/bin/sh
./link_verifier README.md docs/*.md
status=$?
if [ "$status" -ne 0 ]; then
  echo "link_verifier failed"
  exit "$status"
fi

When a bad link is found, output includes file + line number, for example:

docs/guide.md:42: broken link -> ./missing-file.md

About

Verify Markdown style links are valid

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages