READMEのリンクが生きていても案内は古い? 公開状態とのズレを検査するreadme-entry-check

READMEのダウンロードリンクを開くと、ファイルは普通に取得できる。それでも、案内されている内容が今の公開状態と一致しているとは限りません。

たとえば「最新版」と案内しているリンクが、古いバージョンのassetを指したままになっている場合です。リンク先がHTTP 200を返しても、READMEの案内は古いままです。

このようなinstall/download entry pointと公開状態のズレ(semantic drift)を、小さな範囲で機械的に確認するCLI、readme-entry-checkを作りました。この記事では、v0.1.0の対応範囲、実例、結果の読み方を紹介します。

リンク切れだけでは見つからないREADMEのズレ

broken-link checkerで分かるのは、主にリンク先が応答するかどうかです。一方、READMEを使う人が知りたいのは、「この案内から、今の公開物にたどり着けるか」でもあります。

次は架空の例です。実在するプロジェクトの不具合を示すものではありません。

READMEの案内: 最新版のダウンロード
固定リンク: /releases/download/v1.1.0/widget-v1.1.0.zip
GitHubのlatest release: v1.2.0

v1.1.0のファイルが残っていればリンクは生きています。しかし「最新版」という案内とはズレています。readme-entry-checkは、対応する固定version参照とGitHubのlatest releaseを比較して、この種のズレを検出します。

ただし、古い版を意図的に案内している可能性もあります。近くの文脈を使うheuristicはありますが、任意の文章の意図を理解するわけではありません。検出結果は、人がREADMEを確認するための材料です。

作ったもの:小さなread-only CLI

readme-entry-checkは、READMEから対応する参照を抽出し、GitHub API、npm registry、PyPIの公開情報と照合します。Node.js 22以上とネットワーク接続が必要です。

外部依存packageはなく、cloneしたソースから使うsource-only toolです。npmにはpublishしていません。検査にAIやLLMは使わず、抽出ルール、数値version比較、公開APIの応答で判定します。

何を検査するか

  • GitHub release/version/asset:対象repoのversionを固定したrelease tag/download URLと、一部のversion付きassetファイル名を対象にします。GitHubのlatest releaseとの数値version比較や、latest releaseのasset名との照合を行います。
  • npm / npx:npm install、npm i、npm add、npxから抽出したpackageがnpmに存在するかを確認します。
  • PyPI / pip / pipx:pip install、pip3 install、pipx install、python -m pip installから抽出したpackageがPyPIに存在するかを確認します。
  • github:owner/repo:npx github:owner/repoと対応するnpm install/i/add github:owner/repo形式について、参照するGitHub repoの存在を確認します。

registryの検査はpackageの存在確認です。指定versionがあること、実行コマンドが提供されること、依存関係が解決することまでは確認しません。GitHub repoの存在確認も、特定のrefやインストール成功を保証するものではありません。

実例:anymdのPyPI案内と、小規模な24 repo評価

開発・調整に使っていない別のpublic GitHub repo 24件で、既存の小規模評価を行いました。評価時点は2026年10月5日、固定した実装commitはaebb115です。npm/Node、Python、GitHub Release配布、desktop、Android、その他の6カテゴリから各4件を選びました。活動時期、stars、topicによる偏りがあり、無作為な母集団標本ではありません。

status repo数
OK 6
DRIFT 1
AMBIGUOUS 0
NOT_CHECKED 17
ERROR 0

DRIFT 1件は、SylphxAI/anymdのPYPI_PACKAGE_NOT_FOUNDでした。評価時のREADMEには、CLIの利用案内としてpip install anymdが書かれていました。

[DRIFT] PYPI_PACKAGE_NOT_FOUND
README : anymd
actual : pypi registry: 404 Not Found
basis  : line 134: pip install anymd

2026年10月5日18:40 JSTの手動GET確認でも、PyPI JSON endpointとSimple endpointの両方が404でした。READMEの案内とregistryの状態が食い違っていたため、このfindingをTRUE POSITIVEと判定しました。一方、npm側の@sylphx/anymdは200で、プロジェクト全体が利用できないという結果ではありません。

これはその時点の記録です。現在もpackageが存在しないと断定するものではありません。また、この24件の結果をprecision/recallや一般的な精度として扱うことはできません。対応外・比較不能な案内が多かったことも、結果の一部です。

5つのstatusで、何が分かったかを分ける

