import * as fs from "node:fs"; import * as path from "node:path"; import { Decision } from "../types/decisions.js"; import { Escalation } from "../types/escalation.js"; export interface AuditEntry { phase: number; decisions: Decision[]; escalations: Escalation[]; } const AUDIT_DIR = "audit"; function getAuditDir(projectPath: string): string { return path.join(projectPath, ".ciagent", AUDIT_DIR); } function getAuditFilePath(projectPath: string, phase: number): string { const date = new Date().toISOString().split("T")[0]; return path.join(getAuditDir(projectPath), `${date}-phase${phase}-decisions.json`); } function ensureAuditDir(projectPath: string): void { const dir = getAuditDir(projectPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } } export function logDecision( projectPath: string, phase: number, decision: Decision ): void { ensureAuditDir(projectPath); const filePath = getAuditFilePath(projectPath, phase); let entry: AuditEntry; if (fs.existsSync(filePath)) { entry = JSON.parse(fs.readFileSync(filePath, "utf-8")); } else { entry = { phase, decisions: [], escalations: [] }; } entry.decisions.push(decision); fs.writeFileSync(filePath, JSON.stringify(entry, null, 2), "utf-8"); } export function logEscalation( projectPath: string, phase: number, escalation: Escalation ): void { ensureAuditDir(projectPath); const filePath = getAuditFilePath(projectPath, phase); let entry: AuditEntry; if (fs.existsSync(filePath)) { entry = JSON.parse(fs.readFileSync(filePath, "utf-8")); } else { entry = { phase, decisions: [], escalations: [] }; } entry.escalations.push(escalation); fs.writeFileSync(filePath, JSON.stringify(entry, null, 2), "utf-8"); } export function readAudit( projectPath: string, phase?: number ): AuditEntry[] { const auditDir = getAuditDir(projectPath); if (!fs.existsSync(auditDir)) return []; const files = fs .readdirSync(auditDir) .filter((f) => f.endsWith("-decisions.json")) .sort(); const entries: AuditEntry[] = []; for (const file of files) { const content = fs.readFileSync(path.join(auditDir, file), "utf-8"); const entry: AuditEntry = JSON.parse(content); if (phase === undefined || entry.phase === phase) { entries.push(entry); } } return entries; } export function getAuditSummary(projectPath: string): { total_decisions: number; total_escalations: number; phases: number[]; decisions_by_confidence: Record; escalations_by_type: Record; } { const entries = readAudit(projectPath); let total_decisions = 0; let total_escalations = 0; const phases = new Set(); const decisions_by_confidence: Record = { high: 0, medium: 0, low: 0, }; const escalations_by_type: Record = {}; for (const entry of entries) { phases.add(entry.phase); total_decisions += entry.decisions.length; total_escalations += entry.escalations.length; for (const d of entry.decisions) { const level = d.confidence > 0.85 ? "high" : d.confidence >= 0.6 ? "medium" : "low"; decisions_by_confidence[level]++; } for (const e of entry.escalations) { escalations_by_type[e.type] = (escalations_by_type[e.type] || 0) + 1; } } return { total_decisions, total_escalations, phases: [...phases], decisions_by_confidence, escalations_by_type, }; }