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/README.md +210 -229
- package/bin/install.js +557 -158
- package/bin/lib/utils.js +318 -0
- package/bin/uninstall.js +163 -201
- package/commands/wtfp/contribute.md +278 -0
- package/commands/wtfp/help.md +25 -0
- package/commands/wtfp/report-bug.md +131 -0
- package/commands/wtfp/request-feature.md +160 -0
- package/package.json +4 -2
- package/write-the-f-paper/references/github-contrib.md +216 -0
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
71
|
-
|
|
72
|
-
${yellow
|
|
73
|
-
${cyan
|
|
74
|
-
${cyan
|
|
75
|
-
${cyan
|
|
76
|
-
${cyan
|
|
77
|
-
${cyan
|
|
78
|
-
${cyan
|
|
79
|
-
${cyan
|
|
80
|
-
${cyan
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
${dim
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
${dim}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
${dim}
|
|
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
|
|
118
|
+
${c.dim('# Preview what would be removed')}
|
|
97
119
|
npx wtf-p-uninstall --global --dry-run
|
|
98
120
|
|
|
99
|
-
${dim
|
|
121
|
+
${c.dim('# Backup before removing')}
|
|
100
122
|
npx wtf-p-uninstall --global --backup
|
|
101
123
|
|
|
102
|
-
${dim
|
|
124
|
+
${c.dim('# Also clean up backup files from prior installs')}
|
|
103
125
|
npx wtf-p-uninstall --global --clean-backups
|
|
104
126
|
|
|
105
|
-
${dim
|
|
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
|
|
243
|
-
const
|
|
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
|
-
|
|
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.
|
|
259
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
+
out.log(` ${c.dim(b.replace(claudeDir, '.'))}`);
|
|
268
215
|
}
|
|
269
216
|
if (backups.length > 10) {
|
|
270
|
-
|
|
217
|
+
out.log(` ${c.dim('... and ' + (backups.length - 10) + ' more')}`);
|
|
271
218
|
}
|
|
272
|
-
|
|
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
|
-
|
|
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
|
-
|
|
238
|
+
out.log(` ${c.red('-')} ${c.dim(b.replace(claudeDir, '.'))}`);
|
|
292
239
|
}
|
|
293
|
-
|
|
240
|
+
out.log(`\n ${c.green('Cleaned ' + backups.length + ' backup file(s).')}\n`);
|
|
294
241
|
} else {
|
|
295
|
-
|
|
242
|
+
out.log(` ${c.yellow('Dry run: would remove ' + backups.length + ' backup file(s).')}\n`);
|
|
296
243
|
}
|
|
297
244
|
} else {
|
|
298
|
-
|
|
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
|
-
|
|
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.
|
|
308
|
-
|
|
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.
|
|
311
|
-
|
|
263
|
+
if (installed.version) {
|
|
264
|
+
out.log(` ${c.cyan(VERSION_FILE)} (version tracking)`);
|
|
312
265
|
}
|
|
313
|
-
|
|
266
|
+
out.log('');
|
|
314
267
|
|
|
315
|
-
// Check for backups to clean
|
|
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.
|
|
322
|
-
if (installed.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
295
|
+
out.log(`\n ${c.yellow('Aborted.')}\n`);
|
|
344
296
|
return;
|
|
345
297
|
}
|
|
346
|
-
|
|
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.
|
|
306
|
+
if (installed.hasCommands) {
|
|
355
307
|
const dest = path.join(backupDir, 'commands', 'wtfp');
|
|
356
|
-
copyDir(
|
|
308
|
+
copyDir(commandsDir, dest);
|
|
357
309
|
}
|
|
358
|
-
if (installed.
|
|
310
|
+
if (installed.hasSkill) {
|
|
359
311
|
const dest = path.join(backupDir, 'write-the-f-paper');
|
|
360
|
-
copyDir(
|
|
312
|
+
copyDir(skillDir, dest);
|
|
361
313
|
}
|
|
362
314
|
|
|
363
315
|
const backupLabel = backupDir.replace(os.homedir(), '~').replace(process.cwd(), '.');
|
|
364
|
-
|
|
316
|
+
out.log(` ${c.cyan('↻')} Backed up to ${c.dim(backupLabel)}\n`);
|
|
365
317
|
}
|
|
366
318
|
|
|
367
319
|
// Remove directories
|
|
368
|
-
if (installed.
|
|
369
|
-
removeDir(
|
|
370
|
-
|
|
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
|
|
374
|
-
if (fs.existsSync(
|
|
375
|
-
const remaining = fs.readdirSync(
|
|
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(
|
|
378
|
-
|
|
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.
|
|
384
|
-
removeDir(
|
|
385
|
-
|
|
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
|
-
|
|
356
|
+
out.log(` ${c.red('-')} ${c.dim(backups.length + ' backup file(s)')}`);
|
|
398
357
|
}
|
|
399
358
|
|
|
400
|
-
|
|
401
|
-
${green
|
|
359
|
+
out.log(`
|
|
360
|
+
${c.green('Done!')} WTF-P has been uninstalled.
|
|
402
361
|
|
|
403
|
-
${dim
|
|
362
|
+
${c.dim('Your CLAUDE.md and other configs remain untouched.')}
|
|
404
363
|
|
|
405
|
-
To reinstall: ${cyan
|
|
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
|
|
416
|
-
const
|
|
417
|
-
const
|
|
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.
|
|
425
|
-
const hasLocalInstall = localInstalled.
|
|
382
|
+
const hasGlobalInstall = globalInstalled.hasCommands || globalInstalled.hasSkill;
|
|
383
|
+
const hasLocalInstall = localInstalled.hasCommands || localInstalled.hasSkill;
|
|
426
384
|
|
|
427
385
|
if (!hasGlobalInstall && !hasLocalInstall) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
-
|
|
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
|
-
|
|
398
|
+
out.log(` ${c.cyan('1)')} Global ${c.dim('(' + globalLabel + ')')} - ${count} files`);
|
|
441
399
|
} else {
|
|
442
|
-
|
|
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
|
-
|
|
405
|
+
out.log(` ${c.cyan('2)')} Local ${c.dim('(./.claude)')} - ${count} files`);
|
|
448
406
|
} else {
|
|
449
|
-
|
|
407
|
+
out.log(` ${c.dim('2) Local (./.claude) - not installed')}`);
|
|
450
408
|
}
|
|
451
|
-
|
|
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
|
-
|
|
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
|
-
|
|
427
|
+
out.error('Cannot specify both --global and --local');
|
|
469
428
|
process.exit(1);
|
|
470
|
-
}
|
|
471
|
-
|
|
429
|
+
}
|
|
430
|
+
if (explicitConfigDir && hasLocal) {
|
|
431
|
+
out.error('Cannot use --config-dir with --local');
|
|
472
432
|
process.exit(1);
|
|
473
|
-
}
|
|
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
|
-
|
|
445
|
+
out.error(err.message);
|
|
484
446
|
process.exit(1);
|
|
485
447
|
});
|