"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.detectComponents = detectComponents;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const stackMetadata_1 = require("../stackMetadata");
const apiDbHintDetection_1 = require("./apiDbHintDetection");
const projectKindDetection_1 = require("./projectKindDetection");
const stackDetection_1 = require("./stackDetection");
const archpilotIgnore_1 = require("./archpilotIgnore");
function normalizePath(value) {
    return value.replaceAll('\\', '/').replace(/\/+$/u, '') || '.';
}
function uniqueSorted(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function addEvidence(evidence, value) {
    if (!evidence.includes(value)) {
        evidence.push(value);
    }
}
function isIgnoredComponentCandidatePath(componentPath) {
    const normalized = normalizePath(componentPath).replace(/^\.\//u, '');
    if (normalized === '.' || normalized.length === 0) {
        return false;
    }
    const segments = normalized.split('/').filter((segment) => segment.length > 0);
    const firstSegment = segments[0];
    const ignoredRootSegments = [
        'tests',
        'test',
        'cypress',
        'cypress-tests',
        '__tests__',
        'fixtures',
        'smoke',
        'docs',
        'doc',
        'reports',
        'report',
        'examples',
        'example',
        'spec',
        'badges',
    ];
    const ignoredSupportingSegments = [
        'public',
        'static',
        'assets',
        'vendor',
        'plugins',
        'plugin',
        'themes',
        'theme',
        'generated',
        'storage',
        'dist',
        'build',
        'coverage',
    ];
    return ignoredRootSegments.includes(firstSegment ?? '') ||
        segments.some((segment) => ignoredSupportingSegments.includes(segment.toLowerCase()));
}
function pathExists(pathValue) {
    try {
        fs.accessSync(pathValue);
        return true;
    }
    catch {
        return false;
    }
}
function hasManifest(workspaceRoot, componentPath) {
    const absoluteRoot = componentPath === '.'
        ? workspaceRoot
        : path.join(workspaceRoot, ...componentPath.split('/'));
    return [
        'package.json',
        'pom.xml',
        'requirements.txt',
        'go.mod',
        'composer.json',
    ].some((fileName) => pathExists(path.join(absoluteRoot, fileName)));
}
function safeReadJson(filePath) {
    try {
        const parsed = JSON.parse(fs.readFileSync(filePath, { encoding: 'utf8' }));
        return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
            ? parsed
            : undefined;
    }
    catch {
        return undefined;
    }
}
function toStringArray(value) {
    if (Array.isArray(value)) {
        return value.filter((entry) => typeof entry === 'string');
    }
    return [];
}
function recordKeys(value) {
    return value && typeof value === 'object' && !Array.isArray(value)
        ? Object.keys(value)
        : [];
}
function hasObject(value) {
    return Boolean(value && typeof value === 'object' && !Array.isArray(value));
}
function hasPackagePublishSurface(packageJson) {
    return hasObject(packageJson?.exports) ||
        typeof packageJson?.main === 'string' ||
        typeof packageJson?.module === 'string' ||
        typeof packageJson?.types === 'string' ||
        hasObject(packageJson?.publishConfig) ||
        Array.isArray(packageJson?.files);
}
function absoluteComponentRoot(workspaceRoot, componentPath) {
    return componentPath === '.'
        ? workspaceRoot
        : path.join(workspaceRoot, ...componentPath.split('/'));
}
function readPackageJson(workspaceRoot, componentPath) {
    const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
    return safeReadJson(path.join(absoluteRoot, 'package.json'));
}
function readTextFileSafe(filePath) {
    try {
        return fs.readFileSync(filePath, { encoding: 'utf8' });
    }
    catch {
        return undefined;
    }
}
function stripXmlComments(value) {
    return value.replace(/<!--[\s\S]*?-->/gu, '');
}
function readFirstXmlTag(value, tagName) {
    const match = value.match(new RegExp(`<${tagName}(?:\\s[^>]*)?>([\\s\\S]*?)</${tagName}>`, 'iu'));
    return match?.[1]?.trim().replace(/\s+/gu, ' ');
}
function countMatches(value, pattern) {
    return [...value.matchAll(pattern)].length;
}
function readMavenMetadata(workspaceRoot, componentPath) {
    if (isUnderJvmResourceRoot(componentPath)) {
        return undefined;
    }
    const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
    const pomXml = readTextFileSafe(path.join(absoluteRoot, 'pom.xml'));
    if (!pomXml) {
        return undefined;
    }
    const normalizedPom = stripXmlComments(pomXml);
    const packaging = readFirstXmlTag(normalizedPom, 'packaging') ?? 'jar';
    const dependencyBlocks = [...normalizedPom.matchAll(/<dependency\b[\s\S]*?<\/dependency>/giu)].map((match) => match[0]);
    return {
        packaging,
        artifactId: readFirstXmlTag(normalizedPom, 'artifactId'),
        name: readFirstXmlTag(normalizedPom, 'name'),
        hasPom: true,
        hasModules: /<modules\b/iu.test(normalizedPom),
        hasDependencyManagement: /<dependencyManagement\b/iu.test(normalizedPom),
        hasSpringBootPlugin: /\bspring-boot-maven-plugin\b/iu.test(normalizedPom),
        hasSpringBootRepackageGoal: /<goal>\s*repackage\s*<\/goal>/iu.test(normalizedPom),
        configuredMainClass: readFirstXmlTag(normalizedPom, 'mainClass') ?? readFirstXmlTag(normalizedPom, 'start-class'),
        hasSpringBootStarterDependency: dependencyBlocks.some((block) => /<artifactId>\s*spring-boot-starter(?:-[^<]+)?\s*<\/artifactId>/iu.test(block)),
        hasSpringBootAutoconfigureDependency: dependencyBlocks.some((block) => /<artifactId>\s*spring-boot-autoconfigure\s*<\/artifactId>/iu.test(block)),
        hasSpringBootAutoconfigureProcessor: /spring-boot-autoconfigure-processor|spring-boot-configuration-processor/iu.test(normalizedPom),
        hasDocumentationTooling: /asciidoctor-maven-plugin|maven-site-plugin|doxia|antora|javadoc|<goal>\s*site\s*<\/goal>/iu.test(normalizedPom),
        testScopedDependencyCount: dependencyBlocks.filter((block) => /<scope>\s*test\s*<\/scope>/iu.test(block)).length,
        totalDependencyCount: dependencyBlocks.length,
        hasMavenPluginConfiguration: packaging === 'maven-plugin' || /maven-plugin-plugin|<goalPrefix>|<mojo/iu.test(normalizedPom),
        hasTestLifecycleConfiguration: /maven-surefire-plugin|maven-failsafe-plugin|integration-test|failsafe|surefire/iu.test(normalizedPom),
        hasArquillianEvidence: /arquillian/iu.test(normalizedPom),
        hasMainJava: pathExists(path.join(absoluteRoot, 'src', 'main', 'java')),
        hasMainResources: pathExists(path.join(absoluteRoot, 'src', 'main', 'resources')),
        hasTestJava: pathExists(path.join(absoluteRoot, 'src', 'test', 'java')),
        hasTestResources: pathExists(path.join(absoluteRoot, 'src', 'test', 'resources')),
    };
}
function toComponentRoot(modulePath) {
    const normalized = normalizePath(modulePath);
    const segments = normalized.split('/').filter((segment) => segment.length > 0);
    if (segments.length === 0) {
        return undefined;
    }
    if (segments[0] === 'src' || segments[0] === 'app' || segments[0] === 'routes') {
        return '.';
    }
    if (['apps', 'packages', 'services', 'libs'].includes(segments[0]) && segments.length >= 2) {
        return `${segments[0]}/${segments[1]}`;
    }
    if ((segments[0] === 'backend' || segments[0] === 'frontend') && segments.length >= 2) {
        return segments[0];
    }
    return undefined;
}
function buildRootCandidates(input) {
    const candidatesByPath = new Map();
    const addCandidate = (candidatePath, evidence, architectureRoot) => {
        const normalized = normalizePath(candidatePath);
        const existing = candidatesByPath.get(normalized) ?? { path: normalized, evidence: [] };
        addEvidence(existing.evidence, evidence);
        if (architectureRoot) {
            existing.architectureRoot = architectureRoot;
        }
        candidatesByPath.set(normalized, existing);
    };
    for (const root of input.architectureStyleSignals?.roots ?? []) {
        addCandidate(root.root, `Detected architecture root ${root.root}`, root);
    }
    for (const root of input.topology.detectedRoots ?? []) {
        addCandidate(root, `Detected project root ${root}`);
    }
    for (const moduleEntry of input.modules?.modules ?? []) {
        const componentRoot = toComponentRoot(moduleEntry.path);
        if (componentRoot) {
            addCandidate(componentRoot, `Detected module path ${moduleEntry.path}`);
        }
    }
    if (candidatesByPath.size === 0 && input.topology.topology === 'single_project') {
        addCandidate('.', 'Detected single-project repository root');
    }
    return [...candidatesByPath.values()].sort((left, right) => left.path.localeCompare(right.path));
}
function readPackageName(workspaceRoot, componentPath) {
    const packageJson = readPackageJson(workspaceRoot, componentPath);
    const name = packageJson?.name;
    return typeof name === 'string' && name.trim().length > 0 ? name.trim() : undefined;
}
function resolveName(workspaceRoot, componentPath) {
    const packageName = readPackageName(workspaceRoot, componentPath);
    if (packageName) {
        return packageName;
    }
    if (componentPath === '.') {
        return path.basename(workspaceRoot);
    }
    return componentPath.split('/').at(-1) ?? componentPath;
}
function resolveId(componentPath, usedIds) {
    const base = componentPath === '.'
        ? 'repo'
        : componentPath
            .replaceAll('/', '-')
            .replace(/[^a-z0-9_-]+/giu, '-')
            .replace(/^-+|-+$/gu, '')
            .toLowerCase() || 'component';
    let id = base;
    let suffix = 2;
    while (usedIds.has(id)) {
        id = `${base}-${suffix}`;
        suffix += 1;
    }
    usedIds.add(id);
    return id;
}
function hasRunnableFrontendComponentEvidence(workspaceRoot, componentPath) {
    const absoluteRoot = componentPath === '.'
        ? workspaceRoot
        : path.join(workspaceRoot, ...componentPath.split('/'));
    const packageJson = readPackageJson(workspaceRoot, componentPath);
    const dependencies = [
        ...recordKeys(packageJson?.dependencies),
        ...recordKeys(packageJson?.devDependencies),
    ];
    const scripts = recordKeys(packageJson?.scripts);
    const hasFrontendFramework = dependencies.some((dependency) => /^(?:react|next|vue|@angular\/core|@sveltejs\/kit)$/u.test(dependency));
    const hasInteractiveRunnableScript = scripts.some((script) => /^(?:dev|start|serve|preview)(?::|$)/u.test(script));
    const hasBundlerConfig = [
        'next.config.js',
        'next.config.ts',
        'vite.config.js',
        'vite.config.ts',
        'angular.json',
    ].some((relativePath) => pathExists(path.join(absoluteRoot, ...relativePath.split('/'))));
    const hasApplicationEntry = [
        'index.html',
        'src/main.tsx',
        'src/main.ts',
        'src/App.tsx',
        'src/app',
        'pages',
    ].some((relativePath) => pathExists(path.join(absoluteRoot, ...relativePath.split('/'))));
    return hasFrontendFramework && (hasApplicationEntry || (hasInteractiveRunnableScript && hasBundlerConfig));
}
function isUnderJvmResourceRoot(componentPath) {
    return /(^|\/)src\/main\/resources(?:\/|$)/u.test(normalizePath(componentPath).toLowerCase());
}
function hasFileUnderRoot(root, pattern) {
    if (!pathExists(root)) {
        return false;
    }
    const entries = fs.readdirSync(root, { recursive: true, withFileTypes: true });
    return entries.some((entry) => entry.isFile() && pattern.test(entry.name));
}
const javaMainMethodPattern = /\bpublic\s+static\s+void\s+main\s*\(\s*String\s*(?:(?:\[\s*\]\s*)|\.\.\.\s*)[A-Za-z_$][\w$]*\s*(?:\[\s*\])?\s*\)/u;
function stripJavaLikeCommentsAndStrings(value) {
    let result = '';
    let index = 0;
    let state = 'code';
    while (index < value.length) {
        const current = value[index] ?? '';
        const next = value[index + 1] ?? '';
        const nextTwo = value.slice(index, index + 3);
        if (state === 'code') {
            if (current === '/' && next === '/') {
                result += '  ';
                index += 2;
                state = 'line-comment';
                continue;
            }
            if (current === '/' && next === '*') {
                result += '  ';
                index += 2;
                state = 'block-comment';
                continue;
            }
            if (nextTwo === '"""') {
                result += '   ';
                index += 3;
                state = 'text-block';
                continue;
            }
            if (current === '"') {
                result += ' ';
                index += 1;
                state = 'string';
                continue;
            }
            if (current === "'") {
                result += ' ';
                index += 1;
                state = 'char';
                continue;
            }
            result += current;
            index += 1;
            continue;
        }
        if (state === 'line-comment') {
            result += current === '\n' || current === '\r' ? current : ' ';
            index += 1;
            if (current === '\n') {
                state = 'code';
            }
            continue;
        }
        if (state === 'block-comment') {
            if (current === '*' && next === '/') {
                result += '  ';
                index += 2;
                state = 'code';
                continue;
            }
            result += current === '\n' || current === '\r' ? current : ' ';
            index += 1;
            continue;
        }
        if (state === 'text-block') {
            if (nextTwo === '"""') {
                result += '   ';
                index += 3;
                state = 'code';
                continue;
            }
            result += current === '\n' || current === '\r' ? current : ' ';
            index += 1;
            continue;
        }
        if (state === 'string') {
            if (current === '\\') {
                result += ' ';
                index += next ? 2 : 1;
                if (next) {
                    result += ' ';
                }
                continue;
            }
            result += current === '\n' || current === '\r' ? current : ' ';
            index += 1;
            if (current === '"') {
                state = 'code';
            }
            continue;
        }
        if (current === '\\') {
            result += ' ';
            index += next ? 2 : 1;
            if (next) {
                result += ' ';
            }
            continue;
        }
        result += current === '\n' || current === '\r' ? current : ' ';
        index += 1;
        if (current === "'") {
            state = 'code';
        }
    }
    return result;
}
function readMavenProductionSourceFiles(workspaceRoot, componentPath) {
    const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
    const mainRoots = [
        path.join(absoluteRoot, 'src', 'main', 'java'),
        path.join(absoluteRoot, 'src', 'main', 'kotlin'),
        path.join(absoluteRoot, 'src', 'main', 'scala'),
    ];
    const files = [];
    for (const sourceRoot of mainRoots) {
        if (!pathExists(sourceRoot)) {
            continue;
        }
        const entries = fs.readdirSync(sourceRoot, { recursive: true, withFileTypes: true });
        for (const entry of entries) {
            if (!entry.isFile() || !/\.(?:java|kt|scala)$/iu.test(entry.name)) {
                continue;
            }
            const contents = readTextFileSafe(path.join(entry.parentPath, entry.name));
            if (contents) {
                files.push({ fileName: entry.name, contents });
            }
        }
    }
    return files;
}
function findMatchingBrace(value, openBraceIndex) {
    let depth = 0;
    for (let index = openBraceIndex; index < value.length; index += 1) {
        const current = value[index];
        if (current === '{') {
            depth += 1;
        }
        else if (current === '}') {
            depth -= 1;
            if (depth === 0) {
                return index;
            }
        }
    }
    return -1;
}
function getDeclaredPackageName(value) {
    return value.match(/\bpackage\s+([a-zA-Z_$][\w$]*(?:\.[a-zA-Z_$][\w$]*)*)\s*;/u)?.[1];
}
function classNameMatchesConfiguredMainClass(className, packageName, configuredMainClass) {
    const normalized = configuredMainClass.trim();
    if (normalized.length === 0) {
        return false;
    }
    return normalized === className || (packageName ? normalized === `${packageName}.${className}` : false);
}
function normalizeAnnotationName(value) {
    return value.slice(value.lastIndexOf('.') + 1);
}
function hasAnnotation(annotations, annotationNames) {
    return annotations.some((annotation) => annotationNames.has(normalizeAnnotationName(annotation)));
}
function extractAnnotations(annotationBlock) {
    return [...annotationBlock.matchAll(/@\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)/gu)]
        .map((match) => match[1])
        .filter((annotation) => Boolean(annotation));
}
function extractConcreteClassDeclarations(contents) {
    const sanitized = stripJavaLikeCommentsAndStrings(contents);
    const packageName = getDeclaredPackageName(sanitized);
    const classPattern = /((?:@\s*[\w.]+(?:\s*\([^)]*\))?\s*)*)(?:public\s+|protected\s+|private\s+|abstract\s+|final\s+|sealed\s+|non-sealed\s+|static\s+)*class\s+([A-Za-z_$][\w$]*)[^{]*\{/gu;
    const declarations = [];
    for (const match of sanitized.matchAll(classPattern)) {
        const annotationBlock = match[1] ?? '';
        const className = match[2] ?? '';
        const openBraceIndex = (match.index ?? 0) + match[0].length - 1;
        const closeBraceIndex = findMatchingBrace(sanitized, openBraceIndex);
        if (closeBraceIndex < 0) {
            continue;
        }
        declarations.push({
            className,
            ...(packageName ? { packageName } : {}),
            annotations: extractAnnotations(annotationBlock),
            body: sanitized.slice(openBraceIndex + 1, closeBraceIndex),
        });
    }
    return declarations;
}
function extractApplicationAnnotationNames(sourceFiles) {
    const applicationAnnotations = new Set(['SpringBootApplication']);
    let changed = true;
    while (changed) {
        changed = false;
        for (const sourceFile of sourceFiles) {
            const sanitized = stripJavaLikeCommentsAndStrings(sourceFile.contents);
            const annotationPattern = /((?:@\s*[\w.]+(?:\s*\([^)]*\))?\s*)*)public\s+@interface\s+([A-Za-z_$][\w$]*)\b/gu;
            for (const match of sanitized.matchAll(annotationPattern)) {
                const annotations = extractAnnotations(match[1] ?? '');
                const annotationName = match[2];
                if (annotationName &&
                    !applicationAnnotations.has(annotationName) &&
                    hasAnnotation(annotations, applicationAnnotations)) {
                    applicationAnnotations.add(annotationName);
                    changed = true;
                }
            }
        }
    }
    return applicationAnnotations;
}
function hasSpringApplicationRunForClass(body, className) {
    return new RegExp(`\\bSpringApplication\\.run\\s*\\(\\s*${className}\\.class\\b`, 'u').test(body);
}
function hasIndirectSpringApplicationRunForClass(body, className) {
    const helperCalls = [...body.matchAll(/\b([A-Za-z_$][\w$]*)\s*\(\s*([A-Za-z_$][\w$]*)\.class\s*,/gu)]
        .filter((match) => match[2] === className)
        .map((match) => match[1])
        .filter((methodName) => Boolean(methodName));
    return helperCalls.some((methodName) => new RegExp(`\\b${methodName}\\s*\\([^)]*\\)\\s*\\{[\\s\\S]*?\\bSpringApplication\\.run\\s*\\(`, 'u').test(body));
}
function hasConcreteSpringBootApplicationBootstrap(sourceFiles) {
    const applicationAnnotations = extractApplicationAnnotationNames(sourceFiles);
    const declarations = sourceFiles.flatMap((sourceFile) => extractConcreteClassDeclarations(sourceFile.contents));
    const applicationClassNames = new Set(declarations
        .filter((declaration) => hasAnnotation(declaration.annotations, applicationAnnotations))
        .map((declaration) => declaration.className));
    if (applicationClassNames.size === 0) {
        return false;
    }
    const hasLauncherForApplicationClass = (declaration) => javaMainMethodPattern.test(declaration.body) &&
        [...applicationClassNames].some((applicationClassName) => hasSpringApplicationRunForClass(declaration.body, applicationClassName) ||
            (applicationClassName === declaration.className &&
                hasIndirectSpringApplicationRunForClass(declaration.body, applicationClassName)));
    if (declarations.some(hasLauncherForApplicationClass)) {
        return true;
    }
    for (const sourceFile of sourceFiles) {
        for (const declaration of extractConcreteClassDeclarations(sourceFile.contents)) {
            if (!hasAnnotation(declaration.annotations, applicationAnnotations)) {
                continue;
            }
            if (javaMainMethodPattern.test(declaration.body) &&
                (hasSpringApplicationRunForClass(declaration.body, declaration.className) ||
                    hasIndirectSpringApplicationRunForClass(declaration.body, declaration.className))) {
                return true;
            }
        }
    }
    return false;
}
function hasConfiguredMainClassEntrypoint(contents, configuredMainClass) {
    for (const declaration of extractConcreteClassDeclarations(contents)) {
        if (!classNameMatchesConfiguredMainClass(declaration.className, declaration.packageName, configuredMainClass)) {
            continue;
        }
        if (javaMainMethodPattern.test(declaration.body)) {
            return true;
        }
    }
    return false;
}
function hasMavenMainEntrypoint(workspaceRoot, componentPath) {
    return hasConcreteSpringBootApplicationBootstrap(readMavenProductionSourceFiles(workspaceRoot, componentPath));
}
function hasMavenConfiguredMainClassEntrypoint(workspaceRoot, componentPath, configuredMainClass) {
    if (!configuredMainClass) {
        return false;
    }
    return readMavenProductionSourceFiles(workspaceRoot, componentPath)
        .some((sourceFile) => hasConfiguredMainClassEntrypoint(sourceFile.contents, configuredMainClass));
}
function hasMavenAutoConfigurationMetadata(workspaceRoot, componentPath) {
    const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
    return [
        path.join(absoluteRoot, 'src', 'main', 'resources', 'META-INF', 'spring.factories'),
        path.join(absoluteRoot, 'src', 'main', 'resources', 'META-INF', 'spring', 'org.springframework.boot.autoconfigure.AutoConfiguration.imports'),
    ].some(pathExists);
}
function hasMavenMainJvmSource(workspaceRoot, componentPath) {
    const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
    return [
        path.join(absoluteRoot, 'src', 'main', 'java'),
        path.join(absoluteRoot, 'src', 'main', 'kotlin'),
        path.join(absoluteRoot, 'src', 'main', 'scala'),
    ].some((root) => hasFileUnderRoot(root, /\.(?:java|kt|scala)$/iu));
}
function hasRuntimeSourceFiles(workspaceRoot, componentPath) {
    if (isUnderJvmResourceRoot(componentPath)) {
        return false;
    }
    const absoluteRoot = absoluteComponentRoot(workspaceRoot, componentPath);
    const sourceRoots = [
        path.join(absoluteRoot, 'src', 'main', 'java'),
        path.join(absoluteRoot, 'src'),
        path.join(absoluteRoot, 'app'),
        path.join(absoluteRoot, 'server'),
    ];
    for (const sourceRoot of sourceRoots) {
        if (!pathExists(sourceRoot)) {
            continue;
        }
        const entries = fs.readdirSync(sourceRoot, { recursive: true, withFileTypes: true });
        if (entries.some((entry) => entry.isFile() && /\.(?:ts|tsx|js|jsx|java|cs|py|php|go)$/iu.test(entry.name))) {
            return true;
        }
    }
    return false;
}
function resolveKind(workspaceRoot, candidate, localProjectKinds) {
    const lowerPath = candidate.path.toLowerCase();
    const packageJson = readPackageJson(workspaceRoot, candidate.path);
    const maven = readMavenMetadata(workspaceRoot, candidate.path);
    const hasRunnableFrontendEvidence = hasRunnableFrontendComponentEvidence(workspaceRoot, candidate.path);
    const hasPublishSurface = hasPackagePublishSurface(packageJson);
    const hasMavenRunnableEvidence = Boolean(maven &&
        (hasMavenMainEntrypoint(workspaceRoot, candidate.path) ||
            hasMavenConfiguredMainClassEntrypoint(workspaceRoot, candidate.path, maven.configuredMainClass) ||
            (maven.hasSpringBootPlugin && maven.hasSpringBootRepackageGoal && hasMavenMainEntrypoint(workspaceRoot, candidate.path)) ||
            (maven.packaging === 'war' && hasMavenMainEntrypoint(workspaceRoot, candidate.path))));
    const hasMavenLibraryEvidence = Boolean(maven &&
        !hasMavenRunnableEvidence &&
        (maven.packaging === 'jar' || maven.packaging === 'maven-plugin' || maven.packaging === 'pom') &&
        (hasMavenMainJvmSource(workspaceRoot, candidate.path) ||
            maven.hasMainResources ||
            maven.hasDependencyManagement ||
            maven.hasSpringBootAutoconfigureDependency ||
            maven.hasSpringBootAutoconfigureProcessor ||
            hasMavenAutoConfigurationMetadata(workspaceRoot, candidate.path) ||
            maven.hasDocumentationTooling ||
            maven.hasTestJava ||
            maven.hasTestResources));
    if (lowerPath.endsWith('/db') || lowerPath.endsWith('/database') || lowerPath.endsWith('/data')) {
        return 'data';
    }
    if (hasMavenLibraryEvidence) {
        return 'library';
    }
    if (maven && !hasMavenRunnableEvidence && localProjectKinds.primaryKind === 'backend') {
        return 'library';
    }
    const classification = candidate.architectureRoot?.classification;
    if (classification === 'frontend_app') {
        return hasPublishSurface && !hasRunnableFrontendEvidence ? 'library' : 'frontend';
    }
    if (classification === 'backend_app') {
        return 'backend';
    }
    if (hasRunnableFrontendEvidence &&
        localProjectKinds.primaryKind !== 'backend' &&
        localProjectKinds.primaryKind !== 'service' &&
        localProjectKinds.primaryKind !== 'cli') {
        return 'frontend';
    }
    if (classification === 'shared_package') {
        return 'library';
    }
    if (hasPublishSurface && !hasRunnableFrontendEvidence) {
        return 'library';
    }
    if (localProjectKinds.primaryKind === 'service') {
        return 'worker';
    }
    if (localProjectKinds.primaryKind === 'frontend') {
        return 'frontend';
    }
    if (localProjectKinds.primaryKind === 'backend' || localProjectKinds.primaryKind === 'fullstack') {
        return 'backend';
    }
    if (localProjectKinds.primaryKind === 'library' || localProjectKinds.primaryKind === 'cli') {
        return 'library';
    }
    if (lowerPath.startsWith('packages/') || lowerPath.startsWith('libs/')) {
        return 'library';
    }
    if (lowerPath.includes('infra') || lowerPath.includes('terraform')) {
        return 'infrastructure';
    }
    if (lowerPath.endsWith('/db') || lowerPath.endsWith('/database') || lowerPath.endsWith('/data')) {
        return 'data';
    }
    return 'unknown';
}
function resolveStack(kind, stackDetection) {
    if (kind === 'frontend') {
        return stackDetection.frontendStacks[0] ?? stackDetection.stacks[0];
    }
    if (kind === 'backend' || kind === 'worker') {
        return stackDetection.backendStacks.find((stack) => stack !== 'node_typescript' && stack !== 'node_javascript') ??
            stackDetection.backendStacks[0] ??
            stackDetection.stacks[0];
    }
    if (kind === 'infrastructure') {
        return stackDetection.stacks.find((stack) => stack === 'terraform' || stack === 'ansible');
    }
    if (kind === 'library' || kind === 'data') {
        return stackDetection.stacks.find((stack) => stack !== 'other' && stack !== 'unknown');
    }
    return stackDetection.stacks.find((stack) => stack !== 'other' && stack !== 'unknown');
}
function resolveConfidence(input) {
    if (input.kind !== 'unknown' &&
        (input.architectureRoot?.classification !== 'unknown' || input.stackConfidence === 'high')) {
        return 'high';
    }
    if (input.kind !== 'unknown' || input.stack || input.rootEvidenceCount >= 2) {
        return 'medium';
    }
    return 'low';
}
function hasAnyToken(value, patterns) {
    return patterns.some((pattern) => pattern.test(value));
}
function classifyGovernanceRole(input) {
    const packageJson = readPackageJson(input.workspaceRoot, input.candidate.path);
    const maven = readMavenMetadata(input.workspaceRoot, input.candidate.path);
    const evidence = [];
    let supportScore = 0;
    let runtimeScore = 0;
    const packageName = typeof packageJson?.name === 'string' ? packageJson.name : '';
    const description = typeof packageJson?.description === 'string' ? packageJson.description : '';
    const keywords = toStringArray(packageJson?.keywords).join(' ');
    const manifestText = `${packageName} ${description} ${keywords}`.toLowerCase();
    const descriptiveManifestText = `${description} ${keywords}`.toLowerCase();
    const pathText = input.candidate.path.toLowerCase();
    const scripts = recordKeys(packageJson?.scripts);
    const dependencies = recordKeys(packageJson?.dependencies);
    const devDependencies = recordKeys(packageJson?.devDependencies);
    const scriptText = scripts.join(' ').toLowerCase();
    const dependencyText = [...dependencies, ...devDependencies].join(' ').toLowerCase();
    const hasStrongSupportPath = hasAnyToken(pathText, [
        /(^|\/)(testing|test-plugins|cypress-tests|fixtures?|examples?|benchmarks?|smoke)(\/|$)/u,
        /(^|\/)(dev-server|scaffold|scaffolding|generators?|devkit|tooling|tools)(\/|$)/u,
    ]);
    const isPlainResourcesSurface = isUnderJvmResourceRoot(pathText) &&
        !hasRuntimeSourceFiles(input.workspaceRoot, input.candidate.path) &&
        !packageJson &&
        !maven;
    const hasPublishSurface = hasPackagePublishSurface(packageJson);
    const isPrivate = packageJson?.private === true;
    const hasBin = hasObject(packageJson?.bin) || typeof packageJson?.bin === 'string';
    const mavenMetadataText = `${maven?.artifactId ?? ''} ${maven?.name ?? ''}`.toLowerCase();
    const hasMavenMainSource = Boolean(maven?.hasMainJava || maven?.hasMainResources);
    const hasMavenTestSource = Boolean(maven?.hasTestJava || maven?.hasTestResources);
    const hasMavenMainEntrypointEvidence = Boolean(maven && hasMavenMainEntrypoint(input.workspaceRoot, input.candidate.path));
    const hasMavenMainJvmSourceEvidence = Boolean(maven && hasMavenMainJvmSource(input.workspaceRoot, input.candidate.path));
    const hasMavenAutoConfigurationEvidence = Boolean(maven && (hasMavenAutoConfigurationMetadata(input.workspaceRoot, input.candidate.path) ||
        maven.hasSpringBootAutoconfigureDependency ||
        maven.hasSpringBootAutoconfigureProcessor));
    const hasMavenExecutableConfiguration = Boolean(maven && (hasMavenConfiguredMainClassEntrypoint(input.workspaceRoot, input.candidate.path, maven.configuredMainClass) ||
        (maven.hasSpringBootPlugin && maven.hasSpringBootRepackageGoal && hasMavenMainEntrypointEvidence) ||
        (maven.packaging === 'war' && hasMavenMainEntrypointEvidence)));
    const hasMavenRunnableApplicationEvidence = Boolean(hasMavenMainEntrypointEvidence || hasMavenExecutableConfiguration);
    const isMavenPomOnly = Boolean(maven?.packaging === 'pom' && !hasMavenMainSource && !hasMavenTestSource);
    const hasMavenTestContext = hasAnyToken(pathText, [
        /(^|\/)(test-framework|testsuite|integration-tests?|test-apps)(\/|$)/u,
    ]) && (hasAnyToken(mavenMetadataText, [/\b(test|testsuite|test framework|arquillian|integration test)\b/u]) ||
        Boolean(maven?.hasTestLifecycleConfiguration || maven?.hasArquillianEvidence || hasMavenTestSource));
    const hasMavenBomEvidence = Boolean(maven?.packaging === 'pom' &&
        maven.hasDependencyManagement &&
        !hasMavenMainSource &&
        hasAnyToken(mavenMetadataText, [/\b(bom|bill of materials|dependency management|platform)\b/u]));
    const hasMavenDistributionPackagingEvidence = Boolean(maven?.packaging === 'pom' &&
        !hasMavenMainSource &&
        hasAnyToken(pathText, [/(^|\/)(distribution|dist|downloads|assembl(y|ies)|feature-packs?)(\/|$)/u]));
    if (input.candidate.architectureRoot?.classification === 'backend_app' || input.candidate.architectureRoot?.classification === 'frontend_app') {
        runtimeScore += 3;
        addEvidence(evidence, `Runtime architecture root signal: ${input.candidate.architectureRoot.classification}`);
    }
    else if (input.candidate.architectureRoot?.classification === 'shared_package') {
        runtimeScore += 1;
        addEvidence(evidence, 'Shared package architecture root signal');
    }
    if (input.localProjectKinds.primaryKind === 'backend' || input.localProjectKinds.primaryKind === 'frontend' || input.localProjectKinds.primaryKind === 'fullstack' || input.localProjectKinds.primaryKind === 'service') {
        runtimeScore += 2;
        addEvidence(evidence, `Runtime project kind signal: ${input.localProjectKinds.primaryKind}`);
    }
    if (input.kind === 'backend' || input.kind === 'frontend' || input.kind === 'worker') {
        runtimeScore += 1;
    }
    if (input.stack && input.stack !== 'node_typescript' && input.stack !== 'node_javascript') {
        runtimeScore += 1;
        addEvidence(evidence, `Runtime framework stack signal: ${input.stack}`);
    }
    if (hasPublishSurface && !isPrivate) {
        runtimeScore += 1;
        addEvidence(evidence, 'Published package surface signal');
    }
    else if (hasPublishSurface && !hasBin) {
        runtimeScore += 1;
        addEvidence(evidence, 'Package entrypoint/export surface signal');
    }
    if (maven?.hasPom) {
        addEvidence(evidence, `Maven packaging ${maven.packaging ?? 'jar'}`);
    }
    if (maven && (maven.packaging === 'jar' || maven.packaging === 'war') && hasMavenRunnableApplicationEvidence) {
        runtimeScore += 3;
        addEvidence(evidence, 'Maven executable application evidence');
    }
    if (maven?.packaging === 'war' && hasMavenMainJvmSourceEvidence) {
        runtimeScore += 1;
        addEvidence(evidence, 'Maven deployable WAR artifact');
    }
    if (maven && hasMavenMainSource && !hasMavenRunnableApplicationEvidence && !hasMavenTestContext && !hasMavenBomEvidence && maven.packaging !== 'maven-plugin') {
        addEvidence(evidence, 'Maven library main source/resource surface');
    }
    if (hasMavenAutoConfigurationEvidence && !hasMavenRunnableApplicationEvidence) {
        addEvidence(evidence, 'Maven Spring Boot auto-configuration/starter library evidence');
    }
    if (hasAnyToken(descriptiveManifestText, [/\b(e2e|end-to-end|testing|test helper|test utilities|fixtures?)\b/u])) {
        supportScore += 3;
        addEvidence(evidence, 'Package metadata describes test/support usage');
    }
    if (hasAnyToken(descriptiveManifestText, [/\b(scaffold|scaffolding|generator|codegen|template|starter|create)\b/u])) {
        supportScore += 3;
        addEvidence(evidence, 'Package metadata describes scaffolding/generation usage');
    }
    if (hasAnyToken(descriptiveManifestText, [/\b(dev server|development server|development tooling|tooling|devkit)\b/u])) {
        supportScore += 2;
        addEvidence(evidence, 'Package metadata describes development/tooling usage');
    }
    if (hasAnyToken(pathText, [
        /(^|\/)(test|tests|testing|__tests__|cypress|cypress-tests|fixtures?|examples?|benchmarks?|smoke)(\/|$)/u,
        /(^|\/)(dev-server|test-plugins|scaffold|scaffolding|generators?|devkit|tooling|tools)(\/|$)/u,
    ])) {
        supportScore += 1;
        addEvidence(evidence, 'Path contains support/tooling role tokens');
    }
    if (scripts.length > 0 && scripts.every((script) => /^(test|e2e|smoke|bench|benchmark|lint|typecheck|dev|dev:|load-test|generate|scaffold)/u.test(script))) {
        supportScore += 1;
        addEvidence(evidence, 'Package scripts are dominated by development/test workflows');
    }
    if (hasAnyToken(scriptText, [/\b(e2e|test:|load-test|scaffold|generate|dev:server|dev:worker)\b/u])) {
        supportScore += 1;
        addEvidence(evidence, 'Package scripts include development/test/support workflows');
    }
    if (isPrivate && supportScore > 0 && !hasPublishSurface) {
        supportScore += 1;
        addEvidence(evidence, 'Private package without runtime publish surface');
    }
    if (input.localProjectKinds.primaryKind === 'cli' && (isPrivate || supportScore > 0 || hasAnyToken(dependencyText, [/\b(commander|yargs|ts-morph|schematics|yeoman|inquirer|prompts)\b/u]))) {
        supportScore += 1;
        addEvidence(evidence, 'CLI/tooling project kind evidence');
    }
    if (maven?.packaging === 'maven-plugin' && input.candidate.path !== '.') {
        supportScore += 5;
        addEvidence(evidence, 'Maven plugin/build-tooling module');
    }
    if (maven?.hasDocumentationTooling && !hasMavenMainJvmSourceEvidence) {
        supportScore += 4;
        addEvidence(evidence, 'Maven documentation tooling without JVM runtime source');
    }
    if (hasMavenBomEvidence) {
        supportScore += 5;
        addEvidence(evidence, 'Maven BOM/dependency-management module without runtime source');
    }
    if (maven && hasMavenTestSource && !hasMavenMainSource) {
        supportScore += 5;
        addEvidence(evidence, 'Maven test-only source layout');
    }
    if (hasMavenTestContext) {
        supportScore += hasMavenMainSource ? 4 : 5;
        addEvidence(evidence, 'Maven test-framework/testsuite context with structural test evidence');
    }
    if (maven?.hasTestLifecycleConfiguration || maven?.hasArquillianEvidence) {
        supportScore += hasMavenMainSource ? 1 : 2;
        addEvidence(evidence, 'Maven test lifecycle or Arquillian evidence');
    }
    if (maven &&
        maven.totalDependencyCount > 0 &&
        maven.testScopedDependencyCount >= 3 &&
        maven.testScopedDependencyCount >= Math.ceil(maven.totalDependencyCount * 0.5) &&
        !hasMavenMainSource) {
        supportScore += 3;
        addEvidence(evidence, 'Maven dependencies are dominated by test scope without runtime source');
    }
    if (maven?.packaging === 'pom' && !hasMavenMainSource) {
        supportScore += 1;
        addEvidence(evidence, 'Maven POM-only aggregator/packaging module');
    }
    if (isPlainResourcesSurface) {
        supportScore += 4;
        addEvidence(evidence, 'Resources-only surface without runtime source or manifest evidence');
    }
    if (isPlainResourcesSurface &&
        input.kind !== 'infrastructure' &&
        input.stack !== 'terraform' &&
        input.stack !== 'ansible') {
        return {
            governanceRole: 'support',
            governanceEvidence: uniqueSorted(evidence),
            selectedByDefault: false,
        };
    }
    if (hasMavenDistributionPackagingEvidence &&
        hasAnyToken(mavenMetadataText, [/\b(docs?|documentation|metadata tests?|licenses?|maven plugins?|builds?)\b/u])) {
        supportScore += 3;
        addEvidence(evidence, 'Maven documentation/build/distribution packaging evidence without runtime source');
    }
    if ((supportScore >= 3 && supportScore > runtimeScore) ||
        (hasStrongSupportPath && supportScore >= 2 && runtimeScore <= 5) ||
        (maven?.packaging === 'maven-plugin' && input.candidate.path !== '.' && supportScore >= 5) ||
        (maven && supportScore >= 5 && !hasMavenMainSource) ||
        (maven && supportScore >= 4 && hasMavenTestContext) ||
        (maven && supportScore >= 4 && hasMavenDistributionPackagingEvidence &&
            hasAnyToken(mavenMetadataText, [/\b(docs?|documentation|metadata tests?|licenses?|maven plugins?|builds?)\b/u]))) {
        return {
            governanceRole: 'support',
            governanceEvidence: uniqueSorted(evidence),
            selectedByDefault: false,
        };
    }
    if (maven && (isMavenPomOnly || hasMavenDistributionPackagingEvidence) && supportScore >= 1) {
        return {
            governanceRole: 'support',
            governanceEvidence: uniqueSorted(evidence),
            selectedByDefault: false,
        };
    }
    if (runtimeScore >= 2) {
        return {
            governanceRole: 'runtime',
            governanceEvidence: uniqueSorted(evidence),
            selectedByDefault: true,
        };
    }
    return {
        governanceRole: 'ambiguous',
        governanceEvidence: uniqueSorted(evidence.length > 0 ? evidence : ['No strong support-only governance evidence']),
        selectedByDefault: true,
    };
}
function maybeAddAttributedApiAndDb(component, workspaceRoot, componentPath, architectureRoot, runContext) {
    const absoluteRoot = componentPath === '.'
        ? workspaceRoot
        : path.join(workspaceRoot, ...componentPath.split('/'));
    if (!pathExists(absoluteRoot)) {
        return;
    }
    const hints = runContext?.detectApiDbHints(absoluteRoot) ?? (0, apiDbHintDetection_1.detectApiDbHints)(absoluteRoot);
    const stackDetection = runContext?.detectStacks(absoluteRoot) ?? (0, stackDetection_1.detectStacks)(absoluteRoot);
    const selectedStackCandidate = stackDetection.candidates?.find((candidate) => candidate.adapterId === stackDetection.selectedAdapterId);
    if (architectureRoot?.hasApiBoundary &&
        hints.api.apiStyle !== 'unknown' &&
        hints.api.apiStyle !== 'none') {
        component.apiStyle = hints.api.apiStyle;
        addEvidence(component.evidence, `Attributed ${hints.api.apiStyle.toUpperCase()} API hints to ${componentPath}`);
    }
    if (architectureRoot?.hasDatabaseAccess &&
        hints.database.database !== 'unknown' &&
        hints.database.database !== 'none') {
        component.database = hints.database.database;
        addEvidence(component.evidence, `Attributed ${hints.database.database} database hints to ${componentPath}`);
    }
    if (!component.apiStyle &&
        (component.kind === 'backend' || component.kind === 'worker') &&
        (selectedStackCandidate?.apiStyleHint === 'rest' || selectedStackCandidate?.apiStyleHint === 'graphql')) {
        component.apiStyle = selectedStackCandidate.apiStyleHint;
        addEvidence(component.evidence, `Attributed ${selectedStackCandidate.apiStyleHint.toUpperCase()} API hints from local stack evidence`);
    }
}
function detectComponents(input) {
    const ignoreMatcher = input.ignoreMatcher ?? (0, archpilotIgnore_1.loadArchpilotIgnoreMatcher)(input.workspaceRoot);
    const manifestBackedRoots = new Set((input.topology.detectedRoots ?? []).filter((root) => !isUnderJvmResourceRoot(root)));
    const rootCandidates = buildRootCandidates(input)
        .filter((candidate) => !ignoreMatcher.isIgnored(candidate.path))
        .filter((candidate) => !isIgnoredComponentCandidatePath(candidate.path))
        .filter((candidate) => !(isUnderJvmResourceRoot(candidate.path) && !hasRuntimeSourceFiles(input.workspaceRoot, candidate.path)))
        .filter((candidate) => {
        if (input.topology.topology !== 'monorepo' || manifestBackedRoots.size === 0) {
            return true;
        }
        if (candidate.path === '.') {
            return true;
        }
        const firstSegment = candidate.path.split('/')[0];
        if (!['apps', 'packages', 'services', 'libs'].includes(firstSegment ?? '')) {
            return true;
        }
        return manifestBackedRoots.has(candidate.path) || hasManifest(input.workspaceRoot, candidate.path);
    });
    const usedIds = new Set();
    const detectedComponents = [];
    const summaryEvidence = [];
    for (const candidate of rootCandidates) {
        const absoluteRoot = candidate.path === '.'
            ? input.workspaceRoot
            : path.join(input.workspaceRoot, ...candidate.path.split('/'));
        if (!pathExists(absoluteRoot)) {
            continue;
        }
        const localProjectKinds = input.runContext?.detectProjectKinds(absoluteRoot) ?? (0, projectKindDetection_1.detectProjectKinds)(absoluteRoot);
        const stackDetection = input.runContext?.detectStacks(absoluteRoot) ?? (0, stackDetection_1.detectStacks)(absoluteRoot);
        const kind = resolveKind(input.workspaceRoot, candidate, localProjectKinds);
        const stack = resolveStack(kind, stackDetection);
        const evidence = [...candidate.evidence];
        if (candidate.architectureRoot?.classification && candidate.architectureRoot.classification !== 'unknown') {
            addEvidence(evidence, `Classified from architecture root signal: ${candidate.architectureRoot.classification}`);
        }
        if (localProjectKinds.primaryKind !== 'unknown') {
            addEvidence(evidence, `Detected project kind ${localProjectKinds.primaryKind}`);
        }
        if (stack && stack !== 'other' && stack !== 'unknown') {
            const label = stackMetadata_1.canonicalStackLabels[stack] ?? stack;
            addEvidence(evidence, `Detected stack ${label}`);
        }
        const component = {
            id: resolveId(candidate.path, usedIds),
            name: resolveName(input.workspaceRoot, candidate.path),
            kind,
            path: candidate.path,
            ...(stack && stack !== 'other' && stack !== 'unknown' ? { stack } : {}),
            confidence: resolveConfidence({
                kind,
                stack,
                stackConfidence: stackDetection.confidence,
                architectureRoot: candidate.architectureRoot,
                rootEvidenceCount: evidence.length,
            }),
            evidence: uniqueSorted(evidence),
        };
        maybeAddAttributedApiAndDb(component, input.workspaceRoot, candidate.path, candidate.architectureRoot, input.runContext);
        const governance = classifyGovernanceRole({
            workspaceRoot: input.workspaceRoot,
            candidate,
            kind,
            stack,
            localProjectKinds,
        });
        component.governanceRole = governance.governanceRole;
        component.governanceEvidence = governance.governanceEvidence;
        component.selectedByDefault = governance.selectedByDefault;
        for (const item of governance.governanceEvidence) {
            addEvidence(component.evidence, item);
        }
        component.evidence = uniqueSorted(component.evidence);
        detectedComponents.push(component);
        addEvidence(summaryEvidence, `Detected ${component.kind} component at ${component.path}`);
    }
    const confidence = detectedComponents.some((component) => component.confidence === 'high')
        ? 'high'
        : detectedComponents.some((component) => component.confidence === 'medium')
            ? 'medium'
            : 'low';
    return {
        detectedComponents: detectedComponents.sort((left, right) => left.path.localeCompare(right.path)),
        confidence,
        evidence: summaryEvidence.length > 0
            ? uniqueSorted(summaryEvidence)
            : ['No clear component roots detected'],
    };
}
//# sourceMappingURL=componentDetection.js.map