package main import ( "bufio" "encoding/json" "fmt" "os" "os/exec" "path/filepath" "regexp" "strings" "github.com/go-git/go-git/v6" "golang.org/x/sys/unix" ) const statuslineWidthOffset = 7 // Input JSON structure from Claude Code type StatusInput struct { Model struct { DisplayName string `json:"display_name"` } `json:"model"` Workspace struct { CurrentDir string `json:"current_dir"` } `json:"workspace"` ContextWindow struct { ContextWindowSize int `json:"context_window_size"` CurrentUsage *struct { InputTokens int `json:"input_tokens"` CacheCreationTokens int `json:"cache_creation_input_tokens"` CacheReadInputTokens int `json:"cache_read_input_tokens"` } `json:"current_usage"` } `json:"context_window"` } // ANSI color codes const ( reset = "\033[0m" bold = "\033[1m" red = "\033[31m" green = "\033[32m" yellow = "\033[33m" magenta = "\033[35m" cyan = "\033[36m" boldGreen = "\033[1;32m" ) func main() { // Read JSON from stdin reader := bufio.NewReader(os.Stdin) var input strings.Builder for { line, err := reader.ReadString('\n') input.WriteString(line) if err != nil { break } } var data StatusInput if err := json.Unmarshal([]byte(input.String()), &data); err != nil { fmt.Fprintf(os.Stderr, "Error parsing JSON: %v\n", err) os.Exit(1) } // Calculate context info contextInfo := formatContextInfo(data.ContextWindow.ContextWindowSize, data.ContextWindow.CurrentUsage) // Get directory name dirName := filepath.Base(data.Workspace.CurrentDir) // Check gitea status giteaStatus := getGiteaStatus() // Get git info gitInfo := getGitInfo(data.Workspace.CurrentDir) // Build left part left := fmt.Sprintf("%s %s%s%s %sāžœ%s %s%s%s%s", giteaStatus, magenta, data.Model.DisplayName, reset, boldGreen, reset, cyan, dirName, reset, gitInfo) // Build right part right := fmt.Sprintf("%s%s%s", yellow, contextInfo, reset) // Calculate visible lengths (strip ANSI) leftVisible := stripANSI(left) rightVisible := stripANSI(right) // Get terminal width termWidth := getTerminalWidth() - statuslineWidthOffset // Calculate padding padding := max(termWidth-len(leftVisible)-len(rightVisible), 1) // Output with padding fmt.Printf("%s%s%s", left, strings.Repeat(" ", padding), right) } func formatContextInfo(contextSize int, usage *struct { InputTokens int `json:"input_tokens"` CacheCreationTokens int `json:"cache_creation_input_tokens"` CacheReadInputTokens int `json:"cache_read_input_tokens"` }) string { totalK := contextSize / 1000 if usage == nil { return fmt.Sprintf("0/%dk", totalK) } currentTokens := usage.InputTokens + usage.CacheCreationTokens + usage.CacheReadInputTokens currentK := currentTokens / 1000 return fmt.Sprintf("%dk/%dk", currentK, totalK) } func getGiteaStatus() string { // Check if gitea process is running using pgrep cmd := exec.Command("pgrep", "-x", "gitea") if err := cmd.Run(); err == nil { return green + "ā—" + reset } return red + "ā—" + reset } func getGitInfo(cwd string) string { // Open the repository (searches up from cwd) repo, err := git.PlainOpenWithOptions(cwd, &git.PlainOpenOptions{ DetectDotGit: true, }) if err != nil { return "" } // Get HEAD reference head, err := repo.Head() if err != nil { return "" } // Get branch name var branch string if head.Name().IsBranch() { branch = head.Name().Short() } else { // Detached HEAD - use short hash branch = head.Hash().String()[:7] } // Check if working tree is dirty worktree, err := repo.Worktree() if err != nil { return fmt.Sprintf(" git:(%s)", branch) } status, err := worktree.Status() if err != nil { return fmt.Sprintf(" git:(%s)", branch) } if !status.IsClean() { return fmt.Sprintf(" git:(%s) āœ—", branch) } return fmt.Sprintf(" git:(%s)", branch) } func findGitRoot(path string) string { for { if _, err := os.Stat(filepath.Join(path, ".git")); err == nil { return path } parent := filepath.Dir(path) if parent == path { return "" } path = parent } } func getTerminalWidth() int { ws, err := unix.IoctlGetWinsize(int(os.Stdout.Fd()), unix.TIOCGWINSZ) if err != nil { return 80 } return int(ws.Col) } var ansiRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`) func stripANSI(s string) string { return ansiRegex.ReplaceAllString(s, "") }