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,130 @@
1
+ /**
2
+ * SuperClaude component installer
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { Logger } = require('../utils/logger');
8
+ const { Platform } = require('../utils/platform');
9
+
10
+ const SUPERCLAUDE_REPO = 'https://github.com/SuperClaude-Org/SuperClaude_Framework.git';
11
+ const SUPERCLAUDE_ZIP = 'https://github.com/SuperClaude-Org/SuperClaude_Framework/archive/refs/heads/master.zip';
12
+
13
+ class SuperClaudeInstaller {
14
+ constructor() {
15
+ this.platform = new Platform();
16
+ this.logger = new Logger();
17
+ this.installPath = path.join(this.platform.commandsDir, 'sc');
18
+ }
19
+
20
+ /**
21
+ * Check if SuperClaude is already installed
22
+ */
23
+ isInstalled() {
24
+ return fs.existsSync(this.installPath);
25
+ }
26
+
27
+ /**
28
+ * Install SuperClaude
29
+ */
30
+ async install() {
31
+ this.logger.step('Step 2/7: Installing SuperClaude Framework');
32
+
33
+ // Ensure directories exist
34
+ this.platform.ensureDir(this.platform.commandsDir);
35
+
36
+ // Backup existing installation
37
+ if (this.isInstalled()) {
38
+ const backupPath = `${this.installPath}.backup.${Date.now()}`;
39
+ fs.renameSync(this.installPath, backupPath);
40
+ this.logger.info(`Existing installation backed up to: ${backupPath}`);
41
+ }
42
+
43
+ // Try git clone first
44
+ if (this.platform.commandExists('git')) {
45
+ try {
46
+ this.logger.info('Cloning SuperClaude repository...');
47
+ this.platform.exec(`git clone --depth 1 ${SUPERCLAUDE_REPO} "${this.installPath}"`, { silent: true });
48
+
49
+ // Remove .git folder to save space
50
+ const gitDir = path.join(this.installPath, '.git');
51
+ if (fs.existsSync(gitDir)) {
52
+ fs.rmSync(gitDir, { recursive: true });
53
+ }
54
+
55
+ this.logger.success('SuperClaude installed via git');
56
+ return true;
57
+ } catch (error) {
58
+ this.logger.warning('Git clone failed, trying ZIP download...');
59
+ }
60
+ }
61
+
62
+ // Fallback to ZIP download
63
+ try {
64
+ const tempZip = path.join(require('os').tmpdir(), 'superclaude.zip');
65
+ const tempDir = path.join(require('os').tmpdir(), 'superclaude-extract');
66
+
67
+ this.logger.info('Downloading SuperClaude ZIP...');
68
+ await this.platform.download(SUPERCLAUDE_ZIP, tempZip);
69
+
70
+ // Extract ZIP
71
+ this.logger.info('Extracting...');
72
+ if (this.platform.isWindows) {
73
+ this.platform.exec(`powershell -Command "Expand-Archive -Path '${tempZip}' -DestinationPath '${tempDir}' -Force"`, { silent: true });
74
+ } else {
75
+ this.platform.exec(`unzip -q -o "${tempZip}" -d "${tempDir}"`, { silent: true });
76
+ }
77
+
78
+ // Move extracted content to install path
79
+ const extracted = fs.readdirSync(tempDir)[0];
80
+ const extractedPath = path.join(tempDir, extracted);
81
+
82
+ this.platform.ensureDir(path.dirname(this.installPath));
83
+ fs.renameSync(extractedPath, this.installPath);
84
+
85
+ // Cleanup
86
+ fs.rmSync(tempZip, { force: true });
87
+ fs.rmSync(tempDir, { recursive: true, force: true });
88
+
89
+ this.logger.success('SuperClaude installed via ZIP');
90
+ return true;
91
+ } catch (error) {
92
+ this.logger.error(`Failed to install SuperClaude: ${error.message}`);
93
+ return false;
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Uninstall SuperClaude
99
+ */
100
+ uninstall() {
101
+ if (!this.isInstalled()) {
102
+ this.logger.warning('SuperClaude not found');
103
+ return true;
104
+ }
105
+
106
+ try {
107
+ fs.rmSync(this.installPath, { recursive: true });
108
+ this.logger.success('SuperClaude removed');
109
+ return true;
110
+ } catch (error) {
111
+ this.logger.error(`Failed to remove SuperClaude: ${error.message}`);
112
+ return false;
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Verify installation
118
+ */
119
+ verify() {
120
+ if (this.isInstalled()) {
121
+ this.logger.success('SuperClaude: Installed');
122
+ return true;
123
+ } else {
124
+ this.logger.error('SuperClaude: Not installed');
125
+ return false;
126
+ }
127
+ }
128
+ }
129
+
130
+ module.exports = { SuperClaudeInstaller };
package/lib/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * ZuppaClaude - Claude Code Enhancement Installer
3
+ */
4
+
5
+ const { Installer } = require('./installer');
6
+ const { Uninstaller } = require('./uninstaller');
7
+ const { Settings } = require('./settings');
8
+ const { Logger } = require('./utils/logger');
9
+ const { Platform } = require('./utils/platform');
10
+
11
+ module.exports = {
12
+ Installer,
13
+ Uninstaller,
14
+ Settings,
15
+ Logger,
16
+ Platform
17
+ };
@@ -0,0 +1,241 @@
1
+ /**
2
+ * ZuppaClaude Main Installer
3
+ */
4
+
5
+ const { Logger } = require('./utils/logger');
6
+ const { Platform } = require('./utils/platform');
7
+ const { Prompts } = require('./utils/prompts');
8
+ const { Settings } = require('./settings');
9
+ const {
10
+ SuperClaudeInstaller,
11
+ SpecKitInstaller,
12
+ ConfigInstaller,
13
+ ClaudeZInstaller,
14
+ ClaudeHUDInstaller
15
+ } = require('./components');
16
+
17
+ class Installer {
18
+ constructor() {
19
+ this.logger = new Logger();
20
+ this.platform = new Platform();
21
+ this.prompts = new Prompts();
22
+ this.settings = new Settings();
23
+
24
+ // Component installers
25
+ this.superClaude = new SuperClaudeInstaller();
26
+ this.specKit = new SpecKitInstaller();
27
+ this.config = new ConfigInstaller();
28
+ this.claudeZ = new ClaudeZInstaller();
29
+ this.claudeHUD = new ClaudeHUDInstaller();
30
+ }
31
+
32
+ /**
33
+ * Run the installer
34
+ */
35
+ async run() {
36
+ this.logger.banner();
37
+
38
+ // Step 1: Check dependencies
39
+ this.logger.step('Step 1/7: Checking Dependencies');
40
+ const depsOk = await this.checkDependencies();
41
+ if (!depsOk) {
42
+ this.logger.error('Dependency check failed. Please install required dependencies.');
43
+ return false;
44
+ }
45
+
46
+ // Load existing settings
47
+ const existingSettings = this.settings.load();
48
+ let useExisting = false;
49
+
50
+ if (existingSettings) {
51
+ this.logger.info('Found existing settings from previous installation');
52
+ useExisting = await this.prompts.confirm('Use previous settings?', true);
53
+ }
54
+
55
+ // Step 2: Install SuperClaude
56
+ const scInstalled = await this.superClaude.install();
57
+
58
+ // Step 3: Install Spec Kit
59
+ let installSpecKit = true;
60
+ if (useExisting && existingSettings.specKit !== undefined) {
61
+ installSpecKit = existingSettings.specKit;
62
+ } else {
63
+ installSpecKit = await this.prompts.confirm('Install Spec Kit (specify-cli)?', true);
64
+ }
65
+
66
+ let skInstalled = false;
67
+ if (installSpecKit) {
68
+ skInstalled = await this.specKit.install();
69
+ } else {
70
+ this.logger.info('Skipping Spec Kit installation');
71
+ }
72
+
73
+ // Step 4: Install Configuration
74
+ const cfgInstalled = await this.config.install();
75
+
76
+ // Step 5: Install Claude-Z (optional)
77
+ let installClaudeZ = false;
78
+ let zaiApiKey = null;
79
+
80
+ if (useExisting && existingSettings.claudeZ) {
81
+ installClaudeZ = true;
82
+ zaiApiKey = this.settings.decodeApiKey(existingSettings.zaiApiKey);
83
+ this.logger.info('Using saved Z.AI API key');
84
+ } else {
85
+ installClaudeZ = await this.prompts.confirm('Install Claude-Z (z.ai backend)?', false);
86
+ if (installClaudeZ) {
87
+ zaiApiKey = await this.prompts.password('Enter your Z.AI API key:');
88
+ }
89
+ }
90
+
91
+ let czInstalled = false;
92
+ if (installClaudeZ && zaiApiKey) {
93
+ czInstalled = await this.claudeZ.install(zaiApiKey);
94
+ } else {
95
+ this.logger.info('Skipping Claude-Z installation');
96
+ }
97
+
98
+ // Step 6: Install Claude HUD (optional)
99
+ let installClaudeHUD = true;
100
+ if (useExisting && existingSettings.claudeHUD !== undefined) {
101
+ installClaudeHUD = existingSettings.claudeHUD;
102
+ } else {
103
+ installClaudeHUD = await this.prompts.confirm('Install Claude HUD (status display)?', true);
104
+ }
105
+
106
+ let hudInstalled = false;
107
+ if (installClaudeHUD) {
108
+ hudInstalled = await this.claudeHUD.install();
109
+ } else {
110
+ this.logger.info('Skipping Claude HUD installation');
111
+ }
112
+
113
+ // Step 7: Verification
114
+ this.logger.step('Step 7/7: Verifying Installation');
115
+ console.log('');
116
+
117
+ this.superClaude.verify();
118
+ if (installSpecKit) this.specKit.verify();
119
+ this.config.verify();
120
+ if (installClaudeZ) this.claudeZ.verify();
121
+ if (installClaudeHUD) this.claudeHUD.verify();
122
+
123
+ // Save settings
124
+ const newSettings = {
125
+ specKit: installSpecKit,
126
+ claudeZ: installClaudeZ,
127
+ claudeHUD: installClaudeHUD,
128
+ zaiApiKey: zaiApiKey ? this.settings.encodeApiKey(zaiApiKey) : null,
129
+ installedAt: new Date().toISOString(),
130
+ version: require('../package.json').version
131
+ };
132
+
133
+ this.settings.save(newSettings);
134
+ this.logger.success('Settings saved');
135
+
136
+ // Print summary
137
+ this.printSummary({
138
+ superClaude: scInstalled,
139
+ specKit: skInstalled,
140
+ config: cfgInstalled,
141
+ claudeZ: czInstalled,
142
+ claudeHUD: hudInstalled
143
+ });
144
+
145
+ return true;
146
+ }
147
+
148
+ /**
149
+ * Check dependencies
150
+ */
151
+ async checkDependencies() {
152
+ let allOk = true;
153
+
154
+ // Check Node.js version
155
+ const nodeVersion = process.version;
156
+ const major = parseInt(nodeVersion.slice(1).split('.')[0]);
157
+ if (major >= 16) {
158
+ this.logger.success(`Node.js: ${nodeVersion}`);
159
+ } else {
160
+ this.logger.error(`Node.js: ${nodeVersion} (requires 16+)`);
161
+ allOk = false;
162
+ }
163
+
164
+ // Check Claude Code
165
+ if (this.platform.commandExists('claude')) {
166
+ const version = this.platform.exec('claude --version', { silent: true });
167
+ this.logger.success(`Claude Code: ${version?.trim() || 'installed'}`);
168
+ } else {
169
+ this.logger.error('Claude Code: Not found');
170
+ this.logger.info('Install with: npm install -g @anthropic-ai/claude-code');
171
+ allOk = false;
172
+ }
173
+
174
+ // Check git (optional but recommended)
175
+ if (this.platform.commandExists('git')) {
176
+ this.logger.success('Git: Available');
177
+ } else {
178
+ this.logger.warning('Git: Not found (will use ZIP fallback)');
179
+ }
180
+
181
+ // Check Python (for Spec Kit)
182
+ const pipCmd = this.platform.getPipCommand();
183
+ if (pipCmd) {
184
+ this.logger.success(`Python: Available (${pipCmd.split(' ')[0]})`);
185
+ } else {
186
+ this.logger.warning('Python: Not found (Spec Kit will be skipped)');
187
+ }
188
+
189
+ console.log('');
190
+ return allOk;
191
+ }
192
+
193
+ /**
194
+ * Print installation summary
195
+ */
196
+ printSummary(results) {
197
+ console.log('');
198
+ console.log('═══════════════════════════════════════════════════════════════════');
199
+ console.log(' Installation Complete');
200
+ console.log('═══════════════════════════════════════════════════════════════════');
201
+ console.log('');
202
+
203
+ const components = [
204
+ { name: 'SuperClaude', installed: results.superClaude },
205
+ { name: 'Spec Kit', installed: results.specKit },
206
+ { name: 'CLAUDE.md', installed: results.config },
207
+ { name: 'Claude-Z', installed: results.claudeZ },
208
+ { name: 'Claude HUD', installed: results.claudeHUD }
209
+ ];
210
+
211
+ let installed = 0;
212
+ for (const comp of components) {
213
+ const status = comp.installed ? '✓' : '✗';
214
+ const color = comp.installed ? '\x1b[32m' : '\x1b[31m';
215
+ console.log(` ${color}[${status}]\x1b[0m ${comp.name}`);
216
+ if (comp.installed) installed++;
217
+ }
218
+
219
+ console.log('');
220
+ console.log(` Components installed: ${installed}/${components.length}`);
221
+ console.log('');
222
+
223
+ console.log(' Next steps:');
224
+ console.log(' 1. Restart your terminal or run: source ~/.bashrc');
225
+ console.log(' 2. Start Claude Code: claude');
226
+ console.log(' 3. Try: /sc:help');
227
+ console.log('');
228
+
229
+ if (results.claudeZ) {
230
+ console.log(' For z.ai backend: claude-z');
231
+ console.log('');
232
+ }
233
+
234
+ if (results.claudeHUD) {
235
+ console.log(' For Claude HUD: run setup-claude-hud');
236
+ console.log('');
237
+ }
238
+ }
239
+ }
240
+
241
+ module.exports = { Installer };
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Settings management for ZuppaClaude
3
+ */
4
+
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+ const { Logger } = require('./utils/logger');
8
+ const { Platform } = require('./utils/platform');
9
+ const { Prompts } = require('./utils/prompts');
10
+
11
+ const SETTINGS_VERSION = '1.0';
12
+
13
+ class Settings {
14
+ constructor() {
15
+ this.platform = new Platform();
16
+ this.logger = new Logger();
17
+ this.prompts = new Prompts();
18
+ this.configDir = this.platform.zuppaconfigDir;
19
+ this.filePath = path.join(this.configDir, 'zc-settings.json');
20
+ }
21
+
22
+ /**
23
+ * Check if settings file exists
24
+ */
25
+ exists() {
26
+ return fs.existsSync(this.filePath);
27
+ }
28
+
29
+ /**
30
+ * Load settings from file
31
+ */
32
+ load() {
33
+ if (!this.exists()) {
34
+ return null;
35
+ }
36
+
37
+ try {
38
+ const content = fs.readFileSync(this.filePath, 'utf8');
39
+ return JSON.parse(content);
40
+ } catch (error) {
41
+ this.logger.warning(`Could not parse settings file: ${error.message}`);
42
+ return null;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Save settings to file
48
+ */
49
+ save(settings) {
50
+ this.platform.ensureDir(this.configDir);
51
+
52
+ const timestamp = new Date().toISOString();
53
+ const existing = this.load();
54
+
55
+ const data = {
56
+ version: SETTINGS_VERSION,
57
+ created: existing?.created || timestamp,
58
+ updated: timestamp,
59
+ components: settings.components || {},
60
+ preferences: settings.preferences || {
61
+ auto_update_check: true,
62
+ backup_configs: true,
63
+ },
64
+ };
65
+
66
+ fs.writeFileSync(this.filePath, JSON.stringify(data, null, 2), 'utf8');
67
+ fs.chmodSync(this.filePath, 0o600);
68
+
69
+ this.logger.success(`Settings saved to ${this.filePath}`);
70
+ return data;
71
+ }
72
+
73
+ /**
74
+ * Get component settings
75
+ */
76
+ getComponents() {
77
+ const settings = this.load();
78
+ return settings?.components || {};
79
+ }
80
+
81
+ /**
82
+ * Update component settings
83
+ */
84
+ updateComponents(components) {
85
+ const settings = this.load() || { components: {}, preferences: {} };
86
+ settings.components = { ...settings.components, ...components };
87
+ return this.save(settings);
88
+ }
89
+
90
+ /**
91
+ * Encode API key to base64
92
+ */
93
+ encodeApiKey(key) {
94
+ return Buffer.from(key).toString('base64');
95
+ }
96
+
97
+ /**
98
+ * Decode API key from base64
99
+ */
100
+ decodeApiKey(encoded) {
101
+ try {
102
+ return Buffer.from(encoded, 'base64').toString('utf8');
103
+ } catch {
104
+ return '';
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Show current settings
110
+ */
111
+ show() {
112
+ if (!this.exists()) {
113
+ this.logger.warning(`No settings file found at: ${this.filePath}`);
114
+ console.log('\nRun the installer first to create settings.');
115
+ return;
116
+ }
117
+
118
+ const settings = this.load();
119
+ if (!settings) return;
120
+
121
+ const c = settings.components || {};
122
+
123
+ console.log(`\nSettings file: ${this.filePath}\n`);
124
+ console.log(`Version: ${settings.version}`);
125
+ console.log(`Created: ${settings.created}`);
126
+ console.log(`Updated: ${settings.updated}`);
127
+ console.log('\nComponents:');
128
+ console.log(` SuperClaude: ${c.superclaude?.installed ? 'Yes' : 'No'}`);
129
+ console.log(` Spec Kit: ${c.speckit?.installed ? 'Yes' : 'No'}`);
130
+ console.log(` Claude-Z: ${c.claude_z?.installed ? 'Yes' : 'No'}`);
131
+ if (c.claude_z?.api_key_encoded) {
132
+ console.log(' API Key: [configured]');
133
+ }
134
+ console.log(` Claude HUD: ${c.claude_hud?.installed ? 'Yes' : 'No'}`);
135
+
136
+ const p = settings.preferences || {};
137
+ console.log('\nPreferences:');
138
+ console.log(` Auto-update check: ${p.auto_update_check ? 'Yes' : 'No'}`);
139
+ console.log(` Backup configs: ${p.backup_configs ? 'Yes' : 'No'}`);
140
+ console.log('');
141
+ }
142
+
143
+ /**
144
+ * Export settings to file
145
+ */
146
+ export(exportPath) {
147
+ if (!this.exists()) {
148
+ this.logger.error(`No settings file found at: ${this.filePath}`);
149
+ return false;
150
+ }
151
+
152
+ const settings = this.load();
153
+ if (!settings) return false;
154
+
155
+ // Add export metadata
156
+ settings._export = {
157
+ exported_at: new Date().toISOString(),
158
+ hostname: require('os').hostname(),
159
+ source_path: this.filePath,
160
+ };
161
+
162
+ const resolvedPath = path.resolve(exportPath.replace(/^~/, this.platform.homedir));
163
+ fs.writeFileSync(resolvedPath, JSON.stringify(settings, null, 2), 'utf8');
164
+
165
+ this.logger.success(`Settings exported to: ${resolvedPath}`);
166
+ return true;
167
+ }
168
+
169
+ /**
170
+ * Import settings from file
171
+ */
172
+ import(importPath) {
173
+ const resolvedPath = path.resolve(importPath.replace(/^~/, this.platform.homedir));
174
+
175
+ if (!fs.existsSync(resolvedPath)) {
176
+ this.logger.error(`Import file not found: ${resolvedPath}`);
177
+ return false;
178
+ }
179
+
180
+ let settings;
181
+ try {
182
+ const content = fs.readFileSync(resolvedPath, 'utf8');
183
+ settings = JSON.parse(content);
184
+ } catch (error) {
185
+ this.logger.error(`Invalid JSON file: ${error.message}`);
186
+ return false;
187
+ }
188
+
189
+ // Backup existing settings
190
+ if (this.exists()) {
191
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
192
+ const backupPath = `${this.filePath}.backup.${timestamp}`;
193
+ fs.copyFileSync(this.filePath, backupPath);
194
+ this.logger.info(`Existing settings backed up to: ${backupPath}`);
195
+ }
196
+
197
+ // Remove export metadata
198
+ delete settings._export;
199
+
200
+ // Update timestamp
201
+ settings.updated = new Date().toISOString();
202
+
203
+ this.platform.ensureDir(this.configDir);
204
+ fs.writeFileSync(this.filePath, JSON.stringify(settings, null, 2), 'utf8');
205
+ fs.chmodSync(this.filePath, 0o600);
206
+
207
+ this.logger.success(`Settings imported from: ${resolvedPath}`);
208
+ this.logger.info('Run the installer again to apply imported settings');
209
+ return true;
210
+ }
211
+
212
+ /**
213
+ * Reset settings
214
+ */
215
+ async reset() {
216
+ if (!this.exists()) {
217
+ this.logger.warning('No settings file found');
218
+ return true;
219
+ }
220
+
221
+ const confirmed = await this.prompts.confirm('Are you sure you want to reset all settings?', false);
222
+ this.prompts.close();
223
+
224
+ if (!confirmed) {
225
+ this.logger.info('Reset cancelled');
226
+ return false;
227
+ }
228
+
229
+ // Backup before reset
230
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
231
+ const backupPath = `${this.filePath}.backup.${timestamp}`;
232
+ fs.copyFileSync(this.filePath, backupPath);
233
+ this.logger.info(`Settings backed up to: ${backupPath}`);
234
+
235
+ // Remove settings
236
+ fs.unlinkSync(this.filePath);
237
+ this.logger.success('Settings reset complete');
238
+ this.logger.info('Run the installer again to create new settings');
239
+ return true;
240
+ }
241
+ }
242
+
243
+ module.exports = { Settings, SETTINGS_VERSION };