用途
config-gc 为 Claude Code 的配置提供“垃圾回收”。Claude Code 的配置是只增不减的:技能、记忆文件、钩子、权限条目只会不断被添加,若不定期审查就会悄悄腐化。该技能定期扫描 ~/.claude(技能、记忆、钩子、权限、MCP 服务器、缓存等),找出不再被引用、冗余、过期或低价值的内容,并在人工确认后回收空间。
何时启用
- 用户要求清理、审计或精简 Claude Code 配置
- 用户抱怨技能过多、钩子嘈杂或会话启动变慢
- 到了每月/周期性配置审查时间
- 安装大型技能包之后,用于协调与既有配置的重叠
不适用场景:清理项目源代码(那属于重构)、清除聊天历史、卸载 Claude Code 本身。
设计理念
- 只增不减的配置会泄漏:技能、记忆文件、钩子和权限条目只会被添加,缺少定期审查就会静默腐化。
- 定期审计优于一次性清除:约每 30 天扫描一次,每次只提出一小批候选。
- 分渠道策略:每种累积类型(技能、钩子、权限等)有各自的陈旧信号,不能一套规则用到底。
- 先软删除:重命名为 .disabled > 移入 ~/.claude/_gc_trash/ > 真正删除,始终保留撤销路径。
- 强制人在回路:每个候选都单独进行 [y/n/skip] 确认,没有“全部同意”的捷径。
- 保留日志:每次 GC 运行都追加到 ~/.claude/gc_log.md,记录改动了什么、为什么、如何撤销。
扫描渠道
- 技能:~/.claude/skills/*/。信号:名称高度重叠;近期记录中从未触发;领域与用户实际工作不匹配;SKILL.md 损坏或为空。
- 记忆:~/.claude/**/memory/*.md 及其索引。信号:同一主题有多条索引项;内容与更新条目矛盾;日期已过;索引中缺失的孤立文件;应合并的不足百字片段。
- 钩子:~/.claude/hooks/ 及设置。信号:磁盘上存在但无任何钩子配置引用的脚本;被重写版本取代的旧版本。
- 权限:settings.json / settings.local.json 中的 permissions.allow。信号:重复条目;已被通配符覆盖的具体条目(例如已允许 Bash(*) 时仍存在 Bash(git push));过去实验留下的一次性授权。
- MCP 服务器:~/.claude.json 或项目 .mcp.json。信号:连接失败的服务器;功能重复;长期未使用。
- 定时提醒/任务:用户存放它们的位置。信号:超过 30 天的一次性任务;目标脚本已不存在的任务。
- 项目历史:~/.claude/projects/*/。信号:过期的交接快照;被更新状态取代的会话记录。
- 运行时缓存:cache/、file-history/、logs/、shell-snapshots/。按大小和修改时间排序;提出超过 30 天且体积较大的项。
工作流程
- 扫描所有渠道(或用户指定的子集),收集候选并附带:路径、渠道、触发信号、大小、最后修改时间。
- 按置信度排序(损坏/孤立 = 高;仅仅陈旧 = 低),以编号表格呈现。每次运行上限约 20 个候选——GC 是周期性的,不是穷举的。
- 逐条确认。对每个候选展示证据,然后询问 [y/n/skip]。用户可随时停止。
- 对确认项软删除:技能/钩子优先重命名为 .disabled,文件移入 _gc_trash/<日期>/。权限条目位于 JSON 中(无法写注释):先备份设置文件,在 gc_log.md 中逐字记录每个被移除的条目,再用 jq 从 allow 数组中移除。仅在用户明确要求时才硬删除。
- 将本次运行记录到 ~/.claude/gc_log.md:时间戳、已处理项、撤销说明。
- 报告:回收的空间、仍然健康的渠道、建议的下次审查日期。
示例扫描命令
孤立钩子脚本(渠道 3)——磁盘上无任何钩子配置引用的脚本:
for f in ~/.claude/hooks/*; do
name=$(basename "$f")
grep -rq "$name" ~/.claude/settings.json ~/.claude/settings.local.json 2>/dev/null \
|| echo "ORPHAN: $f"
done
冗余权限条目(渠道 4)——重复项,以及被通配符遮蔽的具体授权:
jq -r '.permissions.allow[]' ~/.claude/settings.local.json | sort | uniq -d
if jq -e '.permissions.allow | index("Bash(*)")' ~/.claude/settings.local.json >/dev/null; then
jq -r '.permissions.allow[]' ~/.claude/settings.local.json \
| grep '^Bash(' | grep -vF 'Bash(*)'
fi
最大的陈旧缓存(渠道 8)——使用 du -k 而非仅 GNU 支持的 find -printf,以便在 macOS/BSD 上也能运行:
find ~/.claude/file-history ~/.claude/shell-snapshots -type f -mtime +30 \
-exec du -k {} + 2>/dev/null | sort -rn | head -20
带撤销路径的软删除(只捕获一次日期,使日志与目录不会不一致):
gc_date=$(date +%Y-%m-%d)
mkdir -p ~/.claude/_gc_trash/$gc_date
mv ~/.claude/skills/dead-skill ~/.claude/_gc_trash/$gc_date/
echo "$(date -Iseconds) moved skills/dead-skill -> _gc_trash/$gc_date/ (undo: mv back)" >> ~/.claude/gc_log.md
移除已确认冗余的权限条目(JSON 无法写注释——先备份、记录,再编辑):
cp ~/.claude/settings.local.json ~/.claude/settings.local.json.bak
echo "$(date -Iseconds) removed permission entry: Bash(git push) (undo: restore from .bak or re-add)" >> ~/.claude/gc_log.md
jq '.permissions.allow -= ["Bash(git push)"]' ~/.claude/settings.local.json.bak \
> ~/.claude/settings.local.json
反模式
- 批量批准:询问“删除全部 15 个?[y/n]”违背了设计。一项一决策。
- 首轮就硬删除:如果没有 _gc_trash/ 副本或 .disabled 重命名,就是做错了。
- 把“旧”当作“死”:60 天未触碰的技能可能是季节性的(报税季、季度审查)。年龄是信号,不是判决——这正是需要人工确认的原因。
- 用截断方式清理记忆:合并两个矛盾的记忆文件需要阅读两者并保留更新的真相,而不是删除较长的那个。
- 触碰 ~/.claude(或项目的 .claude/)之外的任何内容:配置 GC 绝不游荡进源码树。
最佳实践
- 在大规模新增之后运行,而不只是按日历:安装 50 个技能的包时,正是与既有技能出现重叠的时候。
- 当两个技能重叠时,优先禁用触发描述较弱的那个——它很可能本来就没被触发过。
- 权限清理是每分钟投入价值最高的渠道:冗余的 allow 条目会让安全审查更难。
- 永久保留 gc_log.md。它很小,而“我什么时候禁用了那个钩子、为什么”出现的频率比想象中高。
相关技能
- skill-stocktake:审计技能质量;config-gc 审计技能是否存在。对 GC 后幸存的技能运行 stocktake。
- workspace-surface-audit:加法对应物,推荐安装什么。config-gc 是同一生命周期的减法一半。
- configure-ecc:用它安装技能后,运行 config-gc 来协调与既有配置的重叠。
- continuous-learning:产生该技能日后审计的记忆文件。
- security-review:与权限渠道搭配良好。