A GUI App's CLI May Not Share Its Name¶
Looking for a Mac app's command-line tool, the obvious check is to peek inside the bundle:
Two executables, neither named spark. Easy conclusion: no CLI here. Wrong. SparklyRemote is the Spark CLI:
A 2026-07-31 audit made exactly this call — listed both filenames, saw no spark, and recorded "no CLI binary… confirmed dead end." The tool had been installed and working the whole time. The resulting task sat blocked in a backlog for eight days, waiting on a decision nobody needed to make.
The Short Answer¶
Never conclude "not installed" from filenames. Ask the binaries what they are:
APP="/Applications/Some App.app"
for b in "$APP/Contents/MacOS/"*; do
echo "--- $b"
"$b" --help 2>&1 | head -5
done
And check /usr/local/bin for a symlink pointing back into the bundle, which is how most Mac apps install their CLI:
Why the Obvious Checks Miss It¶
The CLI is often a differently-named helper inside the bundle. Vendors name the GUI binary after the product and the CLI after an internal component. SparklyRemote reads like a background sync helper; nothing suggests a user-facing CLI.
The PATH check can mislead too. That same audit reported "no spark-named binary on PATH" — but /usr/local/bin/spark existed, symlinked into the bundle, and /usr/local/bin is on the default PATH. Two independent checks agreed, and both were wrong, which is exactly what makes this kind of conclusion stick.
Package managers won't show it. The Homebrew cask (readdle-spark) installs the .app only. brew list reveals nothing about a CLI the app installs into /usr/local/bin itself, usually via an in-app "install command-line tool" action — which leaves no record in any package database.
Dating the Install¶
A symlink's timestamp tells you when the CLI appeared, which is often more reliable than anyone's memory:
ls -l /usr/local/bin/spark
# lrwxr-xr-x 1 root wheel 60 Jul 31 10:58 /usr/local/bin/spark -> /Applications/Spark Desktop.app/Contents/MacOS/SparklyRemote
Useful when reconstructing whether a tool predates a documented "it's missing" claim — here it did.
Check Capability, Not Just Presence¶
Once found, confirm what it can actually do. Spark's CLI reports its access level per account:
read-only matters: reads, search, and threads work, but anything that composes or sends needs a different tool. A tool being present says nothing about the operations it will permit — the same trap as an MCP server reporting "Connected".
Rule of Thumb¶
Presence on disk, absence from PATH, and absence from a package manager are all weak evidence. Running the thing is strong evidence. When a check concludes "the tool isn't here," spend the extra ten seconds running --help on every unfamiliar executable nearby before writing it down as a dead end — a wrong "not installed" gets trusted and re-read for as long as it stays on file.
Related¶
- An MCP Server Reporting "Connected" Can Still Be Broken — the same failure of inference in the opposite direction: a broken tool passing as working
- Uninstalling Docker Desktop Leaves Dangling Symlinks in /usr/local/bin — when
/usr/local/binentries point at bundles that no longer exist