No. 0111 min read
My Prompt Is More Cyberpunk Now
I had work to do.
Real work. The sort with expectations attached to it, and possibly money, and other people waiting somewhere downstream. Naturally, this was the moment I became consumed by the state of my shell configuration.
I do not remember what the other task was. I remember that it had become unpleasant enough for a shell configuration I had neglected for years to present itself, suddenly and convincingly, as the emergency. This was procrastination with a systems-administration budget. I knew it. I opened the file anyway.
My ~/.zshrc was 390 lines long, and I could no longer explain most of it.
The file had grown one reasonable decision at a time. A project began, so I added an alias. A tool looked promising, so I pasted in its initialization block. Something broke, so I found a fix and left it there forever. Years passed. Projects ended. Tools disappeared. The file kept executing all of it.
A cold terminal startup took about 930 milliseconds; a warm one, around 350. Neither is suffering in any meaningful human sense, but the first is long enough for a machine you use all day to communicate its disappointment. I had begun to distrust the file. Worse, I was ashamed of it.
So I opened Claude Code and admitted that I used zsh mostly like bash, did not know much about bash either, and suspected I was getting very little from both. I asked it to inspect a file I no longer understood.
PRIVATE, MORE OR LESS
There were eight API credentials exported in plaintext near the bottom of the file: two for Linear, one for SerpAPI, one for Hugging Face, three for YouTube, one for Gemini. The .zshrc sat in a repository called dot, configured with a private GitHub remote. I did not yet know whether those lines had ever been committed. “Private” was doing more work in my security model than it had any right to do.
There was a theme called spiral, written around 2014 by Steven Fusco, a former workmate who was senior to me. He was lean, somehow, despite his fondness for consuming an entire rotisserie chicken at lunch. His theme contained perfectly serviceable parsers for Mercurial and Subversion branches, neither of which I have used in more than a decade. Steven had moved on. Every morning, his parsers still reported for duty.
Twenty-eight lines of completion machinery remained for Jina, a framework I had not installed in years. hub browse survived as an alias without hub. An nvm initialization had no nvm behind it. Heroku autocomplete had no Heroku CLI. A guarded iTerm2 integration pointed at a file that did not exist, with test -e and || true carefully arranging for the useless machinery to produce nothing.
There were two calls to compinit, because apparently once was not sufficiently complete. ~/.local/bin appeared twice in my PATH, except one copy was quoted so badly that the tilde never expanded.
Then there was ra, an alias I used when macOS audio collapsed into one of its private moods and the coreaudiod daemon needed killing. The clean version had once been:
sudo killall coreaudiod
At some point I had commented that out and replaced it with:
sudo kill `ps -ax | grep 'coreaudiod' | grep 'sbin' |awk '{print $1}'`
A ps, two grep processes, and an awk handed a PID to kill through deprecated backticks, doing in four stages what the commented-out command had already done in one. The replacement was longer, harder to read, and more fragile. I had not inherited it from a stranger online. I had done this myself. I started with the correct answer, labored over it, made it worse, and preserved the original beside it like a before photograph in a malpractice case.
Most of the file was not broken in any dramatic way. That would have been easier. Most of it had once made sense. Now it was simply old intention, still running.
YEARS OF CRUFF
First rule: comments explaining live code could stay. Commented-out code could go. I made a backup and gave Claude the rest.
The first edit arrived as a wall of deletions. I read the diff quickly, scanning every red block for the thing I would need later and fail to recognize now. Oh My Zsh boilerplate, gone. Big Sur compiler flags, gone. Disabled aliases, old setup fragments, line after line beginning # Uncomment the following line, all red and on their way out. I approved it. The count fell from roughly 390 lines to 290. I typed source ~/.zshrc. No complaint. We kept going.
The edit also removed two active aliases, sz and zs.
It did this without telling me. I approved the edit without noticing. The aliases were tiny: sz opened my zshrc in Sublime Text; zs sourced it back into the current shell. Edit. Reload. Edit. Reload. I had used the pair for years, which was precisely why I failed to inventory them consciously. Muscle memory is not documentation.
Two phases later I requested a duplication review and told it, in capitals, DON'T MAKE EDIT, just suggest. The audit described another alias as a duplicate of sz. I corrected it: sz opened my zshrc, and I usually followed it with zs to reload the file.
It checked the file it had edited and called the next message a “quick clarification.” It had muddled the answer. sz was not currently available.
It had not found a duplicate. It had created an absence.
We restored both aliases and reloaded the file. The machine had understood what the lines said and not what my hands knew. I had reviewed the diff and seen what I expected to see.
That should have made me trust it less. In a useful way, it did. It also made the collaboration feel more real. Now we had broken something together.
Then we dealt with the secrets. I moved them into ~/.env, restricted the file, wired it through direnv, and removed eight exports from the tracked file.
This was better. It was also absurd. The secret-handling intervention began with me showing an AI agent the secrets.
Then came the history search. git log -S, one recognizable credential prefix after another: Linear, Gemini, Hugging Face. Enter. Nothing. Next prefix. Enter. Nothing. The empty terminal was good news, narrowly construed. It established that we could not find the keys in the reachable history we checked, not that no copy had ever touched a remote or existed anywhere else. I accepted the narrower result with the enthusiasm of a man who had been expecting to spend his afternoon rotating credentials.
They were gone from the zshrc. The agent had read them, my responsible backups likely preserved them, and applications launched outside the shell would not inherit the new file. Later I added a Moonshot credential, bringing the live collection to nine. Better, not absolved.
Inside the Jina block, two unrelated lines still had plausible jobs. I kept them and deleted the rest.
Bun and Shell-GPT went next. Shell-GPT was not merely an abandoned binary: a dozen lines of live zle code still took whatever English was in my command buffer, sent it through sgpt --shell, and replaced the buffer with the resulting command, all bound to Ctrl-L. I removed the widget, uninstalled the package, and deleted its leftover config.
YT-DLP’S DENO
I asked what Deno was. I decided I did not need it, removed its local files, and asked Homebrew to finish the job.
Homebrew refused.
Error: Refusing to uninstall /opt/homebrew/Cellar/deno/2.7.12
because it is required by yt-dlp, which is currently installed.
I use yt-dlp every week. Somewhere in the dependency graph it had acquired Deno, and Homebrew would not cut a live edge. So Deno stayed. Not my Deno, exactly. yt-dlp’s Deno. The difference mattered to me, if not to the filesystem.
When the removal was over, the file was about 220 lines. I made another backup and declared the cleanup complete. Only then did I ask for an assessment.
It found that I had put the entire ~/dot repository on my PATH. It found the startup delay too: most of the 930 milliseconds went to initializing pyenv, whether or not I intended to use it. The expensive machinery reported for work every morning; I spoke to it perhaps once a month.
The assessment ended with a list of options, fixes, and newer tools. The diff waited beside it.
I had spent hours being cautious. I had asked for suggestions before edits, made multiple backups, caught one deletion only after the fact, interrogated obscure environment settings, and forced the machine to justify its recommendations. The file was clean enough that I could finally see it. Confidence had arrived.
DO EVERYTHING
I typed exactly what the moment deserved: do everything. make the default editor cursor.
This was broad authorization given to an agent that had already removed two of my most-used aliases without either of us noticing. Earlier I had made it earn every change. Now the presumption flipped.
A permission request appeared. Approve. Homebrew poured fzf, zoxide, eza, bat, and ripgrep. Autosuggestions landed in the plugin directory. Five loose executables moved into bin. The PATH contracted. Another diff went red, then green. Approve.
$EDITOR and $VISUAL became cursor --wait. History grew. Navigation changed. A few remembered letters produced a command in gray ghost text. One prompt after another. Read. Approve. Read faster. Approve.
Then pyenv went under the knife. Its shims stayed available, but the expensive initialization would sleep until I called it. Source the file. Check Python. Check pip. Open a clean shell. Time it.
Cold startup fell from 930 milliseconds to 170. I opened another terminal. The prompt snapped into place. I closed it and opened another, the profoundly serious benchmark methodology of a man enjoying a new toy. No launch page, no recent-documents gallery, no invitation to discover a feature. Just the cursor, already waiting.
By the end of that afternoon, the file had grown back to 336 lines. We had begun around 390, stripped it to 220, then added 116 lines of deliberate complexity. I had not achieved purity. I could account for every line, at least for the moment.
There remained the question of how it should look.
Powerlevel10k and Starship made the shortlist, and I ignored the clean-configuration and cross-shell questions long enough to set the criterion that mattered: pick whichever was more cyberpunk and tell me about the cool features.
Powerlevel10k won. We installed the font, adjusted Ghostty, and ran the configuration wizard. The next window showed my directory, Git state, last-command status, execution time, and active environment in blocks of color, with a clear second line for the command. It was excessive, informative, and exactly what I wanted. Cleanliness was for the machinery. The dashboard could look like vice.
CTRL-G
A few hours after deleting the Shell-GPT widget, I asked to rebuild it with a different model behind it.
I wanted to type a description in ordinary English, hit one shortcut, and have the buffer replaced with the corresponding zsh command.
I pointed aichat at Kimi K2.6 and asked for the command to list files modified in the last twenty-four hours. It returned:
find . -maxdepth 1 -type f -mtime -1
Correct. Clean. Then we wrote a widget that replaced the English at my prompt with the generated command. An hourglass appeared while it worked. The answer returned to the input line and waited. The model was writing where I issue instructions to the machine.
The first binding was Ctrl-X a. It worked. I hated it. Ctrl-A collided with beginning-of-line, a behavior I had been using without knowing its name. I chose Ctrl-G. G for generate.
Now plain English can become a shell command under my cursor. The danger is not only an invented flag. It is a valid rm aimed one directory too high, waiting for me to press Enter. Anyone who has watched me approve a diff containing the deletion of my favorite aliases can evaluate that safeguard.
Claude Code had entered the shell as an inspector. I had removed one neglected AI command generator during the cleanup and installed a cleaner replacement before the afternoon was over, another current enthusiasm occupying almost exactly the outline of the old one.
10,051 COMMANDS
Atuin imported 10,051 commands from my existing history. It almost did not get the chance.
I had been told to live with fzf history search for two to four weeks before trying something else. I had no loyalty to a tool installed minutes earlier. Why train one motion for two weeks only to replace it with another?
I typed back: i'm just saying I haven't learned anything new yet - so i don't want to learn it for 2 weeks and then unlearn it.
I rejected the waiting period and installed Atuin immediately. The import produced a leaderboard:
| Count | Command |
|---|---|
| 764 | cd |
| 447 | make |
| 429 | ffmpeg |
| 406 | ls |
| 305 | cursor |
| 304 | git checkout |
| 199 | claude |
This was a better professional biography than anything I had written deliberately. make, the old blunt instrument still holding projects together. ffmpeg, evidence of years spent forcing audio and video through one famously unforgiving program. Calls to claude, already high enough to establish dependency. Above all of them sat cd, 764 times, two letters and a path entered whenever I wanted to go somewhere else.
The numbers showed what configuration files cannot: not what I believed my workflow to be, but what my hands had actually done. Oh My Zsh had enabled AUTO_CD before the overhaul. I had typed cd 764 times anyway. Zoxide was new; the habit was not.
The better part was retrieval. Ctrl-R opened a searchable record where a few fragments could recover the exact ugly ffmpeg or deployment command I had already solved once. Give up a little opacity, gain a little memory, stay on the keyboard.
THE WORK WAS STILL WAITING
“My prompt is more cyberpunk now” was not a deliverable.
I opened a new terminal. The instrument panel lit against the black. The prompt appeared in 170 milliseconds. The terminal was faster, gaudier, more capable, and slightly more dangerous. In other words, it finally felt like mine.
I typed the first command.
The cursor came back.
I had another ready.
