claude-cli-advanced-starter-pack 1.0.4 → 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.
@@ -10,13 +10,27 @@ import chalk from 'chalk';
10
10
  import ora from 'ora';
11
11
  import boxen from 'boxen';
12
12
  import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync, renameSync, copyFileSync } from 'fs';
13
- import { join, basename } from 'path';
13
+ import { join, basename, dirname } from 'path';
14
+ import { fileURLToPath } from 'url';
14
15
  import { runInit } from './init.js';
15
16
  import { detectTechStack } from './detect-tech-stack.js';
16
- import { runClaudeAudit, runEnhancement } from './claude-audit.js';
17
+ import { runEnhancement } from './claude-audit.js';
17
18
  import { runSetup as runGitHubSetup } from './setup.js';
18
19
  import { runList } from './list.js';
19
- import { showProjectSettingsMenu } from '../cli/menu.js';
20
+ import {
21
+ performVersionCheck,
22
+ formatUpdateBanner,
23
+ loadReleaseNotes,
24
+ getReleasesSince,
25
+ getAvailableFeatures,
26
+ markFeatureInstalled,
27
+ markFeatureSkipped,
28
+ dismissUpdateNotification,
29
+ getCurrentVersion,
30
+ } from '../utils/version-check.js';
31
+
32
+ const __filename = fileURLToPath(import.meta.url);
33
+ const __dirname = dirname(__filename);
20
34
 
21
35
  /**
22
36
  * Create backup of a file before overwriting
@@ -39,15 +53,189 @@ export function createBackup(filePath) {
39
53
  return backupPath;
40
54
  }
41
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
+
42
222
  /**
43
223
  * Remove CCASP from a project
44
224
  */
45
225
  async function runRemove() {
46
226
  const claudeDir = join(process.cwd(), '.claude');
227
+ const claudeMdPath = join(process.cwd(), 'CLAUDE.md');
228
+ const existingBackups = findExistingBackups();
47
229
 
48
- 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) {
49
235
  console.log(chalk.yellow('\n⚠️ No .claude folder found in this project.\n'));
50
- return false;
236
+ if (!hasClaudeMd) {
237
+ return false;
238
+ }
51
239
  }
52
240
 
53
241
  // Show what will be removed
@@ -112,41 +300,67 @@ async function runRemove() {
112
300
  }
113
301
  }
114
302
 
115
- 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) {
116
310
  console.log(chalk.yellow(' No CCASP items found.\n'));
117
311
  return false;
118
312
  }
119
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
+
120
319
  console.log('');
121
320
 
122
- // 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
+
123
358
  const { removeAction } = await inquirer.prompt([
124
359
  {
125
360
  type: 'list',
126
361
  name: 'removeAction',
127
362
  message: 'What would you like to do?',
128
- choices: [
129
- {
130
- name: `${chalk.red('1.')} Remove ALL ${chalk.dim('- Delete entire .claude folder')}`,
131
- value: 'all',
132
- short: 'Remove All',
133
- },
134
- {
135
- name: `${chalk.yellow('2.')} Remove with backup ${chalk.dim('- Backup to .claude-backup/ first')}`,
136
- value: 'backup',
137
- short: 'Backup & Remove',
138
- },
139
- {
140
- name: `${chalk.cyan('3.')} Selective removal ${chalk.dim('- Choose what to remove')}`,
141
- value: 'selective',
142
- short: 'Selective',
143
- },
144
- {
145
- name: `${chalk.green('0.')} Cancel ${chalk.dim('- Keep everything')}`,
146
- value: 'cancel',
147
- short: 'Cancel',
148
- },
149
- ],
363
+ choices: removeChoices,
150
364
  },
151
365
  ]);
152
366
 
@@ -155,6 +369,11 @@ async function runRemove() {
155
369
  return false;
156
370
  }
157
371
 
