wtf-p 0.2.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 +6 -2
- 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 +78 -629
- package/bin/lib/manifest.js +41 -0
- package/bin/lib/utils.js +37 -17
- package/bin/lib/wcn-compiler.js +226 -0
- package/bin/uninstall.js +51 -17
- 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 +12 -4
- package/vendors/claude/.claude-plugin/plugin.json +12 -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 +3 -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/github-contrib.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/contribute.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/report-bug.md +0 -0
- /package/{commands → vendors/claude/commands}/wtfp/request-feature.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,35 +1,29 @@
|
|
|
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
|
-
|
|
7
4
|
const {
|
|
8
|
-
expandTilde,
|
|
9
|
-
normalizePath,
|
|
10
|
-
isValidPath,
|
|
11
|
-
getClaudeDir,
|
|
12
|
-
getPathLabel,
|
|
13
|
-
readInstalledVersion,
|
|
14
|
-
writeVersionFile,
|
|
15
|
-
detectInstallation,
|
|
16
|
-
collectFiles,
|
|
17
|
-
createColors,
|
|
18
5
|
createOutput,
|
|
19
6
|
createRL,
|
|
20
7
|
prompt,
|
|
21
|
-
getBackupPath
|
|
22
8
|
} = require('./lib/utils');
|
|
23
9
|
|
|
24
10
|
// Get version from package.json
|
|
25
11
|
const pkg = require('../package.json');
|
|
26
12
|
|
|
27
|
-
//
|
|
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 ============
|
|
28
21
|
|
|
29
22
|
const args = process.argv.slice(2);
|
|
30
23
|
|
|
31
|
-
// Detect subcommand
|
|
32
|
-
const
|
|
24
|
+
// Detect subcommand
|
|
25
|
+
const subcommands = ['status', 'doctor', 'update'];
|
|
26
|
+
const subcommand = args.find(arg => !arg.startsWith('-') && subcommands.includes(arg));
|
|
33
27
|
const subcommandIndex = args.indexOf(subcommand);
|
|
34
28
|
if (subcommandIndex !== -1) {
|
|
35
29
|
args.splice(subcommandIndex, 1);
|
|
@@ -49,70 +43,62 @@ const hasVerbose = args.includes('--verbose');
|
|
|
49
43
|
const hasBeginner = args.includes('--beginner');
|
|
50
44
|
const hasAdvanced = args.includes('--advanced');
|
|
51
45
|
|
|
52
|
-
// Parse --config-dir argument
|
|
53
46
|
function parseConfigDirArg() {
|
|
54
47
|
const configDirIndex = args.findIndex(arg => arg === '--config-dir' || arg === '-c');
|
|
55
48
|
if (configDirIndex !== -1) {
|
|
56
49
|
const nextArg = args[configDirIndex + 1];
|
|
57
|
-
if (
|
|
58
|
-
console.error(' --config-dir requires a path argument');
|
|
59
|
-
process.exit(1);
|
|
60
|
-
}
|
|
61
|
-
return nextArg;
|
|
50
|
+
if (nextArg && !nextArg.startsWith('-')) return nextArg;
|
|
62
51
|
}
|
|
63
52
|
const configDirArg = args.find(arg => arg.startsWith('--config-dir=') || arg.startsWith('-c='));
|
|
64
|
-
if (configDirArg)
|
|
65
|
-
return configDirArg.split('=')[1];
|
|
66
|
-
}
|
|
53
|
+
if (configDirArg) return configDirArg.split('=')[1];
|
|
67
54
|
return null;
|
|
68
55
|
}
|
|
69
56
|
|
|
70
|
-
// Parse --only argument
|
|
71
57
|
function parseOnlyArg() {
|
|
72
58
|
const onlyArg = args.find(arg => arg.startsWith('--only='));
|
|
73
|
-
if (onlyArg)
|
|
74
|
-
const value = onlyArg.split('=')[1];
|
|
75
|
-
if (!['commands', 'workflows', 'all'].includes(value)) {
|
|
76
|
-
console.error(' --only must be: commands, workflows, or all');
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
return value;
|
|
80
|
-
}
|
|
59
|
+
if (onlyArg) return onlyArg.split('=')[1];
|
|
81
60
|
return 'all';
|
|
82
61
|
}
|
|
83
62
|
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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);
|
|
94
79
|
const out = createOutput({ quiet: hasQuiet, verbose: hasVerbose, useColors });
|
|
95
|
-
|
|
80
|
+
options.out = out;
|
|
96
81
|
|
|
97
|
-
// ============ Banner ============
|
|
82
|
+
// ============ Banner ============
|
|
98
83
|
|
|
99
84
|
const banner = `
|
|
100
|
-
${
|
|
101
|
-
${
|
|
102
|
-
${
|
|
103
|
-
${
|
|
104
|
-
${
|
|
105
|
-
${
|
|
106
|
-
|
|
107
|
-
${
|
|
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}`)}
|
|
108
93
|
Context engineering for academic writing.
|
|
109
94
|
`;
|
|
110
95
|
|
|
111
|
-
// ============ Help Text ============
|
|
96
|
+
// ============ Help Text ============
|
|
112
97
|
|
|
113
98
|
function showHelp() {
|
|
99
|
+
const c = out.colors;
|
|
114
100
|
console.log(banner);
|
|
115
|
-
console.log(` ${c.yellow('Usage:')} npx wtf-p [command] [options]
|
|
101
|
+
console.log(` ${c.yellow('Usage:')} npx wtf-p [command] [options]
|
|
116
102
|
|
|
117
103
|
${c.yellow('Commands:')}
|
|
118
104
|
${c.cyan('status')} Show current installation state
|
|
@@ -135,7 +121,6 @@ function showHelp() {
|
|
|
135
121
|
${c.cyan('--verbose')} Show detailed progress
|
|
136
122
|
|
|
137
123
|
${c.yellow('Other:')}
|
|
138
|
-
${c.cyan('--list')} Preview what would be installed
|
|
139
124
|
${c.cyan('-v, --version')} Show version information
|
|
140
125
|
${c.cyan('-h, --help')} Show this help message
|
|
141
126
|
|
|
@@ -146,612 +131,76 @@ function showHelp() {
|
|
|
146
131
|
${c.dim('# Quick global install')}
|
|
147
132
|
npx wtf-p --global --advanced
|
|
148
133
|
|
|
149
|
-
${c.dim('# Install only commands')}
|
|
150
|
-
npx wtf-p --global --only=commands
|
|
151
|
-
|
|
152
|
-
${c.dim('# Check installation status')}
|
|
153
|
-
npx wtf-p status
|
|
154
|
-
|
|
155
|
-
${c.dim('# Diagnose issues')}
|
|
156
|
-
npx wtf-p doctor
|
|
157
|
-
|
|
158
134
|
${c.yellow('After installation:')}
|
|
159
135
|
Run ${c.cyan('/wtfp:help')} in Claude Code to get started.
|
|
160
136
|
`);
|
|
161
137
|
}
|
|
162
138
|
|
|
163
|
-
// ============
|
|
164
|
-
|
|
165
|
-
async function showVersion() {
|
|
166
|
-
console.log(`wtf-p v${pkg.version}`);
|
|
167
|
-
|
|
168
|
-
// Check for updates (non-blocking)
|
|
169
|
-
try {
|
|
170
|
-
const { execSync } = require('child_process');
|
|
171
|
-
const latest = execSync('npm view wtf-p version 2>/dev/null', { encoding: 'utf8' }).trim();
|
|
172
|
-
if (latest && latest !== pkg.version) {
|
|
173
|
-
console.log(`${c.yellow('Update available:')} ${pkg.version} → ${c.green(latest)}`);
|
|
174
|
-
console.log(`Run: ${c.cyan('npx wtf-p update')}`);
|
|
175
|
-
}
|
|
176
|
-
} catch {
|
|
177
|
-
// Ignore - can't check npm
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// ============ Status Command ============
|
|
182
|
-
|
|
183
|
-
async function showStatus() {
|
|
184
|
-
if (!hasQuiet) console.log(banner);
|
|
185
|
-
|
|
186
|
-
const locations = [];
|
|
187
|
-
|
|
188
|
-
// Check global
|
|
189
|
-
const globalDir = getClaudeDir(explicitConfigDir, true);
|
|
190
|
-
const globalDetection = detectInstallation(globalDir);
|
|
191
|
-
locations.push({
|
|
192
|
-
label: 'Global',
|
|
193
|
-
path: globalDir,
|
|
194
|
-
pathLabel: getPathLabel(globalDir, true),
|
|
195
|
-
...globalDetection
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// Check local if different
|
|
199
|
-
const localDir = getClaudeDir(null, false);
|
|
200
|
-
if (localDir !== globalDir) {
|
|
201
|
-
const localDetection = detectInstallation(localDir);
|
|
202
|
-
locations.push({
|
|
203
|
-
label: 'Local',
|
|
204
|
-
path: localDir,
|
|
205
|
-
pathLabel: getPathLabel(localDir, false),
|
|
206
|
-
...localDetection
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
out.log(` ${c.yellow('WTF-P Installation Status')}\n`);
|
|
211
|
-
|
|
212
|
-
for (const loc of locations) {
|
|
213
|
-
const installed = loc.hasCommands || loc.hasSkill;
|
|
214
|
-
|
|
215
|
-
out.log(` ${c.cyan(loc.label)} ${c.dim(`(${loc.pathLabel})`)}`);
|
|
216
|
-
|
|
217
|
-
if (!installed) {
|
|
218
|
-
out.log(` ${c.dim('Not installed')}\n`);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Version
|
|
223
|
-
if (loc.version) {
|
|
224
|
-
const versionLabel = loc.version === 'legacy' ? 'legacy (pre-0.2.0)' : loc.version;
|
|
225
|
-
out.log(` Version: ${c.green(versionLabel)}`);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Components
|
|
229
|
-
const components = [];
|
|
230
|
-
if (loc.hasCommands) components.push(`commands (${loc.commandFiles.length} files)`);
|
|
231
|
-
if (loc.hasSkill) components.push(`workflows (${loc.skillFiles.length} files)`);
|
|
232
|
-
out.log(` Installed: ${components.join(', ')}`);
|
|
233
|
-
|
|
234
|
-
// Warnings
|
|
235
|
-
if (loc.partial) {
|
|
236
|
-
out.log(` ${c.yellow('⚠ Partial installation detected')}`);
|
|
237
|
-
}
|
|
238
|
-
if (loc.corrupt) {
|
|
239
|
-
out.log(` ${c.red('⚠ Corrupt version file')}`);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
out.log('');
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Check for updates
|
|
246
|
-
try {
|
|
247
|
-
const { execSync } = require('child_process');
|
|
248
|
-
const latest = execSync('npm view wtf-p version 2>/dev/null', { encoding: 'utf8' }).trim();
|
|
249
|
-
if (latest && latest !== pkg.version) {
|
|
250
|
-
out.log(` ${c.yellow('Update available:')} ${pkg.version} → ${c.green(latest)}`);
|
|
251
|
-
out.log(` Run: ${c.cyan('npx wtf-p update --global')}\n`);
|
|
252
|
-
}
|
|
253
|
-
} catch {
|
|
254
|
-
// Ignore
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// ============ Doctor Command ============
|
|
259
|
-
|
|
260
|
-
async function runDoctor() {
|
|
261
|
-
if (!hasQuiet) console.log(banner);
|
|
262
|
-
|
|
263
|
-
out.log(` ${c.yellow('WTF-P Installation Doctor')}\n`);
|
|
264
|
-
|
|
265
|
-
const issues = [];
|
|
266
|
-
const checks = [];
|
|
267
|
-
|
|
268
|
-
// Check 1: Node.js version
|
|
269
|
-
const nodeVersion = process.version;
|
|
270
|
-
const nodeMajor = parseInt(nodeVersion.slice(1).split('.')[0], 10);
|
|
271
|
-
if (nodeMajor < 16) {
|
|
272
|
-
issues.push(`Node.js ${nodeVersion} is below minimum (16.7.0)`);
|
|
273
|
-
checks.push({ name: 'Node.js version', status: 'fail', detail: nodeVersion });
|
|
274
|
-
} else {
|
|
275
|
-
checks.push({ name: 'Node.js version', status: 'pass', detail: nodeVersion });
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Check 2: Claude directory exists
|
|
279
|
-
const globalDir = getClaudeDir(explicitConfigDir, true);
|
|
280
|
-
if (fs.existsSync(globalDir)) {
|
|
281
|
-
checks.push({ name: 'Claude config directory', status: 'pass', detail: getPathLabel(globalDir, true) });
|
|
282
|
-
} else {
|
|
283
|
-
checks.push({ name: 'Claude config directory', status: 'warn', detail: 'Does not exist (will be created on install)' });
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Check 3: Write permissions
|
|
287
|
-
const testDir = globalDir.replace(/\.claude$/, '.claude-test-' + Date.now());
|
|
288
|
-
try {
|
|
289
|
-
fs.mkdirSync(testDir, { recursive: true });
|
|
290
|
-
fs.writeFileSync(path.join(testDir, 'test'), 'test');
|
|
291
|
-
fs.rmSync(testDir, { recursive: true });
|
|
292
|
-
checks.push({ name: 'Write permissions', status: 'pass', detail: 'Can write to config directory' });
|
|
293
|
-
} catch (err) {
|
|
294
|
-
issues.push(`Cannot write to ${getPathLabel(globalDir, true)}: ${err.message}`);
|
|
295
|
-
checks.push({ name: 'Write permissions', status: 'fail', detail: err.message });
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Check 4: Installation state
|
|
299
|
-
const detection = detectInstallation(globalDir);
|
|
300
|
-
if (detection.hasCommands || detection.hasSkill) {
|
|
301
|
-
if (detection.partial) {
|
|
302
|
-
issues.push('Partial installation detected - some files are missing');
|
|
303
|
-
checks.push({ name: 'Installation integrity', status: 'warn', detail: 'Partial install' });
|
|
304
|
-
} else if (detection.corrupt) {
|
|
305
|
-
issues.push('Version file is corrupt - reinstall recommended');
|
|
306
|
-
checks.push({ name: 'Installation integrity', status: 'warn', detail: 'Corrupt version file' });
|
|
307
|
-
} else {
|
|
308
|
-
checks.push({ name: 'Installation integrity', status: 'pass', detail: `v${detection.version}` });
|
|
309
|
-
}
|
|
310
|
-
} else {
|
|
311
|
-
checks.push({ name: 'Installation integrity', status: 'info', detail: 'Not installed' });
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Check 5: CLAUDE_CONFIG_DIR env var
|
|
315
|
-
const configDirEnv = process.env.CLAUDE_CONFIG_DIR;
|
|
316
|
-
if (configDirEnv) {
|
|
317
|
-
const expanded = normalizePath(configDirEnv);
|
|
318
|
-
if (fs.existsSync(expanded)) {
|
|
319
|
-
checks.push({ name: 'CLAUDE_CONFIG_DIR', status: 'pass', detail: expanded });
|
|
320
|
-
} else {
|
|
321
|
-
issues.push(`CLAUDE_CONFIG_DIR points to non-existent path: ${configDirEnv}`);
|
|
322
|
-
checks.push({ name: 'CLAUDE_CONFIG_DIR', status: 'warn', detail: `${configDirEnv} (does not exist)` });
|
|
323
|
-
}
|
|
324
|
-
} else {
|
|
325
|
-
checks.push({ name: 'CLAUDE_CONFIG_DIR', status: 'info', detail: 'Not set (using default)' });
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Output results
|
|
329
|
-
for (const check of checks) {
|
|
330
|
-
let icon, color;
|
|
331
|
-
switch (check.status) {
|
|
332
|
-
case 'pass': icon = '✓'; color = c.green; break;
|
|
333
|
-
case 'fail': icon = '✗'; color = c.red; break;
|
|
334
|
-
case 'warn': icon = '⚠'; color = c.yellow; break;
|
|
335
|
-
default: icon = 'ℹ'; color = c.cyan;
|
|
336
|
-
}
|
|
337
|
-
out.log(` ${color(icon)} ${check.name}: ${c.dim(check.detail)}`);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
out.log('');
|
|
341
|
-
|
|
342
|
-
if (issues.length > 0) {
|
|
343
|
-
out.log(` ${c.yellow('Issues found:')}`);
|
|
344
|
-
for (const issue of issues) {
|
|
345
|
-
out.log(` ${c.yellow('•')} ${issue}`);
|
|
346
|
-
}
|
|
347
|
-
out.log('');
|
|
348
|
-
out.log(` ${c.dim('Run')} ${c.cyan('npx wtf-p --global --force')} ${c.dim('to reinstall')}\n`);
|
|
349
|
-
} else {
|
|
350
|
-
out.log(` ${c.green('No issues found!')}\n`);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// ============ Update Command ============
|
|
355
|
-
|
|
356
|
-
async function runUpdate() {
|
|
357
|
-
if (!hasQuiet) console.log(banner);
|
|
358
|
-
|
|
359
|
-
out.log(` ${c.yellow('Checking for updates...')}\n`);
|
|
360
|
-
|
|
361
|
-
// Find installed location
|
|
362
|
-
const globalDir = getClaudeDir(explicitConfigDir, true);
|
|
363
|
-
const localDir = getClaudeDir(null, false);
|
|
364
|
-
|
|
365
|
-
let targetDir = null;
|
|
366
|
-
let isGlobal = true;
|
|
367
|
-
|
|
368
|
-
const globalDetection = detectInstallation(globalDir);
|
|
369
|
-
const localDetection = detectInstallation(localDir);
|
|
370
|
-
|
|
371
|
-
if (hasGlobal || (!hasLocal && (globalDetection.hasCommands || globalDetection.hasSkill))) {
|
|
372
|
-
targetDir = globalDir;
|
|
373
|
-
isGlobal = true;
|
|
374
|
-
} else if (hasLocal || (localDetection.hasCommands || localDetection.hasSkill)) {
|
|
375
|
-
targetDir = localDir;
|
|
376
|
-
isGlobal = false;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (!targetDir) {
|
|
380
|
-
out.log(` ${c.yellow('No existing installation found.')}`);
|
|
381
|
-
out.log(` Run ${c.cyan('npx wtf-p --global')} to install.\n`);
|
|
382
|
-
return;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
const detection = isGlobal ? globalDetection : localDetection;
|
|
386
|
-
const currentVersion = detection.version === 'legacy' ? '0.0.0' : detection.version;
|
|
387
|
-
|
|
388
|
-
out.log(` Current: ${c.dim(`v${currentVersion}`)} in ${c.dim(getPathLabel(targetDir, isGlobal))}`);
|
|
389
|
-
out.log(` Package: ${c.dim(`v${pkg.version}`)}`);
|
|
390
|
-
|
|
391
|
-
if (pkg.version === currentVersion) {
|
|
392
|
-
out.log(`\n ${c.green('Already up to date!')}\n`);
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
out.log(`\n ${c.cyan('Updating...')}\n`);
|
|
397
|
-
|
|
398
|
-
// Run install with backup
|
|
399
|
-
await install(isGlobal, true);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// ============ List Command ============
|
|
403
|
-
|
|
404
|
-
function showList() {
|
|
405
|
-
if (!hasQuiet) console.log(banner);
|
|
406
|
-
|
|
407
|
-
const src = path.join(__dirname, '..');
|
|
408
|
-
const wtfpSrc = path.join(src, 'commands', 'wtfp');
|
|
409
|
-
const skillSrc = path.join(src, 'write-the-f-paper');
|
|
410
|
-
|
|
411
|
-
const files = [];
|
|
412
|
-
|
|
413
|
-
if (onlyInstall === 'all' || onlyInstall === 'commands') {
|
|
414
|
-
const commandFiles = collectFiles(wtfpSrc);
|
|
415
|
-
for (const f of commandFiles) {
|
|
416
|
-
files.push({ type: 'command', path: f.replace(wtfpSrc, 'commands/wtfp') });
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (onlyInstall === 'all' || onlyInstall === 'workflows') {
|
|
421
|
-
const skillFiles = collectFiles(skillSrc);
|
|
422
|
-
for (const f of skillFiles) {
|
|
423
|
-
files.push({ type: 'workflow', path: f.replace(skillSrc, 'write-the-f-paper') });
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
out.log(` ${c.yellow('Files to install:')}\n`);
|
|
428
|
-
|
|
429
|
-
const commands = files.filter(f => f.type === 'command');
|
|
430
|
-
const workflows = files.filter(f => f.type === 'workflow');
|
|
431
|
-
|
|
432
|
-
if (commands.length > 0) {
|
|
433
|
-
out.log(` ${c.cyan('Commands')} (${commands.length} files):`);
|
|
434
|
-
for (const f of commands.slice(0, 10)) {
|
|
435
|
-
out.log(` ${c.dim(f.path)}`);
|
|
436
|
-
}
|
|
437
|
-
if (commands.length > 10) {
|
|
438
|
-
out.log(` ${c.dim(`... and ${commands.length - 10} more`)}`);
|
|
439
|
-
}
|
|
440
|
-
out.log('');
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (workflows.length > 0) {
|
|
444
|
-
out.log(` ${c.cyan('Workflows')} (${workflows.length} files):`);
|
|
445
|
-
for (const f of workflows.slice(0, 10)) {
|
|
446
|
-
out.log(` ${c.dim(f.path)}`);
|
|
447
|
-
}
|
|
448
|
-
if (workflows.length > 10) {
|
|
449
|
-
out.log(` ${c.dim(`... and ${workflows.length - 10} more`)}`);
|
|
450
|
-
}
|
|
451
|
-
out.log('');
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
out.log(` Total: ${files.length} files\n`);
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// ============ Installation ============
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Process file content with path replacement
|
|
461
|
-
*/
|
|
462
|
-
function processContent(srcPath, pathPrefix) {
|
|
463
|
-
if (srcPath.endsWith('.md') || srcPath.endsWith('.json')) {
|
|
464
|
-
let content = fs.readFileSync(srcPath, 'utf8');
|
|
465
|
-
return content.replace(/~\/\.claude\//g, pathPrefix);
|
|
466
|
-
}
|
|
467
|
-
return null;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Collect files to install
|
|
472
|
-
*/
|
|
473
|
-
function collectInstallFiles(srcDir, destDir, pathPrefix, files = []) {
|
|
474
|
-
if (!fs.existsSync(srcDir)) return files;
|
|
475
|
-
|
|
476
|
-
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
477
|
-
|
|
478
|
-
for (const entry of entries) {
|
|
479
|
-
const srcPath = path.join(srcDir, entry.name);
|
|
480
|
-
const destPath = path.join(destDir, entry.name);
|
|
481
|
-
|
|
482
|
-
if (entry.isDirectory()) {
|
|
483
|
-
collectInstallFiles(srcPath, destPath, pathPrefix, files);
|
|
484
|
-
} else {
|
|
485
|
-
files.push({ src: srcPath, dest: destPath, name: entry.name });
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return files;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Install files with conflict resolution
|
|
494
|
-
*/
|
|
495
|
-
async function installWithConflictResolution(files, pathPrefix, claudeDir) {
|
|
496
|
-
const rl = createRL();
|
|
497
|
-
let globalChoice = null;
|
|
498
|
-
const stats = { installed: 0, skipped: 0, backed: 0 };
|
|
499
|
-
|
|
500
|
-
const existingFiles = files.filter(f => fs.existsSync(f.dest));
|
|
501
|
-
|
|
502
|
-
if (existingFiles.length > 0 && !hasForce && !hasBackupAll && isInteractive) {
|
|
503
|
-
const relPath = getPathLabel(claudeDir, true);
|
|
504
|
-
out.log(` ${c.yellow(`Found ${existingFiles.length} existing WTF-P file(s) in ${relPath}`)}\n`);
|
|
505
|
-
|
|
506
|
-
if (showExplanations) {
|
|
507
|
-
out.log(` ${c.dim('For each file, choose:')}`);
|
|
508
|
-
out.log(` ${c.dim('[o]verwrite - Replace with new version')}`);
|
|
509
|
-
out.log(` ${c.dim('[s]kip - Keep your existing file')}`);
|
|
510
|
-
out.log(` ${c.dim('[b]ackup - Save existing, then install new')}`);
|
|
511
|
-
out.log(` ${c.dim('[a]ll - Apply same choice to remaining')}\n`);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
for (const file of files) {
|
|
516
|
-
const exists = fs.existsSync(file.dest);
|
|
517
|
-
const relDest = file.dest.replace(os.homedir(), '~').replace(process.cwd(), '.');
|
|
518
|
-
|
|
519
|
-
fs.mkdirSync(path.dirname(file.dest), { recursive: true });
|
|
520
|
-
|
|
521
|
-
if (exists && !hasForce && !hasBackupAll) {
|
|
522
|
-
let choice = globalChoice;
|
|
523
|
-
|
|
524
|
-
if (!choice && isInteractive) {
|
|
525
|
-
const answer = await prompt(rl,
|
|
526
|
-
` ${c.yellow('?')} ${c.dim(relDest)} exists. [o]verwrite/[s]kip/[b]ackup/[a]ll: `
|
|
527
|
-
);
|
|
528
|
-
|
|
529
|
-
if (answer.startsWith('a')) {
|
|
530
|
-
const allAnswer = await prompt(rl,
|
|
531
|
-
` Apply to all: [o]verwrite/[s]kip/[b]ackup: `
|
|
532
|
-
);
|
|
533
|
-
if (allAnswer.startsWith('o')) globalChoice = 'overwrite';
|
|
534
|
-
else if (allAnswer.startsWith('b')) globalChoice = 'backup';
|
|
535
|
-
else globalChoice = 'skip';
|
|
536
|
-
choice = globalChoice;
|
|
537
|
-
} else if (answer.startsWith('o')) {
|
|
538
|
-
choice = 'overwrite';
|
|
539
|
-
} else if (answer.startsWith('b')) {
|
|
540
|
-
choice = 'backup';
|
|
541
|
-
} else {
|
|
542
|
-
choice = 'skip';
|
|
543
|
-
}
|
|
544
|
-
} else if (!choice) {
|
|
545
|
-
// Non-interactive with existing file and no force: skip
|
|
546
|
-
choice = 'skip';
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
if (choice === 'skip') {
|
|
550
|
-
out.verbose(` ${c.dim('○')} Skipped ${c.dim(relDest)}`);
|
|
551
|
-
stats.skipped++;
|
|
552
|
-
continue;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (choice === 'backup') {
|
|
556
|
-
const backupPath = getBackupPath(file.dest);
|
|
557
|
-
fs.copyFileSync(file.dest, backupPath);
|
|
558
|
-
out.log(` ${c.cyan('↻')} Backed up to ${c.dim(path.basename(backupPath))}`);
|
|
559
|
-
stats.backed++;
|
|
560
|
-
}
|
|
561
|
-
} else if (exists && hasBackupAll) {
|
|
562
|
-
const backupPath = getBackupPath(file.dest);
|
|
563
|
-
fs.copyFileSync(file.dest, backupPath);
|
|
564
|
-
out.verbose(` ${c.cyan('↻')} Backed up ${c.dim(relDest)}`);
|
|
565
|
-
stats.backed++;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
const content = processContent(file.src, pathPrefix);
|
|
569
|
-
if (content !== null) {
|
|
570
|
-
fs.writeFileSync(file.dest, content);
|
|
571
|
-
} else {
|
|
572
|
-
fs.copyFileSync(file.src, file.dest);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
if (!exists) {
|
|
576
|
-
out.verbose(` ${c.green('+')} ${c.dim(relDest)}`);
|
|
577
|
-
} else {
|
|
578
|
-
out.verbose(` ${c.green('✓')} ${c.dim(relDest)}`);
|
|
579
|
-
}
|
|
580
|
-
stats.installed++;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
rl.close();
|
|
584
|
-
return stats;
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
/**
|
|
588
|
-
* Main install function
|
|
589
|
-
*/
|
|
590
|
-
async function install(isGlobal, isUpdate = false) {
|
|
591
|
-
const src = path.join(__dirname, '..');
|
|
592
|
-
const claudeDir = getClaudeDir(explicitConfigDir, isGlobal);
|
|
593
|
-
const locationLabel = getPathLabel(claudeDir, isGlobal);
|
|
594
|
-
|
|
595
|
-
// Validate path
|
|
596
|
-
if (!isValidPath(claudeDir)) {
|
|
597
|
-
out.error(`Invalid path: ${claudeDir}`);
|
|
598
|
-
process.exit(1);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
const pathPrefix = isGlobal
|
|
602
|
-
? (explicitConfigDir ? `${claudeDir}/` : '~/.claude/')
|
|
603
|
-
: './.claude/';
|
|
604
|
-
|
|
605
|
-
if (!hasQuiet) {
|
|
606
|
-
out.log(` Installing to ${c.cyan(locationLabel)}\n`);
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// Collect files based on --only flag
|
|
610
|
-
const allFiles = [];
|
|
611
|
-
|
|
612
|
-
if (onlyInstall === 'all' || onlyInstall === 'commands') {
|
|
613
|
-
const wtfpSrc = path.join(src, 'commands', 'wtfp');
|
|
614
|
-
const wtfpDest = path.join(claudeDir, 'commands', 'wtfp');
|
|
615
|
-
collectInstallFiles(wtfpSrc, wtfpDest, pathPrefix, allFiles);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
if (onlyInstall === 'all' || onlyInstall === 'workflows') {
|
|
619
|
-
const skillSrc = path.join(src, 'write-the-f-paper');
|
|
620
|
-
const skillDest = path.join(claudeDir, 'write-the-f-paper');
|
|
621
|
-
collectInstallFiles(skillSrc, skillDest, pathPrefix, allFiles);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Install with conflict resolution
|
|
625
|
-
const stats = await installWithConflictResolution(allFiles, pathPrefix, claudeDir);
|
|
626
|
-
|
|
627
|
-
// Write version tracking file
|
|
628
|
-
writeVersionFile(claudeDir, pkg.version, allFiles);
|
|
629
|
-
|
|
630
|
-
// Summary
|
|
631
|
-
if (!hasQuiet) {
|
|
632
|
-
out.log(`
|
|
633
|
-
${c.green('Done!')} Installed: ${stats.installed}, Skipped: ${stats.skipped}, Backed up: ${stats.backed}
|
|
634
|
-
|
|
635
|
-
Run ${c.cyan('/wtfp:help')} in Claude Code to get started.
|
|
636
|
-
`);
|
|
637
|
-
|
|
638
|
-
if (showExplanations && !isUpdate) {
|
|
639
|
-
out.log(` ${c.yellow('Quick Start:')}`);
|
|
640
|
-
out.log(` ${c.cyan('/wtfp:new-paper')} Start a new academic paper`);
|
|
641
|
-
out.log(` ${c.cyan('/wtfp:progress')} Check your writing status`);
|
|
642
|
-
out.log(` ${c.cyan('/wtfp:help')} Show all commands\n`);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
/**
|
|
648
|
-
* Prompt for install location (interactive mode)
|
|
649
|
-
*/
|
|
650
|
-
async function promptLocation() {
|
|
651
|
-
const rl = createRL();
|
|
652
|
-
|
|
653
|
-
const globalPath = getClaudeDir(explicitConfigDir, true);
|
|
654
|
-
const globalLabel = getPathLabel(globalPath, true);
|
|
655
|
-
|
|
656
|
-
out.log(` ${c.yellow('Where would you like to install?')}
|
|
657
|
-
`);
|
|
658
|
-
|
|
659
|
-
if (showExplanations) {
|
|
660
|
-
out.log(` ${c.cyan('1)')} Global ${c.dim(`(${globalLabel})`)} - available in all projects`);
|
|
661
|
-
out.log(` ${c.dim('Recommended for most users')}`);
|
|
662
|
-
out.log(` ${c.cyan('2)')} Local ${c.dim('(./.claude)')} - this project only`);
|
|
663
|
-
out.log(` ${c.dim('Useful for project-specific configurations')}`);
|
|
664
|
-
} else {
|
|
665
|
-
out.log(` ${c.cyan('1)')} Global ${c.dim(`(${globalLabel})`)} - available in all projects`);
|
|
666
|
-
out.log(` ${c.cyan('2)')} Local ${c.dim('(./.claude)')} - this project only`);
|
|
667
|
-
}
|
|
668
|
-
out.log('');
|
|
669
|
-
|
|
670
|
-
const answer = await prompt(rl, ` Choice ${c.dim('[1]')}: `);
|
|
671
|
-
rl.close();
|
|
672
|
-
|
|
673
|
-
const choice = answer || '1';
|
|
674
|
-
const isGlobal = choice !== '2';
|
|
675
|
-
await install(isGlobal);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// ============ Main ============
|
|
139
|
+
// ============ Main ============
|
|
679
140
|
|
|
680
141
|
async function main() {
|
|
681
|
-
// Handle --version
|
|
682
142
|
if (hasVersion) {
|
|
683
|
-
|
|
143
|
+
console.log(`wtf-p v${pkg.version}`);
|
|
684
144
|
return;
|
|
685
145
|
}
|
|
686
146
|
|
|
687
|
-
// Handle --help
|
|
688
147
|
if (hasHelp) {
|
|
689
148
|
showHelp();
|
|
690
149
|
return;
|
|
691
150
|
}
|
|
692
151
|
|
|
693
|
-
// Handle
|
|
694
|
-
if (
|
|
695
|
-
|
|
152
|
+
// Handle --list
|
|
153
|
+
if (hasList) {
|
|
154
|
+
if (!hasQuiet) console.log(banner);
|
|
155
|
+
showList(options);
|
|
696
156
|
return;
|
|
697
157
|
}
|
|
698
158
|
|
|
699
|
-
if (subcommand === '
|
|
700
|
-
|
|
159
|
+
if (subcommand === 'status') {
|
|
160
|
+
if (!hasQuiet) console.log(banner);
|
|
161
|
+
await showStatus(options, pkg);
|
|
701
162
|
return;
|
|
702
163
|
}
|
|
703
164
|
|
|
704
|
-
if (subcommand === '
|
|
705
|
-
|
|
165
|
+
if (subcommand === 'doctor') {
|
|
166
|
+
if (!hasQuiet) console.log(banner);
|
|
167
|
+
await runDoctor(options);
|
|
706
168
|
return;
|
|
707
169
|
}
|
|
708
170
|
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
171
|
+
if (subcommand === 'update') {
|
|
172
|
+
if (!hasQuiet) console.log(banner);
|
|
173
|
+
await runUpdate(options, pkg, install);
|
|
712
174
|
return;
|
|
713
175
|
}
|
|
714
176
|
|
|
715
|
-
//
|
|
716
|
-
if (!hasQuiet)
|
|
717
|
-
console.log(banner);
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
// Validate conflicting flags
|
|
721
|
-
if (hasGlobal && hasLocal) {
|
|
722
|
-
out.error('Cannot specify both --global and --local');
|
|
723
|
-
process.exit(1);
|
|
724
|
-
}
|
|
725
|
-
if (explicitConfigDir && hasLocal) {
|
|
726
|
-
out.error('Cannot use --config-dir with --local');
|
|
727
|
-
process.exit(1);
|
|
728
|
-
}
|
|
729
|
-
if (hasForce && hasBackupAll) {
|
|
730
|
-
out.error('Cannot use both --force and --backup-all');
|
|
731
|
-
process.exit(1);
|
|
732
|
-
}
|
|
733
|
-
if (hasBeginner && hasAdvanced) {
|
|
734
|
-
out.error('Cannot use both --beginner and --advanced');
|
|
735
|
-
process.exit(1);
|
|
736
|
-
}
|
|
177
|
+
// Default: Install
|
|
178
|
+
if (!hasQuiet) console.log(banner);
|
|
737
179
|
|
|
738
|
-
// Run install
|
|
739
180
|
if (hasGlobal) {
|
|
740
|
-
await install(true);
|
|
181
|
+
await install(true, false, options, pkg);
|
|
741
182
|
} else if (hasLocal) {
|
|
742
|
-
await install(false);
|
|
743
|
-
} else if (isInteractive) {
|
|
744
|
-
|
|
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?')}
|
|
187
|
+
`);
|
|
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
|
|
190
|
+
`);
|
|
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);
|
|
745
197
|
} else {
|
|
746
|
-
|
|
747
|
-
await install(true);
|
|
198
|
+
await install(true, false, options, pkg);
|
|
748
199
|
}
|
|
749
200
|
}
|
|
750
201
|
|
|
751
202
|
main().catch(err => {
|
|
752
203
|
out.error(err.message);
|
|
753
|
-
if (hasVerbose)
|
|
754
|
-
console.error(err.stack);
|
|
755
|
-
}
|
|
204
|
+
if (hasVerbose) console.error(err.stack);
|
|
756
205
|
process.exit(1);
|
|
757
|
-
});
|
|
206
|
+
});
|