import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import { join, resolve } from 'node:path'; export const INDEX_SCHEMA_VERSION = '1.1'; export const GITIGNORE_CONTENT = `# boocode audit runs /* !index.json `; export interface IndexEntry { id: string; type: string; status: string; task?: string; skill?: string; created?: string; last_updated?: string; record_count?: number; anomaly_count?: number; max_anomaly_level?: string; } export interface IndexFile { schema_version: string; entries: IndexEntry[]; } function findRunsDirFrom(start: string): string { const explicit = process.env['AUDIT_DOT_DIR']?.trim(); const candidates = explicit ? [explicit] : ['.boo']; let cur = resolve(start); while (true) { for (const basename of candidates) { const candidate = join(cur, basename, 'runs'); if (existsSync(candidate)) return candidate; } const parent = resolve(cur, '..'); if (parent === cur) break; cur = parent; } const defaultBasename = explicit || '.boo'; return join(resolve(start), defaultBasename, 'runs'); } export function findRunsDir(projectRoot?: string): string { return findRunsDirFrom(projectRoot || process.cwd()); } export function ensureRunsDir(projectRoot?: string): string { const dir = findRunsDir(projectRoot); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); const gitignorePath = join(dir, '.gitignore'); if (!existsSync(gitignorePath)) { writeFileSync(gitignorePath, GITIGNORE_CONTENT, 'utf-8'); } } return dir; } export function readCurrentSession(projectRoot?: string): string | null { const path = join(ensureRunsDir(projectRoot), '.current_session'); try { return readFileSync(path, 'utf-8').trim(); } catch { return null; } } export function writeCurrentSession(sessionId: string, projectRoot?: string): void { writeFileSync(join(ensureRunsDir(projectRoot), '.current_session'), sessionId, 'utf-8'); } export function clearCurrentSession(projectRoot?: string): void { const path = join(ensureRunsDir(projectRoot), '.current_session'); try { writeFileSync(path, '', 'utf-8'); } catch { // silent } } export function readIndex(projectRoot?: string): IndexFile { const path = join(ensureRunsDir(projectRoot), 'index.json'); try { return JSON.parse(readFileSync(path, 'utf-8')) as IndexFile; } catch { return { schema_version: INDEX_SCHEMA_VERSION, entries: [] }; } } export function writeIndex(index: IndexFile, projectRoot?: string): void { const runsDir = ensureRunsDir(projectRoot); writeFileSync(join(runsDir, 'index.json'), JSON.stringify(index, null, 2), 'utf-8'); } export function updateIndexEntry(entry: IndexEntry, projectRoot?: string): void { const idx = readIndex(projectRoot); const existing = idx.entries.find(e => e.id === entry.id); if (existing) { Object.assign(existing, entry); } else { idx.entries.push({ ...entry }); } writeIndex(idx, projectRoot); } export function findInProgressSessions(projectRoot?: string): IndexEntry[] { const idx = readIndex(projectRoot); return idx.entries.filter(e => e.status === 'in_progress'); }