tmux-grab: downrank scrollback candidates that show up too often
A plain first-seen dedup let ubiquitous tokens -- prompt words, the
cwd, a repeated path component -- crowd out the one-off candidate
being hunted for. Frequency now sinks a candidate to the bottom of
the list (recency order still holds within each bucket) instead of
losing it, so it stays reachable.

The cutoff is a share of the total candidate count, not a fixed
number: what counts as "frequent" scales with how much scrollback
got captured, and a fixed count would need retuning between a
50-line pane and a 50000-line one. Checked against a real captured
pane: commit/change-id-shaped tokens topped out at ~0.02% of the
pool, while prompt/path noise ran 1-3.4%, so the default threshold
(0.5%) sits with wide margin on both sides.

Assisted-by: Claude Sonnet 5 via Claude Code
change vwmlstpvowvssvxwulyzlymxyztwvrxx
commit 92ce2d2b2bd51085123d961d89549509285a67a1
author Alpha Chen <alpha@kejadlen.dev>
date
parent sqpqxnty
diff --git a/bin/,tmux-grab b/bin/,tmux-grab
index c8cdd65d..7b2adca3 100755
--- a/bin/,tmux-grab
+++ b/bin/,tmux-grab
@@ -57,10 +57,9 @@ main() {
     local pane_text_reversed
     pane_text_reversed=$(tail -r <<<"$pane_text")
 
-    # Three passes, feeding a first-seen dedup so exact repeats collapse.
-    # Whitespace-delimited tokens first (so "foo/bar:baz" is a candidate,
-    # and a hard-wrapped URL or path -- one line now, courtesy of -J --
-    # comes through whole); then parenthesized spans with the parens
+    # Three passes. Whitespace-delimited tokens first (so "foo/bar:baz" is a
+    # candidate, and a hard-wrapped URL or path -- one line now, courtesy of
+    # -J -- comes through whole); then parenthesized spans with the parens
     # stripped (so "(utils.rb:42)" yields "utils.rb:42", and a parenthetical
     # phrase survives as one candidate instead of shattering at its inner
     # spaces); then punctuation-split sub-tokens (so "foo", "bar", "baz" are
@@ -70,8 +69,8 @@ main() {
     # or box-drawing character, since those are never what you meant to
     # copy. LC_ALL=C makes the ASCII-range check byte-exact regardless of
     # the caller's locale.
-    local tokens
-    tokens=$(
+    local raw_candidates
+    raw_candidates=$(
         {
             printf '%s\n' "$pane_text_reversed" | grep --only-matching --extended-regexp '\S+' || true
             printf '%s\n' "$pane_text_reversed" | awk '{
@@ -82,8 +81,39 @@ main() {
                 }
             }'
             printf '%s\n' "$pane_text_reversed" | grep --only-matching --extended-regexp '[[:alnum:]_]+' || true
-        } | awk '!seen[$0]++' | grep -Ev '[[:cntrl:]]' | LC_ALL=C grep -E '[ -~]' || true
+        } | grep -Ev '[[:cntrl:]]' | LC_ALL=C grep -E '[ -~]' || true
     )
+
+    # A plain first-seen dedup would let a prompt word or repeated cwd
+    # component -- present on nearly every scrollback line -- crowd out the
+    # one-off candidate actually being hunted for. Sinking anything over a
+    # frequency threshold to the bottom (recency order still applies within
+    # each bucket) fixes that without losing those tokens outright. The
+    # threshold is a share of the total candidate count rather than a fixed
+    # number, since what counts as "frequent" scales with how much
+    # scrollback got captured -- a 50-line pane and a 50000-line one need
+    # different absolute cutoffs but the same relative one. It's set high on
+    # purpose: measured against real scrollback, a commit/change id
+    # referenced a handful of times in a normal session (log, show,
+    # describe, squash target, ...) lands nowhere near the share a prompt
+    # word or repeated path component racks up just by the prompt printing
+    # on every line.
+    local freq_threshold_pct="${TMUX_GRAB_FREQ_THRESHOLD_PCT:-0.5}"
+    local tokens
+    tokens=$(printf '%s\n' "$raw_candidates" | awk -v threshold="$freq_threshold_pct" '
+        {
+            count[$0]++
+            if (!seen[$0]++) order[++n] = $0
+            total++
+        }
+        END {
+            for (i = 1; i <= n; i++) {
+                t = order[i]
+                bucket = ((count[t] / total) * 100 > threshold) ? 1 : 0
+                printf "%d\t%d\t%s\n", bucket, i, t
+            }
+        }
+    ' | sort -t $'\t' -k1,1n -k2,2n | cut -f3-)
     [[ -n "$tokens" ]] || exit 0
 
     # Float via fzf's own tmux popup, unless the caller already gave us a