zuppaclaude 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +487 -0
- package/bin/zuppaclaude.js +147 -0
- package/lib/components/claudehud.js +198 -0
- package/lib/components/claudez.js +205 -0
- package/lib/components/config.js +143 -0
- package/lib/components/index.js +17 -0
- package/lib/components/speckit.js +99 -0
- package/lib/components/superclaude.js +130 -0
- package/lib/index.js +17 -0
- package/lib/installer.js +241 -0
- package/lib/settings.js +243 -0
- package/lib/uninstaller.js +119 -0
- package/lib/utils/index.js +14 -0
- package/lib/utils/logger.js +79 -0
- package/lib/utils/platform.js +217 -0
- package/lib/utils/prompts.js +134 -0
- package/package.json +43 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ZuppaClaude Uninstaller
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { Logger } = require('./utils/logger');
|
|
6
|
+
const { Prompts } = require('./utils/prompts');
|
|
7
|
+
const { Settings } = require('./settings');
|
|
8
|
+
const {
|
|
9
|
+
SuperClaudeInstaller,
|
|
10
|
+
SpecKitInstaller,
|
|
11
|
+
ConfigInstaller,
|
|
12
|
+
ClaudeZInstaller,
|
|
13
|
+
ClaudeHUDInstaller
|
|
14
|
+
} = require('./components');
|
|
15
|
+
|
|
16
|
+
class Uninstaller {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.logger = new Logger();
|
|
19
|
+
this.prompts = new Prompts();
|
|
20
|
+
this.settings = new Settings();
|
|
21
|
+
|
|
22
|
+
// Component installers
|
|
23
|
+
this.superClaude = new SuperClaudeInstaller();
|
|
24
|
+
this.specKit = new SpecKitInstaller();
|
|
25
|
+
this.config = new ConfigInstaller();
|
|
26
|
+
this.claudeZ = new ClaudeZInstaller();
|
|
27
|
+
this.claudeHUD = new ClaudeHUDInstaller();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Run the uninstaller
|
|
32
|
+
*/
|
|
33
|
+
async run() {
|
|
34
|
+
console.log('');
|
|
35
|
+
console.log('\x1b[35m╔═══════════════════════════════════════════════════════════════════╗\x1b[0m');
|
|
36
|
+
console.log('\x1b[35m║ ZuppaClaude Uninstaller ║\x1b[0m');
|
|
37
|
+
console.log('\x1b[35m╚═══════════════════════════════════════════════════════════════════╝\x1b[0m');
|
|
38
|
+
console.log('');
|
|
39
|
+
|
|
40
|
+
// Confirm uninstallation
|
|
41
|
+
const confirm = await this.prompts.confirm('Are you sure you want to uninstall ZuppaClaude?', false);
|
|
42
|
+
if (!confirm) {
|
|
43
|
+
this.logger.info('Uninstallation cancelled');
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Ask about settings preservation
|
|
48
|
+
const preserveSettings = await this.prompts.confirm('Preserve settings for future reinstall?', true);
|
|
49
|
+
|
|
50
|
+
console.log('');
|
|
51
|
+
this.logger.info('Removing components...');
|
|
52
|
+
console.log('');
|
|
53
|
+
|
|
54
|
+
// Uninstall components
|
|
55
|
+
const results = {
|
|
56
|
+
superClaude: this.superClaude.uninstall(),
|
|
57
|
+
specKit: this.specKit.uninstall(),
|
|
58
|
+
config: this.config.uninstall(),
|
|
59
|
+
claudeZ: this.claudeZ.uninstall(),
|
|
60
|
+
claudeHUD: this.claudeHUD.uninstall()
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Handle settings
|
|
64
|
+
if (!preserveSettings) {
|
|
65
|
+
this.settings.reset();
|
|
66
|
+
this.logger.success('Settings removed');
|
|
67
|
+
} else {
|
|
68
|
+
this.logger.info('Settings preserved for future reinstall');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Print summary
|
|
72
|
+
this.printSummary(results, preserveSettings);
|
|
73
|
+
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Print uninstallation summary
|
|
79
|
+
*/
|
|
80
|
+
printSummary(results, settingsPreserved) {
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log('═══════════════════════════════════════════════════════════════════');
|
|
83
|
+
console.log(' Uninstallation Complete');
|
|
84
|
+
console.log('═══════════════════════════════════════════════════════════════════');
|
|
85
|
+
console.log('');
|
|
86
|
+
|
|
87
|
+
const components = [
|
|
88
|
+
{ name: 'SuperClaude', removed: results.superClaude },
|
|
89
|
+
{ name: 'Spec Kit', removed: results.specKit },
|
|
90
|
+
{ name: 'CLAUDE.md', removed: results.config },
|
|
91
|
+
{ name: 'Claude-Z', removed: results.claudeZ },
|
|
92
|
+
{ name: 'Claude HUD', removed: results.claudeHUD }
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
let removed = 0;
|
|
96
|
+
for (const comp of components) {
|
|
97
|
+
const status = comp.removed ? '✓' : '○';
|
|
98
|
+
const color = comp.removed ? '\x1b[32m' : '\x1b[33m';
|
|
99
|
+
console.log(` ${color}[${status}]\x1b[0m ${comp.name}`);
|
|
100
|
+
if (comp.removed) removed++;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log('');
|
|
104
|
+
console.log(` Components removed: ${removed}/${components.length}`);
|
|
105
|
+
console.log('');
|
|
106
|
+
|
|
107
|
+
if (settingsPreserved) {
|
|
108
|
+
console.log(' Settings preserved. Run "zuppaclaude install" to restore.');
|
|
109
|
+
} else {
|
|
110
|
+
console.log(' All settings have been removed.');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log('');
|
|
114
|
+
console.log(' To reinstall: npx zuppaclaude install');
|
|
115
|
+
console.log('');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = { Uninstaller };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger utility with colored output
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const colors = {
|
|
6
|
+
reset: '\x1b[0m',
|
|
7
|
+
red: '\x1b[31m',
|
|
8
|
+
green: '\x1b[32m',
|
|
9
|
+
yellow: '\x1b[33m',
|
|
10
|
+
blue: '\x1b[34m',
|
|
11
|
+
magenta: '\x1b[35m',
|
|
12
|
+
cyan: '\x1b[36m',
|
|
13
|
+
white: '\x1b[37m',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
class Logger {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.colors = colors;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
banner() {
|
|
22
|
+
console.log(`${colors.magenta}
|
|
23
|
+
╔═══════════════════════════════════════════════════════════════════╗
|
|
24
|
+
║ ║
|
|
25
|
+
║ ███████╗██╗ ██╗██████╗ ██████╗ █████╗ ║
|
|
26
|
+
║ ╚══███╔╝██║ ██║██╔══██╗██╔══██╗██╔══██╗ ║
|
|
27
|
+
║ ███╔╝ ██║ ██║██████╔╝██████╔╝███████║ ║
|
|
28
|
+
║ ███╔╝ ██║ ██║██╔═══╝ ██╔═══╝ ██╔══██║ ║
|
|
29
|
+
║ ███████╗╚██████╔╝██║ ██║ ██║ ██║ ║
|
|
30
|
+
║ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ CLAUDE ║
|
|
31
|
+
║ ║
|
|
32
|
+
║ Claude Code Power-Up Installer ║
|
|
33
|
+
║ ║
|
|
34
|
+
╚═══════════════════════════════════════════════════════════════════╝
|
|
35
|
+
${colors.reset}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
step(message) {
|
|
39
|
+
console.log(`\n${colors.cyan}══════════════════════════════════════════════════════════════${colors.reset}`);
|
|
40
|
+
console.log(`${colors.cyan} ${message}${colors.reset}`);
|
|
41
|
+
console.log(`${colors.cyan}══════════════════════════════════════════════════════════════${colors.reset}\n`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
success(message) {
|
|
45
|
+
console.log(`${colors.green}[✓]${colors.reset} ${message}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
error(message) {
|
|
49
|
+
console.log(`${colors.red}[✗]${colors.reset} ${message}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
warning(message) {
|
|
53
|
+
console.log(`${colors.yellow}[!]${colors.reset} ${message}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
info(message) {
|
|
57
|
+
console.log(`${colors.blue}[i]${colors.reset} ${message}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
log(message) {
|
|
61
|
+
console.log(message);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
newline() {
|
|
65
|
+
console.log('');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
box(lines, color = 'green') {
|
|
69
|
+
const c = colors[color] || colors.green;
|
|
70
|
+
console.log(`${c}╔═══════════════════════════════════════════════════════════════════╗${colors.reset}`);
|
|
71
|
+
for (const line of lines) {
|
|
72
|
+
const paddedLine = line.padEnd(67);
|
|
73
|
+
console.log(`${c}║${colors.reset} ${paddedLine}${c}║${colors.reset}`);
|
|
74
|
+
}
|
|
75
|
+
console.log(`${c}╚═══════════════════════════════════════════════════════════════════╝${colors.reset}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = { Logger, colors };
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform detection and utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { execSync, spawn } = require('child_process');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
|
|
10
|
+
class Platform {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.platform = os.platform();
|
|
13
|
+
this.homedir = os.homedir();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get isWindows() {
|
|
17
|
+
return this.platform === 'win32';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get isMac() {
|
|
21
|
+
return this.platform === 'darwin';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get isLinux() {
|
|
25
|
+
return this.platform === 'linux';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get claudeDir() {
|
|
29
|
+
return path.join(this.homedir, '.claude');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get commandsDir() {
|
|
33
|
+
return path.join(this.claudeDir, 'commands');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get localBin() {
|
|
37
|
+
return path.join(this.homedir, '.local', 'bin');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get configDir() {
|
|
41
|
+
return path.join(this.homedir, '.config');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get zaiConfigDir() {
|
|
45
|
+
return path.join(this.configDir, 'zai');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get zuppaconfigDir() {
|
|
49
|
+
return path.join(this.configDir, 'zuppaclaude');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Check if a command exists
|
|
54
|
+
*/
|
|
55
|
+
commandExists(command) {
|
|
56
|
+
try {
|
|
57
|
+
if (this.isWindows) {
|
|
58
|
+
execSync(`where ${command}`, { stdio: 'ignore' });
|
|
59
|
+
} else {
|
|
60
|
+
execSync(`which ${command}`, { stdio: 'ignore' });
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
} catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Execute a command and return output
|
|
70
|
+
*/
|
|
71
|
+
exec(command, options = {}) {
|
|
72
|
+
try {
|
|
73
|
+
return execSync(command, {
|
|
74
|
+
encoding: 'utf8',
|
|
75
|
+
stdio: options.silent ? 'pipe' : 'inherit',
|
|
76
|
+
...options,
|
|
77
|
+
});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (options.throwOnError !== false) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Execute a command asynchronously
|
|
88
|
+
*/
|
|
89
|
+
execAsync(command, args = [], options = {}) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const proc = spawn(command, args, {
|
|
92
|
+
stdio: options.silent ? 'pipe' : 'inherit',
|
|
93
|
+
shell: true,
|
|
94
|
+
...options,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
let stdout = '';
|
|
98
|
+
let stderr = '';
|
|
99
|
+
|
|
100
|
+
if (proc.stdout) {
|
|
101
|
+
proc.stdout.on('data', (data) => {
|
|
102
|
+
stdout += data.toString();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (proc.stderr) {
|
|
107
|
+
proc.stderr.on('data', (data) => {
|
|
108
|
+
stderr += data.toString();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
proc.on('close', (code) => {
|
|
113
|
+
if (code === 0) {
|
|
114
|
+
resolve({ stdout, stderr, code });
|
|
115
|
+
} else {
|
|
116
|
+
reject(new Error(`Command failed with code ${code}: ${stderr || stdout}`));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
proc.on('error', reject);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Ensure directory exists
|
|
126
|
+
*/
|
|
127
|
+
ensureDir(dirPath) {
|
|
128
|
+
if (!fs.existsSync(dirPath)) {
|
|
129
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Get Python command (python3 or python)
|
|
135
|
+
*/
|
|
136
|
+
getPythonCommand() {
|
|
137
|
+
if (this.commandExists('python3')) return 'python3';
|
|
138
|
+
if (this.commandExists('python')) return 'python';
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get pip command (pip3, pip, or via python -m pip)
|
|
144
|
+
*/
|
|
145
|
+
getPipCommand() {
|
|
146
|
+
if (this.commandExists('uv')) return 'uv tool install';
|
|
147
|
+
if (this.commandExists('pipx')) return 'pipx install';
|
|
148
|
+
if (this.commandExists('pip3')) return 'pip3 install --user';
|
|
149
|
+
if (this.commandExists('pip')) return 'pip install --user';
|
|
150
|
+
const python = this.getPythonCommand();
|
|
151
|
+
if (python) return `${python} -m pip install --user`;
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get pip uninstall command
|
|
157
|
+
*/
|
|
158
|
+
getPipUninstallCommand() {
|
|
159
|
+
if (this.commandExists('uv')) return 'uv tool uninstall';
|
|
160
|
+
if (this.commandExists('pipx')) return 'pipx uninstall';
|
|
161
|
+
if (this.commandExists('pip3')) return 'pip3 uninstall -y';
|
|
162
|
+
if (this.commandExists('pip')) return 'pip uninstall -y';
|
|
163
|
+
const python = this.getPythonCommand();
|
|
164
|
+
if (python) return `${python} -m pip uninstall -y`;
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Download a file
|
|
170
|
+
*/
|
|
171
|
+
async download(url, destPath) {
|
|
172
|
+
const https = require('https');
|
|
173
|
+
const http = require('http');
|
|
174
|
+
|
|
175
|
+
return new Promise((resolve, reject) => {
|
|
176
|
+
const protocol = url.startsWith('https') ? https : http;
|
|
177
|
+
const file = fs.createWriteStream(destPath);
|
|
178
|
+
|
|
179
|
+
const request = protocol.get(url, (response) => {
|
|
180
|
+
// Handle redirects
|
|
181
|
+
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
182
|
+
file.close();
|
|
183
|
+
fs.unlinkSync(destPath);
|
|
184
|
+
this.download(response.headers.location, destPath)
|
|
185
|
+
.then(resolve)
|
|
186
|
+
.catch(reject);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (response.statusCode !== 200) {
|
|
191
|
+
file.close();
|
|
192
|
+
fs.unlinkSync(destPath);
|
|
193
|
+
reject(new Error(`Failed to download: ${response.statusCode}`));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
response.pipe(file);
|
|
198
|
+
file.on('finish', () => {
|
|
199
|
+
file.close();
|
|
200
|
+
resolve(destPath);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
request.on('error', (err) => {
|
|
205
|
+
fs.unlink(destPath, () => {});
|
|
206
|
+
reject(err);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
file.on('error', (err) => {
|
|
210
|
+
fs.unlink(destPath, () => {});
|
|
211
|
+
reject(err);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
module.exports = { Platform };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User prompts utility
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const readline = require('readline');
|
|
6
|
+
|
|
7
|
+
class Prompts {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.rl = null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
createInterface() {
|
|
13
|
+
if (!this.rl) {
|
|
14
|
+
this.rl = readline.createInterface({
|
|
15
|
+
input: process.stdin,
|
|
16
|
+
output: process.stdout,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return this.rl;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
close() {
|
|
23
|
+
if (this.rl) {
|
|
24
|
+
this.rl.close();
|
|
25
|
+
this.rl = null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Ask a yes/no question
|
|
31
|
+
*/
|
|
32
|
+
async confirm(question, defaultYes = true) {
|
|
33
|
+
const rl = this.createInterface();
|
|
34
|
+
const suffix = defaultYes ? '[Y/n]' : '[y/N]';
|
|
35
|
+
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
rl.question(`${question} ${suffix} `, (answer) => {
|
|
38
|
+
const normalized = answer.trim().toLowerCase();
|
|
39
|
+
if (normalized === '') {
|
|
40
|
+
resolve(defaultYes);
|
|
41
|
+
} else {
|
|
42
|
+
resolve(normalized === 'y' || normalized === 'yes');
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Ask for text input
|
|
50
|
+
*/
|
|
51
|
+
async input(question, defaultValue = '') {
|
|
52
|
+
const rl = this.createInterface();
|
|
53
|
+
const suffix = defaultValue ? ` [${defaultValue}]` : '';
|
|
54
|
+
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
rl.question(`${question}${suffix}: `, (answer) => {
|
|
57
|
+
resolve(answer.trim() || defaultValue);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Ask for password (hidden input)
|
|
64
|
+
*/
|
|
65
|
+
async password(question) {
|
|
66
|
+
const rl = this.createInterface();
|
|
67
|
+
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
// Disable echo
|
|
70
|
+
if (process.stdin.isTTY) {
|
|
71
|
+
process.stdin.setRawMode(true);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let password = '';
|
|
75
|
+
process.stdout.write(`${question}: `);
|
|
76
|
+
|
|
77
|
+
const onData = (char) => {
|
|
78
|
+
char = char.toString();
|
|
79
|
+
|
|
80
|
+
switch (char) {
|
|
81
|
+
case '\n':
|
|
82
|
+
case '\r':
|
|
83
|
+
case '\u0004': // Ctrl+D
|
|
84
|
+
if (process.stdin.isTTY) {
|
|
85
|
+
process.stdin.setRawMode(false);
|
|
86
|
+
}
|
|
87
|
+
process.stdin.removeListener('data', onData);
|
|
88
|
+
console.log('');
|
|
89
|
+
resolve(password);
|
|
90
|
+
break;
|
|
91
|
+
case '\u0003': // Ctrl+C
|
|
92
|
+
if (process.stdin.isTTY) {
|
|
93
|
+
process.stdin.setRawMode(false);
|
|
94
|
+
}
|
|
95
|
+
process.exit();
|
|
96
|
+
break;
|
|
97
|
+
case '\u007F': // Backspace
|
|
98
|
+
password = password.slice(0, -1);
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
password += char;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
process.stdin.on('data', onData);
|
|
106
|
+
process.stdin.resume();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Ask a multiple choice question
|
|
112
|
+
*/
|
|
113
|
+
async select(question, options) {
|
|
114
|
+
const rl = this.createInterface();
|
|
115
|
+
|
|
116
|
+
console.log(`\n${question}`);
|
|
117
|
+
options.forEach((opt, i) => {
|
|
118
|
+
console.log(` ${i + 1}) ${opt}`);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return new Promise((resolve) => {
|
|
122
|
+
rl.question('Select option: ', (answer) => {
|
|
123
|
+
const index = parseInt(answer, 10) - 1;
|
|
124
|
+
if (index >= 0 && index < options.length) {
|
|
125
|
+
resolve(index);
|
|
126
|
+
} else {
|
|
127
|
+
resolve(0); // Default to first option
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = { Prompts };
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zuppaclaude",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code power-up installer - SuperClaude + Spec Kit + Claude-Z + Claude HUD",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"zuppaclaude": "bin/zuppaclaude.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/zuppaclaude.js",
|
|
11
|
+
"test": "node bin/zuppaclaude.js --help"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"claude",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"anthropic",
|
|
17
|
+
"ai",
|
|
18
|
+
"cli",
|
|
19
|
+
"superclaude",
|
|
20
|
+
"spec-kit",
|
|
21
|
+
"claude-z",
|
|
22
|
+
"mcp"
|
|
23
|
+
],
|
|
24
|
+
"author": "Hasan Kaan Tan",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/hasankaantan/zuppaclaude.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/hasankaantan/zuppaclaude/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/hasankaantan/zuppaclaude#readme",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=16.0.0"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"bin",
|
|
39
|
+
"lib",
|
|
40
|
+
"assets"
|
|
41
|
+
],
|
|
42
|
+
"dependencies": {}
|
|
43
|
+
}
|