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.
- package/README.md +30 -1
- package/package.json +69 -69
- package/src/commands/init.js +272 -39
- package/src/commands/setup-wizard.js +308 -46
- package/src/data/releases.json +349 -265
- package/templates/hooks/ccasp-update-check.template.js +241 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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.
|
|
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
|
-
|
|
221
|
-
|
|
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
|
-
|
|
258
|
-
|
|
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,
|
|
508
|
+
copyDirRecursive(item.path, backupDest);
|
|
263
509
|
rmSync(item.path, { recursive: true, force: true });
|
|
264
510
|
} else {
|
|
265
|
-
|
|
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(
|
|
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;
|