Make the "update available" nudge install-aware and agent-aware
<!-- AI-Sessions
dir: ~/.claude/projects/-Users-samwiskow-projects-glab/
785476a3-9a21-475b-9a1d-914560433da3.jsonl (2026-05-22)
-->
## Problem
glab already prints an update-available nudge after every command (24h throttled, see `internal/commands/update/check_update.go`). Two gaps make it less useful than it could be:
1. **Coding agents don't relay it to users.** Coding-agent detection only landed in glab v1.98.0 (`internal/api/coding_agent.go`), and agents do not independently check whether glab is up to date. They simply pass through (or drop) whatever the binary prints. The current message — `A new version of glab has been released: v1.60.0 -> v1.62.1` plus a URL, colored on stderr — reads as decorative noise, and agents frequently swallow it. A user can run an outdated glab through an agent for months and never find out.
2. **It isn't actionable.** The message points at the release page but doesn't tell the user how to upgrade on their system.
There's also a small secondary benefit: branching the output on `BuildInfo.CodingAgent` reinforces the existing agent-detection signal and helps us understand how glab is used inside coding agents.
## Proposal
Keep the existing trigger, 24h throttle, and config wiring exactly as they are. Improve only the *message* in two ways.
### 1. Install-method-aware upgrade command
Add a helper that inspects `os.Executable()` (with `filepath.EvalSymlinks`) and returns an install method + suggested command:
| Resolved path contains | Method | Suggested command |
| --------------------------------------------------------------------- | ------------ | -------------------------------------------------------------- |
| `/opt/homebrew/`, `/usr/local/Cellar/`, `/home/linuxbrew/.linuxbrew/` | `homebrew` | `brew upgrade glab` |
| `$GOPATH/bin/` (or `$HOME/go/bin/` if `GOPATH` unset) | `go-install` | `go install gitlab.com/gitlab-org/cli/cmd/glab@latest` |
| anything else | `unknown` | *(omit command, link release notes only)* |
Out of scope for this issue: snap, apt, dnf, scoop, winget. Happy to add in follow-ups if there's demand — comment if you'd find any of these useful.
### 2. Two output formats, chosen by `BuildInfo.CodingAgent`
**Human format (no agent detected)** — multi-line, colored, prominent diff:
A new version of glab is available
v1.60.0 → v1.62.1
Run: brew upgrade glab
Release notes: https://gitlab.com/gitlab-org/cli/-/releases/v1.62.1
**Agent format (`CodingAgent != ""`)** — single distinctive line, no ANSI:
[glab] Update available: v1.60.0 → v1.62.1 (installed via homebrew). Suggested upgrade command: `brew upgrade glab`. Release notes: https://gitlab.com/gitlab-org/cli/-/releases/v1.62.1
The bracketed `[glab]` prefix and the word "suggested" (advisory, not imperative) are deliberate: it should read as a hint the agent passes along, not an instruction.
When the install method is `unknown`, the agent variant drops the "installed via" and "Suggested upgrade command" segments and keeps the release-notes link. The human variant drops the "Run:" line similarly.
Both formats continue to write to stderr so structured/JSON output on stdout stays clean.
## Non-goals
- No change to *when* the check fires (still post-command, still 24h throttled).
- No new config keys.
- No structured status file, no JSON marker, no new telemetry payload.
- No detection for snap / apt / dnf / scoop / winget at this stage.
## Files to change
- **add** `internal/commands/update/install_method.go`
- **add** `internal/commands/update/install_method_test.go`
- **modify** `internal/commands/update/check_update.go` — message rendering block only (the throttle / HTTP / config code stays untouched)
- **modify** `internal/commands/update/check_update_test.go` — table cases covering both formats and both install-method states
## Acceptance criteria
- [ ] `DetectInstallMethod()` returns `homebrew` for paths under the three Homebrew prefixes; `go-install` for paths under `$GOPATH/bin` or `$HOME/go/bin`; `unknown` otherwise. Errors from `os.Executable()` or `EvalSymlinks` return `unknown` without panicking.
- [ ] When no coding agent is detected and an upgrade is available, the user sees the four-line human format on stderr with `Run:` and `Release notes:` lines.
- [ ] When `CLAUDECODE=1` (or any other agent env var detected by `api.DetectCodingAgent()`) is set, the user sees the single `[glab] Update available: …` line on stderr, with no ANSI color codes.
- [ ] When install method is `unknown`, neither variant prints an upgrade command — only the release-notes URL.
- [ ] No change to the 24h throttle, the explicit `glab check-update` command, `ShouldSkipUpdate`, or the `silentSuccess` behaviour on automatic checks.
- [ ] Unit tests cover all four combinations (human × known/unknown, agent × known/unknown).
## Open questions for reviewers
- Are there install methods you'd want detected beyond Homebrew and `go install`? snap and apt are the obvious next candidates.
- Should the agent-format prefix be `[glab]` or something more distinctive (e.g. `<glab:update>`)? Open to either.
issue