Skip to content
Tran Cuong
Go back

Resume the right Claude Code session by typing 'claude' in zsh

Updated:
Edit page

I restart Claude Code a lot — jump between projects, close terminals, open panes, come back later with only a vague memory that a useful session existed somewhere.

claude --resume, pick from a list, continue: fine once. All day, it is friction in the wrong place. I do not want to think about session IDs. I want the shell to notice which project directory I am in and offer the most recent session for it.

flowchart LR
  A[user types claude] --> B[autosuggest strategy fires]
  B --> C[_zcr_latest_session]
  C --> D{cache hit?}
  D -- yes --> G[ghost text suggestion]
  D -- no --> E[look up newest session for PWD]
  E --> G
  G --> H[press Right arrow]
  H --> I[claude --resume SESSION_ID]

zsh-claude-resume stays small: pure zsh, POSIX tools, no jq, no Python, no daemon. Two jobs — a zsh-autosuggestions strategy that shows the resume command as ghost text, and tab completion for claude --resume.

The session lookup

Claude stores project sessions under a path derived from PWD, so the plugin turns slashes and dots into dashes and looks there first — behind a short-lived cache, since autosuggestion hooks run on every keystroke.

_zcr_latest_session() {
    local now=${EPOCHSECONDS:-$(command date +%s)}
    local cache_val="${_zcr_session_cache[$PWD]}"

    if [[ -n "$cache_val" ]]; then
        local cache_time="${cache_val%% *}"
        local cache_id="${cache_val#* }"
        if (( now - cache_time < ZSH_CLAUDE_RESUME_CACHE_TTL )); then
            print -r -- "$cache_id"
            return
        fi
    fi

    local project_dir="${HOME}/.claude/projects/${PWD//[\/.]/-}"
    local session_id

A five-second TTL avoids lag without making the shell feel stale.

Handling both storage layouts

It handles two layouts: newer JSONL files under the project directory, and older PID session files under ~/.claude/sessions. Cheap path first, fall back only if the project directory is missing.

if [[ -d "$project_dir" ]]; then
    local latest
    latest=$(command ls -t "$project_dir" 2>/dev/null | command grep -vE "^(sessions-index\.json|memory)$" | command head -1)
    [[ -n "$latest" ]] && session_id="${latest%.jsonl}"
fi
flowchart TD
  Start[need a session for PWD] --> P{projects dir exists?}
  P -- yes --> J[newest .jsonl is the session ID]
  P -- no --> S[scan ~/.claude/sessions]
  S --> M[match by cwd, newest first]
  J --> Cache[cache result for 5s]
  M --> Cache

Flag detection

I run Claude with the same flags per project. The plugin scans recent zsh history for the most common plain claude ... invocation, so if that was claude --dangerously-skip-permissions, the suggestion keeps it.

most_common=$(print -r -- "$hist_source" | \
    command grep -E "^claude " | \
    command grep -vE "(--resume|--continue| -r | -c |mcp |doctor|setup|update|config )" | \
    command sed 's/^ *//;s/ *$//' | \
    LC_ALL=C command sort | LC_ALL=C command uniq -c | LC_ALL=C command sort -rn | \
    command head -1 | command sed 's/^ *[0-9]* *//')

The autosuggestion strategy

The suggestion is just string matching. If the buffer starts with claude, build the best resume candidate and expose it as suggestion for zsh-autosuggestions to render.

_zsh_autosuggest_strategy_claude_resume() {
    typeset -g suggestion=""

    [[ "$1" != claude* ]] && return

    local session_id
    session_id=$(_zcr_latest_session) || return

    local with_flags="claude${_zcr_common_flags} --resume ${session_id}"
    local bare="claude --resume ${session_id}"

    if [[ "$with_flags" == "$1"* ]]; then
        suggestion="$with_flags"
    elif [[ "$bare" == "$1"* ]]; then
        suggestion="$bare"
    fi
}

Setup

Setup is registration — prepend the strategy to zsh-autosuggestions, wire up completion:

_zcr_detect_flags

if [[ -n "$ZSH_AUTOSUGGEST_STRATEGY" ]] || (( ${+functions[_zsh_autosuggest_strategy_default]} )); then
    ZSH_AUTOSUGGEST_STRATEGY=(claude_resume "${ZSH_AUTOSUGGEST_STRATEGY[@]}")
fi

compdef _zcr_complete_claude claude

This is the kind of tool I like most: it removes one repeated thought. Type claude, press right arrow when the suggestion looks right, and I am back in the session that already had the context. Same itch as wtguard, the guard that keeps my parallel agents off main — small pieces of a Claude Code setup I keep sharpening.


Edit page
Share this post:

Previous Post
wtguard: a three-layer guard that keeps parallel agents off main