Skip to content
timileyinpelumiPublic

About

Save, restore, name and hand off Claude Code sessions across Linux terminals

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

ccshift

ccshift

Keep your Claude Code sessions across restarts.

ccshift saves every Claude Code session you have open, in the order of their tabs, and restores them all with one command. It also gives sessions readable names, finds old sessions by what was said in them, warns you when one is running out of context, and hands a full session over to a fresh one. Codex CLI and Gemini CLI sessions are saved and restored too.

ci release licence

ccshift lists three sessions, saves them, restores them after the tabs close, searches past sessions and jumps to one from the picker

Website: https://www.timileyin.dev/ccshift

If ccshift is useful to you, a star on GitHub helps other people find it.

Contents

Install

macOS and Linux

curl -fsSL https://www.timileyin.dev/ccshift/install.sh | sh

Windows (PowerShell)

irm https://www.timileyin.dev/ccshift/install.ps1 | iex

Debian and Ubuntu (apt, with updates through apt upgrade)

sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://timileyinpelumi.github.io/ccshift/apt/ccshift.gpg | sudo tee /etc/apt/keyrings/ccshift.gpg >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/ccshift.gpg] https://timileyinpelumi.github.io/ccshift/apt stable main" | sudo tee /etc/apt/sources.list.d/ccshift.list
sudo apt update && sudo apt install ccshift

Fedora and RHEL

sudo dnf install https://github.com/timileyinpelumi/ccshift/releases/latest/download/ccshift_amd64.rpm

With Go (1.25 or newer)

go install github.com/timileyinpelumi/ccshift/cmd/ccshift@latest

The install scripts pick the build for your machine (x86_64 or arm64), check it against the published checksum, install to ~/.local/bin on macOS and Linux or %LOCALAPPDATA%\Programs\ccshift on Windows, and offer to run ccshift init for you. Set CCSHIFT_INSTALL_DIR to change the location, CCSHIFT_VERSION to pin a release, and CCSHIFT_NO_SETUP=1 to skip the setup question. On arm64, use the arm64 packages.

Requires Claude Code.

Quick start

ccshift init      # add the autosave hooks and statusline to Claude Code
ccshift doctor    # check the setup

Restart any Claude Code sessions that were already running so they pick up the hooks. From then on your layout is saved as you work.

After a reboot or a crash:

ccshift restore

Features

Picker

Run ccshift on its own to get a list of your running and saved sessions. Type to filter, use the arrow keys to move, Enter to switch to a running session or resume a saved one, Ctrl+X to hand it off, Esc to quit.

Search

Find a past session by what was said in it, and resume it:

ccshift find refund webhook      # every session that mentions both words, newest first
ccshift find refund --here       # only sessions from this directory
ccshift find refund --resume 1   # resume the first match without asking

It searches the transcripts Claude Code keeps under ~/.claude/projects. A session that is still running is switched to instead of opened twice.

Restore

ccshift restore reopens every saved session in its own tab, in the saved order, each resumed with claude --resume. Sessions that are already running are skipped, so running it twice is safe.

ccshift restore              # every workspace
ccshift restore work         # one workspace
ccshift restore work --pick  # choose an older save
ccshift restore --dry-run    # show what would open

Autosave

After ccshift init, the layout is saved when a session starts and after each turn. Autosave never drops a session by itself:

  • A session you exit (/exit, Ctrl+D, Ctrl+C) leaves the save.
  • A session that was killed, or whose terminal was closed, stays in the save and comes back on the next restore.
  • ccshift forget <name> removes a session for good.
  • A saved session that has not run for stale_days (default 14) is dropped.

ccshift save saves on demand. ccshift save --edit lets you reorder or remove sessions in your editor first.

Names

Claude Code calls a new session something like api-d7. ccshift gives it a name built from where it is running:

Situation Name
Branch contains a ticket id api · PAY-2193
Any other branch web · checkout-v2
Default branch, once Claude has titled the session docs · Install guide
Two sessions with the same name api · PAY-2193 #2

The name follows the branch. A name you set yourself, with /rename in Claude or ccshift rename, always wins. On terminals that allow it, the tab title is kept in step.

