docs: CLAUDE.md with full 5-stage roadmap and README
CLAUDE.md: architecture table, security rules, all module descriptions, roadmap Stages 1-5, adding-provider guide, commit convention. README.md: quick start, provider table, command reference, security overview. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
# Pyra — Developer Guide
|
||||
|
||||
## What Is This
|
||||
|
||||
Pyra is a personal AI assistant CLI combining a multi-provider AI chat interface with
|
||||
an automation/skills system (Stage 2+) and an encrypted vault (Stage 3+).
|
||||
|
||||
## Project Roadmap
|
||||
|
||||
### Stage 1 — Core CLI (current)
|
||||
Working `pyra` executable with provider setup wizard, streaming chat REPL, .md-based
|
||||
memory in `~/.pyra/memory/`, and hard security boundaries around the vault.
|
||||
|
||||
### Stage 2 — Skills / Automations
|
||||
Shell (.sh), PowerShell (.ps1), and Python (.py) scripts in `~/.pyra/skills/`. The AI
|
||||
can suggest running a skill, but execution requires explicit user approval (y/n prompt).
|
||||
No skill can access the vault. Skills are discovered by the pyra CLI, not by the AI.
|
||||
|
||||
### Stage 3 — Vault Encryption
|
||||
Encrypt `~/.pyra/vault/secrets/` using `age` (or GPG fallback). Pyra decrypts in memory
|
||||
at call time only — no plaintext ever written to disk after initial setup. Secret
|
||||
rotation support. Per-key passphrases optional.
|
||||
|
||||
### Stage 4 — Security Audit Sub-agent
|
||||
A separate `pyra security audit` command that spins up a sandboxed AI agent whose sole
|
||||
job is scanning for vulnerabilities: prompt injection in memory files, unexpected vault
|
||||
access attempts in `security.log`, outdated dependency CVEs, permission drift on `~/.pyra/`.
|
||||
Report written to `~/.pyra/security_audit.md` (not AI-readable during normal chat).
|
||||
|
||||
### Stage 5 — Web UI / Advanced Features
|
||||
Optional local web interface (FastAPI + HTMX or similar). Embedding-based memory search
|
||||
(ChromaDB or sqlite-vec). Scheduled automations via cron-style skill scheduling.
|
||||
Multi-profile support (work vs personal).
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
### Source: `src/pyra/`
|
||||
|
||||
| Module | Purpose |
|
||||
|--------|---------|
|
||||
| `cli.py` | Click entrypoint. Subcommands: `setup`, `chat`, `memory` |
|
||||
| `setup/providers.py` | Provider registry — pure data, no I/O |
|
||||
| `setup/wizard.py` | questionary-based interactive setup wizard |
|
||||
| `config/schema.py` | Pydantic v2 models — no API keys, only `provider_id/model/base_url` |
|
||||
| `config/manager.py` | ruamel.yaml round-trip config read/write, chmod 600 enforced |
|
||||
| `config/dirs.py` | `bootstrap()` — creates `~/.pyra/` tree, checks vault sentinel every startup |
|
||||
| `chat/session.py` | prompt_toolkit REPL loop, slash commands, calls vault reader inline |
|
||||
| `chat/renderer.py` | Live streaming markdown via rich, injection warning panel, key redaction |
|
||||
| `chat/history.py` | Conversation list, token budget trimming, system prompt construction |
|
||||
| `memory/reader.py` | `list_memories()`, `read_memory()`, `load_context_for_session()` |
|
||||
| `memory/writer.py` | `write_memory()`, `append_memory()` — relative names only, no traversal |
|
||||
| `memory/index.py` | Auto-regenerate `MEMORY_INDEX.md` on every write |
|
||||
| `vault/reader.py` | `get_key(provider_id)` — sole accessor of `vault/secrets/api_keys.json` |
|
||||
| `vault/writer.py` | `set_key()`, `delete_key()` — only called from setup wizard |
|
||||
| `security/boundaries.py` | `assert_safe_path()`, `check_vault_lock()`, `BLOCKED_PREFIXES` |
|
||||
| `security/injection.py` | `scan_response()` — 15 regex patterns, 4 categories, logs to `security.log` |
|
||||
| `utils/paths.py` | `pyra_home()`, `ensure_dir()`, `safe_chmod()`, `expand()` |
|
||||
|
||||
### Runtime: `~/.pyra/`
|
||||
|
||||
```
|
||||
~/.pyra/
|
||||
├── config.yaml chmod 600 ← provider_id, model, base_url ONLY
|
||||
├── security.log chmod 600 ← injection event log
|
||||
├── memory/ chmod 700
|
||||
│ ├── user/profile.md
|
||||
│ ├── context/
|
||||
│ ├── knowledge/
|
||||
│ └── MEMORY_INDEX.md
|
||||
├── skills/ chmod 700 ← Stage 2
|
||||
│ ├── bash/
|
||||
│ ├── powershell/
|
||||
│ └── python/
|
||||
└── vault/ chmod 700 ← AI CANNOT ACCESS
|
||||
├── .vault_lock chmod 400 ← sentinel; missing = refuse to start
|
||||
└── secrets/
|
||||
└── api_keys.json chmod 400 ← ALL API keys
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Rules (never break these)
|
||||
|
||||
1. **Never pass config file contents into a system prompt** — config may reveal provider/model
|
||||
2. **Never bypass `assert_safe_path()`** — not even in tests (use `tmp_pyra_home` fixture instead)
|
||||
3. **Always `chmod 600/400`** after writing any file in `~/.pyra/`
|
||||
4. **No shell execution from AI-generated text** — ever (Stage 2 uses explicit approval gates)
|
||||
5. **`vault/reader.py` and `vault/writer.py` are the only modules that import from `pyra.vault`**
|
||||
6. **API key retrieved inline at call time** — never stored as an instance variable or logged
|
||||
|
||||
## Adding a New Provider
|
||||
|
||||
Edit `src/pyra/setup/providers.py`. Add a new `Provider` dataclass entry with all required fields.
|
||||
litellm handles dispatch automatically via the `litellm_prefix` field.
|
||||
Add a test in `tests/unit/test_providers.py` to verify the new entry.
|
||||
|
||||
## Installing for Development
|
||||
|
||||
```bash
|
||||
uv venv && source .venv/bin/activate
|
||||
uv pip install -e ".[dev]"
|
||||
pyra setup
|
||||
```
|
||||
|
||||
Or with pip:
|
||||
```bash
|
||||
python -m venv .venv && source .venv/bin/activate
|
||||
pip install -e ".[dev]"
|
||||
pyra setup
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
pytest tests/ -v # all unit + security tests
|
||||
pytest tests/integration/test_lmstudio.py # requires LM Studio at localhost:1234
|
||||
```
|
||||
|
||||
## Commit Convention
|
||||
|
||||
```
|
||||
feat(module): short description
|
||||
fix(module): short description
|
||||
test: description
|
||||
docs: description
|
||||
chore: description
|
||||
```
|
||||
@@ -0,0 +1,81 @@
|
||||
# Pyra
|
||||
|
||||
A personal AI assistant CLI with vault-first security. Combines multi-provider AI chat with
|
||||
long-term memory and (coming) automation skills.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
pip install -e . # or: pipx install .
|
||||
pyra setup # choose your AI provider
|
||||
pyra chat # start talking
|
||||
```
|
||||
|
||||
## Providers
|
||||
|
||||
**Local (no API key needed):**
|
||||
- LM Studio — `http://localhost:1234`
|
||||
- Ollama — `http://localhost:11434`
|
||||
- llama.cpp server — `http://localhost:8080`
|
||||
|
||||
**Cloud:**
|
||||
- Anthropic (Claude), OpenAI (GPT), Google (Gemini), DeepSeek, Qwen
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `pyra setup` | Run the provider setup wizard |
|
||||
| `pyra chat` | Start interactive chat |
|
||||
| `pyra memory list` | List memory files |
|
||||
| `pyra memory read <name>` | Read a memory file |
|
||||
| `pyra memory write <name> <content>` | Write a memory file |
|
||||
| `pyra memory append <name> <content>` | Append to a memory file |
|
||||
|
||||
### In-chat slash commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/help` | Show available commands |
|
||||
| `/memory list` | List memory files |
|
||||
| `/clear` | Clear conversation history |
|
||||
| `/quit` or `/exit` | Exit Pyra |
|
||||
|
||||
## Security
|
||||
|
||||
- **API keys live in `~/.pyra/vault/`** — the AI cannot read this directory
|
||||
- **`config.yaml` never contains credentials** — only provider ID, model name, and base URL
|
||||
- **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
|
||||
|
||||
## 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/`:
|
||||
|
||||
```
|
||||
~/.pyra/memory/
|
||||
├── user/profile.md ← who you are
|
||||
├── context/ ← ongoing projects
|
||||
└── knowledge/ ← general notes
|
||||
```
|
||||
|
||||
## `~/.pyra/` Directory
|
||||
|
||||
```
|
||||
~/.pyra/
|
||||
├── config.yaml ← provider + model (no secrets)
|
||||
├── security.log ← injection event log
|
||||
├── memory/ ← AI-readable long-term memory
|
||||
├── skills/ ← automation scripts (Stage 2)
|
||||
└── 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
|
||||
Reference in New Issue
Block a user