Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
ci:
runs-on: ubuntu-latest
runs-on: ubuntu-slim
steps:
- uses: actions/checkout@v3
- name: Smoke test
Expand Down
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Repository Guidelines

## Project Structure & Module Organization
This repository manages user environment config and bootstrap tooling. Top-level directories map to target tools: `fish/`, `zsh/`, `nvim/`, `tmux/`, `screen/`, `alacritty/`, `git/`, `colima/`, and `docker/`. The bootstrap entrypoint is [`init`](/Users/mandai/.dotfiles/init), a Python script that creates symlinks under `$XDG_CONFIG_HOME` and clones required plugins such as `tmux/plugins/tpm`. Keep tool-specific changes inside their existing directory, and place longer design notes in `docs/`.

## Build, Test, and Development Commands
Use these commands from the repository root:

- `./init`: installs or refreshes symlinks and is the main smoke test used in CI.
- `pre-commit run --all-files`: runs repository checks, including YAML cleanup and formatting hooks.
- `stylua nvim/**/*.lua`: formats Lua files using the checked-in `.stylua.toml`.
- `black init && isort init`: formats the Python bootstrap script.
- `docker build -f ./docker/Dockerfile -t mandaiy/dotfiles:linux-amd64 --platform linux/amd64 .`: mirrors the Docker workflow.

## Coding Style & Naming Conventions
Follow the style already used in each area. Python in `init` is formatted with `black` and `isort`; prefer small helper functions and clear class names such as `GitClone` or `SymlinkToConfig`. Lua uses `stylua` with spaces, 3-space indentation, and a 120-column limit. Shell files should preserve the existing style of their shell (`fish` syntax in `fish/`, POSIX or zsh syntax in `zsh/`). Name files by tool and purpose, for example `fish/functions/fzf-z.fish`.

## Testing Guidelines
There is no unit-test suite today; validation is primarily smoke-test based. Run `./init` before opening a PR, ideally in a clean environment or container, and confirm the expected symlinks and plugin bootstrap behavior. For config changes, test the affected tool directly, such as launching `nvim` or starting a new `fish` session.

## Commit & Pull Request Guidelines
Recent commits use a short scope prefix followed by a concise summary, for example `tmux: fix TMUX_SHELL` or `nvim: add vim-slime`. Keep that pattern, use imperative mood, and keep the scope aligned to the directory you changed. PRs should describe the user-visible impact, list the commands you ran, and note platform assumptions. Include screenshots only for visual UI changes, such as terminal or editor appearance updates.

## Security & Configuration Tips
Do not commit machine-specific secrets or local overrides. Use `envrc.sample` as the template for environment variables, and keep personal overrides in untracked local config files such as `config.local.fish` or `.zshrc.local`.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
45 changes: 38 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
| neovim | |
| ripgrep | |
| tree-sitter-cli | Required by `nvim-treesitter` main branch |
| tmux | |
| herdr | |
| [macOS Input Source Manager][macism] | |


```
$ /opt/homebrew/bin/brew install bat fd fish fzf neovim ripgrep tree-sitter-cli tmux # macOS
$ /opt/homebrew/bin/brew install bat fd fish fzf neovim ripgrep tree-sitter-cli herdr # macOS

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep tmux in the bootstrap dependencies

The Homebrew setup command no longer installs tmux, but this commit still leaves several restored configs requiring it: init links tmux/tmux.conf and clones TPM, nvim/lua/plugins.lua sets vim.g.slime_target = "tmux", and zsh/zshrc.d/bin/ssh shells out to tmux. A fresh machine following the README will therefore restore tmux-dependent config without the binary, breaking those workflows until tmux is installed separately.

Useful? React with 👍 / 👎.

```

If you use the `nvim-treesitter` `main` branch, install `tree-sitter-cli` as well.
Expand Down Expand Up @@ -54,15 +54,46 @@ esac
> curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher
```

### tmux
``init`` script installs ``tpm`` (tmux plugin manager) into ``tmux/plugins``.
Then reload ``.tmux.conf`` (``bind + shift + I`` in tmux) and the plugins specified in ``.tmux.conf`` will be installed.


[brew]:https://brew.sh/index
[fisher]:https://github.com/jorgebucaran/fisher
[macism]:https://github.com/laishulu/macism

### Setup Neovim

Create a dedicated Python virtual environment for Neovim and install the Python
provider package after running `./init`:

```sh
NVIM_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/nvim"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Export XDG_CONFIG_HOME before creating the venv