status 意味
OK 認識できた参照を1件以上検査し、その範囲でfindingがなかった。
DRIFT 対応する比較で不一致を検出した。人による確認は必要。
AMBIGUOUS 不一致の可能性はあるが、意図的な旧版案内や別projectのpackage/bin名など、解釈が必要。
NOT_CHECKED 対応する参照を検査できなかった。「問題なし」ではない。
ERROR 報告対象の取得/API/registryエラーにより、通常の検査を完了できなかった。

repo単位のstatusには優先順位があり、ERROR → DRIFT → AMBIGUOUS → OK/NOT_CHECKEDの順です。より優先するstatusになっても、個別のfindingは表示されます。

NOT_CHECKED != OK

このツールで特に区別したかったのが、「検査できなかった」と「検査してズレがなかった」です。

たとえばREADMEにHomebrewの案内しかなければ、v0.1.0では検査対象外です。そこでOKと表示すると、案内が正しいと確認したように見えてしまいます。そのため、対応する参照を検査できなければNOT_CHECKEDにします。

24 repo評価のNOT_CHECKED 17件も、問題がないと確認した17件ではありません。また、OKであってもREADME全体が正しいとは限りません。対象外の案内が同じREADMEに含まれている可能性があります。

checked entrypointsは検査した参照数で、成功したインストール数ではありません。依存package名を含む場合があり、同じnpm/PyPI package名やGitHub repo名は重複排除されます。

使い方

Node.js 22以上が入っている環境で、ソースをcloneして実行します。依存packageをinstallする手順はありません。

git clone https://github.com/iwadjp/readme-entry-check.git
cd readme-entry-check
node src/cli.mjs iwadjp/agent-rules-linter

複数のpublic repoを一度に指定できます。owner/repo形式とGitHub URLの両方に対応しています。

node src/cli.mjs iwadjp/agent-rules-linter https://github.com/iwadjp/wol-light

終了コードは、DRIFTも報告対象ERRORもなければ0、ERRORなしでDRIFTがあれば1、報告対象ERRORまたはrepo引数なしなら2です。終了コード0にはAMBIGUOUSとNOT_CHECKEDも含まれます。「0だから全部確認済み」と判断せず、表示されたstatusとfindingを読んでください。不正な引数や読めないローカルファイル、送信時の例外などは、Node.js例外と非0の終了コードになる場合もあります。

安全性:対象のpackageやREADME commandを実行しない

検査中のネットワーク操作は、public GitHub API、npm registry、PyPIへのHTTP GETだけです。packageをinstallせず、README commandを実行せず、検査対象repoをcloneせず、release binaryをdownload・実行しません。ファイルやrepoを変更する処理もありません。

先ほどのcloneコマンドは、利用者がこのツール自身を取得する手順です。検査対象のソフトウェアを実行して確認する仕組みではありません。

アカウントやsecretなしで使えます。GitHub APIのrate limitを引き上げる場合は、環境変数GITHUB_TOKENを任意で設定できます。tokenの送信先はhttps://api.github.comだけで、npmやPyPIには送信しません。shellやsecret managerで設定し、ソースやcommitするファイルには入れないでください。.envは読み込みません。

v0.1.0の制約

  • Homebrew、Docker、cargo/crates.io、Go、Maven/Gradle、NuGet、F-Droid、pnpm/yarn、ローカルNode script、PowerShellのinstall案内などは未対応です。
  • /releasesや/releases/latestのようなversionを固定しない汎用リンクは比較対象外です。nightlyのようなtagも数値比較できません。
  • version比較は数値成分を抽出する簡単な方式です。full SemVer、互換性分析、prereleaseの意図判定ではありません。
  • release参照は対象repoとlatest releaseを中心に比較します。任意のdownload URL、過去assetの存在、リンク先の外部ドキュメントは巡回しません。
  • 抽出はheuristicです。引用、多行、特殊なcommandを見落としたり、文脈を誤解したりする可能性があります。任意の散文を理解しません。
  • packageやrepoの存在は、installability、実行ファイルの有無、安全性、version/refの存在を保証しません。
  • API制限や上流の公開状態の変化で結果は変わります。ツール側にretryやnetwork timeoutの設定はありません。

whole README checkerでも、general broken-link checkerでも、完全なinstallation auditでもありません。自動修正、PR作成、定期監視、GitHub Action、GUI、hosted serviceもありません。

まとめ

READMEのリンクが生きていても、そのinstall/download案内が公開状態と一致しているとは限りません。readme-entry-checkは、そのズレのうち対応する参照だけを確認する、小さなread-only CLIです。

検査できた範囲と検査できなかった範囲を区別し、findingを人が確認する使い方を想定しています。対応範囲や詳しい仕様は、GitHubのREADMEとv0.1.0 releaseを参照してください。