Skip to content

Instantly share code, notes, and snippets.

@Seklfreak
Last active September 24, 2026 15:33
Show Gist options
  • Select an option

  • Save Seklfreak/955eaaabbcc1500fb8ae2ac658355e05 to your computer and use it in GitHub Desktop.

Select an option

Save Seklfreak/955eaaabbcc1500fb8ae2ac658355e05 to your computer and use it in GitHub Desktop.
Claude statusline
package main
import (
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
type Input struct {
HookEventName string `json:"hook_event_name"`
SessionID string `json:"session_id"`
TranscriptPath string `json:"transcript_path"`
Cwd string `json:"cwd"`
Model struct {
ID string `json:"id"`
DisplayName string `json:"display_name"`
} `json:"model"`
Workspace struct {
CurrentDir string `json:"current_dir"`
ProjectDir string `json:"project_dir"`
} `json:"workspace"`
Version string `json:"version"`
OutputStyle struct {
Name string `json:"name"`
} `json:"output_style"`
Cost struct {
TotalCostUSD float64 `json:"total_cost_usd"`
TotalDurationMS int64 `json:"total_duration_ms"`
TotalAPIDurationMS int64 `json:"total_api_duration_ms"`
TotalLinesAdded int `json:"total_lines_added"`
TotalLinesRemoved int `json:"total_lines_removed"`
} `json:"cost"`
ContextWindow struct {
TotalInputTokens int64 `json:"total_input_tokens"`
TotalOutputTokens int64 `json:"total_output_tokens"`
ContextWindowSize int64 `json:"context_window_size"`
UsedPercentage float64 `json:"used_percentage"`
RemainingPercentage float64 `json:"remaining_percentage"`
CurrentUsage struct {
InputTokens int64 `json:"input_tokens"`
OutputTokens int64 `json:"output_tokens"`
CacheCreationInputTokens int64 `json:"cache_creation_input_tokens"`
CacheReadInputTokens int64 `json:"cache_read_input_tokens"`
} `json:"current_usage"`
} `json:"context_window"`
RateLimits *struct {
FiveHour *RateLimitWindow `json:"five_hour"`
SevenDay *RateLimitWindow `json:"seven_day"`
} `json:"rate_limits"`
PR *struct {
Number int `json:"number"`
URL string `json:"url"`
ReviewState string `json:"review_state"`
} `json:"pr"`
}
// RateLimitWindow is a claude.ai rolling usage window (5-hour or 7-day).
// Only present for claude.ai Pro/Max accounts; Console/org-billed sessions
// (e.g. the ZDR PHI profile) have no rate limits and omit this.
type RateLimitWindow struct {
UsedPercentage float64 `json:"used_percentage"`
ResetsAt int64 `json:"resets_at"`
}
func main() {
// Read JSON from stdin
data, err := io.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading stdin: %v\n", err)
os.Exit(1)
}
var input Input
if err := json.Unmarshal(data, &input); err != nil {
fmt.Fprintf(os.Stderr, "Error parsing JSON: %v\n", err)
os.Exit(1)
}
// Get current directory basename
dirName := filepath.Base(input.Workspace.CurrentDir)
// Get git branch
gitBranch := ""
if branch, err := getGitBranch(input.Workspace.CurrentDir); err == nil && branch != "" {
gitBranch = branch
}
// worktrunk names worktrees "<repo>.<branch-with-slashes-as-dashes>", so
// showing both the directory and the branch repeats the same long branch
// name twice. When the directory matches that pattern, collapse it down
// to just the repo name.
displayDir := dirName
if gitBranch != "" {
branchSlug := strings.ReplaceAll(gitBranch, "/", "-")
if repo, slug, ok := strings.Cut(dirName, "."); ok && slug == branchSlug {
displayDir = repo
}
}
branchSuffix := ""
if gitBranch != "" {
branchSuffix = fmt.Sprintf(" (%s)", gitBranch)
}
// Build status line
statusLine := displayDir + branchSuffix
// Add PR badge, if this worktree has an open PR (clickable via OSC 8)
if input.PR != nil && input.PR.Number > 0 {
prText := fmt.Sprintf("#%d", input.PR.Number)
if input.PR.ReviewState != "" {
prText += " " + input.PR.ReviewState
}
statusLine += " | " + hyperlink(prText, input.PR.URL)
}
// Add model info
if input.Model.DisplayName != "" {
statusLine += fmt.Sprintf(" | %s", input.Model.DisplayName)
}
// Add context remaining (danger = how much is used up, i.e. NOT remaining)
if input.ContextWindow.RemainingPercentage > 0 {
remaining := input.ContextWindow.RemainingPercentage
statusLine += fmt.Sprintf(" | %s remaining", colorizePercent(remaining, 100-remaining))
}
// Add spend (PHI/Console sessions) or claude.ai usage-limit percentage
// (default sessions with rate limits); falls back to spend either way
// if rate limits aren't present in the input.
statusLine += usageIndicator(input)
// Add active config profile (PHI-dedicated profile is highlighted)
statusLine += fmt.Sprintf(" | %s", profileBadge())
fmt.Print(statusLine)
}
func usageIndicator(input Input) string {
if !isPHISession() && input.RateLimits != nil {
var parts []string
if w := input.RateLimits.FiveHour; w != nil {
parts = append(parts, rateLimitPart("5h", w))
}
if w := input.RateLimits.SevenDay; w != nil {
parts = append(parts, rateLimitPart("7d", w))
}
if len(parts) > 0 {
return " | " + strings.Join(parts, ", ")
}
}
if input.Cost.TotalCostUSD > 0 {
return fmt.Sprintf(" | $%.2f", input.Cost.TotalCostUSD)
}
return ""
}
// rateLimitPart renders "5h NN%", appending a reset countdown once usage is
// high enough to matter (the same >=50% threshold that turns it yellow) so
// the reset time is actionable information rather than line-length clutter.
func rateLimitPart(label string, w *RateLimitWindow) string {
part := fmt.Sprintf("%s %s", label, colorizePercent(w.UsedPercentage, w.UsedPercentage))
if w.UsedPercentage >= 50 && w.ResetsAt > 0 {
part += fmt.Sprintf(" (resets %s)", formatDuration(time.Until(time.Unix(w.ResetsAt, 0))))
}
return part
}
// formatDuration renders a countdown like "2h14m" or "45m", floored at "now".
func formatDuration(d time.Duration) string {
if d <= 0 {
return "now"
}
h := int(d.Hours())
m := int(d.Minutes()) % 60
if h > 0 {
return fmt.Sprintf("%dh%dm", h, m)
}
return fmt.Sprintf("%dm", m)
}
// activeConfigDir resolves the CLAUDE_CONFIG_DIR this session is running
// under, falling back to the default ~/.claude when unset.
func activeConfigDir() string {
homeDir, err := os.UserHomeDir()
if err != nil {
return ""
}
configDir := os.Getenv("CLAUDE_CONFIG_DIR")
if configDir == "" {
configDir = filepath.Join(homeDir, ".claude")
}
return filepath.Clean(configDir)
}
// isPHISession reports whether this session runs under the PHI-dedicated
// profile (~/.phi-claude, kept logged into the ZDR account for real
// customer data).
func isPHISession() bool {
homeDir, err := os.UserHomeDir()
if err != nil {
return false
}
return activeConfigDir() == filepath.Clean(filepath.Join(homeDir, ".phi-claude"))
}
// profileBadge renders the active profile: bold red "PHI" so it can't be
// missed, or plain "default" otherwise.
func profileBadge() string {
if isPHISession() {
return "\x1b[1;31mPHI\x1b[0m"
}
return "default"
}
const (
colorReset = "\x1b[0m"
colorGreen = "\x1b[32m"
colorYellow = "\x1b[33m"
colorRed = "\x1b[31m"
)
// colorizePercent renders value as "NN%" colored by danger: green under 50,
// yellow 50-79, red 80+. danger and value are separate so callers can invert
// the mapping for "remaining" figures, where a LOW value is the dangerous one.
func colorizePercent(value, danger float64) string {
color := colorGreen
switch {
case danger >= 80:
color = colorRed
case danger >= 50:
color = colorYellow
}
return fmt.Sprintf("%s%.0f%%%s", color, value, colorReset)
}
// hyperlink wraps text in an OSC 8 terminal hyperlink pointing at url (iTerm2
// and most modern terminals render this as clickable; terminals without OSC 8
// support just show the plain text). Returns text unchanged if url is empty.
func hyperlink(text, url string) string {
if url == "" {
return text
}
return fmt.Sprintf("\x1b]8;;%s\x1b\\%s\x1b]8;;\x1b\\", url, text)
}
func getGitBranch(dir string) (string, error) {
cmd := exec.Command("git", "-C", dir, "--no-optional-locks", "branch", "--show-current")
output, err := cmd.Output()
if err != nil {
return "", err
}
return strings.TrimSpace(string(output)), nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment