Compare commits

..

27 commits

Author SHA1 Message Date
dependabot[bot]
46b28c9a08
chore(deps): bump actions/checkout from 4.3.1 to 7.0.0 (#29)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.3.1 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](34e114876b...9c091bb21b)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-06 21:22:10 +02:00
b791c20974 ci: run full installer end-to-end; add Dependabot for actions
The validate job checks configs but never executes install.sh, which
is what actually rots (apt repo URLs, GPG fingerprints, pinned
checksums). New job runs the complete install plus --unlink as a
normal user in a pristine ubuntu:24.04 container. Dependabot keeps
the pinned GitHub Actions current monthly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:18:57 +02:00
c45d1ff108 feat: enable tmux session auto-restore on server start
Resurrect + continuum were already saving every 15 minutes; this makes
sessions come back automatically after a reboot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:18:57 +02:00
c3cd129478 feat: manage bat and Claude Code configs; add --unlink mode
- New managed configs: .config/bat (ANSI theme so bat follows the
  terminal palette) and .claude/settings.json, with a .gitignore guard
  so only settings.json under .claude/ is trackable.
- dotfiles.sh: drive linking from a single link map and add
  unlink_dotfiles; install.sh grows --unlink to cleanly remove every
  symlink pointing into the repo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:17:37 +02:00
6861ff1e16 feat: add local override hooks for zsh and git
~/.zshrc.local and ~/.gitconfig.local hold machine-specific settings
(secrets, work identity, installer PATH additions) so third-party
installers stop dirtying the tracked configs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:16:17 +02:00
5123fb4349 fix: config review fixes
- .gitconfig: correct misspelled user email, fix del-remote alias
  (expanded to 'git git remote prune'), filter 'today' to own commits
- .zshrc: guard lt() shift with no args, drop duplicate ~/.local/bin
  PATH entry, dedupe history (HIST_IGNORE_ALL_DUPS, HIST_IGNORE_SPACE)
- wezterm: fix hyperlink regex char class (0.9 -> 0-9)
- extras: bump nvm to v0.40.5 with verified installer checksum

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:16:01 +02:00
84b99788a7 feat: manage ssh client config
Symlink .ssh/config into ~/.ssh with proper directory permissions.
Guard .gitignore so only the client config is trackable — keys and
anything else under .ssh/ can never be committed. Also drop a
redundant tmux ignore rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 21:16:01 +02:00
Brian Pooe
a33c46d130 Persist zsh command history 2026-07-06 18:58:59 +02:00
Brian Pooe
97ad246684 fix: use canonical Mason plugin dependency 2026-06-11 23:17:53 +02:00
Brian Pooe
2eef4edfc5 fix: persist Homebrew path in validation workflow 2026-06-11 23:15:01 +02:00
Brian Pooe
e2170063f3 fix: install Homebrew in validation workflow 2026-06-11 21:37:32 +02:00
Brian Pooe
db49fc1a56 feat: harden dotfiles installer and add automated validation 2026-06-11 20:38:19 +02:00
Brian Pooe
394a9af367 fix: align setup with Pop!_OS toolchain 2026-06-11 18:37:16 +02:00
995af1df20
Merge pull request #28 from brianpooe/claude/cool-hopper-8vgG8
feat: control WezTerm appearance from .zshrc via generated env.lua
2026-06-11 18:10:50 +02:00
2acf2d481b
Merge pull request #27 from brianpooe/claude/cool-hopper-8vgG8
Claude/cool hopper 8vg g8
2026-06-11 17:37:41 +02:00
f75fb0aff3
Merge pull request #26 from brianpooe/claude/cool-hopper-8vgG8
fix(install): install JetBrainsMono Nerd Font
2026-06-11 17:28:37 +02:00
7f5cd79b5d
Merge pull request #25 from brianpooe/claude/cool-hopper-8vgG8
fix(install): use Brave Beta's own keyring (not the stable one)
2026-06-11 17:23:11 +02:00
f0cc4fc01f
Merge pull request #24 from brianpooe/claude/cool-hopper-8vgG8
fix(install): correct Brave Beta keyring URL + make add_apt_repo robust
2026-06-11 17:20:46 +02:00
4ef5dad9ae
Merge pull request #23 from brianpooe/claude/cool-hopper-8vgG8
Claude/cool hopper 8vg g8
2026-06-10 18:59:13 +02:00
4c69de3ea7
Merge pull request #22 from brianpooe/claude/cool-hopper-8vgG8
Claude/cool hopper 8vg g8
2026-06-09 18:07:06 +02:00
9d5583bcc9
Merge pull request #21 from brianpooe/claude/cool-hopper-8vgG8
fix(omarchy): suppress chromium-related errors in theme.sh
2026-06-08 19:03:27 +02:00
c891fe3b68
Merge pull request #20 from brianpooe/claude/cool-hopper-8vgG8
fix(omarchy): bootstrap Omarchy pacman repo so AUR/omarchy packages r…
2026-06-08 18:19:18 +02:00
c74fb76c7b
Merge pull request #19 from brianpooe/claude/cool-hopper-8vgG8
docs(omarchy): document install steps and environment requirements
2026-06-07 19:44:45 +02:00
230663865a
Merge pull request #18 from brianpooe/claude/cool-hopper-8vgG8
Claude/cool hopper 8vg g8
2026-06-07 19:42:21 +02:00
781e8d6f93
Merge pull request #17 from brianpooe/claude/cool-hopper-8vgG8
Claude/cool hopper 8vg g8
2026-06-07 19:37:09 +02:00
00252bcb78
Merge pull request #16 from brianpooe/claude/cool-hopper-8vgG8
Claude/cool hopper 8vg g8
2026-06-07 19:12:45 +02:00
d9d06db17e
Merge pull request #15 from brianpooe/claude/cool-hopper-8vgG8
Remove offline mode and platform-specific configurations
2026-06-07 18:52:55 +02:00
39 changed files with 542 additions and 854 deletions

7
.claude/settings.json Normal file
View file

@ -0,0 +1,7 @@
{
"effortLevel": "medium",
"tui": "fullscreen",
"skipDangerousModePermissionPrompt": true,
"theme": "dark",
"model": "claude-fable-5[1m]"
}

View file

@ -1,194 +0,0 @@
" ========================================
" Options
" ========================================
set encoding=UTF-8
set spelllang=en_us,de_de,es_es
set nohlsearch " Disable highlight on search
set number " Enable line numbers
set mouse=a " Enable mouse mode
set breakindent " Enable break indent
set undofile " Save undo history
set ignorecase " Case-insensitive searching unless \C or capital in search
set smartcase " Enable smart case
set signcolumn=yes " Keep signcolumn on by default
set updatetime=250 " Decrease update time
set timeoutlen=300 " Time to wait for a mapped sequence to complete (in milliseconds)
set nobackup " Don't create a backup file
set nowritebackup " Don't write backup before overwriting
set completeopt=menuone,noselect " Better completion experience
set whichwrap+=<,>,[,],h,l " Allow certain keys to move to the next line
set nowrap " Display long lines as one line
set linebreak " Don't break words when wrapping
set scrolloff=8 " Keep 8 lines above/below cursor
set sidescrolloff=8 " Keep 8 columns to the left/right of cursor
set relativenumber " Use relative line numbers
set numberwidth=4 " Number column width
set shiftwidth=4 " Spaces per indentation
set tabstop=4 " Spaces per tab
set softtabstop=4 " Spaces per tab during editing ops
set expandtab " Convert tabs to spaces
set nocursorline " Don't highlight the current line
set splitbelow " Horizontal splits below current window
set splitright " Vertical splits to the right
set noswapfile " Don't use a swap file
set smartindent " Smart indentation
set showtabline=2 " Always show tab line
set backspace=indent,eol,start " Configurable backspace behavior
set pumheight=10 " Popup menu height
set conceallevel=0 " Make `` visible in markdown
set fileencoding=utf-8 " File encoding
set cmdheight=1 " Command line height
set autoindent " Auto-indent new lines
set shortmess+=c " Don't show completion menu messages
set iskeyword+=- " Treat hyphenated words as whole words
set showmatch " show the matching part of pairs [] {} and ()
set laststatus=2 " Show status bar
set statusline=%f " Path to the file
set statusline+=%= " Switch to the right side
set statusline+=%l " Current line
set statusline+=/ " Separator
set statusline+=%L " Total lines
" ========================================
" Keymaps
" ========================================
" Set leader key
let mapleader = " "
let maplocalleader = " "
" Disable the spacebar key's default behavior in Normal and Visual modes
nnoremap <Space> <Nop>
vnoremap <Space> <Nop>
" Allow moving the cursor through wrapped lines with j, k
nnoremap <expr> k v:count == 0 ? 'gk' : 'k'
nnoremap <expr> j v:count == 0 ? 'gj' : 'j'
" clear highlights
nnoremap <Esc> :noh<CR>
" save file
nnoremap <C-s> :w<CR>
" save file without auto-formatting
nnoremap <leader>sn :noautocmd w<CR>
" quit file
nnoremap <C-q> :q<CR>
" delete single character without copying into register
nnoremap x "_x
" Vertical scroll and center
nnoremap <C-d> <C-d>zz
nnoremap <C-u> <C-u>zz
" Find and center
nnoremap n nzzzv
nnoremap N Nzzzv
" Resize with arrows
nnoremap <Up> :resize -2<CR>
nnoremap <Down> :resize +2<CR>
nnoremap <Left> :vertical resize -2<CR>
nnoremap <Right> :vertical resize +2<CR>
" Navigate buffers
nnoremap <Tab> :bnext<CR>
nnoremap <S-Tab> :bprevious<CR>
nnoremap <leader>sb :buffers<CR>:buffer<Space>
" increment/decrement numbers
nnoremap <leader>+ <C-a>
nnoremap <leader>- <C-x>
" window management
nnoremap <leader>v <C-w>v
nnoremap <leader>h <C-w>s
nnoremap <leader>se <C-w>=
nnoremap <leader>xs :close<CR>
" Navigate between splits
nnoremap <C-k> :wincmd k<CR>
nnoremap <C-j> :wincmd j<CR>
nnoremap <C-h> :wincmd h<CR>
nnoremap <C-l> :wincmd l<CR>
" tabs
nnoremap <leader>to :tabnew<CR>
nnoremap <leader>tx :tabclose<CR>
nnoremap <leader>tn :tabn<CR>
nnoremap <leader>tp :tabp<CR>
nnoremap <leader>x :bdelete<CR>
nnoremap <leader>b :enew<CR>
" toggle line wrapping
nnoremap <leader>lw :set wrap!<CR>
" Press jk fast to exit insert mode
inoremap jk <ESC>
inoremap kj <ESC>
" Stay in indent mode
" vnoremap < <gv
" vnoremap > >gv
" Keep last yanked when pasting
vnoremap p "_dP
" Explicitly yank to system clipboard (highlighted and entire row)
noremap <leader>y "+y
noremap <leader>Y "+Y
" Open file explorer
noremap <silent> <leader>e :Lex<CR>
" Make W w on save
noremap :W :w
" ========================================
" Other
" ========================================
" Syntax highlighting
syntax on
" Colorscheme
" colorscheme industry
colorscheme wildcharm
set background=dark
" hi Normal ctermbg=NONE guibg=NONE
" hi NonText ctermbg=NONE guibg=NONE guifg=NONE ctermfg=NONE
" hi VertSplit guibg=NONE guifg=NONE ctermbg=NONE ctermfg=NONE
" Sync clipboard with OS
set clipboard=unnamedplus
" True colors
if !has('gui_running') && &term =~ '\%(screen\|tmux\)'
let &t_8f = "\<Esc>[38;2;%lu;%lu;%lum"
let &t_8b = "\<Esc>[48;2;%lu;%lu;%lum"
endif
if has('termguicolors')
set termguicolors
endif
" Use a line cursor within insert mode and a block cursor everywhere else.
let &t_SI = "\e[6 q"
let &t_EI = "\e[2 q"
" Netrw
let g:netrw_banner = 0
let g:netrw_liststyle = 3
let g:netrw_browse_split = 4
let g:netrw_altv = 1
let g:netrw_winsize = 25
" Use 'l' instead of <CR> to open files
augroup netrw_setup | au!
au FileType netrw nmap <buffer> l <CR>
augroup END

5
.config/bat/config Normal file
View file

@ -0,0 +1,5 @@
# Follow the terminal's ANSI palette so bat matches the active Kanagawa
# variant instead of shipping its own colorscheme.
--theme="ansi"
--style="numbers,changes,header"
--italic-text=always

View file

@ -174,7 +174,9 @@ local function angular_switch(section)
local is_ts = file:match '%.component%.ts$'
-- If we're in a .ts file, check for inline sections before switching files
if is_ts and (section == 'template' or section == 'styles' or section == 'ts') then
if
is_ts and (section == 'template' or section == 'styles' or section == 'ts')
then
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
local pattern
if section == 'template' then
@ -213,8 +215,7 @@ local function angular_switch(section)
target = base .. '.component.html'
elseif section == 'styles' then
local scss = base .. '.component.scss'
target = vim.fn.filereadable(scss) == 1 and scss
or base .. '.component.css'
target = vim.fn.filereadable(scss) == 1 and scss or base .. '.component.css'
elseif section == 'spec' then
target = base .. '.component.spec.ts'
end

View file

@ -42,17 +42,20 @@ vim.opt.iskeyword:append '-' -- hyphenated words recognized by searches
vim.opt.formatoptions:remove { 'c', 'r', 'o' } -- don't insert the current comment leader automatically for auto-wrapping comments using 'textwidth', hitting <Enter> in insert mode, or hitting 'o' or 'O' in normal mode.
vim.opt.runtimepath:remove '/usr/share/vim/vimfiles' -- separate vim plugins from neovim in case vim still in use
-- Neovim 0.12 changed directive captures from TSNode to TSNode[] (arrays).
-- nvim-treesitter predicates do match[capture_id] expecting a single TSNode,
-- but now receive a table. The nil guard passes, then get_node_text calls
-- node:range(true) on a plain table which has no range method and crashes.
-- Unwrap the array so nvim-treesitter keeps working until it is patched upstream.
local _get_node_text = vim.treesitter.get_node_text
vim.treesitter.get_node_text = function(node, source, opts)
if not node then return '' end
if type(node) == 'table' then
node = node[1]
if not node then return '' end
-- Temporary compatibility workaround for Neovim 0.12 directive captures.
-- Keep this scoped to 0.12 so later releases use the upstream implementation.
if vim.version().minor == 12 then
local get_node_text = vim.treesitter.get_node_text
vim.treesitter.get_node_text = function(node, source, opts)
if not node then
return ''
end
if type(node) == 'table' then
node = node[1]
if not node then
return ''
end
end
return get_node_text(node, source, opts)
end
return _get_node_text(node, source, opts)
end

View file

@ -12,7 +12,9 @@ end
local function hover_if_supported()
local bufnr = vim.api.nvim_get_current_buf()
for _, client in ipairs(vim.lsp.get_clients { bufnr = bufnr }) do
if client.server_capabilities and client.server_capabilities.hoverProvider then
if
client.server_capabilities and client.server_capabilities.hoverProvider
then
vim.lsp.buf.hover { border = 'rounded' }
return
end
@ -58,12 +60,7 @@ vim.api.nvim_create_autocmd('LspAttach', {
keymap.set('n', 'gt', '<cmd>Telescope lsp_type_definitions<CR>', opts) -- show lsp type definitions
opts.desc = 'See available code actions'
keymap.set(
{ 'n', 'v' },
'<leader>ca',
vim.lsp.buf.code_action,
opts
) -- see available code actions, in visual mode will apply to selection
keymap.set({ 'n', 'v' }, '<leader>ca', vim.lsp.buf.code_action, opts) -- see available code actions, in visual mode will apply to selection
opts.desc = 'Smart rename'
keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts) -- smart rename

View file

@ -46,16 +46,11 @@ return {
-- Mason integration for installing debug adapters
{
'jay-babu/mason-nvim-dap.nvim',
dependencies = 'mason.nvim',
dependencies = 'mason-org/mason.nvim',
cmd = { 'DapInstall', 'DapUninstall' },
opts = {
automatic_installation = true,
automatic_installation = false,
handlers = {},
ensure_installed = {
'python',
'js',
'codelldb',
},
},
},
},
@ -189,15 +184,12 @@ return {
'DapLogPoint',
{ text = 'L', texthl = 'DiagnosticInfo', linehl = '', numhl = '' }
)
vim.fn.sign_define(
'DapStopped',
{
text = '>',
texthl = 'DiagnosticOk',
linehl = 'DapStoppedLine',
numhl = '',
}
)
vim.fn.sign_define('DapStopped', {
text = '>',
texthl = 'DiagnosticOk',
linehl = 'DapStoppedLine',
numhl = '',
})
vim.fn.sign_define(
'DapBreakpointRejected',
{ text = 'R', texthl = 'DiagnosticError', linehl = '', numhl = '' }

View file

@ -28,12 +28,11 @@ return {
html = { 'prettierd', 'prettier', stop_after_first = true },
htmlangular = { 'prettierd', 'prettier', stop_after_first = true },
lua = { 'stylua' },
rust = { 'ast_grep' },
python = { 'ruff', 'black' },
dockerfile = { 'hadolint' },
rust = { 'rustfmt', lsp_format = 'fallback' },
python = { 'ruff_format' },
},
format_on_save = {
lsp_fallback = true,
lsp_format = 'fallback',
async = false,
timeout_ms = 1000,
},
@ -44,7 +43,7 @@ return {
vim.keymap.set({ 'n', 'v' }, '<leader>mp', function()
conform.format {
lsp_fallback = true,
lsp_format = 'fallback',
async = false,
timeout_ms = 1000,
}

View file

@ -52,13 +52,14 @@ local function dedupe(list)
return out
end
local tool_installer_list = dedupe(vim.list_extend(vim.deepcopy(tools), lsp_servers))
local tool_installer_list =
dedupe(vim.list_extend(vim.deepcopy(tools), lsp_servers))
return {
{
'mason-org/mason-lspconfig.nvim',
cond = vim.env.DOTFILES_CI ~= '1',
opts = {
ensure_installed = lsp_servers,
-- Only auto-enable the LSP servers we explicitly manage in this config.
-- This avoids accidental multi-server attaches (e.g. ts_ls + vtsls),
-- which can produce duplicate Telescope definition results.
@ -82,17 +83,21 @@ return {
},
{
'WhoIsSethDaniel/mason-tool-installer.nvim',
cond = vim.env.DOTFILES_CI ~= '1',
dependencies = {
'mason-org/mason.nvim',
},
opts = {
ensure_installed = tool_installer_list,
integrations = {
-- keep mapping enabled so lspconfig names (e.g. ts_ls) resolve to Mason packages
-- Keep mapping enabled so lspconfig names (e.g. ts_ls) resolve to Mason packages.
-- This is the sole installer to avoid racing mason-lspconfig and mason-nvim-dap.
['mason-lspconfig'] = true,
['mason-null-ls'] = false,
['mason-nvim-dap'] = false,
},
run_on_start = true,
start_delay = 0,
run_on_start = vim.env.DOTFILES_CI ~= '1',
start_delay = 3000,
},
},
}

View file

@ -8,17 +8,18 @@ return {
dependencies = {
'nvim-tree/nvim-web-devicons',
},
config = function()
local nvimtree = require 'nvim-tree'
-- disable netrw at the very start of your init.lua
init = function()
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
end,
config = function()
local nvimtree = require 'nvim-tree'
-- optionally enable 24-bit colour
vim.opt.termguicolors = true
-- OR setup with some options
nvimtree.setup {
disable_netrw = true,
hijack_netrw = false,
sort = {
sorter = 'case_sensitive',
},

View file

@ -1,4 +1,35 @@
-- Highlight, edit, and navigate code
local ci = vim.env.DOTFILES_CI == '1'
local parsers = {
'angular',
'lua',
'python',
'javascript',
'typescript',
'vimdoc',
'vim',
'regex',
'terraform',
'sql',
'dockerfile',
'toml',
'json',
'java',
'groovy',
'gitignore',
'graphql',
'yaml',
'make',
'cmake',
'markdown',
'markdown_inline',
'bash',
'tsx',
'css',
'html',
'rust',
}
return {
'nvim-treesitter/nvim-treesitter',
branch = 'master',
@ -8,36 +39,10 @@ return {
'nvim-treesitter/nvim-treesitter-textobjects',
},
opts = {
ensure_installed = {
'angular',
'lua',
'python',
'javascript',
'typescript',
'vimdoc',
'vim',
'regex',
'terraform',
'sql',
'dockerfile',
'toml',
'json',
'java',
'groovy',
'gitignore',
'graphql',
'yaml',
'make',
'cmake',
'markdown',
'markdown_inline',
'bash',
'tsx',
'css',
'html',
'rust',
},
auto_install = true,
ensure_installed = ci and {} or parsers,
auto_install = not ci,
parser_install_dir = ci and (vim.fn.stdpath 'cache' .. '/treesitter')
or nil,
highlight = { enable = true },
indent = { enable = true },
incremental_selection = {

View file

@ -113,29 +113,3 @@ purple = '#a292a3'
gray = '#727169'
black = '#181616'
white = '#c5c9c5'
[palettes.nord]
dark_blue = '#5E81AC'
blue = '#81A1C1'
teal = '#88C0D0'
red = '#BF616A'
orange = '#D08770'
green = '#A3BE8C'
yellow = '#EBCB8B'
purple = '#B48EAD'
gray = '#434C5E'
black = '#2E3440'
white='#D8DEE9'
[palettes.onedark]
dark_blue='#61afef'
blue='#56b6c2'
red='#e06c75'
green='#98c379'
purple='#c678dd'
cyan='#56b6c2'
orange='#be5046'
yellow='#e5c07b'
gray='#828997'
white ='#abb2bf'
black='#2c323c'

View file

@ -113,29 +113,3 @@ purple = '#a292a3'
gray = '#727169'
black = '#181616'
white = '#c5c9c5'
[palettes.nord]
dark_blue = '#5E81AC'
blue = '#81A1C1'
teal = '#88C0D0'
red = '#BF616A'
orange = '#D08770'
green = '#A3BE8C'
yellow = '#EBCB8B'
purple = '#B48EAD'
gray = '#434C5E'
black = '#2E3440'
white='#D8DEE9'
[palettes.onedark]
dark_blue='#61afef'
blue='#56b6c2'
red='#e06c75'
green='#98c379'
purple='#c678dd'
cyan='#56b6c2'
orange='#be5046'
yellow='#e5c07b'
gray='#828997'
white ='#abb2bf'
black='#2c323c'

View file

@ -1,23 +0,0 @@
# bg="#3B4252"
bg="default"
default_fg="#D8DEE9"
session_fg="#A3BE8C"
session_selection_fg="#3B4252"
session_selection_bg="#81A1C1"
active_window_fg="#88C0D0"
active_pane_border="#abb2bf"
set -g status-left-length 200 # default: 10
set -g status-right-length 200 # default: 10
set -g status-left "#[fg=${session_fg},bold,bg=${bg}] #S #[fg=${default_fg},nobold,bg=${bg}] | "
set -g status-right " #{cpu -i 3}  #{mem} "
set -g status-justify left
set -g status-style "bg=${bg}"
set -g window-status-format "#[fg=${default_fg},bg=default] #I:#W"
set -g window-status-current-format "#[fg=${active_window_fg},bold,bg=default]  #[underscore]#I:#W"
set -g window-status-last-style "fg=${default_fg},bg=default"
set -g message-command-style "bg=default,fg=${default_fg}"
set -g message-style "bg=default,fg=${default_fg}"
set -g mode-style "bg=${session_selection_bg},fg=${session_selection_fg}"
set -g pane-active-border-style "fg=${active_pane_border},bg=default"
set -g pane-border-style "fg=brightblack,bg=default"

View file

@ -68,14 +68,14 @@ set -g @plugin 'tmux-plugins/tmux-continuum' # Automatically saves sessions e
set -g @plugin 'hendrikmi/tmux-cpu-mem-monitor' # CPU and memory info
set -g @plugin 'tmux-plugins/tmux-yank'
# Load theme from deterministic selector file written by ~/.zshrc.
if-shell '[ -f ~/.config/tmux/current-theme.conf ]' \
'source-file ~/.config/tmux/current-theme.conf' \
# Load theme from deterministic selector file generated during shell startup.
if-shell '[ -f ~/.cache/dotfiles/tmux-theme.conf ]' \
'source-file ~/.cache/dotfiles/tmux-theme.conf' \
'source-file ~/.config/tmux/dragon-theme.conf'
# Resurrect
# Resurrect + continuum: auto-save every 15 min and restore on server start.
set -g @resurrect-capture-pane-contents 'on'
# set -g @continuum-restore 'on'
set -g @continuum-restore 'on'
# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
run '~/.tmux/plugins/tpm/tpm'

View file

@ -13,12 +13,12 @@ local function env_number(name, fallback)
return parsed
end
-- User-tunable settings live in ~/.zshrc, which writes them to env.lua
-- User-tunable settings live in ~/.zshrc, which writes them to a cache file
-- (GUI-launched WezTerm never sees shell exports). WezTerm watches that
-- file and live-reloads, so editing .zshrc + opening a new shell applies.
-- Precedence: env.lua > process env > defaults.
-- Precedence: cache file > process env > defaults.
local user = {}
local env_file = wezterm.home_dir .. "/.config/wezterm/env.lua"
local env_file = wezterm.home_dir .. "/.cache/dotfiles/wezterm-env.lua"
local ok, loaded = pcall(dofile, env_file)
if ok and type(loaded) == "table" then
user = loaded
@ -149,7 +149,7 @@ config.hyperlink_rules = {
{ regex = "\\[(\\w+://\\S+)\\]", format = "$1", highlight = 1 },
{ regex = "\\{(\\w+://\\S+)\\}", format = "$1", highlight = 1 },
{ regex = "<(\\w+://\\S+)>", format = "$1", highlight = 1 },
{ regex = "[^(]\\b(\\w+://\\S+[)/a-zA-Z0.9-]+)", format = "$1", highlight = 1 },
{ regex = "[^(]\\b(\\w+://\\S+[)/a-zA-Z0-9-]+)", format = "$1", highlight = 1 },
{ regex = "\\b\\w+@[\\w-]+(\\.[\\w-]+)+\\b", format = "mailto:$0" },
}
-- Window size

View file

@ -1,107 +0,0 @@
// Zed keymap
//
// This carries over the Neovim shortcuts that map cleanly onto Zed.
// Leader mappings use Zed's Vim mode sequence support.
[
{
"context": "Editor && !menu && vim_mode == normal",
"bindings": {
"ctrl-s": "workspace::SaveAll",
"ctrl-q": "pane::CloseActiveItem",
"tab": "pane::ActivateNextItem",
"shift-tab": "pane::ActivatePreviousItem",
"ctrl-h": "vim::ToggleProjectPanelFocus",
"ctrl-j": "workspace::ActivatePaneDown",
"ctrl-k": "workspace::ActivatePaneUp",
"ctrl-l": "workspace::ActivatePaneRight",
"shift-k": "editor::Hover",
"alt-j": "editor::MoveLineDown",
"alt-k": "editor::MoveLineUp",
"[ d": "editor::GoToPreviousDiagnostic",
"] d": "editor::GoToDiagnostic",
"g d": "editor::GoToDefinition",
"g i": "editor::GoToImplementation",
"g t": "editor::GoToTypeDefinition",
"g shift-d": "editor::GoToDeclaration",
"g shift-r": "editor::FindAllReferences"
}
},
{
"context": "ProjectPanel",
"bindings": {
"ctrl-h": "project_panel::ToggleFocus",
"ctrl-l": "project_panel::ToggleFocus",
"o": "project_panel::Open"
}
},
{
"context": "Editor && !menu && vim_mode == visual",
"bindings": {
"alt-j": "editor::MoveLineDown",
"alt-k": "editor::MoveLineUp",
"space c a": "editor::ToggleCodeActions",
"space g h": "vim::StartOfLine",
"space g l": "vim::EndOfLine",
"space s t": "editor::SortLinesCaseSensitive",
"space ~": "editor::ToggleCase"
}
},
{
"context": "Editor && !menu && vim_mode == normal",
"bindings": {
"space space": "tab_switcher::Toggle",
"space tab": "tab_switcher::Toggle",
"space /": "buffer_search::Deploy",
"space +": "vim::Increment",
"space -": "vim::Decrement",
"space b": "workspace::NewFile",
"space c a": "editor::ToggleCodeActions",
"space c f": "editor::Format",
"space d b": "editor::ToggleBreakpoint",
"space d c": "debugger::Continue",
"space d f": "editor::Hover",
"space d i": "debugger::StepInto",
"space d o": "debugger::StepOut",
"space d p": "debugger::Pause",
"space d r": "debugger::Rerun",
"space d shift-o": "debugger::StepOver",
"space d t": "debugger::Stop",
"space d u": "debug_panel::ToggleFocus",
"space e": "project_panel::ToggleFocus",
"space g b": "git::Branch",
"space g c f": "git::FileHistory",
"space g h": "vim::StartOfLine",
"space g l": "vim::EndOfLine",
"space g s": "git::Diff",
"space h": "pane::SplitHorizontal",
"space l g": "git_panel::ToggleFocus",
"space l w": "editor::ToggleSoftWrap",
"space m p": "editor::Format",
"space q": "diagnostics::Deploy",
"space q t": "pane::CloseOtherItems",
"space r n": "editor::Rename",
"space r s": "editor::RestartLanguageServer",
"space s /": "workspace::NewSearch",
"space s a": "workspace::SaveAll",
"space s b": "tab_switcher::Toggle",
"space s d": "diagnostics::Deploy",
"space s d s": "project_symbols::Toggle",
"space s f": "file_finder::Toggle",
"space s g": "workspace::NewSearch",
"space s n": "workspace::SaveWithoutFormat",
"space shift-d": "diagnostics::DeployCurrentFile",
"space t o": "terminal_panel::ToggleFocus",
"space t r": "task::Rerun",
"space t s": "zed::OpenTasks",
"space t t": "editor::SpawnNearestTask",
"space u d": "editor::ToggleInlineDiagnostics",
"space v": "pane::SplitVertical",
"space w h": "workspace::ActivatePaneLeft",
"space w j": "workspace::ActivatePaneDown",
"space w k": "workspace::ActivatePaneUp",
"space w l": "workspace::ActivatePaneRight",
"space w p": "workspace::ActivatePreviousPane",
"space x": "pane::CloseActiveItem"
}
}
]

View file

@ -1,160 +0,0 @@
// Zed settings
//
// For information on how to configure Zed, see the Zed
// documentation: https://zed.dev/docs/configuring-zed
//
// This translates the main Neovim behaviors from `.config/n into
// the Zed features that have direct equivalents.
{
"telemetry": {
"diagnostics": false,
"metrics": false,
},
"vim_mode": true,
"base_keymap": "JetBrains",
"auto_install_extensions": {
"html": true,
"angular": true,
"lua": true,
"sql": true,
"kanagawa-themes": true,
},
"icon_theme": {
"mode": "dark",
"light": "Zed (Default)",
"dark": "Zed (Default)",
},
"theme": {
"mode": "dark",
"light": "Kanagawa Lotus",
"dark": "New Darcula",
},
"ui_font_size": 16,
"buffer_font_size": 15,
"gutter": {
"line_numbers": true,
"breakpoints": true,
"folds": true,
"min_line_number_digits": 4,
},
"relative_line_numbers": "enabled",
"soft_wrap": "none",
"show_wrap_guides": false,
"tab_size": 4,
"hard_tabs": false,
"vertical_scroll_margin": 4,
"horizontal_scroll_margin": 8,
"use_smartcase_search": true,
"show_whitespaces": "selection",
"hover_popover_delay": 300,
"auto_signature_help": true,
"remove_trailing_whitespace_on_save": true,
"format_on_save": "on",
"diagnostics_max_severity": null,
"diagnostics": {
"inline": {
"enabled": false,
},
},
"inlay_hints": {
"enabled": true,
"show_type_hints": true,
"show_parameter_hints": true,
"show_other_hints": true,
"show_background": false,
},
"git": {
"git_gutter": "tracked_files",
"inline_blame": {
"enabled": false,
},
},
"tabs": {
"file_icons": true,
"git_status": true,
"show_close_button": "always",
"show_diagnostics": "all",
},
"project_panel": {
"default_width": 320,
"git_status": true,
"show_diagnostics": "all",
"hide_hidden": false,
"auto_reveal_entries": true,
"indent_guides": {
"show": "always",
},
},
"toolbar": {
"breadcrumbs": true,
"quick_actions": true,
"selections_menu": true,
"code_actions": true,
},
"file_types": {
"Groovy": ["**/*.pipeline", "**/*.multibranch"],
"Terraform": ["**/*.tfvars"],
},
"languages": {
"JavaScript": {
"language_servers": ["typescript-language-server", "!vtsls", "..."],
},
"TypeScript": {
"language_servers": ["typescript-language-server", "!vtsls", "..."],
},
"TSX": {
"language_servers": ["typescript-language-server", "!vtsls", "..."],
},
"Lua": {
"formatter": {
"external": {
"command": "stylua",
"arguments": [
"--respect-ignores",
"--stdin-filepath",
"{buffer_path}",
"-",
],
},
},
},
"Python": {
"formatter": {
"external": {
"command": "ruff",
"arguments": [
"format",
"--stdin-filename",
"{buffer_path}",
"-",
],
},
},
},
"Shell Script": {
"tab_size": 4,
"hard_tabs": false,
"formatter": {
"external": {
"command": "shfmt",
"arguments": [
"--filename",
"{buffer_path}",
"--indent",
"4",
],
},
},
},
},
}

View file

@ -1,6 +1,6 @@
[user]
name = Brian Pooe
email = bria.method@gmail.com
email = brian.method@gmail.com
[color]
ui = auto
@ -23,17 +23,17 @@ hlog = !git log --pretty=format:\"%h - %an, %ar : %s\" --graph
all = !git log --pretty=oneline --abbrev-commit --all --no-merges
# all commits today for only me
today = !git all --since='12am'
today = !git all --since='12am' --author=brian.method@gmail.com
pom = push origin main
pos = push origin staging
pod = push origin develop
pou = push origin uat
pou = push origin uat
ruo = remote update origin
cma = commit --amend
cmna = commit -n --amend
del = branch -D
cm = commit -m
pf = push -f origin
pf = push --force-with-lease origin
po = push origin
rv = remote -v
rao = remote add origin
@ -43,13 +43,19 @@ stash-a = stash apply
undo-commit = reset --soft HEAD~1
stash-unapply = !git stash show -p | git apply -R
del-local = "!f() { git branch | grep -v 'main' | xargs git branch -d; }; f"
del-remote = git remote prune
del-remote = remote prune
latest-tag = tag -l --sort=-version:refname
back = !f() { git reset --soft HEAD~$1; }; f
[push]
default = matching
default = simple
[core]
editor=nvim
pager = less -x4
# Machine-specific overrides (e.g. a work identity via includeIf).
# Git silently skips this if the file doesn't exist. Keep last so
# local settings win.
[include]
path = ~/.gitconfig.local

6
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly

57
.github/workflows/validate.yml vendored Normal file
View file

@ -0,0 +1,57 @@
name: Validate
on:
push:
pull_request:
jobs:
dotfiles:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install validation tools
run: |
sudo apt-get update
sudo apt-get install --yes build-essential curl file git gnupg procps shellcheck shfmt zsh
source install/lib/common.sh
source install/lib/brew.sh
source install/lib/packages.sh
install_wezterm
install_homebrew
brew install neovim starship stylua
echo "$BREW_PREFIX/bin" >> "$GITHUB_PATH"
- name: Restore Neovim plugins
env:
DOTFILES_CI: "1"
XDG_CONFIG_HOME: ${{ github.workspace }}/.config
run: nvim --headless "+Lazy! restore" +qa
- name: Validate
run: bin/check-dotfiles
# End-to-end installer test: the dotfiles job validates the configs but
# never runs install.sh itself, which is what rots (apt repo URLs, GPG
# fingerprints, pinned checksums). Run the full install as a normal user
# in a pristine container.
installer:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Run full installer in a fresh container
run: |
docker run --rm -v "$PWD:/src:ro" ubuntu:24.04 bash -ec '
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y --no-install-recommends sudo curl ca-certificates git
useradd -m -s /bin/bash tester
echo "tester ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/tester
cp -r /src /home/tester/dotfiles
chown -R tester:tester /home/tester/dotfiles
su tester -c "cd ~/dotfiles && install/install.sh"
su tester -c "cd ~/dotfiles && install/install.sh --unlink"
'

43
.gitignore vendored
View file

@ -1,56 +1,41 @@
# Created by https://www.toptal.com/developers/gitignore/api/vim
# Edit at https://www.toptal.com/developers/gitignore?templates=vim
### Vim ###
# Swap
# Vim
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]
# Session
Session.vim
Sessionx.vim
# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~
.nvimlog
nvim.log
.config/zed/
# Track only the managed application configurations.
.config/*
!.config/bat/
!.config/nvim/
!.config/lazygit/
!.config/starship/
!.config/wezterm/
# machine-local, generated by .zshrc
.config/wezterm/env.lua
!.config/zed/
!.config/zed/settings.json
!.config/zed/keymap.json
!.config/tmux/
!.config/tmux/nord-theme.conf
!.config/tmux/dragon-theme.conf
!.config/tmux/wave-theme.conf
!.config/tmux/tmux-cheatsheet.md
!.config/tmux/tmux.conf
!.config/.vimrc
!.config/.ideavimrc
# Ignore subfolders in tmux
# Ignore generated or downloaded tmux content.
.config/tmux/**
!.config/tmux/nord-theme.conf
!.config/tmux/dragon-theme.conf
!.config/tmux/wave-theme.conf
!.config/tmux/tmux-cheatsheet.md
!.config/tmux/tmux.conf
!.config/.vimrc
!.config/.ideavimrc
.nvimlog
# End of https://www.toptal.com/developers/gitignore/api/vim
# Track only the ssh client config — never keys or anything else.
.ssh/*
!.ssh/config
# Track only Claude Code settings — never history, sessions, or caches.
.claude/*
!.claude/settings.json

11
.ssh/config Normal file
View file

@ -0,0 +1,11 @@
Host caddy
HostName 10.0.15.5
User brian
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
Host technitium
HostName 10.0.10.5
User brian
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes

View file

@ -1 +0,0 @@
source-file ~/.config/tmux/tmux.conf

93
.zshrc
View file

@ -3,14 +3,24 @@
if [ -x /home/linuxbrew/.linuxbrew/bin/brew ]; then
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
fi
export PATH="$HOME/.local/bin:$PATH"
# Persistent command history. Keep HISTSIZE larger than SAVEHIST so zsh has
# room to merge history from other active shells before trimming the file.
HISTFILE="$HOME/.zsh_history"
HISTSIZE=100000
SAVEHIST=80000
setopt SHARE_HISTORY
setopt HIST_REDUCE_BLANKS
setopt HIST_IGNORE_ALL_DUPS
setopt HIST_IGNORE_SPACE
# ===================== User-tunable environment ========================
# Single place to control the look of the whole stack. Everything below
# (and the wezterm env.lua writer further down) derives from these.
export KANAGAWA_THEME="${KANAGAWA_THEME:-wave}" # wave | dragon
export WEZTERM_FONT_SIZE="${WEZTERM_FONT_SIZE:-16}"
export WEZTERM_WINDOW_OPACITY="${WEZTERM_WINDOW_OPACITY:-0.80}"
export WEZTERM_TEXT_OPACITY="${WEZTERM_TEXT_OPACITY:-1.0}"
# Single place to control the look of the whole stack.
export KANAGAWA_THEME="wave" # wave | dragon
export WEZTERM_FONT_SIZE="13"
export WEZTERM_WINDOW_OPACITY="0.9"
export WEZTERM_TEXT_OPACITY="1.0"
# ========================================================================
# Theme (kanagawa variants: dragon|wave)
@ -31,32 +41,11 @@ case "${KANAGAWA_THEME}" in
;;
esac
# Write a deterministic tmux theme selector file so tmux servers don't depend
# on inherited shell env vars.
export TMUX_THEME_FILE="$HOME/.config/tmux/current-theme.conf"
mkdir -p "$HOME/.config/tmux"
if [ "$TMUX_THEME" = "wave" ]; then
printf 'source-file %s\n' "$HOME/.config/tmux/wave-theme.conf" >| "$TMUX_THEME_FILE"
else
printf 'source-file %s\n' "$HOME/.config/tmux/dragon-theme.conf" >| "$TMUX_THEME_FILE"
# Generate the theme files consumed by GUI-launched WezTerm and tmux.
if command -v dotfiles-apply-theme >/dev/null 2>&1; then
dotfiles-apply-theme
fi
# Write WezTerm settings file (same pattern as tmux above). GUI-launched
# WezTerm never sees shell exports, so it watches this file and live-reloads
# when the values change.
_wez_env_file="$HOME/.config/wezterm/env.lua"
_wez_env_content="return {
theme = \"$WEZTERM_THEME\",
font_size = $WEZTERM_FONT_SIZE,
window_opacity = $WEZTERM_WINDOW_OPACITY,
text_opacity = $WEZTERM_TEXT_OPACITY,
}"
mkdir -p "$HOME/.config/wezterm"
if [ ! -f "$_wez_env_file" ] || [ "$(cat "$_wez_env_file")" != "$_wez_env_content" ]; then
printf '%s\n' "$_wez_env_content" >| "$_wez_env_file"
fi
unset _wez_env_file _wez_env_content
# Starship
if command -v starship >/dev/null 2>&1; then
eval "$(starship init zsh)"
@ -67,10 +56,10 @@ if command -v zoxide >/dev/null 2>&1; then
eval "$(zoxide init zsh)"
fi
# Syntax highlighting (try Debian/Ubuntu path first, then Arch)
# Syntax highlighting
for _p in \
/usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh \
/usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
"$HOMEBREW_PREFIX/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" \
/usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
do
[ -f "$_p" ] && source "$_p" && break
done
@ -81,9 +70,12 @@ ZSH_HIGHLIGHT_STYLES[path]=none
ZSH_HIGHLIGHT_STYLES[path_prefix]=none
# Autosuggestions
# The plugin default is intentionally dim and becomes unreadable with a
# transparent terminal background.
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE='fg=#938aa9'
for _p in \
/usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh \
/usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
"$HOMEBREW_PREFIX/share/zsh-autosuggestions/zsh-autosuggestions.zsh" \
/usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh
do
[ -f "$_p" ] && source "$_p" && break
done
@ -98,26 +90,17 @@ export FZF_CTRL_T_OPTS="--preview 'bat --color=always -n --line-range :500 {}'"
export FZF_ALT_C_OPTS="--preview 'eza --icons=always --tree --color=always {} | head -200'"
export FZF_TMUX_OPTS=" -p90%,70% "
# fzf key bindings and completion (apt uses /usr/share/doc/fzf/examples)
for _p in \
/usr/share/doc/fzf/examples/key-bindings.zsh \
/usr/share/fzf/key-bindings.zsh
do
[ -f "$_p" ] && source "$_p" && break
done
for _p in \
/usr/share/doc/fzf/examples/completion.zsh \
/usr/share/fzf/completion.zsh
do
[ -f "$_p" ] && source "$_p" && break
done
# fzf key bindings and completion
if command -v fzf >/dev/null 2>&1; then
source <(fzf --zsh)
fi
unset _p
# -----------------------------
unalias lt 2>/dev/null
lt() {
local level=${1:-3}
shift
(( $# )) && shift
eza --tree --all --level "$level" "$@"
}
@ -133,10 +116,20 @@ alias sudo="sudo "
alias lg="lazygit"
alias update="brew upgrade && sudo apt update && sudo apt upgrade -y"
if command -v tmux &>/dev/null && [ -z "$TMUX" ]; then
export DOTFILES_AUTO_TMUX="${DOTFILES_AUTO_TMUX:-0}"
if [ "$DOTFILES_AUTO_TMUX" = "1" ] \
&& command -v tmux &>/dev/null \
&& [ -z "$TMUX" ] \
&& [ -z "$SSH_CONNECTION" ]
then
tmux attach -t default 2>/dev/null || tmux new -s default
fi
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
# Machine-specific overrides (secrets, work proxies, installer PATH
# additions). Never tracked in the repo — keep this line last so local
# settings win.
[ -f "$HOME/.zshrc.local" ] && source "$HOME/.zshrc.local"

View file

@ -1,27 +1,16 @@
# dotfiles
Linux dotfiles oriented around **Pop!_OS 24.04** (Cosmic DE). Plain Debian
package paths in `.zshrc` with a fallback for Arch, so they also work on Arch
machines if you ever switch back.
Linux dotfiles oriented around **Pop!_OS 24.04** (Cosmic DE).
## Quick Setup
```bash
# Clone
git clone <repo-url> ~/dotfiles
# Symlink configs
ln -sfn ~/dotfiles/.config/nvim ~/.config/nvim
ln -sfn ~/dotfiles/.config/tmux ~/.config/tmux
ln -sfn ~/dotfiles/.config/starship ~/.config/starship
ln -sfn ~/dotfiles/.config/lazygit ~/.config/lazygit
ln -sfn ~/dotfiles/.config/wezterm ~/.config/wezterm
ln -sfn ~/dotfiles/.config/zed ~/.config/zed
ln -sfn ~/dotfiles/.config/.ideavimrc ~/.ideavimrc
ln -sfn ~/dotfiles/.zshrc ~/.zshrc
ln -sfn ~/dotfiles/.gitconfig ~/.gitconfig
git clone https://github.com/brianpooe/dotfiles.git ~/dotfiles
~/dotfiles/install/install.sh --dotfiles-only
```
The installer backs up existing targets before linking replacements.
## Install
On Pop!_OS / Ubuntu 24.04, run the bundled installer. It uses **apt** for
@ -35,7 +24,8 @@ starship, gh, zsh plugins) so those tools stay current — one
```
See [`install/README.md`](install/README.md) for flags and the full
package list. The `update` alias runs `brew upgrade && sudo apt upgrade`.
package list. The `update` alias runs
`brew upgrade && sudo apt update && sudo apt upgrade -y`.
## Theme
@ -47,3 +37,19 @@ Set in shell:
export KANAGAWA_THEME="wave" # or "dragon"
```
Opening a new shell runs `dotfiles-apply-theme`, which writes the generated
tmux and WezTerm settings to `~/.cache/dotfiles`.
Automatic tmux startup is opt-in:
```bash
export DOTFILES_AUTO_TMUX=1
```
## Validate
```bash
bin/check-dotfiles
```
The same checks run in GitHub Actions.

60
bin/check-dotfiles Executable file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo_root"
export PATH="$HOME/.local/share/nvim/mason/bin:$PATH"
require_cmd() {
command -v "$1" >/dev/null 2>&1 || {
printf 'Required validation tool not found: %s\n' "$1" >&2
exit 1
}
}
for command in bash git nvim shellcheck shfmt starship stylua wezterm zsh; do
require_cmd "$command"
done
shell_files=(bin/dotfiles-apply-theme bin/check-dotfiles install/install.sh install/lib/*.sh)
git diff --check
bash -n "${shell_files[@]}"
zsh -n .zshrc
git config --file .gitconfig --list >/dev/null
shellcheck --exclude=SC1091 "${shell_files[@]}"
shfmt -d -i 2 -ci "${shell_files[@]}"
stylua --check .config/nvim
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
mkdir -p "$tmp/data/nvim"
ln -s "$HOME/.local/share/nvim/lazy" "$tmp/data/nvim/lazy"
HOME="$tmp/home" \
KANAGAWA_THEME=wave \
WEZTERM_FONT_SIZE=13 \
WEZTERM_WINDOW_OPACITY=.9 \
WEZTERM_TEXT_OPACITY=1 \
bin/dotfiles-apply-theme
STARSHIP_CONFIG="$repo_root/.config/starship/starship.toml" starship print-config >/dev/null
STARSHIP_CONFIG="$repo_root/.config/starship/starship-wave.toml" starship print-config >/dev/null
wezterm --config-file "$repo_root/.config/wezterm/wezterm.lua" show-keys --lua >/dev/null
nvim_output="$(
DOTFILES_CI=1 \
XDG_CONFIG_HOME="$repo_root/.config" \
XDG_CACHE_HOME="$tmp/cache" \
XDG_DATA_HOME="$tmp/data" \
XDG_STATE_HOME="$tmp/state" \
nvim --headless '+sleep 100m' '+lua assert(vim.v.errmsg == "", vim.v.errmsg)' '+qa' 2>&1
)"
if [[ -n $nvim_output ]]; then
printf 'Neovim startup produced unexpected output:\n%s\n' "$nvim_output" >&2
exit 1
fi
printf 'All dotfile checks passed.\n'

46
bin/dotfiles-apply-theme Executable file
View file

@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -euo pipefail
theme="${KANAGAWA_THEME:-wave}"
font_size="${WEZTERM_FONT_SIZE:-13}"
window_opacity="${WEZTERM_WINDOW_OPACITY:-0.9}"
text_opacity="${WEZTERM_TEXT_OPACITY:-1.0}"
state_dir="$HOME/.cache/dotfiles"
case "$theme" in
wave | kanagawa-wave) theme=wave ;;
dragon | kanagawa-dragon) theme=dragon ;;
*) theme=dragon ;;
esac
for value in "$font_size" "$window_opacity" "$text_opacity"; do
[[ $value =~ ^([0-9]+([.][0-9]*)?|[.][0-9]+)$ ]] || {
printf 'Invalid numeric theme value: %s\n' "$value" >&2
exit 1
}
done
mkdir -p "$state_dir"
write_if_changed() {
local destination="$1" content="$2" temporary
if [[ -f $destination ]] && [[ $(<"$destination") == "$content" ]]; then
return
fi
temporary="$(mktemp "$state_dir/.theme.XXXXXX")"
printf '%s\n' "$content" >"$temporary"
mv "$temporary" "$destination"
}
write_if_changed "$state_dir/tmux-theme.conf" \
"source-file \"$HOME/.config/tmux/$theme-theme.conf\""
write_if_changed "$state_dir/wezterm-env.lua" "return {
theme = \"$theme\",
font_size = $font_size,
window_opacity = $window_opacity,
text_opacity = $text_opacity,
}"

View file

@ -1,71 +0,0 @@
# Omarchy Install Script — AI Prompt
This is a ready-to-paste prompt for any capable AI to generate a customized
Omarchy-derived install script that integrates these dotfiles.
---
You are going to create a Linux install shell script derived from **Omarchy** (DHH's opinionated Arch + Hyprland setup — https://omarchy.org, https://github.com/basecamp/omarchy). Target: single-user Arch-based Hyprland desktop.
Produce a clean, idempotent `install.sh` (`bash`, `set -euo pipefail`) plus supporting files, structured into small sourced functions so each step is auditable. Do not leave stubs, dead menu entries, or commented-out code. If a removal empties a parent menu, remove the parent too.
## 1. Packages to remove from the install
- **Shell tools:** `try`
- **TUIs:** Lazydocker, Clamp (match Omarchy's spelling — `cliamp`/`climp`)
- **GUIs:** Obsidian, Pinta, Signal, OBS Studio, Kdenlive
- **Commercial apps:** remove every entry in Omarchy's "Commercial apps" install group **and** the "Commercial apps" menu/submenu itself.
- **Default web apps (frameless PWA wrappers):** remove all of them — the install hook, every generated `.desktop` file, and shipped icons.
## 2. Browser swap
- Do **not** install Chromium.
- Install **Brave Beta** (https://brave.com/origin/linux/beta/) and wire it as default everywhere Omarchy referenced Chromium: `xdg-mime` defaults, `$BROWSER`, Hyprland keybinds, walker/wofi entries, any remaining PWA launcher.
- Prefer an official scriptable Arch install path for the Beta channel; if no Beta AUR package exists, fall back to the Beta artifact from the URL above via a documented method. Do **not** silently substitute stable Brave.
## 3. Terminal swap — Alacritty → WezTerm, themed by Omarchy
- Replace Alacritty with **WezTerm** everywhere Omarchy sets the default terminal: `$TERMINAL`, Hyprland `Super+Return` (and any terminal keybind), walker/wofi, `xdg` terminal handling, and any scripts that call `alacritty` (e.g. Omarchy's `omarchy-launch-*` helpers).
- Make WezTerm follow Omarchy's live theme. Omarchy switches themes by symlinking the active theme into `~/.config/omarchy/current/theme/` and each theme ships an `alacritty.toml`. Implement this:
1. Add a generator (`omarchy-wezterm-theme` or similar) that converts the current theme's `alacritty.toml` color table into a WezTerm color table written to `~/.config/omarchy/current/theme/wezterm.lua` (returning a Lua table of `foreground/background/cursor/ansi/brights/selection`).
2. Run the generator during install and **hook it into `omarchy-theme-set`** so it regenerates on every theme switch, then signals WezTerm to reload (WezTerm auto-reloads config; ensure `automatically_reload_config = true`).
3. Ship a WezTerm config that loads it: `local ok, theme = pcall(dofile, wezterm.home_dir .. "/.config/omarchy/current/theme/wezterm.lua"); if ok then config.colors = theme end`, with a safe fallback if the file is absent.
- Confirm every Omarchy theme that previously had `alacritty.toml` now yields a working WezTerm palette.
## 4. Integrating these dotfiles (repo: brianpooe/dotfiles)
These dotfiles are the source of truth for editor/shell/terminal config. Integrate them into the install **without breaking Omarchy's theme switcher**. Read the repo first; key facts:
- **Theming is env-var-driven (Kanagawa).** `.zshrc` reads `KANAGAWA_THEME` (`wave`|`dragon`) and exports `NVIM_THEME / TMUX_THEME / STARSHIP_THEME / WEZTERM_THEME / STARSHIP_CONFIG`, and writes `~/.config/tmux/current-theme.conf`. `wezterm.lua`, `tmux.conf`, `theme.lua` (kanagawa.nvim) and starship all consume these.
- **This conflicts with Omarchy's per-app theme switching.** Resolve it explicitly — pick one and implement consistently, do not half-wire both:
- **Option A (recommended):** WezTerm follows Omarchy's live theme (section 3); the editor/shell stack (nvim, tmux, starship) stays Kanagawa via the dotfiles' `KANAGAWA_THEME` env chain, independent of Omarchy. Document that terminal colors and editor colors are decoupled by design. To avoid a clash, drop the dotfiles' hardcoded Kanagawa palette from `wezterm.lua` and replace it with the Omarchy-theme loader from section 3.
- **Option B:** Make nvim/tmux/starship also follow Omarchy themes by having `omarchy-theme-set` set `KANAGAWA_THEME`/regenerate the selector files — only viable for Omarchy themes that have a Kanagawa-equivalent; state the limitation.
- **Deploy via symlinks (GNU stow or `ln -sfn`), idempotently**, mirroring the repo's own README mapping: `nvim, tmux, starship, lazygit, wezterm, zed → ~/.config/...`; `.config/.ideavimrc → ~/.ideavimrc`; `.zshrc`, `.gitconfig → ~`. Back up any pre-existing target before symlinking. Clone the repo to `~/dotfiles` (or accept a path arg) and link from there so `git pull` keeps things live.
- **Distro: the dotfiles are Arch-native — no dnf/Fedora cleanup needed.** Just install their dependencies via Omarchy's package step (pacman/yay): `zsh, zsh-syntax-highlighting, zsh-autosuggestions, fzf, fd, bat, eza, zoxide, tmux, lazygit, neovim, starship`. The `.zshrc` already sources zsh plugins from `/usr/share/zsh/plugins/...` and fzf from `/usr/share/fzf/`, and its `update` alias already uses `pacman -Syu`. Install `nvm` per the dotfiles' note and tpm (`~/.tmux/plugins/tpm`) so tmux works on first launch.
- **WezTerm config reconciliation:** the repo's `wezterm.lua` is already Linux-only (no macOS/Windows branches). Use it as the base, but swap its Kanagawa-by-env palette for the Omarchy-theme loader (section 3) so the chosen option above holds.
- Set **zsh** as the login shell if the dotfiles assume it (they do — `.zshrc` auto-attaches tmux).
## 5. Menu changes — `Super + Alt + Space` launcher
Remove these submenus and all their entries:
- **Setup menu:** System Sleep, DNS, Security
- **Install menu:** Web App, Services, TUI, Development, Editor, Terminal, Browser, AI, Gaming, Windows
If "Setup" or "Install" has no children left, remove the parent. Update keybind hints/docs referencing removed menus.
## 6. Deliverables
1. `install.sh` — idempotent entry point, safe to re-run.
2. Sourced modules (e.g. `lib/packages.sh`, `lib/menus.sh`, `lib/browser.sh`, `lib/terminal.sh`, `lib/dotfiles.sh`).
3. Modified launcher menu config (match Omarchy's walker/wofi/rofi).
4. The WezTerm theme generator + the `omarchy-theme-set` hook.
5. `README.md`: prerequisites, what was removed vs upstream Omarchy, the terminal/browser swaps, how dotfiles are linked, and the theming decision (Option A/B) you made and why.
6. A diff-style summary listing every removal, both swaps, and each dotfile integration point, so a reviewer can verify nothing was missed.
## Constraints
- Linux/Arch only. No macOS/Windows/WSL branches (the dotfiles are already stripped of these — keep them that way).
- Don't invent package or Omarchy script names — if unsure what Omarchy calls something, say so and ask rather than guess.
- shellcheck-clean. No `curl | bash` from untrusted sources. Pin versions where Omarchy does.
- Begin by listing the files you plan to produce, then output each in full.

View file

@ -7,11 +7,15 @@ Ubuntu 24.04 / Debian-derivative) to the state these dotfiles assume.
| Source | What lives there | Why |
|--------|------------------|-----|
| **apt** | `zsh`, `git`, `curl`, `build-essential`, `wl-clipboard`, `wezterm`, `brave-browser-beta` | System base + GUI vendor apps. Vendor apt repos auto-update Brave/WezTerm with `apt upgrade`. |
| **apt** | `zsh`, `git`, `curl`, `build-essential`, `python3`, `python3-pip`, `python3-venv`, `luarocks`, `wl-clipboard`, `wezterm`, `brave-browser-beta` | System base, Mason prerequisites, and GUI vendor apps. Vendor apt repos auto-update Brave/WezTerm with `apt upgrade`. |
| **Homebrew** | `fzf` `fd` `bat` `eza` `zoxide` `ripgrep` `tmux` `neovim` `lazygit` `starship` `gh` `zsh-syntax-highlighting` `zsh-autosuggestions` | CLI toolchain. Ubuntu 24.04's apt versions are frozen at April 2024; brew always has latest. One `brew upgrade` updates all of them. |
| **nvm** | Node.js runtime | The standard nvm one-liner — `.zshrc` already sources `~/.nvm/nvm.sh`. |
| **nvm** | Node.js runtime | Installs the latest LTS release and sets it as the default; `.zshrc` sources `~/.nvm/nvm.sh`. |
| **tpm** | tmux plugin manager | Clone of `tmux-plugins/tpm` so the bundled `tmux.conf` works on first launch. |
Downloaded installer scripts, apt signing keys, and the Nerd Font archive
are pinned and verified before use. Updating their upstream versions requires
updating the corresponding SHA-256 hashes or fingerprints in `install/lib/`.
GitHub-release-binary downloads (the previous approach for nvim/lazygit/
starship) have been retired — they ran latest at install time then went
stale. Brew solves that.
@ -46,7 +50,7 @@ install/install.sh --help
## Keeping current
```bash
update # alias: brew upgrade && sudo apt upgrade
update # alias: brew upgrade && sudo apt update && sudo apt upgrade -y
```
The CLI tools come from brew, so they stay on bleeding-edge stable. Brave
@ -63,20 +67,22 @@ install/
lib/packages.sh apt base + WezTerm + Brave Beta vendor repos
lib/brew.sh install Homebrew + `brew bundle`
lib/extras.sh nvm + tpm
lib/fonts.sh verified JetBrainsMono Nerd Font install
lib/dotfiles.sh symlink dotfiles + chsh zsh
```
## What the installer does, in order
1. **preflight** — verify apt + not-root + sudo + curl.
2. **apt base**`zsh git curl wget ca-certificates gnupg build-essential procps file unzip xz-utils wl-clipboard`.
2. **apt base**system packages plus Mason's Python venv/pip and LuaRocks prerequisites.
3. **WezTerm apt repo**`apt install wezterm`.
4. **Brave Beta apt repo**`apt install brave-browser-beta`.
5. **Homebrew install** (NONINTERACTIVE) + `brew bundle` against `Brewfile`.
6. **nvm** (pinned `$NVM_VERSION`, default `v0.40.1`).
6. **nvm** (pinned `$NVM_VERSION`, default `v0.40.1`) + latest LTS Node.js.
7. **tpm** clone to `~/.tmux/plugins/tpm`.
8. **Symlink** `nvim tmux starship lazygit wezterm zed → ~/.config/...`;
`.config/.ideavimrc → ~/.ideavimrc`; `.zshrc`, `.gitconfig → ~`.
8. **Symlink** `nvim tmux starship lazygit wezterm → ~/.config/...`;
`.config/.ideavimrc → ~/.ideavimrc`; `.zshrc`, `.gitconfig → ~`;
`dotfiles-apply-theme → ~/.local/bin/`.
9. **chsh** login shell to zsh.
Pre-existing config targets get backed up to `*.pre-install.<timestamp>`,

View file

@ -2,12 +2,12 @@
# Pop!_OS / Ubuntu installer for these dotfiles.
#
# What it does (idempotent — safe to re-run):
# 1. apt: system base (zsh, git, curl, build-essential, wl-clipboard).
# 1. apt: system base + Mason prerequisites (Python venv/pip, LuaRocks).
# 2. Vendor apt repos: WezTerm, Brave Beta (GUI apps stay on apt).
# 3. Homebrew + Brewfile: fzf, fd, bat, eza, zoxide, ripgrep, tmux,
# neovim, lazygit, starship, gh, zsh plugins — always latest,
# updated later with a single `brew upgrade`.
# 4. nvm + tmux plugin manager (tpm).
# 4. nvm + latest LTS Node.js + tmux plugin manager (tpm).
# 5. Symlinks dotfiles into ~ and ~/.config.
# 6. Switches login shell to zsh.
#
@ -15,6 +15,7 @@
# install/install.sh Full install
# install/install.sh --packages-only Only packages/tools (apt + brew)
# install/install.sh --dotfiles-only Only link dotfiles + tpm/nvm + chsh
# install/install.sh --unlink Remove symlinks pointing into this repo
# install/install.sh --help
set -eEo pipefail
@ -28,7 +29,7 @@ source "$SCRIPT_DIR/lib/fonts.sh"
source "$SCRIPT_DIR/lib/dotfiles.sh"
usage() {
sed -n '2,18p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
sed -n '2,19p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
}
MODE=all
@ -36,7 +37,11 @@ for arg in "$@"; do
case $arg in
--packages-only) MODE=packages ;;
--dotfiles-only) MODE=dotfiles ;;
-h|--help) usage; exit 0 ;;
--unlink) MODE="unlink" ;;
-h | --help)
usage
exit 0
;;
*) die "Unknown argument: $arg (try --help)" ;;
esac
done
@ -46,9 +51,16 @@ preflight() {
((EUID != 0)) || die "Do not run as root; run as your regular user."
require_cmd sudo
require_cmd curl
require_cmd sha256sum
}
main() {
# Unlinking only removes symlinks; skip preflight and the closing hints.
if [[ $MODE == unlink ]]; then
unlink_dotfiles
return
fi
preflight
case $MODE in
@ -76,7 +88,7 @@ main() {
info "Done. Open a new shell (or log out + back in) to pick up the new login shell."
info "First tmux launch: prefix + I to fetch tpm plugins."
info "Keep tools current with: brew upgrade && sudo apt upgrade"
info "Keep tools current with: brew upgrade && sudo apt update && sudo apt upgrade -y"
}
main

View file

@ -3,14 +3,25 @@
# Brew owns the CLI toolchain so `brew upgrade` always gets latest versions.
BREW_PREFIX="/home/linuxbrew/.linuxbrew"
HOMEBREW_INSTALL_COMMIT="93a25fd2d2fdb86422303c292b9223f84ef23eaf"
HOMEBREW_INSTALL_SHA256="17f204b128869ec71e6b650051f1e65ef5b74cf994c2ff8b919103241554dd2f"
install_homebrew() {
if [[ -x $BREW_PREFIX/bin/brew ]]; then
info "Homebrew already installed"
else
info "Installing Homebrew..."
NONINTERACTIVE=1 bash -c \
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
local installer
installer="$(mktemp)"
download_verified \
"https://raw.githubusercontent.com/Homebrew/install/$HOMEBREW_INSTALL_COMMIT/install.sh" \
"$HOMEBREW_INSTALL_SHA256" \
"$installer"
if ! NONINTERACTIVE=1 bash "$installer"; then
rm -f "$installer"
die "Homebrew installer failed"
fi
rm -f "$installer"
fi
eval "$("$BREW_PREFIX/bin/brew" shellenv)"
}

View file

@ -8,12 +8,49 @@ APT_LISTS_DIR="/etc/apt/sources.list.d"
info() { printf '\033[0;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mWARNING:\033[0m %s\n' "$*" >&2; }
error() { printf '\033[0;31mERROR:\033[0m %s\n' "$*" >&2; }
die() { error "$*"; exit 1; }
die() {
error "$*"
exit 1
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Required command not found: $1"
}
download_verified() {
local url="$1" sha256="$2" destination="$3"
if ! curl -fsSL --retry 3 "$url" -o "$destination"; then
rm -f "$destination"
die "Failed to download $url"
fi
if ! printf '%s %s\n' "$sha256" "$destination" | sha256sum --check --status -; then
rm -f "$destination"
die "SHA-256 verification failed for $url"
fi
}
key_fingerprints_match() {
local key="$1" expected_fingerprints="$2"
local actual_fingerprints normalized_expected
actual_fingerprints="$(
gpg --show-keys --with-colons "$key" 2>/dev/null |
awk -F: '$1 == "fpr" { print $10 }' |
sort -u |
tr '\n' ' '
)"
normalized_expected="$(
for fingerprint in $expected_fingerprints; do
printf '%s\n' "$fingerprint"
done |
sort -u |
tr '\n' ' '
)"
[[ -n $actual_fingerprints && $actual_fingerprints == "$normalized_expected" ]]
}
apt_install() {
sudo apt-get install -y --no-install-recommends "$@"
}
@ -45,15 +82,14 @@ backup_and_link() {
info "Linked $dest -> $src"
}
# Add a signed apt repo. Args: name keyring_url repo_line
# Downloads the key to a temp file first and verifies it, so a failed
# fetch never leaves an empty keyring or partial list file behind.
# Add a signed apt repo. Args: name keyring_url repo_line expected_fingerprints
add_apt_repo() {
local name="$1" keyring_url="$2" repo_line="$3"
local name="$1" keyring_url="$2" repo_line="$3" expected_fingerprints="$4"
local keyring="$KEYRINGS_DIR/$name.gpg"
local list="$APT_LISTS_DIR/$name.list"
if [[ -f $list && -s $keyring ]]; then
if [[ -f $list && -s $keyring ]] &&
key_fingerprints_match "$keyring" "$expected_fingerprints"; then
return 0
fi
info "Adding apt repo: $name"
@ -68,7 +104,10 @@ add_apt_repo() {
fi
[[ -s $tmp_key ]] || die "Downloaded key for $name is empty"
if ! gpg --yes --dearmor -o "$tmp_gpg" < "$tmp_key" 2>/dev/null; then
key_fingerprints_match "$tmp_key" "$expected_fingerprints" ||
die "Signing-key fingerprint verification failed for $name"
if ! gpg --yes --dearmor -o "$tmp_gpg" <"$tmp_key" 2>/dev/null; then
# Already binary (.gpg) keys can't be dearmored; use the raw bytes instead.
cp "$tmp_key" "$tmp_gpg"
fi
@ -76,5 +115,5 @@ add_apt_repo() {
sudo install -d -m 0755 "$KEYRINGS_DIR"
sudo install -m 0644 "$tmp_gpg" "$keyring"
echo "$repo_line" | sudo tee "$list" >/dev/null
unset _APT_UPDATED # force re-update so the new repo is seen
unset _APT_UPDATED # force re-update so the new repo is seen
}

View file

@ -1,17 +1,46 @@
#!/bin/bash
# Symlink the dotfiles into ~ and ~/.config; switch login shell to zsh.
# Each entry: "<repo-relative source>|<absolute destination>".
# Drives both link_dotfiles and unlink_dotfiles — add new links here.
dotfile_links() {
cat <<EOF
.config/nvim|$HOME/.config/nvim
.config/tmux|$HOME/.config/tmux
.config/starship|$HOME/.config/starship
.config/lazygit|$HOME/.config/lazygit
.config/wezterm|$HOME/.config/wezterm
.config/bat|$HOME/.config/bat
.config/.ideavimrc|$HOME/.ideavimrc
.zshrc|$HOME/.zshrc
.gitconfig|$HOME/.gitconfig
.ssh/config|$HOME/.ssh/config
.claude/settings.json|$HOME/.claude/settings.json
bin/dotfiles-apply-theme|$HOME/.local/bin/dotfiles-apply-theme
EOF
}
link_dotfiles() {
info "Linking dotfiles from $DOTFILES_DIR..."
backup_and_link "$DOTFILES_DIR/.config/nvim" "$HOME/.config/nvim"
backup_and_link "$DOTFILES_DIR/.config/tmux" "$HOME/.config/tmux"
backup_and_link "$DOTFILES_DIR/.config/starship" "$HOME/.config/starship"
backup_and_link "$DOTFILES_DIR/.config/lazygit" "$HOME/.config/lazygit"
backup_and_link "$DOTFILES_DIR/.config/wezterm" "$HOME/.config/wezterm"
backup_and_link "$DOTFILES_DIR/.config/zed" "$HOME/.config/zed"
backup_and_link "$DOTFILES_DIR/.config/.ideavimrc" "$HOME/.ideavimrc"
backup_and_link "$DOTFILES_DIR/.zshrc" "$HOME/.zshrc"
backup_and_link "$DOTFILES_DIR/.gitconfig" "$HOME/.gitconfig"
mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
local entry
while IFS= read -r entry; do
backup_and_link "$DOTFILES_DIR/${entry%%|*}" "${entry#*|}"
done < <(dotfile_links)
}
unlink_dotfiles() {
info "Removing symlinks that point into $DOTFILES_DIR..."
local entry dest
while IFS= read -r entry; do
dest="${entry#*|}"
if [[ -L $dest && $(readlink "$dest") == "$DOTFILES_DIR"/* ]]; then
rm "$dest"
info "Unlinked $dest"
fi
done < <(dotfile_links)
info "Any .pre-install.* backups were left in place; restore them manually."
}
set_zsh_as_shell() {

View file

@ -1,17 +1,32 @@
#!/bin/bash
# Per-user helpers not managed by brew: nvm (official installer) + tpm.
NVM_VERSION="${NVM_VERSION:-v0.40.1}"
NVM_VERSION="${NVM_VERSION:-v0.40.5}"
NVM_INSTALL_SHA256="${NVM_INSTALL_SHA256:-582070e4c44452c1d8d68e16fc786c2216ecba6bc6bf18dc280a03fdba6ed1a9}"
install_nvm() {
if [[ -s $HOME/.nvm/nvm.sh ]]; then
info "nvm already installed"
return
else
info "Installing nvm ($NVM_VERSION)..."
local installer
installer="$(mktemp)"
download_verified \
"https://raw.githubusercontent.com/nvm-sh/nvm/$NVM_VERSION/install.sh" \
"$NVM_INSTALL_SHA256" \
"$installer"
PROFILE=/dev/null bash "$installer" ||
warn "nvm install failed (install manually if you need Node)"
rm -f "$installer"
fi
if [[ -s $HOME/.nvm/nvm.sh ]]; then
# Mason needs npm for its Node-backed language servers and tools.
# Keep Node owned by nvm rather than installing the distro nodejs package.
source "$HOME/.nvm/nvm.sh"
nvm install --lts
nvm alias default 'lts/*'
fi
info "Installing nvm ($NVM_VERSION)..."
PROFILE=/dev/null bash -c \
"curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/$NVM_VERSION/install.sh | bash" ||
warn "nvm install failed (install manually if you need Node)"
}
install_tpm() {

View file

@ -6,6 +6,8 @@
NERD_FONT_NAME="JetBrainsMono"
NERD_FONT_DIR="$HOME/.local/share/fonts/${NERD_FONT_NAME}NerdFont"
NERD_FONT_VERSION="v3.4.0"
NERD_FONT_SHA256="ef552a3e638f25125c6ad4c51176a6adcdce295ab1d2ffacf0db060caf8c1582"
install_nerd_font() {
if fc-list 2>/dev/null | grep -qi "JetBrainsMono Nerd Font"; then
@ -14,15 +16,12 @@ install_nerd_font() {
fi
info "Installing ${NERD_FONT_NAME} Nerd Font..."
local url="https://github.com/ryanoasis/nerd-fonts/releases/latest/download/${NERD_FONT_NAME}.tar.xz"
local url="https://github.com/ryanoasis/nerd-fonts/releases/download/$NERD_FONT_VERSION/${NERD_FONT_NAME}.tar.xz"
local tmp
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' RETURN
if ! curl -fsSL --retry 3 -o "$tmp/font.tar.xz" "$url"; then
warn "Nerd Font download failed; install manually from https://www.nerdfonts.com"
return
fi
download_verified "$url" "$NERD_FONT_SHA256" "$tmp/font.tar.xz"
mkdir -p "$NERD_FONT_DIR"
tar -xJf "$tmp/font.tar.xz" -C "$NERD_FONT_DIR"

View file

@ -18,19 +18,17 @@ APT_PACKAGES=(
unzip
xz-utils
# Mason prerequisites for Python and Lua packages
python3
python3-pip
python3-venv
luarocks
# Wayland clipboard (Pop!_OS Cosmic is Wayland-native)
wl-clipboard
# fc-cache / fc-list for the Nerd Font install (lib/fonts.sh)
fontconfig
# Runtimes Mason uses to install LSPs / formatters / linters / DAPs.
# Without these, every npm-/pip-/luarocks-backed Mason package fails.
nodejs # node runtime (for ts_ls, vscode-langservers-extracted, etc.)
npm # JS package manager Mason shells out to
python3-pip # pip for debugpy/ruff/vint/etc.
python3-venv # so Mason can create per-package venvs (PEP 668 on Ubuntu 24.04)
luarocks # for luacheck
)
install_apt_packages() {
@ -46,7 +44,8 @@ install_wezterm() {
fi
add_apt_repo wezterm-fury \
https://apt.fury.io/wez/gpg.key \
"deb [signed-by=$KEYRINGS_DIR/wezterm-fury.gpg] https://apt.fury.io/wez/ * *"
"deb [signed-by=$KEYRINGS_DIR/wezterm-fury.gpg] https://apt.fury.io/wez/ * *" \
"0CA603116C960BAFB2BF310BD7BA31CF90C4B319 82FA179A44D70EEF9CDEB52659C703F427E1A6C9"
apt_update_once
apt_install wezterm
}
@ -61,7 +60,8 @@ install_brave_beta() {
# "beta" in the name.
add_apt_repo brave-browser-beta \
https://brave-browser-apt-beta.s3.brave.com/brave-browser-beta-archive-keyring.gpg \
"deb [signed-by=$KEYRINGS_DIR/brave-browser-beta.gpg arch=amd64] https://brave-browser-apt-beta.s3.brave.com/ stable main"
"deb [signed-by=$KEYRINGS_DIR/brave-browser-beta.gpg arch=amd64] https://brave-browser-apt-beta.s3.brave.com/ stable main" \
"56F49901AB19BAF099A95A76C3DE1DD4F661CDCB 8C1F16AB24DF8F75C1CF56595929A141E0E87F1F B721E073B7EF8E56ACC6B23ECBC67D2399225CCF"
apt_update_once
apt_install brave-browser-beta
}