@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/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 +400 -115
- package/dist/scripts/cli.js.map +1 -1
- package/package.json +1 -1
- package/scripts/cli.ts +434 -125
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 = {};
|
|
372
|
+
for (const t of templates) {
|
|
373
|
+
const targetDir = t.directory ? path.join(cwd, t.directory) : cwd;
|
|
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);
|
|
293
380
|
for (const t of templates) {
|
|
294
381
|
const targetDir = t.directory ? path.join(cwd, t.directory) : cwd;
|
|
295
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,42 +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..."
|
|
388
501
|
|
|
389
|
-
# Skip lint-staged
|
|
502
|
+
# Skip lint-staged when BYPASS_RULES is set
|
|
390
503
|
if [ "$BYPASS_RULES" = "true" ] || [ "$BYPASS_RULES" = "1" ]; then
|
|
391
|
-
echo "⚡ BYPASS_RULES detected — skipping lint-staged
|
|
504
|
+
echo "⚡ BYPASS_RULES detected — skipping lint-staged."
|
|
392
505
|
exit 0
|
|
393
506
|
fi
|
|
394
507
|
|
|
395
508
|
# Run lint-staged (ESLint + Prettier auto-fix)
|
|
396
509
|
npx lint-staged
|
|
397
|
-
|
|
398
|
-
# Run Code Guardian custom rules
|
|
399
|
-
npm run precommit-check
|
|
400
510
|
# --- code-guardian-hook-end ---
|
|
401
511
|
`;
|
|
402
|
-
const preCommitPath = path.join(huskyDir, 'pre-commit');
|
|
403
512
|
if (fs.existsSync(preCommitPath)) {
|
|
404
513
|
const existing = fs.readFileSync(preCommitPath, 'utf-8');
|
|
405
514
|
if (!existing.includes('code-guardian-hook-start')) {
|
|
@@ -415,7 +524,7 @@ npm run precommit-check
|
|
|
415
524
|
// Create new hook file
|
|
416
525
|
const preCommitHook = `#!/usr/bin/env sh\n${preCommitContent}`;
|
|
417
526
|
fs.writeFileSync(preCommitPath, preCommitHook);
|
|
418
|
-
console.log(` ${c.green('✓')} Created .husky/pre-commit (lint-staged
|
|
527
|
+
console.log(` ${c.green('✓')} Created .husky/pre-commit (lint-staged only)`);
|
|
419
528
|
}
|
|
420
529
|
try {
|
|
421
530
|
fs.chmodSync(preCommitPath, '755');
|
|
@@ -468,43 +577,74 @@ function buildInstallCommand(packageManager, deps) {
|
|
|
468
577
|
}
|
|
469
578
|
}
|
|
470
579
|
// ── Update package.json ─────────────────────────────────────────────────────
|
|
471
|
-
function updatePackageJson(cwd, project) {
|
|
580
|
+
function updatePackageJson(cwd, project, manifest) {
|
|
472
581
|
const packageJsonPath = path.join(cwd, 'package.json');
|
|
473
582
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
474
583
|
const packageName = '@hatem427/code-guard-ci';
|
|
475
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
|
+
};
|
|
476
601
|
// Core Code Guardian scripts
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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');
|
|
482
607
|
// Lint and format scripts
|
|
483
608
|
const extensions = project.usesTypeScript ? '.ts,.tsx,.js,.jsx' : '.js,.jsx';
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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 .');
|
|
488
613
|
// Security scripts
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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`);
|
|
494
619
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
495
620
|
console.log(` ${c.green('✓')} Added npm scripts (lint, format, validate, precommit-check, etc.)`);
|
|
496
621
|
}
|
|
497
622
|
// ── Copy Code Guardian files ────────────────────────────────────────────────
|
|
498
|
-
function copyCodeGuardianFiles(cwd) {
|
|
623
|
+
function copyCodeGuardianFiles(cwd, manifest, projectType) {
|
|
499
624
|
// Determine source directories (handles both dev and built package layouts)
|
|
500
625
|
const distDir = path.join(__dirname, '..');
|
|
501
626
|
const pkgRootDir = path.join(__dirname, '..', '..');
|
|
502
|
-
// Copy config files
|
|
627
|
+
// Copy config files — only guidelines (always) + detected framework config
|
|
503
628
|
const configDir = path.join(cwd, 'config');
|
|
504
629
|
if (!fs.existsSync(configDir)) {
|
|
505
630
|
fs.mkdirSync(configDir, { recursive: true });
|
|
506
631
|
}
|
|
507
|
-
|
|
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
|
+
}
|
|
508
648
|
const sourceConfigDirs = [path.join(distDir, 'config'), path.join(pkgRootDir, 'config')];
|
|
509
649
|
for (const file of configFiles) {
|
|
510
650
|
let src = null;
|
|
@@ -518,6 +658,10 @@ function copyCodeGuardianFiles(cwd) {
|
|
|
518
658
|
const dest = path.join(configDir, file);
|
|
519
659
|
if (src && !fs.existsSync(dest)) {
|
|
520
660
|
fs.copyFileSync(src, dest);
|
|
661
|
+
if (manifest) {
|
|
662
|
+
const { recordCreatedFile } = requireUtil('manifest');
|
|
663
|
+
recordCreatedFile(manifest, `config/${file}`);
|
|
664
|
+
}
|
|
521
665
|
console.log(` ${c.green('✓')} ${file}`);
|
|
522
666
|
}
|
|
523
667
|
}
|
|
@@ -540,6 +684,10 @@ function copyCodeGuardianFiles(cwd) {
|
|
|
540
684
|
const dest = path.join(templatesDir, file);
|
|
541
685
|
if (src && !fs.existsSync(dest)) {
|
|
542
686
|
fs.copyFileSync(src, dest);
|
|
687
|
+
if (manifest) {
|
|
688
|
+
const { recordCreatedFile } = requireUtil('manifest');
|
|
689
|
+
recordCreatedFile(manifest, `templates/${file}`);
|
|
690
|
+
}
|
|
543
691
|
console.log(` ${c.green('✓')} ${file}`);
|
|
544
692
|
}
|
|
545
693
|
}
|
|
@@ -548,6 +696,10 @@ function copyCodeGuardianFiles(cwd) {
|
|
|
548
696
|
if (!fs.existsSync(docsDir)) {
|
|
549
697
|
fs.mkdirSync(docsDir, { recursive: true });
|
|
550
698
|
fs.writeFileSync(path.join(docsDir, '.gitkeep'), '');
|
|
699
|
+
if (manifest) {
|
|
700
|
+
const { recordCreatedFile } = requireUtil('manifest');
|
|
701
|
+
recordCreatedFile(manifest, 'docs/features/.gitkeep');
|
|
702
|
+
}
|
|
551
703
|
console.log(` ${c.green('✓')} Created docs/features/`);
|
|
552
704
|
}
|
|
553
705
|
}
|
|
@@ -883,11 +1035,20 @@ function promptYesNo(question) {
|
|
|
883
1035
|
async function uninstallCodeGuard() {
|
|
884
1036
|
showBanner();
|
|
885
1037
|
console.log(c.bold('🗑️ Code Guardian Uninstall\n'));
|
|
886
|
-
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 = [
|
|
887
1044
|
'.husky/_/husky.sh',
|
|
1045
|
+
'eslint.config.mjs',
|
|
888
1046
|
'eslint.config.js',
|
|
1047
|
+
'.prettierrc.json',
|
|
1048
|
+
'.prettierignore',
|
|
889
1049
|
'prettier.config.js',
|
|
890
1050
|
'.editorconfig',
|
|
1051
|
+
'.lintstagedrc.json',
|
|
891
1052
|
'lint-staged.config.js',
|
|
892
1053
|
'tsconfig.strict.json',
|
|
893
1054
|
];
|
|
@@ -895,7 +1056,6 @@ async function uninstallCodeGuard() {
|
|
|
895
1056
|
'config',
|
|
896
1057
|
'templates',
|
|
897
1058
|
'docs',
|
|
898
|
-
'.code-guardian',
|
|
899
1059
|
];
|
|
900
1060
|
const scriptsToRemove = [
|
|
901
1061
|
'precommit-check',
|
|
@@ -906,11 +1066,16 @@ async function uninstallCodeGuard() {
|
|
|
906
1066
|
'set-admin-password',
|
|
907
1067
|
'view-bypass-log',
|
|
908
1068
|
'delete-bypass-logs',
|
|
1069
|
+
'lint',
|
|
1070
|
+
'lint:fix',
|
|
1071
|
+
'format',
|
|
1072
|
+
'format:check',
|
|
1073
|
+
'validate',
|
|
1074
|
+
'prepare',
|
|
909
1075
|
];
|
|
910
1076
|
// ── Detect AI config files via registry ──────────────────────────────
|
|
911
1077
|
const { defaultRegistry } = requireUtil('ai-config-registry');
|
|
912
1078
|
const aiTemplates = defaultRegistry.getAll();
|
|
913
|
-
const cwd = process.cwd();
|
|
914
1079
|
const aiActions = [];
|
|
915
1080
|
for (const t of aiTemplates) {
|
|
916
1081
|
const dir = t.directory ? path.join(cwd, t.directory) : cwd;
|
|
@@ -920,38 +1085,103 @@ async function uninstallCodeGuard() {
|
|
|
920
1085
|
continue;
|
|
921
1086
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
922
1087
|
if (!content.includes(t.marker))
|
|
923
|
-
continue;
|
|
924
|
-
//
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
// A more robust check: if content before marker (minus separator) has real text
|
|
934
|
-
const withoutTrailingSep = beforeMarker.replace(/\n*---\s*$/, '').trim();
|
|
935
|
-
const realUserContent = withoutTrailingSep.replace(/^---[\s\S]*?---/, '').trim(); // strip frontmatter
|
|
936
|
-
if (realUserContent.length > 0) {
|
|
937
|
-
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
|
+
}
|
|
938
1098
|
}
|
|
939
1099
|
else {
|
|
940
|
-
|
|
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
|
+
}
|
|
941
1111
|
}
|
|
942
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
|
+
}
|
|
943
1125
|
// Check what exists
|
|
944
|
-
const existingFiles =
|
|
1126
|
+
const existingFiles = filesToProcess.filter(f => fs.existsSync(path.join(cwd, f)));
|
|
945
1127
|
const existingDirs = dirsToRemove.filter(d => fs.existsSync(path.join(cwd, d)));
|
|
946
|
-
|
|
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) {
|
|
947
1161
|
console.log(c.yellow('⚠️ No Code Guardian files found to remove.\n'));
|
|
948
1162
|
return;
|
|
949
1163
|
}
|
|
950
|
-
// Show what will be
|
|
1164
|
+
// Show what will be done
|
|
951
1165
|
console.log(c.bold('The following will be removed:\n'));
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
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)')}`));
|
|
955
1185
|
console.log('');
|
|
956
1186
|
}
|
|
957
1187
|
if (existingDirs.length > 0) {
|
|
@@ -971,6 +1201,11 @@ async function uninstallCodeGuard() {
|
|
|
971
1201
|
aiStrips.forEach(a => console.log(` ${c.yellow('⚠')} ${a.relativePath} ${c.dim('(keeping your original content)')}`));
|
|
972
1202
|
console.log('');
|
|
973
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
|
+
}
|
|
974
1209
|
if (!hasFlag('yes') && !hasFlag('y')) {
|
|
975
1210
|
const readline = require('readline');
|
|
976
1211
|
const rl = readline.createInterface({
|
|
@@ -990,47 +1225,72 @@ async function uninstallCodeGuard() {
|
|
|
990
1225
|
}
|
|
991
1226
|
let removedCount = 0;
|
|
992
1227
|
console.log('');
|
|
993
|
-
//
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
for (const action of aiActions) {
|
|
1000
|
-
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)) {
|
|
1001
1234
|
try {
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
1006
|
-
fs.cpSync(action.filePath, destPath);
|
|
1007
|
-
console.log(` ${c.blue('📦')} Backed up ${action.relativePath}`);
|
|
1008
|
-
hasBackup = true;
|
|
1235
|
+
fs.unlinkSync(fullPath);
|
|
1236
|
+
console.log(` ${c.green('✓')} Removed ${file}`);
|
|
1237
|
+
removedCount++;
|
|
1009
1238
|
}
|
|
1010
1239
|
catch (error) {
|
|
1011
|
-
console.log(` ${c.
|
|
1240
|
+
console.log(` ${c.red('✗')} Failed to remove ${file}: ${error.message}`);
|
|
1012
1241
|
}
|
|
1013
1242
|
}
|
|
1014
1243
|
}
|
|
1015
1244
|
console.log('');
|
|
1016
1245
|
}
|
|
1017
|
-
//
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
const
|
|
1021
|
-
|
|
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);
|
|
1022
1276
|
try {
|
|
1023
|
-
|
|
1024
|
-
|
|
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}`);
|
|
1025
1284
|
removedCount++;
|
|
1026
1285
|
}
|
|
1027
1286
|
catch (error) {
|
|
1028
|
-
console.log(` ${c.red('✗')} Failed to
|
|
1287
|
+
console.log(` ${c.red('✗')} Failed to restore ${original}: ${error.message}`);
|
|
1029
1288
|
}
|
|
1030
1289
|
}
|
|
1290
|
+
console.log('');
|
|
1031
1291
|
}
|
|
1032
|
-
// Remove directories
|
|
1033
|
-
console.log(c.bold('
|
|
1292
|
+
// ── Phase 4: Remove directories ─────────────────────────────────────
|
|
1293
|
+
console.log(c.bold('Removing directories...\n'));
|
|
1034
1294
|
for (const dir of dirsToRemove) {
|
|
1035
1295
|
const fullPath = path.join(cwd, dir);
|
|
1036
1296
|
if (fs.existsSync(fullPath)) {
|
|
@@ -1044,12 +1304,11 @@ async function uninstallCodeGuard() {
|
|
|
1044
1304
|
}
|
|
1045
1305
|
}
|
|
1046
1306
|
}
|
|
1047
|
-
// Handle AI config files intelligently
|
|
1307
|
+
// ── Phase 5: Handle AI config files intelligently ───────────────────
|
|
1048
1308
|
console.log(c.bold('\nCleaning AI config files...\n'));
|
|
1049
1309
|
for (const action of aiActions) {
|
|
1050
1310
|
try {
|
|
1051
1311
|
if (action.action === 'delete') {
|
|
1052
|
-
// Entire file was created by Code Guardian → delete it
|
|
1053
1312
|
fs.unlinkSync(action.filePath);
|
|
1054
1313
|
console.log(` ${c.green('✓')} Removed ${action.relativePath}`);
|
|
1055
1314
|
removedCount++;
|
|
@@ -1060,7 +1319,6 @@ async function uninstallCodeGuard() {
|
|
|
1060
1319
|
const remaining = fs.readdirSync(parentDir);
|
|
1061
1320
|
if (remaining.length === 0) {
|
|
1062
1321
|
fs.rmdirSync(parentDir);
|
|
1063
|
-
// Check grandparent too (e.g., .windsurf/rules → .windsurf)
|
|
1064
1322
|
const grandparent = path.dirname(parentDir);
|
|
1065
1323
|
if (grandparent !== cwd && fs.existsSync(grandparent)) {
|
|
1066
1324
|
const gpRemaining = fs.readdirSync(grandparent);
|
|
@@ -1078,16 +1336,12 @@ async function uninstallCodeGuard() {
|
|
|
1078
1336
|
const markerIdx = content.indexOf(action.marker);
|
|
1079
1337
|
if (markerIdx === -1)
|
|
1080
1338
|
continue;
|
|
1081
|
-
// Find the separator (--- on its own line) before the marker
|
|
1082
|
-
// When we append, we add "\n\n---\n\n" before our content
|
|
1083
1339
|
let cutStart = markerIdx;
|
|
1084
1340
|
const beforeMarker = content.substring(0, markerIdx);
|
|
1085
|
-
// Look for the --- separator we added
|
|
1086
1341
|
const sepMatch = beforeMarker.match(/\n*\s*---\s*\n*$/);
|
|
1087
1342
|
if (sepMatch && sepMatch.index !== undefined) {
|
|
1088
1343
|
cutStart = sepMatch.index;
|
|
1089
1344
|
}
|
|
1090
|
-
// Remove from cutStart to end of file (our content is always appended at the end)
|
|
1091
1345
|
const cleaned = content.substring(0, cutStart).trimEnd() + '\n';
|
|
1092
1346
|
fs.writeFileSync(action.filePath, cleaned);
|
|
1093
1347
|
console.log(` ${c.green('✓')} Stripped Code Guardian rules from ${action.relativePath} ${c.dim('(your content preserved)')}`);
|
|
@@ -1098,7 +1352,7 @@ async function uninstallCodeGuard() {
|
|
|
1098
1352
|
console.log(` ${c.red('✗')} Failed to clean ${action.relativePath}: ${error.message}`);
|
|
1099
1353
|
}
|
|
1100
1354
|
}
|
|
1101
|
-
// Clean up .husky hooks smartly
|
|
1355
|
+
// ── Phase 6: Clean up .husky hooks smartly ──────────────────────────
|
|
1102
1356
|
console.log(c.bold('\nCleaning git hooks...\n'));
|
|
1103
1357
|
const huskyDir = path.join(cwd, '.husky');
|
|
1104
1358
|
const huskyHookFiles = ['pre-commit'];
|
|
@@ -1117,7 +1371,6 @@ async function uninstallCodeGuard() {
|
|
|
1117
1371
|
continue;
|
|
1118
1372
|
const beforeHook = content.substring(0, startIdx).trimEnd();
|
|
1119
1373
|
const afterHook = content.substring(endIdx + HOOK_END.length).trimEnd();
|
|
1120
|
-
// Check if there's real user content outside of our markers
|
|
1121
1374
|
const remaining = (beforeHook.replace(/^#!\/usr\/bin\/env\s+sh\s*/, '').trim() + afterHook.trim()).trim();
|
|
1122
1375
|
if (remaining.length === 0) {
|
|
1123
1376
|
// Entire file is ours → delete it
|
|
@@ -1151,22 +1404,45 @@ async function uninstallCodeGuard() {
|
|
|
1151
1404
|
}
|
|
1152
1405
|
catch { }
|
|
1153
1406
|
}
|
|
1154
|
-
//
|
|
1407
|
+
// ── Phase 7: Clean package.json scripts ─────────────────────────────
|
|
1155
1408
|
console.log(c.bold('\nCleaning package.json scripts...\n'));
|
|
1156
1409
|
const packageJsonPath = path.join(cwd, 'package.json');
|
|
1157
1410
|
if (fs.existsSync(packageJsonPath)) {
|
|
1158
1411
|
try {
|
|
1159
1412
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
1160
|
-
let
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
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
|
+
}
|
|
1167
1443
|
}
|
|
1168
1444
|
}
|
|
1169
|
-
if (
|
|
1445
|
+
if (scriptChanged) {
|
|
1170
1446
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
1171
1447
|
console.log(` ${c.green('✓')} Updated package.json`);
|
|
1172
1448
|
}
|
|
@@ -1178,11 +1454,20 @@ async function uninstallCodeGuard() {
|
|
|
1178
1454
|
console.log(` ${c.red('✗')} Failed to clean package.json: ${error.message}`);
|
|
1179
1455
|
}
|
|
1180
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
|
+
}
|
|
1181
1469
|
console.log('');
|
|
1182
1470
|
console.log(c.green(`✅ Cleanup complete! Removed ${removedCount} item(s).`));
|
|
1183
|
-
if (hasBackup) {
|
|
1184
|
-
console.log(c.blue(`📦 Backup saved to: ${path.basename(backupDir)}/`));
|
|
1185
|
-
}
|
|
1186
1471
|
console.log('');
|
|
1187
1472
|
console.log(c.dim('To completely remove the package, run:'));
|
|
1188
1473
|
console.log(c.dim(' npm uninstall @hatem427/code-guard-ci'));
|