UNPKG

@every-env/cli

Version:

Multi-agent orchestrator for AI-powered development workflows

116 lines 4.62 kB
import { readFile } from "fs/promises"; import { resolve } from "path"; import { ConfigSchema } from "../types/config.js"; import { logger } from "../utils/logger.js"; export class ConfigLoader { configCache = new Map(); async load(configPath) { const resolvedPath = await this.resolveConfigPath(configPath); if (this.configCache.has(resolvedPath)) { return this.configCache.get(resolvedPath); } const rawConfig = await this.readConfigFile(resolvedPath); const config = this.validateConfig(rawConfig); const mergedConfig = this.mergeWithDefaults(config); // Always return the docs-level configuration object for consumer simplicity const resultDocs = (mergedConfig.docs ?? mergedConfig); this.configCache.set(resolvedPath, resultDocs); return resultDocs; } async resolveConfigPath(configPath) { if (configPath) { return resolve(configPath); } // Look for config in standard locations const locations = [ ".every-env/config.json", "every-env.json", ".every-env.json", ".docs-config.json", "docs.config.json", ]; for (const location of locations) { try { await readFile(location); return resolve(location); } catch { // Continue to next location } } // Check package.json try { const pkg = JSON.parse(await readFile("package.json", "utf-8")); if (pkg["every-env"]) { return resolve("package.json"); } } catch { // No package.json or no config } throw new Error("No configuration file found"); } async readConfigFile(path) { const content = await readFile(path, "utf-8"); if (path.endsWith("package.json")) { const pkg = JSON.parse(content); // Package.json stores config under "every-env", which may include either full config or docs namespace return (pkg["every-env"]?.["docs"] ?? pkg["every-env"] ?? undefined); } const data = JSON.parse(content); // Support both old format (direct config) and new format (under 'docs' namespace) if ((path.includes("every-env.json") || path.includes(".every-env/config.json")) && data["docs"]) { // Return full data (with version + docs) when present return data; } return data; } validateConfig(rawConfig) { // Normalize legacy shapes to current schema before validation const normalize = (input) => { if (input && typeof input === "object" && "docs" in input) { const cfg = input; const docs = { ...(cfg.docs ?? {}) }; if (!docs.defaultCommand) { docs.defaultCommand = process.env.DOCS_DEFAULT_COMMAND ?? "claude"; } if (!("defaultFlags" in docs)) { docs.defaultFlags = []; } return { version: cfg.version ?? "1.0", docs }; } if (input && typeof input === "object" && "patterns" in input) { // Legacy top-level docs format without "docs" wrapper const legacy = input; const docs = { defaultCommand: legacy.defaultCommand ?? process.env.DOCS_DEFAULT_COMMAND ?? "claude", defaultFlags: legacy.defaultFlags ?? [], parallelism: legacy.parallelism, patterns: legacy.patterns, variables: legacy.variables, }; return { version: "1.0", docs }; } return input; }; try { const normalized = normalize(rawConfig); return ConfigSchema.parse(normalized); } catch (error) { logger.error("Invalid configuration:", error); throw new Error("Configuration validation failed"); } } mergeWithDefaults(config) { const docs = { ...config.docs }; // Environment variable override takes precedence const envDefault = process.env.DOCS_DEFAULT_COMMAND; docs.defaultCommand = envDefault ?? docs.defaultCommand ?? "claude"; docs.defaultFlags = docs.defaultFlags ?? []; return { ...config, docs }; } } //# sourceMappingURL=config-loader.js.map