Patching a Custom Theme into the Grok CLI
⚠️ This can break Grok. If you break it, you can reinstall it. This also disables auto-updates. Try at your own risk.
A reproducible, step-by-step walkthrough for recoloring the Grok Build TUI by editing the binary.
Build-specific warning. Every byte offset in this document is for
grok 0.2.11, macOS arm64. Do not copy the offsets blindly — a different version puts the colors somewhere else. Step 4 shows how to find them on your build; trust that over the literals here.
Safety. Work on a copy, never the original. Disable auto-update. Keep the rollback (Step 9) handy.
Prereqs: macOS (arm64), python3, and the Xcode command-line tools (codesign).
ls -l ~/.grok/bin/grok # this is a symlink
readlink ~/.grok/bin/grok # -> ../downloads/grok-<version>-macos-aarch64
ls -l ~/.grok/downloads/ # the real binaries live hereSet a variable for the real file you'll be working from:
cd ~/.grok/downloads
SRC=grok-0.2.11-macos-aarch64 # adjust to your version
file "$SRC" # Mach-O 64-bit executable arm64
codesign -dv "$SRC" 2>&1 | grep -E "flags|TeamIdentifier"You'll see flags=0x10000(runtime) (hardened runtime) and a Team ID — confirming you'll need to re-sign after editing.
Edit ~/.grok/config.toml:
[cli]
auto_update = falsecd ~/.grok/downloads
cp -p grok-0.2.11-macos-aarch64 grok-0.2.11-neonAll patches below target grok-0.2.11-neon.
Colors are stored the way the ratatui library encodes them: an RGB color is 4 bytes — 0x11 (the Rgb enum tag), then R, G, B. A theme is ~36 of these in a row.
Find a theme by searching for a color you already know. TokyoNight's background is #1a1b26:
python3 - <<'PY'
data = open("grok-0.2.11-macos-aarch64","rb").read()
i = data.find(bytes([0x1a,0x1b,0x26]))
print(hex(i), data[i-1:i+4].hex()) # byte before should be 0x11
PYThen decode the surrounding region to map every built-in theme:
python3 - <<'PY'
data = open("grok-0.2.11-macos-aarch64","rb").read()
lo, hi = 0x4c0ae00, 0x4c0b300 # adjust around your hit from above
for off in range(lo, hi, 4):
t,r,g,b = data[off],data[off+1],data[off+2],data[off+3]
tag = f"#{r:02x}{g:02x}{b:02x}" if t==0x11 else f"[{t:02x}{r:02x}{g:02x}{b:02x}]"
print(f"0x{off:x}: {tag}")
PYIdentify each theme by its signature color, and note the start/end offsets of the one you use:
| Theme | Tell-tale | This build |
|---|---|---|
| OscuraMidnight | #030304 bg |
~0x4c0af70 |
| RosePineMoon | #232136 |
~0x4c0b040 |
| TokyoNight | #1a1b26 |
~0x4c0b120 |
| GrokDay (light) | #f5f5f5 |
~0x4c0b1c0 |
GrokNight (dark, default) |
#0a0a0a bg + magenta #bb9af7 |
0x4c0b250–0x4c0b2dc |
Prove the pipeline with the smallest edit — turn GrokNight's magenta accent green, scoped to its range only:
python3 - <<'PY'
fn="grok-0.2.11-neon"
d=bytearray(open(fn,"rb").read())
LO,HI=0x4c0b250,0x4c0b2e0
GREEN=bytes([0x39,0xff,0x14])
i=LO
while (i:=d.find(bytes([0x11,0xbb,0x9a,0xf7]),i))!=-1 and i<HI:
d[i+1:i+4]=GREEN; i+=4
open(fn,"wb").write(d)
print("accent patched")
PYMap every color to a green of equal luminance (keeps contrast / readability):
python3 - <<'PY'
orig=open("grok-0.2.11-macos-aarch64","rb").read()
neon=bytearray(open("grok-0.2.11-neon","rb").read())
def matrix(r,g,b):
luma=0.299*r+0.587*g+0.114*b
if luma<28: return (0,max(0,round(luma*0.7)),0) # bg -> near-black green
return (0,min(255,round(40+luma*1.05)),0) # text/accent -> bright green
def recolor(lo,hi):
n=0
for off in range(lo,hi,4):
if orig[off]!=0x11: continue # skip non-color / None gaps
r,g,b=orig[off+1],orig[off+2],orig[off+3]
neon[off+1],neon[off+2],neon[off+3]=matrix(r,g,b); n+=1
return n
main = recolor(0x4c0b250, 0x4c0b2dc+4) # GrokNight main palette
md = recolor(0x4c09d60, 0x4c09dcc) # GrokNight markdown/syntax palette
open("grok-0.2.11-neon","wb").write(neon)
print(f"recolored {main} main + {md} markdown colors")
PYWhy two ranges? The main theme colors the chrome/accents. Assistant message text renders as markdown, which uses a separate
Option<Color>palette (00000000=Nonegaps between colors) located elsewhere —~0x4c09d60on this build, containing the body-text white#e1e1e1. Recolor both or the replies stay white.
cd ~/.grok/downloads
codesign -f -s - grok-0.2.11-neon # ad-hoc re-sign
codesign -v grok-0.2.11-neon && echo "sig OK"
./grok-0.2.11-neon version # must print a version => it runscd ~/.grok/bin
ln -sf ../downloads/grok-0.2.11-neon grok
grok version # confirm symlink resolvesRestart fully. Grok keeps a persistent leader process holding the old binary in memory — quit every session (and kill any lingering grok leader) before relaunching, or you won't see the change.
ln -sf ../downloads/grok-0.2.11-macos-aarch64 ~/.grok/bin/grok
# optional: re-enable auto_update in ~/.grok/config.toml- Brighter/pure phosphor: raise the floor in
matrix()(e.g.round(60+luma*1.0)) or clamp the brightest tier toward#00FF41. - Different hue: keep the luminance idea, just steer the output channels (amber = high R+G, ice = high G+B).
- Other themes: repeat Step 4–6 against that theme's offset range; ranges are independent.
- After a version bump: re-run Step 4 — offsets will have moved. Nothing else changes.
- Unofficial, unsupported, breaks on every update.
- Offsets are build-specific (
0.2.11arm64 here). - Ad-hoc re-signing is for running it yourself, not distribution.
- Don't ship a patched binary to anyone else.