wtf-p 0.1.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/LICENSE +21 -0
- package/README.md +338 -0
- package/bin/install.js +358 -0
- package/bin/uninstall.js +485 -0
- package/commands/wtfp/analyze-bib.md +326 -0
- package/commands/wtfp/check-refs.md +205 -0
- package/commands/wtfp/create-outline.md +275 -0
- package/commands/wtfp/discuss-section.md +71 -0
- package/commands/wtfp/export-latex.md +216 -0
- package/commands/wtfp/help.md +342 -0
- package/commands/wtfp/insert-section.md +130 -0
- package/commands/wtfp/list-assumptions.md +109 -0
- package/commands/wtfp/map-project.md +197 -0
- package/commands/wtfp/new-paper.md +443 -0
- package/commands/wtfp/pause-writing.md +145 -0
- package/commands/wtfp/plan-revision.md +180 -0
- package/commands/wtfp/plan-section.md +247 -0
- package/commands/wtfp/polish-prose.md +177 -0
- package/commands/wtfp/progress.md +325 -0
- package/commands/wtfp/remove-section.md +147 -0
- package/commands/wtfp/research-gap.md +235 -0
- package/commands/wtfp/resume-writing.md +142 -0
- package/commands/wtfp/review-section.md +409 -0
- package/commands/wtfp/submit-milestone.md +211 -0
- package/commands/wtfp/write-section.md +273 -0
- package/package.json +40 -0
- package/write-the-f-paper/references/checkpoints.md +213 -0
- package/write-the-f-paper/references/continuation-format.md +223 -0
- package/write-the-f-paper/references/git-integration.md +241 -0
- package/write-the-f-paper/references/imrad-structure.md +212 -0
- package/write-the-f-paper/references/length-estimation.md +148 -0
- package/write-the-f-paper/references/plan-format.md +361 -0
- package/write-the-f-paper/references/principles.md +158 -0
- package/write-the-f-paper/references/questioning.md +132 -0
- package/write-the-f-paper/references/research-pitfalls.md +208 -0
- package/write-the-f-paper/templates/config.json +30 -0
- package/write-the-f-paper/templates/context.md +179 -0
- package/write-the-f-paper/templates/continue-here.md +142 -0
- package/write-the-f-paper/templates/data-inventory.md +142 -0
- package/write-the-f-paper/templates/discovery.md +146 -0
- package/write-the-f-paper/templates/issues.md +59 -0
- package/write-the-f-paper/templates/manuscript.md +253 -0
- package/write-the-f-paper/templates/narrative-arc.md +227 -0
- package/write-the-f-paper/templates/outline.md +274 -0
- package/write-the-f-paper/templates/phase-prompt.md +348 -0
- package/write-the-f-paper/templates/prior-drafts.md +204 -0
- package/write-the-f-paper/templates/project-context/argument-structure.md +267 -0
- package/write-the-f-paper/templates/project-context/bibliography.md +152 -0
- package/write-the-f-paper/templates/project-context/gaps.md +122 -0
- package/write-the-f-paper/templates/project-context/imrad.md +140 -0
- package/write-the-f-paper/templates/project-context/peer-review.md +152 -0
- package/write-the-f-paper/templates/project-context/style-guide.md +198 -0
- package/write-the-f-paper/templates/project-context/tools.md +170 -0
- package/write-the-f-paper/templates/project.md +185 -0
- package/write-the-f-paper/templates/research.md +384 -0
- package/write-the-f-paper/templates/review-comments.md +253 -0
- package/write-the-f-paper/templates/revision-issues.md +169 -0
- package/write-the-f-paper/templates/roadmap.md +187 -0
- package/write-the-f-paper/templates/section-archive.md +178 -0
- package/write-the-f-paper/templates/section-context.md +114 -0
- package/write-the-f-paper/templates/section.md +128 -0
- package/write-the-f-paper/templates/state.md +244 -0
- package/write-the-f-paper/templates/summary.md +277 -0
- package/write-the-f-paper/venues/acm-cs.yaml +102 -0
- package/write-the-f-paper/venues/arxiv-ml.yaml +103 -0
- package/write-the-f-paper/venues/ieee-cs.yaml +100 -0
- package/write-the-f-paper/venues/nature.yaml +81 -0
- package/write-the-f-paper/venues/thesis-chapter.yaml +101 -0
- package/write-the-f-paper/workflows/create-outline.md +462 -0
- package/write-the-f-paper/workflows/create-outline.wcn.md +194 -0
- package/write-the-f-paper/workflows/discuss-section.md +249 -0
- package/write-the-f-paper/workflows/discuss-section.wcn.md +63 -0
- package/write-the-f-paper/workflows/execute-section.md +704 -0
- package/write-the-f-paper/workflows/execute-section.wcn.md +219 -0
- package/write-the-f-paper/workflows/list-assumptions.md +183 -0
- package/write-the-f-paper/workflows/list-assumptions.wcn.md +42 -0
- package/write-the-f-paper/workflows/lit-review-phase.md +281 -0
- package/write-the-f-paper/workflows/lit-review-phase.wcn.md +92 -0
- package/write-the-f-paper/workflows/map-project.md +359 -0
- package/write-the-f-paper/workflows/map-project.wcn.md +73 -0
- package/write-the-f-paper/workflows/plan-section.md +409 -0
- package/write-the-f-paper/workflows/plan-section.wcn.md +236 -0
- package/write-the-f-paper/workflows/research-gap.md +469 -0
- package/write-the-f-paper/workflows/research-gap.wcn.md +119 -0
- package/write-the-f-paper/workflows/resume-paper.md +274 -0
- package/write-the-f-paper/workflows/resume-paper.wcn.md +93 -0
- package/write-the-f-paper/workflows/review-section.md +329 -0
- package/write-the-f-paper/workflows/review-section.wcn.md +91 -0
- package/write-the-f-paper/workflows/submit-draft.md +368 -0
- package/write-the-f-paper/workflows/submit-draft.wcn.md +69 -0
- package/write-the-f-paper/workflows/transition.md +375 -0
- package/write-the-f-paper/workflows/transition.wcn.md +113 -0
package/bin/uninstall.js
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
|
|
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';
|
|
16
|
+
|
|
17
|
+
// Get version from package.json
|
|
18
|
+
let version = 'unknown';
|
|
19
|
+
try {
|
|
20
|
+
const pkg = require('../package.json');
|
|
21
|
+
version = pkg.version;
|
|
22
|
+
} catch {
|
|
23
|
+
// Running standalone
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const banner = `
|
|
27
|
+
${magenta}██╗ ██╗████████╗███████╗ ██████╗
|
|
28
|
+
██║ ██║╚══██╔══╝██╔════╝ ██╔══██╗
|
|
29
|
+
██║ █╗ ██║ ██║ █████╗ █████╗██████╔╝
|
|
30
|
+
██║███╗██║ ██║ ██╔══╝ ╚════╝██╔═══╝
|
|
31
|
+
╚███╔███╔╝ ██║ ██║ ██║
|
|
32
|
+
╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝${reset}
|
|
33
|
+
|
|
34
|
+
${cyan}WTF-P Uninstaller${reset} ${dim}v${version}${reset}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
// Parse args
|
|
38
|
+
const args = process.argv.slice(2);
|
|
39
|
+
const hasGlobal = args.includes('--global') || args.includes('-g');
|
|
40
|
+
const hasLocal = args.includes('--local') || args.includes('-l');
|
|
41
|
+
const hasForce = args.includes('--force') || args.includes('-f');
|
|
42
|
+
const hasBackup = args.includes('--backup') || args.includes('-b');
|
|
43
|
+
const hasDryRun = args.includes('--dry-run') || args.includes('-n');
|
|
44
|
+
const hasCleanBackups = args.includes('--clean-backups');
|
|
45
|
+
|
|
46
|
+
// Parse --config-dir argument
|
|
47
|
+
function parseConfigDirArg() {
|
|
48
|
+
const configDirIndex = args.findIndex(arg => arg === '--config-dir' || arg === '-c');
|
|
49
|
+
if (configDirIndex !== -1) {
|
|
50
|
+
const nextArg = args[configDirIndex + 1];
|
|
51
|
+
if (!nextArg || nextArg.startsWith('-')) {
|
|
52
|
+
console.error(` ${yellow}--config-dir requires a path argument${reset}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
return nextArg;
|
|
56
|
+
}
|
|
57
|
+
const configDirArg = args.find(arg => arg.startsWith('--config-dir=') || arg.startsWith('-c='));
|
|
58
|
+
if (configDirArg) {
|
|
59
|
+
return configDirArg.split('=')[1];
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const explicitConfigDir = parseConfigDirArg();
|
|
64
|
+
const hasHelp = args.includes('--help') || args.includes('-h');
|
|
65
|
+
|
|
66
|
+
console.log(banner);
|
|
67
|
+
|
|
68
|
+
// Show help if requested
|
|
69
|
+
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}
|
|
94
|
+
npx wtf-p-uninstall --global
|
|
95
|
+
|
|
96
|
+
${dim}# Preview what would be removed${reset}
|
|
97
|
+
npx wtf-p-uninstall --global --dry-run
|
|
98
|
+
|
|
99
|
+
${dim}# Backup before removing${reset}
|
|
100
|
+
npx wtf-p-uninstall --global --backup
|
|
101
|
+
|
|
102
|
+
${dim}# Also clean up backup files from prior installs${reset}
|
|
103
|
+
npx wtf-p-uninstall --global --clean-backups
|
|
104
|
+
|
|
105
|
+
${dim}# Uninstall from custom location${reset}
|
|
106
|
+
npx wtf-p-uninstall --global --config-dir ~/.claude-research
|
|
107
|
+
`);
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
|
|
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
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Generate backup directory path with timestamp
|
|
142
|
+
*/
|
|
143
|
+
function getBackupDir(claudeDir) {
|
|
144
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
145
|
+
return path.join(claudeDir, `.wtfp-backup-${timestamp}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
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
|
+
/**
|
|
167
|
+
* Recursively remove directory
|
|
168
|
+
*/
|
|
169
|
+
function removeDir(dir) {
|
|
170
|
+
if (!fs.existsSync(dir)) return;
|
|
171
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Copy directory recursively
|
|
176
|
+
*/
|
|
177
|
+
function copyDir(src, dest) {
|
|
178
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
179
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
180
|
+
for (const entry of entries) {
|
|
181
|
+
const srcPath = path.join(src, entry.name);
|
|
182
|
+
const destPath = path.join(dest, entry.name);
|
|
183
|
+
if (entry.isDirectory()) {
|
|
184
|
+
copyDir(srcPath, destPath);
|
|
185
|
+
} else {
|
|
186
|
+
fs.copyFileSync(srcPath, destPath);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Find backup files in a directory
|
|
193
|
+
*/
|
|
194
|
+
function findBackupFiles(dir, backups = []) {
|
|
195
|
+
if (!fs.existsSync(dir)) return backups;
|
|
196
|
+
|
|
197
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
198
|
+
for (const entry of entries) {
|
|
199
|
+
const fullPath = path.join(dir, entry.name);
|
|
200
|
+
if (entry.isDirectory()) {
|
|
201
|
+
// Check for .wtfp-backup-* directories
|
|
202
|
+
if (entry.name.startsWith('.wtfp-backup-')) {
|
|
203
|
+
backups.push(fullPath);
|
|
204
|
+
} else {
|
|
205
|
+
findBackupFiles(fullPath, backups);
|
|
206
|
+
}
|
|
207
|
+
} else if (entry.name.includes('.backup-')) {
|
|
208
|
+
backups.push(fullPath);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return backups;
|
|
212
|
+
}
|
|
213
|
+
|
|
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
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Uninstall from the specified directory
|
|
240
|
+
*/
|
|
241
|
+
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');
|
|
247
|
+
|
|
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`);
|
|
253
|
+
|
|
254
|
+
// Detect installation
|
|
255
|
+
const installed = detectInstallation(claudeDir);
|
|
256
|
+
const totalFiles = installed.commandFiles.length + installed.skillFiles.length;
|
|
257
|
+
|
|
258
|
+
if (!installed.commands && !installed.skill) {
|
|
259
|
+
console.log(` ${yellow}No WTF-P installation found in ${locationLabel}${reset}\n`);
|
|
260
|
+
|
|
261
|
+
// Check for backups if requested
|
|
262
|
+
if (hasCleanBackups) {
|
|
263
|
+
const backups = findBackupFiles(claudeDir);
|
|
264
|
+
if (backups.length > 0) {
|
|
265
|
+
console.log(` Found ${backups.length} backup file(s)/folder(s) to clean:\n`);
|
|
266
|
+
for (const b of backups.slice(0, 10)) {
|
|
267
|
+
console.log(` ${dim}${b.replace(claudeDir, '.')}${reset}`);
|
|
268
|
+
}
|
|
269
|
+
if (backups.length > 10) {
|
|
270
|
+
console.log(` ${dim}... and ${backups.length - 10} more${reset}`);
|
|
271
|
+
}
|
|
272
|
+
console.log();
|
|
273
|
+
|
|
274
|
+
if (!hasDryRun && !hasForce) {
|
|
275
|
+
const rl = createRL();
|
|
276
|
+
const answer = await prompt(rl, ` Remove these backup files? [y/N]: `);
|
|
277
|
+
rl.close();
|
|
278
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
279
|
+
console.log(`\n ${yellow}Aborted.${reset}\n`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (!hasDryRun) {
|
|
285
|
+
for (const b of backups) {
|
|
286
|
+
if (fs.statSync(b).isDirectory()) {
|
|
287
|
+
removeDir(b);
|
|
288
|
+
} else {
|
|
289
|
+
fs.unlinkSync(b);
|
|
290
|
+
}
|
|
291
|
+
console.log(` ${red}-${reset} ${dim}${b.replace(claudeDir, '.')}${reset}`);
|
|
292
|
+
}
|
|
293
|
+
console.log(`\n ${green}Cleaned ${backups.length} backup file(s).${reset}\n`);
|
|
294
|
+
} else {
|
|
295
|
+
console.log(` ${yellow}Dry run: would remove ${backups.length} backup file(s).${reset}\n`);
|
|
296
|
+
}
|
|
297
|
+
} else {
|
|
298
|
+
console.log(` No backup files found.\n`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Show what will be removed
|
|
305
|
+
console.log(` ${yellow}Found WTF-P installation:${reset}\n`);
|
|
306
|
+
|
|
307
|
+
if (installed.commands) {
|
|
308
|
+
console.log(` ${cyan}commands/wtfp/${reset} (${installed.commandFiles.length} files)`);
|
|
309
|
+
}
|
|
310
|
+
if (installed.skill) {
|
|
311
|
+
console.log(` ${cyan}write-the-f-paper/${reset} (${installed.skillFiles.length} files)`);
|
|
312
|
+
}
|
|
313
|
+
console.log();
|
|
314
|
+
|
|
315
|
+
// Check for backups to clean (only outside wtfp directories since those get removed anyway)
|
|
316
|
+
let backups = [];
|
|
317
|
+
if (hasCleanBackups) {
|
|
318
|
+
const allBackups = findBackupFiles(claudeDir);
|
|
319
|
+
// Filter out backups inside directories we're already removing
|
|
320
|
+
backups = allBackups.filter(b => {
|
|
321
|
+
if (installed.commands && b.startsWith(installed.commands)) return false;
|
|
322
|
+
if (installed.skill && b.startsWith(installed.skill)) return false;
|
|
323
|
+
return true;
|
|
324
|
+
});
|
|
325
|
+
if (backups.length > 0) {
|
|
326
|
+
console.log(` ${dim}+ ${backups.length} backup file(s) outside wtfp dirs will also be removed${reset}\n`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Dry run stops here
|
|
331
|
+
if (hasDryRun) {
|
|
332
|
+
console.log(` ${yellow}Dry run: would remove ${totalFiles} file(s) in 2 directories.${reset}\n`);
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Confirm unless --force
|
|
337
|
+
if (!hasForce) {
|
|
338
|
+
const rl = createRL();
|
|
339
|
+
const answer = await prompt(rl, ` Remove ${totalFiles} file(s)? [y/N]: `);
|
|
340
|
+
rl.close();
|
|
341
|
+
|
|
342
|
+
if (answer !== 'y' && answer !== 'yes') {
|
|
343
|
+
console.log(`\n ${yellow}Aborted.${reset}\n`);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
console.log();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Backup if requested
|
|
350
|
+
if (hasBackup) {
|
|
351
|
+
const backupDir = getBackupDir(claudeDir);
|
|
352
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
353
|
+
|
|
354
|
+
if (installed.commands) {
|
|
355
|
+
const dest = path.join(backupDir, 'commands', 'wtfp');
|
|
356
|
+
copyDir(installed.commands, dest);
|
|
357
|
+
}
|
|
358
|
+
if (installed.skill) {
|
|
359
|
+
const dest = path.join(backupDir, 'write-the-f-paper');
|
|
360
|
+
copyDir(installed.skill, dest);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const backupLabel = backupDir.replace(os.homedir(), '~').replace(process.cwd(), '.');
|
|
364
|
+
console.log(` ${cyan}↻${reset} Backed up to ${dim}${backupLabel}${reset}\n`);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Remove directories
|
|
368
|
+
if (installed.commands) {
|
|
369
|
+
removeDir(installed.commands);
|
|
370
|
+
console.log(` ${red}-${reset} ${dim}commands/wtfp/${reset}`);
|
|
371
|
+
|
|
372
|
+
// Clean up empty commands dir
|
|
373
|
+
const commandsDir = path.join(claudeDir, 'commands');
|
|
374
|
+
if (fs.existsSync(commandsDir)) {
|
|
375
|
+
const remaining = fs.readdirSync(commandsDir);
|
|
376
|
+
if (remaining.length === 0) {
|
|
377
|
+
fs.rmdirSync(commandsDir);
|
|
378
|
+
console.log(` ${red}-${reset} ${dim}commands/${reset} ${dim}(empty)${reset}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (installed.skill) {
|
|
384
|
+
removeDir(installed.skill);
|
|
385
|
+
console.log(` ${red}-${reset} ${dim}write-the-f-paper/${reset}`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Clean backups if requested
|
|
389
|
+
if (hasCleanBackups && backups.length > 0) {
|
|
390
|
+
for (const b of backups) {
|
|
391
|
+
if (fs.statSync(b).isDirectory()) {
|
|
392
|
+
removeDir(b);
|
|
393
|
+
} else {
|
|
394
|
+
fs.unlinkSync(b);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
console.log(` ${red}-${reset} ${dim}${backups.length} backup file(s)${reset}`);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
console.log(`
|
|
401
|
+
${green}Done!${reset} WTF-P has been uninstalled.
|
|
402
|
+
|
|
403
|
+
${dim}Your CLAUDE.md and other configs remain untouched.${reset}
|
|
404
|
+
|
|
405
|
+
To reinstall: ${cyan}npx wtf-p --global${reset}
|
|
406
|
+
`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Prompt for uninstall location
|
|
411
|
+
*/
|
|
412
|
+
async function promptLocation() {
|
|
413
|
+
const rl = createRL();
|
|
414
|
+
|
|
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');
|
|
419
|
+
|
|
420
|
+
// Check what's installed where
|
|
421
|
+
const globalInstalled = detectInstallation(globalPath);
|
|
422
|
+
const localInstalled = detectInstallation(localPath);
|
|
423
|
+
|
|
424
|
+
const hasGlobalInstall = globalInstalled.commands || globalInstalled.skill;
|
|
425
|
+
const hasLocalInstall = localInstalled.commands || localInstalled.skill;
|
|
426
|
+
|
|
427
|
+
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`);
|
|
432
|
+
rl.close();
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
console.log(` ${yellow}Where would you like to uninstall from?${reset}\n`);
|
|
437
|
+
|
|
438
|
+
if (hasGlobalInstall) {
|
|
439
|
+
const count = globalInstalled.commandFiles.length + globalInstalled.skillFiles.length;
|
|
440
|
+
console.log(` ${cyan}1${reset}) Global ${dim}(${globalLabel})${reset} - ${count} files`);
|
|
441
|
+
} else {
|
|
442
|
+
console.log(` ${dim}1) Global (${globalLabel}) - not installed${reset}`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (hasLocalInstall) {
|
|
446
|
+
const count = localInstalled.commandFiles.length + localInstalled.skillFiles.length;
|
|
447
|
+
console.log(` ${cyan}2${reset}) Local ${dim}(./.claude)${reset} - ${count} files`);
|
|
448
|
+
} else {
|
|
449
|
+
console.log(` ${dim}2) Local (./.claude) - not installed${reset}`);
|
|
450
|
+
}
|
|
451
|
+
console.log();
|
|
452
|
+
|
|
453
|
+
const answer = await prompt(rl, ` Choice: `);
|
|
454
|
+
rl.close();
|
|
455
|
+
|
|
456
|
+
if (answer === '1' && hasGlobalInstall) {
|
|
457
|
+
await uninstall(true);
|
|
458
|
+
} else if (answer === '2' && hasLocalInstall) {
|
|
459
|
+
await uninstall(false);
|
|
460
|
+
} else {
|
|
461
|
+
console.log(`\n ${yellow}Invalid choice or no installation at that location.${reset}\n`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Main
|
|
466
|
+
async function main() {
|
|
467
|
+
if (hasGlobal && hasLocal) {
|
|
468
|
+
console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
|
|
469
|
+
process.exit(1);
|
|
470
|
+
} else if (explicitConfigDir && hasLocal) {
|
|
471
|
+
console.error(` ${yellow}Cannot use --config-dir with --local${reset}`);
|
|
472
|
+
process.exit(1);
|
|
473
|
+
} else if (hasGlobal) {
|
|
474
|
+
await uninstall(true);
|
|
475
|
+
} else if (hasLocal) {
|
|
476
|
+
await uninstall(false);
|
|
477
|
+
} else {
|
|
478
|
+
await promptLocation();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
main().catch(err => {
|
|
483
|
+
console.error(` ${red}Error:${reset} ${err.message}`);
|
|
484
|
+
process.exit(1);
|
|
485
|
+
});
|