diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..ec8bb8b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,32 @@
+---
+name: Bug report
+about: Create a report
+title: ''
+labels: bug
+assignees: Knuspii
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. Linux Ubuntu]
+ - Terminal:
+
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..dc6cc20
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: enhancement
+assignees: Knuspii
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ec326ac..bdb4fe3 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,12 +1,13 @@
# 🚀 How Can you Contribute?
## 1. Adding New Programs/Caches
The most common way to contribute is by adding support for more applications.
+* Open the cc_caches.go file.
* Locate the getPrograms() function
* Add a new Program struct to the slice.
* **Important:** Ensure you provide paths for both Windows and Linux where possible.
* Use filepath.Join and environment variables (like localAppData) and maintain cross-platform compatibility.
-## 2. Improving the UI/TUI
+## 2. Improving the TUI
I aim for a "Crunchy" retro terminal feel. Improvements to the menu navigation, spinner, or banner are welcome, provided they don't add external dependencies.
## 3. Bug Reports & Feature Requests
@@ -22,7 +23,7 @@ If you find a bug or have an idea:
## 📥 Pull Request Process
* Fork the repository and create your branch from main.
* Test your changes! Run a dry-run (-d) to ensure paths are detected correctly.
-* Commit with descriptive messages (e.g., "Add Spotify cache support").
+* Commit with descriptive messages (e.g., "Add: Spotify cache support").
* Open a PR and describe what your changes do and why they are necessary.
## ⚖️ License
diff --git a/README.md b/README.md
index bb3860d..45275c3 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,16 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -24,17 +29,21 @@
### Supported Software List:
| Category | Software / Path |
| :--- | :--- |
-| **System** | System Logs, System Temp Folders, Thumbnail Cache, Shader Cache |
-| **Browsers** | Firefox, Google Chrome, Microsoft Edge, Brave, Opera |
-| **Development** | Visual Studio Code, Slack, JetBrains IDE, Go, Pip, NPM, Yarn, Cargo, NuGet, Gradle |
-| **Gaming** | Steam, Epic Games(Heroic) |
-| **Apps** | Discord, Spotify, Thunderbird, Telegram |
+| **System:** | System Logs, System Temp, Thumbnail, Shell History, Package Managers, Shader Cache |
+| **Browsers:** | Firefox, Google Chrome, Microsoft Edge, Brave, Opera |
+| **Development:** | Visual Studio Code, Slack, JetBrains IDE, Go, Pip, NPM, Yarn, Cargo, NuGet, Gradle |
+| **Gaming:** | Steam, Epic Games(Heroic) |
+| **Apps:** | Discord, Spotify, Thunderbird, Telegram |
**Flatpak is supported** \
-**Snap is not supported**
+**Snap is NOT supported**
+
+---
## 📥 How to Install & Download 
-Paste the corresponding command into your terminal and restart it afterwards. \
+**Download the latest binary and just run/open it: [[Download here]](https://github.com/knuspii/crunchycleaner/releases)** \
+Or paste the corresponding command into your terminal and restart it afterwards to fully install it. \
+\
For Linux (using sudo):
```
curl -L https://github.com/Knuspii/CrunchyCleaner/releases/latest/download/crunchycleaner -o cc && sudo install -m 755 cc /usr/local/bin/crunchycleaner && rm cc
@@ -47,10 +56,7 @@ For Go (using go install):
```
go install github.com/knuspii/crunchycleaner/v2@latest
```
-After that you can just type `crunchycleaner -v` into your terminal to verify it's installed. \
-
-Or download the binary and just run/open it:
-[[Download here]](https://github.com/knuspii/crunchycleaner/releases)
+After that you can just type `crunchycleaner -v` into your terminal to verify it's installed.
---
@@ -58,19 +64,16 @@ Or download the binary and just run/open it:
```
-a Automate cleaning (select all and start immediately)
-d Simulation mode without deleting files (for testing)
- -t Skip terminal resizing and environment initialization
-v Display version information
```
-If you only use a terminal, you might want to use the `-t` flag.
-
---
> [!WARNING]
> You use this tool at your own risk!
> [!NOTE]
-> AI was used for this project in some parts.
+> AI was used for this project in some parts. BUT THIS IS NOT VIBECODED!
## Q&A
**Q:** Will this break my system? \
@@ -84,8 +87,11 @@ If you only use a terminal, you might want to use the `-t` flag.
## Other Downloads
CrunchyCleaner is also available on SourceForge \
+\
[](https://sourceforge.net/projects/crunchycleaner/files/latest/download)
-[](https://sourceforge.net/projects/crunchycleaner/files/latest/download)
+[](https://sourceforge.net/projects/crunchycleaner/files/latest/download)
+
+---
### 🎖️ Featured on
[](https://github.com/rothgar/awesome-tuis)
diff --git a/assets/github.png b/assets/github.png
index e76c730..c4d25ac 100644
Binary files a/assets/github.png and b/assets/github.png differ
diff --git a/assets/github.xcf b/assets/github.xcf
index a4850da..5ce7590 100644
Binary files a/assets/github.xcf and b/assets/github.xcf differ
diff --git a/assets/logo.png b/assets/logo.png
index 701230b..bf84a84 100644
Binary files a/assets/logo.png and b/assets/logo.png differ
diff --git a/assets/logo.xcf b/assets/logo.xcf
index 8e04ebb..568b75e 100644
Binary files a/assets/logo.xcf and b/assets/logo.xcf differ
diff --git a/cc_caches.go b/cc_caches.go
new file mode 100644
index 0000000..dd2fbb1
--- /dev/null
+++ b/cc_caches.go
@@ -0,0 +1,224 @@
+// ##################################################################
+// CrunchyCleaner
+// Here are all paths to the specific caches.
+// ##################################################################
+
+package main
+
+import (
+ "os"
+ "path/filepath"
+ "runtime"
+)
+
+func getPrograms() []Program {
+ if runtime.GOOS == "windows" {
+ // Windows
+ home, _ := os.UserHomeDir()
+ appData := os.Getenv("APPDATA")
+ localAppData := os.Getenv("LOCALAPPDATA")
+ programFilesX86 := os.Getenv("ProgramFiles(x86)")
+ programFiles := os.Getenv("ProgramFiles")
+ programData := os.Getenv("ProgramData")
+ winDir := os.Getenv("WINDIR")
+ return []Program{
+ {"System Logs (Admin)", []string{
+ filepath.Join(winDir, "Panther"),
+ filepath.Join(winDir, "Logs"),
+ }, false},
+ {"Font Cache (Admin)", []string{filepath.Join(winDir, "ServiceProfiles/LocalService/AppData/Local/FontCache")}, false},
+ {"System Temp Folders (Admin)", []string{filepath.Join(winDir, "Temp")}, false},
+ {"Update Logs (Admin)", []string{filepath.Join(winDir, "SoftwareDistribution/Download")}, false},
+ {"User Temp Folder", []string{filepath.Join(localAppData, "Temp")}, false},
+ {"Thumbnail Cache", []string{filepath.Join(localAppData, "Microsoft/Windows/Explorer")}, false},
+ {"Powershell History", []string{filepath.Join(appData, "Microsoft/Windows/PowerShell/PSReadLine")}, false},
+ {"Package Manager Caches", []string{
+ filepath.Join(home, "scoop/apps/scoop/current/cache"),
+ filepath.Join(programData, "chocolatey/cache"),
+ }, false},
+ {"Firefox Cache", []string{
+ filepath.Join(localAppData, "Mozilla/Firefox/Profiles/*/cache2"),
+ filepath.Join(localAppData, "Mozilla/Firefox/Profiles/*/jumpListCache"),
+ filepath.Join(appData, "Mozilla/Firefox/Profiles/*/shader-cache"),
+ }, false},
+ {"Chrome Cache", []string{
+ filepath.Join(localAppData, "Google/Chrome/User Data/Default/Cache"),
+ filepath.Join(localAppData, "Google/Chrome/User Data/Default/Code Cache"),
+ filepath.Join(localAppData, "Google/Chrome/User Data/*/Cache"),
+ filepath.Join(localAppData, "Google/Chrome/User Data/Default/Media Cache"),
+ }, false},
+ {"Edge Cache", []string{
+ filepath.Join(localAppData, "Microsoft/Edge/User Data/Default/Cache"),
+ filepath.Join(localAppData, "Microsoft/Edge/User Data/*/Cache"),
+ filepath.Join(localAppData, "Microsoft/Edge/User Data/Default/Media Cache"),
+ }, false},
+ {"Brave Cache", []string{
+ filepath.Join(localAppData, "BraveSoftware/Brave-Browser/User Data/Default/Cache"),
+ filepath.Join(localAppData, "BraveSoftware/Brave-Browser/User Data/*/Cache"),
+ filepath.Join(localAppData, "BraveSoftware/Brave-Browser/User Data/Default/Media Cache"),
+ }, false},
+ {"Opera Cache", []string{
+ filepath.Join(localAppData, "Opera Software/Opera Stable/Cache"),
+ filepath.Join(localAppData, "Opera Software/Opera Stable/Code Cache"),
+ }, false},
+ {"Thunderbird Cache", []string{
+ filepath.Join(localAppData, "Thunderbird/Profiles/*/cache2"),
+ }, false},
+ {"Steam Cache", []string{
+ filepath.Join(programFilesX86, "Steam/appcache"),
+ filepath.Join(programFiles, "Steam/appcache"),
+ filepath.Join(localAppData, "Steam/htmlcache"),
+ }, false},
+ {"Epic Games Cache", []string{filepath.Join(localAppData, "EpicGamesLauncher/Saved/webcache")}, false},
+ {"Discord Cache", []string{
+ filepath.Join(appData, "discord/Cache"),
+ filepath.Join(appData, "discord/Code Cache"),
+ filepath.Join(appData, "discord/GPUCache"),
+ }, false},
+ {"Telegram Cache", []string{filepath.Join(appData, "Telegram Desktop/tdata/user_data/cache")}, false},
+ {"Spotify Cache", []string{filepath.Join(localAppData, "Spotify/Storage")}, false},
+ {"VS Code Cache", []string{
+ filepath.Join(appData, "Code/Cache"),
+ filepath.Join(appData, "Code/CachedData"),
+ filepath.Join(appData, "Code/CachedExtensionVSIXs"),
+ filepath.Join(appData, "Code/User/workspaceStorage"),
+ filepath.Join(appData, "Code/GPUCache"),
+ }, false},
+ {"JetBrains IDE Cache", []string{filepath.Join(localAppData, "JetBrains/*/system/caches")}, false},
+ {"Slack Cache", []string{
+ filepath.Join(appData, "Slack/Cache"),
+ filepath.Join(appData, "Slack/Code Cache"),
+ filepath.Join(appData, "Slack/GPUCache"),
+ }, false},
+ {"Shader Cache", []string{
+ filepath.Join(localAppData, "D3DSCache"),
+ filepath.Join(localAppData, "NVIDIA/GLCache"),
+ filepath.Join(localAppData, "NVIDIA/PerDriverVersion/DXCache"),
+ }, false},
+ {"Go Build Cache", []string{filepath.Join(localAppData, "go-build")}, false},
+ {"Pip Cache", []string{filepath.Join(localAppData, "pip/Cache")}, false},
+ {"NPM Cache", []string{filepath.Join(appData, "npm-cache/_cacache")}, false},
+ {"Yarn Cache", []string{
+ filepath.Join(localAppData, "Yarn/Cache"),
+ filepath.Join(appData, "Yarn/Cache"),
+ }, false},
+ {"Cargo Cache", []string{
+ filepath.Join(home, ".cargo/registry/cache"),
+ filepath.Join(home, ".cargo/git/db"),
+ }, false},
+ {"NuGet Cache", []string{filepath.Join(home, ".nuget/packages")}, false},
+ {"Gradle Cache", []string{filepath.Join(home, ".gradle/caches")}, false},
+ }
+ } else {
+ // Linux
+ home, _ := os.UserHomeDir()
+ cache := ".cache/"
+ flatpak := ".var/app/"
+ return []Program{
+ {"System Logs (Root)", []string{"/var/log/"}, false},
+ {"System Temp Folders (Root)", []string{"/tmp"}, false},
+ {"Package Manager Caches (Root)", []string{
+ "/var/cache/apt/archives/*.deb",
+ "/var/cache/pacman/pkg/*",
+ "/var/cache/dnf/*",
+ "/var/cache/snapd/*",
+ }, false},
+ {"Thumbnail Cache", []string{filepath.Join(home, cache, "thumbnails")}, false},
+ {"Shell History", []string{filepath.Join(home, "*_history")}, false},
+ {"Firefox Cache", []string{
+ filepath.Join(home, cache, "mozilla/firefox/*/cache2"),
+ filepath.Join(home, flatpak, "org.mozilla.firefox/cache/mozilla/firefox/*/cache2"),
+ }, false},
+ {"Chromium Cache", []string{
+ filepath.Join(home, cache, "chromium/*/Cache"),
+ filepath.Join(home, cache, "chromium/*/Code Cache"),
+ filepath.Join(home, flatpak, "com.google.Chrome/cache/chromium/*/Cache"),
+ filepath.Join(home, flatpak, "com.google.Chrome/cache/chromium/*/Code Cache"),
+ }, false},
+ {"Edge Cache", []string{
+ filepath.Join(home, cache, "microsoft-edge/*/Cache"),
+ filepath.Join(home, cache, "microsoft-edge/*/Code Cache"),
+ filepath.Join(home, flatpak, "com.microsoft.Edge/cache/microsoft-edge/*/Cache"),
+ filepath.Join(home, flatpak, "com.microsoft.Edge/cache/microsoft-edge/*/Code Cache"),
+ }, false},
+ {"Brave Cache", []string{
+ filepath.Join(home, cache, "BraveSoftware/Brave-Browser/*/Cache"),
+ filepath.Join(home, cache, "BraveSoftware/Brave-Browser/*/Code Cache"),
+ filepath.Join(home, flatpak, "com.brave.Browser/cache/Brave-Browser/*/Cache"),
+ filepath.Join(home, flatpak, "com.brave.Browser/cache/Brave-Browser/*/Code Cache"),
+ }, false},
+ {"Opera Cache", []string{
+ filepath.Join(home, cache, "opera/Cache"),
+ filepath.Join(home, ".config/opera/Cache"),
+ filepath.Join(home, flatpak, "com.opera.Opera/cache/opera/Cache"),
+ filepath.Join(home, flatpak, "com.opera.Opera/config/opera/Cache"),
+ }, false},
+ {"Thunderbird Cache", []string{
+ filepath.Join(home, cache, "thunderbird/*/cache2"),
+ filepath.Join(home, flatpak, "org.mozilla.Thunderbird/cache/mozilla/Thunderbird/*/cache2"),
+ }, false},
+ {"Steam Cache", []string{
+ filepath.Join(home, ".steam/steam/appcache"),
+ filepath.Join(home, ".local/share/Steam/appcache"),
+ filepath.Join(home, ".local/share/Steam/config/htmlcache"),
+ filepath.Join(home, flatpak, "com.valvesoftware.Steam/steam/steam/appcache"),
+ filepath.Join(home, flatpak, "com.valvesoftware.Steam/.local/share/Steam/appcache"),
+ filepath.Join(home, flatpak, "com.valvesoftware.Steam/.local/share/Steam/config/htmlcache"),
+ }, false},
+ {"Epic Games (Heroic/Lutris) Cache", []string{
+ filepath.Join(home, ".config/heroic/WebCache"),
+ filepath.Join(home, ".local/share/lutris/runtime"),
+ filepath.Join(home, flatpak, "com.heroicgameslauncher.hgl/config/heroic/WebCache"),
+ filepath.Join(home, flatpak, "com.heroicgameslauncher.hgl/.local/share/lutris/runtime"),
+ }, false},
+ {"Discord Cache", []string{
+ filepath.Join(home, ".config/discord/Cache"),
+ filepath.Join(home, ".config/discord/Code Cache"),
+ filepath.Join(home, ".config/discord/GPUCache"),
+ filepath.Join(home, flatpak, "com.discordapp.Discord/config/discord/Cache"),
+ filepath.Join(home, flatpak, "com.discordapp.Discord/config/discord/Code Cache"),
+ filepath.Join(home, flatpak, "com.discordapp.Discord/config/discord/GPUCache"),
+ }, false},
+ {"Telegram Cache", []string{filepath.Join(
+ home, ".local/share/TelegramDesktop/tdata/user_data/cache"),
+ filepath.Join(home, flatpak, "org.telegram.desktop/data/TelegramDesktop/tdata/user_data/cache"),
+ }, false},
+ {"Spotify Cache", []string{
+ filepath.Join(home, cache, "spotify"),
+ filepath.Join(home, flatpak, "com.spotify.Client/cache/spotify"),
+ }, false},
+ {"VS Code Cache", []string{
+ filepath.Join(home, ".config/Code/Cache"),
+ filepath.Join(home, ".config/Code/Code Cache"),
+ filepath.Join(home, ".config/Code/CachedData"),
+ filepath.Join(home, ".config/Code/GPUCache"),
+ filepath.Join(home, ".config/Code/User/workspaceStorage"),
+ filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/Cache"),
+ filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/Code Cache"),
+ filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/CachedData"),
+ filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/GPUCache"),
+ filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/User/workspaceStorage"),
+ }, false},
+ {"JetBrains IDE Cache", []string{filepath.Join(home, cache, "JetBrains/*/caches")}, false},
+ {"Slack Cache", []string{
+ filepath.Join(home, ".config/Slack/Cache"),
+ filepath.Join(home, ".config/Slack/Code Cache"),
+ filepath.Join(home, ".config/Slack/GPUCache"),
+ filepath.Join(home, flatpak, "com.slack.Slack/config/Slack/Cache"),
+ filepath.Join(home, flatpak, "com.slack.Slack/config/Slack/Code Cache"),
+ filepath.Join(home, flatpak, "com.slack.Slack/config/Slack/GPUCache"),
+ }, false},
+ {"Shader Cache", []string{
+ filepath.Join(home, cache, "mesa_shader_cache"),
+ filepath.Join(home, cache, "nvidia/GLCache"),
+ }, false},
+ {"Go Build Cache", []string{filepath.Join(home, cache, "go-build")}, false},
+ {"Pip Cache", []string{filepath.Join(home, cache, "pip")}, false},
+ {"NPM Cache", []string{filepath.Join(home, ".npm/_cacache")}, false},
+ {"Yarn Cache", []string{filepath.Join(home, cache, "yarn")}, false},
+ {"Cargo Cache", []string{filepath.Join(home, ".cargo/registry/cache")}, false},
+ {"NuGet Cache", []string{filepath.Join(home, ".nuget/packages")}, false},
+ {"Gradle Cache", []string{filepath.Join(home, ".gradle/caches")}, false},
+ }
+ }
+}
diff --git a/cc_funcs.go b/cc_funcs.go
new file mode 100644
index 0000000..eabd04a
--- /dev/null
+++ b/cc_funcs.go
@@ -0,0 +1,126 @@
+// ##################################################################
+// CrunchyCleaner
+// Here are all tech functions.
+// ##################################################################
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/shirou/gopsutil/v4/disk"
+)
+
+// getDiskMetrics uses gopsutil to fetch precise, platform-independent disk data.
+func getDiskMetrics() (freeGB float64, totalStr, freeStr string) {
+ const GB = 1024 * 1024 * 1024
+
+ // On Linux, "/" is the root. On Windows, gopsutil handles the mapping to the system drive.
+ usage, err := disk.Usage("/")
+ if err != nil {
+ // Fallback for specific Windows environments if "/" fails
+ usage, err = disk.Usage("C:")
+ if err != nil {
+ return 0, "N/A", "N/A"
+ }
+ }
+
+ freeGB = float64(usage.Free) / GB
+ totalStr = fmt.Sprintf("%.2f GB", float64(usage.Total)/GB)
+ freeStr = fmt.Sprintf("%.2f GB", freeGB)
+ return
+}
+
+// formatMB converts bytes to a string representing Megabytes
+func formatMB(bytes int64) string {
+ mb := float64(bytes) / 1024 / 1024
+ return fmt.Sprintf("%.2f MB", mb)
+}
+
+// getDirSize utilizes WalkDir (introduced in Go 1.16), which is significantly
+// faster than the older filepath.Walk because it avoids unnecessary Lstat calls.
+func getDirSize(path string) int64 {
+ var size int64
+ // Resolve glob patterns (e.g., paths containing '*')
+ matches, _ := filepath.Glob(expandHome(path))
+
+ for _, m := range matches {
+ // WalkDir is the high-performance standard for scanning directories
+ _ = filepath.WalkDir(m, func(_ string, d os.DirEntry, err error) error {
+ if err != nil {
+ // If a folder is restricted (e.g. System Cache), we just skip it
+ return nil
+ }
+ if !d.IsDir() {
+ info, err := d.Info()
+ if err == nil {
+ size += info.Size()
+ }
+ }
+ return nil
+ })
+ }
+ return size
+}
+
+// expandHome resolves the shorthand '~/ ' to the absolute user home directory
+func expandHome(path string) string {
+ if strings.HasPrefix(path, "~/") {
+ home, _ := os.UserHomeDir()
+ return filepath.Join(home, path[2:])
+ }
+ return path
+}
+
+// function to scan which programs actually exist on the disk
+func scanForExisting() []Program {
+ allPrograms := getPrograms()
+ existing := []Program{}
+ for _, p := range allPrograms {
+ found := false
+ var totalSize int64
+ for _, path := range p.Paths {
+ matches, _ := filepath.Glob(expandHome(path))
+ if len(matches) > 0 {
+ found = true
+ totalSize += getDirSize(path)
+ }
+ }
+ if found {
+ // We format the name here so it's ready for both UI and Logs
+ p.Name = fmt.Sprintf("%-30s %s(%s)%s", p.Name, YELLOW, formatMB(totalSize), RC)
+ existing = append(existing, p)
+ }
+ }
+ return existing
+}
+
+func deletePath(path string) {
+ info, err := os.Stat(path)
+ if err != nil {
+ return
+ }
+
+ if !info.IsDir() {
+ _ = os.Remove(path)
+ return
+ }
+
+ entries, err := os.ReadDir(path)
+ if err != nil {
+ fmt.Printf(CLEARLINE)
+ logWarn("Cannot read " + path + ": " + err.Error())
+ return
+ }
+
+ for _, e := range entries {
+ full := filepath.Join(path, e.Name())
+ if err := os.RemoveAll(full); err != nil {
+ fmt.Printf(CLEARLINE)
+ logWarn("Skipped " + e.Name() + ": " + err.Error())
+ }
+ }
+}
diff --git a/cc_main.go b/cc_main.go
index 9011b2b..45a2bde 100644
--- a/cc_main.go
+++ b/cc_main.go
@@ -24,7 +24,6 @@ import (
"flag"
"fmt"
"os"
- "os/exec"
"os/signal"
"os/user"
"path/filepath"
@@ -34,13 +33,12 @@ import (
"time"
"github.com/eiannone/keyboard"
- "github.com/shirou/gopsutil/v3/disk"
)
// Global constants for UI and Versioning
const (
- CC_VERSION = "2.6"
- COLS = 62
+ CC_VERSION = "2.7"
+ COLS = 60
LINES = 32
GOOS = runtime.GOOS
CLEARLINE = "\r\033[K"
@@ -52,10 +50,8 @@ const (
)
var (
- origCols, origLines int
// CLI Flags
Flagversion = flag.Bool("v", false, "Display version information")
- Flagnoinit = flag.Bool("t", false, "Skip terminal resizing and environment initialization")
Flagdryrun = flag.Bool("d", false, "Simulation mode without deleting files (for testing)")
Flagauto = flag.Bool("a", false, "Automate cleaning (select all and start immediately)")
)
@@ -69,48 +65,6 @@ type Program struct {
// ========================= HELPER FUNCTIONS =========================
-// initApp prepares the terminal environment (Title, Resize, User Info)
-func initApp() {
- fmt.Printf("Initializing CrunchyCleaner %s...\n", CC_VERSION)
-
- // Get current terminal size
- if GOOS == "windows" {
- cmd := exec.Command(
- "powershell", "-NoProfile", "-Command",
- "$s=$Host.UI.RawUI.WindowSize; Write-Output \"$($s.Width) $($s.Height)\"",
- )
-
- out, _ := cmd.Output()
- fmt.Sscanf(strings.TrimSpace(string(out)), "%d %d", &origCols, &origLines)
-
- } else {
- cmd := exec.Command("sh", "-c", "stty size < /dev/tty")
-
- out, _ := cmd.Output()
- fmt.Sscanf(strings.TrimSpace(string(out)), "%d %d", &origLines, &origCols)
- }
-
- // Clear screen
- if GOOS == "windows" {
- // Windows CMD requires an external call to 'cls'
- cmd := exec.Command("cls")
- cmd.Stdout = os.Stdout
- cmd.Run()
- } else {
- cmd := exec.Command("clear")
- cmd.Stdout = os.Stdout
- cmd.Run()
-
- }
- // Fallback use ANSI escape sequences
- fmt.Print(CLEARSCREEN)
-
- // Set Terminal Title via ANSI sequence
- fmt.Printf("\033]0;CrunchyCleaner %s\007", CC_VERSION)
- // Resize terminal
- terminalresize(COLS, LINES)
-}
-
// cc_exit provides a clean termination of the application
func cc_exit() {
// Close keyboard
@@ -119,11 +73,6 @@ func cc_exit() {
// Enable cursor
fmt.Print("\033[?25h")
- // Restore size
- if !*Flagnoinit && !*Flagauto {
- terminalresize(origCols, origLines)
- }
-
fmt.Printf("\nExiting CrunchyCleaner...\n")
os.Exit(0)
}
@@ -158,285 +107,6 @@ func spinner(text string, stop chan bool, ack chan bool) {
}
}
-func terminalresize(w int, h int) {
- // OS-specific Terminal Resizing
- if GOOS == "windows" {
- psCmd := fmt.Sprintf(
- `$w=(Get-Host).UI.RawUI;
- $newSize=New-Object System.Management.Automation.Host.Size(%d,%d);
- $newBuffer=New-Object System.Management.Automation.Host.Size(%d,999);
- $w.BufferSize=$newBuffer;
- $w.WindowSize=$newSize`,
- w, h, w,
- )
- exec.Command("powershell", "-NoProfile", "-Command", psCmd).Run()
- }
-
- // Generic ANSI resize fallback for modern terminals
- fmt.Printf("\033[8;%d;%dt", h, w)
-}
-
-// ========================= PROGRAMS =========================
-
-func getPrograms() []Program {
- if runtime.GOOS == "windows" {
- // Windows
- home, _ := os.UserHomeDir()
- appData := os.Getenv("APPDATA")
- localAppData := os.Getenv("LOCALAPPDATA")
- programFilesX86 := os.Getenv("ProgramFiles(x86)")
- programFiles := os.Getenv("ProgramFiles")
- winDir := os.Getenv("WINDIR")
- return []Program{
- {"System Logs (Admin)", []string{
- filepath.Join(winDir, "Panther"),
- filepath.Join(winDir, "Logs"),
- }, false},
- {"Font Cache (Admin)", []string{filepath.Join(winDir, "ServiceProfiles/LocalService/AppData/Local/FontCache")}, false},
- {"System Temp Folders (Admin)", []string{filepath.Join(winDir, "Temp")}, false},
- {"Update Logs (Admin)", []string{filepath.Join(winDir, "SoftwareDistribution/Download")}, false},
- {"User Temp Folder", []string{filepath.Join(localAppData, "Temp")}, false},
- {"Thumbnail Cache", []string{filepath.Join(localAppData, "Microsoft/Windows/Explorer")}, false},
- {"Firefox Cache", []string{
- filepath.Join(localAppData, "Mozilla/Firefox/Profiles/*/cache2"),
- filepath.Join(localAppData, "Mozilla/Firefox/Profiles/*/jumpListCache"),
- filepath.Join(appData, "Mozilla/Firefox/Profiles/*/shader-cache"),
- }, false},
- {"Chrome Cache", []string{
- filepath.Join(localAppData, "Google/Chrome/User Data/Default/Cache"),
- filepath.Join(localAppData, "Google/Chrome/User Data/Default/Code Cache"),
- filepath.Join(localAppData, "Google/Chrome/User Data/*/Cache"),
- filepath.Join(localAppData, "Google/Chrome/User Data/Default/Media Cache"),
- }, false},
- {"Edge Cache", []string{
- filepath.Join(localAppData, "Microsoft/Edge/User Data/Default/Cache"),
- filepath.Join(localAppData, "Microsoft/Edge/User Data/*/Cache"),
- filepath.Join(localAppData, "Microsoft/Edge/User Data/Default/Media Cache"),
- }, false},
- {"Brave Cache", []string{
- filepath.Join(localAppData, "BraveSoftware/Brave-Browser/User Data/Default/Cache"),
- filepath.Join(localAppData, "BraveSoftware/Brave-Browser/User Data/*/Cache"),
- filepath.Join(localAppData, "BraveSoftware/Brave-Browser/User Data/Default/Media Cache"),
- }, false},
- {"Opera Cache", []string{
- filepath.Join(localAppData, "Opera Software/Opera Stable/Cache"),
- filepath.Join(localAppData, "Opera Software/Opera Stable/Code Cache"),
- }, false},
- {"Thunderbird Cache", []string{
- filepath.Join(localAppData, "Thunderbird/Profiles/*/cache2"),
- }, false},
- {"Steam Cache", []string{
- filepath.Join(programFilesX86, "Steam/appcache"),
- filepath.Join(programFiles, "Steam/appcache"),
- filepath.Join(localAppData, "Steam/htmlcache"),
- }, false},
- {"Epic Games Cache", []string{filepath.Join(localAppData, "EpicGamesLauncher/Saved/webcache")}, false},
- {"Discord Cache", []string{
- filepath.Join(appData, "discord/Cache"),
- filepath.Join(appData, "discord/Code Cache"),
- filepath.Join(appData, "discord/GPUCache"),
- }, false},
- {"Telegram Cache", []string{filepath.Join(appData, "Telegram Desktop/tdata/user_data/cache")}, false},
- {"Spotify Cache", []string{filepath.Join(localAppData, "Spotify/Storage")}, false},
- {"VS Code Cache", []string{
- filepath.Join(appData, "Code/Cache"),
- filepath.Join(appData, "Code/CachedData"),
- filepath.Join(appData, "Code/CachedExtensionVSIXs"),
- filepath.Join(appData, "Code/User/workspaceStorage"),
- filepath.Join(appData, "Code/GPUCache"),
- }, false},
- {"JetBrains IDE Cache", []string{filepath.Join(localAppData, "JetBrains/*/system/caches")}, false},
- {"Slack Cache", []string{
- filepath.Join(appData, "Slack/Cache"),
- filepath.Join(appData, "Slack/Code Cache"),
- filepath.Join(appData, "Slack/GPUCache"),
- }, false},
- {"Shader Cache", []string{
- filepath.Join(localAppData, "D3DSCache"),
- filepath.Join(localAppData, "NVIDIA/GLCache"),
- }, false},
- {"Go Build Cache", []string{filepath.Join(localAppData, "go-build")}, false},
- {"Pip Cache", []string{filepath.Join(localAppData, "pip/Cache")}, false},
- {"NPM Cache", []string{filepath.Join(appData, "npm-cache/_cacache")}, false},
- {"Yarn Cache", []string{
- filepath.Join(localAppData, "Yarn/Cache"),
- filepath.Join(appData, "Yarn/Cache"),
- }, false},
- {"Cargo Cache", []string{
- filepath.Join(home, ".cargo/registry/cache"),
- filepath.Join(home, ".cargo/git/db"),
- }, false},
- {"NuGet Cache", []string{filepath.Join(home, ".nuget/packages")}, false},
- {"Gradle Cache", []string{filepath.Join(home, ".gradle/caches")}, false},
- }
- } else {
- // Linux
- home, _ := os.UserHomeDir()
- cache := ".cache/"
- flatpak := ".var/app/"
- return []Program{
- {"System Logs (Root)", []string{"/var/log/*.log"}, false},
- {"System Temp Folders (Root)", []string{"/tmp"}, false},
- {"Thumbnail Cache", []string{filepath.Join(home, cache, "thumbnails")}, false},
- {"Firefox Cache", []string{
- filepath.Join(home, cache, "mozilla/firefox/*/cache2"),
- filepath.Join(home, flatpak, "org.mozilla.firefox/cache/mozilla/firefox/*/cache2"),
- }, false},
- {"Chromium Cache", []string{
- filepath.Join(home, cache, "chromium/*/Cache"),
- filepath.Join(home, cache, "chromium/*/Code Cache"),
- filepath.Join(home, flatpak, "com.google.Chrome/cache/chromium/*/Cache"),
- filepath.Join(home, flatpak, "com.google.Chrome/cache/chromium/*/Code Cache"),
- }, false},
- {"Edge Cache", []string{
- filepath.Join(home, cache, "microsoft-edge/*/Cache"),
- filepath.Join(home, cache, "microsoft-edge/*/Code Cache"),
- filepath.Join(home, flatpak, "com.microsoft.Edge/cache/microsoft-edge/*/Cache"),
- filepath.Join(home, flatpak, "com.microsoft.Edge/cache/microsoft-edge/*/Code Cache"),
- }, false},
- {"Brave Cache", []string{
- filepath.Join(home, cache, "BraveSoftware/Brave-Browser/*/Cache"),
- filepath.Join(home, cache, "BraveSoftware/Brave-Browser/*/Code Cache"),
- filepath.Join(home, flatpak, "com.brave.Browser/cache/Brave-Browser/*/Cache"),
- filepath.Join(home, flatpak, "com.brave.Browser/cache/Brave-Browser/*/Code Cache"),
- }, false},
- {"Opera Cache", []string{
- filepath.Join(home, cache, "opera/Cache"),
- filepath.Join(home, ".config/opera/Cache"),
- filepath.Join(home, flatpak, "com.opera.Opera/cache/opera/Cache"),
- filepath.Join(home, flatpak, "com.opera.Opera/config/opera/Cache"),
- }, false},
- {"Thunderbird Cache", []string{
- filepath.Join(home, cache, "thunderbird/*/cache2"),
- filepath.Join(home, flatpak, "org.mozilla.Thunderbird/cache/mozilla/Thunderbird/*/cache2"),
- }, false},
- {"Steam Cache", []string{
- filepath.Join(home, ".steam/steam/appcache"),
- filepath.Join(home, ".local/share/Steam/appcache"),
- filepath.Join(home, ".local/share/Steam/config/htmlcache"),
- filepath.Join(home, flatpak, "com.valvesoftware.Steam/steam/steam/appcache"),
- filepath.Join(home, flatpak, "com.valvesoftware.Steam/.local/share/Steam/appcache"),
- filepath.Join(home, flatpak, "com.valvesoftware.Steam/.local/share/Steam/config/htmlcache"),
- }, false},
- {"Epic Games (Heroic/Lutris) Cache", []string{
- filepath.Join(home, ".config/heroic/WebCache"),
- filepath.Join(home, ".local/share/lutris/runtime"),
- filepath.Join(home, flatpak, "com.heroicgameslauncher.hgl/config/heroic/WebCache"),
- filepath.Join(home, flatpak, "com.heroicgameslauncher.hgl/.local/share/lutris/runtime"),
- }, false},
- {"Discord Cache", []string{
- filepath.Join(home, ".config/discord/Cache"),
- filepath.Join(home, ".config/discord/Code Cache"),
- filepath.Join(home, ".config/discord/GPUCache"),
- filepath.Join(home, flatpak, "com.discordapp.Discord/config/discord/Cache"),
- filepath.Join(home, flatpak, "com.discordapp.Discord/config/discord/Code Cache"),
- filepath.Join(home, flatpak, "com.discordapp.Discord/config/discord/GPUCache"),
- }, false},
- {"Telegram Cache", []string{filepath.Join(
- home, ".local/share/TelegramDesktop/tdata/user_data/cache"),
- filepath.Join(home, flatpak, "org.telegram.desktop/data/TelegramDesktop/tdata/user_data/cache"),
- }, false},
- {"Spotify Cache", []string{
- filepath.Join(home, cache, "spotify"),
- filepath.Join(home, flatpak, "com.spotify.Client/cache/spotify"),
- }, false},
- {"VS Code Cache", []string{
- filepath.Join(home, ".config/Code/Cache"),
- filepath.Join(home, ".config/Code/Code Cache"),
- filepath.Join(home, ".config/Code/CachedData"),
- filepath.Join(home, ".config/Code/GPUCache"),
- filepath.Join(home, ".config/Code/User/workspaceStorage"),
- filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/Cache"),
- filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/Code Cache"),
- filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/CachedData"),
- filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/GPUCache"),
- filepath.Join(home, flatpak, "com.visualstudio.code/config/Code/User/workspaceStorage"),
- }, false},
- {"JetBrains IDE Cache", []string{filepath.Join(home, cache, "JetBrains/*/caches")}, false},
- {"Slack Cache", []string{
- filepath.Join(home, ".config/Slack/Cache"),
- filepath.Join(home, ".config/Slack/Code Cache"),
- filepath.Join(home, ".config/Slack/GPUCache"),
- filepath.Join(home, flatpak, "com.slack.Slack/config/Slack/Cache"),
- filepath.Join(home, flatpak, "com.slack.Slack/config/Slack/Code Cache"),
- filepath.Join(home, flatpak, "com.slack.Slack/config/Slack/GPUCache"),
- }, false},
- {"Shader Cache", []string{
- filepath.Join(home, cache, "mesa_shader_cache"),
- filepath.Join(home, cache, "nvidia/GLCache"),
- }, false},
- {"Go Build Cache", []string{filepath.Join(home, cache, "go-build")}, false},
- {"Pip Cache", []string{filepath.Join(home, cache, "pip")}, false},
- {"NPM Cache", []string{filepath.Join(home, ".npm/_cacache")}, false},
- {"Yarn Cache", []string{filepath.Join(home, cache, "yarn")}, false},
- {"Cargo Cache", []string{filepath.Join(home, ".cargo/registry/cache")}, false},
- {"NuGet Cache", []string{filepath.Join(home, ".nuget/packages")}, false},
- {"Gradle Cache", []string{filepath.Join(home, ".gradle/caches")}, false},
- }
- }
-}
-
-// getDiskMetrics uses gopsutil to fetch precise, platform-independent disk data.
-func getDiskMetrics() (freeGB float64, totalStr, freeStr string) {
- const GB = 1024 * 1024 * 1024
-
- // On Linux, "/" is the root. On Windows, gopsutil handles the mapping to the system drive.
- usage, err := disk.Usage("/")
- if err != nil {
- // Fallback for specific Windows environments if "/" fails
- usage, err = disk.Usage("C:")
- if err != nil {
- return 0, "N/A", "N/A"
- }
- }
-
- freeGB = float64(usage.Free) / GB
- totalStr = fmt.Sprintf("%.2f GB", float64(usage.Total)/GB)
- freeStr = fmt.Sprintf("%.2f GB", freeGB)
- return
-}
-
-// formatMB converts bytes to a string representing Megabytes
-func formatMB(bytes int64) string {
- mb := float64(bytes) / 1024 / 1024
- return fmt.Sprintf("%.2f MB", mb)
-}
-
-// getDirSize utilizes WalkDir (introduced in Go 1.16), which is significantly
-// faster than the older filepath.Walk because it avoids unnecessary Lstat calls.
-func getDirSize(path string) int64 {
- var size int64
- // Resolve glob patterns (e.g., paths containing '*')
- matches, _ := filepath.Glob(expandHome(path))
-
- for _, m := range matches {
- // WalkDir is the high-performance standard for scanning directories
- _ = filepath.WalkDir(m, func(_ string, d os.DirEntry, err error) error {
- if err != nil {
- // If a folder is restricted (e.g. System Cache), we just skip it
- return nil
- }
- if !d.IsDir() {
- info, err := d.Info()
- if err == nil {
- size += info.Size()
- }
- }
- return nil
- })
- }
- return size
-}
-
-// expandHome resolves the shorthand '~/ ' to the absolute user home directory
-func expandHome(path string) string {
- if strings.HasPrefix(path, "~/") {
- home, _ := os.UserHomeDir()
- return filepath.Join(home, path[2:])
- }
- return path
-}
-
// ========================= MENU UI LOGIC =========================
func showBanner() {
@@ -466,8 +136,8 @@ func logWarn(msg string) { fmt.Printf("%s[!] %s%s\n", YELLOW, msg, RC) }
func renderMenu(existing []Program, idx int, fullRedraw bool) {
if fullRedraw {
showBanner()
- fmt.Printf("Use ↑/↓ or W/S to navigate | [ENTER] to select | [C] to clean\n")
- fmt.Printf("Folders found: [%d]\n", len(existing))
+ fmt.Printf("↑/↓ or W/S to navigate | [ENTER] to select | [C] to clean\n")
+ fmt.Printf("Folders found: %d\n", len(existing))
}
// Render each detected program entry
@@ -487,29 +157,6 @@ func renderMenu(existing []Program, idx int, fullRedraw bool) {
}
}
-// function to scan which programs actually exist on the disk
-func scanForExisting() []Program {
- allPrograms := getPrograms()
- existing := []Program{}
- for _, p := range allPrograms {
- found := false
- var totalSize int64
- for _, path := range p.Paths {
- matches, _ := filepath.Glob(expandHome(path))
- if len(matches) > 0 {
- found = true
- totalSize += getDirSize(path)
- }
- }
- if found {
- // We format the name here so it's ready for both UI and Logs
- p.Name = fmt.Sprintf("%-30s %s(%s)%s", p.Name, YELLOW, formatMB(totalSize), RC)
- existing = append(existing, p)
- }
- }
- return existing
-}
-
// handleMenu manages user input for navigation and selection
func handleMenu() {
// Initial scan of the filesystem to find existing directories
@@ -680,33 +327,6 @@ func runCleanup(programs []Program) {
cc_exit()
}
-func deletePath(path string) {
- info, err := os.Stat(path)
- if err != nil {
- return
- }
-
- if !info.IsDir() {
- _ = os.Remove(path)
- return
- }
-
- entries, err := os.ReadDir(path)
- if err != nil {
- fmt.Printf(CLEARLINE)
- logWarn("Cannot read " + path + ": " + err.Error())
- return
- }
-
- for _, e := range entries {
- full := filepath.Join(path, e.Name())
- if err := os.RemoveAll(full); err != nil {
- fmt.Printf(CLEARLINE)
- logWarn("Skipped " + e.Name() + ": " + err.Error())
- }
- }
-}
-
func main() {
flag.Parse()
@@ -723,10 +343,6 @@ func main() {
return
}
- if !*Flagnoinit && !*Flagauto {
- initApp()
- }
-
// AUTOMATION LOGIC
if *Flagauto {
showBanner()
diff --git a/go.mod b/go.mod
index 900cad5..4525eec 100644
--- a/go.mod
+++ b/go.mod
@@ -4,12 +4,13 @@ go 1.25.0
require (
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203
- github.com/shirou/gopsutil/v3 v3.24.5
+ github.com/shirou/gopsutil/v4 v4.26.6
)
require (
+ github.com/ebitengine/purego v0.10.1 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
- golang.org/x/sys v0.44.0 // indirect
+ golang.org/x/sys v0.46.0 // indirect
)
diff --git a/go.sum b/go.sum
index ac0d256..76c6a9e 100644
--- a/go.sum
+++ b/go.sum
@@ -1,16 +1,26 @@
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/ebitengine/purego v0.10.1 h1:dewVBCBT2GaMu1SrNTYxQhgQBethzfhiwvZiLGP/qyY=
+github.com/ebitengine/purego v0.10.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg=
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
-github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
-github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
+github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs=
+github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
-golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
+golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/scripts/lin_build.sh b/scripts/lin_build.sh
index 45dce49..d48a6a5 100644
--- a/scripts/lin_build.sh
+++ b/scripts/lin_build.sh
@@ -13,7 +13,7 @@ echo "Formatting Go source files..."
gofmt -s -w ../.
echo "Formatting done."
-mkdir ../bin/
+mkdir -p ../bin/
echo "Building CrunchyCleaner for Windows amd64..."
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o ../bin/crunchycleaner.exe ../.