"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LANGUAGES = void 0;
exports.getLanguage = getLanguage;
exports.detectActiveLanguages = detectActiveLanguages;
exports.languageForFile = languageForFile;
exports.activeLanguagesFromStack = activeLanguagesFromStack;
exports.activeLanguagesFromFlags = activeLanguagesFromFlags;
exports.allSourceExtensions = allSourceExtensions;
exports.allTestFilePatterns = allTestFilePatterns;
exports.allAutogenSourcePatterns = allAutogenSourcePatterns;
exports.allDocCommentPatterns = allDocCommentPatterns;
exports.allTlsBypassPatterns = allTlsBypassPatterns;
exports.allExportDetectionDeclarations = allExportDetectionDeclarations;
exports.allClocLanguageNames = allClocLanguageNames;
exports.allPrimaryComponentPaths = allPrimaryComponentPaths;
exports.allRoutePaths = allRoutePaths;
exports.allModelPaths = allModelPaths;
exports.allTestGapPriorityPaths = allTestGapPriorityPaths;
exports.dominantVocabulary = dominantVocabulary;
exports.splitTestFilePatterns = splitTestFilePatterns;
exports.buildDevcontainerFeatures = buildDevcontainerFeatures;
exports.buildDevcontainerExtensions = buildDevcontainerExtensions;
const csharp_1 = require("./csharp");
const go_1 = require("./go");
const python_1 = require("./python");
const rust_1 = require("./rust");
const typescript_1 = require("./typescript");
const kotlin_1 = require("./kotlin");
const java_1 = require("./java");
const ruby_1 = require("./ruby");
exports.LANGUAGES = [
    python_1.python,
    typescript_1.typescript,
    csharp_1.csharp,
    go_1.go,
    rust_1.rust,
    kotlin_1.kotlin,
    java_1.java,
    ruby_1.ruby,
];
function getLanguage(id) {
    return exports.LANGUAGES.find((l) => l.id === id);
}
function detectActiveLanguages(cwd) {
    return exports.LANGUAGES.filter((l) => l.detect(cwd));
}
/**
 * Map a source-file path to its owning language pack by extension,
 * using each pack's declared `sourceExtensions` (pack-driven per
 * CLAUDE.md Rule 6 — no hardcoded ext→lang table). Returns undefined
 * for files no registered pack claims. Longest-extension-wins so a
 * compound extension (e.g. a future `.d.ts`) beats a shorter one.
 */
function languageForFile(filePath) {
    const lower = filePath.toLowerCase();
    let best;
    let bestLen = -1;
    for (const lang of exports.LANGUAGES) {
        for (const ext of lang.sourceExtensions) {
            const e = ext.toLowerCase();
            if (lower.endsWith(e) && e.length > bestLen) {
                best = lang;
                bestLen = e.length;
            }
        }
    }
    return best;
}
/**
 * Map a `DetectedStack` (or `ResolvedConfig`, which extends it) to the
 * set of `LanguageSupport` packs that are active for the project.
 * Pack-driven via `DetectedStack.languages` keyed on `LanguageId` —
 * adding a pack means extending `LanguageId` + registering in
 * `LANGUAGES`; this function never changes.
 */
function activeLanguagesFromStack(stack) {
    return activeLanguagesFromFlags(stack.languages);
}
/**
 * Same as `activeLanguagesFromStack`, but for callers who only have
 * the `languages` sub-shape (e.g. `tool-registry.ts:buildRequiredTools`
 * receives `DetectedStack['languages']`, not the full stack).
 */
function activeLanguagesFromFlags(flags) {
    return exports.LANGUAGES.filter((l) => flags[l.id] ?? false);
}
/**
 * All source-file extensions across every registered pack, deduplicated.
 * The pack-driven analog of the pre-LP.3 hardcoded
 * `'.ts .tsx .js .jsx .py .go .rs .cs'` constant in `generic.ts` —
 * grows automatically as new packs land.
 */
function allSourceExtensions() {
    return [...new Set(exports.LANGUAGES.flatMap((l) => l.sourceExtensions))];
}
/**
 * All test-file patterns across every registered pack, deduplicated.
 * Patterns without a slash are basename-style (matched by find `-name`);
 * patterns containing a slash are path-anchored (e.g. Rust's
 * tests-directory glob for integration tests) and need find
 * `-path` semantics — see `splitTestFilePatterns()`.
 */