When these new instructions are run from a shell that does not export XDG_CONFIG_HOME, the venv is created at $HOME/.config/nvim/venv, but nvim/init.lua falls back to an empty string and probes /nvim/venv/ before setting python3_host_prog. Users following this setup path will still fail provider health checks unless they manually export XDG_CONFIG_HOME or set NVIM_PYTHON_PATH.

Useful? React with 👍 / 👎.

python3 -m venv "$NVIM_CONFIG_HOME/venv"
"$NVIM_CONFIG_HOME/venv/bin/python" -m pip install --upgrade pip pynvim
```

With this repository Neovim automatically uses `$XDG_CONFIG_HOME/nvim/venv/bin/python` when the
virtual environment exists. To use a Python interpreter in another location,
set `NVIM_PYTHON_PATH` to its executable path instead. In that case, make sure
`pynvim` is installed for that interpreter.

Run `:checkhealth provider` in Neovim to verify the setup.

#### Restore Neovim plugins

The `./init` script links the version-controlled `nvim/lazy-lock.json` to
`$XDG_CONFIG_HOME/nvim/lazy-lock.json`. After setting up a new machine, start
Neovim and restore the plugin revisions recorded in the lockfile:

```vim
:Lazy restore
```

If `$XDG_CONFIG_HOME/nvim/lazy-lock.json` already exists as a regular file,
move or remove it before running `./init` so that the script can create the
symlink.

To update plugins intentionally, run `:Lazy update`, review the resulting
`nvim/lazy-lock.json` changes, and commit the lockfile together with any related
plugin configuration changes.

### Environment Variables

| Variable | Description |
Expand Down
72 changes: 72 additions & 0 deletions docs/nvim_plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Neovim Plugins

This document lists the plugins configured in `nvim/lua/plugins.lua`.

## Appearance and UI

| Plugin | Purpose | Loading condition and key bindings |
| --- | --- | --- |
| [`folke/tokyonight.nvim`](https://github.com/folke/tokyonight.nvim) | Color scheme | Loaded at startup with the highest priority |
| [`nvim-tree/nvim-web-devicons`](https://github.com/nvim-tree/nvim-web-devicons) | Displays icons for file types and other items | Loaded normally |
| [`RRethy/vim-illuminate`](https://github.com/RRethy/vim-illuminate) | Highlights the word under the cursor and its references | Loaded normally |
| [`nvim-mini/mini.indentscope`](https://github.com/nvim-mini/mini.indentscope) | Displays the current indent scope | Loaded normally |
| [`folke/noice.nvim`](https://github.com/folke/noice.nvim) | Enhances messages, the command line, and the LSP UI | Loaded on the `VeryLazy` event |

## Editing and Productivity

| Plugin | Purpose | Loading condition and key bindings |
| --- | --- | --- |
| [`keaising/im-select.nvim`](https://github.com/keaising/im-select.nvim) | Disables the IME when leaving Insert mode | Loaded normally |
| [`jpalardy/vim-slime`](https://github.com/jpalardy/vim-slime) | Sends buffer content to a REPL running in tmux | `<Leader>ip` sends the current IPython cell |
| [`editorconfig/editorconfig-vim`](https://github.com/editorconfig/editorconfig-vim) | Applies EditorConfig settings | Loaded normally |
| [`tpope/vim-commentary`](https://github.com/tpope/vim-commentary) | Comments and uncomments code | Loaded normally |
| [`ntpeters/vim-better-whitespace`](https://github.com/ntpeters/vim-better-whitespace) | Detects and removes unwanted whitespace | Removes trailing whitespace when saving |
| [`nvim-mini/mini.surround`](https://github.com/nvim-mini/mini.surround) | Adds operations for surrounding text with brackets, quotes, and similar characters | Loaded normally |
| [`kamykn/spelunker.vim`](https://github.com/kamykn/spelunker.vim) | Provides real-time spell checking | Loaded normally |

## Navigation and Search

| Plugin | Purpose | Loading condition and key bindings |
| --- | --- | --- |
| [`nvim-mini/mini.files`](https://github.com/nvim-mini/mini.files) | File explorer | `<Leader>b` opens the current file's directory |
| [`stevearc/aerial.nvim`](https://github.com/stevearc/aerial.nvim) | Displays a code symbol outline | `<Leader>go` toggles the outline on the left |
| [`nvim-telescope/telescope.nvim`](https://github.com/nvim-telescope/telescope.nvim) | Fuzzy finder for files, text, and other items | Loaded by `<Leader>ff`, `<Leader>fr`, `<Leader>fb`, and related mappings |
| [`nvim-telescope/telescope-file-browser.nvim`](https://github.com/nvim-telescope/telescope-file-browser.nvim) | Adds a file browser to Telescope | Loaded by `<Leader>fe` |

## Git

| Plugin | Purpose | Loading condition and key bindings |
| --- | --- | --- |
| [`nvim-mini/mini.diff`](https://github.com/nvim-mini/mini.diff) | Displays differences in the buffer | The diff source is disabled by default |
| [`sindrets/diffview.nvim`](https://github.com/sindrets/diffview.nvim) | Displays Git diffs and file history | Loaded by `DiffviewOpen` or `DiffviewFileHistory` |
| [`lewis6991/gitsigns.nvim`](https://github.com/lewis6991/gitsigns.nvim) | Displays Git changes, hunks, and blame information in buffers | Loaded when opening a buffer; operated with `<Leader>d` mappings |
| [`almo7aya/openingh.nvim`](https://github.com/almo7aya/openingh.nvim) | Opens GitHub repositories and files in a browser | Loaded by commands such as `OpenInGHFile` |

## Completion and LSP

| Plugin | Purpose | Loading condition and key bindings |
| --- | --- | --- |
| [`hrsh7th/nvim-cmp`](https://github.com/hrsh7th/nvim-cmp) | Provides completion from LSP, buffers, paths, and other sources | Loaded on the `InsertEnter` event |
| [`neovim/nvim-lspconfig`](https://github.com/neovim/nvim-lspconfig) | Configures language servers | Loaded when opening a buffer and enables available language servers |
| [`aznhe21/actions-preview.nvim`](https://github.com/aznhe21/actions-preview.nvim) | Previews LSP code actions | Loaded by `<Leader>gf` |
| [`kosayoda/nvim-lightbulb`](https://github.com/kosayoda/nvim-lightbulb) | Indicates when code actions are available | Loaded on the `LspAttach` event |

## AI Assistance

| Plugin | Purpose | Loading condition and key bindings |
| --- | --- | --- |
| [`coder/claudecode.nvim`](https://github.com/coder/claudecode.nvim) | Controls Claude Code from Neovim | Operated with `<Leader>a` mappings |

## Language and Syntax Support

| Plugin | Purpose | Loading condition and key bindings |
| --- | --- | --- |
| [`gbakes/dbt-forge.nvim`](https://github.com/gbakes/dbt-forge.nvim) | Supports working with dbt projects | Loaded for SQL files |
| [`nvim-treesitter/nvim-treesitter`](https://github.com/nvim-treesitter/nvim-treesitter) | Provides syntax parsing and highlighting | Loaded at startup; runs `TSUpdate` when updated |
| [`windwp/nvim-ts-autotag`](https://github.com/windwp/nvim-ts-autotag) | Automatically adds and updates closing tags in HTML, JSX, and similar formats | Loaded on `InsertEnter` for supported file types |

## Utilities

| Plugin | Purpose | Loading condition and key bindings |
| --- | --- | --- |
| [`nvim-lua/plenary.nvim`](https://github.com/nvim-lua/plenary.nvim) | Lua utilities for Neovim plugins | Loaded normally |
16 changes: 8 additions & 8 deletions fish/config.fish
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ if status is-interactive
# Commands to run in interactive sessions can go here
_brew_shellenv

fish_add_path $HOME/.local/bin

if test -z "$XDG_CONFIG_HOME"
set -gx XDG_CONFIG_HOME $HOME/.config
end
Expand All @@ -31,11 +33,9 @@ if status is-interactive

if type -q direnv
eval (direnv hook fish)
# Prevent persistent servers from inheriting project-specific environments.
alias tmux "direnv exec / tmux"
end

if type -q rbenv
rbenv init - fish | source
alias herdr "direnv exec / herdr"
end

alias nvimdiff="nvim -d"
Expand Down Expand Up @@ -77,9 +77,9 @@ if status is-interactive
if not test -z "$Z_DATA"
z-cleanup
end
end

set -l config_local $XDG_CONFIG_HOME/fish/config.local.fish
if [ -e $config_local ]
source $config_local
set -l config_local $XDG_CONFIG_HOME/fish/config.local.fish
if [ -e $config_local ]
source $config_local
end
end
19 changes: 19 additions & 0 deletions herdr/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[ui]
pane_gaps = false
prompt_new_tab_name = false

[ui.sound]
enabled = false

[ui.toast]
delivery = "terminal"

[keys]
prefix = "ctrl+q"
detach = "prefix+d"

new_tab = "prefix+c"
next_tab = "prefix+n"
previous_tab = "prefix+p"
split_vertical = "prefix+v"
split_horizontal = "prefix+s"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid reusing Herdr's settings binding for splits

In this config, prefix+s is assigned to horizontal splitting, but Herdr's config reference shows keys.settings defaults to prefix+s while keys.split_horizontal defaults to prefix+minus (https://herdr.dev/docs/config-reference/). Unless settings is explicitly moved or unset, this creates a duplicate binding in the default keymap, so users pressing the documented split key can get the settings action or a startup binding conflict instead of a horizontal split.

Useful? React with 👍 / 👎.

3 changes: 3 additions & 0 deletions init
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,11 @@ def process() -> None:
SymlinkToConfig("alacritty/alacritty.toml", exist_ok=True),
# git
SymlinkToConfig("git/ignore", exist_ok=True),
# herdr
SymlinkToConfig("herdr/config.toml", exist_ok=True),
# nvim
SymlinkToConfig("nvim/init.lua", exist_ok=True),
SymlinkToConfig("nvim/lazy-lock.json", exist_ok=True),
SymlinkToConfig("nvim/lua", exist_ok=True),
# tmux
SymlinkToConfig("tmux/tmux.conf", exist_ok=True),
Expand Down
33 changes: 23 additions & 10 deletions nvim/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,12 @@ end, { expr = true })
-- Enable editorconfig
vim.g.editorconfig = true

-- Disable perl support.
vim.g.loaded_perl_provider = 0
-- Disable ruby support.
vim.g.loaded_ruby_provider = 0
-- Disable node support.
vim.g.loaded_node_provider = 0
-- Disable python2 support.
vim.g.loaded_python_provider = 0

Expand All @@ -262,7 +268,14 @@ if not vim.loop.fs_stat(lazypath) then
end
vim.opt.rtp:prepend(lazypath)

require("lazy").setup("plugins")
require("lazy").setup({
{ import = "plugins" },
}, {
lockfile = vim.fn.stdpath("config") .. "/lazy-lock.json",
rocks = {
enabled = false,
},
})

vim.api.nvim_set_hl(0, "WinSeparator", { fg = "#FFFFFF", bg = "#1e1e2e" })
vim.api.nvim_set_hl(0, "StatusLine", { bg = "#87afff", fg = "#000000", bold = true })
Expand All @@ -271,14 +284,14 @@ vim.opt.fillchars:append({ vert = "│" })

if is_ssh() then
vim.g.clipboard = {
name = 'OSC 52',
copy = {
['+'] = require('vim.ui.clipboard.osc52').copy('+'),
['*'] = require('vim.ui.clipboard.osc52').copy('*'),
},
paste = {
['+'] = require('vim.ui.clipboard.osc52').paste('+'),
['*'] = require('vim.ui.clipboard.osc52').paste('*'),
},
name = "OSC 52",
copy = {
["+"] = require("vim.ui.clipboard.osc52").copy("+"),
["*"] = require("vim.ui.clipboard.osc52").copy("*"),
},
paste = {
["+"] = require("vim.ui.clipboard.osc52").paste("+"),
["*"] = require("vim.ui.clipboard.osc52").paste("*"),
},
}
end
41 changes: 41 additions & 0 deletions nvim/lazy-lock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"actions-preview.nvim": { "branch": "master", "commit": "0ac9c2aa3cfc8c885321c0862b50b6b1c3392405" },
"aerial.nvim": { "branch": "master", "commit": "28fe6e822ae344544c379d60fcb13c9519a1f08a" },
"claudecode.nvim": { "branch": "main", "commit": "2390c6e45c4789072c293ac69de051d169668b29" },
"cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
"cmp-calc": { "branch": "main", "commit": "5947b412da67306c5b68698a02a846760059be2e" },
"cmp-cmdline": { "branch": "main", "commit": "d126061b624e0af6c3a556428712dd4d4194ec6d" },
"cmp-emoji": { "branch": "main", "commit": "e8398e2adf512a03bb4e1728ca017ffeac670a9f" },
"cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
"cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
"dbt-forge.nvim": { "branch": "main", "commit": "b7ca59acb14827f0c2c6954820a231995bf330e5" },
"diffview.nvim": { "branch": "main", "commit": "4516612fe98ff56ae0415a259ff6361a89419b0a" },
"editorconfig-vim": { "branch": "master", "commit": "c0227885a06b155d5aa5465e08b9800e8c939f70" },
"gitsigns.nvim": { "branch": "main", "commit": "31d6fb2d618bca1482b9f274751ead5f03461408" },
"im-select.nvim": { "branch": "master", "commit": "963a4e9d528ef8a8d328eeff690593b0146d30e2" },
"lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
"mini.diff": { "branch": "main", "commit": "b3d8507929f8df54ab9b08c77564c1afd7d62110" },
"mini.files": { "branch": "main", "commit": "d08e8da87be8bf07d09245294821706afcca31cd" },
"mini.indentscope": { "branch": "main", "commit": "92fbaf895f83c59575ec599df532b297ebf62b14" },
"mini.surround": { "branch": "main", "commit": "8d5d0c5aa92449368ac251e85451d79d8f69d296" },
"noice.nvim": { "branch": "main", "commit": "7bfd942445fb63089b59f97ca487d605e715f155" },
"nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" },
"nvim-cmp": { "branch": "main", "commit": "2ffe79f1f021def8dd1fcd81deb16f1bb0d989f3" },
"nvim-lightbulb": { "branch": "master", "commit": "b8c08c5f3b1586dfcdd9f34d7d54fe6982e01ac9" },
"nvim-lspconfig": { "branch": "master", "commit": "e7ca2c95ba316a8b846d3f3546d407908c5c4ccb" },
"nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
"nvim-treesitter-textobjects": { "branch": "main", "commit": "851e865342e5a4cb1ae23d31caf6e991e1c99f1e" },
"nvim-ts-autotag": { "branch": "main", "commit": "88c1453db4ba7dd24131086fe51fdf74e587d275" },
"nvim-web-devicons": { "branch": "master", "commit": "0ca28b61a04fe7426cefbdd52c2647ef0e335b5f" },
"openingh.nvim": { "branch": "main", "commit": "7cc8c897cb6b34d8ed28e99d95baccef609ed251" },
"plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
"snacks.nvim": { "branch": "main", "commit": "882c996cf28183f4d63640de0b4c02ec886d01f2" },
"spelunker.vim": { "branch": "master", "commit": "a0bc530f62798bbe053905555a4aa9ed713485eb" },
"telescope-file-browser.nvim": { "branch": "master", "commit": "3610dc7dc91f06aa98b11dca5cc30dfa98626b7e" },
"telescope.nvim": { "branch": "master", "commit": "427b576c16792edad01a92b89721d923c19ad60f" },
"tokyonight.nvim": { "branch": "main", "commit": "cdc07ac78467a233fd62c493de29a17e0cf2b2b6" },
"vim-better-whitespace": { "branch": "master", "commit": "de99b55a6fe8c96a69f9376f16b1d5d627a56e81" },
"vim-commentary": { "branch": "master", "commit": "64a654ef4a20db1727938338310209b6a63f60c9" },
"vim-illuminate": { "branch": "master", "commit": "91313e598ca62e110bc71535c49069b66b9883c9" },
"vim-slime": { "branch": "main", "commit": "2cfdc3b24e7ebaa64f5a1a04b00555600c622b79" }
}
Loading
Loading