- TypeScript 52.4%
- Shell 45.2%
- AppleScript 2.4%
Add common emacs Ctrl+ shortcuts to the viins keymap so they work alongside vi mode. These keys were all unbound (self-insert) or already performing the same action, so there are no conflicts. Bindings added: Ctrl+A/E beginning/end of line Ctrl+F/B forward/backward char Ctrl+N/P next/previous history Ctrl+K kill to end of line Ctrl+U kill whole line Ctrl+W backward kill word Ctrl+D delete char or list completions Ctrl+T is intentionally left unbound (tmux prefix). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .ssh | ||
| atuin | ||
| karabiner-ts | ||
| scripts | ||
| tmux | ||
| zsh | ||
| .gitignore | ||
| dot | ||
| LICENSE | ||
| README.md | ||
Dotfiles
Configuration files and a management script for a personalized development environment.
Usage
Clone
Clone the repository to any path you like. I keep mine at ${HOME}/.config/dotfiles.
The repository is mirrored on both GitHub and a self-hosted GitLab instance:
GitHub (primary)
git clone https://github.com/kaygdotorg/dotfiles.git "${HOME}/.config/dotfiles"
Self-hosted GitLab (mirror)
git clone https://git.kayg.org/kayg/dotfiles.git "${HOME}/.config/dotfiles"
Both remotes are kept in sync. Use whichever is more convenient or accessible.
Setup
The dot script handles all linking and installation. Start by symlinking it into your PATH:
"${HOME}/.config/dotfiles/dot" setup dot
Then set up whichever apps you need:
dot setup zsh
dot setup tmux
dot setup atuin
dot setup ssh
dot setup karabiner # macOS only, requires npx
Each dot setup <app> command creates the necessary directories, symlinks configuration files from this repository into the correct system paths, and installs any dependencies (plugins, binaries, etc.). Running setup again is safe: existing symlinks are overwritten, existing zsh plugins are skipped, and existing Atuin installs are reused.
Command format and validation
The dot CLI accepts exactly two arguments:
dot <setup|update> <dot|tmux|zsh|atuin|ssh|karabiner>
If arguments are missing or invalid, dot prints usage and exits with a non-zero status.
Re-running setup commands
dot setup zshreuses the existing install directory and skips plugin repos that are already present.dot setup atuinreuses an existing~/.atuin/bin/atuininstallation, ensures~/.local/binexists, and then refreshes the symlink/config.dot setup dot,dot setup tmux, anddot setup sshare symlink-based and can be run repeatedly.
Apps
- Zsh — Standalone configuration with Oh My Posh prompt, vi-mode, lazy-loaded nvm, and plugins (autosuggestions, syntax highlighting, history substring search, completions).
- Tmux — Standalone configuration with Catppuccin Mocha theme, OSC 52 clipboard support for nested sessions, vi-mode copy bindings, mouse support, F12 pass-through toggle for nested sessions, searchable keybindings cheatsheet (
prefix + ?), and TPM for plugin management. - Atuin — Shell history replacement with sync to a self-hosted server, replacing the default zsh history search.
- SSH — Managed SSH client configuration.
- Karabiner — Advanced keyboard customization via karabiner.ts with Colemak-DH layout and hyper key layers.
Repository Structure
Configuration files are stored flat under each app's directory. The dot script is responsible for creating directories at the destination and symlinking files into place. No file in this repository needs to match its final system path.
.
├── dot # Setup and management script (POSIX sh)
├── zsh/
│ ├── .zshenv # Sets ZDOTDIR so zsh finds its config
│ ├── .zshrc # Main shell configuration
│ └── omp.yaml # Oh My Posh prompt theme
├── tmux/
│ └── .tmux.conf # Tmux configuration
├── atuin/
│ └── config.toml # Atuin shell history configuration
├── .ssh/
│ └── config # SSH client configuration
├── karabiner-ts/
│ └── index.ts # Generates Karabiner-Elements JSON profile
└── scripts/
└── toggle-menu-bar-visibility.applescript
Linking map
The dot script symlinks each file from this repository into its expected system location. The diagram below shows what goes where:
graph LR
subgraph Repository
A["zsh/.zshenv"]
B["zsh/.zshrc"]
C["zsh/omp.yaml"]
D["tmux/.tmux.conf"]
E["atuin/config.toml"]
F[".ssh/config"]
G["karabiner-ts/index.ts"]
H["dot"]
end
subgraph System
A1["~/.zshenv"]
B1["~/.config/zsh/.zshrc"]
C1["~/.config/zsh/omp.yaml"]
D1["~/.config/tmux/tmux.conf"]
E1["~/.config/atuin/config.toml"]
F1["~/.ssh/config"]
G1["~/.config/karabiner/karabiner.json"]
H1["~/.local/bin/dot"]
end
A -->|symlink| A1
B -->|symlink| B1
C -->|symlink| C1
D -->|symlink| D1
E -->|symlink| E1
F -->|symlink| F1
G -->|generates| G1
H -->|symlink| H1
Note: Karabiner is the exception —
index.tsis executed vianpx karabiner.ts, which writes the profile JSON directly to~/.config/karabiner/karabiner.json. It is not symlinked.
Quirks
Nested tmux clipboard (inner SSH → outer Mac → iTerm2)
Tmux's set-clipboard on correctly intercepts and re-emits OSC 52 escape sequences from applications (e.g., a printf in the shell or vim yanking), but it does not emit OSC 52 from its own copy mode when running nested inside another tmux (TERM=tmux-256color). This means yanking or mouse-selecting in copy mode silently fails to reach the outer clipboard.
The workaround: all copy-mode bindings use copy-pipe-and-cancel with an explicit command that base64-encodes the selection and writes an OSC 52 sequence directly to #{client_tty} (the terminal the tmux client is attached to). The full clipboard chain looks like this:
graph LR
A["Copy-mode selection"] --> B["copy-pipe base64-encodes\nand writes OSC 52\nto #{client_tty}"]
B --> C["SSH forwards bytes"]
C --> D["Outer tmux intercepts\n(set-clipboard on)\nand re-emits OSC 52"]
D --> E["iTerm2 sets\nmacOS clipboard"]
See the clipboard and copy-mode sections in tmux/.tmux.conf for the full implementation and explanation.
Nested tmux pass-through (F12 toggle)
When running tmux inside tmux (e.g., SSH into a remote machine that also runs tmux), key bindings are captured by the outer session. Press F12 to toggle pass-through mode — all keys go directly to the inner tmux. The outer status bar shows a red lock indicator when pass-through is active. Press F12 again to return to normal mode.
Keybindings cheatsheet (prefix + ?)
Press prefix + ? to open a searchable, scrollable popup listing every key binding with human-readable descriptions. It covers all three key tables (prefix, root, copy mode) and includes plugin bindings. Use / to search, arrow keys or j/k to scroll, and q to close.
Paste buffer reference view
Tmux stores every yanked selection in a paste buffer stack. Two bindings make it easy to reference previous yanks while working:
Yin copy mode — Yanks the selection to both the paste buffer and the system clipboard (via OSC 52), then immediately opens it in a resizable split pane for reference.prefix + b— Browse all paste buffers, select one, and open it in a split pane. Useprefix + =to browse and paste instead.
The reference split is scrollable and searchable (powered by less), resizable with prefix + H/J/K/L or mouse drag, and closes with q.
Shift+Enter in Claude Code inside tmux
Shift+Enter for newlines does not work inside tmux. Tmux only forwards extended key sequences (kitty keyboard protocol) to applications that explicitly request them, and Claude Code does not opt in.
The extended-keys always setting would fix this, but it causes breakage elsewhere:
- Shift+Tab sends raw escape codes instead of working properly (tmux#4304)
- Pasting in Neovim produces artifacts (gpakosz/.tmux#776)
- Fish shell completions break in tmux 3.5+ (tmux#2705)
Use \ + Enter for newlines in Claude Code instead. This is documented in the terminal section of tmux/.tmux.conf.
License
See LICENSE file for details.