UNPKG

@felixgeelhaar/cclint

Version:

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

96 lines 3.15 kB
import { ContextFile } from './ContextFile.js'; import { LintingResult } from './LintingResult.js'; import { Violation } from './Violation.js'; /** * Core linting engine that aggregates rules and runs validation. * * @remarks * The RulesEngine follows the hexagonal architecture pattern, residing in the * domain layer. It coordinates rule execution and aggregates violations into * a {@link LintingResult}. * * @example * ```typescript * const rules = [ * new FileSizeRule(10000), * new StructureRule(), * new FormatRule() * ]; * * const engine = new RulesEngine(rules); * const file = new ContextFile('CLAUDE.md', readFileSync('CLAUDE.md', 'utf-8')); * const result = engine.lint(file); * * console.log(`Errors: ${result.errorCount}`); * ``` * * @category Domain */ export class RulesEngine { _rules = new Map(); _severityOverrides; /** * @param rules - the rules to run, in order * @param severityOverrides - optional per-rule severity (by rule id). When a * rule has an override, ALL of its violations are re-emitted at that * severity — the standard linter model where a rule is configured to one * level (error/warning/info). This is how `config.rules.<id>.severity` * takes effect. */ constructor(rules, severityOverrides) { for (const rule of rules) { if (this._rules.has(rule.id)) { throw new Error(`Duplicate rule ID: ${rule.id}`); } this._rules.set(rule.id, rule); } this._severityOverrides = severityOverrides ?? new Map(); } get rules() { return Array.from(this._rules.values()); } /** * Lint a context file using all registered rules. * * @param file - The {@link ContextFile} to validate * @returns A {@link LintingResult} containing all violations found * * @remarks * Rules are executed in the order they were registered. Each rule's violations * are aggregated into a single result object. */ lint(file) { const result = new LintingResult(file); for (const rule of this._rules.values()) { if (rule.appliesTo && !rule.appliesTo(file)) { continue; } const override = this._severityOverrides.get(rule.id); for (const violation of rule.lint(file)) { result.addViolation(override ? new Violation(violation.ruleId, violation.message, override, violation.location, violation.fix) : violation); } } return result; } /** * Retrieve a rule by its ID. * * @param ruleId - The unique identifier of the rule * @returns The rule if found, undefined otherwise */ getRuleById(ruleId) { return this._rules.get(ruleId); } /** * Check if a rule is registered. * * @param ruleId - The unique identifier of the rule * @returns True if the rule is registered */ hasRule(ruleId) { return this._rules.has(ruleId); } } //# sourceMappingURL=RulesEngine.js.map