From a55457ee5eaee0e96f90442fcd34a8acfaddad8c Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 17 May 2026 06:47:44 -0700 Subject: [PATCH] =?UTF-8?q?fix(@scripts/session-tools):=20=F0=9F=90=9B=20o?= =?UTF-8?q?ptimize=20session=20uuid=20filtering=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- bin/rclaude | 89 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/bin/rclaude b/bin/rclaude index 3f778c4..c238b96 100755 --- a/bin/rclaude +++ b/bin/rclaude @@ -176,51 +176,58 @@ list_search_on() { list_sessions_on "$1" } -# Grep the full content of every Claude session JSONL AND the per-session -# metadata index (~/.claude/sessions/*.json — which holds display names set -# via `claude -n `) on for , and emit matching rows in -# list_sessions_on() format. Used as a fallback when the cheap pattern path -# (first-message snippets + cwd) returns nothing. -deep_search_on() { +# Filter list_sessions_on output to the rows whose uuid is in +# (newline-separated). Centralizes the regex-build + lookup. +_filter_sessions_to_uuids() { + _host=$1; _uuids=$2 + [ -z "$_uuids" ] && return 0 + _re=$(printf '%s\n' "$_uuids" | sort -u | grep -v '^$' | tr '\n' '|' | sed 's/|$//') + [ -z "$_re" ] && return 0 + CLAUDE_PROJECTS_LIMIT=5000 list_sessions_on "$_host" \ + | awk -F'\t' -v r="^($_re)$" '$3 ~ r' +} + +# Cheap match by session display name (the `claude -n ` label, stored in +# ~/.claude/sessions/.json). Single ssh round-trip. Always included in +# pattern searches because the file count is bounded (one per active pid). +name_search_on() { _host=$1; _pat=$2 _q=$(printf %s "$_pat" | sed "s/'/'\\\\''/g") - # Two passes: - # (a) grep every projects/*.jsonl for the pattern → recover uuid via filename - # (b) scan sessions/*.json (per-pid metadata: display name set via `claude -n`) - # via python, extract sessionId of any whose `name` contains the pattern - _py_script='import json, os, sys, glob + _py='import json, os, sys, glob pat = os.environ.get("RCLAUDE_PAT", "").lower() if not pat: sys.exit(0) for f in glob.glob(os.path.expanduser("~/.claude/sessions/*.json")): - try: - d = json.load(open(f)) - except Exception: - continue + try: d = json.load(open(f)) + except Exception: continue name = (d.get("name") or "").lower() - sid = d.get("sessionId") or "" - if pat in name and sid: - print(sid)' + sid = d.get("sessionId") or "" + if pat in name and sid: print(sid)' if is_local "$_host"; then - _uuids_a=$(grep -l -F -i -- "$_pat" "$HOME/.claude/projects/"*/*.jsonl 2>/dev/null \ - | awk -F/ '{print $NF}' | sed 's/\.jsonl$//') - _uuids_b=$(RCLAUDE_PAT="$_pat" python3 -c "$_py_script" 2>/dev/null || true) + _uuids=$(RCLAUDE_PAT="$_pat" python3 -c "$_py" 2>/dev/null || true) else - _uuids_a=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$_host" \ - "grep -l -F -i -- '$_q' \$HOME/.claude/projects/*/*.jsonl 2>/dev/null \ - | awk -F/ '{print \$NF}' | sed 's/\\.jsonl\$//'" 2>/dev/null || true) - _uuids_b=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$_host" \ + _uuids=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$_host" \ "RCLAUDE_PAT='$_q' python3 -" 2>/dev/null < for . +# Expensive — only used as the final fallback when cheap searches return nothing. +deep_search_on() { + _host=$1; _pat=$2 + _q=$(printf %s "$_pat" | sed "s/'/'\\\\''/g") + if is_local "$_host"; then + _uuids=$(grep -l -F -i -- "$_pat" "$HOME/.claude/projects/"*/*.jsonl 2>/dev/null \ + | awk -F/ '{print $NF}' | sed 's/\.jsonl$//') + else + _uuids=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$_host" \ + "grep -l -F -i -- '$_q' \$HOME/.claude/projects/*/*.jsonl 2>/dev/null \ + | awk -F/ '{print \$NF}' | sed 's/\\.jsonl\$//'" 2>/dev/null || true) + fi + _filter_sessions_to_uuids "$_host" "$_uuids" } # Get $HOME on (cached per host in /tmp for the life of this shell). @@ -510,8 +517,15 @@ cmd_resume() { _matches=${_tmux:-$_disk_slice} fi else - _matches=$(scan_hosts | while IFS= read -r h; do list_search_on "$h"; done) - _matches=$(printf '%s\n' "$_matches" | grep -F -i -- "$_pattern" || true) + # Pattern search ordering (cheapest → expensive): + # 1. session display names (~/.claude/sessions/*.json) — fast: one + # bounded python scan per host, hits sessions named via `claude -n` + # 2. first-user-message snippets + cwd — cheap row-grep + # Fallback (only if both empty): full-transcript grep over every jsonl + _name_rows=$(scan_hosts | while IFS= read -r h; do name_search_on "$h" "$_pattern"; done) + _cheap_all=$(scan_hosts | while IFS= read -r h; do list_search_on "$h"; done) + _cheap_rows=$(printf '%s\n' "$_cheap_all" | grep -F -i -- "$_pattern" || true) + _matches=$(printf '%s\n%s\n' "$_name_rows" "$_cheap_rows" | grep -v '^$' || true) _tmux_part=$(printf '%s\n' "$_matches" | awk -F'\t' '$2=="tmux"') _disk_part=$(printf '%s\n' "$_matches" | awk -F'\t' '$2!="tmux"' | dedupe_sessions) if [ -n "$_tmux_part" ] && [ -n "$_disk_part" ]; then @@ -519,11 +533,8 @@ cmd_resume() { else _matches=${_tmux_part:-$_disk_part} fi - # Cheap path searches first-user-message snippets + cwd. If nothing - # matched, fall back to greping the full transcript content of every - # JSONL on each host. Slower but catches mid-conversation references. if [ -z "$_matches" ]; then - printf 'rclaude: no first-message hits; searching full transcripts...\n' >&2 + printf 'rclaude: no name/snippet hits; searching full transcripts...\n' >&2 _matches=$(scan_hosts | while IFS= read -r h; do deep_search_on "$h" "$_pattern"; done | dedupe_sessions) fi fi