UNPKG

framework

Version:

The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.

78 lines 3.34 kB
import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; import { parse as parseYaml } from 'yaml'; import { errorMessage } from './error-message.js'; import { isHandoffLevel, HANDOFF_LEVELS } from './handoff-level.js'; /** Config file names read from the workspace root, in precedence order. */ export const FRAMEWORK_CONFIG_FILES = ['the-framework.yml', 'the-framework.yaml']; /** * The keys whose values come from a closed set, so parsing checks the value rather than the type * (B5). Only the publish ladder so far; each is validated by name in {@link parseFrameworkConfig}. */ const ENUM_CONFIG_KEYS = ['handoff']; /** * The boolean-valued mode keys. This is the canonical mode list: parsing, the config-layer copy, * resolution, and the resolved-config summary all iterate it, so a new mode is added here once and * flows through them (only its default and any renamed output field are declared per key). */ export const BOOLEAN_CONFIG_KEYS = ['vanilla', 'transparent']; /** Every config key, enum then boolean, in declaration order. */ export const CONFIG_KEYS = [...ENUM_CONFIG_KEYS, ...BOOLEAN_CONFIG_KEYS]; /** * Read `the-framework.yml` (or `.yaml`) from a directory. A missing file yields * `{}`. Best-effort: a malformed file is reported via `onWarn` and treated as * empty, never a failed agent. CLI flags override whatever this returns. */ export async function loadFrameworkConfig(dir, onWarn) { for (const name of FRAMEWORK_CONFIG_FILES) { let raw; try { raw = await readFile(join(dir, name), 'utf8'); } catch { continue; // not this name; try the next } try { return parseFrameworkConfig(raw, name); } catch (err) { // parseFrameworkConfig already prefixes the file name in its message. onWarn?.(`ignoring ${errorMessage(err)}`); return {}; } } return {}; } /** * Parse and validate a `the-framework.yml` body into a {@link FrameworkFileConfig}. * An empty document is `{}`. Throws on a non-map document or a mistyped field so * {@link loadFrameworkConfig} can surface it as a warning. */ export function parseFrameworkConfig(raw, source = 'the-framework.yml') { const data = parseYaml(raw); if (data == null) return {}; if (typeof data !== 'object' || Array.isArray(data)) { throw new Error(`${source} must be a YAML map of settings`); } const obj = data; const config = {}; // The one key with a closed set (B5), so it is checked by its values rather than by its type: a // typo — or a leftover `handoff: true` — has to be an error, not a silently ignored rung that // leaves the repo publishing more than its file says. if (obj['handoff'] !== undefined) { if (!isHandoffLevel(obj['handoff'])) { throw new Error(`${source}: "handoff" must be one of ${HANDOFF_LEVELS.join(' | ')}`); } config.handoff = obj['handoff']; } for (const key of BOOLEAN_CONFIG_KEYS) { if (obj[key] !== undefined) { if (typeof obj[key] !== 'boolean') throw new Error(`${source}: "${key}" must be a boolean`); config[key] = obj[key]; } } return config; } //# sourceMappingURL=config.js.map