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 <