@hatem427/code-guard-ci 3.5.4 → 3.5.5

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/scripts/cli.ts CHANGED
@@ -93,7 +93,6 @@ ${c.bold('Init Options:')}
93
93
 
94
94
  ${c.bold('Uninstall Options:')}
95
95
  --yes, -y Skip confirmation prompt
96
- --no-backup Don't create backup of AI configs
97
96
 
98
97
  ${c.bold('Examples:')}
99
98
  ${c.dim('# Full automatic setup')}
@@ -111,11 +110,11 @@ ${c.bold('Examples:')}
111
110
  ${c.dim('# Generate docs')}
112
111
  code-guard doc --name="user-card" --type=ui
113
112
 
114
- ${c.dim('# Clean uninstall with backup')}
113
+ ${c.dim('# Clean uninstall')}
115
114
  code-guard uninstall
116
115
 
117
116
  ${c.dim('# Force uninstall without prompts')}
118
- code-guard uninstall --yes --no-backup
117
+ code-guard uninstall --yes
119
118
  `);
120
119
  }
121
120
 
@@ -143,6 +142,16 @@ function initProject(): void {
143
142
  const skipAI = hasFlag('skip-ai');
144
143
  const skipHooks = hasFlag('skip-hooks');
145
144
 
145
+ // ── Initialize manifest for tracking ────────────────────────────────────
146
+ const {
147
+ createManifest, saveManifest, loadManifest,
148
+ recordCreatedFile, recordModifiedFile, recordBackup,
149
+ recordAddedScript, recordModifiedScript,
150
+ } = requireUtil('manifest');
151
+
152
+ // Load existing manifest or create new one
153
+ const manifest = loadManifest(cwd) || createManifest();
154
+
146
155
  // ── Step 1: Detect Framework ────────────────────────────────────────────
147
156
  console.log(c.bold('📡 Step 1: Detecting project...'));
148
157
 
@@ -186,9 +195,25 @@ function initProject(): void {
186
195
  // ── Step 3: Generate ESLint Config ───────────────────────────────────────
187
196
  console.log(c.bold('⚡ Step 3: Configuring ESLint...'));
188
197
  try {
198
+ const eslintFile = 'eslint.config.mjs';
199
+ const eslintPath = path.join(cwd, eslintFile);
200
+ const eslintExisted = fs.existsSync(eslintPath);
201
+
189
202
  const { generateEslintConfig } = requireGenerator('eslint-generator');
190
203
  generateEslintConfig(project);
191
- console.log(` ${c.green('✓')} Created .eslintrc.json`);
204
+
205
+ // Track: eslint-generator already creates .backup files for existing configs
206
+ if (eslintExisted) {
207
+ recordModifiedFile(manifest, eslintFile, 'eslint');
208
+ // The generator already backed up the old file — record it
209
+ if (fs.existsSync(eslintPath + '.backup')) {
210
+ recordBackup(manifest, eslintFile, eslintFile + '.backup');
211
+ }
212
+ } else {
213
+ recordCreatedFile(manifest, eslintFile);
214
+ }
215
+
216
+ console.log(` ${c.green('✓')} Created eslint.config.mjs`);
192
217
  console.log(` ${c.green('✓')} Created .eslintignore\n`);
193
218
  } catch (error: any) {
194
219
  console.warn(c.yellow(` ⚠️ ESLint config generation failed: ${error.message}\n`));
@@ -197,8 +222,33 @@ function initProject(): void {
197
222
  // ── Step 4: Generate Prettier Config ──────────────────────────────────────
198
223
  console.log(c.bold('🎨 Step 4: Configuring Prettier...'));
199
224
  try {
225
+ const prettierFile = '.prettierrc.json';
226
+ const prettierPath = path.join(cwd, prettierFile);
227
+ const prettierExisted = fs.existsSync(prettierPath);
228
+ const ignoreFile = '.prettierignore';
229
+ const ignorePath = path.join(cwd, ignoreFile);
230
+ const ignoreExisted = fs.existsSync(ignorePath);
231
+
200
232
  const { generatePrettierConfig } = requireGenerator('prettier-generator');
201
233
  generatePrettierConfig(project);
234
+
235
+ // Track: prettier-generator already creates .backup files for existing configs
236
+ if (prettierExisted) {
237
+ recordModifiedFile(manifest, prettierFile, 'prettier');
238
+ // Check for any backed-up prettier files
239
+ const prettierFiles = ['.prettierrc', '.prettierrc.js', '.prettierrc.cjs', '.prettierrc.json', '.prettierrc.yml', '.prettierrc.yaml', '.prettierrc.toml', 'prettier.config.js'];
240
+ for (const pf of prettierFiles) {
241
+ if (fs.existsSync(path.join(cwd, pf + '.backup'))) {
242
+ recordBackup(manifest, pf, pf + '.backup');
243
+ }
244
+ }
245
+ } else {
246
+ recordCreatedFile(manifest, prettierFile);
247
+ }
248
+ if (!ignoreExisted && fs.existsSync(ignorePath)) {
249
+ recordCreatedFile(manifest, ignoreFile);
250
+ }
251
+
202
252
  console.log(` ${c.green('✓')} Created .prettierrc.json`);
203
253
  console.log(` ${c.green('✓')} Created .prettierignore\n`);
204
254
  } catch (error: any) {
@@ -209,8 +259,19 @@ function initProject(): void {
209
259
  if (project.usesTypeScript) {
210
260
  console.log(c.bold('📘 Step 5: Configuring TypeScript...'));
211
261
  try {
262
+ const tsFile = project.existingTooling.hasTypescript ? 'tsconfig.strict.json' : 'tsconfig.json';
263
+ const tsPath = path.join(cwd, tsFile);
264
+ const tsExisted = fs.existsSync(tsPath);
265
+
212
266
  const { generateTypescriptConfig } = requireGenerator('typescript-generator');
213
267
  generateTypescriptConfig(project);
268
+
269
+ if (tsExisted) {
270
+ recordModifiedFile(manifest, tsFile, 'typescript');
271
+ } else {
272
+ recordCreatedFile(manifest, tsFile);
273
+ }
274
+
214
275
  if (project.existingTooling.hasTypescript) {
215
276
  console.log(` ${c.green('✓')} Created tsconfig.strict.json (extends existing tsconfig)`);
216
277
  } else {
@@ -227,18 +288,34 @@ function initProject(): void {
227
288
  // ── Step 6: Generate lint-staged Config ───────────────────────────────────
228
289
  console.log(c.bold('📋 Step 6: Configuring lint-staged...'));
229
290
  try {
291
+ const lsFile = '.lintstagedrc.json';
292
+ const lsPath = path.join(cwd, lsFile);
293
+ const lsExisted = fs.existsSync(lsPath);
294
+
230
295
  const { generateLintStagedConfig } = requireGenerator('lint-staged-generator');
231
296
  generateLintStagedConfig(project);
297
+
298
+ if (!lsExisted && fs.existsSync(lsPath)) {
299
+ recordCreatedFile(manifest, lsFile);
300
+ }
232
301
  console.log(` ${c.green('✓')} Created .lintstagedrc.json\n`);
233
302
  } catch (error: any) {
234
303
  console.warn(c.yellow(` ⚠️ lint-staged config generation failed: ${error.message}\n`));
235
304
  }
236
305
 
237
306
  // ── Step 7: Generate .editorconfig ────────────────────────────────────────
238
- console.log(c.bold('📐 Step 8: Creating .editorconfig...'));
307
+ console.log(c.bold('📐 Step 7: Creating .editorconfig...'));
239
308
  try {
309
+ const ecFile = '.editorconfig';
310
+ const ecPath = path.join(cwd, ecFile);
311
+ const ecExisted = fs.existsSync(ecPath);
312
+
240
313
  const { generateEditorConfig } = requireGenerator('editorconfig-generator');
241
314
  generateEditorConfig(project);
315
+
316
+ if (!ecExisted && fs.existsSync(ecPath)) {
317
+ recordCreatedFile(manifest, ecFile);
318
+ }
242
319
  console.log(` ${c.green('✓')} Created .editorconfig\n`);
243
320
  } catch (error: any) {
244
321
  console.warn(c.yellow(` ⚠️ EditorConfig generation failed: ${error.message}\n`));
@@ -247,11 +324,24 @@ function initProject(): void {
247
324
  // ── Step 8: Generate VS Code Settings ─────────────────────────────────────
248
325
  console.log(c.bold('💻 Step 8: Creating VS Code workspace settings...'));
249
326
  try {
327
+ const vscodeFiles = ['.vscode/settings.json', '.vscode/extensions.json', '.vscode/tasks.json', '.vscode/launch.json'];
328
+ const vscodeExisted: Record<string, boolean> = {};
329
+ for (const vf of vscodeFiles) {
330
+ vscodeExisted[vf] = fs.existsSync(path.join(cwd, vf));
331
+ }
332
+
250
333
  const { generateVSCodeSettings, generateVSCodeExtensions, generateVSCodeTasks, generateVSCodeLaunch } = requireGenerator('vscode-generator');
251
334
  generateVSCodeSettings(project);
252
335
  generateVSCodeExtensions(project);
253
336
  generateVSCodeTasks(project);
254
337
  generateVSCodeLaunch(project);
338
+
339
+ for (const vf of vscodeFiles) {
340
+ if (!vscodeExisted[vf] && fs.existsSync(path.join(cwd, vf))) {
341
+ recordCreatedFile(manifest, vf);
342
+ }
343
+ }
344
+
255
345
  console.log(` ${c.green('✓')} Created .vscode/settings.json`);
256
346
  console.log(` ${c.green('✓')} Created .vscode/extensions.json`);
257
347
  console.log(` ${c.green('✓')} Created .vscode/tasks.json`);
@@ -263,7 +353,14 @@ function initProject(): void {
263
353
  // ── Step 9: Setup Git Hooks ───────────────────────────────────────────────
264
354
  if (!skipHooks) {
265
355
  console.log(c.bold('🐶 Step 9: Setting up Git hooks...'));
356
+ const hookFile = '.husky/pre-commit';
357
+ const hookExisted = fs.existsSync(path.join(cwd, hookFile));
266
358
  setupGitHooks(cwd);
359
+ if (hookExisted) {
360
+ recordModifiedFile(manifest, hookFile, 'husky');
361
+ } else if (fs.existsSync(path.join(cwd, hookFile))) {
362
+ recordCreatedFile(manifest, hookFile);
363
+ }
267
364
  console.log('');
268
365
  } else {
269
366
  console.log(c.dim('🐶 Step 9: Skipping git hooks (--skip-hooks)\n'));
@@ -275,17 +372,32 @@ function initProject(): void {
275
372
  try {
276
373
  const { loadCompanyRules } = requireUtil('custom-rules-loader');
277
374
  const companyRules = loadCompanyRules(cwd);
278
- const { generateAIConfigs } = requireGenerator('ai-config-generator');
279
- generateAIConfigs(project, companyRules.aiGuidelines);
280
375
 
376
+ // Snapshot which AI files exist before generation
281
377
  const { defaultRegistry } = requireUtil('ai-config-registry');
282
378
  const templates = defaultRegistry.getAll();
379
+ const aiExisted: Record<string, boolean> = {};
283
380
  for (const t of templates) {
284
381
  const targetDir = t.directory ? path.join(cwd, t.directory) : cwd;
285
382
  const targetPath = path.join(targetDir, t.fileName);
383
+ const relPath = t.directory ? `${t.directory}/${t.fileName}` : t.fileName;
384
+ aiExisted[relPath] = fs.existsSync(targetPath);
385
+ }
386
+
387
+ const { generateAIConfigs } = requireGenerator('ai-config-generator');
388
+ generateAIConfigs(project, companyRules.aiGuidelines);
389
+
390
+ for (const t of templates) {
391
+ const targetDir = t.directory ? path.join(cwd, t.directory) : cwd;
392
+ const targetPath = path.join(targetDir, t.fileName);
393
+ const relPath = t.directory ? `${t.directory}/${t.fileName}` : t.fileName;
286
394
  if (fs.existsSync(targetPath)) {
287
- const displayPath = t.directory ? `${t.directory}/${t.fileName}` : t.fileName;
288
- console.log(` ${c.green('✓')} ${t.name}: ${displayPath}`);
395
+ if (aiExisted[relPath]) {
396
+ recordModifiedFile(manifest, relPath, t.marker);
397
+ } else {
398
+ recordCreatedFile(manifest, relPath);
399
+ }
400
+ console.log(` ${c.green('✓')} ${t.name}: ${relPath}`);
289
401
  }
290
402
  }
291
403
  console.log('');
@@ -299,6 +411,12 @@ function initProject(): void {
299
411
  // ── Step 11: Custom Rules Template ─────────────────────────────────────────
300
412
  console.log(c.bold('🏢 Step 11: Creating custom rules templates...'));
301
413
  try {
414
+ const cgFiles = ['.code-guardian/custom-rules.json', '.code-guardian/structure-rules.json', '.code-guardian/naming-rules.json'];
415
+ const cgExisted: Record<string, boolean> = {};
416
+ for (const cf of cgFiles) {
417
+ cgExisted[cf] = fs.existsSync(path.join(cwd, cf));
418
+ }
419
+
302
420
  const { generateCustomRulesTemplate } = requireUtil('custom-rules-loader');
303
421
  generateCustomRulesTemplate(cwd);
304
422
  console.log(` ${c.green('✓')} Created .code-guardian/custom-rules.json`);
@@ -310,20 +428,30 @@ function initProject(): void {
310
428
  const { generateNamingRulesTemplate } = requireUtil('naming-validator');
311
429
  generateNamingRulesTemplate(cwd);
312
430
  console.log(` ${c.green('✓')} Created .code-guardian/naming-rules.json\n`);
431
+
432
+ for (const cf of cgFiles) {
433
+ if (!cgExisted[cf] && fs.existsSync(path.join(cwd, cf))) {
434
+ recordCreatedFile(manifest, cf);
435
+ }
436
+ }
313
437
  } catch (error: any) {
314
438
  console.warn(c.yellow(` ⚠️ Custom rules template generation failed: ${error.message}\n`));
315
439
  }
316
440
 
317
441
  // ── Step 12: Update package.json ───────────────────────────────────────────
318
442
  console.log(c.bold('📦 Step 12: Updating package.json...'));
319
- updatePackageJson(cwd, project);
443
+ updatePackageJson(cwd, project, manifest);
320
444
  console.log('');
321
445
 
322
446
  // ── Step 13: Copy Code Guardian configs and templates ──────────────────────
323
447
  console.log(c.bold('📁 Step 13: Copying Code Guardian rules and templates...'));
324
- copyCodeGuardianFiles(cwd);
448
+ copyCodeGuardianFiles(cwd, manifest, project.type);
325
449
  console.log('');
326
450
 
451
+ // ── Save manifest ─────────────────────────────────────────────────────────
452
+ saveManifest(cwd, manifest);
453
+ console.log(` ${c.green('✓')} Saved manifest to .code-guardian/manifest.json\n`);
454
+
327
455
  // ── Done! ─────────────────────────────────────────────────────────────────
328
456
  console.log(c.bold(c.green('═══════════════════════════════════════════════════════════')));
329
457
  console.log(c.bold(c.green(' ✅ Code Guardian initialized successfully!')));
@@ -358,44 +486,50 @@ function initProject(): void {
358
486
 
359
487
  function setupGitHooks(cwd: string): void {
360
488
  try {
361
- // Initialize Husky
489
+ const huskyDir = path.join(cwd, '.husky');
490
+ const preCommitPath = path.join(huskyDir, 'pre-commit');
491
+
492
+ // Save existing pre-commit content BEFORE husky init (which overwrites it)
493
+ let existingPreCommit: string | null = null;
494
+ if (fs.existsSync(preCommitPath)) {
495
+ existingPreCommit = fs.readFileSync(preCommitPath, 'utf-8');
496
+ }
497
+
498
+ // Initialize Husky (creates .husky/ dir and default pre-commit)
362
499
  try {
363
500
  execSync('npx husky init', { stdio: 'pipe', cwd });
364
501
  } catch {
365
502
  // Fallback: manual setup
366
- const huskyDir = path.join(cwd, '.husky');
367
503
  if (!fs.existsSync(huskyDir)) {
368
504
  fs.mkdirSync(huskyDir, { recursive: true });
369
505
  }
370
506
  }
371
507
 
372
- const huskyDir = path.join(cwd, '.husky');
373
508
  if (!fs.existsSync(huskyDir)) {
374
509
  fs.mkdirSync(huskyDir, { recursive: true });
375
510
  }
376
511
 
377
- // Create or append pre-commit hook (lint-staged + Code Guardian)
378
- const preCommitContent = `
379
- # npm test
512
+ // Restore the original pre-commit content if husky init overwrote it
513
+ if (existingPreCommit !== null) {
514
+ fs.writeFileSync(preCommitPath, existingPreCommit);
515
+ }
380
516
 
