Personal dotfiles managed with GNU Stow.
Caution
This will install dotfiles to $HOME.
Using git worktree:
git clone --bare \
"git@github.com:hllvc/dotfiles.git" \
"dotfiles/.bare" && cd "dotfiles" \
&& echo "gitdir: ./.bare" > .git \
&& printf "\tfetch = +refs/heads/*:refs/remotes/origin/*" >> .bare/config \
&& git worktree add "$(git branch --show-current)" \
&& ./dotctl allWithout git worktree, normal:
git clone "git@github.com:hllvc/dotfiles.git" \
&& cd "dotfiles" \
&& ./dotctl all./dotctl- Show help (running without a command is a no-op)./dotctl all- Stow + load launch agents + install crons + apply macOS defaults./dotctl stow [--adopt]- Symlink dotfiles into$HOME(adopt folds existing files into the repo), then link Homebrew-shipped Claude skills (see Cask-shipped skills)./dotctl agents <load|unload|list> [name...]- Manage launch agents under~/.config/launch-agents; names are optional (tmux-autosaveorcom.hllvc.tmux-autosave), all agents when omitted./dotctl crons <install|list>- Run per-croninstall.shhooks under.shell/scripts/crons/*/./dotctl macos <apply>- Apply macOSdefaults writetweaks (Finder, Dock, trackpad, Safari, Mail, …)./dotctl doctor- Audit launch-agent plists and scripts for worktree-pinned or missing absolute paths./dotctl orphans [--fix]- Report (and with--fixdelete) dangling symlinks left in$HOMEby renamed or removed repo files./dotctl -h(or./dotctl <command> -h) - Help
Zsh tab completion for all of the above ships in .shell/completions/_dotctl and is picked up via fpath once stowed.
- Homebrew (auto-installed by
dotctl) - GNU Stow (auto-installed by
dotctl) - Zsh with Oh-My-Zsh
- Powerlevel10k theme
- sleepwatcher —
brew install sleepwatcher(required for sleep/hibernate save hooks)
.
├── .config/ # Application configs
│ ├── nvim/ # Neovim (Lua + lazy.nvim)
│ ├── ghostty/ # Ghostty terminal
│ ├── karabiner/ # Keyboard remapping
│ ├── launch-agents/ # macOS LaunchAgents
│ └── 1Password/ # SSH agent config
├── .shell/ # Shell configuration
│ ├── profile # Environment variables, PATH
│ ├── aliases # Shell aliases
│ ├── zshinit # Zsh plugins/theme setup
│ └── scripts/ # Custom scripts
│ ├── functions/ # Scripts that can cd (gbare.sh, sw.sh)
│ ├── init/ # Startup scripts (tt.sh)
│ ├── tmux/ # Tmux helper scripts
│ ├── crons/ # Scheduled jobs (installed by `dotctl crons install`)
│ ├── macos/ # macOS `defaults write` tweaks (`dotctl macos apply`)
│ └── unloaded/ # Inactive scripts
├── .claude/ # Claude Code CLI
│ ├── CLAUDE.md # Global instructions
│ ├── settings.json # Settings, permissions, plugins
│ └── skills/ # Custom skills
├── .zshrc # Main Zsh config
├── .tmux.conf # Tmux config
├── .gitconfig # Git config
├── .p10k.zsh # Powerlevel10k theme
└── dotctl # Dotfiles CLI (stow, agents, crons)
See detailed documentation: .config/ | .shell/
- gbare.sh - Set up a bare repository with GitHub integration
- sw.sh - Smart branch switching with worktree support
Separate tmux sockets for work/personal contexts:
personal- Personal projectswork- Work projects
LaunchAgents auto-start these on login (com.hllvc.personal.tmux, com.hllvc.work.tmux).
Session state is preserved across reboots via tmux-resurrect (forks: hllvc/tmux-resurrect, hllvc/tmux-continuum). The in-tmux auto-save is disabled (@continuum-save-interval 0) to avoid pty hitches; saving is handled externally:
- Idle / periodic —
com.hllvc.tmux-autosavefires every 30 min, saves when HID idle ≥ 30 s (force-saves after 90 min) - Sleep / hibernate —
sleepwatcherruns~/.sleepbefore the system sleeps - Shutdown / reboot —
com.hllvc.tmux-shutdown-savedaemon catchesSIGTERMfrom launchd during shutdown
Scheduled user-level jobs live under .shell/scripts/crons/<name>/ and are installed via dotctl crons install. Each cron is free to define its own install.sh (e.g. for system-level hooks like newsyslog.d configs) — dotctl auto-discovers and runs them.
Path convention: launchd does no variable expansion, so plists must carry an absolute path — always the stowed
~/.shell/...location, never.repos/private/dotfiles/<worktree>/.... A plist pinned to a worktree dies silently (launchd exit 78) the moment that worktree is renamed. Scripts use$HOME/.shell/....dotctl doctorenforces both.
- homebrew-update — daily at 10:00; skips on battery or offline; runs
brew update,upgrade,autoremove,cleanup, then re-points cask-shipped Claude skills at the new version (link-skills.sh); notifies on failure (LaunchAgent:com.hllvc.homebrew-update). - memory-pressure — samples
memory_pressureevery 5 min, logs to~/Library/Logs/com.hllvc.memory-pressure.log, fires a macOS alert when free memory drops below 40% (LaunchAgent:com.hllvc.memory-pressure). - tmux-autosave — idle-gated tmux session saver; fires every 30 min, saves when idle ≥ 30 s or 90 min has elapsed; also triggered on sleep (
~/.sleepviasleepwatcher) and shutdown (SIGTERMtrap viacom.hllvc.tmux-shutdown-save). Replaces@continuum-save-interval(LaunchAgents:com.hllvc.tmux-autosave,com.hllvc.tmux-shutdown-save,com.hllvc.sleepwatcher). - tmux-warmup — pre-starts the
workandpersonaltmux servers at login sotmux-continuumauto-restore runs before first attach (LaunchAgents:com.hllvc.personal.tmux,com.hllvc.work.tmux).
- com.sh - Git commit with branch prefix extraction
- j.sh - Jira integration (sprints, worktrees from tickets)
- tt.sh - Tmux socket selector with fzf
Lua-based config with:
- lazy.nvim plugin manager
- LSP (TypeScript, Python, Go, Rust, Terraform, Helm, YAML, JSON, Lua, Markdown)
- Telescope, Treesitter, Copilot
- Custom keymaps (jk escape, L/H buffer nav)
LSP plugins (pyright, lua-language-server) reuse Neovim's Mason-managed binaries.
Mason installs to ~/.local/share/nvim/mason/bin/, which isn't in PATH by default.
mason-link.sh symlinks all Mason binaries into ~/.local/bin/:
~/.shell/scripts/unloaded/mason-link.shSome casks ship their own Claude skill (currently terminal-browser). Its path
is version-pinned (/opt/homebrew/Caskroom/<cask>/<version>/...) and brew cleanup deletes the old version, so the link is not tracked here. Stow
also refuses absolute symlinks and aborts the whole run if it finds one.
Instead, .shell/scripts/crons/homebrew-update/link-skills.sh resolves the
current version through the cask's stable /opt/homebrew/bin/<bin> link and
(re)creates ~/.claude/skills/<skill>. It runs after dotctl stow and after
every homebrew-update run. To add another cask, add an entry to its SKILLS array.
terminal-browser also links its skill on its own (on the first command after
a version change), including into ~/.agents/skills. That path points at this
repo's .claude/skills, so the link lands in the working tree. It's listed in
both .gitignore and .stow-local-ignore: keep both entries, or the next
dotctl stow aborts again.
allowedMcpServers: [] blocks every MCP server (user-added, plugin, and
claude.ai connectors). Allowlist entries merge across settings sources, so a
single session can opt servers back in without touching settings.json:
# claude.ai connectors match by URL (their names contain spaces/dots,
# which serverName entries don't accept)
claude --settings '{"allowedMcpServers":[{"serverUrl":"https://mcp.notion.com/*"}]}'
# user-added servers match by name
claude --settings '{"allowedMcpServers":[{"serverName":"testapi"}]}'cmcp (.shell/scripts/cmcp.sh) wraps this: cmcp notion slack opens a session
with just those, cmcp -c figma checks what would load, cmcp -l lists aliases.
claude mcp list accepts the same --settings flag to check what would load.
Connector URLs: Notion mcp.notion.com, Slack mcp.slack.com, Fathom
api.fathom.ai, PostHog mcp.posthog.com, Figma mcp.figma.com, Excalidraw
mcp.excalidraw.com, Claude Docs api.anthropic.com/v1/pages.
Don't re-add disableClaudeAiConnectors: true — any source setting it to
true wins, so no per-session flag can turn connectors back on.
- Ticket links in the footer —
footerLinksRegexescan turnSG-####IDs in output into clickable badges. Tickets live in Notion; needs the Notion database/page URL shape for a ticket ID before it can be wired up.
Conditional include for work repositories:
~/.repos/stackguardian/.gitconfig- Included for repos under~/.repos/stackguardian/