UNPKG

@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.

162 lines 8.6 kB
/** * Shared parsing and reference-validation primitives for the learning-loop fact * extractors (footguns, lessons, patterns, decisions). Owns the markdown reading, * frontmatter parsing, freshness computation, and the evidence-reference checks * that flag stale paths, out-of-bounds line numbers, and broken `(search: ...)` * anchors. * * Reference validation is intentionally conservative: ambiguous shorthand (bare * source filenames, gitignored task paths, URLs/hostnames) is skipped rather than * reported, because a false "stale" finding on a clean checkout erodes trust in the * whole audit. The regexes here are the canonical evidence grammar - footgun and * lesson extractors must reuse them so the same string is judged identically * everywhere. ADR-024 governs the line-number-versus-semantic-anchor policy these * checks enforce. */ import type { BucketFreshness, ReadonlyFS } from "../../types.js"; /** Strict YYYY-MM-DD format - rejects full ISO 8601 timestamps in `last_reviewed`. */ export declare const ISO_DATE_REGEX: RegExp; /** * Matches file path evidence in multiple formats: * - `src/auth.ts` (backtick-wrapped file path) * - `src/auth.ts:42` (backtick-wrapped with line number) * - `src/auth.ts:42-50` (backtick-wrapped with line range) * - (lines 866-880) or (line 52) (prose-style) * Line numbers are discouraged per ADR-024; flagged for cleanup when found alongside a semantic anchor. * File paths alone remain valid evidence. */ export declare const EVIDENCE_PATTERN: RegExp; /** Regex to extract file paths from backtick-wrapped references (with optional line numbers). */ export declare const FILE_REF_REGEX: RegExp; /** One markdown file read from a learning-loop directory. */ export interface MarkdownEntry { path: string; content: string; } /** A learning-loop directory with its existence flag and contained markdown entries. */ export interface EntryDir { path: string; exists: boolean; files: MarkdownEntry[]; } /** Aggregated file-reference validation results for footgun entries. */ export interface FootgunRefSummary { staleRefs: string[]; invalidLineRefs: string[]; totalRefs: number; validRefs: number; } /** * Decide whether a backtick-wrapped reference names a real file path rather than a * URL or hostname (which share the `host:port` shape). Used to gate staleness * checks so a `localhost:3000`-style token is never treated as a missing file. * * @param filePath - candidate reference text with any trailing `:line` already split off * @returns true for paths with a slash or a root-level filename extension; false for URLs, hostnames, and bare extensionless names */ export declare function isFileRef(filePath: string): boolean; /** * Find learning-loop artifact surfaces that exist on disk but sit outside the * configured canonical location - the signal that a project is splitting one * concern across two directories. Returns nothing unless a canonical path is * actually present, so a project that simply hasn't adopted the surface yet is * not flagged. Trailing slashes are normalized before comparison. * * @param fs - read-only filesystem adapter for the target project * @param canonicalPaths - the configured/blessed locations; at least one must exist or the result is empty * @param knownPaths - candidate surfaces to test against the canonical set * @returns existing non-canonical paths, sorted lexicographically for deterministic output; empty when none compete */ export declare function findCompetingArtifactSurfaces(fs: ReadonlyFS, canonicalPaths: string[], knownPaths: string[]): string[]; /** * Read a learning-loop location into a stable, sorted set of markdown entries. * Handles both config shapes uniformly: a directory (every `.md` except the * README.md/INDEX.md metadata files, sorted lexicographically) and a single flat * `.md` file (one entry). INDEX.md is generated bucket metadata (`goat-flow index`), * not entry content - including it would count phantom legacy entries and force * entry frontmatter onto a generated file. The sort is load-bearing - downstream * entry ordering and report output must be deterministic across machines, so * directory listing order is never trusted. * * @param fs - read-only filesystem adapter for the target project * @param dir - directory path, or a single `.md` file path for flat-file config mode * @returns the location with its existence flag and entries; files is empty when the location is absent or unreadable */ export declare function listMarkdownEntries(fs: ReadonlyFS, dir: string): EntryDir; /** * Separate a leading `---`-delimited YAML frontmatter block from the markdown body. * Recognizes frontmatter only at the very start of the content; a `---` later in * the document is left in the body untouched. * * @param content - raw markdown file content * @returns the frontmatter text without its `---` fences (null when there is none) and the remaining body */ export declare function parseMarkdownFrontmatter(content: string): { frontmatter: string | null; body: string; }; /** * Parse simple `key: value` pairs from a YAML frontmatter block. * Only handles flat scalar fields (sufficient for goat-flow's single-level frontmatter); * nested structures, arrays, and multi-line scalars are intentionally unsupported. * * @param frontmatter - YAML frontmatter body without the surrounding `---` markers * @returns flat key/value fields parsed from the frontmatter block */ export declare function parseFrontmatterFields(frontmatter: string): Record<string, string>; /** * Compute days-since-review and a coarse freshness band for a bucket file. * Returns `unknown` for missing or non-YYYY-MM-DD values so callers can flag them. * * @param lastReviewed - ISO date from bucket frontmatter, or null when absent * @param now - comparison clock for deterministic tests and reports */ export declare function computeFreshness(lastReviewed: string | null, now?: Date): { days: number | null; band: BucketFreshness["freshnessBand"]; }; /** * Count how many times a pattern matches across a string. Pass a global (`/g`) * regex - `matchAll` requires it, and without the flag the match count is not what * a caller expects. * * @param content - text to scan * @param pattern - global regular expression; non-global patterns will throw under matchAll * @returns the total number of non-overlapping matches; 0 when none match */ export declare function countMatches(content: string, pattern: RegExp): number; /** * Remove `~~...~~` strikethrough spans before evidence is scanned, so a reference * an author has struck through (marked as historical) is not counted as live * evidence. Run this first in every reference check; otherwise retired anchors * resurface as findings. * * @param content - markdown that may contain strikethrough spans, including multi-line ones * @returns the content with all strikethrough spans removed */ export declare function stripStrikethrough(content: string): string; /** * Validate every file reference in one footgun section and tally the result. * Reports a path as stale when the file no longer exists, and flags a `file:line` * reference when the line is out of bounds, lacks a semantic anchor, or carries a * line number made redundant by an anchor (the ADR-024 anchor-over-line-number * contract). Strikethrough is stripped first so struck evidence is ignored. * * @param fs - read-only filesystem adapter used to resolve and line-count referenced files * @param content - the footgun section's markdown * @returns counts plus the stale-path and invalid-line-reference lists; all empty when every reference is valid */ export declare function summarizeFootgunRefs(fs: ReadonlyFS, content: string): FootgunRefSummary; /** * Validate the file references in one lesson or pattern section, sharing the same * staleness and ADR-024 line-reference rules as footguns. Lessons cite full * project-rooted paths (src/, lib/, docs/, .goat-flow/, ...), so this matches that * prefix grammar and skips glob-like or `...`-elided tokens that cannot be resolved * to a single file. * * @param fs - read-only filesystem adapter used to resolve and line-count referenced files * @param content - the lesson or pattern section's markdown * @returns counts plus the stale-path and invalid-line-reference lists; all empty when every reference is valid */ export declare function summarizeLessonRefs(fs: ReadonlyFS, content: string): FootgunRefSummary; //# sourceMappingURL=learning-loop-common.d.ts.map