diff --git a/tools/secretary-flag.mjs b/tools/secretary-flag.mjs index b624579..98e2309 100644 --- a/tools/secretary-flag.mjs +++ b/tools/secretary-flag.mjs @@ -1,3 +1,7 @@ +import { existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; + // Детект команды секретаря в тексте промпта. Кавычки/код снимаются до сопоставления, // чтобы цитирование не срабатывало (приём как в существующих детекторах). function stripQuoted(text) { @@ -55,8 +59,11 @@ export function resolveCaseActivation(requested, existing = []) { return { action: 'activate', work: String(requested || '').trim() }; } -// Флаг нового конвейера «пушистое дерево». ВЫКЛ по умолчанию — подпроект A кладётся «тёмным», -// включается только когда воркер (подпроект B) вживит конвейер. -export function fluffyPipelineOn(env = process.env) { - return env.SECRETARY_FLUFFY === '1' || env.SECRETARY_FLUFFY === 'true'; +function defaultFluffyFile() { return join(homedir(), '.claude', 'runtime', 'secretary-fluffy'); } + +/** Флаг конвейера «пушистое дерево». ВКЛ если env SECRETARY_FLUFFY=1|true ИЛИ есть рантайм-файл-сентинел. + * Рантайм-файл позволяет тогглить без правки settings.json. */ +export function fluffyPipelineOn(env = process.env, file = defaultFluffyFile()) { + if (env.SECRETARY_FLUFFY === '1' || env.SECRETARY_FLUFFY === 'true') return true; + try { return existsSync(file); } catch { return false; } } diff --git a/tools/secretary-flag.test.mjs b/tools/secretary-flag.test.mjs index 16a7b8f..86f92a8 100644 --- a/tools/secretary-flag.test.mjs +++ b/tools/secretary-flag.test.mjs @@ -1,4 +1,7 @@ -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; import { detectSecretaryCommand, secretaryModeFileName, resolveCaseActivation, fluffyPipelineOn } from './secretary-flag.mjs'; describe('detectSecretaryCommand', () => { @@ -61,3 +64,12 @@ describe('fluffyPipelineOn', () => { expect(fluffyPipelineOn({ SECRETARY_FLUFFY: 'true' })).toBe(true); }); }); + +describe('fluffyPipelineOn runtime file', () => { + let dir, file; + beforeEach(() => { dir = mkdtempSync(join(tmpdir(), 'secflag-')); file = join(dir, 'secretary-fluffy'); }); + afterEach(() => { rmSync(dir, { recursive: true, force: true }); }); + it('env wins when set', () => { expect(fluffyPipelineOn({ SECRETARY_FLUFFY: '1' }, file)).toBe(true); }); + it('runtime file present → on, even without env', () => { writeFileSync(file, ''); expect(fluffyPipelineOn({}, file)).toBe(true); }); + it('neither → off', () => { expect(fluffyPipelineOn({}, file)).toBe(false); }); +});