All about coding

Ruby and Rails technical content written by Lucian Ghinda

You can use Ruby to set the status line in Claude Code

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.

How does it work

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:

  1. This works like an event update where Claude runs the command after each turn and writes the JSON session data to the script’s stdin to hand over the session state.
  2. The script should process that information and output to stdout

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.

How to test the script

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%

Pay attention to processing that takes time

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.