wtf-p 0.1.0 → 0.3.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 (110) hide show
  1. package/README.md +215 -230
  2. package/bin/commands/doctor.js +100 -0
  3. package/bin/commands/install-logic.js +235 -0
  4. package/bin/commands/list.js +88 -0
  5. package/bin/commands/status.js +85 -0
  6. package/bin/commands/update.js +50 -0
  7. package/bin/install.js +157 -309
  8. package/bin/lib/manifest.js +41 -0
  9. package/bin/lib/utils.js +338 -0
  10. package/bin/lib/wcn-compiler.js +226 -0
  11. package/bin/uninstall.js +202 -206
  12. package/core/write-the-f-paper/references/github-contrib.md +216 -0
  13. package/core/write-the-f-paper/templates/posters/basic.md +34 -0
  14. package/core/write-the-f-paper/templates/slides/basic.md +31 -0
  15. package/package.json +14 -4
  16. package/vendors/claude/.claude-plugin/plugin.json +12 -0
  17. package/vendors/claude/commands/wtfp/contribute.md +278 -0
  18. package/vendors/claude/commands/wtfp/create-poster.md +42 -0
  19. package/vendors/claude/commands/wtfp/create-slides.md +41 -0
  20. package/{commands → vendors/claude/commands}/wtfp/help.md +28 -0
  21. package/vendors/claude/commands/wtfp/report-bug.md +131 -0
  22. package/vendors/claude/commands/wtfp/request-feature.md +160 -0
  23. package/vendors/claude/skills/wtfp/echarts/SKILL.md +47 -0
  24. package/vendors/claude/skills/wtfp/marp/SKILL.md +49 -0
  25. /package/{write-the-f-paper → core/write-the-f-paper}/references/checkpoints.md +0 -0
  26. /package/{write-the-f-paper → core/write-the-f-paper}/references/continuation-format.md +0 -0
  27. /package/{write-the-f-paper → core/write-the-f-paper}/references/git-integration.md +0 -0
  28. /package/{write-the-f-paper → core/write-the-f-paper}/references/imrad-structure.md +0 -0
  29. /package/{write-the-f-paper → core/write-the-f-paper}/references/length-estimation.md +0 -0
  30. /package/{write-the-f-paper → core/write-the-f-paper}/references/plan-format.md +0 -0
  31. /package/{write-the-f-paper → core/write-the-f-paper}/references/principles.md +0 -0
  32. /package/{write-the-f-paper → core/write-the-f-paper}/references/questioning.md +0 -0
  33. /package/{write-the-f-paper → core/write-the-f-paper}/references/research-pitfalls.md +0 -0
  34. /package/{write-the-f-paper → core/write-the-f-paper}/templates/config.json +0 -0
  35. /package/{write-the-f-paper → core/write-the-f-paper}/templates/context.md +0 -0
  36. /package/{write-the-f-paper → core/write-the-f-paper}/templates/continue-here.md +0 -0
  37. /package/{write-the-f-paper → core/write-the-f-paper}/templates/data-inventory.md +0 -0
  38. /package/{write-the-f-paper → core/write-the-f-paper}/templates/discovery.md +0 -0
  39. /package/{write-the-f-paper → core/write-the-f-paper}/templates/issues.md +0 -0
  40. /package/{write-the-f-paper → core/write-the-f-paper}/templates/manuscript.md +0 -0
  41. /package/{write-the-f-paper → core/write-the-f-paper}/templates/narrative-arc.md +0 -0
  42. /package/{write-the-f-paper → core/write-the-f-paper}/templates/outline.md +0 -0
  43. /package/{write-the-f-paper → core/write-the-f-paper}/templates/phase-prompt.md +0 -0
  44. /package/{write-the-f-paper → core/write-the-f-paper}/templates/prior-drafts.md +0 -0
  45. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/argument-structure.md +0 -0
  46. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/bibliography.md +0 -0
  47. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/gaps.md +0 -0
  48. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/imrad.md +0 -0
  49. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/peer-review.md +0 -0
  50. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/style-guide.md +0 -0
  51. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/tools.md +0 -0
  52. /package/{write-the-f-paper → core/write-the-f-paper}/templates/project.md +0 -0
  53. /package/{write-the-f-paper → core/write-the-f-paper}/templates/research.md +0 -0
  54. /package/{write-the-f-paper → core/write-the-f-paper}/templates/review-comments.md +0 -0
  55. /package/{write-the-f-paper → core/write-the-f-paper}/templates/revision-issues.md +0 -0
  56. /package/{write-the-f-paper → core/write-the-f-paper}/templates/roadmap.md +0 -0
  57. /package/{write-the-f-paper → core/write-the-f-paper}/templates/section-archive.md +0 -0
  58. /package/{write-the-f-paper → core/write-the-f-paper}/templates/section-context.md +0 -0
  59. /package/{write-the-f-paper → core/write-the-f-paper}/templates/section.md +0 -0
  60. /package/{write-the-f-paper → core/write-the-f-paper}/templates/state.md +0 -0
  61. /package/{write-the-f-paper → core/write-the-f-paper}/templates/summary.md +0 -0
  62. /package/{write-the-f-paper → core/write-the-f-paper}/venues/acm-cs.yaml +0 -0
  63. /package/{write-the-f-paper → core/write-the-f-paper}/venues/arxiv-ml.yaml +0 -0
  64. /package/{write-the-f-paper → core/write-the-f-paper}/venues/ieee-cs.yaml +0 -0
  65. /package/{write-the-f-paper → core/write-the-f-paper}/venues/nature.yaml +0 -0
  66. /package/{write-the-f-paper → core/write-the-f-paper}/venues/thesis-chapter.yaml +0 -0
  67. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/create-outline.md +0 -0
  68. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/create-outline.wcn.md +0 -0
  69. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/discuss-section.md +0 -0
  70. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/discuss-section.wcn.md +0 -0
  71. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/execute-section.md +0 -0
  72. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/execute-section.wcn.md +0 -0
  73. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/list-assumptions.md +0 -0
  74. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/list-assumptions.wcn.md +0 -0
  75. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/lit-review-phase.md +0 -0
  76. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/lit-review-phase.wcn.md +0 -0
  77. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/map-project.md +0 -0
  78. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/map-project.wcn.md +0 -0
  79. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/plan-section.md +0 -0
  80. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/plan-section.wcn.md +0 -0
  81. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/research-gap.md +0 -0
  82. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/research-gap.wcn.md +0 -0
  83. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/resume-paper.md +0 -0
  84. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/resume-paper.wcn.md +0 -0
  85. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/review-section.md +0 -0
  86. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/review-section.wcn.md +0 -0
  87. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/submit-draft.md +0 -0
  88. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/submit-draft.wcn.md +0 -0
  89. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/transition.md +0 -0
  90. /package/{write-the-f-paper → core/write-the-f-paper}/workflows/transition.wcn.md +0 -0
  91. /package/{commands → vendors/claude/commands}/wtfp/analyze-bib.md +0 -0
  92. /package/{commands → vendors/claude/commands}/wtfp/check-refs.md +0 -0
  93. /package/{commands → vendors/claude/commands}/wtfp/create-outline.md +0 -0
  94. /package/{commands → vendors/claude/commands}/wtfp/discuss-section.md +0 -0
  95. /package/{commands → vendors/claude/commands}/wtfp/export-latex.md +0 -0
  96. /package/{commands → vendors/claude/commands}/wtfp/insert-section.md +0 -0
  97. /package/{commands → vendors/claude/commands}/wtfp/list-assumptions.md +0 -0
  98. /package/{commands → vendors/claude/commands}/wtfp/map-project.md +0 -0
  99. /package/{commands → vendors/claude/commands}/wtfp/new-paper.md +0 -0
  100. /package/{commands → vendors/claude/commands}/wtfp/pause-writing.md +0 -0
  101. /package/{commands → vendors/claude/commands}/wtfp/plan-revision.md +0 -0
  102. /package/{commands → vendors/claude/commands}/wtfp/plan-section.md +0 -0
  103. /package/{commands → vendors/claude/commands}/wtfp/polish-prose.md +0 -0
  104. /package/{commands → vendors/claude/commands}/wtfp/progress.md +0 -0
  105. /package/{commands → vendors/claude/commands}/wtfp/remove-section.md +0 -0
  106. /package/{commands → vendors/claude/commands}/wtfp/research-gap.md +0 -0
  107. /package/{commands → vendors/claude/commands}/wtfp/resume-writing.md +0 -0
  108. /package/{commands → vendors/claude/commands}/wtfp/review-section.md +0 -0
  109. /package/{commands → vendors/claude/commands}/wtfp/submit-milestone.md +0 -0
  110. /package/{commands → vendors/claude/commands}/wtfp/write-section.md +0 -0
