Skip to content

Commit 7d8f11e

Browse files
committed
feat: Add Go, Python, Rust, and Homebrew scanning support, extend ScanOptions with new types, and enhance scan command documentation and TUI features.
1 parent 247be7d commit 7d8f11e

File tree

10 files changed

+1129
-73
lines changed

10 files changed

+1129
-73
lines changed

cmd/root/clean.go

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,54 @@ var (
2727

2828
// cleanCmd represents the clean command
2929
var cleanCmd = &cobra.Command{
30-
Use: "clean",
30+
Use: "clean [flags]",
3131
Short: "Clean development artifacts",
3232
Long: `Interactively select and clean development artifacts.
3333
34-
By default, runs in TUI mode with interactive selection.
35-
Use --confirm to actually delete files (default is dry-run).
34+
By default, runs in TUI mode with interactive selection and dry-run
35+
enabled (preview only). Use --confirm to actually delete files.
36+
37+
The TUI provides:
38+
• Real-time deletion progress with package-manager style output
39+
• Tree navigation for exploring nested folders
40+
• Quick single-item cleanup or batch operations
41+
• All operations logged to ~/.dev-cleaner.log
42+
43+
Safety Features:
44+
✓ Dry-run mode by default (files are safe)
45+
✓ Confirmation required before deletion
46+
✓ Path validation (never touches system files)
47+
✓ All actions logged for audit trail
3648
3749
Examples:
38-
dev-cleaner clean # Interactive TUI (dry-run)
39-
dev-cleaner clean --confirm # Interactive TUI (actually delete)
40-
dev-cleaner clean --no-tui # Simple text mode
41-
dev-cleaner clean --ios # Clean iOS artifacts only`,
50+
dev-cleaner clean # Interactive TUI (dry-run)
51+
dev-cleaner clean --confirm # Interactive TUI (actually delete)
52+
dev-cleaner clean --no-tui # Simple text mode
53+
dev-cleaner clean --ios --confirm # Clean iOS artifacts only
54+
dev-cleaner clean --node # Preview Node.js cleanup (dry-run)
55+
56+
Flags:
57+
--confirm Actually delete files (disables dry-run)
58+
--dry-run Preview only, don't delete (default: true)
59+
--ios Clean iOS/Xcode artifacts only
60+
--android Clean Android/Gradle artifacts only
61+
--node Clean Node.js artifacts only
62+
--flutter Clean Flutter/Dart artifacts only
63+
--no-tui, -T Disable TUI, use simple text mode
64+
--tui Use interactive TUI mode (default: true)
65+
66+
TUI Keyboard Shortcuts:
67+
c Quick clean current item (ignores selections)
68+
Enter Clean all selected items (batch mode)
69+
Space Toggle selection
70+
a/n Select all / none
71+
→/l Enter tree mode (explore folders)
72+
? Show help screen
73+
74+
Important:
75+
• 'c' clears all selections and cleans ONLY the current item
76+
• 'Enter' cleans ALL selected items (batch operation)
77+
• Tree mode allows deletion at any folder level`,
4278
Run: runClean,
4379
}
4480

cmd/root/root.go

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,47 @@ var rootCmd = &cobra.Command{
2020
Long: `Mac Dev Cleaner - A CLI tool to clean development project artifacts
2121
2222
Quickly free up disk space by removing:
23-
• Xcode DerivedData and caches
24-
• Android Gradle caches
23+
• Xcode DerivedData, Archives, and caches
24+
• Android Gradle caches and SDK artifacts
2525
• Node.js node_modules directories
2626
• Package manager caches (npm, yarn, pnpm, bun)
27+
• Flutter/Dart build artifacts and pub-cache
2728
28-
Examples:
29-
dev-cleaner scan # Scan and show all cleanable items
30-
dev-cleaner scan --ios # Scan iOS/Xcode only
31-
dev-cleaner clean # Interactive clean (dry-run by default)
32-
dev-cleaner clean --confirm # Actually delete selected items`,
29+
Features:
30+
✨ Interactive TUI with keyboard navigation
31+
✨ Tree mode for exploring nested folders
32+
✨ Dry-run mode by default (safe preview)
33+
✨ Quick clean with single keypress
34+
✨ Batch operations for multiple items
35+
✨ Real-time deletion progress tracking
36+
37+
Scan Examples:
38+
dev-cleaner scan # Scan all + launch TUI
39+
dev-cleaner scan --ios # Scan iOS/Xcode only
40+
dev-cleaner scan --android # Scan Android/Gradle only
41+
dev-cleaner scan --node # Scan Node.js artifacts only
42+
dev-cleaner scan --flutter # Scan Flutter/Dart only
43+
dev-cleaner scan --no-tui # Text output without TUI
44+
45+
Clean Examples:
46+
dev-cleaner clean # Interactive TUI (dry-run)
47+
dev-cleaner clean --confirm # Interactive TUI (actually delete)
48+
dev-cleaner clean --ios --confirm # Clean iOS artifacts only
49+
dev-cleaner clean --no-tui # Simple text mode cleanup
50+
51+
TUI Keyboard Shortcuts:
52+
↑/↓, k/j Navigate up/down
53+
Space Toggle selection
54+
a Select all items
55+
n Deselect all items
56+
c Quick clean current item (single-item mode)
57+
Enter Clean all selected items (batch mode)
58+
→/l Drill down into folder (tree mode)
59+
←/h Go back to parent (in tree mode)
60+
? Show detailed help screen
61+
q Quit
62+
63+
Tip: Use 'dev-cleaner scan' to start the interactive TUI mode!`,
3364
Version: Version,
3465
}
3566

cmd/root/scan.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,43 @@ var (
2222

2323
// scanCmd represents the scan command
2424
var scanCmd = &cobra.Command{
25-
Use: "scan",
25+
Use: "scan [flags]",
2626
Short: "Scan for development artifacts",
2727
Long: `Scan your system for development artifacts that can be cleaned.
2828
29-
By default, opens interactive TUI for selection.
30-
Use --no-tui for simple text output.
29+
By default, scans all supported categories and opens interactive TUI
30+
for browsing, selection, and cleanup. The TUI provides tree navigation,
31+
keyboard shortcuts, and real-time deletion progress.
32+
33+
Categories Scanned:
34+
• Xcode (DerivedData, Archives, CoreSimulator, CocoaPods)
35+
• Android (Gradle caches, SDK system images)
36+
• Node.js (node_modules, npm/yarn/pnpm/bun caches)
37+
• Flutter (build artifacts, .pub-cache, .dart_tool)
3138
3239
Examples:
33-
dev-cleaner scan # Scan + TUI (default)
34-
dev-cleaner scan --no-tui # Scan + text output
35-
dev-cleaner scan --ios # Scan iOS/Xcode only`,
40+
dev-cleaner scan # Scan all, launch TUI (default)
41+
dev-cleaner scan --ios # Scan iOS/Xcode only
42+
dev-cleaner scan --android # Scan Android only
43+
dev-cleaner scan --node # Scan Node.js only
44+
dev-cleaner scan --flutter # Scan Flutter only
45+
dev-cleaner scan --no-tui # Text output without TUI
46+
47+
Flags:
48+
--ios Scan iOS/Xcode artifacts only
49+
--android Scan Android/Gradle artifacts only
50+
--node Scan Node.js artifacts only
51+
--flutter Scan Flutter/Dart artifacts only
52+
--no-tui, -T Disable TUI, show simple text output
53+
--all Scan all categories (default: true)
54+
55+
TUI Features:
56+
• Navigate with arrow keys or vim bindings (k/j/h/l)
57+
• Select items with Space, 'a' for all, 'n' for none
58+
• Quick clean single item with 'c'
59+
• Batch clean selected items with Enter
60+
• Drill down into folders with → or 'l'
61+
• Press '?' for detailed help`,
3662
Run: runScan,
3763
}
3864

internal/cleaner/cleaner.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ func (c *Cleaner) SetDryRun(dryRun bool) {
5353
c.dryRun = dryRun
5454
}
5555

56+
// Logger returns the cleaner's logger instance
57+
func (c *Cleaner) Logger() *log.Logger {
58+
return c.logger
59+
}
60+
5661
// CleanResult represents the result of a clean operation
5762
type CleanResult struct {
5863
Path string

internal/scanner/golang.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package scanner
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/thanhdevapp/dev-cleaner/pkg/types"
8+
)
9+
10+
// getGOCACHE returns GOCACHE path or default
11+
func getGOCACHE() string {
12+
if gocache := os.Getenv("GOCACHE"); gocache != "" {
13+
return gocache
14+
}
15+
// macOS default
16+
home, _ := os.UserHomeDir()
17+
return filepath.Join(home, "Library", "Caches", "go-build")
18+
}
19+
20+
// getGOMODCACHE returns GOMODCACHE path or default
21+
func getGOMODCACHE() string {
22+
if gomodcache := os.Getenv("GOMODCACHE"); gomodcache != "" {
23+
return gomodcache
24+
}
25+
// Default: $GOPATH/pkg/mod or ~/go/pkg/mod
26+
gopath := os.Getenv("GOPATH")
27+
if gopath == "" {
28+
home, _ := os.UserHomeDir()
29+
gopath = filepath.Join(home, "go")
30+
}
31+
return filepath.Join(gopath, "pkg", "mod")
32+
}
33+
34+
// ScanGo scans for Go development artifacts
35+
func (s *Scanner) ScanGo(maxDepth int) []types.ScanResult {
36+
var results []types.ScanResult
37+
38+
// Go build cache
39+
gocache := getGOCACHE()
40+
if s.PathExists(gocache) {
41+
size, count, err := s.calculateSize(gocache)
42+
if err == nil && size > 0 {
43+
results = append(results, types.ScanResult{
44+
Path: gocache,
45+
Type: types.TypeGo,
46+
Size: size,
47+
FileCount: count,
48+
Name: "Go Build Cache",
49+
})
50+
}
51+
}
52+
53+
// Go module cache
54+
gomodcache := getGOMODCACHE()
55+
if s.PathExists(gomodcache) {
56+
size, count, err := s.calculateSize(gomodcache)
57+
if err == nil && size > 0 {
58+
results = append(results, types.ScanResult{
59+
Path: gomodcache,
60+
Type: types.TypeGo,
61+
Size: size,
62+
FileCount: count,
63+
Name: "Go Module Cache",
64+
})
65+
}
66+
}
67+
68+
// Go test cache (same location as build cache typically)
69+
gotestcache := os.Getenv("GOTESTCACHE")
70+
if gotestcache != "" && gotestcache != gocache && s.PathExists(gotestcache) {
71+
size, count, err := s.calculateSize(gotestcache)
72+
if err == nil && size > 0 {
73+
results = append(results, types.ScanResult{
74+
Path: gotestcache,
75+
Type: types.TypeGo,
76+
Size: size,
77+
FileCount: count,
78+
Name: "Go Test Cache",
79+
})
80+
}
81+
}
82+
83+
return results
84+
}

internal/scanner/homebrew.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package scanner
2+
3+
import (
4+
"github.com/thanhdevapp/dev-cleaner/pkg/types"
5+
)
6+
7+
// HomebrewPaths contains Homebrew cache paths
8+
var HomebrewPaths = []struct {
9+
Path string
10+
Name string
11+
}{
12+
// User cache
13+
{"~/Library/Caches/Homebrew", "Homebrew Cache"},
14+
// Apple Silicon Homebrew
15+
{"/opt/homebrew/Library/Caches/Homebrew", "Homebrew Cache (ARM)"},
16+
// Intel Homebrew
17+
{"/usr/local/Homebrew/Library/Caches/Homebrew", "Homebrew Cache (Intel)"},
18+
}
19+
20+
// ScanHomebrew scans for Homebrew caches
21+
func (s *Scanner) ScanHomebrew() []types.ScanResult {
22+
var results []types.ScanResult
23+
24+
for _, target := range HomebrewPaths {
25+
path := s.ExpandPath(target.Path)
26+
if !s.PathExists(path) {
27+
continue
28+
}
29+
30+
size, count, err := s.calculateSize(path)
31+
if err != nil || size == 0 {
32+
continue
33+
}
34+
35+
results = append(results, types.ScanResult{
36+
Path: path,
37+
Type: types.TypeHomebrew,
38+
Size: size,
39+
FileCount: count,
40+
Name: target.Name,
41+
})
42+
}
43+
44+
return results
45+
}

0 commit comments

Comments
 (0)