# Snip - Local-First Code Snippet Manager ![CI](https://7000pct.gitea.bloupla.net/7000pctAUTO/snippet-manager/actions/workflows/ci.yml/badge.svg) 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. ## Features - **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 ## Installation ### From Source ```bash git clone https://7000pct.gitea.bloupla.net/7000pctAUTO/snippet-manager.git cd snippet-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 ```bash # Initialize the database snip init # Add a snippet snip add --title "Hello World" --code 'print("Hello, World!")' --language python # List all snippets snip list # Get a snippet with syntax highlighting snip get 1 # Search snippets snip search hello # Add tags to organize snip tag add 1 python basics # 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 ``` ## Commands Reference ### Snippet Commands | Command | Description | |---------|-------------| | `snip init` | Initialize the snippet database | | `snip add` | Add a new snippet | | `snip get ` | Get a snippet by ID with syntax highlighting | | `snip list` | List all snippets with pagination | | `snip edit ` | Edit a snippet in your default editor | | `snip delete ` | Delete a snippet (with confirmation) | | `snip search ` | Full-text search with FTS5 | ### Tag Commands | Command | Description | |---------|-------------| | `snip tag add ` | Add a tag to a snippet | | `snip tag remove ` | Remove a tag from a snippet | | `snip tag list` | List all tags in use | ### Collection Commands | Command | Description | |---------|-------------| | `snip collection create ` | Create a new collection | | `snip collection list` | List all collections | | `snip collection delete ` | Delete a collection | | `snip collection add ` | Add snippet to collection | | `snip collection remove ` | Remove snippet from collection | ### Import/Export Commands | Command | Description | |---------|-------------| | `snip export all --file ` | Export all snippets to JSON | | `snip export collection --file ` | Export a specific collection | | `snip export snippet --file ` | Export a single snippet | | `snip import --file --strategy ` | Import snippets (strategies: skip, replace, duplicate) | ### P2P Sync Commands | Command | Description | |---------|-------------| | `snip discover list` | Discover peers on the local network | | `snip sync --peer-id ` | Sync snippets with a discovered peer | | `snip peers` | List known sync peers | ## Configuration ### Environment Variables | 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 | ### Configuration File Optional configuration can be placed at `~/.snip/config.toml`: ```toml [database] path = "~/.snip/snippets.db" [sync] port = 8765 auto_discover = true [display] style = "monokai" line_numbers = true ``` ## Encryption Snippets can be encrypted using AES encryption with PBKDF2 key derivation (480k iterations, SHA256). ```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 # 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 ``` ## License MIT License - see [LICENSE](LICENSE) for details. ## Contributing Contributions welcome! Please open an issue or submit a pull request.