core/cli: /config opens plyngent.toml and reloads

Add slash /config to edit the active config path in $EDITOR, then reload
providers/agent settings while keeping a valid provider/model selection.
This commit is contained in:
2026-07-15 14:52:19 +08:00
parent 95df11713d
commit 54712b541a
5 changed files with 88 additions and 0 deletions
+29
View File
@@ -293,6 +293,35 @@ def edit_cmd(state: ReplState) -> None:
click.echo(f"(edit) {len(text)} characters ready to send")
@slash.command("config")
@click.pass_obj
def config_cmd(state: ReplState) -> None:
"""Open plyngent.toml in ``$EDITOR``, then reload providers/agent settings.
Same file as ``plyngent config edit``. After the editor exits, config is
re-read; current provider/model are kept when still valid.
"""
from plyngent import config as config_mod
from plyngent.cli.editor import open_in_editor
path = state.config.path
try:
open_in_editor(path)
except click.ClickException as exc:
click.echo(f"error: {exc}")
return
try:
state.reload_config_from_disk()
except (config_mod.ConfigFormatError, ValueError, OSError) as exc:
click.secho(f"error: config reload failed: {exc}", fg="red")
click.echo(f"config file: {path}")
return
if state.config.bad_providers:
names = ", ".join(sorted(state.config.bad_providers.keys()))
click.secho(f"warning: ignored bad providers: {names}", fg="yellow")
click.echo(f"config reloaded from {path}\nprovider={state.provider_name} model={state.model}")
@slash.command("status")
@click.pass_obj
def status_cmd(state: ReplState) -> None:
+33
View File
@@ -111,6 +111,39 @@ class ReplState:
self.agent.messages = messages
self.sync_display_flags()
def reload_config_from_disk(self) -> None:
"""Re-read TOML config and re-bind provider/model when still valid."""
import click
from plyngent.cli.selection import select_model, select_provider
from plyngent.runtime import ProviderNotSupportedError
from plyngent.tools import set_path_denylist
self.config.reload()
set_path_denylist(self.config.agent_config.path_denylist or None)
preferred_provider = self.provider_name if self.provider_name in self.config.providers else None
preferred_model = self.model
try:
pname, provider = select_provider(
self.config.providers,
preferred=preferred_provider,
interactive=False,
)
except (click.ClickException, ProviderNotSupportedError) as exc:
msg = f"config reloaded but provider selection failed: {exc}"
raise ValueError(msg) from exc
try:
model_id = select_model(provider, preferred=preferred_model, interactive=False)
except click.ClickException:
# Previous model not on this provider; pick first listed or keep free-form.
model_id = next(iter(sorted(provider.models.keys()))) if provider.models else preferred_model
self.provider_name = pname
self.provider = provider
self.model = model_id
self.rebuild_client()
def _set_workspace(self, path: Path) -> None:
"""Update REPL + tool workspace root."""
resolved = path.expanduser().resolve()
+5
View File
@@ -99,6 +99,11 @@ class ConfigStore:
self._agent = _parse_agent(cast("dict[str, object]", raw.get("agent", {})))
self._providers, self._bad_providers = _parse_providers(document)
@property
def path(self) -> Path:
"""Filesystem path of the TOML config file."""
return self._path
# -- database (read-only) --
@property