"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.driftRules = void 0;
function diag(rule, filePath, message, opts = {}) {
    return {
        ruleId: rule.id,
        filePath,
        line: opts.line,
        severity: rule.severity,
        message,
        help: opts.help || rule.help,
        autoFixable: opts.autoFixable ?? false,
    };
}
const PROJECT_TYPES = ["nextjs", "turbo", "node"];
const REQUIRED_CONTEXT_KEYS = ["prd", "features", "specs", "metadata", "brain"];
const EXPECTED_BRAIN_VERSION = "1.x";
function getContextValidationMessage(brain) {
    if (!brain || typeof brain !== "object") {
        return {
            valid: false,
            message: "Living Brain is invalid JSON object",
            help: `Expected keys: ${REQUIRED_CONTEXT_KEYS.join(", ")}. Run 'mycontext sync' to regenerate.`,
        };
    }
    const missing = REQUIRED_CONTEXT_KEYS.filter((k) => !(k in brain));
    if (missing.length > 0) {
        return {
            valid: false,
            message: `Living Brain is missing required keys: ${missing.join(", ")}`,
            help: `Expected schema version: ${EXPECTED_BRAIN_VERSION}. Minimal shape: {"prd":{},"features":[],"specs":{},"metadata":{"version":"1.0.0"},"brain":{"version":"1.0.0"}}`,
        };
    }
    return { valid: true };
}
const contextExists = {
    id: "drift/context-exists",
    name: "Context Exists",
    category: "node",
    severity: "error",
    description: "Verify that .mycontext/context.json exists and is valid",
    help: "Run 'mycontext sync' to generate or repair the Living Brain",
    appliesTo: PROJECT_TYPES,
    async check(ctx) {
        const results = [];
        const brainPath = ".mycontext/context.json";
        if (!(await ctx.fileExists(brainPath))) {
            results.push(diag(this, brainPath, "Living Brain (context.json) is missing", { autoFixable: true }));
        }
        else {
            const brain = await ctx.readJson(brainPath);
            const validation = getContextValidationMessage(brain);
            if (!validation.valid) {
                results.push(diag(this, brainPath, validation.message || "Living Brain is corrupted or incomplete", { help: validation.help, autoFixable: true }));
            }
        }
        return results;
    },
    async fix(ctx, diag) {
        const defaultContext = {
            prd: { title: ctx.project.name },
            features: [],
            specs: {},
            metadata: { version: "1.0.0", generatedAt: new Date().toISOString() },
            brain: { version: "1.0.0", status: "initialized" }
        };
        if (await ctx.fileExists(".mycontext/context.json")) {
            const existing = await ctx.readJson(".mycontext/context.json") || {};
            const merged = { ...defaultContext, ...existing };
            return await ctx.writeFile(".mycontext/context.json", JSON.stringify(merged, null, 2));
        }
        return await ctx.writeFile(".mycontext/context.json", JSON.stringify(defaultContext, null, 2));
    }
};
const featureDrift = {
    id: "drift/feature-drift",
    name: "Feature Drift",
    category: "node",
    severity: "warning",
    description: "Detect features in context.json that have no corresponding code",
    help: "Implement the missing feature or remove it from context.json if abandoned",
    appliesTo: PROJECT_TYPES,
    async check(ctx) {
        const results = [];
        const brain = await ctx.readJson(".mycontext/context.json");
        if (!brain || !brain.features || !Array.isArray(brain.features))
            return results;
        const allFiles = await ctx.findFiles(/\..*$/);
        for (const feature of brain.features) {
            // 1. Check explicit paths if provided
            if (feature.paths && Array.isArray(feature.paths)) {
                let pathFound = false;
                for (const p of feature.paths) {
                    if (await ctx.fileExists(p)) {
                        pathFound = true;
                        break;
                    }
                }
                if (!pathFound) {
                    results.push(diag(this, ".mycontext/context.json", `Feature "${feature.name}" missing implementation at specified paths: ${feature.paths.join(", ")}`));
                }
                continue;
            }
            // 2. Fallback to slug heuristic
            const featureName = feature.name.toLowerCase().replace(/\s+/g, "-");
            const hasCode = allFiles.some(f => f.toLowerCase().includes(`/${featureName}/`) ||
                f.toLowerCase().includes(`${featureName}.`));
            if (!hasCode) {
                results.push(diag(this, ".mycontext/context.json", `Feature "${feature.name}" has no corresponding implementation code`));
            }
        }
        return results;
    },
};
const groundingQuality = {
    id: "drift/grounding-quality",
    name: "Grounding Quality",
    category: "node",
    severity: "warning",
    description: "Verify that the Living Brain is grounded in actual project documentation",
    help: "Run 'mycontext init --yes' to re-initialize and capture documentation context (README, etc.)",
    appliesTo: PROJECT_TYPES,
    async check(ctx) {
        const results = [];
        const brain = await ctx.readJson(".mycontext/context.json");
        if (!brain || !brain.prd)
            return results;
        const corePurpose = brain.prd.problemStatement || "";
        // Check if grounding header is missing
        if (corePurpose.includes("Stats:") && !corePurpose.includes("GROUNDING DOC")) {
            results.push(diag(this, ".mycontext/context.json", "Living Brain is un-grounded (missing project documentation context)", {
                help: "The current brain was initialized purely from file statistics. Re-run 'mycontext init' on your project to ground it in your README and docs."
            }));
        }
        return results;
    },
};
exports.driftRules = [
    contextExists,
    featureDrift,
    groundingQuality,
];
//# sourceMappingURL=drift-rules.js.map