wtf-p 0.1.0 → 0.2.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.
package/bin/uninstall.js CHANGED
@@ -3,16 +3,20 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
- const readline = require('readline');
7
6
 
8
- // Colors
9
- const cyan = '\x1b[36m';
10
- const green = '\x1b[32m';
11
- const yellow = '\x1b[33m';
12
- const red = '\x1b[31m';
13
- const magenta = '\x1b[35m';
14
- const dim = '\x1b[2m';
15
- const reset = '\x1b[0m';
7
+ const {
8
+ expandTilde,
9
+ normalizePath,
10
+ getClaudeDir,
11
+ getPathLabel,
12
+ detectInstallation,
13
+ collectFiles,
14
+ createColors,
15
+ createOutput,
16
+ createRL,
17
+ prompt,
18
+ VERSION_FILE
19
+ } = require('./lib/utils');
16
20
 
17
21
  // Get version from package.json
18
22
  let version = 'unknown';
@@ -23,18 +27,8 @@ try {
23
27
  // Running standalone
24
28
  }
25
29
 
26
- const banner = `
27
- ${magenta}██╗ ██╗████████╗███████╗ ██████╗
28
- ██║ ██║╚══██╔══╝██╔════╝ ██╔══██╗
29
- ██║ █╗ ██║ ██║ █████╗ █████╗██████╔╝
30
- ██║███╗██║ ██║ ██╔══╝ ╚════╝██╔═══╝
31
- ╚███╔███╔╝ ██║ ██║ ██║
32
- ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝${reset}
33
-
34
- ${cyan}WTF-P Uninstaller${reset} ${dim}v${version}${reset}
35
- `;
30
+ // ============ Argument Parsing ============
36
31
 
37
- // Parse args
38
32
  const args = process.argv.slice(2);
39
33
  const hasGlobal = args.includes('--global') || args.includes('-g');
40
34
  const hasLocal = args.includes('--local') || args.includes('-l');
@@ -42,6 +36,9 @@ const hasForce = args.includes('--force') || args.includes('-f');
42
36
  const hasBackup = args.includes('--backup') || args.includes('-b');
43
37
  const hasDryRun = args.includes('--dry-run') || args.includes('-n');
44
38
  const hasCleanBackups = args.includes('--clean-backups');
39
+ const hasHelp = args.includes('--help') || args.includes('-h');
40
+ const hasNoColor = args.includes('--no-color');
41
+ const hasQuiet = args.includes('--quiet') || args.includes('-q');
45
42
 
46
43
  // Parse --config-dir argument
