@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.
124 lines • 4.68 kB
JavaScript
/**
* Build a passing harness-check result with the same report shape every check uses.
*
* @param findings - Evidence strings that explain what the check confirmed.
* @param details - Optional structured details rendered by callers that need more than prose.
* @returns Harness result with no recommendations because the check passed.
*/
export function pass(findings, details) {
return details
? { status: "pass", findings, recommendations: [], details }
: { status: "pass", findings, recommendations: [] };
}
/**
* Build a failing harness-check result with recommendations.
*
* @param findings - Evidence strings that explain what failed.
* @param recommendations - Human-facing next actions for repairing the harness.
* @param howToFix - Optional command-level repair steps when the fix is mechanical.
* @param details - Optional structured details rendered by callers that need more than prose.
* @returns Harness result that keeps evidence, recommendations, and repair steps separate.
*/
export function fail(findings, recommendations, howToFix, details) {
return {
status: "fail",
findings,
recommendations,
...(howToFix ? { howToFix } : {}),
...(details ? { details } : {}),
};
}
/**
* Classify backtick tokens that are technical prose, not local repo paths.
*
* The harness only verifies path existence, so this filter is intentionally
* conservative because package names, URLs, globs, and home paths would create
* noisy false positives rather than useful cross-reference failures.
*/
function isNonRepoPathToken(path) {
const hasSyntax = path.includes("://") ||
path.includes("*") ||
path.includes("(") ||
path.includes("<") ||
path.includes(">");
const isExternalPath = path.startsWith("/") ||
path.startsWith("~/") ||
path.includes(" ") ||
/^@[a-z0-9._-]+\/[a-z0-9._/-]+$/i.test(path);
return hasSyntax || isExternalPath || !looksRepoRelativePath(path);
}
/** Return true when a token has the shape of a repo-relative path. */
function looksRepoRelativePath(path) {
return (/^(?:\.|src\/|app\/|apps\/|lib\/|libs\/|docs\/|test\/|tests\/|scripts\/|workflow\/|config\/|packages\/|web-components\/|\.github\/|\.goat-flow\/|\.claude\/|\.codex\/|\.agents\/)/i.test(path) || /\/[^/]+\.[a-z0-9]+$/i.test(path));
}
/**
* Extract repo-looking file paths from markdown backticks.
*
* This is an existence-signal helper, not an ownership or agent-context check;
* callers that need stronger semantics must layer those checks on top.
*
* @param content - Markdown content to scan.
* @returns Backtick tokens that look like repo-relative paths.
*/
export function extractBacktickPaths(content) {
const paths = [];
for (const match of content.matchAll(/`([^`]+)`/g)) {
const path = match[1];
if (path === undefined)
continue;
const isRootLineRef = /^[a-z0-9._-]+\.[a-z0-9]+:\d+$/i.test(path);
if (isRootLineRef) {
paths.push(path);
continue;
}
if (isNonRepoPathToken(path))
continue;
const isNestedPath = path.includes("/");
if (!isNestedPath)
continue;
paths.push(path);
}
return paths;
}
/**
* Collect markdown files from a shallow documentation tree.
*
* Missing directories and non-directory children are swallowed because harness
* checks treat absent optional buckets as empty sets, not audit crashes.
*
* @param fs - Read-only filesystem view rooted at the audited project.
* @param dir - Directory to scan for markdown files and one level of children.
* @returns Repo-relative markdown file paths in the scanned tree.
*/
export function collectMarkdownFiles(fs, dir) {
const mdFiles = [];
let entries;
try {
entries = fs.listDir(dir);
}
catch {
return mdFiles;
}
// One level of descent is enough for the current docs layout and keeps the scan
// deterministic for tests instead of walking arbitrarily deep trees.
for (const entry of entries) {
const entryPath = `${dir}/${entry}`;
if (entry.endsWith(".md")) {
mdFiles.push(entryPath);
}
else {
try {
for (const childEntry of fs.listDir(entryPath)) {
if (childEntry.endsWith(".md")) {
mdFiles.push(`${entryPath}/${childEntry}`);
}
}
}
catch {
// Not a directory, skip
}
}
}
return mdFiles;
}
//# sourceMappingURL=helpers.js.map