"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectScanner = void 0;
/**
 * ProjectScanner — Scans the real file structure and compares against context.json
 *
 * This is the "eyes" of the Living Brain:
 * 1. Walks the actual project file tree
 * 2. Reads key files (package.json, route files, components, schemas)
 * 3. Builds a structured ProjectSnapshot
 * 4. Passes snapshot + context.json to LLM for progress assessment
 * 5. Returns a ContextDiffReport with suggested merges/updates
 */
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const child_process_1 = require("child_process");
const AICore_1 = require("../core/ai/AICore");
// --- Scanner ---
const IGNORE_DIRS = new Set([
    "node_modules", ".next", ".git", "dist", "build", ".turbo",
    ".pnpm-store", "coverage", ".cache", ".vercel", "__pycache__",
    "storybook-static", "out", ".expo", ".server", ".client",
    ".mycontext",
]);
const IGNORE_EXTENSIONS = new Set([
    ".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".woff", ".woff2",
    ".ttf", ".eot", ".mp4", ".webm", ".mp3", ".wav", ".pdf", ".lock",
    ".map", ".webp", ".avif", ".bmp", ".tiff",
]);
// --- Priority-Based Scanning ---
// Tier 1: Core app structure — defines routing & architecture
const PRIORITY_DIRS_TIER1 = [
    "app", "src/app", "pages", "src/pages",
];
// Tier 2: Design system, business logic, shared code
const PRIORITY_DIRS_TIER2 = [
    "components", "src/components",
    "lib", "src/lib",
    "hooks", "src/hooks",
    "types", "src/types",
    "services", "src/services",
    "providers", "src/providers",
    "features", "src/features",
    "styles", "src/styles",
    "actions", "src/actions",
    "utils", "src/utils",
    "dictionaries",
    "docs", "documentation", "spec", "requirements",
];
// Root-level files to always read (no walk needed)
const ROOT_KEY_FILES = [
    "package.json", "tsconfig.json", "components.json",
    "next.config.js", "next.config.ts", "next.config.mjs",
    "tailwind.config.ts", "tailwind.config.js", "tailwind.config.mjs",
    "instant.schema.ts", "instant.perms.ts",
    ".env.example",
    "pnpm-workspace.yaml",
    "README.md", "readme.md",
    "middleware.ts", "middleware.js",
    "src/middleware.ts", "src/middleware.js",
    "app/globals.css", "src/app/globals.css", "styles/globals.css",
    "app/layout.tsx", "src/app/layout.tsx",
    "app/page.tsx", "src/app/page.tsx",
    "lib/utils.ts", "src/lib/utils.ts",
];
// Key file patterns for files found during walk (more selective than before)
const KEY_FILE_PATTERNS = [
    // Markdown docs (Highest priority for grounding)
    /\.md$/i,
    // Config & schema (always valuable)
    /package\.json$/,
    /tsconfig\.json$/,
    /components\.json$/,
    /instant\.schema\.(ts|js)$/,
    /instant\.perms\.(ts|js)$/,
    /schema\.(ts|js|prisma)$/,
    /\.env\.example$/,
    // Next.js routing files (architecture-defining)
    /layout\.(tsx|jsx|ts|js)$/,
    /page\.(tsx|jsx|ts|js)$/,
    /route\.(tsx|jsx|ts|js)$/,
    /loading\.(tsx|jsx)$/,
    /error\.(tsx|jsx)$/,
    /globals\.css$/,
    /middleware\.(ts|js)$/,
    // CLI-specific
    /src\/cli\.(ts|js)$/,
];
// Tier 0: Attack Surface — files most likely to contain vulnerabilities
const ATTACK_SURFACE_PATTERNS = [
    /auth/i,
    /api\//i,
    /session/i,
    /token/i,
    /crypto/i,
    /password/i,
    /encrypt|decrypt/i,
    /sign|verify/i,
    /jwt/i,
    /credential/i,
    /permission|role|access/i,
    /admin|internal/i,
    /upload|download/i, // Potential RCE/Path traversal points
    /exec|spawn|fork/i, // Potential command injection points
    /eval|Function/i, // Potential code injection points
];
// Broader patterns — only used if budget remains after essentials
const SECONDARY_KEY_FILE_PATTERNS = [
    /src\/commands\/.*\.(ts|js)$/,
    /src\/agents\/.*\.(ts|js)$/,
    /lib\/.*\.(ts|js)$/,
    /types\/.*\.(ts|js)$/,
    /hooks\/.*\.(ts|js)$/,
    /constants\/.*\.(ts|js)$/,
    /services\/.*\.(ts|js)$/,
    /providers\/.*\.(ts|js)$/,
    /dictionaries\/.*\.json$/,
];
const MAX_FILE_READ_SIZE = 4000; // chars per file
const MAX_KEY_FILES_PRIMARY = 30; // essential key files
const MAX_KEY_FILES_SECONDARY = 20; // secondary files if budget allows
const MAX_TREE_DEPTH = 8; // reasonable depth
const MAX_SCAN_FILES = 500; // stop walking after this many files
const MAX_SCAN_TIME_MS = 15000; // hard timeout at 15 seconds
class ProjectScanner {
    constructor(projectRoot = process.cwd()) {
        this.ai = null;
        /**
         * Shared state for budget-aware scanning
         */
        this.scanState = {
            startTime: 0,
            fileCount: 0,
            budgetExhausted: false,
        };
        this.projectRoot = projectRoot;
    }
    /**
     * Check if scan budget (time or file count) is exhausted
     */
    isBudgetExhausted() {
        if (this.scanState.budgetExhausted)
            return true;
        if (this.scanState.fileCount >= MAX_SCAN_FILES) {
            this.scanState.budgetExhausted = true;
            return true;
        }
        if (Date.now() - this.scanState.startTime >= MAX_SCAN_TIME_MS) {
            this.scanState.budgetExhausted = true;
            return true;
        }
        return false;
    }
    /**
     * Fast file discovery using bash commands (git or find)
     */
    discoverFilesBash(ignore) {
        try {
            // 1. Try git ls-files if it's a git repo
            try {
                const gitFiles = (0, child_process_1.execSync)("git ls-files --cached --others --exclude-standard", { cwd: this.projectRoot, stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" });
                if (gitFiles)
                    return gitFiles.trim().split("\n");
            }
            catch {
                // Not a git repo or git not found
            }
            // 2. Fallback to find command (standard Unix)
            try {
                // Find files but skip depth-heavy noise like node_modules and .git
                // We allow some hidden files (like .env.example) but skip .git/
                const findCmd = `find . -maxdepth 6 -not -path '*/node_modules*' -not -path '*/.git*'`;
                const findFiles = (0, child_process_1.execSync)(findCmd, { cwd: this.projectRoot, stdio: ["ignore", "pipe", "ignore"], encoding: "utf8" });
                if (findFiles) {
                    return findFiles.trim().split("\n")
                        .map(f => f.replace(/^\.\//, ""))
                        .filter(f => f !== "." && f !== "" && !f.startsWith(".git/"));
                }
            }
            catch {
                // find failed
            }
        }
        catch {
            // General failure
        }
        return null;
    }
    /**
     * Full scan: priority-based walk + read key files + build snapshot
     *
     * Scanning strategy:
     * 1. Read root-level config files directly (no walk needed)
     * 2. Walk Tier 1 priority dirs (app/, pages/) — defines architecture
     * 3. Walk Tier 2 priority dirs (components/, lib/, hooks/) — design system & logic
     * 4. Walk remaining top-level dirs only if budget allows
     * 5. Read key files from collected entries (primary then secondary patterns)
     */
    async scan(onProgress) {
        const fileTree = [];
        const keyFiles = [];
        const stats = {
            totalFiles: 0,
            totalDirs: 0,
            routeFiles: 0,
            componentFiles: 0,
            apiFiles: 0,
            schemaFiles: 0,
            testFiles: 0,
            i18nFiles: 0,
            securitySurface: 0,
        };
        // Initialize scan budget
        this.scanState = { startTime: Date.now(), fileCount: 0, budgetExhausted: false };
        // Load scope and ignore from config if available
        let scope;
        let ignore;
        try {
            const configPath = path.join(this.projectRoot, ".mycontext", "config.json");
            if (fs.existsSync(configPath)) {
                const config = fs.readJsonSync(configPath);
                scope = config.scope;
                ignore = config.ignore;
            }
        }
        catch (e) { }
        // --- Phase 0: Read root-level key files directly (instant, no walk) ---
        for (const rootFile of ROOT_KEY_FILES) {
            const fullPath = path.join(this.projectRoot, rootFile);
            try {
                const stat = await fs.stat(fullPath);
                if (stat.isFile()) {
                    const ext = path.extname(rootFile);
                    fileTree.push({ path: rootFile, type: "file", size: stat.size, extension: ext });
                    stats.totalFiles++;
                    this.scanState.fileCount++;
                    if (onProgress)
                        onProgress(`Reading config: ${rootFile}`);
                    // Always read root key files as snippets
                    const snippet = await this.readFileSnippet(rootFile);
                    if (snippet)
                        keyFiles.push(snippet);
                }
            }
            catch {
                // File doesn't exist, skip
            }
        }
        // Track which dirs we've already walked to avoid duplicates
        const walkedDirs = new Set();
        // --- Phase 1: File Discovery (Bash-Accelerated) ---
        const bashFiles = this.discoverFilesBash(ignore);
        let discoveredFiles = [];
        if (bashFiles && bashFiles.length > 0) {
            if (onProgress)
                onProgress(`Bash-accelerated discovery found ${bashFiles.length} files...`);
            // Filter for scope if present
            let filteredFiles = bashFiles;
            if (scope && scope.length > 0) {
                filteredFiles = bashFiles.filter(f => scope.some(s => f === s || f.startsWith(s + "/")));
            }
            // Prioritize files based on Tiers
            const tier1 = filteredFiles.filter(f => PRIORITY_DIRS_TIER1.some(d => f === d || f.startsWith(d + "/")));
            const tier2 = filteredFiles.filter(f => PRIORITY_DIRS_TIER2.some(d => f === d || f.startsWith(d + "/")));
            const rest = filteredFiles.filter(f => !tier1.includes(f) && !tier2.includes(f));
            discoveredFiles = [...tier1, ...tier2, ...rest];
            // Convert to fileTree with minimal stats (lazy stat later if needed)
            const uniqueDirs = new Set();
            for (const relPath of discoveredFiles) {
                if (this.isBudgetExhausted())
                    break;
                const fileName = path.basename(relPath);
                const dirName = path.dirname(relPath);
                if (dirName !== ".") {
                    // Add all parent directories to the count
                    const parts = dirName.split("/");
                    let current = "";
                    for (const part of parts) {
                        current = current ? `${current}/${part}` : part;
                        uniqueDirs.add(current);
                    }
                }
                const ext = path.extname(relPath);
                fileTree.push({
                    path: relPath,
                    type: "file",
                    extension: ext
                });
                stats.totalFiles++;
                this.scanState.fileCount++;
                // Categorize file for stats
                this.categorizeFile(fileName, relPath, stats);
            }
            stats.totalDirs = uniqueDirs.size;
        }
        else {
            // Fallback: Manual priority-based walk (Tiered)
            if (scope && scope.length > 0) {
                for (const s of scope) {
                    if (!this.isBudgetExhausted()) {
                        if (onProgress)
                            onProgress(`Scanning scope: ${s}`);
                        await this.walkDirectory(s, fileTree, stats, 0, ignore, onProgress);
                        walkedDirs.add(s);
                    }
                }
            }
            else {
                // --- Manual Phase 1: Tier 1 priority dirs ---
                for (const dir of PRIORITY_DIRS_TIER1) {
                    if (this.isBudgetExhausted())
                        break;
                    const fullPath = path.join(this.projectRoot, dir);
                    try {
                        const stat = await fs.stat(fullPath);
                        if (stat.isDirectory()) {
                            if (onProgress)
                                onProgress(`Scanning ${dir}/...`);
                            await this.walkDirectory(dir, fileTree, stats, 1, ignore, onProgress);
                            walkedDirs.add(dir);
                            const parent = path.dirname(dir);
                            if (parent !== ".")
                                walkedDirs.add(parent);
                        }
                    }
                    catch { }
                }
                // --- Manual Phase 2: Tier 2 priority dirs ---
                for (const dir of PRIORITY_DIRS_TIER2) {
                    if (this.isBudgetExhausted())
                        break;
                    const fullPath = path.join(this.projectRoot, dir);
                    try {
                        const stat = await fs.stat(fullPath);
                        if (stat.isDirectory()) {
                            if (onProgress)
                                onProgress(`Scanning ${dir}/...`);
                            await this.walkDirectory(dir, fileTree, stats, 1, ignore, onProgress);
                            walkedDirs.add(dir);
                        }
                    }
                    catch { }
                }
                // --- Manual Phase 3: Remaining top-level dirs ---
                if (!this.isBudgetExhausted()) {
                    try {
                        const topLevelItems = await fs.readdir(this.projectRoot);
                        for (const item of topLevelItems) {
                            if (this.isBudgetExhausted())
                                break;
                            if (IGNORE_DIRS.has(item) || item.startsWith(".") || walkedDirs.has(item))
                                continue;
                            const itemFullPath = path.join(this.projectRoot, item);
                            try {
                                const stat = await fs.stat(itemFullPath);
                                if (stat.isDirectory()) {
                                    if (onProgress)
                                        onProgress(`Scanning ${item}/...`);
                                    await this.walkDirectory(item, fileTree, stats, 1, ignore, onProgress);
                                    walkedDirs.add(item);
                                }
                            }
                            catch { }
                        }
                    }
                    catch { }
                }
            }
        }
        // --- Phase 4: Collect key files from walked entries (primary patterns) ---
        // Root key files were already read in Phase 0, now read key files found during walk
        const alreadyRead = new Set(keyFiles.map(k => k.path));
        for (const entry of fileTree) {
            if (entry.type !== "file")
                continue;
            if (alreadyRead.has(entry.path))
                continue;
            if (keyFiles.length >= MAX_KEY_FILES_PRIMARY)
                break;
            const isKeyFile = KEY_FILE_PATTERNS.some((p) => p.test(entry.path));
            if (isKeyFile) {
                if (onProgress)
                    onProgress(`Analyzing: ${entry.path.split('/').pop()}`);
                const snippet = await this.readFileSnippet(entry.path);
                if (snippet) {
                    keyFiles.push(snippet);
                    alreadyRead.add(entry.path);
                }
            }
        }
        // --- Phase 5: Read secondary key files if budget allows ---
        if (keyFiles.length < MAX_KEY_FILES_PRIMARY + MAX_KEY_FILES_SECONDARY) {
            for (const entry of fileTree) {
                if (entry.type !== "file")
                    continue;
                if (alreadyRead.has(entry.path))
                    continue;
                if (keyFiles.length >= MAX_KEY_FILES_PRIMARY + MAX_KEY_FILES_SECONDARY)
                    break;
                const isSecondary = SECONDARY_KEY_FILE_PATTERNS.some((p) => p.test(entry.path));
                if (isSecondary) {
                    if (onProgress)
                        onProgress(`Analyzing: ${entry.path.split('/').pop()}`);
                    const snippet = await this.readFileSnippet(entry.path);
                    if (snippet) {
                        keyFiles.push(snippet);
                        alreadyRead.add(entry.path);
                    }
                }
            }
        }
        return {
            scannedAt: new Date().toISOString(),
            projectRoot: this.projectRoot,
            fileTree,
            keyFiles,
            stats,
        };
    }
    /**
     * Recursively walk a directory with budget-aware early termination
     */
    async walkDirectory(relativePath, entries, stats, depth, ignorePatterns, onProgress) {
        if (depth > MAX_TREE_DEPTH)
            return;
        if (this.isBudgetExhausted())
            return;
        const fullPath = path.join(this.projectRoot, relativePath);
        let items;
        try {
            items = await fs.readdir(fullPath);
        }
        catch {
            return;
        }
        for (const item of items) {
            if (this.isBudgetExhausted())
                return;
            const itemRelPath = relativePath ? `${relativePath}/${item}` : item;
            // Check ignore patterns
            if (ignorePatterns && ignorePatterns.some(p => itemRelPath.includes(p) || item.includes(p)))
                continue;
            if (IGNORE_DIRS.has(item))
                continue;
            if (item.startsWith(".") && item !== ".env.example")
                continue;
            const itemFullPath = path.join(this.projectRoot, itemRelPath);
            let stat;
            try {
                stat = await fs.stat(itemFullPath);
            }
            catch {
                continue;
            }
            if (stat.isDirectory()) {
                stats.totalDirs++;
                entries.push({ path: itemRelPath, type: "dir" });
                await this.walkDirectory(itemRelPath, entries, stats, depth + 1, ignorePatterns);
            }
            else if (stat.isFile()) {
                const ext = path.extname(item);
                if (IGNORE_EXTENSIONS.has(ext))
                    continue;
                stats.totalFiles++;
                this.scanState.fileCount++;
                entries.push({
                    path: itemRelPath,
                    type: "file",
                    size: stat.size,
                    extension: ext,
                });
                // Periodic heartbeat for budget/progress
                if (this.scanState.fileCount % 20 === 0) {
                    if (onProgress)
                        onProgress(`Scanning: ${relativePath || item}`);
                }
                // Categorize
                this.categorizeFile(item, itemRelPath, stats);
            }
        }
    }
    /**
     * Categorize a file for stats tracking (extracted for clarity)
     */
    categorizeFile(fileName, relativePath, stats) {
        if (/page\.(tsx|jsx)$/.test(fileName) || /route\.(tsx|jsx|ts)$/.test(fileName))
            stats.routeFiles++;
        if (relativePath.includes("dictionaries/") && fileName.endsWith(".json"))
            stats.i18nFiles++;
        // Component detection (exclude Next.js conventions, shadcn/ui primitives, etc.)
        const isNextJsConvention = /^(page|layout|loading|error|not-found|template|route|middleware|global-error)\.(tsx|jsx|ts|js)$/.test(fileName);
        const isInMyContext = relativePath.startsWith(".mycontext/") || relativePath.includes("/.mycontext/");
        const isInActions = relativePath.includes("/actions/") || relativePath.startsWith("actions/");
        const isInLib = /^lib\/|\/lib\//.test(relativePath) && !relativePath.includes("/components/");
        const isInHooks = /^hooks\/|\/hooks\//.test(relativePath);
        const isTestFile = /\.(test|spec|stories)\.(tsx|jsx)$/.test(fileName);
        const isIconFile = /Icon\.(tsx|jsx)$/i.test(fileName) || relativePath.includes("/icons/") || relativePath.includes("/svgs/");
        const isShadcnUI = relativePath.includes("components/ui/") || relativePath.includes("/ui/");
        const isBarrelFile = fileName.startsWith("index.") && /\.(tsx|jsx|ts|js)$/.test(fileName);
        if (!isNextJsConvention && !isInMyContext && !isInActions && !isInLib && !isInHooks &&
            !isTestFile && !isIconFile && !isShadcnUI && !isBarrelFile &&
            ((/^[A-Z].*\.(tsx|jsx)$/.test(fileName)) ||
                ((relativePath.includes("/components/") || relativePath.startsWith("components/")) && /\.(tsx|jsx)$/.test(fileName)) ||
                /[-.](?:client|form|sheet|drawer|modal|dialog)\.(tsx|jsx)$/i.test(fileName) ||
                /(?:Client|Form|Sheet|Drawer|Modal|Dialog)\.(tsx|jsx)$/.test(fileName)))
            stats.componentFiles++;
        if (relativePath.includes("/api/"))
            stats.apiFiles++;
        if (/schema|model|entity/i.test(fileName))
            stats.schemaFiles++;
        if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(fileName))
            stats.testFiles++;
        // Security Surface detection
        const isAttackSurface = ATTACK_SURFACE_PATTERNS.some(p => p.test(fileName) || p.test(relativePath));
        if (isAttackSurface && /\.(ts|tsx|js|jsx|prisma)$/.test(fileName)) {
            stats.securitySurface++;
        }
    }
    /**
     * Read a file, truncated to MAX_FILE_READ_SIZE
     */
    async readFileSnippet(relativePath) {
        try {
            const fullPath = path.join(this.projectRoot, relativePath);
            const content = await fs.readFile(fullPath, "utf-8");
            const lines = content.split("\n").length;
            return {
                path: relativePath,
                content: content.substring(0, MAX_FILE_READ_SIZE),
                lines,
            };
        }
        catch {
            return null;
        }
    }
    /**
     * Compare a snapshot against context.json and get LLM assessment
     */
    async assessProgress(snapshot, existingContext) {
        // Build a compact representation for the LLM
        const treeCompact = snapshot.fileTree
            .filter((f) => f.type === "file")
            .map((f) => f.path)
            .join("\n");
        const keyFilesCompact = snapshot.keyFiles
            .map((f) => `--- ${f.path} (${f.lines} lines) ---\n${f.content}`)
            .join("\n\n");
        const contextStr = JSON.stringify(existingContext, null, 2).substring(0, 5000);
        const prompt = `You are the Living Brain's Assessment Engine for a MyContext-managed project.

TASK: Compare the ACTUAL project file structure against the project's PLANNED context (context.json) and provide a progress assessment.

## ACTUAL FILE STRUCTURE (${snapshot.stats.totalFiles} files, ${snapshot.stats.totalDirs} dirs)
Stats: ${snapshot.stats.routeFiles} route files, ${snapshot.stats.componentFiles} components, ${snapshot.stats.apiFiles} API routes, ${snapshot.stats.schemaFiles} schema files, ${snapshot.stats.securitySurface} security surface files, ${snapshot.stats.testFiles} test files

File tree:
${treeCompact.substring(0, 3000)}

## KEY FILES CONTENT
${keyFilesCompact.substring(0, 4000)}

## PLANNED CONTEXT (context.json)
${contextStr}

## INSTRUCTIONS
Analyze the gap between PLANNED and ACTUAL. Return valid JSON:
{
  "overallProgress": <0-100 int>,
  "progressSummary": "<2-3 sentence summary of current state>",
  "diffs": [
    {
      "type": "missing_in_context" | "missing_in_code" | "outdated" | "suggestion",
      "category": "route" | "component" | "entity" | "config" | "general",
      "description": "<what's missing/outdated>",
      "suggestion": "<concrete fix>"
    }
  ],
  "suggestedContextMerge": {
    // Partial context.json structure with suggested additions/updates
    // Only include NEW items that should be added to context.json
  }
}

Rules:
- "missing_in_context": Code exists but context.json doesn't know about it
- "missing_in_code": context.json plans it but code doesn't have it yet
- "outdated": Code has evolved beyond what context.json describes
- Focus on actionable, specific findings. Max 10 diffs.
- **CRITICAL**: Read the file path names and contents carefully to deduce the project's REAL BUSINESS DOMAIN. (e.g. Look for keywords in lib/, types/, and schema files to differentiate between "Health/Wellness" and "Real Estate/Vacancies").
- **SECURITY FOCUS**: Evaluate if sensitive features described in 'context.json' (auth, payments, user data) are properly implemented in the identified 'security surface' files. Are there missing auth checks or direct raw DB queries in the route files?
- If PLANNED CONTEXT is empty or minimal, infer the Project Name, Description, and Tech Stack from the code and include them in 'suggestedContextMerge'.`;
        try {
            const ai = this.getAICore();
            const report = await ai.generateStructuredText(prompt, "ContextDiffReport");
            return {
                assessedAt: new Date().toISOString(),
                overallProgress: report.overallProgress || 0,
                progressSummary: report.progressSummary || "Assessment complete.",
                diffs: report.diffs || [],
                suggestedContextMerge: report.suggestedContextMerge || {},
            };
        }
        catch (error) {
            return {
                assessedAt: new Date().toISOString(),
                overallProgress: -1,
                progressSummary: `LLM assessment failed: ${error.message}`,
                diffs: [],
                suggestedContextMerge: {},
            };
        }
    }
    /**
     * Display the scan results nicely
     */
    displaySnapshot(snapshot) {
        console.log(chalk_1.default.cyan("\n┌──────────────────────────────────────────────┐"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.bold.white("  📂 Project Scan Results") + " ".repeat(22) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("├──────────────────────────────────────────────┤"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.gray(`  Files:      ${String(snapshot.stats.totalFiles).padEnd(6)} Dirs: ${snapshot.stats.totalDirs}`.padEnd(45)) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.gray(`  Routes:     ${String(snapshot.stats.routeFiles).padEnd(6)} Components: ${snapshot.stats.componentFiles}`.padEnd(45)) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.gray(`  API Routes: ${String(snapshot.stats.apiFiles).padEnd(6)} Schemas: ${snapshot.stats.schemaFiles}`.padEnd(45)) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.gray(`  Security:   ${String(snapshot.stats.securitySurface).padEnd(6)} Tests:   ${snapshot.stats.testFiles}`.padEnd(45)) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.gray(`  Key Files:  ${snapshot.keyFiles.length} read for analysis`.padEnd(45)) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("└──────────────────────────────────────────────┘"));
    }
    /**
     * Display the diff report nicely
     */
    displayReport(report) {
        const progressBar = this.makeProgressBar(report.overallProgress, 20);
        console.log(chalk_1.default.cyan("\n┌──────────────────────────────────────────────┐"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.bold.white("  🧠 Context Sync Assessment") + " ".repeat(18) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("├──────────────────────────────────────────────┤"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.white(`  Progress: ${progressBar} ${report.overallProgress}%`.padEnd(45)) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("│") + chalk_1.default.gray(`  ${report.progressSummary.substring(0, 43)}`.padEnd(45)) + chalk_1.default.cyan("│"));
        console.log(chalk_1.default.cyan("└──────────────────────────────────────────────┘"));
        if (report.diffs.length > 0) {
            console.log(chalk_1.default.yellow(`\n📋 Found ${report.diffs.length} items to sync:\n`));
            for (const diff of report.diffs) {
                const icon = {
                    missing_in_context: "🔵",
                    missing_in_code: "🔴",
                    outdated: "🟡",
                    suggestion: "💡",
                }[diff.type];
                console.log(`  ${icon} [${diff.category}] ${diff.description}`);
                console.log(chalk_1.default.gray(`     → ${diff.suggestion}\n`));
            }
        }
        if (Object.keys(report.suggestedContextMerge).length > 0) {
            console.log(chalk_1.default.green("📦 Suggested context.json merge:"));
            console.log(chalk_1.default.gray(JSON.stringify(report.suggestedContextMerge, null, 2).substring(0, 500)));
        }
    }
    /**
     * Get or initialize AICore safely
     */
    getAICore() {
        if (this.ai)
            return this.ai;
        try {
            this.ai = AICore_1.AICore.getInstance();
        }
        catch {
            this.ai = AICore_1.AICore.getInstance({
                fallbackEnabled: true,
                workingDirectory: this.projectRoot,
            });
        }
        return this.ai;
    }
    makeProgressBar(percent, width) {
        if (percent < 0)
            return "❌".padEnd(width);
        const filled = Math.round((percent / 100) * width);
        const empty = width - filled;
        return "█".repeat(filled) + "░".repeat(empty);
    }
}
exports.ProjectScanner = ProjectScanner;
//# sourceMappingURL=ProjectScanner.js.map