diff --git a/README.md b/README.md index 80aa75edf7..e85c00c9f6 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,62 @@ For more installation options, uninstall steps, and troubleshooting, see the [se 2. Navigate to your project directory and run `claude`. +## Known issues + +### False-positive "Update available" banner (Homebrew / WinGet) + +Users who installed via Homebrew or WinGet may see an "Update available!" banner +even though their package manager reports no update is available. For example: + +``` +Update available! Run: brew upgrade claude-code +``` + +**Why this happens:** Claude Code checks the npm registry for the latest +version. When a new release is published to npm, the Homebrew cask and WinGet +manifest may not be updated yet. During this window the banner fires even though +`brew upgrade` / `winget upgrade` has nothing to install. The banner resolves on +its own once the package registries catch up. + +**Verify whether the update is real:** + +```bash +# Homebrew +brew update && brew info --cask claude-code # compare "Installed" vs cask version + +# WinGet +winget list Anthropic.ClaudeCode # compare installed vs available + +# npm (if installed via npm) +npm outdated -g @anthropic-ai/claude-code +``` + +**Workarounds:** + +Set the `DISABLE_AUTOUPDATER` environment variable to suppress the banner: + +```bash +# One-time +DISABLE_AUTOUPDATER=1 claude + +# Persistent (add to ~/.bashrc, ~/.zshrc, or equivalent) +export DISABLE_AUTOUPDATER=1 +``` + +```powershell +# One-time +$env:DISABLE_AUTOUPDATER = 1; claude + +# Persistent (PowerShell profile) +[System.Environment]::SetEnvironmentVariable('DISABLE_AUTOUPDATER', '1', 'User') +``` + +> [!NOTE] +> This suppresses **all** update notifications, including legitimate ones. +> Remove the variable once your package manager has the latest version. + +Tracked in [#18047](https://github.com/anthropics/claude-code/issues/18047). + ## Plugins This repository includes several Claude Code plugins that extend functionality with custom commands and agents. See the [plugins directory](./plugins/README.md) for detailed documentation on available plugins. diff --git a/plugins/README.md b/plugins/README.md index cf4a21ecc5..a307f8a61f 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -25,6 +25,7 @@ Learn more in the [official plugins documentation](https://docs.claude.com/en/do | [pr-review-toolkit](./pr-review-toolkit/) | Comprehensive PR review agents specializing in comments, tests, error handling, type design, code quality, and code simplification | **Command:** `/pr-review-toolkit:review-pr` - Run with optional review aspects (comments, tests, errors, types, code, simplify, all)
**Agents:** `comment-analyzer`, `pr-test-analyzer`, `silent-failure-hunter`, `type-design-analyzer`, `code-reviewer`, `code-simplifier` | | [ralph-wiggum](./ralph-wiggum/) | Interactive self-referential AI loops for iterative development. Claude works on the same task repeatedly until completion | **Commands:** `/ralph-loop`, `/cancel-ralph` - Start/stop autonomous iteration loops
**Hook:** Stop - Intercepts exit attempts to continue iteration | | [security-guidance](./security-guidance/) | Security reminder hook that warns about potential security issues when editing files | **Hook:** PreToolUse - Monitors 9 security patterns including command injection, XSS, eval usage, dangerous HTML, pickle deserialization, and os.system calls | +| [update-checker](./update-checker/) | Detects false-positive "Update available" banners for Homebrew/WinGet installs by checking the correct package source | **Hook:** SessionStart - Detects installation method and queries the right version source | ## Installation diff --git a/plugins/update-checker/.claude-plugin/plugin.json b/plugins/update-checker/.claude-plugin/plugin.json new file mode 100644 index 0000000000..b39b41716b --- /dev/null +++ b/plugins/update-checker/.claude-plugin/plugin.json @@ -0,0 +1,5 @@ +{ + "name": "update-checker", + "version": "1.0.0", + "description": "Installation-method-aware update checker that detects false-positive update banners for Homebrew and WinGet installations" +} \ No newline at end of file diff --git a/plugins/update-checker/README.md b/plugins/update-checker/README.md new file mode 100644 index 0000000000..fd863c66c5 --- /dev/null +++ b/plugins/update-checker/README.md @@ -0,0 +1,49 @@ +# Update Checker Plugin + +Detects how Claude Code was installed and checks the **correct package source** +for available updates. This addresses the false-positive "Update available!" +banner that appears for Homebrew and WinGet users when the npm registry is ahead +of their package manager ([#18047](https://github.com/anthropics/claude-code/issues/18047)). + +## What it does + +At the start of each session, the plugin: + +1. Reads the installed Claude Code version. +2. Detects the installation method (Homebrew, WinGet, or npm). +3. Queries that package source for the latest available version. +4. Reports whether the update banner is accurate or a false positive. + +The result is injected as session context so Claude is aware of the real update +status. + +## Supported installation methods + +| Method | Detection | Version source | +|----------|-------------------------------|------------------------------| +| Homebrew | `brew info --cask claude-code`| Homebrew cask formula | +| WinGet | `winget show` | WinGet manifest | +| npm | `npm view` | npm registry | + +If the installation method cannot be detected, the plugin exits silently and +does not add any context. + +## Usage + +Install the plugin, then start a session as usual. No configuration needed. + +When the plugin detects a false positive it adds context like: + +> Update check (homebrew): Claude Code 2.1.73 is the latest version available +> via homebrew. If you see an 'Update available' banner it is a false positive +> caused by the npm registry being ahead of your package manager. + +## Limitations + +- The plugin provides **informational context only** -- it cannot suppress the + built-in update banner. To hide the banner entirely, set + `DISABLE_AUTOUPDATER=1` (see the main README for details). +- Version checks add a few seconds to session startup (configurable via + the `timeout` setting in `hooks.json`, default 15 s). +- On Windows/WSL, WinGet detection requires `winget.exe` to be accessible from + the shell. \ No newline at end of file diff --git a/plugins/update-checker/hooks-handlers/check-update.sh b/plugins/update-checker/hooks-handlers/check-update.sh new file mode 100755 index 0000000000..8cd4cdaf6b --- /dev/null +++ b/plugins/update-checker/hooks-handlers/check-update.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# check-update.sh — Detects how Claude Code was installed and checks +# the correct version source. Reports whether the built-in "Update +# available!" banner is accurate or a false positive caused by +# npm-vs-package-manager version lag. +# +# Runs as a SessionStart hook. Output is a JSON object with +# hookSpecificOutput.hookEventName = "SessionStart" and additionalContext +# containing the result. + +set -euo pipefail + +installed_version=$(claude --version 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || true) + +if [ -z "$installed_version" ]; then + # Cannot determine version — skip silently. + exit 0 +fi + +# --- Detect installation method --- +install_method="unknown" +available_version="" + +detect_brew() { + if ! command -v brew >/dev/null 2>&1; then return 1; fi + local info + info=$(brew info --cask claude-code 2>/dev/null) || return 1 + # Homebrew cask output format: "==> claude-code (Claude Code): 2.1.118" + available_version=$(echo "$info" | grep -iE 'claude-code.*: [0-9]' | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) + if [ -n "$available_version" ]; then + install_method="homebrew" + return 0 + fi + return 1 +} + +detect_winget() { + if ! command -v winget.exe >/dev/null 2>&1 && ! command -v winget >/dev/null 2>&1; then return 1; fi + local winget_cmd + winget_cmd=$(command -v winget.exe 2>/dev/null || command -v winget 2>/dev/null) + local info + info=$($winget_cmd show Anthropic.ClaudeCode --accept-source-agreements 2>/dev/null) || return 1 + available_version=$(echo "$info" | grep -iE '^Version' | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) + if [ -n "$available_version" ]; then + install_method="winget" + return 0 + fi + return 1 +} + +detect_npm() { + if ! command -v npm >/dev/null 2>&1; then return 1; fi + local info + info=$(npm view @anthropic-ai/claude-code version 2>/dev/null) || return 1 + available_version=$(echo "$info" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) + if [ -n "$available_version" ]; then + install_method="npm" + return 0 + fi + return 1 +} + +# Try each detection method in order of likelihood. +detect_brew || detect_winget || detect_npm || true + +if [ -z "$available_version" ] || [ "$install_method" = "unknown" ]; then + # Could not determine package source — skip silently. + exit 0 +fi + +# --- Compare versions --- +# Simple lexicographic comparison works for semver with equal-length segments. +# For robustness, compare each segment numerically. +version_gt() { + local IFS=. + local i a=($1) b=($2) + for ((i = 0; i < ${#a[@]}; i++)); do + local va=${a[i]:-0} + local vb=${b[i]:-0} + if ((va > vb)); then return 0; fi + if ((va < vb)); then return 1; fi + done + return 1 +} + +if [ "$installed_version" = "$available_version" ]; then + status="up-to-date" + message="Claude Code $installed_version is the latest version available via $install_method. If you see an 'Update available' banner it is a false positive caused by the npm registry being ahead of your package manager." +elif version_gt "$available_version" "$installed_version"; then + status="update-available" + message="A real update is available via $install_method: $installed_version -> $available_version." +else + status="up-to-date" + message="Claude Code $installed_version is up to date (${install_method} latest: $available_version)." +fi + +cat <