47
44
  function parseConfigDirArg() {
@@ -49,7 +46,7 @@ function parseConfigDirArg() {
49
46
  if (configDirIndex !== -1) {
50
47
  const nextArg = args[configDirIndex + 1];
51
48
  if (!nextArg || nextArg.startsWith('-')) {
52
- console.error(` ${yellow}--config-dir requires a path argument${reset}`);
49
+ console.error(' --config-dir requires a path argument');
53
50
  process.exit(1);
54
51
  }
55
52
  return nextArg;
@@ -60,82 +57,80 @@ function parseConfigDirArg() {
60
57
  }
61
58
  return null;
62
59
  }
60
+
63
61
  const explicitConfigDir = parseConfigDirArg();
64
- const hasHelp = args.includes('--help') || args.includes('-h');
65
62
 
66
- console.log(banner);
63
+ // Setup output helpers
64
+ const isTTY = process.stdout.isTTY && process.stdin.isTTY;
65
+ const useColors = !hasNoColor && (isTTY || process.env.FORCE_COLOR);
66
+ const out = createOutput({ quiet: hasQuiet, useColors });
67
+ const c = out.colors;
68
+
69
+ // ============ Banner ============
70
+
71
+ const banner = `
72
+ ${c.magenta('██╗ ██╗████████╗███████╗ ██████╗')}
73
+ ${c.magenta('██║ ██║╚══██╔══╝██╔════╝ ██╔══██╗')}
74
+ ${c.magenta('██║ █╗ ██║ ██║ █████╗ █████╗██████╔╝')}
75
+ ${c.magenta('██║███╗██║ ██║ ██╔══╝ ╚════╝██╔═══╝')}
76
+ ${c.magenta('╚███╔███╔╝ ██║ ██║ ██║')}
77
+ ${c.magenta(' ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝')}
78
+
79
+ ${c.cyan('WTF-P Uninstaller')} ${c.dim(`v${version}`)}
80
+ `;
81
+
82
+ if (!hasQuiet) {
83
+ console.log(banner);
84
+ }
85
+
86
+ // ============ Help Text ============
67
87
 
68
- // Show help if requested
69
88
  if (hasHelp) {
70
- console.log(` ${yellow}Usage:${reset} npx wtf-p-uninstall [options]
71
-
72
- ${yellow}Options:${reset}
73
- ${cyan}-g, --global${reset} Uninstall from Claude config directory
74
- ${cyan}-l, --local${reset} Uninstall from ./.claude in current directory
75
- ${cyan}-c, --config-dir <path>${reset} Specify custom Claude config directory
76
- ${cyan}-f, --force${reset} Skip confirmation prompts
77
- ${cyan}-b, --backup${reset} Backup files before removing
78
- ${cyan}-n, --dry-run${reset} Show what would be removed without removing
79
- ${cyan}--clean-backups${reset} Also remove .backup-* files from prior installs
80
- ${cyan}-h, --help${reset} Show this help message
81
-
82
- ${yellow}What gets removed:${reset}
83
- ${dim}commands/wtfp/${reset} - WTF-P slash commands
84
- ${dim}write-the-f-paper/${reset} - WTF-P skill and workflows
85
-
86
- ${yellow}What stays intact:${reset}
87
- ${dim}CLAUDE.md${reset} - Your personal instructions (never touched)
88
- ${dim}commands/${reset} - Other slash commands you may have
89
- ${dim}settings.json${reset} - Your Claude settings
90
- ${dim}Other directories${reset} - Any other config or plugins
91
-
92
- ${yellow}Examples:${reset}
93
- ${dim}# Interactive uninstall from ~/.claude${reset}
89
+ console.log(` ${c.yellow('Usage:')} npx wtf-p-uninstall [options]
90
+
91
+ ${c.yellow('Options:')}
92
+ ${c.cyan('-g, --global')} Uninstall from Claude config directory
93
+ ${c.cyan('-l, --local')} Uninstall from ./.claude in current directory
94
+ ${c.cyan('-c, --config-dir <path>')} Specify custom Claude config directory
95
+ ${c.cyan('-f, --force')} Skip confirmation prompts
96
+ ${c.cyan('-b, --backup')} Backup files before removing
97
+ ${c.cyan('-n, --dry-run')} Show what would be removed without removing
98
+ ${c.cyan('--clean-backups')} Also remove .backup-* files from prior installs
99
+ ${c.cyan('--no-color')} Disable colored output
100
+ ${c.cyan('-q, --quiet')} Suppress non-essential output
101
+ ${c.cyan('-h, --help')} Show this help message
102
+
103
+ ${c.yellow('What gets removed:')}
104
+ ${c.dim('commands/wtfp/')} - WTF-P slash commands
105
+ ${c.dim('write-the-f-paper/')} - WTF-P skill and workflows
106
+ ${c.dim('.wtfp-version')} - Version tracking file
107
+
108
+ ${c.yellow('What stays intact:')}
109
+ ${c.dim('CLAUDE.md')} - Your personal instructions (never touched)
110
+ ${c.dim('commands/')} - Other slash commands you may have
111
+ ${c.dim('settings.json')} - Your Claude settings
112
+ ${c.dim('Other directories')} - Any other config or plugins
113
+
114
+ ${c.yellow('Examples:')}
115
+ ${c.dim('# Interactive uninstall from ~/.claude')}
94
116
  npx wtf-p-uninstall --global
95
117
 
96
- ${dim}# Preview what would be removed${reset}
118
+ ${c.dim('# Preview what would be removed')}
97
119
  npx wtf-p-uninstall --global --dry-run
98
120
 
99
- ${dim}# Backup before removing${reset}
121
+ ${c.dim('# Backup before removing')}
100
122
  npx wtf-p-uninstall --global --backup
101
123
 
102
- ${dim}# Also clean up backup files from prior installs${reset}
124
+ ${c.dim('# Also clean up backup files from prior installs')}
103
125
  npx wtf-p-uninstall --global --clean-backups
104
126
 
105
- ${dim}# Uninstall from custom location${reset}
127
+ ${c.dim('# Uninstall from custom location')}
106
128
  npx wtf-p-uninstall --global --config-dir ~/.claude-research
107
129
  `);
108
130
  process.exit(0);
109
131
  }
110
132
 
111
- /**
112
- * Expand ~ to home directory
113
- */
114
- function expandTilde(filePath) {
115
- if (filePath && filePath.startsWith('~/')) {
116
- return path.join(os.homedir(), filePath.slice(2));
117
- }
118
- return filePath;
119
- }
120
-
121
- /**
122
- * Create readline interface for prompts
123
- */
124
- function createRL() {
125
- return readline.createInterface({
126
- input: process.stdin,
127
- output: process.stdout
128
- });
129
- }
130
-
131
- /**
132
- * Prompt user with a question
133
- */
134
- function prompt(rl, question) {
135
- return new Promise(resolve => {
136
- rl.question(question, answer => resolve(answer.trim().toLowerCase()));
137
- });
138
- }
133
+ // ============ Utilities ============
139
134
 
140
135
  /**
141
136
  * Generate backup directory path with timestamp
@@ -145,24 +140,6 @@ function getBackupDir(claudeDir) {
145
140
  return path.join(claudeDir, `.wtfp-backup-${timestamp}`);
146
141
  }
147
142
 
148
- /**
149
- * Recursively collect all files in a directory
150
- */
151
- function collectFiles(dir, files = []) {
152
- if (!fs.existsSync(dir)) return files;
153
-
154
- const entries = fs.readdirSync(dir, { withFileTypes: true });
155
- for (const entry of entries) {
156
- const fullPath = path.join(dir, entry.name);
157
- if (entry.isDirectory()) {
158
- collectFiles(fullPath, files);
159
- } else {
160
- files.push(fullPath);
161
- }
162
- }
163
- return files;
164
- }
165
-
166
143
  /**
167
144
  * Recursively remove directory
168
145
  */
@@ -198,7 +175,6 @@ function findBackupFiles(dir, backups = []) {
198
175
  for (const entry of entries) {
199
176
  const fullPath = path.join(dir, entry.name);
200
177
  if (entry.isDirectory()) {
201
- // Check for .wtfp-backup-* directories
202
178
  if (entry.name.startsWith('.wtfp-backup-')) {
203
179
  backups.push(fullPath);
204
180
  } else {
@@ -211,72 +187,43 @@ function findBackupFiles(dir, backups = []) {
211
187
  return backups;
212
188
  }
213
189
 
214
- /**
215
- * Detect what's installed
216
- */
217
- function detectInstallation(claudeDir) {
218
- const wtfpCommands = path.join(claudeDir, 'commands', 'wtfp');
219
- const wtfpSkill = path.join(claudeDir, 'write-the-f-paper');
220
-
221
- const installed = {
222
- commands: fs.existsSync(wtfpCommands) ? wtfpCommands : null,
223
- skill: fs.existsSync(wtfpSkill) ? wtfpSkill : null,
224
- commandFiles: [],
225
- skillFiles: []
226
- };
227
-
228
- if (installed.commands) {
229
- installed.commandFiles = collectFiles(installed.commands);
230
- }
231
- if (installed.skill) {
232
- installed.skillFiles = collectFiles(installed.skill);
233
- }
234
-
235
- return installed;
236
- }
190
+ // ============ Uninstall Logic ============
237
191
 
238
192
  /**
239
193
  * Uninstall from the specified directory
240
194
  */
241
195
  async function uninstall(isGlobal) {
242
- const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.CLAUDE_CONFIG_DIR);
243
- const defaultGlobalDir = configDir || path.join(os.homedir(), '.claude');
244
- const claudeDir = isGlobal
245
- ? defaultGlobalDir
246
- : path.join(process.cwd(), '.claude');
196
+ const claudeDir = getClaudeDir(explicitConfigDir, isGlobal);
197
+ const locationLabel = getPathLabel(claudeDir, isGlobal);
247
198
 
248
- const locationLabel = isGlobal
249
- ? claudeDir.replace(os.homedir(), '~')
250
- : claudeDir.replace(process.cwd(), '.');
251
-
252
- console.log(` Checking ${cyan}${locationLabel}${reset} for WTF-P installation...\n`);
199
+ out.log(` Checking ${c.cyan(locationLabel)} for WTF-P installation...\n`);
253
200
 
254
201
  // Detect installation
255
202
  const installed = detectInstallation(claudeDir);
256
203
  const totalFiles = installed.commandFiles.length + installed.skillFiles.length;
257
204
 
258
- if (!installed.commands && !installed.skill) {
259
- console.log(` ${yellow}No WTF-P installation found in ${locationLabel}${reset}\n`);
205
+ if (!installed.hasCommands && !installed.hasSkill) {
206
+ out.log(` ${c.yellow('No WTF-P installation found in ' + locationLabel)}\n`);
260
207
 
261
208
  // Check for backups if requested
262
209
  if (hasCleanBackups) {
263
210
  const backups = findBackupFiles(claudeDir);
264
211
  if (backups.length > 0) {
265
- console.log(` Found ${backups.length} backup file(s)/folder(s) to clean:\n`);
212
+ out.log(` Found ${backups.length} backup file(s)/folder(s) to clean:\n`);
266
213
  for (const b of backups.slice(0, 10)) {
267
- console.log(` ${dim}${b.replace(claudeDir, '.')}${reset}`);
214
+ out.log(` ${c.dim(b.replace(claudeDir, '.'))}`);
268
215
  }
269
216
  if (backups.length > 10) {
270
- console.log(` ${dim}... and ${backups.length - 10} more${reset}`);
217
+ out.log(` ${c.dim('... and ' + (backups.length - 10) + ' more')}`);
271
218
  }
272
- console.log();
219
+ out.log('');
273
220
 
274
221
  if (!hasDryRun && !hasForce) {
275
222
  const rl = createRL();
276
223
  const answer = await prompt(rl, ` Remove these backup files? [y/N]: `);
277
224
  rl.close();
278
225
  if (answer !== 'y' && answer !== 'yes') {
279
- console.log(`\n ${yellow}Aborted.${reset}\n`);
226
+ out.log(`\n ${c.yellow('Aborted.')}\n`);
280
227
  return;
281
228
  }
282
229
  }
@@ -288,48 +235,53 @@ async function uninstall(isGlobal) {
288
235
  } else {
289
236
  fs.unlinkSync(b);
290
237
  }
291
- console.log(` ${red}-${reset} ${dim}${b.replace(claudeDir, '.')}${reset}`);
238
+ out.log(` ${c.red('-')} ${c.dim(b.replace(claudeDir, '.'))}`);
292
239
  }
293
- console.log(`\n ${green}Cleaned ${backups.length} backup file(s).${reset}\n`);
240
+ out.log(`\n ${c.green('Cleaned ' + backups.length + ' backup file(s).')}\n`);
294
241
  } else {
295
- console.log(` ${yellow}Dry run: would remove ${backups.length} backup file(s).${reset}\n`);
242
+ out.log(` ${c.yellow('Dry run: would remove ' + backups.length + ' backup file(s).')}\n`);
296
243
  }
297
244
  } else {
298
- console.log(` No backup files found.\n`);
245
+ out.log(` No backup files found.\n`);
299
246
  }
300
247
  }
301
248
  return;
302
249
  }
303
250
 
304
251
  // Show what will be removed
305
- console.log(` ${yellow}Found WTF-P installation:${reset}\n`);
252
+ out.log(` ${c.yellow('Found WTF-P installation:')}\n`);
253
+
254
+ const commandsDir = path.join(claudeDir, 'commands', 'wtfp');
255
+ const skillDir = path.join(claudeDir, 'write-the-f-paper');
306
256
 
307
- if (installed.commands) {
308
- console.log(` ${cyan}commands/wtfp/${reset} (${installed.commandFiles.length} files)`);
257
+ if (installed.hasCommands) {
258
+ out.log(` ${c.cyan('commands/wtfp/')} (${installed.commandFiles.length} files)`);
259
+ }
260
+ if (installed.hasSkill) {
261
+ out.log(` ${c.cyan('write-the-f-paper/')} (${installed.skillFiles.length} files)`);
309
262
  }
310
- if (installed.skill) {
311
- console.log(` ${cyan}write-the-f-paper/${reset} (${installed.skillFiles.length} files)`);
263
+ if (installed.version) {
264
+ out.log(` ${c.cyan(VERSION_FILE)} (version tracking)`);
312
265
  }
313
- console.log();
266
+ out.log('');
314
267
 
315
- // Check for backups to clean (only outside wtfp directories since those get removed anyway)
268
+ // Check for backups to clean
316
269
  let backups = [];
317
270
  if (hasCleanBackups) {
318
271
  const allBackups = findBackupFiles(claudeDir);
319
- // Filter out backups inside directories we're already removing
320
272
  backups = allBackups.filter(b => {
321
- if (installed.commands && b.startsWith(installed.commands)) return false;
322
- if (installed.skill && b.startsWith(installed.skill)) return false;
273
+ if (installed.hasCommands && b.startsWith(commandsDir)) return false;
274
+ if (installed.hasSkill && b.startsWith(skillDir)) return false;
323
275
  return true;
324
276
  });
325
277
  if (backups.length > 0) {
326
- console.log(` ${dim}+ ${backups.length} backup file(s) outside wtfp dirs will also be removed${reset}\n`);
278
+ out.log(` ${c.dim('+ ' + backups.length + ' backup file(s) outside wtfp dirs will also be removed')}\n`);
327
279
  }
328
280
  }
329
281
 
330
282
  // Dry run stops here
331
283
  if (hasDryRun) {
332
- console.log(` ${yellow}Dry run: would remove ${totalFiles} file(s) in 2 directories.${reset}\n`);
284
+ out.log(` ${c.yellow('Dry run: would remove ' + totalFiles + ' file(s) in 2 directories.')}\n`);
333
285
  return;
334
286
  }
335
287
 
@@ -340,10 +292,10 @@ async function uninstall(isGlobal) {
340
292
  rl.close();
341
293
 
342
294
  if (answer !== 'y' && answer !== 'yes') {
343
- console.log(`\n ${yellow}Aborted.${reset}\n`);
295
+ out.log(`\n ${c.yellow('Aborted.')}\n`);
344
296
  return;
345
297
  }
346
- console.log();
298
+ out.log('');
347
299
  }
348
300
 
349
301
  // Backup if requested
@@ -351,38 +303,45 @@ async function uninstall(isGlobal) {
351
303
  const backupDir = getBackupDir(claudeDir);
352
304
  fs.mkdirSync(backupDir, { recursive: true });
353
305
 
354
- if (installed.commands) {
306
+ if (installed.hasCommands) {
355
307
  const dest = path.join(backupDir, 'commands', 'wtfp');
356
- copyDir(installed.commands, dest);
308
+ copyDir(commandsDir, dest);
357
309
  }
358
- if (installed.skill) {
310
+ if (installed.hasSkill) {
359
311
  const dest = path.join(backupDir, 'write-the-f-paper');
360
- copyDir(installed.skill, dest);
312
+ copyDir(skillDir, dest);
361
313
  }
362
314
 
363
315
  const backupLabel = backupDir.replace(os.homedir(), '~').replace(process.cwd(), '.');
364
- console.log(` ${cyan}↻${reset} Backed up to ${dim}${backupLabel}${reset}\n`);
316
+ out.log(` ${c.cyan('↻')} Backed up to ${c.dim(backupLabel)}\n`);
365
317
  }
366
318
 
367
319
  // Remove directories
368
- if (installed.commands) {
369
- removeDir(installed.commands);
370
- console.log(` ${red}-${reset} ${dim}commands/wtfp/${reset}`);
320
+ if (installed.hasCommands) {
321
+ removeDir(commandsDir);
322
+ out.log(` ${c.red('-')} ${c.dim('commands/wtfp/')}`);
371
323
 
372
324
  // Clean up empty commands dir
373
- const commandsDir = path.join(claudeDir, 'commands');
374
- if (fs.existsSync(commandsDir)) {
375
- const remaining = fs.readdirSync(commandsDir);
325
+ const parentCommandsDir = path.join(claudeDir, 'commands');
326
+ if (fs.existsSync(parentCommandsDir)) {
327
+ const remaining = fs.readdirSync(parentCommandsDir);
376
328
  if (remaining.length === 0) {
377
- fs.rmdirSync(commandsDir);
378
- console.log(` ${red}-${reset} ${dim}commands/${reset} ${dim}(empty)${reset}`);
329
+ fs.rmdirSync(parentCommandsDir);
330
+ out.log(` ${c.red('-')} ${c.dim('commands/')} ${c.dim('(empty)')}`);
379
331
  }
380
332
  }
381
333
  }
382
334
 
383
- if (installed.skill) {
384
- removeDir(installed.skill);
385
- console.log(` ${red}-${reset} ${dim}write-the-f-paper/${reset}`);
335
+ if (installed.hasSkill) {
336
+ removeDir(skillDir);
337
+ out.log(` ${c.red('-')} ${c.dim('write-the-f-paper/')}`);
338
+ }
339
+
340
+ // Remove version file
341
+ const versionFile = path.join(claudeDir, VERSION_FILE);
342
+ if (fs.existsSync(versionFile)) {
343
+ fs.unlinkSync(versionFile);
344
+ out.log(` ${c.red('-')} ${c.dim(VERSION_FILE)}`);
386
345
  }
387
346
 
388
347
  // Clean backups if requested
@@ -394,15 +353,15 @@ async function uninstall(isGlobal) {
394
353
  fs.unlinkSync(b);
395
354
  }
396
355
  }
397
- console.log(` ${red}-${reset} ${dim}${backups.length} backup file(s)${reset}`);
356
+ out.log(` ${c.red('-')} ${c.dim(backups.length + ' backup file(s)')}`);
398
357
  }
399
358
 
400
- console.log(`
401
- ${green}Done!${reset} WTF-P has been uninstalled.
359
+ out.log(`
360
+ ${c.green('Done!')} WTF-P has been uninstalled.
402
361
 
403
- ${dim}Your CLAUDE.md and other configs remain untouched.${reset}
362
+ ${c.dim('Your CLAUDE.md and other configs remain untouched.')}
404
363
 
405
- To reinstall: ${cyan}npx wtf-p --global${reset}
364
+ To reinstall: ${c.cyan('npx wtf-p --global')}
406
365
  `);
407
366
  }
408
367
 
@@ -412,43 +371,42 @@ async function uninstall(isGlobal) {
412
371
  async function promptLocation() {
413
372
  const rl = createRL();
414
373
 
415
- const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.CLAUDE_CONFIG_DIR);
416
- const globalPath = configDir || path.join(os.homedir(), '.claude');
417
- const globalLabel = globalPath.replace(os.homedir(), '~');
418
- const localPath = path.join(process.cwd(), '.claude');
374
+ const globalPath = getClaudeDir(explicitConfigDir, true);
375
+ const globalLabel = getPathLabel(globalPath, true);
376
+ const localPath = getClaudeDir(null, false);
419
377
 
420
378
  // Check what's installed where
421
379
  const globalInstalled = detectInstallation(globalPath);
422
380
  const localInstalled = detectInstallation(localPath);
423
381
 
424
- const hasGlobalInstall = globalInstalled.commands || globalInstalled.skill;
425
- const hasLocalInstall = localInstalled.commands || localInstalled.skill;
382
+ const hasGlobalInstall = globalInstalled.hasCommands || globalInstalled.hasSkill;
383
+ const hasLocalInstall = localInstalled.hasCommands || localInstalled.hasSkill;
426
384
 
427
385
  if (!hasGlobalInstall && !hasLocalInstall) {
428
- console.log(` ${yellow}No WTF-P installation found.${reset}\n`);
429
- console.log(` Checked:`);
430
- console.log(` ${dim}${globalLabel}${reset}`);
431
- console.log(` ${dim}./.claude${reset}\n`);
386
+ out.log(` ${c.yellow('No WTF-P installation found.')}\n`);
387
+ out.log(` Checked:`);
388
+ out.log(` ${c.dim(globalLabel)}`);
389
+ out.log(` ${c.dim('./.claude')}\n`);
432
390
  rl.close();
433
391
  return;
434
392
  }
435
393
 
436
- console.log(` ${yellow}Where would you like to uninstall from?${reset}\n`);
394
+ out.log(` ${c.yellow('Where would you like to uninstall from?')}\n`);
437
395
 
438
396
  if (hasGlobalInstall) {
439
397
  const count = globalInstalled.commandFiles.length + globalInstalled.skillFiles.length;
440
- console.log(` ${cyan}1${reset}) Global ${dim}(${globalLabel})${reset} - ${count} files`);
398
+ out.log(` ${c.cyan('1)')} Global ${c.dim('(' + globalLabel + ')')} - ${count} files`);
441
399
  } else {
442
- console.log(` ${dim}1) Global (${globalLabel}) - not installed${reset}`);
400
+ out.log(` ${c.dim('1) Global (' + globalLabel + ') - not installed')}`);
443
401
  }
444
402
 
445
403
  if (hasLocalInstall) {
446
404
  const count = localInstalled.commandFiles.length + localInstalled.skillFiles.length;
447
- console.log(` ${cyan}2${reset}) Local ${dim}(./.claude)${reset} - ${count} files`);
405
+ out.log(` ${c.cyan('2)')} Local ${c.dim('(./.claude)')} - ${count} files`);
448
406
  } else {
449
- console.log(` ${dim}2) Local (./.claude) - not installed${reset}`);
407
+ out.log(` ${c.dim('2) Local (./.claude) - not installed')}`);
450
408
  }
451
- console.log();
409
+ out.log('');
452
410
 
453
411
  const answer = await prompt(rl, ` Choice: `);
454
412
  rl.close();
@@ -458,19 +416,23 @@ async function promptLocation() {
458
416
  } else if (answer === '2' && hasLocalInstall) {
459
417
  await uninstall(false);
460
418
  } else {
461
- console.log(`\n ${yellow}Invalid choice or no installation at that location.${reset}\n`);
419
+ out.log(`\n ${c.yellow('Invalid choice or no installation at that location.')}\n`);
462
420
  }
463
421
  }
464
422
 
465
- // Main
423
+ // ============ Main ============
424
+
466
425
  async function main() {
467
426
  if (hasGlobal && hasLocal) {
468
- console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
427
+ out.error('Cannot specify both --global and --local');
469
428
  process.exit(1);
470
- } else if (explicitConfigDir && hasLocal) {
471
- console.error(` ${yellow}Cannot use --config-dir with --local${reset}`);
429
+ }
430
+ if (explicitConfigDir && hasLocal) {
431
+ out.error('Cannot use --config-dir with --local');
472
432
  process.exit(1);
473
- } else if (hasGlobal) {
433
+ }
434
+
435
+ if (hasGlobal) {
474
436
  await uninstall(true);
475
437
  } else if (hasLocal) {
476
438
  await uninstall(false);
@@ -480,6 +442,6 @@ async function main() {
480
442
  }
481
443
 
482
444
  main().catch(err => {
483
- console.error(` ${red}Error:${reset} ${err.message}`);
445
+ out.error(err.message);
484
446
  process.exit(1);
485
447
  });