package/bin/install.js CHANGED
@@ -1,358 +1,206 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
3
  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';
4
+ const {
5
+ createOutput,
6
+ createRL,
7
+ prompt,
8
+ } = require('./lib/utils');
16
9
 
17
10
  // Get version from package.json
18
11
  const pkg = require('../package.json');
19
12
 
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
- `;
13
+ // Command modules
14
+ const showStatus = require('./commands/status');
15
+ const runDoctor = require('./commands/doctor');
16
+ const runUpdate = require('./commands/update');
17
+ const install = require('./commands/install-logic');
18
+ const showList = require('./commands/list');
19
+
20
+ // ============ Argument Parsing ============
32
21
 
33
- // Parse args
34
22
  const args = process.argv.slice(2);
23
+
24
+ // Detect subcommand
25
+ const subcommands = ['status', 'doctor', 'update'];
26
+ const subcommand = args.find(arg => !arg.startsWith('-') && subcommands.includes(arg));
27
+ const subcommandIndex = args.indexOf(subcommand);
28
+ if (subcommandIndex !== -1) {
29
+ args.splice(subcommandIndex, 1);
30
+ }
31
+
32
+ // Parse flags
35
33
  const hasGlobal = args.includes('--global') || args.includes('-g');
36
34
  const hasLocal = args.includes('--local') || args.includes('-l');
37
35
  const hasForce = args.includes('--force') || args.includes('-f');
38
36
  const hasBackupAll = args.includes('--backup-all') || args.includes('-b');
37
+ const hasHelp = args.includes('--help') || args.includes('-h');
38
+ const hasVersion = args.includes('--version') || args.includes('-v');
39
+ const hasList = args.includes('--list');
40
+ const hasNoColor = args.includes('--no-color');
41
+ const hasQuiet = args.includes('--quiet') || args.includes('-q');
42
+ const hasVerbose = args.includes('--verbose');
43
+ const hasBeginner = args.includes('--beginner');
44
+ const hasAdvanced = args.includes('--advanced');
39
45
 
40
- // Parse --config-dir argument
41
46
  function parseConfigDirArg() {
42
47
  const configDirIndex = args.findIndex(arg => arg === '--config-dir' || arg === '-c');
43
48
  if (configDirIndex !== -1) {
44
49
  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
+ if (nextArg && !nextArg.startsWith('-')) return nextArg;
50
51
  }
51
52
  const configDirArg = args.find(arg => arg.startsWith('--config-dir=') || arg.startsWith('-c='));
52
- if (configDirArg) {
53
- return configDirArg.split('=')[1];
54
- }
53
+ if (configDirArg) return configDirArg.split('=')[1];
55
54
  return null;
56
55
  }
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
56
 
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;
57
+ function parseOnlyArg() {
58
+ const onlyArg = args.find(arg => arg.startsWith('--only='));
59
+ if (onlyArg) return onlyArg.split('=')[1];
60
+ return 'all';
111
61
  }
112
62
 
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
- }
63
+ const options = {
64
+ explicitConfigDir: parseConfigDirArg(),
65
+ onlyInstall: parseOnlyArg(),
66
+ hasGlobal,
67
+ hasLocal,
68
+ hasForce,
69
+ hasBackupAll,
70
+ hasQuiet,
71
+ hasVerbose,
72
+ hasBeginner,
73
+ hasAdvanced,
74
+ isInteractive: process.stdout.isTTY && process.stdin.isTTY && !hasQuiet && !hasAdvanced,
75
+ };
76
+ options.showExplanations = options.hasBeginner || (options.isInteractive && !options.hasAdvanced);
77
+
78
+ const useColors = !hasNoColor && (process.stdout.isTTY || process.env.FORCE_COLOR);
79
+ const out = createOutput({ quiet: hasQuiet, verbose: hasVerbose, useColors });
80
+ options.out = out;
81
+
82
+ // ============ Banner ============
122
83
 
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
- }
84
+ const banner = `
85
+ ${out.colors.magenta('██╗ ██╗████████╗███████╗ ██████╗')}
86
+ ${out.colors.magenta('██║ ██║╚══██╔══╝██╔════╝ ██╔══██╗')}
87
+ ${out.colors.magenta('██║ █╗ ██║ ██║ █████╗ █████╗██████╔╝')}
88
+ ${out.colors.magenta('██║███╗██║ ██║ ██╔══╝ ╚════╝██╔═══╝')}
89
+ ${out.colors.magenta('╚███╔███╔╝ ██║ ██║ ██║')}
90
+ ${out.colors.magenta(' ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝')}
91
+
92
+ ${out.colors.cyan('Write The F***ing Paper')} ${out.colors.dim(`v${pkg.version}`)}
93
+ Context engineering for academic writing.
94
+ `;
131
95
 
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}`);
96
+ // ============ Help Text ============
97
+
98
+ function showHelp() {
99
+ const c = out.colors;
100
+ console.log(banner);
101
+ console.log(` ${c.yellow('Usage:')} npx wtf-p [command] [options]
102
+
103
+ ${c.yellow('Commands:')}
104
+ ${c.cyan('status')} Show current installation state
105
+ ${c.cyan('doctor')} Diagnose installation issues
106
+ ${c.cyan('update')} Update existing installation
107
+
108
+ ${c.yellow('Install Options:')}
109
+ ${c.cyan('-g, --global')} Install globally (to Claude config directory)
110
+ ${c.cyan('-l, --local')} Install locally (to ./.claude in current directory)
111
+ ${c.cyan('-c, --config-dir <path>')} Specify custom Claude config directory
112
+ ${c.cyan('-f, --force')} Overwrite all existing files without prompting
113
+ ${c.cyan('-b, --backup-all')} Backup all existing files before overwriting
114
+ ${c.cyan('--only=<type>')} Install only: commands, workflows, or all
115
+
116
+ ${c.yellow('Output Options:')}
117
+ ${c.cyan('--beginner')} Show detailed explanations
118
+ ${c.cyan('--advanced')} Minimal output, skip confirmations
119
+ ${c.cyan('--no-color')} Disable colored output
120
+ ${c.cyan('-q, --quiet')} Suppress non-essential output
121
+ ${c.cyan('--verbose')} Show detailed progress
122
+
123
+ ${c.yellow('Other:')}
124
+ ${c.cyan('-v, --version')} Show version information
125
+ ${c.cyan('-h, --help')} Show this help message
126
+
127
+ ${c.yellow('Examples:')}
128
+ ${c.dim('# Interactive install with prompts')}
129
+ npx wtf-p
130
+
131
+ ${c.dim('# Quick global install')}
132
+ npx wtf-p --global --advanced
133
+
134
+ ${c.yellow('After installation:')}
135
+ Run ${c.cyan('/wtfp:help')} in Claude Code to get started.
136
+ `);
141
137
  }
