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.

165 lines 8.48 kB
/** * Loads and schema-validates the shipped detector tables in * workflow/project-stack-data.json, then re-exports each table as a typed * constant for the project-stack detector and setup signals. * * Loaded eagerly at module import: a malformed shipped table throws during * startup rather than producing silently wrong detection. The exported * PROJECT_STACK_* constants are the only supported way to read these rows; * consumers must not re-parse the JSON. */ import { readFileSync } from "node:fs"; import { getTemplatePath } from "../paths.js"; /** Relative path to the shipped project-stack data tables. */ const PROJECT_STACK_DATA_PATH = "workflow/project-stack-data.json"; /** Treat arrays as invalid records because every shipped data row uses named fields. */ function isRecord(candidate) { return (typeof candidate === "object" && candidate !== null && !Array.isArray(candidate)); } /** Read a string array from shipped data; throws with the table label on schema drift. */ function readStringArray(rawValue, label) { if (!Array.isArray(rawValue) || rawValue.some((entry) => typeof entry !== "string")) { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label} array`); } return [...rawValue]; } /** Read language/path/glob rows; throws with row indexes so bad shipped data is fixable. */ function readLanguageSignals(value, label) { if (!Array.isArray(value)) { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label} list`); } return value.map((entry, index) => { if (!isRecord(entry) || typeof entry.language !== "string") { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label}[${index}] entry`); } return { language: entry.language, paths: readStringArray(entry.paths, `${label}[${index}].paths`), globs: readStringArray(entry.globs, `${label}[${index}].globs`), }; }); } /** Read tool/path/glob rows; throws before detector startup can use malformed data. */ function readToolSignals(rawValue, label) { if (!Array.isArray(rawValue)) { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label} list`); } return rawValue.map((entry, index) => { if (!isRecord(entry) || typeof entry.tool !== "string") { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label}[${index}] entry`); } return { tool: entry.tool, paths: readStringArray(entry.paths, `${label}[${index}].paths`), globs: readStringArray(entry.globs, `${label}[${index}].globs`), }; }); } /** Read setup-framework marker rows from the project-stack data JSON. */ function readSetupFrameworkMarkers(value, label) { if (!Array.isArray(value)) { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label} list`); } return value.map((entry, index) => { if (!isRecord(entry) || typeof entry.name !== "string") { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label}[${index}] entry`); } return { name: entry.name, files: readStringArray(entry.files, `${label}[${index}].files`), markers: readStringArray(entry.markers, `${label}[${index}].markers`), }; }); } /** Read Node framework rows from the project-stack data JSON. */ function readNodeFrameworkSignals(value, label) { if (!Array.isArray(value)) { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label} list`); } return value.map((entry, index) => { if (!isRecord(entry) || typeof entry.language !== "string") { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label}[${index}] entry`); } return { language: entry.language, packages: readStringArray(entry.packages, `${label}[${index}].packages`), }; }); } /** Read Node test framework rows from the project-stack data JSON. */ function readNodeTestFrameworkSignals(value, label) { if (!Array.isArray(value)) { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label} list`); } return value.map((entry, index) => { if (!isRecord(entry) || typeof entry.name !== "string") { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid ${label}[${index}] entry`); } return { name: entry.name, packages: readStringArray(entry.packages, `${label}[${index}].packages`), }; }); } /** Read formatter mappings; throws if a language maps to anything other than strings. */ function readFormatterMap(rawValue) { if (!isRecord(rawValue)) { throw new Error(`${PROJECT_STACK_DATA_PATH} has an invalid formatterMap`); } return Object.fromEntries(Object.entries(rawValue).map(([language, formatters]) => [ language, readStringArray(formatters, `formatterMap.${language}`), ])); } /** Load shipped detector tables once; throws during startup when the JSON schema drifts. */ function loadProjectStackData() { const path = getTemplatePath(PROJECT_STACK_DATA_PATH); const raw = JSON.parse(readFileSync(path, "utf-8")); if (!isRecord(raw)) { throw new Error(`${PROJECT_STACK_DATA_PATH} must contain a JSON object`); } return { nodeFrameworks: readNodeFrameworkSignals(raw.nodeFrameworks, "nodeFrameworks"), nodeTestFrameworks: readNodeTestFrameworkSignals(raw.nodeTestFrameworks, "nodeTestFrameworks"), extraLanguageSignals: readLanguageSignals(raw.extraLanguageSignals, "extraLanguageSignals"), codeGenSignals: readToolSignals(raw.codeGenSignals, "codeGenSignals"), deploySignals: readToolSignals(raw.deploySignals, "deploySignals"), setupFrameworkMarkers: readSetupFrameworkMarkers(raw.setupFrameworkMarkers, "setupFrameworkMarkers"), rootPythonFiles: readStringArray(raw.rootPythonFiles, "rootPythonFiles"), subdirPythonGlobs: readStringArray(raw.subdirPythonGlobs, "subdirPythonGlobs"), javaManifestPaths: readStringArray(raw.javaManifestPaths, "javaManifestPaths"), llmEnvFiles: readStringArray(raw.llmEnvFiles, "llmEnvFiles"), llmDepFiles: readStringArray(raw.llmDepFiles, "llmDepFiles"), complianceDocs: readStringArray(raw.complianceDocs, "complianceDocs"), formatterMap: readFormatterMap(raw.formatterMap), }; } const PROJECT_STACK_DATA = loadProjectStackData(); /** Node.js framework indicators matched against package dependencies. */ export const PROJECT_STACK_NODE_FRAMEWORKS = PROJECT_STACK_DATA.nodeFrameworks; /** Additional language/template indicators beyond primary manifest detection. */ export const PROJECT_STACK_EXTRA_LANGUAGE_SIGNALS = PROJECT_STACK_DATA.extraLanguageSignals; /** Code generation tool indicators detected from config files. */ export const PROJECT_STACK_CODE_GENERATION_SIGNALS = PROJECT_STACK_DATA.codeGenSignals; /** Deployment platform indicators detected from config files. */ export const PROJECT_STACK_DEPLOYMENT_SIGNALS = PROJECT_STACK_DATA.deploySignals; /** Extra framework markers used only for setup-view framework display names. */ export const PROJECT_STACK_SETUP_FRAMEWORK_MARKERS = PROJECT_STACK_DATA.setupFrameworkMarkers; /** Root-level files that indicate a Python project. */ export const PROJECT_STACK_ROOT_PYTHON_FILES = PROJECT_STACK_DATA.rootPythonFiles; /** Glob patterns for detecting Python projects in subdirectories. */ export const PROJECT_STACK_SUBDIRECTORY_PYTHON_GLOBS = PROJECT_STACK_DATA.subdirPythonGlobs; /** Build manifest paths read to detect Java framework dependencies. */ export const PROJECT_STACK_JAVA_MANIFEST_PATHS = PROJECT_STACK_DATA.javaManifestPaths; /** Environment files checked for LLM provider API key variables. */ export const PROJECT_STACK_LLM_ENV_FILES = PROJECT_STACK_DATA.llmEnvFiles; /** Dependency files checked for LLM SDK references. */ export const PROJECT_STACK_LLM_DEPENDENCY_FILES = PROJECT_STACK_DATA.llmDepFiles; /** Files checked for compliance-related keywords (HIPAA, GDPR, etc.). */ export const PROJECT_STACK_COMPLIANCE_DOCS = PROJECT_STACK_DATA.complianceDocs; /** Maps languages to their known formatter tool names for gap detection. */ export const PROJECT_STACK_FORMATTER_MAP = PROJECT_STACK_DATA.formatterMap; //# sourceMappingURL=project-stack-data.js.map