import * as fs from "node:fs"; import * as path from "node:path"; import { CIAgentConfig, DEFAULT_CIAGENT_CONFIG } from "../types/config.js"; const CI_DIR = ".ciagent"; const CONFIG_FILE = "config.json"; export function getCIAgentConfigPath(projectPath: string): string { return path.join(projectPath, CI_DIR, CONFIG_FILE); } export function getCIDir(projectPath: string): string { return path.join(projectPath, CI_DIR); } export function ensureCIDir(projectPath: string): void { const ciDir = getCIDir(projectPath); if (!fs.existsSync(ciDir)) { fs.mkdirSync(ciDir, { recursive: true }); } } function deepMerge(base: CIAgentConfig, override: Record): CIAgentConfig { const result = { ...base } as Record; for (const key of Object.keys(override)) { const baseVal = result[key]; const overrideVal = override[key]; if ( baseVal && typeof baseVal === "object" && !Array.isArray(baseVal) && overrideVal && typeof overrideVal === "object" && !Array.isArray(overrideVal) ) { result[key] = deepMerge( baseVal as unknown as CIAgentConfig, overrideVal as Record ) as unknown; } else if (overrideVal !== undefined) { result[key] = overrideVal; } } return result as unknown as CIAgentConfig; } export function loadConfig(projectPath: string): CIAgentConfig { const configPath = getCIAgentConfigPath(projectPath); if (!fs.existsSync(configPath)) { return { ...DEFAULT_CIAGENT_CONFIG }; } const raw = fs.readFileSync(configPath, "utf-8"); const parsed = JSON.parse(raw); return deepMerge(DEFAULT_CIAGENT_CONFIG, parsed); } export function saveConfig(projectPath: string, config: CIAgentConfig): void { ensureCIDir(projectPath); const configPath = getCIAgentConfigPath(projectPath); fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8"); } export function isCIAgentInitialized(projectPath: string): boolean { const ciDir = getCIDir(projectPath); const configPath = getCIAgentConfigPath(projectPath); return fs.existsSync(ciDir) && fs.existsSync(configPath); } export function initCIAgent(projectPath: string, config?: Partial, projectSlug?: string, projectName?: string): CIAgentConfig { ensureCIDir(projectPath); let projects = config?.projects || DEFAULT_CIAGENT_CONFIG.projects; let activeProject = config?.active_project || DEFAULT_CIAGENT_CONFIG.active_project; if (projectSlug) { if (!projects.some((p) => p.slug === projectSlug)) { projects = [...projects, { slug: projectSlug, name: projectName || projectSlug, default: projects.length === 0 }]; } activeProject = projectSlug; } const fullConfig: CIAgentConfig = { ...DEFAULT_CIAGENT_CONFIG, ...config, projects, active_project: activeProject, autonomy: { ...DEFAULT_CIAGENT_CONFIG.autonomy, ...config?.autonomy }, parallelization: { ...DEFAULT_CIAGENT_CONFIG.parallelization, ...config?.parallelization, }, verification: { ...DEFAULT_CIAGENT_CONFIG.verification, ...config?.verification }, security: { ...DEFAULT_CIAGENT_CONFIG.security, ...config?.security }, git: { ...DEFAULT_CIAGENT_CONFIG.git, ...config?.git }, backend: { ...DEFAULT_CIAGENT_CONFIG.backend, ...config?.backend }, }; saveConfig(projectPath, fullConfig); return fullConfig; }