SLOPSHOPPER

context-bar

Five-hour usage window as a colored bar above the prompt, toggled with /context-bar

newbandcommand
v0.1.0no licenseupdated 2026-10-05zhisme/dotfiles/claude/mods/context-bar
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · context-bar
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /context-bar ⎿ context-bar: Five-hour usage bar hidden ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

Preview

Terminal preview

Requirements

  • git
  • zsh
  • oh-my-zsh
  • neovim
  • homebrew
  • alacritty

Configuration

Check .config/zsh/01-initializers.zsh Check .config/zsh/03-user-export.zsh Check .gitconfig

They need to be updated for your personal needs

Installation

# install configuration files
# they will be installed in ~/.config/ and symlinked
./install.sh

# install brew packages
xargs brew install < brew_packages.txt

Claude Code

install.sh symlinks claude/ items into ~/.claude/ (settings, statusline, context-bar mod) and registers MCP servers from claude/mcp-servers.json at user scope. CLAUDE.md lives in zhisme/prompt. Plugins (caveman) are reinstalled from enabledPlugins in settings.json on first launch.

Languages

  • Ruby

rbenv install 3.2.2 && rbenv global 3.2.2

  • Go
  • goenv (installed outside brew; initialized in .config/zsh/04-plugins.zsh)

goenv install 1.22.0 && goenv global 1.22.0

  • Node
  • nvm (installed outside brew; .nvmrc auto-switch hook in .config/zsh/04-plugins.zsh)

nvm install --lts

Utilities

ripgrep is a line-oriented search tool that recursively searches the current directory for a regex pattern. Usage

    ls / | rg 'myname'

Code-search tool, similar to ripgrep.

Command-line fuzzy finder.

Terminal UI for git.

GitHub CLI.

Command-line JSON processor.

Switch between kubectl contexts and namespaces.

Recursive directory listing.

Persistent memory for coding agents — issue/knowledge store your agent reads and writes across sessions.

Make your terminal fun again

Quick install

    brew install zsh
    # make default shell
    sudo -s 'echo /usr/local/bin/zsh >> /etc/shells' && chsh -s /usr/local/bin/zsh

Many useful packages, browse /plugins

Better git diff with syntax highlight

Better cat command with syntax highlight

Replacement for default terminal

brew install nvim

Troubleshooting

  • zsh-syntax-highlightning

If occurs plugin zsh-syntax-highlighting not found run the following.

    git clone https://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh/custom/plugins/zsh-autosuggestions
    git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ~/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting
Source 2 files
hooks/register.tsx 105 lines
1import { atom, read, update } from 'claude-code'
2import type { Register, SessionRateLimit } from 'claude-code'
3
4import type { FiveHour } from '../types'
5
6const fiveHour = atom({ plugin: 'context-bar', key: 'fiveHour' } as const, null)
7const isVisible = atom({ plugin: 'context-bar', key: 'isVisible' } as const, true)
8
9const BAR_CELLS = 40
10
11type Rgb = [number, number, number]
12
13const STOPS: { at: number; rgb: Rgb }[] = [
14  { at: 0, rgb: [46, 160, 67] },
15  { at: 25, rgb: [46, 160, 67] },
16  { at: 45, rgb: [205, 180, 45] },
17  { at: 65, rgb: [230, 130, 30] },
18  { at: 80, rgb: [210, 45, 40] },
19  { at: 100, rgb: [120, 50, 20] },
20]
21
22function colorAt(percent: number): string {
23  const p = Math.max(0, Math.min(100, percent))
24  let lo = STOPS[0]!
25  for (const hi of STOPS) {
26    if (p <= hi.at) {
27      const t = hi.at === lo.at ? 0 : (p - lo.at) / (hi.at - lo.at)
28      const rgb = lo.rgb.map((c, k) => Math.round(c + (hi.rgb[k]! - c) * t))
29      return '#' + rgb.map(c => c.toString(16).padStart(2, '0')).join('')
30    }
31    lo = hi
32  }
33  return '#783214'
34}
35
36function pickFiveHour(limits: SessionRateLimit[]): FiveHour | null {
37  const found = limits.find(l => l.kind === 'five_hour')
38  if (!found) return null
39  return { percentUsed: found.percentUsed, resetsAt: found.resetsAt ?? null }
40}
41
42function formatReset(resetsAt: string | null): string {
43  if (!resetsAt) return ''
44  const at = new Date(resetsAt)
45  if (Number.isNaN(at.getTime())) return ''
46  const hh = String(at.getHours()).padStart(2, '0')
47  const mm = String(at.getMinutes()).padStart(2, '0')
48  return ` . R at ${hh}:${mm}`
49}
50
51export const register: Register = on => {
52  on('session.start', async ($, e, next) => {
53    await $.command.register({
54      name: 'context-bar',
55      description: 'Toggle the five-hour usage bar above the prompt',
56    })
57    const usage = await $.session.usage()
58    const found = pickFiveHour(usage.rateLimits)
59    if (found) await update($, fiveHour, () => found)
60
61    return next(e)
62  })
63
64  on('session.measure', async ($, e, next) => {
65    const found = pickFiveHour(e.rateLimits)
66    if (found) await update($, fiveHour, () => found)
67
68    return next(e)
69  })
70
71  on('command.run', { command: 'context-bar' }, async $ => {
72    const shown = await update($, isVisible, v => !v)
73
74    return { text: shown ? 'Five-hour usage bar shown' : 'Five-hour usage bar hidden' }
75  })
76
77  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
78    const window = await read($, fiveHour)
79    const isQuiet = e.props.hasSurvey || window === null || !(await read($, isVisible))
80    if (isQuiet) return next(e)
81
82    const { Box, Text } = $.ui.resolve(e)
83    const label = ` ${window.percentUsed}%${formatReset(window.resetsAt)}`
84    const cells = Math.max(10, Math.min(BAR_CELLS, e.props.bodyColumns - label.length - 1))
85    const filled = Math.round((Math.min(100, window.percentUsed) / 100) * cells)
86
87    const bar = []
88    for (let i = 0; i < cells; i++) {
89      const at = ((i + 0.5) / cells) * 100
90      bar.push(
91        i < filled
92          ? <Text key={`c${i}`} color={colorAt(at)}>█</Text>
93          : <Text key={`c${i}`} dimColor>░</Text>,
94      )
95    }
96
97    return (
98      <Box flexDirection="row">
99        {bar}
100        <Text color={colorAt(window.percentUsed)}>{label}</Text>
101      </Box>
102    )
103  })
104}
105
types/index.d.ts 8 lines
1export type FiveHour = { percentUsed: number; resetsAt: string | null }
2
3declare module 'claude-code' {
4  interface PluginState {
5    'context-bar': { fiveHour: FiveHour | null; isVisible: boolean }
6  }
7}
8