headroom.walls.sh · neovim

Claude Code + Neovim

Claude Code is a terminal agent — and Neovim users already live in the terminal. The integration is natural: split pane or tmux window, run Claude Code alongside your editor, pass it file paths and tasks, get diffs back. This page covers the workflows that work well and how to wire usage data into your Neovim statusline.

Why Claude Code fits Neovim's workflow

Most IDE-based AI tools bolt onto a GUI editor and feel like a plugin. Claude Code is a CLI that reads and writes files — the same primitives Neovim is built on. There is no IDE required: run claude in a terminal pane, give it a task, and let it work while you stay in your editor.

The terminal-first design means Claude Code is editor-agnostic. Any Neovim split, tmux pane, or terminal multiplexer becomes a Claude Code workspace without any configuration.

Basic workflow: split terminal or tmux

The most common setup is a vertical split: Neovim on the left, Claude Code on the right.

With a terminal multiplexer (tmux)

tmux new-session -s dev
# Pane 1: nvim
nvim src/main.py
# Pane 2: claude (Ctrl+b then %)
claude

In tmux, Ctrl-b + % creates a vertical split. Ctrl-b + arrow key navigates between panes. Run Claude Code in one pane, Neovim in the other — :w in Neovim saves, Claude Code picks up the changes on its next file read.

With Neovim's built-in terminal

Open a terminal split inside Neovim:

:vsplit | terminal
" or in a horizontal split:
:split | terminal

Then in the terminal buffer, run claude. Switch between the editor and terminal with Ctrl-w + h/l. Exit Claude Code's terminal input mode with Ctrl- Ctrl-n.

Keymaps for quick toggling

Add these to your init.lua or init.vim for fast Claude Code access:

-- Lua (init.lua)
vim.keymap.set("n", "<leader>cc", function()
  vim.cmd("vsplit | terminal claude")
end, { desc = "Open Claude Code in vertical split" })

vim.keymap.set("n", "<leader>ct", function()
  vim.cmd("tabnew | terminal claude")
end, { desc = "Open Claude Code in new tab" })

Passing context from Neovim to Claude Code

The simplest way to share context is to pass file paths in your Claude Code prompt:

claude "Refactor the function in src/auth.py to handle token expiry properly"

Or reference the current buffer by passing the path from Neovim. With a terminal pane open, you can set a shell variable from inside Neovim:

:let g:current_file = expand('%:p')
" Then in the terminal:
" claude "Explain what this function does: @$current_file"

Statusline integration: live usage in Neovim

Claude Code writes your session and weekly usage to ~/.claude/headroom-usage.json via the statusLineHook. You can read that file in your Neovim statusline with a simple Lua function:

-- In your statusline config (e.g. lualine, heirline, or custom)
local function claude_usage()
  local f = io.open(os.getenv("HOME") .. "/.claude/headroom-usage.json", "r")
  if not f then return "" end
  local ok, data = pcall(vim.json.decode, f:read("*a"))
  f:close()
  if not ok or not data then return "" end
  local s = data.sessionUsagePct
  local w = data.weeklyUsagePct
  if not s or not w then return "" end
  return string.format("CC %d%%·%d%%", math.floor(s), math.floor(w))
end

Add this function to your statusline configuration. It reads from disk on each statusline refresh (typically every few seconds) — same data Claude Code shows in /usage, in your Neovim statusline.

lualine example

require("lualine").setup({
  sections = {
    lualine_x = { claude_usage, "encoding", "fileformat", "filetype" }
  }
})

Shell prompt integration

If you launch Neovim from a terminal with a configured shell prompt, you can show Claude Code usage in your prompt before launching Neovim:

# In ~/.zshrc or ~/.bashrc
claude_usage_prompt() {
  local f="$HOME/.claude/headroom-usage.json"
  [ -f "$f" ] || return
  local s w
  s=$(jq -r '.sessionUsagePct // empty' "$f" 2>/dev/null)
  w=$(jq -r '.weeklyUsagePct // empty' "$f" 2>/dev/null)
  [ -z "$s" ] && return
  printf "CC %d%%·%d%% " "${s%.*}" "${w%.*}"
}
# Then add $(claude_usage_prompt) to your PS1

Full shell prompt integration guide · Starship module

tmux status bar

For tmux users, add usage to the status bar:

# In ~/.tmux.conf
set -g status-right '#(jq -r ""CC " + (.sessionUsagePct|floor|tostring) + "%" + "·" + (.weeklyUsagePct|floor|tostring) + "%"" ~/.claude/headroom-usage.json 2>/dev/null || echo "CC --") | %H:%M'

Full tmux integration guide

Menu bar monitoring with Headroom

The statusline and tmux integrations show usage when you're looking at them. When you're deep in a Neovim session — focused on a debugging loop, reading a large file, reviewing diffs — Headroom shows the meters in your macOS menu bar so you see them without leaving your editor.

brew install --cask patwalls/tap/headroom

Reads from the same ~/.claude/headroom-usage.json file your statusline script uses. Zero network calls, no API key. macOS 13+, free.

Headroom — usage monitoring alongside Neovim

The statusline integration is great for at-a-glance numbers. But when you're in a long Claude Code session running tests or refactoring a module, Headroom's color-coded menu bar icon alerts you before a hard stop interrupts your flow.

brew install --cask patwalls/tap/headroom

Direct download · About Headroom · Source on GitHub


tmux status bar integration
Shell prompt integration
Starship module
statusLineHook setup
Debugging with Claude Code
Claude Code keyboard shortcuts