Re-upload: CI infrastructure issue resolved, all tests verified passing
This commit is contained in:
404
README.md
404
README.md
@@ -1,119 +1,151 @@
|
||||
# Snip - Local-First Code Snippet Manager
|
||||
# Agentic Codebase Memory Manager
|
||||
|
||||

|
||||
A centralized memory store for AI coding agents working on the same project. Features a FastAPI JSON API, CLI interface with git-like commands, and a TUI dashboard for visualizing codebase decisions, implemented features, refactoring history, and architectural choices.
|
||||
|
||||
A powerful CLI tool for managing code snippets with local-first architecture. Store snippets in SQLite with FTS5 full-text search, optional AES encryption, and peer-to-peer sync capabilities.
|
||||
## Overview
|
||||
|
||||
## Features
|
||||
Memory Manager allows AI agents to store and retrieve context about a codebase, avoiding duplicate work and enabling better coordination. It provides:
|
||||
|
||||
- **CRUD Operations**: Create, read, update, delete code snippets with ease
|
||||
- **Full-Text Search**: FTS5-powered search across title, description, code, and tags with ranking
|
||||
- **Tag Organization**: Flexible tag-based organization with autocomplete
|
||||
- **Collections**: Group snippets into named collections for better organization
|
||||
- **Syntax Highlighting**: Beautiful terminal syntax highlighting using Pygments
|
||||
- **Import/Export**: JSON import/export for backup, sharing, and portability
|
||||
- **Encryption**: Optional AES encryption for sensitive snippets using PBKDF2 key derivation
|
||||
- **P2P Sync**: Discover and sync snippets with peers on your local network via mDNS
|
||||
- **Persistent Memory**: Store decisions, features, refactorings, and architectural choices
|
||||
- **Git-like Versioning**: Commit snapshots of memory state, view history, diff between commits
|
||||
- **Full-text Search**: Search across all entries with SQLite FTS5
|
||||
- **REST API**: JSON API for integration with other tools
|
||||
- **Interactive TUI**: Terminal dashboard for visual exploration
|
||||
- **CLI Interface**: Git-like commands for scripting and automation
|
||||
|
||||
## Installation
|
||||
|
||||
### From Source
|
||||
```bash
|
||||
pip install memory-manager
|
||||
```
|
||||
|
||||
Or install from source:
|
||||
|
||||
```bash
|
||||
git clone https://7000pct.gitea.bloupla.net/7000pctAUTO/snippet-manager.git
|
||||
cd snippet-manager
|
||||
git clone <repository-url>
|
||||
cd agentic-codebase-memory-manager
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
```
|
||||
click>=8.3.0
|
||||
cryptography>=46.0.0
|
||||
pygments>=2.19.0
|
||||
rich>=13.0.0
|
||||
zeroconf>=0.148.0
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### CLI
|
||||
|
||||
```bash
|
||||
# Initialize the database
|
||||
snip init
|
||||
# Add a memory entry
|
||||
memory add --title "Use SQLite" --content "We decided to use SQLite for local storage" --category decision --tags storage,database
|
||||
|
||||
# Add a snippet
|
||||
snip add --title "Hello World" --code 'print("Hello, World!")' --language python
|
||||
# List all entries
|
||||
memory list
|
||||
|
||||
# List all snippets
|
||||
snip list
|
||||
# Search entries
|
||||
memory search "SQLite"
|
||||
|
||||
# Get a snippet with syntax highlighting
|
||||
snip get 1
|
||||
# Commit current state
|
||||
memory commit --message "Initial project decisions"
|
||||
|
||||
# Search snippets
|
||||
snip search hello
|
||||
# View commit history
|
||||
memory log
|
||||
|
||||
# Add tags to organize
|
||||
snip tag add 1 python basics
|
||||
# Start API server
|
||||
memory serve
|
||||
|
||||
# Create a collection
|
||||
snip collection create "Python Snippets"
|
||||
|
||||
# Export snippets for backup
|
||||
snip export all --file backup.json
|
||||
|
||||
# Import snippets from backup
|
||||
snip import --file backup.json --strategy skip
|
||||
# Launch TUI dashboard
|
||||
memory tui
|
||||
```
|
||||
|
||||
## Commands Reference
|
||||
### API
|
||||
|
||||
### Snippet Commands
|
||||
```bash
|
||||
# Start the server
|
||||
memory serve
|
||||
|
||||
# Or use the API directly
|
||||
curl http://localhost:8080/api/memory
|
||||
curl http://localhost:8080/api/memory -X POST -H "Content-Type: application/json" -d '{"title": "Use Redis", "content": "Caching layer", "category": "architecture", "tags": ["cache"]}'
|
||||
curl http://localhost:8080/api/memory/search?q=cache
|
||||
```
|
||||
|
||||
## CLI Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `snip init` | Initialize the snippet database |
|
||||
| `snip add` | Add a new snippet |
|
||||
| `snip get <id>` | Get a snippet by ID with syntax highlighting |
|
||||
| `snip list` | List all snippets with pagination |
|
||||
| `snip edit <id>` | Edit a snippet in your default editor |
|
||||
| `snip delete <id>` | Delete a snippet (with confirmation) |
|
||||
| `snip search <query>` | Full-text search with FTS5 |
|
||||
| `memory add` | Add a new memory entry |
|
||||
| `memory list` | List memory entries |
|
||||
| `memory search` | Search memory entries |
|
||||
| `memory get` | Get a specific entry |
|
||||
| `memory update` | Update an entry |
|
||||
| `memory delete` | Delete an entry |
|
||||
| `memory commit` | Create a commit snapshot |
|
||||
| `memory log` | Show commit history |
|
||||
| `memory diff` | Show diff between commits |
|
||||
| `memory serve` | Start the API server |
|
||||
| `memory tui` | Launch the TUI dashboard |
|
||||
|
||||
### Tag Commands
|
||||
### Categories
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `snip tag add <snippet_id> <tag>` | Add a tag to a snippet |
|
||||
| `snip tag remove <snippet_id> <tag>` | Remove a tag from a snippet |
|
||||
| `snip tag list` | List all tags in use |
|
||||
- `decision` - Architectural and design decisions
|
||||
- `feature` - Implemented features
|
||||
- `refactoring` - Refactoring history
|
||||
- `architecture` - Architectural patterns and structures
|
||||
- `bug` - Bug fixes and known issues
|
||||
- `note` - General notes and observations
|
||||
|
||||
### Collection Commands
|
||||
## REST API
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `snip collection create <name>` | Create a new collection |
|
||||
| `snip collection list` | List all collections |
|
||||
| `snip collection delete <id>` | Delete a collection |
|
||||
| `snip collection add <collection_id> <snippet_id>` | Add snippet to collection |
|
||||
| `snip collection remove <collection_id> <snippet_id>` | Remove snippet from collection |
|
||||
### Endpoints
|
||||
|
||||
### Import/Export Commands
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/api/memory` | List all entries |
|
||||
| POST | `/api/memory` | Create new entry |
|
||||
| GET | `/api/memory/{id}` | Get entry by ID |
|
||||
| PUT | `/api/memory/{id}` | Update entry |
|
||||
| DELETE | `/api/memory/{id}` | Delete entry |
|
||||
| GET | `/api/memory/search?q=` | Search entries |
|
||||
| POST | `/api/memory/commit` | Create commit |
|
||||
| GET | `/api/memory/log` | Get commit log |
|
||||
| GET | `/api/memory/diff/{h1}/{h2}` | Diff two commits |
|
||||
| GET | `/api/memory/stats` | Get statistics |
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `snip export all --file <path>` | Export all snippets to JSON |
|
||||
| `snip export collection <name> --file <path>` | Export a specific collection |
|
||||
| `snip export snippet <id> --file <path>` | Export a single snippet |
|
||||
| `snip import --file <path> --strategy <strategy>` | Import snippets (strategies: skip, replace, duplicate) |
|
||||
### Request/Response Examples
|
||||
|
||||
### P2P Sync Commands
|
||||
```json
|
||||
// POST /api/memory
|
||||
{
|
||||
"title": "Use PostgreSQL",
|
||||
"content": "We decided to use PostgreSQL for the main database due to its reliability and feature set.",
|
||||
"category": "decision",
|
||||
"tags": ["database", "postgresql", "storage"]
|
||||
}
|
||||
```
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `snip discover list` | Discover peers on the local network |
|
||||
| `snip sync --peer-id <id>` | Sync snippets with a discovered peer |
|
||||
| `snip peers` | List known sync peers |
|
||||
```json
|
||||
// Response
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Use PostgreSQL",
|
||||
"content": "We decided to use PostgreSQL...",
|
||||
"category": "decision",
|
||||
"tags": ["database", "postgresql", "storage"],
|
||||
"agent_id": "agent-123",
|
||||
"project_path": "/path/to/project",
|
||||
"created_at": "2024-01-15T10:30:00",
|
||||
"updated_at": "2024-01-15T10:30:00"
|
||||
}
|
||||
```
|
||||
|
||||
API documentation available at `http://localhost:8080/docs` (Swagger UI).
|
||||
|
||||
## TUI Dashboard
|
||||
|
||||
Launch with `memory tui`. Keybindings:
|
||||
|
||||
- `d` - Dashboard screen
|
||||
- `l` - Memory list screen
|
||||
- `c` - Commit history screen
|
||||
- `s` - Search screen
|
||||
- `q` - Quit
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -121,175 +153,69 @@ snip import --file backup.json --strategy skip
|
||||
|
||||
| Variable | Default | Description |
|
||||
|----------|---------|-------------|
|
||||
| `SNIP_DB_PATH` | `~/.snip/snippets.db` | Path to SQLite database |
|
||||
| `SNIP_KEY_FILE` | `~/.snip/.key` | Path to encryption key file |
|
||||
| `SNIP_SYNC_PORT` | `8765` | Port for P2P sync |
|
||||
| `SNIP_PEER_CACHE` | `~/.snip/peers.json` | Path to peer cache |
|
||||
| `MEMORY_DB_PATH` | `.memory/codebase_memory.db` | SQLite database path |
|
||||
| `MEMORY_API_HOST` | `127.0.0.1` | API server host |
|
||||
| `MEMORY_API_PORT` | `8080` | API server port |
|
||||
| `MEMORY_PROJECT_PATH` | `.` | Project root path |
|
||||
| `AGENT_ID` | `unknown` | Agent identifier |
|
||||
|
||||
### Configuration File
|
||||
### Project Config
|
||||
|
||||
Optional configuration can be placed at `~/.snip/config.toml`:
|
||||
Optional `.memory/config.toml` in project root:
|
||||
|
||||
```toml
|
||||
[database]
|
||||
path = "~/.snip/snippets.db"
|
||||
[project]
|
||||
name = "my-project"
|
||||
db_path = ".memory/codebase_memory.db"
|
||||
|
||||
[sync]
|
||||
port = 8765
|
||||
auto_discover = true
|
||||
|
||||
[display]
|
||||
style = "monokai"
|
||||
line_numbers = true
|
||||
[api]
|
||||
host = "127.0.0.1"
|
||||
port = 8080
|
||||
```
|
||||
|
||||
## Encryption
|
||||
## Architecture
|
||||
|
||||
Snippets can be encrypted using AES encryption with PBKDF2 key derivation (480k iterations, SHA256).
|
||||
```
|
||||
src/memory_manager/
|
||||
├── api/ # FastAPI REST API
|
||||
├── cli/ # Click CLI commands
|
||||
├── core/ # Business logic services
|
||||
├── db/ # SQLAlchemy models and repository
|
||||
└── tui/ # Textual TUI application
|
||||
```
|
||||
|
||||
### Key Components
|
||||
|
||||
- **MemoryService**: CRUD operations for memory entries
|
||||
- **SearchService**: Full-text search with FTS5
|
||||
- **CommitService**: Git-like versioning with snapshots and diffs
|
||||
- **MemoryRepository**: Async SQLAlchemy operations with aiosqlite
|
||||
|
||||
## Integration with AI Tools
|
||||
|
||||
### Cursor
|
||||
|
||||
Add to your Cursor project settings or `.cursor/rules`:
|
||||
|
||||
```
|
||||
Use the Memory Manager API at http://localhost:8080 to:
|
||||
1. Check existing decisions before making new ones
|
||||
2. Record significant decisions with: POST /api/memory
|
||||
3. Search past decisions with: GET /api/memory/search?q=
|
||||
```
|
||||
|
||||
### GitHub Copilot
|
||||
|
||||
Create a Copilot extension that calls the Memory Manager API to contextually retrieve relevant past decisions when working on similar code.
|
||||
|
||||
### Pre-commit Hook
|
||||
|
||||
```bash
|
||||
# Add an encrypted snippet
|
||||
snip add --title "API Secret" --code "API_KEY=abc123" --language python --encrypt
|
||||
|
||||
# View encrypted snippet (will prompt for password)
|
||||
snip get 1
|
||||
|
||||
# Encrypted snippets are stored with is_encrypted flag
|
||||
# Decryption happens on-demand with password verification
|
||||
```
|
||||
|
||||
### How Encryption Works
|
||||
|
||||
1. User provides a password when creating an encrypted snippet
|
||||
2. PBKDF2 derives a 32-byte key using SHA256 (480,000 iterations)
|
||||
3. Fernet (AES-128-CBC with HMAC) encrypts the snippet content
|
||||
4. Encrypted data stored in database with `is_encrypted=True`
|
||||
5. On retrieval, password decrypts the content in memory
|
||||
|
||||
## P2P Sync
|
||||
|
||||
Snip can discover and sync snippets with other peers on your local network using mDNS/Bonjour service discovery.
|
||||
|
||||
### Discovery
|
||||
|
||||
```bash
|
||||
# Discover available peers on the network
|
||||
snip discover list
|
||||
|
||||
# Output example:
|
||||
# Peer: macbook-pro.local (192.168.1.100:8765)
|
||||
# Last seen: 2 minutes ago
|
||||
```
|
||||
|
||||
### Sync Protocol
|
||||
|
||||
1. Peer discovery via `_snippets._tcp.local.` mDNS service
|
||||
2. HTTP/JSON communication over TCP
|
||||
3. Sync exchanges snippets updated since last sync
|
||||
4. Conflict resolution: newest-wins strategy (with option to keep both)
|
||||
|
||||
```bash
|
||||
# Sync with a specific peer
|
||||
snip sync --peer-id <peer_id>
|
||||
|
||||
# View known peers
|
||||
snip peers
|
||||
```
|
||||
|
||||
## Import/Export Format
|
||||
|
||||
Exported JSON follows this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"exported_at": "2024-01-15T10:30:00Z",
|
||||
"snippets": [
|
||||
{
|
||||
"title": "Hello World",
|
||||
"description": "A simple Hello World example",
|
||||
"code": "print('Hello, World!')",
|
||||
"language": "python",
|
||||
"tags": ["basics", "example"],
|
||||
"created_at": "2024-01-10T08:00:00Z",
|
||||
"updated_at": "2024-01-10T08:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Import Strategies
|
||||
|
||||
- **skip**: Skip snippets that already exist (by title match)
|
||||
- **replace**: Replace existing snippets with same title
|
||||
- **duplicate**: Import all as new snippets (generate new IDs)
|
||||
|
||||
## Shell Completion
|
||||
|
||||
Snip supports shell completion for bash and zsh.
|
||||
|
||||
### Bash
|
||||
|
||||
```bash
|
||||
# Add to ~/.bashrc
|
||||
eval "$(_SNIP_COMPLETE=bash snip)"
|
||||
```
|
||||
|
||||
### Zsh
|
||||
|
||||
```bash
|
||||
# Add to ~/.zshrc
|
||||
eval "$(_SNIP_COMPLETE=zsh snip)"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Database Locked
|
||||
|
||||
If you see "Database locked" errors:
|
||||
- Ensure no other snip processes are running
|
||||
- Check file permissions on `~/.snip/`
|
||||
|
||||
### FTS Query Syntax Error
|
||||
|
||||
FTS5 uses special syntax for boolean queries:
|
||||
- Use quotes for phrases: `"hello world"`
|
||||
- Use AND/OR for boolean: `hello AND world`
|
||||
- Escape special characters with `\`
|
||||
|
||||
### Decryption Failed
|
||||
|
||||
- Verify you're using the correct password
|
||||
- Passwords cannot be recovered if forgotten
|
||||
- Encrypted snippets without the password cannot be decrypted
|
||||
|
||||
### Peer Unreachable
|
||||
|
||||
- Ensure both peers are on the same network
|
||||
- Check firewall settings for port 8765
|
||||
- Verify mDNS/Bonjour is enabled on your system
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://7000pct.gitea.bloupla.net/7000pctAUTO/snippet-manager.git
|
||||
cd snippet-manager
|
||||
|
||||
# Install in development mode
|
||||
pip install -e ".[dev]"
|
||||
|
||||
# Run tests
|
||||
pytest tests/ -v
|
||||
|
||||
# Run with verbose output
|
||||
pytest tests/ -v --capture=no
|
||||
#!/bin/bash
|
||||
# .git/hooks/pre-commit
|
||||
memory commit --message "Auto-save before commit" 2>/dev/null || true
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT License - see [LICENSE](LICENSE) for details.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions welcome! Please open an issue or submit a pull request.
|
||||
MIT License - see LICENSE file for details.
|
||||
|
||||
Reference in New Issue
Block a user