// 정적 판정 묶음. 기본 판정(judge)이 끝난 이벤트에 경로 판정(V-36)과 읽기 비용(V-40)을 얹는다. // run.mjs(실행 직후)와 rejudge.mjs(기존 원본 스트림 재판정)가 같은 함수를 쓴다. 모델을 부르지 않는다. import fs from "node:fs"; import path from "node:path"; import { detectSkillInvoked } from "./judge.mjs"; import { judgeRoute } from "./route.mjs"; import { readStats, judgeReads, formatReadStats } from "./read-stats.mjs"; /** 실행 디렉터리(`…///`)에서 평가한 스킬 사본을 찾는다. 없으면 null. */ export function findSkillCopy(runDir) { for (const rel of [".claude/skills/designpaca", ".agents/skills/designpaca"]) { const p = path.join(runDir, "work", rel); if (fs.existsSync(path.join(p, "SKILL.md"))) return p; } return null; } /** * @param {object} scenario scenarios.json 의 한 항목 * @param {Array} events normalizeClaude/normalizeCodex 결과 * @param {{skillDir?: string|null}} [opts] * @returns {{route: object|null, reads: object, readsVerdict: object|null, readsLine: string}} * route judgeRoute 결과(`expectedRoute` 가 없거나 스킬이 호출되지 않았으면 null) * reads readStats 결과 * readsVerdict judgeReads 결과(스킬 문서를 읽지 않았으면 null) */ export function staticJudgements(scenario, events, opts = {}) { const invoked = detectSkillInvoked(events); const reads = readStats(events, { skillDir: opts.skillDir || undefined }); return { route: invoked ? judgeRoute(scenario, events) : null, reads, readsVerdict: judgeReads(scenario, reads), readsLine: formatReadStats(reads), }; }