zuppaclaude 1.3.1 → 1.3.3

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.
@@ -4,15 +4,28 @@
4
4
 
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
+ const { execSync, spawn } = require('child_process');
7
8
  const { Logger } = require('../utils/logger');
8
9
  const { Platform } = require('../utils/platform');
10
+ const { Prompts } = require('../utils/prompts');
9
11
 
10
12
  class CloudManager {
11
13
  constructor() {
12
14
  this.platform = new Platform();
13
15
  this.logger = new Logger();
16
+ this.prompts = new Prompts();
14
17
  this.backupDir = path.join(this.platform.zuppaconfigDir, 'backups');
15
18
  this.cloudPath = 'zuppaclaude-backups';
19
+
20
+ // Supported providers
21
+ this.providers = [
22
+ { name: 'Google Drive', type: 'drive', remoteName: 'gdrive' },
23
+ { name: 'Dropbox', type: 'dropbox', remoteName: 'dropbox' },
24
+ { name: 'OneDrive', type: 'onedrive', remoteName: 'onedrive' },
25
+ { name: 'Amazon S3', type: 's3', remoteName: 's3' },
26
+ { name: 'SFTP', type: 'sftp', remoteName: 'sftp' },
27
+ { name: 'Skip (configure later)', type: null, remoteName: null }
28
+ ];
16
29
  }
17
30
 
18
31
  /**
@@ -76,6 +89,115 @@ class CloudManager {
76
89
  }
77
90
  }
78
91
 
92
+ /**
93
+ * Configure a cloud provider
94
+ */
95
+ async configureProvider() {
96
+ if (!this.isRcloneInstalled()) {
97
+ this.logger.error('rclone is not installed');
98
+ return null;
99
+ }
100
+
101
+ console.log('');
102
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
103
+ console.log('\x1b[35m Cloud Provider Setup\x1b[0m');
104
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
105
+ console.log('');
106
+
107
+ // Show provider options
108
+ const providerNames = this.providers.map(p => p.name);
109
+ const selectedIndex = await this.prompts.select('Select a cloud provider:', providerNames);
110
+ const provider = this.providers[selectedIndex];
111
+
112
+ if (!provider.type) {
113
+ this.logger.info('Skipping cloud provider configuration');
114
+ this.logger.info('Run "rclone config" later to set up manually');
115
+ return null;
116
+ }
117
+
118
+ console.log('');
119
+ this.logger.step(`Configuring ${provider.name}...`);
120
+
121
+ // Check if remote already exists
122
+ const existingRemotes = this.getRemotes();
123
+ if (existingRemotes.includes(provider.remoteName)) {
124
+ this.logger.warning(`Remote "${provider.remoteName}" already exists`);
125
+ const overwrite = await this.prompts.confirm('Overwrite existing configuration?', false);
126
+ if (!overwrite) {
127
+ return provider.remoteName;
128
+ }
129
+ // Delete existing remote
130
+ try {
131
+ this.platform.exec(`rclone config delete ${provider.remoteName}`, { silent: true });
132
+ } catch (e) {
133
+ // Ignore
134
+ }
135
+ }
136
+
137
+ // Run rclone config create
138
+ try {
139
+ await this.runProviderAuth(provider);
140
+
141
+ // Verify configuration
142
+ if (this.getRemotes().includes(provider.remoteName)) {
143
+ this.logger.success(`${provider.name} configured successfully`);
144
+ console.log('');
145
+ console.log(' Backup path: \x1b[36m' + provider.remoteName + ':zuppaclaude-backups/\x1b[0m');
146
+ console.log(' ├── sessions/');
147
+ console.log(' └── settings/');
148
+ console.log('');
149
+ return provider.remoteName;
150
+ } else {
151
+ this.logger.error('Configuration failed');
152
+ return null;
153
+ }
154
+ } catch (error) {
155
+ this.logger.error(`Failed to configure ${provider.name}: ${error.message}`);
156
+ return null;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Run provider-specific authentication
162
+ */
163
+ async runProviderAuth(provider) {
164
+ return new Promise((resolve, reject) => {
165
+ console.log('');
166
+
167
+ if (provider.type === 'drive' || provider.type === 'dropbox' || provider.type === 'onedrive') {
168
+ // OAuth providers - need browser auth
169
+ this.logger.info('A browser window will open for authentication...');
170
+ this.logger.info('Please authorize ZuppaClaude to access your ' + provider.name);
171
+ console.log('');
172
+ }
173
+
174
+ // Use spawn for interactive rclone config
175
+ const args = ['config', 'create', provider.remoteName, provider.type];
176
+
177
+ // Add provider-specific defaults
178
+ if (provider.type === 'drive') {
179
+ args.push('scope', 'drive');
180
+ }
181
+
182
+ const rclone = spawn('rclone', args, {
183
+ stdio: 'inherit',
184
+ shell: true
185
+ });
186
+
187
+ rclone.on('close', (code) => {
188
+ if (code === 0) {
189
+ resolve();
190
+ } else {
191
+ reject(new Error(`rclone exited with code ${code}`));
192
+ }
193
+ });
194
+
195
+ rclone.on('error', (err) => {
196
+ reject(err);
197
+ });
198
+ });
199
+ }
200
+
79
201
  /**
80
202
  * Verify rclone installation
81
203
  */
package/lib/installer.js CHANGED
@@ -56,15 +56,27 @@ class Installer {
56
56
  useExisting = await this.prompts.confirm('Use previous settings?', true);
57
57
  }
58
58
 
59
- // Step 2: Install SuperClaude
60
- const scInstalled = await this.superClaude.install();
59
+ // Step 2: Install SuperClaude (optional)
60
+ let installSuperClaude = true;
61
+ if (useExisting && existingSettings.superClaude !== undefined) {
62
+ installSuperClaude = existingSettings.superClaude;
63
+ } else {
64
+ installSuperClaude = await this.prompts.confirm('Install SuperClaude (30+ slash commands)?', true);
65
+ }
66
+
67
+ let scInstalled = false;
68
+ if (installSuperClaude) {
69
+ scInstalled = await this.superClaude.install();
70
+ } else {
71
+ this.logger.info('Skipping SuperClaude installation');
72
+ }
61
73
 
62
- // Step 3: Install Spec Kit
74
+ // Step 3: Install Spec Kit (optional)
63
75
  let installSpecKit = true;
64
76
  if (useExisting && existingSettings.specKit !== undefined) {
65
77
  installSpecKit = existingSettings.specKit;
66
78
  } else {
67
- installSpecKit = await this.prompts.confirm('Install Spec Kit (specify-cli)?', true);
79
+ installSpecKit = await this.prompts.confirm('Install Spec Kit (spec-driven development)?', true);
68
80
  }
69
81
 
70
82
  let skInstalled = false;
@@ -118,6 +130,7 @@ class Installer {
118
130
  this.logger.step('Step 7/9: Cloud Backup Setup (rclone)');
119
131
  let installRclone = false;
120
132
  let rcloneInstalled = this.cloud.isRcloneInstalled();
133
+ let configuredRemote = null;
121
134
 
122
135
  if (rcloneInstalled) {
123
136
  this.logger.success('rclone is already installed');
@@ -135,6 +148,19 @@ class Installer {
135
148
  }
136
149
  }
137
150
 
151
+ // Configure cloud provider if rclone is installed
152
+ if (rcloneInstalled) {
153
+ const existingRemotes = this.cloud.getRemotes();
154
+ if (existingRemotes.length === 0) {
155
+ const configureNow = await this.prompts.confirm('Configure a cloud provider now?', true);
156
+ if (configureNow) {
157
+ configuredRemote = await this.cloud.configureProvider();
158
+ }
159
+ } else {
160
+ this.logger.info(`Existing remotes: ${existingRemotes.join(', ')}`);
161
+ }
162
+ }
163
+
138
164
  // Step 8: Install ZuppaClaude Commands
139
165
  this.logger.step('Step 8/9: Installing ZuppaClaude Slash Commands');
140
166
  const cmdsInstalled = await this.commands.install();
@@ -143,7 +169,7 @@ class Installer {
143
169
  this.logger.step('Step 9/9: Verifying Installation');
144
170
  console.log('');
145
171
 
146
- this.superClaude.verify();
172
+ if (installSuperClaude) this.superClaude.verify();
147
173
  if (installSpecKit) this.specKit.verify();
148
174
  this.config.verify();
149
175
  if (installClaudeZ) this.claudeZ.verify();
@@ -153,10 +179,12 @@ class Installer {
153
179
 
154
180
  // Save settings
155
181
  const newSettings = {
182
+ superClaude: installSuperClaude,
156
183
  specKit: installSpecKit,
157
184
  claudeZ: installClaudeZ,
158
185
  claudeHUD: installClaudeHUD,
159
186
  rclone: rcloneInstalled,
187
+ cloudRemote: configuredRemote,
160
188
  zaiApiKey: zaiApiKey ? this.settings.encodeApiKey(zaiApiKey) : null,
161
189
  installedAt: new Date().toISOString(),
162
190
  version: require('../package.json').version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuppaclaude",
3
- "version": "1.3.1",
3
+ "version": "1.3.3",
4
4
  "description": "Claude Code power-up installer - SuperClaude + Spec Kit + Claude-Z + Claude HUD",
5
5
  "main": "lib/index.js",
6
6
  "bin": {