Commit Graph

13 Commits

Author SHA1 Message Date
curo1305 fd6313acd9 feat(tui): auto-correct base URL if required path suffix is missing
Providers that need /v1 (lmstudio, llamacpp, qwen) now declare a
url_suffix field. The AI settings save action appends it automatically
and notifies the user if they entered a URL without it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 00:56:37 +02:00
curo1305 bf29ffc7d8 fix(tui): refresh API key placeholder when switching providers
When the user switches providers in the AI tab, the key Input now shows
"set" or "not set" based on what's actually stored in the vault for that
provider, and clears any in-progress key entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 00:10:31 +02:00
curo1305 f1213e28c8 feat(tui): AI provider tab + expanded General settings
Add a dedicated AI tab with provider Select, model Input, base URL Input,
and masked API key Input (write-only, stored in vault). Switching providers
reactively updates the model placeholder, base URL default, and shows/hides
the API key row for cloud vs. local providers. ctrl+s saves config and vault.

Extend GENERAL_FIELDS with Memory, Security, Plugin, and Daemon sections
using a new "section" header type and optional int cast for numeric fields.
_CoreField gains cast: type | None for automatic value coercion on save.

Add 5 new tests covering AI tab rendering, config save, vault key write,
vault key skip-on-empty, and section header rendering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:43:36 +02:00
curo1305 3b89d940de fix(tui): remove border-bottom from _TitleBar so title text renders
height: 1 + border-bottom: ascii left no row for content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:32:11 +02:00
curo1305 ee6c32b035 feat(tui): keyboard-only ASCII redesign of config TUI
Remove all Button widgets — saves and plugin toggles are keyboard-only
(ctrl+s, e, d). Replace Header with a plain _TitleBar Static. Apply a
dark monochrome ASCII theme: +---+ borders on inputs, DataTable, and
tab panes; #0d0d0d background; grey/white palette. Disable mouse at the
driver level via run(mouse=False). Update save test to drive via ctrl+s.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:28:59 +02:00
curo1305 1412ced7a8 feat(tui): full keyboard support for config TUI and chat slash completion
Add Header/Footer with visible key hints, ctrl+right/ctrl+left tab navigation,
ctrl+s save bindings for General and plugin config tabs, e/d bindings for
plugin enable/disable in the Plugins tab. Extract shared _do_save() and
_toggle_plugin() helpers so button and key paths share one code path.

Add WordCompleter to the chat REPL so Tab completes slash commands.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:11:39 +02:00
curo1305 54241a9e4e fix(config): fix empty General tab — height collapse and invalid CSS variable
_GeneralTab and _PluginConfigTab inherited from Widget (height: auto), causing
the inner VerticalScroll to get height: 1fr of an auto-height parent, which
collapsed to 0. Fix: inherit from VerticalScroll directly and remove the inner
wrapper. _PluginsTab gets DEFAULT_CSS to fill its TabPane.

Also replace $text-muted (invalid in Textual 8.x) with $foreground 50%.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 22:15:45 +02:00
curo1305 1201606187 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>
2026-05-18 21:28:19 +02:00
curo1305 b9b0918d3a feat(memory): wire database into reader, writer, and bootstrap
- reader: list_memories() queries memory_meta; lookup_memories() uses FTS5 with
  fallback to JSON index substring search
- writer: write_memory() and append_memory() upsert to DB after every file write
- dirs: bootstrap() calls init_db() + migrate_from_files() on startup

Existing .md files remain the canonical store; SQLite is the search index.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 15:23:49 +02:00
curo1305 7bdb2c3faf chore: remove dead expand() function and skills/ bootstrap dirs
expand() had zero callers anywhere in the codebase. The skills/bash,
skills/powershell, and skills/python directories were created on every
startup but the skills/ tree is not part of the current architecture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 18:09:36 +02:00
curo1305 c0c0156468 feat(plugins): Stage 2.1 — plugin framework and AI tool-use
Introduces a standalone plugin system where every integration lives as
an independent Python script in ~/.pyra/plugins/, not hardcoded in core.

Plugin framework (src/pyra/plugins/):
- base.py: Tool dataclass, PyraPlugin Protocol, BasePlugin helper
- loader.py: importlib-based discovery; one bad plugin never crashes pyra
- registry.py: singleton aggregating tools, slash commands, system prompts
- executor.py: approval gate — scans args, prompts y/N, scans result, logs
- install.py: copies bundled_plugins/ to ~/.pyra/plugins/ on install

Chat integration:
- AI tool-use loop (litellm function calling, up to 10 iterations)
- Plugin system prompt additions injected per session
- Plugin slash commands merged with static commands

CLI additions:
- pyra plugin list/install/enable/disable/setup
- pyra daemon start/stop/status/restart/install/uninstall (stubs for 2.4)

Config: PluginConfig + DaemonConfig added to PyraConfig (backwards-compatible)
Bootstrap: ~/.pyra/plugins/ and ~/.pyra/logs/ created on startup
Security: tool args and results always injection-scanned; plugin dirs
validated with assert_safe_path() before loading (symlink protection)

Tests: 37 new tests (loader, registry, executor, plugin isolation security)
161 total, all passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 15:35:20 +02:00
curo1305 ae565b0d68 feat(config): directory bootstrap and config manager
- config/schema.py: Pydantic v2 models — no API keys, only provider_id/model/base_url
- config/manager.py: ruamel.yaml round-trip load/save, chmod 600 enforced on write
- config/dirs.py: bootstrap() creates ~/.pyra/ tree, vault sentinel checked every startup

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:49:22 +02:00
curo1305 0a04e04490 chore: init project skeleton
Directory structure, pyproject.toml with hatchling build, and all
subpackage stubs for pyra Stage 1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:48:32 +02:00