Assessor_2.0/.claude/hooks/core-areas.mjs

88 lines
4.4 KiB
JavaScript

// Core Engine area map shared by core-guard.mjs and core-release.mjs (AGENTS.md, "Core Engine and engines").
// Paths are repository-relative and lowercase; the first matching prefix wins.
import path from 'node:path';
export const RELEASE_FILE = '.claude/core-release.json';
export const ALL_AREAS = 'alle';
export const GOVERNANCE = 'Governance';
export const WHOLE_REPOSITORY = 'Gesamtes Repository';
// Engine paths precede the Core areas they sit in.
export const ENGINE_PATHS = [
['Context Engine', ['src/assessment-context/', 'src/features/context-layer/', 'tests/assessment-context/',
'tests/features/finding-context-layer.test.tsx', 'tests/features/pathfinder-context-layer.test.tsx',
'tests/features/context-cluster-facet.test.tsx', 'tests/context-toggle-browser-check.mjs']],
['Report Engine', ['src/reporting/', 'server/reporting/', 'tests/reporting/', 'tests/server/report-archive.test.ts',
'tests/postgres/report-read-only-runtime.test.ts']],
];
export const CORE_AREAS = [
[GOVERNANCE, ['agents.md', 'claude.md', '.claude/hooks/', '.claude/settings.json']],
['Disclosure', ['src/domain/result-db-disclosure.ts', 'src/exports/']],
['Ingest', ['src/importers/', 'src/recognition/', 'src/profiles/', 'src/analysis/', 'src/gpo/', 'server/gpo/',
'server/import-executor.ts', 'server/import-worker-client.ts', 'server/import-worker.ts',
'server/node-import-runtime-adapters.ts', 'server/artifacts/']],
['Normalization', ['src/domain/']],
['Evaluation', ['src/evaluation/', 'src/comparison/', 'src/scoring/']],
['Store', ['src/persistence/', 'server/postgres/', 'server/repository-factory.ts', 'docker-compose.postgres.yml']],
['Reference Catalogs', ['src/references/', 'server/reference-data/']],
['Shared Kernel', ['src/shared/', 'src/vite-env.d.ts']],
['Presentation', ['src/app/', 'src/features/', 'src/components/', 'src/styles/', 'src/hooks/', 'src/assets/',
'src/main.tsx', 'index.html']],
['Platform', ['server/', 'src/api/', 'src/platform/', 'src/legacy/', 'package.json', 'package-lock.json',
'vite.config.ts', 'vite.client.config.ts', 'vite.server.config.ts', 'vitest.config.ts', 'tsconfig.json',
'tsconfig.app.json', 'tsconfig.node.json', 'tsconfig.server.json', 'dockerfile', 'docker-compose.deploy.yml',
'deploy/', 'scripts/']],
['Core Tests', ['tests/', 'fixtures/']],
// Any other source path stays Core until it is registered as an engine above.
['Unclassified Source', ['src/']],
];
// Spellings accepted after "Core-Freigabe:", mapped to the area names above.
const AREA_ALIASES = new Map([
['alle', ALL_AREAS], ['all', ALL_AREAS], ['core', ALL_AREAS],
['governance', GOVERNANCE],
['disclosure', 'Disclosure'], ['result db', 'Disclosure'],
['ingest', 'Ingest'], ['import', 'Ingest'],
['normalization', 'Normalization'], ['normalisierung', 'Normalization'],
['evaluation', 'Evaluation'], ['bewertung', 'Evaluation'], ['scoring', 'Evaluation'],
['store', 'Store'], ['persistenz', 'Store'], ['datenbank', 'Store'],
['reference catalogs', 'Reference Catalogs'], ['referenzkataloge', 'Reference Catalogs'],
['shared kernel', 'Shared Kernel'],
['presentation', 'Presentation'], ['präsentation', 'Presentation'], ['praesentation', 'Presentation'], ['ui', 'Presentation'],
['platform', 'Platform'], ['plattform', 'Platform'],
['core tests', 'Core Tests'], ['tests', 'Core Tests'],
['unclassified source', 'Unclassified Source'],
]);
export function resolveAreaName(raw) {
return AREA_ALIASES.get(raw.trim().toLowerCase().replace(/\s+/g, ' ')) ?? null;
}
export function relativeToProject(filePath, projectDir) {
const normalized = path.resolve(projectDir, filePath);
const relative = path.relative(projectDir, normalized).split(path.sep).join('/');
if (relative.startsWith('..') || path.isAbsolute(relative)) return null;
return relative.toLowerCase();
}
function matchPrefix(relative, table) {
for (const [name, prefixes] of table) {
if (prefixes.some((prefix) => relative === prefix || relative.startsWith(prefix))) return name;
}
return null;
}
export function classifyPath(relative) {
if (relative === null) return null;
if (matchPrefix(relative, ENGINE_PATHS)) return null;
return matchPrefix(relative, CORE_AREAS);
}
export function isReleased(area, releasedAreas) {
if (releasedAreas.includes(area)) return true;
// "alle" never covers Governance: the guard and these rules must always be named.
return area !== GOVERNANCE && releasedAreas.includes(ALL_AREAS);
}