feat: brain-config classifyFilePath стемы нормативки config-driven — greenfield #3 observer
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -357,7 +357,16 @@ function collectToolResultText(turn) {
|
||||
// Pass 3 — path-pattern classifier (project-brain-factor-analysis-4passes).
|
||||
// Returns one of: test / config / spec / norm / data / src / other.
|
||||
// Priority order matters (test before src, norm before src, etc).
|
||||
export function classifyFilePath(path) {
|
||||
//
|
||||
// Greenfield #3-observer: project-normative stems are config-driven (design
|
||||
// 2026-06-16-greenfield-regex-names-config-design §5). Default = the three Лидерра
|
||||
// stems → byte-identical to the previous hardcoded patterns; a greenfield project
|
||||
// supplies its own via loadConfig().normative_files → docStem (wired in observer-stop-hook).
|
||||
export const DEFAULT_NORMATIVE_STEMS = Object.freeze(['Pravila_raboty_Claude', 'Plugin_stack_rules', 'Tooling']);
|
||||
|
||||
const _escRe = (s) => String(s).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
export function classifyFilePath(path, normativeStems = DEFAULT_NORMATIVE_STEMS) {
|
||||
if (!path) return 'other';
|
||||
const p = String(path).replace(/\\/g, '/');
|
||||
const base = p.split('/').pop() || p;
|
||||
@@ -366,11 +375,12 @@ export function classifyFilePath(path) {
|
||||
if (/\.(?:test|spec)\.[a-z0-9]+$/i.test(base)) return 'test';
|
||||
if (/(?:^|\/)(?:tests?|spec)\//i.test(p)) return 'test';
|
||||
|
||||
// 2. normative documents (CLAUDE.md / Pravila / PSR / Tooling / Открытые_вопросы / memory store).
|
||||
// 2. normative documents. Universal docs (CLAUDE.md / Открытые_вопросы / MEMORY.md / memory
|
||||
// store) stay hardcoded; project-normative docs match config stems (default = Лидерра quintet).
|
||||
if (/(?:^|\/)CLAUDE\.md$/i.test(p)) return 'norm';
|
||||
if (/(?:^|\/)Pravila_raboty_Claude[^/]*\.md$/i.test(p)) return 'norm';
|
||||
if (/(?:^|\/)Plugin_stack_rules[^/]*\.md$/i.test(p)) return 'norm';
|
||||
if (/(?:^|\/)Tooling[^/]*\.md$/i.test(p)) return 'norm';
|
||||
for (const stem of (normativeStems || [])) {
|
||||
if (stem && new RegExp(`(?:^|/)${_escRe(stem)}[^/]*\\.md$`, 'i').test(p)) return 'norm';
|
||||
}
|
||||
if (/(?:^|\/)Открытые_вопросы[^/]*\.md$/i.test(p)) return 'norm';
|
||||
if (/(?:^|\/)MEMORY\.md$/i.test(p)) return 'norm';
|
||||
if (/\/memory\/[^/]+\.md$/i.test(p)) return 'norm';
|
||||
@@ -395,10 +405,10 @@ export function classifyFilePath(path) {
|
||||
|
||||
const FILE_TYPE_CATEGORIES = ['src', 'test', 'config', 'spec', 'norm', 'data', 'other'];
|
||||
|
||||
export function extractFileTypeDistribution(files) {
|
||||
export function extractFileTypeDistribution(files, normativeStems = DEFAULT_NORMATIVE_STEMS) {
|
||||
const dist = Object.fromEntries(FILE_TYPE_CATEGORIES.map((c) => [c, 0]));
|
||||
for (const f of files || []) {
|
||||
dist[classifyFilePath(f)] += 1;
|
||||
dist[classifyFilePath(f, normativeStems)] += 1;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
@@ -933,7 +943,7 @@ export function parseTranscript(transcriptText, fallbackSessionId = null, option
|
||||
return {
|
||||
prompt_length_chars: typeof prompt === 'string' ? prompt.length : 0,
|
||||
mcp_servers_used: extractMcpServers(turn),
|
||||
file_type_distribution: extractFileTypeDistribution(ts.files),
|
||||
file_type_distribution: extractFileTypeDistribution(ts.files, options.normativeStems),
|
||||
};
|
||||
})(),
|
||||
classifier_output: _classifierOutput,
|
||||
|
||||
Reference in New Issue
Block a user