AgentCLIHub Development Notes 2: Finding Installed CLIs on Windows

Anime-style developer tracing branching CLI paths, with the resolved route highlighted in gold

AI-generated illustration of Windows CLI path discovery.

한국어로 읽기 / Korean version

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


Previous: Development Notes 1 · ConPTY and xterm.js

댓글