Skip to main content

pathctl — グローバル PATH の点検・修復とツールの入手元一覧

Windows のグローバル PATH(マシン / ユーザー)を点検・修復し、どのツールがどの入手元でグローバルに使えるか(mise / winget / npm / 直置き)を一覧する CLI です。

  • 実行時の依存なし(Python 3.15 の標準ライブラリのみ)
  • ネットワークを使わない。プロンプトを出さない。--json で機械可読
  • 書き換えは --apply を付けたときだけ。マシン PATH の書き換えは管理者権限が必要

使い方

# リポジトリ内から(uv が .venv を作ります)
uv run --project tools/pathctl pathctl doctor --json

# どのツールが mise 無しでも使えるか
uv run --project tools/pathctl pathctl tools --without-mise

# グローバルにコマンドとして入れる(uv のツール置き場に入ります)
uv tool install --from tools/pathctl pathctl
pathctl doctor

uv tool install の bin(%USERPROFILE%\.local\bin)が PATH に無い場合は、この CLI 自身で足せます。

pathctl path add '%USERPROFILE%\.local\bin' --scope user --apply

コマンド

コマンド 内容
pathctl path list [--scope machine|user|effective|all] PATH の内容を一覧(exists / ユーザー固有かどうか付き)
pathctl path check 重複・個人パスの混入・存在しないパス・空エントリを検出
pathctl path fix [--scope ...] [--apply] [--move-user-entries] [--prune-missing] 安全な修復。既定は下見のみ
pathctl path add <dir> [--scope user|machine] [--apply] エントリを追加(%USERPROFILE% などの変数可)
pathctl path remove <dir> [--scope ...] [--apply] エントリを削除
pathctl tools [--without-mise] [--via <入手元>] [--name <glob>] ツールと入手元の一覧
pathctl audit [--strict] **宣言に無いグローバル導入(野良)**を一覧
pathctl doctor [--strict] PATH とツールをまとめて点検
pathctl schema エージェント向けの入出力契約(コマンド・JSON・終了コード)

検出コード(findings[].code)

empty-entry / duplicate-in-scope / duplicate-across-scopes / user-path-in-machine / missing-directory / trailing-separator / mise-shims-not-first / tool-shadowed / tool-not-on-path

入手元(provenance)

mise-shim / mise-install / winget / npm-global / direct / unavailable

without_mise: true は「mise が無くても PATH から起動できる」コマンドです(mise の shim 以外のディレクトリから解決できる場合)。

エージェント向けの契約

--json の出力は常に同じ形です。

{
  "schema": "pathctl/1",
  "command": "doctor",
  "ok": false,
  "data": { "...": "コマンド固有のデータ" },
  "findings": [
    { "code": "user-path-in-machine", "severity": "error", "scope": "machine", "detail": "...", "hint": "..." }
  ],
  "hints": ["..."]
}
  • 終了コード: 0 成功 / 1 findings あり・適用失敗 / 2 引数の誤り
  • pathctl schema --json に全コマンドと例が入っています。まずこれを読むのが最短です
  • TTY 以外では色を自動で無効化します(--no-color / NO_COLOR も有効)

設計上の約束

  • %VAR% を展開して保存し直さない。エントリは生の文字列のまま編集します(C:\Users\%USERNAME%\... の意味を壊さないため)
  • レジストリの値の型(REG_SZ / REG_EXPAND_SZ)を保ったまま書き戻します
  • pathctl path fix は、空エントリの削除・同一スコープ内の重複統合・末尾区切りの整理だけを既定にし、個人パスの移動と存在しないパスの削除は明示フラグが要ります
  • 書き換え後は WM_SETTINGCHANGE をブロードキャストします(既存のアプリは再起動が必要)

効率性と不変性(Effective Python 第2版)

