@blundergoat/goat-flow
Version:
AI coding agent harness and local dashboard for Claude Code, OpenAI Codex, Google Antigravity, and GitHub Copilot - setup audits, guardrails, structured skills, deny hooks, and persistent learning loops.
39 lines • 2.05 kB
JavaScript
/**
* The `index-fresh` collector behind `goat-flow stats --check`: detects when a generated
* learning-loop INDEX.md no longer matches its bucket content. Freshness is decided by re-running
* the generator in memory and string-comparing against the on-disk file - content comparison, not
* mtimes, because mtimes false-stale after checkout/restore and are not portable. A missing
* INDEX.md is reported as `missing` (advisory - a fresh install must not fail CI before the first
* `goat-flow index` run); a missing bucket directory is `no-bucket` and never reported.
*/
import { formatIndex } from "../learning-loop-index/format-index.js";
import { INDEX_BUCKETS, parseBucket, } from "../learning-loop-index/parse-bucket.js";
/** Build the INDEX.md path inside a bucket directory regardless of trailing-slash config style. */
function indexPathFor(dirPath) {
return `${dirPath.replace(/\/$/, "")}/INDEX.md`;
}
/**
* Compute index freshness for all four learning-loop buckets.
*
* @param fs - read-only filesystem adapter rooted at the target project
* @param paths - bucket-keyed directory paths, normally from `resolveIndexBucketPaths`
* @returns one freshness record per bucket in stable INDEX_BUCKETS order
*/
export function collectIndexFreshness(fs, paths) {
return INDEX_BUCKETS.map((bucket) => {
const dirPath = paths[bucket];
const indexPath = indexPathFor(dirPath);
if (!fs.exists(dirPath)) {
return { bucket, dirPath, indexPath, state: "no-bucket" };
}
const onDisk = fs.readFile(indexPath);
if (onDisk === null) {
return { bucket, dirPath, indexPath, state: "missing" };
}
const expected = formatIndex(bucket, parseBucket(fs, dirPath, bucket));
// CRLF-normalize so a Windows autocrlf checkout does not read as permanent staleness.
const state = onDisk.replace(/\r\n/g, "\n") === expected ? "fresh" : "stale";
return { bucket, dirPath, indexPath, state };
});
}
//# sourceMappingURL=index-freshness.js.map