From claude-hud
Detects ghost Claude plugin installations by checking cache, registry, and temp files on macOS/Linux/Windows; cleans up on user confirmation.
npx claudepluginhub jarrodwatts/claude-hud --plugin claude-hud/setupInitializes or resumes project setup via interactive Q&A, creating conductor/ artifacts for product definition, guidelines, tech stack, workflow, and style guides.
/setupChecks local Codex CLI readiness, prompts to install if unavailable via npm, and optionally toggles stop-time review gate.
/setupGuides enterprise admins through Claude Office add-in setup for Vertex AI, Bedrock, or custom gateway; provisions credentials and generates deployable manifest.xml.
/setupConfigures claude-hud as Claude Code statusline by providing ~/.claude/settings.json config, build instructions, manual setup, and troubleshooting steps.
/setupRuns interactive setup wizard: detects AI providers (Codex, Gemini, Ollama, etc.), offers installs/configs/auth setup, optimizes RTK tokens.
Note: Placeholders like {RUNTIME_PATH}, {SOURCE}, and {GENERATED_COMMAND} should be substituted with actual detected values.
Check for inconsistent plugin state that can occur after failed installations:
macOS/Linux:
# Check 1: Cache exists?
CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
CACHE_EXISTS=$(ls -d "$CLAUDE_DIR/plugins/cache/claude-hud" 2>/dev/null && echo "YES" || echo "NO")
# Check 2: Registry entry exists?
REGISTRY_EXISTS=$(grep -q "claude-hud" "$CLAUDE_DIR/plugins/installed_plugins.json" 2>/dev/null && echo "YES" || echo "NO")
# Check 3: Temp files left behind?
TEMP_FILES=$(ls -d "$CLAUDE_DIR/plugins/cache/temp_local_"* 2>/dev/null | head -1)
echo "Cache: $CACHE_EXISTS | Registry: $REGISTRY_EXISTS | Temp: ${TEMP_FILES:-none}"
Windows (PowerShell):
$claudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME ".claude" }
$cache = Test-Path (Join-Path $claudeDir "plugins\cache\claude-hud")
$registry = (Get-Content (Join-Path $claudeDir "plugins\installed_plugins.json") -ErrorAction SilentlyContinue) -match "claude-hud"
$temp = Get-ChildItem (Join-Path $claudeDir "plugins\cache\temp_local_*") -ErrorAction SilentlyContinue
Write-Host "Cache: $cache | Registry: $registry | Temp: $($temp.Count) files"
| Cache | Registry | Meaning | Action |
|---|---|---|---|
| YES | YES | Normal install (may still be broken) | Continue to Step 1 |
| YES | NO | Ghost install - cache orphaned | Clean up cache |
| NO | YES | Ghost install - registry stale | Clean up registry |
| NO | NO | Not installed | Continue to Step 1 |
If temp files exist, a previous install was interrupted. Clean them up.
If ghost installation detected, ask user if they want to reset. If yes:
macOS/Linux:
CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
# Remove orphaned cache
rm -rf "$CLAUDE_DIR/plugins/cache/claude-hud"
# Remove temp files from failed installs
rm -rf "$CLAUDE_DIR/plugins/cache/temp_local_"*
# Reset registry (removes ALL plugins - warn user first!)
# Only run if user confirms they have no other plugins they want to keep:
echo '{"version": 2, "plugins": {}}' > "$CLAUDE_DIR/plugins/installed_plugins.json"
Windows (PowerShell):
$claudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME ".claude" }
# Remove orphaned cache
Remove-Item -Recurse -Force (Join-Path $claudeDir "plugins\cache\claude-hud") -ErrorAction SilentlyContinue
# Remove temp files
Remove-Item -Recurse -Force (Join-Path $claudeDir "plugins\cache\temp_local_*") -ErrorAction SilentlyContinue
# Reset registry (removes ALL plugins - warn user first!)
'{"version": 2, "plugins": {}}' | Set-Content (Join-Path $claudeDir "plugins\installed_plugins.json")
After cleanup, tell user to restart Claude Code and run /plugin install claude-hud again.
On Linux only, if install keeps failing, check for EXDEV issue:
[ "$(df --output=source ~ /tmp 2>/dev/null | tail -2 | uniq | wc -l)" = "2" ] && echo "CROSS_DEVICE"
If this outputs CROSS_DEVICE, /tmp and home are on different filesystems. This causes EXDEV: cross-device link not permitted during installation. Workaround:
mkdir -p ~/.cache/tmp && TMPDIR=~/.cache/tmp claude /plugin install claude-hud
This is a Claude Code platform limitation.
IMPORTANT: Use the environment context values (Platform: and Shell:), not uname -s or ad-hoc checks. The Bash tool may report MINGW/MSYS on Windows, so branch only by the context values.
| Platform | Shell | Command Format |
|---|---|---|
darwin | any | bash (macOS instructions) |
linux | any | bash (Linux instructions) |
win32 | bash (Git Bash, MSYS2) | bash - use macOS/Linux instructions. Never use PowerShell commands with bash. |
win32 | powershell, pwsh, or cmd | PowerShell (use Windows + PowerShell instructions) |
macOS/Linux (Platform: darwin or linux):
Get plugin path (sorted by dotted numeric version, not modification time):
ls -d "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/claude-hud/claude-hud/*/ 2>/dev/null | awk -F/ '{ print $(NF-1) "\t" $(0) }' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1 | cut -f2-
If empty, the plugin is not installed. Go back to Step 0 to check for ghost installation or EXDEV issues. If Step 0 was clean, ask the user to install via /plugin install claude-hud first.
Get runtime absolute path:
darwin or linux, prefer bun for performance and fall back to node:
command -v bun 2>/dev/null || command -v node 2>/dev/null
win32 + bash, prefer node first and only fall back to bun because Bun is currently unstable for frequent statusLine invocations on Windows:
command -v node 2>/dev/null || command -v bun 2>/dev/null
If empty, stop setup and explain that the current shell cannot find bun or node.
bun or node, even if Claude Code itself is installed.winget is available, recommend:
winget install OpenJS.NodeJS.LTS
/claude-hud:setup.Verify the runtime exists:
ls -la {RUNTIME_PATH}
If it doesn't exist, re-detect or ask user to verify their installation.
Determine source file based on runtime:
basename {RUNTIME_PATH}
If result is "bun", use src/index.ts (bun has native TypeScript support). Otherwise use dist/index.js (pre-compiled).
Generate command (quotes around runtime path handle spaces):
When runtime is bun - add --env-file /dev/null to prevent Bun from auto-loading project .env files:
bash -c 'plugin_dir=$(ls -d "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/claude-hud/claude-hud/*/ 2>/dev/null | awk -F/ '"'"'{ print $(NF-1) "\t" $(0) }'"'"' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1 | cut -f2-); exec "{RUNTIME_PATH}" --env-file /dev/null "${plugin_dir}{SOURCE}"'
When runtime is node:
bash -c 'plugin_dir=$(ls -d "${CLAUDE_CONFIG_DIR:-$HOME/.claude}"/plugins/cache/claude-hud/claude-hud/*/ 2>/dev/null | awk -F/ '"'"'{ print $(NF-1) "\t" $(0) }'"'"' | sort -t. -k1,1n -k2,2n -k3,3n -k4,4n | tail -1 | cut -f2-); exec "{RUNTIME_PATH}" "${plugin_dir}{SOURCE}"'
Windows + Git Bash (Platform: win32, Shell: bash):
Use the macOS/Linux bash command format above, but on Windows prefer node first and only fall back to bun. Do not use PowerShell commands when the shell is bash. Claude Code invokes statusLine commands through bash, which will interpret PowerShell variables like $env and $p before PowerShell ever sees them.
Windows + PowerShell (Platform: win32, Shell: powershell, pwsh, or cmd):
Get plugin path:
$claudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME ".claude" }
(Get-ChildItem (Join-Path $claudeDir "plugins\cache\claude-hud\claude-hud") -Directory | Where-Object { $_.Name -match '^\d+(\.\d+)+$' } | Sort-Object { [version]$_.Name } -Descending | Select-Object -First 1).FullName
If empty or errors, the plugin is not installed. Ask the user to install via marketplace first.
Get runtime absolute path (prefer node, fallback to bun on Windows):
if (Get-Command node -ErrorAction SilentlyContinue) { (Get-Command node).Source } elseif (Get-Command bun -ErrorAction SilentlyContinue) { (Get-Command bun).Source } else { Write-Error "Neither node nor bun found" }
If neither found, stop setup and explain that the current PowerShell session cannot find bun or node.
winget is available, recommend:
winget install OpenJS.NodeJS.LTS
/claude-hud:setup.Check if runtime is bun (by filename). If bun, use src\index.ts. Otherwise use dist\index.js.
Generate command (note: quotes around runtime path handle spaces in paths):
When runtime is bun - add --env-file NUL to prevent Bun from auto-loading project .env files:
powershell -Command "& {$claudeDir=if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME '.claude' }; $p=(Get-ChildItem (Join-Path $claudeDir 'plugins\cache\claude-hud\claude-hud') -Directory | Where-Object { $_.Name -match '^\d+(\.\d+)+$' } | Sort-Object { [version]$_.Name } -Descending | Select-Object -First 1).FullName; & '{RUNTIME_PATH}' '--env-file' 'NUL' (Join-Path $p '{SOURCE}')}"
When runtime is node:
powershell -Command "& {$claudeDir=if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME '.claude' }; $p=(Get-ChildItem (Join-Path $claudeDir 'plugins\cache\claude-hud\claude-hud') -Directory | Where-Object { $_.Name -match '^\d+(\.\d+)+$' } | Sort-Object { [version]$_.Name } -Descending | Select-Object -First 1).FullName; & '{RUNTIME_PATH}' (Join-Path $p '{SOURCE}')}"
WSL (Windows Subsystem for Linux): If running in WSL, use the macOS/Linux instructions. Ensure the plugin is installed in the Linux environment (${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/...), not the Windows side.
Run the generated command. It should produce output (the HUD lines) within a few seconds.
Read the settings file and merge in the statusLine config, preserving all existing settings:
darwin or linux, or Platform win32 + Shell bash: ${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.jsonwin32 + Shell powershell, pwsh, or cmd: settings.json inside $env:CLAUDE_CONFIG_DIR when set, otherwise Join-Path $HOME ".claude"If the file doesn't exist, create it. If it contains invalid JSON, report the error and do not overwrite.
If a write fails with File has been unexpectedly modified, re-read the file and retry the merge once.
{
"statusLine": {
"type": "command",
"command": "{GENERATED_COMMAND}"
}
}
JSON safety: Write settings.json with a real JSON serializer or editor API, not manual string concatenation.
If you must inspect the saved JSON manually, the embedded bash command must preserve escaped backslashes inside the awk fragment.
For example, the saved JSON should contain \\$(NF-1) and \\$0, not \$(NF-1) and \$0.
After successfully writing the config, tell the user:
✅ Config written. Please restart Claude Code now — quit and run
claudeagain in your terminal. Once restarted, run/claude-hud:setupagain to complete Step 4 and verify the HUD is working.
Windows note: Keep the restart guidance separate from runtime installation guidance.
bun or node is available in PATH.statusLine is written successfully, they should fully quit Claude Code and launch a fresh session before judging whether the HUD setup worked.Note: The generated command dynamically finds and runs the latest installed plugin version. Updates are automatic - no need to re-run setup after plugin updates. If the HUD suddenly stops working, re-run /claude-hud:setup to verify the plugin is still installed.
After the statusLine is applied, ask the user if they'd like to enable additional HUD features beyond the default 2-line display.
Use AskUserQuestion:
If user selects any options, write plugins/claude-hud/config.json inside the Claude config directory (${CLAUDE_CONFIG_DIR:-$HOME/.claude} on bash, $env:CLAUDE_CONFIG_DIR or Join-Path $HOME ".claude" on PowerShell). Create directories if needed:
| Selection | Config keys |
|---|---|
| Tools activity | display.showTools: true |
| Agents & Todos | display.showAgents: true, display.showTodos: true |
| Session info | display.showDuration: true, display.showConfigCounts: true |
| Session name | display.showSessionName: true |
| Custom line | display.customLine: "<user's text>" — ask user for the text (max 80 chars) |
Merge with existing config if the file already exists. Only write keys the user selected — don't write false for unselected items (defaults handle that).
If user selects nothing (or picks "Other" and says skip/none), do not create a config file. The defaults are fine.
First, confirm the user has restarted Claude Code since Step 3 wrote the config. If they haven't, ask them to restart before proceeding — the HUD cannot appear in the same session where setup was run.
Use AskUserQuestion:
If yes: Ask the user if they'd like to ⭐ star the claude-hud repository on GitHub to support the project. If they agree and gh CLI is available, first check whether their gh version supports gh repo star. If it does, run gh repo star jarrodwatts/claude-hud. Otherwise fall back to gh api -X PUT /user/starred/jarrodwatts/claude-hud. Only run the star command if they explicitly say yes.
If no: Debug systematically:
Restart Claude Code (most common cause on macOS):
claude again, then re-run /claude-hud:setup to verifyVerify config was applied:
${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json on bash, or settings.json inside $env:CLAUDE_CONFIG_DIR when set, otherwise Join-Path $HOME ".claude" on PowerShell)Test the command manually and capture error output:
{GENERATED_COMMAND} 2>&1
Common issues to check:
"command not found" or empty output:
ls -la {RUNTIME_PATH}command -v node often returns a symlink that can break after version updatescommand -v bun or command -v node, and verify with realpath {RUNTIME_PATH} (or readlink -f {RUNTIME_PATH}) to get the true absolute path"No such file or directory" for plugin:
ls "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/cache/claude-hud/"Windows shell mismatch (for example, "bash not recognized"):
Platform: + Shell:Windows: PowerShell execution policy error:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSignedPermission denied:
chmod +x {RUNTIME_PATH}WSL confusion:
ls "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/plugins/cache/claude-hud/"If still stuck: Show the user the exact command that was generated and the error, so they can report it or debug further