Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion config/tmuxinator/default.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{ ... }:
{
xdg.configFile."tmuxinator".source = ./tmuxinator;
xdg.configFile."tmuxinator/primary.yml".source = ./primary.yml;
xdg.configFile."tmuxinator/mobile.yml".source = ./mobile.yml;
xdg.configFile."tmuxinator/work.yml".source = ./work.yml;
xdg.configFile."tmuxinator/desktop.yml".source = ./desktop.yml;
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions home-manager/programs/fish/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
tmo = "_tmo_function";
tpo = "_tpo_function";
tsh = "_tsh_function";
tsk = "_tsk_function";
tss = "_tss_function";
tsw = "_tsw_function";
two = "_two_function";
Expand Down Expand Up @@ -211,6 +212,7 @@
"_tmo_function"
"_tpo_function"
"_tsh_function"
"_tsk_function"
"_tss_function"
"_tsw_function"
"_two_function"
Expand Down
71 changes: 17 additions & 54 deletions home-manager/programs/fish/functions/_tsh_function.fish
Original file line number Diff line number Diff line change
@@ -1,72 +1,35 @@
function _tsh_function --description "Search tmux session history log or pane contents"
set -l log ~/.local/share/tmux/session-history.log
function _tsh_function --description "Search tmux pane contents (live + archived)"
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-window=right:60%)

if test -z "$selected"
return
end

# 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]

if not tmux has-session -t "$sess" 2>/dev/null
echo "Session '$sess' no longer exists (archived pane — content shown above in preview)"
return
end

if test -n "$TMUX"
tmux switch-client -t "$sess"
tmux select-window -t "$sess:$widx" 2>/dev/null
else
tmux attach-session -t "$sess" \; select-window -t "$sess:$widx"
end
return
end

# No argument: fzf over the history log (session/window/path metadata)
if not test -f "$log"
echo "No session history found at $log"
# Default: fzf over pane content files, optional query pre-fills search
if not test -d "$pane_dir"
echo "No pane content store found at $pane_dir"
return
Comment on lines +6 to 8

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Archive-only search is currently unreachable.

Line [6] returns when pane_dir is missing, even if archive_dir exists. That breaks the intended “live + archived” behavior.

