feat(config): add /config TUI with tab-based settings and plugin config framework

- textual-based ConfigApp with General, Plugins, and per-plugin tabs
- GeneralConfig (user_name, assistant_name) + plugin_settings dict added to PyraConfig
- ConfigField dataclass and config_fields() method added to plugin protocol
- /config slash command in chat REPL launches the TUI
- pyra auto-runs setup wizard on first invocation when no config.yaml exists
- CLAUDE.md updated with config_fields() plugin guide and Code Inventory entries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curo1305
2026-05-18 21:28:19 +02:00
parent 6bb7c77692
commit 1201606187
7 changed files with 291 additions and 4 deletions
+15 -1
View File
@@ -1,12 +1,22 @@
from __future__ import annotations
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any, Callable, Coroutine, Protocol, runtime_checkable
if TYPE_CHECKING:
from rich.console import Console
@dataclass
class ConfigField:
key: str # key in plugin_settings[name] dict
label: str # display label in the config TUI
type: str # "text" | "bool" | "select"
default: Any = ""
options: list[str] = field(default_factory=list) # for "select" type
description: str = "" # optional hint shown below the field
@dataclass
class Tool:
name: str
@@ -35,6 +45,7 @@ class PyraPlugin(Protocol):
def agent_spec(self) -> AgentSpec | None: ...
def setup(self, console: Console, vault_writer: Callable[[str, str], None]) -> None: ...
def daemon_tasks(self) -> list[Coroutine]: ... # type: ignore[type-arg]
def config_fields(self) -> list[ConfigField]: ...
class BasePlugin:
@@ -64,3 +75,6 @@ class BasePlugin:
def daemon_tasks(self) -> list[Coroutine]: # type: ignore[type-arg]
return []
def config_fields(self) -> list[ConfigField]:
return []