Every developer has their setup. Here’s mine — refined over years of trying too many tools and settling on the ones that actually stick. I optimize for fewer, better tools and treat my keyboard as the application layer.
The Keyboard Is the Application Layer
The single most defining tool in my setup isn’t an app — it’s Karabiner-Elements, configured to turn Caps Lock into a Hyper key (⌃⌥⇧⌘) when held, and Escape when tapped. The entire 600+ line keymap is generated from TypeScript and version-controlled like code.
Hyper acts as a universal prefix into ~15 sublayers. A few of the ones I actually use every day:
Hyper + Space— opens Jump, a Raycast extension I built. It’s a fuzzy-finder over every app and browser tab I’ve registered, so I can focus Cursor on the customer-app project or that one Notion doc in two keystrokes.Hyper + C— Screen OCR. Drag a region with the mouse and the text lands in my clipboard. Used constantly for copying things out of images, screenshots, and Figma.Hyper + V— opens Raycast’s Clipboard History. Past 50+ clipboard entries searchable in two keystrokes. The natural counterpart to Hyper+C — one captures, the other recalls.Hyper + F + <key>— focus a specific app on a specific project. The sublayer is dynamically generated from a JSON file that the Jump extension writes to. To add a new binding: pick an app in Raycast →⌘K→ “Set Keyboard Shortcut” → done, it’s a Hyper-key chord forever.Hyper + T + <key>— same idea but scoped to browser tabs. Pin a tab, bind a key, and it’s one chord away no matter how many tabs are open.
The Hyper + F and Hyper + T layers are the trick that makes the whole system maintainable: I’m not editing TypeScript every time I want a new shortcut. The Raycast extension does the binding for me, and Karabiner picks it up on the next reload.
Once you start thinking of the keyboard as a configurable surface instead of a fixed input device, the question stops being “what app does X?” and becomes “what keystroke does X?”
Editor & AI
Cursor is my primary IDE. I moved away from Neovim because pair-programming with an AI feels qualitatively different inside an editor that’s designed for it. Hyper + O + C opens it.
Claude Code lives in my terminal as cc — the most-touched alias in my .zshrc. I use it for exploring unfamiliar codebases, refactoring at scale, and rubber-ducking decisions. My tmux config has a special Shift+Enter rebind specifically so multi-line input works inside it.
The key to using AI tools effectively is knowing when to use them and when to think for yourself. I use Claude for the mechanical parts and do the architectural thinking myself.
Terminal & Shell
iTerm2 + tmux for persistent sessions per project, Catppuccin Mocha theme with a hot-pink active-window highlight. tmux-resurrect and tmux-continuum save every 15 minutes — I haven’t lost a session in years. Window nav is Ctrl+Option+←/→, no prefix needed.
zsh + Oh-My-Zsh + Starship. Starship handles the prompt; OMZ gives me zsh-autosuggestions, syntax highlighting, and the git plugin. The shell stack underneath is all modern-CLI drop-ins:
cd→ zoxide (smart-jump to any dir I’ve visited)ls/ll/lt→ eza (icons, dirs-first, tree view)cat→ bat (syntax highlighting, no pager by default)- direnv for per-project env vars
- fastfetch as terminal splash — custom Snabbit ASCII logo
Git Workflow
Three layers, used in different moods:
Conductor for git worktrees. Most of my work happens here now — running multiple branches as parallel worktrees beats stashing and switching. lp (launch picker) and wr (worktree run) are the daily commands.
Rebased is my JetBrains-based git client when I want a GUI — especially for interactive rebases, conflict resolution, and reviewing big diffs. rebased alias launches it.
Git CLI muscle memory. For the 80% case I never leave the shell. gs, gpush, gpull, gaa, gcd, glog, gsa/gas/gsl for stash, grs for reset. Conventional commits (feat:, fix:, docs:), short-lived branches, squash merge.
Raycast — Extensions I Built
Raycast is the launcher. The interesting part is the custom extensions I’ve written for my own workflow:
- projects-folder — keystroke-launched browser of every project folder I touch, with one-key open in Cursor / iTerm / Finder / Claude Code
- snabbit_resource_manager — quick access to internal Snabbit work links
- thermal-print — sends arbitrary text (and now images) to my FlashToy F2 thermal printer over USB. Yes, I print receipts at my desk
- jump — dynamically populates Karabiner sublayers from a JSON file, so I can bind any app or browser tab to a Hyper-key chord in seconds
- add-a-journal-entry, prompts, habit_raycast, yts-ask-youtube — small utilities for specific friction points
Custom Scripts as Permanent Memory
The folder I most often modify is ~/custom_scripts/ — numbered shell files, sourced on every shell start. They’ve become a kind of permanent operational memory:
001_work_related_scripts.sh— Flutter/iOS/Android release pipelines (prepare_ios_release,make_qa_build_and_upload, etc.)002_notion_scripts.sh— 19 single-purposenotion_log_*functions, one per daily habit008_ai_commands.sh— bootstrapping new AI projects (setup_cursor_project)conductor_tools.sh— worktree helpers (lp,wr,svsfor syncing VSCode state across worktrees)
Everything in this folder is version-controlled in settings-sync-all and pushed/pulled across machines via spush / spull / sstatus.
Knowledge Management
Obsidian for long-form: notes, articles, project docs, daily logs. Local-first markdown, [[wikilinks]] for connecting ideas, iCloud sync, no vendor lock-in. This very site is published from an Obsidian vault. See building-a-tui-website for how that works.
Notion for structured tracking: habits, todos, project hubs. Integrated so deeply via Karabiner and custom scripts that capturing a thought is a single keypress and logging a habit is two.
The Philosophy
I optimize for fewer, better tools rather than chasing every new thing. A tool earns its place by:
- Solving a real problem (not a hypothetical one)
- Being fast enough that I never wait for it
- Having a CLI or keyboard-driven interface
- Composing with everything else I already use
If a tool doesn’t meet these criteria, I probably don’t need it. The best tools disappear — they become invisible infrastructure for the work you actually care about.