方針 適用箇所
値オブジェクトは frozen=True PathEntry / Finding / Tool / Report(不変・比較可能・ハッシュ可能)
構築中に変わるものは可変 FixPlan(slots=True のみ。actions / moved を追記する builder)
NamedTuple(frozen な値) docs.Guide(名前+要約の組。unpack もできる)
frozenset を定数に model.MISE_PROVENANCES(プロパティ内の set リテラルはアクセスごとに作り直されるため)
functools.cache tools.known_dirs()、render.display_width()(同じセルを毎行数え直さない)
ループの不変条件を巻き上げ inventory() の shims パス正規化、findings_block() の色判定
計算量の見直し FixPlan.as_dict() は key 集合を先に作る(O(n²) を回避)
例外は絞る OSError / RuntimeError のみ(except Exception を使わない)
テストは unittest 44 件(エージェント向け 8 原則の検証を含む)。追加依存なし

freeze の使い分けの基準は「値か、途中状態か」です。値(エントリ・検出結果・ツール・レポート)は frozen にして取り違えを防ぎ、組み立て中の FixPlan だけを可変にしています。集合は定数として frozenset に、辞書は Final にして、うっかり書き換えないようにしています。

野良グローバルを防ぐ

ホストのグローバル導入は、2 つの宣言だけを正とします。

宣言 対象
~/.config/mise/config.toml(このリポジトリの mise/mise.toml) CLI・ランタイムの既定
winget/host.dsc.yaml mise で扱えない例外(Store 版・Publisher 検証が強いもの)

それ以外の場所に入ったもの(npm -g、uv tool、インストーラが足した PATH など)は 野良グローバルです。pathctl audit が一覧します。

pathctl audit --format json     # WARN: mise/npm/uv の野良、INFO: アプリが足した PATH
  • mise の判定: mise ls -g --json の source.path がグローバル設定でないものを検出します (別プロジェクトの mise.toml で入れたものも分かります)。
  • npm / uv: %APPDATA%\npm\node_modules と %LOCALAPPDATA%\uv\tools を列挙します。
  • PATH: mise shims・winget・WindowsApps・~/.local/bin・Program Files 以外のユーザー PATH を INFO で出します(アプリのインストーラが足すことが多いため、警告にはしていません)。

方針として、ホストに入れたくないものは Docker Sandboxes(sbx)で実行します (言語のバージョン違い、検証目的のツール、生成物のビルドなど)。ホストには mise で宣言したものだけを残します。

補助として、グローバル設定に PIP_REQUIRE_VIRTUALENV = "true" を入れてあり、pip install の うっかりグローバル導入は失敗します(mise 経由のシェルで有効)。

開発

uv sync                 # 開発環境(dev 依存は ty のみ)
uv run ty check --python-platform win32 tools/pathctl   # 型チェック(Windows 専用ツールのため win32 を明示)
uv run python -m unittest discover -s tests   # テスト(追加依存なし)
uv run pathctl doctor   # 動作確認

pyproject.toml の [tool.ty.environment] でも python-platform = "win32" を指定していますが、 リポジトリ直下から実行すると設定が読まれないことがあるため、CI と開発コマンドではフラグで明示します (Linux ランナーで winreg が未解決になるのを防ぐため)。

リリースと検証(SLSA Build Level 3)

タグ pathctl-v<version> を push すると .github/workflows/release.yml が走ります。

  1. pyproject.toml の version を上げてコミット(タグと一致しないと失敗します)
  2. git tag pathctl-v0.1.2 && git push origin pathctl-v0.1.2
  3. GitHub Release に wheel / sdist / SHA256SUMS が添付され、provenance が発行されます

SLSA v1.0(build track)への対応

SLSA の要件 この実装
ビルドはコードで完全に定義される release.yml のみ。手作業なし
ビルドは隔離された使い捨て環境で走る GitHub ホストの ubuntu-latest。キャッシュを復元しない(cache: false)ので前回の実行が成果物に影響しない
依存は固定される ツールは mise.toml / mise.lock(--locked)、Python は uv.lock(uv sync --frozen)
provenance はビルド基盤が発行し、改ざんできない actions/attest-build-provenance(GitHub が OIDC で署名。リポジトリ内のスクリプトは関与しない)
公開される成果物はビルドしたものと同一 1 回ビルド → upload-artifact → Release / PyPI はそのファイルのみ(再ビルドしない)
成果物の完全性が検証できる SHA256SUMS を添付し、公開前に sha256sum -c で再検証
公開は短期トークンを使わない PyPI Trusted Publishing(OIDC)。uv publish --trusted-publishing always
公開物に attestation が付く uv publish が PEP 740 の publish attestation を添付(PyPI の Attestations に表示)

利用者側の検証

# GitHub の provenance(SLSA Build L3)
gh attestation verify pathctl-0.1.2-py3-none-any.whl --repo kotarotaniguchi0523/win-host-setup

# ダイジェスト(成果物と同じディレクトリで実行)
sha256sum -c SHA256SUMS

# PyPI の attestation はプロジェクトのファイル一覧から確認できる(PEP 740)

検証結果として、provenance は次の内容を主張します(--format json で確認できます)。

項目 値の例
predicateType https://slsa.dev/provenance/v1
issuer https://token.actions.githubusercontent.com(GitHub の OIDC)
workflow .github/workflows/release.yml @ refs/tags/pathctl-v0.1.2
subject 公開した wheel / sdist の sha256(SHA256SUMS と一致)

PyPI へ公開する場合(任意・1 回だけの設定)

PyPI 側で Trusted Publisher を登録します(トークンは不要です)。

  • Publisher: GitHub / Owner: kotarotaniguchi0523 / Repository: win-host-setup
  • Workflow name: release.yml / Environment name: pypi

登録後、リポジトリの変数 PYPI_PUBLISH を true にすると、以後のタグ push で uv publish --trusted-publishing always が実行されます(未設定なら GitHub Release だけが作られます)。 環境 pypi には必要なら Required reviewers を設定して、公開を人手で承認する運用にもできます。

現在は PYPI_PUBLISH=true で有効化済みです。PyPI 側の Trusted Publisher 登録が済んでいれば タグ push で PyPI にも公開されます(未登録の場合は PyPI 公開ジョブだけが失敗し、 GitHub Release と provenance はそのまま作られます)。

Python 3.15 を前提に、新しい文法を使っています(下限は 3.14。理由は下の「注意」)。

  • type 文(PEP 695)と型パラメータ記法
  • StrEnum / dataclass(slots=True, frozen=True) / match
  • t-strings(PEP 750): render.style() が t"{値:ok}" の書式指定を色として解釈します
  • pathlib.Path / winreg / ctypes(標準ライブラリのみ)

注意: pyproject.toml の requires-python は >=3.14 です。実行環境(このホストと CI)は 3.15.0 固定ですが、 下限を 3.15 にすると GitHub の依存グラフ更新(Dependabot)が tool_version_not_supported(対応は 3.14 まで)で 失敗するため、コードが実際に必要とする下限(t-strings = 3.14)を宣言しています。

Metadata

Release files for pathctl 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pathctl 0.2.2
File Size Uploaded
pathctl-0.2.2.tar.gz 37.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pathctl 0.2.2
File Interpreter ABI Platform
pathctl-0.2.2-py3-none-any.whl Python 3 none any Details

Total release size: 81.4 kB

Release files / pathctl-0.2.2.tar.gz

Download URL pathctl-0.2.2.tar.gz
Size 37.5 kB
Tags Source
SHA-256 checksum
How to use checksums
97442bc90731a86cb2e84d03f103e786f0a577732d41a79ecfeec19721836b06
BLAKE2b-256 checksum
How to use checksums
5b02814cc69abe5be983717882a632637a15ec8a78b1568faac363121e6775de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","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}

Release files / pathctl-0.2.2-py3-none-any.whl

Download URL pathctl-0.2.2-py3-none-any.whl
Size 43.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b090727559b7a2bcf7e5e8a24cf1e7c9da358f35d1de66f3f91d677d8e12ca55
BLAKE2b-256 checksum
How to use checksums
60f750407ea5ea27cda24c755ff8f35bcf446f8413b59ced187225804432f4cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.0 {"installer":{"name":"uv","version":"0.13.0","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}

Release history Release notifications | RSS feed

0.2.4

2 release files

This release

0.2.2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page