zuppaclaude 1.3.5 → 1.3.6

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.
@@ -202,6 +202,21 @@ async function main() {
202
202
  }
203
203
  await cloudMgr.listCloudBackups(cloudRemote);
204
204
  break;
205
+ case 'delete':
206
+ case 'rm':
207
+ if (!cloudRemote) {
208
+ logger.error('Please specify remote');
209
+ logger.info('Usage: zuppaclaude cloud delete <remote> [backup-id]');
210
+ process.exit(1);
211
+ }
212
+ if (args[3]) {
213
+ // Direct delete with backup ID
214
+ await cloudMgr.deleteCloudBackup(cloudRemote, args[3]);
215
+ } else {
216
+ // Interactive delete menu
217
+ await cloudMgr.deleteCloudBackupInteractive(cloudRemote);
218
+ }
219
+ break;
205
220
  default:
206
221
  logger.error(`Unknown cloud command: ${cloudCmd}`);
207
222
  showCloudHelp();
@@ -296,6 +311,7 @@ Cloud Commands:
296
311
  cloud upload <r> Upload backups to remote
297
312
  cloud download <r> Download backups from remote
298
313
  cloud backups <r> List cloud backups
314
+ cloud delete <r> Delete backup from cloud (interactive)
299
315
 
300
316
  Update Commands:
301
317
  update Check for updates
@@ -363,6 +379,7 @@ Cloud Commands (requires rclone):
363
379
  upload Upload backups to a cloud remote
364
380
  download Download backups from a cloud remote
365
381
  backups List backups stored on a cloud remote
382
+ delete Delete backup from cloud (interactive menu)
366
383
 
367
384
  Examples:
368
385
  zuppaclaude cloud setup # Setup instructions
@@ -370,6 +387,8 @@ Examples:
370
387
  zuppaclaude cloud upload gdrive # Upload all backups
371
388
  zuppaclaude cloud download gdrive # Download all backups
372
389
  zuppaclaude cloud backups gdrive # List cloud backups
390
+ zuppaclaude cloud delete gdrive # Interactive delete menu
391
+ zuppaclaude cloud delete gdrive Jan-05-2026-14.09 # Direct delete
373
392
 
374
393
  Supported providers (via rclone):
375
394
  Google Drive, Dropbox, OneDrive, S3, SFTP, FTP, and 40+ more
@@ -722,6 +722,121 @@ class CloudManager {
722
722
  }
723
723
  }
724
724
 
725
+ /**
726
+ * Delete a backup from cloud
727
+ */
728
+ async deleteCloudBackup(remote, backupId) {
729
+ if (!this.isRcloneInstalled()) {
730
+ this.logger.error('rclone is not installed');
731
+ return false;
732
+ }
733
+
734
+ if (!this.remoteExists(remote)) {
735
+ this.logger.error(`Remote not found: ${remote}`);
736
+ return false;
737
+ }
738
+
739
+ const zipFileName = `${backupId}.zip`;
740
+
741
+ try {
742
+ // Check if backup exists
743
+ const cmd = `rclone lsf "${remote}:${this.cloudPath}/${zipFileName}" 2>/dev/null`;
744
+ const output = this.platform.exec(cmd, { silent: true });
745
+
746
+ if (!output || !output.trim()) {
747
+ this.logger.error(`Backup not found on cloud: ${backupId}`);
748
+ return false;
749
+ }
750
+
751
+ this.logger.step(`Deleting ${zipFileName} from ${remote}...`);
752
+
753
+ // Delete the backup
754
+ const deleteCmd = `rclone delete "${remote}:${this.cloudPath}/${zipFileName}"`;
755
+ this.platform.exec(deleteCmd, { silent: true });
756
+
757
+ this.logger.success(`Deleted ${backupId} from ${remote}`);
758
+ return true;
759
+ } catch (error) {
760
+ this.logger.error(`Failed to delete: ${error.message}`);
761
+ return false;
762
+ }
763
+ }
764
+
765
+ /**
766
+ * Interactive delete - show list and let user choose
767
+ */
768
+ async deleteCloudBackupInteractive(remote) {
769
+ if (!this.isRcloneInstalled()) {
770
+ this.logger.error('rclone is not installed');
771
+ return false;
772
+ }
773
+
774
+ if (!this.remoteExists(remote)) {
775
+ this.logger.error(`Remote not found: ${remote}`);
776
+ return false;
777
+ }
778
+
779
+ // Get list of backups
780
+ const backups = await this.getCloudBackupList(remote);
781
+
782
+ if (backups.length === 0) {
783
+ this.logger.warning('No backups found on cloud');
784
+ return false;
785
+ }
786
+
787
+ console.log('');
788
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
789
+ console.log(`\x1b[35m Delete Cloud Backup (${remote})\x1b[0m`);
790
+ console.log('\x1b[35m═══════════════════════════════════════════════════════════════════\x1b[0m');
791
+ console.log('');
792
+
793
+ // Show numbered list
794
+ for (let i = 0; i < backups.length; i++) {
795
+ console.log(` ${i + 1}. 📦 ${backups[i]}`);
796
+ }
797
+ console.log(` ${backups.length + 1}. ❌ Cancel`);
798
+ console.log('');
799
+
800
+ // Get user choice
801
+ const choice = await this.prompts.number(`Select backup to delete (1-${backups.length + 1}):`, 1, backups.length + 1);
802
+
803
+ if (choice === backups.length + 1 || choice === null) {
804
+ this.logger.info('Cancelled');
805
+ return false;
806
+ }
807
+
808
+ const selectedBackup = backups[choice - 1];
809
+
810
+ // Confirm deletion
811
+ const confirm = await this.prompts.confirm(`Delete "${selectedBackup}" from ${remote}?`, false);
812
+
813
+ if (!confirm) {
814
+ this.logger.info('Cancelled');
815
+ return false;
816
+ }
817
+
818
+ return await this.deleteCloudBackup(remote, selectedBackup);
819
+ }
820
+
821
+ /**
822
+ * Get list of cloud backups (without printing)
823
+ */
824
+ async getCloudBackupList(remote) {
825
+ try {
826
+ const cmd = `rclone lsf "${remote}:${this.cloudPath}/" --files-only 2>/dev/null`;
827
+ const output = this.platform.exec(cmd, { silent: true });
828
+
829
+ if (!output) return [];
830
+
831
+ return output
832
+ .split('\n')
833
+ .filter(f => f.endsWith('.zip'))
834
+ .map(f => f.replace('.zip', ''));
835
+ } catch (error) {
836
+ return [];
837
+ }
838
+ }
839
+
725
840
  /**
726
841
  * List cloud backups (zip files)
727
842
  */
@@ -129,6 +129,24 @@ class Prompts {
129
129
  });
130
130
  });
131
131
  }
132
+
133
+ /**
134
+ * Ask for a number within a range
135
+ */
136
+ async number(question, min = 1, max = 10) {
137
+ const rl = this.createInterface();
138
+
139
+ return new Promise((resolve) => {
140
+ rl.question(`${question} `, (answer) => {
141
+ const num = parseInt(answer, 10);
142
+ if (!isNaN(num) && num >= min && num <= max) {
143
+ resolve(num);
144
+ } else {
145
+ resolve(null);
146
+ }
147
+ });
148
+ });
149
+ }
132
150
  }
133
151
 
134
152
  module.exports = { Prompts };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuppaclaude",
3
- "version": "1.3.5",
3
+ "version": "1.3.6",
4
4
  "description": "Claude Code power-up installer - SuperClaude + Spec Kit + Claude-Z + Claude HUD",
5
5
  "main": "lib/index.js",
6
6
  "bin": {