<feed xmlns='http://www.w3.org/2005/Atom'>
<title>archsetup/dotfiles/common/.local/bin/tmux-util, branch main</title>
<subtitle>Builds a full dev workstation from a bare Arch Linux install.
</subtitle>
<id>https://git.cjennings.net/archsetup/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/archsetup/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/'/>
<updated>2026-06-02T17:16:38+00:00</updated>
<entry>
<title>refactor: drop in-repo dotfiles/, move stow tooling to the dotfiles repo</title>
<updated>2026-06-02T17:16:38+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-02T17:16:38+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=8c32bbbf9fab115dc51afa17c081e0763cbee717'/>
<id>urn:sha1:8c32bbbf9fab115dc51afa17c081e0763cbee717</id>
<content type='text'>
Since the installer clones DOTFILES_REPO into ~/.dotfiles and stows from there, the in-repo dotfiles/ tree was dead weight. Nothing reads it at install time. I removed it (831 files) now that both machines are migrated.

The Makefile's stow / restow / reset / unstow / import targets and the dotfile-script unit suites moved to the dotfiles repo. They sit alongside the scripts they manage and run standalone (cd ~/.dotfiles &amp;&amp; make ...). This Makefile keeps the VM-integration targets and the installer-helper suite (safe-rm-rf).

I updated CLAUDE.md and README.md so stow operations run from ~/.dotfiles, and the dotfile-management, theme, and unit-test sections point at the standalone repo. The README was already describing the old in-repo model from before the installer switched to cloning. This brings it in line.
</content>
</entry>
<entry>
<title>feat(tmux-util): add rename subcommand (fzf pick + prompt)</title>
<updated>2026-05-19T18:17:06+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-19T18:17:06+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=a33eb10faddba2595deba81f52f89352255f1cf2'/>
<id>urn:sha1:a33eb10faddba2595deba81f52f89352255f1cf2</id>
<content type='text'>
tmux-util rename closes out the original six-subcommand plan. The flow:
1. fzf-pick a session from the list.
2. Prompt for a new name on stdin.
3. Bail with a useful message on empty input, same-as-old, or conflict with an existing session.
4. Otherwise `tmux rename-session -t &lt;old&gt; &lt;new&gt;` and confirm.

The conflict check uses `tmux has-session -t =&lt;new&gt;` with the same `=`-prefix exact-match guard as the go subcommand. Without it, tmux's default prefix matching would let `rename foo` succeed even when a session named `foobar` already exists, then surprise the user later.

5 new tests cover Normal cases (pick + rename happy path) and Boundary cases (no sessions, fzf cancel, empty new name, same-as-old no-op, conflict with existing session). The test harness's run_script grew an `stdin=` param so tests can feed the prompt input. fake-tmux picked up a rename-session handler that mutates the state file. Total suite: 48 tests, all green.