142
138
 
143
- /**
144
- * Collect all files that would be installed
145
- */
146
- function collectFiles(srcDir, destDir, pathPrefix, files = []) {
147
- if (!fs.existsSync(srcDir)) return files;
139
+ // ============ Main ============
148
140
 
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
- }
141
+ async function main() {
142
+ if (hasVersion) {
143
+ console.log(`wtf-p v${pkg.version}`);
144
+ return;
160
145
  }
161
146
 
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);
147
+ if (hasHelp) {
148
+ showHelp();
149
+ return;
172
150
  }
173
- return null; // Binary copy
174
- }
175
151
 
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 };
152
+ // Handle --list
153
+ if (hasList) {
154
+ if (!hasQuiet) console.log(banner);
155
+ showList(options);
156
+ return;
157
+ }
183
158
 
184
- // Check for existing installation
185
- const existingFiles = files.filter(f => fs.existsSync(f.dest));
159
+ if (subcommand === 'status') {
160
+ if (!hasQuiet) console.log(banner);
161
+ await showStatus(options, pkg);
162
+ return;
163
+ }
186
164
 
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`);
165
+ if (subcommand === 'doctor') {
166
+ if (!hasQuiet) console.log(banner);
167
+ await runDoctor(options);
168
+ return;
190
169
  }
191
170
 
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++;
171
+ if (subcommand === 'update') {
172
+ if (!hasQuiet) console.log(banner);
173
+ await runUpdate(options, pkg, install);
174
+ return;
258
175
  }
259
176
 
260
- rl.close();
261
- return stats;
262
- }
177
+ // Default: Install
178
+ if (!hasQuiet) console.log(banner);
263
179
 
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
180
+ if (hasGlobal) {
181
+ await install(true, false, options, pkg);
182
+ } else if (hasLocal) {
183
+ await install(false, false, options, pkg);
184
+ } else if (options.isInteractive) {
185
+ const rl = createRL();
186
+ out.log(` ${out.colors.yellow('Where would you like to install?')}
308
187
  `);
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
188
+ out.log(` ${out.colors.cyan('1)')} Global (~/.claude) - available in all projects`);
189
+ out.log(` ${out.colors.cyan('2)')} Local (./.claude) - this project only
325
190
  `);
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);
191
+
192
+ const answer = await prompt(rl, ` Choice ${out.colors.dim('[1]')}: `);
193
+ rl.close();
194
+
195
+ const choice = answer || '1';
196
+ await install(choice !== '2', false, options, pkg);
350
197
  } else {
351
- await promptLocation();
198
+ await install(true, false, options, pkg);
352
199
  }
353
200
  }
354
201
 
355
202
  main().catch(err => {
356
- console.error(` ${red}Error:${reset} ${err.message}`);
203
+ out.error(err.message);
204
+ if (hasVerbose) console.error(err.stack);
357
205
  process.exit(1);
358
- });
206
+ });
@@ -0,0 +1,41 @@
1
+ const path = require('path');
2
+
3
+ const ROOT = path.resolve(__dirname, '../..');
4
+
5
+ const MANIFEST = {
6
+ claude: {
7
+ name: 'Claude Code',
8
+ configDirEnv: 'CLAUDE_CONFIG_DIR',
9
+ defaultDir: '.claude',
10
+ components: [
11
+ {
12
+ id: 'workflows',
13
+ src: path.join(ROOT, 'core', 'write-the-f-paper'),
14
+ dest: 'write-the-f-paper',
15
+ type: 'dir'
16
+ },
17
+ {
18
+ id: 'commands',
19
+ src: path.join(ROOT, 'vendors', 'claude', 'commands', 'wtfp'),
20
+ dest: 'commands/wtfp',
21
+ type: 'dir'
22
+ },
23
+ {
24
+ id: 'skills',
25
+ src: path.join(ROOT, 'vendors', 'claude', 'skills', 'wtfp'),
26
+ dest: 'skills/wtfp',
27
+ type: 'dir'
28
+ },
29
+ {
30
+ id: 'plugin',
31
+ src: path.join(ROOT, 'vendors', 'claude', '.claude-plugin'),
32
+ dest: '.claude-plugin',
33
+ type: 'dir'
34
+ }
35
+ ]
36
+ },
37
+ // Future:
38
+ // gemini: { ... }
39
+ };
40
+
41
+ module.exports = MANIFEST;