@vibe-validate/history
Version:
Validation history tracking via git notes for vibe-validate
138 lines • 4.6 kB
JavaScript
/**
* Run cache reader - Read run cache entries from git notes
*
* Run cache is stored at: refs/notes/vibe-validate/run/{treeHash}/{cacheKey}
* where cacheKey = encodeURIComponent(workdir ? `${workdir}:${command}` : command)
*/
import { execSync } from 'node:child_process';
import { parse as parseYaml } from 'yaml';
const GIT_TIMEOUT = 30000;
const GIT_OPTIONS = {
encoding: 'utf8',
timeout: GIT_TIMEOUT,
stdio: ['pipe', 'pipe', 'ignore'],
};
/**
* List all run cache entries for a tree hash
*
* @param treeHash - Git tree hash
* @returns Array of run cache entry metadata
*/
export async function listRunCacheEntries(treeHash) {
try {
// List all notes under refs/notes/vibe-validate/run/{treeHash}/*
const output = execSync(`git for-each-ref --format="%(refname) %(objectname)" refs/notes/vibe-validate/run/${treeHash}/`, GIT_OPTIONS);
if (!output.trim()) {
return [];
}
// Parse output: "refs/notes/vibe-validate/run/{treeHash}/{cacheKey} {noteObjectName}"
const entries = output
.trim()
.split('\n')
.map((line) => {
const [refPath] = line.split(' ');
// Extract cache key from ref path
const regex = /refs\/notes\/vibe-validate\/run\/([^/]+)\/(.+)$/;
const match = regex.exec(refPath);
if (!match)
return null;
const [, treeHashFromRef, cacheKey] = match;
return {
treeHash: treeHashFromRef,
cacheKey,
refPath,
};
})
.filter(Boolean);
return entries;
}
catch {
// No run cache exists - expected for tree hashes without run cache
return [];
}
}
/**
* Get a specific run cache entry
*
* @param treeHash - Git tree hash
* @param cacheKey - URL-encoded cache key
* @returns Run cache note or null if not found
*/
export async function getRunCacheEntry(treeHash, cacheKey) {
try {
const refPath = `vibe-validate/run/${treeHash}/${cacheKey}`;
const yaml = execSync(`git notes --ref=${refPath} show HEAD`, GIT_OPTIONS);
const note = parseYaml(yaml);
return note;
}
catch {
// Entry doesn't exist or parse failed
return null;
}
}
/**
* Get all run cache entries for a tree hash
*
* @param treeHash - Git tree hash
* @returns Array of all run cache notes for this tree hash
*/
export async function getAllRunCacheForTree(treeHash) {
const entries = await listRunCacheEntries(treeHash);
const notes = [];
for (const entry of entries) {
const note = await getRunCacheEntry(entry.treeHash, entry.cacheKey);
if (note) {
notes.push(note);
}
}
// Sort by timestamp (newest first) for deterministic ordering
notes.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
return notes;
}
/**
* List all tree hashes that have run cache entries
*
* @returns Array of tree hashes with run cache
*/
export async function listRunCacheTreeHashes() {
try {
// List all notes under refs/notes/vibe-validate/run/
const output = execSync('git for-each-ref --format="%(refname)" refs/notes/vibe-validate/run/', GIT_OPTIONS);
if (!output.trim()) {
return [];
}
// Parse output and extract unique tree hashes
const treeHashes = new Set();
const lines = output.trim().split('\n');
for (const line of lines) {
// Extract tree hash from ref path: refs/notes/vibe-validate/run/{treeHash}/...
const regex = /refs\/notes\/vibe-validate\/run\/([^/]+)/;
const match = regex.exec(line);
if (match) {
treeHashes.add(match[1]);
}
}
return Array.from(treeHashes);
}
catch {
// No run cache exists - expected for repos without run cache
return [];
}
}
/**
* Get all run cache entries across all tree hashes
*
* @returns Array of all run cache notes
*/
export async function getAllRunCacheEntries() {
const treeHashes = await listRunCacheTreeHashes();
const allNotes = [];
for (const treeHash of treeHashes) {
const notes = await getAllRunCacheForTree(treeHash);
allNotes.push(...notes);
}
// Sort by timestamp (newest first)
allNotes.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
return allNotes;
}
//# sourceMappingURL=run-cache-reader.js.map