zuppaclaude 1.0.2 → 1.1.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 CHANGED
@@ -197,26 +197,48 @@ npx zuppaclaude settings reset # 🔄 Fabrika ayarları
197
197
  | 🔐 API key koruması | Base64 encoded saklama |
198
198
  | 🛡️ Uninstall koruması | Kaldırırken ayarları koruma opsiyonu |
199
199
 
200
- ### 💾 Session Yedekleme
200
+ ### 💾 Yedekleme & Geri Yükleme
201
201
 
202
- Claude Code session'larınızı yedekleyin ve geri yükleyin. Context kaybı, format veya compacting durumlarında kullanışlı.
202
+ Claude Code session'larınızı ve ayarlarınızı yedekleyin. Context kaybı, format veya compacting durumlarında kullanışlı.
203
203
 
204
+ **🔄 Tam Yedekleme (Sessions + Ayarlar):**
205
+ ```bash
206
+ npx zuppaclaude backup # 💾 Lokal tam yedek
207
+ npx zuppaclaude backup --cloud gdrive # ☁️ Google Drive'a yedekle
208
+ npx zuppaclaude restore <id> # ♻️ Yedekten geri yükle
209
+ npx zuppaclaude restore <id> --cloud gdrive # ☁️ Cloud'dan geri yükle
210
+ ```
211
+
212
+ **📋 Sadece Session İşlemleri:**
204
213
  ```bash
205
214
  npx zuppaclaude session list # 📋 Tüm session'ları listele
206
- npx zuppaclaude session backup # 💾 Tüm session'ları yedekle
215
+ npx zuppaclaude session backup # 💾 Sadece session'ları yedekle
207
216
  npx zuppaclaude session backups # 📦 Mevcut yedekleri listele
208
- npx zuppaclaude session restore <id> # ♻️ Yedekten geri yükle
209
- npx zuppaclaude session export <id> # 📤 Belirli session'ı export et
210
217
  ```
211
218
 
212
- **📁 Yedek konumu:** `~/.config/zuppaclaude/backups/`
219
+ ### ☁️ Cloud Yedekleme (rclone)
220
+
221
+ Google Drive, Dropbox, OneDrive, S3, SFTP ve 40+ cloud servise yedekleme.
222
+
223
+ ```bash
224
+ # Kurulum
225
+ brew install rclone # macOS
226
+ rclone config # Remote ayarla (gdrive, dropbox vs.)
227
+
228
+ # Kullanım
229
+ npx zuppaclaude cloud setup # 📖 Kurulum talimatları
230
+ npx zuppaclaude cloud remotes # 📋 Mevcut remote'ları listele
231
+ npx zuppaclaude backup --cloud gdrive # ☁️ Yedekle ve upload et
232
+ npx zuppaclaude cloud backups gdrive # 📦 Cloud'daki yedekleri listele
233
+ ```
213
234
 
214
235
  **✨ Özellikler:**
215
236
  | Özellik | Açıklama |
216
237
  |---------|----------|
217
238
  | 🔒 Güvenli restore | Mevcut session'lar üzerine yazılmaz |
218
- | 📜 History desteği | Command history de yedeklenir |
219
- | 📋 Manifest | Her yedekte metadata saklanır |
239
+ | 📜 Tam yedek | Sessions + Settings + History |
240
+ | ☁️ 40+ cloud | rclone ile Google Drive, Dropbox, S3, SFTP... |
241
+ | 🔐 Encryption | rclone encryption desteği |
220
242
 
221
243
  ### 🗑️ Kaldırma
222
244
 
@@ -444,26 +466,48 @@ npx zuppaclaude settings reset # 🔄 Reset to defaults
444
466
  | 🔐 API key protection | Base64 encoded storage |
445
467
  | 🛡️ Uninstall protection | Option to preserve settings when uninstalling |
446
468
 
447
- ### 💾 Session Backup
469
+ ### 💾 Backup & Restore
448
470
 
449
- Backup and restore your Claude Code sessions. Useful for context loss, formatting, or conversation compacting.
471
+ Backup your Claude Code sessions and settings. Useful for context loss, formatting, or conversation compacting.
450
472
 
