feat: restore previous sessions on startup - #333
Merged
Conversation
Quitting ccmanager kills the assistant processes it started, so the next run began with an empty menu and every session had to be reopened by hand. This adds a startup offer to launch them again. A durable record of the currently open sessions is written to ~/.config/ccmanager/sessions.json as sessions come and go, rather than on exit, so a crash or a kill -9 leaves it intact. Only what is needed to launch the same command again is recorded (worktree, command preset id, session name, devcontainer commands); terminal output and the conversation inside the assistant are deliberately not restored. - sessionRestoreStore: the record itself. Each mutation re-reads the file before rewriting it so two ccmanager processes sharing the file do not discard each other's sessions, and writes go through a temp file plus rename so an interrupted write cannot truncate it. - globalSessionOrchestrator: keeps the record in step with the sessions, since it is the only place that knows which project a session manager belongs to. A killed or self-exited session is forgotten; the teardown on quit suspends tracking first, so those sessions stay restorable. - sessionRestorer: filters the record down to what this run may offer (worktree still present, owner process gone, matching project) and relaunches each one, one at a time so session numbering stays correct. - RestoreSessions/App: the startup confirmation. Declining forgets the listed sessions so the offer does not come back. Single-project mode offers the repository being opened; multi-project mode offers every recorded project at once. Session now carries the preset id it was launched with, and renaming goes through SessionManager.renameSession so the record can follow it. Also extracts two blocks that would otherwise have gained a third copy: the ccmanager config directory path (utils/configDir) and the error message formatter (utils/errorMessage). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LhdLZV7mP84ZzcK81HWzVU
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Quitting CCManager kills the assistant processes it started, so the next run opens on an empty menu and every session has to be found and started again by hand. This adds a startup offer to launch them again.
Restoring here means running each session's launch command in its worktree again — the previous terminal output and the conversation held inside the assistant are deliberately not brought back. Reproducing those was considered and dropped: it would mean owning a transcript format per assistant, and every assistant already has its own resume story.
How
A record of the sessions CCManager currently has open is kept in
~/.config/ccmanager/sessions.json. It is written as sessions come and go, not on exit, so a crash, akill -9, or a closed terminal leaves it intact. Only what is needed to launch the same command again is stored (worktree, command preset id, session name, devcontainer commands, owning pid).sessionRestoreStore— the record itself. Each mutation re-reads the file before rewriting it, so two CCManager processes sharing the file do not discard each other's sessions; writes go to a temp file and are renamed into place, so an interrupted write cannot truncate it. A corrupt or hand-edited file is ignored rather than fatal.globalSessionOrchestrator— keeps the record in step with the live sessions. It is the only place that knows which project a session manager belongs to, so the wiring lives there instead of insideSessionManager. A session the user killed, or whose command exited on its own, is forgotten. The teardown on quit suspends tracking first — otherwise the shutdown would erase exactly the sessions the next run should offer.sessionRestorer— narrows the record to what this run may offer (worktree still exists, owner process no longer running, matching project) and relaunches each entry. Sessions are started one at a time because each is numbered relative to the sessions already in its worktree. An initial prompt is not replayed.RestoreSessions+App— the startup confirmation. Declining forgets the listed sessions so the offer does not return on the next start. Single-project mode offers only the repository being opened; multi-project mode offers every recorded project at once.Supporting changes:
Sessionnow carries the preset id it was launched with, and renaming goes throughSessionManager.renameSessionso the record can follow the new name. Two blocks that were about to gain a third copy are extracted instead — the config directory path (utils/configDir) and the error message formatter (utils/errorMessage).User-facing behaviour and the file location are documented in the README section added here.
Screens
The offer shown at startup when the last run left sessions open:
Verification
bun run typecheckandbun run lintare clean.bun run test: 1889 passing. New coverage: the record store (9), the restorer's filtering and relaunch paths (13), the orchestrator wiring including "keeps records on quit" (5), and the App startup flow (4).SessionManager: creating a session writes a record, killing it removes the record, and the quit-time teardown leaves it in place.Reviewers on macOS may see the three
*.submodule.test.tssuites fail locally; they callgit config --globalinbeforeAlland fail if that write is not permitted. They pass in a normal shell and are unrelated to this change.🤖 Generated with Claude Code
https://claude.ai/code/session_01LhdLZV7mP84ZzcK81HWzVU