LM Studio's /v1/models returns all downloaded models, not just loaded
ones. Use /api/v0/models with state filtering in both fetch_loaded_models()
and _fetch_local_models() so only RAM-resident models are shown as loaded.
This also restores the _choose_model() fallback that offers downloaded-but-
unloaded models when nothing is active in LM Studio.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
For Ollama, /api/tags returns all installed models, not running ones.
Add fetch_loaded_models() using /api/ps for Ollama (and /v1/models for
LM Studio/llama.cpp, which already return only loaded models).
_show_local_model_status() now calls fetch_loaded_models() so the
setup wizard correctly shows only in-memory models for Ollama.
At chat session startup, local providers warn when the configured model
is not currently loaded, or when nothing is loaded at all.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cover _fetch_local_models (LM Studio and Ollama parsing, error paths,
missing base_url), _fetch_lmstudio_available_models (happy path and
errors), and _load_lmstudio_model (success, API failure, exception).
All mocked via monkeypatch/MagicMock — no real HTTP calls.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cover _USE_CASE_PLUGINS mapping, _suggest_plugins side effects, _build_system_base
output for all name/purpose combinations, and GeneralConfig.purpose round-trip.
Also update CLAUDE.md with the testing workflow rule.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
conftest patches mdb._DB_PATH and calls init_db() after directory creation
so all existing tests continue to work with the new DB layer. New
test_memory_db.py covers upsert, search, remove, migration, and the
updated list_memories/lookup_memories integration paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
load_all() now builds a _tools: dict[str, Tool] index at startup.
get_all_tools() returns list(_tools.values()) and find_tool() is a
direct dict.get() instead of rebuilding the full tool list from every
plugin on every tool call during a session.
Updated test helper to populate _tools alongside _plugins to match
the actual load_all() behaviour.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>