ccshift new                     # start Claude in this tab with a generated name
ccshift new login fix           # ...or your own
ccshift rename 3 login fix      # rename session 3 from ccshift ls
ccshift rename . login fix      # rename the session you are in: !ccshift rename . login fix
ccshift rename 3 --reset        # back to the generated name
ccshift rename --edit           # edit every name in your editor

Claude itself takes a new name when the session is next resumed. For an immediate change, run /rename in that session.

Context warning

ccshift ls shows how much of its context window each session has used. You get a desktop notification when a session passes 70% and again at 85%. It names the session's code, so handing it off is ccshift handoff KTR, and on Linux it has a Hand off button that does it for you. Your own statusline keeps working: ccshift runs it and shows its output.

Handoff

When a session is nearly full, continue it in a fresh one:

ccshift handoff api              # hand off the session named "api"
ccshift handoff api --edit       # read and edit the brief first
ccshift handoff api --close-old  # close the old tab afterwards
ccshift handoff api --no-launch  # only write the brief
ccshift handoff api --reuse      # start from the brief already written

Inside Claude, !ccshift handoff hands off the session you are in.

ccshift reads the session's transcript, has a fresh Claude write a brief (goal, constraints and decisions, what is done, current state, next steps, open questions, gotchas), and opens a new session named api (2) that reads the brief and carries on, using the same model as the old session. The brief writer can read the repository but cannot change anything. The new session takes the old one's place in the saved layout.

To have this happen by itself, set auto_handoff = true. When a session passes the last warning threshold (85% by default), ccshift writes the brief in the background and opens the new session in a new tab. The old session stays open.

Codex CLI and Gemini CLI

If ~/.codex or ~/.gemini exists, ccshift init adds the same hooks to Codex CLI (~/.codex/hooks.json) and Gemini CLI (~/.gemini/settings.json). Their sessions show up in ccshift ls, are saved with the rest of the layout, and are restored with codex resume <id> or gemini --resume <id>. Handoff, names and the context warning are for Claude Code sessions only. Codex reports every exit the same way, so an exited Codex session stays in the save until you ccshift forget it or it goes stale.

Workspaces

Group sessions by directory. Each workspace is saved separately and restored into its own window.

[workspaces]
work = ["~/dev/work"]
personal = ["~/dev/personal"]

Sessions outside these paths go into default. ccshift ws lists workspaces.

Commands

Command What it does
ccshift Open the picker
ccshift find <words> [--here] [--resume N] Search past sessions and resume one
ccshift ls [--all] [--json] List running sessions in tab order, with status and context used
ccshift save [workspace] [--edit] [--force] Save the layout now
ccshift restore [workspace] [--pick] [--dry-run] Reopen saved sessions
ccshift new [name] [-- claude args] Start a named session in this tab
ccshift rename <target> <name> Name a session. Also --reset and --edit
ccshift focus <target> Switch to a session's tab
ccshift handoff [target] Continue a session in a fresh one
ccshift forget <name> Remove a session from the save
ccshift ws List workspaces
ccshift init [--remove] Add or remove the hooks and statusline
ccshift update [--check] Install the latest version
ccshift uninstall [--keep-data] Remove ccshift and everything it added
ccshift doctor Check the setup
ccshift feedback Open the feedback form
ccshift version Print the version

A <target> is a number from ccshift ls, a session's three letter code from ccshift ls (such as KTR, in any case), a name, the start of a name, the start of a session id, or . for the session the command is run from. A session keeps its code across restarts. Most commands take --terminal <name> to override terminal detection.

Terminals

Terminal System Restore opens Tab order Titles and focus
tmux, kitty, WezTerm, zellij 0.44+ Linux, macOS tabs read from the terminal yes
Konsole Linux tabs read from the terminal yes
iTerm2 macOS tabs read from the terminal yes
Terminal macOS windows read from the terminal yes
WezTerm Windows tabs read from the terminal yes
Windows Terminal Windows tabs kept from the last restore no
GNOME Terminal, Ptyxis, Tilix, Xfce Terminal Linux tabs kept from the last restore no
Alacritty, foot, Ghostty Linux, macOS windows kept from the last restore no

Some terminals cannot report their tab order to another program. For those, ccshift keeps the order it last restored, and ccshift save --edit sets it by hand.