function allTestFilePatterns() {
    return [...new Set(exports.LANGUAGES.flatMap((l) => l.testFilePatterns))];
}
/**
 * All auto-generated source-file basename patterns across every
 * registered pack, deduplicated. D028 (2.4.7): consumers (currently
 * `src/analyzers/tools/generic.ts`) build find `-not -name` excludes
 * from this list to keep per-file metrics from being inflated by
 * generated code (designer.cs, *.pb.go, *Generated.java, etc.).
 *
 * Packs that don't declare `autogeneratedSourcePatterns` contribute
 * nothing — the helper returns the union of all declared patterns
 * across active+inactive packs. Conservative by design: a designer.cs
 * file in a polyglot repo with both csharp and go active is still
 * filtered even if the csharp pack happens to be inactive in this
 * cwd's scope.
 */
function allAutogenSourcePatterns() {
    return [...new Set(exports.LANGUAGES.flatMap((l) => l.autogeneratedSourcePatterns ?? []))];
}
/**
 * All doc-comment regex patterns across every registered pack,
 * deduplicated. D027 (2.4.7): consumed by `generic.ts` to build a
 * union grep alternation for `docCommentFiles`. Empty array when no
 * pack declares any patterns — caller short-circuits to 0.
 *
 * Scope mirrors `allAutogenSourcePatterns`: union across all packs
 * (active + inactive). A `///` line in a `.cs` file inside a
 * polyglot repo gets counted even if csharp isn't the dominant pack
 * — the false-positive rate across language boundaries is negligible
 * (e.g. `///` doesn't appear in `.py` files).
 */
function allDocCommentPatterns() {
    return [...new Set(exports.LANGUAGES.flatMap((l) => l.docCommentPatterns ?? []))];
}
/**
 * All TLS-bypass regex patterns across every registered pack,
 * deduplicated. D034 (2.4.7): consumed by `generic.ts` to build a
 * union grep alternation for `tlsDisabledCount` — a Security-score
 * input that previously matched only Node-shaped idioms.
 *
 * Scope mirrors `allDocCommentPatterns`: union across all packs
 * (active + inactive). A `.cs` file containing
 * `ServerCertificateValidationCallback = ...` in a polyglot repo
 * gets flagged regardless of which pack the customer "primarily" uses
 * — security findings should never be silently scoped out.
 */
function allTlsBypassPatterns() {
    return [...new Set(exports.LANGUAGES.flatMap((l) => l.tlsBypassPatterns ?? []))];
}
function allExportDetectionDeclarations() {
    return exports.LANGUAGES.flatMap((l) => l.exportDetection
        ? [
            {
                pack: l.id,
                reliability: l.exportDetection.reliability,
                strategy: l.exportDetection.strategy,
            },
        ]
        : []);
}
/**
 * Cloc language names declared by every pack, deduplicated. D073
 * (2.4.7): consumed by `gatherClocMetrics` to filter cloc's per-
 * language summary + `totalLines` aggregation down to "actual source
 * code" — JSON / XML / CSV / Markdown that cloc emits stay out of the
 * source-line counters that quality's Comment Ratio + health's
 * Documentation derive from. Pre-D073 cloc's `SUM` summed every
 * language including markup/data, deflating the .NET WinForms
 * benchmark's comment ratio (1.6M JSON lines dragged 25%-true C#
 * comment ratio down to
 * 4.3%).
 *
 * Scope mirrors `allAutogenSourcePatterns` — union across active +
 * inactive packs. A `.cs` file in a polyglot repo gets counted even
 * when csharp isn't the detect-time dominant pack, which is fine:
 * cloc names map to languages, not pack-presence signals.
 */
function allClocLanguageNames() {
    return [...new Set(exports.LANGUAGES.flatMap((l) => l.clocLanguageNames ?? []))];
}
/**
 * Union of every active pack's `architecturalShape.primaryComponentPaths`,
 * deduplicated. Consumed by `gatherGenericMetrics` to count the
 * primary-component file metric (`HealthMetrics.controllers`) and by
 * the test-gap classifier in `analyzers/tests/gather.ts` to populate
 * the MEDIUM bucket by default.
 *
 * Pre-extension, generic.ts hardcoded `controllers/`, `handlers/`,
 * `views/` and gather.ts hardcoded the same set inline — a pure
 * React frontend or .NET WinForms desktop app matched zero paths
 * and reported empty MEDIUM/HIGH test-gap buckets. Pack-driven
 * union grows automatically as new packs land.
 *
 * Scope is active-only: matching `<dxkit health>` analyzes the
 * project the user is in, not every pack that exists. This keeps
 * Python pack's `views/` from incorrectly matching a Java repo's
 * Spring MVC views layer (cross-pack false positives).
 */
