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.
@@ -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,14 @@
1
+ /**
2
+ * Utils index
3
+ */
4
+
5
+ const { Logger, colors } = require('./logger');
6
+ const { Platform } = require('./platform');
7
+ const { Prompts } = require('./prompts');
8
+
9
+ module.exports = {
10
+ Logger,
11
+ colors,
12
+ Platform,
13
+ Prompts,
14
+ };
@@ -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
+ }