// Planu — .claude/rules/ auditor for SPEC-092 / SPEC-255
import { readFile, readdir } from 'node:fs/promises';
import { homedir } from 'node:os';
import { join } from 'node:path';
import { glob } from 'glob';
const DOCS_REF = 'https://code.claude.com/docs/en/memory#rules';
async function auditRuleFile(file, rulesPath, projectPath) {
    const findings = [];
    try {
        const content = await readFile(join(rulesPath, file), 'utf-8');
        const firstLines = content.split('\n').slice(0, 5).join('\n');
        const hasPaths = /^paths:/m.test(firstLines) || /^---[\s\S]*?paths:/m.test(firstLines);
        if (!hasPaths) {
            const lc = content.toLowerCase();
            const isScoped = lc.includes('typescript') ||
                lc.includes('.test.') ||
                lc.includes('vitest') ||
                lc.includes('jest') ||
                lc.includes('.tsx') ||
                lc.includes('.jsx');
            findings.push(isScoped
                ? {
                    area: 'rules',
                    severity: 'warn',
                    message: `Rule "${file}" has language/test-specific content but no paths: frontmatter`,
                    suggestion: `Add "paths: src/**/*.ts" or similar frontmatter so it only loads for matching files`,
                    docsRef: DOCS_REF,
                }
                : {
                    area: 'rules',
                    severity: 'info',
                    code: 'RULE_MISSING_PATHS',
                    message: `Rule "${file}" has no paths: frontmatter — it loads for every file`,
                    suggestion: 'Add paths: frontmatter (e.g., "paths: src/**/*.ts") to scope when this rule loads',
                    docsRef: DOCS_REF,
                });
        }
        else if (projectPath !== undefined) {
            const pathsMatch = /^---[\s\S]*?^paths:\s*(.+)$/m.exec(content);
            if (pathsMatch) {
                const patterns = (pathsMatch[1]?.trim() ?? '')
                    .split(',')
                    .map((p) => p.trim())
                    .filter((p) => p.length > 0);
                for (const pattern of patterns) {
                    try {
                        const matches = await glob(pattern, { cwd: projectPath });
                        if (matches.length === 0) {
                            findings.push({
                                area: 'rules',
                                severity: 'info',
                                code: 'RULE_DEAD_PATH',
                                message: `Rule "${file}" has paths: "${pattern}" but no files match in project`,
                                suggestion: 'Update the paths: pattern to match existing files, or remove the rule if obsolete',
                                docsRef: DOCS_REF,
                            });
                        }
                    }
                    catch {
                        /* invalid glob — skip */
                    }
                }
            }
        }
    }
    catch {
        /* unreadable file — skip */
    }
    return findings;
}
async function auditRulesDir(rulesPath, label, projectPath) {
    let files;
    try {
        const entries = await readdir(rulesPath);
        files = entries.filter((f) => f.endsWith('.md'));
    }
    catch {
        return [
            {
                area: 'rules',
                severity: 'info',
                message: `${label} directory does not exist`,
                suggestion: 'Create .claude/rules/ with scoped rule files to keep CLAUDE.md lean',
                docsRef: DOCS_REF,
            },
        ];
    }
    if (files.length === 0) {
        return [
            {
                area: 'rules',
                severity: 'info',
                message: `${label} directory exists but has no .md files`,
                suggestion: 'Add rule files with paths: frontmatter for context-aware loading',
                docsRef: DOCS_REF,
            },
        ];
    }
    const results = await Promise.all(files.map((f) => auditRuleFile(f, rulesPath, projectPath)));
    return results.flat();
}
export async function auditRules(projectPath, includeGlobal) {
    const findings = [];
    findings.push(...(await auditRulesDir(join(projectPath, '.claude', 'rules'), 'Project .claude/rules/', projectPath)));
    if (includeGlobal) {
        findings.push(...(await auditRulesDir(join(homedir(), '.claude', 'rules'), 'Global ~/.claude/rules/')));
    }
    return findings;
}
//# sourceMappingURL=rules-auditor.js.map