-
Notifications
You must be signed in to change notification settings - Fork 0
feat(tmux): session logging, pane archive, and tmuxinator integration #926
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
67cea14
45a1485
5fa87a7
da8c973
f6d5f04
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,7 @@ in | |
| ./pi | ||
| ./serena | ||
| ./starship | ||
| ./tmuxinator | ||
| ./worktrunk | ||
| ./zellij | ||
| ] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| { ... }: | ||
| { | ||
| xdg.configFile."tmuxinator".source = ./tmuxinator; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| name: desktop | ||
| windows: | ||
| - btop: btop | ||
| - dotfiles: | ||
| layout: even-horizontal | ||
| root: ~/dotfiles | ||
| panes: | ||
| - | ||
| - |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| name: mobile | ||
| windows: | ||
| - btop: btop | ||
| - dotfiles: | ||
| layout: even-horizontal | ||
| root: ~/dotfiles | ||
| panes: | ||
| - | ||
| - |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| name: primary | ||
| windows: | ||
| - btop: btop | ||
| - dotfiles: | ||
| layout: even-horizontal | ||
| root: ~/dotfiles | ||
| panes: | ||
| - | ||
| - |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| name: work | ||
| windows: | ||
| - editor: nvim | ||
| - shell: | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -81,6 +81,7 @@ with pkgs; | |
| sqlite | ||
| stern | ||
| tealdeer | ||
| tmuxinator | ||
| tokei | ||
| tree | ||
| turso-cli | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,11 @@ | ||||||||||||||
| function _tdo_function --description "Attach to tmux desktop session" | ||||||||||||||
| tmux new-session -A -s desktop | ||||||||||||||
| if tmux has-session -t desktop 2>/dev/null | ||||||||||||||
| if test -n "$TMUX" | ||||||||||||||
| tmux switch-client -t desktop | ||||||||||||||
| else | ||||||||||||||
| tmux attach-session -t desktop | ||||||||||||||
| end | ||||||||||||||
| else | ||||||||||||||
|
||||||||||||||
| else | |
| else | |
| if not command -q tmuxinator | |
| echo "tmuxinator is not installed or not in PATH. Please install tmuxinator to start the 'desktop' session." >&2 | |
| return 1 | |
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
find . -name "*_t*_function.fish" -o -name "*tdo*" -o -name "*tmo*" -o -name "*tpo*" | head -20Repository: shunkakinoki/dotfiles
Length of output: 484
🏁 Script executed:
fd -t f "*function.fish" | grep -E "(tdo|tmo|tpo)" | head -20Repository: shunkakinoki/dotfiles
Length of output: 358
🏁 Script executed:
find . -path "*/fish/functions*" -type f | head -20Repository: shunkakinoki/dotfiles
Length of output: 1283
🏁 Script executed:
cat -n ./home-manager/programs/fish/functions/_tdo_function.fishRepository: shunkakinoki/dotfiles
Length of output: 415
🏁 Script executed:
cat -n ./home-manager/programs/fish/functions/_tmo_function.fishRepository: shunkakinoki/dotfiles
Length of output: 410
🏁 Script executed:
cat -n ./home-manager/programs/fish/functions/_tpo_function.fishRepository: shunkakinoki/dotfiles
Length of output: 415
🌐 Web query:
tmuxinator issue 924 TMUX environment variable
💡 Result:
tmuxinator issue #924 was caused by tmuxinator generating the session creation command with an empty TMUX env var, e.g.:
TMUX= tmux new-session -d ...(github.com)
Because TMUX contains the current tmux server’s socket path, unsetting it makes tmux new-session talk to (or start) a different server (typically the default socket). Then the rest of tmuxinator’s commands run against your original server and spam can't find session. (github.com)
Fix: PR #925 removed that TMUX= prefix and was released in tmuxinator v3.3.1 (“Don’t unset TMUX env variable for new-session”). (github.com)
What to do:
- Upgrade tmuxinator to >= 3.3.1. (github.com)
- If you’re stuck on an older version, the workaround is exactly what the issue reporter did: run
tmuxinator debug <project>and stripTMUX=before executing. (github.com)
Citations:
- 1: Unable to start sessions due to "TMUX= tmux new-session ..." tmuxinator/tmuxinator#924
- 2: fix: Don't unset TMUX variable for new-session tmuxinator/tmuxinator#925
- 3: fix: Don't unset TMUX variable for new-session tmuxinator/tmuxinator#925
- 4: https://github.com/tmuxinator/tmuxinator/releases
- 5: Unable to start sessions due to "TMUX= tmux new-session ..." tmuxinator/tmuxinator#924
🌐 Web query:
tmuxinator attach-session switch-client inside tmux
💡 Result:
When you’re already inside tmux, you generally want to switch the current client to another session (not “attach” a new client).
Use switch-client (recommended inside tmux)
tmux switch-client -t <session-name>switch-client is explicitly meant to “switch the current session for a client”. [1]
What about attach-session inside tmux?
attach-session is primarily for outside tmux (“create a new client … and attach it”). When run inside tmux, it effectively switches the current client—but this is often confusing; the clearer equivalent command is switch-client. [1][2]
If this is from tmuxinator
tmuxinator can be configured to not auto-attach:
attach: falseThen you can start the project and switch from within tmux using tmux switch-client -t .... [3]
Sources:
[1] Unix & Linux Stack Exchange (explains attach-session vs switch-client inside tmux) (unix.stackexchange.com)
[2] tmux Wiki: Getting Started (client/session model) (github.com)
[3] tmuxinator README (attach: false) (github.com)
Citations:
- 1: https://unix.stackexchange.com/questions/654534/whats-the-expected-way-to-reconnect-to-shell-instantiated-tmux-sessions-that-ha?utm_source=openai
- 2: https://github.com/tmux/tmux/wiki/Getting-Started/2b5809b8334ebb321ffa3e6d130cbb19a0353f20?utm_source=openai
- 3: https://github.com/tmuxinator/tmuxinator?utm_source=openai
After tmuxinator start, add switch-client to navigate to the new session when inside tmux.
The three functions consistently use switch-client when navigating to an existing session inside tmux (lines 3–4), but the new-session path (lines 8–10) lacks this call after tmuxinator start. Inside an active tmux session, switch-client is the correct command to move the current client to a different session; without it, the user remains in their original session with no visible change.
Proposed fix
else
tmuxinator start desktop
+ if test -n "$TMUX"
+ tmux switch-client -t desktop
+ end
endApply the same fix to _tmo_function.fish (line 9) and _tpo_function.fish (line 9), replacing desktop with mobile and primary respectively.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@home-manager/programs/fish/functions/_tdo_function.fish` around lines 8 - 10,
After starting a new session with tmuxinator, call tmux's switch-client so an
attached client inside tmux moves to the new session: in _tdo_function.fish add
a call to switch-client immediately after tmuxinator start desktop; apply the
same change in _tmo_function.fish (after tmuxinator start mobile) and
_tpo_function.fish (after tmuxinator start primary) so the commands use
switch-client when executed from within an active tmux client.
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,11 @@ | ||||||||||||||||
| function _tmo_function --description "Attach to tmux mobile session" | ||||||||||||||||
| tmux new-session -A -s mobile | ||||||||||||||||
| if tmux has-session -t mobile 2>/dev/null | ||||||||||||||||
| if test -n "$TMUX" | ||||||||||||||||
| tmux switch-client -t mobile | ||||||||||||||||
| else | ||||||||||||||||
| tmux attach-session -t mobile | ||||||||||||||||
| end | ||||||||||||||||
| else | ||||||||||||||||
| tmuxinator start mobile | ||||||||||||||||
|
||||||||||||||||
| tmuxinator start mobile | |
| if command -q tmuxinator | |
| tmuxinator start mobile | |
| else | |
| echo "Error: 'tmuxinator' command not found. Please install tmuxinator to start the 'mobile' session." >&2 | |
| return 1 | |
| end |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,11 @@ | ||||||||||||||
| function _tpo_function --description "Attach to tmux primary session" | ||||||||||||||
| tmux new-session -A -s primary | ||||||||||||||
| if tmux has-session -t primary 2>/dev/null | ||||||||||||||
| if test -n "$TMUX" | ||||||||||||||
| tmux switch-client -t primary | ||||||||||||||
| else | ||||||||||||||
| tmux attach-session -t primary | ||||||||||||||
| end | ||||||||||||||
| else | ||||||||||||||
|
||||||||||||||
| else | |
| else | |
| if not command -q tmuxinator | |
| echo "Error: 'tmuxinator' is not installed or not found in PATH. Cannot start primary session." >&2 | |
| return 1 | |
| end |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,79 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| function _tsh_function --description "Search tmux session history log or pane contents" | ||||||||||||||||||||||||||||||||||||||||||||
| set -l log ~/.local/share/tmux/session-history.log | ||||||||||||||||||||||||||||||||||||||||||||
| set -l pane_dir ~/.local/share/tmux/panes | ||||||||||||||||||||||||||||||||||||||||||||
| set -l archive_dir ~/.local/share/tmux/archive | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| # With argument(s): search stored pane content files (live + archived) | ||||||||||||||||||||||||||||||||||||||||||||
| if test (count $argv) -gt 0 | ||||||||||||||||||||||||||||||||||||||||||||
| if not test -d "$pane_dir" | ||||||||||||||||||||||||||||||||||||||||||||
| echo "No pane content store found at $pane_dir" | ||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||
| end | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| set -l query (string join ' ' $argv) | ||||||||||||||||||||||||||||||||||||||||||||
| set -l selected (rg -l -- "$query" "$pane_dir" "$archive_dir" 2>/dev/null \ | ||||||||||||||||||||||||||||||||||||||||||||
| | fzf --prompt="pane-search> " \ | ||||||||||||||||||||||||||||||||||||||||||||
| --height=40% \ | ||||||||||||||||||||||||||||||||||||||||||||
| --preview="rg -n -- '$query' {} 2>/dev/null | head -80" \ | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
| --preview="rg -n -- '$query' {} 2>/dev/null | head -80" \ | |
| --preview="sh -c 'rg -n -- \"\$1\" \"\$2\" 2>/dev/null | head -80' sh {q} {}" \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preview command breaks on quotes and is command-injection prone.
$query is interpolated into a shell command inside single quotes. Queries containing ' can break preview, and crafted text can inject shell tokens.
Suggested fix (pass query via env var)
set -l query (string join ' ' $argv)
+ set -lx TSH_QUERY $query
set -l selected (rg -l -- "$query" "$pane_dir" "$archive_dir" 2>/dev/null \
| fzf --prompt="pane-search> " \
--height=40% \
- --preview="rg -n -- '$query' {} 2>/dev/null | head -80" \
+ --preview='rg -n -- "$TSH_QUERY" {} 2>/dev/null | head -80' \
--preview-window=right:60%)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| set -l selected (rg -l -- "$query" "$pane_dir" "$archive_dir" 2>/dev/null \ | |
| | fzf --prompt="pane-search> " \ | |
| --height=40% \ | |
| --preview="rg -n -- '$query' {} 2>/dev/null | head -80" \ | |
| --preview-window=right:60%) | |
| set -lx TSH_QUERY $query | |
| set -l selected (rg -l -- "$query" "$pane_dir" "$archive_dir" 2>/dev/null \ | |
| | fzf --prompt="pane-search> " \ | |
| --height=40% \ | |
| --preview='rg -n -- "$TSH_QUERY" {} 2>/dev/null | head -80' \ | |
| --preview-window=right:60%) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@home-manager/programs/fish/functions/_tsh_function.fish` around lines 14 -
18, The preview command is vulnerable because $query is directly interpolated
into the --preview shell string; instead export/pass the query as an environment
variable to fzf and reference that safe var inside the preview command to avoid
quote-breaking and injection. Concretely, when constructing the pipeline that
assigns to the variable selected (the set -l selected ... | fzf invocation),
prefix the fzf invocation with an environment assignment like QUERY="$query" (or
use env QUERY="$query") and change the --preview value to use "$QUERY" (e.g.
--preview='rg -n -- "$QUERY" {} 2>/dev/null | head -80') so the user-provided
query is not shell-interpolated into the preview command.
Copilot
AI
Feb 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function doesn't verify that required commands (rg and fzf) are available before using them. If either command is missing, the function will fail with unclear error messages. Consider adding checks like 'if not command -q rg' and providing helpful error messages to guide users to install missing dependencies.
Copilot
AI
Feb 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The parsing logic assumes filename format 'sess--widx--pidx.txt' or 'sess--widx--pidx--timestamp.txt', but doesn't handle edge cases where session names might contain '--'. For example, a session named 'my--project' with window 0 and pane 0 would create 'my--project--0--0.txt', and parsing this by splitting on '--' would incorrectly extract 'my' as the session name instead of 'my--project'. Consider using a more robust delimiter or parsing strategy.
| # work--0--0.txt or work--0--0--20260226-103000.txt → sess=work, widx=0 | |
| set -l fname (string replace -r '.*/' '' "$selected" \ | |
| | string replace -r '--\d{8}-\d{6}\.txt$' '' \ | |
| | string replace '.txt' '') | |
| set -l parts (string split -- '--' $fname) | |
| set -l sess $parts[1] | |
| set -l widx $parts[2] | |
| # sess may contain `--`: e.g. my--project--0--0.txt or my--project--0--0--20260226-103000.txt | |
| set -l fname (string replace -r '.*/' '' "$selected" \ | |
| | string replace -r '--\d{8}-\d{6}\.txt$' '' \ | |
| | string replace '.txt' '') | |
| # From right: [..., sess parts..., widx, pidx] | |
| set -l parts (string split -- '--' $fname) | |
| if test (count $parts) -lt 3 | |
| echo "Unrecognized pane filename format: $fname" | |
| return | |
| end | |
| set -l widx $parts[-2] | |
| set -l sess (string join -- '--' $parts[1..-3]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Archived matches are treated as live in some cases.
Current logic only checks whether the session exists. If an archived file matches and that session still exists, the function jumps to a live window instead of returning the graceful archived message.
Suggested fix (explicit archived-path check)
if test -z "$selected"
return
end
+
+ if string match -q "$archive_dir/*" "$selected"
+ echo "Archived pane selected — content available in preview only"
+ return
+ end
# work--0--0.txt or work--0--0--20260226-103000.txt → sess=work, widx=0🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@home-manager/programs/fish/functions/_tsh_function.fish` around lines 24 -
43, The function currently strips archived timestamps from $selected and then
checks tmux session existence, causing archived files with matching live
sessions to be treated as live; detect archived files first by testing $selected
for the timestamp pattern (e.g. use string match -r '\-\-\d{8}-\d{6}\.txt$' on
$selected) and if it matches, print the archived message and return (use the
same message that currently appears when a session no longer exists), otherwise
proceed to extract fname/parts and perform the tmux checks and attach/switch
logic; update references to $selected, $fname, $parts, $sess, and $widx in the
existing _tsh_function.fish code.
Copilot
AI
Feb 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using 'cat' to pipe to fzf is unnecessary. You can pass the file directly to fzf using input redirection for better performance and fewer processes. Replace 'cat "$log" | fzf' with 'fzf < "$log"'.
| set -l selected (cat "$log" | fzf \ | |
| set -l selected (fzf < "$log" \ |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,52 @@ | ||||||||||||||||||||||
| function _tss_function --description "Fuzzy-pick or create a tmux session" | ||||||||||||||||||||||
| set -l default_sessions primary mobile desktop work | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| # Build candidate list: default sessions first, then any extra existing sessions | ||||||||||||||||||||||
| set -l existing (tmux list-sessions -F '#S' 2>/dev/null) | ||||||||||||||||||||||
| set -l candidates $default_sessions | ||||||||||||||||||||||
| for s in $existing | ||||||||||||||||||||||
| if not contains $s $default_sessions | ||||||||||||||||||||||
| set -a candidates $s | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| set -l selected (printf '%s\n' $candidates | fzf \ | ||||||||||||||||||||||
| --prompt="session> " \ | ||||||||||||||||||||||
| --height=40% \ | ||||||||||||||||||||||
| --preview='tmux list-windows -F "#I: #W" -t {} 2>/dev/null' \ | ||||||||||||||||||||||
| --bind='ctrl-x:execute-silent(tmux kill-session -t {})+abort') | ||||||||||||||||||||||
|
Comment on lines
+13
to
+17
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if test -n "$selected" | ||||||||||||||||||||||
| if tmux has-session -t "$selected" 2>/dev/null | ||||||||||||||||||||||
| if test -n "$TMUX" | ||||||||||||||||||||||
| tmux switch-client -t "$selected" | ||||||||||||||||||||||
| else | ||||||||||||||||||||||
| tmux attach-session -t "$selected" | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
| else if test "$selected" = work | ||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The 'work' session handling logic here (resurrect script lookup, session check, switch/attach, tmuxinator fallback) is duplicated from Prompt for AI agents |
||||||||||||||||||||||
| set -l restore (tmux list-keys 2>/dev/null | string match -rg '(/\S+/resurrect/scripts/restore\.sh)') | ||||||||||||||||||||||
| set -l restore $restore[1] | ||||||||||||||||||||||
| if test -n "$restore" | ||||||||||||||||||||||
| tmux run-shell "$restore" | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
|
Comment on lines
+29
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If the user invokes 🛡️ Proposed fix — guard or surface the failure- if test -n "$restore"
- tmux run-shell "$restore"
- end
+ if test -n "$restore"
+ if tmux list-sessions &>/dev/null
+ tmux run-shell "$restore"
+ else
+ echo "tmux server not running; skipping resurrect restore" >&2
+ end
+ end📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| if tmux has-session -t work 2>/dev/null | ||||||||||||||||||||||
| if test -n "$TMUX" | ||||||||||||||||||||||
| tmux switch-client -t work | ||||||||||||||||||||||
| else | ||||||||||||||||||||||
| tmux attach-session -t work | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
| else | ||||||||||||||||||||||
| tmuxinator start work | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
|
Comment on lines
+26
to
+40
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for handling the 'work' session, including the
Comment on lines
+26
to
+40
|
||||||||||||||||||||||
| else if contains $selected $default_sessions | ||||||||||||||||||||||
| tmuxinator start "$selected" | ||||||||||||||||||||||
| else | ||||||||||||||||||||||
| tmux new-session -d -s "$selected" | ||||||||||||||||||||||
| if test -n "$TMUX" | ||||||||||||||||||||||
| tmux switch-client -t "$selected" | ||||||||||||||||||||||
| else | ||||||||||||||||||||||
| tmux attach-session -t "$selected" | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
| end | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,21 @@ | ||||||||||||||
| function _tsw_function --description "Fuzzy-pick any window across all sessions" | ||||||||||||||
|
||||||||||||||
| function _tsw_function --description "Fuzzy-pick any window across all sessions" | |
| function _tsw_function --description "Fuzzy-pick any window across all sessions" | |
| if not command -q fzf | |
| echo "Error: fzf is required by _tsw_function but is not installed or not in PATH." >&2 | |
| return 1 | |
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: The fzf preview uses fish-only syntax but fzf executes previews via $SHELL -c (or sh -c). If SHELL isn’t fish (common when fish is launched from another login shell), the preview command fails and the preview pane won’t render. Consider forcing fish for the preview command or using POSIX-compatible syntax.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/programs/fish/functions/_tsw_function.fish, line 4:
<comment>The fzf preview uses fish-only syntax but fzf executes previews via `$SHELL -c` (or `sh -c`). If `SHELL` isn’t fish (common when fish is launched from another login shell), the preview command fails and the preview pane won’t render. Consider forcing fish for the preview command or using POSIX-compatible syntax.</comment>
<file context>
@@ -0,0 +1,21 @@
+function _tsw_function --description "Fuzzy-pick any window across all sessions"
+ set -l selected (tmux list-windows -a -F '#{session_name}:#{window_index} #{window_name}' 2>/dev/null \
+ | fzf --prompt="window> " --height=40% \
+ --preview='set t (string split " " {})[1]; tmux list-panes -t $t -F "#P: #{pane_current_command} #{pane_current_path}" 2>/dev/null')
+
+ if test -z "$selected"
</file context>
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||||||||||||
| function _two_function --description "Attach to tmux work session" | ||||||||||||||||
| set -l restore (tmux list-keys 2>/dev/null | string match -rg '(/\S+/resurrect/scripts/restore\.sh)') | ||||||||||||||||
| set -l restore $restore[1] | ||||||||||||||||
| if test -n "$restore" | ||||||||||||||||
| tmux run-shell "$restore" | ||||||||||||||||
| end | ||||||||||||||||
|
|
||||||||||||||||
| if tmux has-session -t work 2>/dev/null | ||||||||||||||||
| if test -n "$TMUX" | ||||||||||||||||
| tmux switch-client -t work | ||||||||||||||||
| else | ||||||||||||||||
| tmux attach-session -t work | ||||||||||||||||
| end | ||||||||||||||||
| else | ||||||||||||||||
| tmuxinator start work | ||||||||||||||||
|
||||||||||||||||
| tmuxinator start work | |
| if command -q tmuxinator | |
| tmuxinator start work | |
| else | |
| echo "tmuxinator is not installed or not in PATH; cannot start 'work' session" >&2 | |
| return 1 | |
| end |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,44 @@ | ||||||||||
| #!/usr/bin/env sh | ||||||||||
| mkdir -p ~/.local/share/tmux/panes | ||||||||||
| mkdir -p ~/.local/share/tmux/archive | ||||||||||
| LOG=~/.local/share/tmux/session-history.log | ||||||||||
|
||||||||||
| PANE_DIR=~/.local/share/tmux/panes | ||||||||||
| ARCHIVE_DIR=~/.local/share/tmux/archive | ||||||||||
|
|
||||||||||
| while true; do | ||||||||||
| sleep 30 | ||||||||||
|
Comment on lines
+1
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing single-instance guard can cause logger races. If multiple logger processes run concurrently, they can race on Suggested fix (lock directory) #!/usr/bin/env sh
+LOCK_DIR="${XDG_RUNTIME_DIR:-/tmp}/tmux-session-logger.lock"
+if ! mkdir "$LOCK_DIR" 2>/dev/null; then
+ exit 0
+fi
+trap 'rmdir "$LOCK_DIR"' EXIT INT TERM
+
mkdir -p ~/.local/share/tmux/panes
mkdir -p ~/.local/share/tmux/archiveAlso applies to: 35-43 🤖 Prompt for AI Agents |
||||||||||
|
|
||||||||||
| # Append window metadata | ||||||||||
| tmux list-windows -a \ | ||||||||||
| -F "$(date +%Y-%m-%dT%H:%M:%S) #{session_name}:#{window_index} #{window_name} #{pane_current_path}" \ | ||||||||||
| >>"$LOG" 2>/dev/null | ||||||||||
|
|
||||||||||
| # Rotate existing live snapshots to .old | ||||||||||
| for f in "$PANE_DIR"/*.txt; do | ||||||||||
|
||||||||||
| for f in "$PANE_DIR"/*.txt; do | |
| set -- "$PANE_DIR"/*.txt | |
| [ -e "$1" ] || set -- | |
| for f in "$@"; do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Archived pane filenames include a duplicated .txt suffix because .txt is kept in base and then .txt is appended again. Strip .txt when computing base so archived files match the intended naming scheme.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/programs/tmux/session-logger.sh, line 37:
<comment>Archived pane filenames include a duplicated `.txt` suffix because `.txt` is kept in `base` and then `.txt` is appended again. Strip `.txt` when computing `base` so archived files match the intended naming scheme.</comment>
<file context>
@@ -0,0 +1,44 @@
+ ts=$(date +%Y%m%d-%H%M%S)
+ for old in "$PANE_DIR"/*.old; do
+ [ -f "$old" ] || continue
+ base=$(basename "${old%.old}")
+ if [ -f "$PANE_DIR/$base.txt" ]; then
+ rm -f "$old"
</file context>
Copilot
AI
Feb 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The timestamp is generated once per cycle (line 34) and used for all archived panes in that cycle. If multiple panes close in the same 30-second window, they will all have identical timestamps in their archive filenames, which could cause filename collisions and data loss. Consider adding a unique identifier (like the pane index) to the timestamp or generating a fresh timestamp for each archived file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pane index reuse can silently drop closed-pane archives.
Using sess--widx--pidx as the unique key is not stable. If a pane closes and a new pane reuses the same index before the next cycle, the old snapshot is treated as “survived” and deleted instead of archived.
Suggested fix (use `pane_id` in filenames)
- tmux list-panes -a -F "#{session_name} #{window_index} #{pane_index} #{pane_id}" \
+ tmux list-panes -a -F "#{session_name} #{window_index} #{pane_index} #{pane_id}" \
2>/dev/null | while IFS= read -r line; do
@@
- tmux capture-pane -pt "$pane_id" -S - 2>/dev/null \
- > "$PANE_DIR/$sess--$widx--$pidx.txt"
+ tmux capture-pane -pt "$pane_id" -S - 2>/dev/null \
+ > "$PANE_DIR/$sess--$widx--$pidx--$pane_id.txt"
done🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@home-manager/programs/tmux/session-logger.sh` around lines 22 - 42, The
current snapshot filenames use sess--widx--pidx which is not stable; change the
snapshot creation in the tmux capture step to include the unique pane_id (e.g.
write to "$PANE_DIR/$sess--$widx--$pidx--$pane_id.txt" instead of
"$PANE_DIR/$sess--$widx--$pidx.txt") and update the .old handling loop to
compare/move using that same pane_id-aware basename (keep using
base="${old%.old}" but ensure live check uses "$PANE_DIR/$base.txt" where base
now contains the pane_id component); in short, modify the tmux capture target
and the logic in the for-old loop so filenames and comparisons incorporate
pane_id (references: variables sess, widx, pidx, pane_id, PANE_DIR, ARCHIVE_DIR,
old, base).
Copilot
AI
Feb 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The session-logger.sh script lacks test coverage. Given that this repository has comprehensive test coverage for other shell scripts (as seen in spec/ directory), consider adding tests for session-logger.sh to verify: 1) correct filename generation, 2) archival logic when panes close, 3) handling of edge cases like missing directories or tmux not running, and 4) proper cleanup of .old files.
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -136,5 +136,19 @@ set -g @thumbs-key Space | |||||||||
| # Sessionx (session manager) | ||||||||||
| set -g @sessionx-bind 'o' | ||||||||||
|
|
||||||||||
| # Tmux sessionizer (fzf session picker) | ||||||||||
| bind S run-shell "tmux new-window 'fish -c _tss_function'" | ||||||||||
|
|
||||||||||
| # Last session (quick flip) | ||||||||||
| bind Tab switch-client -l | ||||||||||
|
|
||||||||||
| # Cross-session window picker | ||||||||||
| bind W run-shell "tmux new-window 'fish -c _tsw_function'" | ||||||||||
|
|
||||||||||
| # Extrakto (text extraction) | ||||||||||
| set -g @extrakto_key 'tab' | ||||||||||
|
|
||||||||||
| set -g history-limit 0 | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: In tmux,
Example “effectively unlimited” config: set -g history-limit 200000Citations:
In tmux, Suggested change-set -g history-limit 0
+set -g history-limit 200000📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||
| set -g history-limit 0 | |
| set -g history-limit 500000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to
primary.yml, theshell:entry is redundant. Tmuxinator will open a shell by default if no command is specified for a window.- shell