claude-cli-advanced-starter-pack 1.0.2 → 1.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-cli-advanced-starter-pack",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Advanced Claude Code CLI toolkit - agents, hooks, skills, MCP servers, phased development, and GitHub integration",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -14,6 +14,7 @@ import { join, dirname, basename } from 'path';
14
14
  import { fileURLToPath } from 'url';
15
15
  import { showHeader, showSuccess, showError, showWarning, showInfo } from '../cli/menu.js';
16
16
  import { getVersion } from '../utils.js';
17
+ import { createBackup } from './setup-wizard.js';
17
18
 
18
19
  const __filename = fileURLToPath(import.meta.url);
19
20
  const __dirname = dirname(__filename);
@@ -1226,17 +1227,12 @@ export default async function ${hookName.replace(/-/g, '_')}(context) {
1226
1227
  */
1227
1228
  function generateSettingsJson(projectName) {
1228
1229
  return JSON.stringify({
1229
- "$schema": "https://raw.githubusercontent.com/anthropics/claude-code/main/.claude/settings.schema.json",
1230
- "project": {
1231
- "name": projectName,
1232
- "description": "Project configured with Claude CLI Advanced Starter Pack"
1233
- },
1230
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
1234
1231
  "permissions": {
1235
- "allowedTools": ["Read", "Write", "Edit", "Glob", "Grep", "Bash", "Task"],
1236
- "deniedTools": []
1232
+ "allow": [],
1233
+ "deny": []
1237
1234
  },
1238
- "agents": [],
1239
- "hooks": []
1235
+ "hooks": {}
1240
1236
  }, null, 2);
1241
1237
  }
1242
1238
 
@@ -1245,12 +1241,12 @@ function generateSettingsJson(projectName) {
1245
1241
  */
1246
1242
  function generateSettingsLocalJson() {
1247
1243
  return JSON.stringify({
1248
- "$schema": "https://raw.githubusercontent.com/anthropics/claude-code/main/.claude/settings.schema.json",
1244
+ "$schema": "https://json.schemastore.org/claude-code-settings.json",
1249
1245
  "permissions": {
1250
- "allowedTools": [],
1251
- "deniedTools": []
1246
+ "allow": [],
1247
+ "deny": []
1252
1248
  },
1253
- "hooks": []
1249
+ "hooks": {}
1254
1250
  }, null, 2);
1255
1251
  }
1256
1252
 
@@ -1528,7 +1524,8 @@ export async function runInit(options = {}) {
1528
1524
  message: 'How would you like to handle existing commands?',
1529
1525
  choices: [
1530
1526
  { name: 'Skip existing - only install new commands (recommended)', value: 'skip' },
1531
- { name: 'Overwrite all - replace existing with starter pack versions', value: 'overwrite' },
1527
+ { name: 'Overwrite with backup - save existing to .claude/backups/ first', value: 'backup' },
1528
+ { name: 'Overwrite all - replace existing (no backup)', value: 'overwrite' },
1532
1529
  { name: 'Cancel installation', value: 'cancel' },
1533
1530
  ],
1534
1531
  },
@@ -1539,7 +1536,8 @@ export async function runInit(options = {}) {
1539
1536
  return;
1540
1537
  }
1541
1538
 
1542
- overwrite = overwriteChoice === 'overwrite';
1539
+ overwrite = overwriteChoice === 'overwrite' || overwriteChoice === 'backup';
1540
+ const createBackups = overwriteChoice === 'backup';
1543
1541
 
1544
1542
  if (!overwrite) {
1545
1543
  // Filter out existing commands (keep only new ones + required)
@@ -1547,11 +1545,17 @@ export async function runInit(options = {}) {
1547
1545
  finalCommands.length = 0;
1548
1546
  finalCommands.push(...filtered);
1549
1547
  console.log(chalk.green(`\n ✓ Will install ${finalCommands.length} new command(s), preserving ${commandsToOverwrite.length} existing`));
1548
+ } else if (createBackups) {
1549
+ console.log(chalk.cyan(`\n ✓ Will backup and overwrite ${commandsToOverwrite.length} existing command(s)`));
1550
1550
  } else {
1551
1551
  console.log(chalk.yellow(`\n ⚠ Will overwrite ${commandsToOverwrite.length} existing command(s)`));
1552
1552
  }
1553
1553
  }
1554
1554
 
1555
+ // Track if we should create backups (set outside the if block for use later)
1556
+ const createBackups = options.backup || (typeof overwrite !== 'undefined' && commandsToOverwrite.length > 0 && !options.force);
1557
+ let backedUpFiles = [];
1558
+
1555
1559
  // Step 7: Install commands
1556
1560
  console.log(chalk.bold('Step 6: Installing slash commands\n'));
1557
1561
 
@@ -1594,6 +1598,14 @@ export async function runInit(options = {}) {
1594
1598
  }
1595
1599
  }
1596
1600
 
1601
+ // Create backup if overwriting existing file
1602
+ if (existsSync(cmdPath) && createBackups) {
1603
+ const backupPath = createBackup(cmdPath);
1604
+ if (backupPath) {
1605
+ backedUpFiles.push({ original: cmdPath, backup: backupPath });
1606
+ }
1607
+ }
1608
+
1597
1609
  writeFileSync(cmdPath, content, 'utf8');
1598
1610
  installed.push(cmdName);
1599
1611
  } catch (error) {
@@ -1603,6 +1615,11 @@ export async function runInit(options = {}) {
1603
1615
 
1604
1616
  spinner.stop();
1605
1617
 
1618
+ // Show backup summary if any files were backed up
1619
+ if (backedUpFiles.length > 0) {
1620
+ console.log(chalk.cyan(`\n 📁 Backed up ${backedUpFiles.length} file(s) to .claude/backups/`));
1621
+ }
1622
+
1606
1623
  // Step 7: Generate INDEX.md
1607
1624
  const indexPath = join(commandsDir, 'INDEX.md');
1608
1625
  const indexContent = generateIndexFile(installed, projectName);
@@ -9,8 +9,8 @@ import inquirer from 'inquirer';
9
9
  import chalk from 'chalk';
10
10
  import ora from 'ora';
11
11
  import boxen from 'boxen';
12
- import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
13
- import { join } from 'path';
12
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync, renameSync, copyFileSync } from 'fs';
13
+ import { join, basename } from 'path';
14
14
  import { runInit } from './init.js';
15
15
  import { detectTechStack } from './detect-tech-stack.js';
16
16
  import { runClaudeAudit, runEnhancement } from './claude-audit.js';
@@ -18,6 +18,272 @@ import { runSetup as runGitHubSetup } from './setup.js';
18
18
  import { runList } from './list.js';
19
19
  import { showProjectSettingsMenu } from '../cli/menu.js';
20
20
 
21
+ /**
22
+ * Create backup of a file before overwriting
23
+ * @param {string} filePath - Path to file to backup
24
+ * @returns {string|null} - Path to backup file, or null if no backup needed
25
+ */
26
+ export function createBackup(filePath) {
27
+ if (!existsSync(filePath)) return null;
28
+
29
+ const backupDir = join(process.cwd(), '.claude', 'backups');
30
+ if (!existsSync(backupDir)) {
31
+ mkdirSync(backupDir, { recursive: true });
32
+ }
33
+
34
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
35
+ const fileName = basename(filePath);
36
+ const backupPath = join(backupDir, `${fileName}.${timestamp}.bak`);
37
+
38
+ copyFileSync(filePath, backupPath);
39
+ return backupPath;
40
+ }
41
+
42
+ /**
43
+ * Remove CCASP from a project
44
+ */
45
+ async function runRemove() {
46
+ const claudeDir = join(process.cwd(), '.claude');
47
+
48
+ if (!existsSync(claudeDir)) {
49
+ console.log(chalk.yellow('\n⚠️ No .claude folder found in this project.\n'));
50
+ return false;
51
+ }
52
+
53
+ // Show what will be removed
54
+ console.log(chalk.bold('\n📁 CCASP files found in this project:\n'));
55
+
56
+ const itemsToRemove = [];
57
+
58
+ // Check for commands
59
+ const commandsDir = join(claudeDir, 'commands');
60
+ if (existsSync(commandsDir)) {
61
+ const commands = readdirSync(commandsDir).filter(f => f.endsWith('.md'));
62
+ if (commands.length > 0) {
63
+ console.log(` ${chalk.cyan('commands/')} - ${commands.length} command(s)`);
64
+ itemsToRemove.push({ type: 'dir', path: commandsDir, label: 'commands/' });
65
+ }
66
+ }
67
+
68
+ // Check for agents
69
+ const agentsDir = join(claudeDir, 'agents');
70
+ if (existsSync(agentsDir)) {
71
+ const agents = readdirSync(agentsDir).filter(f => f.endsWith('.md'));
72
+ if (agents.length > 0) {
73
+ console.log(` ${chalk.cyan('agents/')} - ${agents.length} agent(s)`);
74
+ itemsToRemove.push({ type: 'dir', path: agentsDir, label: 'agents/' });
75
+ }
76
+ }
77
+
78
+ // Check for skills
79
+ const skillsDir = join(claudeDir, 'skills');
80
+ if (existsSync(skillsDir)) {
81
+ const skills = readdirSync(skillsDir).filter(f => !f.startsWith('.'));
82
+ if (skills.length > 0) {
83
+ console.log(` ${chalk.cyan('skills/')} - ${skills.length} skill(s)`);
84
+ itemsToRemove.push({ type: 'dir', path: skillsDir, label: 'skills/' });
85
+ }
86
+ }
87
+
88
+ // Check for hooks
89
+ const hooksDir = join(claudeDir, 'hooks');
90
+ if (existsSync(hooksDir)) {
91
+ const hooks = readdirSync(hooksDir).filter(f => f.endsWith('.js'));
92
+ if (hooks.length > 0) {
93
+ console.log(` ${chalk.cyan('hooks/')} - ${hooks.length} hook(s)`);
94
+ itemsToRemove.push({ type: 'dir', path: hooksDir, label: 'hooks/' });
95
+ }
96
+ }
97
+
98
+ // Check for docs
99
+ const docsDir = join(claudeDir, 'docs');
100
+ if (existsSync(docsDir)) {
101
+ console.log(` ${chalk.cyan('docs/')} - documentation`);
102
+ itemsToRemove.push({ type: 'dir', path: docsDir, label: 'docs/' });
103
+ }
104
+
105
+ // Check for config files
106
+ const configFiles = ['settings.json', 'settings.local.json', 'tech-stack.json'];
107
+ for (const file of configFiles) {
108
+ const filePath = join(claudeDir, file);
109
+ if (existsSync(filePath)) {
110
+ console.log(` ${chalk.cyan(file)}`);
111
+ itemsToRemove.push({ type: 'file', path: filePath, label: file });
112
+ }
113
+ }
114
+
115
+ if (itemsToRemove.length === 0) {
116
+ console.log(chalk.yellow(' No CCASP items found.\n'));
117
+ return false;
118
+ }
119
+
120
+ console.log('');
121
+
122
+ // Removal options
123
+ const { removeAction } = await inquirer.prompt([
124
+ {
125
+ type: 'list',
126
+ name: 'removeAction',
127
+ 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
+ ],
150
+ },
151
+ ]);
152
+
153
+ if (removeAction === 'cancel') {
154
+ console.log(chalk.dim('\nCancelled. No changes made.\n'));
155
+ return false;
156
+ }
157
+
158
+ if (removeAction === 'backup' || removeAction === 'all') {
159
+ // Confirm dangerous action
160
+ const { confirmRemove } = await inquirer.prompt([
161
+ {
162
+ type: 'confirm',
163
+ name: 'confirmRemove',
164
+ message: chalk.red(`Are you sure you want to ${removeAction === 'all' ? 'DELETE' : 'backup and delete'} all CCASP files?`),
165
+ default: false,
166
+ },
167
+ ]);
168
+
169
+ if (!confirmRemove) {
170
+ console.log(chalk.dim('\nCancelled. No changes made.\n'));
171
+ return false;
172
+ }
173
+ }
174
+
175
+ const spinner = ora('Processing...').start();
176
+
177
+ try {
178
+ if (removeAction === 'backup') {
179
+ // Create backup first
180
+ const backupDir = join(process.cwd(), '.claude-backup', new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19));
181
+ mkdirSync(backupDir, { recursive: true });
182
+
183
+ // Copy entire .claude folder to backup
184
+ copyDirRecursive(claudeDir, backupDir);
185
+ spinner.succeed(`Backed up to ${chalk.cyan(backupDir)}`);
186
+
187
+ // Then remove
188
+ spinner.start('Removing .claude folder...');
189
+ rmSync(claudeDir, { recursive: true, force: true });
190
+ spinner.succeed('.claude folder removed');
191
+
192
+ console.log(
193
+ boxen(
194
+ chalk.green('✅ CCASP removed with backup\n\n') +
195
+ `Backup location:\n${chalk.cyan(backupDir)}\n\n` +
196
+ chalk.dim('To restore: copy backup contents to .claude/'),
197
+ {
198
+ padding: 1,
199
+ borderStyle: 'round',
200
+ borderColor: 'green',
201
+ }
202
+ )
203
+ );
204
+ } else if (removeAction === 'all') {
205
+ // Remove without backup
206
+ rmSync(claudeDir, { recursive: true, force: true });
207
+ spinner.succeed('.claude folder removed');
208
+
209
+ console.log(
210
+ boxen(
211
+ chalk.green('✅ CCASP removed\n\n') +
212
+ chalk.dim('Run ') + chalk.cyan('ccasp wizard') + chalk.dim(' to set up again.'),
213
+ {
214
+ padding: 1,
215
+ borderStyle: 'round',
216
+ borderColor: 'green',
217
+ }
218
+ )
219
+ );
220
+ } else if (removeAction === 'selective') {
221
+ spinner.stop();
222
+
223
+ // Let user select what to remove
224
+ const { itemsToDelete } = await inquirer.prompt([
225
+ {
226
+ type: 'checkbox',
227
+ name: 'itemsToDelete',
228
+ message: 'Select items to remove:',
229
+ choices: itemsToRemove.map(item => ({
230
+ name: item.label,
231
+ value: item,
232
+ checked: false,
233
+ })),
234
+ },
235
+ ]);
236
+
237
+ if (itemsToDelete.length === 0) {
238
+ console.log(chalk.dim('\nNo items selected. No changes made.\n'));
239
+ return false;
240
+ }
241
+
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 });
245
+
246
+ for (const item of itemsToDelete) {
247
+ if (item.type === 'dir') {
248
+ copyDirRecursive(item.path, join(backupDir, basename(item.path)));
249
+ rmSync(item.path, { recursive: true, force: true });
250
+ } else {
251
+ copyFileSync(item.path, join(backupDir, basename(item.path)));
252
+ rmSync(item.path, { force: true });
253
+ }
254
+ console.log(` ${chalk.red('✗')} Removed ${item.label}`);
255
+ }
256
+
257
+ console.log(chalk.dim(`\nBackups saved to: ${backupDir}\n`));
258
+ }
259
+
260
+ return true;
261
+ } catch (error) {
262
+ spinner.fail('Removal failed');
263
+ console.error(chalk.red(error.message));
264
+ return false;
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Recursively copy a directory
270
+ */
271
+ function copyDirRecursive(src, dest) {
272
+ mkdirSync(dest, { recursive: true });
273
+ const entries = readdirSync(src, { withFileTypes: true });
274
+
275
+ for (const entry of entries) {
276
+ const srcPath = join(src, entry.name);
277
+ const destPath = join(dest, entry.name);
278
+
279
+ if (entry.isDirectory()) {
280
+ copyDirRecursive(srcPath, destPath);
281
+ } else {
282
+ copyFileSync(srcPath, destPath);
283
+ }
284
+ }
285
+ }
286
+
21
287
  /**
22
288
  * Display setup header
23
289
  */
@@ -79,6 +345,11 @@ const SETUP_OPTIONS = [
79
345
  value: 'settings',
80
346
  short: 'Settings',
81
347
  },
348
+ {
349
+ name: `${chalk.yellow('9.')} Remove CCASP ${chalk.dim('- Uninstall from this project')}`,
350
+ value: 'remove',
351
+ short: 'Remove',
352
+ },
82
353
  {
83
354
  name: `${chalk.yellow('0.')} Exit`,
84
355
  value: 'exit',
@@ -435,6 +706,13 @@ export async function runSetupWizard(options = {}) {
435
706
  }
436
707
  break;
437
708
 
709
+ case 'remove':
710
+ const removed = await runRemove();
711
+ if (removed) {
712
+ running = false; // Exit wizard after removal
713
+ }
714
+ break;
715
+
438
716
  case 'exit':
439
717
  running = false;
440
718
  console.log(chalk.dim('\nRun `ccasp wizard` anytime to return.\n'));