Claude Code can run executables to show the status line, and, of course, that executable can be a Ruby script.
Here is the simple setup to do that:
{
"statusLine": {
"type": "command",
"command": "/Users/lucianghinda/.claude/statusline.rb"
}
}
statusLine is a top-level setting and sits on the same level as permissions and hooks. If you want to apply it to all projects, put that configuration in ~/.claude/settings.json.
The script that you specify there should be marked as executable with chmod +x, and it should include this at the top of the file:
#!/usr/bin/env ruby
I have not yet tested whether I can remove that shebang instruction and change the “command” argument to ruby /Users/lucianghinda/.claude/statusline.rb. I would assume it could work but have not yet tested it.
The status line behaves like a filter: Claude sends a JSON session data to a script that you set up there and expects to get back a string, which will be displayed as the status line.
The flow looks like this:
There is also an optional refreshInterval that can rerun the command more often if you like.
You can find the list of events that will trigger a status line update at How status lines work.
Here is the script that I used (that Claude generated for me when asked):
def compact(tokens)
return tokens.to_s if tokens < 1_000
return format('%.1fk', tokens / 1_000.0).sub('.0k', 'k') if tokens < 1_000_000
format('%.2fM', tokens / 1_000_000.0).sub('.00M', 'M')
end
def color_for(percent)
return RED if percent >= 80
return YELLOW if percent >= 50
GREEN
end
def git_branch(cwd)
branch = `cd #{cwd.shellescape} && git -c core.fileMode=false rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
branch.empty? ? 'no-git' : branch
end
def context_segment(window)
return nil unless window
used = window['total_input_tokens'].to_i
size = window['context_window_size'].to_i
percent = window['used_percentage']
percent = (size.positive? ? (used.to_f / size * 100).round : 0) if percent.nil?
label = "ctx #{compact(used)}/#{compact(size)}"
"#{label} #{color_for(percent)}#{percent}%#{RESET}#{DIM}"
end
input = begin
JSON.parse($stdin.read)
rescue StandardError
{}
end
cwd = input.dig('workspace', 'current_dir') || input['cwd'] || Dir.pwd
parts = [File.basename(cwd), git_branch(cwd)]
parts << context_segment(input['context_window'])
puts "#{DIM}#{parts.compact.join(' | ')}#{RESET}"
In my case, I want to pay attention only to the context window, and that is the most important piece of information added there.
You can test the script by doing something like this:
echo '{"workspace":{"current_dir":"/Users/lucianghinda/.claude"},"context_window":{"total_input_tokens":123456,"context_window_size":1000000}}' | ~/.claude/statusline.rb
Output should be:
.claude | no-git | ctx 123.5k/1M 12%
My current script checks git with rev-parse, which forks a process each time it runs. For a normal repository, that should return quickly, but in case of a slow network or a more complex repository with many objects in Git, it can take longer, and Claude will time out on the command and not display anything in the status line.