Skip to content

Repository files navigation

listpath

A smart Windows PATH inspector and tidier. It lists every component of your PATH, flags the broken or redundant ones, and can clean or reorder the User or system PATH — safely, with dry-run previews and automatic backups.

 1. ✅ [system]  C:\Windows\System32
 2. ❌ [user]    C:\Tools\gone            (missing)
 3. ⚠️ [both]   c:\windows\system32\     (duplicate of #1)
 4. 📄 [user]    C:\Users\me\notes.txt    (not a folder)

Scope:  [system] machine   [user] user   [both] both   [session] process-only
Summary: 4 entries — 1 ok, 1 missing, 1 not-a-folder, 1 duplicate

Features

  • Inspect the live PATH, with each entry flagged:
    • ❌ missing folder · 📄 path is a file, not a folder · 🚫 empty segment · ⛔ invalid characters · ⚠️ duplicate · ✅ healthy
    • A colored scope tag shows where each entry comes from: [system] (Machine PATH), [user] (User PATH), [both], or [session] (present only in the live process — injected by a dev shell, or a surplus duplicate beyond what the registry holds; see Scopes).
  • --clean removes empty, invalid, missing, and duplicate entries.
  • --optimize cleans, then reorders: canonical system dirs first, then related tools grouped together (Visual Studio, CUDA, Python, …).
  • Targets the User PATH (default) or the Machine (system) PATH (--machine).
  • Safe by default: clean/optimize are dry-run previews; nothing is written without --apply. Applying writes a timestamped backup first, preserves %VAR% references and the REG_EXPAND_SZ value type, and broadcasts WM_SETTINGCHANGE so new programs pick up the change.

Requirements

Build & run

dotnet build
dotnet run                      # list the PATH
dotnet run -- --clean           # preview a cleaned User PATH

# or run the built exe directly:
.\bin\x64\Debug\net11.0-windows\listpath.exe --optimize

Usage

listpath                       List PATH components, flagging problems and scope.

listpath --clean               Preview a cleaned PATH (dry-run).
listpath --optimize            Preview a cleaned + reordered PATH (dry-run).
listpath --remove <path>       Preview removing the named entry (repeatable).
listpath --prune               Interactively pick entries to remove.
listpath ... --apply           Persist the change (writes a backup first).

Target (default --user):
  --user                       Operate on the User PATH (no admin needed).
  --machine                    Operate on the system PATH. Previews work
                               unelevated; --apply requires an admin shell.

listpath --help                Show help.

Examples

listpath                              # inspect
listpath --clean                      # what would clean remove from my User PATH?
listpath --optimize --apply           # tidy + reorder the User PATH for real
listpath --machine --clean            # preview cleaning the system PATH (read-only)
listpath --machine --clean --apply    # must be run from an elevated terminal

Exit codes

Code Meaning
0 Success (or list mode with no problems found)
1 List mode found problems / an apply failed to write
2 Bad arguments (unknown flag, conflicting flags)
3 --machine --apply attempted without administrator rights

How it works

Duplicate detection (smart normalization)

Entries are compared by their normalized key: %VAR% references are expanded, the path is resolved to a full path, the case is ignored, and trailing slashes are stripped. So C:\Bin, c:\bin\, and %SystemDrive%\Bin all count as the same entry. The first occurrence is kept; later ones are flagged ⚠️.

Scopes and the "merged-PATH trap"

List mode reads the process PATH, which Windows builds by merging Machine + User. Scope tags are computed by occurrence counting: the Machine and User PATHs each supply a fixed number of occurrences per normalized key, entries are walked in order, and each registry-backed occurrence is "consumed". Any surplus occurrence — more than the registry accounts for — is session-injected and tagged [session].

This is why a duplicate can show [session]: e.g. pwsh prepends its own $PSHOME (C:\Program Files\PowerShell\7) to the process PATH at startup, on top of the copy already in the Machine PATH. The registry has it once, so the extra live copy is correctly flagged [session] and --clean leaves the registry untouched. See Troubleshooting.

Because of this merge, --clean/--optimize operate on one target only (User by default). When cleaning the User PATH, Machine entries are seeded as "already seen", so a User entry that merely duplicates a system dir is removed — without copying Machine entries into the User value. When cleaning the Machine PATH, deduplication is within Machine only; a Machine entry is never dropped just because the User PATH also has it.

--optimize ordering

  1. Canonical system directories float to the top in order: System32, Windows, Wbem, WindowsPowerShell\v1.0.
  2. Remaining entries get a group key from their recognizable root (keyword rules for Visual Studio / Windows Kits / CUDA / Python / PowerShell / .NET / Java / coreutils, else the child of Program Files, else the first path segment). A stable sort clusters groups while preserving each group's first-appearance order — deterministic, no surprising reshuffles.

Lossless apply

Apply reads the raw registry value (DoNotExpandEnvironmentNames), keeps each entry's original text (so %VAR% survives), and writes back as REG_EXPAND_SZ. It backs up the previous raw value to %USERPROFILE%\.listpath-backups\{User|Machine}-PATH-<timestamp>.txt and prints the exact restore command. After writing it broadcasts WM_SETTINGCHANGE.

  • User PATH: HKCU\Environment — no admin required.
  • Machine PATH: HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment — requires an elevated shell. Without it, --machine --apply refuses before writing and prints the elevated retry command (exit 3).

Pruning specific entries

--clean/--optimize only touch duplicates, missing, and invalid entries. To remove a valid entry you simply don't want (e.g. a redundant 32-bit tool):

  • listpath --remove "<path>" — repeatable, matched by normalized key (case / trailing-slash / %VAR% insensitive), dry-run by default, --apply to persist.
  • listpath --prune — interactive checkbox picker over the target's entries.

Both are surgical (they touch only what you select) and share the same backup + lossless apply + elevation rules. Use --machine to target the system PATH. See docs/path-pruning-candidates.md for saved cleanup candidates.

Troubleshooting

A duplicate won't go away, even after --clean --apply. Check its scope tag:

  • ⚠️ [session] — the duplicate is injected into your shell at startup, not stored in any registry PATH. Editing PATH can't remove it; it reappears each launch. The classic case is pwsh adding its own $PSHOME on top of the Machine entry. Harmless — PATH resolves left-to-right. To stop it you'd change whatever launches the shell, not PATH.
  • ⚠️ [system] — it's in the Machine PATH. --clean defaults to User and won't touch it. Run --machine --clean --apply from an elevated shell.
  • ⚠️ [user]listpath --clean --apply removes it. If it persists, you're looking in the same shell you applied from (it keeps a cached PATH) — open a new terminal.

--machine --apply says "nothing to do" but I see a [system] duplicate. The registry holds it once; the extra copy is [session] (injected). The tool is correct — there's nothing in the registry to remove.

Project structure

File Responsibility
Program.cs Argument parsing → command dispatch; elevation pre-flight.
PathInspector.cs EntryStatus/PathScope/PathEntry types; pure Analyze + normalization.
PathTransformer.cs Pure Clean / Optimize over analyzed entries.
PathStore.cs Registry read/write per target, backups, admin check, WM_SETTINGCHANGE.
Renderer.cs All console output: list view, diff view, colors.
docs/superpowers/specs/ Design specs for each feature (read these to resume).
TODO.md Roadmap and notes.

The Analyze and Clean/Optimize functions are pure (string/entry-list in, flagged entries out, no I/O), which keeps the logic easy to reason about and test.

Testing

There is no automated test project yet (see roadmap). The transforms are verified manually via dry-run output against crafted and real PATH values.

Verifying --apply without risking your real PATH: PathStore reads an env var LISTPATH_TEST_HKCU_SUBKEY (unset in normal use) that redirects the User target to a throwaway HKCU subkey. Point it at a scratch key, seed a Path value, and run --user --clean --apply against it. Always confirm the redirect with a dry-run first (the preview should show the scratch data, not your real PATH).

⚠️ Do not test a real --apply against your live HKCU\Environment value. Use the test seam or a throwaway key. When changing the target framework, delete stale build outputs first — running an old binary can ignore the test seam.

Roadmap / ideas

  • Automated test project (xUnit) around the pure transforms.
  • --user/--machine "clean both" convenience, or a combined report.
  • Preference to keep %VAR% form vs literal when both exist as duplicates.
  • See TODO.md for the current list.

Color

Colors are disabled automatically when output is redirected (piped to a file) or when the NO_COLOR environment variable is set.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages