@tukuyomil032/broom 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +554 -0
  3. package/dist/commands/analyze.js +371 -0
  4. package/dist/commands/backup.js +257 -0
  5. package/dist/commands/clean.js +255 -0
  6. package/dist/commands/completion.js +714 -0
  7. package/dist/commands/config.js +474 -0
  8. package/dist/commands/doctor.js +280 -0
  9. package/dist/commands/duplicates.js +325 -0
  10. package/dist/commands/help.js +34 -0
  11. package/dist/commands/index.js +22 -0
  12. package/dist/commands/installer.js +266 -0
  13. package/dist/commands/optimize.js +270 -0
  14. package/dist/commands/purge.js +271 -0
  15. package/dist/commands/remove.js +184 -0
  16. package/dist/commands/reports.js +173 -0
  17. package/dist/commands/schedule.js +249 -0
  18. package/dist/commands/status.js +468 -0
  19. package/dist/commands/touchid.js +230 -0
  20. package/dist/commands/uninstall.js +336 -0
  21. package/dist/commands/update.js +182 -0
  22. package/dist/commands/watch.js +258 -0
  23. package/dist/index.js +131 -0
  24. package/dist/scanners/base.js +21 -0
  25. package/dist/scanners/browser-cache.js +111 -0
  26. package/dist/scanners/dev-cache.js +64 -0
  27. package/dist/scanners/docker.js +96 -0
  28. package/dist/scanners/downloads.js +66 -0
  29. package/dist/scanners/homebrew.js +82 -0
  30. package/dist/scanners/index.js +126 -0
  31. package/dist/scanners/installer.js +87 -0
  32. package/dist/scanners/ios-backups.js +82 -0
  33. package/dist/scanners/node-modules.js +75 -0
  34. package/dist/scanners/temp-files.js +65 -0
  35. package/dist/scanners/trash.js +90 -0
  36. package/dist/scanners/user-cache.js +62 -0
  37. package/dist/scanners/user-logs.js +53 -0
  38. package/dist/scanners/xcode.js +124 -0
  39. package/dist/types/index.js +23 -0
  40. package/dist/ui/index.js +5 -0
  41. package/dist/ui/monitors.js +345 -0
  42. package/dist/ui/output.js +304 -0
  43. package/dist/ui/prompts.js +270 -0
  44. package/dist/utils/config.js +133 -0
  45. package/dist/utils/debug.js +119 -0
  46. package/dist/utils/fs.js +283 -0
  47. package/dist/utils/help.js +265 -0
  48. package/dist/utils/index.js +6 -0
  49. package/dist/utils/paths.js +142 -0
  50. package/dist/utils/report.js +404 -0
  51. package/package.json +87 -0
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Path definitions for cleanup operations
3
+ */
4
+ import { homedir } from 'os';
5
+ import { join } from 'path';
6
+ const HOME = homedir();
7
+ export const paths = {
8
+ // User caches
9
+ userCache: join(HOME, 'Library/Caches'),
10
+ userLogs: join(HOME, 'Library/Logs'),
11
+ // System caches (requires sudo)
12
+ systemCache: '/Library/Caches',
13
+ systemLogs: '/Library/Logs',
14
+ // Trash
15
+ trash: join(HOME, '.Trash'),
16
+ // Downloads
17
+ downloads: join(HOME, 'Downloads'),
18
+ // Browser caches
19
+ browserCache: {
20
+ chrome: join(HOME, 'Library/Caches/Google/Chrome'),
21
+ chromeProfile: join(HOME, 'Library/Application Support/Google/Chrome/Default/Cache'),
22
+ safari: join(HOME, 'Library/Caches/com.apple.Safari'),
23
+ firefox: join(HOME, 'Library/Caches/Firefox'),
24
+ firefoxProfile: join(HOME, 'Library/Application Support/Firefox/Profiles'),
25
+ edge: join(HOME, 'Library/Caches/Microsoft Edge'),
26
+ edgeProfile: join(HOME, 'Library/Application Support/Microsoft Edge/Default/Cache'),
27
+ brave: join(HOME, 'Library/Caches/BraveSoftware/Brave-Browser'),
28
+ braveProfile: join(HOME, 'Library/Application Support/BraveSoftware/Brave-Browser/Default/Cache'),
29
+ arc: join(HOME, 'Library/Caches/company.thebrowser.Browser'),
30
+ arcProfile: join(HOME, 'Library/Application Support/Arc/User Data/Default/Cache'),
31
+ },
32
+ // Development caches
33
+ devCache: {
34
+ npm: join(HOME, '.npm'),
35
+ npmCache: join(HOME, '.npm/_cacache'),
36
+ yarn: join(HOME, '.yarn/cache'),
37
+ pnpm: join(HOME, '.pnpm-store'),
38
+ bun: join(HOME, '.bun'),
39
+ pip: join(HOME, 'Library/Caches/pip'),
40
+ pipCache: join(HOME, '.cache/pip'),
41
+ cargo: join(HOME, '.cargo/registry/cache'),
42
+ rustup: join(HOME, '.rustup/downloads'),
43
+ go: join(HOME, 'go/pkg/mod/cache'),
44
+ gradle: join(HOME, '.gradle/caches'),
45
+ maven: join(HOME, '.m2/repository'),
46
+ cocoapods: join(HOME, 'Library/Caches/CocoaPods'),
47
+ carthage: join(HOME, 'Library/Caches/org.carthage.CarthageKit'),
48
+ composer: join(HOME, '.composer/cache'),
49
+ },
50
+ // Xcode caches
51
+ xcode: {
52
+ derivedData: join(HOME, 'Library/Developer/Xcode/DerivedData'),
53
+ archives: join(HOME, 'Library/Developer/Xcode/Archives'),
54
+ deviceSupport: join(HOME, 'Library/Developer/Xcode/iOS DeviceSupport'),
55
+ simulatorCache: join(HOME, 'Library/Developer/CoreSimulator/Caches'),
56
+ simulatorDevices: join(HOME, 'Library/Developer/CoreSimulator/Devices'),
57
+ modulesCache: join(HOME, 'Library/Developer/Xcode/ModuleCache'),
58
+ previewsCache: join(HOME, 'Library/Developer/Xcode/UserData/Previews'),
59
+ },
60
+ // Homebrew
61
+ homebrew: {
62
+ cache: join(HOME, 'Library/Caches/Homebrew'),
63
+ logs: join(HOME, 'Library/Logs/Homebrew'),
64
+ downloads: '/opt/homebrew/var/homebrew/cache', // Apple Silicon
65
+ },
66
+ // Docker
67
+ docker: {
68
+ data: join(HOME, 'Library/Containers/com.docker.docker/Data'),
69
+ vmDisk: join(HOME, 'Library/Containers/com.docker.docker/Data/vms'),
70
+ },
71
+ // iOS backups
72
+ iosBackups: join(HOME, 'Library/Application Support/MobileSync/Backup'),
73
+ // Temp files
74
+ tempFiles: {
75
+ userTemp: join(HOME, 'Library/Caches/TemporaryItems'),
76
+ systemTemp: '/private/tmp',
77
+ varTmp: '/var/tmp',
78
+ },
79
+ // Spotlight
80
+ spotlight: {
81
+ index: '/.Spotlight-V100',
82
+ },
83
+ // Time Machine local snapshots
84
+ timeMachine: '/.MobileBackups',
85
+ // Application Support
86
+ applicationSupport: join(HOME, 'Library/Application Support'),
87
+ // Preferences
88
+ preferences: join(HOME, 'Library/Preferences'),
89
+ // Saved Application State
90
+ savedState: join(HOME, 'Library/Saved Application State'),
91
+ // Applications
92
+ applications: '/Applications',
93
+ userApplications: join(HOME, 'Applications'),
94
+ };
95
+ // Common app data patterns
96
+ export const appDataPatterns = [
97
+ // General caches
98
+ 'Library/Caches/*',
99
+ 'Library/Application Support/*',
100
+ 'Library/Preferences/*',
101
+ 'Library/Saved Application State/*',
102
+ 'Library/Logs/*',
103
+ // Launch agents/daemons
104
+ 'Library/LaunchAgents/*',
105
+ 'Library/LaunchDaemons/*',
106
+ // Containers
107
+ 'Library/Containers/*',
108
+ 'Library/Group Containers/*',
109
+ ];
110
+ // Files to exclude from cleanup
111
+ export const excludePatterns = [
112
+ // System essentials
113
+ 'com.apple.*',
114
+ // Active browser profiles
115
+ '**/Cookies',
116
+ '**/Bookmarks',
117
+ '**/History',
118
+ '**/Login Data',
119
+ // Development essentials
120
+ 'node_modules/.package-lock.json',
121
+ '.git',
122
+ // User data
123
+ '**/LocalStorage',
124
+ '**/IndexedDB',
125
+ ];
126
+ // Category paths mapping
127
+ export const categoryPaths = {
128
+ 'user-cache': [paths.userCache],
129
+ 'system-cache': [paths.systemCache],
130
+ 'user-logs': [paths.userLogs],
131
+ 'system-logs': [paths.systemLogs],
132
+ trash: [paths.trash],
133
+ downloads: [paths.downloads],
134
+ 'browser-cache': Object.values(paths.browserCache),
135
+ 'dev-cache': Object.values(paths.devCache),
136
+ xcode: Object.values(paths.xcode),
137
+ homebrew: [paths.homebrew.cache, paths.homebrew.logs],
138
+ docker: [paths.docker.data],
139
+ 'ios-backups': [paths.iosBackups],
140
+ 'temp-files': Object.values(paths.tempFiles),
141
+ };
142
+ export default paths;
@@ -0,0 +1,404 @@
1
+ /**
2
+ * HTML Report Generator
3
+ */
4
+ import Handlebars from 'handlebars';
5
+ import { formatSize } from '../utils/fs.js';
6
+ import { writeFile, mkdir } from 'fs/promises';
7
+ import { dirname } from 'path';
8
+ // Register Handlebars helpers
9
+ Handlebars.registerHelper('formatSize', (bytes) => {
10
+ return formatSize(bytes);
11
+ });
12
+ Handlebars.registerHelper('formatDate', (date) => {
13
+ return new Date(date).toLocaleString('ja-JP');
14
+ });
15
+ Handlebars.registerHelper('formatDuration', (ms) => {
16
+ const seconds = Math.floor(ms / 1000);
17
+ const minutes = Math.floor(seconds / 60);
18
+ const hours = Math.floor(minutes / 60);
19
+ if (hours > 0) {
20
+ return `${hours}時間${minutes % 60}分${seconds % 60}秒`;
21
+ }
22
+ else if (minutes > 0) {
23
+ return `${minutes}分${seconds % 60}秒`;
24
+ }
25
+ else {
26
+ return `${seconds}秒`;
27
+ }
28
+ });
29
+ Handlebars.registerHelper('toFixed', (num, decimals) => {
30
+ return num.toFixed(decimals);
31
+ });
32
+ Handlebars.registerHelper('json', (obj) => {
33
+ return JSON.stringify(obj);
34
+ });
35
+ // Comparison helper
36
+ Handlebars.registerHelper('eq', (a, b) => {
37
+ return a === b;
38
+ });
39
+ // Greater than helper
40
+ Handlebars.registerHelper('gt', (a, b) => {
41
+ return a > b;
42
+ });
43
+ // Map helper - extract property from array
44
+ Handlebars.registerHelper('map', (array, property) => {
45
+ if (!Array.isArray(array))
46
+ return [];
47
+ return array.map((item) => item[property]);
48
+ });
49
+ const HTML_TEMPLATE = `<!DOCTYPE html>
50
+ <html lang="ja">
51
+ <head>
52
+ <meta charset="UTF-8">
53
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
54
+ <title>Broom Cleanup Report - {{formatDate metadata.generatedAt}}</title>
55
+ <script src="https://cdn.tailwindcss.com"></script>
56
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
57
+ <style>
58
+ @media print {
59
+ .no-print { display: none; }
60
+ body { background: white; }
61
+ }
62
+ @page {
63
+ margin: 2cm;
64
+ }
65
+ </style>
66
+ </head>
67
+ <body class="bg-gray-50">
68
+ <!-- Header -->
69
+ <header class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white py-8 no-print">
70
+ <div class="container mx-auto px-4">
71
+ <h1 class="text-4xl font-bold flex items-center gap-2">
72
+ 🧹 Broom Cleanup Report
73
+ </h1>
74
+ <p class="text-cyan-100 mt-2">{{formatDate metadata.generatedAt}}</p>
75
+ </div>
76
+ </header>
77
+
78
+ <!-- Summary Cards -->
79
+ <section class="container mx-auto px-4 py-8">
80
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-6">
81
+ <!-- Space Freed -->
82
+ <div class="bg-white rounded-lg shadow-md p-6">
83
+ <div class="text-gray-600 text-sm font-medium">回収した容量</div>
84
+ <div class="text-3xl font-bold text-green-600 mt-2">{{formatSize summary.totalSpaceFreed}}</div>
85
+ <div class="text-gray-500 text-xs mt-1">{{summary.totalFilesDeleted}} ファイル</div>
86
+ </div>
87
+
88
+ <!-- Files Deleted -->
89
+ <div class="bg-white rounded-lg shadow-md p-6">
90
+ <div class="text-gray-600 text-sm font-medium">削除ファイル数</div>
91
+ <div class="text-3xl font-bold text-blue-600 mt-2">{{summary.totalFilesDeleted}}</div>
92
+ <div class="text-gray-500 text-xs mt-1">合計</div>
93
+ </div>
94
+
95
+ <!-- Time Elapsed -->
96
+ <div class="bg-white rounded-lg shadow-md p-6">
97
+ <div class="text-gray-600 text-sm font-medium">処理時間</div>
98
+ <div class="text-3xl font-bold text-purple-600 mt-2">{{formatDuration summary.timeElapsed}}</div>
99
+ <div class="text-gray-500 text-xs mt-1">経過時間</div>
100
+ </div>
101
+
102
+ <!-- Status -->
103
+ <div class="bg-white rounded-lg shadow-md p-6">
104
+ <div class="text-gray-600 text-sm font-medium">ステータス</div>
105
+ <div class="text-3xl font-bold {{#if (eq summary.status 'success')}}text-green-600{{else}}text-yellow-600{{/if}} mt-2">
106
+ {{#if (eq summary.status 'success')}}✓ 成功{{else}}⚠ 部分的{{/if}}
107
+ </div>
108
+ <div class="text-gray-500 text-xs mt-1">完了</div>
109
+ </div>
110
+ </div>
111
+ </section>
112
+
113
+ <!-- Charts -->
114
+ <section class="container mx-auto px-4 py-8">
115
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
116
+ <!-- Category Breakdown -->
117
+ <div class="bg-white rounded-lg shadow-md p-6">
118
+ <h3 class="text-xl font-semibold mb-4">カテゴリ別内訳</h3>
119
+ <div class="h-64">
120
+ <canvas id="categoryChart"></canvas>
121
+ </div>
122
+ </div>
123
+
124
+ <!-- Disk Comparison -->
125
+ <div class="bg-white rounded-lg shadow-md p-6">
126
+ <h3 class="text-xl font-semibold mb-4">ディスク使用量の変化</h3>
127
+ <div class="h-64">
128
+ <canvas id="diskChart"></canvas>
129
+ </div>
130
+ </div>
131
+ </div>
132
+ </section>
133
+
134
+ <!-- Files Table -->
135
+ <section class="container mx-auto px-4 py-8">
136
+ <div class="bg-white rounded-lg shadow-md p-6">
137
+ <h3 class="text-xl font-semibold mb-4">削除されたファイル</h3>
138
+ <div class="overflow-x-auto">
139
+ <table class="min-w-full table-auto">
140
+ <thead class="bg-gray-50">
141
+ <tr>
142
+ <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">パス</th>
143
+ <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">サイズ</th>
144
+ <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">カテゴリ</th>
145
+ <th class="px-4 py-2 text-left text-xs font-medium text-gray-500 uppercase">削除時刻</th>
146
+ </tr>
147
+ </thead>
148
+ <tbody class="divide-y divide-gray-200">
149
+ {{#each files}}
150
+ <tr class="hover:bg-gray-50">
151
+ <td class="px-4 py-3 font-mono text-xs text-gray-800">{{this.path}}</td>
152
+ <td class="px-4 py-3 text-sm">{{formatSize this.size}}</td>
153
+ <td class="px-4 py-3">
154
+ <span class="px-2 py-1 rounded text-xs bg-blue-100 text-blue-800">
155
+ {{this.category}}
156
+ </span>
157
+ </td>
158
+ <td class="px-4 py-3 text-sm text-gray-600">{{formatDate this.deletedAt}}</td>
159
+ </tr>
160
+ {{/each}}
161
+ </tbody>
162
+ </table>
163
+ </div>
164
+ </div>
165
+ </section>
166
+
167
+ <!-- Metadata -->
168
+ <section class="container mx-auto px-4 py-8">
169
+ <div class="bg-white rounded-lg shadow-md p-6">
170
+ <h3 class="text-xl font-semibold mb-4">実行情報</h3>
171
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
172
+ <div>
173
+ <span class="text-gray-600">コマンド:</span>
174
+ <code class="ml-2 px-2 py-1 bg-gray-100 rounded">{{metadata.command}}</code>
175
+ </div>
176
+ <div>
177
+ <span class="text-gray-600">Broomバージョン:</span>
178
+ <span class="ml-2 font-mono">{{metadata.broomVersion}}</span>
179
+ </div>
180
+ <div>
181
+ <span class="text-gray-600">ホスト名:</span>
182
+ <span class="ml-2 font-mono">{{metadata.hostname}}</span>
183
+ </div>
184
+ <div>
185
+ <span class="text-gray-600">ユーザー:</span>
186
+ <span class="ml-2 font-mono">{{metadata.username}}</span>
187
+ </div>
188
+ </div>
189
+ </div>
190
+ </section>
191
+
192
+ <!-- Export Button -->
193
+ <div class="container mx-auto px-4 py-8 no-print">
194
+ <button onclick="window.print()"
195
+ class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-6 rounded-lg shadow transition">
196
+ 📄 PDFとして保存
197
+ </button>
198
+ </div>
199
+
200
+ <!-- Chart Scripts -->
201
+ <script>
202
+ // Category Pie Chart
203
+ const categoryCtx = document.getElementById('categoryChart').getContext('2d');
204
+ new Chart(categoryCtx, {
205
+ type: 'doughnut',
206
+ data: {
207
+ labels: {{{json (map categories 'name')}}},
208
+ datasets: [{
209
+ data: {{{json (map categories 'spaceFreed')}}},
210
+ backgroundColor: {{{json (map categories 'color')}}}
211
+ }]
212
+ },
213
+ options: {
214
+ responsive: true,
215
+ maintainAspectRatio: false,
216
+ plugins: {
217
+ legend: {
218
+ position: 'bottom',
219
+ labels: {
220
+ padding: 15,
221
+ font: { size: 12 }
222
+ }
223
+ },
224
+ tooltip: {
225
+ callbacks: {
226
+ label: function(context) {
227
+ const label = context.label || '';
228
+ const value = context.parsed || 0;
229
+ const size = (value / 1024 / 1024 / 1024).toFixed(2) + ' GB';
230
+ return label + ': ' + size;
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
236
+ });
237
+
238
+ // Disk Comparison Bar Chart
239
+ const diskCtx = document.getElementById('diskChart').getContext('2d');
240
+ new Chart(diskCtx, {
241
+ type: 'bar',
242
+ data: {
243
+ labels: ['削除前', '削除後'],
244
+ datasets: [
245
+ {
246
+ label: '使用中',
247
+ data: [
248
+ {{diskComparison.before.used}},
249
+ {{diskComparison.after.used}}
250
+ ],
251
+ backgroundColor: 'rgba(239, 68, 68, 0.8)'
252
+ },
253
+ {
254
+ label: '空き容量',
255
+ data: [
256
+ {{diskComparison.before.free}},
257
+ {{diskComparison.after.free}}
258
+ ],
259
+ backgroundColor: 'rgba(16, 185, 129, 0.8)'
260
+ }
261
+ ]
262
+ },
263
+ options: {
264
+ responsive: true,
265
+ maintainAspectRatio: false,
266
+ scales: {
267
+ x: { stacked: true },
268
+ y: {
269
+ stacked: true,
270
+ ticks: {
271
+ callback: function(value) {
272
+ return (value / 1024 / 1024 / 1024 / 1024).toFixed(1) + ' TB';
273
+ }
274
+ }
275
+ }
276
+ },
277
+ plugins: {
278
+ tooltip: {
279
+ callbacks: {
280
+ label: function(context) {
281
+ const label = context.dataset.label || '';
282
+ const value = context.parsed.y || 0;
283
+ const size = (value / 1024 / 1024 / 1024).toFixed(2) + ' GB';
284
+ return label + ': ' + size;
285
+ }
286
+ }
287
+ }
288
+ }
289
+ }
290
+ });
291
+
292
+ // Helper for mapping arrays
293
+ Handlebars.registerHelper('map', function(array, property) {
294
+ return array.map(item => item[property]);
295
+ });
296
+
297
+ Handlebars.registerHelper('eq', function(a, b) {
298
+ return a === b;
299
+ });
300
+ </script>
301
+
302
+ <footer class="container mx-auto px-4 py-8 text-center text-gray-600 text-sm">
303
+ <p>Generated by Broom v{{metadata.broomVersion}} - macOS Disk Cleanup Tool</p>
304
+ </footer>
305
+ </body>
306
+ </html>`;
307
+ export class ReportGenerator {
308
+ constructor() {
309
+ this.deletedFiles = [];
310
+ this.categories = new Map();
311
+ this.beforeDisk = null;
312
+ this.startTime = new Date();
313
+ }
314
+ /**
315
+ * Record disk state before cleanup
316
+ */
317
+ recordDiskBefore(disk) {
318
+ this.beforeDisk = disk;
319
+ }
320
+ /**
321
+ * Record a file deletion
322
+ */
323
+ recordDeletion(file, size, category) {
324
+ this.deletedFiles.push({
325
+ path: file,
326
+ size,
327
+ category,
328
+ deletedAt: new Date(),
329
+ });
330
+ // Update category stats
331
+ const existing = this.categories.get(category) || {
332
+ filesDeleted: 0,
333
+ spaceFreed: 0,
334
+ color: this.getCategoryColor(category),
335
+ };
336
+ existing.filesDeleted++;
337
+ existing.spaceFreed += size;
338
+ this.categories.set(category, existing);
339
+ }
340
+ /**
341
+ * Get color for category
342
+ */
343
+ getCategoryColor(category) {
344
+ const colors = {
345
+ 'User Cache': '#3B82F6',
346
+ 'Browser Cache': '#10B981',
347
+ Logs: '#F59E0B',
348
+ Trash: '#EF4444',
349
+ 'Dev Cache': '#8B5CF6',
350
+ Xcode: '#EC4899',
351
+ Installer: '#6366F1',
352
+ Downloads: '#F97316',
353
+ };
354
+ return colors[category] || '#6B7280';
355
+ }
356
+ /**
357
+ * Generate HTML report
358
+ */
359
+ async generate(outputPath, afterDisk) {
360
+ const { hostname } = await import('os');
361
+ const { execSync } = await import('child_process');
362
+ const username = execSync('whoami').toString().trim();
363
+ // Calculate summary
364
+ const totalSpaceFreed = this.deletedFiles.reduce((sum, f) => sum + f.size, 0);
365
+ const timeElapsed = Date.now() - this.startTime.getTime();
366
+ // Prepare category data
367
+ const categoryData = Array.from(this.categories.entries()).map(([name, data]) => ({
368
+ name,
369
+ filesDeleted: data.filesDeleted,
370
+ spaceFreed: data.spaceFreed,
371
+ percentage: (data.spaceFreed / totalSpaceFreed) * 100,
372
+ color: data.color,
373
+ }));
374
+ const report = {
375
+ metadata: {
376
+ generatedAt: new Date(),
377
+ broomVersion: '1.0.0',
378
+ command: process.argv.slice(2).join(' '),
379
+ hostname: hostname(),
380
+ username,
381
+ },
382
+ summary: {
383
+ totalFilesDeleted: this.deletedFiles.length,
384
+ totalSpaceFreed,
385
+ timeElapsed,
386
+ status: 'success',
387
+ errors: [],
388
+ },
389
+ categories: categoryData,
390
+ files: this.deletedFiles,
391
+ diskComparison: {
392
+ before: this.beforeDisk || afterDisk,
393
+ after: afterDisk,
394
+ },
395
+ };
396
+ // Compile template
397
+ const template = Handlebars.compile(HTML_TEMPLATE);
398
+ const html = template(report);
399
+ // Ensure directory exists
400
+ await mkdir(dirname(outputPath), { recursive: true });
401
+ // Write HTML file
402
+ await writeFile(outputPath, html, 'utf-8');
403
+ }
404
+ }
package/package.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "@tukuyomil032/broom",
3
+ "private": false,
4
+ "version": "1.0.0",
5
+ "description": "🧹 macOS Disk Cleanup CLI - Clean up caches, logs, trash, browser data, dev artifacts, and more",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "bin": {
10
+ "broom": "./dist/index.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build": "tsc -p tsconfig.json && chmod +x dist/index.js",
18
+ "prepare": "husky",
19
+ "prepublishOnly": "bun run build",
20
+ "dev": "bun run src/index.ts",
21
+ "typecheck": "tsc --noEmit",
22
+ "lint": "eslint \"src/**/*.ts\"",
23
+ "lint:fix": "eslint \"src/**/*.ts\" --fix",
24
+ "lint-staged": "lint-staged",
25
+ "format": "prettier --write \"src/**/*.ts\"",
26
+ "format:check": "prettier --check \"src/**/*.ts\""
27
+ },
28
+ "keywords": [
29
+ "cli",
30
+ "macos",
31
+ "cleanup",
32
+ "disk-space",
33
+ "cache-cleaner",
34
+ "system-maintenance",
35
+ "typescript"
36
+ ],
37
+ "author": "tukuyomil032",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/tukuyomil032/broom.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/tukuyomil032/broom/issues"
45
+ },
46
+ "homepage": "https://github.com/tukuyomil032/broom#readme",
47
+ "dependencies": {
48
+ "@inquirer/prompts": "^8.2.0",
49
+ "@types/handlebars": "^4.1.0",
50
+ "blessed": "^0.1.81",
51
+ "blessed-contrib": "^4.11.0",
52
+ "chalk": "^5.6.2",
53
+ "cli-table3": "^0.6.5",
54
+ "commander": "^14.0.2",
55
+ "fast-glob": "^3.3.3",
56
+ "handlebars": "^4.7.8",
57
+ "inquirer": "^13.2.1",
58
+ "ora": "^9.1.0",
59
+ "p-limit": "^7.2.0",
60
+ "systeminformation": "^5.30.6",
61
+ "yargs": "^18.0.0"
62
+ },
63
+ "devDependencies": {
64
+ "@eslint/json": "^0.14.0",
65
+ "@types/blessed": "^0.1.27",
66
+ "@types/node": "^25.0.10",
67
+ "@typescript-eslint/eslint-plugin": "^8.53.1",
68
+ "@typescript-eslint/parser": "^8.53.1",
69
+ "eslint": "^9.39.2",
70
+ "eslint-config-prettier": "^10.1.8",
71
+ "eslint-plugin-prettier": "^5.5.5",
72
+ "globals": "^17.1.0",
73
+ "husky": "^9.1.7",
74
+ "lint-staged": "^16.2.7",
75
+ "prettier": "^3.8.1",
76
+ "ts-node": "^10.9.2",
77
+ "typescript": "^5.9.3",
78
+ "typescript-eslint": "^8.54.0"
79
+ },
80
+ "lint-staged": {
81
+ "*.ts": [
82
+ "eslint --fix",
83
+ "prettier --write"
84
+ ]
85
+ },
86
+ "packageManager": "bun@1.3.6"
87
+ }