Claude-skill-registry config-hygiene
Fixes configuration hygiene issues including gitignore patterns, ESLint config duplication, and hook scripts. Use when encountering backup files in repo, missing gitignore patterns, or config maintenance issues.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/config-hygiene" ~/.claude/skills/majiayu000-claude-skill-registry-config-hygiene && rm -rf "$T"
skills/data/config-hygiene/SKILL.mdConfiguration Hygiene Fixes
Fixes for project configuration issues. Clean configs prevent noise, ensure consistency, and improve developer experience.
Quick Start
- Identify the config issue (gitignore, ESLint, husky)
- Apply the recommended fix
- Verify no unintended side effects
- Commit config changes
Priority Matrix
| Issue | Priority | Impact |
|---|---|---|
| Backup files in repo | P2 | Noise, confusion |
| Missing gitignore patterns | P2 | Unwanted files tracked |
| Husky silent failure | P2 | Hidden hook failures |
| ESLint config duplication | P3 | Maintenance burden |
| Permissive lint threshold | P2 | Quality regression |
Workflows
Backup/Artifact Files in Repository (#35 - 12 occurrences)
Detection: Files matching
*.bak, *.orig, *~ patterns.
Pattern: Editor/merge artifacts committed to repo.
Fix Strategy: Remove from git, add to gitignore.
# Remove from git (keep locally) git rm --cached "*.bak" git rm --cached "*.orig" git rm --cached "*~" # Add to .gitignore echo "*.bak" >> .gitignore echo "*.orig" >> .gitignore echo "*~" >> .gitignore # Commit git add .gitignore git commit -m "chore: ignore backup/artifact files"
Missing .gitignore Patterns (#36 - 8 occurrences)
Detection: Common patterns not in .gitignore.
Fix Strategy: Add comprehensive patterns.
# Editor artifacts *.swp *.swo *~ *.bak *.orig # OS files .DS_Store Thumbs.db # IDE .idea/ .vscode/ *.sublime-* # Build outputs dist/ build/ *.tsbuildinfo # Dependencies node_modules/ # Environment .env .env.* !.env.example # Logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Test coverage coverage/ .nyc_output/ # Cache .cache/ .eslintcache .parcel-cache/
Template: See
templates/gitignore-node.txt for complete Node.js template.
Husky Prepare Script Silent Failure (#37 - 1 occurrence)
Detection:
prepare script uses || true fallback.
Pattern: Hook installation fails silently.
// PROBLEM - hides failures { "scripts": { "prepare": "husky install || true" } }
Fix Strategy 1: Conditional execution based on CI.
// SOLUTION - check CI environment { "scripts": { "prepare": "node -e \"if (process.env.CI !== 'true') require('husky').install()\"" } }
Fix Strategy 2: Use is-ci package.
npm install --save-dev is-ci
{ "scripts": { "prepare": "is-ci || husky install" } }
Fix Strategy 3: Husky v9+ auto-setup.
// Husky v9 uses .husky/_/husky.sh automatically { "scripts": { "prepare": "husky" } }
Permissive lint:strict Threshold (#38 - 1 occurrence)
Detection: High
--max-warnings value in lint script.
Pattern: Too many warnings allowed.
// PROBLEM - too permissive { "scripts": { "lint:strict": "eslint . --max-warnings 500" } }
Fix Strategy: Progressive reduction.
// Week 1: Lower to current count + small buffer { "scripts": { "lint:strict": "eslint . --max-warnings 350" } } // Week 2: Lower further { "scripts": { "lint:strict": "eslint . --max-warnings 200" } } // Target: Zero tolerance { "scripts": { "lint:strict": "eslint . --max-warnings 0" } }
Tracking Progress:
# Get current warning count npx eslint . 2>&1 | tail -1 # Output: "X warnings" or "X problems (Y errors, Z warnings)"
ESLint Config Duplication (#39, #40)
Issue #39: Repeated plugin/globals definitions.
// PROBLEM - repeated in multiple blocks export default [ { files: ['**/*.ts'], plugins: { '@typescript-eslint': tsPlugin }, }, { files: ['**/*.tsx'], plugins: { '@typescript-eslint': tsPlugin }, // Duplicated }, ];
Fix Strategy: Extract shared config.
// SOLUTION - shared base config const baseTypescriptConfig = { plugins: { '@typescript-eslint': tsPlugin, }, languageOptions: { parser: tsParser, parserOptions: { project: './tsconfig.json', }, }, }; export default [ { files: ['**/*.ts'], ...baseTypescriptConfig, rules: { /* ts-specific rules */ }, }, { files: ['**/*.tsx'], ...baseTypescriptConfig, rules: { /* tsx-specific rules */ }, }, ];
Issue #40: Disabled rules duplicated across blocks.
// PROBLEM - duplicated disabled rules { rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', }, },
Fix Strategy: Extract to named constant with documentation.
// SOLUTION - extracted with docs /** * Rules disabled during migration to strict TypeScript. * TODO: Re-enable progressively - see issue #456 */ const migrationDisabledRules = { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', } as const; export default [ { files: ['**/*.ts'], rules: { ...migrationDisabledRules, // ts-specific rules }, }, ];
Recursive Directory Walking Without Limits (#23 - 1 occurrence)
Pattern: Unbounded recursion in file walking.
// PROBLEM - no limits async function walkDir(dir: string): Promise<string[]> { const entries = await fs.readdir(dir, { withFileTypes: true }); const files: string[] = []; for (const entry of entries) { if (entry.isDirectory()) { files.push(...await walkDir(path.join(dir, entry.name))); // No limit! } } return files; }
Fix Strategy: Add depth limit and visited tracking.
// SOLUTION - with limits interface WalkOptions { maxDepth?: number; maxFiles?: number; } async function walkDir( dir: string, options: WalkOptions = {}, depth = 0, visited = new Set<string>() ): Promise<string[]> { const { maxDepth = 10, maxFiles = 10000 } = options; if (depth > maxDepth) { return []; } const realPath = await fs.realpath(dir); if (visited.has(realPath)) { return []; // Circular symlink } visited.add(realPath); const entries = await fs.readdir(dir, { withFileTypes: true }); const files: string[] = []; for (const entry of entries) { if (files.length >= maxFiles) break; const fullPath = path.join(dir, entry.name); if (entry.isDirectory()) { files.push(...await walkDir(fullPath, options, depth + 1, visited)); } else { files.push(fullPath); } } return files; }
Regex Created Per-Match (#24 - 1 occurrence)
Pattern: Regex compiled on every function call.
// PROBLEM - regex created every call function containsKeyword(text: string, keyword: string): boolean { const regex = new RegExp(`\\b${keyword}\\b`, 'i'); return regex.test(text); }
Fix Strategy: Pre-compile and cache.
// SOLUTION - cached regex const keywordRegexCache = new Map<string, RegExp>(); function getKeywordRegex(keyword: string): RegExp { let regex = keywordRegexCache.get(keyword); if (!regex) { regex = new RegExp(`\\b${escapeRegex(keyword)}\\b`, 'i'); keywordRegexCache.set(keyword, regex); } return regex; } function escapeRegex(str: string): string { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } function containsKeyword(text: string, keyword: string): boolean { return getKeywordRegex(keyword).test(text); }
Scripts
Check Config Hygiene
node scripts/check-config.js
Fix Gitignore
node scripts/fix-gitignore.js
Templates
Node.js .gitignore
See
templates/gitignore-node.txt
ESLint Base Config
See
templates/eslint-base.js
CI/CD Considerations
Ensuring Hooks Run
# GitHub Actions - name: Install dependencies run: npm ci # This runs "prepare" script which installs husky # Make sure CI=true is set to skip if needed
Lint in CI
- name: Lint run: npm run lint:strict # Fails if warnings exceed threshold
Progressive Strictness
# Allow gradual improvement - name: Check lint progress run: | WARNINGS=$(npx eslint . 2>&1 | grep -oP '\d+ warning' | grep -oP '\d+' || echo "0") THRESHOLD=200 if [ "$WARNINGS" -gt "$THRESHOLD" ]; then echo "Too many warnings: $WARNINGS (max: $THRESHOLD)" exit 1 fi