function allPrimaryComponentPaths(flags) {
    return [
        ...new Set(activeLanguagesFromFlags(flags).flatMap((l) => l.architecturalShape?.primaryComponentPaths ?? [])),
    ];
}
/**
 * Union of every active pack's `architecturalShape.routePaths`,
 * deduplicated. Consumed by `gatherGenericMetrics` to populate
 * `HealthMetrics.routeHandlerFiles` — the count gating the "Add API
 * documentation" health action. A WinForms desktop app (.NET pack
 * active but no `Controllers/` directory) reports zero here even when
 * `primaryComponentPaths` matches Forms/ViewModels, so the action
 * stays correctly silenced.
 */
function allRoutePaths(flags) {
    return [
        ...new Set(activeLanguagesFromFlags(flags).flatMap((l) => l.architecturalShape?.routePaths ?? [])),
    ];
}
/**
 * Union of every active pack's `architecturalShape.modelPaths`,
 * deduplicated. Consumed by `gatherGenericMetrics` to populate
 * `HealthMetrics.models` (the "data model files" count surfaced in
 * Maintainability prose).
 */
function allModelPaths(flags) {
    return [
        ...new Set(activeLanguagesFromFlags(flags).flatMap((l) => l.architecturalShape?.modelPaths ?? [])),
    ];
}
/**
 * Per-bucket union of active packs' test-gap path patterns. Empty
 * arrays for any bucket no pack declares. Consumed by
 * `analyzers/tests/gather.ts:classifyRisk` to tier source files into
 * the CRITICAL / HIGH / MEDIUM / LOW buckets.
 *
 * The `medium` bucket defaults to a pack's `primaryComponentPaths`
 * when its `testGapPriority.medium` is omitted — the common case
 * being "any primary component without a matching test is MEDIUM
 * risk at minimum."
 */
function allTestGapPriorityPaths(flags) {
    const active = activeLanguagesFromFlags(flags);
    const critical = new Set();
    const high = new Set();
    const medium = new Set();
    for (const pack of active) {
        const shape = pack.architecturalShape;
        if (!shape)
            continue;
        for (const p of shape.testGapPriority?.critical ?? [])
            critical.add(p);
        for (const p of shape.testGapPriority?.high ?? [])
            high.add(p);
        if (shape.testGapPriority?.medium && shape.testGapPriority.medium.length > 0) {
            for (const p of shape.testGapPriority.medium)
                medium.add(p);
        }
        else {
            // Default: primary components fall into MEDIUM when no explicit
            // bucket is declared. Lets packs declare just primaryComponentPaths
            // and get a sensible test-gap tiering for free.
            for (const p of shape.primaryComponentPaths ?? [])
                medium.add(p);
        }
    }
    return { critical: [...critical], high: [...high], medium: [...medium] };
}
/**
 * Pick the dominant vocabulary for prose rendering. When cloc data is
 * available, the pack with the most source lines wins — the
 * source-files-weighted heuristic plan v4 anchored to. When cloc
 * data isn't available, falls back to registry order. Returns null
 * when no active pack provides a vocabulary — callers fall through
 * to generic words ("components", "models", "routes").
 *
 * The cloc weight closes a polyglot-detection edge case: a
 * 3,000-file C# repo with one stray build-output `.py` file would
 * activate both csharp and python packs, and registry-order picked
 * python's vocabulary (first declared) despite csharp dominating by
 * 4 orders of magnitude. Source-line weighting picks the pack the
 * code is actually written in.
 */
function dominantVocabulary(flags, clocLanguages) {
    const active = activeLanguagesFromFlags(flags);
    const ranked = clocLanguages && clocLanguages.length > 0
        ? [...active].sort((a, b) => countPackLines(b, clocLanguages) - countPackLines(a, clocLanguages))
        : active;
    for (const pack of ranked) {
        const v = pack.architecturalShape?.vocabulary;
        if (v && (v.components || v.models || v.routes))
            return v;
    }
    return null;
}
/**
 * Sum cloc-reported line counts for every language name this pack
 * claims to own. Returns 0 when the pack declares no cloc names or
 * none of them appear in the cloc summary — that pack contributes
 * no signal to the weighting and falls behind packs that did
 * produce real source lines.
 */
