Keep several Claude Code sessions open at once? With this, after the terminal restarts each tab resumes its own conversation instead of a shared "most recent" one.
- Solves a concrete pain: multiple parallel Claude Code sessions survive a restart, each reopening exactly its own conversation.
- Zero state: the conversation id
is the tab's uuid (
AGTERM_SESSION_ID
). No mapping file to keep in sync or let go stale. - Idempotent: the first launch pins the conversation to the tab
(
--session-id
); later launches continue it (--resume
) - it flips automatically. - Stays out of the way:
claude mcp
,-p
, an explicitclaude --resume <other-id>
, and any launch outside agterm pass through untouched. - Small, dependency-free, pure zsh; options are function-local (
emulate -L
), so nothing leaks into your interactive shell.
agterm-specific. It needs a stable per-tab identifier and relies on agterm feeding the restored command backthrough the login shell(that is how the function intercepts it). It won't work as-is in another terminal - you'd adapt it to that terminal's equivalent.Rests on restore behavior that is verified empirically, not documented- it could change between agterm versions.** One conversation per tab.**Since conversation id = tab id, two liveclaude
processes in the same tab (a split/scratch pane shares oneAGTERM_SESSION_ID
) collide with "Session ID ... is already in use".It shadows the with a shell function. Passthrough is handled, but the list of subcommands/flags that must not be touched has to be kept current if the CLI grows new ones.claude
commandIt knows Claude Code's on-disk layout(~/.claude/projects/*/<id>.jsonl
). If that storage location changes, the "does this conversation exist" check breaks (it would always create, then hit "already in use" on restart). A one-line fix, but worth knowing.zsh only(${:l}
, the(N)
glob qualifier,emulate
). Bash needs a rewrite.Existing conversations aren't bound to tabs. After you install this, the first launch in a tab starts a new conversation pinned to that tab - it does not adopt an arbitrary earlier one.
agterm can remember the command running in a tab and re-run it on restart. The
catch: it re-runs the command verbatim, and claude
with no arguments is a new conversation, not a continuation.
The trick rests on two facts. Every agterm tab has a stable identifier
(AGTERM_SESSION_ID
) that survives a restart. And Claude Code lets you supply a
session id from the outside (--session-id
). So you can use the tab's id as the conversation id - and the tab permanently "owns" one conversation.
The function then wraps claude
: on the first launch in a tab it pins the
conversation to the tab id (--session-id
); if that conversation's file already
exists on disk it continues it (--resume
). The whole decision is one check: is
<tab-id>.jsonl
present under ~/.claude/projects/
?
On restart agterm replays the remembered command, the function recognizes "its own" id, sees the conversation already exists, and reopens exactly that one. Each tab, its own.
claude() {
emulate -L zsh
local sid=${AGTERM_SESSION_ID:l}
[[ -z $sid ]] && { command claude "$@"; return; } # not in an agterm tab -> passthrough
if [[ "$1" == (--session-id|-r|--resume) && "${2:l}" == $sid ]]; then
shift 2 # restore replayed our own flag -> re-decide below
else
local a
for a in "$@"; do # user steering a session/subcommand/headless?
case $a in
-r|--resume|--session-id|--resume=*|--session-id=*|-r=*|-c|--continue|-p|--print|-v|--version|-h|--help|mcp|update|doctor|config|install|migrate-installer|setup-token)
command claude "$@"; return ;; # -> hand off untouched
esac
done
fi
local -a f=(~/.claude/projects/*/$sid.jsonl(N)) # does this tab's session already exist?
if (( $#f )); then command claude --resume "$sid" "$@" # yes -> continue it
else command claude --session-id "$sid" "$@"; fi # no -> create it with this fixed id
}
File:~/.zshrc
- the config your interactive login zsh reads. That matters: agterm feeds the restored command into that shell, so the function must be defined there (not in
.zshenv
/.zprofile
).Requirements: zsh; agterm with "Restore running commands on restart" enabled (Settings); Claude Code sessions stored in the default~/.claude/projects/
.Activate: new tabs/shells pick it up automatically; in an already-open shell runsource ~/.zshrc
.Verify: open a tab, runclaude
, say a few words, restart the terminal - the tab should return to the same conversation.ps
will showclaude --resume <tab-id>
.Remove: delete the function block from~/.zshrc
.