// engine/project-drift-detector.ts — SPEC-666: Detect project stack drift from git commit data
// Dependency manifest filenames — changes signal potential new deps
const DEP_FILENAMES = new Set([
    'package.json',
    'go.mod',
    'requirements.txt',
    'Cargo.toml',
    'pom.xml',
    'build.gradle',
    'build.gradle.kts',
    'pyproject.toml',
    'Gemfile',
    'composer.json',
]);
// Well-known extensions that don't signal stack drift
const BASELINE_EXTENSIONS = new Set([
    '.ts',
    '.tsx',
    '.js',
    '.jsx',
    '.mjs',
    '.cjs',
    '.json',
    '.jsonc',
    '.md',
    '.mdx',
    '.sh',
    '.bash',
    '.zsh',
    '.txt',
    '.yaml',
    '.yml',
    '.toml',
    '.lock',
    '.sum',
    '.mod',
    '.py',
    '.pyx',
    '.go',
    '.rs',
    '.java',
    '.kt',
    '.kts',
    '.rb',
    '.php',
    '.cs',
    '.dart',
    '.swift',
    '.html',
    '.htm',
    '.css',
    '.scss',
    '.sass',
    '.less',
    '.vue',
    '.svelte',
    '.env',
    '.gitignore',
    '.gitattributes',
    '.prettierrc',
    '.eslintrc',
    '.test',
    '.spec',
    '.sql',
    '.csv',
    '.xml',
    '.gradle',
    '.properties',
    '.cfg',
    '.ini',
]);
/**
 * Extract the filename (without directory) from a file path.
 * Pure function — no I/O.
 */
function basename(filePath) {
    const parts = filePath.split('/');
    return parts[parts.length - 1] ?? filePath;
}
/**
 * Extract the file extension from a path, including the dot.
 * Returns empty string for files with no extension.
 */
function extension(filePath) {
    const base = basename(filePath);
    const dot = base.lastIndexOf('.');
    if (dot <= 0) {
        return '';
    }
    return base.slice(dot).toLowerCase();
}
/**
 * SPEC-666 AC2: Detect new dependency signals from a list of changed file paths.
 * Returns one signal per dependency manifest file found in the changeset.
 */
export function detectDepChanges(changedFiles, detectedAt) {
    const signals = [];
    for (const file of changedFiles) {
        const name = basename(file);
        if (DEP_FILENAMES.has(name)) {
            signals.push({ type: 'new-dep', value: name, detectedAt });
        }
    }
    return signals;
}
/**
 * SPEC-666 AC3: Detect new file extension signals from a list of changed file paths.
 * Only returns signals for extensions not in the baseline set.
 */
export function detectNewExtensions(changedFiles, baselineExtensions = BASELINE_EXTENSIONS, detectedAt) {
    const signals = [];
    const seen = new Set();
    for (const file of changedFiles) {
        const ext = extension(file);
        if (ext.length > 1 && !baselineExtensions.has(ext) && !seen.has(ext)) {
            seen.add(ext);
            signals.push({ type: 'new-file-type', value: ext, detectedAt });
        }
    }
    return signals;
}
/**
 * SPEC-666 AC2+AC3: Analyze a DriftCommitRecord and return all drift signals.
 * Pure function — no I/O.
 */
export function analyzeCommitRecord(record) {
    const signals = [
        ...detectDepChanges(record.files, record.ts),
        ...detectNewExtensions(record.files, BASELINE_EXTENSIONS, record.ts),
    ];
    return signals;
}
/**
 * SPEC-666 AC5: Check if a list of changed files contains any drift signals.
 * Returns true if no drift — the hook can exit immediately.
 */
export function hasNoDrift(changedFiles) {
    if (changedFiles.length === 0) {
        return true;
    }
    const now = new Date().toISOString();
    const signals = analyzeCommitRecord({ ts: now, files: changedFiles });
    return signals.length === 0;
}
/** BASELINE_EXTENSIONS exported for testing. */
export { BASELINE_EXTENSIONS };
//# sourceMappingURL=project-drift-detector.js.map