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


Check .config/zsh/01-initializers.zsh Check .config/zsh/03-user-export.zsh Check .gitconfig
They need to be updated for your personal needs
# install configuration files
# they will be installed in ~/.config/ and symlinked
./install.sh
# install brew packages
xargs brew install < brew_packages.txt
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.
rbenv install 3.2.2 && rbenv global 3.2.2
.config/zsh/04-plugins.zsh)goenv install 1.22.0 && goenv global 1.22.0
.nvmrc auto-switch hook in .config/zsh/04-plugins.zsh)nvm install --lts
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
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-highlightinghooks/register.tsx 105 lines1import { 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}
105types/index.d.ts 8 lines1export 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