@hatem427/code-guard-ci 3.5.3 → 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/config/angular.config.ts +24 -24
- package/config/guidelines.config.ts +152 -68
- package/config/nextjs.config.ts +46 -1
- package/dist/config/angular.config.js +24 -22
- package/dist/config/angular.config.js.map +1 -1
- package/dist/config/guidelines.config.d.ts.map +1 -1
- package/dist/config/guidelines.config.js +139 -77
- package/dist/config/guidelines.config.js.map +1 -1
- package/dist/config/nextjs.config.d.ts.map +1 -1
- package/dist/config/nextjs.config.js +39 -1
- package/dist/config/nextjs.config.js.map +1 -1
- package/dist/scripts/cli.js +404 -113
- package/dist/scripts/cli.js.map +1 -1
- package/package.json +1 -1
- package/scripts/cli.ts +438 -123
package/dist/scripts/cli.js
CHANGED
|
@@ -116,7 +116,6 @@ ${c.bold('Init Options:')}
|
|
|
116
116
|
|
|
117
117
|
${c.bold('Uninstall Options:')}
|
|
118
118
|
--yes, -y Skip confirmation prompt
|
|
119
|
-
--no-backup Don't create backup of AI configs
|
|
120
119
|
|
|
121
120
|
${c.bold('Examples:')}
|
|
122
121
|
${c.dim('# Full automatic setup')}
|
|
@@ -134,11 +133,11 @@ ${c.bold('Examples:')}
|
|
|
134
133
|
${c.dim('# Generate docs')}
|
|
135
134
|
code-guard doc --name="user-card" --type=ui
|
|
136
135
|
|
|
137
|
-
${c.dim('# Clean uninstall
|
|
136
|
+
${c.dim('# Clean uninstall')}
|
|
138
137
|
code-guard uninstall
|
|
139
138
|
|
|
140
139
|
${c.dim('# Force uninstall without prompts')}
|
|
141
|
-
code-guard uninstall --yes
|
|
140
|
+
code-guard uninstall --yes
|
|
142
141
|
`);
|
|
143
142
|
}
|
|
144
143
|
function showVersion() {
|
|
@@ -159,6 +158,10 @@ function initProject() {
|
|
|
159
158
|
const isDryRun = hasFlag('dry-run');
|
|
160
159
|
const skipAI = hasFlag('skip-ai');
|
|
161
160
|
const skipHooks = hasFlag('skip-hooks');
|
|
161
|
+
// ── Initialize manifest for tracking ────────────────────────────────────
|
|
162
|
+
const { createManifest, saveManifest, loadManifest, recordCreatedFile, recordModifiedFile, recordBackup, recordAddedScript, recordModifiedScript, } = requireUtil('manifest');
|
|
163
|
+
// Load existing manifest or create new one
|
|
164
|
+
const manifest = loadManifest(cwd) || createManifest();
|
|
162
165
|
// ── Step 1: Detect Framework ────────────────────────────────────────────
|
|
163
166
|
console.log(c.bold('📡 Step 1: Detecting project...'));
|
|
164
167
|
// Dynamic import (handles both dev and built)
|
|
@@ -195,9 +198,23 @@ function initProject() {
|
|
|
195
198
|
// ── Step 3: Generate ESLint Config ───────────────────────────────────────
|
|
196
199
|
console.log(c.bold('⚡ Step 3: Configuring ESLint...'));
|
|
197
200
|
try {
|
|
201
|
+
const eslintFile = 'eslint.config.mjs';
|
|
202
|
+
const eslintPath = path.join(cwd, eslintFile);
|
|
203
|
+
const eslintExisted = fs.existsSync(eslintPath);
|
|
198
204
|
const { generateEslintConfig } = requireGenerator('eslint-generator');
|
|
199
205
|
generateEslintConfig(project);
|
|
200
|
-
|
|
206
|
+
// Track: eslint-generator already creates .backup files for existing configs
|
|
207
|
+
if (eslintExisted) {
|
|
208
|
+
recordModifiedFile(manifest, eslintFile, 'eslint');
|
|
209
|
+
// The generator already backed up the old file — record it
|
|
210
|
+
if (fs.existsSync(eslintPath + '.backup')) {
|
|
211
|
+
recordBackup(manifest, eslintFile, eslintFile + '.backup');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
recordCreatedFile(manifest, eslintFile);
|
|
216
|
+
}
|
|
217
|
+
console.log(` ${c.green('✓')} Created eslint.config.mjs`);
|
|
201
218
|
console.log(` ${c.green('✓')} Created .eslintignore\n`);
|
|
202
219
|
}
|
|
203
220
|
catch (error) {
|
|
@@ -206,8 +223,31 @@ function initProject() {
|
|
|
206
223
|
// ── Step 4: Generate Prettier Config ──────────────────────────────────────
|
|
207
224
|
console.log(c.bold('🎨 Step 4: Configuring Prettier...'));
|
|
208
225
|
try {
|
|
226
|
+
const prettierFile = '.prettierrc.json';
|
|
227
|
+
const prettierPath = path.join(cwd, prettierFile);
|
|
228
|
+
const prettierExisted = fs.existsSync(prettierPath);
|
|
229
|
+
const ignoreFile = '.prettierignore';
|
|
230
|
+
const ignorePath = path.join(cwd, ignoreFile);
|
|
231
|
+
const ignoreExisted = fs.existsSync(ignorePath);
|
|
209
232
|
const { generatePrettierConfig } = requireGenerator('prettier-generator');
|
|
210
233
|
generatePrettierConfig(project);
|
|
234
|
+
// Track: prettier-generator already creates .backup files for existing configs
|
|
235
|
+
if (prettierExisted) {
|
|
236
|
+
recordModifiedFile(manifest, prettierFile, 'prettier');
|
|
237
|
+
// Check for any backed-up prettier files
|
|
238
|
+
const prettierFiles = ['.prettierrc', '.prettierrc.js', '.prettierrc.cjs', '.prettierrc.json', '.prettierrc.yml', '.prettierrc.yaml', '.prettierrc.toml', 'prettier.config.js'];
|
|
239
|
+
for (const pf of prettierFiles) {
|
|
240
|
+
if (fs.existsSync(path.join(cwd, pf + '.backup'))) {
|
|
241
|
+
recordBackup(manifest, pf, pf + '.backup');
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
recordCreatedFile(manifest, prettierFile);
|
|
247
|
+
}
|
|
248
|
+
if (!ignoreExisted && fs.existsSync(ignorePath)) {
|
|
249
|
+
recordCreatedFile(manifest, ignoreFile);
|
|
250
|
+
}
|
|
211
251
|
console.log(` ${c.green('✓')} Created .prettierrc.json`);
|
|
212
252
|
console.log(` ${c.green('✓')} Created .prettierignore\n`);
|
|
213
253
|
}
|
|
@@ -218,8 +258,17 @@ function initProject() {
|
|
|
218
258
|
if (project.usesTypeScript) {
|
|
219
259
|
console.log(c.bold('📘 Step 5: Configuring TypeScript...'));
|
|
220
260
|
try {
|
|
261
|
+
const tsFile = project.existingTooling.hasTypescript ? 'tsconfig.strict.json' : 'tsconfig.json';
|
|
262
|
+
const tsPath = path.join(cwd, tsFile);
|
|
263
|
+
const tsExisted = fs.existsSync(tsPath);
|
|
221
264
|
const { generateTypescriptConfig } = requireGenerator('typescript-generator');
|
|
222
265
|
generateTypescriptConfig(project);
|
|
266
|
+
if (tsExisted) {
|
|
267
|
+
recordModifiedFile(manifest, tsFile, 'typescript');
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
recordCreatedFile(manifest, tsFile);
|
|
271
|
+
}
|
|
223
272
|
if (project.existingTooling.hasTypescript) {
|
|
224
273
|
console.log(` ${c.green('✓')} Created tsconfig.strict.json (extends existing tsconfig)`);
|
|
225
274
|
}
|
|
@@ -238,18 +287,30 @@ function initProject() {
|
|
|
238
287
|
// ── Step 6: Generate lint-staged Config ───────────────────────────────────
|
|
239
288
|
console.log(c.bold('📋 Step 6: Configuring lint-staged...'));
|
|
240
289
|
try {
|
|
290
|
+
const lsFile = '.lintstagedrc.json';
|
|
291
|
+
const lsPath = path.join(cwd, lsFile);
|
|
292
|
+
const lsExisted = fs.existsSync(lsPath);
|
|
241
293
|
const { generateLintStagedConfig } = requireGenerator('lint-staged-generator');
|
|
242
294
|
generateLintStagedConfig(project);
|
|
295
|
+
if (!lsExisted && fs.existsSync(lsPath)) {
|
|
296
|
+
recordCreatedFile(manifest, lsFile);
|
|
297
|
+
}
|
|
243
298
|
console.log(` ${c.green('✓')} Created .lintstagedrc.json\n`);
|
|
244
299
|
}
|
|
245
300
|
catch (error) {
|
|
246
301
|
console.warn(c.yellow(` ⚠️ lint-staged config generation failed: ${error.message}\n`));
|
|
247
302
|
}
|
|
248
303
|
// ── Step 7: Generate .editorconfig ────────────────────────────────────────
|
|
249
|
-
console.log(c.bold('📐 Step
|
|
304
|
+
console.log(c.bold('📐 Step 7: Creating .editorconfig...'));
|
|
250
305
|
try {
|
|
306
|
+
const ecFile = '.editorconfig';
|
|
307
|
+
const ecPath = path.join(cwd, ecFile);
|
|
308
|
+
const ecExisted = fs.existsSync(ecPath);
|
|
251
309
|
const { generateEditorConfig } = requireGenerator('editorconfig-generator');
|
|
252
310
|
generateEditorConfig(project);
|
|
311
|
+
if (!ecExisted && fs.existsSync(ecPath)) {
|
|
312
|
+
recordCreatedFile(manifest, ecFile);
|
|
313
|
+
}
|
|
253
314
|
console.log(` ${c.green('✓')} Created .editorconfig\n`);
|
|
254
315
|
}
|
|
255
316
|
catch (error) {
|
|
@@ -258,11 +319,21 @@ function initProject() {
|
|
|
258
319
|
// ── Step 8: Generate VS Code Settings ─────────────────────────────────────
|
|
259
320
|
console.log(c.bold('💻 Step 8: Creating VS Code workspace settings...'));
|
|
260
321
|
try {
|
|
322
|
+
const vscodeFiles = ['.vscode/settings.json', '.vscode/extensions.json', '.vscode/tasks.json', '.vscode/launch.json'];
|
|
323
|
+
const vscodeExisted = {};
|
|
324
|
+
for (const vf of vscodeFiles) {
|
|
325
|
+
vscodeExisted[vf] = fs.existsSync(path.join(cwd, vf));
|
|
326
|
+
}
|
|
261
327
|
const { generateVSCodeSettings, generateVSCodeExtensions, generateVSCodeTasks, generateVSCodeLaunch } = requireGenerator('vscode-generator');
|
|
262
328
|
generateVSCodeSettings(project);
|
|
263
329
|
generateVSCodeExtensions(project);
|
|
264
330
|
generateVSCodeTasks(project);
|
|
265
331
|
generateVSCodeLaunch(project);
|
|
332
|
+
for (const vf of vscodeFiles) {
|
|
333
|
+
if (!vscodeExisted[vf] && fs.existsSync(path.join(cwd, vf))) {
|
|
334
|
+
recordCreatedFile(manifest, vf);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
266
337
|
console.log(` ${c.green('✓')} Created .vscode/settings.json`);
|
|
267
338
|
console.log(` ${c.green('✓')} Created .vscode/extensions.json`);
|
|
268
339
|
console.log(` ${c.green('✓')} Created .vscode/tasks.json`);
|
|
@@ -274,7 +345,15 @@ function initProject() {
|
|
|
274
345
|
// ── Step 9: Setup Git Hooks ───────────────────────────────────────────────
|
|
275
346
|
if (!skipHooks) {
|
|
276
347
|
console.log(c.bold('🐶 Step 9: Setting up Git hooks...'));
|
|
348
|
+
const hookFile = '.husky/pre-commit';
|
|
349
|
+
const hookExisted = fs.existsSync(path.join(cwd, hookFile));
|
|
277
350
|
setupGitHooks(cwd);
|
|
351
|
+
if (hookExisted) {
|
|
352
|
+
recordModifiedFile(manifest, hookFile, 'husky');
|
|
353
|
+
}
|
|
354
|
+
else if (fs.existsSync(path.join(cwd, hookFile))) {
|
|
355
|
+
recordCreatedFile(manifest, hookFile);
|
|
356
|
+
}
|
|
278
357
|
console.log('');
|
|
279
358
|
}
|
|
280
359
|
else {
|
|
@@ -286,16 +365,30 @@ function initProject() {
|
|
|
286
365
|
try {
|
|
287
366
|
const { loadCompanyRules } = requireUtil('custom-rules-loader');
|
|
288
367
|
const companyRules = loadCompanyRules(cwd);
|
|
289
|
-
|
|
290
|
-
generateAIConfigs(project, companyRules.aiGuidelines);
|
|
368
|
+
// Snapshot which AI files exist before generation
|
|
291
369
|
const { defaultRegistry } = requireUtil('ai-config-registry');
|
|
292
370
|
const templates = defaultRegistry.getAll();
|
|
371
|
+
const aiExisted = {};
|
|
293
372
|
for (const t of templates) {
|
|
294
373
|
const targetDir = t.directory ? path.join(cwd, t.directory) : cwd;
|
|
295
374
|
const targetPath = path.join(targetDir, t.fileName);
|
|
375
|
+
const relPath = t.directory ? `${t.directory}/${t.fileName}` : t.fileName;
|
|
376
|
+
aiExisted[relPath] = fs.existsSync(targetPath);
|
|
377
|
+
}
|
|
378
|
+
const { generateAIConfigs } = requireGenerator('ai-config-generator');
|
|
379
|
+
generateAIConfigs(project, companyRules.aiGuidelines);
|
|
380
|
+
for (const t of templates) {
|
|
381
|
+
const targetDir = t.directory ? path.join(cwd, t.directory) : cwd;
|
|
382
|
+
const targetPath = path.join(targetDir, t.fileName);
|
|
383
|
+
const relPath = t.directory ? `${t.directory}/${t.fileName}` : t.fileName;
|
|
296
384
|
if (fs.existsSync(targetPath)) {
|
|
297
|
-
|
|
298
|
-
|
|
385
|
+
if (aiExisted[relPath]) {
|
|
386
|
+
recordModifiedFile(manifest, relPath, t.marker);
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
recordCreatedFile(manifest, relPath);
|
|
390
|
+
}
|
|
391
|
+
console.log(` ${c.green('✓')} ${t.name}: ${relPath}`);
|
|
299
392
|
}
|
|
300
393
|
}
|
|
301
394
|
console.log('');
|
|
@@ -310,6 +403,11 @@ function initProject() {
|
|
|
310
403
|
// ── Step 11: Custom Rules Template ─────────────────────────────────────────
|
|
311
404
|
console.log(c.bold('🏢 Step 11: Creating custom rules templates...'));
|
|
312
405
|
try {
|
|
406
|
+
const cgFiles = ['.code-guardian/custom-rules.json', '.code-guardian/structure-rules.json', '.code-guardian/naming-rules.json'];
|
|
407
|
+
const cgExisted = {};
|
|
408
|
+
for (const cf of cgFiles) {
|
|
409
|
+
cgExisted[cf] = fs.existsSync(path.join(cwd, cf));
|
|
410
|
+
}
|
|
313
411
|
const { generateCustomRulesTemplate } = requireUtil('custom-rules-loader');
|
|
314
412
|
generateCustomRulesTemplate(cwd);
|
|
315
413
|
console.log(` ${c.green('✓')} Created .code-guardian/custom-rules.json`);
|
|
@@ -319,18 +417,26 @@ function initProject() {
|
|
|
319
417
|
const { generateNamingRulesTemplate } = requireUtil('naming-validator');
|
|
320
418
|
generateNamingRulesTemplate(cwd);
|
|
321
419
|
console.log(` ${c.green('✓')} Created .code-guardian/naming-rules.json\n`);
|
|
420
|
+
for (const cf of cgFiles) {
|
|
421
|
+
if (!cgExisted[cf] && fs.existsSync(path.join(cwd, cf))) {
|
|
422
|
+
recordCreatedFile(manifest, cf);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
322
425
|
}
|
|
323
426
|
catch (error) {
|
|
324
427
|
console.warn(c.yellow(` ⚠️ Custom rules template generation failed: ${error.message}\n`));
|
|
325
428
|
}
|
|
326
429
|
// ── Step 12: Update package.json ───────────────────────────────────────────
|
|
327
430
|
console.log(c.bold('📦 Step 12: Updating package.json...'));
|
|
328
|
-
updatePackageJson(cwd, project);
|
|
431
|
+
updatePackageJson(cwd, project, manifest);
|
|
329
432
|
console.log('');
|
|
330
433
|
// ── Step 13: Copy Code Guardian configs and templates ──────────────────────
|
|
331
434
|
console.log(c.bold('📁 Step 13: Copying Code Guardian rules and templates...'));
|
|
332
|
-
copyCodeGuardianFiles(cwd);
|
|
435
|
+
copyCodeGuardianFiles(cwd, manifest, project.type);
|
|
333
436
|
console.log('');
|
|
437
|
+
// ── Save manifest ─────────────────────────────────────────────────────────
|
|
438
|
+
saveManifest(cwd, manifest);
|
|
439
|
+
console.log(` ${c.green('✓')} Saved manifest to .code-guardian/manifest.json\n`);
|
|
334
440
|
// ── Done! ─────────────────────────────────────────────────────────────────
|
|
335
441
|
console.log(c.bold(c.green('═══════════════════════════════════════════════════════════')));
|
|
336
442
|
console.log(c.bold(c.green(' ✅ Code Guardian initialized successfully!')));
|
|
@@ -364,36 +470,45 @@ function initProject() {
|
|
|
364
470
|
// ── Setup Git Hooks ─────────────────────────────────────────────────────────
|
|
365
471
|
function setupGitHooks(cwd) {
|
|
366
472
|
try {
|
|
367
|
-
|
|
473
|
+
const huskyDir = path.join(cwd, '.husky');
|
|
474
|
+
const preCommitPath = path.join(huskyDir, 'pre-commit');
|
|
475
|
+
// Save existing pre-commit content BEFORE husky init (which overwrites it)
|
|
476
|
+
let existingPreCommit = null;
|
|
477
|
+
if (fs.existsSync(preCommitPath)) {
|
|
478
|
+
existingPreCommit = fs.readFileSync(preCommitPath, 'utf-8');
|
|
479
|
+
}
|
|
480
|
+
// Initialize Husky (creates .husky/ dir and default pre-commit)
|
|
368
481
|
try {
|
|
369
482
|
(0, child_process_1.execSync)('npx husky init', { stdio: 'pipe', cwd });
|
|
370
483
|
}
|
|
371
484
|
catch {
|
|
372
485
|
// Fallback: manual setup
|
|
373
|
-
const huskyDir = path.join(cwd, '.husky');
|
|
374
486
|
if (!fs.existsSync(huskyDir)) {
|
|
375
487
|
fs.mkdirSync(huskyDir, { recursive: true });
|
|
376
488
|
}
|
|
377
489
|
}
|
|
378
|
-
const huskyDir = path.join(cwd, '.husky');
|
|
379
490
|
if (!fs.existsSync(huskyDir)) {
|
|
380
491
|
fs.mkdirSync(huskyDir, { recursive: true });
|
|
381
492
|
}
|
|
382
|
-
//
|
|
493
|
+
// Restore the original pre-commit content if husky init overwrote it
|
|
494
|
+
if (existingPreCommit !== null) {
|
|
495
|
+
fs.writeFileSync(preCommitPath, existingPreCommit);
|
|
496
|
+
}
|
|
497
|
+
// Create or append pre-commit hook (lint-staged only — Code Guardian checks run via `code-guard check`)
|
|
383
498
|
const preCommitContent = `
|
|
384
|
-
# npm test
|
|
385
|
-
|
|
386
499
|
# --- code-guardian-hook-start ---
|
|
387
|
-
echo "🛡️ Code Guardian — Pre-commit
|
|
500
|
+
echo "🛡️ Code Guardian — Pre-commit lint & format..."
|
|
501
|
+
|
|
502
|
+
# Skip lint-staged when BYPASS_RULES is set
|
|
503
|
+
if [ "$BYPASS_RULES" = "true" ] || [ "$BYPASS_RULES" = "1" ]; then
|
|
504
|
+
echo "⚡ BYPASS_RULES detected — skipping lint-staged."
|
|
505
|
+
exit 0
|
|
506
|
+
fi
|
|
388
507
|
|
|
389
508
|
# Run lint-staged (ESLint + Prettier auto-fix)
|
|
390
509
|
npx lint-staged
|
|
391
|
-
|
|
392
|
-
# Run Code Guardian custom rules
|
|
393
|
-
npm run precommit-check
|
|
394
510
|
# --- code-guardian-hook-end ---
|
|
395
511
|
`;
|
|
396
|
-
const preCommitPath = path.join(huskyDir, 'pre-commit');
|
|
397
512
|
if (fs.existsSync(preCommitPath)) {
|
|
398
513
|
const existing = fs.readFileSync(preCommitPath, 'utf-8');
|
|
399
514
|
if (!existing.includes('code-guardian-hook-start')) {
|
|
@@ -409,7 +524,7 @@ npm run precommit-check
|
|
|
409
524
|
// Create new hook file
|
|
410
525
|
const preCommitHook = `#!/usr/bin/env sh\n${preCommitContent}`;
|
|
411
526
|
fs.writeFileSync(preCommitPath, preCommitHook);
|
|
412
|
-
console.log(` ${c.green('✓')} Created .husky/pre-commit (lint-staged
|
|
527
|
+
console.log(` ${c.green('✓')} Created .husky/pre-commit (lint-staged only)`);
|
|
413
528
|
}
|
|
414
529
|
try {
|
|
415
530
|
fs.chmodSync(preCommitPath, '755');
|
|
@@ -462,43 +577,74 @@ function buildInstallCommand(packageManager, deps) {
|
|
|
462
577
|
}
|
|
463
578
|
}
|
|
464
579
|
// ── Update package.json ─────────────────────────────────────────────────────
|
|
465
|
-
function updatePackageJson(cwd, project) {
|
|
580
|
+
function updatePackageJson(cwd, project, manifest) {
|
|
466
581
|
const packageJsonPath = path.join(cwd, 'package.json');
|
|
467
582
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
468
583
|
const packageName = '@hatem427/code-guard-ci';
|
|
469
584
|
packageJson.scripts = packageJson.scripts || {};
|
|
585
|
+
// Helper to track script changes in manifest
|
|
586
|
+
const setScript = (name, value) => {
|
|
587
|
+
if (manifest) {
|
|
588
|
+
if (packageJson.scripts[name] && packageJson.scripts[name] !== value) {
|
|
589
|
+
// Script existed with different value — record original
|
|
590
|
+
const { recordModifiedScript } = requireUtil('manifest');
|
|
591
|
+
recordModifiedScript(manifest, name, packageJson.scripts[name]);
|
|
592
|
+
}
|
|
593
|
+
else if (!packageJson.scripts[name]) {
|
|
594
|
+
// New script
|
|
595
|
+
const { recordAddedScript } = requireUtil('manifest');
|
|
596
|
+
recordAddedScript(manifest, name);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
packageJson.scripts[name] = value;
|
|
600
|
+
};
|
|
470
601
|
// Core Code Guardian scripts
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
602
|
+
setScript('precommit-check', 'code-guard check');
|
|
603
|
+
setScript('generate-doc', 'code-guard doc');
|
|
604
|
+
setScript('generate-pr-checklist', 'code-guard checklist');
|
|
605
|
+
setScript('validate', 'code-guard validate');
|
|
606
|
+
setScript('prepare', 'husky');
|
|
476
607
|
// Lint and format scripts
|
|
477
608
|
const extensions = project.usesTypeScript ? '.ts,.tsx,.js,.jsx' : '.js,.jsx';
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
609
|
+
setScript('lint', `eslint . --ext ${extensions}`);
|
|
610
|
+
setScript('lint:fix', `eslint . --ext ${extensions} --fix`);
|
|
611
|
+
setScript('format', 'prettier --write .');
|
|
612
|
+
setScript('format:check', 'prettier --check .');
|
|
482
613
|
// Security scripts
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
614
|
+
setScript('set-bypass-password', `node node_modules/${packageName}/dist/scripts/set-bypass-password.js`);
|
|
615
|
+
setScript('set-admin-password', `node node_modules/${packageName}/dist/scripts/set-admin-password.js`);
|
|
616
|
+
setScript('delete-bypass-logs', `node node_modules/${packageName}/dist/scripts/delete-bypass-logs.js`);
|
|
617
|
+
setScript('view-bypass-log', `node node_modules/${packageName}/dist/scripts/view-bypass-log.js`);
|
|
618
|
+
setScript('auto-fix', `node node_modules/${packageName}/dist/scripts/auto-fix.js`);
|
|
488
619
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
489
620
|
console.log(` ${c.green('✓')} Added npm scripts (lint, format, validate, precommit-check, etc.)`);
|
|
490
621
|
}
|
|
491
622
|
// ── Copy Code Guardian files ────────────────────────────────────────────────
|
|
492
|
-
function copyCodeGuardianFiles(cwd) {
|
|
623
|
+
function copyCodeGuardianFiles(cwd, manifest, projectType) {
|
|
493
624
|
// Determine source directories (handles both dev and built package layouts)
|
|
494
625
|
const distDir = path.join(__dirname, '..');
|
|
495
626
|
const pkgRootDir = path.join(__dirname, '..', '..');
|
|
496
|
-
// Copy config files
|
|
627
|
+
// Copy config files — only guidelines (always) + detected framework config
|
|
497
628
|
const configDir = path.join(cwd, 'config');
|
|
498
629
|
if (!fs.existsSync(configDir)) {
|
|
499
630
|
fs.mkdirSync(configDir, { recursive: true });
|
|
500
631
|
}
|
|
501
|
-
|
|
632
|
+
// Map project type to its config file
|
|
633
|
+
const frameworkConfigMap = {
|
|
634
|
+
angular: 'angular.config.ts',
|
|
635
|
+
react: 'react.config.ts',
|
|
636
|
+
nextjs: 'nextjs.config.ts',
|
|
637
|
+
node: 'node.config.ts',
|
|
638
|
+
};
|
|
639
|
+
// Always include guidelines + detected framework config (+ react for nextjs since it inherits)
|
|
640
|
+
const configFiles = ['guidelines.config.ts'];
|
|
641
|
+
if (projectType && frameworkConfigMap[projectType]) {
|
|
642
|
+
configFiles.push(frameworkConfigMap[projectType]);
|
|
643
|
+
}
|
|
644
|
+
// Next.js inherits React rules, so include react config too
|
|
645
|
+
if (projectType === 'nextjs' && !configFiles.includes('react.config.ts')) {
|
|
646
|
+
configFiles.push('react.config.ts');
|
|
647
|
+
}
|
|
502
648
|
const sourceConfigDirs = [path.join(distDir, 'config'), path.join(pkgRootDir, 'config')];
|
|
503
649
|
for (const file of configFiles) {
|
|
504
650
|
let src = null;
|
|
@@ -512,6 +658,10 @@ function copyCodeGuardianFiles(cwd) {
|
|
|
512
658
|
const dest = path.join(configDir, file);
|
|
513
659
|
if (src && !fs.existsSync(dest)) {
|
|
514
660
|
fs.copyFileSync(src, dest);
|
|
661
|
+
if (manifest) {
|
|
662
|
+
const { recordCreatedFile } = requireUtil('manifest');
|
|
663
|
+
recordCreatedFile(manifest, `config/${file}`);
|
|
664
|
+
}
|
|
515
665
|
console.log(` ${c.green('✓')} ${file}`);
|
|
516
666
|
}
|
|
517
667
|
}
|
|
@@ -534,6 +684,10 @@ function copyCodeGuardianFiles(cwd) {
|
|
|
534
684
|
const dest = path.join(templatesDir, file);
|
|
535
685
|
if (src && !fs.existsSync(dest)) {
|
|
536
686
|
fs.copyFileSync(src, dest);
|
|
687
|
+
if (manifest) {
|
|
688
|
+
const { recordCreatedFile } = requireUtil('manifest');
|
|
689
|
+
recordCreatedFile(manifest, `templates/${file}`);
|
|
690
|
+
}
|
|
537
691
|
console.log(` ${c.green('✓')} ${file}`);
|
|
538
692
|
}
|
|
539
693
|
}
|
|
@@ -542,6 +696,10 @@ function copyCodeGuardianFiles(cwd) {
|
|
|
542
696
|
if (!fs.existsSync(docsDir)) {
|
|
543
697
|
fs.mkdirSync(docsDir, { recursive: true });
|
|
544
698
|
fs.writeFileSync(path.join(docsDir, '.gitkeep'), '');
|
|
699
|
+
if (manifest) {
|
|
700
|
+
const { recordCreatedFile } = requireUtil('manifest');
|
|
701
|
+
recordCreatedFile(manifest, 'docs/features/.gitkeep');
|
|
702
|
+
}
|
|
545
703
|
console.log(` ${c.green('✓')} Created docs/features/`);
|
|
546
704
|
}
|
|
547
705
|
}
|
|
@@ -877,11 +1035,20 @@ function promptYesNo(question) {
|
|
|
877
1035
|
async function uninstallCodeGuard() {
|
|
878
1036
|
showBanner();
|
|
879
1037
|
console.log(c.bold('🗑️ Code Guardian Uninstall\n'));
|
|
880
|
-
const
|
|
1038
|
+
const cwd = process.cwd();
|
|
1039
|
+
// ── Load manifest for smart uninstall ────────────────────────────────
|
|
1040
|
+
const { loadManifest, removeMarkedContent } = requireUtil('manifest');
|
|
1041
|
+
const manifest = loadManifest(cwd);
|
|
1042
|
+
// Hardcoded fallbacks for files that Code Guardian always creates
|
|
1043
|
+
const fallbackFilesToRemove = [
|
|
881
1044
|
'.husky/_/husky.sh',
|
|
1045
|
+
'eslint.config.mjs',
|
|
882
1046
|
'eslint.config.js',
|
|
1047
|
+
'.prettierrc.json',
|
|
1048
|
+
'.prettierignore',
|
|
883
1049
|
'prettier.config.js',
|
|
884
1050
|
'.editorconfig',
|
|
1051
|
+
'.lintstagedrc.json',
|
|
885
1052
|
'lint-staged.config.js',
|
|
886
1053
|
'tsconfig.strict.json',
|
|
887
1054
|
];
|
|
@@ -889,7 +1056,6 @@ async function uninstallCodeGuard() {
|
|
|
889
1056
|
'config',
|
|
890
1057
|
'templates',
|
|
891
1058
|
'docs',
|
|
892
|
-
'.code-guardian',
|
|
893
1059
|
];
|
|
894
1060
|
const scriptsToRemove = [
|
|
895
1061
|
'precommit-check',
|
|
@@ -900,11 +1066,16 @@ async function uninstallCodeGuard() {
|
|
|
900
1066
|
'set-admin-password',
|
|
901
1067
|
'view-bypass-log',
|
|
902
1068
|
'delete-bypass-logs',
|
|
1069
|
+
'lint',
|
|
1070
|
+
'lint:fix',
|
|
1071
|
+
'format',
|
|
1072
|
+
'format:check',
|
|
1073
|
+
'validate',
|
|
1074
|
+
'prepare',
|
|
903
1075
|
];
|
|
904
1076
|
// ── Detect AI config files via registry ──────────────────────────────
|
|
905
1077
|
const { defaultRegistry } = requireUtil('ai-config-registry');
|
|
906
1078
|
const aiTemplates = defaultRegistry.getAll();
|
|
907
|
-
const cwd = process.cwd();
|
|
908
1079
|
const aiActions = [];
|
|
909
1080
|
for (const t of aiTemplates) {
|
|
910
1081
|
const dir = t.directory ? path.join(cwd, t.directory) : cwd;
|
|
@@ -914,38 +1085,103 @@ async function uninstallCodeGuard() {
|
|
|
914
1085
|
continue;
|
|
915
1086
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
916
1087
|
if (!content.includes(t.marker))
|
|
917
|
-
continue;
|
|
918
|
-
//
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
// A more robust check: if content before marker (minus separator) has real text
|
|
928
|
-
const withoutTrailingSep = beforeMarker.replace(/\n*---\s*$/, '').trim();
|
|
929
|
-
const realUserContent = withoutTrailingSep.replace(/^---[\s\S]*?---/, '').trim(); // strip frontmatter
|
|
930
|
-
if (realUserContent.length > 0) {
|
|
931
|
-
aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'strip' });
|
|
1088
|
+
continue;
|
|
1089
|
+
// Use manifest to determine action if available
|
|
1090
|
+
if (manifest && manifest.files[relativePath]) {
|
|
1091
|
+
const record = manifest.files[relativePath];
|
|
1092
|
+
if (record.action === 'created') {
|
|
1093
|
+
aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'delete' });
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'strip' });
|
|
1097
|
+
}
|
|
932
1098
|
}
|
|
933
1099
|
else {
|
|
934
|
-
|
|
1100
|
+
// Fallback: detect by checking for user content before marker
|
|
1101
|
+
const markerIdx = content.indexOf(t.marker);
|
|
1102
|
+
const beforeMarker = content.substring(0, markerIdx).trimEnd();
|
|
1103
|
+
const withoutTrailingSep = beforeMarker.replace(/\n*---\s*$/, '').trim();
|
|
1104
|
+
const realUserContent = withoutTrailingSep.replace(/^---[\s\S]*?---/, '').trim();
|
|
1105
|
+
if (realUserContent.length > 0) {
|
|
1106
|
+
aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'strip' });
|
|
1107
|
+
}
|
|
1108
|
+
else {
|
|
1109
|
+
aiActions.push({ name: t.name, filePath, relativePath, marker: t.marker, action: 'delete' });
|
|
1110
|
+
}
|
|
935
1111
|
}
|
|
936
1112
|
}
|
|
1113
|
+
// Determine which files to remove (use manifest if available, fallback to hardcoded)
|
|
1114
|
+
let filesToProcess;
|
|
1115
|
+
if (manifest) {
|
|
1116
|
+
// Use manifest — it knows exactly what we created vs modified
|
|
1117
|
+
filesToProcess = Object.keys(manifest.files).filter(f => {
|
|
1118
|
+
// Skip AI files — handled separately
|
|
1119
|
+
return !aiActions.some(a => a.relativePath === f);
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
else {
|
|
1123
|
+
filesToProcess = fallbackFilesToRemove;
|
|
1124
|
+
}
|
|
937
1125
|
// Check what exists
|
|
938
|
-
const existingFiles =
|
|
1126
|
+
const existingFiles = filesToProcess.filter(f => fs.existsSync(path.join(cwd, f)));
|
|
939
1127
|
const existingDirs = dirsToRemove.filter(d => fs.existsSync(path.join(cwd, d)));
|
|
940
|
-
|
|
1128
|
+
// Also check for backup files that need restoring
|
|
1129
|
+
const backupsToRestore = [];
|
|
1130
|
+
if (manifest && manifest.backups) {
|
|
1131
|
+
for (const [original, backup] of Object.entries(manifest.backups)) {
|
|
1132
|
+
if (fs.existsSync(path.join(cwd, backup))) {
|
|
1133
|
+
backupsToRestore.push({ original, backup });
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
// Also find any .backup files from generators (eslint, prettier create them)
|
|
1138
|
+
for (const file of existingFiles) {
|
|
1139
|
+
const backupPath = path.join(cwd, file + '.backup');
|
|
1140
|
+
if (fs.existsSync(backupPath) && !backupsToRestore.some(b => b.backup === file + '.backup')) {
|
|
1141
|
+
backupsToRestore.push({ original: file, backup: file + '.backup' });
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
// Check common backup locations from generators
|
|
1145
|
+
const commonBackups = [
|
|
1146
|
+
'.eslintrc.backup', '.eslintrc.js.backup', '.eslintrc.cjs.backup', '.eslintrc.json.backup',
|
|
1147
|
+
'eslint.config.js.backup', 'eslint.config.mjs.backup', 'eslint.config.cjs.backup',
|
|
1148
|
+
'.prettierrc.backup', '.prettierrc.js.backup', '.prettierrc.cjs.backup', '.prettierrc.json.backup',
|
|
1149
|
+
'.prettierrc.yml.backup', '.prettierrc.yaml.backup', '.prettierrc.toml.backup', 'prettier.config.js.backup',
|
|
1150
|
+
'.eslintignore.backup',
|
|
1151
|
+
];
|
|
1152
|
+
for (const backup of commonBackups) {
|
|
1153
|
+
if (fs.existsSync(path.join(cwd, backup))) {
|
|
1154
|
+
const original = backup.replace(/\.backup$/, '');
|
|
1155
|
+
if (!backupsToRestore.some(b => b.backup === backup)) {
|
|
1156
|
+
backupsToRestore.push({ original, backup });
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
if (existingFiles.length === 0 && existingDirs.length === 0 && aiActions.length === 0 && backupsToRestore.length === 0) {
|
|
941
1161
|
console.log(c.yellow('⚠️ No Code Guardian files found to remove.\n'));
|
|
942
1162
|
return;
|
|
943
1163
|
}
|
|
944
|
-
// Show what will be
|
|
1164
|
+
// Show what will be done
|
|
945
1165
|
console.log(c.bold('The following will be removed:\n'));
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1166
|
+
// Separate created vs modified files
|
|
1167
|
+
const createdFiles = [];
|
|
1168
|
+
const modifiedFiles = [];
|
|
1169
|
+
for (const f of existingFiles) {
|
|
1170
|
+
if (manifest && manifest.files[f] && manifest.files[f].action === 'modified') {
|
|
1171
|
+
modifiedFiles.push(f);
|
|
1172
|
+
}
|
|
1173
|
+
else {
|
|
1174
|
+
createdFiles.push(f);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
if (createdFiles.length > 0) {
|
|
1178
|
+
console.log(c.bold('Files (created by Code Guardian — will be deleted):'));
|
|
1179
|
+
createdFiles.forEach(f => console.log(` ${c.red('✗')} ${f}`));
|
|
1180
|
+
console.log('');
|
|
1181
|
+
}
|
|
1182
|
+
if (modifiedFiles.length > 0) {
|
|
1183
|
+
console.log(c.bold('Files (modified — Code Guardian content will be stripped):'));
|
|
1184
|
+
modifiedFiles.forEach(f => console.log(` ${c.yellow('⚠')} ${f} ${c.dim('(your content preserved)')}`));
|
|
949
1185
|
console.log('');
|
|
950
1186
|
}
|
|
951
1187
|
if (existingDirs.length > 0) {
|
|
@@ -965,6 +1201,11 @@ async function uninstallCodeGuard() {
|
|
|
965
1201
|
aiStrips.forEach(a => console.log(` ${c.yellow('⚠')} ${a.relativePath} ${c.dim('(keeping your original content)')}`));
|
|
966
1202
|
console.log('');
|
|
967
1203
|
}
|
|
1204
|
+
if (backupsToRestore.length > 0) {
|
|
1205
|
+
console.log(c.bold('Backups to restore (original configs):'));
|
|
1206
|
+
backupsToRestore.forEach(b => console.log(` ${c.blue('↩')} ${b.backup} → ${b.original}`));
|
|
1207
|
+
console.log('');
|
|
1208
|
+
}
|
|
968
1209
|
if (!hasFlag('yes') && !hasFlag('y')) {
|
|
969
1210
|
const readline = require('readline');
|
|
970
1211
|
const rl = readline.createInterface({
|
|
@@ -984,47 +1225,72 @@ async function uninstallCodeGuard() {
|
|
|
984
1225
|
}
|
|
985
1226
|
let removedCount = 0;
|
|
986
1227
|
console.log('');
|
|
987
|
-
//
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
for (const action of aiActions) {
|
|
994
|
-
if (fs.existsSync(action.filePath)) {
|
|
1228
|
+
// ── Phase 1: Handle created files (delete) ──────────────────────────
|
|
1229
|
+
if (createdFiles.length > 0) {
|
|
1230
|
+
console.log(c.bold('Removing created files...\n'));
|
|
1231
|
+
for (const file of createdFiles) {
|
|
1232
|
+
const fullPath = path.join(cwd, file);
|
|
1233
|
+
if (fs.existsSync(fullPath)) {
|
|
995
1234
|
try {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
1000
|
-
fs.cpSync(action.filePath, destPath);
|
|
1001
|
-
console.log(` ${c.blue('📦')} Backed up ${action.relativePath}`);
|
|
1002
|
-
hasBackup = true;
|
|
1235
|
+
fs.unlinkSync(fullPath);
|
|
1236
|
+
console.log(` ${c.green('✓')} Removed ${file}`);
|
|
1237
|
+
removedCount++;
|
|
1003
1238
|
}
|
|
1004
1239
|
catch (error) {
|
|
1005
|
-
console.log(` ${c.
|
|
1240
|
+
console.log(` ${c.red('✗')} Failed to remove ${file}: ${error.message}`);
|
|
1006
1241
|
}
|
|
1007
1242
|
}
|
|
1008
1243
|
}
|
|
1009
1244
|
console.log('');
|
|
1010
1245
|
}
|
|
1011
|
-
//
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
const
|
|
1015
|
-
|
|
1246
|
+
// ── Phase 2: Handle modified files (strip marked content) ───────────
|
|
1247
|
+
if (modifiedFiles.length > 0) {
|
|
1248
|
+
console.log(c.bold('Stripping Code Guardian content from modified files...\n'));
|
|
1249
|
+
for (const file of modifiedFiles) {
|
|
1250
|
+
const fullPath = path.join(cwd, file);
|
|
1251
|
+
if (fs.existsSync(fullPath)) {
|
|
1252
|
+
try {
|
|
1253
|
+
const record = manifest?.files[file];
|
|
1254
|
+
const stripped = removeMarkedContent(fullPath, record?.marker);
|
|
1255
|
+
if (stripped) {
|
|
1256
|
+
console.log(` ${c.green('✓')} Stripped Code Guardian content from ${file}`);
|
|
1257
|
+
}
|
|
1258
|
+
else {
|
|
1259
|
+
console.log(` ${c.dim('○')} No marked content found in ${file}`);
|
|
1260
|
+
}
|
|
1261
|
+
removedCount++;
|
|
1262
|
+
}
|
|
1263
|
+
catch (error) {
|
|
1264
|
+
console.log(` ${c.red('✗')} Failed to clean ${file}: ${error.message}`);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
console.log('');
|
|
1269
|
+
}
|
|
1270
|
+
// ── Phase 3: Restore backup files ───────────────────────────────────
|
|
1271
|
+
if (backupsToRestore.length > 0) {
|
|
1272
|
+
console.log(c.bold('Restoring original config files...\n'));
|
|
1273
|
+
for (const { original, backup } of backupsToRestore) {
|
|
1274
|
+
const backupFullPath = path.join(cwd, backup);
|
|
1275
|
+
const originalFullPath = path.join(cwd, original);
|
|
1016
1276
|
try {
|
|
1017
|
-
|
|
1018
|
-
|
|
1277
|
+
// Remove the Code Guardian version first (if it exists)
|
|
1278
|
+
if (fs.existsSync(originalFullPath)) {
|
|
1279
|
+
fs.unlinkSync(originalFullPath);
|
|
1280
|
+
}
|
|
1281
|
+
// Restore the backup as the original
|
|
1282
|
+
fs.renameSync(backupFullPath, originalFullPath);
|
|
1283
|
+
console.log(` ${c.green('✓')} Restored ${original} from ${backup}`);
|
|
1019
1284
|
removedCount++;
|
|
1020
1285
|
}
|
|
1021
1286
|
catch (error) {
|
|
1022
|
-
console.log(` ${c.red('✗')} Failed to
|
|
1287
|
+
console.log(` ${c.red('✗')} Failed to restore ${original}: ${error.message}`);
|
|
1023
1288
|
}
|
|
1024
1289
|
}
|
|
1290
|
+
console.log('');
|
|
1025
1291
|
}
|
|
1026
|
-
// Remove directories
|
|
1027
|
-
console.log(c.bold('
|
|
1292
|
+
// ── Phase 4: Remove directories ─────────────────────────────────────
|
|
1293
|
+
console.log(c.bold('Removing directories...\n'));
|
|
1028
1294
|
for (const dir of dirsToRemove) {
|
|
1029
1295
|
const fullPath = path.join(cwd, dir);
|
|
1030
1296
|
if (fs.existsSync(fullPath)) {
|
|
@@ -1038,12 +1304,11 @@ async function uninstallCodeGuard() {
|
|
|
1038
1304
|
}
|
|
1039
1305
|
}
|
|
1040
1306
|
}
|
|
1041
|
-
// Handle AI config files intelligently
|
|
1307
|
+
// ── Phase 5: Handle AI config files intelligently ───────────────────
|
|
1042
1308
|
console.log(c.bold('\nCleaning AI config files...\n'));
|
|
1043
1309
|
for (const action of aiActions) {
|
|
1044
1310
|
try {
|
|
1045
1311
|
if (action.action === 'delete') {
|
|
1046
|
-
// Entire file was created by Code Guardian → delete it
|
|
1047
1312
|
fs.unlinkSync(action.filePath);
|
|
1048
1313
|
console.log(` ${c.green('✓')} Removed ${action.relativePath}`);
|
|
1049
1314
|
removedCount++;
|
|
@@ -1054,7 +1319,6 @@ async function uninstallCodeGuard() {
|
|
|
1054
1319
|
const remaining = fs.readdirSync(parentDir);
|
|
1055
1320
|
if (remaining.length === 0) {
|
|
1056
1321
|
fs.rmdirSync(parentDir);
|
|
1057
|
-
// Check grandparent too (e.g., .windsurf/rules → .windsurf)
|
|
1058
1322
|
const grandparent = path.dirname(parentDir);
|
|
1059
1323
|
if (grandparent !== cwd && fs.existsSync(grandparent)) {
|
|
1060
1324
|
const gpRemaining = fs.readdirSync(grandparent);
|
|
@@ -1072,16 +1336,12 @@ async function uninstallCodeGuard() {
|
|
|
1072
1336
|
const markerIdx = content.indexOf(action.marker);
|
|
1073
1337
|
if (markerIdx === -1)
|
|
1074
1338
|
continue;
|
|
1075
|
-
// Find the separator (--- on its own line) before the marker
|
|
1076
|
-
// When we append, we add "\n\n---\n\n" before our content
|
|
1077
1339
|
let cutStart = markerIdx;
|
|
1078
1340
|
const beforeMarker = content.substring(0, markerIdx);
|
|
1079
|
-
// Look for the --- separator we added
|
|
1080
1341
|
const sepMatch = beforeMarker.match(/\n*\s*---\s*\n*$/);
|
|
1081
1342
|
if (sepMatch && sepMatch.index !== undefined) {
|
|
1082
1343
|
cutStart = sepMatch.index;
|
|
1083
1344
|
}
|
|
1084
|
-
// Remove from cutStart to end of file (our content is always appended at the end)
|
|
1085
1345
|
const cleaned = content.substring(0, cutStart).trimEnd() + '\n';
|
|
1086
1346
|
fs.writeFileSync(action.filePath, cleaned);
|
|
1087
1347
|
console.log(` ${c.green('✓')} Stripped Code Guardian rules from ${action.relativePath} ${c.dim('(your content preserved)')}`);
|
|
@@ -1092,7 +1352,7 @@ async function uninstallCodeGuard() {
|
|
|
1092
1352
|
console.log(` ${c.red('✗')} Failed to clean ${action.relativePath}: ${error.message}`);
|
|
1093
1353
|
}
|
|
1094
1354
|
}
|
|
1095
|
-
// Clean up .husky hooks smartly
|
|
1355
|
+
// ── Phase 6: Clean up .husky hooks smartly ──────────────────────────
|
|
1096
1356
|
console.log(c.bold('\nCleaning git hooks...\n'));
|
|
1097
1357
|
const huskyDir = path.join(cwd, '.husky');
|
|
1098
1358
|
const huskyHookFiles = ['pre-commit'];
|
|
@@ -1111,7 +1371,6 @@ async function uninstallCodeGuard() {
|
|
|
1111
1371
|
continue;
|
|
1112
1372
|
const beforeHook = content.substring(0, startIdx).trimEnd();
|
|
1113
1373
|
const afterHook = content.substring(endIdx + HOOK_END.length).trimEnd();
|
|
1114
|
-
// Check if there's real user content outside of our markers
|
|
1115
1374
|
const remaining = (beforeHook.replace(/^#!\/usr\/bin\/env\s+sh\s*/, '').trim() + afterHook.trim()).trim();
|
|
1116
1375
|
if (remaining.length === 0) {
|
|
1117
1376
|
// Entire file is ours → delete it
|
|
@@ -1145,22 +1404,45 @@ async function uninstallCodeGuard() {
|
|
|
1145
1404
|
}
|
|
1146
1405
|
catch { }
|
|
1147
1406
|
}
|
|
1148
|
-
//
|
|
1407
|
+
// ── Phase 7: Clean package.json scripts ─────────────────────────────
|
|
1149
1408
|
console.log(c.bold('\nCleaning package.json scripts...\n'));
|
|
1150
1409
|
const packageJsonPath = path.join(cwd, 'package.json');
|
|
1151
1410
|
if (fs.existsSync(packageJsonPath)) {
|
|
1152
1411
|
try {
|
|
1153
1412
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
1154
|
-
let
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1413
|
+
let scriptChanged = false;
|
|
1414
|
+
if (manifest && manifest.packageJsonScripts) {
|
|
1415
|
+
// Restore modified scripts to original values
|
|
1416
|
+
for (const [scriptName, originalValue] of Object.entries(manifest.packageJsonScripts.modified)) {
|
|
1417
|
+
if (packageJson.scripts && packageJson.scripts[scriptName]) {
|
|
1418
|
+
packageJson.scripts[scriptName] = originalValue;
|
|
1419
|
+
console.log(` ${c.green('✓')} Restored script: ${scriptName} ${c.dim('(original value)')}`);
|
|
1420
|
+
scriptChanged = true;
|
|
1421
|
+
removedCount++;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
// Remove scripts that were added by Code Guardian
|
|
1425
|
+
for (const scriptName of manifest.packageJsonScripts.added) {
|
|
1426
|
+
if (packageJson.scripts && packageJson.scripts[scriptName]) {
|
|
1427
|
+
delete packageJson.scripts[scriptName];
|
|
1428
|
+
console.log(` ${c.green('✓')} Removed script: ${scriptName}`);
|
|
1429
|
+
scriptChanged = true;
|
|
1430
|
+
removedCount++;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
else {
|
|
1435
|
+
// Fallback: remove known Code Guardian scripts
|
|
1436
|
+
for (const script of scriptsToRemove) {
|
|
1437
|
+
if (packageJson.scripts && packageJson.scripts[script]) {
|
|
1438
|
+
delete packageJson.scripts[script];
|
|
1439
|
+
console.log(` ${c.green('✓')} Removed script: ${script}`);
|
|
1440
|
+
scriptChanged = true;
|
|
1441
|
+
removedCount++;
|
|
1442
|
+
}
|
|
1161
1443
|
}
|
|
1162
1444
|
}
|
|
1163
|
-
if (
|
|
1445
|
+
if (scriptChanged) {
|
|
1164
1446
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
1165
1447
|
console.log(` ${c.green('✓')} Updated package.json`);
|
|
1166
1448
|
}
|
|
@@ -1172,11 +1454,20 @@ async function uninstallCodeGuard() {
|
|
|
1172
1454
|
console.log(` ${c.red('✗')} Failed to clean package.json: ${error.message}`);
|
|
1173
1455
|
}
|
|
1174
1456
|
}
|
|
1457
|
+
// ── Phase 8: Remove .code-guardian directory (including manifest) ────
|
|
1458
|
+
const codeGuardianDir = path.join(cwd, '.code-guardian');
|
|
1459
|
+
if (fs.existsSync(codeGuardianDir)) {
|
|
1460
|
+
try {
|
|
1461
|
+
fs.rmSync(codeGuardianDir, { recursive: true, force: true });
|
|
1462
|
+
console.log(`\n ${c.green('✓')} Removed .code-guardian/`);
|
|
1463
|
+
removedCount++;
|
|
1464
|
+
}
|
|
1465
|
+
catch (error) {
|
|
1466
|
+
console.log(` ${c.red('✗')} Failed to remove .code-guardian/: ${error.message}`);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1175
1469
|
console.log('');
|
|
1176
1470
|
console.log(c.green(`✅ Cleanup complete! Removed ${removedCount} item(s).`));
|
|
1177
|
-
if (hasBackup) {
|
|
1178
|
-
console.log(c.blue(`📦 Backup saved to: ${path.basename(backupDir)}/`));
|
|
1179
|
-
}
|
|
1180
1471
|
console.log('');
|
|
1181
1472
|
console.log(c.dim('To completely remove the package, run:'));
|
|
1182
1473
|
console.log(c.dim(' npm uninstall @hatem427/code-guard-ci'));
|