dotfiles are your virtual home... this is my home
| My choice | |
|---|---|
| OS | Arch Linux + WSL2 on Windows 11 |
| Desktop | KDE Plasma |
| Shell | bash |
| Terminal | Konsole (Arch) · Windows Terminal (WSL2) |
| Editor | vim |
| Theme | Solarized dark (everywhere) |
A dotfiles repository is not intended to be forked — it is a personal backup and a source of inspiration. Feel free to take what is useful; sharing configs is great.
- Dependencies
- Bootstrap
- Directory structure
- bash
- vim
- Plugin set
- Language Server Protocol (LSP)
- Linting and autofix (ALE)
- Completion workflow
- Git (fugitive + gitgutter)
- Fuzzy finders (fzf.vim)
- Navigation (easymotion)
- Text manipulation (surround, commentary, unimpaired, repeat)
- Alignment (tabular, easy-align, maketable)
- Incrementing columns (VisIncr)
- Auto-pairs (lexima)
- Buffer and file navigation (dirvish, bbye, tagbar)
- Folding (FoldFocus, native markers)
- LaTeX (vimtex)
- Markdown (markdown-preview)
- Encryption (gnupg)
- Arithmetic (HowMuch)
- Per-filetype configuration
- Key mappings reference
- git
- modules — HPC environments
- claude
- Layout
- Global configuration (CLAUDE.md, settings.json)
- Status line
- Slash commands
- Skills — three-class machinery (skills-apply)
- Claude CLI wrappers — overview
- Cloud: Anthropic
- Cloud: OpenRouter
- Cloud: Z.ai
- Local: three backends (ollama, llama.cpp, ik_llama.cpp)
- Local server management (llm-local-server)
- Ollama utilities
- llama.cpp utilities
- Machine-specific overrides
- Environment reference
- scripts
- Extending the dotfiles
- Copyrights
| Tool | Purpose | Arch | Ubuntu / WSL2 |
|---|---|---|---|
| git | Version control | pacman -S git |
apt install git |
| bash ≥ 4.4 | Shell (uses autocd, globstar, histappend) |
pre-installed | apt install bash |
| vim | Editor | pacman -S vim |
apt install vim |
| GNU Stow | Symlink manager — deploys packages | pacman -S stow |
apt install stow |
| Lmod | Environment module system | yay -S lmod (AUR) |
apt install lmod |
| dircolors | Colour-coded ls output (reads ~/.dircolors.256dark) |
coreutils (pre-installed) | coreutils (pre-installed) |
| Tool | Purpose | Arch | Ubuntu / WSL2 |
|---|---|---|---|
| fzf | Fuzzy finder — used by vim (fzf.vim) and shell |
pacman -S fzf |
apt install fzf |
| rsync | synccp / syncmv shell functions |
pacman -S rsync |
apt install rsync |
| bc | Arithmetic in shell functions | pacman -S bc |
apt install bc |
| lesspipe | Rich pager for binary files | pacman -S lesspipe |
apt install lesspipe |
| python3 | Claude Code status line, helper scripts | pacman -S python |
apt install python3 |
| curl | Ollama API calls in claude_code helpers |
pre-installed | apt install curl |
These are not packaged in distro repos. Install to the paths expected by each
modulefile (see modules/.modules/<name>/<version>.lua for the exact root).
| Toolchain | Source |
|---|---|
| NVIDIA HPC SDK | developer.nvidia.com/hpc-sdk |
| Intel oneAPI Base + HPC Toolkit | intel.com/oneapi |
| AMD AOCC | developer.amd.com/amd-aocc |
| OpenMPI | open-mpi.org — build from source against the desired compiler |
| Tool | Purpose | Arch | Ubuntu / WSL2 |
|---|---|---|---|
| Ollama | Local LLM server for claude-local |
yay -S ollama (AUR) |
curl -fsSL https://ollama.com/install.sh | sh |
| Claude Code | AI coding assistant (claude CLI) |
npm install -g @anthropic-ai/claude-code |
same |
| nvm | Node.js version manager | github.com/nvm-sh/nvm | same |
| pnpm | Fast Node package manager | npm install -g pnpm |
same |
| texlive + latexmk | LaTeX compilation | pacman -S texlive-core latexmk |
apt install texlive latexmk |
| ImageMagick | Image conversion scripts in scripts/images/ |
pacman -S imagemagick |
apt install imagemagick |
| ffmpeg | Video-to-GIF and media helpers | pacman -S ffmpeg |
apt install ffmpeg |
| exiftool | EXIF metadata in image scripts | pacman -S perl-image-exiftool |
apt install libimage-exiftool-perl |
| borg | Automated backup (scripts/borg-automated-backup/) |
pacman -S borg |
apt install borgbackup |
| nvidia-smi | GPU status in ollama-status |
part of NVIDIA driver | part of NVIDIA driver |
Deployment is handled by dotify.sh, a custom symlink script.
It creates ~/.bash/, ~/.vim/, etc. and populates them with symlinks
pointing back into this repository.
# 1. Clone the repository
git clone https://github.com/szaghi/dotfiles ~/dotfiles
# 2. (optional) copy your private bash file
cp your-private-stuff ~/dotfiles/bash/private
# 3. Run the deploy script
bash ~/dotfiles/dotify.shdotify.sh is idempotent — re-running it is safe; it overwrites symlinks with ln -fs.
One third-party tool is tracked as a git submodule:
| Submodule | Purpose |
|---|---|
scripts/bd |
bd — back-directory navigation |
Initialize it after cloning:
git submodule update --initdotfiles/
├── bash/ shell configuration
├── bin/ standalone binaries (act)
├── claude/ Claude Code configuration
├── git/ git configuration and commit template
├── miscellanea/ single-file configs (latexmkrc, NAS mount script)
├── modules/ Lmod modulefiles for HPC toolchains
├── python/ Python env (pythonrc, pylintrc)
├── scripts/ bundled third-party scripts and image utilities
├── usr/ user-level service files
├── vim/ vim configuration and plugins
└── dotify.sh deploy script
bash/bashrc loads modular files from ~/.bash/:
| File | Purpose |
|---|---|
aliases |
Command shortcuts (ll, bd, pacman helpers, LaTeX, git push aliases) |
exports |
Environment variables |
paths |
PATH additions |
functions |
Shell utility functions |
compilers |
Compiler flags and module helpers |
optprogs |
Optional program configuration |
prompt |
Two-line bash prompt with git status integration |
claude_code |
Dual-mode Claude Code setup (local Ollama + cloud Anthropic) |
private |
Machine-specific secrets — not tracked |
The prompt is two-line and git-aware: it shows branch, dirty state, and ahead/behind counts.
Vim is the editor for everything I do — Fortran/HPC, Python, LaTeX, prose.
The setup is a single .vimrc plus per-filetype ftplugins and per-plugin
configuration files, all managed through vim-plug.
Persistent undo, native relative line numbers, LSP-driven navigation and
completion for Fortran/Python/LaTeX/bash, and ALE-powered linting round out
an IDE-grade experience that stays 100% Vim (no Neovim required).
- Leader:
,— all custom mappings are prefixed with,. - Localleader:
,— set beforeplug#beginso vimtex picks it up at load. - Persistent undo:
~/.vim/undo/(auto-created, excluded from stow/git). - Color scheme: Solarized dark with cursorline, 1-column foldcolumn,
signcolumn=yespinned (no gitgutter flicker).
Install / update plugins:
:PlugInstall " install missing plugins after pulling the repo
:PlugUpdate " update all plugins
:PlugClean! " remove plugins no longer listed in .vimrc| Category | Plugins |
|---|---|
| Appearance | vim-colors-solarized, lightline + lightline-bufferline, rainbow_parentheses |
| LSP & linting | yegappan/lsp, ALE |
| Git | vim-fugitive, vim-gitgutter |
| Fuzzy finders | fzf + fzf.vim |
| Navigation | easymotion, vim-dirvish, tagbar, vim-bbye, vim-foldfocus |
| Text editing | vim-surround, vim-repeat, vim-commentary, vim-unimpaired, lexima, VisIncr |
| Alignment | tabular, vim-easy-align, vim-maketable |
| Languages | vimtex, markdown-preview.nvim, python-syntax |
| Utilities | vim-gnupg, HowMuch, plugconf |
Per-plugin config lives in vim/.vim/plugconf/*.vim (loaded by plugconf
after plug#end()).
Provided by yegappan/lsp, a pure-Vim9 LSP client (no Neovim, no Node). Configured servers:
| Language | Server | Install |
|---|---|---|
| Fortran | fortls | pipx install fortls |
| Python | basedpyright | pipx install basedpyright |
| LaTeX | texlab | GitHub release (Ubuntu 24.04 has no apt package) |
| Bash | bash-language-server | npm install -g bash-language-server |
One-shot install for all four plus shellcheck:
~/.scripts/install-vim-lsp.shThe script is idempotent and detects missing binaries, so it is safe to re-run after a distro upgrade or on a fresh machine.
Key mappings (active only in buffers with an attached server):
| Key | Action |
|---|---|
gd |
:LspGotoDefinition |
gr |
:LspShowReferences (populates quickfix) |
K |
:LspHover — docs in a floating popup under the cursor (dismissed on motion) |
<leader>rn |
:LspRename — rename symbol across project |
<leader>la |
:LspCodeAction — quick-fixes and refactors |
<leader>lf |
:LspFormat — format buffer via LSP |
[d / ]d |
Previous / next diagnostic |
<Tab> |
Trigger completion (see below) |
Fortran note. fortls diagnostics are intentionally disabled via
features: { diagnostics: v:false }. fortls's CLI --excl_paths only
accepts absolute paths, so on FoBiS-managed
repos with third_party/*/docs/api/src/ shadow trees it emits spurious
diagnostics for any symbol coming from a shadow-duplicated module.
Navigation, hover, completion and references still work perfectly —
only the red-E> diagnostic signs are suppressed. Projects that actually
want diagnostics can drop a per-project .fortls and flip the feature
back on.
ALE handles linting and fixing
without running its own LSP client (g:ale_disable_lsp = 1 — LSP ownership
stays with yegappan/lsp).
| Filetype | Linter | Fixer |
|---|---|---|
| Python | ruff | ruff + ruff_format on save |
| Bash / sh | shellcheck | — |
| Any | — | trim_whitespace, remove_trailing_lines |
- Python files are automatically formatted with ruff on
:w. Other filetypes only lint — no surprise autofix. - Diagnostic popup under the cursor:
:ALEDetail. - Gutter signs:
✗(error),!(warning).
Completion is manual, not auto-popup (to keep Vim feeling like Vim). The popup menu is triggered from LSP's omnifunc:
| Key (insert mode) | Behavior |
|---|---|
<Tab> |
If popup visible → next item; after a word char → trigger omnicomplete; otherwise → literal <Tab> |
<S-Tab> |
If popup visible → previous item; otherwise → literal <S-Tab> |
<CR> |
If popup visible → accept selected item; otherwise → delegate to lexima (auto-pair Enter) |
<C-x><C-o> |
Manual omnicomplete trigger |
The <Tab> behavior is context-aware: it will not eat indentation
whitespace at the start of a line.
vim-fugitive provides the git
porcelain inside Vim. vim-gitgutter
shows per-hunk change indicators in the signcolumn.
| Key | Action |
|---|---|
<leader>gs |
:Git — status window (stage/unstage with s/u, diff with =) |
<leader>gb |
:Git blame |
<leader>gd |
:Gdiffsplit — 3-way diff in a split |
<leader>gl |
:Git log --oneline --decorate --all |
<leader>gc |
:Git commit |
<leader>gp |
:Git push |
Example — stage a hunk, commit, push from inside Vim without leaving the buffer:
,gs " open status
→ press s on the file to stage
→ press cc to write the commit message
→ :wq to commit
,gp " push
signcolumn=yes is pinned in .vimrc so the gutter never flickers as
gitgutter adds or removes signs.
Requires fzf and ripgrep
on $PATH.
| Key | Action |
|---|---|
<leader>f |
:Files — fuzzy file finder in cwd |
<leader>b |
:Buffers — fuzzy buffer switcher |
<leader>r |
:Rg — ripgrep (type pattern, <CR> to run) |
<leader>t |
:Tags — ctags jump |
<leader>h |
:History — recently-opened files |
<leader>/ |
:Lines — fuzzy search across buffer lines |
easymotion gives 2-keystroke
jumps to anywhere visible. Default aggressive mappings are disabled
(g:EasyMotion_do_mapping = 0) so single-, mappings stay untouched.
| Key | Action |
|---|---|
,,s{char} |
Jump to any occurrence of {char} on screen |
,,w |
Jump to the start of a word |
,,j / ,,k |
Jump down / up to a target line |
,,l / ,,h |
Jump forward / backward within the current line |
Arrow keys for window splits:
| Key | Action |
|---|---|
<A-Up> / <A-Down> / <A-Left> / <A-Right> |
Focus window above / below / left / right |
<C-Right> / <C-Left> |
Next / previous buffer (bnext / bprevious) |
qq |
Close buffer (keeps window layout, via vim-bbye's :Bdelete) |
<C-N> |
Toggle relativenumber on current window |
<leader>v |
Toggle virtualedit=all (cursor allowed in empty columns) |
Four tpope plugins that become muscle memory within days.
vim-surround — manipulate surrounding delimiters:
| Command | Effect |
|---|---|
cs"' |
Change surrounding " to ': "foo" → 'foo' |
ds( |
Delete surrounding (): (foo) → foo |
ysiw] |
Wrap word in []: foo → [foo] |
yss) |
Wrap whole line in () |
S< (visual) |
Wrap selection in <> |
vim-commentary — toggle comments:
| Command | Effect |
|---|---|
gcc |
Toggle comment on current line |
gc{motion} |
Toggle comment over motion (e.g. gcap — around paragraph) |
gc (visual) |
Toggle comment on selection |
vim-unimpaired — paired bracket mappings:
| Command | Effect |
|---|---|
]q / [q |
Next / previous quickfix entry |
]l / [l |
Next / previous location list entry |
]b / [b |
Next / previous buffer |
]<Space> / [<Space> |
Add blank line below / above |
]e / [e |
Swap current line with next / previous |
yo{char} |
Toggle option: yos (spell), yow (wrap), yoh (hlsearch), yol (list), yon (number)… |
vim-repeat — lets . repeat
complex plugin actions (surround, commentary, unimpaired). No mappings;
just works.
Three overlapping plugins, used for different jobs.
vim-easy-align — interactive visual alignment (fastest for one-off work):
" select a visual block, then:
ga= " align on first =
ga*= " align on every =
ga<CR>, " align on , with centered spacing
tabular — scriptable alignment for macros and autocmds:
:Tabularize /=
:Tabularize /|vim-maketable — convert a CSV/TSV selection to a markdown/rst/org table. Useful when pasting data into prose.
VisIncr — fills a visual-block
column with an arithmetic / alphabetical / date / power sequence. More
expressive than Vim's native g<C-a> / g<C-x>.
" visually select a column, then:
:I " increment: 1 2 3 4 ...
:II " decrement
:IA " alphabetical: a b c d ...
:IYMD " dates: 2026-04-24 2026-04-25 ...
:IPOW2 " powers: 1 2 4 8 16 32 ...
Loaded lazily — vim-plug only sources it when one of the I* commands
is invoked.
lexima.vim auto-closes (), [],
{}, "", '' and friends. Smart enough to avoid duplicating the closer
when the cursor is already at it. <CR> inside an empty pair opens a new
indented block:
if foo| — press <CR> → if foo
| |
end
The completion config above wires lexima's <CR> expansion into the
completion-<CR> fallback, so both behaviors coexist.
vim-dirvish — directory browser.
Replaces netrw. :Dirvish or - (dash) opens the current file's directory
as a plain, sortable buffer.
- " open parent dir in dirvish
R " rename with native vim commands on the buffer
:x " filter (e.g. :sort)
Directory-as-buffer model — you :w to persist changes, like a normal file.
vim-bbye — :Bdelete closes a buffer
without destroying the window layout. Mapped to qq.
tagbar — ctags-driven symbol outline. Sidebar on the left, sorted:
| Key | Action |
|---|---|
<F3> |
Toggle tagbar |
Requires ctags (apt install exuberant-ctags or universal-ctags).
For Fortran/Python/LaTeX the LSP symbol lookup (gd, gr) is usually
more accurate; tagbar is the fallback for filetypes without an LSP.
.vimrc sets foldmethod=marker globally so {{{ / }}} triple-brace
markers define folds. Fold column shows as a 1-col gutter.
vim-foldfocus (Python
and Fortran only) opens the current fold in an isolated split for
distraction-free editing:
| Key | Action |
|---|---|
<C-f> |
Open current fold in a vertical split (FoldFocus) |
za |
Native: toggle fold under cursor |
zR / zM |
Native: open all / close all folds |
foldlevelstart=0 — files open fully folded. foldopen is tuned so
most navigation commands auto-open folds as needed.
A dedicated autocmd (augroup folding) temporarily sets foldmethod=manual
during insert mode to prevent fold recalculation on every keystroke in
large Fortran files.
vimtex is loaded for *.tex files.
Compilation uses latexmk (configured globally in ~/.latexmkrc, see
miscellanea/.latexmkrc). PDF viewer: Evince.
Key (localleader = ,) |
Action |
|---|---|
,ll |
Start continuous compilation (:VimtexCompile) |
,lv |
Forward search to PDF viewer (:VimtexView) |
,lc |
Clean aux files |
,le |
Show compilation errors in quickfix |
,li |
Show compilation info |
,lt |
Toggle table of contents |
The localleader = "," is set in .vimrc before plug#begin — setting
it later (e.g. in an ftplugin) is too late, vimtex registers its mappings
at plugin-load time.
markdown-preview.nvim
renders markdown live in the browser. Works in plain Vim despite the .nvim
suffix.
:MarkdownPreview " open preview in default browser
:MarkdownPreviewStop " stop the preview server
Dark theme by default (g:mkdp_theme = 'dark'); see vim/.vim/plugconf/markdown-preview.vim
for all tunables (TOC, KaTeX math, Mermaid, PlantUML, custom CSS).
vim-gnupg transparently
encrypts/decrypts files matching *.gpg, *.pgp, *.asc. Opening a
.gpg file prompts for the passphrase; saving re-encrypts in place.
vim secrets.txt.gpg
" edit normally
:w
" → file is re-encrypted to disk
GPG agent handles passphrase caching.
HowMuch evaluates an arithmetic
expression in the current selection and appends the result. Loaded
lazily via :HowMuch.
" visually select: 2 * (3 + 4)
:HowMuch
" → appends: = 14
Per-filetype settings live in vim/.vim/ftplugin/<filetype>.vim and are
auto-sourced by Vim when a buffer of that type is opened:
| File | Purpose |
|---|---|
fortran.vim |
Per-buffer free/fixed source-form detection (b:fortran_fixed_source), textwidth per form, syntax folding |
python.vim |
4-space indent (overrides the global 3), indent-based folding, python-syntax highlight knobs |
tex.vim |
English spell check, synmaxcol=0 for long equations, vimtex viewer set to Evince |
A custom filetype detection file vim/.vim/ftdetect/fobos.vim treats
any file whose name starts with fobos as dosini (so
FoBiS project files get syntax
highlighting).
Common programming-file behavior (whitespace trimming, blank-line squash,
RainbowParentheses activation, syntax folding) is defined in a single
augroup programming block in .vimrc.
Leader = ,, Localleader = ,. Only custom mappings are listed — native
Vim keys still behave as usual.
Editing / windows
| Key | Mode | Action |
|---|---|---|
/ |
n, x | Search with Perl/Python regex semantics (\v) |
Y |
n | Yank to end of line (analogous to D) |
<NL> (Ctrl-J) |
n | Insert line break at cursor (opposite of J) |
v |
x | Cycle through visual → visual-block → visual-line |
<C-e> / <C-y> |
n | Scroll viewport 2 lines down / up |
<A-↑↓←→> |
n | Focus window above / below / left / right |
<F2> |
n | Toggle line wrap |
<C-N> |
n | Toggle relativenumber |
<leader>v |
n | Toggle virtualedit=all |
Buffers
| Key | Action |
|---|---|
<C-Right> / <C-Left> |
:bnext / :bprevious |
]b / [b |
Same (via unimpaired) |
qq |
:Bdelete (vim-bbye) |
Finders (fzf.vim) — see Fuzzy finders. LSP — see LSP. Git — see Git. Easymotion — see Navigation.
Plugin togglers
| Key | Action |
|---|---|
<F3> |
Toggle tagbar |
<C-f> |
FoldFocus — current fold in vsplit (Python/Fortran only) |
| File | Deployed to | Purpose |
|---|---|---|
git/gitconfig |
~/.gitconfig |
Git identity, aliases, GPG signing |
git/git_commit_message_template |
~/.git/git_commit_message_template |
Conventional Commits template |
Commits follow Conventional Commits:
type(scope): description
Lmod manages compiler toolchains.
Modulefiles live in modules/.modules/ (deployed to ~/.modules/ via stow).
Lmod is initialised automatically in bash/.bash/exports.
Daily usage:
module avail # list all available modules
module load gcc/15.1.0 # load GCC 15.1.0
module load nvhpc/24.11 # load NVIDIA HPC SDK 24.11
module load openmpi/5.0.7-gnu14.2.0 # load OpenMPI (built against GCC 14)
module list # show currently loaded modules
module unload gcc/15.1.0 # unload a single module
module purge # unload everythingThe bash prompt shows loaded modules automatically (env {gcc/15.1.0 openmpi/5.0.7-gnu14.2.0}).
Available modules:
| Module | Description |
|---|---|
gcc/15.1.0 |
GCC 15.1.0 compiler toolchain |
nvhpc/23.1 · 24.11 · 25 · 26 |
NVIDIA HPC SDK (nvfortran, nvc, nvc++) |
intel/oneapi |
Intel oneAPI compilers (ifort, icc, icx) |
amd/5.1.0 |
AMD AOCC 5.1.0 compilers |
openmpi/3.1.5-nvhpc{20.7,22.3,23.1} |
OpenMPI 3.1.5 built against NVHPC |
openmpi/4.1.4-gnu11.2.0 |
OpenMPI 4.1.4 built against GCC 11 |
openmpi/4.1.4-intel2021.5.0 |
OpenMPI 4.1.4 built against Intel 2021 |
openmpi/5.0.7-gnu14.2.0 |
OpenMPI 5.0.7 built against GCC 14 |
Adding a new module:
Modulefiles are Lua scripts. Create one at modules/.modules/<name>/<version>.lua
mirroring the existing ones. Minimal template:
whatis("Short one-line description")
help([[
Long description shown by `module help <name>/<version>`.
]])
local root = "/path/to/install"
family("compiler") -- optional: ensures only one compiler is active at a time
if not isDir(root) then
LmodError(root .. " not found")
end
prepend_path("PATH", pathJoin(root, "bin"))
prepend_path("LD_LIBRARY_PATH", pathJoin(root, "lib"))
prepend_path("MANPATH", pathJoin(root, "share/man"))
setenv("CC", "gcc")
setenv("CXX", "g++")
setenv("FC", "gfortran")Because ~/.modules is a stow symlink pointing into the repo, the new file is
immediately visible to Lmod — no re-stowing needed. Verify with module avail.
Configuration and wrappers for Claude Code, a CLI coding agent. The setup provides a single mental model over three cloud providers (Anthropic, OpenRouter, Z.ai) and three local inference backends (Ollama, llama.cpp, ik_llama.cpp), plus project-aware context, persona instructions and a custom Solarized status line.
Everything is driven by one command — claude-help — which prints the
live quick-reference. If a detail here ever drifts from reality, trust
claude-help and open a PR against this section.
claude/ is a stow package deployed to ~/.claude/:
| File | Purpose |
|---|---|
CLAUDE.md |
Global persona and project rules (loaded into every session) |
settings.json |
Permissions, model default, status-line binding, enabled plugins, marketplaces |
settings.local.json |
Machine-specific overrides — gitignored |
statusline-command.sh |
Custom status line (Python-backed JSON parser, Solarized palette) |
commands/ |
Custom slash commands — /semantic-commit, /capture-findings (see Slash commands) |
skills/ |
Custom user-authored skills (class A) + manifest.toml for third-party loose skills (class C) — see Skills |
The shell wrappers live in a separate package: bash/.bash/claude_code
(≈820 LOC, sourced from ~/.bashrc). Machine-specific knobs (GPU IDs,
binary paths, model defaults) go into ~/.bash/claude_code.local —
loaded automatically at the end of claude_code if present.
CLAUDE.md is the system-prompt overlay, loaded into every Claude
session. It encodes:
- Persona — analytic peer, not service assistant; unvarnished honesty; challenge premises; steel-man before dismantling; Socratic questioning on evaluative disagreements.
- Repo layout rules —
~/fortran/for Fortran,~/python/for Python; verify paths before using them. - Build system rules — FoBiS.py is primary; no make/cmake substitutions;
dependency management via
FoBiS.py fetch, not submodules. - Fortran conventions —
.F90uppercase,implicit noneeverywhere,allocatableoverpointer, column-major loop ordering, kind specifications viaiso_fortran_env,iso_c_bindingfor C interop, strictimplicit SAVEtrap guard, OpenMPdefault(none). - GPU/OpenACC rules —
parallel loopwith explicitgang/vector,!$acc declarediscipline, atomic-ops red-flag, reproducibility caveats. - Python conventions — Ruff patterns (B904 chained exceptions, B905
strict=on zip, RUF002 no Unicode math symbols), PEP 604 union syntax, NumPy-style docstrings for scientific code, Makefile as standard dev interface,.venvalways,git-clifffor changelogs. - Commit rules — Conventional Commits; never
Co-authored-bylines for AI; never auto-commit; GPG signing disabled (no TTY).
settings.json configures the runtime:
| Key | Value | Purpose |
|---|---|---|
permissions.allow |
Read/Write/Edit on ~/fortran/** and ~/python/** |
Frictionless tool use inside my project trees |
model |
opus |
Default cloud model |
statusLine.command |
bash /home/stefano/.claude/statusline-command.sh |
Custom status line |
enabledPlugins |
frontend-design, skill-creator, cli-anything |
Pre-enabled plugins |
extraKnownMarketplaces |
anthropic-agent-skills, cli-anything |
Extra plugin sources |
alwaysThinkingEnabled |
false |
Thinking is opt-in per request |
effortLevel |
high |
Max reasoning effort by default |
skipDangerousModePermissionPrompt |
true |
Skip the --dangerously-skip-permissions warning |
agentPushNotifEnabled |
true |
Push notifications when background agents finish |
statusline-command.sh reads the Claude Code statusLine JSON from stdin,
parses it with a one-shot inline python3 call, and renders a Solarized-dark
status line:
<model>·<GPUs>×GPU │ <cwd-basename> │ <branch>[<dirty>↑n↓m] │ ctx:N% │ 5h:N%(Xh) │ 7d:N%(Xd)
- Model — from the JSON
display_name. On local backends, appended with<N>×GPUwhenCLAUDE_LOCAL_GPUSis exported. - Git — branch,
+staged,!unstaged,?untracked,$stashed,↑N↓Mahead/behind upstream. - Context % — percentage of context window used. Colour-graded green → yellow → orange → red at 50/80/95%.
- Rate limits — 5-hour and 7-day usage percentages with countdown to reset. Same colour ladder.
Requires python3 (used once per render) and works on any terminal with
256-colour support.
Custom slash commands are plain markdown files under claude/.claude/commands/,
deployed by stow as symlinks into ~/.claude/commands/. Lifecycle is identical
to class-A custom skills — git tracks the source, stow deploys symlinks, no
installer or extra machinery.
| Command | Purpose |
|---|---|
/semantic-commit |
Analyze staged diff + recent commit style, emit a Conventional Commits-formatted message (no auto-commit, no Co-authored-by lines) |
/capture-findings |
Capture session findings into repo docs, repo CLAUDE.md / .claude/, and global ~/.claude/CLAUDE.md — auto-detects branch, merge-base, and existing repo Claude config |
Each command is a markdown file with a YAML front-matter declaring its
description and the allowed-tools (whitelisted Bash invocations plus
file ops). The body is the prompt template; lines beginning with !`
are shell substitutions evaluated at invocation time (see
Claude Code custom commands docs).
See commands/semantic-commit.md for a minimal template and
commands/capture-findings.md for a richer one with git-aware substitutions.
Add a new slash command:
# 1. write the command (front-matter + prompt body)
vim ~/dotfiles/claude/.claude/commands/my-command.md
# 2. version it
cd ~/dotfiles && git add claude/.claude/commands/my-command.md
# 3. deploy (idempotent — stow only adds the new symlink)
bash dotify.sh claudeThe new /my-command is immediately available in the next Claude Code
session on this host; other hosts pick it up on their next git pull && bash dotify.sh claude.
Skills in ~/.claude/skills/ fall into three structurally different classes
with three different lifecycle owners. The dotfiles wire all three into one
declarative, per-host workflow via skills-apply (scripts/.scripts/skills-apply,
≈340 LOC bash) plus a TOML manifest. The full design is in
claude/.claude/skills/README.md; the
summary:
| Class | Examples | Lifecycle owner | Where declared |
|---|---|---|---|
| A. Custom user-authored | fobis, research-lookup, markdown-mermaid-writing, markitdown, scientific-writing, generate-image, latex-posters |
git + stow | claude/.claude/skills/<name>/ (real source dirs) |
| B. Plugin / marketplace | frontend-design, skill-creator, cli-anything |
claude plugin CLI |
settings.json → enabledPlugins |
| C. Third-party loose | perplexity-search |
upstream installers (pipx, venv, curl | bash…) |
claude/.claude/skills/manifest.toml |
Per-host filtering of class C: machines/<hostname>.skills (one skill name
per line, blanks and # comments allowed). A missing file means "install
every manifest entry on this host". Class A always stows everywhere
(source-only, cheap); class B is host-uniform via settings.json.
skills-apply interface:
skills-apply install # install everything declared (idempotent)
skills-apply update # update everything installed
skills-apply status # show installed-vs-declared for all three classes
skills-apply remove <name> # uninstall one skill (plugin or manifest)Restart Claude Code after install or update so plugin changes load.
Adding a new skill:
- Class A — drop the directory under
claude/.claude/skills/<name>/(at minimum aSKILL.md),git add, re-stow withbash dotify.sh claude. - Class B —
claude plugin install <name>@<marketplace>, then commit the resultingsettings.jsonchange. Future hosts pick it up viaskills-apply install. - Class C — add a
[skills.<name>]block tomanifest.tomlwithcheck/install/update/uninstallshell commands; add the name to eachmachines/<host>.skillsfile that should sync it.
The new dotfiles dependency this adds is just claude (the Claude Code
CLI binary) — already required as a Development extra. Python 3 (already
required) is reused for inline tomllib parsing of the manifest.
All wrappers in bash/.bash/claude_code call the real claude binary
with a curated set of env vars and CLI flags. None replace the binary —
they're small convenience functions.
Overall flow:
┌────────────────────┐
│ claude-help │ full quick-reference
└────────────────────┘
│
┌─────────────────────────────┼─────────────────────────────┐
│ │ │
CLOUD LOCAL UTILITIES
claude claude-local llm-local-server
claude-sonnet (ollama|llama|ikllama) ollama-{pull,create,…}
claude-opus llama-{models,alias,info}
claude-plan
claude-openrouter
claude-zai[-fast|-turbo|-premium]
claude-local auto-starts the requested backend and auto-stops any
other local backend currently running — only one of ollama/llama/ikllama
is live at a time. llm-local-server is the lower-level management verb.
Uses your standard Anthropic subscription (no extra key file).
| Command | What it does |
|---|---|
claude |
Subscription default (model from settings.json → opus) |
claude-sonnet |
Force Sonnet (claude --model sonnet) |
claude-opus |
Force Opus (claude --model opus) |
claude-plan |
Read-only plan mode (Opus) → auto-switches to Sonnet on execute (--permission-mode plan --model opusplan) |
Routes through OpenRouter, giving access to 100+ models including free tiers.
claude-openrouter # default model
claude-openrouter google/gemma-3-27b-it:free
claude-openrouter anthropic/claude-3.5-sonnet- API key:
~/.openrouter-ai-key(gitignored) orOPENROUTER_API_KEYenv override - Endpoint:
https://openrouter.ai/api(the Anthropic SDK appends/v1/messages) - Default model:
$OPENROUTER_DEFAULT_MODEL(currentlyqwen/qwen3.6-plus-preview:free)
Routes through Z.ai's Anthropic-compatible endpoint for the GLM model family.
| Command | Model |
|---|---|
claude-zai [model] |
default: glm-5-turbo, or any GLM model by ID |
claude-zai-fast |
glm-4.5-air |
claude-zai-turbo |
glm-5-turbo |
claude-zai-premium |
glm-5.1 |
- API key:
~/.z-ai-key(gitignored) orZAI_API_KEYenv override - Endpoint:
https://api.z.ai/api/anthropic
claude-local is a single entry point over three local inference servers.
It auto-starts the requested backend, stops any other local backend
currently running, and routes claude at the right port.
| Backend | Port | Why |
|---|---|---|
| ollama | 11434 | Easy model management (ollama pull), stable default |
| llama.cpp | 8080 | Direct GGUF loading, latest architectures first, fine-grained GPU offload |
| ik_llama.cpp | 8081 | Fork of llama.cpp with aggressive CPU/hybrid kernels and new quant types (IQ4_KS, IQ2_KS); faster for MoE models that spill to system RAM |
Usage:
# default backend (from LOCAL_DEFAULT_BACKEND, default: ollama)
claude-local
# explicit backend
claude-local --backend llama
claude-local --backend ikllama
# override model
claude-local --model qwen2.5-coder:14b
claude-local --backend llama --model /path/to/model.gguf
# override context (llama/ikllama only; Ollama requires ollama-create)
claude-local --backend llama --ctx 128k
# GPU topology (Ollama only; HPC-safe single-GPU mode)
claude-local --gpus 1
# do not auto-start / auto-stop
claude-local --no-autostart # server must already be running
claude-local --no-autostop # keep server alive after exit
# pass-through args to the claude binary
claude-local -- --verbose some promptThe -- [CLAUDE_ARGS] form separates wrapper flags from the real claude
invocation — anything after -- goes straight to claude.
llm-local-server is the lower-level control verb. claude-local calls
it implicitly, but use it directly when you want the server up or down
independent of Claude.
llm-local-server start # start default backend
llm-local-server start --backend llama --ctx 128k # start llama-server with custom ctx
llm-local-server stop # stop default backend
llm-local-server stop --backend llama # stop specific backend
llm-local-server restart [opts] # stop + start
llm-local-server status # process + loaded model + GPU usagestatus shows the PID, the bound port, and nvidia-smi GPU memory/utilization.
ollama-pull [model] # pull a model (wraps `ollama pull`)
ollama-create [--ctx N] [model] # create a context-capped variant (context window in Ollama is per-model)
ollama-models # list models grouped by type (coder, chat, embedding…)
ollama-rename <old> <new> # rename a model (preserves exact casing)ollama-create --ctx N is the workaround for Ollama's static context.
It clones a model with a PARAMETER num_ctx N override so you can pin a
specific context length per session.
llama-models # list all GGUFs in $LLAMACPP_MODELS_DIR + HF cache
llama-alias <name> <path> # create short-name alias (symlink) in models dir
llama-alias <name> # remove alias
llama-info [model] # dump GGUF metadata (arch, params, quant) + tensor summaryAliases let you invoke heavy GGUF paths via a short name:
llama-alias qwen3-32b ~/models/Qwen3-32B-Instruct-Q4_K_M.gguf
claude-local --backend llama --model qwen3-32bllama-info reads the GGUF header and prints architecture (llama/qwen/
mixtral/…), total parameters, quantization, tensor count, and context
window — useful when auditing a model before loading.
~/.bash/claude_code.local is loaded at the end of the main claude_code
file. Use it for anything that differs per machine:
# Example ~/.bash/claude_code.local
export LOCAL_DEFAULT_BACKEND="llama"
export LLAMACPP_BIN="/opt/llama.cpp/build/bin/llama-server"
export IKLLAMA_BIN="/opt/ik_llama.cpp/build/bin/llama-server"
export LLAMACPP_MODELS_DIR="$HOME/models"
export LLAMACPP_DEFAULT_MODEL="qwen3-coder-30b"
export LLAMACPP_CTX=262144
export LLAMACPP_GPU_LAYERS=-1
export CUDA_VISIBLE_DEVICES="0,1"Deploy it via its own machine-specific stow package (e.g.
bash-adam/ listed in machines/adam) — see
Machine-specific packages.
| Variable | Default | Purpose |
|---|---|---|
LOCAL_DEFAULT_BACKEND |
ollama |
Backend used when claude-local runs without --backend |
OLLAMA_HOST |
127.0.0.1:11434 |
Ollama server bind |
OLLAMA_KEEP_ALIVE |
10m |
Keep model in VRAM after last request |
OLLAMA_NUM_PARALLEL |
1 |
Concurrent request slots |
OLLAMA_MAX_LOADED_MODELS |
1 |
Prevent VRAM thrashing |
OLLAMA_DEFAULT_MODEL |
qwen2.5-coder:7b |
Default model for claude-local --backend ollama |
LLAMACPP_BIN |
llama-server |
llama.cpp server binary |
LLAMACPP_HOST / LLAMACPP_PORT |
127.0.0.1 / 8080 |
Bind address |
LLAMACPP_MODELS_DIR |
~/models |
Where llama-models / llama-alias operate |
LLAMACPP_DEFAULT_MODEL |
(unset) | Default GGUF path or alias |
LLAMACPP_GPU_LAYERS |
-1 |
GPU offload layers (-1 = all) |
LLAMACPP_CTX |
131072 |
Default context window |
IKLLAMA_BIN |
llama-server |
ik_llama.cpp server binary |
IKLLAMA_HOST / IKLLAMA_PORT |
127.0.0.1 / 8081 |
Bind address (distinct from mainline) |
IKLLAMA_GPU_LAYERS |
99 |
GPU layers (ik_llama.cpp treats -1 as 0, so a large sentinel is used) |
IKLLAMA_CTX |
131072 |
Default context window |
IKLLAMA_DEFAULT_MODEL / IKLLAMA_MODELS_DIR |
fall back to LLAMACPP_* |
Resolved lazily at call time |
OPENROUTER_API_KEY / ~/.openrouter-ai-key |
(required) | OpenRouter auth |
ZAI_API_KEY / ~/.z-ai-key |
(required) | Z.ai auth |
CLAUDE_LOCAL_GPUS |
(optional) | Shown in status line (<N>×GPU) when set by claude-local --gpus N |
Run claude-help at any time for the live version of this reference.
| Path | Purpose |
|---|---|
scripts/bd/ |
bd back-directory submodule |
scripts/images/ |
Image processing utilities (convert, crop, scale, alpha…) |
scripts/iso/ |
ISO mount/umount helpers |
scripts/miscellanea/ |
Misc scripts (archive, PDF preview, NAS mount…) |
scripts/borg-automated-backup/ |
Borg backup automation |
scripts/pdf/ |
PDF utilities |
scripts/.scripts/mbox-index.py |
Index Gmail Takeout MBOX into searchable SQLite (details) |
scripts/.scripts/skills-apply |
Declarative install/update/status for Claude Code skills across three classes (details) |
skills-apply is the per-host driver for the three-class skills machinery
introduced in Skills. It
reads:
~/.claude/settings.json→enabledPlugins(class B — plugin/marketplace)~/.claude/skills/manifest.toml→[skills.*]blocks (class C — third-party)~/dotfiles/machines/<hostname>.skills(optional per-host class-C filter)
…and delegates to the upstream installer for each class: claude plugin install/update/uninstall for B, the manifest's own install/update/
uninstall shell strings for C. Class A (custom user-authored) is managed
entirely by stow — skills-apply status simply reports whether each
expected symlink resolves.
skills-apply install # idempotent: skip already-installed entries
skills-apply update # update every installed skill
skills-apply status # group by class, mark drift, exit 0
skills-apply remove <name> # uninstall (plugin or manifest); does NOT
# rewrite settings.json or manifest.toml —
# script warns you to do that yourselfRestart Claude Code after install or update so plugin changes load
(the claude plugin update CLI itself says "restart required to apply").
Dependencies: claude (Claude Code CLI), python3 (for inline tomllib
JSON/TOML parsing of settings.json and manifest.toml).
Reclaim Google account quota by exporting Gmail to a local archive that stays searchable after you delete the cloud copy. Indexes a Google Takeout MBOX export into a SQLite FTS5 database and extracts attachments to a date/sender file tree. Stdlib-only — no dependencies.
# 0. Get the mbox: Takeout (Mail → MBOX) → download zip → unzip
unzip -o takeout-*.zip -d /mnt/d/gmail-backup # yields Takeout/Mail/*.mbox
# 1. Build the searchable index + extract attachments
python3 ~/.scripts/mbox-index.py build /mnt/d/gmail-backup \
--db /mnt/d/gmail-backup/mail.db --attach-dir /mnt/d/gmail-backup/attachments
# 2. Search (full-text + filters: from: to: subject: has:attachment larger:N before:/after:)
python3 ~/.scripts/mbox-index.py search "invoice AND from:acme larger:5M after:2020-01-01"
python3 ~/.scripts/mbox-index.py search "larger:25M" # find quota hogs before deleting
# 3. Verify the archive, then delete from Gmail by size and empty Trash to reclaim quotaFull workflow — including the Takeout export, archive verification, and the cloud-deletion
steps — is in scripts/.scripts/mbox-index.md.
If the config belongs to a tool already covered by a stow package, place the file at the correct mirrored path inside that package and re-run stow:
# Example: add a new bash helper
cp my-helper ~/dotfiles/bash/.bash/my-helper
# Re-deploy the package (safe to re-run, stow is idempotent)
bash ~/dotfiles/dotify.sh bashStow will create ~/.bash/my-helper → ~/dotfiles/bash/.bash/my-helper.
-
Create the package directory and mirror the
$HOMElayout inside it:# Example: track ~/.config/foo/config mkdir -p ~/dotfiles/foo/.config/foo cp ~/.config/foo/config ~/dotfiles/foo/.config/foo/config
-
Add the package name to
PACKAGESindotify.sh:PACKAGES=(bash vim git claude modules python scripts miscellanea foo)
-
Deploy:
bash ~/dotfiles/dotify.sh fooStow will symlink
~/.config/foo/config → ~/dotfiles/foo/.config/foo/config. -
Commit:
git add dotify.sh foo/ git commit
For configs that only belong on one machine, list the package in
machines/<hostname> (one package name per line):
echo "foo" >> ~/dotfiles/machines/$(hostname -s)dotify.sh reads this file automatically and stows those packages after the
common ones. The machines/ directory is tracked in git so the per-host
setup is reproducible.
My dotfiles come from many years of GNU/Linux usage and inspiration from countless people sharing their configs on the web. Distributed under the terms of the WTFPL — Do What the Fuck You Want to Public License, without any warranty.
Go to Top