@@ -768,13 +768,15 @@ def _version_callback(value: bool) -> None:
768768plugin_app = typer .Typer (name = "plugin" , help = "Manage plugins" )
769769auth_app = typer .Typer (name = "auth" , help = "Manage authentication" )
770770provider_app = typer .Typer (name = "provider" , help = "Manage provider profiles" )
771+ config_app = typer .Typer (name = "config" , help = "Show or update settings" )
771772cron_app = typer .Typer (name = "cron" , help = "Manage cron scheduler and jobs" )
772773autopilot_app = typer .Typer (name = "autopilot" , help = "Manage repo autopilot" )
773774
774775app .add_typer (mcp_app )
775776app .add_typer (plugin_app )
776777app .add_typer (auth_app )
777778app .add_typer (provider_app )
779+ app .add_typer (config_app )
778780app .add_typer (cron_app )
779781app .add_typer (autopilot_app )
780782
@@ -1967,6 +1969,72 @@ def auth_copilot_logout() -> None:
19671969 print ("Copilot authentication cleared." )
19681970
19691971
1972+ # ---- config subcommands ----
1973+
1974+
1975+ def _config_resolve_target (settings : object , key : str ) -> tuple [object , str ]:
1976+ target = settings
1977+ parts = key .split ("." )
1978+ for part in parts [:- 1 ]:
1979+ if not hasattr (target , part ):
1980+ raise KeyError (key )
1981+ target = getattr (target , part )
1982+ leaf = parts [- 1 ]
1983+ if not hasattr (target , leaf ):
1984+ raise KeyError (key )
1985+ return target , leaf
1986+
1987+
1988+ def _config_coerce_value (current : object , raw : str ) -> object :
1989+ if isinstance (current , bool ):
1990+ lowered = raw .strip ().lower ()
1991+ if lowered in {"1" , "true" , "yes" , "on" }:
1992+ return True
1993+ if lowered in {"0" , "false" , "no" , "off" }:
1994+ return False
1995+ raise ValueError (f"Invalid boolean value: { raw } " )
1996+ if isinstance (current , int ) and not isinstance (current , bool ):
1997+ return int (raw )
1998+ if isinstance (current , float ):
1999+ return float (raw )
2000+ if isinstance (current , list ):
2001+ return [entry .strip () for entry in raw .split ("," ) if entry .strip ()]
2002+ return raw
2003+
2004+
2005+ @config_app .command ("show" )
2006+ def config_show () -> None :
2007+ """Print the resolved settings JSON."""
2008+ from openharness .commands .registry import _settings_json_for_display
2009+ from openharness .config .settings import load_settings
2010+
2011+ print (_settings_json_for_display (load_settings ()), flush = True )
2012+
2013+
2014+ @config_app .command ("set" )
2015+ def config_set (
2016+ key : str = typer .Argument (..., help = "Setting key, including dotted nested keys" ),
2017+ value : str = typer .Argument (..., help = "Value to store" ),
2018+ ) -> None :
2019+ """Persist one setting in ~/.openharness/settings.json."""
2020+ from openharness .config .settings import load_settings , save_settings
2021+
2022+ settings = load_settings ()
2023+ try :
2024+ target , leaf = _config_resolve_target (settings , key )
2025+ except KeyError :
2026+ print (f"Unknown config key: { key } " , file = sys .stderr )
2027+ raise typer .Exit (1 )
2028+ try :
2029+ coerced = _config_coerce_value (getattr (target , leaf ), value )
2030+ except ValueError as exc :
2031+ print (f"Error: { exc } " , file = sys .stderr )
2032+ raise typer .Exit (1 )
2033+ setattr (target , leaf , coerced )
2034+ save_settings (settings )
2035+ print (f"Updated { key } " , flush = True )
2036+
2037+
19702038# ---- provider subcommands ----
19712039
19722040
0 commit comments