Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # Claude Code Status Line - PowerShell Version for Windows
- # Inspired by https://github.com/daniel3303/ClaudeCodeStatusLine
- # Displays: Model | Directory@Branch(+adds -dels) | Tokens (used%) | Effort | 5h% | 7d%
- $ErrorActionPreference = 'SilentlyContinue'
- # ===== OAuth Token Resolution =====
- function Get-OAuthToken {
- # 1. Environment variable override
- if ($env:CLAUDE_CODE_OAUTH_TOKEN) {
- return $env:CLAUDE_CODE_OAUTH_TOKEN
- }
- # 2. Windows Credential Manager
- try {
- $cred = Get-StoredCredential -Target "Claude Code-credentials" -ErrorAction SilentlyContinue
- if ($cred) {
- $blob = $cred.GetNetworkCredential().Password
- $credJson = $blob | ConvertFrom-Json
- if ($credJson.claudeAiOauth.accessToken) {
- return $credJson.claudeAiOauth.accessToken
- }
- }
- } catch { }
- # 3. Credentials file fallback
- $credsFile = Join-Path $env:USERPROFILE ".claude\.credentials.json"
- if (Test-Path $credsFile) {
- try {
- $credJson = Get-Content $credsFile -Raw | ConvertFrom-Json
- if ($credJson.claudeAiOauth.accessToken) {
- return $credJson.claudeAiOauth.accessToken
- }
- } catch { }
- }
- return $null
- }
- # ===== Usage API with Caching =====
- function Get-UsageData {
- $cacheDir = Join-Path $env:TEMP "claude"
- $cacheFile = Join-Path $cacheDir "statusline-usage-cache.json"
- $cacheMaxAge = 120 # seconds
- # Ensure cache directory exists
- if (-not (Test-Path $cacheDir)) {
- New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null
- }
- # Check cache
- $needsRefresh = $true
- $usageData = $null
- if (Test-Path $cacheFile) {
- $cacheInfo = Get-Item $cacheFile
- $cacheAge = (Get-Date) - $cacheInfo.LastWriteTime
- if ($cacheAge.TotalSeconds -lt $cacheMaxAge) {
- $needsRefresh = $false
- $usageData = Get-Content $cacheFile -Raw | ConvertFrom-Json
- }
- }
- # Fetch fresh data if needed
- if ($needsRefresh) {
- $token = Get-OAuthToken
- if ($token) {
- try {
- $headers = @{
- "Accept" = "application/json"
- "Content-Type" = "application/json"
- "Authorization" = "Bearer $token"
- "anthropic-beta" = "oauth-2025-04-20"
- "User-Agent" = "claude-code/2.1.34"
- }
- $response = Invoke-RestMethod -Uri "https://api.anthropic.com/api/oauth/usage" `
- -Headers $headers -Method Get -TimeoutSec 10 -ErrorAction Stop
- $usageData = $response
- $response | ConvertTo-Json -Depth 10 | Set-Content $cacheFile -Force
- } catch {
- # Fall back to stale cache
- if (Test-Path $cacheFile) {
- $usageData = Get-Content $cacheFile -Raw | ConvertFrom-Json
- }
- }
- }
- }
- return $usageData
- }
- # ===== Format Reset Time =====
- function Format-ResetTime {
- param(
- [string]$isoString,
- [string]$style = "time"
- )
- if ([string]::IsNullOrEmpty($isoString) -or $isoString -eq "null") {
- return $null
- }
- try {
- $dt = [DateTime]::Parse($isoString).ToLocalTime()
- switch ($style) {
- "time" { return $dt.ToString("h:mmtt").ToLower() }
- "datetime" { return $dt.ToString("MMM d, h:mmtt").ToLower() }
- default { return $dt.ToString("MMM d").ToLower() }
- }
- } catch {
- return $null
- }
- }
- # Read JSON input from stdin
- $input_data = [Console]::In.ReadToEnd()
- if ([string]::IsNullOrWhiteSpace($input_data)) {
- Write-Host "Claude"
- exit 0
- }
- try {
- $json = $input_data | ConvertFrom-Json
- # ANSI escape character (compatible with PowerShell 5.1+)
- $esc = [char]27
- # ANSI color codes
- $blue = "$esc[38;2;0;153;255m"
- $orange = "$esc[38;2;255;176;85m"
- $green = "$esc[38;2;0;160;0m"
- $cyan = "$esc[38;2;46;149;153m"
- $red = "$esc[38;2;255;85;85m"
- $yellow = "$esc[38;2;230;200;0m"
- $white = "$esc[38;2;220;220;220m"
- $dim = "$esc[2m"
- $reset = "$esc[0m"
- # Helper function to format token counts
- function Format-Tokens {
- param([int64]$num)
- if ($num -ge 1000000) {
- return "{0:F1}m" -f ($num / 1000000)
- } elseif ($num -ge 1000) {
- return "{0:F0}k" -f ($num / 1000)
- } else {
- return "$num"
- }
- }
- # Helper function to get color based on usage percentage
- function Get-UsageColor {
- param([int]$pct)
- if ($pct -ge 90) { return $red }
- elseif ($pct -ge 70) { return $orange }
- elseif ($pct -ge 50) { return $yellow }
- else { return $green }
- }
- # Extract model name
- $model_name = if ($json.model.display_name) { $json.model.display_name } else { "Claude" }
- # Build output
- $out = "${blue}${model_name}${reset}"
- # Current directory with git branch
- $cwd = $json.cwd
- if ($cwd) {
- $display_dir = Split-Path -Leaf $cwd
- $out += " ${dim}|${reset} ${cyan}${display_dir}${reset}"
- # Try to get git branch
- Push-Location $cwd
- $git_branch = git rev-parse --abbrev-ref HEAD 2>$null
- if ($git_branch) {
- $out += "${dim}@${reset}${green}${git_branch}${reset}"
- # Get git diff stats
- $git_diff = git diff --numstat 2>$null
- if ($git_diff) {
- $additions = 0
- $deletions = 0
- foreach ($line in $git_diff) {
- $parts = $line -split '\s+'
- if ($parts[0] -match '^\d+$') { $additions += [int]$parts[0] }
- if ($parts[1] -match '^\d+$') { $deletions += [int]$parts[1] }
- }
- if ($additions + $deletions -gt 0) {
- $out += " ${dim}(${reset}${green}+${additions}${reset} ${red}-${deletions}${reset}${dim})${reset}"
- }
- }
- }
- Pop-Location
- }
- # Token usage
- $context = $json.context_window
- if ($context) {
- $size = if ($context.context_window_size) { $context.context_window_size } else { 200000 }
- # Calculate current token usage
- $input_tokens = if ($context.current_usage.input_tokens) { $context.current_usage.input_tokens } else { 0 }
- $cache_create = if ($context.current_usage.cache_creation_input_tokens) { $context.current_usage.cache_creation_input_tokens } else { 0 }
- $cache_read = if ($context.current_usage.cache_read_input_tokens) { $context.current_usage.cache_read_input_tokens } else { 0 }
- $current = $input_tokens + $cache_create + $cache_read
- $used_tokens = Format-Tokens $current
- $total_tokens = Format-Tokens $size
- $pct_used = if ($size -gt 0) { [int](($current * 100) / $size) } else { 0 }
- $pct_remain = 100 - $pct_used
- $token_color = Get-UsageColor $pct_used
- $out += " ${dim}|${reset} ${orange}${used_tokens}/${total_tokens}${reset} ${dim}(${reset}${token_color}${pct_used}%${reset}${dim})${reset}"
- }
- # Effort level
- $effort_level = "high"
- if ($env:CLAUDE_CODE_EFFORT_LEVEL) {
- $effort_level = $env:CLAUDE_CODE_EFFORT_LEVEL
- } else {
- $settings_path = Join-Path $env:USERPROFILE ".claude\settings.json"
- if (Test-Path $settings_path) {
- $settings = Get-Content $settings_path | ConvertFrom-Json
- if ($settings.effortLevel) {
- $effort_level = $settings.effortLevel
- }
- }
- }
- $out += " ${dim}|${reset} ${white}effort${reset} "
- switch ($effort_level) {
- "low" { $out += "${dim}low${reset}" }
- "medium" { $out += "${orange}med${reset}" }
- default { $out += "${green}high${reset}" }
- }
- # ===== 5h and 7d Usage Limits =====
- $usageData = Get-UsageData
- if ($usageData) {
- # 5-hour usage
- if ($usageData.five_hour) {
- $fiveHourPct = [int]$usageData.five_hour.utilization
- $fiveHourColor = Get-UsageColor $fiveHourPct
- $fiveHourReset = Format-ResetTime $usageData.five_hour.resets_at "time"
- $out += " ${dim}|${reset} ${white}5h${reset} ${fiveHourColor}${fiveHourPct}%${reset}"
- if ($fiveHourReset) {
- $out += " ${dim}@${fiveHourReset}${reset}"
- }
- }
- # 7-day usage
- if ($usageData.seven_day) {
- $sevenDayPct = [int]$usageData.seven_day.utilization
- $sevenDayColor = Get-UsageColor $sevenDayPct
- $sevenDayReset = Format-ResetTime $usageData.seven_day.resets_at "datetime"
- $out += " ${dim}|${reset} ${white}7d${reset} ${sevenDayColor}${sevenDayPct}%${reset}"
- if ($sevenDayReset) {
- $out += " ${dim}@${sevenDayReset}${reset}"
- }
- }
- # Extra usage (if enabled)
- if ($usageData.extra_usage.is_enabled -eq $true) {
- $extraPct = [int]$usageData.extra_usage.utilization
- $extraUsed = "{0:F2}" -f ($usageData.extra_usage.used_credits / 100)
- $extraLimit = "{0:F2}" -f ($usageData.extra_usage.monthly_limit / 100)
- $extraColor = Get-UsageColor $extraPct
- $out += " ${dim}|${reset} ${white}extra${reset} ${extraColor}`$${extraUsed}/`$${extraLimit}${reset}"
- }
- }
- # Output mode style if available
- if ($json.output_style.name) {
- $style_name = $json.output_style.name
- $out += " ${dim}|${reset} ${white}style${reset} ${cyan}${style_name}${reset}"
- }
- # Vim mode if enabled
- if ($json.vim.mode) {
- $vim_mode = $json.vim.mode
- $vim_color = if ($vim_mode -eq "INSERT") { $green } else { $blue }
- $out += " ${dim}|${reset} ${vim_color}${vim_mode}${reset}"
- }
- Write-Host $out
- } catch {
- Write-Host "Claude"
- }
- exit 0
Advertisement
Add Comment
Please, Sign In to add comment