brain-cleaner 1.2.2 β 1.2.4
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.
- package/README.es.md +5 -0
- package/README.md +49 -71
- package/app.py +59 -7
- package/assets/icon.ico +0 -0
- package/bin/brain-cleaner.js +39 -40
- package/bin/postinstall.js +69 -0
- package/console/brain_cleaner_cli.py +13 -3
- package/package.json +5 -3
- package/scanner.py +21 -2
package/README.es.md
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
# Brain Cleaner
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
**Language / Idioma:**
|
|
4
6
|
πͺπΈ EspaΓ±ol | [π¬π§ English](https://github.com/konstantinWDK/brain-cleaner/blob/main/README.md)
|
|
5
7
|
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
---
|
|
6
11
|
[](https://www.python.org/downloads/)
|
|
7
12
|
[](https://opensource.org/licenses/MIT)
|
|
8
13
|
[](https://www.apple.com/macos/)
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Brain Cleaner
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
[](https://www.npmjs.com/package/brain-cleaner)
|
|
4
6
|
[](https://opensource.org/licenses/MIT)
|
|
5
7
|
[](https://www.python.org/downloads/)
|
|
@@ -7,103 +9,79 @@
|
|
|
7
9
|
**Language / Idioma:**
|
|
8
10
|
π¬π§ English | [πͺπΈ EspaΓ±ol](https://github.com/konstantinWDK/brain-cleaner/blob/main/README.es.md)
|
|
9
11
|
|
|
12
|
+

|
|
13
|
+
|
|
10
14
|
---
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
- **AI Residue Cleaner** β Finds cache, logs, and config files from Gemini, Claude, Cursor, Windsurf, and more.
|
|
14
|
-
- **NPM Optimization** β Detects and safely deletes heavy `node_modules` folders from your development projects.
|
|
15
|
-
- **Interactive UI** β High-performance terminal interface with granular selection and safety checks.
|
|
16
|
+
## Why Brain Cleaner?
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
While distributed via NPM for ease of use, Brain Cleaner is a hybrid tool. The Node.js wrapper automatically:
|
|
19
|
-
1. Detects your local Python environment.
|
|
20
|
-
2. **Auto-installs missing dependencies** (`blessed`, `Pillow`) on first run.
|
|
21
|
-
3. Seamlessly executes the core Python cleaning engine.
|
|
18
|
+
In the modern development era, our systems are constantly cluttered with "digital noise." Every interaction with AI assistants (Gemini, Claude, Cursor), every NPM project, and every Python experiment leaves behind a trail of logs, cache, and heavy virtual environments that quickly eat up hundreds of gigabytes of disk space.
|
|
22
19
|
|
|
23
|
-
|
|
24
|
-
Install directly from NPM to get the latest stable version:
|
|
20
|
+
**Brain Cleaner was built to solve this.** It provides a professional, high-performance interface to reclaim your disk space by targeting precisely those development residues that standard cleaners miss.
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
npm install -g brain-cleaner
|
|
28
|
-
```
|
|
22
|
+
---
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
If you prefer to install via Python/Pip directly from the repository:
|
|
24
|
+
## π Key Features
|
|
32
25
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
- **AI Residue Cleaner** β Deep scan for cache, logs, and configs from Gemini, Claude, Cursor, Windsurf, Trae, and more.
|
|
27
|
+
- **NPM Optimization** β Instantly find and safely delete heavy `node_modules` folders from forgotten projects.
|
|
28
|
+
- **Python Env Manager** β Detect obsolete virtual environments (`venv`, `.venv`) that haven't been touched in over 90 days.
|
|
29
|
+
- **Hybrid Power** β The ease of an NPM global install with the high-performance scanning engine of Python.
|
|
30
|
+
- **Interactive UI** β Choice between a sleek Desktop GUI or a professional Command Line Interface (CLI).
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
- **Python 3.9+** is required.
|
|
39
|
-
- **Node.js 14+** (if installing via NPM).
|
|
32
|
+
---
|
|
40
33
|
|
|
41
|
-
##
|
|
34
|
+
## βοΈ Installation
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
cd brain-cleaner
|
|
45
|
-
```
|
|
46
|
-
3. **Install the package**:
|
|
47
|
-
```bash
|
|
48
|
-
pip install .
|
|
49
|
-
```
|
|
36
|
+
Install globally via NPM to start cleaning immediately:
|
|
50
37
|
|
|
51
|
-
### Installation (NPM Mode)
|
|
52
|
-
If you are coming from the Node.js ecosystem, you can install it globally via NPM:
|
|
53
38
|
```bash
|
|
54
39
|
npm install -g brain-cleaner
|
|
55
40
|
```
|
|
56
|
-
*Note: Requires Python 3.9+ installed on your system.*
|
|
57
|
-
|
|
58
|
-
## Installation
|
|
59
|
-
|
|
60
|
-
To install this console interface as a global command:
|
|
61
41
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
pip install .
|
|
66
|
-
```
|
|
42
|
+
### Requirements
|
|
43
|
+
- **Python 3.9+** (Required for the scanning engine).
|
|
44
|
+
- **Node.js 14+**.
|
|
67
45
|
|
|
68
|
-
|
|
46
|
+
---
|
|
69
47
|
|
|
70
|
-
|
|
48
|
+
## π How to Use
|
|
71
49
|
|
|
50
|
+
### 1. Launch the Interface
|
|
51
|
+
Simply run the command from any terminal:
|
|
72
52
|
```bash
|
|
73
53
|
brain-cleaner
|
|
74
54
|
```
|
|
75
|
-
*Tip: Use the CLI for fast, keyboard-driven system cleaning.*
|
|
76
55
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
> ```bash
|
|
80
|
-
> brew install python@3.11 python-tk@3.11
|
|
81
|
-
> python3.11 -m venv .venv
|
|
82
|
-
> source .venv/bin/activate
|
|
83
|
-
> pip install .
|
|
84
|
-
> ```
|
|
56
|
+
### 2. Choose Your Scope
|
|
57
|
+
Select between scanning your **Home** directory, the **Full System**, or a **Custom Folder** in the sidebar.
|
|
85
58
|
|
|
86
|
-
|
|
59
|
+
### 3. Select Mode & Scan
|
|
60
|
+
- **AI Tools**: For logs and cache from AI assistants.
|
|
61
|
+
- **NPM Modules**: For heavy `node_modules`.
|
|
62
|
+
- **Python Envs**: For identifying abandoned virtual environments.
|
|
87
63
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
3. **Scan** β Click `START SCAN`. Results appear in two labeled sections.
|
|
91
|
-
4. **Review** β Click `βΊ` on any row to expand its subfolders. Check/uncheck individual items.
|
|
92
|
-
5. **Clean** β Use `Clean Selected` for marked items or `Clean All (Visible)` for everything in the active filter.
|
|
64
|
+
### 4. Review & Clean
|
|
65
|
+
Expand entries to review subfolders, check individual items, and click **Clean Selected** or **Clean All**.
|
|
93
66
|
|
|
94
|
-
|
|
95
|
-
> Deletion is **permanent**. There is no recycle bin. Review carefully before cleaning.
|
|
67
|
+
---
|
|
96
68
|
|
|
97
|
-
##
|
|
69
|
+
## π Technical Details
|
|
98
70
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
71
|
+
While distributed via NPM, Brain Cleaner is a hybrid tool. The Node.js wrapper automatically:
|
|
72
|
+
1. Detects your local Python environment.
|
|
73
|
+
2. Auto-installs core dependencies (`customtkinter`, `blessed`, `Pillow`) on the first run.
|
|
74
|
+
3. Securely executes the cross-platform cleaning engine.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## β οΈ Safety First
|
|
79
|
+
|
|
80
|
+
> [!WARNING]
|
|
81
|
+
> Deletion is **permanent**. Brain Cleaner does not move files to the trash; it deletes them to reclaim space immediately. Always review the scan results before confirming cleanup.
|
|
82
|
+
|
|
83
|
+
---
|
|
106
84
|
|
|
107
|
-
## License
|
|
85
|
+
## π License
|
|
108
86
|
|
|
109
|
-
MIT β *Developed to keep your system
|
|
87
|
+
MIT β *Developed to keep your development system lean and focused.*
|
package/app.py
CHANGED
|
@@ -9,6 +9,17 @@ import subprocess
|
|
|
9
9
|
from scanner import BrainScanner
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from PIL import Image
|
|
12
|
+
import webbrowser
|
|
13
|
+
|
|
14
|
+
def resource_path(relative_path):
|
|
15
|
+
""" Get absolute path to resource, works for dev and for PyInstaller """
|
|
16
|
+
try:
|
|
17
|
+
# PyInstaller creates a temp folder and stores path in _MEIPASS
|
|
18
|
+
base_path = sys._MEIPASS
|
|
19
|
+
except Exception:
|
|
20
|
+
base_path = os.path.abspath(".")
|
|
21
|
+
|
|
22
|
+
return os.path.join(base_path, relative_path)
|
|
12
23
|
|
|
13
24
|
ctk.set_appearance_mode("Dark")
|
|
14
25
|
ctk.set_default_color_theme("blue")
|
|
@@ -29,8 +40,7 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
29
40
|
self.current_loc_type = "Home"
|
|
30
41
|
|
|
31
42
|
# Load Icon
|
|
32
|
-
|
|
33
|
-
icon_ui_path = os.path.join(assets_dir, "icon_ui.png")
|
|
43
|
+
icon_ui_path = resource_path(os.path.join("assets", "icon_ui.png"))
|
|
34
44
|
self.logo_image = None
|
|
35
45
|
if os.path.exists(icon_ui_path):
|
|
36
46
|
try:
|
|
@@ -42,6 +52,7 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
42
52
|
# Category definitions
|
|
43
53
|
self.AI_CATS = ["Gemini", "Claude", "IDE Agents", "Other Tools"]
|
|
44
54
|
self.NPM_CATS = ["Node Modules"]
|
|
55
|
+
self.PY_CATS = ["Python Envs", "Python Envs (Obsolete)"]
|
|
45
56
|
|
|
46
57
|
# Info slider data
|
|
47
58
|
self.info_states = [
|
|
@@ -79,7 +90,7 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
79
90
|
ctk.CTkLabel(title_f, text="Brain Cleaner",
|
|
80
91
|
font=ctk.CTkFont(size=19, weight="bold")
|
|
81
92
|
).pack(pady=(0, 2))
|
|
82
|
-
ctk.CTkLabel(title_f, text="v1.
|
|
93
|
+
ctk.CTkLabel(title_f, text="v1.2.3",
|
|
83
94
|
font=ctk.CTkFont(size=11, slant="italic"), text_color="#a1a1a1"
|
|
84
95
|
).pack()
|
|
85
96
|
|
|
@@ -124,7 +135,14 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
124
135
|
font=ctk.CTkFont(size=12), border_color="#2e7d32",
|
|
125
136
|
hover_color="#2e7d32", fg_color="#2e7d32",
|
|
126
137
|
command=self._update_mode_ui
|
|
127
|
-
).grid(row=7, column=0, padx=24, pady=
|
|
138
|
+
).grid(row=7, column=0, padx=24, pady=2, sticky="w")
|
|
139
|
+
|
|
140
|
+
ctk.CTkRadioButton(self.sidebar, text="Python Envs",
|
|
141
|
+
variable=self.scan_mode_var, value="python",
|
|
142
|
+
font=ctk.CTkFont(size=12), border_color="#3776ab",
|
|
143
|
+
hover_color="#3776ab", fg_color="#3776ab",
|
|
144
|
+
command=self._update_mode_ui
|
|
145
|
+
).grid(row=8, column=0, padx=24, pady=(2, 4), sticky="w")
|
|
128
146
|
|
|
129
147
|
# ββ Bottom Sidebar Controls βββββββββββββββββββββββββββββββ
|
|
130
148
|
bottom = ctk.CTkFrame(self.sidebar, fg_color="transparent")
|
|
@@ -294,6 +312,25 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
294
312
|
font=ctk.CTkFont(size=11))
|
|
295
313
|
self.status_label.pack(side="right", padx=10)
|
|
296
314
|
|
|
315
|
+
# Project Links
|
|
316
|
+
links_f = ctk.CTkFrame(footer, fg_color="transparent")
|
|
317
|
+
links_f.pack(side="left", padx=20)
|
|
318
|
+
|
|
319
|
+
ctk.CTkButton(links_f, text="braincleaner.dev",
|
|
320
|
+
font=ctk.CTkFont(size=10, underline=True),
|
|
321
|
+
fg_color="transparent", text_color=("#1f538d", "#5da7e6"),
|
|
322
|
+
hover_color=("#e0e0e0", "#3a3a3a"),
|
|
323
|
+
width=80, height=20,
|
|
324
|
+
command=lambda: webbrowser.open("https://braincleaner.dev")
|
|
325
|
+
).pack(side="left", padx=2)
|
|
326
|
+
|
|
327
|
+
ctk.CTkButton(links_f, text="β Star on GitHub",
|
|
328
|
+
font=ctk.CTkFont(size=10, weight="bold"),
|
|
329
|
+
fg_color=("#24292e", "#333333"), hover_color=("#2c3137", "#444444"),
|
|
330
|
+
text_color="white", width=100, height=22, corner_radius=6,
|
|
331
|
+
command=lambda: webbrowser.open("https://github.com/konstantinwdk/brain-cleaner")
|
|
332
|
+
).pack(side="left", padx=5)
|
|
333
|
+
|
|
297
334
|
# Log Area
|
|
298
335
|
self.log_textbox = ctk.CTkTextbox(main, height=80, font=ctk.CTkFont(size=10))
|
|
299
336
|
self.log_textbox.grid(row=6, column=0, padx=10, pady=(0, 8), sticky="nsew")
|
|
@@ -312,10 +349,14 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
312
349
|
self.scan_header_frame.configure(fg_color="#1f538d")
|
|
313
350
|
self.scan_header_title.configure(text="AI Tools Cleanup")
|
|
314
351
|
self.scan_header_desc.configure(text="Identify and remove cache, logs, and configs left by AI assistants (Gemini, Claude, Cursor...).")
|
|
315
|
-
|
|
352
|
+
elif mode == "npm":
|
|
316
353
|
self.scan_header_frame.configure(fg_color="#2e7d32")
|
|
317
354
|
self.scan_header_title.configure(text="NPM Modules Cleanup")
|
|
318
355
|
self.scan_header_desc.configure(text="Free up space by removing heavy node_modules directories from web projects.")
|
|
356
|
+
elif mode == "python":
|
|
357
|
+
self.scan_header_frame.configure(fg_color="#3776ab")
|
|
358
|
+
self.scan_header_title.configure(text="Python Envs Cleanup")
|
|
359
|
+
self.scan_header_desc.configure(text="Remove obsolete virtual environments (venv, .venv) from your local projects.")
|
|
319
360
|
self.scan_header_count.configure(text="")
|
|
320
361
|
|
|
321
362
|
# ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -338,7 +379,9 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
338
379
|
"Claude": "#d97757",
|
|
339
380
|
"IDE Agents": "#7c4dff",
|
|
340
381
|
"Other Tools": "#546e7a",
|
|
341
|
-
"Node Modules": "#2e7d32"
|
|
382
|
+
"Node Modules": "#2e7d32",
|
|
383
|
+
"Python Envs": "#3776ab",
|
|
384
|
+
"Python Envs (Obsolete)": "#d32f2f"
|
|
342
385
|
}.get(cat, "#757575")
|
|
343
386
|
|
|
344
387
|
# ββ Info Slider βββββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -440,6 +483,10 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
440
483
|
for c in self.NPM_CATS:
|
|
441
484
|
results[c] = raw.get(c, [])
|
|
442
485
|
results["All"] += results[c]
|
|
486
|
+
elif mode == "python":
|
|
487
|
+
for c in self.PY_CATS:
|
|
488
|
+
results[c] = raw.get(c, [])
|
|
489
|
+
results["All"] += results[c]
|
|
443
490
|
self.scanning_active = False
|
|
444
491
|
self.after(0, lambda: self._finish_scan(results, mode))
|
|
445
492
|
|
|
@@ -462,7 +509,7 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
462
509
|
self.status_label.configure(text=f"Found {total} items β {total_str}")
|
|
463
510
|
self.log(f"Scan complete. {total} items found ({total_str}).")
|
|
464
511
|
|
|
465
|
-
found_cats = [c for c in (self.AI_CATS + self.NPM_CATS) if results.get(c)]
|
|
512
|
+
found_cats = [c for c in (self.AI_CATS + self.NPM_CATS + self.PY_CATS) if results.get(c)]
|
|
466
513
|
self.create_filter_bubbles(["All"] + found_cats)
|
|
467
514
|
|
|
468
515
|
count = 0
|
|
@@ -476,6 +523,11 @@ class BrainCleanerApp(ctk.CTk):
|
|
|
476
523
|
count = sum(len(results[c]) for c in section_cats)
|
|
477
524
|
for cat in section_cats:
|
|
478
525
|
self._render_rows(results[cat], cat)
|
|
526
|
+
elif mode == "python":
|
|
527
|
+
section_cats = [c for c in self.PY_CATS if results.get(c)]
|
|
528
|
+
count = sum(len(results[c]) for c in section_cats)
|
|
529
|
+
for cat in section_cats:
|
|
530
|
+
self._render_rows(results[cat], cat)
|
|
479
531
|
|
|
480
532
|
if count > 0:
|
|
481
533
|
self.scan_header_count.configure(text=f"{count} items")
|
package/assets/icon.ico
ADDED
|
Binary file
|
package/bin/brain-cleaner.js
CHANGED
|
@@ -4,63 +4,62 @@ const { spawn, execSync } = require('child_process');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Robust JS Wrapper for Brain Cleaner (Python)
|
|
8
|
+
* Focuses on execution and clear error reporting.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
continue;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
11
|
+
const COLORS = {
|
|
12
|
+
red: '\x1b[31m',
|
|
13
|
+
yellow: '\x1b[33m',
|
|
14
|
+
cyan: '\x1b[36m',
|
|
15
|
+
bold: '\x1b[1m',
|
|
16
|
+
reset: '\x1b[0m'
|
|
17
|
+
};
|
|
23
18
|
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} catch (e) {
|
|
33
|
-
console.log(`\x1b[33mDependency '${dep}' not found. Installing...\x1b[0m`);
|
|
34
|
-
try {
|
|
35
|
-
execSync(`${python} -m pip install ${dep}`, { stdio: 'inherit' });
|
|
36
|
-
} catch (pipErr) {
|
|
37
|
-
console.error(`\x1b[31mFailed to install '${dep}'. Please run 'pip install ${dep}' manually.\x1b[0m`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
19
|
+
function findPython() {
|
|
20
|
+
const commands = ['python3', 'python', 'py'];
|
|
21
|
+
for (const cmd of commands) {
|
|
22
|
+
try {
|
|
23
|
+
execSync(`${cmd} --version`, { stdio: 'ignore' });
|
|
24
|
+
return cmd;
|
|
25
|
+
} catch (e) {
|
|
26
|
+
continue;
|
|
40
27
|
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
41
30
|
}
|
|
42
31
|
|
|
43
32
|
const python = findPython();
|
|
44
33
|
|
|
45
34
|
if (!python) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
35
|
+
console.error(`\n${COLORS.red}${COLORS.bold}Error: Python not found!${COLORS.reset}`);
|
|
36
|
+
console.error(`${COLORS.yellow}Brain Cleaner requires Python 3.9 or higher to operate.${COLORS.reset}`);
|
|
37
|
+
console.error(`Please install Python: ${COLORS.cyan}https://www.python.org/downloads/${COLORS.reset}\n`);
|
|
38
|
+
process.exit(1);
|
|
50
39
|
}
|
|
51
40
|
|
|
52
|
-
// Ensure dependencies are present
|
|
53
|
-
checkAndInstallDeps(python);
|
|
54
|
-
|
|
55
41
|
// Path to the python entry point
|
|
56
42
|
const scriptPath = path.join(__dirname, '..', 'console', 'brain_cleaner_cli.py');
|
|
57
43
|
const args = process.argv.slice(2);
|
|
58
44
|
|
|
59
45
|
const pythonProcess = spawn(python, [scriptPath, ...args], {
|
|
60
|
-
|
|
61
|
-
|
|
46
|
+
stdio: 'inherit',
|
|
47
|
+
env: {
|
|
48
|
+
...process.env,
|
|
49
|
+
PYTHONPATH: path.join(__dirname, '..'),
|
|
50
|
+
PYTHONIOENCODING: 'utf-8' // Ensure rich terminal output works well
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
pythonProcess.on('error', (err) => {
|
|
55
|
+
console.error(`\n${COLORS.red}Failed to start Brain Cleaner:${COLORS.reset}`, err.message);
|
|
56
|
+
process.exit(1);
|
|
62
57
|
});
|
|
63
58
|
|
|
64
59
|
pythonProcess.on('close', (code) => {
|
|
65
|
-
|
|
60
|
+
if (code !== 0 && code !== null) {
|
|
61
|
+
console.log(`\n${COLORS.yellow}Note: If you see "ModuleNotFoundError", please run:${COLORS.reset}`);
|
|
62
|
+
console.log(`${COLORS.bold}npm install${COLORS.reset} again to finalize the setup.\n`);
|
|
63
|
+
}
|
|
64
|
+
process.exit(code);
|
|
66
65
|
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Premium Post-install script for Brain Cleaner
|
|
9
|
+
* Automatically handles Python dependency setup.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const COLORS = {
|
|
13
|
+
cyan: '\x1b[36m',
|
|
14
|
+
green: '\x1b[32m',
|
|
15
|
+
yellow: '\x1b[33m',
|
|
16
|
+
red: '\x1b[31m',
|
|
17
|
+
bold: '\x1b[1m',
|
|
18
|
+
reset: '\x1b[0m'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function log(msg, color = COLORS.reset) {
|
|
22
|
+
console.log(`${color}${msg}${COLORS.reset}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function findPython() {
|
|
26
|
+
const commands = ['python3', 'python', 'py'];
|
|
27
|
+
for (const cmd of commands) {
|
|
28
|
+
try {
|
|
29
|
+
execSync(`${cmd} --version`, { stdio: 'ignore' });
|
|
30
|
+
return cmd;
|
|
31
|
+
} catch (e) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function run() {
|
|
39
|
+
log('\nββ Brain Cleaner CLI Setup ββββββββββββββββββ', COLORS.cyan + COLORS.bold);
|
|
40
|
+
|
|
41
|
+
const python = findPython();
|
|
42
|
+
if (!python) {
|
|
43
|
+
log('β οΈ Python 3.9+ not found in your PATH.', COLORS.yellow);
|
|
44
|
+
log('Brain Cleaner requires Python to run its core logic.');
|
|
45
|
+
log('Please install it from https://python.org\n');
|
|
46
|
+
process.exit(0); // Exit gracefully during install to not break npm flow
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
log(`Found ${COLORS.bold}${python}${COLORS.reset}. Installing dependencies...`, COLORS.cyan);
|
|
50
|
+
|
|
51
|
+
const requirementsPath = path.join(__dirname, '..', 'requirements.txt');
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
// Use -m pip to ensure we use the pip associated with the detected python
|
|
55
|
+
execSync(`${python} -m pip install --upgrade pip`, { stdio: 'ignore' });
|
|
56
|
+
execSync(`${python} -m pip install -r "${requirementsPath}"`, { stdio: 'inherit' });
|
|
57
|
+
|
|
58
|
+
log('\nβ
Dependencies installed successfully!', COLORS.green + COLORS.bold);
|
|
59
|
+
log('You can now run Brain Cleaner using: ' + COLORS.bold + 'brain-cleaner', COLORS.green);
|
|
60
|
+
log('ββββββββββββββββββββββββββββββββββββββββββββ\n', COLORS.cyan);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
log('\nβ οΈ Could not install dependencies automatically.', COLORS.yellow);
|
|
63
|
+
log('This often happens due to permission restrictions.');
|
|
64
|
+
log(`Please run manually: ${COLORS.bold}${python} -m pip install -r requirements.txt`, COLORS.reset);
|
|
65
|
+
log('ββββββββββββββββββββββββββββββββββββββββββββ\n', COLORS.cyan);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
run();
|
|
@@ -50,11 +50,12 @@ class BrainCleanerCLI:
|
|
|
50
50
|
def draw_splash(self):
|
|
51
51
|
content = []
|
|
52
52
|
content.append(self.term.cyan(ASCII_ART))
|
|
53
|
-
content.append(self.term.bold("\n Welcome to Brain Cleaner CLI v1.
|
|
53
|
+
content.append(self.term.bold("\n Welcome to Brain Cleaner CLI v1.2.3"))
|
|
54
54
|
content.append(" " + "-" * 40)
|
|
55
55
|
content.append("\n Select Mode to begin:")
|
|
56
56
|
content.append(self.term.blue(" [1] AI Tools Cleanup"))
|
|
57
57
|
content.append(self.term.green(" [2] NPM Modules Cleanup"))
|
|
58
|
+
content.append(self.term.yellow(" [3] Python Envs Cleanup"))
|
|
58
59
|
content.append("\n Press 'q' to exit")
|
|
59
60
|
|
|
60
61
|
output = self.term.home + self.term.clear + "\n".join(content)
|
|
@@ -82,9 +83,15 @@ class BrainCleanerCLI:
|
|
|
82
83
|
|
|
83
84
|
# Mutual exclusivity like GUI
|
|
84
85
|
if mode == 'ai':
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
# AI Categories only
|
|
87
|
+
ai_cats = ["Gemini", "Claude", "IDE Agents", "Other Tools"]
|
|
88
|
+
self.scanner.categories = {k: v for k, v in self.scanner.categories.items() if k in ai_cats}
|
|
89
|
+
elif mode == 'npm':
|
|
87
90
|
self.scanner.categories = {k: v for k, v in self.scanner.categories.items() if k == "Node Modules"}
|
|
91
|
+
elif mode == 'python':
|
|
92
|
+
# Both Python Envs and Python Envs (Obsolete) are captured by the scanner.py logic
|
|
93
|
+
self.scanner.categories = {k: v for k, v in self.scanner.categories.items() if k == "Python Envs"}
|
|
94
|
+
|
|
88
95
|
self.scanner.all_patterns = [p for patterns in self.scanner.categories.values() for p in patterns]
|
|
89
96
|
|
|
90
97
|
self.status_msg = f"Scanning {mode.upper()} residues in {scan_root}..."
|
|
@@ -270,6 +277,9 @@ class BrainCleanerCLI:
|
|
|
270
277
|
elif val == '2':
|
|
271
278
|
self.mode = 'npm'
|
|
272
279
|
state = "SELECT_PATH"
|
|
280
|
+
elif val == '3':
|
|
281
|
+
self.mode = 'python'
|
|
282
|
+
state = "SELECT_PATH"
|
|
273
283
|
elif val.lower() == 'q':
|
|
274
284
|
return
|
|
275
285
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brain-cleaner",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Professional CLI utility to reclaim disk space by removing residues from AI tools (Gemini, Claude, Cursor) and heavy node_modules folders. Optimized for system performance and digital decluttering.",
|
|
5
5
|
"main": "bin/brain-cleaner.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"brain-cleaner": "bin/brain-cleaner.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"postinstall": "node bin/postinstall.js"
|
|
11
12
|
},
|
|
12
13
|
"repository": {
|
|
13
14
|
"type": "git",
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
"developer-tools"
|
|
35
36
|
],
|
|
36
37
|
"engines": {
|
|
37
|
-
"node": ">=14.0.0"
|
|
38
|
+
"node": ">=14.0.0",
|
|
39
|
+
"python": ">=3.9"
|
|
38
40
|
}
|
|
39
41
|
}
|
package/scanner.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import shutil
|
|
3
|
+
import time
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
class BrainScanner:
|
|
@@ -28,6 +29,7 @@ class BrainScanner:
|
|
|
28
29
|
".github-copilot",
|
|
29
30
|
"brain-recordings"
|
|
30
31
|
],
|
|
32
|
+
"Python Envs": ["venv", ".venv", "env", ".env", "virtualenv", "pyenv"],
|
|
31
33
|
"Node Modules": ["node_modules"]
|
|
32
34
|
}
|
|
33
35
|
# Flattened list for the walker
|
|
@@ -78,8 +80,9 @@ class BrainScanner:
|
|
|
78
80
|
|
|
79
81
|
for cat, path, size_str, size_bytes in self.scan_stream(start_path, interrupt_event):
|
|
80
82
|
item = (path, size_str, size_bytes)
|
|
81
|
-
if cat in found:
|
|
82
|
-
found[cat]
|
|
83
|
+
if cat not in found:
|
|
84
|
+
found[cat] = []
|
|
85
|
+
found[cat].append(item)
|
|
83
86
|
found["All"].append(item)
|
|
84
87
|
|
|
85
88
|
# Sort by path
|
|
@@ -107,6 +110,22 @@ class BrainScanner:
|
|
|
107
110
|
for cat, patterns in self.categories.items():
|
|
108
111
|
if d in patterns or any(d.startswith(p) for p in patterns):
|
|
109
112
|
full_path = os.path.join(root, d)
|
|
113
|
+
|
|
114
|
+
# Verify if it's a real Python Virtual Env
|
|
115
|
+
if cat == "Python Envs":
|
|
116
|
+
cfg_path = os.path.join(full_path, "pyvenv.cfg")
|
|
117
|
+
if not os.path.exists(cfg_path):
|
|
118
|
+
continue
|
|
119
|
+
|
|
120
|
+
# Check obsolescence (90 days)
|
|
121
|
+
try:
|
|
122
|
+
mtime = os.path.getmtime(cfg_path)
|
|
123
|
+
days_old = (time.time() - mtime) / (24 * 3600)
|
|
124
|
+
if days_old > 90:
|
|
125
|
+
cat = "Python Envs (Obsolete)"
|
|
126
|
+
except Exception:
|
|
127
|
+
pass
|
|
128
|
+
|
|
110
129
|
size_bytes = self.get_dir_size(full_path)
|
|
111
130
|
size_str = self.format_size(size_bytes)
|
|
112
131
|
|