A comprehensive macOS development environment managed as a git repository cloned directly to ~/.config. Because all XDG-compliant tools look in ~/.config by default, every config file is automatically in the right place with no extra symlinking needed.
The setup includes a full Neovim IDE configuration, Kitty terminal, Zsh shell config, global Git settings, ESLint / Prettier / TypeScript configs, and a collection of utility shell scripts for daily development.
Theme: The entire environment uses the Catppuccin Frappé color palette — Neovim, Kitty, and all terminal tools are configured to match.
Directory Structure
All configuration lives under ~/.config:
nvim/
Neovim configuration (init.lua + Lua modules)
kitty/
Kitty terminal — fonts, theme, window settings
fish/
Fish shell — minimal PATH additions
configs/
Home-dir dotfiles (.zshrc, .gitconfig, .tmux.conf, etc.)
bin/
Shell utility scripts (android/, dev/, utils.sh)
git/
Global git config and gitignore
assets/
Screenshots, patched fonts, theme files
XDG compliant: Cloning to ~/.config means tools like Neovim, Kitty, Fish, and gh pick up their configs automatically without any manual linking.
Requirements
neovim 0.10+
Editor (required for all nvim features)
node 18+ / nvm
JavaScript tooling, LSP servers
git
Version control
kitty
Terminal emulator
zsh
Primary shell
brew
macOS package manager — installed by setup-environment if missing
JetBrainsMono Nerd Font
Font (all terminals + Neovim)
Installation
Clone the repository directly to ~/.config:
# Clone directly to ~/.config (XDG base dir)
git clone https://github.com/san-siva/dotfiles ~/.config
# If ~/.config already exists
cd ~/.config
git init
git remote add origin https://github.com/san-siva/dotfiles
git pull origin mainThree setup scripts handle the full environment bootstrap. Run them in order, or use setup-environment to run all three at once.
setup-environment
The top-level bootstrapper. Installs Homebrew first if it is missing and puts it on PATH for the rest of the run, then installs all system tools and delegates to install-global-deps and link-dotfiles.
~/.config/bin/dev/setup/setup-environmentLanguages
python3, node (via nvm), ruby, go, lua, rust
Java
openjdk, openjdk@21, ant, maven, jdtls, google-java-format
Editor / Terminal
neovim, kitty, tmux + TPM
Shell
oh-my-zsh, powerlevel10k, zsh-history-substring-search
CLI tools
bash 5, ripgrep, fzf, fd, zoxide, jq, yq, gh, fish, deno, wget, tree, fastfetch
Formatters
black (Python), sqlfluff, shfmt (via Go), stylua (via Cargo)
install-global-deps
Installs all global npm packages, symlinks ESLint configs, and enables Corepack. Safe to re-run.
~/.config/bin/dev/setup/install-global-depsESLint core
eslint, eslint_d, jiti, @eslint/js, @eslint/eslintrc, @eslint/compat
ESLint plugins
eslint-plugin-react, -react-hooks, -jest, -import, -redux-saga, -unicorn, simple-import-sort, and more
TypeScript
typescript, typescript-eslint, @typescript-eslint/eslint-plugin, @typescript-eslint/parser
Prettier
prettier, eslint-config-prettier
Testing
jest, markdownlint-cli2
Tools
@san-siva/gitsy, @anthropic-ai/claude-code, local-ssl-proxy, neovim (npm provider)
ESLint configs
Symlinks eslint.config.ts and eslint-utilities.ts into the npm global prefix
link-dotfiles
Creates symlinks from ~/.config/configs/ into the home directory using ln -sfn, so re-running it is safe: existing files and symlinks at each target are replaced in place. It also links private agent skills from WORK_AGENT_SKILLS_DIR when that variable is set — see Private skills.
~/.config/bin/dev/setup/link-dotfilesconfigs/.tmux.conf
~/.tmux.conf
configs/.prettierrc.json
~/.prettierrc.json
configs/.eslintrc.json
~/.eslintrc.json
configs/.p10k.zsh
~/.p10k.zsh
configs/.gitconfig
~/.gitconfig
configs/.gitconfig__wrk
~/.gitconfig__wrk
configs/ssh_config
~/.ssh/config
configs/eclipse-java-google-style.xml
~/.local/share/eclipse/
configs/lombok.jar
~/.local/share/eclipse/
configs/agent-skills
~/.claude/skills
configs/agent-skills
~/.gemini/antigravity-cli/skills
$WORK_AGENT_SKILLS_DIR/*
configs/agent-skills/<skill>
configs/antigravity-keybindings.json
~/.gemini/antigravity-cli/keybindings.json
Existing files and symlinks at the target paths are replaced. A real directory at a target is moved aside to <target>.bak-<timestamp> rather than deleted — review and remove these backups once you have checked their contents.
Create the local zshrc
link-dotfiles doesn't create ~/.zshrc — it's a machine-local file that holds secrets (see Shell). Create it once so new shells load the tracked config:
echo 'source ~/.config/configs/.zshrc' > ~/.zshrcNeovim
A full IDE experience built in Lua using lazy.nvim. Entry point is nvim/init.lua; leader key is ,.
nvim/
├── init.lua Entry point — options, loads bindings & plugins
├── lua/
│ ├── bindings/
│ │ ├── mappings.lua Key mappings
│ │ └── autocmd.lua Autocommands
│ ├── core/
│ │ └── large-files.lua Large file detection & feature toggling
│ ├── plugins/
│ │ ├── init.lua Plugin list (lazy.nvim)
│ │ ├── lspconfig.lua LSP server configs
│ │ ├── conform.lua Format-on-save setup
│ │ └── ...
│ ├── shared_ftplugins/
│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
│ └── utils/
│ ├── large-file-check.lua
│ └── wildignores.lua
└── ftplugin/
├── typescript.lua
├── javascript.lua
├── typescriptreact.lua
├── javascriptreact.lua
├── md.lua
├── python.lua
└── java.luaSet NVIM_NPM=1 to open Neovim with all plugins disabled — useful when invoked inside Node.js scripts or tooling where startup time matters.
Key Bindings
<C-c>
Escape / cancel
<Esc>
Clear search highlight
<leader>tl / <leader>th
Next / prev tab
<leader>tL / <leader>tH
New / close tab
<leader>1–9
Jump to tab by number
<leader>bb
Alternate buffer
<leader>p
Copy relative path + line number
<leader>P
Copy absolute path
<leader>l
Copy line number
<leader>e / <leader>E
Float diagnostic / loclist
[e / ]e
Next / prev diagnostic
<leader>N / <leader>n
Toggle relative numbers
<leader>S
Reload vimrc
<leader>M
Delete all marks
<leader>ff
Telescope: find files
<leader>fg
Telescope: live grep
<leader>fb
Telescope: open buffers
<leader>fd
Telescope: diagnostics
<leader>tt
Toggle file tree
<leader>gs
Git status
<leader>gc
Git commit
<leader>gh / <leader>gl
Diffget ours / theirs
<leader>cJ / <leader>cK
Open / close all folds
<C-o>
Supermaven: accept suggestion
<C-y>
Supermaven: accept word
<C-r>
Supermaven: dismiss
Autocommands
TextYankPost
Flash-highlight yanked text
BufReadPre
Detect large files; set hash-based undo path for long file paths
BufReadPost
Disable swapfile, undofile, syntax, synmaxcol, treesitter for large files
BufWinEnter
Disable folding, cursorline, spell for large files
BufReadPre / BufNewFile
Read prettier tabWidth from config and set local indentation for JS/TS files
Plugins
catppuccin/nvim
Colorscheme — Frappé flavor
nvim-treesitter
Syntax highlighting + text objects
nvim-lspconfig + mason
LSP server management
conform.nvim
Format on save
nvim-cmp + luasnip
Completion — LSP, snippets, path, dictionary
telescope.nvim
Fuzzy finder — files, grep, buffers, diagnostics
nvim-tree.lua
File explorer sidebar
gitsigns.nvim
Git diff signs in gutter
vim-fugitive
Git commands inside Neovim
supermaven-nvim
AI inline completion
nvim-ufo
Code folding via LSP / treesitter
lualine.nvim
Status line + buffer tabline
indent-blankline.nvim
Indent guides
nvim-colorizer.lua
Hex/CSS color preview
markdown-preview.nvim
Live browser preview with Mermaid
nvim-jdtls
Java LSP (Eclipse JDT)
todo-comments.nvim
TODO / FIXME highlighting
Comment.nvim
Comment toggling
fidget.nvim
LSP progress spinner
Language Support
TypeScript / JavaScript
ts_ls
prettier
ESLint diagnostics
eslint
—
Go
gopls
goimports + gofmt
Python
basedpyright
isort + black
Lua
lua_ls
stylua
Bash
bashls
shfmt
TailwindCSS
tailwindcss
—
Java
nvim-jdtls
google-java-format
CSS / SCSS / HTML / JSON / Markdown
—
prettier
SQL
—
sqlfmt
XML
—
xmllint
YAML
—
yamllint
Monorepo TypeScript: ts_ls walks up the directory tree to find the topmost directory containing both tsconfig.json and node_modules — works correctly in monorepos without per-project config.
Shell
The Zsh config is split into three layers so the tracked parts stay portable and secrets never touch the repo.
graph LR
A["~/.zshrc<br/>untracked, machine-local"] -->|sources| B["configs/.zshrc<br/>tracked"]
B -->|sources first| C["configs/.zshrc_pre<br/>prompt + oh-my-zsh"]
B --> D["main()"]
A --> E["main_local()<br/>secrets + local setup"]~/.zshrc
No
Entry point plus machine-local setup_* functions for secrets and tooling
configs/.zshrc
Yes
PATH, language toolchains, integrations, keybindings, aliases
configs/.zshrc_pre
Yes
Catppuccin syntax highlighting, Powerlevel10k instant prompt, oh-my-zsh load
Machine-local entry point
~/.zshrc isn't symlinked. It's a plain file that sources the tracked config, then follows the same pattern — one setup_* function per concern and a main_local() that calls them — for anything that must stay off GitHub:
# ~/.zshrc
source ~/.config/configs/.zshrc
setup_env_vars() {
# Secrets — not tracked in any repo
export JIRA_API_TOKEN="..."
export FIGMA_API_TOKEN="..."
}
setup_local_paths() {
export PATH="$HOME/.local/some-tool/bin:$PATH"
}
setup_local_aliases() {
alias work='cd ~/Work'
}
main_local() {
setup_env_vars
setup_local_paths
setup_local_aliases
}
main_localAvoid name clashes: The tracked config already defines main and setup_aliases, so local functions use distinct names such as main_local and setup_local_aliases.
Keep secrets in ~/.zshrc only. Everything under ~/.config is pushed to a public repository.
One function per concern
Instead of one long script, configs/.zshrc groups each concern into its own setup_* function, and a single main() calls them in order. Reordering, disabling, or debugging a step is a one-line change:
# configs/.zshrc
main() {
setup_environment_variables
setup_homebrew_paths
setup_go_environment
setup_rust_environment
setup_ruby_environment
setup_python_environment
setup_java_environment
setup_custom_bins
setup_node_environment
setup_shell_integrations
setup_keybindings
setup_ssh_agent
setup_aliases
}
mainsetup_environment_variables
TERM, EDITOR, VISUAL
setup_homebrew_paths
Homebrew bin / sbin and Rancher Desktop
setup_go_environment
GOPATH and ~/go/bin
setup_rust_environment
~/.cargo/bin
setup_ruby_environment
Homebrew Ruby, compiler flags, user gem bin
setup_python_environment
Python user-base bin
setup_java_environment
OpenJDK, Zulu 8, JAVA_HOME
setup_custom_bins
~/.local/bin, ~/bin, and the bin/ script folders in this repo
setup_node_environment
nvm, Yarn global bin, Zscaler CA for Node tooling
setup_shell_integrations
zoxide — runs after PATH is complete
setup_keybindings
^n / ^p completion, ^j / ^k history substring search
setup_ssh_agent
Loads keys via bin/dev/setup-ssh-agent
setup_aliases
sed → gsed, gh copilot shortcuts, navigation aliases
Safe PATH handling
PATH is built through two guarded helpers. A directory is only added if it exists, and typeset -aU path keeps entries unique, so re-sourcing the file never produces duplicates. The same config works on a machine that's missing some toolchains:
# configs/.zshrc
typeset -aU path
prepend_path() {
if [ -d "$1" ]; then
path=("$1" $path)
fi
}
append_path() {
if [ -d "$1" ]; then
path=($path "$1")
fi
}Terminal
Kitty
Config at kitty/kitty.conf. Kitty is the primary terminal — configured for a clean, distraction-free experience with the full Catppuccin Frappé color scheme.
Font
JetBrainsMono Nerd Font Mono, 13pt
Theme
Catppuccin Frappé (themes/frappe.conf)
Window size
150c × 50c
Cursor
block
Option key
macos_option_as_alt yes
Switching themes: All four Catppuccin flavors are in kitty/themes/ — change the include line to latte.conf, macchiato.conf, or mocha.conf to switch.
Tmux
Config at configs/.tmux.conf (symlinked to ~/.tmux.conf). Uses TPM for plugin management with Catppuccin-matching status bar colors.
Prefix
C-a
Mouse
Enabled
History limit
10000
Pane navigation
prefix + h/j/k/l (vim-style)
Window navigation
prefix + i/u (next/prev), prefix + p (last)
tmux-resurrect
Save and restore sessions across restarts
tmux-yank
Copy to system clipboard from tmux
Git
Multi Account Setup
Personal and work profiles with separate SSH keys using includeIf and SSH host aliases — blog post
Gitsy
Terminal UI for Git — log, diff, branch, and stash views — gitsy.santhoshsiva.dev
Agent Skills
A shared set of AI agent skills lives in configs/agent-skills/. link-dotfiles symlinks the same directory into both Claude Code (~/.claude/skills) and Antigravity CLI (~/.gemini/antigravity-cli/skills), so one edit updates both agents.
configs/agent-skills/
├── <skill-name>/
│ ├── SKILL.md Frontmatter (name, description) + instructions
│ └── references/ Optional supporting docs loaded on demand
└── ...add-logs
Add log statements in the SAN_SIVA logging format
bash-scripts
Write bash scripts in the gitsy style — sourced utils, set_flags parsing, etc.
branch-name
Suggest a git branch name for a Jira ticket
commit-and-push
Suggest commit messages, pick one, then commit and push
commit-message
Suggest a Conventional Commits message for the staged changes
create-markdown
Write markdown following a consistent style guide, including blogkit-md rules
create-worktree
Create a git worktree using g-wa
document-progress
Record task progress into ~/Work/TASKS
gitsy
Perform Git operations through the gitsy CLI
mermaid-wizard
Produce syntactically correct Mermaid diagrams for code, architecture, and flows
Private skills
Work-specific skills live in a separate private repository, not here. link-dotfiles reads their location from WORK_AGENT_SKILLS_DIR — set in the untracked ~/.zshrc — and symlinks each one into configs/agent-skills/, which is gitignored. If the variable is unset, the step is skipped.
# ~/.zshrc
setup_env_vars() {
export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
}Invoking skills: User-invocable skills can be run directly as slash commands, e.g. /commit-and-push. Agents also pick them up automatically when a request matches the skill's description.
Utility Scripts
All scripts in bin/ are on PATH and share a common utility library at bin/utils.sh.
wireless-adb <ip>
Android
Connect to a device over WiFi
adb-install <apk>
Android
Install APK to connected device
adb-reverse <port>
Android
Reverse a port (e.g. Metro bundler)
adb-uninstall <package>
Android
Uninstall a package by name
kill-port <port>
Dev
Kill the process listening on a given port
setup-ssh-agent
Dev
Load SSH key into the running agent
pwdc
Dev
Print current working directory (clean output)
reinstall-claude
Dev
Reinstall Claude Code CLI tool
reinstall-npm-pkg <pkg> [version]
Dev
Reinstall/upgrade any global npm package
Assets
The assets/ directory contains static resources:
screenshot.png
Environment screenshot for README
catppuccin/
Catppuccin syntax highlight theme files for Zsh
JetBrainsMonoPatched.zip
Patched JetBrainsMono with Nerd Font glyphs
License
This project is licensed under the MIT License.
About
Author: Santhosh Siva GitHub: github.com/san-siva
A comprehensive macOS development environment managed as a git repository cloned directly to ~/.config. Because all XDG-compliant tools look in ~/.config by default, every config file is automatically in the right place with no extra symlinking needed.
The setup includes a full Neovim IDE configuration, Kitty terminal, Zsh shell config, global Git settings, ESLint / Prettier / TypeScript configs, and a collection of utility shell scripts for daily development.
Theme: The entire environment uses the Catppuccin Frappé color palette — Neovim, Kitty, and all terminal tools are configured to match.
Directory Structure
All configuration lives under ~/.config:
nvim/
Neovim configuration (init.lua + Lua modules)
kitty/
Kitty terminal — fonts, theme, window settings
fish/
Fish shell — minimal PATH additions
configs/
Home-dir dotfiles (.zshrc, .gitconfig, .tmux.conf, etc.)
bin/
Shell utility scripts (android/, dev/, utils.sh)
git/
Global git config and gitignore
assets/
Screenshots, patched fonts, theme files
XDG compliant: Cloning to ~/.config means tools like Neovim, Kitty, Fish, and gh pick up their configs automatically without any manual linking.
Requirements
neovim 0.10+
Editor (required for all nvim features)
node 18+ / nvm
JavaScript tooling, LSP servers
git
Version control
kitty
Terminal emulator
zsh
Primary shell
brew
macOS package manager — installed by setup-environment if missing
JetBrainsMono Nerd Font
Font (all terminals + Neovim)
Installation
Clone the repository directly to ~/.config:
# Clone directly to ~/.config (XDG base dir)
git clone https://github.com/san-siva/dotfiles ~/.config
# If ~/.config already exists
cd ~/.config
git init
git remote add origin https://github.com/san-siva/dotfiles
git pull origin mainThree setup scripts handle the full environment bootstrap. Run them in order, or use setup-environment to run all three at once.
setup-environment
The top-level bootstrapper. Installs Homebrew first if it is missing and puts it on PATH for the rest of the run, then installs all system tools and delegates to install-global-deps and link-dotfiles.
~/.config/bin/dev/setup/setup-environmentLanguages
python3, node (via nvm), ruby, go, lua, rust
Java
openjdk, openjdk@21, ant, maven, jdtls, google-java-format
Editor / Terminal
neovim, kitty, tmux + TPM
Shell
oh-my-zsh, powerlevel10k, zsh-history-substring-search
CLI tools
bash 5, ripgrep, fzf, fd, zoxide, jq, yq, gh, fish, deno, wget, tree, fastfetch
Formatters
black (Python), sqlfluff, shfmt (via Go), stylua (via Cargo)
install-global-deps
Installs all global npm packages, symlinks ESLint configs, and enables Corepack. Safe to re-run.
~/.config/bin/dev/setup/install-global-depsESLint core
eslint, eslint_d, jiti, @eslint/js, @eslint/eslintrc, @eslint/compat
ESLint plugins
eslint-plugin-react, -react-hooks, -jest, -import, -redux-saga, -unicorn, simple-import-sort, and more
TypeScript
typescript, typescript-eslint, @typescript-eslint/eslint-plugin, @typescript-eslint/parser
Prettier
prettier, eslint-config-prettier
Testing
jest, markdownlint-cli2
Tools
@san-siva/gitsy, @anthropic-ai/claude-code, local-ssl-proxy, neovim (npm provider)
ESLint configs
Symlinks eslint.config.ts and eslint-utilities.ts into the npm global prefix
link-dotfiles
Creates symlinks from ~/.config/configs/ into the home directory using ln -sfn, so re-running it is safe: existing files and symlinks at each target are replaced in place. It also links private agent skills from WORK_AGENT_SKILLS_DIR when that variable is set — see Private skills.
~/.config/bin/dev/setup/link-dotfilesconfigs/.tmux.conf
~/.tmux.conf
configs/.prettierrc.json
~/.prettierrc.json
configs/.eslintrc.json
~/.eslintrc.json
configs/.p10k.zsh
~/.p10k.zsh
configs/.gitconfig
~/.gitconfig
configs/.gitconfig__wrk
~/.gitconfig__wrk
configs/ssh_config
~/.ssh/config
configs/eclipse-java-google-style.xml
~/.local/share/eclipse/
configs/lombok.jar
~/.local/share/eclipse/
configs/agent-skills
~/.claude/skills
configs/agent-skills
~/.gemini/antigravity-cli/skills
$WORK_AGENT_SKILLS_DIR/*
configs/agent-skills/<skill>
configs/antigravity-keybindings.json
~/.gemini/antigravity-cli/keybindings.json
Existing files and symlinks at the target paths are replaced. A real directory at a target is moved aside to <target>.bak-<timestamp> rather than deleted — review and remove these backups once you have checked their contents.
Create the local zshrc
link-dotfiles doesn't create ~/.zshrc — it's a machine-local file that holds secrets (see Shell). Create it once so new shells load the tracked config:
echo 'source ~/.config/configs/.zshrc' > ~/.zshrcNeovim
A full IDE experience built in Lua using lazy.nvim. Entry point is nvim/init.lua; leader key is ,.
nvim/
├── init.lua Entry point — options, loads bindings & plugins
├── lua/
│ ├── bindings/
│ │ ├── mappings.lua Key mappings
│ │ └── autocmd.lua Autocommands
│ ├── core/
│ │ └── large-files.lua Large file detection & feature toggling
│ ├── plugins/
│ │ ├── init.lua Plugin list (lazy.nvim)
│ │ ├── lspconfig.lua LSP server configs
│ │ ├── conform.lua Format-on-save setup
│ │ └── ...
│ ├── shared_ftplugins/
│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
│ └── utils/
│ ├── large-file-check.lua
│ └── wildignores.lua
└── ftplugin/
├── typescript.lua
├── javascript.lua
├── typescriptreact.lua
├── javascriptreact.lua
├── md.lua
├── python.lua
└── java.luaSet NVIM_NPM=1 to open Neovim with all plugins disabled — useful when invoked inside Node.js scripts or tooling where startup time matters.
Key Bindings
<C-c>
Escape / cancel
<Esc>
Clear search highlight
<leader>tl / <leader>th
Next / prev tab
<leader>tL / <leader>tH
New / close tab
<leader>1–9
Jump to tab by number
<leader>bb
Alternate buffer
<leader>p
Copy relative path + line number
<leader>P
Copy absolute path
<leader>l
Copy line number
<leader>e / <leader>E
Float diagnostic / loclist
[e / ]e
Next / prev diagnostic
<leader>N / <leader>n
Toggle relative numbers
<leader>S
Reload vimrc
<leader>M
Delete all marks
<leader>ff
Telescope: find files
<leader>fg
Telescope: live grep
<leader>fb
Telescope: open buffers
<leader>fd
Telescope: diagnostics
<leader>tt
Toggle file tree
<leader>gs
Git status
<leader>gc
Git commit
<leader>gh / <leader>gl
Diffget ours / theirs
<leader>cJ / <leader>cK
Open / close all folds
<C-o>
Supermaven: accept suggestion
<C-y>
Supermaven: accept word
<C-r>
Supermaven: dismiss
Autocommands
TextYankPost
Flash-highlight yanked text
BufReadPre
Detect large files; set hash-based undo path for long file paths
BufReadPost
Disable swapfile, undofile, syntax, synmaxcol, treesitter for large files
BufWinEnter
Disable folding, cursorline, spell for large files
BufReadPre / BufNewFile
Read prettier tabWidth from config and set local indentation for JS/TS files
Plugins
catppuccin/nvim
Colorscheme — Frappé flavor
nvim-treesitter
Syntax highlighting + text objects
nvim-lspconfig + mason
LSP server management
conform.nvim
Format on save
nvim-cmp + luasnip
Completion — LSP, snippets, path, dictionary
telescope.nvim
Fuzzy finder — files, grep, buffers, diagnostics
nvim-tree.lua
File explorer sidebar
gitsigns.nvim
Git diff signs in gutter
vim-fugitive
Git commands inside Neovim
supermaven-nvim
AI inline completion
nvim-ufo
Code folding via LSP / treesitter
lualine.nvim
Status line + buffer tabline
indent-blankline.nvim
Indent guides
nvim-colorizer.lua
Hex/CSS color preview
markdown-preview.nvim
Live browser preview with Mermaid
nvim-jdtls
Java LSP (Eclipse JDT)
todo-comments.nvim
TODO / FIXME highlighting
Comment.nvim
Comment toggling
fidget.nvim
LSP progress spinner
Language Support
TypeScript / JavaScript
ts_ls
prettier
ESLint diagnostics
eslint
—
Go
gopls
goimports + gofmt
Python
basedpyright
isort + black
Lua
lua_ls
stylua
Bash
bashls
shfmt
TailwindCSS
tailwindcss
—
Java
nvim-jdtls
google-java-format
CSS / SCSS / HTML / JSON / Markdown
—
prettier
SQL
—
sqlfmt
XML
—
xmllint
YAML
—
yamllint
Monorepo TypeScript: ts_ls walks up the directory tree to find the topmost directory containing both tsconfig.json and node_modules — works correctly in monorepos without per-project config.
Shell
The Zsh config is split into three layers so the tracked parts stay portable and secrets never touch the repo.
graph LR
A["~/.zshrc<br/>untracked, machine-local"] -->|sources| B["configs/.zshrc<br/>tracked"]
B -->|sources first| C["configs/.zshrc_pre<br/>prompt + oh-my-zsh"]
B --> D["main()"]
A --> E["main_local()<br/>secrets + local setup"]~/.zshrc
No
Entry point plus machine-local setup_* functions for secrets and tooling
configs/.zshrc
Yes
PATH, language toolchains, integrations, keybindings, aliases
configs/.zshrc_pre
Yes
Catppuccin syntax highlighting, Powerlevel10k instant prompt, oh-my-zsh load
Machine-local entry point
~/.zshrc isn't symlinked. It's a plain file that sources the tracked config, then follows the same pattern — one setup_* function per concern and a main_local() that calls them — for anything that must stay off GitHub:
# ~/.zshrc
source ~/.config/configs/.zshrc
setup_env_vars() {
# Secrets — not tracked in any repo
export JIRA_API_TOKEN="..."
export FIGMA_API_TOKEN="..."
}
setup_local_paths() {
export PATH="$HOME/.local/some-tool/bin:$PATH"
}
setup_local_aliases() {
alias work='cd ~/Work'
}
main_local() {
setup_env_vars
setup_local_paths
setup_local_aliases
}
main_localAvoid name clashes: The tracked config already defines main and setup_aliases, so local functions use distinct names such as main_local and setup_local_aliases.
Keep secrets in ~/.zshrc only. Everything under ~/.config is pushed to a public repository.
One function per concern
Instead of one long script, configs/.zshrc groups each concern into its own setup_* function, and a single main() calls them in order. Reordering, disabling, or debugging a step is a one-line change:
# configs/.zshrc
main() {
setup_environment_variables
setup_homebrew_paths
setup_go_environment
setup_rust_environment
setup_ruby_environment
setup_python_environment
setup_java_environment
setup_custom_bins
setup_node_environment
setup_shell_integrations
setup_keybindings
setup_ssh_agent
setup_aliases
}
mainsetup_environment_variables
TERM, EDITOR, VISUAL
setup_homebrew_paths
Homebrew bin / sbin and Rancher Desktop
setup_go_environment
GOPATH and ~/go/bin
setup_rust_environment
~/.cargo/bin
setup_ruby_environment
Homebrew Ruby, compiler flags, user gem bin
setup_python_environment
Python user-base bin
setup_java_environment
OpenJDK, Zulu 8, JAVA_HOME
setup_custom_bins
~/.local/bin, ~/bin, and the bin/ script folders in this repo
setup_node_environment
nvm, Yarn global bin, Zscaler CA for Node tooling
setup_shell_integrations
zoxide — runs after PATH is complete
setup_keybindings
^n / ^p completion, ^j / ^k history substring search
setup_ssh_agent
Loads keys via bin/dev/setup-ssh-agent
setup_aliases
sed → gsed, gh copilot shortcuts, navigation aliases
Safe PATH handling
PATH is built through two guarded helpers. A directory is only added if it exists, and typeset -aU path keeps entries unique, so re-sourcing the file never produces duplicates. The same config works on a machine that's missing some toolchains:
# configs/.zshrc
typeset -aU path
prepend_path() {
if [ -d "$1" ]; then
path=("$1" $path)
fi
}
append_path() {
if [ -d "$1" ]; then
path=($path "$1")
fi
}Terminal
Kitty
Config at kitty/kitty.conf. Kitty is the primary terminal — configured for a clean, distraction-free experience with the full Catppuccin Frappé color scheme.
Font
JetBrainsMono Nerd Font Mono, 13pt
Theme
Catppuccin Frappé (themes/frappe.conf)
Window size
150c × 50c
Cursor
block
Option key
macos_option_as_alt yes
Switching themes: All four Catppuccin flavors are in kitty/themes/ — change the include line to latte.conf, macchiato.conf, or mocha.conf to switch.
Tmux
Config at configs/.tmux.conf (symlinked to ~/.tmux.conf). Uses TPM for plugin management with Catppuccin-matching status bar colors.
Prefix
C-a
Mouse
Enabled
History limit
10000
Pane navigation
prefix + h/j/k/l (vim-style)
Window navigation
prefix + i/u (next/prev), prefix + p (last)
tmux-resurrect
Save and restore sessions across restarts
tmux-yank
Copy to system clipboard from tmux
Git
Multi Account Setup
Personal and work profiles with separate SSH keys using includeIf and SSH host aliases — blog post
Gitsy
Terminal UI for Git — log, diff, branch, and stash views — gitsy.santhoshsiva.dev
Agent Skills
A shared set of AI agent skills lives in configs/agent-skills/. link-dotfiles symlinks the same directory into both Claude Code (~/.claude/skills) and Antigravity CLI (~/.gemini/antigravity-cli/skills), so one edit updates both agents.
configs/agent-skills/
├── <skill-name>/
│ ├── SKILL.md Frontmatter (name, description) + instructions
│ └── references/ Optional supporting docs loaded on demand
└── ...add-logs
Add log statements in the SAN_SIVA logging format
bash-scripts
Write bash scripts in the gitsy style — sourced utils, set_flags parsing, etc.
branch-name
Suggest a git branch name for a Jira ticket
commit-and-push
Suggest commit messages, pick one, then commit and push
commit-message
Suggest a Conventional Commits message for the staged changes
create-markdown
Write markdown following a consistent style guide, including blogkit-md rules
create-worktree
Create a git worktree using g-wa
document-progress
Record task progress into ~/Work/TASKS
gitsy
Perform Git operations through the gitsy CLI
mermaid-wizard
Produce syntactically correct Mermaid diagrams for code, architecture, and flows
Private skills
Work-specific skills live in a separate private repository, not here. link-dotfiles reads their location from WORK_AGENT_SKILLS_DIR — set in the untracked ~/.zshrc — and symlinks each one into configs/agent-skills/, which is gitignored. If the variable is unset, the step is skipped.
# ~/.zshrc
setup_env_vars() {
export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
}Invoking skills: User-invocable skills can be run directly as slash commands, e.g. /commit-and-push. Agents also pick them up automatically when a request matches the skill's description.
Utility Scripts
All scripts in bin/ are on PATH and share a common utility library at bin/utils.sh.
wireless-adb <ip>
Android
Connect to a device over WiFi
adb-install <apk>
Android
Install APK to connected device
adb-reverse <port>
Android
Reverse a port (e.g. Metro bundler)
adb-uninstall <package>
Android
Uninstall a package by name
kill-port <port>
Dev
Kill the process listening on a given port
setup-ssh-agent
Dev
Load SSH key into the running agent
pwdc
Dev
Print current working directory (clean output)
reinstall-claude
Dev
Reinstall Claude Code CLI tool
reinstall-npm-pkg <pkg> [version]
Dev
Reinstall/upgrade any global npm package
Assets
The assets/ directory contains static resources:
screenshot.png
Environment screenshot for README
catppuccin/
Catppuccin syntax highlight theme files for Zsh
JetBrainsMonoPatched.zip
Patched JetBrainsMono with Nerd Font glyphs
License
This project is licensed under the MIT License.
About
Author: Santhosh Siva GitHub: github.com/san-siva
No title
A comprehensive macOS development environment managed as a git repository cloned directly to ~/.config. Because all XDG-compliant tools look in ~/.config by default, every config file is automatically in the right place with no extra symlinking needed.
The setup includes a full Neovim IDE configuration, Kitty terminal, Zsh shell config, global Git settings, ESLint / Prettier / TypeScript configs, and a collection of utility shell scripts for daily development.
Theme: The entire environment uses the Catppuccin Frappé color palette — Neovim, Kitty, and all terminal tools are configured to match.
Directory Structure
All configuration lives under ~/.config:
nvim/
Neovim configuration (init.lua + Lua modules)
kitty/
Kitty terminal — fonts, theme, window settings
fish/
Fish shell — minimal PATH additions
configs/
Home-dir dotfiles (.zshrc, .gitconfig, .tmux.conf, etc.)
bin/
Shell utility scripts (android/, dev/, utils.sh)
git/
Global git config and gitignore
assets/
Screenshots, patched fonts, theme files
XDG compliant: Cloning to ~/.config means tools like Neovim, Kitty, Fish, and gh pick up their configs automatically without any manual linking.
Requirements
neovim 0.10+
Editor (required for all nvim features)
node 18+ / nvm
JavaScript tooling, LSP servers
git
Version control
kitty
Terminal emulator
zsh
Primary shell
brew
macOS package manager — installed by setup-environment if missing
JetBrainsMono Nerd Font
Font (all terminals + Neovim)
Installation
Clone the repository directly to ~/.config:
# Clone directly to ~/.config (XDG base dir)
git clone https://github.com/san-siva/dotfiles ~/.config
# If ~/.config already exists
cd ~/.config
git init
git remote add origin https://github.com/san-siva/dotfiles
git pull origin mainThree setup scripts handle the full environment bootstrap. Run them in order, or use setup-environment to run all three at once.
setup-environment
The top-level bootstrapper. Installs Homebrew first if it is missing and puts it on PATH for the rest of the run, then installs all system tools and delegates to install-global-deps and link-dotfiles.
~/.config/bin/dev/setup/setup-environmentLanguages
python3, node (via nvm), ruby, go, lua, rust
Java
openjdk, openjdk@21, ant, maven, jdtls, google-java-format
Editor / Terminal
neovim, kitty, tmux + TPM
Shell
oh-my-zsh, powerlevel10k, zsh-history-substring-search
CLI tools
bash 5, ripgrep, fzf, fd, zoxide, jq, yq, gh, fish, deno, wget, tree, fastfetch
Formatters
black (Python), sqlfluff, shfmt (via Go), stylua (via Cargo)
install-global-deps
Installs all global npm packages, symlinks ESLint configs, and enables Corepack. Safe to re-run.
~/.config/bin/dev/setup/install-global-depsESLint core
eslint, eslint_d, jiti, @eslint/js, @eslint/eslintrc, @eslint/compat
ESLint plugins
eslint-plugin-react, -react-hooks, -jest, -import, -redux-saga, -unicorn, simple-import-sort, and more
TypeScript
typescript, typescript-eslint, @typescript-eslint/eslint-plugin, @typescript-eslint/parser
Prettier
prettier, eslint-config-prettier
Testing
jest, markdownlint-cli2
Tools
@san-siva/gitsy, @anthropic-ai/claude-code, local-ssl-proxy, neovim (npm provider)
ESLint configs
Symlinks eslint.config.ts and eslint-utilities.ts into the npm global prefix
link-dotfiles
Creates symlinks from ~/.config/configs/ into the home directory using ln -sfn, so re-running it is safe: existing files and symlinks at each target are replaced in place. It also links private agent skills from WORK_AGENT_SKILLS_DIR when that variable is set — see Private skills.
~/.config/bin/dev/setup/link-dotfilesconfigs/.tmux.conf
~/.tmux.conf
configs/.prettierrc.json
~/.prettierrc.json
configs/.eslintrc.json
~/.eslintrc.json
configs/.p10k.zsh
~/.p10k.zsh
configs/.gitconfig
~/.gitconfig
configs/.gitconfig__wrk
~/.gitconfig__wrk
configs/ssh_config
~/.ssh/config
configs/eclipse-java-google-style.xml
~/.local/share/eclipse/
configs/lombok.jar
~/.local/share/eclipse/
configs/agent-skills
~/.claude/skills
configs/agent-skills
~/.gemini/antigravity-cli/skills
$WORK_AGENT_SKILLS_DIR/*
configs/agent-skills/<skill>
configs/antigravity-keybindings.json
~/.gemini/antigravity-cli/keybindings.json
Existing files and symlinks at the target paths are replaced. A real directory at a target is moved aside to <target>.bak-<timestamp> rather than deleted — review and remove these backups once you have checked their contents.
Create the local zshrc
link-dotfiles doesn't create ~/.zshrc — it's a machine-local file that holds secrets (see Shell). Create it once so new shells load the tracked config:
echo 'source ~/.config/configs/.zshrc' > ~/.zshrcNo title
Clone the repository directly to ~/.config:
1# Clone directly to ~/.config (XDG base dir)
2git clone https://github.com/san-siva/dotfiles ~/.config
3
4# If ~/.config already exists
5cd ~/.config
6git init
7git remote add origin https://github.com/san-siva/dotfiles
8git pull origin mainThree setup scripts handle the full environment bootstrap. Run them in order, or use setup-environment to run all three at once.
1# Clone directly to ~/.config (XDG base dir)
2git clone https://github.com/san-siva/dotfiles ~/.config
3
4# If ~/.config already exists
5cd ~/.config
6git init
7git remote add origin https://github.com/san-siva/dotfiles
8git pull origin mainsetup-environment
The top-level bootstrapper. Installs Homebrew first if it is missing and puts it on PATH for the rest of the run, then installs all system tools and delegates to install-global-deps and link-dotfiles.
1~/.config/bin/dev/setup/setup-environmentLanguages
python3, node (via nvm), ruby, go, lua, rust
Java
openjdk, openjdk@21, ant, maven, jdtls, google-java-format
Editor / Terminal
neovim, kitty, tmux + TPM
Shell
oh-my-zsh, powerlevel10k, zsh-history-substring-search
CLI tools
bash 5, ripgrep, fzf, fd, zoxide, jq, yq, gh, fish, deno, wget, tree, fastfetch
Formatters
black (Python), sqlfluff, shfmt (via Go), stylua (via Cargo)
1~/.config/bin/dev/setup/setup-environmentinstall-global-deps
Installs all global npm packages, symlinks ESLint configs, and enables Corepack. Safe to re-run.
1~/.config/bin/dev/setup/install-global-depsESLint core
eslint, eslint_d, jiti, @eslint/js, @eslint/eslintrc, @eslint/compat
ESLint plugins
eslint-plugin-react, -react-hooks, -jest, -import, -redux-saga, -unicorn, simple-import-sort, and more
TypeScript
typescript, typescript-eslint, @typescript-eslint/eslint-plugin, @typescript-eslint/parser
Prettier
prettier, eslint-config-prettier
Testing
jest, markdownlint-cli2
Tools
@san-siva/gitsy, @anthropic-ai/claude-code, local-ssl-proxy, neovim (npm provider)
ESLint configs
Symlinks eslint.config.ts and eslint-utilities.ts into the npm global prefix
1~/.config/bin/dev/setup/install-global-depslink-dotfiles
Creates symlinks from ~/.config/configs/ into the home directory using ln -sfn, so re-running it is safe: existing files and symlinks at each target are replaced in place. It also links private agent skills from WORK_AGENT_SKILLS_DIR when that variable is set — see Private skills.
1~/.config/bin/dev/setup/link-dotfilesconfigs/.tmux.conf
~/.tmux.conf
configs/.prettierrc.json
~/.prettierrc.json
configs/.eslintrc.json
~/.eslintrc.json
configs/.p10k.zsh
~/.p10k.zsh
configs/.gitconfig
~/.gitconfig
configs/.gitconfig__wrk
~/.gitconfig__wrk
configs/ssh_config
~/.ssh/config
configs/eclipse-java-google-style.xml
~/.local/share/eclipse/
configs/lombok.jar
~/.local/share/eclipse/
configs/agent-skills
~/.claude/skills
configs/agent-skills
~/.gemini/antigravity-cli/skills
$WORK_AGENT_SKILLS_DIR/*
configs/agent-skills/<skill>
configs/antigravity-keybindings.json
~/.gemini/antigravity-cli/keybindings.json
Existing files and symlinks at the target paths are replaced. A real directory at a target is moved aside to <target>.bak-<timestamp> rather than deleted — review and remove these backups once you have checked their contents.
1~/.config/bin/dev/setup/link-dotfilesCreate the local zshrc
link-dotfiles doesn't create ~/.zshrc — it's a machine-local file that holds secrets (see Shell). Create it once so new shells load the tracked config:
1echo 'source ~/.config/configs/.zshrc' > ~/.zshrc1echo 'source ~/.config/configs/.zshrc' > ~/.zshrcNeovim
A full IDE experience built in Lua using lazy.nvim. Entry point is nvim/init.lua; leader key is ,.
nvim/
├── init.lua Entry point — options, loads bindings & plugins
├── lua/
│ ├── bindings/
│ │ ├── mappings.lua Key mappings
│ │ └── autocmd.lua Autocommands
│ ├── core/
│ │ └── large-files.lua Large file detection & feature toggling
│ ├── plugins/
│ │ ├── init.lua Plugin list (lazy.nvim)
│ │ ├── lspconfig.lua LSP server configs
│ │ ├── conform.lua Format-on-save setup
│ │ └── ...
│ ├── shared_ftplugins/
│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
│ └── utils/
│ ├── large-file-check.lua
│ └── wildignores.lua
└── ftplugin/
├── typescript.lua
├── javascript.lua
├── typescriptreact.lua
├── javascriptreact.lua
├── md.lua
├── python.lua
└── java.luaSet NVIM_NPM=1 to open Neovim with all plugins disabled — useful when invoked inside Node.js scripts or tooling where startup time matters.
Key Bindings
<C-c>
Escape / cancel
<Esc>
Clear search highlight
<leader>tl / <leader>th
Next / prev tab
<leader>tL / <leader>tH
New / close tab
<leader>1–9
Jump to tab by number
<leader>bb
Alternate buffer
<leader>p
Copy relative path + line number
<leader>P
Copy absolute path
<leader>l
Copy line number
<leader>e / <leader>E
Float diagnostic / loclist
[e / ]e
Next / prev diagnostic
<leader>N / <leader>n
Toggle relative numbers
<leader>S
Reload vimrc
<leader>M
Delete all marks
<leader>ff
Telescope: find files
<leader>fg
Telescope: live grep
<leader>fb
Telescope: open buffers
<leader>fd
Telescope: diagnostics
<leader>tt
Toggle file tree
<leader>gs
Git status
<leader>gc
Git commit
<leader>gh / <leader>gl
Diffget ours / theirs
<leader>cJ / <leader>cK
Open / close all folds
<C-o>
Supermaven: accept suggestion
<C-y>
Supermaven: accept word
<C-r>
Supermaven: dismiss
Autocommands
TextYankPost
Flash-highlight yanked text
BufReadPre
Detect large files; set hash-based undo path for long file paths
BufReadPost
Disable swapfile, undofile, syntax, synmaxcol, treesitter for large files
BufWinEnter
Disable folding, cursorline, spell for large files
BufReadPre / BufNewFile
Read prettier tabWidth from config and set local indentation for JS/TS files
Plugins
catppuccin/nvim
Colorscheme — Frappé flavor
nvim-treesitter
Syntax highlighting + text objects
nvim-lspconfig + mason
LSP server management
conform.nvim
Format on save
nvim-cmp + luasnip
Completion — LSP, snippets, path, dictionary
telescope.nvim
Fuzzy finder — files, grep, buffers, diagnostics
nvim-tree.lua
File explorer sidebar
gitsigns.nvim
Git diff signs in gutter
vim-fugitive
Git commands inside Neovim
supermaven-nvim
AI inline completion
nvim-ufo
Code folding via LSP / treesitter
lualine.nvim
Status line + buffer tabline
indent-blankline.nvim
Indent guides
nvim-colorizer.lua
Hex/CSS color preview
markdown-preview.nvim
Live browser preview with Mermaid
nvim-jdtls
Java LSP (Eclipse JDT)
todo-comments.nvim
TODO / FIXME highlighting
Comment.nvim
Comment toggling
fidget.nvim
LSP progress spinner
Language Support
TypeScript / JavaScript
ts_ls
prettier
ESLint diagnostics
eslint
—
Go
gopls
goimports + gofmt
Python
basedpyright
isort + black
Lua
lua_ls
stylua
Bash
bashls
shfmt
TailwindCSS
tailwindcss
—
Java
nvim-jdtls
google-java-format
CSS / SCSS / HTML / JSON / Markdown
—
prettier
SQL
—
sqlfmt
XML
—
xmllint
YAML
—
yamllint
Monorepo TypeScript: ts_ls walks up the directory tree to find the topmost directory containing both tsconfig.json and node_modules — works correctly in monorepos without per-project config.
No title
A full IDE experience built in Lua using lazy.nvim. Entry point is nvim/init.lua; leader key is ,.
1nvim/
2├── init.lua Entry point — options, loads bindings & plugins
3├── lua/
4│ ├── bindings/
5│ │ ├── mappings.lua Key mappings
6│ │ └── autocmd.lua Autocommands
7│ ├── core/
8│ │ └── large-files.lua Large file detection & feature toggling
9│ ├── plugins/
10│ │ ├── init.lua Plugin list (lazy.nvim)
11│ │ ├── lspconfig.lua LSP server configs
12│ │ ├── conform.lua Format-on-save setup
13│ │ └── ...
14│ ├── shared_ftplugins/
15│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
16│ └── utils/
17│ ├── large-file-check.lua
18│ └── wildignores.lua
19└── ftplugin/
20 ├── typescript.lua
21 ├── javascript.lua
22 ├── typescriptreact.lua
23 ├── javascriptreact.lua
24 ├── md.lua
25 ├── python.lua
26 └── java.luaSet NVIM_NPM=1 to open Neovim with all plugins disabled — useful when invoked inside Node.js scripts or tooling where startup time matters.
1nvim/
2├── init.lua Entry point — options, loads bindings & plugins
3├── lua/
4│ ├── bindings/
5│ │ ├── mappings.lua Key mappings
6│ │ └── autocmd.lua Autocommands
7│ ├── core/
8│ │ └── large-files.lua Large file detection & feature toggling
9│ ├── plugins/
10│ │ ├── init.lua Plugin list (lazy.nvim)
11│ │ ├── lspconfig.lua LSP server configs
12│ │ ├── conform.lua Format-on-save setup
13│ │ └── ...
14│ ├── shared_ftplugins/
15│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
16│ └── utils/
17│ ├── large-file-check.lua
18│ └── wildignores.lua
19└── ftplugin/
20 ├── typescript.lua
21 ├── javascript.lua
22 ├── typescriptreact.lua
23 ├── javascriptreact.lua
24 ├── md.lua
25 ├── python.lua
26 └── java.luaKey Bindings
<C-c>
Escape / cancel
<Esc>
Clear search highlight
<leader>tl / <leader>th
Next / prev tab
<leader>tL / <leader>tH
New / close tab
<leader>1–9
Jump to tab by number
<leader>bb
Alternate buffer
<leader>p
Copy relative path + line number
<leader>P
Copy absolute path
<leader>l
Copy line number
<leader>e / <leader>E
Float diagnostic / loclist
[e / ]e
Next / prev diagnostic
<leader>N / <leader>n
Toggle relative numbers
<leader>S
Reload vimrc
<leader>M
Delete all marks
<leader>ff
Telescope: find files
<leader>fg
Telescope: live grep
<leader>fb
Telescope: open buffers
<leader>fd
Telescope: diagnostics
<leader>tt
Toggle file tree
<leader>gs
Git status
<leader>gc
Git commit
<leader>gh / <leader>gl
Diffget ours / theirs
<leader>cJ / <leader>cK
Open / close all folds
<C-o>
Supermaven: accept suggestion
<C-y>
Supermaven: accept word
<C-r>
Supermaven: dismiss
Autocommands
TextYankPost
Flash-highlight yanked text
BufReadPre
Detect large files; set hash-based undo path for long file paths
BufReadPost
Disable swapfile, undofile, syntax, synmaxcol, treesitter for large files
BufWinEnter
Disable folding, cursorline, spell for large files
BufReadPre / BufNewFile
Read prettier tabWidth from config and set local indentation for JS/TS files
Plugins
catppuccin/nvim
Colorscheme — Frappé flavor
nvim-treesitter
Syntax highlighting + text objects
nvim-lspconfig + mason
LSP server management
conform.nvim
Format on save
nvim-cmp + luasnip
Completion — LSP, snippets, path, dictionary
telescope.nvim
Fuzzy finder — files, grep, buffers, diagnostics
nvim-tree.lua
File explorer sidebar
gitsigns.nvim
Git diff signs in gutter
vim-fugitive
Git commands inside Neovim
supermaven-nvim
AI inline completion
nvim-ufo
Code folding via LSP / treesitter
lualine.nvim
Status line + buffer tabline
indent-blankline.nvim
Indent guides
nvim-colorizer.lua
Hex/CSS color preview
markdown-preview.nvim
Live browser preview with Mermaid
nvim-jdtls
Java LSP (Eclipse JDT)
todo-comments.nvim
TODO / FIXME highlighting
Comment.nvim
Comment toggling
fidget.nvim
LSP progress spinner
Language Support
TypeScript / JavaScript
ts_ls
prettier
ESLint diagnostics
eslint
—
Go
gopls
goimports + gofmt
Python
basedpyright
isort + black
Lua
lua_ls
stylua
Bash
bashls
shfmt
TailwindCSS
tailwindcss
—
Java
nvim-jdtls
google-java-format
CSS / SCSS / HTML / JSON / Markdown
—
prettier
SQL
—
sqlfmt
XML
—
xmllint
YAML
—
yamllint
Monorepo TypeScript: ts_ls walks up the directory tree to find the topmost directory containing both tsconfig.json and node_modules — works correctly in monorepos without per-project config.
Shell
The Zsh config is split into three layers so the tracked parts stay portable and secrets never touch the repo.
Rendering diagram...
~/.zshrc
No
Entry point plus machine-local setup_* functions for secrets and tooling
configs/.zshrc
Yes
PATH, language toolchains, integrations, keybindings, aliases
configs/.zshrc_pre
Yes
Catppuccin syntax highlighting, Powerlevel10k instant prompt, oh-my-zsh load
Machine-local entry point
~/.zshrc isn't symlinked. It's a plain file that sources the tracked config, then follows the same pattern — one setup_* function per concern and a main_local() that calls them — for anything that must stay off GitHub:
# ~/.zshrc
source ~/.config/configs/.zshrc
setup_env_vars() {
# Secrets — not tracked in any repo
export JIRA_API_TOKEN="..."
export FIGMA_API_TOKEN="..."
}
setup_local_paths() {
export PATH="$HOME/.local/some-tool/bin:$PATH"
}
setup_local_aliases() {
alias work='cd ~/Work'
}
main_local() {
setup_env_vars
setup_local_paths
setup_local_aliases
}
main_localAvoid name clashes: The tracked config already defines main and setup_aliases, so local functions use distinct names such as main_local and setup_local_aliases.
Keep secrets in ~/.zshrc only. Everything under ~/.config is pushed to a public repository.
One function per concern
Instead of one long script, configs/.zshrc groups each concern into its own setup_* function, and a single main() calls them in order. Reordering, disabling, or debugging a step is a one-line change:
# configs/.zshrc
main() {
setup_environment_variables
setup_homebrew_paths
setup_go_environment
setup_rust_environment
setup_ruby_environment
setup_python_environment
setup_java_environment
setup_custom_bins
setup_node_environment
setup_shell_integrations
setup_keybindings
setup_ssh_agent
setup_aliases
}
mainsetup_environment_variables
TERM, EDITOR, VISUAL
setup_homebrew_paths
Homebrew bin / sbin and Rancher Desktop
setup_go_environment
GOPATH and ~/go/bin
setup_rust_environment
~/.cargo/bin
setup_ruby_environment
Homebrew Ruby, compiler flags, user gem bin
setup_python_environment
Python user-base bin
setup_java_environment
OpenJDK, Zulu 8, JAVA_HOME
setup_custom_bins
~/.local/bin, ~/bin, and the bin/ script folders in this repo
setup_node_environment
nvm, Yarn global bin, Zscaler CA for Node tooling
setup_shell_integrations
zoxide — runs after PATH is complete
setup_keybindings
^n / ^p completion, ^j / ^k history substring search
setup_ssh_agent
Loads keys via bin/dev/setup-ssh-agent
setup_aliases
sed → gsed, gh copilot shortcuts, navigation aliases
Safe PATH handling
PATH is built through two guarded helpers. A directory is only added if it exists, and typeset -aU path keeps entries unique, so re-sourcing the file never produces duplicates. The same config works on a machine that's missing some toolchains:
# configs/.zshrc
typeset -aU path
prepend_path() {
if [ -d "$1" ]; then
path=("$1" $path)
fi
}
append_path() {
if [ -d "$1" ]; then
path=($path "$1")
fi
}No title
The Zsh config is split into three layers so the tracked parts stay portable and secrets never touch the repo.
Rendering diagram...
~/.zshrc
No
Entry point plus machine-local setup_* functions for secrets and tooling
configs/.zshrc
Yes
PATH, language toolchains, integrations, keybindings, aliases
configs/.zshrc_pre
Yes
Catppuccin syntax highlighting, Powerlevel10k instant prompt, oh-my-zsh load
Rendering diagram...
Machine-local entry point
~/.zshrc isn't symlinked. It's a plain file that sources the tracked config, then follows the same pattern — one setup_* function per concern and a main_local() that calls them — for anything that must stay off GitHub:
1# ~/.zshrc
2source ~/.config/configs/.zshrc
3
4setup_env_vars() {
5 # Secrets — not tracked in any repo
6 export JIRA_API_TOKEN="..."
7 export FIGMA_API_TOKEN="..."
8}
9
10setup_local_paths() {
11 export PATH="$HOME/.local/some-tool/bin:$PATH"
12}
13
14setup_local_aliases() {
15 alias work='cd ~/Work'
16}
17
18main_local() {
19 setup_env_vars
20 setup_local_paths
21 setup_local_aliases
22}
23
24main_localAvoid name clashes: The tracked config already defines main and setup_aliases, so local functions use distinct names such as main_local and setup_local_aliases.
Keep secrets in ~/.zshrc only. Everything under ~/.config is pushed to a public repository.
1# ~/.zshrc
2source ~/.config/configs/.zshrc
3
4setup_env_vars() {
5 # Secrets — not tracked in any repo
6 export JIRA_API_TOKEN="..."
7 export FIGMA_API_TOKEN="..."
8}
9
10setup_local_paths() {
11 export PATH="$HOME/.local/some-tool/bin:$PATH"
12}
13
14setup_local_aliases() {
15 alias work='cd ~/Work'
16}
17
18main_local() {
19 setup_env_vars
20 setup_local_paths
21 setup_local_aliases
22}
23
24main_localOne function per concern
Instead of one long script, configs/.zshrc groups each concern into its own setup_* function, and a single main() calls them in order. Reordering, disabling, or debugging a step is a one-line change:
1# configs/.zshrc
2main() {
3 setup_environment_variables
4 setup_homebrew_paths
5 setup_go_environment
6 setup_rust_environment
7 setup_ruby_environment
8 setup_python_environment
9 setup_java_environment
10 setup_custom_bins
11 setup_node_environment
12 setup_shell_integrations
13 setup_keybindings
14 setup_ssh_agent
15 setup_aliases
16}
17
18mainsetup_environment_variables
TERM, EDITOR, VISUAL
setup_homebrew_paths
Homebrew bin / sbin and Rancher Desktop
setup_go_environment
GOPATH and ~/go/bin
setup_rust_environment
~/.cargo/bin
setup_ruby_environment
Homebrew Ruby, compiler flags, user gem bin
setup_python_environment
Python user-base bin
setup_java_environment
OpenJDK, Zulu 8, JAVA_HOME
setup_custom_bins
~/.local/bin, ~/bin, and the bin/ script folders in this repo
setup_node_environment
nvm, Yarn global bin, Zscaler CA for Node tooling
setup_shell_integrations
zoxide — runs after PATH is complete
setup_keybindings
^n / ^p completion, ^j / ^k history substring search
setup_ssh_agent
Loads keys via bin/dev/setup-ssh-agent
setup_aliases
sed → gsed, gh copilot shortcuts, navigation aliases
1# configs/.zshrc
2main() {
3 setup_environment_variables
4 setup_homebrew_paths
5 setup_go_environment
6 setup_rust_environment
7 setup_ruby_environment
8 setup_python_environment
9 setup_java_environment
10 setup_custom_bins
11 setup_node_environment
12 setup_shell_integrations
13 setup_keybindings
14 setup_ssh_agent
15 setup_aliases
16}
17
18mainSafe PATH handling
PATH is built through two guarded helpers. A directory is only added if it exists, and typeset -aU path keeps entries unique, so re-sourcing the file never produces duplicates. The same config works on a machine that's missing some toolchains:
1# configs/.zshrc
2typeset -aU path
3
4prepend_path() {
5 if [ -d "$1" ]; then
6 path=("$1" $path)
7 fi
8}
9
10append_path() {
11 if [ -d "$1" ]; then
12 path=($path "$1")
13 fi
14}1# configs/.zshrc
2typeset -aU path
3
4prepend_path() {
5 if [ -d "$1" ]; then
6 path=("$1" $path)
7 fi
8}
9
10append_path() {
11 if [ -d "$1" ]; then
12 path=($path "$1")
13 fi
14}Terminal
Kitty
Config at kitty/kitty.conf. Kitty is the primary terminal — configured for a clean, distraction-free experience with the full Catppuccin Frappé color scheme.
Font
JetBrainsMono Nerd Font Mono, 13pt
Theme
Catppuccin Frappé (themes/frappe.conf)
Window size
150c × 50c
Cursor
block
Option key
macos_option_as_alt yes
Switching themes: All four Catppuccin flavors are in kitty/themes/ — change the include line to latte.conf, macchiato.conf, or mocha.conf to switch.
Tmux
Config at configs/.tmux.conf (symlinked to ~/.tmux.conf). Uses TPM for plugin management with Catppuccin-matching status bar colors.
Prefix
C-a
Mouse
Enabled
History limit
10000
Pane navigation
prefix + h/j/k/l (vim-style)
Window navigation
prefix + i/u (next/prev), prefix + p (last)
tmux-resurrect
Save and restore sessions across restarts
tmux-yank
Copy to system clipboard from tmux
Kitty
Config at kitty/kitty.conf. Kitty is the primary terminal — configured for a clean, distraction-free experience with the full Catppuccin Frappé color scheme.
Font
JetBrainsMono Nerd Font Mono, 13pt
Theme
Catppuccin Frappé (themes/frappe.conf)
Window size
150c × 50c
Cursor
block
Option key
macos_option_as_alt yes
Switching themes: All four Catppuccin flavors are in kitty/themes/ — change the include line to latte.conf, macchiato.conf, or mocha.conf to switch.
Tmux
Config at configs/.tmux.conf (symlinked to ~/.tmux.conf). Uses TPM for plugin management with Catppuccin-matching status bar colors.
Prefix
C-a
Mouse
Enabled
History limit
10000
Pane navigation
prefix + h/j/k/l (vim-style)
Window navigation
prefix + i/u (next/prev), prefix + p (last)
tmux-resurrect
Save and restore sessions across restarts
tmux-yank
Copy to system clipboard from tmux
Git
Multi Account Setup
Personal and work profiles with separate SSH keys using includeIf and SSH host aliases — blog post
Gitsy
Terminal UI for Git — log, diff, branch, and stash views — gitsy.santhoshsiva.dev
Agent Skills
No title
A shared set of AI agent skills lives in configs/agent-skills/. link-dotfiles symlinks the same directory into both Claude Code (~/.claude/skills) and Antigravity CLI (~/.gemini/antigravity-cli/skills), so one edit updates both agents.
configs/agent-skills/
├── <skill-name>/
│ ├── SKILL.md Frontmatter (name, description) + instructions
│ └── references/ Optional supporting docs loaded on demand
└── ...add-logs
Add log statements in the SAN_SIVA logging format
bash-scripts
Write bash scripts in the gitsy style — sourced utils, set_flags parsing, etc.
branch-name
Suggest a git branch name for a Jira ticket
commit-and-push
Suggest commit messages, pick one, then commit and push
commit-message
Suggest a Conventional Commits message for the staged changes
create-markdown
Write markdown following a consistent style guide, including blogkit-md rules
create-worktree
Create a git worktree using g-wa
document-progress
Record task progress into ~/Work/TASKS
gitsy
Perform Git operations through the gitsy CLI
mermaid-wizard
Produce syntactically correct Mermaid diagrams for code, architecture, and flows
Private skills
Work-specific skills live in a separate private repository, not here. link-dotfiles reads their location from WORK_AGENT_SKILLS_DIR — set in the untracked ~/.zshrc — and symlinks each one into configs/agent-skills/, which is gitignored. If the variable is unset, the step is skipped.
# ~/.zshrc
setup_env_vars() {
export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
}Invoking skills: User-invocable skills can be run directly as slash commands, e.g. /commit-and-push. Agents also pick them up automatically when a request matches the skill's description.
No title
A shared set of AI agent skills lives in configs/agent-skills/. link-dotfiles symlinks the same directory into both Claude Code (~/.claude/skills) and Antigravity CLI (~/.gemini/antigravity-cli/skills), so one edit updates both agents.
1configs/agent-skills/
2├── <skill-name>/
3│ ├── SKILL.md Frontmatter (name, description) + instructions
4│ └── references/ Optional supporting docs loaded on demand
5└── ...add-logs
Add log statements in the SAN_SIVA logging format
bash-scripts
Write bash scripts in the gitsy style — sourced utils, set_flags parsing, etc.
branch-name
Suggest a git branch name for a Jira ticket
commit-and-push
Suggest commit messages, pick one, then commit and push
commit-message
Suggest a Conventional Commits message for the staged changes
create-markdown
Write markdown following a consistent style guide, including blogkit-md rules
create-worktree
Create a git worktree using g-wa
document-progress
Record task progress into ~/Work/TASKS
gitsy
Perform Git operations through the gitsy CLI
mermaid-wizard
Produce syntactically correct Mermaid diagrams for code, architecture, and flows
1configs/agent-skills/
2├── <skill-name>/
3│ ├── SKILL.md Frontmatter (name, description) + instructions
4│ └── references/ Optional supporting docs loaded on demand
5└── ...Private skills
Work-specific skills live in a separate private repository, not here. link-dotfiles reads their location from WORK_AGENT_SKILLS_DIR — set in the untracked ~/.zshrc — and symlinks each one into configs/agent-skills/, which is gitignored. If the variable is unset, the step is skipped.
1# ~/.zshrc
2setup_env_vars() {
3 export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
4}Invoking skills: User-invocable skills can be run directly as slash commands, e.g. /commit-and-push. Agents also pick them up automatically when a request matches the skill's description.
1# ~/.zshrc
2setup_env_vars() {
3 export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
4}Utility Scripts
All scripts in bin/ are on PATH and share a common utility library at bin/utils.sh.
wireless-adb <ip>
Android
Connect to a device over WiFi
adb-install <apk>
Android
Install APK to connected device
adb-reverse <port>
Android
Reverse a port (e.g. Metro bundler)
adb-uninstall <package>
Android
Uninstall a package by name
kill-port <port>
Dev
Kill the process listening on a given port
setup-ssh-agent
Dev
Load SSH key into the running agent
pwdc
Dev
Print current working directory (clean output)
reinstall-claude
Dev
Reinstall Claude Code CLI tool
reinstall-npm-pkg <pkg> [version]
Dev
Reinstall/upgrade any global npm package
Assets
The assets/ directory contains static resources:
screenshot.png
Environment screenshot for README
catppuccin/
Catppuccin syntax highlight theme files for Zsh
JetBrainsMonoPatched.zip
Patched JetBrainsMono with Nerd Font glyphs
About
Author: Santhosh Siva GitHub: github.com/san-siva
No title
A comprehensive macOS development environment managed as a git repository cloned directly to ~/.config. Because all XDG-compliant tools look in ~/.config by default, every config file is automatically in the right place with no extra symlinking needed.
The setup includes a full Neovim IDE configuration, Kitty terminal, Zsh shell config, global Git settings, ESLint / Prettier / TypeScript configs, and a collection of utility shell scripts for daily development.
Theme: The entire environment uses the Catppuccin Frappé color palette — Neovim, Kitty, and all terminal tools are configured to match.
Directory Structure
All configuration lives under ~/.config:
nvim/
Neovim configuration (init.lua + Lua modules)
kitty/
Kitty terminal — fonts, theme, window settings
fish/
Fish shell — minimal PATH additions
configs/
Home-dir dotfiles (.zshrc, .gitconfig, .tmux.conf, etc.)
bin/
Shell utility scripts (android/, dev/, utils.sh)
git/
Global git config and gitignore
assets/
Screenshots, patched fonts, theme files
XDG compliant: Cloning to ~/.config means tools like Neovim, Kitty, Fish, and gh pick up their configs automatically without any manual linking.
Requirements
neovim 0.10+
Editor (required for all nvim features)
node 18+ / nvm
JavaScript tooling, LSP servers
git
Version control
kitty
Terminal emulator
zsh
Primary shell
brew
macOS package manager — installed by setup-environment if missing
JetBrainsMono Nerd Font
Font (all terminals + Neovim)
Installation
Clone the repository directly to ~/.config:
# Clone directly to ~/.config (XDG base dir)
git clone https://github.com/san-siva/dotfiles ~/.config
# If ~/.config already exists
cd ~/.config
git init
git remote add origin https://github.com/san-siva/dotfiles
git pull origin mainThree setup scripts handle the full environment bootstrap. Run them in order, or use setup-environment to run all three at once.
setup-environment
The top-level bootstrapper. Installs Homebrew first if it is missing and puts it on PATH for the rest of the run, then installs all system tools and delegates to install-global-deps and link-dotfiles.
~/.config/bin/dev/setup/setup-environmentLanguages
python3, node (via nvm), ruby, go, lua, rust
Java
openjdk, openjdk@21, ant, maven, jdtls, google-java-format
Editor / Terminal
neovim, kitty, tmux + TPM
Shell
oh-my-zsh, powerlevel10k, zsh-history-substring-search
CLI tools
bash 5, ripgrep, fzf, fd, zoxide, jq, yq, gh, fish, deno, wget, tree, fastfetch
Formatters
black (Python), sqlfluff, shfmt (via Go), stylua (via Cargo)
install-global-deps
Installs all global npm packages, symlinks ESLint configs, and enables Corepack. Safe to re-run.
~/.config/bin/dev/setup/install-global-depsESLint core
eslint, eslint_d, jiti, @eslint/js, @eslint/eslintrc, @eslint/compat
ESLint plugins
eslint-plugin-react, -react-hooks, -jest, -import, -redux-saga, -unicorn, simple-import-sort, and more
TypeScript
typescript, typescript-eslint, @typescript-eslint/eslint-plugin, @typescript-eslint/parser
Prettier
prettier, eslint-config-prettier
Testing
jest, markdownlint-cli2
Tools
@san-siva/gitsy, @anthropic-ai/claude-code, local-ssl-proxy, neovim (npm provider)
ESLint configs
Symlinks eslint.config.ts and eslint-utilities.ts into the npm global prefix
link-dotfiles
Creates symlinks from ~/.config/configs/ into the home directory using ln -sfn, so re-running it is safe: existing files and symlinks at each target are replaced in place. It also links private agent skills from WORK_AGENT_SKILLS_DIR when that variable is set — see Private skills.
~/.config/bin/dev/setup/link-dotfilesconfigs/.tmux.conf
~/.tmux.conf
configs/.prettierrc.json
~/.prettierrc.json
configs/.eslintrc.json
~/.eslintrc.json
configs/.p10k.zsh
~/.p10k.zsh
configs/.gitconfig
~/.gitconfig
configs/.gitconfig__wrk
~/.gitconfig__wrk
configs/ssh_config
~/.ssh/config
configs/eclipse-java-google-style.xml
~/.local/share/eclipse/
configs/lombok.jar
~/.local/share/eclipse/
configs/agent-skills
~/.claude/skills
configs/agent-skills
~/.gemini/antigravity-cli/skills
$WORK_AGENT_SKILLS_DIR/*
configs/agent-skills/<skill>
configs/antigravity-keybindings.json
~/.gemini/antigravity-cli/keybindings.json
Existing files and symlinks at the target paths are replaced. A real directory at a target is moved aside to <target>.bak-<timestamp> rather than deleted — review and remove these backups once you have checked their contents.
Create the local zshrc
link-dotfiles doesn't create ~/.zshrc — it's a machine-local file that holds secrets (see Shell). Create it once so new shells load the tracked config:
echo 'source ~/.config/configs/.zshrc' > ~/.zshrcNo title
Clone the repository directly to ~/.config:
1# Clone directly to ~/.config (XDG base dir)
2git clone https://github.com/san-siva/dotfiles ~/.config
3
4# If ~/.config already exists
5cd ~/.config
6git init
7git remote add origin https://github.com/san-siva/dotfiles
8git pull origin mainThree setup scripts handle the full environment bootstrap. Run them in order, or use setup-environment to run all three at once.
1# Clone directly to ~/.config (XDG base dir)
2git clone https://github.com/san-siva/dotfiles ~/.config
3
4# If ~/.config already exists
5cd ~/.config
6git init
7git remote add origin https://github.com/san-siva/dotfiles
8git pull origin mainsetup-environment
The top-level bootstrapper. Installs Homebrew first if it is missing and puts it on PATH for the rest of the run, then installs all system tools and delegates to install-global-deps and link-dotfiles.
1~/.config/bin/dev/setup/setup-environmentLanguages
python3, node (via nvm), ruby, go, lua, rust
Java
openjdk, openjdk@21, ant, maven, jdtls, google-java-format
Editor / Terminal
neovim, kitty, tmux + TPM
Shell
oh-my-zsh, powerlevel10k, zsh-history-substring-search
CLI tools
bash 5, ripgrep, fzf, fd, zoxide, jq, yq, gh, fish, deno, wget, tree, fastfetch
Formatters
black (Python), sqlfluff, shfmt (via Go), stylua (via Cargo)
1~/.config/bin/dev/setup/setup-environmentinstall-global-deps
Installs all global npm packages, symlinks ESLint configs, and enables Corepack. Safe to re-run.
1~/.config/bin/dev/setup/install-global-depsESLint core
eslint, eslint_d, jiti, @eslint/js, @eslint/eslintrc, @eslint/compat
ESLint plugins
eslint-plugin-react, -react-hooks, -jest, -import, -redux-saga, -unicorn, simple-import-sort, and more
TypeScript
typescript, typescript-eslint, @typescript-eslint/eslint-plugin, @typescript-eslint/parser
Prettier
prettier, eslint-config-prettier
Testing
jest, markdownlint-cli2
Tools
@san-siva/gitsy, @anthropic-ai/claude-code, local-ssl-proxy, neovim (npm provider)
ESLint configs
Symlinks eslint.config.ts and eslint-utilities.ts into the npm global prefix
1~/.config/bin/dev/setup/install-global-depslink-dotfiles
Creates symlinks from ~/.config/configs/ into the home directory using ln -sfn, so re-running it is safe: existing files and symlinks at each target are replaced in place. It also links private agent skills from WORK_AGENT_SKILLS_DIR when that variable is set — see Private skills.
1~/.config/bin/dev/setup/link-dotfilesconfigs/.tmux.conf
~/.tmux.conf
configs/.prettierrc.json
~/.prettierrc.json
configs/.eslintrc.json
~/.eslintrc.json
configs/.p10k.zsh
~/.p10k.zsh
configs/.gitconfig
~/.gitconfig
configs/.gitconfig__wrk
~/.gitconfig__wrk
configs/ssh_config
~/.ssh/config
configs/eclipse-java-google-style.xml
~/.local/share/eclipse/
configs/lombok.jar
~/.local/share/eclipse/
configs/agent-skills
~/.claude/skills
configs/agent-skills
~/.gemini/antigravity-cli/skills
$WORK_AGENT_SKILLS_DIR/*
configs/agent-skills/<skill>
configs/antigravity-keybindings.json
~/.gemini/antigravity-cli/keybindings.json
Existing files and symlinks at the target paths are replaced. A real directory at a target is moved aside to <target>.bak-<timestamp> rather than deleted — review and remove these backups once you have checked their contents.
1~/.config/bin/dev/setup/link-dotfilesCreate the local zshrc
link-dotfiles doesn't create ~/.zshrc — it's a machine-local file that holds secrets (see Shell). Create it once so new shells load the tracked config:
1echo 'source ~/.config/configs/.zshrc' > ~/.zshrc1echo 'source ~/.config/configs/.zshrc' > ~/.zshrcNeovim
A full IDE experience built in Lua using lazy.nvim. Entry point is nvim/init.lua; leader key is ,.
nvim/
├── init.lua Entry point — options, loads bindings & plugins
├── lua/
│ ├── bindings/
│ │ ├── mappings.lua Key mappings
│ │ └── autocmd.lua Autocommands
│ ├── core/
│ │ └── large-files.lua Large file detection & feature toggling
│ ├── plugins/
│ │ ├── init.lua Plugin list (lazy.nvim)
│ │ ├── lspconfig.lua LSP server configs
│ │ ├── conform.lua Format-on-save setup
│ │ └── ...
│ ├── shared_ftplugins/
│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
│ └── utils/
│ ├── large-file-check.lua
│ └── wildignores.lua
└── ftplugin/
├── typescript.lua
├── javascript.lua
├── typescriptreact.lua
├── javascriptreact.lua
├── md.lua
├── python.lua
└── java.luaSet NVIM_NPM=1 to open Neovim with all plugins disabled — useful when invoked inside Node.js scripts or tooling where startup time matters.
Key Bindings
<C-c>
Escape / cancel
<Esc>
Clear search highlight
<leader>tl / <leader>th
Next / prev tab
<leader>tL / <leader>tH
New / close tab
<leader>1–9
Jump to tab by number
<leader>bb
Alternate buffer
<leader>p
Copy relative path + line number
<leader>P
Copy absolute path
<leader>l
Copy line number
<leader>e / <leader>E
Float diagnostic / loclist
[e / ]e
Next / prev diagnostic
<leader>N / <leader>n
Toggle relative numbers
<leader>S
Reload vimrc
<leader>M
Delete all marks
<leader>ff
Telescope: find files
<leader>fg
Telescope: live grep
<leader>fb
Telescope: open buffers
<leader>fd
Telescope: diagnostics
<leader>tt
Toggle file tree
<leader>gs
Git status
<leader>gc
Git commit
<leader>gh / <leader>gl
Diffget ours / theirs
<leader>cJ / <leader>cK
Open / close all folds
<C-o>
Supermaven: accept suggestion
<C-y>
Supermaven: accept word
<C-r>
Supermaven: dismiss
Autocommands
TextYankPost
Flash-highlight yanked text
BufReadPre
Detect large files; set hash-based undo path for long file paths
BufReadPost
Disable swapfile, undofile, syntax, synmaxcol, treesitter for large files
BufWinEnter
Disable folding, cursorline, spell for large files
BufReadPre / BufNewFile
Read prettier tabWidth from config and set local indentation for JS/TS files
Plugins
catppuccin/nvim
Colorscheme — Frappé flavor
nvim-treesitter
Syntax highlighting + text objects
nvim-lspconfig + mason
LSP server management
conform.nvim
Format on save
nvim-cmp + luasnip
Completion — LSP, snippets, path, dictionary
telescope.nvim
Fuzzy finder — files, grep, buffers, diagnostics
nvim-tree.lua
File explorer sidebar
gitsigns.nvim
Git diff signs in gutter
vim-fugitive
Git commands inside Neovim
supermaven-nvim
AI inline completion
nvim-ufo
Code folding via LSP / treesitter
lualine.nvim
Status line + buffer tabline
indent-blankline.nvim
Indent guides
nvim-colorizer.lua
Hex/CSS color preview
markdown-preview.nvim
Live browser preview with Mermaid
nvim-jdtls
Java LSP (Eclipse JDT)
todo-comments.nvim
TODO / FIXME highlighting
Comment.nvim
Comment toggling
fidget.nvim
LSP progress spinner
Language Support
TypeScript / JavaScript
ts_ls
prettier
ESLint diagnostics
eslint
—
Go
gopls
goimports + gofmt
Python
basedpyright
isort + black
Lua
lua_ls
stylua
Bash
bashls
shfmt
TailwindCSS
tailwindcss
—
Java
nvim-jdtls
google-java-format
CSS / SCSS / HTML / JSON / Markdown
—
prettier
SQL
—
sqlfmt
XML
—
xmllint
YAML
—
yamllint
Monorepo TypeScript: ts_ls walks up the directory tree to find the topmost directory containing both tsconfig.json and node_modules — works correctly in monorepos without per-project config.
No title
A full IDE experience built in Lua using lazy.nvim. Entry point is nvim/init.lua; leader key is ,.
1nvim/
2├── init.lua Entry point — options, loads bindings & plugins
3├── lua/
4│ ├── bindings/
5│ │ ├── mappings.lua Key mappings
6│ │ └── autocmd.lua Autocommands
7│ ├── core/
8│ │ └── large-files.lua Large file detection & feature toggling
9│ ├── plugins/
10│ │ ├── init.lua Plugin list (lazy.nvim)
11│ │ ├── lspconfig.lua LSP server configs
12│ │ ├── conform.lua Format-on-save setup
13│ │ └── ...
14│ ├── shared_ftplugins/
15│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
16│ └── utils/
17│ ├── large-file-check.lua
18│ └── wildignores.lua
19└── ftplugin/
20 ├── typescript.lua
21 ├── javascript.lua
22 ├── typescriptreact.lua
23 ├── javascriptreact.lua
24 ├── md.lua
25 ├── python.lua
26 └── java.luaSet NVIM_NPM=1 to open Neovim with all plugins disabled — useful when invoked inside Node.js scripts or tooling where startup time matters.
1nvim/
2├── init.lua Entry point — options, loads bindings & plugins
3├── lua/
4│ ├── bindings/
5│ │ ├── mappings.lua Key mappings
6│ │ └── autocmd.lua Autocommands
7│ ├── core/
8│ │ └── large-files.lua Large file detection & feature toggling
9│ ├── plugins/
10│ │ ├── init.lua Plugin list (lazy.nvim)
11│ │ ├── lspconfig.lua LSP server configs
12│ │ ├── conform.lua Format-on-save setup
13│ │ └── ...
14│ ├── shared_ftplugins/
15│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
16│ └── utils/
17│ ├── large-file-check.lua
18│ └── wildignores.lua
19└── ftplugin/
20 ├── typescript.lua
21 ├── javascript.lua
22 ├── typescriptreact.lua
23 ├── javascriptreact.lua
24 ├── md.lua
25 ├── python.lua
26 └── java.luaKey Bindings
<C-c>
Escape / cancel
<Esc>
Clear search highlight
<leader>tl / <leader>th
Next / prev tab
<leader>tL / <leader>tH
New / close tab
<leader>1–9
Jump to tab by number
<leader>bb
Alternate buffer
<leader>p
Copy relative path + line number
<leader>P
Copy absolute path
<leader>l
Copy line number
<leader>e / <leader>E
Float diagnostic / loclist
[e / ]e
Next / prev diagnostic
<leader>N / <leader>n
Toggle relative numbers
<leader>S
Reload vimrc
<leader>M
Delete all marks
<leader>ff
Telescope: find files
<leader>fg
Telescope: live grep
<leader>fb
Telescope: open buffers
<leader>fd
Telescope: diagnostics
<leader>tt
Toggle file tree
<leader>gs
Git status
<leader>gc
Git commit
<leader>gh / <leader>gl
Diffget ours / theirs
<leader>cJ / <leader>cK
Open / close all folds
<C-o>
Supermaven: accept suggestion
<C-y>
Supermaven: accept word
<C-r>
Supermaven: dismiss
Autocommands
TextYankPost
Flash-highlight yanked text
BufReadPre
Detect large files; set hash-based undo path for long file paths
BufReadPost
Disable swapfile, undofile, syntax, synmaxcol, treesitter for large files
BufWinEnter
Disable folding, cursorline, spell for large files
BufReadPre / BufNewFile
Read prettier tabWidth from config and set local indentation for JS/TS files
Plugins
catppuccin/nvim
Colorscheme — Frappé flavor
nvim-treesitter
Syntax highlighting + text objects
nvim-lspconfig + mason
LSP server management
conform.nvim
Format on save
nvim-cmp + luasnip
Completion — LSP, snippets, path, dictionary
telescope.nvim
Fuzzy finder — files, grep, buffers, diagnostics
nvim-tree.lua
File explorer sidebar
gitsigns.nvim
Git diff signs in gutter
vim-fugitive
Git commands inside Neovim
supermaven-nvim
AI inline completion
nvim-ufo
Code folding via LSP / treesitter
lualine.nvim
Status line + buffer tabline
indent-blankline.nvim
Indent guides
nvim-colorizer.lua
Hex/CSS color preview
markdown-preview.nvim
Live browser preview with Mermaid
nvim-jdtls
Java LSP (Eclipse JDT)
todo-comments.nvim
TODO / FIXME highlighting
Comment.nvim
Comment toggling
fidget.nvim
LSP progress spinner
Language Support
TypeScript / JavaScript
ts_ls
prettier
ESLint diagnostics
eslint
—
Go
gopls
goimports + gofmt
Python
basedpyright
isort + black
Lua
lua_ls
stylua
Bash
bashls
shfmt
TailwindCSS
tailwindcss
—
Java
nvim-jdtls
google-java-format
CSS / SCSS / HTML / JSON / Markdown
—
prettier
SQL
—
sqlfmt
XML
—
xmllint
YAML
—
yamllint
Monorepo TypeScript: ts_ls walks up the directory tree to find the topmost directory containing both tsconfig.json and node_modules — works correctly in monorepos without per-project config.
Shell
The Zsh config is split into three layers so the tracked parts stay portable and secrets never touch the repo.
Rendering diagram...
~/.zshrc
No
Entry point plus machine-local setup_* functions for secrets and tooling
configs/.zshrc
Yes
PATH, language toolchains, integrations, keybindings, aliases
configs/.zshrc_pre
Yes
Catppuccin syntax highlighting, Powerlevel10k instant prompt, oh-my-zsh load
Machine-local entry point
~/.zshrc isn't symlinked. It's a plain file that sources the tracked config, then follows the same pattern — one setup_* function per concern and a main_local() that calls them — for anything that must stay off GitHub:
# ~/.zshrc
source ~/.config/configs/.zshrc
setup_env_vars() {
# Secrets — not tracked in any repo
export JIRA_API_TOKEN="..."
export FIGMA_API_TOKEN="..."
}
setup_local_paths() {
export PATH="$HOME/.local/some-tool/bin:$PATH"
}
setup_local_aliases() {
alias work='cd ~/Work'
}
main_local() {
setup_env_vars
setup_local_paths
setup_local_aliases
}
main_localAvoid name clashes: The tracked config already defines main and setup_aliases, so local functions use distinct names such as main_local and setup_local_aliases.
Keep secrets in ~/.zshrc only. Everything under ~/.config is pushed to a public repository.
One function per concern
Instead of one long script, configs/.zshrc groups each concern into its own setup_* function, and a single main() calls them in order. Reordering, disabling, or debugging a step is a one-line change:
# configs/.zshrc
main() {
setup_environment_variables
setup_homebrew_paths
setup_go_environment
setup_rust_environment
setup_ruby_environment
setup_python_environment
setup_java_environment
setup_custom_bins
setup_node_environment
setup_shell_integrations
setup_keybindings
setup_ssh_agent
setup_aliases
}
mainsetup_environment_variables
TERM, EDITOR, VISUAL
setup_homebrew_paths
Homebrew bin / sbin and Rancher Desktop
setup_go_environment
GOPATH and ~/go/bin
setup_rust_environment
~/.cargo/bin
setup_ruby_environment
Homebrew Ruby, compiler flags, user gem bin
setup_python_environment
Python user-base bin
setup_java_environment
OpenJDK, Zulu 8, JAVA_HOME
setup_custom_bins
~/.local/bin, ~/bin, and the bin/ script folders in this repo
setup_node_environment
nvm, Yarn global bin, Zscaler CA for Node tooling
setup_shell_integrations
zoxide — runs after PATH is complete
setup_keybindings
^n / ^p completion, ^j / ^k history substring search
setup_ssh_agent
Loads keys via bin/dev/setup-ssh-agent
setup_aliases
sed → gsed, gh copilot shortcuts, navigation aliases
Safe PATH handling
PATH is built through two guarded helpers. A directory is only added if it exists, and typeset -aU path keeps entries unique, so re-sourcing the file never produces duplicates. The same config works on a machine that's missing some toolchains:
# configs/.zshrc
typeset -aU path
prepend_path() {
if [ -d "$1" ]; then
path=("$1" $path)
fi
}
append_path() {
if [ -d "$1" ]; then
path=($path "$1")
fi
}No title
The Zsh config is split into three layers so the tracked parts stay portable and secrets never touch the repo.
Rendering diagram...
~/.zshrc
No
Entry point plus machine-local setup_* functions for secrets and tooling
configs/.zshrc
Yes
PATH, language toolchains, integrations, keybindings, aliases
configs/.zshrc_pre
Yes
Catppuccin syntax highlighting, Powerlevel10k instant prompt, oh-my-zsh load
Rendering diagram...
Machine-local entry point
~/.zshrc isn't symlinked. It's a plain file that sources the tracked config, then follows the same pattern — one setup_* function per concern and a main_local() that calls them — for anything that must stay off GitHub:
1# ~/.zshrc
2source ~/.config/configs/.zshrc
3
4setup_env_vars() {
5 # Secrets — not tracked in any repo
6 export JIRA_API_TOKEN="..."
7 export FIGMA_API_TOKEN="..."
8}
9
10setup_local_paths() {
11 export PATH="$HOME/.local/some-tool/bin:$PATH"
12}
13
14setup_local_aliases() {
15 alias work='cd ~/Work'
16}
17
18main_local() {
19 setup_env_vars
20 setup_local_paths
21 setup_local_aliases
22}
23
24main_localAvoid name clashes: The tracked config already defines main and setup_aliases, so local functions use distinct names such as main_local and setup_local_aliases.
Keep secrets in ~/.zshrc only. Everything under ~/.config is pushed to a public repository.
1# ~/.zshrc
2source ~/.config/configs/.zshrc
3
4setup_env_vars() {
5 # Secrets — not tracked in any repo
6 export JIRA_API_TOKEN="..."
7 export FIGMA_API_TOKEN="..."
8}
9
10setup_local_paths() {
11 export PATH="$HOME/.local/some-tool/bin:$PATH"
12}
13
14setup_local_aliases() {
15 alias work='cd ~/Work'
16}
17
18main_local() {
19 setup_env_vars
20 setup_local_paths
21 setup_local_aliases
22}
23
24main_localOne function per concern
Instead of one long script, configs/.zshrc groups each concern into its own setup_* function, and a single main() calls them in order. Reordering, disabling, or debugging a step is a one-line change:
1# configs/.zshrc
2main() {
3 setup_environment_variables
4 setup_homebrew_paths
5 setup_go_environment
6 setup_rust_environment
7 setup_ruby_environment
8 setup_python_environment
9 setup_java_environment
10 setup_custom_bins
11 setup_node_environment
12 setup_shell_integrations
13 setup_keybindings
14 setup_ssh_agent
15 setup_aliases
16}
17
18mainsetup_environment_variables
TERM, EDITOR, VISUAL
setup_homebrew_paths
Homebrew bin / sbin and Rancher Desktop
setup_go_environment
GOPATH and ~/go/bin
setup_rust_environment
~/.cargo/bin
setup_ruby_environment
Homebrew Ruby, compiler flags, user gem bin
setup_python_environment
Python user-base bin
setup_java_environment
OpenJDK, Zulu 8, JAVA_HOME
setup_custom_bins
~/.local/bin, ~/bin, and the bin/ script folders in this repo
setup_node_environment
nvm, Yarn global bin, Zscaler CA for Node tooling
setup_shell_integrations
zoxide — runs after PATH is complete
setup_keybindings
^n / ^p completion, ^j / ^k history substring search
setup_ssh_agent
Loads keys via bin/dev/setup-ssh-agent
setup_aliases
sed → gsed, gh copilot shortcuts, navigation aliases
1# configs/.zshrc
2main() {
3 setup_environment_variables
4 setup_homebrew_paths
5 setup_go_environment
6 setup_rust_environment
7 setup_ruby_environment
8 setup_python_environment
9 setup_java_environment
10 setup_custom_bins
11 setup_node_environment
12 setup_shell_integrations
13 setup_keybindings
14 setup_ssh_agent
15 setup_aliases
16}
17
18mainSafe PATH handling
PATH is built through two guarded helpers. A directory is only added if it exists, and typeset -aU path keeps entries unique, so re-sourcing the file never produces duplicates. The same config works on a machine that's missing some toolchains:
1# configs/.zshrc
2typeset -aU path
3
4prepend_path() {
5 if [ -d "$1" ]; then
6 path=("$1" $path)
7 fi
8}
9
10append_path() {
11 if [ -d "$1" ]; then
12 path=($path "$1")
13 fi
14}1# configs/.zshrc
2typeset -aU path
3
4prepend_path() {
5 if [ -d "$1" ]; then
6 path=("$1" $path)
7 fi
8}
9
10append_path() {
11 if [ -d "$1" ]; then
12 path=($path "$1")
13 fi
14}Terminal
Kitty
Config at kitty/kitty.conf. Kitty is the primary terminal — configured for a clean, distraction-free experience with the full Catppuccin Frappé color scheme.
Font
JetBrainsMono Nerd Font Mono, 13pt
Theme
Catppuccin Frappé (themes/frappe.conf)
Window size
150c × 50c
Cursor
block
Option key
macos_option_as_alt yes
Switching themes: All four Catppuccin flavors are in kitty/themes/ — change the include line to latte.conf, macchiato.conf, or mocha.conf to switch.
Tmux
Config at configs/.tmux.conf (symlinked to ~/.tmux.conf). Uses TPM for plugin management with Catppuccin-matching status bar colors.
Prefix
C-a
Mouse
Enabled
History limit
10000
Pane navigation
prefix + h/j/k/l (vim-style)
Window navigation
prefix + i/u (next/prev), prefix + p (last)
tmux-resurrect
Save and restore sessions across restarts
tmux-yank
Copy to system clipboard from tmux
Kitty
Config at kitty/kitty.conf. Kitty is the primary terminal — configured for a clean, distraction-free experience with the full Catppuccin Frappé color scheme.
Font
JetBrainsMono Nerd Font Mono, 13pt
Theme
Catppuccin Frappé (themes/frappe.conf)
Window size
150c × 50c
Cursor
block
Option key
macos_option_as_alt yes
Switching themes: All four Catppuccin flavors are in kitty/themes/ — change the include line to latte.conf, macchiato.conf, or mocha.conf to switch.
Tmux
Config at configs/.tmux.conf (symlinked to ~/.tmux.conf). Uses TPM for plugin management with Catppuccin-matching status bar colors.
Prefix
C-a
Mouse
Enabled
History limit
10000
Pane navigation
prefix + h/j/k/l (vim-style)
Window navigation
prefix + i/u (next/prev), prefix + p (last)
tmux-resurrect
Save and restore sessions across restarts
tmux-yank
Copy to system clipboard from tmux
Git
Multi Account Setup
Personal and work profiles with separate SSH keys using includeIf and SSH host aliases — blog post
Gitsy
Terminal UI for Git — log, diff, branch, and stash views — gitsy.santhoshsiva.dev
Agent Skills
No title
A shared set of AI agent skills lives in configs/agent-skills/. link-dotfiles symlinks the same directory into both Claude Code (~/.claude/skills) and Antigravity CLI (~/.gemini/antigravity-cli/skills), so one edit updates both agents.
configs/agent-skills/
├── <skill-name>/
│ ├── SKILL.md Frontmatter (name, description) + instructions
│ └── references/ Optional supporting docs loaded on demand
└── ...add-logs
Add log statements in the SAN_SIVA logging format
bash-scripts
Write bash scripts in the gitsy style — sourced utils, set_flags parsing, etc.
branch-name
Suggest a git branch name for a Jira ticket
commit-and-push
Suggest commit messages, pick one, then commit and push
commit-message
Suggest a Conventional Commits message for the staged changes
create-markdown
Write markdown following a consistent style guide, including blogkit-md rules
create-worktree
Create a git worktree using g-wa
document-progress
Record task progress into ~/Work/TASKS
gitsy
Perform Git operations through the gitsy CLI
mermaid-wizard
Produce syntactically correct Mermaid diagrams for code, architecture, and flows
Private skills
Work-specific skills live in a separate private repository, not here. link-dotfiles reads their location from WORK_AGENT_SKILLS_DIR — set in the untracked ~/.zshrc — and symlinks each one into configs/agent-skills/, which is gitignored. If the variable is unset, the step is skipped.
# ~/.zshrc
setup_env_vars() {
export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
}Invoking skills: User-invocable skills can be run directly as slash commands, e.g. /commit-and-push. Agents also pick them up automatically when a request matches the skill's description.
No title
A shared set of AI agent skills lives in configs/agent-skills/. link-dotfiles symlinks the same directory into both Claude Code (~/.claude/skills) and Antigravity CLI (~/.gemini/antigravity-cli/skills), so one edit updates both agents.
1configs/agent-skills/
2├── <skill-name>/
3│ ├── SKILL.md Frontmatter (name, description) + instructions
4│ └── references/ Optional supporting docs loaded on demand
5└── ...add-logs
Add log statements in the SAN_SIVA logging format
bash-scripts
Write bash scripts in the gitsy style — sourced utils, set_flags parsing, etc.
branch-name
Suggest a git branch name for a Jira ticket
commit-and-push
Suggest commit messages, pick one, then commit and push
commit-message
Suggest a Conventional Commits message for the staged changes
create-markdown
Write markdown following a consistent style guide, including blogkit-md rules
create-worktree
Create a git worktree using g-wa
document-progress
Record task progress into ~/Work/TASKS
gitsy
Perform Git operations through the gitsy CLI
mermaid-wizard
Produce syntactically correct Mermaid diagrams for code, architecture, and flows
1configs/agent-skills/
2├── <skill-name>/
3│ ├── SKILL.md Frontmatter (name, description) + instructions
4│ └── references/ Optional supporting docs loaded on demand
5└── ...Private skills
Work-specific skills live in a separate private repository, not here. link-dotfiles reads their location from WORK_AGENT_SKILLS_DIR — set in the untracked ~/.zshrc — and symlinks each one into configs/agent-skills/, which is gitignored. If the variable is unset, the step is skipped.
1# ~/.zshrc
2setup_env_vars() {
3 export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
4}Invoking skills: User-invocable skills can be run directly as slash commands, e.g. /commit-and-push. Agents also pick them up automatically when a request matches the skill's description.
1# ~/.zshrc
2setup_env_vars() {
3 export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
4}Utility Scripts
All scripts in bin/ are on PATH and share a common utility library at bin/utils.sh.
wireless-adb <ip>
Android
Connect to a device over WiFi
adb-install <apk>
Android
Install APK to connected device
adb-reverse <port>
Android
Reverse a port (e.g. Metro bundler)
adb-uninstall <package>
Android
Uninstall a package by name
kill-port <port>
Dev
Kill the process listening on a given port
setup-ssh-agent
Dev
Load SSH key into the running agent
pwdc
Dev
Print current working directory (clean output)
reinstall-claude
Dev
Reinstall Claude Code CLI tool
reinstall-npm-pkg <pkg> [version]
Dev
Reinstall/upgrade any global npm package
Assets
The assets/ directory contains static resources:
screenshot.png
Environment screenshot for README
catppuccin/
Catppuccin syntax highlight theme files for Zsh
JetBrainsMonoPatched.zip
Patched JetBrainsMono with Nerd Font glyphs
About
Author: Santhosh Siva GitHub: github.com/san-siva
No title
Clone the repository directly to ~/.config:
1# Clone directly to ~/.config (XDG base dir)
2git clone https://github.com/san-siva/dotfiles ~/.config
3
4# If ~/.config already exists
5cd ~/.config
6git init
7git remote add origin https://github.com/san-siva/dotfiles
8git pull origin mainThree setup scripts handle the full environment bootstrap. Run them in order, or use setup-environment to run all three at once.
1# Clone directly to ~/.config (XDG base dir)
2git clone https://github.com/san-siva/dotfiles ~/.config
3
4# If ~/.config already exists
5cd ~/.config
6git init
7git remote add origin https://github.com/san-siva/dotfiles
8git pull origin mainsetup-environment
The top-level bootstrapper. Installs Homebrew first if it is missing and puts it on PATH for the rest of the run, then installs all system tools and delegates to install-global-deps and link-dotfiles.
1~/.config/bin/dev/setup/setup-environmentLanguages
python3, node (via nvm), ruby, go, lua, rust
Java
openjdk, openjdk@21, ant, maven, jdtls, google-java-format
Editor / Terminal
neovim, kitty, tmux + TPM
Shell
oh-my-zsh, powerlevel10k, zsh-history-substring-search
CLI tools
bash 5, ripgrep, fzf, fd, zoxide, jq, yq, gh, fish, deno, wget, tree, fastfetch
Formatters
black (Python), sqlfluff, shfmt (via Go), stylua (via Cargo)
1~/.config/bin/dev/setup/setup-environmentinstall-global-deps
Installs all global npm packages, symlinks ESLint configs, and enables Corepack. Safe to re-run.
1~/.config/bin/dev/setup/install-global-depsESLint core
eslint, eslint_d, jiti, @eslint/js, @eslint/eslintrc, @eslint/compat
ESLint plugins
eslint-plugin-react, -react-hooks, -jest, -import, -redux-saga, -unicorn, simple-import-sort, and more
TypeScript
typescript, typescript-eslint, @typescript-eslint/eslint-plugin, @typescript-eslint/parser
Prettier
prettier, eslint-config-prettier
Testing
jest, markdownlint-cli2
Tools
@san-siva/gitsy, @anthropic-ai/claude-code, local-ssl-proxy, neovim (npm provider)
ESLint configs
Symlinks eslint.config.ts and eslint-utilities.ts into the npm global prefix
1~/.config/bin/dev/setup/install-global-depslink-dotfiles
Creates symlinks from ~/.config/configs/ into the home directory using ln -sfn, so re-running it is safe: existing files and symlinks at each target are replaced in place. It also links private agent skills from WORK_AGENT_SKILLS_DIR when that variable is set — see Private skills.
1~/.config/bin/dev/setup/link-dotfilesconfigs/.tmux.conf
~/.tmux.conf
configs/.prettierrc.json
~/.prettierrc.json
configs/.eslintrc.json
~/.eslintrc.json
configs/.p10k.zsh
~/.p10k.zsh
configs/.gitconfig
~/.gitconfig
configs/.gitconfig__wrk
~/.gitconfig__wrk
configs/ssh_config
~/.ssh/config
configs/eclipse-java-google-style.xml
~/.local/share/eclipse/
configs/lombok.jar
~/.local/share/eclipse/
configs/agent-skills
~/.claude/skills
configs/agent-skills
~/.gemini/antigravity-cli/skills
$WORK_AGENT_SKILLS_DIR/*
configs/agent-skills/<skill>
configs/antigravity-keybindings.json
~/.gemini/antigravity-cli/keybindings.json
Existing files and symlinks at the target paths are replaced. A real directory at a target is moved aside to <target>.bak-<timestamp> rather than deleted — review and remove these backups once you have checked their contents.
1~/.config/bin/dev/setup/link-dotfilesCreate the local zshrc
link-dotfiles doesn't create ~/.zshrc — it's a machine-local file that holds secrets (see Shell). Create it once so new shells load the tracked config:
1echo 'source ~/.config/configs/.zshrc' > ~/.zshrc1echo 'source ~/.config/configs/.zshrc' > ~/.zshrcNo title
A full IDE experience built in Lua using lazy.nvim. Entry point is nvim/init.lua; leader key is ,.
1nvim/
2├── init.lua Entry point — options, loads bindings & plugins
3├── lua/
4│ ├── bindings/
5│ │ ├── mappings.lua Key mappings
6│ │ └── autocmd.lua Autocommands
7│ ├── core/
8│ │ └── large-files.lua Large file detection & feature toggling
9│ ├── plugins/
10│ │ ├── init.lua Plugin list (lazy.nvim)
11│ │ ├── lspconfig.lua LSP server configs
12│ │ ├── conform.lua Format-on-save setup
13│ │ └── ...
14│ ├── shared_ftplugins/
15│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
16│ └── utils/
17│ ├── large-file-check.lua
18│ └── wildignores.lua
19└── ftplugin/
20 ├── typescript.lua
21 ├── javascript.lua
22 ├── typescriptreact.lua
23 ├── javascriptreact.lua
24 ├── md.lua
25 ├── python.lua
26 └── java.luaSet NVIM_NPM=1 to open Neovim with all plugins disabled — useful when invoked inside Node.js scripts or tooling where startup time matters.
1nvim/
2├── init.lua Entry point — options, loads bindings & plugins
3├── lua/
4│ ├── bindings/
5│ │ ├── mappings.lua Key mappings
6│ │ └── autocmd.lua Autocommands
7│ ├── core/
8│ │ └── large-files.lua Large file detection & feature toggling
9│ ├── plugins/
10│ │ ├── init.lua Plugin list (lazy.nvim)
11│ │ ├── lspconfig.lua LSP server configs
12│ │ ├── conform.lua Format-on-save setup
13│ │ └── ...
14│ ├── shared_ftplugins/
15│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
16│ └── utils/
17│ ├── large-file-check.lua
18│ └── wildignores.lua
19└── ftplugin/
20 ├── typescript.lua
21 ├── javascript.lua
22 ├── typescriptreact.lua
23 ├── javascriptreact.lua
24 ├── md.lua
25 ├── python.lua
26 └── java.luaKey Bindings
<C-c>
Escape / cancel
<Esc>
Clear search highlight
<leader>tl / <leader>th
Next / prev tab
<leader>tL / <leader>tH
New / close tab
<leader>1–9
Jump to tab by number
<leader>bb
Alternate buffer
<leader>p
Copy relative path + line number
<leader>P
Copy absolute path
<leader>l
Copy line number
<leader>e / <leader>E
Float diagnostic / loclist
[e / ]e
Next / prev diagnostic
<leader>N / <leader>n
Toggle relative numbers
<leader>S
Reload vimrc
<leader>M
Delete all marks
<leader>ff
Telescope: find files
<leader>fg
Telescope: live grep
<leader>fb
Telescope: open buffers
<leader>fd
Telescope: diagnostics
<leader>tt
Toggle file tree
<leader>gs
Git status
<leader>gc
Git commit
<leader>gh / <leader>gl
Diffget ours / theirs
<leader>cJ / <leader>cK
Open / close all folds
<C-o>
Supermaven: accept suggestion
<C-y>
Supermaven: accept word
<C-r>
Supermaven: dismiss
Autocommands
TextYankPost
Flash-highlight yanked text
BufReadPre
Detect large files; set hash-based undo path for long file paths
BufReadPost
Disable swapfile, undofile, syntax, synmaxcol, treesitter for large files
BufWinEnter
Disable folding, cursorline, spell for large files
BufReadPre / BufNewFile
Read prettier tabWidth from config and set local indentation for JS/TS files
Plugins
catppuccin/nvim
Colorscheme — Frappé flavor
nvim-treesitter
Syntax highlighting + text objects
nvim-lspconfig + mason
LSP server management
conform.nvim
Format on save
nvim-cmp + luasnip
Completion — LSP, snippets, path, dictionary
telescope.nvim
Fuzzy finder — files, grep, buffers, diagnostics
nvim-tree.lua
File explorer sidebar
gitsigns.nvim
Git diff signs in gutter
vim-fugitive
Git commands inside Neovim
supermaven-nvim
AI inline completion
nvim-ufo
Code folding via LSP / treesitter
lualine.nvim
Status line + buffer tabline
indent-blankline.nvim
Indent guides
nvim-colorizer.lua
Hex/CSS color preview
markdown-preview.nvim
Live browser preview with Mermaid
nvim-jdtls
Java LSP (Eclipse JDT)
todo-comments.nvim
TODO / FIXME highlighting
Comment.nvim
Comment toggling
fidget.nvim
LSP progress spinner
Language Support
TypeScript / JavaScript
ts_ls
prettier
ESLint diagnostics
eslint
—
Go
gopls
goimports + gofmt
Python
basedpyright
isort + black
Lua
lua_ls
stylua
Bash
bashls
shfmt
TailwindCSS
tailwindcss
—
Java
nvim-jdtls
google-java-format
CSS / SCSS / HTML / JSON / Markdown
—
prettier
SQL
—
sqlfmt
XML
—
xmllint
YAML
—
yamllint
Monorepo TypeScript: ts_ls walks up the directory tree to find the topmost directory containing both tsconfig.json and node_modules — works correctly in monorepos without per-project config.
No title
The Zsh config is split into three layers so the tracked parts stay portable and secrets never touch the repo.
Rendering diagram...
~/.zshrc
No
Entry point plus machine-local setup_* functions for secrets and tooling
configs/.zshrc
Yes
PATH, language toolchains, integrations, keybindings, aliases
configs/.zshrc_pre
Yes
Catppuccin syntax highlighting, Powerlevel10k instant prompt, oh-my-zsh load
Machine-local entry point
~/.zshrc isn't symlinked. It's a plain file that sources the tracked config, then follows the same pattern — one setup_* function per concern and a main_local() that calls them — for anything that must stay off GitHub:
1# ~/.zshrc
2source ~/.config/configs/.zshrc
3
4setup_env_vars() {
5 # Secrets — not tracked in any repo
6 export JIRA_API_TOKEN="..."
7 export FIGMA_API_TOKEN="..."
8}
9
10setup_local_paths() {
11 export PATH="$HOME/.local/some-tool/bin:$PATH"
12}
13
14setup_local_aliases() {
15 alias work='cd ~/Work'
16}
17
18main_local() {
19 setup_env_vars
20 setup_local_paths
21 setup_local_aliases
22}
23
24main_localAvoid name clashes: The tracked config already defines main and setup_aliases, so local functions use distinct names such as main_local and setup_local_aliases.
Keep secrets in ~/.zshrc only. Everything under ~/.config is pushed to a public repository.
1# ~/.zshrc
2source ~/.config/configs/.zshrc
3
4setup_env_vars() {
5 # Secrets — not tracked in any repo
6 export JIRA_API_TOKEN="..."
7 export FIGMA_API_TOKEN="..."
8}
9
10setup_local_paths() {
11 export PATH="$HOME/.local/some-tool/bin:$PATH"
12}
13
14setup_local_aliases() {
15 alias work='cd ~/Work'
16}
17
18main_local() {
19 setup_env_vars
20 setup_local_paths
21 setup_local_aliases
22}
23
24main_localOne function per concern
Instead of one long script, configs/.zshrc groups each concern into its own setup_* function, and a single main() calls them in order. Reordering, disabling, or debugging a step is a one-line change:
1# configs/.zshrc
2main() {
3 setup_environment_variables
4 setup_homebrew_paths
5 setup_go_environment
6 setup_rust_environment
7 setup_ruby_environment
8 setup_python_environment
9 setup_java_environment
10 setup_custom_bins
11 setup_node_environment
12 setup_shell_integrations
13 setup_keybindings
14 setup_ssh_agent
15 setup_aliases
16}
17
18mainsetup_environment_variables
TERM, EDITOR, VISUAL
setup_homebrew_paths
Homebrew bin / sbin and Rancher Desktop
setup_go_environment
GOPATH and ~/go/bin
setup_rust_environment
~/.cargo/bin
setup_ruby_environment
Homebrew Ruby, compiler flags, user gem bin
setup_python_environment
Python user-base bin
setup_java_environment
OpenJDK, Zulu 8, JAVA_HOME
setup_custom_bins
~/.local/bin, ~/bin, and the bin/ script folders in this repo
setup_node_environment
nvm, Yarn global bin, Zscaler CA for Node tooling
setup_shell_integrations
zoxide — runs after PATH is complete
setup_keybindings
^n / ^p completion, ^j / ^k history substring search
setup_ssh_agent
Loads keys via bin/dev/setup-ssh-agent
setup_aliases
sed → gsed, gh copilot shortcuts, navigation aliases
1# configs/.zshrc
2main() {
3 setup_environment_variables
4 setup_homebrew_paths
5 setup_go_environment
6 setup_rust_environment
7 setup_ruby_environment
8 setup_python_environment
9 setup_java_environment
10 setup_custom_bins
11 setup_node_environment
12 setup_shell_integrations
13 setup_keybindings
14 setup_ssh_agent
15 setup_aliases
16}
17
18mainSafe PATH handling
PATH is built through two guarded helpers. A directory is only added if it exists, and typeset -aU path keeps entries unique, so re-sourcing the file never produces duplicates. The same config works on a machine that's missing some toolchains:
1# configs/.zshrc
2typeset -aU path
3
4prepend_path() {
5 if [ -d "$1" ]; then
6 path=("$1" $path)
7 fi
8}
9
10append_path() {
11 if [ -d "$1" ]; then
12 path=($path "$1")
13 fi
14}1# configs/.zshrc
2typeset -aU path
3
4prepend_path() {
5 if [ -d "$1" ]; then
6 path=("$1" $path)
7 fi
8}
9
10append_path() {
11 if [ -d "$1" ]; then
12 path=($path "$1")
13 fi
14}1configs/agent-skills/
2├── <skill-name>/
3│ ├── SKILL.md Frontmatter (name, description) + instructions
4│ └── references/ Optional supporting docs loaded on demand
5└── ...1# ~/.zshrc
2setup_env_vars() {
3 export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
4}No title
Clone the repository directly to ~/.config:
1# Clone directly to ~/.config (XDG base dir)
2git clone https://github.com/san-siva/dotfiles ~/.config
3
4# If ~/.config already exists
5cd ~/.config
6git init
7git remote add origin https://github.com/san-siva/dotfiles
8git pull origin mainThree setup scripts handle the full environment bootstrap. Run them in order, or use setup-environment to run all three at once.
1# Clone directly to ~/.config (XDG base dir)
2git clone https://github.com/san-siva/dotfiles ~/.config
3
4# If ~/.config already exists
5cd ~/.config
6git init
7git remote add origin https://github.com/san-siva/dotfiles
8git pull origin mainsetup-environment
The top-level bootstrapper. Installs Homebrew first if it is missing and puts it on PATH for the rest of the run, then installs all system tools and delegates to install-global-deps and link-dotfiles.
1~/.config/bin/dev/setup/setup-environmentLanguages
python3, node (via nvm), ruby, go, lua, rust
Java
openjdk, openjdk@21, ant, maven, jdtls, google-java-format
Editor / Terminal
neovim, kitty, tmux + TPM
Shell
oh-my-zsh, powerlevel10k, zsh-history-substring-search
CLI tools
bash 5, ripgrep, fzf, fd, zoxide, jq, yq, gh, fish, deno, wget, tree, fastfetch
Formatters
black (Python), sqlfluff, shfmt (via Go), stylua (via Cargo)
1~/.config/bin/dev/setup/setup-environmentinstall-global-deps
Installs all global npm packages, symlinks ESLint configs, and enables Corepack. Safe to re-run.
1~/.config/bin/dev/setup/install-global-depsESLint core
eslint, eslint_d, jiti, @eslint/js, @eslint/eslintrc, @eslint/compat
ESLint plugins
eslint-plugin-react, -react-hooks, -jest, -import, -redux-saga, -unicorn, simple-import-sort, and more
TypeScript
typescript, typescript-eslint, @typescript-eslint/eslint-plugin, @typescript-eslint/parser
Prettier
prettier, eslint-config-prettier
Testing
jest, markdownlint-cli2
Tools
@san-siva/gitsy, @anthropic-ai/claude-code, local-ssl-proxy, neovim (npm provider)
ESLint configs
Symlinks eslint.config.ts and eslint-utilities.ts into the npm global prefix
1~/.config/bin/dev/setup/install-global-depslink-dotfiles
Creates symlinks from ~/.config/configs/ into the home directory using ln -sfn, so re-running it is safe: existing files and symlinks at each target are replaced in place. It also links private agent skills from WORK_AGENT_SKILLS_DIR when that variable is set — see Private skills.
1~/.config/bin/dev/setup/link-dotfilesconfigs/.tmux.conf
~/.tmux.conf
configs/.prettierrc.json
~/.prettierrc.json
configs/.eslintrc.json
~/.eslintrc.json
configs/.p10k.zsh
~/.p10k.zsh
configs/.gitconfig
~/.gitconfig
configs/.gitconfig__wrk
~/.gitconfig__wrk
configs/ssh_config
~/.ssh/config
configs/eclipse-java-google-style.xml
~/.local/share/eclipse/
configs/lombok.jar
~/.local/share/eclipse/
configs/agent-skills
~/.claude/skills
configs/agent-skills
~/.gemini/antigravity-cli/skills
$WORK_AGENT_SKILLS_DIR/*
configs/agent-skills/<skill>
configs/antigravity-keybindings.json
~/.gemini/antigravity-cli/keybindings.json
Existing files and symlinks at the target paths are replaced. A real directory at a target is moved aside to <target>.bak-<timestamp> rather than deleted — review and remove these backups once you have checked their contents.
1~/.config/bin/dev/setup/link-dotfilesCreate the local zshrc
link-dotfiles doesn't create ~/.zshrc — it's a machine-local file that holds secrets (see Shell). Create it once so new shells load the tracked config:
1echo 'source ~/.config/configs/.zshrc' > ~/.zshrc1echo 'source ~/.config/configs/.zshrc' > ~/.zshrcNo title
A full IDE experience built in Lua using lazy.nvim. Entry point is nvim/init.lua; leader key is ,.
1nvim/
2├── init.lua Entry point — options, loads bindings & plugins
3├── lua/
4│ ├── bindings/
5│ │ ├── mappings.lua Key mappings
6│ │ └── autocmd.lua Autocommands
7│ ├── core/
8│ │ └── large-files.lua Large file detection & feature toggling
9│ ├── plugins/
10│ │ ├── init.lua Plugin list (lazy.nvim)
11│ │ ├── lspconfig.lua LSP server configs
12│ │ ├── conform.lua Format-on-save setup
13│ │ └── ...
14│ ├── shared_ftplugins/
15│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
16│ └── utils/
17│ ├── large-file-check.lua
18│ └── wildignores.lua
19└── ftplugin/
20 ├── typescript.lua
21 ├── javascript.lua
22 ├── typescriptreact.lua
23 ├── javascriptreact.lua
24 ├── md.lua
25 ├── python.lua
26 └── java.luaSet NVIM_NPM=1 to open Neovim with all plugins disabled — useful when invoked inside Node.js scripts or tooling where startup time matters.
1nvim/
2├── init.lua Entry point — options, loads bindings & plugins
3├── lua/
4│ ├── bindings/
5│ │ ├── mappings.lua Key mappings
6│ │ └── autocmd.lua Autocommands
7│ ├── core/
8│ │ └── large-files.lua Large file detection & feature toggling
9│ ├── plugins/
10│ │ ├── init.lua Plugin list (lazy.nvim)
11│ │ ├── lspconfig.lua LSP server configs
12│ │ ├── conform.lua Format-on-save setup
13│ │ └── ...
14│ ├── shared_ftplugins/
15│ │ └── javascript.lua Shared JS/TS ftplugin (reads prettier tabWidth)
16│ └── utils/
17│ ├── large-file-check.lua
18│ └── wildignores.lua
19└── ftplugin/
20 ├── typescript.lua
21 ├── javascript.lua
22 ├── typescriptreact.lua
23 ├── javascriptreact.lua
24 ├── md.lua
25 ├── python.lua
26 └── java.luaKey Bindings
<C-c>
Escape / cancel
<Esc>
Clear search highlight
<leader>tl / <leader>th
Next / prev tab
<leader>tL / <leader>tH
New / close tab
<leader>1–9
Jump to tab by number
<leader>bb
Alternate buffer
<leader>p
Copy relative path + line number
<leader>P
Copy absolute path
<leader>l
Copy line number
<leader>e / <leader>E
Float diagnostic / loclist
[e / ]e
Next / prev diagnostic
<leader>N / <leader>n
Toggle relative numbers
<leader>S
Reload vimrc
<leader>M
Delete all marks
<leader>ff
Telescope: find files
<leader>fg
Telescope: live grep
<leader>fb
Telescope: open buffers
<leader>fd
Telescope: diagnostics
<leader>tt
Toggle file tree
<leader>gs
Git status
<leader>gc
Git commit
<leader>gh / <leader>gl
Diffget ours / theirs
<leader>cJ / <leader>cK
Open / close all folds
<C-o>
Supermaven: accept suggestion
<C-y>
Supermaven: accept word
<C-r>
Supermaven: dismiss
Autocommands
TextYankPost
Flash-highlight yanked text
BufReadPre
Detect large files; set hash-based undo path for long file paths
BufReadPost
Disable swapfile, undofile, syntax, synmaxcol, treesitter for large files
BufWinEnter
Disable folding, cursorline, spell for large files
BufReadPre / BufNewFile
Read prettier tabWidth from config and set local indentation for JS/TS files
Plugins
catppuccin/nvim
Colorscheme — Frappé flavor
nvim-treesitter
Syntax highlighting + text objects
nvim-lspconfig + mason
LSP server management
conform.nvim
Format on save
nvim-cmp + luasnip
Completion — LSP, snippets, path, dictionary
telescope.nvim
Fuzzy finder — files, grep, buffers, diagnostics
nvim-tree.lua
File explorer sidebar
gitsigns.nvim
Git diff signs in gutter
vim-fugitive
Git commands inside Neovim
supermaven-nvim
AI inline completion
nvim-ufo
Code folding via LSP / treesitter
lualine.nvim
Status line + buffer tabline
indent-blankline.nvim
Indent guides
nvim-colorizer.lua
Hex/CSS color preview
markdown-preview.nvim
Live browser preview with Mermaid
nvim-jdtls
Java LSP (Eclipse JDT)
todo-comments.nvim
TODO / FIXME highlighting
Comment.nvim
Comment toggling
fidget.nvim
LSP progress spinner
Language Support
TypeScript / JavaScript
ts_ls
prettier
ESLint diagnostics
eslint
—
Go
gopls
goimports + gofmt
Python
basedpyright
isort + black
Lua
lua_ls
stylua
Bash
bashls
shfmt
TailwindCSS
tailwindcss
—
Java
nvim-jdtls
google-java-format
CSS / SCSS / HTML / JSON / Markdown
—
prettier
SQL
—
sqlfmt
XML
—
xmllint
YAML
—
yamllint
Monorepo TypeScript: ts_ls walks up the directory tree to find the topmost directory containing both tsconfig.json and node_modules — works correctly in monorepos without per-project config.
No title
The Zsh config is split into three layers so the tracked parts stay portable and secrets never touch the repo.
Rendering diagram...
~/.zshrc
No
Entry point plus machine-local setup_* functions for secrets and tooling
configs/.zshrc
Yes
PATH, language toolchains, integrations, keybindings, aliases
configs/.zshrc_pre
Yes
Catppuccin syntax highlighting, Powerlevel10k instant prompt, oh-my-zsh load
Machine-local entry point
~/.zshrc isn't symlinked. It's a plain file that sources the tracked config, then follows the same pattern — one setup_* function per concern and a main_local() that calls them — for anything that must stay off GitHub:
1# ~/.zshrc
2source ~/.config/configs/.zshrc
3
4setup_env_vars() {
5 # Secrets — not tracked in any repo
6 export JIRA_API_TOKEN="..."
7 export FIGMA_API_TOKEN="..."
8}
9
10setup_local_paths() {
11 export PATH="$HOME/.local/some-tool/bin:$PATH"
12}
13
14setup_local_aliases() {
15 alias work='cd ~/Work'
16}
17
18main_local() {
19 setup_env_vars
20 setup_local_paths
21 setup_local_aliases
22}
23
24main_localAvoid name clashes: The tracked config already defines main and setup_aliases, so local functions use distinct names such as main_local and setup_local_aliases.
Keep secrets in ~/.zshrc only. Everything under ~/.config is pushed to a public repository.
1# ~/.zshrc
2source ~/.config/configs/.zshrc
3
4setup_env_vars() {
5 # Secrets — not tracked in any repo
6 export JIRA_API_TOKEN="..."
7 export FIGMA_API_TOKEN="..."
8}
9
10setup_local_paths() {
11 export PATH="$HOME/.local/some-tool/bin:$PATH"
12}
13
14setup_local_aliases() {
15 alias work='cd ~/Work'
16}
17
18main_local() {
19 setup_env_vars
20 setup_local_paths
21 setup_local_aliases
22}
23
24main_localOne function per concern
Instead of one long script, configs/.zshrc groups each concern into its own setup_* function, and a single main() calls them in order. Reordering, disabling, or debugging a step is a one-line change:
1# configs/.zshrc
2main() {
3 setup_environment_variables
4 setup_homebrew_paths
5 setup_go_environment
6 setup_rust_environment
7 setup_ruby_environment
8 setup_python_environment
9 setup_java_environment
10 setup_custom_bins
11 setup_node_environment
12 setup_shell_integrations
13 setup_keybindings
14 setup_ssh_agent
15 setup_aliases
16}
17
18mainsetup_environment_variables
TERM, EDITOR, VISUAL
setup_homebrew_paths
Homebrew bin / sbin and Rancher Desktop
setup_go_environment
GOPATH and ~/go/bin
setup_rust_environment
~/.cargo/bin
setup_ruby_environment
Homebrew Ruby, compiler flags, user gem bin
setup_python_environment
Python user-base bin
setup_java_environment
OpenJDK, Zulu 8, JAVA_HOME
setup_custom_bins
~/.local/bin, ~/bin, and the bin/ script folders in this repo
setup_node_environment
nvm, Yarn global bin, Zscaler CA for Node tooling
setup_shell_integrations
zoxide — runs after PATH is complete
setup_keybindings
^n / ^p completion, ^j / ^k history substring search
setup_ssh_agent
Loads keys via bin/dev/setup-ssh-agent
setup_aliases
sed → gsed, gh copilot shortcuts, navigation aliases
1# configs/.zshrc
2main() {
3 setup_environment_variables
4 setup_homebrew_paths
5 setup_go_environment
6 setup_rust_environment
7 setup_ruby_environment
8 setup_python_environment
9 setup_java_environment
10 setup_custom_bins
11 setup_node_environment
12 setup_shell_integrations
13 setup_keybindings
14 setup_ssh_agent
15 setup_aliases
16}
17
18mainSafe PATH handling
PATH is built through two guarded helpers. A directory is only added if it exists, and typeset -aU path keeps entries unique, so re-sourcing the file never produces duplicates. The same config works on a machine that's missing some toolchains:
1# configs/.zshrc
2typeset -aU path
3
4prepend_path() {
5 if [ -d "$1" ]; then
6 path=("$1" $path)
7 fi
8}
9
10append_path() {
11 if [ -d "$1" ]; then
12 path=($path "$1")
13 fi
14}1# configs/.zshrc
2typeset -aU path
3
4prepend_path() {
5 if [ -d "$1" ]; then
6 path=("$1" $path)
7 fi
8}
9
10append_path() {
11 if [ -d "$1" ]; then
12 path=($path "$1")
13 fi
14}1configs/agent-skills/
2├── <skill-name>/
3│ ├── SKILL.md Frontmatter (name, description) + instructions
4│ └── references/ Optional supporting docs loaded on demand
5└── ...1# ~/.zshrc
2setup_env_vars() {
3 export WORK_AGENT_SKILLS_DIR="$HOME/path/to/private/agent-skills"
4}