Proposed fix
-  if not test -d "$pane_dir"
-    echo "No pane content store found at $pane_dir"
-    return
-  end
+  set -l search_dirs
+  if test -d "$pane_dir"
+    set -a search_dirs "$pane_dir"
+  end
+  if test -d "$archive_dir"
+    set -a search_dirs "$archive_dir"
+  end
+  if test (count $search_dirs) -eq 0
+    echo "No pane content store found at $pane_dir or $archive_dir"
+    return
+  end
@@
-  set -l selected (rg -l -- "$query" "$pane_dir" "$archive_dir" 2>/dev/null \
+  set -l selected (rg -l -- "$query" $search_dirs 2>/dev/null \

Also applies to: 12-13

🤖 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 6 - 8,
The current early return in _tsh_function.fish aborts when pane_dir is missing
even if archive_dir exists; change the logic so it only returns when neither
pane_dir nor archive_dir exist: update the check around the "if not test -d
\"$pane_dir\"" block (and the similar block around lines 12-13) to test both
pane_dir and archive_dir, proceeding when archive_dir exists (so archive-only
search is reachable) and only echoing "No pane content store..." and returning
when both are absent.

end

set -l selected (cat "$log" | fzf \
--prompt="session-history> " \
--height=40% \
--tac \
--no-sort \
--preview='echo {}')
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% \
--query="$query" \
--preview="rg -n -- '$query' {} 2>/dev/null | head -80" \
--preview-window=right:60%)
Comment on lines +11 to +17

@cubic-dev-ai cubic-dev-ai Bot Feb 26, 2026

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.

P1: This preview command is vulnerable to command injection. If $query contains a single quote (e.g., user's), it breaks the quoting in the shell command executed by fzf, potentially executing arbitrary code.

Escape single quotes in the query before passing it to the preview command.

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/_tsh_function.fish, line 11:

<comment>This preview command is vulnerable to command injection. If `$query` contains a single quote (e.g., `user's`), it breaks the quoting in the shell command executed by `fzf`, potentially executing arbitrary code. 

Escape single quotes in the query before passing it to the preview command.</comment>

<file context>
@@ -1,72 +1,35 @@
-    --tac \
-    --no-sort \
-    --preview='echo {}')
+  set -l query (string join ' ' $argv)
+  set -l selected (rg -l -- "$query" "$pane_dir" "$archive_dir" 2>/dev/null \
+    | fzf --prompt="pane-search> " \
</file context>
Suggested change
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% \
--query="$query" \
--preview="rg -n -- '$query' {} 2>/dev/null | head -80" \
--preview-window=right:60%)
set -l query (string join ' ' $argv)
set -l query_escaped (string replace -a "'" "'\\''" "$query")
set -l selected (rg -l -- "$query" "$pane_dir" "$archive_dir" 2>/dev/null \
| fzf --prompt="pane-search> " \
--height=40% \
--query="$query" \
--preview="rg -n -- '$query_escaped' {} 2>/dev/null | head -80" \
--preview-window=right:60%)
Fix with Cubic


if test -z "$selected"
return
end

set -l target (string split ' ' $selected)[2]
set -l parts (string split ':' $target)
# 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]

if not tmux has-session -t "$sess" 2>/dev/null
echo "Session '$sess' no longer exists"
bat --style=plain "$selected" 2>/dev/null; or less "$selected"
return
end

Expand Down
34 changes: 34 additions & 0 deletions home-manager/programs/fish/functions/_tsk_function.fish
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
function _tsk_function --description "Kill tmux sessions or windows via fzf"
# Build list: sessions and windows
set -l items (begin
tmux list-sessions -F 'session #{session_name}' 2>/dev/null

@cubic-dev-ai cubic-dev-ai Bot Feb 26, 2026

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.

P2: Using a double-space delimiter is fragile, as session or window names containing double spaces will break the parsing. Consider using a more robust delimiter like a tab character (\t) in both the tmux format strings and the split logic.

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/_tsk_function.fish, line 4:

<comment>Using a double-space delimiter is fragile, as session or window names containing double spaces will break the parsing. Consider using a more robust delimiter like a tab character (`\t`) in both the tmux format strings and the split logic.</comment>

<file context>
@@ -0,0 +1,34 @@
+function _tsk_function --description "Kill tmux sessions or windows via fzf"
+  # Build list: sessions and windows
+  set -l items (begin
+    tmux list-sessions -F 'session  #{session_name}' 2>/dev/null
+    tmux list-windows -a -F 'window   #{session_name}:#{window_index}  #{window_name}' 2>/dev/null
+  end)
</file context>
Fix with Cubic

tmux list-windows -a -F 'window #{session_name}:#{window_index} #{window_name}' 2>/dev/null

@cubic-dev-ai cubic-dev-ai Bot Feb 26, 2026

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.

P1: The format string uses 3 spaces after 'window' (likely for alignment), but the parsing logic splits on 2 spaces ( ). This results in the target variable containing a leading space (e.g., ' s1:1'), causing tmux kill-window to fail. Reduce to 2 spaces to match the delimiter.

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/_tsk_function.fish, line 5:

<comment>The format string uses 3 spaces after 'window' (likely for alignment), but the parsing logic splits on 2 spaces (`  `). This results in the `target` variable containing a leading space (e.g., `' s1:1'`), causing `tmux kill-window` to fail. Reduce to 2 spaces to match the delimiter.</comment>

<file context>
@@ -0,0 +1,34 @@
+  # Build list: sessions and windows
+  set -l items (begin
+    tmux list-sessions -F 'session  #{session_name}' 2>/dev/null
+    tmux list-windows -a -F 'window   #{session_name}:#{window_index}  #{window_name}' 2>/dev/null
+  end)
+
</file context>
Fix with Cubic

end)

set -l selected (printf '%s\n' $items \
| fzf --prompt="kill> " \
--height=40% \
--multi \
--preview='
set kind (string split " " {})[1]

@cubic-dev-ai cubic-dev-ai Bot Feb 26, 2026

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.

P1: The {} placeholder is unquoted in the preview command. If a session name contains special characters (like parentheses ( ) in Fish), this allows command injection or causes syntax errors. Quote the placeholder to treat it as a string.

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/_tsk_function.fish, line 13:

<comment>The `{}` placeholder is unquoted in the preview command. If a session name contains special characters (like parentheses `( )` in Fish), this allows command injection or causes syntax errors. Quote the placeholder to treat it as a string.</comment>

<file context>
@@ -0,0 +1,34 @@
+          --height=40% \
+          --multi \
+          --preview='
+            set kind (string split "  " {})[1]
+            set target (string split "  " {})[2]
+            if test "$kind" = session
</file context>
Fix with Cubic

set target (string split " " {})[2]
if test "$kind" = session
tmux list-windows -F "#I: #W #{pane_current_path}" -t $target 2>/dev/null
else
tmux list-panes -F "#P: #{pane_current_command} #{pane_current_path}" -t $target 2>/dev/null
end')

if test -z "$selected"
return
end

for line in $selected
set -l kind (string split ' ' $line)[1]
set -l target (string split ' ' $line)[2]
if test "$kind" = session
tmux kill-session -t "$target" 2>/dev/null
else
tmux kill-window -t "$target" 2>/dev/null
end
end
end
39 changes: 39 additions & 0 deletions home-manager/programs/fish/functions/_tss_function.fish
Original file line number Diff line number Diff line change
@@ -1,4 +1,43 @@
function _tss_function --description "Fuzzy-pick or create a tmux session"
set -l log ~/.local/share/tmux/session-history.log

# --log: browse session/window metadata log
if test (count $argv) -gt 0 -a "$argv[1]" = --log
if not test -f "$log"
echo "No session history found at $log"
return
end

set -l selected (cat "$log" | fzf \
--prompt="session-history> " \
--height=40% \
--tac \
--no-sort \
--preview='echo {}')

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.

security-high high

The fzf preview command echo {} uses the {} placeholder, which is replaced by the literal content of the selected line from the session history log. Since the log contains session names, window names, and paths that can be influenced by users or processes (e.g., by creating a tmux session with a malicious name), an attacker can inject shell commands into these fields to achieve command execution when the log is browsed via tss --log. For example, a session named $(touch /tmp/pwned) would cause the command to execute when highlighted in the fzf list.


if test -z "$selected"
return
end

set -l target (string split ' ' $selected)[2]
set -l parts (string split ':' $target)
set -l sess $parts[1]
set -l widx $parts[2]
Comment on lines +22 to +25

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "_tss_function.fish" | head -5

Repository: shunkakinoki/dotfiles

Length of output: 122


🏁 Script executed:

cat -n ./home-manager/programs/fish/functions/_tss_function.fish

Repository: shunkakinoki/dotfiles

Length of output: 3250


🏁 Script executed:

find . -name "*session-history*" -o -name "*tss*" | grep -E "(log|script|config)" | head -20

Repository: shunkakinoki/dotfiles

Length of output: 47


🏁 Script executed:

rg "session-history" --type fish --type sh --type bash

Repository: shunkakinoki/dotfiles

Length of output: 93


🏁 Script executed:

rg "session-history"

Repository: shunkakinoki/dotfiles

Length of output: 355


🏁 Script executed:

cat -n ./home-manager/programs/tmux/session-logger.sh

Repository: shunkakinoki/dotfiles

Length of output: 1809


Harden log-entry parsing before using tmux targets.

Line 22 assumes a strict " " (two-space) delimiter and fixed field position [2]; if the log format is malformed or incomplete, $sess and $widx can be empty or invalid. This causes silent failures in the subsequent tmux commands (lines 27, 34, 36) because errors are suppressed. Parse the session:window token defensively and validate before use.

Proposed fix
-    set -l target (string split '  ' $selected)[2]
-    set -l parts (string split ':' $target)
-    set -l sess $parts[1]
-    set -l widx $parts[2]
+    set -l target (string match -r '[^[:space:]]+:[0-9]+' -- $selected)
+    if test -z "$target"
+      echo "Could not parse session/window from: $selected"
+      return 1
+    end
+    set -l parts (string split ':' -- $target)
+    set -l sess $parts[1]
+    set -l widx $parts[2]
📝 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.

Suggested change
set -l target (string split ' ' $selected)[2]
set -l parts (string split ':' $target)
set -l sess $parts[1]
set -l widx $parts[2]
set -l target (string match -r '[^[:space:]]+:[0-9]+' -- $selected)
if test -z "$target"
echo "Could not parse session/window from: $selected"
return 1
end
set -l parts (string split ':' -- $target)
set -l sess $parts[1]
set -l widx $parts[2]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@home-manager/programs/fish/functions/_tss_function.fish` around lines 22 -
25, The parsing of the tmux target token is fragile: replace the rigid
split-by-"  " + index access (variables target, parts, sess, widx) with
defensive parsing and validation; first ensure $selected contains a token with a
session:window form, split the token on ':' into parts and check that parts has
at least 2 elements before assigning sess and widx, and if validation fails log
an error or return early instead of calling tmux commands (so the tmux calls
that use sess/widx are not run with empty values); update the code paths that
reference target, parts, sess, and widx to use the validated values only.

Comment on lines +22 to +25

@cubic-dev-ai cubic-dev-ai Bot Feb 26, 2026

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.

P2: Validate the parsed log entry before using it to build tmux targets; otherwise malformed lines can lead to empty session/window values and broken tmux commands.

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/_tss_function.fish, line 22:

<comment>Validate the parsed log entry before using it to build tmux targets; otherwise malformed lines can lead to empty session/window values and broken tmux commands.</comment>

<file context>
@@ -1,4 +1,43 @@
+      return
+    end
+
+    set -l target (string split '  ' $selected)[2]
+    set -l parts (string split ':' $target)
+    set -l sess $parts[1]
</file context>
Suggested change
set -l target (string split ' ' $selected)[2]
set -l parts (string split ':' $target)
set -l sess $parts[1]
set -l widx $parts[2]
set -l target (string split ' ' $selected)[2]
set -l parts (string split ':' $target)
if test -z "$target" -o (count $parts) -lt 2
echo "Malformed log entry: $selected"
return
end
set -l sess $parts[1]
set -l widx $parts[2]
Fix with Cubic


if not tmux has-session -t "$sess" 2>/dev/null
echo "Session '$sess' no longer exists"
return
end

if test -n "$TMUX"
tmux switch-client -t "$sess"
tmux select-window -t "$sess:$widx" 2>/dev/null
else
tmux attach-session -t "$sess" \; select-window -t "$sess:$widx"
end
return
end

set -l default_sessions primary mobile desktop work

# Build candidate list: default sessions first, then any extra existing sessions
Expand Down
39 changes: 39 additions & 0 deletions home-manager/programs/fish/functions/_tsw_function.fish
Original file line number Diff line number Diff line change
@@ -1,4 +1,43 @@
function _tsw_function --description "Fuzzy-pick any window across all sessions"
set -l log ~/.local/share/tmux/session-history.log

# --log: browse session/window metadata log
if test (count $argv) -gt 0 -a "$argv[1]" = --log
if not test -f "$log"
echo "No session history found at $log"
return
end

set -l selected (cat "$log" | fzf \
--prompt="session-history> " \
--height=40% \
--tac \
--no-sort \
--preview='echo {}')

if test -z "$selected"
return
end

set -l target (string split ' ' $selected)[2]
set -l parts (string split ':' $target)
set -l sess $parts[1]
set -l widx $parts[2]

if not tmux has-session -t "$sess" 2>/dev/null
echo "Session '$sess' no longer exists"
return
end

if test -n "$TMUX"
tmux switch-client -t "$sess"
tmux select-window -t "$sess:$widx" 2>/dev/null
else
tmux attach-session -t "$sess" \; select-window -t "$sess:$widx"
end
return
end

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')
Expand Down
6 changes: 6 additions & 0 deletions home-manager/programs/tmux/tmux.conf
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ bind Tab switch-client -l
# Cross-session window picker
bind W run-shell "tmux new-window 'fish -c _tsw_function'"

# Pane content search (live + archived)
bind H run-shell "tmux new-window 'fish -c _tsh_function'"

# Kill sessions/windows via fzf
bind K run-shell "tmux new-window 'fish -c _tsk_function'"

@cubic-dev-ai cubic-dev-ai Bot Feb 26, 2026

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.

P2: This rebinds the existing K resize-pane shortcut, so pane resize up via Prefix+K stops working. Pick a different key or update the resize binding accordingly to avoid a regression.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At home-manager/programs/tmux/tmux.conf, line 152:

<comment>This rebinds the existing `K` resize-pane shortcut, so pane resize up via `Prefix+K` stops working. Pick a different key or update the resize binding accordingly to avoid a regression.</comment>

<file context>
@@ -148,6 +148,9 @@ bind W run-shell "tmux new-window 'fish -c _tsw_function'"
 bind H run-shell "tmux new-window 'fish -c _tsh_function'"
 
+# Kill sessions/windows via fzf
+bind K run-shell "tmux new-window 'fish -c _tsk_function'"
+
 # Extrakto (text extraction)
</file context>
Suggested change
bind K run-shell "tmux new-window 'fish -c _tsk_function'"
bind M run-shell "tmux new-window 'fish -c _tsk_function'"
Fix with Cubic


# Extrakto (text extraction)
set -g @extrakto_key 'tab'

Expand Down
Loading