一文详解如何使用脚本清理Claude Code缓存信息

Written by

in

文章目录
  • du -sh ~/.claude/*/ | sort -rh 目录 大小 存储内容 projects/ 1.0 GB 会话日志(UUID.jsonl + UUID目录) 会话日志(UUID.jsonl + UUID 目录) debug/ 145 MB 调试日志 shell-snapshots/ 83 MB Shell环境快照 file-history/ 23 MB 文件编辑历史(撤消) todos/ 8.6 MB 每会话TODO文件 plans/ 1.3 MB 计划模式输出 其他 ~800 KB 任务、粘贴缓存、图片缓存、安全警告状态_*.json 最大的罪魁祸首是 projects/。每次会话创建一个 UUID.jsonl (完整对话日志)和一个 UUID/ 目录(子代理输出,计划文件)。这些用于 claude –resume <session-id> ,但你很少会重新启动超过一周的会话。
  • 在每个项目目录中,有一个 memory/ 文件夹,包含 MEMORY.md ——这是Claude Code在会话间的持久记忆。删除它你将失去该项目的所有学习上下文。
  • 我写了一个清理脚本,带有以下安全功能: 默认干运行——除非你传递 –execute,否则不会删除任何内容 双重保护——检查目录名称和UUID模式 可配置年龄——默认为7天,传递任何数字以更改 保存到 ~/.claude/scripts/cleanup-sessions.sh: #!/bin/bash set -euo pipefail CLAUDE_DIR=”$HOME/.claude” PROJECTS_DIR=”$CLAUDE_DIR/projects” MAX_AGE_DAYS=7 DRY_RUN=true numfmt_bytes() { local bytes=$1 if [ “$bytes” -ge 1073741824 ]; then printf “%.1f GB” “$(echo “$bytes / 1073741824″ | bc -l)” elif [ “$bytes” -ge 1048576 ]; then printf “%.1f MB” “$(echo “$bytes / 1048576″ | bc -l)” elif [ “$bytes” -ge 1024 ]; then printf “%.1f KB” “$(echo “$bytes / 1024″ | bc -l)” else printf “%d B” “$bytes” fi } cleanup_files() { local dir=”$1″ pattern=”$2″ label=”$3″ local count=0 bytes=0 [ -d “$dir” ] || return 0 while IFS= read -r -d ” file; do local size size=$(stat -f%z “$file” 2&gt;/dev/null || echo 0) bytes=$((bytes + size)) count=$((count + 1)) $DRY_RUN || rm -f “$file” done &lt; &lt;(find “$dir” -maxdepth 1 -name “$pattern” -type f -mtime +”$MAX_AGE_DAYS” -print0) if [ “$count” -gt 0 ]; then echo ” $label: ${count} 个文件 ($(numfmt_bytes “$bytes”))” total_files=$((total_files + count)) total_bytes=$((total_bytes + bytes)) fi } cleanup_dir_contents() { local dir=”$1″ label=”$2″ local count=0 bytes=0 [ -d “$dir” ] || return 0 while IFS= read -r -d ” file; do local size size=$(stat -f%z “$file” 2&gt;/dev/null || echo 0) bytes=$((bytes + size)) count=$((count + 1)) $DRY_RUN || rm -f “$file” done &lt; &lt;(find “$dir” -type f -mtime +”$MAX_AGE_DAYS” -print0) if [ “$count” -gt 0 ]; then echo ” $label: ${count} 个文件 ($(numfmt_bytes “$bytes”))” total_files=$((total_files + count)) total_bytes=$((total_bytes + bytes)) fi } for arg in “$@”; do [[ “$arg” == “–execute” ]] &amp;&amp; DRY_RUN=false [[ “$arg” =~ ^[0-9]+$ ]] &amp;&amp; MAX_AGE_DAYS=”$arg” done $DRY_RUN &amp;&amp; echo “=== 干运行(添加 –execute 以实际删除) ===” || echo “=== 执行模式 ===” echo “目标:超过${MAX_AGE_DAYS}天的文件” echo “” total_files=0 total_dirs=0 total_bytes=0 echo “[项目/会话日志]” for project_dir in “$PROJECTS_DIR”/*/; do [ -d “$project_dir” ] || continue project_name=$(basename “$project_dir”) project_files=0 project_dirs=0 project_bytes=0 while IFS= read -r -d ” file; do size=$(stat -f%z “$file” 2&gt;/dev/null || echo 0) project_bytes=$((project_bytes + size)) project_files=$((project_files + 1)) $DRY_RUN || rm -f “$file” done &lt; &lt;(find “$project_dir” -maxdepth 1 -name “*.jsonl” -type f -mtime +”$MAX_AGE_DAYS” -print0) while IFS= read -r -d ” dir; do dirname=$(basename “$dir”) [[ “$dirname” == “memory” ]] &amp;&amp; continue if [[ “$dirname” =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]]; then size=$(du -sk “$dir” 2&gt;/dev/null | cut -f1) project_bytes=$((project_bytes + size * 1024)) project_dirs=$((project_dirs + 1)) $DRY_RUN || rm -rf “$dir” fi done &lt; &lt;(find “$project_dir” -maxdepth 1 -type d -mtime +”$MAX_AGE_DAYS” -not -path “$project_dir” -print0) if [ $((project_files + project_dirs)) -gt 0 ]; then echo ” $project_name: ${project_files} 个文件, ${project_dirs} 个目录 ($(numfmt_bytes $project_bytes))” total_files=$((total_files + project_files)) total_dirs=$((total_dirs + project_dirs)) total_bytes=$((total_bytes + project_bytes)) fi done echo “” echo “[其他临时数据]” cleanup_dir_contents “$CLAUDE_DIR/debug” “调试/” cleanup_dir_contents “$CLAUDE_DIR/shell-snapshots” “shell快照/” cleanup_dir_contents “$CLAUDE_DIR/file-history” “文件历史/” cleanup_dir_contents “$CLAUDE_DIR/todos” “待办事项/” cleanup_dir_contents “$CLAUDE_DIR/plans” “计划/” cleanup_dir_contents “$CLAUDE_DIR/tasks” “任务/” cleanup_dir_contents “$CLAUDE_DIR/paste-cache” “粘贴缓存/” cleanup_dir_contents “$CLAUDE_DIR/image-cache” “图片缓存/” cleanup_files “$CLAUDE_DIR” “security_warnings_state_*.json” “安全警告状态” echo “” echo “— 摘要 —” echo “文件: ${total_files}” echo “目录: ${total_dirs} (UUID会话)” echo “节省空间: $(numfmt_bytes $total_bytes)” $DRY_RUN &amp;&amp; [ $((total_files + total_dirs)) -gt 0 ] &amp;&amp; echo “” &amp;&amp; echo “要删除: $0 ${MAX_AGE_DAYS} –execute”
  • chmod +x ~/.claude/scripts/cleanup-sessions.sh # 干运行(默认,不删除任何东西) ~/.claude/scripts/cleanup-sessions.sh # 将年龄阈值更改为14天 ~/.claude/scripts/cleanup-sessions.sh 14 # 实际删除 ~/.claude/scripts/cleanup-sessions.sh 7 –execute 我在4周后的结果 文件: 6,806目录: 246 (UUID会话)节省空间: 1.3 GB
  • 路径 原因 projects/*/memory/ 持久记忆(MEMORY.md) CLAUDE.md 全局指令 settings.json 用户设置 commands/ 自定义斜杠命令 plugins/ 已安装的插件 history.jsonl 命令历史 Linux用户注意 这个脚本使用macOS stat -f%z。在Linux上,替换为 stat –format=%s 或使用 wc -c < "$file" 以实现跨平台兼容性。
  • 清理 Claude Code 缓存信息脚本 #!/usr/bin/env bash # # clean-claude-cache.sh — Clean Claude Code cache files # # Usage: # ./clean-claude-cache.sh # Dry-run (preview only, no deletion) # ./clean-claude-cache.sh -f # Actually delete # ./clean-claude-cache.sh -f –aggressive # Delete everything including sessions & history # ./clean-claude-cache.sh -f –project-only # Only clean current project’s .claude/ # ./clean-claude-cache.sh -f –global-only # Only clean ~/.claude/ (not project-level) # # Safety: Default mode is dry-run. Use -f to actually perform deletion. # set -euo pipefail # ─── Colors ─────────────────────────────────────────────────────────────────── RED=’33[0;31m’ GREEN=’33[0;32m’ YELLOW=’33[1;33m’ CYAN=’33[0;36m’ BOLD=’33[1m’ DIM=’33[2m’ NC=’33[0m’ # No Color # ─── Defaults ───────────────────────────────────────────────────────────────── FORCE=false AGGRESSIVE=false PROJECT_ONLY=false GLOBAL_ONLY=false VERBOSE=false CLAude_DIR=”$HOME/.claude” TOTAL_FREED=0 # ─── Help ───────────────────────────────────────────────────────────────────── usage() { cat <<EOF ${BOLD}clean-claude-cache.sh${NC} — Clean Claude Code cache files ${BOLD}USAGE${NC} $(basename “$0”) [OPTIONS] ${BOLD}OPTIONS${NC} -f, –force Actually delete files (default: dry-run) -a, –aggressive Also clean sessions, history, and usage data -p, –project-only Only clean project-level .claude/ directory -g, –global-only Only clean global ~/.claude/ directory -v, –verbose Show detailed file listing -h, –help Show this help message ${BOLD}EXAMPLES${NC} $(basename “$0”) # Preview what would be cleaned $(basename “$0”) -f # Clean safe items only $(basename “$0”) -f –aggressive # Deep clean everything $(basename “$0”) -f –project-only # Clean only current project cache ${BOLD}SAFE CLEANUP${NC} (default -f mode) • Cache files, debug logs, paste cache, downloads, stats cache • File edit history, shell snapshots, session environments (>3 days old) • Stale task files (>3 days), plan files (>7 days), backups (>3 days) • Empty project directories (only sessions-index.json, no real data) ${BOLD}AGGRESSIVE CLEANUP${NC} (–aggressive, adds) • Session metadata (>7 days old) • Command history (history.jsonl — full delete) • Usage data (>7 days old) • Scheduled tasks ${BOLD}PRESERVED${NC} (never deleted) • Settings (settings.json, settings.local.json) • Skills and agents • Project memories (*/memory/) • IDE configuration • Harness rules EOF exit 0 } # ─── Parse Arguments ────────────────────────────────────────────────────────── while [[ $# -gt 0 ]]; do case “$1” in -f|–force) FORCE=true; shift ;; -a|–aggressive) AGGRESSIVE=true; shift ;; -p|–project-only) PROJECT_ONLY=true; shift ;; -g|–global-only) GLOBAL_ONLY=true; shift ;; -v|–verbose) VERBOSE=true; shift ;; -h|–help) usage ;; *) echo -e “${RED}Unknown option: $1${NC}”; usage ;; esac done if [[ “$PROJECT_ONLY” == true && “$GLOBAL_ONLY” == true ]]; then echo -e “${RED}Error: –project-only and –global-only are mutually exclusive${NC}” exit 1 fi # ─── Helper Functions ───────────────────────────────────────────────────────── # Calculate directory size in bytes dir_size() { local path=”$1″ if [[ -e “$path” ]]; then du -sk “$path” 2>/dev/null | cut -f1 else echo 0 fi } # Format bytes to human-readable human_size() { local kb=”$1″ if [[ “$kb” -ge 1048576 ]]; then echo “$(echo “scale=1; $kb / 1048576″ | bc)G” elif [[ “$kb” -ge 1024 ]]; then echo “$(echo “scale=1; $kb / 1024″ | bc)M” else echo “${kb}K” fi } # Clean a directory or file clean_item() { local path=”$1″ local label=”$2″ local category=”$3″ # “safe” or “aggressive” # Skip if aggressive-only item but not in aggressive mode if [[ “$category” == “aggressive” && “$AGGRESSIVE” != true ]]; then return 0 fi if [[ ! -e “$path” ]]; then return 0 fi local size_kb size_kb=$(dir_size “$path”) local size_hr size_hr=$(human_size “$size_kb”) if [[ “$FORCE” == true ]]; then rm -rf “$path” echo -e ” ${GREEN}✓ Deleted${NC} ${DIM}${label}${NC} (${size_hr})” else echo -e ” ${YELLOW}✗ Would delete${NC} ${DIM}${label}${NC} (${size_hr})” fi TOTAL_FREED=$((TOTAL_FREED + size_kb)) } # Clean old files in a directory (older than N days) clean_old_files() { local dir=”$1″ local label=”$2″ local days=”$3″ local category=”$4″ if [[ “$category” == “aggressive” && “$AGGRESSIVE” != true ]]; then return 0 fi if [[ ! -d “$dir” ]]; then return 0 fi local count count=$(find “$dir” -mindepth 1 -maxdepth 1 -mtime +”$days” 2>/dev/null | wc -l | tr -d ‘ ‘) if [[ “$count” -eq 0 ]]; then return 0 fi local size_kb=0 while IFS= read -r f; do local fsize fsize=$(dir_size “$f”) size_kb=$((size_kb + fsize)) done < <(find “$dir” -mindepth 1 -maxdepth 1 -mtime +”$days” 2>/dev/null) local size_hr size_hr=$(human_size “$size_kb”) if [[ “$FORCE” == true ]]; then find “$dir” -mindepth 1 -maxdepth 1 -mtime +”$days” -exec rm -rf {} + echo -e ” ${GREEN}✓ Deleted ${count} old items${NC} ${DIM}${label} (>${days}d)${NC} (${size_hr})” else echo -e ” ${YELLOW}✗ Would delete ${count} old items${NC} ${DIM}${label} (>${days}d)${NC} (${size_hr})” fi TOTAL_FREED=$((TOTAL_FREED + size_kb)) } # ─── Header ─────────────────────────────────────────────────────────────────── echo “” echo -e “${BOLD}${CYAN}╔══════════════════════════════════════════════════╗${NC}” echo -e “${BOLD}${CYAN}║ Claude Code Cache Cleaner ║${NC}” echo -e “${BOLD}${CYAN}╚══════════════════════════════════════════════════╝${NC}” echo “” if [[ “$FORCE” != true ]]; then echo -e “${YELLOW}⚠ DRY-RUN MODE — No files will be deleted. Use -f to apply.${NC}” else echo -e “${RED}⚠ FORCE MODE — Files WILL be deleted!${NC}” fi if [[ “$AGGRESSIVE” == true ]]; then echo -e “${RED}⚠ AGGRESSIVE — Including sessions, history, and usage data.${NC}” fi echo “” # ─── Global Cleanup ─────────────────────────────────────────────────────────── if [[ “$PROJECT_ONLY” != true ]]; then echo -e “${BOLD}📦 Global Cache: ${CLAude_DIR}${NC}” echo -e “${DIM}─────────────────────────────────────────────────${NC}” # ── Safe to delete entirely ── clean_item “$CLAude_DIR/cache” “Cache files” “safe” clean_item “$CLAude_DIR/debug” “Debug logs” “safe” clean_item “$CLAude_DIR/paste-cache” “Paste cache” “safe” clean_item “$CLAude_DIR/downloads” “Downloads” “safe” clean_item “$CLAude_DIR/stats-cache.json” “Stats cache” “safe” # ── Clean contents (preserve directory) — only files older than 3 days ── # Using time-based cleanup to avoid deleting files from active sessions clean_old_files “$CLAude_DIR/file-history” “File edit history” 3 “safe” clean_old_files “$CLAude_DIR/shell-snapshots” “Shell snapshots” 3 “safe” clean_old_files “$CLAude_DIR/session-env” “Session environments” 3 “safe” clean_old_files “$CLAude_DIR/backups” “Backup files” 3 “safe” clean_old_files “$CLAude_DIR/tasks” “Task history” 3 “safe” clean_old_files “$CLAude_DIR/plans” “Plan files” 7 “safe” # ── Aggressive-only — also time-limited to protect active sessions ── clean_old_files “$CLAude_DIR/sessions” “Session metadata” 7 “aggressive” clean_item “$CLAude_DIR/history.jsonl” “Command history” “aggressive” clean_old_files “$CLAude_DIR/usage-data” “Usage data” 7 “aggressive” # ── Clean stale projects ── # NOTE: Claude Code encodes project paths with a complex scheme that doesn’t # simply map “/” to “-“. Paths with CJK characters, spaces, or dots produce # encoding patterns like “—-” that cannot be reliably reversed. # Instead, we only clean project dirs that have ONLY a sessions-index.json # (meaning no real session data was ever written — just an empty index). echo “” echo -e “${DIM} Checking for empty/unused project directories…${NC}” if [[ -d “$CLAude_DIR/projects” ]]; then while IFS= read -r proj_dir; do # Count total files in the project directory file_count=$(find “$proj_dir” -type f 2>/dev/null | wc -l | tr -d ‘ ‘) # If only 1 file and it’s sessions-index.json → never had real sessions if [[ “$file_count” -le 1 ]]; then has_only_index=$([[ -f “${proj_dir}/sessions-index.json” ]] && echo “yes” || echo “no”) if [[ “$has_only_index” == “yes” ]]; then clean_item “$proj_dir” “Empty project: $(basename “$proj_dir”)” “safe” fi fi done < <(find “$CLAude_DIR/projects” -mindepth 1 -maxdepth 1 -type d 2>/dev/null) fi # ── Clean old files in large directories ── echo “” echo -e “${DIM} Checking for old files (>${STALE_DAYS:-30} days)…${NC}” # Clean stale scheduled tasks (not from current session) if [[ -f “$CLAude_DIR/scheduled_tasks.json” ]]; then clean_item “$CLAude_DIR/scheduled_tasks.json” “Scheduled tasks” “aggressive” fi echo “” fi # ─── Project-Level Cleanup ──────────────────────────────────────────────────── if [[ “$GLOBAL_ONLY” != true ]]; then # Find all project-level .claude directories echo -e “${BOLD}📂 Project-Level Cache${NC}” echo -e “${DIM}─────────────────────────────────────────────────${NC}” # Current project if [[ -d “.claude” ]]; then echo -e ” ${CYAN}Current project:${NC} $(pwd)/.claude/” # Clean worktrees if [[ -d “.claude/worktrees” ]]; then worktree_count=$(find .claude/worktrees -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ‘ ‘) if [[ “$worktree_count” -gt 0 ]]; then # Check if any worktrees are still registered in git stale_worktrees=0 while IFS= read -r wt_dir; do if ! git worktree list 2>/dev/null | grep -q “$(basename “$wt_dir”)”; then stale_worktrees=$((stale_worktrees + 1)) fi done < <(find .claude/worktrees -mindepth 1 -maxdepth 1 -type d 2>/dev/null) if [[ “$stale_worktrees” -gt 0 ]]; then echo -e ” ${DIM} Found ${stale_worktrees} stale worktree(s) (not registered in git)${NC}” # Only clean stale worktrees in force mode if [[ “$FORCE” == true ]]; then while IFS= read -r wt_dir; do if ! git worktree list 2>/dev/null | grep -q “$(basename “$wt_dir”)”; then rm -rf “$wt_dir” echo -e ” ${GREEN}✓ Deleted stale worktree${NC} ${DIM}$(basename “$wt_dir”)${NC}” fi done < <(find .claude/worktrees -mindepth 1 -maxdepth 1 -type d 2>/dev/null) else echo -e ” ${YELLOW}✗ Would clean stale worktrees${NC}” fi fi fi fi # Clean project-level scheduled tasks clean_item “.claude/scheduled_tasks.json” “Project scheduled tasks” “aggressive” # Clean project-level session/task artifacts (but preserve memory/ and skills/) for subdir in .claude/tasks .claude/plans .claude/session-env; do if [[ -d “$subdir” ]]; then clean_old_files “$subdir” “$(basename “$subdir”)” 3 “safe” fi done else echo -e ” ${DIM}No .claude/ directory in current project${NC}” fi echo “” fi # ─── Summary ────────────────────────────────────────────────────────────────── echo -e “${BOLD}══════════════════════════════════════════════════${NC}” total_hr=$(human_size “$TOTAL_FREED”) if [[ “$FORCE” == true ]]; then echo -e “${GREEN}${BOLD} Freed: ${total_hr}${NC}” else echo -e “${YELLOW}${BOLD} Would free: ${total_hr}${NC}” echo -e “” echo -e ” Run with ${BOLD}-f${NC} to apply: $(basename “$0″) -f” if [[ “$AGGRESSIVE” != true ]]; then echo -e ” Add ${BOLD}–aggressive${NC} for deeper clean: $(basename “$0″) -f –aggressive” fi fi echo -e “${BOLD}══════════════════════════════════════════════════${NC}” echo “” # ─── Preserved Items Info ───────────────────────────────────────────────────── echo -e “${DIM}Preserved (not deleted):${NC}” echo -e “${DIM} • Settings files (settings.json, settings.local.json)${NC}” echo -e “${DIM} • Skills, agents, and plugins${NC}” echo -e “${DIM} • Project memories (*/memory/)${NC}” echo -e “${DIM} • IDE configuration${NC}” echo -e “${DIM} • Harness rules${NC}” echo “” 到此这篇关于一文详解如何使用脚本清理Claude Code缓存信息的文章就介绍到这了,更多相关Claude Code缓存信息清理内容请搜索风君子博客以前的文章或继续浏览下面的相关文章,希望大家以后多多支持风君子博客!
  • 目录
    • 空间去哪儿了?
    • 重要:不要碰 memory/
    • 脚本
    • 用法
    • 你绝对不应该清理的文件
    • 知识扩展

    我每天使用Claude Code大约一个月,注意到我的 ~/.claude 目录占了1.3GB。没有自动清理,会议数据不断堆积。

    du -sh ~/.claude/*/ | sort -rh
    目录 大小 存储内容
    projects/ 1.0 GB 会话日志(UUID.jsonl + UUID目录) 会话日志(UUID.jsonl + UUID 目录)
    debug/ 145 MB 调试日志
    shell-snapshots/ 83 MB Shell环境快照
    file-history/ 23 MB 文件编辑历史(撤消)
    todos/ 8.6 MB 每会话TODO文件
    plans/ 1.3 MB 计划模式输出
    其他 ~800 KB 任务、粘贴缓存、图片缓存、安全警告状态_*.json

    最大的罪魁祸首是 projects/。每次会话创建一个 UUID.jsonl (完整对话日志)和一个 UUID/ 目录(子代理输出,计划文件)。这些用于 claude --resume <session-id> ,但你很少会重新启动超过一周的会话。

    在每个项目目录中,有一个 memory/ 文件夹,包含 MEMORY.md ——这是Claude Code在会话间的持久记忆。删除它你将失去该项目的所有学习上下文。

    我写了一个清理脚本,带有以下安全功能:

    • 默认干运行——除非你传递 --execute,否则不会删除任何内容
    • 双重保护——检查目录名称和UUID模式
    • 可配置年龄——默认为7天,传递任何数字以更改

    保存到 ~/.claude/scripts/cleanup-sessions.sh

    #!/bin/bash
    set -euo pipefail
    
    CLAUDE_DIR="$HOME/.claude"
    PROJECTS_DIR="$CLAUDE_DIR/projects"
    MAX_AGE_DAYS=7
    DRY_RUN=true
    
    numfmt_bytes() {
      local bytes=$1
      if [ "$bytes" -ge 1073741824 ]; then
        printf "%.1f GB" "$(echo "$bytes / 1073741824" | bc -l)"
      elif [ "$bytes" -ge 1048576 ]; then
        printf "%.1f MB" "$(echo "$bytes / 1048576" | bc -l)"
      elif [ "$bytes" -ge 1024 ]; then
        printf "%.1f KB" "$(echo "$bytes / 1024" | bc -l)"
      else
        printf "%d B" "$bytes"
      fi
    }
    
    cleanup_files() {
      local dir="$1" pattern="$2" label="$3"
      local count=0 bytes=0
      [ -d "$dir" ] || return 0
      while IFS= read -r -d '' file; do
        local size
        size=$(stat -f%z "$file" 2&gt;/dev/null || echo 0)
        bytes=$((bytes + size))
        count=$((count + 1))
        $DRY_RUN || rm -f "$file"
      done &lt; &lt;(find "$dir" -maxdepth 1 -name "$pattern" -type f -mtime +"$MAX_AGE_DAYS" -print0)
      if [ "$count" -gt 0 ]; then
        echo "  $label: ${count} 个文件 ($(numfmt_bytes "$bytes"))"
        total_files=$((total_files + count))
        total_bytes=$((total_bytes + bytes))
      fi
    }
    
    cleanup_dir_contents() {
      local dir="$1" label="$2"
      local count=0 bytes=0
      [ -d "$dir" ] || return 0
      while IFS= read -r -d '' file; do
        local size
        size=$(stat -f%z "$file" 2&gt;/dev/null || echo 0)
        bytes=$((bytes + size))
        count=$((count + 1))
        $DRY_RUN || rm -f "$file"
      done &lt; &lt;(find "$dir" -type f -mtime +"$MAX_AGE_DAYS" -print0)
      if [ "$count" -gt 0 ]; then
        echo "  $label: ${count} 个文件 ($(numfmt_bytes "$bytes"))"
        total_files=$((total_files + count))
        total_bytes=$((total_bytes + bytes))
      fi
    }
    
    for arg in "$@"; do
      [[ "$arg" == "--execute" ]] &amp;&amp; DRY_RUN=false
      [[ "$arg" =~ ^[0-9]+$ ]] &amp;&amp; MAX_AGE_DAYS="$arg"
    done
    
    $DRY_RUN &amp;&amp; echo "=== 干运行(添加 --execute 以实际删除) ===" 
             || echo "=== 执行模式 ==="
    echo "目标:超过${MAX_AGE_DAYS}天的文件"
    echo ""
    
    total_files=0
    total_dirs=0
    total_bytes=0
    
    echo "[项目/会话日志]"
    for project_dir in "$PROJECTS_DIR"/*/; do
      [ -d "$project_dir" ] || continue
      project_name=$(basename "$project_dir")
      project_files=0 project_dirs=0 project_bytes=0
    
      while IFS= read -r -d '' file; do
        size=$(stat -f%z "$file" 2&gt;/dev/null || echo 0)
        project_bytes=$((project_bytes + size))
        project_files=$((project_files + 1))
        $DRY_RUN || rm -f "$file"
      done &lt; &lt;(find "$project_dir" -maxdepth 1 -name "*.jsonl" -type f -mtime +"$MAX_AGE_DAYS" -print0)
    
      while IFS= read -r -d '' dir; do
        dirname=$(basename "$dir")
        [[ "$dirname" == "memory" ]] &amp;&amp; continue
        if [[ "$dirname" =~ ^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$ ]]; then
          size=$(du -sk "$dir" 2&gt;/dev/null | cut -f1)
          project_bytes=$((project_bytes + size * 1024))
          project_dirs=$((project_dirs + 1))
          $DRY_RUN || rm -rf "$dir"
        fi
      done &lt; &lt;(find "$project_dir" -maxdepth 1 -type d -mtime +"$MAX_AGE_DAYS" -not -path "$project_dir" -print0)
    
      if [ $((project_files + project_dirs)) -gt 0 ]; then
        echo "  $project_name: ${project_files} 个文件, ${project_dirs} 个目录 ($(numfmt_bytes $project_bytes))"
        total_files=$((total_files + project_files))
        total_dirs=$((total_dirs + project_dirs))
        total_bytes=$((total_bytes + project_bytes))
      fi
    done
    echo ""
    
    echo "[其他临时数据]"
    cleanup_dir_contents "$CLAUDE_DIR/debug" "调试/"
    cleanup_dir_contents "$CLAUDE_DIR/shell-snapshots" "shell快照/"
    cleanup_dir_contents "$CLAUDE_DIR/file-history" "文件历史/"
    cleanup_dir_contents "$CLAUDE_DIR/todos" "待办事项/"
    cleanup_dir_contents "$CLAUDE_DIR/plans" "计划/"
    cleanup_dir_contents "$CLAUDE_DIR/tasks" "任务/"
    cleanup_dir_contents "$CLAUDE_DIR/paste-cache" "粘贴缓存/"
    cleanup_dir_contents "$CLAUDE_DIR/image-cache" "图片缓存/"
    cleanup_files "$CLAUDE_DIR" "security_warnings_state_*.json" "安全警告状态"
    
    echo ""
    echo "--- 摘要 ---"
    echo "文件: ${total_files}"
    echo "目录: ${total_dirs} (UUID会话)"
    echo "节省空间: $(numfmt_bytes $total_bytes)"
    $DRY_RUN &amp;&amp; [ $((total_files + total_dirs)) -gt 0 ] &amp;&amp; echo "" &amp;&amp; echo "要删除: $0 ${MAX_AGE_DAYS} --execute"

    chmod +x ~/.claude/scripts/cleanup-sessions.sh
    # 干运行(默认,不删除任何东西)
    ~/.claude/scripts/cleanup-sessions.sh
    # 将年龄阈值更改为14天
    ~/.claude/scripts/cleanup-sessions.sh 14
    # 实际删除
    ~/.claude/scripts/cleanup-sessions.sh 7 --execute

    我在4周后的结果

    文件: 6,806
    目录: 246 (UUID会话)
    节省空间: 1.3 GB

    路径 原因
    projects/*/memory/ 持久记忆(MEMORY.md)
    CLAUDE.md 全局指令
    settings.json 用户设置
    commands/ 自定义斜杠命令
    plugins/ 已安装的插件
    history.jsonl 命令历史

    Linux用户注意

    这个脚本使用macOS stat -f%z。在Linux上,替换为 stat --format=%s 或使用 wc -c < "$file" 以实现跨平台兼容性。

    清理 Claude Code 缓存信息脚本

    #!/usr/bin/env bash
    #
    # clean-claude-cache.sh — Clean Claude Code cache files
    #
    # Usage:
    #   ./clean-claude-cache.sh              # Dry-run (preview only, no deletion)
    #   ./clean-claude-cache.sh -f           # Actually delete
    #   ./clean-claude-cache.sh -f --aggressive  # Delete everything including sessions & history
    #   ./clean-claude-cache.sh -f --project-only # Only clean current project's .claude/
    #   ./clean-claude-cache.sh -f --global-only  # Only clean ~/.claude/ (not project-level)
    #
    # Safety: Default mode is dry-run. Use -f to actually perform deletion.
    #
    set -euo pipefail
    # ─── Colors ───────────────────────────────────────────────────────────────────
    RED='33[0;31m'
    GREEN='33[0;32m'
    YELLOW='33[1;33m'
    CYAN='33[0;36m'
    BOLD='33[1m'
    DIM='33[2m'
    NC='33[0m' # No Color
    # ─── Defaults ─────────────────────────────────────────────────────────────────
    FORCE=false
    AGGRESSIVE=false
    PROJECT_ONLY=false
    GLOBAL_ONLY=false
    VERBOSE=false
    CLAude_DIR="$HOME/.claude"
    TOTAL_FREED=0
    # ─── Help ─────────────────────────────────────────────────────────────────────
    usage() {
        cat <<EOF
    ${BOLD}clean-claude-cache.sh${NC} — Clean Claude Code cache files
    ${BOLD}USAGE${NC}
        $(basename "$0") [OPTIONS]
    ${BOLD}OPTIONS${NC}
        -f, --force          Actually delete files (default: dry-run)
        -a, --aggressive     Also clean sessions, history, and usage data
        -p, --project-only   Only clean project-level .claude/ directory
        -g, --global-only    Only clean global ~/.claude/ directory
        -v, --verbose        Show detailed file listing
        -h, --help           Show this help message
    ${BOLD}EXAMPLES${NC}
        $(basename "$0")                     # Preview what would be cleaned
        $(basename "$0") -f                  # Clean safe items only
        $(basename "$0") -f --aggressive     # Deep clean everything
        $(basename "$0") -f --project-only   # Clean only current project cache
    ${BOLD}SAFE CLEANUP${NC} (default -f mode)
        • Cache files, debug logs, paste cache, downloads, stats cache
        • File edit history, shell snapshots, session environments (>3 days old)
        • Stale task files (>3 days), plan files (>7 days), backups (>3 days)
        • Empty project directories (only sessions-index.json, no real data)
    ${BOLD}AGGRESSIVE CLEANUP${NC} (--aggressive, adds)
        • Session metadata (>7 days old)
        • Command history (history.jsonl — full delete)
        • Usage data (>7 days old)
        • Scheduled tasks
    ${BOLD}PRESERVED${NC} (never deleted)
        • Settings (settings.json, settings.local.json)
        • Skills and agents
        • Project memories (*/memory/)
        • IDE configuration
        • Harness rules
    EOF
        exit 0
    }
    # ─── Parse Arguments ──────────────────────────────────────────────────────────
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -f|--force)       FORCE=true; shift ;;
            -a|--aggressive)  AGGRESSIVE=true; shift ;;
            -p|--project-only) PROJECT_ONLY=true; shift ;;
            -g|--global-only)  GLOBAL_ONLY=true; shift ;;
            -v|--verbose)     VERBOSE=true; shift ;;
            -h|--help)        usage ;;
            *) echo -e "${RED}Unknown option: $1${NC}"; usage ;;
        esac
    done
    if [[ "$PROJECT_ONLY" == true && "$GLOBAL_ONLY" == true ]]; then
        echo -e "${RED}Error: --project-only and --global-only are mutually exclusive${NC}"
        exit 1
    fi
    # ─── Helper Functions ─────────────────────────────────────────────────────────
    # Calculate directory size in bytes
    dir_size() {
        local path="$1"
        if [[ -e "$path" ]]; then
            du -sk "$path" 2>/dev/null | cut -f1
        else
            echo 0
        fi
    }
    # Format bytes to human-readable
    human_size() {
        local kb="$1"
        if [[ "$kb" -ge 1048576 ]]; then
            echo "$(echo "scale=1; $kb / 1048576" | bc)G"
        elif [[ "$kb" -ge 1024 ]]; then
            echo "$(echo "scale=1; $kb / 1024" | bc)M"
        else
            echo "${kb}K"
        fi
    }
    # Clean a directory or file
    clean_item() {
        local path="$1"
        local label="$2"
        local category="$3"  # "safe" or "aggressive"
        # Skip if aggressive-only item but not in aggressive mode
        if [[ "$category" == "aggressive" && "$AGGRESSIVE" != true ]]; then
            return 0
        fi
        if [[ ! -e "$path" ]]; then
            return 0
        fi
        local size_kb
        size_kb=$(dir_size "$path")
        local size_hr
        size_hr=$(human_size "$size_kb")
        if [[ "$FORCE" == true ]]; then
            rm -rf "$path"
            echo -e "  ${GREEN}✓ Deleted${NC}  ${DIM}${label}${NC} (${size_hr})"
        else
            echo -e "  ${YELLOW}✗ Would delete${NC}  ${DIM}${label}${NC} (${size_hr})"
        fi
        TOTAL_FREED=$((TOTAL_FREED + size_kb))
    }
    # Clean old files in a directory (older than N days)
    clean_old_files() {
        local dir="$1"
        local label="$2"
        local days="$3"
        local category="$4"
        if [[ "$category" == "aggressive" && "$AGGRESSIVE" != true ]]; then
            return 0
        fi
        if [[ ! -d "$dir" ]]; then
            return 0
        fi
        local count
        count=$(find "$dir" -mindepth 1 -maxdepth 1 -mtime +"$days" 2>/dev/null | wc -l | tr -d ' ')
        if [[ "$count" -eq 0 ]]; then
            return 0
        fi
        local size_kb=0
        while IFS= read -r f; do
            local fsize
            fsize=$(dir_size "$f")
            size_kb=$((size_kb + fsize))
        done < <(find "$dir" -mindepth 1 -maxdepth 1 -mtime +"$days" 2>/dev/null)
        local size_hr
        size_hr=$(human_size "$size_kb")
        if [[ "$FORCE" == true ]]; then
            find "$dir" -mindepth 1 -maxdepth 1 -mtime +"$days" -exec rm -rf {} +
            echo -e "  ${GREEN}✓ Deleted ${count} old items${NC}  ${DIM}${label} (>${days}d)${NC} (${size_hr})"
        else
            echo -e "  ${YELLOW}✗ Would delete ${count} old items${NC}  ${DIM}${label} (>${days}d)${NC} (${size_hr})"
        fi
        TOTAL_FREED=$((TOTAL_FREED + size_kb))
    }
    # ─── Header ───────────────────────────────────────────────────────────────────
    echo ""
    echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════════╗${NC}"
    echo -e "${BOLD}${CYAN}║     Claude Code Cache Cleaner                   ║${NC}"
    echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════════╝${NC}"
    echo ""
    if [[ "$FORCE" != true ]]; then
        echo -e "${YELLOW}⚠  DRY-RUN MODE — No files will be deleted. Use -f to apply.${NC}"
    else
        echo -e "${RED}⚠  FORCE MODE — Files WILL be deleted!${NC}"
    fi
    if [[ "$AGGRESSIVE" == true ]]; then
        echo -e "${RED}⚠  AGGRESSIVE — Including sessions, history, and usage data.${NC}"
    fi
    echo ""
    # ─── Global Cleanup ───────────────────────────────────────────────────────────
    if [[ "$PROJECT_ONLY" != true ]]; then
        echo -e "${BOLD}📦 Global Cache: ${CLAude_DIR}${NC}"
        echo -e "${DIM}─────────────────────────────────────────────────${NC}"
        # ── Safe to delete entirely ──
        clean_item "$CLAude_DIR/cache"                 "Cache files"                "safe"
        clean_item "$CLAude_DIR/debug"                 "Debug logs"                 "safe"
        clean_item "$CLAude_DIR/paste-cache"           "Paste cache"                "safe"
        clean_item "$CLAude_DIR/downloads"             "Downloads"                  "safe"
        clean_item "$CLAude_DIR/stats-cache.json"      "Stats cache"                "safe"
        # ── Clean contents (preserve directory) — only files older than 3 days ──
        # Using time-based cleanup to avoid deleting files from active sessions
        clean_old_files "$CLAude_DIR/file-history"  "File edit history"    3   "safe"
        clean_old_files "$CLAude_DIR/shell-snapshots" "Shell snapshots"    3   "safe"
        clean_old_files "$CLAude_DIR/session-env"   "Session environments" 3   "safe"
        clean_old_files "$CLAude_DIR/backups"       "Backup files"         3   "safe"
        clean_old_files "$CLAude_DIR/tasks"         "Task history"         3   "safe"
        clean_old_files "$CLAude_DIR/plans"         "Plan files"           7   "safe"
        # ── Aggressive-only — also time-limited to protect active sessions ──
        clean_old_files "$CLAude_DIR/sessions"      "Session metadata"      7   "aggressive"
        clean_item "$CLAude_DIR/history.jsonl"         "Command history"            "aggressive"
        clean_old_files "$CLAude_DIR/usage-data"    "Usage data"            7   "aggressive"
        # ── Clean stale projects ──
        # NOTE: Claude Code encodes project paths with a complex scheme that doesn't
        # simply map "/" to "-". Paths with CJK characters, spaces, or dots produce
        # encoding patterns like "----" that cannot be reliably reversed.
        # Instead, we only clean project dirs that have ONLY a sessions-index.json
        # (meaning no real session data was ever written — just an empty index).
        echo ""
        echo -e "${DIM}  Checking for empty/unused project directories...${NC}"
        if [[ -d "$CLAude_DIR/projects" ]]; then
            while IFS= read -r proj_dir; do
                # Count total files in the project directory
                file_count=$(find "$proj_dir" -type f 2>/dev/null | wc -l | tr -d ' ')
                # If only 1 file and it's sessions-index.json → never had real sessions
                if [[ "$file_count" -le 1 ]]; then
                    has_only_index=$([[ -f "${proj_dir}/sessions-index.json" ]] && echo "yes" || echo "no")
                    if [[ "$has_only_index" == "yes" ]]; then
                        clean_item "$proj_dir" "Empty project: $(basename "$proj_dir")" "safe"
                    fi
                fi
            done < <(find "$CLAude_DIR/projects" -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
        fi
        # ── Clean old files in large directories ──
        echo ""
        echo -e "${DIM}  Checking for old files (>${STALE_DAYS:-30} days)...${NC}"
        # Clean stale scheduled tasks (not from current session)
        if [[ -f "$CLAude_DIR/scheduled_tasks.json" ]]; then
            clean_item "$CLAude_DIR/scheduled_tasks.json" "Scheduled tasks" "aggressive"
        fi
        echo ""
    fi
    # ─── Project-Level Cleanup ────────────────────────────────────────────────────
    if [[ "$GLOBAL_ONLY" != true ]]; then
        # Find all project-level .claude directories
        echo -e "${BOLD}📂 Project-Level Cache${NC}"
        echo -e "${DIM}─────────────────────────────────────────────────${NC}"
        # Current project
        if [[ -d ".claude" ]]; then
            echo -e "  ${CYAN}Current project:${NC} $(pwd)/.claude/"
            # Clean worktrees
            if [[ -d ".claude/worktrees" ]]; then
                worktree_count=$(find .claude/worktrees -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
                if [[ "$worktree_count" -gt 0 ]]; then
                    # Check if any worktrees are still registered in git
                    stale_worktrees=0
                    while IFS= read -r wt_dir; do
                        if ! git worktree list 2>/dev/null | grep -q "$(basename "$wt_dir")"; then
                            stale_worktrees=$((stale_worktrees + 1))
                        fi
                    done < <(find .claude/worktrees -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
                    if [[ "$stale_worktrees" -gt 0 ]]; then
                        echo -e "  ${DIM}  Found ${stale_worktrees} stale worktree(s) (not registered in git)${NC}"
                        # Only clean stale worktrees in force mode
                        if [[ "$FORCE" == true ]]; then
                            while IFS= read -r wt_dir; do
                                if ! git worktree list 2>/dev/null | grep -q "$(basename "$wt_dir")"; then
                                    rm -rf "$wt_dir"
                                    echo -e "  ${GREEN}✓ Deleted stale worktree${NC}  ${DIM}$(basename "$wt_dir")${NC}"
                                fi
                            done < <(find .claude/worktrees -mindepth 1 -maxdepth 1 -type d 2>/dev/null)
                        else
                            echo -e "  ${YELLOW}✗ Would clean stale worktrees${NC}"
                        fi
                    fi
                fi
            fi
            # Clean project-level scheduled tasks
            clean_item ".claude/scheduled_tasks.json" "Project scheduled tasks" "aggressive"
            # Clean project-level session/task artifacts (but preserve memory/ and skills/)
            for subdir in .claude/tasks .claude/plans .claude/session-env; do
                if [[ -d "$subdir" ]]; then
                    clean_old_files "$subdir" "$(basename "$subdir")" 3 "safe"
                fi
            done
        else
            echo -e "  ${DIM}No .claude/ directory in current project${NC}"
        fi
        echo ""
    fi
    # ─── Summary ──────────────────────────────────────────────────────────────────
    echo -e "${BOLD}══════════════════════════════════════════════════${NC}"
    total_hr=$(human_size "$TOTAL_FREED")
    if [[ "$FORCE" == true ]]; then
        echo -e "${GREEN}${BOLD}  Freed: ${total_hr}${NC}"
    else
        echo -e "${YELLOW}${BOLD}  Would free: ${total_hr}${NC}"
        echo -e ""
        echo -e "  Run with ${BOLD}-f${NC} to apply:  $(basename "$0") -f"
        if [[ "$AGGRESSIVE" != true ]]; then
            echo -e "  Add ${BOLD}--aggressive${NC} for deeper clean:  $(basename "$0") -f --aggressive"
        fi
    fi
    echo -e "${BOLD}══════════════════════════════════════════════════${NC}"
    echo ""
    # ─── Preserved Items Info ─────────────────────────────────────────────────────
    echo -e "${DIM}Preserved (not deleted):${NC}"
    echo -e "${DIM}  • Settings files (settings.json, settings.local.json)${NC}"
    echo -e "${DIM}  • Skills, agents, and plugins${NC}"
    echo -e "${DIM}  • Project memories (*/memory/)${NC}"
    echo -e "${DIM}  • IDE configuration${NC}"
    echo -e "${DIM}  • Harness rules${NC}"
    echo ""

    到此这篇关于一文详解如何使用脚本清理Claude Code缓存信息的文章就介绍到这了,更多相关Claude Code缓存信息清理内容请搜索风君子博客以前的文章或继续浏览下面的相关文章,希望大家以后多多支持风君子博客!

    站内搜索