473
+ **🔄 Full Backup (Sessions + Settings):**
474
+ ```bash
475
+ npx zuppaclaude backup # 💾 Local full backup
476
+ npx zuppaclaude backup --cloud gdrive # ☁️ Backup to Google Drive
477
+ npx zuppaclaude restore <id> # ♻️ Restore from backup
478
+ npx zuppaclaude restore <id> --cloud gdrive # ☁️ Restore from cloud
479
+ ```
480
+
481
+ **📋 Session-only Operations:**
451
482
  ```bash
452
483
  npx zuppaclaude session list # 📋 List all sessions
453
- npx zuppaclaude session backup # 💾 Backup all sessions
484
+ npx zuppaclaude session backup # 💾 Backup sessions only
454
485
  npx zuppaclaude session backups # 📦 List available backups
455
- npx zuppaclaude session restore <id> # ♻️ Restore from backup
456
- npx zuppaclaude session export <id> # 📤 Export specific session
457
486
  ```
458
487
 
459
- **📁 Backup location:** `~/.config/zuppaclaude/backups/`
488
+ ### ☁️ Cloud Backup (rclone)
489
+
490
+ Backup to Google Drive, Dropbox, OneDrive, S3, SFTP, and 40+ cloud services.
491
+
492
+ ```bash
493
+ # Setup
494
+ brew install rclone # macOS
495
+ rclone config # Configure remote (gdrive, dropbox, etc.)
496
+
497
+ # Usage
498
+ npx zuppaclaude cloud setup # 📖 Setup instructions
499
+ npx zuppaclaude cloud remotes # 📋 List configured remotes
500
+ npx zuppaclaude backup --cloud gdrive # ☁️ Backup and upload
501
+ npx zuppaclaude cloud backups gdrive # 📦 List cloud backups
502
+ ```
460
503
 
461
504
  **✨ Features:**
462
505
  | Feature | Description |
463
506
  |---------|-------------|
464
507
  | 🔒 Safe restore | Existing sessions are not overwritten |
465
- | 📜 History support | Command history is also backed up |
466
- | 📋 Manifest | Metadata saved with each backup |
508
+ | 📜 Full backup | Sessions + Settings + History |
509
+ | ☁️ 40+ clouds | Google Drive, Dropbox, S3, SFTP via rclone |
510
+ | 🔐 Encryption | rclone encryption support |
467
511
 
468
512
  ### 🗑️ Uninstall
469
513
 
@@ -12,6 +12,17 @@ const { Logger } = require('../lib/utils/logger');
12
12
  const args = process.argv.slice(2);
13
13
  const command = args[0] || 'install';
14
14
 
