UNPKG

@phren/cli

Version:

Knowledge layer for AI agents - CLI, MCP server, and data layer

65 lines (64 loc) • 2.39 kB
import * as fs from "fs"; import * as path from "path"; import * as crypto from "crypto"; import { getProjectDirs } from "../shared.js"; import { TASK_FILE_ALIASES } from "../data/tasks.js"; import { FINDINGS_FILENAME } from "../data/access.js"; import { errorMessage } from "../utils.js"; import { logger } from "../logger.js"; function fileChecksum(filePath) { const content = fs.readFileSync(filePath); return crypto.createHash("sha256").update(content).digest("hex"); } function checksumStorePath(phrenPath) { return path.join(phrenPath, ".config", "file-checksums.json"); } function loadChecksums(phrenPath) { const file = checksumStorePath(phrenPath); if (!fs.existsSync(file)) return {}; try { return JSON.parse(fs.readFileSync(file, "utf8")); } catch (err) { logger.debug("checksums", `loadChecksums: ${errorMessage(err)}`); return {}; } } function saveChecksums(phrenPath, store) { const file = checksumStorePath(phrenPath); fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, JSON.stringify(store, null, 2) + "\n"); } export function updateFileChecksums(phrenPath, profileName) { const store = loadChecksums(phrenPath); const now = new Date().toISOString(); const tracked = []; const dirs = getProjectDirs(phrenPath, profileName); for (const dir of dirs) { for (const name of [FINDINGS_FILENAME, ...TASK_FILE_ALIASES, "truths.md"]) { const full = path.join(dir, name); if (!fs.existsSync(full)) continue; const rel = path.relative(phrenPath, full).replace(/\\/g, "/"); store[rel] = { sha256: fileChecksum(full), updatedAt: now }; tracked.push(rel); } } saveChecksums(phrenPath, store); return { updated: tracked.length, files: tracked }; } export function verifyFileChecksums(phrenPath) { const store = loadChecksums(phrenPath); const results = []; for (const [rel, entry] of Object.entries(store)) { const full = path.join(phrenPath, rel); if (!fs.existsSync(full)) { results.push({ file: rel, status: "missing" }); continue; } const current = fileChecksum(full); results.push({ file: rel, status: current === entry.sha256 ? "ok" : "mismatch" }); } return results; }