A powerful Python-based bookmark manager and URL shortcut system with advanced command palette, Chrome integration, and real-time GitHub Copilot code reviews.
- Python 3.14+
- uv - Fast Python package manager
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh- Smart Search: Type "pr 12345" or "g search terms" directly in your browser
- Dynamic URL Redirects: Navigate to
/<key>/to be redirected to the bookmark's URL - Parameter Substitution: Supports URLs with placeholders (e.g.,
#{pr_number},#{search_terms}) - Multi-Parameter Support: Bookmarks can accept multiple parameters with optional defaults
- JSON Schema Validation: Validates the bookmark JSON file before loading
- Web Interface: Browse all bookmarks with search and filtering
- Tab Completion: Auto-complete shortcuts and commands
- Command History: Navigate previous commands with β/β arrows
- Filtered History: Type a prefix and use arrows to filter history
- Reverse Search (Ctrl-R): Bash-style interactive history search
- Special Commands: Built-in shortcuts like
h(help) to list all bookmarks - Autocomplete Suggestions: Real-time suggestions as you type
- Opens in New Tab: All commands open in new tabs for quick workflows
- OpenSearch API: Add Bunnify as a search engine in Chrome
- Address Bar Suggestions: Auto-complete suggestions in Chrome's omnibox
- Seamless Navigation: Type shortcuts directly in the address bar
- PR Code Reviews: Use the
rprshortcut to request Copilot reviews on PRs - Streaming Responses: Real-time progress updates during review generation
- Private Reviews: Reviews displayed in-app without public PR comments
- Dual-Stack Networking: IPv4 and IPv6 support (accessible via 127.0.0.1, [::1], or localhost)
- Comprehensive Logging: Detailed logs with PID/function/line numbers to
/tmp/bunnify.log - File Watching: Auto-reload bookmarks when JSON file changes
- Daemonization: Background process management with proper cleanup
# Clone the repository
git clone https://github.com/thehcma/bunnify.git
cd bunnify
# Install dependencies with uv
uv sync
# Run migrations
uv run python manage.py migrate
# Create your bookmarks file
mkdir -p ~/work/bunnify
cp bunnify.json.example ~/work/bunnify/bunnify.json
# Edit ~/work/bunnify/bunnify.json with your bookmarks
# Load bookmarks
uv run python manage.py load_bookmarksAlways use the bunnify-server script to ensure proper setup:
./bunnify-serverThis will:
- Start the server on port 8000 (dual-stack IPv4/IPv6 binding)
- Start the bookmark file watcher for auto-reload
- Daemonize both processes
- Show URLs for access
Logging options:
./bunnify-server --console # Log to console instead of file
./bunnify-server --log-level DEBUG # Change log level
./bunnify-server --help # Show all optionsNote: The bunnify-server script uses dual-stack binding ([::]:8000), making the server accessible via IPv4, IPv6, and localhost.
The server is accessible at:
http://127.0.0.1:8000/(IPv4)http://[::1]:8000/(IPv6)http://localhost:8000/(auto)
Set up Bunnify as a search engine in Chrome:
- Visit
http://127.0.0.1:8000/(orhttp://[::1]:8000/for IPv6) while the server is running - Go to Chrome Settings β Search engine β Manage search engines
- Find "Bunnify" (added automatically via OpenSearch) or add manually:
- Search engine: Bunnify
- Shortcut:
s(or any letter you prefer) - URL (IPv4):
http://127.0.0.1:8000/search/?q=%s - URL (IPv6):
http://[::1]:8000/search/?q=%s - URL (localhost):
http://localhost:8000/search/?q=%s
- Save
Note: Choose the URL that matches how you're running the server:
- Use IPv4 (
127.0.0.1) if running with127.0.0.1:8000 - Use IPv6 (
[::1]) if you prefer IPv6-only access - Use
localhostif running with[::]:8000(dual-stack) - Chrome will auto-select
Optional: Set as Default Search Engine
- Click the three dots next to "Bunnify" and select "Make default"
- Now you can type bookmarks directly without any prefix!
Use the provided setup script instead of manual systemd steps:
./scripts/setup-serviceTo verify configuration and service health:
./scripts/setup-service --statusFor live logs:
journalctl --user -u bunnify.service -fVisit http://127.0.0.1:8000/cmd/ for the enhanced command palette:
Features:
- Type to filter shortcuts with auto-complete
- Tab to complete suggestions
- β/β to navigate command history (with prefix filtering)
- Ctrl-R for bash-style reverse search through history
- Enter to execute (opens in new tab)
- Esc to cancel
Examples:
- Type
prthen β to see recent PR commands - Type
prand β to cycle through filtered history - Press Ctrl-R and type
12345to find commands with that PR number
Type in Chrome's address bar:
s pr 12345β Opens PR #12345s g python tutorialβ Google search for "python tutorial"s vaultβ Opens Vaults hβ Shows all bookmarks
Simple redirects:
http://127.0.0.1:8000/c/β Google Calendarhttp://127.0.0.1:8000/vault/β Vault
Parameterized redirects:
http://127.0.0.1:8000/pr/?pr_id=12345β PR #12345http://127.0.0.1:8000/g/?search_terms=python+tutorialβ Google search
Special endpoints:
http://127.0.0.1:8000/list/β Browse all bookmarkshttp://127.0.0.1:8000/cmd/β Command palettehttp://127.0.0.1:8000/review-pr/?pr=12345β Request Copilot review
The application expects a JSON file with the following structure:
{
"key": {
"description": "Description of the bookmark",
"url": "https://example.com/path",
"old-url": "https://old-url.com/path" // optional
}
}URLs can contain placeholders in the format #{parameter_name}:
Single Parameter:
{
"g": {
"description": "Google search",
"url": "https://www.google.com/search?q=#{search_terms}"
}
}Usage: g python tutorial β https://www.google.com/search?q=python+tutorial
Multiple Parameters with Defaults:
{
"pr": {
"description": "GitHub Pull Request",
"url": "https://github.com/#{repo}/pull/#{pr_number}",
"defaults": {
"repo": "your-org/your-repo"
}
}
}Usage:
pr 12345β Uses default repo βhttps://github.com/your-org/your-repo/pull/12345pr 12345 Shopify/shopify-buildβ Overrides default βhttps://github.com/Shopify/shopify-build/pull/12345
Parameter Order: Required parameters (no defaults) are mapped first, then optional parameters (with defaults).
bunnify/
βββ bookmarks/
β βββ management/
β β βββ commands/
β β βββ load_bookmarks.py # Command to load JSON data
β βββ templates/
β β βββ bookmarks/
β β βββ base.html # Base template
β β βββ index.html # Home page
β β βββ list.html # Bookmark list
β β βββ opensearch.xml # OpenSearch descriptor
β βββ models.py # Bookmark model
β βββ views.py # View logic (includes search_redirect)
β βββ urls.py # URL routing
βββ bunnify/
β βββ settings.py # Application settings
β βββ urls.py # Main URL config
βββ .venv/ # Virtual environment (managed by uv)
βββ manage.py # Management script
βββ pyproject.toml # Project dependencies
The load_bookmarks command validates the JSON file against a schema that ensures:
- All keys match the pattern
^[a-zA-Z0-9_]+$ - Each bookmark has required fields:
descriptionandurl - Optional fields:
old-urloroldurl - Reserved keywords "h" and "help" are blocked and will cause an error
GET /- Home page with usage instructionsGET /search/?q=<query>- Smart search endpoint (e.g., "pr 12345")GET /list/- List all bookmarks with searchGET /<key>/- Redirect to bookmark URL- With parameters:
GET /<key>/?param1=value1¶m2=value2
- With parameters:
GET /opensearch.xml- OpenSearch descriptor for browser integration
The following keywords are reserved and cannot be used as bookmark keys:
h- Shows all bookmarks (help shortcut)help- Shows all bookmarks (help shortcut)
The project includes comprehensive smoke tests that verify core functionality.
Run all tests:
./test_bunnifyRun specific test suite:
# Run only smoke tests
./test_bunnify bookmarks.tests.SmokeTests
# Run with verbose output
./test_bunnify -v 2
# Run a specific test
./test_bunnify bookmarks.tests.SmokeTests.test_search_with_parameterTest coverage includes:
- Page loading (index, list, command palette, OpenSearch XML)
- Search redirects with/without parameters
- Parameter substitution in URLs
- Direct bookmark redirects
- Help command functionality
- API suggestions endpoint
- Model methods and ordering
- Error handling (404, 400)
uv run python manage.py createsuperuserThen access the admin interface at http://127.0.0.1:8000/admin/
To reload bookmarks after updating your JSON file:
uv run python manage.py load_bookmarksThis will clear existing bookmarks and load fresh data.
- Python web stack: ASGI/WSGI app with URL routing and templates
- jsonschema: JSON validation
- SQLite: Database (default storage)
- Python 3.14: Programming language with type hints
- uv: Fast Python package manager
- pathlib: Modern file path handling
- OpenSearch: Browser integration protocol
- localStorage: Client-side command history
- Streaming responses: Real-time progress updates
bunnify/
βββ bookmarks/ # Core application logic
β βββ management/
β β βββ commands/ # Management commands
β β βββ load_bookmarks.py # Load bookmarks from JSON
β β βββ watch_bookmarks.py # Auto-reload on file changes
β βββ templates/ # HTML templates
β β βββ bookmarks/
β β βββ cmd.html # Command palette
β β βββ list.html # Browse bookmarks
β β βββ opensearch.xml # Chrome integration
β β βββ copilot_review.html # Copilot review UI
β βββ models.py # Bookmark model
β βββ views.py # View functions
β βββ urls.py # URL routing
βββ bunnify/ # Main configuration directory
β βββ settings.py # Configuration with logging
β βββ urls.py # Root URL configuration
βββ scripts/ # Helper scripts
β βββ checks # Local preflight checks (format/lint/tests)
β βββ setup-service # Systemd user service setup/status helper
β βββ dev/start-development # Session bootstrap for worktree/dev setup
βββ manage.py # Management script
βββ bunnify-server # Server startup + watcher script
βββ requirements.txt # Python dependencies
βββ bunnify.json.example # Example bookmark configuration
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
This project is licensed under the MIT License - see the LICENSE file for details.
thehcma - GitHub
- Built with modern Python features
- Inspired by browser bookmark management needs
- Enhanced with GitHub Copilot integration for code reviews
- Quick Access: Set Bunnify as your default search engine for the fastest access
- Discover Bookmarks: Type
hto quickly see all available shortcuts - Parameterized Shortcuts: For frequently used parameterized bookmarks (like
pr), you can create individual Chrome search engines for even faster access - Auto-start: Run
scripts/setup-serviceto configure Bunnify as a persistent systemd service.
For production-like use, you should run Bunnify as a systemd user service. This ensures it starts on boot and restarts automatically if it crashes.
We provide a script to automate the systemd configuration and enable lingering:
./scripts/setup-serviceYou can verify that everything is correctly configured using the --status flag:
./scripts/setup-service --statusYou can check the service status with:
systemctl --user status bunnify.serviceTo ensure the service runs even when you are not logged in, lingering must be enabled (the setup script does this for you):
loginctl enable-linger $(whoami)# Make sure you're in the right directory
cd ~/work/ai/bunnify
# Use the bunnify-server script
./bunnify-server# Check if the JSON file exists and is valid
cat ~/work/bunnify/bunnify.json
# Reload bookmarks
uv run python manage.py load_bookmarks- Make sure the server is running at
http://127.0.0.1:8000/ - Visit the homepage to trigger OpenSearch detection
- Manually add the search engine with URL:
http://127.0.0.1:8000/search/?q=%s