Commit Graph

59 Commits

Author SHA1 Message Date
curo1305 e792c5e0c9 feat(cli): wire all subcommands
pyra (default→chat), pyra setup, pyra chat, pyra memory list/read/write/append
All routes call bootstrap() first; PyraSecurityError exits with clear message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:52:26 +02:00
curo1305 0fe6332316 feat(chat): streaming REPL with rich renderer
- chat/renderer.py: Live streaming markdown, injection warning panel, redaction
- chat/history.py: ConversationHistory with system prompt + memory context injection,
  token budget trimming
- chat/session.py: prompt_toolkit REPL, slash commands (/quit /clear /help /memory list),
  vault key retrieval inline at call time (not stored), injection scan after each response

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:52:08 +02:00
curo1305 1448bb4650 feat(security): prompt injection scanner and API key redaction
- 15 regex patterns across 4 categories: instruction-override, role-switch,
  jailbreak, exfiltration, credential-fishing
- scan_response() returns InjectionWarning list and logs to ~/.pyra/security.log
- redact_api_keys() strips sk-ant-, sk-, AIza patterns before display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:51:23 +02:00
curo1305 18c39cc152 feat(memory): sandboxed markdown memory system
- memory/index.py: auto-regenerate MEMORY_INDEX.md on every write
- memory/reader.py: list_memories(), read_memory(), load_context_for_session()
  all go through assert_safe_path() + relative_to check
- memory/writer.py: write_memory(), append_memory() — relative names only,
  no absolute paths or traversal, calls update_index() after every write

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:50:55 +02:00
curo1305 7617a80595 feat(vault): API key storage in vault only
- vault/reader.py: get_key() reads from ~/.pyra/vault/secrets/api_keys.json
- vault/writer.py: set_key(), delete_key() — only writer callable from setup
- Both call assert_safe_path() as defense-in-depth
- Keys file stays chmod 400; temporarily 600 during write then locked again
- Config.yaml never touched by either module

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:50:24 +02:00
curo1305 43a8a363ba feat(setup): provider registry and interactive setup wizard
- setup/providers.py: registry for 8 providers (3 local, 5 cloud), frozen dataclasses
- setup/wizard.py: questionary-based wizard — provider select, model input,
  API key collected via vault.writer (not config.yaml), connectivity check,
  test call via litellm

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:50:04 +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 a96b540234 feat(security): vault wall, path guard, and utils
- utils/paths.py: pyra_home(), ensure_dir(), safe_chmod(), expand()
- security/boundaries.py: VaultAccessError, PyraSecurityError,
  assert_safe_path() (called before every file read), check_vault_lock()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:48:50 +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