"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;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectProjectKinds = detectProjectKinds;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const kindOrder = ['backend', 'frontend', 'library', 'service', 'cli', 'fullstack'];
const ignoredDirectoryNames = new Set([
    '.git',
    '.archpilot',
    '.archpilot_',
    '.vscode-test',
    'tests',
    'test',
    '__tests__',
    'fixtures',
    'smoke',
    'docs',
    'doc',
    'reports',
    'report',
    'badges',
    'examples',
    'node_modules',
    'dist',
    'build',
    'out',
    'coverage',
    '.next',
    'target',
    'vendor',
    '.venv',
    'venv',
    '__pycache__',
]);
const ignoredRootPathPrefixes = ['tests', 'test', 'fixtures', 'examples', 'example', 'spec', '__tests__', '.vscode-test'];
function normalizeRelativePath(value) {
    return value.replaceAll('\\', '/');
}
function sortUnique(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function addEvidence(evidence, value) {
    if (!evidence.includes(value)) {
        evidence.push(value);
    }
}
function safeReadDirectoryEntries(directoryPath) {
    try {
        return fs.readdirSync(directoryPath, { withFileTypes: true });
    }
    catch {
        return [];
    }
}
function safeReadJsonFile(filePath) {
    try {
        return JSON.parse(fs.readFileSync(filePath, { encoding: 'utf8' }));
    }
    catch {
        return undefined;
    }
}
function isIgnoredRelativePath(relativePath) {
    const normalized = normalizeRelativePath(relativePath).replace(/^\.\/+/u, '');
    if (normalized.length === 0 || normalized === '.') {
        return false;
    }
    const rootSegment = normalized.split('/')[0];
    if (!rootSegment) {
        return false;
    }
    return ignoredRootPathPrefixes.includes(rootSegment);
}
function isIgnoredPath(ignoreMatcher, relativePath) {
    return ignoreMatcher?.isIgnored(normalizeRelativePath(relativePath)) ?? false;
}
function scanWorkspace(workspaceRoot, maxDepth = 6, ignoreMatcher) {
    const filePaths = [];
    const directoryPaths = [];
    const queue = [
        { absolutePath: workspaceRoot, relativePath: '', depth: 0 },
    ];
    while (queue.length > 0) {
        const current = queue.shift();
        if (!current) {
            continue;
        }
        const entries = safeReadDirectoryEntries(current.absolutePath)
            .sort((left, right) => left.name.localeCompare(right.name));
        for (const entry of entries) {
            const absoluteEntryPath = path.join(current.absolutePath, entry.name);
            const relativeEntryPath = current.relativePath.length > 0
                ? `${current.relativePath}/${entry.name}`
                : entry.name;
            if (isIgnoredRelativePath(relativeEntryPath) || isIgnoredPath(ignoreMatcher, relativeEntryPath)) {
                continue;
            }
            if (entry.isDirectory()) {
                if (current.depth >= maxDepth || ignoredDirectoryNames.has(entry.name)) {
                    continue;
                }
                directoryPaths.push(normalizeRelativePath(relativeEntryPath));
                queue.push({
                    absolutePath: absoluteEntryPath,
                    relativePath: relativeEntryPath,
                    depth: current.depth + 1,
                });
                continue;
            }
            if (entry.isFile()) {
                filePaths.push(normalizeRelativePath(relativeEntryPath));
            }
        }
    }
    return {
        filePaths: sortUnique(filePaths),
        directoryPaths: sortUnique(directoryPaths),
    };
}
function parsePackageJsonSummaries(workspaceRoot, filePaths) {
    const packageJsonPaths = filePaths.filter((entry) => entry.endsWith('/package.json') || entry === 'package.json');
    const summaries = [];
    for (const packageJsonPath of packageJsonPaths) {
        const absolutePath = path.join(workspaceRoot, ...packageJsonPath.split('/'));
        const parsed = safeReadJsonFile(absolutePath);
        if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
            continue;
        }
        const typed = parsed;
        const dependencies = new Set();
        for (const fieldName of ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies']) {
            const value = typed[fieldName];
            if (!value || typeof value !== 'object' || Array.isArray(value)) {
                continue;
            }
            for (const dependencyName of Object.keys(value)) {
                dependencies.add(dependencyName);
            }
        }
        const hasBin = Boolean(typed.bin && (typeof typed.bin === 'string' || (typeof typed.bin === 'object' && !Array.isArray(typed.bin))));
        const hasExports = typed.exports !== undefined;
        const hasMainLikeFields = typeof typed.main === 'string' || typeof typed.module === 'string' || typeof typed.types === 'string';
        summaries.push({
            path: packageJsonPath,
            dependencies,
            hasBin,
            hasExports,
            hasMainLikeFields,
        });
    }
    return summaries;
}
function detectProjectKinds(workspaceRoot, options) {
    const scan = scanWorkspace(workspaceRoot, 6, options?.ignoreMatcher);
    const packageSummaries = parsePackageJsonSummaries(workspaceRoot, scan.filePaths);
    const evidence = [];
    const scores = {
        backend: 0,
        frontend: 0,
        library: 0,
        service: 0,
        cli: 0,
    };
    const strongCounts = {
        backend: 0,
        frontend: 0,
        library: 0,
        service: 0,
        cli: 0,
    };
    const addSignal = (kind, points, evidenceMessage, strong = false) => {
        scores[kind] += points;
        if (strong) {
            strongCounts[kind] += 1;
        }
        addEvidence(evidence, evidenceMessage);
    };
    const hasFile = (fileName) => scan.filePaths.some((entry) => entry === fileName || entry.endsWith(`/${fileName}`));
    const hasRootFile = (fileName) => scan.filePaths.some((entry) => entry === fileName);
    const hasDirectoryNamed = (directoryName) => scan.directoryPaths.some((entry) => entry === directoryName || entry.endsWith(`/${directoryName}`));
    const hasRootDirectory = (directoryName) => scan.directoryPaths.some((entry) => entry === directoryName);
    const hasDirectoryPath = (directoryPath) => scan.directoryPaths.some((entry) => entry === directoryPath || entry.endsWith(`/${directoryPath}`));
    const packageHasDependency = (dependencyName) => packageSummaries.some((summary) => summary.dependencies.has(dependencyName));
    const hasLaravelBackendEvidence = packageHasDependency('laravel/framework') ||
        (hasRootFile('artisan') && hasRootFile('composer.json')) ||
        (hasRootDirectory('app') && hasRootDirectory('config') && hasRootDirectory('routes'));
    // Backend signals
    if (hasFile('pom.xml')) {
        addSignal('backend', 3, 'Found pom.xml', true);
    }
    if (hasFile('build.gradle')) {
        addSignal('backend', 3, 'Found build.gradle', true);
    }
    if (hasFile('build.gradle.kts')) {
        addSignal('backend', 3, 'Found build.gradle.kts', true);
    }
    if (hasFile('requirements.txt')) {
        addSignal('backend', 3, 'Found requirements.txt', true);
    }
    if (hasFile('pyproject.toml')) {
        addSignal('library', 2, 'Found pyproject.toml');
    }
    if (hasFile('setup.py')) {
        addSignal('library', 3, 'Found setup.py', true);
    }
    if (hasFile('composer.json')) {
        addSignal('backend', 3, 'Found composer.json', true);
    }
    if (hasLaravelBackendEvidence) {
        addSignal('backend', 5, 'Found Laravel app-root backend evidence', true);
    }
    if (hasFile('go.mod')) {
        addSignal('library', 2, 'Found go.mod');
    }
    if (hasDirectoryPath('cmd') || hasDirectoryNamed('cmd')) {
        addSignal('backend', 2, 'Found Go cmd/ entrypoint directory');
    }
    if (hasFile('openapi.yaml') || hasFile('openapi.yml')) {
        addSignal('backend', 2, 'Found openapi.yaml/openapi.yml', true);
    }
    if (hasDirectoryNamed('db') || hasDirectoryNamed('migrations')) {
        addSignal('backend', 2, 'Found db/ or migrations/ directory');
    }
    if (hasDirectoryPath('src/main/java')) {
        addSignal('backend', 3, 'Found src/main/java', true);
    }
    if (scan.filePaths.some((entry) => /(^|\/)(repositories?|repository)\.py$/u.test(entry))) {
        addSignal('backend', 2, 'Found Python repository-layer files');
    }
    for (const dependency of ['express', 'fastify', '@nestjs/core', 'koa', 'hapi']) {
        if (packageHasDependency(dependency)) {
            addSignal('backend', 2, `Found package.json with ${dependency} dependency`);
        }
    }
    // Frontend signals
    for (const nextConfig of ['next.config.js', 'next.config.ts', 'next.config.mjs']) {
        if (hasFile(nextConfig)) {
            addSignal('frontend', 3, `Found ${nextConfig}`, true);
        }
    }
    if (hasFile('angular.json')) {
        addSignal('frontend', 3, 'Found angular.json', true);
    }
    for (const dependency of ['react', 'next', 'vue']) {
        if (packageHasDependency(dependency)) {
            addSignal('frontend', 2, `Found package.json with ${dependency} dependency`);
        }
    }
    if (hasDirectoryNamed('public')) {
        addSignal('frontend', 1, 'Found public/ static directory');
    }
    const hasFrontendAppRouterContext = packageHasDependency('next') ||
        hasFile('next.config.js') ||
        hasFile('next.config.ts') ||
        hasFile('next.config.mjs');
    if (hasFrontendAppRouterContext && (hasDirectoryNamed('app') || hasDirectoryNamed('pages'))) {
        addSignal('frontend', 2, 'Found frontend app/ or pages/ directory');
    }
    if (hasDirectoryPath('src/app')) {
        addSignal('frontend', 2, 'Found src/app directory');
    }
    if (hasFile('vite.config.js') || hasFile('vite.config.ts')) {
        addSignal('frontend', 2, 'Found vite.config.js or vite.config.ts');
    }
    if (hasFile('index.html')) {
        addSignal('frontend', 1, 'Found index.html');
    }
    // Lightweight infrastructure / automation signals mapped conservatively to library
    if (hasRootFile('main.tf') ||
        hasRootFile('variables.tf') ||
        hasRootFile('outputs.tf') ||
        hasRootFile('providers.tf') ||
        hasRootDirectory('modules') ||
        hasRootDirectory('environments') ||
        hasRootDirectory('live') ||
        hasRootDirectory('stacks')) {
        addSignal('library', 2, 'Found Terraform-style infrastructure layout');
    }
    if (hasRootFile('ansible.cfg') ||
        hasRootDirectory('roles') ||
        hasRootDirectory('playbooks') ||
        hasRootDirectory('inventories') ||
        hasRootDirectory('group_vars') ||
        hasRootDirectory('host_vars')) {
        addSignal('library', 2, 'Found Ansible-style automation layout');
    }
    // Library signals
    if (hasFile('src/index.ts') || hasFile('src/index.js')) {
        addSignal('library', 2, 'Found src/index.ts or src/index.js');
    }
    if (hasDirectoryPath('src/modules') && !hasFile('requirements.txt')) {
        addSignal('library', 1, 'Found reusable module folders under src/modules');
    }
    if (scan.filePaths.some((entry) => entry.startsWith('src/') && entry.endsWith('/__init__.py'))) {
        addSignal('library', 2, 'Found Python package __init__.py files');
    }
    if (packageSummaries.some((summary) => summary.hasExports)) {
        addSignal('library', 3, 'Found package.json exports field', true);
    }
    if (packageSummaries.some((summary) => summary.hasMainLikeFields)) {
        addSignal('library', 1, 'Found package.json main/module/types fields');
    }
    if (hasLaravelBackendEvidence && scores.library > 0) {
        scores.library = Math.max(0, scores.library - 1);
    }
    // CLI signals
    if (packageSummaries.some((summary) => summary.hasBin)) {
        addSignal('cli', 3, 'Found package.json bin field', true);
    }
    for (const dependency of ['commander', 'yargs', '@oclif/core']) {
        if (packageHasDependency(dependency)) {
            addSignal('cli', 2, `Found package.json with ${dependency} dependency`);
        }
    }
    if (hasFile('cli.ts') || hasFile('cli.js') || hasFile('main.ts') || hasFile('main.js')) {
        addSignal('cli', 1, 'Found CLI-style entrypoint file (cli/main)');
    }
    if (hasFile('__main__.py')) {
        addSignal('cli', 2, 'Found Python __main__.py');
    }
    if (hasFile('main.go') && (hasDirectoryPath('cmd') || hasDirectoryNamed('cmd'))) {
        addSignal('cli', 2, 'Found Go main entrypoint under cmd/');
    }
    // Service signals
    if (hasDirectoryNamed('worker') || hasDirectoryNamed('workers') || hasDirectoryNamed('jobs') || hasDirectoryNamed('queue') || hasDirectoryNamed('cron')) {
        addSignal('service', 2, 'Found worker/jobs/queue/cron directory');
    }
    for (const dependency of ['bull', 'bullmq', 'celery', 'rq']) {
        if (packageHasDependency(dependency)) {
            addSignal('service', 2, `Found background processing dependency (${dependency})`);
        }
    }
    if (scores.frontend === 0 && scores.service > 0) {
        addSignal('service', 1, 'Service-style signals detected without clear frontend signals');
    }
    const hasStrongStandaloneFrontendEvidence = packageHasDependency('react') ||
        packageHasDependency('next') ||
        packageHasDependency('vue') ||
        packageHasDependency('@angular/core') ||
        hasRootFile('next.config.js') ||
        hasRootFile('next.config.ts') ||
        hasRootFile('next.config.mjs') ||
        hasRootFile('angular.json');
    if (hasLaravelBackendEvidence && !hasStrongStandaloneFrontendEvidence) {
        scores.frontend = 0;
    }
    if (hasLaravelBackendEvidence) {
        scores.service = 0;
    }
    // Library confidence reduction when strong app/server signals exist
    const hasStrongAppSignals = scores.backend >= 3 || scores.frontend >= 3;
    if (hasStrongAppSignals && scores.library > 0 && !packageSummaries.some((summary) => summary.hasExports)) {
        scores.library = Math.max(0, scores.library - 1);
    }
    const kinds = kindOrder.filter((kind) => kind === 'fullstack'
        ? false
        : scores[kind] > 0);
    const backendStrong = strongCounts.backend >= 2 || scores.backend >= 5;
    const frontendStrong = strongCounts.frontend >= 2 || scores.frontend >= 5;
    const withFullstack = [...kinds];
    if (backendStrong && frontendStrong) {
        withFullstack.push('fullstack');
        addEvidence(evidence, 'Detected both backend and frontend signals');
    }
    if (withFullstack.length === 0) {
        return {
            kinds: ['unknown'],
            primaryKind: 'unknown',
            confidence: 'low',
            evidence: evidence.length > 0 ? evidence : ['No strong project kind signals detected'],
        };
    }
    let primaryKind;
    if (withFullstack.includes('fullstack')) {
        primaryKind = 'fullstack';
    }
    else {
        const scoredKinds = kinds
            .map((kind) => ({ kind, score: scores[kind] }))
            .sort((left, right) => {
            if (right.score !== left.score) {
                return right.score - left.score;
            }
            return kindOrder.indexOf(left.kind) - kindOrder.indexOf(right.kind);
        });
        primaryKind = scoredKinds[0]?.kind ?? 'unknown';
    }
    let confidence = 'low';
    const primarySignalKind = primaryKind === 'fullstack' ? undefined : primaryKind;
    if (primaryKind === 'fullstack' ||
        (primarySignalKind ? strongCounts[primarySignalKind] >= 2 : false)) {
        confidence = 'high';
    }
    else if ((primarySignalKind ? scores[primarySignalKind] >= 2 : false) || evidence.length >= 2) {
        confidence = 'medium';
    }
    return {
        kinds: withFullstack,
        primaryKind,
        confidence,
        evidence,
    };
}
//# sourceMappingURL=projectKindDetection.js.map