Bunnify
A Python bookmark manager and URL shortcut system: terminal CLI, web command palette, Chrome OpenSearch integration, and parameterized redirects.
Install
pipx is the recommended path for end users:
pipx install bunnify
pipx ensurepath # adds ~/.local/bin to PATH if needed (restart the shell)
bunnify --version
bunnify onboard # print bookmarks / setup / Chrome next steps
pipx installs bunnify and bunnify-server under ~/.local/bin by default
(or $PIPX_BIN_DIR when set). Ensure that directory is on your PATH before
running either command. Prefer the pipx apps over any checkout
./scripts/bunnify still on PATH.
The wheel installs bunnify (CLI), bunnify-server (Django
server), and spotty-bunny (Spotlight-style search box; PyObjC on macOS
and pywin32 on Windows are installed automatically). No repository checkout or
uv is required at runtime. The bunnify[macos] and bunnify[windows] extras
from earlier releases still install, as empty aliases.
Package on PyPI: pypi.org/project/bunnify.
After install or upgrade
pipx does not print package docs after install. Run:
bunnify onboard
That prints the ready-to-go checklist (bookmarks path, bunnify setup,
Chrome/Edge, and upgrade). Same text:
bunnify --onboard
Summary of what it covers:
- Bookmarks at
~/.config/bunnify/bookmarks.json(required before the server starts) —bunnify setupcan install the example shortcuts, or seed from bunnify.json.example bunnify setup— local on a laptop; remote for a home/always-on host
LOCAL.md- Chrome / Edge — match
base_urlfromconfig.toml
CHROME_SETUP.md - Try it:
bunnify ghor address-bar keyword (e.g.b gh) - macOS Spotty Bunny (optional) —
bunnify spotty-bunny install(LOCAL.md) - Windows Spotty Bunny (optional) —
bunnify spotty-bunny install(LOCAL.md)
Upgrade
Preferred:
bunnify upgrade
That prints the version/commit you are running from, the PyPI target, then
the pipx app version/commit to after pipx upgrade. Use this instead of
bare pipx upgrade bunnify so you can see when PATH is still a git checkout.
On macOS, bunnify upgrade also refreshes installed server and Spotty Bunny
LaunchAgents when their plists are present (or run bunnify-server upgrade /
bunnify spotty-bunny upgrade manually). On Windows, bunnify upgrade
also refreshes an installed Spotty Bunny Scheduled Task the same way — there
is no Windows equivalent of the server LaunchAgent yet, so only Spotty Bunny
is refreshed.
pipx upgrade only updates ~/.local/bin/bunnify. If bunnify --version still
shows a checkout SHA, PATH is hitting ./scripts/bunnify or a repo .venv.
After pipx ensurepath, command -v bunnify should be ~/.local/bin/bunnify.
Bookmarks and ~/.config/bunnify/config.toml are user data — upgrades do not
overwrite them. After a major server change, re-run bunnify setup only if
docs or release notes say so. Setup will offer to stop a different local
Bunnify build and start this CLI's build when the port is already in use.
Source and docs: github.com/the-hcma/bunnify.
Quick start
1. Bookmarks file
bunnify setup offers to install the example bookmarks when none exist yet.
You can also create the file yourself from the
documented example
into XDG config:
mkdir -p ~/.config/bunnify
curl -fsSL https://raw.githubusercontent.com/the-hcma/bunnify/main/bunnify.json.example \
-o ~/.config/bunnify/bookmarks.json
# edit ~/.config/bunnify/bookmarks.json with your shortcuts
See Configuration
for overrides (BUNNIFY_BOOKMARKS, XDG_CONFIG_HOME).
2. Configure local or remote mode
bunnify setup
Laptop / daily machine: choose local (default). On macOS, setup
installs the server LaunchAgent (com.thehcma.bunnify), verifies
/health, records the port, and saves settings to ~/.config/bunnify/config.toml.
Elsewhere it starts a managed background server the same way as before. Point
Chrome or Edge at the same base_url
(Chrome / Edge setup).
Home server / always-on host: choose remote on client devices and enter
that host’s URL. Setup probes /health; if the host is unreachable it warns
and asks before saving. Prefer a centralized remote install when several
machines share one server — not as a laptop’s only dependency if you often go
offline.
One-time override without saving: bunnify --base-url https://… shortcut.
Details: Local and remote setup.
3. Run shortcuts
bunnify # interactive REPL (Tab completion, history)
bunnify gh # open a shortcut in the browser
bunnify bun # Bunnify source on GitHub
bunnify pr the-hcma/bunnify 272 # parameterized shortcut
bunnify --fzf # fuzzy picker
bunnify --print-url gh
Unknown keys exit non-zero in direct mode (no search-engine fallback).
4. Shell completion (optional)
Tab completion for shortcut keys, outside the interactive REPL:
mkdir -p ~/.local/share/bash-completion/completions
bunnify --completion bash > ~/.local/share/bash-completion/completions/bunnify.bash
That's a bash-completion v2 user
directory — no rc file edit, lazy-loaded on first Tab press in a new shell.
zsh/fish aren't supported yet. The emitted script calls bunnify --list-keys
at completion time, so Tab-completing shortcuts needs the server running.
source etc/bunnify-completion (from a source checkout) still works too.
Features
- CLI / REPL — fuzzy Tab completion, fzf mode, Vim/Emacs edit keys
- Spotty Bunny — configurable-chord search box (
spotty-bunny; auto Control/Option by default,spotty-bunny hotkeyto change; login LaunchAgent viainstall/upgrade/uninstall) - macOS server LaunchAgent — local setup installs
bunnify-serverunder launchd (bunnify-server install|status|upgrade|uninstall) - Web —
/cmd/command palette,/list/browser, smart/search/ - Chrome / Edge — OpenSearch at
/opensearch.xml(setup guide) - Parameters — URLs with
#{name}placeholders and optional defaults - Validation — JSON Schema on load; reserved keys
h/help
Spotty Bunny (macOS)
Optional Spotlight-style search box. Hold the left (or right) copy of the
chord's modifier key, then tap the other copy of the same modifier — for
example, hold left Control and tap right Control. By default (auto) the
modifier is chosen automatically from which keyboard is attached — see below
to pin it or change it. PyObjC is installed automatically on macOS.
Hotkey chord
Standard MacBook keyboards have only one physical Control key, so the
dual-Control chord doesn't work on laptop-only setups — the default auto
choice already accounts for that by using Option instead. Configure the
chord in ~/.config/bunnify/config.toml (spotty_bunny_hotkey), or via:
bunnify spotty-bunny hotkey # show the current choice
bunnify spotty-bunny hotkey auto # Option on laptops with no external
# keyboard, Control when one is attached
bunnify spotty-bunny hotkey control # dual-Control (needs two Control keys)
bunnify spotty-bunny hotkey option # dual-Option
bunnify spotty-bunny hotkey command # dual-Command
Default (auto) keystrokes by keyboard:
| Keyboard attached | Chord used | Keystroke |
|---|---|---|
| Built-in only (no external keyboard) | Option | Hold left Option, tap right Option (or the reverse) |
| External keyboard present (USB/Bluetooth) | Control | Hold left Control, tap right Control (or the reverse) |
auto is re-checked on every periodic tap health check, so plugging in or
unplugging an external keyboard switches the chord without restarting Spotty
Bunny. Pinning a choice (control / option / command) always uses both
copies of that modifier, regardless of what's attached — useful on an
external keyboard that lacks a second Option or Command key, or if you just
prefer one chord everywhere.
Install
pipx install bunnify
pipx ensurepath
bunnify spotty-bunny install # login LaunchAgent (TCC + KeepAlive)
bunnify spotty-bunny status
install grants Accessibility and Input Monitoring to the interpreter
launchd will exec (typically the pipx venv Python), writes
~/Library/LaunchAgents/com.thehcma.bunnify.spotty-bunny.plist, and bootstraps
it. Bare spotty-bunny (or bunnify spotty-bunny with no subcommand) still
runs in the foreground for debugging.
Upgrade
bunnify upgrade # pipx package; on macOS refreshes LaunchAgents
bunnify spotty-bunny upgrade # manual plist bounce when needed
On macOS, bunnify upgrade rewrites both LaunchAgents when installed. Use
bunnify spotty-bunny upgrade only when you need to refresh Spotty without
upgrading the pipx package.
Uninstall
bunnify spotty-bunny uninstall
That boots the agent out, removes the plist, and stops a leftover overlay
process. Bookmarks and config.toml are unchanged. Right-click the bunny icon
for Check for Updates (forces an immediate PyPI check, skipping the daily
cache), Install (when the LaunchAgent is missing), Quit, Uninstall,
and (when installed and either a newer PyPI release is out or this running
overlay predates what's actually installed) Upgrade. An up-arrow badge on
the icon and an About line mark either case — a newer PyPI release (checked
at most once a day) or this process simply running stale bits after a local
upgrade; both clear once Upgrade restarts the overlay.
Left-click the bunny for About: bookmarks file, GitHub repo when that file lives in a GitHub checkout, and whether the CLI is talking to a local or remote server (with its URL).
Details: Local and remote setup.
Spotty Bunny (Windows)
Optional Spotlight-style search box, same overlay behavior as macOS. pywin32 is installed automatically on Windows.
Hotkey chord
Only the dual-Control chord actually works on Windows today: hold left
Control, tap right Control (or the reverse). auto and control both
resolve to it; option and command fall back to Control with a warning
logged, rather than inventing an Alt or Windows-key chord.
bunnify spotty-bunny hotkey # show the current choice
bunnify spotty-bunny hotkey control # dual-Control (the only chord that works)
Install
pipx install bunnify
pipx ensurepath
bunnify spotty-bunny install # Scheduled Task (LogonTrigger + RestartOnFailure)
bunnify spotty-bunny status
There is no TCC-style permission step on Windows — install registers a
Scheduled Task named "Bunnify Spotty Bunny", runs it once, and waits for the
overlay to come up. Bare spotty-bunny (or bunnify spotty-bunny with no
subcommand) still runs in the foreground for debugging.
Upgrade
bunnify upgrade # pipx package; also refreshes an installed task
bunnify spotty-bunny upgrade # re-register the task for the current binary
bunnify upgrade refreshes an already-installed Scheduled Task automatically
after the pipx package upgrade (skip this with bunnify spotty-bunny upgrade
if you only need to re-register the task by itself). Either way, a failed
upgrade restores the previous task configuration when one existed (or removes
the non-functional registration when it didn't) rather than silently leaving
a broken task behind.
Uninstall
bunnify spotty-bunny uninstall
That deletes the Scheduled Task and stops a leftover overlay process.
Bookmarks and config.toml are unchanged. The right-click menu (Check for
Updates, Install/Uninstall/Upgrade, Quit) and left-click-for-About behavior
are the same as macOS's.
WH_KEYBOARD_LL, the low-level keyboard hook Spotty Bunny uses to detect the
chord, is a known heuristic trigger for antivirus software and Windows
SmartScreen on unsigned pipx-installed binaries — the hook is listen-only
(it always calls CallNextHookEx, so keystrokes are never intercepted or
altered, only observed to detect the chord), but the first run may still show
a SmartScreen prompt. Details:
Local and remote setup.
Server lifecycle
Installed users manage the server with bunnify-server:
bunnify-server --help
bunnify-server install --port 8000 # macOS: server LaunchAgent only
bunnify-server status
bunnify-server upgrade # rewrite plist for current binary
# Foreground (systemd, debugging):
bunnify-server --foreground --noninteractive --port 8000
# Background managed daemon (non-macOS or manual):
bunnify-server --port 8000 --noninteractive --pid-dir ~/.local/share/bunnify/run
bunnify-server --stop --pid-dir ~/.local/share/bunnify/run
curl --max-time 2 http://127.0.0.1:8000/health
Local setup: bunnify setup (macOS installs the server LaunchAgent; other
platforms start a managed background server). Stop with:
bunnify stop # macOS: boot out server LaunchAgent; else stop managed server
That prints the URL and runtime directory before stopping. Check the current mode, target URL, and reachability at any time:
bunnify status
Details: Local and remote setup.
Linux production:
systemd user service
via setup-service from
repository-helpers.
macOS: prefer bunnify setup (local) or the commands above. Manual plist
copy is optional — see
LaunchAgent example
and LOCAL.md.
Web usage
With the server running (default http://127.0.0.1:8000 after setup):
| URL | Purpose |
|---|---|
/cmd/ |
Command palette (recommended) |
/search/?q=pr+12345 |
Smart search |
/list/ |
Browse all bookmarks |
/<key>/ |
Direct redirect |
/opensearch.xml |
Chrome search engine descriptor |
Bookmarks format
{
"gh": {
"description": "GitHub",
"url": "https://github.com/"
},
"pr": {
"description": "Pull request",
"url": "https://github.com/#{repo}/pull/#{pr_number}",
"defaults": { "repo": "org/repo" }
}
}
Required fields: description, url. Placeholders use #{parameter_name}.
Reload after edits: the server watches the JSON file, or run
load_bookmarks in a development checkout.
Development checkout
Contributors clone the repo and use uv — separate from the pipx path above.
git clone https://github.com/the-hcma/bunnify.git
cd bunnify
uv sync
uv run python manage.py migrate
mkdir -p ~/.config/bunnify
cp bunnify.json.example ~/.config/bunnify/bookmarks.json
./scripts/bunnify setup
./scripts/bunnify-server --console --log-level DEBUG # optional
./test_bunnify
Full guidelines:
CONTRIBUTING.md.
Quality gates: ./scripts/checks.
Wrappers under ./scripts/ prefer uv run when uv is on PATH, otherwise
the checkout .venv (same entry points systemd uses on service hosts).
Documentation
| Doc | Audience |
|---|---|
| CONFIG.md | XDG paths and environment variables |
| LOCAL.md | Local vs remote setup, ports, Spotty Bunny |
| SYSTEMD.md | Linux user service |
| CHROME_SETUP.md | Browser search engine |
| QUICK_REFERENCE.md | Cheat sheet |
| RELEASING.md | Maintainers: PyPI releases |
Troubleshooting
Server won't start
bunnify-server --console --log-level DEBUG
# or in a checkout: ./scripts/bunnify-server --console
Bookmarks missing
ls -l ~/.config/bunnify/bookmarks.json
# run `bunnify setup` (offers the example), or copy bunnify.json.example
CLI can't reach server
bunnify setup
curl -sf "$(grep '^base_url' ~/.config/bunnify/config.toml | sed -E 's/^base_url = "(.*)"$/\1/')/health"
Stale managed process
bunnify-server --stop --pid-dir ~/.local/share/bunnify/run
Releasing
Maintainers: docs/RELEASING.md (Release Please + PyPI).
License
MIT © 2026 Henrique Andrade (GitHub's thehcma) — see LICENSE.
Metadata
Release files for bunnify 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bunnify-1.0.0.tar.gz | 336.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bunnify-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 722.2 kB
Release files / bunnify-1.0.0.tar.gz
| Download URL | bunnify-1.0.0.tar.gz |
|---|---|
| Size | 336.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dcadc4c4e79bcc32cf4223bda8b5ba4aa1c13be9881d2c1bf95f7ab7e9ed7b4c
|
|
BLAKE2b-256 checksum How to use checksums |
4546295fe830954914b04c4f3f6470a66dd019cefc19860c11b9ae468adfca02
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency logRelease files / bunnify-1.0.0-py3-none-any.whl
| Download URL | bunnify-1.0.0-py3-none-any.whl |
|---|---|
| Size | 385.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7bc2bbda34b1d4b85ae195ca7decb4da182653ae520dc73df353f800e2f7c95c
|
|
BLAKE2b-256 checksum How to use checksums |
3e1640ef19c232e9c752d11d1a45fda8be32853bcd98c69b9e8b30ed7944812b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 21, 2026.
Transparency log