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.
Files changed (92) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +338 -0
  3. package/bin/install.js +358 -0
  4. package/bin/uninstall.js +485 -0
  5. package/commands/wtfp/analyze-bib.md +326 -0
  6. package/commands/wtfp/check-refs.md +205 -0
  7. package/commands/wtfp/create-outline.md +275 -0
  8. package/commands/wtfp/discuss-section.md +71 -0
  9. package/commands/wtfp/export-latex.md +216 -0
  10. package/commands/wtfp/help.md +342 -0
  11. package/commands/wtfp/insert-section.md +130 -0
  12. package/commands/wtfp/list-assumptions.md +109 -0
  13. package/commands/wtfp/map-project.md +197 -0
  14. package/commands/wtfp/new-paper.md +443 -0
  15. package/commands/wtfp/pause-writing.md +145 -0
  16. package/commands/wtfp/plan-revision.md +180 -0
  17. package/commands/wtfp/plan-section.md +247 -0
  18. package/commands/wtfp/polish-prose.md +177 -0
  19. package/commands/wtfp/progress.md +325 -0
  20. package/commands/wtfp/remove-section.md +147 -0
  21. package/commands/wtfp/research-gap.md +235 -0
  22. package/commands/wtfp/resume-writing.md +142 -0
  23. package/commands/wtfp/review-section.md +409 -0
  24. package/commands/wtfp/submit-milestone.md +211 -0
  25. package/commands/wtfp/write-section.md +273 -0
  26. package/package.json +40 -0
  27. package/write-the-f-paper/references/checkpoints.md +213 -0
  28. package/write-the-f-paper/references/continuation-format.md +223 -0
  29. package/write-the-f-paper/references/git-integration.md +241 -0
  30. package/write-the-f-paper/references/imrad-structure.md +212 -0
  31. package/write-the-f-paper/references/length-estimation.md +148 -0
  32. package/write-the-f-paper/references/plan-format.md +361 -0
  33. package/write-the-f-paper/references/principles.md +158 -0
  34. package/write-the-f-paper/references/questioning.md +132 -0
  35. package/write-the-f-paper/references/research-pitfalls.md +208 -0
  36. package/write-the-f-paper/templates/config.json +30 -0
  37. package/write-the-f-paper/templates/context.md +179 -0
  38. package/write-the-f-paper/templates/continue-here.md +142 -0
  39. package/write-the-f-paper/templates/data-inventory.md +142 -0
  40. package/write-the-f-paper/templates/discovery.md +146 -0
  41. package/write-the-f-paper/templates/issues.md +59 -0
  42. package/write-the-f-paper/templates/manuscript.md +253 -0
  43. package/write-the-f-paper/templates/narrative-arc.md +227 -0
  44. package/write-the-f-paper/templates/outline.md +274 -0
  45. package/write-the-f-paper/templates/phase-prompt.md +348 -0
  46. package/write-the-f-paper/templates/prior-drafts.md +204 -0
  47. package/write-the-f-paper/templates/project-context/argument-structure.md +267 -0
  48. package/write-the-f-paper/templates/project-context/bibliography.md +152 -0
  49. package/write-the-f-paper/templates/project-context/gaps.md +122 -0
  50. package/write-the-f-paper/templates/project-context/imrad.md +140 -0
  51. package/write-the-f-paper/templates/project-context/peer-review.md +152 -0
  52. package/write-the-f-paper/templates/project-context/style-guide.md +198 -0
  53. package/write-the-f-paper/templates/project-context/tools.md +170 -0
  54. package/write-the-f-paper/templates/project.md +185 -0
  55. package/write-the-f-paper/templates/research.md +384 -0
  56. package/write-the-f-paper/templates/review-comments.md +253 -0
  57. package/write-the-f-paper/templates/revision-issues.md +169 -0
  58. package/write-the-f-paper/templates/roadmap.md +187 -0
  59. package/write-the-f-paper/templates/section-archive.md +178 -0
  60. package/write-the-f-paper/templates/section-context.md +114 -0
  61. package/write-the-f-paper/templates/section.md +128 -0
  62. package/write-the-f-paper/templates/state.md +244 -0
  63. package/write-the-f-paper/templates/summary.md +277 -0
  64. package/write-the-f-paper/venues/acm-cs.yaml +102 -0
  65. package/write-the-f-paper/venues/arxiv-ml.yaml +103 -0
  66. package/write-the-f-paper/venues/ieee-cs.yaml +100 -0
  67. package/write-the-f-paper/venues/nature.yaml +81 -0
  68. package/write-the-f-paper/venues/thesis-chapter.yaml +101 -0
  69. package/write-the-f-paper/workflows/create-outline.md +462 -0
  70. package/write-the-f-paper/workflows/create-outline.wcn.md +194 -0
  71. package/write-the-f-paper/workflows/discuss-section.md +249 -0
  72. package/write-the-f-paper/workflows/discuss-section.wcn.md +63 -0
  73. package/write-the-f-paper/workflows/execute-section.md +704 -0
  74. package/write-the-f-paper/workflows/execute-section.wcn.md +219 -0
  75. package/write-the-f-paper/workflows/list-assumptions.md +183 -0
  76. package/write-the-f-paper/workflows/list-assumptions.wcn.md +42 -0
  77. package/write-the-f-paper/workflows/lit-review-phase.md +281 -0
  78. package/write-the-f-paper/workflows/lit-review-phase.wcn.md +92 -0
  79. package/write-the-f-paper/workflows/map-project.md +359 -0
  80. package/write-the-f-paper/workflows/map-project.wcn.md +73 -0
  81. package/write-the-f-paper/workflows/plan-section.md +409 -0
  82. package/write-the-f-paper/workflows/plan-section.wcn.md +236 -0
  83. package/write-the-f-paper/workflows/research-gap.md +469 -0
  84. package/write-the-f-paper/workflows/research-gap.wcn.md +119 -0
  85. package/write-the-f-paper/workflows/resume-paper.md +274 -0
  86. package/write-the-f-paper/workflows/resume-paper.wcn.md +93 -0
  87. package/write-the-f-paper/workflows/review-section.md +329 -0
  88. package/write-the-f-paper/workflows/review-section.wcn.md +91 -0
  89. package/write-the-f-paper/workflows/submit-draft.md +368 -0
  90. package/write-the-f-paper/workflows/submit-draft.wcn.md +69 -0
  91. package/write-the-f-paper/workflows/transition.md +375 -0
  92. package/write-the-f-paper/workflows/transition.wcn.md +113 -0
