mac_cleaner 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,540 @@
1
+ require 'fileutils'
2
+
3
+ module MacCleaner
4
+ class Cleaner
5
+ def initialize(dry_run: false, sudo: false, interactive: false, input: $stdin)
6
+ @dry_run = dry_run
7
+ @sudo = sudo
8
+ @interactive = interactive
9
+ @input = input
10
+ @total_size_cleaned = 0
11
+ end
12
+
13
+ def clean
14
+ sections = if @interactive
15
+ interactive_section_selection(CLEANUP_SECTIONS)
16
+ else
17
+ CLEANUP_SECTIONS
18
+ end
19
+
20
+ if sections.empty?
21
+ puts "\nNo sections selected. Exiting."
22
+ return
23
+ end
24
+
25
+ sections.each do |section|
26
+ if section[:sudo] && !@sudo
27
+ puts "\nSkipping '#{section[:name]}' (requires sudo)"
28
+ next
29
+ end
30
+ puts "\n#{section[:name]}"
31
+ section[:targets].each do |target|
32
+ clean_target(target, section[:sudo])
33
+ end
34
+ end
35
+ puts "\nCleanup complete. Total space freed: #{format_bytes(@total_size_cleaned)}"
36
+ end
37
+
38
+ private
39
+
40
+ def clean_target(target, sudo = false)
41
+ if target[:command]
42
+ puts " - #{target[:name]}"
43
+ system(target[:command]) unless @dry_run
44
+ return
45
+ end
46
+
47
+ paths = safe_glob(target[:path])
48
+ return if paths.empty?
49
+
50
+ deletion_candidates = []
51
+ total_size = 0
52
+
53
+ paths.each do |path|
54
+ next unless File.exist?(path)
55
+
56
+ size = get_size(path, sudo)
57
+ next if size.zero?
58
+
59
+ total_size += size
60
+ deletion_candidates << path
61
+ end
62
+
63
+ return if total_size.zero?
64
+
65
+ puts " - #{target[:name]}: #{format_bytes(total_size)}"
66
+ @total_size_cleaned += total_size
67
+
68
+ return if @dry_run
69
+
70
+ deletion_candidates.each do |path|
71
+ if sudo
72
+ system("sudo", "rm", "-rf", path)
73
+ else
74
+ FileUtils.rm_rf(path, verbose: false)
75
+ end
76
+ end
77
+ rescue MacCleaner::TooManyOpenFilesError
78
+ puts " - #{target[:name]}: skipped (too many files to scan)"
79
+ rescue Errno::EPERM, Errno::EACCES
80
+ # Skip paths we cannot access
81
+ end
82
+
83
+ def interactive_section_selection(sections)
84
+ puts "\nInteractive mode enabled. Review each section before cleaning."
85
+ sections.each_with_object([]) do |section, selected|
86
+ label = section[:sudo] ? "#{section[:name]} (requires sudo)" : section[:name]
87
+ puts "\n#{label}"
88
+ section[:targets].each do |target|
89
+ puts " - #{target[:name]}"
90
+ end
91
+ if confirm_selection?(section[:name])
92
+ selected << section
93
+ else
94
+ puts " Skipping '#{section[:name]}'"
95
+ end
96
+ end
97
+ end
98
+
99
+ def confirm_selection?(name)
100
+ print "Proceed with '#{name}'? [y/N]: "
101
+ $stdout.flush
102
+ response = @input.gets
103
+ return false unless response
104
+
105
+ case response.strip.downcase
106
+ when "y", "yes"
107
+ true
108
+ else
109
+ false
110
+ end
111
+ end
112
+
113
+ def get_size(path, sudo = false)
114
+ return 0 unless File.exist?(path)
115
+ return 0 unless File.readable?(path)
116
+ command = sudo ? "sudo du -sk \"#{path}\"" : "du -sk \"#{path}\""
117
+ begin
118
+ `#{command}`.split.first.to_i * 1024
119
+ rescue
120
+ 0
121
+ end
122
+ end
123
+
124
+ def format_bytes(bytes)
125
+ return "0B" if bytes.zero?
126
+ units = ["B", "KB", "MB", "GB", "TB"]
127
+ i = (Math.log(bytes) / Math.log(1024)).floor
128
+ "%.2f%s" % [bytes.to_f / 1024**i, units[i]]
129
+ end
130
+
131
+ def safe_glob(pattern)
132
+ expanded = File.expand_path(pattern)
133
+ return [] unless File.exist?(expanded) || wildcard_pattern?(expanded)
134
+
135
+ segments = expanded.split(File::SEPARATOR)
136
+
137
+ current_paths =
138
+ if expanded.start_with?(File::SEPARATOR)
139
+ segments.shift
140
+ [File::SEPARATOR]
141
+ else
142
+ [segments.shift || expanded]
143
+ end
144
+
145
+ segments.reject!(&:empty?)
146
+ return current_paths if segments.empty? && File.exist?(expanded)
147
+
148
+ segments.each do |segment|
149
+ current_paths = current_paths.each_with_object([]) do |base, acc|
150
+ next unless base
151
+ next unless File.exist?(base)
152
+
153
+ if wildcard_pattern?(segment)
154
+ next unless File.directory?(base)
155
+
156
+ begin
157
+ Dir.each_child(base) do |entry|
158
+ next if entry == "." || entry == ".."
159
+ next unless File.fnmatch?(segment, entry, GLOB_FLAGS)
160
+ acc << File.join(base, entry)
161
+ end
162
+ rescue Errno::EMFILE
163
+ raise MacCleaner::TooManyOpenFilesError
164
+ rescue Errno::ENOENT, Errno::EACCES, Errno::EPERM
165
+ next
166
+ end
167
+ else
168
+ candidate = File.join(base, segment)
169
+ acc << candidate if File.exist?(candidate)
170
+ end
171
+ end
172
+
173
+ return [] if current_paths.empty?
174
+ end
175
+
176
+ current_paths.map { |path| File.expand_path(path) }.uniq.sort
177
+ end
178
+
179
+ def wildcard_pattern?(segment)
180
+ segment.match?(WILDCARD_PATTERN)
181
+ end
182
+
183
+ GLOB_FLAGS = File::FNM_EXTGLOB | File::FNM_DOTMATCH
184
+ WILDCARD_PATTERN = /[*?\[\]{}]/.freeze
185
+ private_constant :GLOB_FLAGS, :WILDCARD_PATTERN
186
+
187
+ CLEANUP_SECTIONS = [
188
+ {
189
+ name: "Deep System Cleanup",
190
+ sudo: true,
191
+ targets: [
192
+ { name: "System library caches", path: "/Library/Caches/*" },
193
+ { name: "System library updates", path: "/Library/Updates/*" },
194
+ ]
195
+ },
196
+ {
197
+ name: "System Essentials",
198
+ targets: [
199
+ { name: "User app cache", path: "~/Library/Caches/*" },
200
+ { name: "User app logs", path: "~/Library/Logs/*" },
201
+ { name: "Trash", path: "~/.Trash/*" },
202
+ { name: "Crash reports", path: "~/Library/Application Support/CrashReporter/*" },
203
+ { name: "Diagnostic reports", path: "~/Library/DiagnosticReports/*" },
204
+ { name: "QuickLook thumbnails", path: "~/Library/Caches/com.apple.QuickLook.thumbnailcache" },
205
+ ]
206
+ },
207
+ {
208
+ name: "macOS System Caches",
209
+ targets: [
210
+ { name: "Saved application states", path: "~/Library/Saved Application State/*" },
211
+ { name: "Spotlight cache", path: "~/Library/Caches/com.apple.spotlight" },
212
+ { name: "Font registry cache", path: "~/Library/Caches/com.apple.FontRegistry" },
213
+ { name: "Font cache", path: "~/Library/Caches/com.apple.ATS" },
214
+ { name: "Photo analysis cache", path: "~/Library/Caches/com.apple.photoanalysisd" },
215
+ { name: "Apple ID cache", path: "~/Library/Caches/com.apple.akd" },
216
+ { name: "Safari webpage previews", path: "~/Library/Caches/com.apple.Safari/Webpage Previews/*" },
217
+ { name: "iCloud session cache", path: "~/Library/Application Support/CloudDocs/session/db/*" },
218
+ ]
219
+ },
220
+ {
221
+ name: "Developer Tools",
222
+ targets: [
223
+ { name: "npm cache directory", path: "~/.npm/_cacache/*" },
224
+ { name: "npm logs", path: "~/.npm/_logs/*" },
225
+ { name: "Yarn cache", path: "~/.yarn/cache/*" },
226
+ { name: "Bun cache", path: "~/.bun/install/cache/*" },
227
+ { name: "pip cache directory", path: "~/.cache/pip/*" },
228
+ { name: "pip cache (macOS)", path: "~/Library/Caches/pip/*" },
229
+ { name: "pyenv cache", path: "~/.pyenv/cache/*" },
230
+ { name: "Go build cache", path: "~/Library/Caches/go-build/*" },
231
+ { name: "Go module cache", path: "~/go/pkg/mod/cache/*" },
232
+ { name: "Rust cargo cache", path: "~/.cargo/registry/cache/*" },
233
+ { name: "Kubernetes cache", path: "~/.kube/cache/*" },
234
+ { name: "Container storage temp", path: "~/.local/share/containers/storage/tmp/*" },
235
+ { name: "AWS CLI cache", path: "~/.aws/cli/cache/*" },
236
+ { name: "Google Cloud logs", path: "~/.config/gcloud/logs/*" },
237
+ { name: "Azure CLI logs", path: "~/.azure/logs/*" },
238
+ { name: "Homebrew cache", path: "~/Library/Caches/Homebrew/*" },
239
+ { name: "Homebrew lock files (M series)", path: "/opt/homebrew/var/homebrew/locks/*" },
240
+ { name: "Homebrew lock files (Intel)", path: "/usr/local/var/homebrew/locks/*" },
241
+ { name: "Git config lock", path: "~/.gitconfig.lock" },
242
+ ]
243
+ },
244
+ {
245
+ name: "Tool Caches",
246
+ targets: [
247
+ { name: "npm cache", command: "npm cache clean --force" },
248
+ { name: "pip cache", command: "pip cache purge" },
249
+ { name: "Go cache", command: "go clean -modcache" },
250
+ { name: "Homebrew cleanup", command: "brew cleanup -s" },
251
+ ]
252
+ },
253
+ {
254
+ name: "Sandboxed App Caches",
255
+ targets: [
256
+ { name: "Wallpaper agent cache", path: "~/Library/Containers/com.apple.wallpaper.agent/Data/Library/Caches/*" },
257
+ { name: "Media analysis cache", path: "~/Library/Containers/com.apple.mediaanalysisd/Data/Library/Caches/*" },
258
+ { name: "App Store cache", path: "~/Library/Containers/com.apple.AppStore/Data/Library/Caches/*" },
259
+ { name: "Sandboxed app caches", path: "~/Library/Containers/*/Data/Library/Caches/*" },
260
+ ]
261
+ },
262
+ {
263
+ name: "Browser Cleanup",
264
+ targets: [
265
+ { name: "Safari cache", path: "~/Library/Caches/com.apple.Safari/*" },
266
+ { name: "Chrome cache", path: "~/Library/Caches/Google/Chrome/*" },
267
+ { name: "Chrome app cache", path: "~/Library/Application Support/Google/Chrome/*/Application Cache/*" },
268
+ { name: "Chrome GPU cache", path: "~/Library/Application Support/Google/Chrome/*/GPUCache/*" },
269
+ { name: "Chromium cache", path: "~/Library/Caches/Chromium/*" },
270
+ { name: "Edge cache", path: "~/Library/Caches/com.microsoft.edgemac/*" },
271
+ { name: "Arc cache", path: "~/Library/Caches/company.thebrowser.Browser/*" },
272
+ { name: "Brave cache", path: "~/Library/Caches/BraveSoftware/Brave-Browser/*" },
273
+ { name: "Firefox cache", path: "~/Library/Caches/Firefox/*" },
274
+ { name: "Opera cache", path: "~/Library/Caches/com.operasoftware.Opera/*" },
275
+ { name: "Vivaldi cache", path: "~/Library/Caches/com.vivaldi.Vivaldi/*" },
276
+ { name: "Firefox profile cache", path: "~/Library/Application Support/Firefox/Profiles/*/cache2/*" },
277
+ ]
278
+ },
279
+ {
280
+ name: "Cloud Storage Caches",
281
+ targets: [
282
+ { name: "Dropbox cache", path: "~/Library/Caches/com.dropbox.*" },
283
+ { name: "Dropbox cache", path: "~/Library/Caches/com.getdropbox.dropbox" },
284
+ { name: "Google Drive cache", path: "~/Library/Caches/com.google.GoogleDrive" },
285
+ { name: "Baidu Netdisk cache", path: "~/Library/Caches/com.baidu.netdisk" },
286
+ { name: "Alibaba Cloud cache", path: "~/Library/Caches/com.alibaba.teambitiondisk" },
287
+ { name: "Box cache", path: "~/Library/Caches/com.box.desktop" },
288
+ { name: "OneDrive cache", path: "~/Library/Caches/com.microsoft.OneDrive" },
289
+ ]
290
+ },
291
+ {
292
+ name: "Office Applications",
293
+ targets: [
294
+ { name: "Microsoft Word cache", path: "~/Library/Caches/com.microsoft.Word" },
295
+ { name: "Microsoft Excel cache", path: "~/Library/Caches/com.microsoft.Excel" },
296
+ { name: "Microsoft PowerPoint cache", path: "~/Library/Caches/com.microsoft.Powerpoint" },
297
+ { name: "Microsoft Outlook cache", path: "~/Library/Caches/com.microsoft.Outlook/*" },
298
+ { name: "Apple iWork cache", path: "~/Library/Caches/com.apple.iWork.*" },
299
+ { name: "WPS Office cache", path: "~/Library/Caches/com.kingsoft.wpsoffice.mac" },
300
+ { name: "Thunderbird cache", path: "~/Library/Caches/org.mozilla.thunderbird/*" },
301
+ { name: "Apple Mail cache", path: "~/Library/Caches/com.apple.mail/*" },
302
+ ]
303
+ },
304
+ {
305
+ name: "Extended Developer Caches",
306
+ targets: [
307
+ { name: "pnpm store cache", path: "~/.pnpm-store/*" },
308
+ { name: "pnpm global store", path: "~/.local/share/pnpm/store/*" },
309
+ { name: "TypeScript cache", path: "~/.cache/typescript/*" },
310
+ { name: "Electron cache", path: "~/.cache/electron/*" },
311
+ { name: "node-gyp cache", path: "~/.cache/node-gyp/*" },
312
+ { name: "node-gyp build cache", path: "~/.node-gyp/*" },
313
+ { name: "Turbo cache", path: "~/.turbo/*" },
314
+ { name: "Next.js cache", path: "~/.next/*" },
315
+ { name: "Vite cache", path: "~/.vite/*" },
316
+ { name: "Vite global cache", path: "~/.cache/vite/*" },
317
+ { name: "Webpack cache", path: "~/.cache/webpack/*" },
318
+ { name: "Parcel cache", path: "~/.parcel-cache/*" },
319
+ { name: "Android Studio cache", path: "~/Library/Caches/Google/AndroidStudio*/*" },
320
+ { name: "Unity cache", path: "~/Library/Caches/com.unity3d.*/*" },
321
+ { name: "JetBrains Toolbox cache", path: "~/Library/Caches/com.jetbrains.toolbox/*" },
322
+ { name: "Postman cache", path: "~/Library/Caches/com.postmanlabs.mac/*" },
323
+ { name: "Insomnia cache", path: "~/Library/Caches/com.konghq.insomnia/*" },
324
+ { name: "TablePlus cache", path: "~/Library/Caches/com.tinyapp.TablePlus/*" },
325
+ { name: "MongoDB Compass cache", path: "~/Library/Caches/com.mongodb.compass/*" },
326
+ { name: "Figma cache", path: "~/Library/Caches/com.figma.Desktop/*" },
327
+ { name: "GitHub Desktop cache", path: "~/Library/Caches/com.github.GitHubDesktop/*" },
328
+ { name: "VS Code cache", path: "~/Library/Caches/com.microsoft.VSCode/*" },
329
+ { name: "Sublime Text cache", path: "~/Library/Caches/com.sublimetext.*/*" },
330
+ { name: "Poetry cache", path: "~/.cache/poetry/*" },
331
+ { name: "uv cache", path: "~/.cache/uv/*" },
332
+ { name: "Ruff cache", path: "~/.cache/ruff/*" },
333
+ { name: "MyPy cache", path: "~/.cache/mypy/*" },
334
+ { name: "Pytest cache", path: "~/.pytest_cache/*" },
335
+ { name: "Jupyter runtime cache", path: "~/.jupyter/runtime/*" },
336
+ { name: "Hugging Face cache", path: "~/.cache/huggingface/*" },
337
+ { name: "PyTorch cache", path: "~/.cache/torch/*" },
338
+ { name: "TensorFlow cache", path: "~/.cache/tensorflow/*" },
339
+ { name: "Conda packages cache", path: "~/.conda/pkgs/*" },
340
+ { name: "Anaconda packages cache", path: "~/anaconda3/pkgs/*" },
341
+ { name: "Weights & Biases cache", path: "~/.cache/wandb/*" },
342
+ { name: "Cargo git cache", path: "~/.cargo/git/*" },
343
+ { name: "Rust documentation cache", path: "~/.rustup/toolchains/*/share/doc/*" },
344
+ { name: "Rust downloads cache", path: "~/.rustup/downloads/*" },
345
+ { name: "Gradle caches", path: "~/.gradle/caches/*" },
346
+ { name: "Maven repository cache", path: "~/.m2/repository/*" },
347
+ { name: "SBT cache", path: "~/.sbt/*" },
348
+ { name: "Docker BuildX cache", path: "~/.docker/buildx/cache/*" },
349
+ { name: "Terraform cache", path: "~/.cache/terraform/*" },
350
+ { name: "Paw API cache", path: "~/Library/Caches/com.getpaw.Paw/*" },
351
+ { name: "Charles Proxy cache", path: "~/Library/Caches/com.charlesproxy.charles/*" },
352
+ { name: "Proxyman cache", path: "~/Library/Caches/com.proxyman.NSProxy/*" },
353
+ { name: "Grafana cache", path: "~/.grafana/cache/*" },
354
+ { name: "Prometheus WAL cache", path: "~/.prometheus/data/wal/*" },
355
+ { name: "Jenkins workspace cache", path: "~/.jenkins/workspace/*/target/*" },
356
+ { name: "GitLab Runner cache", path: "~/.cache/gitlab-runner/*" },
357
+ { name: "GitHub Actions cache", path: "~/.github/cache/*" },
358
+ { name: "CircleCI cache", path: "~/.circleci/cache/*" },
359
+ { name: "Oh My Zsh cache", path: "~/.oh-my-zsh/cache/*" },
360
+ { name: "Fish shell backup", path: "~/.config/fish/fish_history.bak*" },
361
+ { name: "Bash history backup", path: "~/.bash_history.bak*" },
362
+ { name: "Zsh history backup", path: "~/.zsh_history.bak*" },
363
+ { name: "SonarQube cache", path: "~/.sonar/*" },
364
+ { name: "ESLint cache", path: "~/.cache/eslint/*" },
365
+ { name: "Prettier cache", path: "~/.cache/prettier/*" },
366
+ { name: "CocoaPods cache", path: "~/Library/Caches/CocoaPods/*" },
367
+ { name: "Ruby Bundler cache", path: "~/.bundle/cache/*" },
368
+ { name: "PHP Composer cache", path: "~/.composer/cache/*" },
369
+ { name: "NuGet packages cache", path: "~/.nuget/packages/*" },
370
+ { name: "Ivy cache", path: "~/.ivy2/cache/*" },
371
+ { name: "Dart Pub cache", path: "~/.pub-cache/*" },
372
+ { name: "curl cache", path: "~/.cache/curl/*" },
373
+ { name: "wget cache", path: "~/.cache/wget/*" },
374
+ { name: "curl cache (macOS)", path: "~/Library/Caches/curl/*" },
375
+ { name: "wget cache (macOS)", path: "~/Library/Caches/wget/*" },
376
+ { name: "pre-commit cache", path: "~/.cache/pre-commit/*" },
377
+ { name: "Git config backup", path: "~/.gitconfig.bak*" },
378
+ { name: "Flutter cache", path: "~/.cache/flutter/*" },
379
+ { name: "Gradle daemon logs", path: "~/.gradle/daemon/*" },
380
+ { name: "Android build cache", path: "~/.android/build-cache/*" },
381
+ { name: "Android SDK cache", path: "~/.android/cache/*" },
382
+ { name: "iOS device cache", path: "~/Library/Developer/Xcode/iOS DeviceSupport/*/Symbols/System/Library/Caches/*" },
383
+ { name: "Xcode Interface Builder cache", path: "~/Library/Developer/Xcode/UserData/IB Support/*" },
384
+ { name: "Swift package manager cache", path: "~/.cache/swift-package-manager/*" },
385
+ { name: "Bazel cache", path: "~/.cache/bazel/*" },
386
+ { name: "Zig cache", path: "~/.cache/zig/*" },
387
+ { name: "Deno cache", path: "~/Library/Caches/deno/*" },
388
+ { name: "Sequel Ace cache", path: "~/Library/Caches/com.sequel-ace.sequel-ace/*" },
389
+ { name: "Sequel Pro cache", path: "~/Library/Caches/com.eggerapps.Sequel-Pro/*" },
390
+ { name: "Redis Desktop Manager cache", path: "~/Library/Caches/redis-desktop-manager/*" },
391
+ { name: "Navicat cache", path: "~/Library/Caches/com.navicat.*" },
392
+ { name: "DBeaver cache", path: "~/Library/Caches/com.dbeaver.*" },
393
+ { name: "Redis Insight cache", path: "~/Library/Caches/com.redis.RedisInsight" },
394
+ { name: "Sentry crash reports", path: "~/Library/Caches/SentryCrash/*" },
395
+ { name: "KSCrash reports", path: "~/Library/Caches/KSCrash/*" },
396
+ { name: "Crashlytics data", path: "~/Library/Caches/com.crashlytics.data/*" },
397
+ ]
398
+ },
399
+ {
400
+ name: "Applications",
401
+ targets: [
402
+ { name: "Xcode derived data", path: "~/Library/Developer/Xcode/DerivedData/*" },
403
+ { name: "Simulator cache", path: "~/Library/Developer/CoreSimulator/Caches/*" },
404
+ { name: "Simulator temp files", path: "~/Library/Developer/CoreSimulator/Devices/*/data/tmp/*" },
405
+ { name: "Xcode cache", path: "~/Library/Caches/com.apple.dt.Xcode/*" },
406
+ { name: "iOS device logs", path: "~/Library/Developer/Xcode/iOS Device Logs/*" },
407
+ { name: "watchOS device logs", path: "~/Library/Developer/Xcode/watchOS Device Logs/*" },
408
+ { name: "Xcode build products", path: "~/Library/Developer/Xcode/Products/*" },
409
+ { name: "VS Code logs", path: "~/Library/Application Support/Code/logs/*" },
410
+ { name: "VS Code cache", path: "~/Library/Application Support/Code/Cache/*" },
411
+ { name: "VS Code extension cache", path: "~/Library/Application Support/Code/CachedExtensions/*" },
412
+ { name: "VS Code data cache", path: "~/Library/Application Support/Code/CachedData/*" },
413
+ { name: "IntelliJ IDEA logs", path: "~/Library/Logs/IntelliJIdea*/*" },
414
+ { name: "PhpStorm logs", path: "~/Library/Logs/PhpStorm*/*" },
415
+ { name: "PyCharm logs", path: "~/Library/Logs/PyCharm*/*" },
416
+ { name: "WebStorm logs", path: "~/Library/Logs/WebStorm*/*" },
417
+ { name: "GoLand logs", path: "~/Library/Logs/GoLand*/*" },
418
+ { name: "CLion logs", path: "~/Library/Logs/CLion*/*" },
419
+ { name: "DataGrip logs", path: "~/Library/Logs/DataGrip*/*" },
420
+ { name: "JetBrains cache", path: "~/Library/Caches/JetBrains/*" },
421
+ { name: "Discord cache", path: "~/Library/Application Support/discord/Cache/*" },
422
+ { name: "Slack cache", path: "~/Library/Application Support/Slack/Cache/*" },
423
+ { name: "Zoom cache", path: "~/Library/Caches/us.zoom.xos/*" },
424
+ { name: "WeChat cache", path: "~/Library/Caches/com.tencent.xinWeChat/*" },
425
+ { name: "Telegram cache", path: "~/Library/Caches/ru.keepcoder.Telegram/*" },
426
+ { name: "ChatGPT cache", path: "~/Library/Caches/com.openai.chat/*" },
427
+ { name: "Claude desktop cache", path: "~/Library/Caches/com.anthropic.claudefordesktop/*" },
428
+ { name: "Claude logs", path: "~/Library/Logs/Claude/*" },
429
+ { name: "Microsoft Teams cache", path: "~/Library/Caches/com.microsoft.teams2/*" },
430
+ { name: "WhatsApp cache", path: "~/Library/Caches/net.whatsapp.WhatsApp/*" },
431
+ { name: "Skype cache", path: "~/Library/Caches/com.skype.skype/*" },
432
+ { name: "DingTalk (iDingTalk) cache", path: "~/Library/Caches/dd.work.exclusive4aliding/*" },
433
+ { name: "AliLang security component", path: "~/Library/Caches/com.alibaba.AliLang.osx/*" },
434
+ { name: "DingTalk logs", path: "~/Library/Application Support/iDingTalk/log/*" },
435
+ { name: "DingTalk holmes logs", path: "~/Library/Application Support/iDingTalk/holmeslogs/*" },
436
+ { name: "Tencent Meeting cache", path: "~/Library/Caches/com.tencent.meeting/*" },
437
+ { name: "WeCom cache", path: "~/Library/Caches/com.tencent.WeWorkMac/*" },
438
+ { name: "Feishu cache", path: "~/Library/Caches/com.feishu.*/*" },
439
+ { name: "Sketch cache", path: "~/Library/Caches/com.bohemiancoding.sketch3/*" },
440
+ { name: "Sketch app cache", path: "~/Library/Application Support/com.bohemiancoding.sketch3/cache/*" },
441
+ { name: "ScreenFlow cache", path: "~/Library/Caches/net.telestream.screenflow10/*" },
442
+ { name: "Adobe cache", path: "~/Library/Caches/Adobe/*" },
443
+ { name: "Adobe app caches", path: "~/Library/Caches/com.adobe.*/*" },
444
+ { name: "Adobe media cache", path: "~/Library/Application Support/Adobe/Common/Media Cache Files/*" },
445
+ { name: "Adobe peak files", path: "~/Library/Application Support/Adobe/Common/Peak Files/*" },
446
+ { name: "Final Cut Pro cache", path: "~/Library/Caches/com.apple.FinalCut/*" },
447
+ { name: "Final Cut render cache", path: "~/Library/Application Support/Final Cut Pro/*/Render Files/*" },
448
+ { name: "Motion render cache", path: "~/Library/Application Support/Motion/*/Render Files/*" },
449
+ { name: "DaVinci Resolve cache", path: "~/Library/Caches/com.blackmagic-design.DaVinciResolve/*" },
450
+ { name: "Premiere Pro cache", path: "~/Library/Caches/com.adobe.PremierePro.*/*" },
451
+ { name: "Blender cache", path: "~/Library/Caches/org.blenderfoundation.blender/*" },
452
+ { name: "Cinema 4D cache", path: "~/Library/Caches/com.maxon.cinema4d/*" },
453
+ { name: "Autodesk cache", path: "~/Library/Caches/com.autodesk.*/*" },
454
+ { name: "SketchUp cache", path: "~/Library/Caches/com.sketchup.*/*" },
455
+ { name: "Raycast cache", path: "~/Library/Caches/com.raycast.macos/*" },
456
+ { name: "MiaoYan cache", path: "~/Library/Caches/com.tw93.MiaoYan/*" },
457
+ { name: "Filo cache", path: "~/Library/Caches/com.filo.client/*" },
458
+ { name: "Flomo cache", path: "~/Library/Caches/com.flomoapp.mac/*" },
459
+ { name: "Spotify cache", path: "~/Library/Caches/com.spotify.client/*" },
460
+ { name: "Apple Music cache", path: "~/Library/Caches/com.apple.Music" },
461
+ { name: "Apple Podcasts cache", path: "~/Library/Caches/com.apple.podcasts" },
462
+ { name: "Apple TV cache", path: "~/Library/Caches/com.apple.TV/*" },
463
+ { name: "Plex cache", path: "~/Library/Caches/tv.plex.player.desktop" },
464
+ { name: "NetEase Music cache", path: "~/Library/Caches/com.netease.163music" },
465
+ { name: "QQ Music cache", path: "~/Library/Caches/com.tencent.QQMusic/*" },
466
+ { name: "Kugou Music cache", path: "~/Library/Caches/com.kugou.mac/*" },
467
+ { name: "Kuwo Music cache", path: "~/Library/Caches/com.kuwo.mac/*" },
468
+ { name: "IINA cache", path: "~/Library/Caches/com.colliderli.iina" },
469
+ { name: "VLC cache", path: "~/Library/Caches/org.videolan.vlc" },
470
+ { name: "MPV cache", path: "~/Library/Caches/io.mpv" },
471
+ { name: "iQIYI cache", path: "~/Library/Caches/com.iqiyi.player" },
472
+ { name: "Tencent Video cache", path: "~/Library/Caches/com.tencent.tenvideo" },
473
+ { name: "Bilibili cache", path: "~/Library/Caches/tv.danmaku.bili/*" },
474
+ { name: "Douyu cache", path: "~/Library/Caches/com.douyu.*/*" },
475
+ { name: "Huya cache", path: "~/Library/Caches/com.huya.*/*" },
476
+ { name: "Aria2 cache", path: "~/Library/Caches/net.xmac.aria2gui" },
477
+ { name: "Transmission cache", path: "~/Library/Caches/org.m0k.transmission" },
478
+ { name: "qBittorrent cache", path: "~/Library/Caches/com.qbittorrent.qBittorrent" },
479
+ { name: "Downie cache", path: "~/Library/Caches/com.downie.Downie-*" },
480
+ { name: "Folx cache", path: "~/Library/Caches/com.folx.*/*" },
481
+ { name: "Pacifist cache", path: "~/Library/Caches/com.charlessoft.pacifist/*" },
482
+ { name: "Steam cache", path: "~/Library/Caches/com.valvesoftware.steam/*" },
483
+ { name: "Steam app cache", path: "~/Library/Application Support/Steam/appcache/*" },
484
+ { name: "Steam web cache", path: "~/Library/Application Support/Steam/htmlcache/*" },
485
+ { name: "Epic Games cache", path: "~/Library/Caches/com.epicgames.EpicGamesLauncher/*" },
486
+ { name: "Battle.net cache", path: "~/Library/Caches/com.blizzard.Battle.net/*" },
487
+ { name: "Battle.net app cache", path: "~/Library/Application Support/Battle.net/Cache/*" },
488
+ { name: "EA Origin cache", path: "~/Library/Caches/com.ea.*/*" },
489
+ { name: "GOG Galaxy cache", path: "~/Library/Caches/com.gog.galaxy/*" },
490
+ { name: "Riot Games cache", path: "~/Library/Caches/com.riotgames.*/*" },
491
+ { name: "Youdao Dictionary cache", path: "~/Library/Caches/com.youdao.YoudaoDict" },
492
+ { name: "Eudict cache", path: "~/Library/Caches/com.eudic.*" },
493
+ { name: "Bob Translation cache", path: "~/Library/Caches/com.bob-build.Bob" },
494
+ { name: "CleanShot cache", path: "~/Library/Caches/com.cleanshot.*" },
495
+ { name: "Camo cache", path: "~/Library/Caches/com.reincubate.camo" },
496
+ { name: "Xnip cache", path: "~/Library/Caches/com.xnipapp.xnip" },
497
+ { name: "Spark cache", path: "~/Library/Caches/com.readdle.smartemail-Mac" },
498
+ { name: "Airmail cache", path: "~/Library/Caches/com.airmail.*" },
499
+ { name: "Todoist cache", path: "~/Library/Caches/com.todoist.mac.Todoist" },
500
+ { name: "Any.do cache", path: "~/Library/Caches/com.any.do.*" },
501
+ { name: "Zsh completion cache", path: "~/.zcompdump*" },
502
+ { name: "less history", path: "~/.lesshst" },
503
+ { name: "Vim temporary files", path: "~/.viminfo.tmp" },
504
+ { name: "wget HSTS cache", path: "~/.wget-hsts" },
505
+ { name: "Input Source Pro cache", path: "~/Library/Caches/com.runjuu.Input-Source-Pro/*" },
506
+ { name: "WakaTime cache", path: "~/Library/Caches/macos-wakatime.WakaTime/*" },
507
+ { name: "Notion cache", path: "~/Library/Caches/notion.id/*" },
508
+ { name: "Obsidian cache", path: "~/Library/Caches/md.obsidian/*" },
509
+ { name: "Logseq cache", path: "~/Library/Caches/com.logseq.*/*" },
510
+ { name: "Bear cache", path: "~/Library/Caches/com.bear-writer.*/*" },
511
+ { name: "Evernote cache", path: "~/Library/Caches/com.evernote.*/*" },
512
+ { name: "Yinxiang Note cache", path: "~/Library/Caches/com.yinxiang.*/*" },
513
+ { name: "Alfred cache", path: "~/Library/Caches/com.runningwithcrayons.Alfred/*" },
514
+ { name: "The Unarchiver cache", path: "~/Library/Caches/cx.c3.theunarchiver/*" },
515
+ { name: "TeamViewer cache", path: "~/Library/Caches/com.teamviewer.*/*" },
516
+ { name: "AnyDesk cache", path: "~/Library/Caches/com.anydesk.*/*" },
517
+ { name: "ToDesk cache", path: "~/Library/Caches/com.todesk.*/*" },
518
+ { name: "Sunlogin cache", path: "~/Library/Caches/com.sunlogin.*/*" },
519
+ ]
520
+ },
521
+ {
522
+ name: "Virtualization Tools",
523
+ targets: [
524
+ { name: "VMware Fusion cache", path: "~/Library/Caches/com.vmware.fusion" },
525
+ { name: "Parallels cache", path: "~/Library/Caches/com.parallels.*" },
526
+ { name: "VirtualBox cache", path: "~/VirtualBox VMs/.cache" },
527
+ { name: "Vagrant temporary files", path: "~/.vagrant.d/tmp/*" },
528
+ ]
529
+ },
530
+ {
531
+ name: "Application Support Logs",
532
+ targets: [
533
+ { name: "App logs", path: "~/Library/Application Support/*/log/*" },
534
+ { name: "App logs", path: "~/Library/Application Support/*/logs/*" },
535
+ { name: "Activity logs", path: "~/Library/Application Support/*/activitylog/*" },
536
+ ]
537
+ }
538
+ ]
539
+ end
540
+ end
@@ -0,0 +1,27 @@
1
+ require 'thor'
2
+
3
+ module MacCleaner
4
+ class CLI < Thor
5
+ class_option :dry_run, type: :boolean, aliases: "-n", desc: "Perform a dry run without deleting files"
6
+ class_option :sudo, type: :boolean, desc: "Run with sudo for system-level cleanup"
7
+ class_option :interactive, type: :boolean, aliases: "-i", desc: "Interactively choose sections to clean before executing"
8
+
9
+ desc "clean", "Clean up your Mac"
10
+ def clean
11
+ require_relative 'cleaner'
12
+ cleaner = MacCleaner::Cleaner.new(
13
+ dry_run: options[:dry_run],
14
+ sudo: options[:sudo],
15
+ interactive: options[:interactive]
16
+ )
17
+ cleaner.clean
18
+ end
19
+
20
+ desc "analyze [PATH]", "Analyze disk space"
21
+ def analyze(path = "~")
22
+ require_relative 'analyzer'
23
+ analyzer = MacCleaner::Analyzer.new(path: path)
24
+ analyzer.analyze
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module MacCleaner
2
+ VERSION = "1.2.0"
3
+ end
@@ -0,0 +1,8 @@
1
+ require_relative "mac_cleaner/version"
2
+ require_relative "mac_cleaner/cli"
3
+
4
+ module MacCleaner
5
+ class Error < StandardError; end
6
+ class TooManyOpenFilesError < Error; end
7
+ # Your code goes here...
8
+ end