diff --git a/packages/cli/package.json b/packages/cli/package.json index ca46f03..0e2709b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -46,8 +46,8 @@ "license": "MIT", "dependencies": { "@sentry/node": "^9.3.0", + "c12": "^3.0.2", "chalk": "^5", - "cosmiconfig": "^9.0.0", "deepmerge": "^4.3.1", "dotenv": "^16", "mycoder-agent": "workspace:*", diff --git a/packages/cli/src/settings/config.ts b/packages/cli/src/settings/config.ts index 8e3d5ad..8fee650 100644 --- a/packages/cli/src/settings/config.ts +++ b/packages/cli/src/settings/config.ts @@ -1,4 +1,4 @@ -import { cosmiconfig } from 'cosmiconfig'; +import { loadConfig as loadC12Config, watchConfig } from 'c12'; import { ArgumentsCamelCase } from 'yargs'; import { SharedOptions } from '../options'; @@ -107,12 +107,6 @@ export const getConfigFromArgv = (argv: ArgumentsCamelCase) => { }; }; -function removeUndefined(obj: any) { - return Object.fromEntries( - Object.entries(obj).filter(([_, value]) => value !== undefined), - ); -} - /** * Validates custom commands configuration * @param config The configuration object @@ -138,32 +132,53 @@ function validateCustomCommands(config: Config): void { }); } /** - * Load configuration using cosmiconfig + * Load configuration using c12 * @returns Merged configuration with default values */ export async function loadConfig( cliOptions: Partial = {}, ): Promise { - // Initialize cosmiconfig - const explorer = cosmiconfig('mycoder', { - searchStrategy: 'global', + // Load configuration using c12 + const { config } = await loadC12Config({ + name: 'mycoder', + defaults: defaultConfig, + overrides: cliOptions, + // Optionally enable .env support + // dotenv: true, }); - // Search for configuration file - const result = await explorer.search(); - - // Merge configurations with precedence: default < file < cli - const fileConfig = result?.config || {}; + // Convert to Config type and validate custom commands + const typedConfig = config as unknown as Config; + validateCustomCommands(typedConfig); - // Return merged configuration - const mergedConfig = { - ...defaultConfig, - ...removeUndefined(fileConfig), - ...removeUndefined(cliOptions), - }; + return typedConfig; +} - // Validate custom commands if present - validateCustomCommands(mergedConfig); +/** + * Watch configuration for changes + * @param cliOptions CLI options to override configuration + * @param onUpdate Callback when configuration is updated + */ +export async function watchConfigForChanges( + cliOptions: Partial = {}, + onUpdate?: (config: Config) => void, +) { + const { config, watchingFiles, unwatch } = await watchConfig({ + name: 'mycoder', + defaults: defaultConfig, + overrides: cliOptions, + onUpdate: ({ newConfig }) => { + const typedConfig = newConfig as unknown as Config; + validateCustomCommands(typedConfig); + if (onUpdate) { + onUpdate(typedConfig); + } + }, + }); - return mergedConfig; + return { + config: config as unknown as Config, + watchingFiles, + unwatch, + }; }