Update description

This commit is contained in:
curo1305
2026-05-19 14:28:37 +02:00
parent b3851a2715
commit 01655124b5
2 changed files with 51 additions and 18 deletions
+43 -10
View File
@@ -1,7 +1,7 @@
# Pyra # Pyra
A personal AI assistant CLI with vault-first security. Combines multi-provider AI chat with A personal AI assistant CLI with vault-first security. Combines multi-provider AI chat,
long-term memory and (coming) automation skills. long-term memory, and an extensible plugin system.
## Quick Start ## Quick Start
@@ -31,6 +31,11 @@ pyra chat # start talking
| `pyra memory read <name>` | Read a memory file | | `pyra memory read <name>` | Read a memory file |
| `pyra memory write <name> <content>` | Write a memory file | | `pyra memory write <name> <content>` | Write a memory file |
| `pyra memory append <name> <content>` | Append to a memory file | | `pyra memory append <name> <content>` | Append to a memory file |
| `pyra plugin list` | List installed and available plugins |
| `pyra plugin install <name>` | Install a bundled plugin |
| `pyra plugin enable <name>` | Enable an installed plugin |
| `pyra plugin disable <name>` | Disable a plugin (keeps it installed) |
| `pyra plugin setup <name>` | Run a plugin's credential setup wizard |
### In-chat slash commands ### In-chat slash commands
@@ -38,6 +43,7 @@ pyra chat # start talking
|---------|-------------| |---------|-------------|
| `/help` | Show available commands | | `/help` | Show available commands |
| `/memory list` | List memory files | | `/memory list` | List memory files |
| `/config` | Open the configuration TUI |
| `/clear` | Clear conversation history | | `/clear` | Clear conversation history |
| `/quit` or `/exit` | Exit Pyra | | `/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` - **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 - **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:<name>:<key>`) — never in `config.yaml`.
```bash
pyra plugin list # see what's available
pyra plugin install <name> # copy a bundled plugin to ~/.pyra/plugins/
pyra plugin setup <name> # enter credentials (stored in vault)
pyra plugin enable <name> # activate for the next chat session
```
## Memory ## Memory
Pyra reads your memory files at the start of each session and injects them as context. 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/ ~/.pyra/memory/
├── user/profile.md ← who you are ├── user/profile.md ← who you are
├── context/ ← ongoing projects ├── context/ ← ongoing projects
── knowledge/ ← general notes ── knowledge/ ← general notes
└── memory.db ← FTS5 search index (auto-managed)
``` ```
## `~/.pyra/` Directory ## `~/.pyra/` Directory
@@ -67,15 +90,25 @@ Files are plain Markdown stored in `~/.pyra/memory/`:
├── config.yaml ← provider + model (no secrets) ├── config.yaml ← provider + model (no secrets)
├── security.log ← injection event log ├── security.log ← injection event log
├── memory/ ← AI-readable long-term memory ├── memory/ ← AI-readable long-term memory
├── skills/ ← automation scripts (Stage 2) │ └── memory.db ← SQLite FTS5 search index
├── plugins/ ← installed plugins
│ └── <name>/
│ ├── manifest.json
│ └── plugin.py
├── logs/ ← execution logs
│ ├── tool_executions.log
│ └── plugin_errors.log
└── vault/ ← secure, AI-inaccessible storage └── vault/ ← secure, AI-inaccessible storage
└── secrets/api_keys.json └── secrets/api_keys.json
``` ```
## Roadmap ## Roadmap
- **Stage 1** (now): Core CLI, multi-provider chat, memory, vault security - **Stage 1** Core CLI multi-provider chat, memory, vault security
- **Stage 2**: Skills — shell/PowerShell/Python automations with user approval gates - **Stage 2** ✅ Plugin Framework — extensible tools, slash commands, approval gates
- **Stage 3**: Vault encryption with `age` - **Stage 3** ✅ Memory Database — SQLite + FTS5 full-text search index
- **Stage 4**: Security audit sub-agent - **Stage 4** Vault Encryption — `age`-based encryption of `~/.pyra/vault/secrets/`
- **Stage 5**: Web UI, embedding-based memory search - **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
+8 -8
View File
@@ -174,7 +174,7 @@ def plugin_install(name: str) -> None:
install_bundled_plugin(name, bundled_dir, plugins_dir) install_bundled_plugin(name, bundled_dir, plugins_dir)
console.print(f"[green]Installed:[/green] {name}") console.print(f"[green]Installed:[/green] {name}")
console.print(f" Enable: [dim]pyra plugin enable {name}[/dim]") 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: except FileNotFoundError as exc:
console.print(f"[red]Error:[/red] {exc}") console.print(f"[red]Error:[/red] {exc}")
except Exception as exc: except Exception as exc:
@@ -269,40 +269,40 @@ def daemon() -> None:
@daemon.command("start") @daemon.command("start")
def daemon_start() -> None: def daemon_start() -> None:
"""Start the Pyra daemon in the background.""" """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") @daemon.command("stop")
def daemon_stop() -> None: def daemon_stop() -> None:
"""Stop the running Pyra daemon.""" """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") @daemon.command("status")
def daemon_status() -> None: def daemon_status() -> None:
"""Show daemon status.""" """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") @daemon.command("restart")
def daemon_restart() -> None: def daemon_restart() -> None:
"""Restart the Pyra daemon.""" """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") @daemon.command("install")
def daemon_install() -> None: def daemon_install() -> None:
"""Install Pyra as a system service (launchd/systemd).""" """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") @daemon.command("uninstall")
def daemon_uninstall() -> None: def daemon_uninstall() -> None:
"""Remove the Pyra system service.""" """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) @daemon.command("run", hidden=True)
def daemon_run() -> None: def daemon_run() -> None:
"""Run daemon in foreground (used by service manager).""" """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]")