Discover and apply my carefully curated dotfiles for effortless macOS system customization. Follow the step-by-step instructions to personalize your machine…

My macOS dotfiles, managed with chezmoi.
sudo softwareupdate -iaxcode-select --install/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"brew install chezmoi (or sh -c "$(curl -fsLS get.chezmoi.io)")chezmoi init git@github.com:slvstr-dev/dotfiles.git --applyDotfiles should only be edited in this repo (df) and applied by running chezmoi apply.
brew:install
brew:update
brew:cleanup
brew:uninstall
gh auth login
Disable Spotlight hotkey and assign it to Raycast using the Raycast hotkey instructions.
herdr:open [cmd] # workspace from cwd + optional cmd
herdr:worktree <branch> [cmd] # worktree from repo root for a branch + optional cmdhooks/register.tsx 191 lines1import { atom, read, update } from "claude-code";
2import type { EngineInterface, Register, SessionContextBreakdown } from "claude-code";
3
4import type { Reading, Slice } from "../types";
5
6const MIN_WIDTH = 20;
7const SPLIT = " ";
8const ACCENT = "#d97757";
9const GREY = "#808080";
10const PALETTE = [
11 "#7aa2f7",
12 "#7dcfff",
13 "#bb9af7",
14 "#9ece6a",
15 "#e0af68",
16 "#f7768e",
17 "#73daca",
18 "#ff9e64",
19 "#c0caf5",
20];
21const KINDS = ["used", "free", "buffer"];
22const BAR = { used: "█", free: "─", buffer: "░" };
23const MARKER = { ...BAR, used: "■" };
24
25const reading = atom({ plugin: "context-bar", key: "reading" } as const, null as Reading | null);
26
27const ignore = () => {};
28
29export const register: Register = (on) => {
30 on("session.start", async ($, e, next) => {
31 const result = await next(e);
32 let tries = 0;
33 const retry = $.clock.every(500, () => {
34 void refresh($)
35 .then((ok) => {
36 if (ok || ++tries >= 20) retry.cancel();
37 })
38 .catch(ignore);
39 });
40 return result;
41 });
42
43 on("tool.call", async ($, e, next) => {
44 const result = await next(e);
45 if (!e.agentId) void refresh($).catch(ignore);
46 return result;
47 });
48
49 on("turn.complete", async ($, e, next) => {
50 const result = await next(e);
51 if (!e.agentId) await refresh($).catch(ignore);
52 return result;
53 });
54
55 on("session.compact", async ($, e, next) => {
56 const result = await next(e);
57 if (!e.agentId && "messages" in result) void refresh($).catch(ignore);
58 return result;
59 });
60
61 on("ui.render", { component: "AbovePrompt" }, async ($, e, next) => {
62 const rest = await next(e);
63 const r = await read($, reading);
64 const width = e.props.bodyColumns - 4;
65 if (!r || e.props.hasSurvey || width < MIN_WIDTH) return rest;
66
67 const { Box, Text } = $.ui.resolve(e);
68 const fill = r.total / (r.compactsAt || r.window);
69 const head = `${tokens(r.total)} of ${tokens(r.window)}${r.compactsAt ? ` · compacts at ${tokens(r.compactsAt)}` : ""} `;
70 return (
71 <Box flexDirection="column">
72 <Box flexDirection="column" borderStyle="round" borderColor="inactive" paddingX={1}>
73 <Box flexDirection="row" justifyContent="space-between">
74 <Text wrap="truncate-end">
75 <Text color={ACCENT}>{"◆ "}</Text>
76 <Text bold>context</Text>
77 </Text>
78 <Text wrap="truncate-start">
79 <Text dimColor>{head}</Text>
80 <Text
81 bold
82 color="black"
83 backgroundColor={fill >= 0.9 ? "red" : fill >= 0.7 ? "yellow" : "green"}
84 >{` ${r.percent}% `}</Text>
85 </Text>
86 </Box>
87 <Text>
88 {bar(r, width).map((run) => (
89 <Text color={run.color}>{BAR[run.kind].repeat(run.size)}</Text>
90 ))}
91 </Text>
92 {legend(r, width).map((line) => (
93 <Text wrap="truncate-end">
94 {line.map((s, i) => (
95 <Text>
96 {i > 0 && <Text>{SPLIT}</Text>}
97 <Text color={s.color}>{`${MARKER[s.kind]} `}</Text>
98 <Text dimColor={s.kind !== "used"}>{`${s.name} `}</Text>
99 <Text bold={s.kind === "used"}>{tokens(s.tokens)}</Text>
100 {s.kind === "used" && <Text dimColor>{` ${share(s.tokens, r.window)}`}</Text>}
101 </Text>
102 ))}
103 </Text>
104 ))}
105 </Box>
106 {rest}
107 </Box>
108 );
109 });
110};
111
112async function refresh($: EngineInterface) {
113 const { breakdown } = (await $.session.usage({ breakdown: "summary" })).context;
114 if (!breakdown || !(breakdown.rawMaxTokens > 0)) return false;
115 await update($, reading, () => toReading(breakdown));
116 return true;
117}
118
119function toReading(b: SessionContextBreakdown): Reading {
120 let color = 0;
121 const slices = b.categories
122 .filter((c) => c.kind !== "deferred" && c.tokens > 0)
123 .sort((x, y) => KINDS.indexOf(x.kind) - KINDS.indexOf(y.kind))
124 .map((c) => ({
125 name: c.name.toLowerCase(),
126 tokens: c.tokens,
127 color:
128 c.kind !== "used"
129 ? GREY
130 : c.name.toLowerCase() === "messages"
131 ? ACCENT
132 : PALETTE[color++ % PALETTE.length]!,
133 kind: c.kind as Slice["kind"],
134 }));
135 return {
136 slices,
137 total: b.totalTokens,
138 window: b.rawMaxTokens,
139 percent: b.percentage,
140 compactsAt: b.isAutoCompactEnabled ? b.autoCompactThreshold : undefined,
141 };
142}
143
144function bar(r: Reading, width: number) {
145 const runs = r.slices.map((s) => {
146 const min = s.kind === "used" ? 1 : 0;
147 return { ...s, min, size: Math.max(min, Math.round((s.tokens / r.window) * width)) };
148 });
149 let diff = width - runs.reduce((sum, run) => sum + run.size, 0);
150 for (const run of [...runs].sort((a, b) => a.min - b.min || b.size - a.size)) {
151 const size = Math.max(run.min, run.size + diff);
152 diff -= size - run.size;
153 run.size = size;
154 }
155 return runs.filter((run) => run.size > 0);
156}
157
158function legend(r: Reading, width: number) {
159 const lines: Slice[][] = [];
160 let used = 0;
161 for (const s of r.slices) {
162 const size = label(s, r.window).length;
163 const line = lines.at(-1);
164 if (line && used + SPLIT.length + size <= width) {
165 line.push(s);
166 used += SPLIT.length + size;
167 } else {
168 lines.push([s]);
169 used = size;
170 }
171 }
172 return lines;
173}
174
175function label(s: Slice, window: number) {
176 return `${MARKER[s.kind]} ${s.name} ${tokens(s.tokens)}${s.kind === "used" ? ` ${share(s.tokens, window)}` : ""}`;
177}
178
179function tokens(n: number) {
180 if (n >= 1_000_000) return `${+(n / 1_000_000).toFixed(1)}M`;
181 if (n >= 10_000) return `${Math.round(n / 1000)}k`;
182 if (n >= 1000) return `${+(n / 1000).toFixed(1)}k`;
183 return String(n);
184}
185
186function share(n: number, window: number) {
187 const p = (n / window) * 100;
188 if (p > 0 && p < 0.1) return "<0.1%";
189 return `${p >= 10 ? Math.round(p) : +p.toFixed(1)}%`;
190}
191types/index.d.ts 21 lines1export type Slice = {
2 name: string;
3 tokens: number;
4 color: string;
5 kind: "used" | "free" | "buffer";
6};
7
8export type Reading = {
9 slices: Slice[];
10 total: number;
11 window: number;
12 percent: number;
13 compactsAt?: number;
14};
15
16declare module "claude-code" {
17 interface PluginState {
18 "context-bar": { reading: Reading | null };
19 }
20}
21