AgentCLIHub Development Notes 2: Finding Installed CLIs on Windows

AI-generated illustration of Windows CLI path discovery.
This article explains the code at fixed commit 46dfca2, reviewed on October 17, 2026. It does not report runtime tests or performance measurements.
codex can run in PowerShell while a GUI application on
the same computer fails to find its executable. The two processes may
have different PATH values and command-resolution environments. Agent
CLI Hub’s discovery code determines both where a CLI is located and how
to launch it.
1. Why a discovery result contains more than a path
Alongside the executable path and discovery source,
CliLocator.Result stores the Node executable, JavaScript
entry point, PowerShell path, and whether to load a PowerShell profile.
Even after locating a command, the launch method depends on whether it
is a native program, an npm wrapper, or a function defined in a shell
profile.
For example, finding claude.cmd does not necessarily
mean executing that file directly. If the associated JavaScript entry
point and Node executable are available, their paths form the beginning
of the launch command. For a CLI registered as a PowerShell function,
the result instead contains the command name and the shell information
needed to run it with the profile loaded.
Found therefore means that discovery produced a result.
It does not prove that the CLI can launch successfully or that login is
complete. Keeping discovery separate from startup lets the application
handle “not found” and “found but failed to start” at different stages.
Discovery
results and launch commands
2. Define the search order first
Discovery starts with the path configured by the user. The code expands environment variables, normalizes the path, and uses it if the file exists. If the configured path is invalid, automatic discovery continues.
The next steps depend on the CLI. For Codex, discovery first uses
PowerShell’s Get-Command. For other CLIs, it searches PATH
and conventional installation locations, falling back to PowerShell if
no file is found. This combines the command that a shell actually
resolves with a search for candidate files, in an explicit order.
The PATH search preserves directory order. Suppose an earlier
directory contains npm’s codex.cmd, while a later directory
contains codex.exe. Scanning the entire PATH separately for
each extension could select the executable in the later directory first.
The current implementation checks each CLI’s candidate filenames within
each directory, preserving the priority of earlier directories during
the file search.
CLI-specific filenames and conventional installation paths are
defined in CliProfile. CliLocator handles how
to search; the profile defines what to look for. Adding another
installation location does not require duplicating the search algorithm.
Discovery
order, CLI-specific
candidates
3. A running application’s PATH may be out of date
An application that was already running when a CLI was installed may still have its pre-installation environment. Looking only at the process PATH can therefore miss a directory that was added for the new executable.
PathDirectories reads the process, user, and system PATH
values in that order. It normalizes each entry and removes duplicates
using a case-insensitive set. This preserves the existing process
environment’s order while adding newly configured user and system
locations to the candidates.
The resulting list is also used at launch time. When
TerminalHost starts a child CLI, it includes the result of
LaunchPath in the child’s environment. This helps reduce
the gap between successfully locating a CLI and that CLI being able to
find the supporting tools it needs. Antigravity also has separate path
additions for locating Git Bash.
Combining PATH values does not reproduce every behavior of a newly opened shell. Functions and modules added by a profile cannot be represented by a list of directories alone. PowerShell discovery handles that part. PATH collection and the launch environment
4. PowerShell profiles are part of the execution environment
PowerShell discovery checks candidate pwsh.exe locations
and Windows PowerShell, then reads the results of
Get-Command. Aliases are resolved to their underlying
commands. If the command is a function, the result records that it must
run using the same shell with its profile loaded.
A profile may write other text to the console during initialization.
To handle this, the implementation emits JSON prefixed with
LLMTOOLS_COMMAND: and parses the line containing that
marker. It does not assume that all standard output is JSON. The shell
has a three-second exit timeout. If it exceeds that limit, the code
terminates its process tree and moves on to the next candidate.
This discovery method does not run the CLI itself. It does, however, load PowerShell profiles, so profile initialization costs and any actions performed by those profiles can affect the process. Shell discovery should not be described as an invariably lightweight, side-effect-free file lookup.
When a script needs to run, its arguments are formatted as PowerShell
string literals, and the script is passed through UTF-16-based
EncodedCommand. This explicitly manages the points at which
strings could otherwise be interpreted again as command syntax. PowerShell
lookup
5. Handle wrappers and auto-update paths separately
For npm installations, discovery checks for the JavaScript entry
point alongside the wrapper and locates Node. When both are available,
it constructs a command that invokes node.exe and the
entry-point file directly. This reduces the extra interpretation
involved in running the wrapper. It is not a general-purpose converter
for arbitrary custom scripts. If the required files cannot be
identified, the original launch path is retained.
Auto-updates present a different problem. A current
component in the launch path may be a link to a version-specific
directory. For a Codex .exe, the code opens a file handle
and obtains the final path with GetFinalPathNameByHandleW.
This resolves the actual file location, including links in parent
directories.
The conventional-path search checks
standalone/current/bin/codex.exe and reads the
auto-update-version marker left by the installer. It limits
the marker’s size and validates that its contents can be used as a
single filename before constructing a version-specific path. It does not
scan all version directories and arbitrarily choose the name that
appears largest.
The settings screen shows the discovered path, where it was found, and whether Node will be invoked directly. Launch logs also record the path and discovery source. As automatic discovery becomes more complex, making the selected installation visible helps users narrow down problems. Wrapper handling, Version paths, Displaying discovery results
Key takeaways
- User-configured paths, shell commands, PATH, and conventional installation locations need an explicit search order.
- A running process’s PATH is distinct from the current user and system PATH values.
- Discovery results should preserve both the launch method and the discovery source.
- Finding a file, launching it successfully, and having a CLI ready to use are separate states.
Source code at the pinned commit
Reference: Repository at commit 46dfca2
댓글
댓글 쓰기