@@ -9,6 +9,10 @@ import {
99 getDefaultConfig ,
1010 updateConfig ,
1111 clearAllConfig ,
12+ getConfigAtLevel ,
13+ clearConfigAtLevel ,
14+ clearConfigKey ,
15+ ConfigLevel ,
1216} from '../settings/config.js' ;
1317import { nameToLogIndex } from '../utils/nameToLogIndex.js' ;
1418
@@ -38,6 +42,8 @@ export interface ConfigOptions extends SharedOptions {
3842 key ?: string ;
3943 value ?: string ;
4044 all ?: boolean ;
45+ global ?: boolean ;
46+ g ?: boolean ; // Alias for global
4147}
4248
4349export const command : CommandModule < SharedOptions , ConfigOptions > = {
@@ -64,6 +70,12 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
6470 type : 'boolean' ,
6571 default : false ,
6672 } )
73+ . option ( 'global' , {
74+ alias : 'g' ,
75+ describe : 'Use global configuration instead of project-level' ,
76+ type : 'boolean' ,
77+ default : false ,
78+ } )
6779 . example ( '$0 config list' , 'List all configuration values' )
6880 . example (
6981 '$0 config get githubMode' ,
@@ -80,7 +92,23 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
8092 )
8193 . example (
8294 '$0 config clear --all' ,
83- 'Clear all configuration settings' ,
95+ 'Clear all project-level configuration settings' ,
96+ )
97+ . example (
98+ '$0 config set githubMode true --global' ,
99+ 'Enable GitHub mode in global configuration' ,
100+ )
101+ . example (
102+ '$0 config set model claude-3-haiku-20240307 -g' ,
103+ 'Set model in global configuration using short flag' ,
104+ )
105+ . example (
106+ '$0 config list --global' ,
107+ 'List all global configuration settings' ,
108+ )
109+ . example (
110+ '$0 config clear --all --global' ,
111+ 'Clear all global configuration settings' ,
84112 ) as any ; // eslint-disable-line @typescript-eslint/no-explicit-any
85113 } ,
86114 handler : async ( argv : ArgumentsCamelCase < ConfigOptions > ) => {
@@ -89,31 +117,61 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
89117 logLevel : nameToLogIndex ( argv . logLevel ) ,
90118 } ) ;
91119
92- const config = getConfig ( ) ;
120+ // Determine which config level to use based on flags
121+ const configLevel =
122+ argv . global || argv . g ? ConfigLevel . GLOBAL : ConfigLevel . PROJECT ;
123+ const levelName = configLevel === ConfigLevel . GLOBAL ? 'global' : 'project' ;
124+
125+ // Get merged config for display
126+ const mergedConfig = getConfig ( ) ;
127+
128+ // Get level-specific configs for reference
129+ const defaultConfig = getConfigAtLevel ( ConfigLevel . DEFAULT ) ;
130+ const globalConfig = getConfigAtLevel ( ConfigLevel . GLOBAL ) ;
131+ const projectConfig = getConfigAtLevel ( ConfigLevel . PROJECT ) ;
93132
94133 // Handle 'list' command
95134 if ( argv . command === 'list' ) {
96135 logger . info ( 'Current configuration:' ) ;
97- const defaultConfig = getDefaultConfig ( ) ;
98136
99137 // Get all valid config keys
100138 const validKeys = Object . keys ( defaultConfig ) ;
101139
102140 // Filter and sort config entries
103- const configEntries = Object . entries ( config )
141+ const configEntries = Object . entries ( mergedConfig )
104142 . filter ( ( [ key ] ) => validKeys . includes ( key ) )
105143 . sort ( ( [ keyA ] , [ keyB ] ) => keyA . localeCompare ( keyB ) ) ;
106144
107- // Display config entries with default indicators
145+ // Display config entries with source indicators
108146 configEntries . forEach ( ( [ key , value ] ) => {
109- const isDefault =
110- JSON . stringify ( value ) ===
111- JSON . stringify ( defaultConfig [ key as keyof typeof defaultConfig ] ) ;
112- const valueDisplay = isDefault
113- ? chalk . dim ( `${ value } (default)` )
114- : chalk . green ( value ) ;
115- logger . info ( ` ${ key } : ${ valueDisplay } ` ) ;
147+ const inProject = key in projectConfig ;
148+ const inGlobal = key in globalConfig ;
149+ const isDefault = ! inProject && ! inGlobal ;
150+
151+ let valueDisplay = '' ;
152+ let sourceDisplay = '' ;
153+
154+ if ( isDefault ) {
155+ valueDisplay = chalk . dim ( `${ value } (default)` ) ;
156+ } else if ( inProject ) {
157+ valueDisplay = chalk . green ( `${ value } ` ) ;
158+ sourceDisplay = chalk . blue ( ' [project]' ) ;
159+ } else if ( inGlobal ) {
160+ valueDisplay = chalk . yellow ( `${ value } ` ) ;
161+ sourceDisplay = chalk . magenta ( ' [global]' ) ;
162+ }
163+
164+ logger . info ( ` ${ key } : ${ valueDisplay } ${ sourceDisplay } ` ) ;
116165 } ) ;
166+
167+ logger . info ( '' ) ;
168+ logger . info ( 'Configuration levels (in order of precedence):' ) ;
169+ logger . info ( ' CLI options (highest)' ) ;
170+ logger . info (
171+ ' Project config (.mycoder/config.json in current directory)' ,
172+ ) ;
173+ logger . info ( ' Global config (~/.mycoder/config.json)' ) ;
174+ logger . info ( ' Default values (lowest)' ) ;
117175 return ;
118176 }
119177
@@ -124,10 +182,29 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
124182 return ;
125183 }
126184
127- if ( argv . key in config ) {
128- logger . info (
129- `${ argv . key } : ${ chalk . green ( config [ argv . key as keyof typeof config ] ) } ` ,
130- ) ;
185+ // Check if the key exists in the merged config
186+ if ( argv . key in mergedConfig ) {
187+ const value = mergedConfig [ argv . key as keyof typeof mergedConfig ] ;
188+
189+ // Determine the source of this value
190+ const inProject = argv . key in projectConfig ;
191+ const inGlobal = argv . key in globalConfig ;
192+ const isDefault = ! inProject && ! inGlobal ;
193+
194+ let valueDisplay = '' ;
195+ let sourceDisplay = '' ;
196+
197+ if ( isDefault ) {
198+ valueDisplay = chalk . dim ( `${ value } (default)` ) ;
199+ } else if ( inProject ) {
200+ valueDisplay = chalk . green ( `${ value } ` ) ;
201+ sourceDisplay = chalk . blue ( ' [project]' ) ;
202+ } else if ( inGlobal ) {
203+ valueDisplay = chalk . yellow ( `${ value } ` ) ;
204+ sourceDisplay = chalk . magenta ( ' [global]' ) ;
205+ }
206+
207+ logger . info ( `${ argv . key } : ${ valueDisplay } ${ sourceDisplay } ` ) ;
131208 } else {
132209 logger . error ( `Configuration key '${ argv . key } ' not found` ) ;
133210 }
@@ -186,11 +263,15 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
186263 let parsedValue : string | boolean | number = argv . value ;
187264
188265 // Check if config already exists to determine type
189- if ( argv . key in config ) {
190- if ( typeof config [ argv . key as keyof typeof config ] === 'boolean' ) {
266+ if ( argv . key in mergedConfig ) {
267+ if (
268+ typeof mergedConfig [ argv . key as keyof typeof mergedConfig ] ===
269+ 'boolean'
270+ ) {
191271 parsedValue = argv . value . toLowerCase ( ) === 'true' ;
192272 } else if (
193- typeof config [ argv . key as keyof typeof config ] === 'number'
273+ typeof mergedConfig [ argv . key as keyof typeof mergedConfig ] ===
274+ 'number'
194275 ) {
195276 parsedValue = Number ( argv . value ) ;
196277 }
@@ -206,9 +287,14 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
206287 }
207288 }
208289
209- const updatedConfig = updateConfig ( { [ argv . key ] : parsedValue } ) ;
290+ // Update config at the specified level
291+ const updatedConfig = updateConfig (
292+ { [ argv . key ] : parsedValue } ,
293+ configLevel ,
294+ ) ;
295+
210296 logger . info (
211- `Updated ${ argv . key } : ${ chalk . green ( updatedConfig [ argv . key as keyof typeof updatedConfig ] ) } ` ,
297+ `Updated ${ argv . key } : ${ chalk . green ( updatedConfig [ argv . key as keyof typeof updatedConfig ] ) } at ${ levelName } level ` ,
212298 ) ;
213299 return ;
214300 }
@@ -217,20 +303,47 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
217303 if ( argv . command === 'clear' ) {
218304 // Check if --all flag is provided
219305 if ( argv . all ) {
220- // Confirm with the user before clearing all settings
221- const isConfirmed = await confirm (
222- 'Are you sure you want to clear all configuration settings? This action cannot be undone.' ,
223- ) ;
306+ const confirmMessage = `Are you sure you want to clear all ${ levelName } configuration settings? This action cannot be undone.` ;
307+
308+ if ( configLevel === ConfigLevel . GLOBAL && ! argv . global && ! argv . g ) {
309+ // If no level was explicitly specified and we're using the default (project),
310+ // ask if they want to clear all levels
311+ const clearAllLevels = await confirm (
312+ 'Do you want to clear both project and global configuration? (No will clear only project config)' ,
313+ ) ;
314+
315+ if ( clearAllLevels ) {
316+ // Confirm before clearing all levels
317+ const isConfirmed = await confirm (
318+ 'Are you sure you want to clear ALL configuration settings (both project and global)? This action cannot be undone.' ,
319+ ) ;
320+
321+ if ( ! isConfirmed ) {
322+ logger . info ( 'Operation cancelled.' ) ;
323+ return ;
324+ }
325+
326+ // Clear all settings at all levels
327+ clearAllConfig ( ) ;
328+ logger . info (
329+ 'All configuration settings (both project and global) have been cleared. Default values will be used.' ,
330+ ) ;
331+ return ;
332+ }
333+ }
334+
335+ // Confirm before clearing the specified level
336+ const isConfirmed = await confirm ( confirmMessage ) ;
224337
225338 if ( ! isConfirmed ) {
226339 logger . info ( 'Operation cancelled.' ) ;
227340 return ;
228341 }
229342
230- // Clear all settings
231- clearAllConfig ( ) ;
343+ // Clear settings at the specified level
344+ clearConfigAtLevel ( configLevel ) ;
232345 logger . info (
233- ' All configuration settings have been cleared. Default values will be used.' ,
346+ ` All ${ levelName } configuration settings have been cleared.` ,
234347 ) ;
235348 return ;
236349 }
@@ -244,9 +357,12 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
244357
245358 const defaultConfig = getDefaultConfig ( ) ;
246359
247- // Check if the key exists in the config
248- if ( ! ( argv . key in config ) ) {
249- logger . error ( `Configuration key '${ argv . key } ' not found` ) ;
360+ // Check if the key exists at the specified level
361+ const levelConfig = getConfigAtLevel ( configLevel ) ;
362+ if ( ! ( argv . key in levelConfig ) ) {
363+ logger . error (
364+ `Configuration key '${ argv . key } ' not found at ${ levelName } level` ,
365+ ) ;
250366 return ;
251367 }
252368
@@ -258,22 +374,32 @@ export const command: CommandModule<SharedOptions, ConfigOptions> = {
258374 return ;
259375 }
260376
261- // Get the current config, create a new object without the specified key
262- const currentConfig = getConfig ( ) ;
263- const { [ argv . key ] : _ , ...newConfig } = currentConfig as Record <
264- string ,
265- any
266- > ;
267-
268- // Update the config file with the new object
269- updateConfig ( newConfig ) ;
270-
271- // Get the default value that will now be used
272- const defaultValue =
273- defaultConfig [ argv . key as keyof typeof defaultConfig ] ;
377+ // Clear the key at the specified level
378+ clearConfigKey ( argv . key , configLevel ) ;
379+
380+ // Get the value that will now be used
381+ const mergedAfterClear = getConfig ( ) ;
382+ const newValue =
383+ mergedAfterClear [ argv . key as keyof typeof mergedAfterClear ] ;
384+
385+ // Determine the source of the new value
386+ const afterClearInProject =
387+ argv . key in getConfigAtLevel ( ConfigLevel . PROJECT ) ;
388+ const afterClearInGlobal =
389+ argv . key in getConfigAtLevel ( ConfigLevel . GLOBAL ) ;
390+ const isDefaultAfterClear = ! afterClearInProject && ! afterClearInGlobal ;
391+
392+ let sourceDisplay = '' ;
393+ if ( isDefaultAfterClear ) {
394+ sourceDisplay = '(default)' ;
395+ } else if ( afterClearInProject ) {
396+ sourceDisplay = '(from project config)' ;
397+ } else if ( afterClearInGlobal ) {
398+ sourceDisplay = '(from global config)' ;
399+ }
274400
275401 logger . info (
276- `Cleared ${ argv . key } , now using default value : ${ chalk . green ( defaultValue ) } ` ,
402+ `Cleared ${ argv . key } at ${ levelName } level , now using: ${ chalk . green ( newValue ) } ${ sourceDisplay } ` ,
277403 ) ;
278404 return ;
279405 }
0 commit comments