UNPKG

@felixgeelhaar/cclint

Version:

Catch CLAUDE.md drift before Claude misbehaves. Lints CLAUDE.md, skills, subagents, and hooks for Claude Code projects.

206 lines 6.58 kB
import { Command } from 'commander'; import { getRuleMetadata, getAllRuleIds, isValidRule, } from '../../infrastructure/RuleMetadata.js'; /** * Format rule metadata for display */ function formatRuleDetails(rule) { const lines = []; // Header lines.push(''); lines.push('━'.repeat(60)); lines.push(`📋 ${rule.name} (${rule.id})`); lines.push('━'.repeat(60)); lines.push(''); // Description and rationale lines.push(`📝 Description:`); lines.push(` ${rule.description}`); lines.push(''); lines.push(`💡 Rationale:`); wrapText(rule.rationale, 55).forEach(line => { lines.push(` ${line}`); }); lines.push(''); // Properties lines.push(`⚙️ Properties:`); lines.push(` Severity: ${rule.defaultSeverity.toUpperCase()}`); lines.push(` Fixable: ${rule.fixable ? '✅ Yes' : '❌ No'}`); lines.push(''); // Bad examples if (rule.badExamples.length > 0) { lines.push(`❌ Bad Examples:`); rule.badExamples.forEach((example, idx) => { lines.push(` ${idx + 1}. ${example.explanation}`); lines.push(' ```'); example.code.split('\\n').forEach(codeLine => { lines.push(` ${codeLine}`); }); lines.push(' ```'); }); lines.push(''); } // Good examples if (rule.goodExamples.length > 0) { lines.push(`✅ Good Examples:`); rule.goodExamples.forEach((example, idx) => { lines.push(` ${idx + 1}. ${example.explanation}`); lines.push(' ```'); example.code.split('\\n').forEach(codeLine => { lines.push(` ${codeLine}`); }); lines.push(' ```'); }); lines.push(''); } // Options if (rule.options && rule.options.length > 0) { lines.push(`🔧 Configuration Options:`); rule.options.forEach(option => { lines.push(` ${option.name} (${option.type})`); lines.push(` Default: ${option.default}`); lines.push(` ${option.description}`); }); lines.push(''); } // Related rules if (rule.related && rule.related.length > 0) { lines.push(`🔗 Related Rules: ${rule.related.join(', ')}`); lines.push(''); } // References if (rule.references && rule.references.length > 0) { lines.push(`📚 References:`); rule.references.forEach(ref => { lines.push(` • ${ref}`); }); lines.push(''); } lines.push('━'.repeat(60)); return lines.join('\n'); } /** * List all available rules */ function formatRuleList() { const lines = []; lines.push(''); lines.push('━'.repeat(60)); lines.push('📋 Available Rules'); lines.push('━'.repeat(60)); lines.push(''); const ruleIds = getAllRuleIds(); // Group rules by severity const errorRules = []; const warningRules = []; const infoRules = []; ruleIds.forEach(id => { const rule = getRuleMetadata(id); if (rule) { switch (rule.defaultSeverity) { case 'error': errorRules.push(id); break; case 'warning': warningRules.push(id); break; case 'info': infoRules.push(id); break; } } }); if (errorRules.length > 0) { lines.push('🔴 Error Rules:'); errorRules.forEach(id => { const rule = getRuleMetadata(id); if (rule) { const fixable = rule.fixable ? ' [fixable]' : ''; lines.push(` • ${id.padEnd(25)} ${rule.name}${fixable}`); } }); lines.push(''); } if (warningRules.length > 0) { lines.push('🟡 Warning Rules:'); warningRules.forEach(id => { const rule = getRuleMetadata(id); if (rule) { const fixable = rule.fixable ? ' [fixable]' : ''; lines.push(` • ${id.padEnd(25)} ${rule.name}${fixable}`); } }); lines.push(''); } if (infoRules.length > 0) { lines.push('🔵 Info Rules:'); infoRules.forEach(id => { const rule = getRuleMetadata(id); if (rule) { const fixable = rule.fixable ? ' [fixable]' : ''; lines.push(` • ${id.padEnd(25)} ${rule.name}${fixable}`); } }); lines.push(''); } lines.push(`Total: ${ruleIds.length} rules`); lines.push(''); lines.push('Run `cclint explain <rule-id>` for detailed information.'); lines.push('━'.repeat(60)); return lines.join('\n'); } /** * Wrap text to specified width */ function wrapText(text, width) { const words = text.split(' '); const lines = []; let currentLine = ''; words.forEach(word => { if (currentLine.length + word.length + 1 <= width) { currentLine += (currentLine ? ' ' : '') + word; } else { if (currentLine) lines.push(currentLine); currentLine = word; } }); if (currentLine) lines.push(currentLine); return lines; } export const explainCommand = new Command('explain') .description('Explain a linting rule in detail') .argument('[rule-id]', 'The rule ID to explain (omit to list all rules)') .option('--json', 'Output as JSON') .action((ruleId, options) => { if (!ruleId) { // List all rules if (options.json) { const ruleIds = getAllRuleIds(); const rules = ruleIds.map(id => getRuleMetadata(id)); console.log(JSON.stringify(rules, null, 2)); } else { console.log(formatRuleList()); } return; } // Explain specific rule if (!isValidRule(ruleId)) { console.error(`Error: Unknown rule "${ruleId}".`); console.error(`Run 'cclint explain' to see all available rules.`); process.exit(1); } const rule = getRuleMetadata(ruleId); if (!rule) { console.error(`Error: Could not load metadata for rule "${ruleId}".`); process.exit(1); } if (options.json) { console.log(JSON.stringify(rule, null, 2)); } else { console.log(formatRuleDetails(rule)); } }); //# sourceMappingURL=explain.js.map