517
+ // Create or append pre-commit hook (lint-staged only — Code Guardian checks run via `code-guard check`)
518
+ const preCommitContent = `
381
519
  # --- code-guardian-hook-start ---
382
- echo "🛡️ Code Guardian — Pre-commit checks..."
520
+ echo "🛡️ Code Guardian — Pre-commit lint & format..."
383
521
 
384
- # Skip lint-staged and all checks when BYPASS_RULES is set
522
+ # Skip lint-staged when BYPASS_RULES is set
385
523
  if [ "$BYPASS_RULES" = "true" ] || [ "$BYPASS_RULES" = "1" ]; then
386
- echo "⚡ BYPASS_RULES detected — skipping lint-staged and Code Guardian."
524
+ echo "⚡ BYPASS_RULES detected — skipping lint-staged."
387
525
  exit 0
388
526
  fi
389
527
 
390
528
  # Run lint-staged (ESLint + Prettier auto-fix)
391
529
  npx lint-staged
392
-
393
- # Run Code Guardian custom rules
394
- npm run precommit-check
395
530
  # --- code-guardian-hook-end ---
396
531
  `;
397
532
 
398
- const preCommitPath = path.join(huskyDir, 'pre-commit');
399
533
  if (fs.existsSync(preCommitPath)) {
400
534
  const existing = fs.readFileSync(preCommitPath, 'utf-8');
401
535
  if (!existing.includes('code-guardian-hook-start')) {
@@ -409,7 +543,7 @@ npm run precommit-check
409
543
  // Create new hook file
410
544
  const preCommitHook = `#!/usr/bin/env sh\n${preCommitContent}`;
411
545
  fs.writeFileSync(preCommitPath, preCommitHook);
412
- console.log(` ${c.green('✓')} Created .husky/pre-commit (lint-staged + Code Guardian)`);
546
+ console.log(` ${c.green('✓')} Created .husky/pre-commit (lint-staged only)`);
413
547
  }
414
548
  try { fs.chmodSync(preCommitPath, '755'); } catch {}
415
549
 
@@ -470,33 +604,49 @@ function buildInstallCommand(packageManager: string, deps: string[]): string {
470
604
 
471
605
  // ── Update package.json ─────────────────────────────────────────────────────
472
606
 
473
- function updatePackageJson(cwd: string, project: any): void {
607
+ function updatePackageJson(cwd: string, project: any, manifest?: any): void {
474
608
  const packageJsonPath = path.join(cwd, 'package.json');
475
609
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
476
610
  const packageName = '@hatem427/code-guard-ci';
477
611
 
478
612
  packageJson.scripts = packageJson.scripts || {};
479
613
 
614
+ // Helper to track script changes in manifest
615
+ const setScript = (name: string, value: string) => {
616
+ if (manifest) {
617
+ if (packageJson.scripts[name] && packageJson.scripts[name] !== value) {
618
+ // Script existed with different value — record original
619
+ const { recordModifiedScript } = requireUtil('manifest');
620
+ recordModifiedScript(manifest, name, packageJson.scripts[name]);
621
+ } else if (!packageJson.scripts[name]) {
622
+ // New script
623
+ const { recordAddedScript } = requireUtil('manifest');
624
+ recordAddedScript(manifest, name);
625
+ }
626
+ }
627
+ packageJson.scripts[name] = value;
628
+ };
629
+
480
630
  // Core Code Guardian scripts
481
- packageJson.scripts['precommit-check'] = 'code-guard check';
482
- packageJson.scripts['generate-doc'] = 'code-guard doc';
483
- packageJson.scripts['generate-pr-checklist'] = 'code-guard checklist';
484
- packageJson.scripts['validate'] = 'code-guard validate';
485
- packageJson.scripts['prepare'] = 'husky';
631
+ setScript('precommit-check', 'code-guard check');
632
+ setScript('generate-doc', 'code-guard doc');
633
+ setScript('generate-pr-checklist', 'code-guard checklist');
634
+ setScript('validate', 'code-guard validate');
635
+ setScript('prepare', 'husky');
486
636
 
487
637
  // Lint and format scripts
488
638
  const extensions = project.usesTypeScript ? '.ts,.tsx,.js,.jsx' : '.js,.jsx';
489
- packageJson.scripts['lint'] = `eslint . --ext ${extensions}`;
490
- packageJson.scripts['lint:fix'] = `eslint . --ext ${extensions} --fix`;
491
- packageJson.scripts['format'] = 'prettier --write .';
492
- packageJson.scripts['format:check'] = 'prettier --check .';
639
+ setScript('lint', `eslint . --ext ${extensions}`);
640
+ setScript('lint:fix', `eslint . --ext ${extensions} --fix`);
641
+ setScript('format', 'prettier --write .');
642
+ setScript('format:check', 'prettier --check .');
493
643
 
494
644
  // Security scripts
495
- packageJson.scripts['set-bypass-password'] = `node node_modules/${packageName}/dist/scripts/set-bypass-password.js`;
496
- packageJson.scripts['set-admin-password'] = `node node_modules/${packageName}/dist/scripts/set-admin-password.js`;
497
- packageJson.scripts['delete-bypass-logs'] = `node node_modules/${packageName}/dist/scripts/delete-bypass-logs.js`;
498
- packageJson.scripts['view-bypass-log'] = `node node_modules/${packageName}/dist/scripts/view-bypass-log.js`;
499
- packageJson.scripts['auto-fix'] = `node node_modules/${packageName}/dist/scripts/auto-fix.js`;
645
+ setScript('set-bypass-password', `node node_modules/${packageName}/dist/scripts/set-bypass-password.js`);
646
+ setScript('set-admin-password', `node node_modules/${packageName}/dist/scripts/set-admin-password.js`);
647
+ setScript('delete-bypass-logs', `node node_modules/${packageName}/dist/scripts/delete-bypass-logs.js`);
648
+ setScript('view-bypass-log', `node node_modules/${packageName}/dist/scripts/view-bypass-log.js`);
649
+ setScript('auto-fix', `node node_modules/${packageName}/dist/scripts/auto-fix.js`);
500
650
 
501
651
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
502
652
  console.log(` ${c.green('✓')} Added npm scripts (lint, format, validate, precommit-check, etc.)`);
@@ -504,18 +654,35 @@ function updatePackageJson(cwd: string, project: any): void {
504
654
 
505
655
  // ── Copy Code Guardian files ────────────────────────────────────────────────
506
656
 
507
- function copyCodeGuardianFiles(cwd: string): void {
657
+ function copyCodeGuardianFiles(cwd: string, manifest?: any, projectType?: string): void {
508
658
  // Determine source directories (handles both dev and built package layouts)
509
659
  const distDir = path.join(__dirname, '..');
510
660
  const pkgRootDir = path.join(__dirname, '..', '..');
511
661
 
512
- // Copy config files
662
+ // Copy config files — only guidelines (always) + detected framework config
513
663
  const configDir = path.join(cwd, 'config');
514
664
  if (!fs.existsSync(configDir)) {
515
665
  fs.mkdirSync(configDir, { recursive: true });
516
666
  }
517
667
 
518
- const configFiles = ['guidelines.config.ts', 'angular.config.ts', 'react.config.ts', 'nextjs.config.ts'];
668
+ // Map project type to its config file
669
+ const frameworkConfigMap: Record<string, string> = {
670
+ angular: 'angular.config.ts',
671
+ react: 'react.config.ts',
672
+ nextjs: 'nextjs.config.ts',
673
+ node: 'node.config.ts',
674
+ };
675
+
676
+ // Always include guidelines + detected framework config (+ react for nextjs since it inherits)
677
+ const configFiles = ['guidelines.config.ts'];
678
+ if (projectType && frameworkConfigMap[projectType]) {
679
+ configFiles.push(frameworkConfigMap[projectType]);
680
+ }
681
+ // Next.js inherits React rules, so include react config too
682
+ if (projectType === 'nextjs' && !configFiles.includes('react.config.ts')) {
683
+ configFiles.push('react.config.ts');
684
+ }
685
+
519
686
  const sourceConfigDirs = [path.join(distDir, 'config'), path.join(pkgRootDir, 'config')];
520
687
 
521
688
  for (const file of configFiles) {
@@ -530,6 +697,10 @@ function copyCodeGuardianFiles(cwd: string): void {
530
697
  const dest = path.join(configDir, file);
531
698
  if (src && !fs.existsSync(dest)) {
532
699
  fs.copyFileSync(src, dest);
700
+ if (manifest) {
701
+ const { recordCreatedFile } = requireUtil('manifest');
702
+ recordCreatedFile(manifest, `config/${file}`);
703
+ }
533
704
  console.log(` ${c.green('✓')} ${file}`);
534
705
  }
535
706
  }
@@ -555,6 +726,10 @@ function copyCodeGuardianFiles(cwd: string): void {
555
726
  const dest = path.join(templatesDir, file);
556
727
  if (src && !fs.existsSync(dest)) {
557
728
  fs.copyFileSync(src, dest);
729
+ if (manifest) {
730
+ const { recordCreatedFile } = requireUtil('manifest');
731
+ recordCreatedFile(manifest, `templates/${file}`);
732
+ }
558
733
  console.log(` ${c.green('✓')} ${file}`);
559
734
  }
560
735
  }
@@ -564,6 +739,10 @@ function copyCodeGuardianFiles(cwd: string): void {
564
739
  if (!fs.existsSync(docsDir)) {
565
740
  fs.mkdirSync(docsDir, { recursive: true });
566
741
  fs.writeFileSync(path.join(docsDir, '.gitkeep'), '');
742
+ if (manifest) {
743
+ const { recordCreatedFile } = requireUtil('manifest');
744
+ recordCreatedFile(manifest, 'docs/features/.gitkeep');
745
+ }
567
746
  console.log(` ${c.green('✓')} Created docs/features/`);
568
747
  }
569
748
  }
@@ -931,11 +1110,22 @@ async function uninstallCodeGuard(): Promise<void> {
931
1110
  showBanner();
932
1111
  console.log(c.bold('🗑️ Code Guardian Uninstall\n'));
933
1112
 
934
- const filesToRemove = [
1113
+ const cwd = process.cwd();
1114
+
1115
+ // ── Load manifest for smart uninstall ────────────────────────────────
1116
+ const { loadManifest, removeMarkedContent } = requireUtil('manifest');
1117
+ const manifest = loadManifest(cwd);
1118
+
1119
+ // Hardcoded fallbacks for files that Code Guardian always creates
1120
+ const fallbackFilesToRemove = [
935
1121
  '.husky/_/husky.sh',
1122
+ 'eslint.config.mjs',
936
1123
  'eslint.config.js',
1124
+ '.prettierrc.json',
1125
+ '.prettierignore',
937
1126
  'prettier.config.js',
938
1127
  '.editorconfig',
1128
+ '.lintstagedrc.json',
939
1129
  'lint-staged.config.js',
940
1130
  'tsconfig.strict.json',
941
1131
  ];
@@ -944,7 +1134,6 @@ async function uninstallCodeGuard(): Promise<void> {
944
1134
  'config',
945
1135
  'templates',
946
1136
  'docs',
947
- '.code-guardian',
948
1137
  ];
949
1138
 
950
1139
  const scriptsToRemove = [
@@ -956,12 +1145,17 @@ async function uninstallCodeGuard(): Promise<void> {
956
1145
  'set-admin-password',
957
1146
  'view-bypass-log',
958
1147
  'delete-bypass-logs',
1148
+ 'lint',
1149
+ 'lint:fix',
1150
+ 'format',
1151
+ 'format:check',
1152
+ 'validate',
1153
+ 'prepare',
959
1154
  ];
960
1155
 
961
1156
  // ── Detect AI config files via registry ──────────────────────────────
962
1157
  const { defaultRegistry } = requireUtil('ai-config-registry');
963
1158
  const aiTemplates = defaultRegistry.getAll();
964
- const cwd = process.cwd();
965
1159
 
966
1160
  // Classify each AI config: full-delete vs strip-only
967
1161
  interface AIConfigAction {
@@ -969,7 +1163,7 @@ async function uninstallCodeGuard(): Promise<void> {
969
1163
  filePath: string;
970
1164
  relativePath: string;
971
1165
  marker: string;
972
- action: 'delete' | 'strip'; // delete = whole file is ours, strip = remove only our section
1166
+ action: 'delete' | 'strip';
973
1167
  }
974
1168
  const aiActions: AIConfigAction[] = [];
975
1169
 
@@ -981,48 +1175,111 @@ async function uninstallCodeGuard(): Promise<void> {
981
1175
  if (!fs.existsSync(filePath)) continue;
982
1176
 
983
1177
  const content = fs.readFileSync(filePath, 'utf-8');
984
- if (!content.includes(t.marker)) continue; // not ours
985
-
986
- // Determine if the file was created by us or if we appended
987
- // If appended, there will be a --- separator before our marker with user content above
988
- const markerIdx = content.indexOf(t.marker);
989
- const beforeMarker = content.substring(0, markerIdx).trimEnd();
1178
+ if (!content.includes(t.marker)) continue;
990
1179
 
991
- // Check if there's substantial user content before our section
992
- // When we append we add "\n\n---\n\n" before our content
993
- // When we create, the marker is at or near the top (possibly after frontmatter)
994
- const stripped = beforeMarker.replace(/^---[\s\S]*?---/, '').trim(); // strip MDC frontmatter
995
- const hasUserContent = stripped.length > 0 && !stripped.endsWith('---');
996
-
997
- // A more robust check: if content before marker (minus separator) has real text
998
- const withoutTrailingSep = beforeMarker.replace(/\n*---\s*$/, '').trim();
999
- const realUserContent = withoutTrailingSep.replace(/^---[\s\S]*?---/, '').trim(); // strip frontmatter
1000
-
1001
- if (realUserContent.length > 0) {
1002
- aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'strip' });
1180
+ // Use manifest to determine action if available
1181
+ if (manifest && manifest.files[relativePath]) {
1182
+ const record = manifest.files[relativePath];
1183
+ if (record.action === 'created') {
1184
+ aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'delete' });
1185
+ } else {
1186
+ aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'strip' });
1187
+ }
1003
1188
  } else {
1004
- aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'delete' });
1189
+ // Fallback: detect by checking for user content before marker
1190
+ const markerIdx = content.indexOf(t.marker);
1191
+ const beforeMarker = content.substring(0, markerIdx).trimEnd();
1192
+ const withoutTrailingSep = beforeMarker.replace(/\n*---\s*$/, '').trim();
1193
+ const realUserContent = withoutTrailingSep.replace(/^---[\s\S]*?---/, '').trim();
1194
+
1195
+ if (realUserContent.length > 0) {
1196
+ aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'strip' });
1197
+ } else {
1198
+ aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'delete' });
1199
+ }
1005
1200
  }
1006
1201
  }
1007
1202
 
1203
+ // Determine which files to remove (use manifest if available, fallback to hardcoded)
1204
+ let filesToProcess: string[];
1205
+ if (manifest) {
1206
+ // Use manifest — it knows exactly what we created vs modified
1207
+ filesToProcess = Object.keys(manifest.files).filter(f => {
1208
+ // Skip AI files — handled separately
1209
+ return !aiActions.some(a => a.relativePath === f);
1210
+ });
1211
+ } else {
1212
+ filesToProcess = fallbackFilesToRemove;
1213
+ }
1214
+
1008
1215
  // Check what exists
1009
- const existingFiles = filesToRemove.filter(f => fs.existsSync(path.join(cwd, f)));
1216
+ const existingFiles = filesToProcess.filter(f => fs.existsSync(path.join(cwd, f)));
1010
1217
  const existingDirs = dirsToRemove.filter(d => fs.existsSync(path.join(cwd, d)));
1011
1218
 
1012
- if (existingFiles.length === 0 && existingDirs.length === 0 && aiActions.length === 0) {
1219
+ // Also check for backup files that need restoring
1220
+ const backupsToRestore: Array<{ original: string; backup: string }> = [];
1221
+ if (manifest && manifest.backups) {
1222
+ for (const [original, backup] of Object.entries(manifest.backups) as [string, string][]) {
1223
+ if (fs.existsSync(path.join(cwd, backup))) {
1224
+ backupsToRestore.push({ original, backup });
1225
+ }
1226
+ }
1227
+ }
1228
+ // Also find any .backup files from generators (eslint, prettier create them)
1229
+ for (const file of existingFiles) {
1230
+ const backupPath = path.join(cwd, file + '.backup');
1231
+ if (fs.existsSync(backupPath) && !backupsToRestore.some(b => b.backup === file + '.backup')) {
1232
+ backupsToRestore.push({ original: file, backup: file + '.backup' });
1233
+ }
1234
+ }
1235
+ // Check common backup locations from generators
1236
+ const commonBackups = [
1237
+ '.eslintrc.backup', '.eslintrc.js.backup', '.eslintrc.cjs.backup', '.eslintrc.json.backup',
1238
+ 'eslint.config.js.backup', 'eslint.config.mjs.backup', 'eslint.config.cjs.backup',
1239
+ '.prettierrc.backup', '.prettierrc.js.backup', '.prettierrc.cjs.backup', '.prettierrc.json.backup',
1240
+ '.prettierrc.yml.backup', '.prettierrc.yaml.backup', '.prettierrc.toml.backup', 'prettier.config.js.backup',
1241
+ '.eslintignore.backup',
1242
+ ];
1243
+ for (const backup of commonBackups) {
1244
+ if (fs.existsSync(path.join(cwd, backup))) {
1245
+ const original = backup.replace(/\.backup$/, '');
1246
+ if (!backupsToRestore.some(b => b.backup === backup)) {
1247
+ backupsToRestore.push({ original, backup });
1248
+ }
1249
+ }
1250
+ }
1251
+
1252
+ if (existingFiles.length === 0 && existingDirs.length === 0 && aiActions.length === 0 && backupsToRestore.length === 0) {
1013
1253
  console.log(c.yellow('⚠️ No Code Guardian files found to remove.\n'));
1014
1254
  return;
1015
1255
  }
1016
1256
 
1017
- // Show what will be removed
1257
+ // Show what will be done
1018
1258
  console.log(c.bold('The following will be removed:\n'));
1019
-
1020
- if (existingFiles.length > 0) {
1021
- console.log(c.bold('Files:'));
1022
- existingFiles.forEach(f => console.log(` ${c.red('✗')} ${f}`));
1259
+
1260
+ // Separate created vs modified files
1261
+ const createdFiles: string[] = [];
1262
+ const modifiedFiles: string[] = [];
1263
+ for (const f of existingFiles) {
1264
+ if (manifest && manifest.files[f] && manifest.files[f].action === 'modified') {
1265
+ modifiedFiles.push(f);
1266
+ } else {
1267
+ createdFiles.push(f);
1268
+ }
1269
+ }
1270
+
1271
+ if (createdFiles.length > 0) {
1272
+ console.log(c.bold('Files (created by Code Guardian — will be deleted):'));
1273
+ createdFiles.forEach(f => console.log(` ${c.red('✗')} ${f}`));
1023
1274
  console.log('');
1024
1275
  }
1025
-
1276
+
1277
+ if (modifiedFiles.length > 0) {
1278
+ console.log(c.bold('Files (modified — Code Guardian content will be stripped):'));
1279
+ modifiedFiles.forEach(f => console.log(` ${c.yellow('⚠')} ${f} ${c.dim('(your content preserved)')}`));
1280
+ console.log('');
1281
+ }
1282
+
1026
1283
  if (existingDirs.length > 0) {
1027
1284
  console.log(c.bold('Directories:'));
1028
1285
  existingDirs.forEach(d => console.log(` ${c.red('✗')} ${d}/`));
@@ -1044,6 +1301,12 @@ async function uninstallCodeGuard(): Promise<void> {
1044
1301
  console.log('');
1045
1302
  }
1046
1303
 
1304
+ if (backupsToRestore.length > 0) {
1305
+ console.log(c.bold('Backups to restore (original configs):'));
1306
+ backupsToRestore.forEach(b => console.log(` ${c.blue('↩')} ${b.backup} → ${b.original}`));
1307
+ console.log('');
1308
+ }
1309
+
1047
1310
  if (!hasFlag('yes') && !hasFlag('y')) {
1048
1311
  const readline = require('readline');
1049
1312
  const rl = readline.createInterface({
@@ -1067,48 +1330,71 @@ async function uninstallCodeGuard(): Promise<void> {
1067
1330
  let removedCount = 0;
1068
1331
  console.log('');
1069
1332
 
1070
- // Backup AI config files before modifying
1071
- const backupDir = path.join(cwd, '.code-guardian-backup-' + Date.now());
1072
- let hasBackup = false;
1333
+ // ── Phase 1: Handle created files (delete) ──────────────────────────
1334
+ if (createdFiles.length > 0) {
1335
+ console.log(c.bold('Removing created files...\n'));
1336
+ for (const file of createdFiles) {
1337
+ const fullPath = path.join(cwd, file);
1338
+ if (fs.existsSync(fullPath)) {
1339
+ try {
1340
+ fs.unlinkSync(fullPath);
1341
+ console.log(` ${c.green('✓')} Removed ${file}`);
1342
+ removedCount++;
1343
+ } catch (error: any) {
1344
+ console.log(` ${c.red('✗')} Failed to remove ${file}: ${error.message}`);
1345
+ }
1346
+ }
1347
+ }
1348
+ console.log('');
1349
+ }
1073
1350
 
1074
- if (aiActions.length > 0 && !hasFlag('no-backup')) {
1075
- console.log(c.bold('Creating backup...\n'));
1076
- fs.mkdirSync(backupDir, { recursive: true });
1077
-
1078
- for (const action of aiActions) {
1079
- if (fs.existsSync(action.filePath)) {
1351
+ // ── Phase 2: Handle modified files (strip marked content) ───────────
1352
+ if (modifiedFiles.length > 0) {
1353
+ console.log(c.bold('Stripping Code Guardian content from modified files...\n'));
1354
+ for (const file of modifiedFiles) {
1355
+ const fullPath = path.join(cwd, file);
1356
+ if (fs.existsSync(fullPath)) {
1080
1357
  try {
1081
- const destPath = path.join(backupDir, action.relativePath);
1082
- const destDir = path.dirname(destPath);
1083
- if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
1084
- fs.cpSync(action.filePath, destPath);
1085
- console.log(` ${c.blue('📦')} Backed up ${action.relativePath}`);
1086
- hasBackup = true;
1358
+ const record = manifest?.files[file];
1359
+ const stripped = removeMarkedContent(fullPath, record?.marker);
1360
+ if (stripped) {
1361
+ console.log(` ${c.green('✓')} Stripped Code Guardian content from ${file}`);
1362
+ } else {
1363
+ console.log(` ${c.dim('○')} No marked content found in ${file}`);
1364
+ }
1365
+ removedCount++;
1087
1366
  } catch (error: any) {
1088
- console.log(` ${c.yellow('')} Failed to backup ${action.relativePath}: ${error.message}`);
1367
+ console.log(` ${c.red('')} Failed to clean ${file}: ${error.message}`);
1089
1368
  }
1090
1369
  }
1091
1370
  }
1092
1371
  console.log('');
1093
1372
  }
1094
1373
 
1095
- // Remove config files created entirely by Code Guardian
1096
- console.log(c.bold('Removing files...\n'));
1097
- for (const file of filesToRemove) {
1098
- const fullPath = path.join(cwd, file);
1099
- if (fs.existsSync(fullPath)) {
1374
+ // ── Phase 3: Restore backup files ───────────────────────────────────
1375
+ if (backupsToRestore.length > 0) {
1376
+ console.log(c.bold('Restoring original config files...\n'));
1377
+ for (const { original, backup } of backupsToRestore) {
1378
+ const backupFullPath = path.join(cwd, backup);
1379
+ const originalFullPath = path.join(cwd, original);
1100
1380
  try {
1101
- fs.unlinkSync(fullPath);
1102
- console.log(` ${c.green('✓')} Removed ${file}`);
1381
+ // Remove the Code Guardian version first (if it exists)
1382
+ if (fs.existsSync(originalFullPath)) {
1383
+ fs.unlinkSync(originalFullPath);
1384
+ }
1385
+ // Restore the backup as the original
1386
+ fs.renameSync(backupFullPath, originalFullPath);
1387
+ console.log(` ${c.green('✓')} Restored ${original} from ${backup}`);
1103
1388
  removedCount++;
1104
1389
  } catch (error: any) {
1105
- console.log(` ${c.red('✗')} Failed to remove ${file}: ${error.message}`);
1390
+ console.log(` ${c.red('✗')} Failed to restore ${original}: ${error.message}`);
1106
1391
  }
1107
1392
  }
1393
+ console.log('');
1108
1394
  }
1109
1395
 
1110
- // Remove directories
1111
- console.log(c.bold('\nRemoving directories...\n'));
1396
+ // ── Phase 4: Remove directories ─────────────────────────────────────
1397
+ console.log(c.bold('Removing directories...\n'));
1112
1398
  for (const dir of dirsToRemove) {
1113
1399
  const fullPath = path.join(cwd, dir);
1114
1400
  if (fs.existsSync(fullPath)) {
@@ -1122,12 +1408,11 @@ async function uninstallCodeGuard(): Promise<void> {
1122
1408
  }
1123
1409
  }
1124
1410
 
1125
- // Handle AI config files intelligently
1411
+ // ── Phase 5: Handle AI config files intelligently ───────────────────
1126
1412
  console.log(c.bold('\nCleaning AI config files...\n'));
1127
1413
  for (const action of aiActions) {
1128
1414
  try {
1129
1415
  if (action.action === 'delete') {
1130
- // Entire file was created by Code Guardian → delete it
1131
1416
  fs.unlinkSync(action.filePath);
1132
1417
  console.log(` ${c.green('✓')} Removed ${action.relativePath}`);
1133
1418
  removedCount++;
@@ -1139,7 +1424,6 @@ async function uninstallCodeGuard(): Promise<void> {
1139
1424
  const remaining = fs.readdirSync(parentDir);
1140
1425
  if (remaining.length === 0) {
1141
1426
  fs.rmdirSync(parentDir);
1142
- // Check grandparent too (e.g., .windsurf/rules → .windsurf)
1143
1427
  const grandparent = path.dirname(parentDir);
1144
1428
  if (grandparent !== cwd && fs.existsSync(grandparent)) {
1145
1429
  const gpRemaining = fs.readdirSync(grandparent);
@@ -1154,18 +1438,13 @@ async function uninstallCodeGuard(): Promise<void> {
1154
1438
  const markerIdx = content.indexOf(action.marker);
1155
1439
  if (markerIdx === -1) continue;
1156
1440
 
1157
- // Find the separator (--- on its own line) before the marker
1158
- // When we append, we add "\n\n---\n\n" before our content
1159
1441
  let cutStart = markerIdx;
1160
1442
  const beforeMarker = content.substring(0, markerIdx);
1161
-
1162
- // Look for the --- separator we added
1163
1443
  const sepMatch = beforeMarker.match(/\n*\s*---\s*\n*$/);
1164
1444
  if (sepMatch && sepMatch.index !== undefined) {
1165
1445
  cutStart = sepMatch.index;
1166
1446
  }
1167
1447
 
1168
- // Remove from cutStart to end of file (our content is always appended at the end)
1169
1448
  const cleaned = content.substring(0, cutStart).trimEnd() + '\n';
1170
1449
  fs.writeFileSync(action.filePath, cleaned);
1171
1450
  console.log(` ${c.green('✓')} Stripped Code Guardian rules from ${action.relativePath} ${c.dim('(your content preserved)')}`);
@@ -1176,7 +1455,7 @@ async function uninstallCodeGuard(): Promise<void> {
1176
1455
  }
1177
1456
  }
1178
1457
 
1179
- // Clean up .husky hooks smartly
1458
+ // ── Phase 6: Clean up .husky hooks smartly ──────────────────────────
1180
1459
  console.log(c.bold('\nCleaning git hooks...\n'));
1181
1460
  const huskyDir = path.join(cwd, '.husky');
1182
1461
  const huskyHookFiles = ['pre-commit'];
@@ -1198,7 +1477,6 @@ async function uninstallCodeGuard(): Promise<void> {
1198
1477
  const beforeHook = content.substring(0, startIdx).trimEnd();
1199
1478
  const afterHook = content.substring(endIdx + HOOK_END.length).trimEnd();
1200
1479
 
1201
- // Check if there's real user content outside of our markers
1202
1480
  const remaining = (beforeHook.replace(/^#!\/usr\/bin\/env\s+sh\s*/, '').trim() + afterHook.trim()).trim();
1203
1481
 
1204
1482
  if (remaining.length === 0) {
@@ -1229,23 +1507,47 @@ async function uninstallCodeGuard(): Promise<void> {
1229
1507
  } catch {}
1230
1508
  }
1231
1509
 
1232
- // Remove scripts from package.json
1510
+ // ── Phase 7: Clean package.json scripts ─────────────────────────────
1233
1511
  console.log(c.bold('\nCleaning package.json scripts...\n'));
1234
- const packageJsonPath = path.join(cwd, 'package.json'); if (fs.existsSync(packageJsonPath)) {
1512
+ const packageJsonPath = path.join(cwd, 'package.json');
1513
+ if (fs.existsSync(packageJsonPath)) {
1235
1514
  try {
1236
1515
  const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
1237
- let scriptRemoved = false;
1516
+ let scriptChanged = false;
1517
+
1518
+ if (manifest && manifest.packageJsonScripts) {
1519
+ // Restore modified scripts to original values
1520
+ for (const [scriptName, originalValue] of Object.entries(manifest.packageJsonScripts.modified)) {
1521
+ if (packageJson.scripts && packageJson.scripts[scriptName]) {
1522
+ packageJson.scripts[scriptName] = originalValue;
1523
+ console.log(` ${c.green('✓')} Restored script: ${scriptName} ${c.dim('(original value)')}`);
1524
+ scriptChanged = true;
1525
+ removedCount++;
1526
+ }
1527
+ }
1238
1528
 
1239
- for (const script of scriptsToRemove) {
1240
- if (packageJson.scripts && packageJson.scripts[script]) {
1241
- delete packageJson.scripts[script];
1242
- console.log(` ${c.green('✓')} Removed script: ${script}`);
1243
- scriptRemoved = true;
1244
- removedCount++;
1529
+ // Remove scripts that were added by Code Guardian
1530
+ for (const scriptName of manifest.packageJsonScripts.added) {
1531
+ if (packageJson.scripts && packageJson.scripts[scriptName]) {
1532
+ delete packageJson.scripts[scriptName];
1533
+ console.log(` ${c.green('✓')} Removed script: ${scriptName}`);
1534
+ scriptChanged = true;
1535
+ removedCount++;
1536
+ }
1537
+ }
1538
+ } else {
1539
+ // Fallback: remove known Code Guardian scripts
1540
+ for (const script of scriptsToRemove) {
1541
+ if (packageJson.scripts && packageJson.scripts[script]) {
1542
+ delete packageJson.scripts[script];
1543
+ console.log(` ${c.green('✓')} Removed script: ${script}`);
1544
+ scriptChanged = true;
1545
+ removedCount++;
1546
+ }
1245
1547
  }
1246
1548
  }
1247
1549
 
1248
- if (scriptRemoved) {
1550
+ if (scriptChanged) {
1249
1551
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
1250
1552
  console.log(` ${c.green('✓')} Updated package.json`);
1251
1553
  } else {
@@ -1256,13 +1558,20 @@ async function uninstallCodeGuard(): Promise<void> {
1256
1558
  }
1257
1559
  }
1258
1560
 
1561
+ // ── Phase 8: Remove .code-guardian directory (including manifest) ────
1562
+ const codeGuardianDir = path.join(cwd, '.code-guardian');
1563
+ if (fs.existsSync(codeGuardianDir)) {
1564
+ try {
1565
+ fs.rmSync(codeGuardianDir, { recursive: true, force: true });
1566
+ console.log(`\n ${c.green('✓')} Removed .code-guardian/`);
1567
+ removedCount++;
1568
+ } catch (error: any) {
1569
+ console.log(` ${c.red('✗')} Failed to remove .code-guardian/: ${error.message}`);
1570
+ }
1571
+ }
1572
+
1259
1573
  console.log('');
1260
1574
  console.log(c.green(`✅ Cleanup complete! Removed ${removedCount} item(s).`));
1261
-
1262
- if (hasBackup) {
1263
- console.log(c.blue(`📦 Backup saved to: ${path.basename(backupDir)}/`));
1264
- }
1265
-
1266
1575
  console.log('');
1267
1576
  console.log(c.dim('To completely remove the package, run:'));
1268
1577
  console.log(c.dim(' npm uninstall @hatem427/code-guard-ci'));