Skip to content

feat(tmux): LazyVim-cohesive remote-first tmux config - #7

Merged
jarodtaylor merged 6 commits into
mainfrom
feat/tmux-lazyvim-remote
Jun 9, 2026
Merged

jarodtaylor merged 6 commits into
mainfrom
feat/tmux-lazyvim-remote

Conversation

@jarodtaylor

Copy link
Copy Markdown
Owner

Summary

home/dot_config/tmux/tmux.conf was empty. This makes tmux feel like the terminal-layer of LazyVim and turns each fleet host (M5, Mac Mini, VPS) into a durable workspace you can reattach to from any device — the iPad over Termius + Tailscale included. Prefix is C-Space (the sibling of Neovim's Space leader), Ctrl-hjkl crosses seamlessly between Neovim splits and tmux panes, and C-Space Space opens a discoverability menu so you learn the bindings instead of memorizing them.

Notable decisions

  • tmux-menus, not tmux-which-key, for discoverability. which-key was the original pick, but vendoring it proved fragile — a Python build step, a pyyaml git submodule (empty under a shallow clone), an autobuild that calls GNU realpath (absent on macOS, fails silently under set -e), and no release tags to pin. jaclu/tmux-menus delivers the same press-a-key-see-your-options popup as pure POSIX/tmux script: no build, no Python, no submodule — robust by construction.
  • Plugin load order is load-bearing. continuum prepends its autosave hook to status-right, which catppuccin sets — so the order is catppuccin → resurrect → continuum, or autosave is silently lost.
  • Portable vendoring. Plugins are cloned via chezmoiexternal to ~/.config/tmux/plugins/ — a path the plain (non-chezmoi) VPS repo can populate the same way — so one tmux.conf works fleet-wide with no chezmoi-only coupling. Only catppuccin is tag-pinned (v2.3.0, it ships breaking syntax); the rest track unpinned.
  • Clipboard, persistence, prefix coexistence. OSC52 copy works over SSH in Ghostty and Termius (paste into tmux stays manual — a tmux limitation). resurrect + continuum restore the workspace across reboots, with vim-obsession so the Neovim session restores too. blink.cmp's <C-Space> is freed since the tmux prefix now owns that chord.

Verification

No automated suite (dotfiles config). Statically checked here: .chezmoiexternal.toml parses, both Lua specs parse, and a focused code-review pass confirmed cross-file path consistency, load order, tmux syntax, and plan fidelity.

Runtime acceptance is pending — run on the M5 after chezmoi apply:

  • C-Space is the prefix; C-Space Space opens the tmux-menus popup
  • Ctrl-hjkl crosses Neovim splits ↔ tmux panes seamlessly
  • copy-mode y over SSH lands in the local system clipboard
  • catppuccin status renders; tmux show-options -g status-right includes the continuum save hook
  • a reboot (or tmux kill-server + restart) restores the session, Neovim buffers included

Deferred (intentional)

auto-attach-on-SSH (the guarded block is already specified), sesh, smart-splits, a curated tmux-menus menu, and the VPS plugin bootstrap (owned by the separate VPS repo).

Post-Deploy Monitoring & Validation

No additional operational monitoring required — local dotfiles config; validation is the M5 checklist above (owner: Jarod).


Compound Engineering
Claude Code

Keep brainstorm/plan artifacts (docs/brainstorms/) local — agent-session
scratch, not something to publish in a public dotfiles repo.
Greenfield tmux.conf that makes tmux the terminal-layer of Neovim and a
durable cross-device workspace: C-Space prefix, Ctrl-hjkl into Neovim splits,
OSC52 clipboard over SSH, tmux-menus discoverability, catppuccin theme, and
resurrect+continuum persistence.

Plugins are vendored via chezmoiexternal to ~/.config/tmux/plugins/ — a path
the plain VPS repo can populate too. catppuccin is pinned to v2.3.0 (it ships
breaking syntax); the rest track unpinned. Load order catppuccin→resurrect→
continuum is load-bearing — continuum prepends its autosave hook to
status-right, which catppuccin sets.
vim-tmux-navigator completes the seamless Ctrl-hjkl seam between Neovim splits
and tmux panes; vim-obsession keeps a Session.vim so tmux-resurrect can restore
the Neovim session after a reboot. blink.cmp's <C-Space> is freed since the
tmux prefix now owns that chord.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a remote-first tmux configuration (plus supporting Neovim + chezmoi changes) to make tmux behave as a durable, per-host workspace with LazyVim-cohesive keybindings and plugin-based UX enhancements.

Changes:

  • Add a full ~/.config/tmux/tmux.conf with C-Space prefix, OSC52 clipboard support, vim/tmux pane navigation, and a themed status line with persistence plugins.
  • Vendor tmux plugins via home/.chezmoiexternal.toml and add Neovim plugin specs to support seamless vim↔tmux navigation and session restoration.
  • Add a zsh abbreviation to attach/create a per-host main tmux session and ignore an additional docs folder.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
home/dot_config/zsh/abbreviations Adds ta abbreviation to attach/create the main tmux session.
home/dot_config/tmux/tmux.conf New tmux configuration: prefix, terminal features, navigation bindings, and plugin sourcing.
home/dot_config/nvim/lua/plugins/editor.lua Adds vim-tmux-navigator + vim-obsession plugin specs for cross-pane navigation and session restore.
home/dot_config/nvim/lua/plugins/blink.lua Frees <C-Space> in blink.cmp to avoid conflict with tmux prefix.
home/.chezmoiexternal.toml Vendors tmux plugins into ~/.config/tmux/plugins/ (catppuccin pinned).
.gitignore Ignores /docs/brainstorms/.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread home/dot_config/tmux/tmux.conf Outdated
Comment thread home/dot_config/tmux/tmux.conf
Comment thread home/.chezmoiexternal.toml Outdated
@jarodtaylor

Copy link
Copy Markdown
Owner Author

@claude please review PR

@claude

claude Bot commented Jun 8, 2026 •

Copy link
Copy Markdown

Claude finished @jarodtaylor's task in 2m 54s —— View job


PR Review: feat(tmux): LazyVim-cohesive remote-first tmux config

  • Gather context and read changed files
  • Review tmux.conf for correctness and portability
  • Review chezmoiexternal.toml for plugin vendoring
  • Review Neovim Lua specs (editor.lua, blink.lua)
  • Review zsh abbreviation
  • Synthesize and post full review

Overall

Solid, well-reasoned config. The architecture decisions (load order, inline vim-tmux-navigator, tmux-menus over which-key) are clearly thought out and the PR description explains the why at every step. Three issues below need attention before runtime testing — one is a genuine portability bug, one is a misleading comment, and one is a disagreement with a Copilot finding.


Issues

1. \S in vim_pattern — genuine portability bug on macOS [tmux.conf:50]

vim_pattern='(\S+/)?g?\.?(view|l?n?vim?x?|fzf)(diff)?(-wrapped)?'

\S is PCRE / GNU grep extension, not POSIX ERE. On macOS/BSD grep -E, it matches a literal S (or errors), so any Neovim binary path with a directory prefix (e.g. /usr/local/bin/nvim) will never match — the navigator silently degrades to always moving tmux panes, never forwarding to Neovim. Since M5 and Mac Mini are the primary targets this will hit.

Fix: replace \S with [^ \t]:

vim_pattern='([^ \t]+/)?g?\.?(view|l?n?vim?x?|fzf)(diff)?(-wrapped)?'

Fix this →


2. Misleading comment about catppuccin path [.chezmoiexternal.toml:14-15]

# The key matches catppuccin's upstream clone path (repo root holds catppuccin.tmux under tmux/)

This reads as "in the catppuccin/tmux GitHub repo, catppuccin.tmux lives inside a tmux/ subdirectory." That's backwards — catppuccin.tmux is at the repo root. The /tmux/ in the final resolved path (~/.config/tmux/plugins/catppuccin/tmux/catppuccin.tmux) comes from the chezmoi destination key [".config/tmux/plugins/catppuccin/tmux"], which clones the repo into that path. The comment will mislead anyone debugging path resolution.

Suggested replacement:

# Cloned to ~/.config/tmux/plugins/catppuccin/tmux/ (the chezmoiexternal key is the
# destination dir). catppuccin.tmux sits at the repo root, so the full path
# ~/.config/tmux/plugins/catppuccin/tmux/catppuccin.tmux resolves correctly.

Fix this →


3. Copilot's 'C-\' quoting finding — false positive [tmux.conf:57]

Copilot flagged bind -n 'C-\' if-shell ... as broken single-quote quoting. This is incorrect. In POSIX shell and tmux's config parser, single-quoted strings are fully literal — backslash has no special meaning inside them. 'C-\' is a valid three-character string (C, -, \). This exact form appears verbatim in christoomey/vim-tmux-navigator's own README as the recommended config. No change needed.


Minor notes

escape-time 0 (tmux.conf:28): Setting this to 0 is a common choice for Neovim, but values of 10–20 are safer against edge cases with certain terminal emulators. Given you're using Ghostty (which handles this well), 0 is fine — just noting it if you ever see odd escape-sequence artifacts.

continuum comment (tmux.conf:69): The comment says continuum "PREPENDS its autosave hook to status-right." Continuum actually appends via set -ag. The load-order reasoning is still correct (catppuccin sets status-right, then continuum modifies it), but the word "prepend" is inaccurate.

blink.lua: Freeing <C-space> is the right call. LazyVim's blink config already handles auto-popup, so removing the now-shadowed manual trigger is clean.

editor.lua: vim-obsession loading eagerly (no lazy = true) is correct — obsession needs to be active from session start to write Session.vim continuously.

ta abbreviation: The -d flag on tmux attach (detach other clients to reclaim full terminal dimensions on device-hop) is a good touch for the Termius/iPad use case.


Summary

Two things to fix before the M5 acceptance run: the \S portability bug (will silently break C-hjkl on macOS) and the catppuccin path comment. The C-\ quoting concern from Copilot can be dismissed. Everything else is solid.
| Branch: feat/tmux-lazyvim-remote

Address PR #7 review (Copilot + Claude):
- vim_pattern used \S (GNU/PCRE), which breaks under macOS/BSD grep -E where
  `ps -o comm=` returns nvim's full path — silently degrading C-hjkl to
  tmux-only nav. Use [^[:space:]] (POSIX non-whitespace), correct on both BSD
  and GNU (the suggested [^ \t] would wrongly exclude paths containing 't').
- Fix the backwards catppuccin path comment and the inaccurate "prepends".
The 'C-\' quoting concern was a false positive (single quotes are literal).
* origin/main:
  Add Claude Code GitHub Workflow (#8)
@jarodtaylor

Copy link
Copy Markdown
Owner Author

Thanks Copilot and Claude — addressed in adf47cf (pushed; the branch is now up to date with main, including the new Claude Actions workflows).

Fixed:

  • \S portability bug (tmux.conf:50) — real bug, both of you caught it. \S is a GNU/PCRE extension; under macOS/BSD grep -E, ps -o comm= returns nvim's full path and the directory-prefix group never matches, silently degrading C-hjkl to tmux-only nav on the primary fleet. Fixed with [^[:space:]] (POSIX non-whitespace). One note: I used [^[:space:]] rather than the suggested [^ \t] — inside a POSIX bracket expression [^ \t] excludes a literal t, so it would fail on any path containing t (e.g. /opt/...). [^[:space:]] is unambiguous on both BSD and GNU.
  • catppuccin path comment (.chezmoiexternal.toml) — reworded; it now correctly states the chezmoi key is the clone destination dir and catppuccin.tmux lives at the repo root.
  • "prepends" → "extends" (tmux.conf load-order comment) — corrected; the load-order constraint itself was already right.

Dismissed:

  • 'C-\' quoting (tmux.conf:57, Copilot) — false positive, and Claude flagged this too. Single-quoted strings are fully literal in both the shell and tmux's parser, so 'C-\' is a valid 3-character key name (C, -, \). It's the verbatim form from christoomey/vim-tmux-navigator's README. No change.

Noted, no change:

  • escape-time 0 — both reviews agree it's fine for Ghostty. It's a one-line knob; if ESC artifacts ever show up over Termius/Tailscale I'll bump it to 10 (still imperceptible for Neovim).

Runtime acceptance — especially the C-hjkl seam now that the regex is portable — is still pending on the M5; the checklist is in the PR description.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 2 comments.

Comment thread home/dot_config/tmux/tmux.conf
Comment thread home/dot_config/nvim/lua/plugins/blink.lua
Addresses PR #7 review. `{}` already disables the preset mapping (blink docs
list `false` and `{}` as equivalent), but `false` is the more explicit intent.
@jarodtaylor

Copy link
Copy Markdown
Owner Author

Round 2 — thanks Copilot. One verified false positive, one doc-equivalent I tightened anyway, and a re-raise.

vim_pattern= / is_vim= "aren't valid tmux.conf syntax" (tmux.conf:54) — false positive, verified empirically. tmux's config parser does support name="value" assignment and $name/${name} expansion. I sourced the block in a throwaway tmux 3.6b server — zero parse errors, and the resulting bind resolved correctly:

$ tmux -L probe -f probe.conf new-session -d        # exit 0, no error messages
$ tmux -L probe list-keys -T root | grep C-h
bind-key -T root C-h  if-shell "ps -o state= -o comm= -t '#{pane_tty}' | grep -iqE '^[^TXZ ]+ +([^[:space:]]+/)?...$'"  "send-keys C-h"  "select-pane -L"

$is_vim expanded to the full ps … | grep … check (with the [^[:space:]] portability fix in it). This is the verbatim christoomey/vim-tmux-navigator README form, used directly in .tmux.conf for years. No change.

['<C-space>'] = {} "may not disable the preset" (blink.lua:13) — not quite. blink's own docs list them as equivalent: ['<C-e>'] = false, -- or {}. So {} does disable it. Switched to false anyway (just pushed) — more explicit intent for a future reader.

'C-\' quoting (tmux.conf:59) — same false positive, re-raised after the line number shifted from the earlier edit. Single quotes are fully literal in shell and tmux's parser; dismissed last round (Claude concurred).

@jarodtaylor
jarodtaylor merged commit 05b141b into main Jun 9, 2026
1 check passed
@jarodtaylor
jarodtaylor deleted the feat/tmux-lazyvim-remote branch June 9, 2026 01:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants