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 CHANGED
@@ -1,8 +1,13 @@
1
1
  # Brain Cleaner
2
2
 
3
+ ![Brain Cleaner Banner](https://braincleaner.dev/og-image.png)
4
+
3
5
  **Language / Idioma:**
4
6
  πŸ‡ͺπŸ‡Έ EspaΓ±ol  |  [πŸ‡¬πŸ‡§ English](https://github.com/konstantinWDK/brain-cleaner/blob/main/README.md)
5
7
 
8
+ ![Brain Cleaner UI Mockup](https://braincleaner.dev/mockup.png)
9
+
10
+ ---
6
11
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
7
12
  [![Licencia: MIT](https://img.shields.io/badge/Licencia-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
13
  [![Plataforma macOS](https://img.shields.io/badge/plataforma-macOS-lightgrey.svg)](https://www.apple.com/macos/)
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Brain Cleaner
2
2
 
3
+ ![Brain Cleaner Banner](https://braincleaner.dev/og-image.png)
4
+
3
5
  [![NPM Version](https://img.shields.io/npm/v/brain-cleaner.svg)](https://www.npmjs.com/package/brain-cleaner)
4
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
7
  [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](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
+ ![Brain Cleaner UI Mockup](https://braincleaner.dev/mockup.png)
13
+
10
14
  ---
11
15
 
12
- **Brain Cleaner** is a professional CLI utility designed to reclaim disk space by identifying and removing digital noise:
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
- ### βš™οΈ Technical Architecture
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
- ### πŸš€ Recommended Installation (Global)
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
- ```bash
27
- npm install -g brain-cleaner
28
- ```
22
+ ---
29
23
 
30
- #### Alternative: Install from Source (Python)
31
- If you prefer to install via Python/Pip directly from the repository:
24
+ ## πŸš€ Key Features
32
25
 
33
- ```bash
34
- pip install git+https://github.com/konstantinwdk/brain-cleaner
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
- ### πŸš€ Requirements
38
- - **Python 3.9+** is required.
39
- - **Node.js 14+** (if installing via NPM).
32
+ ---
40
33
 
41
- ## Quick Start
34
+ ## βš™οΈ Installation
42
35
 
43
- ```bash
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
- 1. Open your terminal in the root directory of the project.
63
- 2. Run:
64
- ```bash
65
- pip install .
66
- ```
42
+ ### Requirements
43
+ - **Python 3.9+** (Required for the scanning engine).
44
+ - **Node.js 14+**.
67
45
 
68
- ## Quick Start
46
+ ---
69
47
 
70
- Once installed, you can run the interactive interface from anywhere:
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
- > [!IMPORTANT]
78
- > **macOS 26 (Tahoe) Compatibility**: Use Homebrew Python 3.11 or later to avoid "macOS 26 required" errors in GUI mode.
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
- ## Usage
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
- 1. **Scope** β€” Choose `Home`, `Full System` or `Custom Folder` in the sidebar.
89
- 2. **Mode** β€” Choose between `AI Tools` or `NPM Modules` to set what to scan.
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
- > [!WARNING]
95
- > Deletion is **permanent**. There is no recycle bin. Review carefully before cleaning.
67
+ ---
96
68
 
97
- ## Detected Categories
69
+ ## πŸ›  Technical Details
98
70
 
99
- | Category | Tools |
100
- |---|---|
101
- | **Gemini** | Google Gemini CLI / API cache |
102
- | **Claude** | Anthropic Claude logs & config |
103
- | **IDE Agents** | Cursor, Windsurf, Trae, Roo-Code, Claude-Dev |
104
- | **Other Tools** | Uncategorized AI tools |
105
- | **Node Modules** | `node_modules` in Node.js projects |
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 free of digital noise.*
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
- assets_dir = os.path.join(os.path.dirname(__file__), "assets")
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.1.0",
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=(2, 4), sticky="w")
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
- else:
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")
Binary file
@@ -4,63 +4,62 @@ const { spawn, execSync } = require('child_process');
4
4
  const path = require('path');
5
5
 
6
6
  /**
7
- * Enhanced JS Wrapper for Brain Cleaner (Python)
8
- * Handles auto-installation of dependencies.
7
+ * Robust JS Wrapper for Brain Cleaner (Python)
8
+ * Focuses on execution and clear error reporting.
9
9
  */
10
10
 
11
- function findPython() {
12
- const commands = ['python3', 'python', 'py'];
13
- for (const cmd of commands) {
14
- try {
15
- execSync(`${cmd} --version`, { stdio: 'ignore' });
16
- return cmd;
17
- } catch (e) {
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 checkAndInstallDeps(python) {
25
- console.log('\x1b[36mFinalizing Brain Cleaner installation...\x1b[0m');
26
- const deps = ['blessed', 'Pillow'];
27
-
28
- for (const dep of deps) {
29
- try {
30
- // Check if dependency exists
31
- execSync(`${python} -c "import ${dep.toLowerCase()}"`, { stdio: 'ignore' });
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
- console.error('\x1b[31mError: Python not found!\x1b[0m');
47
- console.error('Brain Cleaner requires Python 3.9 or higher.');
48
- console.error('Please install Python: https://www.python.org/downloads/');
49
- process.exit(1);
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
- stdio: 'inherit',
61
- env: { ...process.env, PYTHONPATH: path.join(__dirname, '..') }
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
- process.exit(code);
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.1.0"))
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
- self.scanner.categories = {k: v for k, v in self.scanner.categories.items() if k != "Node Modules"}
86
- else:
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.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].append(item)
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