Skip to content

feat(doctor): add Pi agent detection, install fixes, and update flows - #900

Open
matt2e wants to merge 2 commits into
mainfrom
add-pi-to-agent-provider-catalog
Open

feat(doctor): add Pi agent detection, install fixes, and update flows#900
matt2e wants to merge 2 commits into
mainfrom
add-pi-to-agent-provider-catalog

Conversation

@matt2e

@matt2e matt2e commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Fills in the ai-agent-pi stub in doctor's agent/provider catalog so Pi is
detected, fixable, and updatable across every realistic install method.

Changes

  • agents.rs — install and bridge commands for the ai-agent-pi check
    (npm install -g --ignore-scripts @earendil-works/pi-coding-agent /
    npm install -g pi-acp), so a missing bridge reports fixType: bridge and
    berd can route both through the configured registry. Adds a per-agent
    self_update_command (pi update --self): a curl-installed Pi doesn't
    auto-update but ships an updater that infers its own prefix and package
    manager, so a CurlPipe main readout keeps the update nag and surfaces that
    command instead of a broken npm recipe.
  • types.rs / resolve.rs — new Pnpm and Bun install sources. Both
    link global bins into node_modules trees, so previously the classifier
    called them Npm and suggested an update command that wouldn't work.
    Classification reads $PNPM_HOME / $BUN_INSTALL from the probe env with
    platform defaults. Pi's curl-installer footprint
    (~/.local/lib/node_modules tree or ~/.local/share/pi-node) classifies as
    CurlPipe ahead of the npm-layout check.
  • freshness.rsPnpm/Bun readouts resolve installed versions from
    package.json like Npm. pnpm global bins are generated shim scripts rather
    than symlinks, so the canonicalize-and-walk probe now falls back to parsing
    the shim's embedded entrypoint path, then to scanning the
    global/<layout-version> trees (pnpm ≤ 10 flat and pnpm 11 hashed layouts).
  • package_ids.rs — Pi main/bridge package ids for Npm, Pnpm, Bun,
    and CurlPipe (main only).
  • lib.rsapply_freshness threads the agent's self-update command into
    main readouts; derive_update_command emits pnpm add -g / bun add -g
    recipes for the new sources.

Testing

Unit tests cover the new sources, package-id lookups, pnpm layout probing, and
fix/update command derivation (140 doctor tests passing).

Every scenario was also live-verified on a real machine through
run_checks_with_options and execute_fix_streaming_with_env_options:
not-installed, main-only + bridge fix, bridge-only, curl ~/.local layout +
pi update --self, and npm and pnpm outdated-to-updated flows
(0.82.1 → 0.83.0, pi-acp 0.0.32 → 0.0.33), with and without a registry
override. Bun coverage is unit-test only (bun not installed on the test
machine).

Fill in the ai-agent-pi stub with full support for every realistic Pi
install method:

- agents.rs: install/bridge commands for the ai-agent-pi check
  (npm install -g --ignore-scripts @earendil-works/pi-coding-agent /
  npm install -g pi-acp), so a missing bridge reports fixType bridge
  and berd can route both through the configured registry. New
  per-agent self_update_command ("pi update --self"): a curl-installed
  Pi does not auto-update, but ships an updater that infers its own
  prefix and package manager, so a CurlPipe main readout keeps the
  update nag and surfaces that command instead of a broken npm recipe.
- types.rs/resolve.rs: new Pnpm and Bun install sources. Both link
  global bins into node_modules trees, so without them the classifier
  called these installs Npm and nagged with a command that would not
  update them. Classification reads $PNPM_HOME/$BUN_INSTALL from the
  probe env with platform defaults. Pi's curl installer footprint
  (~/.local/lib/node_modules tree or ~/.local/share/pi-node) classifies
  as CurlPipe ahead of the npm-layout check.
- freshness.rs: Pnpm/Bun readouts resolve installed versions from
  package.json like Npm. pnpm global bins are generated shim scripts
  (not symlinks), so the canonicalize-and-walk probe now falls back to
  parsing the shim's embedded entrypoint path, then to scanning the
  global/<layout-version> trees (pnpm <= 10 flat and pnpm 11 hashed
  layouts).
- package_ids.rs: Pi main/bridge package ids for Npm, Pnpm, Bun, and
  CurlPipe (main only) sources.
- lib.rs: apply_freshness threads the agent's self-update command into
  main readouts; derive_update_command emits pnpm add -g / bun add -g
  recipes for the new sources.