package/bin/install.js ADDED
@@ -0,0 +1,358 @@
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
+ const pkg = require('../package.json');
19
+
20
+ const banner = `
21
+ ${magenta}██╗ ██╗████████╗███████╗ ██████╗
22
+ ██║ ██║╚══██╔══╝██╔════╝ ██╔══██╗
23
+ ██║ █╗ ██║ ██║ █████╗ █████╗██████╔╝
24
+ ██║███╗██║ ██║ ██╔══╝ ╚════╝██╔═══╝
25
+ ╚███╔███╔╝ ██║ ██║ ██║
26
+ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝${reset}
27
+
28
+ ${cyan}Write The F***ing Paper${reset} ${dim}v${pkg.version}${reset}
29
+ Context engineering for academic writing.
30
+ Ship papers, not excuses.
31
+ `;
32
+
33
+ // Parse args
34
+ const args = process.argv.slice(2);
35
+ const hasGlobal = args.includes('--global') || args.includes('-g');
36
+ const hasLocal = args.includes('--local') || args.includes('-l');
37
+ const hasForce = args.includes('--force') || args.includes('-f');
38
+ const hasBackupAll = args.includes('--backup-all') || args.includes('-b');
39
+
40
+ // Parse --config-dir argument
41
+ function parseConfigDirArg() {
42
+ const configDirIndex = args.findIndex(arg => arg === '--config-dir' || arg === '-c');
43
+ if (configDirIndex !== -1) {
44
+ const nextArg = args[configDirIndex + 1];
45
+ if (!nextArg || nextArg.startsWith('-')) {
46
+ console.error(` ${yellow}--config-dir requires a path argument${reset}`);
47
+ process.exit(1);
48
+ }
49
+ return nextArg;
50
+ }
51
+ const configDirArg = args.find(arg => arg.startsWith('--config-dir=') || arg.startsWith('-c='));
52
+ if (configDirArg) {
53
+ return configDirArg.split('=')[1];
54
+ }
55
+ return null;
56
+ }
57
+ const explicitConfigDir = parseConfigDirArg();
58
+ const hasHelp = args.includes('--help') || args.includes('-h');
59
+
60
+ console.log(banner);
61
+
62
+ // Show help if requested
63
+ if (hasHelp) {
64
+ console.log(` ${yellow}Usage:${reset} npx wtf-p [options]
65
+
66
+ ${yellow}Options:${reset}
67
+ ${cyan}-g, --global${reset} Install globally (to Claude config directory)
68
+ ${cyan}-l, --local${reset} Install locally (to ./.claude in current directory)
69
+ ${cyan}-c, --config-dir <path>${reset} Specify custom Claude config directory
70
+ ${cyan}-f, --force${reset} Overwrite all existing files without prompting
71
+ ${cyan}-b, --backup-all${reset} Backup all existing files before overwriting
72
+ ${cyan}-h, --help${reset} Show this help message
73
+
74
+ ${yellow}Conflict Resolution:${reset}
75
+ When existing WTF-P files are detected, you'll be prompted for each:
76
+ ${dim}[o]verwrite${reset} - Replace with new version
77
+ ${dim}[s]kip${reset} - Keep existing file
78
+ ${dim}[b]ackup${reset} - Backup existing, then install new
79
+ ${dim}[a]ll${reset} - Apply same choice to remaining conflicts
80
+
81
+ ${yellow}Examples:${reset}
82
+ ${dim}# Install with prompts for conflicts${reset}
83
+ npx wtf-p --global
84
+
85
+ ${dim}# Force overwrite all existing files${reset}
86
+ npx wtf-p --global --force
87
+
88
+ ${dim}# Backup all existing files before installing${reset}
89
+ npx wtf-p --global --backup-all
90
+
91
+ ${dim}# Install to custom config directory${reset}
92
+ npx wtf-p --global --config-dir ~/.claude-research
93
+
94
+ ${yellow}Uninstall:${reset}
95
+ npx wtf-p-uninstall [--global|--local] [--config-dir <path>]
96
+
97
+ ${yellow}After installation:${reset}
98
+ Run ${cyan}/wtfp:help${reset} in Claude Code to get started.
99
+ `);
100
+ process.exit(0);
101
+ }
102
+
103
+ /**
104
+ * Expand ~ to home directory
105
+ */
106
+ function expandTilde(filePath) {
107
+ if (filePath && filePath.startsWith('~/')) {
108
+ return path.join(os.homedir(), filePath.slice(2));
109
+ }
110
+ return filePath;
111
+ }
112
+
113
+ /**
114
+ * Create readline interface for prompts
115
+ */
116
+ function createRL() {
117
+ return readline.createInterface({
118
+ input: process.stdin,
119
+ output: process.stdout
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Prompt user with a question
125
+ */
126
+ function prompt(rl, question) {
127
+ return new Promise(resolve => {
128
+ rl.question(question, answer => resolve(answer.trim().toLowerCase()));
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Generate backup path with timestamp
134
+ */
135
+ function getBackupPath(filePath) {
136
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
137
+ const dir = path.dirname(filePath);
138
+ const ext = path.extname(filePath);
139
+ const base = path.basename(filePath, ext);
140
+ return path.join(dir, `${base}.backup-${timestamp}${ext}`);
141
+ }
142
+
143
+ /**
144
+ * Collect all files that would be installed
145
+ */
146
+ function collectFiles(srcDir, destDir, pathPrefix, files = []) {
147
+ if (!fs.existsSync(srcDir)) return files;
148
+
149
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true });
150
+
151
+ for (const entry of entries) {
152
+ const srcPath = path.join(srcDir, entry.name);
153
+ const destPath = path.join(destDir, entry.name);
154
+
155
+ if (entry.isDirectory()) {
156
+ collectFiles(srcPath, destPath, pathPrefix, files);
157
+ } else {
158
+ files.push({ src: srcPath, dest: destPath, name: entry.name });
159
+ }
160
+ }
161
+
162
+ return files;
163
+ }
164
+
165
+ /**
166
+ * Process file content with path replacement
167
+ */
168
+ function processContent(srcPath, pathPrefix) {
169
+ if (srcPath.endsWith('.md') || srcPath.endsWith('.json')) {
170
+ let content = fs.readFileSync(srcPath, 'utf8');
171
+ return content.replace(/~\/\.claude\//g, pathPrefix);
172
+ }
173
+ return null; // Binary copy
174
+ }
175
+
176
+ /**
177
+ * Install files with conflict resolution
178
+ */
179
+ async function installWithConflictResolution(files, pathPrefix, claudeDir) {
180
+ const rl = createRL();
181
+ let globalChoice = null; // 'overwrite', 'skip', 'backup'
182
+ const stats = { installed: 0, skipped: 0, backed: 0 };
183
+
184
+ // Check for existing installation
185
+ const existingFiles = files.filter(f => fs.existsSync(f.dest));
186
+
187
+ if (existingFiles.length > 0 && !hasForce && !hasBackupAll) {
188
+ const relPath = claudeDir.replace(os.homedir(), '~');
189
+ console.log(` ${yellow}Found ${existingFiles.length} existing WTF-P file(s) in ${relPath}${reset}\n`);
190
+ }
191
+
192
+ for (const file of files) {
193
+ const exists = fs.existsSync(file.dest);
194
+ const relDest = file.dest.replace(os.homedir(), '~').replace(process.cwd(), '.');
195
+
196
+ // Ensure directory exists
197
+ fs.mkdirSync(path.dirname(file.dest), { recursive: true });
198
+
199
+ if (exists && !hasForce && !hasBackupAll) {
200
+ let choice = globalChoice;
201
+
202
+ if (!choice) {
203
+ const answer = await prompt(rl,
204
+ ` ${yellow}?${reset} ${dim}${relDest}${reset} exists. [o]verwrite/[s]kip/[b]ackup/[a]ll: `
205
+ );
206
+
207
+ if (answer.startsWith('a')) {
208
+ // Apply to all: ask which action
209
+ const allAnswer = await prompt(rl,
210
+ ` Apply to all: [o]verwrite/[s]kip/[b]ackup: `
211
+ );
212
+ if (allAnswer.startsWith('o')) globalChoice = 'overwrite';
213
+ else if (allAnswer.startsWith('b')) globalChoice = 'backup';
214
+ else globalChoice = 'skip';
215
+ choice = globalChoice;
216
+ } else if (answer.startsWith('o')) {
217
+ choice = 'overwrite';
218
+ } else if (answer.startsWith('b')) {
219
+ choice = 'backup';
220
+ } else {
221
+ choice = 'skip';
222
+ }
223
+ }
224
+
225
+ if (choice === 'skip') {
226
+ console.log(` ${dim}○${reset} Skipped ${dim}${relDest}${reset}`);
227
+ stats.skipped++;
228
+ continue;
229
+ }
230
+
231
+ if (choice === 'backup') {
232
+ const backupPath = getBackupPath(file.dest);
233
+ fs.copyFileSync(file.dest, backupPath);
234
+ console.log(` ${cyan}↻${reset} Backed up to ${dim}${path.basename(backupPath)}${reset}`);
235
+ stats.backed++;
236
+ }
237
+ } else if (exists && hasBackupAll) {
238
+ const backupPath = getBackupPath(file.dest);
239
+ fs.copyFileSync(file.dest, backupPath);
240
+ console.log(` ${cyan}↻${reset} Backed up ${dim}${relDest}${reset}`);
241
+ stats.backed++;
242
+ }
243
+
244
+ // Install the file
245
+ const content = processContent(file.src, pathPrefix);
246
+ if (content !== null) {
247
+ fs.writeFileSync(file.dest, content);
248
+ } else {
249
+ fs.copyFileSync(file.src, file.dest);
250
+ }
251
+
252
+ if (!exists) {
253
+ console.log(` ${green}+${reset} ${dim}${relDest}${reset}`);
254
+ } else {
255
+ console.log(` ${green}✓${reset} ${dim}${relDest}${reset}`);
256
+ }
257
+ stats.installed++;
258
+ }
259
+
260
+ rl.close();
261
+ return stats;
262
+ }
263
+
264
+ /**
265
+ * Install to the specified directory
266
+ */
267
+ async function install(isGlobal) {
268
+ const src = path.join(__dirname, '..');
269
+ const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.CLAUDE_CONFIG_DIR);
270
+ const defaultGlobalDir = configDir || path.join(os.homedir(), '.claude');
271
+ const claudeDir = isGlobal
272
+ ? defaultGlobalDir
273
+ : path.join(process.cwd(), '.claude');
274
+
275
+ const locationLabel = isGlobal
276
+ ? claudeDir.replace(os.homedir(), '~')
277
+ : claudeDir.replace(process.cwd(), '.');
278
+
279
+ const pathPrefix = isGlobal
280
+ ? (configDir ? `${claudeDir}/` : '~/.claude/')
281
+ : './.claude/';
282
+
283
+ console.log(` Installing to ${cyan}${locationLabel}${reset}\n`);
284
+
285
+ // Collect all files to install
286
+ const wtfpSrc = path.join(src, 'commands', 'wtfp');
287
+ const wtfpDest = path.join(claudeDir, 'commands', 'wtfp');
288
+ const skillSrc = path.join(src, 'write-the-f-paper');
289
+ const skillDest = path.join(claudeDir, 'write-the-f-paper');
290
+
291
+ const allFiles = [
292
+ ...collectFiles(wtfpSrc, wtfpDest, pathPrefix),
293
+ ...collectFiles(skillSrc, skillDest, pathPrefix)
294
+ ];
295
+
296
+ // Install with conflict resolution
297
+ const stats = await installWithConflictResolution(allFiles, pathPrefix, claudeDir);
298
+
299
+ console.log(`
300
+ ${green}Done!${reset} Installed: ${stats.installed}, Skipped: ${stats.skipped}, Backed up: ${stats.backed}
301
+
302
+ Run ${cyan}/wtfp:help${reset} in Claude Code to get started.
303
+
304
+ ${yellow}Quick Start:${reset}
305
+ ${cyan}/wtfp:new-paper${reset} Start a new academic paper
306
+ ${cyan}/wtfp:progress${reset} Check your writing status
307
+ ${cyan}/wtfp:help${reset} Show all commands
308
+ `);
309
+ }
310
+
311
+ /**
312
+ * Prompt for install location
313
+ */
314
+ async function promptLocation() {
315
+ const rl = createRL();
316
+
317
+ const configDir = expandTilde(explicitConfigDir) || expandTilde(process.env.CLAUDE_CONFIG_DIR);
318
+ const globalPath = configDir || path.join(os.homedir(), '.claude');
319
+ const globalLabel = globalPath.replace(os.homedir(), '~');
320
+
321
+ console.log(` ${yellow}Where would you like to install?${reset}
322
+
323
+ ${cyan}1${reset}) Global ${dim}(${globalLabel})${reset} - available in all projects
324
+ ${cyan}2${reset}) Local ${dim}(./.claude)${reset} - this project only
325
+ `);
326
+
327
+ const answer = await prompt(rl, ` Choice ${dim}[1]${reset}: `);
328
+ rl.close();
329
+
330
+ const choice = answer || '1';
331
+ const isGlobal = choice !== '2';
332
+ await install(isGlobal);
333
+ }
334
+
335
+ // Main
336
+ async function main() {
337
+ if (hasGlobal && hasLocal) {
338
+ console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
339
+ process.exit(1);
340
+ } else if (explicitConfigDir && hasLocal) {
341
+ console.error(` ${yellow}Cannot use --config-dir with --local${reset}`);
342
+ process.exit(1);
343
+ } else if (hasForce && hasBackupAll) {
344
+ console.error(` ${yellow}Cannot use both --force and --backup-all${reset}`);
345
+ process.exit(1);
346
+ } else if (hasGlobal) {
347
+ await install(true);
348
+ } else if (hasLocal) {
349
+ await install(false);
350
+ } else {
351
+ await promptLocation();
352
+ }
353
+ }
354
+
355
+ main().catch(err => {
356
+ console.error(` ${red}Error:${reset} ${err.message}`);
357
+ process.exit(1);
358
+ });