Setup notes:

  • kitty needs remote control. Add to kitty.conf:
    allow_remote_control socket-only
    listen_on unix:/tmp/kitty-{kitty_pid}
    
  • Konsole needs qdbus for tab order.
  • zellij needs 0.44 or newer.
  • WSL runs the Linux version.

ccshift doctor reports anything your terminal is missing.

Configuration

Optional. ~/.config/ccshift/config.toml on macOS and Linux, %AppData%\ccshift\config.toml on Windows.

warn_thresholds = [70, 85]        # context percentages that notify
autosave_debounce_seconds = 30    # at most one autosave per this many seconds
stale_days = 14                   # drop a saved session that has not run for this long
history_keep = 20                 # manual saves kept per workspace
sync_tab_titles = true            # keep tab titles in step with session names
brief_model = "sonnet"            # model that writes handoff briefs
support_note = true               # an occasional line asking for support
auto_update = true                # install new releases automatically
auto_handoff = false              # hand off by itself at the last warning threshold
telemetry = true                  # send anonymous usage data
ticket_pattern = "\\b([A-Z][A-Z0-9]{1,5})[-_](\\d{2,})\\b"   # prefix and number of a ticket id
ticket_ignore = ["fix", "node", "react"]                     # prefixes that are not tickets

[workspaces]
work = ["~/dev/work"]

How it works

ccshift is a single binary with no daemon. ccshift init adds three hooks to Claude Code's settings.json (SessionStart, Stop, SessionEnd) and wraps your statusline command. The hooks call ccshift hook …, which saves the layout and records which tab each session is in. The statusline wrapper records context usage and then runs your own statusline. For Codex CLI and Gemini CLI, init adds the same three hooks to their own config files.

  • Hooks always exit 0, run under a two second budget and log failures to a file. They cannot break a Claude session.
  • State lives in ~/.local/state/ccshift (%LocalAppData%\ccshift\state on Windows): saved layouts and their history, session names, context usage, handoff briefs and a log.
  • init backs up settings.json to settings.json.ccshift-bak, keeps its key order, and leaves your other hooks alone.

The design is written up in docs/design.

Platform status

System Status
Linux Stable
macOS Beta
Windows Beta

Update

ccshift checks for a new release once a day, after a command you run in a terminal, and installs it. Set auto_update = false to turn that off and update by hand:

ccshift update          # install the latest version
ccshift update --check  # only say whether there is one

Running the install script again also updates, and asks before replacing an existing install. An apt install updates with sudo apt upgrade. An .rpm install is updated by installing the new package.

Uninstall

ccshift uninstall              # hooks, saved data, settings and the program
ccshift uninstall --keep-data  # keep saved layouts, names and settings

It asks before removing anything; --yes skips the question. If ccshift came from a .deb or .rpm, remove the program with sudo apt remove ccshift or sudo dnf remove ccshift after running ccshift uninstall.

Development

go test ./...                              # unit tests
go build -o ccshift ./cmd/ccshift
scripts/e2e.sh ./ccshift                   # end to end against a real tmux

CI runs the tests on Linux, macOS and Windows. Pushing a v* tag builds and publishes a release.

Feedback

ccshift feedback opens a short form. Bugs go to GitHub issues.

Telemetry

ccshift sends anonymous usage data once a day: a random install id, the version, operating system and its version, architecture, terminal, shell, whether it runs under WSL, the Claude Code version, the approximate location (country and city) the server sees the request coming from, and for each command its name, whether it worked, how long it took, a count (such as sessions restored) and, on failure, the error message with paths, names, quoted text and numbers removed. It never sends paths, session names, repository or branch names, or anything from your sessions, and your IP address is not stored. Turn it off with telemetry = false in the config, or DO_NOT_TRACK=1 or CCSHIFT_NO_TELEMETRY=1 in the environment.

Support

ccshift is free and built in spare time. If it saves you time, you can support it at https://paystack.shop/pay/ne1sknbf3o. Once a month, after a restore or handoff, ccshift prints one line with this link; support_note = false turns it off.

Licence

MIT. An independent project, not affiliated with Anthropic.

About

Save, restore, name and hand off Claude Code sessions across Linux terminals

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages