From 01655124b5fd24e4d7c2d68ef62deebe50f7d23d Mon Sep 17 00:00:00 2001 From: curo1305 Date: Tue, 19 May 2026 14:28:37 +0200 Subject: [PATCH] Update description --- README.md | 53 +++++++++++++++++++++++++++++++++++++++---------- src/pyra/cli.py | 16 +++++++-------- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ddda8b0..92aa3b2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Pyra -A personal AI assistant CLI with vault-first security. Combines multi-provider AI chat with -long-term memory and (coming) automation skills. +A personal AI assistant CLI with vault-first security. Combines multi-provider AI chat, +long-term memory, and an extensible plugin system. ## Quick Start @@ -31,6 +31,11 @@ pyra chat # start talking | `pyra memory read ` | Read a memory file | | `pyra memory write ` | Write a memory file | | `pyra memory append ` | Append to a memory file | +| `pyra plugin list` | List installed and available plugins | +| `pyra plugin install ` | Install a bundled plugin | +| `pyra plugin enable ` | Enable an installed plugin | +| `pyra plugin disable ` | Disable a plugin (keeps it installed) | +| `pyra plugin setup ` | Run a plugin's credential setup wizard | ### In-chat slash commands @@ -38,6 +43,7 @@ pyra chat # start talking |---------|-------------| | `/help` | Show available commands | | `/memory list` | List memory files | +| `/config` | Open the configuration TUI | | `/clear` | Clear conversation history | | `/quit` or `/exit` | Exit Pyra | @@ -48,16 +54,33 @@ pyra chat # start talking - **Prompt injection scanner** — warns on suspicious AI output, logs to `~/.pyra/security.log` - **Path sandboxing** — the AI can only reference memory files by name; traversal is blocked +## Plugins + +Pyra has an extensible plugin system. Bundled plugins are shipped with Pyra and installed on +demand; third-party plugins can be dropped into `~/.pyra/plugins/` directly. + +Each plugin is a directory containing a `manifest.json` and a `plugin.py`. Plugin credentials +are stored in the vault under namespaced keys (`plugin::`) — never in `config.yaml`. + +```bash +pyra plugin list # see what's available +pyra plugin install # copy a bundled plugin to ~/.pyra/plugins/ +pyra plugin setup # enter credentials (stored in vault) +pyra plugin enable # activate for the next chat session +``` + ## Memory Pyra reads your memory files at the start of each session and injects them as context. -Files are plain Markdown stored in `~/.pyra/memory/`: +Files are plain Markdown stored in `~/.pyra/memory/`, indexed by a SQLite full-text search +database (`memory.db`) for fast in-chat lookup. ``` ~/.pyra/memory/ ├── user/profile.md ← who you are ├── context/ ← ongoing projects -└── knowledge/ ← general notes +├── knowledge/ ← general notes +└── memory.db ← FTS5 search index (auto-managed) ``` ## `~/.pyra/` Directory @@ -67,15 +90,25 @@ Files are plain Markdown stored in `~/.pyra/memory/`: ├── config.yaml ← provider + model (no secrets) ├── security.log ← injection event log ├── memory/ ← AI-readable long-term memory -├── skills/ ← automation scripts (Stage 2) +│ └── memory.db ← SQLite FTS5 search index +├── plugins/ ← installed plugins +│ └── / +│ ├── manifest.json +│ └── plugin.py +├── logs/ ← execution logs +│ ├── tool_executions.log +│ └── plugin_errors.log └── vault/ ← secure, AI-inaccessible storage └── secrets/api_keys.json ``` ## Roadmap -- **Stage 1** (now): Core CLI, multi-provider chat, memory, vault security -- **Stage 2**: Skills — shell/PowerShell/Python automations with user approval gates -- **Stage 3**: Vault encryption with `age` -- **Stage 4**: Security audit sub-agent -- **Stage 5**: Web UI, embedding-based memory search +- **Stage 1** ✅ Core CLI — multi-provider chat, memory, vault security +- **Stage 2** ✅ Plugin Framework — extensible tools, slash commands, approval gates +- **Stage 3** ✅ Memory Database — SQLite + FTS5 full-text search index +- **Stage 4** Vault Encryption — `age`-based encryption of `~/.pyra/vault/secrets/` +- **Stage 5** Skills System — YAML-defined multi-plugin workflows with event triggers +- **Stage 6** Daemon + Messaging Bots — always-on asyncio daemon, Matrix/Telegram/Signal bots +- **Stage 7** Security Audit Sub-agent — automated scanning for injection, CVEs, permission drift +- **Stage 8** Web UI — optional local interface, embedding-based memory search diff --git a/src/pyra/cli.py b/src/pyra/cli.py index 255f031..d4f53da 100644 --- a/src/pyra/cli.py +++ b/src/pyra/cli.py @@ -174,7 +174,7 @@ def plugin_install(name: str) -> None: install_bundled_plugin(name, bundled_dir, plugins_dir) console.print(f"[green]Installed:[/green] {name}") console.print(f" Enable: [dim]pyra plugin enable {name}[/dim]") - console.print(f" Confirm: [dim]pyra plugin setup {name}[/dim]") + console.print(f" Configure: [dim]pyra plugin setup {name}[/dim]") except FileNotFoundError as exc: console.print(f"[red]Error:[/red] {exc}") except Exception as exc: @@ -269,40 +269,40 @@ def daemon() -> None: @daemon.command("start") def daemon_start() -> None: """Start the Pyra daemon in the background.""" - console.print("[yellow]Daemon (Stage 2.4) is not yet implemented.[/yellow]") + console.print("[yellow]Daemon (Stage 6) is not yet implemented.[/yellow]") @daemon.command("stop") def daemon_stop() -> None: """Stop the running Pyra daemon.""" - console.print("[yellow]Daemon (Stage 2.4) is not yet implemented.[/yellow]") + console.print("[yellow]Daemon (Stage 6) is not yet implemented.[/yellow]") @daemon.command("status") def daemon_status() -> None: """Show daemon status.""" - console.print("[yellow]Daemon (Stage 2.4) is not yet implemented.[/yellow]") + console.print("[yellow]Daemon (Stage 6) is not yet implemented.[/yellow]") @daemon.command("restart") def daemon_restart() -> None: """Restart the Pyra daemon.""" - console.print("[yellow]Daemon (Stage 2.4) is not yet implemented.[/yellow]") + console.print("[yellow]Daemon (Stage 6) is not yet implemented.[/yellow]") @daemon.command("install") def daemon_install() -> None: """Install Pyra as a system service (launchd/systemd).""" - console.print("[yellow]Daemon service install (Stage 2.4) is not yet implemented.[/yellow]") + console.print("[yellow]Daemon service install (Stage 6) is not yet implemented.[/yellow]") @daemon.command("uninstall") def daemon_uninstall() -> None: """Remove the Pyra system service.""" - console.print("[yellow]Daemon service uninstall (Stage 2.4) is not yet implemented.[/yellow]") + console.print("[yellow]Daemon service uninstall (Stage 6) is not yet implemented.[/yellow]") @daemon.command("run", hidden=True) def daemon_run() -> None: """Run daemon in foreground (used by service manager).""" - console.print("[yellow]Daemon (Stage 2.4) is not yet implemented.[/yellow]") + console.print("[yellow]Daemon (Stage 6) is not yet implemented.[/yellow]")