// Planu — project-dna-handler.ts
// Handlers for detect_project_dna, bootstrap_project_intelligence, update_project_dna.
import { z } from 'zod';
import { writeFile, mkdir, readFile } from 'node:fs/promises';
import { join, dirname } from 'node:path';
import { detectStack, detectPackageManager, detectTestingFramework, detectLinter, detectFormatter, } from '../engine/project-dna/stack-detector.js';
import { detectAITool, loadAIToolProfile } from '../engine/project-dna/ai-tool-detector.js';
import { generateRulesForStack, getSupportedStackTokens, } from '../engine/project-dna/rules-generator.js';
// ---------------------------------------------------------------------------
// Input schemas
// ---------------------------------------------------------------------------
export const DetectProjectDNAInputSchema = {
    projectPath: z.string().max(4096).describe('Absolute path to the project root to scan'),
};
export const BootstrapProjectIntelligenceInputSchema = {
    projectPath: z
        .string()
        .max(4096)
        .describe('Absolute path to the project root where rules and skills will be installed'),
    dryRun: z
        .boolean()
        .optional()
        .default(false)
        .describe('If true, report what would be created without writing files. Default: false'),
    skipSkills: z
        .boolean()
        .optional()
        .default(false)
        .describe('If true, skip skill installation. Default: false'),
    skipRules: z
        .boolean()
        .optional()
        .default(false)
        .describe('If true, skip rules generation. Default: false'),
};
export const UpdateDNAInputSchema = {
    projectPath: z.string().max(4096).describe('Absolute path to the project root to re-scan'),
    force: z
        .boolean()
        .optional()
        .default(false)
        .describe('If true, regenerate rules even if they already exist. Default: false'),
};
// ---------------------------------------------------------------------------
// Shared DNA detection
// ---------------------------------------------------------------------------
async function buildProjectDNA(projectPath) {
    const [stack, packageManager, aiTool] = await Promise.all([
        detectStack(projectPath),
        detectPackageManager(projectPath),
        detectAITool(projectPath),
    ]);
    const [aiToolProfile, testingFramework, linter, formatter] = await Promise.all([
        loadAIToolProfile(aiTool),
        detectTestingFramework(projectPath, stack),
        detectLinter(projectPath, stack),
        detectFormatter(projectPath, stack),
    ]);
    const conventions = [];
    if (stack.includes('typescript-strict')) {
        conventions.push('typescript-strict');
    }
    if (stack.includes('github-actions')) {
        conventions.push('github-actions');
    }
    if (packageManager === 'pnpm') {
        conventions.push('pnpm-workspace');
    }
    return {
        projectPath,
        detectedAt: new Date().toISOString(),
        stack,
        aiTool,
        aiToolProfile,
        packageManager,
        testingFramework,
        linter,
        formatter,
        cicd: stack.includes('github-actions')
            ? 'github-actions'
            : stack.includes('gitlab-ci')
                ? 'gitlab-ci'
                : undefined,
        conventions,
    };
}
// ---------------------------------------------------------------------------
// Handler: detect_project_dna
// ---------------------------------------------------------------------------
export async function handleDetectProjectDNA(args) {
    const dna = await buildProjectDNA(args.projectPath);
    const lines = [
        `Project DNA detected for: ${dna.projectPath}`,
        ``,
        `AI Tool: ${dna.aiToolProfile.name} (${dna.aiTool})`,
        `Package Manager: ${dna.packageManager}`,
        `Stack: ${dna.stack.length > 0 ? dna.stack.join(', ') : '(none detected)'}`,
        `Testing: ${dna.testingFramework ?? '(none detected)'}`,
        `Linter: ${dna.linter ?? '(none detected)'}`,
        `Formatter: ${dna.formatter ?? '(none detected)'}`,
        `CI/CD: ${dna.cicd ?? '(none detected)'}`,
        `Conventions: ${dna.conventions.length > 0 ? dna.conventions.join(', ') : '(none)'}`,
        ``,
        `Rules format: ${dna.aiToolProfile.rulesFormat}`,
    ];
    if (dna.aiToolProfile.rulesDir !== undefined) {
        lines.push(`Rules directory: ${dna.aiToolProfile.rulesDir}`);
    }
    if (dna.aiToolProfile.rulesFile !== undefined) {
        lines.push(`Rules file: ${dna.aiToolProfile.rulesFile}`);
    }
    const supportedTokens = getSupportedStackTokens();
    const matchedTokens = dna.stack.filter((t) => supportedTokens.includes(t));
    lines.push(``, `Rules available for: ${matchedTokens.length > 0 ? matchedTokens.join(', ') : '(none)'}`);
    lines.push(`Run bootstrap_project_intelligence to install rules and skills.`);
    return { content: [{ type: 'text', text: lines.join('\n') }] };
}
// ---------------------------------------------------------------------------
// Handler: bootstrap_project_intelligence
// ---------------------------------------------------------------------------
export async function handleBootstrapProjectIntelligence(args) {
    const { projectPath, dryRun = false, skipSkills = false, skipRules = false } = args;
    const dna = await buildProjectDNA(projectPath);
    const result = {
        filesCreated: [],
        skillsInstalled: [],
        rulesGenerated: [],
        warnings: [],
        summary: '',
    };
    if (!skipRules) {
        await generateRules(dna, projectPath, dryRun, result);
    }
    if (!skipSkills) {
        recordSkillSuggestions(dna, result);
    }
    result.summary = buildBootstrapSummary(result, dryRun);
    const lines = [result.summary];
    if (result.filesCreated.length > 0) {
        lines.push(``, `Files created:`);
        result.filesCreated.forEach((f) => lines.push(`  - ${f}`));
    }
    if (result.rulesGenerated.length > 0) {
        lines.push(``, `Rules generated for:`);
        result.rulesGenerated.forEach((r) => lines.push(`  - ${r}`));
    }
    if (result.skillsInstalled.length > 0) {
        lines.push(``, `Suggested skills:`);
        result.skillsInstalled.forEach((s) => lines.push(`  - ${s}`));
    }
    if (result.warnings.length > 0) {
        lines.push(``, `Warnings:`);
        result.warnings.forEach((w) => lines.push(`  - ${w}`));
    }
    return { content: [{ type: 'text', text: lines.join('\n') }] };
}
async function generateRules(dna, projectPath, dryRun, result) {
    const supportedTokens = getSupportedStackTokens();
    const matchedTokens = dna.stack.filter((t) => supportedTokens.includes(t));
    // For markdown-append and yaml formats, collect all content into one file
    const appendFileMap = new Map();
    for (const token of matchedTokens) {
        const generated = await generateRulesForStack(dna, token);
        if (generated === null) {
            continue;
        }
        const { filePath, content } = generated;
        const absPath = join(projectPath, filePath);
        if (dna.aiToolProfile.rulesFormat === 'markdown-append' ||
            dna.aiToolProfile.rulesFormat === 'yaml') {
            const existing = appendFileMap.get(absPath) ?? [];
            existing.push(content);
            appendFileMap.set(absPath, existing);
        }
        else {
            if (!dryRun) {
                await mkdir(dirname(absPath), { recursive: true });
                await writeFile(absPath, content, 'utf-8');
                result.filesCreated.push(absPath);
            }
            else {
                result.filesCreated.push(`[dry-run] ${absPath}`);
            }
            result.rulesGenerated.push(token);
        }
    }
    for (const [absPath, contents] of appendFileMap) {
        const combined = contents.join('\n');
        if (!dryRun) {
            await mkdir(dirname(absPath), { recursive: true });
            let existing = '';
            try {
                existing = await readFile(absPath, 'utf-8');
            }
            catch {
                // file does not exist yet
            }
            await writeFile(absPath, existing + combined, 'utf-8');
            result.filesCreated.push(absPath);
        }
        else {
            result.filesCreated.push(`[dry-run] ${absPath}`);
        }
        // Record which tokens were written to this file
        const tokenChecks = await Promise.all(matchedTokens.map(async (t) => {
            const gen = await generateRulesForStack(dna, t);
            return gen !== null && join(projectPath, gen.filePath) === absPath ? t : null;
        }));
        const tokensForFile = tokenChecks.filter((t) => t !== null);
        result.rulesGenerated.push(...tokensForFile.map((t) => `${t} -> ${absPath}`));
    }
}
function recordSkillSuggestions(dna, result) {
    const skillMap = {
        supabase: 'supabase-patterns',
        nextjs: 'nextjs-patterns',
        typescript: 'typescript-strict',
        vitest: 'vitest-testing',
        stripe: 'stripe-payments',
        prisma: 'prisma-orm',
        zod: 'zod-validation',
        fastapi: 'fastapi-patterns',
        django: 'django-patterns',
    };
    for (const token of dna.stack) {
        const skill = skillMap[token];
        if (skill !== undefined) {
            result.skillsInstalled.push(skill);
        }
    }
}
function buildBootstrapSummary(result, dryRun) {
    const prefix = dryRun ? '[DRY RUN] ' : '';
    const fileCount = result.filesCreated.length;
    const ruleCount = result.rulesGenerated.length;
    const skillCount = result.skillsInstalled.length;
    return (`${prefix}Bootstrap complete. ` +
        `${fileCount} file(s) ${dryRun ? 'would be' : 'were'} created, ` +
        `${ruleCount} rule(s) generated, ` +
        `${skillCount} skill(s) suggested.`);
}
// ---------------------------------------------------------------------------
// Handler: update_project_dna
// ---------------------------------------------------------------------------
export async function handleUpdateProjectDNA(args) {
    const { projectPath, force = false } = args;
    const dna = await buildProjectDNA(projectPath);
    const forceNote = force ? ' (force mode: existing rules will be overwritten)' : '';
    const lines = [
        `Project DNA updated for: ${projectPath}${forceNote}`,
        ``,
        `Detected at: ${dna.detectedAt}`,
        `AI Tool: ${dna.aiToolProfile.name} (${dna.aiTool})`,
        `Stack: ${dna.stack.length > 0 ? dna.stack.join(', ') : '(none detected)'}`,
        `Package Manager: ${dna.packageManager}`,
        ``,
        `Run bootstrap_project_intelligence to apply the updated configuration.`,
    ];
    return { content: [{ type: 'text', text: lines.join('\n') }] };
}
//# sourceMappingURL=project-dna-handler.js.map