Every scenario was live-verified on a real machine through
run_checks_with_options and execute_fix_streaming_with_env_options:
not-installed, main-only + bridge fix, bridge-only, curl ~/.local
layout + pi update --self, npm and pnpm outdated-to-updated flows
(0.82.1 -> 0.83.0, pi-acp 0.0.32 -> 0.0.33), with and without a
registry override. Bun coverage is unit-test only (not installed here).

Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
@matt2e
matt2e requested review from baxen and wesbillman as code owners August 4, 2026 04:27
`CURL_INSTALLER_FOOTPRINTS` decided a binary was curl-installed by checking
whether some path existed under `$HOME`. That answers a question about the
machine, not about the binary PATH resolved to: with amp installed both ways
(curl at `~/.amp/bin`, npm-global on a user-configured prefix) the marker is
present either way, so a user-managed npm install could be labelled `CurlPipe`
and have its update nag silenced. Delete the heuristic and fix the *action* it
was standing in for.

- resolve.rs: drop `CurlInstallerFootprint`, `CURL_INSTALLER_FOOTPRINTS`,
  `matches_curl_installer_footprint`, its early promotion in
  `detect_install_source_inner`, and signal 1 of `fingerprint_curl_pipe`.
  The fingerprint itself stays: signal 2 (a `~/.local/bin` entry symlinked into
  a versioned install dir) is the sole positive classifier for cursor-agent's
  and the native claude's layouts.
- resolve.rs: repair signal 2's home gate, which compared a canonicalized
  symlink target against a non-canonicalized `$HOME` — any home reached through
  a symlinked ancestor never matched, and the footprint was masking it.
- resolve.rs: add `npm_prefix_for_binary`, deriving `<prefix>` from the package
  tree the bin entry canonicalizes into (`<prefix>/lib/node_modules/<pkg>/…`),
  anchored on the first `lib`/`node_modules` pair so nested dependency trees
  still yield the outer prefix.
- agents.rs/lib.rs: `derive_update_command` takes that prefix and emits
  `npm install -g --prefix <P> <pkg>@latest` (shell-quoted — the command runs
  under `sh -c`). npm installs into whichever prefix npm is *configured* with,
  which is not always the one the resolved binary lives in; without this, an
  install made with an explicit `--prefix`, or one under a node version the user
  has since switched away from, gets "updated" by a second copy landing
  elsewhere while the stale binary keeps resolving. Where the two agree the flag
  is a no-op, and an underivable prefix falls back to the bare command. Main and
  bridge readouts derive independently.
- Pi's `~/.local` installer fallback now classifies `Npm` — honestly, since the
  installer runs `npm install -g --ignore-scripts --prefix ~/.local` — and
  updates through the npm recipe. That makes the `CurlPipe`-gated
  `self_update_command` path unreachable, so drop the field, its `pi update
  --self` entry, and `agent_self_update_command`. `pi update --self` is not a
  drop-in replacement in the general case: it errors out on a pnpm-managed
  install ("not managed by a global npm install"), so gating on it per source
  would reintroduce the same species of per-agent table.
- package_ids.rs: add Amp's npm main package `@ampcode/cli` (canonical;
  `@sourcegraph/amp` is the renamed alias). Correct but inert behind a mirror
  that filters young versions — Amp publishes continuously, so Block's
  Artifactory serves both packages with no `latest` dist-tag; the comment says
  so.

Live-verified through `run_checks_with_options` and
`execute_fix_streaming_with_env_options`: pi at `~/.local` classifies `Npm`,
gets `--prefix '/Users/…/.local'`, and upgrades 0.82.1 -> 0.83.0 in place with
no second install in any other prefix; pi at the configured prefix emits a
prefix equal to `npm prefix -g` (a no-op) and upgrades in place, main and
bridge (pi-acp 0.0.32 -> 0.0.33); a pi under a prefix that differs from the
configured one upgrades in place while the configured prefix stays untouched.
cursor-agent and the native claude still classify `CurlPipe`; curl-installed
amp still reports `Unknown` raw and `CurlPipe` via its per-agent override. All
with the Artifactory registry override appended.

Note for berd: `InstallSource` is a cross-repo wire contract and the `pnpm`/
`bun` variants added by e84c6df are still missing from berd's TS union and its
exhaustive `SOURCE_LABEL_KEYS`, so those installs render a broken "installed
via …" line. That remains outstanding and must land with the doctor rev bump.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant