claude-cli-advanced-starter-pack 1.0.5 → 1.0.7

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.
@@ -53,15 +53,189 @@ export function createBackup(filePath) {
53
53
  return backupPath;
54
54
  }
55
55
 
56
+ /**
57
+ * Find existing CCASP backups in the project
58
+ */
59
+ function findExistingBackups() {
60
+ const backupDir = join(process.cwd(), '.claude-backup');
61
+ const backups = [];
62
+
63
+ if (!existsSync(backupDir)) {
64
+ return backups;
65
+ }
66
+
67
+ try {
68
+ const entries = readdirSync(backupDir, { withFileTypes: true });
69
+ for (const entry of entries) {
70
+ if (entry.isDirectory()) {
71
+ const backupPath = join(backupDir, entry.name);
72
+ const hasClaudeDir = existsSync(join(backupPath, '.claude'));
73
+ const hasClaudeMd = existsSync(join(backupPath, 'CLAUDE.md'));
74
+
75
+ if (hasClaudeDir || hasClaudeMd) {
76
+ // Parse timestamp from folder name (format: 2025-01-30T12-30-45)
77
+ let date = entry.name;
78
+ try {
79
+ const isoDate = entry.name.replace(/T(\d{2})-(\d{2})-(\d{2})/, 'T$1:$2:$3');
80
+ date = new Date(isoDate).toLocaleString();
81
+ } catch {
82
+ // Keep original name if parsing fails
83
+ }
84
+
85
+ backups.push({
86
+ name: entry.name,
87
+ path: backupPath,
88
+ date,
89
+ hasClaudeDir,
90
+ hasClaudeMd,
91
+ });
92
+ }
93
+ }
94
+ }
95
+ } catch {
96
+ // Silently fail
97
+ }
98
+
99
+ // Sort by name (newest first)
100
+ return backups.sort((a, b) => b.name.localeCompare(a.name));
101
+ }
102
+
103
+ /**
104
+ * Restore from a backup
105
+ */
106
+ async function runRestore(backups) {
107
+ if (backups.length === 0) {
108
+ console.log(chalk.yellow('\n⚠️ No backups found in .claude-backup/\n'));
109
+ return false;
110
+ }
111
+
112
+ console.log(chalk.bold('\n📦 Available backups:\n'));
113
+
114
+ const choices = backups.map((backup, index) => {
115
+ const contents = [];
116
+ if (backup.hasClaudeDir) contents.push('.claude/');
117
+ if (backup.hasClaudeMd) contents.push('CLAUDE.md');
118
+
119
+ return {
120
+ name: `${chalk.yellow(`${index + 1}.`)} ${backup.date} ${chalk.dim(`(${contents.join(', ')})`)}`,
121
+ value: backup,
122
+ short: backup.date,
123
+ };
124
+ });
125
+
126
+ choices.push({
127
+ name: `${chalk.green('0.')} Cancel`,
128
+ value: null,
129
+ short: 'Cancel',
130
+ });
131
+
132
+ const { selectedBackup } = await inquirer.prompt([
133
+ {
134
+ type: 'list',
135
+ name: 'selectedBackup',
136
+ message: 'Select a backup to restore:',
137
+ choices,
138
+ },
139
+ ]);
140
+
141
+ if (!selectedBackup) {
142
+ console.log(chalk.dim('\nCancelled. No changes made.\n'));
143
+ return false;
144
+ }
145
+
146
+ // Confirm restore
147
+ const claudeDir = join(process.cwd(), '.claude');
148
+ const claudeMdPath = join(process.cwd(), 'CLAUDE.md');
149
+ const willOverwrite = [];
150
+
151
+ if (existsSync(claudeDir) && selectedBackup.hasClaudeDir) {
152
+ willOverwrite.push('.claude/');
153
+ }
154
+ if (existsSync(claudeMdPath) && selectedBackup.hasClaudeMd) {
155
+ willOverwrite.push('CLAUDE.md');
156
+ }
157
+
158
+ if (willOverwrite.length > 0) {
159
+ console.log(chalk.yellow(`\n⚠️ This will overwrite: ${willOverwrite.join(', ')}`));
160
+ }
161
+
162
+ const { confirmRestore } = await inquirer.prompt([
163
+ {
164
+ type: 'confirm',
165
+ name: 'confirmRestore',
166
+ message: `Restore from backup "${selectedBackup.date}"?`,
167
+ default: true,
168
+ },
169
+ ]);
170
+
171
+ if (!confirmRestore) {
172
+ console.log(chalk.dim('\nCancelled. No changes made.\n'));
173
+ return false;
174
+ }
175
+
176
+ const spinner = ora('Restoring from backup...').start();
177
+
178
+ try {
179
+ // Restore .claude folder
180
+ if (selectedBackup.hasClaudeDir) {
181
+ const backupClaudeDir = join(selectedBackup.path, '.claude');
182
+ if (existsSync(claudeDir)) {
183
+ rmSync(claudeDir, { recursive: true, force: true });
184
+ }
185
+ copyDirRecursive(backupClaudeDir, claudeDir);
186
+ spinner.text = 'Restored .claude folder...';
187
+ }
188
+
189
+ // Restore CLAUDE.md
190
+ if (selectedBackup.hasClaudeMd) {
191
+ const backupClaudeMd = join(selectedBackup.path, 'CLAUDE.md');
192
+ copyFileSync(backupClaudeMd, claudeMdPath);
193
+ spinner.text = 'Restored CLAUDE.md...';
194
+ }
195
+
196
+ spinner.succeed('Backup restored successfully!');
197
+
198
+ console.log(
199
+ boxen(
200
+ chalk.green('✅ Restored from backup\n\n') +
201
+ `Backup date: ${chalk.cyan(selectedBackup.date)}\n` +
202
+ (selectedBackup.hasClaudeDir ? ` • ${chalk.cyan('.claude/')} restored\n` : '') +
203
+ (selectedBackup.hasClaudeMd ? ` • ${chalk.cyan('CLAUDE.md')} restored\n` : '') +
204
+ '\n' +
205
+ chalk.dim('Restart Claude Code CLI to use restored configuration.'),
206
+ {
207
+ padding: 1,
208
+ borderStyle: 'round',
209
+ borderColor: 'green',
210
+ }
211
+ )
212
+ );
213
+
214
+ return true;
215
+ } catch (error) {
216
+ spinner.fail('Restore failed');
217
+ console.error(chalk.red(error.message));
218
+ return false;
219
+ }
220
+ }
221
+
56
222
  /**
57
223
  * Remove CCASP from a project
58
224
  */
59
225
  async function runRemove() {
60
226
  const claudeDir = join(process.cwd(), '.claude');
227
+ const claudeMdPath = join(process.cwd(), 'CLAUDE.md');
228
+ const existingBackups = findExistingBackups();
61
229
 
62
- if (!existsSync(claudeDir)) {
230
+ // Check if there's anything to work with
231
+ const hasClaudeDir = existsSync(claudeDir);
232
+ const hasClaudeMd = existsSync(claudeMdPath);
233
+
234
+ if (!hasClaudeDir && existingBackups.length === 0) {
63
235
  console.log(chalk.yellow('\n⚠️ No .claude folder found in this project.\n'));
64
- return false;
236
+ if (!hasClaudeMd) {
237
+ return false;
238
+ }
65
239
  }
66
240
 
67
241
  // Show what will be removed
@@ -126,41 +300,67 @@ async function runRemove() {
126
300
  }
127
301
  }
128
302
 
129
- if (itemsToRemove.length === 0) {
303
+ // Check for CLAUDE.md in project root
304
+ if (hasClaudeMd) {
305
+ console.log(` ${chalk.cyan('CLAUDE.md')} ${chalk.dim('(project root)')}`);
306
+ itemsToRemove.push({ type: 'file', path: claudeMdPath, label: 'CLAUDE.md', isRoot: true });
307
+ }
308
+
309
+ if (itemsToRemove.length === 0 && existingBackups.length === 0) {
130
310
  console.log(chalk.yellow(' No CCASP items found.\n'));
131
311
  return false;
132
312
  }
133
313
 
314
+ // Show existing backups count
315
+ if (existingBackups.length > 0) {
316
+ console.log(chalk.dim(`\n 📦 ${existingBackups.length} backup(s) available in .claude-backup/`));
317
+ }
318
+
134
319
  console.log('');
135
320
 
136
- // Removal options
321
+ // Removal options - dynamically build based on what exists
322
+ const removeChoices = [];
323
+
324
+ if (itemsToRemove.length > 0) {
325
+ removeChoices.push(
326
+ {
327
+ name: `${chalk.red('1.')} Remove ALL ${chalk.dim('- Delete .claude/ and CLAUDE.md')}`,
328
+ value: 'all',
329
+ short: 'Remove All',
330
+ },
331
+ {
332
+ name: `${chalk.yellow('2.')} Remove with backup ${chalk.dim('- Full backup to .claude-backup/ first')}`,
333
+ value: 'backup',
334
+ short: 'Backup & Remove',
335
+ },
336
+ {
337
+ name: `${chalk.cyan('3.')} Selective removal ${chalk.dim('- Choose what to remove')}`,
338
+ value: 'selective',
339
+ short: 'Selective',
340
+ }
341
+ );
342
+ }
343
+
344
+ if (existingBackups.length > 0) {
345
+ removeChoices.push({
346
+ name: `${chalk.green('4.')} Restore from backup ${chalk.dim(`- ${existingBackups.length} backup(s) available`)}`,
347
+ value: 'restore',
348
+ short: 'Restore',
349
+ });
350
+ }
351
+
352
+ removeChoices.push({
353
+ name: `${chalk.dim('0.')} Cancel ${chalk.dim('- Keep everything')}`,
354
+ value: 'cancel',
355
+ short: 'Cancel',
356
+ });
357
+
137
358
  const { removeAction } = await inquirer.prompt([
138
359
  {
139
360
  type: 'list',
140
361
  name: 'removeAction',
141
362
  message: 'What would you like to do?',
142
- choices: [
143
- {
144
- name: `${chalk.red('1.')} Remove ALL ${chalk.dim('- Delete entire .claude folder')}`,
145
- value: 'all',
146
- short: 'Remove All',
147
- },
148
- {
149
- name: `${chalk.yellow('2.')} Remove with backup ${chalk.dim('- Backup to .claude-backup/ first')}`,
150
- value: 'backup',
151
- short: 'Backup & Remove',
152
- },
153
- {
154
- name: `${chalk.cyan('3.')} Selective removal ${chalk.dim('- Choose what to remove')}`,
155
- value: 'selective',
156
- short: 'Selective',
157
- },
158
- {
159
- name: `${chalk.green('0.')} Cancel ${chalk.dim('- Keep everything')}`,
160
- value: 'cancel',
161
- short: 'Cancel',
162
- },
163
- ],
363
+ choices: removeChoices,
164
364
  },
165
365
  ]);
166
366
 
@@ -169,6 +369,11 @@ async function runRemove() {
169
369
  return false;
170
370
  }
171
371
 
372
+ // Handle restore action
373
+ if (removeAction === 'restore') {
374
+ return await runRestore(existingBackups);
375
+ }
376
+
172
377
  if (removeAction === 'backup' || removeAction === 'all') {
173
378
  // Confirm dangerous action
174
379
  const { confirmRemove } = await inquirer.prompt([
@@ -190,24 +395,49 @@ async function runRemove() {
190
395
 
191
396
  try {
192
397
  if (removeAction === 'backup') {
193
- // Create backup first
194
- const backupDir = join(process.cwd(), '.claude-backup', new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19));
398
+ // Create backup first - include timestamp folder
399
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
400
+ const backupDir = join(process.cwd(), '.claude-backup', timestamp);
195
401
  mkdirSync(backupDir, { recursive: true });
196
402
 
197
- // Copy entire .claude folder to backup
198
- copyDirRecursive(claudeDir, backupDir);
403
+ // Copy entire .claude folder to backup (nested under .claude/)
404
+ if (hasClaudeDir) {
405
+ const backupClaudeDir = join(backupDir, '.claude');
406
+ copyDirRecursive(claudeDir, backupClaudeDir);
407
+ spinner.text = 'Backed up .claude folder...';
408
+ }
409
+
410
+ // Also backup CLAUDE.md if it exists
411
+ if (hasClaudeMd) {
412
+ copyFileSync(claudeMdPath, join(backupDir, 'CLAUDE.md'));
413
+ spinner.text = 'Backed up CLAUDE.md...';
414
+ }
415
+
199
416
  spinner.succeed(`Backed up to ${chalk.cyan(backupDir)}`);
200
417
 
201
- // Then remove
202
- spinner.start('Removing .claude folder...');
203
- rmSync(claudeDir, { recursive: true, force: true });
204
- spinner.succeed('.claude folder removed');
418
+ // Then remove .claude folder
419
+ if (hasClaudeDir) {
420
+ spinner.start('Removing .claude folder...');
421
+ rmSync(claudeDir, { recursive: true, force: true });
422
+ spinner.succeed('.claude folder removed');
423
+ }
424
+
425
+ // Remove CLAUDE.md
426
+ if (hasClaudeMd) {
427
+ spinner.start('Removing CLAUDE.md...');
428
+ rmSync(claudeMdPath, { force: true });
429
+ spinner.succeed('CLAUDE.md removed');
430
+ }
205
431
 
206
432
  console.log(
207
433
  boxen(
208
- chalk.green('✅ CCASP removed with backup\n\n') +
434
+ chalk.green('✅ CCASP removed with full backup\n\n') +
209
435
  `Backup location:\n${chalk.cyan(backupDir)}\n\n` +
210
- chalk.dim('To restore: copy backup contents to .claude/'),
436
+ chalk.bold('Contents backed up:\n') +
437
+ (hasClaudeDir ? ` • ${chalk.cyan('.claude/')} folder\n` : '') +
438
+ (hasClaudeMd ? ` • ${chalk.cyan('CLAUDE.md')} file\n` : '') +
439
+ '\n' +
440
+ chalk.dim('To restore: run ') + chalk.cyan('ccasp wizard') + chalk.dim(' → Remove CCASP → Restore'),
211
441
  {
212
442
  padding: 1,
213
443
  borderStyle: 'round',
@@ -217,12 +447,22 @@ async function runRemove() {
217
447
  );
218
448
  } else if (removeAction === 'all') {
219
449
  // Remove without backup
220
- rmSync(claudeDir, { recursive: true, force: true });
221
- spinner.succeed('.claude folder removed');
450
+ if (hasClaudeDir) {
451
+ rmSync(claudeDir, { recursive: true, force: true });
452
+ spinner.text = '.claude folder removed...';
453
+ }
454
+
455
+ if (hasClaudeMd) {
456
+ rmSync(claudeMdPath, { force: true });
457
+ spinner.text = 'CLAUDE.md removed...';
458
+ }
459
+
460
+ spinner.succeed('CCASP files removed');
222
461
 
223
462
  console.log(
224
463
  boxen(
225
464
  chalk.green('✅ CCASP removed\n\n') +
465
+ chalk.yellow('⚠️ No backup was created.\n\n') +
226
466
  chalk.dim('Run ') + chalk.cyan('ccasp wizard') + chalk.dim(' to set up again.'),
227
467
  {
228
468
  padding: 1,
@@ -240,8 +480,8 @@ async function runRemove() {
240
480
  type: 'checkbox',
241
481
  name: 'itemsToDelete',
242
482
  message: 'Select items to remove:',
243
- choices: itemsToRemove.map(item => ({
244
- name: item.label,
483
+ choices: itemsToRemove.map((item) => ({
484
+ name: item.isRoot ? `${item.label} ${chalk.dim('(project root)')}` : item.label,
245
485
  value: item,
246
486
  checked: false,
247
487
  })),
@@ -253,22 +493,44 @@ async function runRemove() {
253
493
  return false;
254
494
  }
255
495
 
256
- // Create backups for selected items
257
- const backupDir = join(process.cwd(), '.claude', 'backups', new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19));
258
- mkdirSync(backupDir, { recursive: true });
496
+ // Create backups for selected items in .claude-backup/ (not .claude/backups/)
497
+ // This ensures backups survive if user removes entire .claude folder
498
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
499
+ const backupDir = join(process.cwd(), '.claude-backup', `selective-${timestamp}`);
500
+ const backupClaudeDir = join(backupDir, '.claude');
501
+ mkdirSync(backupClaudeDir, { recursive: true });
259
502
 
260
503
  for (const item of itemsToDelete) {
504
+ // Determine backup destination - CLAUDE.md goes to backup root, others under .claude/
505
+ const backupDest = item.isRoot ? join(backupDir, basename(item.path)) : join(backupClaudeDir, basename(item.path));
506
+
261
507
  if (item.type === 'dir') {
262
- copyDirRecursive(item.path, join(backupDir, basename(item.path)));
508
+ copyDirRecursive(item.path, backupDest);
263
509
  rmSync(item.path, { recursive: true, force: true });
264
510
  } else {
265
- copyFileSync(item.path, join(backupDir, basename(item.path)));
511
+ // Ensure parent directory exists
512
+ const parentDir = dirname(backupDest);
513
+ if (!existsSync(parentDir)) {
514
+ mkdirSync(parentDir, { recursive: true });
515
+ }
516
+ copyFileSync(item.path, backupDest);
266
517
  rmSync(item.path, { force: true });
267
518
  }
268
519
  console.log(` ${chalk.red('✗')} Removed ${item.label}`);
269
520
  }
270
521
 
271
- console.log(chalk.dim(`\nBackups saved to: ${backupDir}\n`));
522
+ console.log(
523
+ boxen(
524
+ chalk.green('✅ Selected items removed\n\n') +
525
+ `Backup location:\n${chalk.cyan(backupDir)}\n\n` +
526
+ chalk.dim('To restore: run ') + chalk.cyan('ccasp wizard') + chalk.dim(' → Remove CCASP → Restore'),
527
+ {
528
+ padding: 1,
529
+ borderStyle: 'round',
530
+ borderColor: 'green',
531
+ }
532
+ )
533
+ );
272
534
  }
273
535
 
274
536
  return true;