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.
Website: https://www.timileyin.dev/ccshift
If ccshift is useful to you, a star on GitHub helps other people find it.
- Install
- Quick start
- Features
- Commands
- Terminals
- Configuration
- How it works
- Platform status
- Update
- Uninstall
- Development
- Feedback
- Telemetry
- Support
macOS and Linux
curl -fsSL https://www.timileyin.dev/ccshift/install.sh | shWindows (PowerShell)
irm https://www.timileyin.dev/ccshift/install.ps1 | iexDebian 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 ccshiftFedora and RHEL
sudo dnf install https://github.com/timileyinpelumi/ccshift/releases/latest/download/ccshift_amd64.rpmWith Go (1.25 or newer)
go install github.com/timileyinpelumi/ccshift/cmd/ccshift@latestThe 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.
ccshift init # add the autosave hooks and statusline to Claude Code
ccshift doctor # check the setupRestart 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 restoreRun 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.
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 askingIt searches the transcripts Claude Code keeps under ~/.claude/projects. A session that is still running is switched to instead of opened twice.
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 openAfter 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.
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 editorClaude itself takes a new name when the session is next resumed. For an immediate change, run /rename in that session.
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.
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 writtenInside 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.
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.
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.
| 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.
| 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
qdbusfor tab order. - zellij needs 0.44 or newer.
- WSL runs the Linux version.
ccshift doctor reports anything your terminal is missing.
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"]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\stateon Windows): saved layouts and their history, session names, context usage, handoff briefs and a log. initbacks upsettings.jsontosettings.json.ccshift-bak, keeps its key order, and leaves your other hooks alone.
The design is written up in docs/design.
| System | Status |
|---|---|
| Linux | Stable |
| macOS | Beta |
| Windows | Beta |
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 oneRunning 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.
ccshift uninstall # hooks, saved data, settings and the program
ccshift uninstall --keep-data # keep saved layouts, names and settingsIt 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.
go test ./... # unit tests
go build -o ccshift ./cmd/ccshift
scripts/e2e.sh ./ccshift # end to end against a real tmuxCI runs the tests on Linux, macOS and Windows. Pushing a v* tag builds and publishes a release.
ccshift feedback opens a short form. Bugs go to GitHub issues.
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.
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.
MIT. An independent project, not affiliated with Anthropic.
