Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions plugins/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)<br>**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<br>**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

Expand Down
5 changes: 5 additions & 0 deletions plugins/update-checker/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
}
49 changes: 49 additions & 0 deletions plugins/update-checker/README.md
Original file line number Diff line number Diff line change
@@ -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.
106 changes: 106 additions & 0 deletions plugins/update-checker/hooks-handlers/check-update.sh
Original file line number Diff line number Diff line change
@@ -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 <<EOF
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Update check ($install_method): $message"
}
}
EOF

exit 0
16 changes: 16 additions & 0 deletions plugins/update-checker/hooks/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"description": "Checks the correct package source for the installation method and reports accurate update status",
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks-handlers/check-update.sh",
"timeout": 15
}
]
}
]
}
}