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成功 /1findings あり・適用失敗 /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 が走ります。
pyproject.tomlのversionを上げてコミット(タグと一致しないと失敗します)git tag pathctl-v0.1.2 && git push origin pathctl-v0.1.2- 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)
| File | Size | Uploaded | |
|---|---|---|---|
| pathctl-0.2.2.tar.gz | 37.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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}
|