15
+ /**
16
+ * Extract --cloud <remote> argument
17
+ */
18
+ function getCloudArg(args) {
19
+ const cloudIndex = args.indexOf('--cloud');
20
+ if (cloudIndex !== -1 && args[cloudIndex + 1]) {
21
+ return args[cloudIndex + 1];
22
+ }
23
+ return null;
24
+ }
25
+
15
26
  async function main() {
16
27
  const logger = new Logger();
17
28
 
@@ -114,6 +125,73 @@ async function main() {
114
125
  }
115
126
  break;
116
127
 
128
+ case 'backup':
129
+ const { BackupManager } = require('../lib/components/backup');
130
+ const backupMgr = new BackupManager();
131
+ const cloudArg = getCloudArg(args);
132
+ await backupMgr.backup({ cloud: cloudArg });
133
+ break;
134
+
135
+ case 'restore':
136
+ const { BackupManager: RestoreBackupManager } = require('../lib/components/backup');
137
+ const restoreMgr = new RestoreBackupManager();
138
+ const restoreId = args[1];
139
+ const restoreCloud = getCloudArg(args);
140
+
141
+ if (!restoreId || restoreId.startsWith('--')) {
142
+ logger.error('Please specify backup ID');
143
+ logger.info('Usage: zuppaclaude restore <backup-id> [--cloud <remote>]');
144
+ logger.info('Run "zuppaclaude backup list" to see available backups');
145
+ process.exit(1);
146
+ }
147
+
148
+ await restoreMgr.restore(restoreId, { cloud: restoreCloud });
149
+ break;
150
+
151
+ case 'cloud':
152
+ const { CloudManager } = require('../lib/components/cloud');
153
+ const cloudMgr = new CloudManager();
154
+ const cloudCmd = args[1] || 'remotes';
155
+ const cloudRemote = args[2];
156
+
157
+ switch (cloudCmd) {
158
+ case 'setup':
159
+ cloudMgr.showSetupInstructions();
160
+ break;
161
+ case 'remotes':
162
+ case 'list':
163
+ cloudMgr.listRemotes();
164
+ break;
165
+ case 'upload':
166
+ if (!cloudRemote) {
167
+ logger.error('Please specify remote');
168
+ logger.info('Usage: zuppaclaude cloud upload <remote> [backup-id]');
169
+ process.exit(1);
170
+ }
171
+ await cloudMgr.upload(cloudRemote, args[3]);
172
+ break;
173
+ case 'download':
174
+ if (!cloudRemote) {
175
+ logger.error('Please specify remote');
176
+ logger.info('Usage: zuppaclaude cloud download <remote> [backup-id]');
177
+ process.exit(1);
178
+ }
179
+ await cloudMgr.download(cloudRemote, args[3]);
180
+ break;
181
+ case 'backups':
182
+ if (!cloudRemote) {
183
+ logger.error('Please specify remote');
184
+ logger.info('Usage: zuppaclaude cloud backups <remote>');
185
+ process.exit(1);
186
+ }
187
+ await cloudMgr.listCloudBackups(cloudRemote);
188
+ break;
189
+ default:
190
+ logger.error(`Unknown cloud command: ${cloudCmd}`);
191
+ showCloudHelp();
192
+ }
193
+ break;
194
+
117
195
  case 'version':
118
196
  case 'v':
119
197
  case '-v':
@@ -150,32 +228,40 @@ Usage: zuppaclaude [command] [options]
150
228
  Commands:
151
229
  install, i Install ZuppaClaude components (default)
152
230
  uninstall, u Uninstall ZuppaClaude components
231
+ backup Full backup (sessions + settings) with cloud support
232
+ restore Restore from backup
153
233
  settings, s Manage settings
154
234
  session Manage Claude Code sessions
235
+ cloud Manage cloud remotes (rclone)
155
236
  version, v Show version
156
237
  help, h Show this help
157
238
 
158
- Settings Commands:
159
- settings show Display current settings
160
- settings export Export settings to file
161
- settings import Import settings from file
162
- settings reset Reset settings to default
163
- settings path Show settings file path
239
+ Backup Commands:
240
+ backup Local backup (sessions + settings)
241
+ backup --cloud <remote> Backup and upload to cloud
242
+ restore <id> Restore from local backup
243
+ restore <id> --cloud <remote> Download and restore from cloud
244
+
245
+ Cloud Commands:
246
+ cloud setup Show rclone setup instructions
247
+ cloud remotes List configured remotes
248
+ cloud upload <r> Upload backups to remote
249
+ cloud download <r> Download backups from remote
250
+ cloud backups <r> List cloud backups
164
251
 
165
252
  Session Commands:
166
- session list List all sessions
167
- session backup Backup all sessions
168
- session backups List available backups
169
- session restore Restore from backup
170
- session export Export a specific session
253
+ session list List all sessions
254
+ session backup Backup sessions only
255
+ session backups List available backups
256
+ session restore Restore sessions only
257
+ session export Export a specific session
171
258
 
172
259
  Examples:
173
- npx zuppaclaude # Install
174
- npx zuppaclaude install # Install
175
- npx zuppaclaude uninstall # Uninstall
176
- npx zuppaclaude settings show # View settings
177
- npx zuppaclaude session backup # Backup sessions
178
- npx zuppaclaude session restore 2026-01-05T12-00-00
260
+ npx zuppaclaude # Install
261
+ npx zuppaclaude backup # Full local backup
262
+ npx zuppaclaude backup --cloud gdrive # Backup to Google Drive
263
+ npx zuppaclaude restore 2026-01-05T12-00-00 # Restore from backup
264
+ npx zuppaclaude cloud setup # Configure cloud
179
265
  `);
180
266
  }
181
267
 
@@ -214,4 +300,25 @@ Examples:
214
300
  `);
215
301
  }
216
302
 
303
+ function showCloudHelp() {
304
+ console.log(`
305
+ Cloud Commands (requires rclone):
306
+ setup Show rclone installation and configuration instructions
307
+ remotes List configured cloud remotes
308
+ upload Upload backups to a cloud remote
309
+ download Download backups from a cloud remote
310
+ backups List backups stored on a cloud remote
311
+
312
+ Examples:
313
+ zuppaclaude cloud setup # Setup instructions
314
+ zuppaclaude cloud remotes # List remotes
315
+ zuppaclaude cloud upload gdrive # Upload all backups
316
+ zuppaclaude cloud download gdrive # Download all backups
317
+ zuppaclaude cloud backups gdrive # List cloud backups
318
+
319
+ Supported providers (via rclone):
320
+ Google Drive, Dropbox, OneDrive, S3, SFTP, FTP, and 40+ more
321
+ `);
322
+ }
323
+
217
324
  main();
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Unified Backup Manager - Sessions + Settings + Cloud
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 { SessionManager } = require('./session');
10
+ const { CloudManager } = require('./cloud');
11
+ const { Settings } = require('../settings');
12
+
13
+ class BackupManager {
14
+ constructor() {
15
+ this.platform = new Platform();
16
+ this.logger = new Logger();
17
+ this.sessionManager = new SessionManager();
18
+ this.cloudManager = new CloudManager();
19
+ this.settings = new Settings();
20
+ this.backupDir = path.join(this.platform.zuppaconfigDir, 'backups');
21
+ }
22
+
23
+ /**
24
+ * Full backup - sessions + settings
25
+ */
26
+ async backup(options = {}) {
27
+ const { cloud } = options;
28
+
29
+ console.log('');
30
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
31
+ console.log('\x1b[35m ZuppaClaude Full Backup\x1b[0m');
32
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
33
+ console.log('');
34
+
35
+ // Step 1: Backup sessions
36
+ this.logger.step('Step 1/3: Backing up Claude Code sessions...');
37
+ const sessionResult = this.sessionManager.backup();
38
+
39
+ if (!sessionResult) {
40
+ this.logger.warning('No sessions to backup');
41
+ }
42
+
43
+ // Step 2: Backup settings to the same directory
44
+ this.logger.step('Step 2/3: Backing up ZuppaClaude settings...');
45
+
46
+ if (sessionResult) {
47
+ const settingsBackupPath = path.join(sessionResult.path, 'zc-settings.json');
48
+ const currentSettings = this.settings.load();
49
+
50
+ if (currentSettings) {
51
+ fs.writeFileSync(settingsBackupPath, JSON.stringify(currentSettings, null, 2));
52
+ this.logger.success('Settings backed up');
53
+ } else {
54
+ this.logger.info('No settings to backup');
55
+ }
56
+
57
+ // Update manifest with settings info
58
+ const manifestPath = path.join(sessionResult.path, 'manifest.json');
59
+ if (fs.existsSync(manifestPath)) {
60
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
61
+ manifest.settings = currentSettings ? true : false;
62
+ manifest.backupType = 'full';
63
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
64
+ }
65
+ }
66
+
67
+ // Step 3: Upload to cloud if specified
68
+ if (cloud) {
69
+ this.logger.step('Step 3/3: Uploading to cloud...');
70
+
71
+ if (!this.cloudManager.isRcloneInstalled()) {
72
+ this.logger.warning('rclone not installed, skipping cloud upload');
73
+ this.cloudManager.showSetupInstructions();
74
+ } else if (!this.cloudManager.remoteExists(cloud)) {
75
+ this.logger.error(`Remote not found: ${cloud}`);
76
+ this.cloudManager.listRemotes();
77
+ } else {
78
+ await this.cloudManager.upload(cloud, sessionResult?.timestamp);
79
+ }
80
+ } else {
81
+ this.logger.step('Step 3/3: Cloud upload skipped (use --cloud <remote>)');
82
+ }
83
+
84
+ // Summary
85
+ this.printBackupSummary(sessionResult, cloud);
86
+
87
+ return sessionResult;
88
+ }
89
+
90
+ /**
91
+ * Full restore - sessions + settings
92
+ */
93
+ async restore(backupId, options = {}) {
94
+ const { cloud, settingsOnly, sessionsOnly } = options;
95
+
96
+ console.log('');
97
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
98
+ console.log('\x1b[35m ZuppaClaude Restore\x1b[0m');
99
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
100
+ console.log('');
101
+
102
+ // Step 1: Download from cloud if specified
103
+ if (cloud) {
104
+ this.logger.step('Step 1/3: Downloading from cloud...');
105
+
106
+ if (!this.cloudManager.isRcloneInstalled()) {
107
+ this.logger.error('rclone not installed');
108
+ this.cloudManager.showSetupInstructions();
109
+ return false;
110
+ }
111
+
112
+ if (!this.cloudManager.remoteExists(cloud)) {
113
+ this.logger.error(`Remote not found: ${cloud}`);
114
+ return false;
115
+ }
116
+
117
+ await this.cloudManager.download(cloud, backupId);
118
+ } else {
119
+ this.logger.info('Step 1/3: Using local backup');
120
+ }
121
+
122
+ const backupPath = path.join(this.backupDir, backupId);
123
+
124
+ if (!fs.existsSync(backupPath)) {
125
+ this.logger.error(`Backup not found: ${backupId}`);
126
+ this.logger.info('Run "npx zuppaclaude backup list" to see available backups');
127
+ return false;
128
+ }
129
+
130
+ let sessionsRestored = false;
131
+ let settingsRestored = false;
132
+
133
+ // Step 2: Restore sessions
134
+ if (!settingsOnly) {
135
+ this.logger.step('Step 2/3: Restoring sessions...');
136
+ sessionsRestored = this.sessionManager.restore(backupId);
137
+ } else {
138
+ this.logger.info('Step 2/3: Sessions restore skipped');
139
+ }
140
+
141
+ // Step 3: Restore settings
142
+ if (!sessionsOnly) {
143
+ this.logger.step('Step 3/3: Restoring settings...');
144
+
145
+ const settingsBackupPath = path.join(backupPath, 'zc-settings.json');
146
+
147
+ if (fs.existsSync(settingsBackupPath)) {
148
+ try {
149
+ const backupSettings = JSON.parse(fs.readFileSync(settingsBackupPath, 'utf8'));
150
+ this.settings.save(backupSettings);
151
+ this.logger.success('Settings restored');
152
+ settingsRestored = true;
153
+ } catch (error) {
154
+ this.logger.warning(`Failed to restore settings: ${error.message}`);
155
+ }
156
+ } else {
157
+ this.logger.info('No settings in backup');
158
+ }
159
+ } else {
160
+ this.logger.info('Step 3/3: Settings restore skipped');
161
+ }
162
+
163
+ // Summary
164
+ console.log('');
165
+ console.log('\x1b[32m═══════════════════════════════════════════════════════════════════\x1b[0m');
166
+ console.log('\x1b[32m Restore Complete\x1b[0m');
167
+ console.log('\x1b[32m═══════════════════════════════════════════════════════════════════\x1b[0m');
168
+ console.log('');
169
+ console.log(` Sessions: ${sessionsRestored ? '✅ Restored' : '⏭️ Skipped'}`);
170
+ console.log(` Settings: ${settingsRestored ? '✅ Restored' : '⏭️ Skipped'}`);
171
+ console.log('');
172
+ console.log(' Restart Claude Code to see restored sessions');
173
+ console.log('');
174
+
175
+ return true;
176
+ }
177
+
178
+ /**
179
+ * List all backups (local + cloud)
180
+ */
181
+ async list(options = {}) {
182
+ const { cloud } = options;
183
+
184
+ // List local backups
185
+ this.sessionManager.listBackups();
186
+
187
+ // List cloud backups if remote specified
188
+ if (cloud) {
189
+ await this.cloudManager.listCloudBackups(cloud);
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Print backup summary
195
+ */
196
+ printBackupSummary(result, cloud) {
197
+ console.log('');
198
+ console.log('\x1b[32m═══════════════════════════════════════════════════════════════════\x1b[0m');
199
+ console.log('\x1b[32m Backup Complete\x1b[0m');
200
+ console.log('\x1b[32m═══════════════════════════════════════════════════════════════════\x1b[0m');
201
+ console.log('');
202
+
203
+ if (result) {
204
+ console.log(` 📦 Backup ID: ${result.timestamp}`);
205
+ console.log(` 📁 Sessions: ${result.sessions}`);
206
+ console.log(` 💾 Size: ${this.formatSize(result.size)}`);
207
+ console.log(` 📍 Location: ${result.path}`);
208
+
209
+ if (cloud) {
210
+ console.log(` ☁️ Cloud: ${cloud}:zuppaclaude-backups/${result.timestamp}`);
211
+ }
212
+ }
213
+
214
+ console.log('');
215
+ console.log(' Restore with:');
216
+ if (result) {
217
+ console.log(` \x1b[36mnpx zuppaclaude restore ${result.timestamp}\x1b[0m`);
218
+ if (cloud) {
219
+ console.log(` \x1b[36mnpx zuppaclaude restore ${result.timestamp} --cloud ${cloud}\x1b[0m`);
220
+ }
221
+ }
222
+ console.log('');
223
+ }
224
+
225
+ /**
226
+ * Format file size
227
+ */
228
+ formatSize(bytes) {
229
+ if (bytes < 1024) return bytes + ' B';
230
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
231
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
232
+ }
233
+ }
234
+
235
+ module.exports = { BackupManager };
@@ -0,0 +1,335 @@
1
+ /**
2
+ * Cloud Backup Manager - rclone integration for cloud sync
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
+ class CloudManager {
11
+ constructor() {
12
+ this.platform = new Platform();
13
+ this.logger = new Logger();
14
+ this.backupDir = path.join(this.platform.zuppaconfigDir, 'backups');
15
+ this.cloudPath = 'zuppaclaude-backups';
16
+ }
17
+
18
+ /**
19
+ * Check if rclone is installed
20
+ */
21
+ isRcloneInstalled() {
22
+ return this.platform.commandExists('rclone');
23
+ }
24
+
25
+ /**
26
+ * Get available rclone remotes
27
+ */
28
+ getRemotes() {
29
+ if (!this.isRcloneInstalled()) {
30
+ return [];
31
+ }
32
+
33
+ try {
34
+ const output = this.platform.exec('rclone listremotes', { silent: true });
35
+ if (!output) return [];
36
+
37
+ return output
38
+ .split('\n')
39
+ .map(r => r.trim().replace(/:$/, ''))
40
+ .filter(r => r.length > 0);
41
+ } catch (error) {
42
+ return [];
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Check remote exists
48
+ */
49
+ remoteExists(remote) {
50
+ const remotes = this.getRemotes();
51
+ return remotes.includes(remote);
52
+ }
53
+
54
+ /**
55
+ * Show rclone setup instructions
56
+ */
57
+ showSetupInstructions() {
58
+ console.log('');
59
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
60
+ console.log('\x1b[35m Cloud Backup Setup\x1b[0m');
61
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
62
+ console.log('');
63
+
64
+ if (!this.isRcloneInstalled()) {
65
+ console.log('\x1b[33m[!]\x1b[0m rclone is not installed.');
66
+ console.log('');
67
+ console.log('Install rclone:');
68
+ console.log('');
69
+ if (this.platform.isMac) {
70
+ console.log(' \x1b[36mbrew install rclone\x1b[0m');
71
+ } else if (this.platform.isLinux) {
72
+ console.log(' \x1b[36mcurl https://rclone.org/install.sh | sudo bash\x1b[0m');
73
+ } else {
74
+ console.log(' \x1b[36mwinget install Rclone.Rclone\x1b[0m');
75
+ }
76
+ console.log('');
77
+ console.log('Or visit: https://rclone.org/install/');
78
+ console.log('');
79
+ return false;
80
+ }
81
+
82
+ console.log('\x1b[32m[✓]\x1b[0m rclone is installed');
83
+ console.log('');
84
+
85
+ const remotes = this.getRemotes();
86
+
87
+ if (remotes.length === 0) {
88
+ console.log('\x1b[33m[!]\x1b[0m No cloud remotes configured.');
89
+ console.log('');
90
+ console.log('Configure a remote:');
91
+ console.log('');
92
+ console.log(' \x1b[36mrclone config\x1b[0m');
93
+ console.log('');
94
+ console.log('Popular options:');
95
+ console.log(' • Google Drive: Choose "drive"');
96
+ console.log(' • Dropbox: Choose "dropbox"');
97
+ console.log(' • OneDrive: Choose "onedrive"');
98
+ console.log(' • S3/Minio: Choose "s3"');
99
+ console.log(' • SFTP: Choose "sftp"');
100
+ console.log(' • FTP: Choose "ftp"');
101
+ console.log('');
102
+ return false;
103
+ }
104
+
105
+ console.log('Available remotes:');
106
+ console.log('');
107
+ for (const remote of remotes) {
108
+ console.log(` \x1b[36m${remote}\x1b[0m`);
109
+ }
110
+ console.log('');
111
+ console.log('Usage:');
112
+ console.log('');
113
+ console.log(` \x1b[36mnpx zuppaclaude backup --cloud ${remotes[0]}\x1b[0m`);
114
+ console.log(` \x1b[36mnpx zuppaclaude restore --cloud ${remotes[0]}\x1b[0m`);
115
+ console.log('');
116
+
117
+ return true;
118
+ }
119
+
120
+ /**
121
+ * List remotes
122
+ */
123
+ listRemotes() {
124
+ if (!this.isRcloneInstalled()) {
125
+ this.logger.error('rclone is not installed');
126
+ this.showSetupInstructions();
127
+ return [];
128
+ }
129
+
130
+ const remotes = this.getRemotes();
131
+
132
+ if (remotes.length === 0) {
133
+ this.logger.warning('No remotes configured');
134
+ this.showSetupInstructions();
135
+ return [];
136
+ }
137
+
138
+ console.log('');
139
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
140
+ console.log('\x1b[35m Available Cloud Remotes\x1b[0m');
141
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
142
+ console.log('');
143
+
144
+ for (const remote of remotes) {
145
+ // Try to get remote type
146
+ let type = 'unknown';
147
+ try {
148
+ const config = this.platform.exec(`rclone config show ${remote}`, { silent: true });
149
+ const typeMatch = config?.match(/type\s*=\s*(\w+)/);
150
+ if (typeMatch) type = typeMatch[1];
151
+ } catch (e) {
152
+ // Ignore
153
+ }
154
+
155
+ const icon = this.getRemoteIcon(type);
156
+ console.log(` ${icon} \x1b[36m${remote}\x1b[0m (${type})`);
157
+ }
158
+
159
+ console.log('');
160
+ return remotes;
161
+ }
162
+
163
+ /**
164
+ * Get icon for remote type
165
+ */
166
+ getRemoteIcon(type) {
167
+ const icons = {
168
+ 'drive': '📁',
169
+ 'dropbox': '📦',
170
+ 'onedrive': '☁️',
171
+ 's3': '🪣',
172
+ 'sftp': '🔐',
173
+ 'ftp': '📡',
174
+ 'b2': '🅱️',
175
+ 'box': '📥',
176
+ 'mega': '🔷',
177
+ 'pcloud': '☁️'
178
+ };
179
+ return icons[type] || '☁️';
180
+ }
181
+
182
+ /**
183
+ * Upload backup to cloud
184
+ */
185
+ async upload(remote, backupId = null) {
186
+ if (!this.isRcloneInstalled()) {
187
+ this.logger.error('rclone is not installed');
188
+ this.showSetupInstructions();
189
+ return false;
190
+ }
191
+
192
+ if (!this.remoteExists(remote)) {
193
+ this.logger.error(`Remote not found: ${remote}`);
194
+ this.logger.info('Run "npx zuppaclaude cloud remotes" to see available remotes');
195
+ return false;
196
+ }
197
+
198
+ // Determine what to upload
199
+ let sourcePath = this.backupDir;
200
+ let destPath = `${remote}:${this.cloudPath}`;
201
+
202
+ if (backupId) {
203
+ sourcePath = path.join(this.backupDir, backupId);
204
+ if (!fs.existsSync(sourcePath)) {
205
+ this.logger.error(`Backup not found: ${backupId}`);
206
+ return false;
207
+ }
208
+ destPath = `${remote}:${this.cloudPath}/${backupId}`;
209
+ }
210
+
211
+ if (!fs.existsSync(sourcePath)) {
212
+ this.logger.error('No backups to upload');
213
+ this.logger.info('Run "npx zuppaclaude backup" first');
214
+ return false;
215
+ }
216
+
217
+ console.log('');
218
+ this.logger.step(`Uploading to ${remote}...`);
219
+ console.log('');
220
+
221
+ try {
222
+ // Use rclone sync for efficiency
223
+ const cmd = `rclone sync "${sourcePath}" "${destPath}" --progress`;
224
+ this.platform.exec(cmd, { silent: false, stdio: 'inherit' });
225
+
226
+ console.log('');
227
+ this.logger.success(`Backup uploaded to ${remote}:${this.cloudPath}`);
228
+ console.log('');
229
+ return true;
230
+ } catch (error) {
231
+ this.logger.error(`Upload failed: ${error.message}`);
232
+ return false;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Download backup from cloud
238
+ */
239
+ async download(remote, backupId = null) {
240
+ if (!this.isRcloneInstalled()) {
241
+ this.logger.error('rclone is not installed');
242
+ this.showSetupInstructions();
243
+ return false;
244
+ }
245
+
246
+ if (!this.remoteExists(remote)) {
247
+ this.logger.error(`Remote not found: ${remote}`);
248
+ return false;
249
+ }
250
+
251
+ let sourcePath = `${remote}:${this.cloudPath}`;
252
+ let destPath = this.backupDir;
253
+
254
+ if (backupId) {
255
+ sourcePath = `${remote}:${this.cloudPath}/${backupId}`;
256
+ destPath = path.join(this.backupDir, backupId);
257
+ }
258
+
259
+ this.platform.ensureDir(destPath);
260
+
261
+ console.log('');
262
+ this.logger.step(`Downloading from ${remote}...`);
263
+ console.log('');
264
+
265
+ try {
266
+ const cmd = `rclone sync "${sourcePath}" "${destPath}" --progress`;
267
+ this.platform.exec(cmd, { silent: false, stdio: 'inherit' });
268
+
269
+ console.log('');
270
+ this.logger.success(`Backup downloaded from ${remote}`);
271
+ console.log('');
272
+ return true;
273
+ } catch (error) {
274
+ this.logger.error(`Download failed: ${error.message}`);
275
+ return false;
276
+ }
277
+ }
278
+
279
+ /**
280
+ * List cloud backups
281
+ */
282
+ async listCloudBackups(remote) {
283
+ if (!this.isRcloneInstalled()) {
284
+ this.logger.error('rclone is not installed');
285
+ return [];
286
+ }
287
+
288
+ if (!this.remoteExists(remote)) {
289
+ this.logger.error(`Remote not found: ${remote}`);
290
+ return [];
291
+ }
292
+
293
+ try {
294
+ const cmd = `rclone lsd "${remote}:${this.cloudPath}" 2>/dev/null`;
295
+ const output = this.platform.exec(cmd, { silent: true });
296
+
297
+ if (!output) {
298
+ this.logger.warning('No cloud backups found');
299
+ return [];
300
+ }
301
+
302
+ const backups = output
303
+ .split('\n')
304
+ .filter(line => line.trim())
305
+ .map(line => {
306
+ const parts = line.trim().split(/\s+/);
307
+ const name = parts[parts.length - 1];
308
+ return name;
309
+ })
310
+ .filter(name => name && name.match(/^\d{4}-\d{2}-\d{2}T/));
311
+
312
+ console.log('');
313
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
314
+ console.log(`\x1b[35m Cloud Backups (${remote})\x1b[0m`);
315
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
316
+ console.log('');
317
+
318
+ if (backups.length === 0) {
319
+ console.log(' No backups found');
320
+ } else {
321
+ for (const backup of backups) {
322
+ console.log(` 📦 ${backup}`);
323
+ }
324
+ }
325
+
326
+ console.log('');
327
+ return backups;
328
+ } catch (error) {
329
+ this.logger.warning('Could not list cloud backups');
330
+ return [];
331
+ }
332
+ }
333
+ }
334
+
335
+ module.exports = { CloudManager };
@@ -8,6 +8,8 @@ const { ConfigInstaller } = require('./config');
8
8
  const { ClaudeZInstaller } = require('./claudez');
9
9
  const { ClaudeHUDInstaller } = require('./claudehud');
10
10
  const { SessionManager } = require('./session');
11
+ const { CloudManager } = require('./cloud');
12
+ const { BackupManager } = require('./backup');
11
13
 
12
14
  module.exports = {
13
15
  SuperClaudeInstaller,
@@ -15,5 +17,7 @@ module.exports = {
15
17
  ConfigInstaller,
16
18
  ClaudeZInstaller,
17
19
  ClaudeHUDInstaller,
18
- SessionManager
20
+ SessionManager,
21
+ CloudManager,
22
+ BackupManager
19
23
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuppaclaude",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Claude Code power-up installer - SuperClaude + Spec Kit + Claude-Z + Claude HUD",
5
5
  "main": "lib/index.js",
6
6
  "bin": {