Repository navigation
Hygiene
This page tells what the hygiene slice of the core plugin claudetto does at the start of a session and after each edit.
A user reads it to learn which files and folders claudetto removes.
A contributor reads it before an edit of slices/hygiene/.
The session.start hook starts the cleanup and does not wait for it, so it does not block the start of the session.
The cleanup is one sh script.
It removes only what it can show is not in use.
It finds the age of a folder from its modification time.
A session is live when a file in ~/.claude/sessions (or $CLAUDE_CONFIG_DIR/sessions) names its id, or when it is the current session.
| What | Where | Rule |
|---|---|---|
| Leftover probe folders |
fswatch-probe-* in <CLAUDE_CODE_TMPDIR or /tmp>/claude-<uid>
|
Older than PROBE_MAX_AGE_MIN (60 minutes). A bug of Claude Code leaves them (anthropics/claude-code#104170) |
| Session folders |
claude-<uid>/*/<uuid> in the same folder |
Older than cleanupPeriodDays (30 days when the setting is not set), and not live |
| Gate logs and other session files | <tmp>/claudetto/*/<uuid>.* |
The same rule as session folders |
| Worktrees | <project>/.claude/worktrees/* |
See below |
The probe sweep has a timeout of SWEEP_TIMEOUT_MS (60,000 ms).
A worktree goes with git worktree remove only when all of these are true:
- No session file has its
cwdin the worktree. - It is older than the age.
- It is a
gitworktree. - It is clean, with no changed, untracked, or git-ignored file, because
git worktree removedeletes the git-ignored files, such as.env. The files in a folder that a tool makes again do not count:node_modules,.venv,__pycache__,dist,build,target,.next, andcoverage. - It has no commit that is not on another branch or a remote.
Otherwise the cleanup keeps it and names the reason: live, young, not a worktree, dirty, unmerged, or not removed.
The hook writes what it did to the log of the interface, which the model does not read:
-
CLEANEDgives the counts of the removed folders, bytes, files, worktrees, and probe folders. -
WORKTREES_KEPTnames each kept worktree with its reason. -
UNKNOWN_KEPTcounts the entries of theclaude-<uid>folder that no rule names. The cleanup keeps them.
Claude cannot know that a file changed after its last edit when the user or a tool changed it. The slice keeps a ledger for this.
- After each
EditorWrite, theclassic.PostToolUsehook stores the path, a hash of the file (FNV-1a, 64 bits), and the agent that edited it (main, or the agent type). The ledger lives in the plugin state. A write that lost a race to another write runs again, at mostLEDGER_WRITE_TRIES(5) times. The ledger holds at mostLEDGER_MAX_FILES(200) files, and the oldest entry goes first. - At each prompt, the
classic.UserPromptSubmithook hashes the files of the ledger again. For a file that changed after the last edit, it adds one line to the context:The user or a tool changed `<path>` after the last edit of `<agent>`.It marks the file as told, so it says the line once for each change. - When no file changed, the hook adds nothing, so the cost is 0 tokens.
Two limits of the ledger:
- A subagent gets no line at its own next turn.
- An edit by a
Bashcommand shows as a change by "the user or a tool".
claudetto
- Start
- Slices
- Add-Ons
- Reference
- Rationale
- Evidence
- Development
Claude Code