Six subcommands shipped: go, pick, ls, find, reap, rename. The original "no args prints help" requirement still holds, and the stub-test for unimplemented subcommands got removed since everything's wired now.
</content>
</entry>
<entry>
<title>feat(tmux-util): add pick subcommand (fzf session switcher)</title>
<updated>2026-05-19T18:14:52+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-19T18:14:52+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=d78c6b906afa4c29f56f6d9eb050961b0df5b703'/>
<id>urn:sha1:d78c6b906afa4c29f56f6d9eb050961b0df5b703</id>
<content type='text'>
tmux-util pick lists every session ("attached"/"detached" plus name), pipes it through fzf, and attaches or switch-clients to the chosen one (matching the go subcommand's inside-vs-outside-tmux discipline).

If the user cancels fzf (Ctrl-C, Esc, empty selection), the pipeline returns empty and pick exits 0 without touching tmux state.

The new fake-fzf testing fake is driven by env vars:
- FAKE_FZF_CHOICE_LINE=&lt;N&gt;: return the Nth line of stdin as the selection
- FAKE_FZF_CHOICE=&lt;string&gt;: return the literal string (ignores stdin)
- (neither): exit 130 to simulate cancel

4 new tests cover Normal cases (pick second line, inside/outside tmux behavior) and Boundary cases (no sessions, user cancellation). Total suite: 43 tests, all green.
</content>
</entry>
<entry>
<title>feat(tmux-util): add find subcommand</title>
<updated>2026-05-19T18:09:23+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-19T18:09:23+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=0f9e0d5fc26604a395c04b45746e89090f5558e5'/>
<id>urn:sha1:0f9e0d5fc26604a395c04b45746e89090f5558e5</id>
<content type='text'>
tmux-util find &lt;pattern&gt; walks every pane across every session, queries each pane's foreground command (`#{pane_current_command}`), and prints the location of any pane whose command matches the pattern. Output format: `&lt;session&gt;:&lt;window&gt;.&lt;pane&gt; &lt;command&gt;`, one per line.

Pattern is a basic ERE (passed through `grep -E`), so anchors and alternation work. Substring matching is the common case.

Exit code:
- 0 with output: matches found
- 1 with no output: no matches (lets you script around it)
- 2 with usage on stderr: missing or empty pattern

7 new tests cover Normal cases (single match, multi-match across sessions, format verification) and Boundary cases (no matches, no sessions, missing pattern, empty pattern). fake-tmux now parses pid:cmd entries in the state file so panes can carry a synthetic command name. Total suite: 39 tests, all green.
</content>
</entry>
<entry>
<title>feat(tmux-util): add go subcommand (attach-or-create)</title>
<updated>2026-05-19T18:01:19+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-19T18:01:19+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=b4cd792f847e9513fd4e9bac4ee26b492dbdadc3'/>
<id>urn:sha1:b4cd792f847e9513fd4e9bac4ee26b492dbdadc3</id>
<content type='text'>
tmux-util go &lt;name&gt; attaches to a session named &lt;name&gt; if it exists, creates it otherwise. Behavior depends on whether the caller is already inside tmux:

- Outside tmux: `tmux attach-session -t &lt;name&gt;` (existing) or `tmux new-session -s &lt;name&gt; -c $PWD` (new).
- Inside tmux (TMUX env set): `tmux switch-client -t &lt;name&gt;` (existing) or `tmux new-session -d -s &lt;name&gt; -c $PWD` followed by `switch-client` (new). Attaching from inside tmux would nest sessions and break the outer view, so the inside path uses switch-client instead.

The existence check uses `tmux has-session -t =&lt;name&gt;` with the leading `=` to force exact-match. Without it, tmux does prefix matching, which would let `go foo` resolve to a session named `foobar`.

I added 6 new tests covering both inside/outside-tmux paths, both create/attach paths, plus error handling for missing or empty name arguments. fake-tmux picked up handlers for new-session (mutates state), attach-session and switch-client (record-only), and the `=`-prefix form of has-session. Total suite: 32 tests, all green.
</content>
</entry>
<entry>
<title>feat(tmux-util): add ls subcommand</title>
<updated>2026-05-19T17:31:17+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-19T17:31:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=fbe3cd67ac04ec73746fdcda0e5f37145d735235'/>
<id>urn:sha1:fbe3cd67ac04ec73746fdcda0e5f37145d735235</id>
<content type='text'>
tmux-util ls is an opinionated replacement for `tmux ls` with columns for state (attached/detached), name, idle time (humanized), window count, and the current pane's cwd (tilde-fied if it sits under $HOME).

The cwd query goes through `tmux display -p -t &lt;session&gt; '#{pane_current_path}'`, which returns the cwd of the active pane of the active window. That's close enough to "what the session is about" for a one-line summary.

Idle calculation reads `date +%s` by default and accepts an override via the TMUX_UTIL_NOW env var so tests can pin "now" to a known epoch.

12 new tests cover Normal cases (attached / detached, multiple sessions) and Boundary cases (no sessions, idle exactly at minute / hour / day boundaries, $HOME tilde). One existing dispatch test got reworked because the original stub target (`ls`) is no longer unimplemented. Total suite is 26 tests, all green.

The fake-tmux harness picked up two things along the way: real format-string parsing for `list-sessions -F` and a new handler for `display -p`. The state file format extended to include activity epoch, window count, and cwd, with sensible defaults for older 3-tuple test inputs so the reap tests keep passing untouched.
</content>
</entry>
<entry>
<title>feat(tmux-util): add script skeleton and reap subcommand</title>
<updated>2026-05-19T17:24:40+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-19T17:24:40+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=9557006dd2409437a346f66611e6fa2762aa1e63'/>
<id>urn:sha1:9557006dd2409437a346f66611e6fa2762aa1e63</id>
<content type='text'>
A new utility in dotfiles/common/.local/bin/ for managing tmux sessions. The eventual plan covers six subcommands (go, pick, ls, find, reap, rename). This commit ships the skeleton, the dispatch + help, and the first subcommand: reap.

reap walks every unattached tmux session whose name doesn't match $TMUX_UTIL_REAP_SKIP (default `^aiv-`), sends SIGHUP to each pane's PID (the same signal that fires when you close a terminal window), waits up to three seconds for the session to wind down, and falls back to `tmux kill-session` if anything's still alive.

Tests live under tests/tmux-util/ with the same fake-binary-on-PATH pattern layout-navigate uses. fake-tmux reads canned session state from a file and records every invocation. fake-kill records signal calls without sending them. fake-sleep is a no-op so tests don't actually wait. 14 tests cover Normal / Boundary cases for dispatch + reap. Run them with:

    cd tests &amp;&amp; python3 -m unittest tmux-util.test_tmux_util

The other five subcommands stub out for now and exit non-zero with "not implemented yet" so future TDD turns can drop them in one at a time.
</content>
</entry>
</feed>
