buzz on PATH shadows the real headless CLI with the GUI-launch wrapper — silent no-op on messages send for ACP agents
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
# `buzz` on PATH shadows the real headless CLI with the GUI-launch wrapper — silent no-op on `buzz messages send` (and likely other commands) when invoked by an agent
## Summary
On Linux, the Buzz installer places a small wrapper script at `~/.local/bin/buzz` that is meant for launching the desktop GUI with a clean browser handoff for OAuth. This wrapper `exec`s the AppImage directly with whatever args are passed to it:
```bash
#!/usr/bin/env bash
# Launch Buzz with a clean browser handoff for OAuth / external links.
set -euo pipefail
APPIMAGE="${BUZZ_APPIMAGE:-$HOME/Applications/Buzz.AppImage}"
if [[ ! -e "$APPIMAGE" ]]; then
echo "buzz: AppImage not found at $APPIMAGE" >&2
exit 1
fi
export BROWSER="${HOME}/.local/bin/buzz-browser"
exec "$APPIMAGE" "$@"
```
Because this wrapper is named exactly `buzz` and lives on `PATH`, it **shadows the real headless CLI binary** that ships bundled inside the AppImage at `usr/bin/buzz` (visible once the AppImage FUSE-mounts itself, e.g. `/tmp/.mount_Buzz./usr/bin/buzz`). That bundled binary is a proper Rust CLI with real subcommands (`buzz messages send`, `buzz channels list`, etc.) and structured JSON error output — but it is unreachable by name because `~/.local/bin/buzz` wins path resolution first.
## Impact — silent failure for ACP agents
This is not just a naming nit: it silently breaks the documented ACP agent workflow (https://hermes-agent.nousresearch.com/docs/user-guide/features/acp). The ACP host's injected system prompt instructs the agent to interact with Buzz exclusively through the `buzz` CLI on `PATH`, e.g.:
```
printf 'Hello!' | buzz messages send --channel --reply-to --content -
```
When an ACP agent (in my case, Hermes Agent) runs this, it resolves to the GUI-launch wrapper instead of the real CLI. The wrapper `exec`s the AppImage — an Electron single-instance app — which either does nothing headlessly or silently forwards to the already-running Buzz Desktop instance and returns. The **only output observed** was a single startup log line from the Electron app:
```
buzz-desktop: configured identity pubkey 26cd6944a2e8bafb7973f3b74421897cff409f606f545a795474a39970d68af4
```
with **exit code 0**. There is no indication anywhere in that output that the message was not sent. The agent (reasonably) interpreted exit 0 + no error as success and reported "Posted a reply in the Buzz thread" — but nothing was ever posted. I only caught this by manually checking Hermes' session transcript and cross-referencing the channel, where the message never appeared.
## Reproduction
Environment: Arch Linux (kernel 7.1.4-arch1-1, Hyprland/Wayland session), Buzz Desktop `0.5.0` (AppImage), installed to `~/Applications/Buzz_0.5.0_amd64.AppImage` with `~/.local/bin/buzz` as the installer-provided wrapper.
1. Install Buzz Desktop via the standard Linux AppImage installer flow (creates `~/.local/bin/buzz`).
2. Add an ACP runtime (e.g. `hermes acp`) as a Buzz agent, per Settings → Runtimes.
3. `@mention` the agent in a channel and ask it to reply.
4. Observe: the agent runs `buzz messages send ...`, gets exit code 0 and only the identity-config log line as output, reports success — but the channel never receives the message.
5. Confirm the real CLI works when invoked directly, bypassing the wrapper:
```bash
MOUNT=$(mount | awk '/type fuse\.Buzz_/ {print $3; exit}')
"$MOUNT/usr/bin/buzz" --help
# → prints full real CLI help: messages, channels, agents, canvas, reactions, etc.
"$MOUNT/usr/bin/buzz" --version
# → {"error":"user_error","message":"error: unexpected argument '--version' found...
# (confirms it's a real clap-based CLI, distinct from the wrapper)
```
## Expected behavior
One of:
- The GUI-launch convenience script should **not** be named `buzz` (e.g. `buzz-desktop-launch`), leaving `buzz` on `PATH` free to be a real symlink/wrapper to the bundled CLI binary; or
- If `buzz` must remain the GUI launcher for zero-arg / GUI-style invocations, it should detect CLI-style invocations (any known subcommand as `$1`: `messages`, `channels`, `agents`, `canvas`, `reactions`, `dms`, `users`, `workflows`, `feed`, `social`, `repos`, `pr`, `upload`) and `exec` the real bundled CLI binary for those, falling back to the GUI-launch behavior only for no-args/unknown-args invocations; or
- At minimum, the wrapper should fail loudly (non-zero exit, clear stderr) when it can't actually execute a CLI-shaped command, instead of returning 0 with an ambiguous Electron log line that looks like harmless startup noise.
Given the ACP docs explicitly document `buzz` subcommands as the agent's primary interface, and Buzz's own agent-facing system prompt hardcodes `buzz messages send`/`buzz messages get`/etc., this shadowing turns every headless agent interaction into a silent no-op on this install path. It's a correctness/trust issue: agents (and the humans watching them) have no signal that the command didn't do what it claimed.
## Workaround (for other users hitting this)
Until fixed, a targeted wrapper resolving the live FUSE mount works around it without touching the installer-provided `buzz`:
```bash
cat > ~/.local/bin/buzz-cli <<'WRAP'
#!/usr/bin/env bash
set -euo pipefail
MOUNT=$(mount | awk '/type fuse\.Buzz_/ {print $3; exit}')
if [[ -z "$MOUNT" || ! -x "$MOUNT/usr/bin/buzz" ]]; then
echo "buzz-cli: could not find the real buzz CLI binary; is Buzz Desktop running?" >&2
exit 1
fi
exec "$MOUNT/usr/bin/buzz" "$@"
WRAP
chmod +x ~/.local/bin/buzz-cli
```
This doesn't fully fix the ACP flow since the agent's system prompt hardcodes `buzz`, not `buzz-cli` — the actual fix needs to happen in how the installer names/wires the `buzz` binary on `PATH`.
## Related
While diagnosing this, I also hit an unrelated but adjacent packaging issue worth a mention: `buzz-acp` (the ACP relay bridge, used when connecting an external ACP agent as a runtime from Buzz Desktop's Settings → Runtimes) exports `PYTHONHOME`/`PYTHONPATH` pointing into the AppImage's own FUSE mount when spawning the agent subprocess. If the agent's launcher happens to be a Python venv binary (as with Hermes Agent's `hermes-acp`), the child process inherits those variables and crashes on startup with `ModuleNotFoundError: No module named 'encodings'` because it tries to load its own stdlib through Buzz's bundled Python paths. Happy to file this as a separate issue with full logs if useful — flagging here in case it's the same root cause (environment leaking from the AppImage's Electron/Python bundle into subprocesses it spawns).
Contributor guide
Assessment
This issue has not been assessed yet.