(cwd: string = process.cwd())
| 79 | } |
| 80 | |
| 81 | export async function loadConfig(cwd: string = process.cwd()): Promise<QodexConfig> { |
| 82 | await ensureQodexHome(); |
| 83 | |
| 84 | let config = { ...DEFAULT_CONFIG }; |
| 85 | |
| 86 | // User-level config |
| 87 | try { |
| 88 | const userYaml = await fs.readFile(QODEX_CONFIG_FILE, 'utf-8'); |
| 89 | const userCfg = yaml.load(userYaml); |
| 90 | if (isMergeableConfig(userCfg)) config = deepMerge(config, userCfg); |
| 91 | else if (userCfg != null) { |
| 92 | logger.warn('Ignoring user config: top-level value is not a mapping', { file: QODEX_CONFIG_FILE, got: Array.isArray(userCfg) ? 'array' : typeof userCfg }); |
| 93 | } |
| 94 | } catch (err: any) { |
| 95 | if (err.code !== 'ENOENT') { |
| 96 | logger.warn('Failed to load user config', { err: err.message }); |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | // Project-level config |
| 101 | const projectConfig = path.join(cwd, '.qodex', 'config.yaml'); |
| 102 | try { |
| 103 | const projectYaml = await fs.readFile(projectConfig, 'utf-8'); |
| 104 | const projCfg = yaml.load(projectYaml); |
| 105 | if (isMergeableConfig(projCfg)) config = deepMerge(config, projCfg); |
| 106 | else if (projCfg != null) { |
| 107 | logger.warn('Ignoring project config: top-level value is not a mapping', { file: projectConfig, got: Array.isArray(projCfg) ? 'array' : typeof projCfg }); |
| 108 | } |
| 109 | } catch (err: any) { |
| 110 | if (err.code !== 'ENOENT') { |
| 111 | logger.warn('Failed to load project config', { err: err.message }); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | return config; |
| 116 | } |
| 117 | |
| 118 | export async function saveUserConfig(config: Partial<QodexConfig>): Promise<void> { |
| 119 | await ensureQodexHome(); |
no test coverage detected