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:
@@ -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 []
|
||||
|
||||
Reference in New Issue
Block a user