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.
- package/README.md +215 -230
- package/bin/commands/doctor.js +100 -0
- package/bin/commands/install-logic.js +235 -0
- package/bin/commands/list.js +88 -0
- package/bin/commands/status.js +85 -0
- package/bin/commands/update.js +50 -0
- package/bin/install.js +157 -309
- package/bin/lib/manifest.js +41 -0
- package/bin/lib/utils.js +338 -0
- package/bin/lib/wcn-compiler.js +226 -0
- package/bin/uninstall.js +202 -206
- package/core/write-the-f-paper/references/github-contrib.md +216 -0
- package/core/write-the-f-paper/templates/posters/basic.md +34 -0
- package/core/write-the-f-paper/templates/slides/basic.md +31 -0
- package/package.json +14 -4
- package/vendors/claude/.claude-plugin/plugin.json +12 -0
- package/vendors/claude/commands/wtfp/contribute.md +278 -0
- package/vendors/claude/commands/wtfp/create-poster.md +42 -0
- package/vendors/claude/commands/wtfp/create-slides.md +41 -0
- package/{commands → vendors/claude/commands}/wtfp/help.md +28 -0
- package/vendors/claude/commands/wtfp/report-bug.md +131 -0
- package/vendors/claude/commands/wtfp/request-feature.md +160 -0
- package/vendors/claude/skills/wtfp/echarts/SKILL.md +47 -0
- package/vendors/claude/skills/wtfp/marp/SKILL.md +49 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/references/checkpoints.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/references/continuation-format.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/references/git-integration.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/references/imrad-structure.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/references/length-estimation.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/references/plan-format.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/references/principles.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/references/questioning.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/references/research-pitfalls.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/config.json +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/context.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/continue-here.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/data-inventory.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/discovery.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/issues.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/manuscript.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/narrative-arc.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/outline.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/phase-prompt.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/prior-drafts.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/argument-structure.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/bibliography.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/gaps.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/imrad.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/peer-review.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/style-guide.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/project-context/tools.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/project.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/research.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/review-comments.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/revision-issues.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/roadmap.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/section-archive.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/section-context.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/section.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/state.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/templates/summary.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/venues/acm-cs.yaml +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/venues/arxiv-ml.yaml +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/venues/ieee-cs.yaml +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/venues/nature.yaml +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/venues/thesis-chapter.yaml +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/create-outline.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/create-outline.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/discuss-section.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/discuss-section.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/execute-section.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/execute-section.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/list-assumptions.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/list-assumptions.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/lit-review-phase.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/lit-review-phase.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/map-project.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/map-project.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/plan-section.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/plan-section.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/research-gap.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/research-gap.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/resume-paper.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/resume-paper.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/review-section.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/review-section.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/submit-draft.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/submit-draft.wcn.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/transition.md +0 -0
- /package/{write-the-f-paper → core/write-the-f-paper}/workflows/transition.wcn.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/analyze-bib.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/check-refs.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/create-outline.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/discuss-section.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/export-latex.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/insert-section.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/list-assumptions.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/map-project.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/new-paper.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/pause-writing.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/plan-revision.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/plan-section.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/polish-prose.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/progress.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/remove-section.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/research-gap.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/resume-writing.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/review-section.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/submit-milestone.md +0 -0
- /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
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 (
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
185
|
-
|
|
159
|
+
if (subcommand === 'status') {
|
|
160
|
+
if (!hasQuiet) console.log(banner);
|
|
161
|
+
await showStatus(options, pkg);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
186
164
|
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
|
|
165
|
+
if (subcommand === 'doctor') {
|
|
166
|
+
if (!hasQuiet) console.log(banner);
|
|
167
|
+
await runDoctor(options);
|
|
168
|
+
return;
|
|
190
169
|
}
|
|
191
170
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
177
|
+
// Default: Install
|
|
178
|
+
if (!hasQuiet) console.log(banner);
|
|
263
179
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
|
198
|
+
await install(true, false, options, pkg);
|
|
352
199
|
}
|
|
353
200
|
}
|
|
354
201
|
|
|
355
202
|
main().catch(err => {
|
|
356
|
-
|
|
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;
|