Scheduled tasks (routines) in the Claude desktop app leave a session behind every time they run. An hourly routine makes 700+ sessions a month. They pile up in the routine's Runs list, take disk space, and slow the app down when it loads them. Until now, the only way to remove them was one click at a time.
Routine Cleanup is a Claude Code skill that prunes a routine's old runs in bulk:
- Keeps the newest N runs you choose.
- Never touches a run you replied in. It reads each transcript and only counts what a person actually typed, not skill loads, tool output, or background notifications.
- Shows the full plan first and deletes nothing until you approve it.
- Deletes through the desktop app itself, so the Runs list updates immediately, instead of leaving "Session not found on disk" ghosts behind.
First: stop the 30-day expiry from deleting your history
Claude Code already deletes old session data on its own. Anything under ~/.claude
older than cleanupPeriodDays
is swept: transcripts, subagent transcripts, checkpoint snapshots, plans. The default is
30 days, so a terminal session you want to resume or search next month is gone.
Desktop app sessions (including routine runs) are kept at any age since Claude Code
v2.1.248, unless you set desktopSessionCleanupPeriodDays. Earlier versions deleted
them after cleanupPeriodDays too, and the sweep removes only the transcript, not the
app's Runs entry. That leaves runs you can no longer open, which this skill reports as
"unverifiable".
An age cutoff can't tell the sessions you care about from routine noise, so it deletes both. We recommend turning the expiry up so nothing is lost to age, and pruning routine runs with this skill instead:
// ~/.claude/settings.json { "cleanupPeriodDays": 3650 }
Leave desktopSessionCleanupPeriodDays unset. (0 is not "keep forever": Claude Code
rejects it. The minimum is 1.) The planner prints a reminder when your setting is
below a year.
What a plan looks like
Routine : Nightly report
scheduledTaskId: nightly-report
Total runs : 618
Plan (keeping newest 4):
AUTONOMOUS (no human reply) 332 runs 403.3 MB WILL DELETE 2026-07-16 .. 2026-10-02
ENGAGED (a human replied) 43 runs 120.0 MB PROTECTED (needs --include-engaged)
UNVERIFIABLE (transcript gone) 239 runs 47.2 MB PROTECTED (needs --include-unverifiable)
Selected for deletion: 332 runs, 403.3 MB, in 14 batches of <=25
PLAN ONLY: nothing was deleted. Deletion goes through the app's delete_session tool.
Install
As a plugin (Claude Code CLI or the desktop app's Code tab):
/plugin marketplace add naw103/claude-routine-cleanup
/plugin install routine-cleanup@routine-cleanup
As a plain skill: copy skills/routine-cleanup/ to ~/.claude/skills/routine-cleanup/.
git clone https://github.com/naw103/claude-routine-cleanup
cp -r claude-routine-cleanup/skills/routine-cleanup ~/.claude/skills/Requires Python 3.9+ (standard library only) and the Claude desktop app.
Use
In an ordinary conversation in the Code tab (not inside a routine run):
/routine-cleanup nightly-report keep 10
or just ask: "clean up my bug-fix routine's old runs, keep the last 10".
Claude lists your routines, confirms which one you mean, shows the plan, and waits for your yes. The app then shows its own approval card for each batch of 25 runs.
How it decides what to delete
Each run lands in one bucket:
| Bucket | Meaning | Default |
|---|---|---|
| autonomous | nobody typed anything after the scheduled kickoff | deleted |
| engaged | you typed a message or a slash command in it | protected |
| unverifiable | the transcript is gone, so replies can't be checked | protected |
Also never selected: the newest N runs, the conversation you are in, and any run active in the last 15 minutes.
If your old transcripts were pruned by Claude Code's cleanupPeriodDays and you are
happy to delete those runs too, either say so when asked or make it the default:
// ~/.claude/routine-cleanup.json { "include_unverifiable": true }
Why it deletes through the app
Two things learned the hard way, so you don't have to:
- Deleting the files directly does not work. The desktop app keeps the Runs list
in memory and writes a run's record back when it saves or you click the run. You
get "Session not found on disk" entries that won't go away. So the bundled script
only plans; Claude deletes through the app's own
delete_sessiontool. - That tool is off inside routine runs, and accepts 25 sessions per call. So the skill runs from a normal conversation and works in batches of 25.
Known limitation: 25 runs per approval
The desktop app's delete tool accepts at most 25 sessions per call, and every call shows its own approval card. That limit is the app's, not this skill's, and there is no way around it: deleting the files directly is exactly what leaves the ghost entries described above.
In practice:
| Runs to delete | Approval cards |
|---|---|
| 100 | 4 |
| 571 | 23 |
| 1,338 | 54 |
To make it less tedious, Claude sends several batches at once, so the cards arrive together and you can approve them one after another without waiting between them. The plan tells you the batch count up front, so you know how many clicks it will be before you start. Runs only pile up this far once; after the first cleanup, running it every few weeks keeps it to a card or two.
If you know a way to delete more per call, or Anthropic raises the limit, please open an issue.
The planner on its own
skills/routine-cleanup/cleanup_runs.py never deletes anything, so it is safe to run
by hand to see where your disk went:
python3 skills/routine-cleanup/cleanup_runs.py # list routines
python3 skills/routine-cleanup/cleanup_runs.py --routine ID --keep 10 --brief
python3 skills/routine-cleanup/cleanup_runs.py --routine ID --keep 10 --json| Flag | Meaning |
|---|---|
--routine ID |
exact scheduledTaskId (omit to list routines) |
--keep N / --session ID |
bulk, or one run by id or unique prefix |
--include-engaged |
also select runs you replied in |
--include-unverifiable / --exclude-unverifiable |
override the config default |
--live-window-minutes M |
skip runs active this recently (default 15) |
--brief |
counts and date ranges instead of one line per run |
--ids / --ids-file PATH |
session ids in batches of 25 |
--json |
machine-readable plan |
Environment overrides: CLAUDE_CONFIG_DIR (default ~/.claude) and
ROUTINE_CLEANUP_APP_DIR (default: the app's data dir for your OS).
Platform notes
Built and tested on macOS with the Claude desktop app. The app data paths for
Windows (%APPDATA%\Claude) and Linux (~/.config/Claude) follow the app's usual
layout but are untested; ROUTINE_CLEANUP_APP_DIR overrides them. Issues and PRs welcome.
Development
python3 -m unittest discover -s tests -v
claude plugin validate . --strictPrivacy
Routine Cleanup runs entirely on your machine. It has no server, no analytics and no network calls.
- What it reads: your Claude settings (
~/.claude/settings.json, plus~/.claude/routine-cleanup.jsonif you create one), the desktop app's records of routine runs, and each run's transcript. It reads transcripts only to count runs and to see whether a person typed in them. - What it writes: nothing, unless you pass
--ids-file, which saves the list of run ids it plans to delete to the path you choose. - What it sends: nothing. Deletion goes through the Claude desktop app's own session tool, which asks you to approve each batch.
- What it keeps: nothing. No data leaves your computer, and the plugin stores none.
License
MIT. Not affiliated with Anthropic.