import { accessSync, constants, statSync } from "node:fs"; import * as path from "node:path"; export type VerifierResourceSource = "project" | "package"; export interface ResolvedVerifierResource { path: string; source: VerifierResourceSource; attemptedPaths: string[]; } interface ResourceCandidate { path: string; source: VerifierResourceSource; } export function resolveVerifierAgentPath(opts: { cwd: string; packageRoot: string; agentName: string; }): ResolvedVerifierResource { return firstReadableResource([ { source: "project", path: path.resolve(opts.cwd, ".pi", "agents", `${opts.agentName}.md`), }, { source: "package", path: path.resolve(opts.packageRoot, "agents", `${opts.agentName}.md`), }, ]); } export function resolveVerifierPromptPath(opts: { cwd: string; packageRoot: string; promptFileName: string; }): ResolvedVerifierResource { return firstReadableResource([ { source: "project", path: path.resolve(opts.cwd, ".pi", "verifier", "prompts", opts.promptFileName), }, { source: "package", path: path.resolve(opts.packageRoot, "prompts", opts.promptFileName), }, ]); } function firstReadableResource(candidates: ResourceCandidate[]): ResolvedVerifierResource { const attemptedPaths = candidates.map((candidate) => candidate.path); for (const candidate of candidates) { if (isReadableFile(candidate.path)) { return { ...candidate, attemptedPaths }; } } const fallback = candidates[candidates.length - 1]; if (!fallback) { return { path: "", source: "package", attemptedPaths }; } return { ...fallback, attemptedPaths }; } function isReadableFile(filePath: string): boolean { try { if (!statSync(filePath).isFile()) return false; accessSync(filePath, constants.R_OK); return true; } catch { return false; } }