@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,249 @@
1
+ /**
2
+ * Schedule command - Automated cleanup scheduling
3
+ */
4
+ import chalk from 'chalk';
5
+ import { Command } from 'commander';
6
+ import { enhanceCommandHelp } from '../utils/help.js';
7
+ import { expandPath, exists } from '../utils/fs.js';
8
+ import { printHeader, success, error, warning } from '../ui/output.js';
9
+ import { writeFile, readFile, unlink } from 'fs/promises';
10
+ import { execSync } from 'child_process';
11
+ const LAUNCH_AGENT_PATH = expandPath('~/Library/LaunchAgents/com.broom.cleanup.plist');
12
+ /**
13
+ * Days of week mapping
14
+ */
15
+ const WEEKDAYS = {
16
+ sunday: 0,
17
+ monday: 1,
18
+ tuesday: 2,
19
+ wednesday: 3,
20
+ thursday: 4,
21
+ friday: 5,
22
+ saturday: 6,
23
+ };
24
+ /**
25
+ * Generate LaunchAgent plist content
26
+ */
27
+ function generateLaunchAgent(config) {
28
+ const [hour, minute] = config.time.split(':').map(Number);
29
+ const broomPath = execSync('which broom').toString().trim() || '/usr/local/bin/broom';
30
+ const args = ['<string>clean</string>', '<string>--yes</string>', '<string>--safe</string>'];
31
+ if (config.scanners && config.scanners.length > 0) {
32
+ args.push(`<string>--scanners</string>`, `<string>${config.scanners.join(',')}</string>`);
33
+ }
34
+ let calendarInterval = '';
35
+ if (config.interval === 'daily') {
36
+ calendarInterval = `
37
+ <key>StartCalendarInterval</key>
38
+ <dict>
39
+ <key>Hour</key>
40
+ <integer>${hour}</integer>
41
+ <key>Minute</key>
42
+ <integer>${minute}</integer>
43
+ </dict>`;
44
+ }
45
+ else if (config.interval === 'weekly' && config.weekday !== undefined) {
46
+ calendarInterval = `
47
+ <key>StartCalendarInterval</key>
48
+ <dict>
49
+ <key>Weekday</key>
50
+ <integer>${config.weekday}</integer>
51
+ <key>Hour</key>
52
+ <integer>${hour}</integer>
53
+ <key>Minute</key>
54
+ <integer>${minute}</integer>
55
+ </dict>`;
56
+ }
57
+ else if (config.interval === 'monthly') {
58
+ calendarInterval = `
59
+ <key>StartCalendarInterval</key>
60
+ <dict>
61
+ <key>Day</key>
62
+ <integer>1</integer>
63
+ <key>Hour</key>
64
+ <integer>${hour}</integer>
65
+ <key>Minute</key>
66
+ <integer>${minute}</integer>
67
+ </dict>`;
68
+ }
69
+ return `<?xml version="1.0" encoding="UTF-8"?>
70
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
71
+ <plist version="1.0">
72
+ <dict>
73
+ <key>Label</key>
74
+ <string>com.broom.cleanup</string>
75
+
76
+ <key>ProgramArguments</key>
77
+ <array>
78
+ <string>${broomPath}</string>
79
+ ${args.join('\n ')}
80
+ </array>
81
+
82
+ ${calendarInterval}
83
+
84
+ <key>StandardOutPath</key>
85
+ <string>${expandPath('~/.config/broom/schedule.log')}</string>
86
+
87
+ <key>StandardErrorPath</key>
88
+ <string>${expandPath('~/.config/broom/schedule-error.log')}</string>
89
+ </dict>
90
+ </plist>`;
91
+ }
92
+ /**
93
+ * Install schedule
94
+ */
95
+ async function installSchedule(options) {
96
+ const time = options.time || '09:00';
97
+ let interval = 'weekly';
98
+ let weekday;
99
+ if (options.daily) {
100
+ interval = 'daily';
101
+ }
102
+ else if (options.monthly) {
103
+ interval = 'monthly';
104
+ }
105
+ else if (options.weekly) {
106
+ interval = 'weekly';
107
+ const dayStr = (options.day || 'monday').toLowerCase();
108
+ weekday = WEEKDAYS[dayStr];
109
+ if (weekday === undefined) {
110
+ error(`Invalid day: ${options.day}. Use: ${Object.keys(WEEKDAYS).join(', ')}`);
111
+ return;
112
+ }
113
+ }
114
+ const scanners = options.scanners?.split(',');
115
+ printHeader('⏰ Schedule Cleanup');
116
+ console.log(chalk.bold('Configuration:'));
117
+ console.log(` Interval: ${chalk.cyan(interval)}`);
118
+ if (interval === 'weekly' && weekday !== undefined) {
119
+ const dayName = Object.keys(WEEKDAYS).find((k) => WEEKDAYS[k] === weekday);
120
+ console.log(` Day: ${chalk.cyan(dayName)}`);
121
+ }
122
+ console.log(` Time: ${chalk.cyan(time)}`);
123
+ if (scanners) {
124
+ console.log(` Scanners: ${chalk.cyan(scanners.join(', '))}`);
125
+ }
126
+ else {
127
+ console.log(` Scanners: ${chalk.cyan('all safe scanners')}`);
128
+ }
129
+ console.log();
130
+ // Check if already scheduled
131
+ if (exists(LAUNCH_AGENT_PATH)) {
132
+ warning('A schedule already exists. Remove it first with --remove');
133
+ return;
134
+ }
135
+ // Generate plist
136
+ const plist = generateLaunchAgent({ interval, time, weekday, scanners });
137
+ // Write plist
138
+ await writeFile(LAUNCH_AGENT_PATH, plist, 'utf-8');
139
+ // Load with launchctl
140
+ try {
141
+ execSync(`launchctl load "${LAUNCH_AGENT_PATH}"`);
142
+ success('Schedule installed successfully!');
143
+ console.log();
144
+ console.log(chalk.dim('Cleanup will run automatically according to the schedule.'));
145
+ console.log(chalk.dim(`Logs: ~/.config/broom/schedule.log`));
146
+ }
147
+ catch (err) {
148
+ error(`Failed to load schedule: ${err}`);
149
+ await unlink(LAUNCH_AGENT_PATH);
150
+ }
151
+ }
152
+ /**
153
+ * Remove schedule
154
+ */
155
+ async function removeSchedule() {
156
+ printHeader('⏰ Remove Schedule');
157
+ if (!exists(LAUNCH_AGENT_PATH)) {
158
+ warning('No schedule found');
159
+ return;
160
+ }
161
+ try {
162
+ // Unload from launchctl
163
+ execSync(`launchctl unload "${LAUNCH_AGENT_PATH}"`);
164
+ // Remove plist
165
+ await unlink(LAUNCH_AGENT_PATH);
166
+ success('Schedule removed successfully');
167
+ }
168
+ catch (err) {
169
+ error(`Failed to remove schedule: ${err}`);
170
+ }
171
+ }
172
+ /**
173
+ * List schedules
174
+ */
175
+ async function listSchedule() {
176
+ printHeader('⏰ Scheduled Cleanups');
177
+ if (!exists(LAUNCH_AGENT_PATH)) {
178
+ warning('No schedule configured');
179
+ console.log();
180
+ console.log(chalk.dim('Use "broom schedule --weekly --time 09:00" to create a schedule'));
181
+ return;
182
+ }
183
+ try {
184
+ const content = await readFile(LAUNCH_AGENT_PATH, 'utf-8');
185
+ // Parse basic info from plist
186
+ const timeMatch = content.match(/<key>Hour<\/key>\s*<integer>(\d+)<\/integer>/);
187
+ const minuteMatch = content.match(/<key>Minute<\/key>\s*<integer>(\d+)<\/integer>/);
188
+ const weekdayMatch = content.match(/<key>Weekday<\/key>\s*<integer>(\d+)<\/integer>/);
189
+ const dayMatch = content.match(/<key>Day<\/key>\s*<integer>(\d+)<\/integer>/);
190
+ console.log(chalk.bold('Active Schedule:'));
191
+ if (dayMatch) {
192
+ console.log(` Interval: ${chalk.cyan('Monthly')}`);
193
+ console.log(` Day: ${chalk.cyan('1st of month')}`);
194
+ }
195
+ else if (weekdayMatch) {
196
+ const weekday = parseInt(weekdayMatch[1]);
197
+ const dayName = Object.keys(WEEKDAYS).find((k) => WEEKDAYS[k] === weekday) || 'Unknown';
198
+ console.log(` Interval: ${chalk.cyan('Weekly')}`);
199
+ console.log(` Day: ${chalk.cyan(dayName)}`);
200
+ }
201
+ else {
202
+ console.log(` Interval: ${chalk.cyan('Daily')}`);
203
+ }
204
+ if (timeMatch && minuteMatch) {
205
+ const hour = timeMatch[1].padStart(2, '0');
206
+ const minute = minuteMatch[1].padStart(2, '0');
207
+ console.log(` Time: ${chalk.cyan(`${hour}:${minute}`)}`);
208
+ }
209
+ console.log();
210
+ console.log(chalk.dim(`Config: ${LAUNCH_AGENT_PATH}`));
211
+ console.log(chalk.dim('Use "broom schedule --remove" to uninstall'));
212
+ }
213
+ catch (err) {
214
+ error(`Failed to read schedule: ${err}`);
215
+ }
216
+ }
217
+ /**
218
+ * Execute schedule command
219
+ */
220
+ export async function scheduleCommand(options) {
221
+ if (options.list || (!options.daily && !options.weekly && !options.monthly && !options.remove)) {
222
+ await listSchedule();
223
+ return;
224
+ }
225
+ if (options.remove) {
226
+ await removeSchedule();
227
+ return;
228
+ }
229
+ await installSchedule(options);
230
+ }
231
+ /**
232
+ * Create schedule command
233
+ */
234
+ export function createScheduleCommand() {
235
+ const cmd = new Command('schedule')
236
+ .description('Schedule automated cleanups')
237
+ .option('-l, --list', 'Show current schedule')
238
+ .option('-r, --remove', 'Remove scheduled cleanup')
239
+ .option('--daily', 'Schedule daily cleanup')
240
+ .option('--weekly', 'Schedule weekly cleanup')
241
+ .option('--monthly', 'Schedule monthly cleanup')
242
+ .option('--day <day>', 'Day of week for weekly (default: monday)')
243
+ .option('--time <time>', 'Time to run (HH:MM format, default: 09:00)')
244
+ .option('--scanners <list>', 'Comma-separated list of scanners to run')
245
+ .action(async (options) => {
246
+ await scheduleCommand(options);
247
+ });
248
+ return enhanceCommandHelp(cmd);
249
+ }