Repository navigation
Add ccli clean: find empty sessions and set them aside after asking - #17
Merged
Merged
Conversation
ccli clean lists sessions that hold nothing: no prompt, reply, tool call, tokens, cost, subagent, workflow run or name, not running, and idle for at least --idle (1h). A name counts as content, since a named Cursor chat whose messages ccli cannot read is not empty. --unanswered adds sessions whose prompts never got a reply; it is opt in because those hold typed text. The plan is printed, and on a terminal ccli asks before changing anything; without one, or with --json, nothing changes unless --apply is given. A Claude Code transcript moves to archive/clean/<stamp>/<account>/ with the path checks move uses and leaves the index (store.Forget); one whose transcript is already gone only leaves the index. Codex, Pi and Cursor sessions are hidden, since ccli does not write their homes. Each session is checked again just before it changes: running, moved or changed since the plan means it is left with a note. A manifest beside the archive lets --undo <stamp> move the transcripts back and unhide what was hidden. A transcript that names no folder takes the folder its project key's other sessions recorded, so --dir finds it. Tested: plan, --unanswered, --dir, apply, undo and a transcript written to between plan and apply, end to end through Main on a temporary home; a dry run on real data plans 4 Claude Code archives and 9 Codex hides.
The exit trap ran under set -e, and wait on the killed sleep returns 143, so the trap stopped before it killed the private tmux server and removed the temp folder. Every run left a tmux server with a ccli view in it and a demo home behind. The cleanup now runs with set +e.
The demo home gains two sessions that stopped at their first metadata line, one whose prompt got no reply and an empty Codex thread, written after every other session so the ids in the docs stay the same. The README, tutorial and DESIGN.md describe clean and its rules; the projects sample and image change with the demo home.
From a review of clean:
- The run's record was written only at the end, so an interrupt, a crash
or a failed session could leave files in the archive that --undo did not
know about. The run now claims its folder with an exclusive mkdir (which
also refuses a second run in the same second), writes its record before
the first change and after each session, and stops at an interrupt. Undo
works from the folder, with or without the record.
- Restore failed on any file it did not expect, such as a .DS_Store, and
then skipped the remaining accounts, the unhide and the index update. It
now leaves such files where they are, and undo always unhides and updates
the index. A transcript is linked back into place, so one the tool wrote
meanwhile is never replaced, and a linked project folder is refused.
- A moved file kept neither its mode nor its time across devices; it now
keeps both, for move too.
- Tagged and hidden sessions are left alone, a Codex, Pi or Cursor session
with no recorded time is not taken as idle, billed Cursor usage is read
before deciding, and --no-index is refused.
- --json prints one shape, {sessions, undo}, with undo only when there is
something to undo, and an account that no longer exists is reported as
such on undo.
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.
ccli cleanlists sessions that hold nothing and, once you agree, sets them aside. It does this without deleting anything, and--undoputs a run back.Rules
--idle(1h). A name counts as content: a named Cursor chat whose messages ccli cannot read is not empty. Billed Cursor usage is read first and counts as content.--unanswered. Adds sessions whose prompts never got a reply. It is opt in, because those hold text you typed.--dirfinds it.What it does
--json, it changes nothing unless--applyis given.--no-indexis refused.archive/clean/<stamp>/<account>/with the path checksmoveuses, and leaves the index (store.Forget). A session whose transcript is already gone only leaves the index.manifest.jsonbefore the first change and after each session, and stops cleanly on an interrupt.--undo <stamp>works from the folder even without the record. It links transcripts back, so it never replaces one the tool wrote meanwhile, and leaves anything that is not a session in place. It unhides what the run hid and updates the index, even when one account fails.Also in this PR
set -e, so it stopped before killing its tmux server and removing its temp folder; every run leaked both. It now cleans up.movetoo.clean, with a screenshot.internal/store/tags.go.newis removed.Test plan
go vet ./...andgo test -race ./...Mainon a temporary home: dry run,--unanswered,--dir, a tagged session, apply, undo with the record removed, a transcript written to between the plan and the apply, an interrupted run, and--no-indextransfer: archive and restore round trip, a conflict, a foreign file beside a session, and a linked project folder.store:Forgetremoves one session in one account only.--unanswered. Nothing was changed.