From 167beadec61b67ba15541a0a0a7cb0d65e74c173 Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 17 May 2026 07:48:00 -0700 Subject: [PATCH] =?UTF-8?q?fix(@scripts/session-tools):=20=F0=9F=90=9B=20l?= =?UTF-8?q?ocate=20sessions=20by=20uuid=20instead=20of=20slug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- bin/rclaude | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/bin/rclaude b/bin/rclaude index 9b79f58..b793dfe 100755 --- a/bin/rclaude +++ b/bin/rclaude @@ -283,23 +283,26 @@ dedupe_sessions() { ' | sort -t"$(printf '\t')" -k6,6nr } -# Mirror a session JSONL from 's ~/.claude/projects//.jsonl +# Mirror a session JSONL from 's ~/.claude/projects/*/.jsonl # to 's ~/.claude/projects//.jsonl, rewriting every -# `"cwd":""` occurrence to point at . +# `"cwd":""` occurrence to point at . The source +# project-slug is NOT computed from — claude stores sessions under +# the slug of the cwd-at-session-start, which can differ from any later cwd +# the session recorded — so we search for the file by uuid instead. migrate_session() { _src=$1; _dst=$2; _uuid=$3; _src_cwd=$4; _dst_cwd=$5 - _src_slug=$(claude_slug "$_src_cwd") _dst_slug=$(claude_slug "$_dst_cwd") - _src_path="\$HOME/.claude/projects/${_src_slug}/${_uuid}.jsonl" _dst_path="\$HOME/.claude/projects/${_dst_slug}/${_uuid}.jsonl" if is_local "$_src"; then - _src_data=$(cat "$HOME/.claude/projects/${_src_slug}/${_uuid}.jsonl" 2>/dev/null || true) + _src_path=$(ls "$HOME/.claude/projects/"*/"${_uuid}.jsonl" 2>/dev/null | head -1) + [ -n "$_src_path" ] && _src_data=$(cat "$_src_path" 2>/dev/null) || _src_data="" else - _src_data=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$_src" "cat $_src_path" 2>/dev/null || true) + _src_data=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$_src" \ + "cat \$(ls \$HOME/.claude/projects/*/${_uuid}.jsonl 2>/dev/null | head -1) 2>/dev/null" || true) fi if [ -z "$_src_data" ]; then - echo "rclaude: source session not found on $_src ($_src_path)" >&2 + echo "rclaude: source session $_uuid not found anywhere under $_src:~/.claude/projects/" >&2 return 1 fi @@ -707,19 +710,22 @@ cmd_resume() { _keys="123456789abcdefghijklmnopqrstuvwxyz" # Append a trailing name column (host+uuid → display name from # ~/.claude/sessions/*.json). Empty for tmux rows and for sessions - # with no `claude -n` label. - _name_map=$(build_name_map) - _matches=$(printf '%s\n' "$_matches" | awk -F'\t' -v OFS='\t' -v map="$_name_map" ' + # with no `claude -n` label. Map is passed via tempfile because + # awk -v can't hold a multi-line value. + _name_map_f=$(mktemp /tmp/rclaude-namemap.XXXXXX 2>/dev/null || echo /tmp/rclaude-namemap.$$) + build_name_map > "$_name_map_f" + _matches=$(printf '%s\n' "$_matches" | awk -F'\t' -v OFS='\t' -v mapf="$_name_map_f" ' BEGIN { - n = split(map, lines, "\n") - for (i=1; i<=n; i++) { - split(lines[i], f, "\t") + while ((getline line < mapf) > 0) { + split(line, f, "\t") if (f[1] && f[2]) names[f[1] SUBSEP f[2]] = f[3] } + close(mapf) } { nm = ($2=="tmux") ? "" : (($1 SUBSEP $3) in names ? names[$1 SUBSEP $3] : "") print $0, nm } ') + rm -f "$_name_map_f" if [ "$_count" -gt 35 ]; then _orig_count=$_count _matches=$(printf '%s\n' "$_matches" | head -n 35) @@ -739,9 +745,10 @@ cmd_resume() { _Cp5=$(printf '\033[1;31m'); _Cp4=$(printf '\033[33m') _Cblk=$(printf '\033[31m'); _Cwait=$(printf '\033[33m') _Cinp=$(printf '\033[36m'); _Cdone=$(printf '\033[32m') + _Cname=$(printf '\033[1;35m') # display name: bold magenta else _R=; _Chost=; _Ctmux=; _Cdim=; _Ckey= - _Cp5=; _Cp4=; _Cblk=; _Cwait=; _Cinp=; _Cdone= + _Cp5=; _Cp4=; _Cblk=; _Cwait=; _Cinp=; _Cdone=; _Cname= fi # Kind column dropped — it carried no signal when all rows were the # same kind. Kind is now encoded by row shape: tmux has a ▶ marker, @@ -778,13 +785,19 @@ cmd_resume() { return "" } function fit(s, n) { return length(s) > n ? substr(s, 1, n-1) "…" : s } + # The trailing column (NF) is the optional display name set via + # `claude -n`. Wrap it in c_name when present. + function name_tag() { + if ($NF == "" || $2 == "tmux") return "" + return " " c_name "‹" $NF "›" r + } function display() { if ($2 == "tmux") return c_tmux "▶ " r $3 if ($2 == "triage") - return prio_c($4) "P" $4 r " " stat_c($5) sprintf("%-15s", $5) r " " $6 " " c_dim "[" substr($3,1,8) "]" r + return prio_c($4) "P" $4 r " " stat_c($5) sprintf("%-15s", $5) r " " $6 " " c_dim "[" substr($3,1,8) "]" r name_tag() if ($2 == "session") - return $4 " " c_dim "[" substr($3,1,8) "]" r + return $4 " " c_dim "[" substr($3,1,8) "]" r name_tag() return $3 } { @@ -797,6 +810,7 @@ cmd_resume() { -v r="$_R" -v c_host="$_Chost" -v c_tmux="$_Ctmux" -v c_dim="$_Cdim" \ -v c_p5="$_Cp5" -v c_p4="$_Cp4" \ -v c_blk="$_Cblk" -v c_wait="$_Cwait" -v c_inp="$_Cinp" -v c_done="$_Cdone" \ + -v c_name="$_Cname" \ "$_fmt_row"'{printf "\n"}' >&2 exit 1 fi @@ -813,6 +827,7 @@ cmd_resume() { -v r="$_R" -v c_host="$_Chost" -v c_tmux="$_Ctmux" -v c_dim="$_Cdim" \ -v c_p5="$_Cp5" -v c_p4="$_Cp4" \ -v c_blk="$_Cblk" -v c_wait="$_Cwait" -v c_inp="$_Cinp" -v c_done="$_Cdone" \ + -v c_name="$_Cname" \ "$_fmt_row") printf ' %s[%s]%s %s\n' "$_Ckey" "$_k" "$_R" "$_row_text" >&2 _prev_kind=$_kind_now