372
+ // Handle restore action
373
+ if (removeAction === 'restore') {
374
+ return await runRestore(existingBackups);
375
+ }
376
+
158
377
  if (removeAction === 'backup' || removeAction === 'all') {
159
378
  // Confirm dangerous action
160
379
  const { confirmRemove } = await inquirer.prompt([
@@ -176,24 +395,49 @@ async function runRemove() {
176
395
 
177
396
  try {
178
397
  if (removeAction === 'backup') {
179
- // Create backup first
180
- 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);
181
401
  mkdirSync(backupDir, { recursive: true });
182
402
 
183
- // Copy entire .claude folder to backup
184
- 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
+
185
416
  spinner.succeed(`Backed up to ${chalk.cyan(backupDir)}`);
186
417
 
187
- // Then remove
188
- spinner.start('Removing .claude folder...');
189
- rmSync(claudeDir, { recursive: true, force: true });
190
- 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
+ }
191
431
 
192
432
  console.log(
193
433
  boxen(
194
- chalk.green('✅ CCASP removed with backup\n\n') +
434
+ chalk.green('✅ CCASP removed with full backup\n\n') +
195
435
  `Backup location:\n${chalk.cyan(backupDir)}\n\n` +
196
- 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'),
197
441
  {
198
442
  padding: 1,
199
443
  borderStyle: 'round',
@@ -203,12 +447,22 @@ async function runRemove() {
203
447
  );
204
448
  } else if (removeAction === 'all') {
205
449
  // Remove without backup
206
- rmSync(claudeDir, { recursive: true, force: true });
207
- 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');
208
461
 
209
462
  console.log(
210
463
  boxen(
211
464
  chalk.green('✅ CCASP removed\n\n') +
465
+ chalk.yellow('⚠️ No backup was created.\n\n') +
212
466
  chalk.dim('Run ') + chalk.cyan('ccasp wizard') + chalk.dim(' to set up again.'),
213
467
  {
214
468
  padding: 1,
@@ -226,8 +480,8 @@ async function runRemove() {
226
480
  type: 'checkbox',
227
481
  name: 'itemsToDelete',
228
482
  message: 'Select items to remove:',
229
- choices: itemsToRemove.map(item => ({
230
- name: item.label,
483
+ choices: itemsToRemove.map((item) => ({
484
+ name: item.isRoot ? `${item.label} ${chalk.dim('(project root)')}` : item.label,
231
485
  value: item,
232
486
  checked: false,
233
487
  })),
@@ -239,22 +493,44 @@ async function runRemove() {
239
493
  return false;
240
494
  }
241
495
 
242
- // Create backups for selected items
243
- const backupDir = join(process.cwd(), '.claude', 'backups', new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19));
244
- 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 });
245
502
 
246
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
+
247
507
  if (item.type === 'dir') {
248
- copyDirRecursive(item.path, join(backupDir, basename(item.path)));
508
+ copyDirRecursive(item.path, backupDest);
249
509
  rmSync(item.path, { recursive: true, force: true });
250
510
  } else {
251
- 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);
252
517
  rmSync(item.path, { force: true });
253
518
  }
254
519
  console.log(` ${chalk.red('✗')} Removed ${item.label}`);
255
520
  }
256
521
 
257
- 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
+ );
258
534
  }
259
535
 
260
536
  return true;
@@ -321,32 +597,17 @@ const SETUP_OPTIONS = [
321
597
  short: 'GitHub',
322
598
  },
323
599
  {
324
- name: `${chalk.yellow('4.')} Audit CLAUDE.md ${chalk.dim('- Check existing config')}`,
325
- value: 'audit',
326
- short: 'Audit',
327
- },
328
- {
329
- name: `${chalk.yellow('5.')} Enhance CLAUDE.md ${chalk.dim('- Generate/improve docs')}`,
330
- value: 'enhance',
331
- short: 'Enhance',
332
- },
333
- {
334
- name: `${chalk.yellow('6.')} Detect Tech Stack ${chalk.dim('- Auto-detect project')}`,
335
- value: 'detect',
336
- short: 'Detect',
337
- },
338
- {
339
- name: `${chalk.yellow('7.')} View Templates ${chalk.dim('- Browse available items')}`,
600
+ name: `${chalk.yellow('4.')} View Templates ${chalk.dim('- Browse available items')}`,
340
601
  value: 'templates',
341
602
  short: 'Templates',
342
603
  },
343
604
  {
344
- name: `${chalk.yellow('8.')} Project Settings ${chalk.dim('- Configure deployment, tunnels, etc.')}`,
345
- value: 'settings',
346
- short: 'Settings',
605
+ name: `${chalk.yellow('5.')} Prior Releases ${chalk.dim('- Review & add features from past versions')}`,
606
+ value: 'releases',
607
+ short: 'Releases',
347
608
  },
348
609
  {
349
- name: `${chalk.yellow('9.')} Remove CCASP ${chalk.dim('- Uninstall from this project')}`,
610
+ name: `${chalk.yellow('6.')} Remove CCASP ${chalk.dim('- Uninstall from this project')}`,
350
611
  value: 'remove',
351
612
  short: 'Remove',
352
613
  },
@@ -618,12 +879,407 @@ async function showTemplates() {
618
879
  }
619
880
  }
620
881
 
882
+ /**
883
+ * Show prior releases and allow adding features
884
+ */
885
+ async function showPriorReleases() {
886
+ console.log(chalk.bold('\n📜 Prior Releases\n'));
887
+
888
+ const { releases } = loadReleaseNotes();
889
+ const currentVersion = getCurrentVersion();
890
+
891
+ if (!releases || releases.length === 0) {
892
+ console.log(chalk.yellow(' No release history available.\n'));
893
+ return;
894
+ }
895
+
896
+ // Show release list
897
+ console.log(chalk.dim(' Select a release to view details and available features:\n'));
898
+
899
+ releases.forEach((release, i) => {
900
+ const isCurrent = release.version === currentVersion;
901
+ const marker = isCurrent ? chalk.green('●') : chalk.dim('○');
902
+ const currentLabel = isCurrent ? chalk.green(' (current)') : '';
903
+ console.log(` ${chalk.yellow(i + 1 + '.')} ${marker} v${release.version}${currentLabel} ${chalk.dim(`(${release.date})`)}`);
904
+ console.log(` ${chalk.dim(release.summary)}`);
905
+ });
906
+
907
+ console.log('');
908
+
909
+ const { releaseChoice } = await inquirer.prompt([
910
+ {
911
+ type: 'list',
912
+ name: 'releaseChoice',
913
+ message: 'Select a release to view details:',
914
+ choices: [
915
+ ...releases.map((r, i) => ({
916
+ name: `${i + 1}. v${r.version} - ${r.summary}`,
917
+ value: i,
918
+ short: `v${r.version}`,
919
+ })),
920
+ {
921
+ name: `${chalk.cyan('A.')} Add available features to project`,
922
+ value: 'add',
923
+ short: 'Add Features',
924
+ },
925
+ {
926
+ name: `${chalk.dim('0.')} Back to menu`,
927
+ value: 'back',
928
+ short: 'Back',
929
+ },
930
+ ],
931
+ pageSize: 12,
932
+ },
933
+ ]);
934
+
935
+ if (releaseChoice === 'back') {
936
+ return;
937
+ }
938
+
939
+ if (releaseChoice === 'add') {
940
+ await showAddFeaturesMenu();
941
+ return;
942
+ }
943
+
944
+ // Show release details
945
+ const release = releases[releaseChoice];
946
+ await showReleaseDetails(release);
947
+ }
948
+
949
+ /**
950
+ * Show detailed release information
951
+ */
952
+ async function showReleaseDetails(release) {
953
+ console.log(
954
+ boxen(
955
+ chalk.bold.cyan(`v${release.version}\n`) +
956
+ chalk.dim(`Released: ${release.date}\n\n`) +
957
+ chalk.white(release.summary),
958
+ {
959
+ padding: 1,
960
+ borderStyle: 'round',
961
+ borderColor: 'cyan',
962
+ title: '📦 Release Details',
963
+ titleAlignment: 'center',
964
+ }
965
+ )
966
+ );
967
+
968
+ // Show highlights
969
+ if (release.highlights && release.highlights.length > 0) {
970
+ console.log(chalk.bold('\n✨ Highlights:\n'));
971
+ release.highlights.forEach((h) => {
972
+ console.log(` • ${h}`);
973
+ });
974
+ }
975
+
976
+ // Show new features
977
+ if (release.newFeatures) {
978
+ const { commands, agents, skills, hooks, other } = release.newFeatures;
979
+
980
+ if (commands && commands.length > 0) {
981
+ console.log(chalk.bold('\n📝 New Commands:\n'));
982
+ commands.forEach((cmd) => {
983
+ console.log(` ${chalk.cyan(`/${cmd.name}`)} - ${cmd.description}`);
984
+ });
985
+ }
986
+
987
+ if (agents && agents.length > 0) {
988
+ console.log(chalk.bold('\n🤖 New Agents:\n'));
989
+ agents.forEach((agent) => {
990
+ console.log(` ${chalk.cyan(agent.name)} - ${agent.description}`);
991
+ });
992
+ }
993
+
994
+ if (skills && skills.length > 0) {
995
+ console.log(chalk.bold('\n🎯 New Skills:\n'));
996
+ skills.forEach((skill) => {
997
+ console.log(` ${chalk.cyan(skill.name)} - ${skill.description}`);
998
+ });
999
+ }
1000
+
1001
+ if (hooks && hooks.length > 0) {
1002
+ console.log(chalk.bold('\n🪝 New Hooks:\n'));
1003
+ hooks.forEach((hook) => {
1004
+ console.log(` ${chalk.cyan(hook.name)} - ${hook.description}`);
1005
+ });
1006
+ }
1007
+
1008
+ if (other && other.length > 0) {
1009
+ console.log(chalk.bold('\n🔧 Other Improvements:\n'));
1010
+ other.forEach((item) => {
1011
+ console.log(` ${chalk.cyan(item.name)} - ${item.description}`);
1012
+ });
1013
+ }
1014
+ }
1015
+
1016
+ // Show breaking changes
1017
+ if (release.breaking && release.breaking.length > 0) {
1018
+ console.log(chalk.bold.red('\n⚠️ Breaking Changes:\n'));
1019
+ release.breaking.forEach((b) => {
1020
+ console.log(` ${chalk.red('!')} ${b}`);
1021
+ });
1022
+ }
1023
+
1024
+ console.log('');
1025
+
1026
+ // Offer to add features from this release
1027
+ const hasNewFeatures =
1028
+ release.newFeatures &&
1029
+ (release.newFeatures.commands?.length > 0 ||
1030
+ release.newFeatures.agents?.length > 0 ||
1031
+ release.newFeatures.skills?.length > 0 ||
1032
+ release.newFeatures.hooks?.length > 0);
1033
+
1034
+ if (hasNewFeatures) {
1035
+ const { addFeatures } = await inquirer.prompt([
1036
+ {
1037
+ type: 'confirm',
1038
+ name: 'addFeatures',
1039
+ message: 'Would you like to add features from this release to your project?',
1040
+ default: false,
1041
+ },
1042
+ ]);
1043
+
1044
+ if (addFeatures) {
1045
+ await addFeaturesFromRelease(release);
1046
+ }
1047
+ }
1048
+ }
1049
+
1050
+ /**
1051
+ * Show menu to add available features
1052
+ */
1053
+ async function showAddFeaturesMenu() {
1054
+ const claudeDir = join(process.cwd(), '.claude');
1055
+ const commandsDir = join(claudeDir, 'commands');
1056
+
1057
+ if (!existsSync(claudeDir)) {
1058
+ console.log(chalk.yellow('\n⚠️ No .claude folder found. Run Quick Start (1) or Full Setup (2) first.\n'));
1059
+ return;
1060
+ }
1061
+
1062
+ // Get existing commands
1063
+ const existingCommands = existsSync(commandsDir)
1064
+ ? readdirSync(commandsDir).filter((f) => f.endsWith('.md') && f !== 'INDEX.md' && f !== 'README.md').map((f) => f.replace('.md', ''))
1065
+ : [];
1066
+
1067
+ // Get all available features from releases
1068
+ const { releases, featureRegistry } = loadReleaseNotes();
1069
+
1070
+ if (!featureRegistry || !featureRegistry.commands) {
1071
+ console.log(chalk.yellow('\n No feature registry available.\n'));
1072
+ return;
1073
+ }
1074
+
1075
+ // Find commands not yet installed
1076
+ const availableCommands = Object.entries(featureRegistry.commands)
1077
+ .filter(([name, info]) => !existingCommands.includes(name) && !info.required)
1078
+ .map(([name, info]) => {
1079
+ // Find description from releases
1080
+ let description = 'No description available';
1081
+ for (const release of releases) {
1082
+ const cmd = release.newFeatures?.commands?.find((c) => c.name === name);
1083
+ if (cmd) {
1084
+ description = cmd.description;
1085
+ break;
1086
+ }
1087
+ }
1088
+ return { name, description, addedIn: info.addedIn };
1089
+ });
1090
+
1091
+ if (availableCommands.length === 0) {
1092
+ console.log(chalk.green('\n✓ All available commands are already installed!\n'));
1093
+ return;
1094
+ }
1095
+
1096
+ console.log(chalk.bold('\n📦 Available Commands to Add:\n'));
1097
+ console.log(chalk.dim(' Select commands to add to your project:\n'));
1098
+
1099
+ const { selectedCommands } = await inquirer.prompt([
1100
+ {
1101
+ type: 'checkbox',
1102
+ name: 'selectedCommands',
1103
+ message: 'Select commands to install:',
1104
+ choices: availableCommands.map((cmd) => ({
1105
+ name: `/${cmd.name} - ${cmd.description} ${chalk.dim(`(v${cmd.addedIn})`)}`,
1106
+ value: cmd.name,
1107
+ checked: false,
1108
+ })),
1109
+ pageSize: 15,
1110
+ },
1111
+ ]);
1112
+
1113
+ if (selectedCommands.length === 0) {
1114
+ console.log(chalk.dim('\n No commands selected.\n'));
1115
+ return;
1116
+ }
1117
+
1118
+ // Install selected commands
1119
+ const spinner = ora('Installing commands...').start();
1120
+ const installed = [];
1121
+ const failed = [];
1122
+
1123
+ for (const cmdName of selectedCommands) {
1124
+ try {
1125
+ // Look for template file
1126
+ const templatePath = join(__dirname, '..', '..', 'templates', 'commands', `${cmdName}.template.md`);
1127
+
1128
+ if (existsSync(templatePath)) {
1129
+ const content = readFileSync(templatePath, 'utf8');
1130
+ const cmdPath = join(commandsDir, `${cmdName}.md`);
1131
+ writeFileSync(cmdPath, content, 'utf8');
1132
+ installed.push(cmdName);
1133
+ markFeatureInstalled(cmdName);
1134
+ } else {
1135
+ failed.push({ name: cmdName, error: 'Template not found' });
1136
+ }
1137
+ } catch (error) {
1138
+ failed.push({ name: cmdName, error: error.message });
1139
+ }
1140
+ }
1141
+
1142
+ spinner.stop();
1143
+
1144
+ if (installed.length > 0) {
1145
+ console.log(chalk.green(`\n✓ Installed ${installed.length} command(s):`));
1146
+ installed.forEach((cmd) => {
1147
+ console.log(` ${chalk.cyan(`/${cmd}`)}`);
1148
+ });
1149
+ }
1150
+
1151
+ if (failed.length > 0) {
1152
+ console.log(chalk.red(`\n✗ Failed to install ${failed.length} command(s):`));
1153
+ failed.forEach((f) => {
1154
+ console.log(` ${chalk.red(`/${f.name}`)}: ${f.error}`);
1155
+ });
1156
+ }
1157
+
1158
+ if (installed.length > 0) {
1159
+ showRestartReminder();
1160
+ }
1161
+ }
1162
+
1163
+ /**
1164
+ * Add features from a specific release
1165
+ */
1166
+ async function addFeaturesFromRelease(release) {
1167
+ const claudeDir = join(process.cwd(), '.claude');
1168
+ const commandsDir = join(claudeDir, 'commands');
1169
+
1170
+ if (!existsSync(claudeDir)) {
1171
+ console.log(chalk.yellow('\n⚠️ No .claude folder found. Run Quick Start (1) or Full Setup (2) first.\n'));
1172
+ return;
1173
+ }
1174
+
1175
+ if (!release.newFeatures?.commands || release.newFeatures.commands.length === 0) {
1176
+ console.log(chalk.yellow('\n No commands to add from this release.\n'));
1177
+ return;
1178
+ }
1179
+
1180
+ // Get existing commands
1181
+ const existingCommands = existsSync(commandsDir)
1182
+ ? readdirSync(commandsDir).filter((f) => f.endsWith('.md')).map((f) => f.replace('.md', ''))
1183
+ : [];
1184
+
1185
+ // Filter to commands not yet installed
1186
+ const availableCommands = release.newFeatures.commands.filter((cmd) => !existingCommands.includes(cmd.name));
1187
+
1188
+ if (availableCommands.length === 0) {
1189
+ console.log(chalk.green('\n✓ All commands from this release are already installed!\n'));
1190
+ return;
1191
+ }
1192
+
1193
+ const { selectedCommands } = await inquirer.prompt([
1194
+ {
1195
+ type: 'checkbox',
1196
+ name: 'selectedCommands',
1197
+ message: 'Select commands to install:',
1198
+ choices: availableCommands.map((cmd) => ({
1199
+ name: `/${cmd.name} - ${cmd.description}`,
1200
+ value: cmd.name,
1201
+ checked: true,
1202
+ })),
1203
+ pageSize: 10,
1204
+ },
1205
+ ]);
1206
+
1207
+ if (selectedCommands.length === 0) {
1208
+ console.log(chalk.dim('\n No commands selected.\n'));
1209
+ return;
1210
+ }
1211
+
1212
+ // Install selected commands
1213
+ const spinner = ora('Installing commands...').start();
1214
+ const installed = [];
1215
+ const failed = [];
1216
+
1217
+ for (const cmdName of selectedCommands) {
1218
+ try {
1219
+ const templatePath = join(__dirname, '..', '..', 'templates', 'commands', `${cmdName}.template.md`);
1220
+
1221
+ if (existsSync(templatePath)) {
1222
+ const content = readFileSync(templatePath, 'utf8');
1223
+ const cmdPath = join(commandsDir, `${cmdName}.md`);
1224
+ writeFileSync(cmdPath, content, 'utf8');
1225
+ installed.push(cmdName);
1226
+ markFeatureInstalled(cmdName);
1227
+ } else {
1228
+ failed.push({ name: cmdName, error: 'Template not found' });
1229
+ }
1230
+ } catch (error) {
1231
+ failed.push({ name: cmdName, error: error.message });
1232
+ }
1233
+ }
1234
+
1235
+ spinner.stop();
1236
+
1237
+ if (installed.length > 0) {
1238
+ console.log(chalk.green(`\n✓ Installed ${installed.length} command(s):`));
1239
+ installed.forEach((cmd) => {
1240
+ console.log(` ${chalk.cyan(`/${cmd}`)}`);
1241
+ });
1242
+ showRestartReminder();
1243
+ }
1244
+
1245
+ if (failed.length > 0) {
1246
+ console.log(chalk.red(`\n✗ Failed to install ${failed.length} command(s):`));
1247
+ failed.forEach((f) => {
1248
+ console.log(` ${chalk.red(`/${f.name}`)}: ${f.error}`);
1249
+ });
1250
+ }
1251
+ }
1252
+
1253
+ /**
1254
+ * Check for updates and show banner if available
1255
+ */
1256
+ async function checkAndShowUpdateBanner() {
1257
+ try {
1258
+ const checkResult = await performVersionCheck(process.cwd(), false);
1259
+
1260
+ if (checkResult.updateAvailable && checkResult.shouldNotify) {
1261
+ const banner = formatUpdateBanner(checkResult);
1262
+ if (banner) {
1263
+ console.log(chalk.yellow(banner));
1264
+ }
1265
+ }
1266
+
1267
+ return checkResult;
1268
+ } catch {
1269
+ // Silently fail - network might be unavailable
1270
+ return null;
1271
+ }
1272
+ }
1273
+
621
1274
  /**
622
1275
  * Main setup wizard - entry point
623
1276
  */
624
1277
  export async function runSetupWizard(options = {}) {
625
1278
  showSetupHeader();
626
1279
 
1280
+ // Check for updates in background (non-blocking display)
1281
+ await checkAndShowUpdateBanner();
1282
+
627
1283
  // Check if .claude already exists
628
1284
  const claudeDir = join(process.cwd(), '.claude');
629
1285
  const claudeMd = join(process.cwd(), 'CLAUDE.md');
@@ -644,7 +1300,7 @@ export async function runSetupWizard(options = {}) {
644
1300
  name: 'action',
645
1301
  message: 'What would you like to do?',
646
1302
  choices: SETUP_OPTIONS,
647
- pageSize: 10,
1303
+ pageSize: 12,
648
1304
  },
649
1305
  ]);
650
1306
 
@@ -666,44 +1322,12 @@ export async function runSetupWizard(options = {}) {
666
1322
  showRestartReminder();
667
1323
  break;
668
1324
 
669
- case 'audit':
670
- await runClaudeAudit();
671
- // Audit doesn't modify files, no restart needed
672
- break;
673
-
674
- case 'enhance':
675
- await runEnhancement();
676
- // Enhancement modifies CLAUDE.md which requires restart
677
- showRestartReminder();
678
- break;
679
-
680
- case 'detect':
681
- const spinner = ora('Detecting tech stack...').start();
682
- try {
683
- const techStack = await detectTechStack(process.cwd());
684
- spinner.succeed('Detection complete!');
685
- console.log(chalk.bold('\nDetected Tech Stack:'));
686
- console.log(JSON.stringify(techStack, null, 2));
687
- } catch (error) {
688
- spinner.fail('Detection failed');
689
- console.error(chalk.red(error.message));
690
- }
691
- console.log('');
692
- break;
693
-
694
1325
  case 'templates':
695
1326
  await showTemplates();
696
1327
  break;
697
1328
 
698
- case 'settings':
699
- // Check if .claude folder exists first
700
- if (!existsSync(join(process.cwd(), '.claude'))) {
701
- console.log(chalk.yellow('\n⚠️ No .claude folder found. Run Quick Start (1) or Full Setup (2) first.\n'));
702
- } else {
703
- await showProjectSettingsMenu();
704
- // Settings modify tech-stack.json which may require restart
705
- showRestartReminder();
706
- }
1329
+ case 'releases':
1330
+ await showPriorReleases();
707
1331
  break;
708
1332
 
709
1333
  case 'remove':
@@ -733,8 +1357,6 @@ Run the Claude CLI Advanced Starter Pack setup wizard.
733
1357
 
734
1358
  This command launches the interactive setup wizard for configuring:
735
1359
  - .claude folder structure
736
- - CLAUDE.md generation
737
- - Tech stack detection
738
1360
  - GitHub project integration
739
1361
  - Agents, hooks, and skills
740
1362
 
@@ -744,10 +1366,14 @@ Reply with a number to jump to that option:
744
1366
  1. Quick Start - Auto-detect and initialize
745
1367
  2. Full Setup - All features with customization
746
1368
  3. GitHub Setup - Connect to project board
747
- 4. Audit - Check existing CLAUDE.md
748
- 5. Enhance - Generate/improve CLAUDE.md
749
- 6. Detect - Show detected tech stack
750
- 7. Templates - Browse available templates
1369
+ 4. View Templates - Browse available templates
1370
+ 5. Prior Releases - Review & add features from past versions
1371
+ 6. Remove CCASP - Uninstall from this project
1372
+
1373
+ ## Related Commands
1374
+
1375
+ - \`/project-impl\` - Agent-powered project implementation (audit, enhance, detect, configure)
1376
+ - \`/update-check\` - Check for updates and add new features
751
1377
 
752
1378
  ## From Terminal
753
1379