function countPackLines(pack, clocLanguages) {
    const names = pack.clocLanguageNames ?? [];
    if (names.length === 0)
        return 0;
    let total = 0;
    for (const entry of clocLanguages) {
        if (names.includes(entry.name))
            total += entry.lines;
    }
    return total;
}
/**
 * Split test-file patterns into the two shapes find treats differently:
 * basename patterns (matched via `-name`) and path-anchored patterns
 * (matched via `-path`). Pre-LP.3, generic.ts used only `-name` and
 * silently missed Rust's integration tests under the tests directory
 * because that pattern doesn't match a basename.
 */
function splitTestFilePatterns(patterns = allTestFilePatterns()) {
    return {
        nameOnly: patterns.filter((p) => !p.includes('/')),
        pathAnchored: patterns.filter((p) => p.includes('/')),
    };
}
/**
 * Build the devcontainer `features` block for a given detected stack.
 * Returns a `{ [featureName]: opts }` map ready to serialize as the
 * value of `features` in `.devcontainer/devcontainer.json`.
 *
 * Always-on entries (Node — dxkit's own runtime; GitHub CLI) land
 * regardless of detected stack so the post-create script can run npm
 * and `gh` even on non-Node projects. Per-pack entries layer on top
 * via object-key dedup, so the typescript pack's node feature
 * overrides the always-on default (e.g. with a different version).
 *
 * Pre-Phase-2.5.1 the features block was a static JSON object that
 * unconditionally enabled every toolchain dxkit supports — pure-TS
 * repos still pulled .NET / Ruby / Java / Rust toolchains (~25 min
 * of unused image build). This helper drives a stack-aware generation
 * path that's testable in isolation.
 */
function buildDevcontainerFeatures(flags) {
    const features = {
        // Always-on: dxkit's own runtime + the gh CLI used by
        // setup-branch-protection / setup-prebuild / PR review.
        'ghcr.io/devcontainers/features/node:1': { version: '22', nvmVersion: 'latest' },
        'ghcr.io/devcontainers/features/github-cli:1': {},
    };
    for (const lang of activeLanguagesFromFlags(flags)) {
        if (lang.devcontainerFeature) {
            features[lang.devcontainerFeature.name] = lang.devcontainerFeature.opts ?? {};
        }
    }
    return features;
}
/**
 * Union the VSCode extensions across active language packs and the
 * always-on dxkit / GitHub baseline. Companion to
 * `buildDevcontainerFeatures` — features install the toolchain
 * (compiler / runtime); extensions drop the editor support (syntax,
 * lint, debug).
 *
 * Always-on extensions are anthropic.claude-code (Claude Code agent
 * surface — the six dxkit-* skills live under it), GitHub Actions +
 * Pull Requests (workflow + PR review skills target them). Per-pack
 * extensions come from `LanguageSupport.devcontainerExtensions`.
 *
 * Output is order-stable (always-on first, then per-pack in language
 * registration order) and deduplicated — a pack accidentally listing
 * an always-on extension, or two packs sharing one, can't generate
 * a duplicate entry. The renderer relies on stable order so unchanged
 * stacks produce byte-identical devcontainer.json output.
 *
 * Pre-extension the devcontainer.json template hardcoded the extensions
 * list with every language's extension. Pure-TS repos pulled the go,
 * rust, csharp, java, kotlin, ruby extensions on every container start
 * (~6 extensions for stacks that don't use those languages); this
 * helper trims to only the active packs' editor support.
 */
function buildDevcontainerExtensions(flags) {
    const seen = new Set();
    const out = [];
    const add = (id) => {
        if (seen.has(id))
            return;
        seen.add(id);
        out.push(id);
    };
    // Always-on. Order matters here for output stability.
    add('anthropic.claude-code');
    add('github.vscode-github-actions');
    add('github.vscode-pull-request-github');
    for (const lang of activeLanguagesFromFlags(flags)) {
        for (const ext of lang.devcontainerExtensions ?? [])
            add(ext);
    }
    return out;
}
//# sourceMappingURL=index.js.map