Pounce config & CLI
Reference for configuring and driving Pounce. For writing your own commands, see Writing pounce commands.
Config file
Section titled “Config file”Pounce reads ~/.config/pounce/config.json. Every key is optional; the file is
re-read each time the palette opens (no daemon restart needed).
{ "theme": "nebelung", // "nebelung" (default), "mocha", or a themes/ file "themeLight": "nebelung-latte", // used when macOS is in Light Mode "themeDark": "nebelung", // used when macOS is in Dark Mode "windowMode": "default", // "default" (720px) or "compact" (600px, tighter) "hotkey": { "enabled": true, // register the global hotkey in-process "key": "space", // "space", "return", "tab", "escape", "a"–"z", "0"–"9" "modifiers": ["cmd"] // any of "cmd", "shift", "opt", "ctrl" }, "clipboard": { "enabled": true, // watch the pasteboard "maxEntries": 200, // history size "blacklistBundleIds": ["com.apple.Passwords"], // never record from these "autoPaste": false // synthesize ⌘V into the prior app (needs Accessibility) }, "quickAnswers": { "currency": true // fetch ECB rates so "100 usd in eur" answers inline }, "updates": { "check": true // nudge (never install) when a new release is out }, "fileSearch": { "enabled": true, // the Find Files mode "homeOnly": true, // scope to ~ instead of the whole index "maxResults": 60 // rows kept per query }, "apps": { "demoteBundleIds": [], // sink these apps below everything else "hideBundleIds": [] // drop these apps from the list entirely }, "windows": { "enabled": false, // the MRU window switcher (needs Accessibility) "key": "tab", // hold the modifiers, tap this to walk windows "modifiers": ["cmd"] }, "items": { // per-item enable / alias / hotkey — see below "cmd:emoji": { "alias": "emo", "hotkey": "opt+space e" } }}| Key | Values | Default |
|---|---|---|
theme | "nebelung" | "mocha" | a themes/ file name | "nebelung" |
themeLight | same values; applies in macOS Light Mode | "nebelung-latte" |
themeDark | same values; applies in macOS Dark Mode | "nebelung" |
windowMode | "default" | "compact" | "default" |
hotkey.enabled | true | false | true |
hotkey.key | key name | "space" |
hotkey.modifiers | array of cmd/shift/opt/ctrl | ["cmd"] |
clipboard.enabled | true | false | true |
clipboard.maxEntries | number | 200 |
clipboard.blacklistBundleIds | array of bundle ids | ["com.apple.Passwords"] |
clipboard.autoPaste | true | false | false |
quickAnswers.currency | true | false | true |
updates.check | true | false | true |
fileSearch.enabled | true | false | true |
fileSearch.homeOnly | true | false | true |
fileSearch.maxResults | number | 60 |
apps.demoteBundleIds | array of bundle ids | a built-in list of background/helper apps |
apps.hideBundleIds | array of bundle ids | [] |
windows.enabled | true | false | false |
windows.key | key name | "tab" |
windows.modifiers | array of cmd/shift/opt/ctrl | ["cmd"] |
items | map of item key → { enabled, alias, hotkey } | {} |
themeLight / themeDark are resolved per open (like everything else here), so
flipping macOS appearance shows on the next summon. Either one falls back to
theme, and theme alone pins one palette for both modes.
Setting windows.enabled turns on the MRU
window switcher; it needs the
Accessibility grant to install its event tap, and without the grant stock ⌘Tab
keeps working.
quickAnswers.currency and updates.check are the only two things in Pounce
that touch the network. The first fetches the ECB daily reference rates from
api.frankfurter.app (at most every 12 hours, cached to
~/.local/share/pounce/currency-rates.json). The second asks GitHub once an
hour whether there is a newer release. Set both to false and Pounce makes no
network requests at all.
updates.check only ever tells you — it never installs anything. While a
release is pending, the Update Pounce row is renamed with the new version
and pinned to the palette’s first row, and a notification repeats at most once
a day. Press ⌘⏎ on that row to skip the version: the pin and the notification
stop until the next release, though searching for the row still shows what’s
waiting. The wording follows how you installed Pounce, because not every
install can update itself in place:
| Install | What the nudge says |
|---|---|
| Homebrew | Return runs brew upgrade pounce and restarts the service |
Dragged to /Applications | Return downloads the new release and swaps the app in place |
| The nebelhaus rice | Run haus update — Pounce comes down with the rest of the rice |
| Your own flake | Update your pounce input |
The last two update through the Nix store rather than in place, so Pounce deliberately won’t try to overwrite itself there — the next rebuild would revert it anyway.
Setting hotkey.enabled to false frees the hotkey so an external launcher
(skhd, AeroSpace) can bind a key to pounce-palette instead.
Any theme value that isn’t a built-in resolves to
~/.config/pounce/themes/<name>.json — a flat catppuccin-style
name → "#hex" map (nebelung’s
palette/*.hex.json files verbatim), re-read on each open like the config
itself. That’s how the rice’s theme.flavor / theme.contrast reach Pounce
without a rebuild, and it works the same on a Homebrew install:
mkdir -p ~/.config/pounce/themescurl -fsSLo ~/.config/pounce/themes/nebelung-latte.json \ https://raw.githubusercontent.com/nebelhaus/nebelung/main/palette/nebelung-latte.hex.json# config.json: "theme": "nebelung-latte"An unknown name or malformed file falls back to the built-in nebelung palette.
Per-item settings (items)
Section titled “Per-item settings (items)”One map covers what you’d otherwise want three keys for — hide a row, give it a search shorthand, give it a global key. Each entry is keyed by an item key:
| Item key | Addresses |
|---|---|
cmd:<id> | a command script, by filename without .sh |
app:/Applications/Foo.app | an application, by path |
mode:<name> | a built-in window — launcher, clipboard, emoji, screenshots, camera, filesearch |
{ "items": { "cmd:emoji": { "alias": "emo", "hotkey": "opt+e" }, "cmd:brew-services": { "enabled": false }, "app:/Applications/Ghostty.app": { "alias": "term", "hotkey": "opt+t" }, "mode:clipboard": { "hotkey": "cmd+shift+v" } }}enabled: falsedrops the row from the launcher. It does not disarm a hotkey you bound to it — keeping an item off the list but on a key is a legitimate setup.aliasis a search shorthand, matched at a bonus over the item’s real name so it wins over whatever app fuzzy-matches the same letters.hotkeyruns the item directly, skipping the palette. The last segment is the key, the rest modifiers (cmd/shift/opt/ctrl); the{"key": …, "modifiers": …}object form works here too.
Leader sequences
Section titled “Leader sequences”Add a space for a two-step key: whitespace separates steps, + separates
modifiers, so "opt+space e" is ⌥Space then E — the notation Emacs and VS Code
use. Sequences sharing a leader share it (⌥Space registers once and owns a map of
next keys), and a sequence can run longer than two: "opt+space g s".
The point on a tiling setup: a leader opens a namespace that can’t collide with
the ⌥/⌘ chords AeroSpace already owns, and it needs no Accessibility grant —
pressing the leader grabs its next-step keys as ordinary global hotkeys for ~2s
and releases them the instant one fires, so there’s no event tap and no TCC
prompt. Escape cancels; hesitate ~0.45s and a which-key overlay lists the next
keys. pounce doctor reports every binding it actually armed, so a typo or a key
some other app holds is visible at startup, not on the day you press it.
Driving pounce from another binder
Section titled “Driving pounce from another binder”If a tool already owns your keystrokes — AeroSpace binding modes, skhd, Shortcuts — let it do the chord and have pounce do the action:
pounce run cmd:emojipounce run mode:clipboardSame target grammar as items, dispatched through the identical path a native
binding takes. It exits non-zero with a reason on a malformed target, so a typo
fails loudly instead of arming a key that does nothing.
pounce --launcher # apps + commands palette (the default mode)pounce --max-empty 7 # rows to show before you typepounce -p "Pick:" # generic picker; reads lines from stdinpounce -i "sf.symbol.name" # icon for the pickerpounce -p "Search:" --chain # picker whose free-text Enter feeds another pounce steppounce run cmd:emoji # run one item by its key (for external binders)
# built-in windows — items, not flags (`pounce run <item-key>`)pounce run mode:clipboard # clipboard historypounce run mode:emoji # emoji pickerpounce run mode:screenshots # screenshot browserpounce run mode:camera # live camera previewpounce run mode:filesearch # file/folder search (Spotlight index)pounce run cmd:hush # any command, by script name without .shpounce run app:/Applications/Ghostty.apppounce --cheatsheet [path] # cheatsheet overlaypounce --transform 'tr a-z A-Z' # rewrite the selected text through a shell filter
# housekeepingpounce doctor # diagnose a dead/slow hotkey (see Troubleshooting)pounce --help # every flag, from the binary itselfpounce --versionpounce --daemon # run the resident daemon (launchd uses this)pounce --copy-file <path> # copy a file (contents) to the clipboardpounce --request-accessibility # prompt for the Accessibility grantpounce --check-accessibility # exit 0 / prints true when grantedpounce --request-bluetooth # prompt for the Bluetooth grant (v0.4.4+)pounce --check-bluetooth # exit 0 / prints true when granted| Flag | Purpose |
|---|---|
-p, --placeholder | Prompt text for the search field |
-i, --icon | SF Symbol icon for the picker |
--chain | Mark a free-text Enter as feeding another pounce step — holds the window with the loading skeleton instead of fading. See two-step commands |
run <item-key> | Run one item by the key items uses (cmd:emoji, mode:clipboard, app:/…), for binders that own the keystroke |
--launcher | Apps + commands mode |
--max-empty N | Rows shown before any query is typed |
--cheatsheet [path] | Overlay a cheatsheet (JSON) |
--transform <filter> | Pipe the current selection through a shell filter and paste the result back (needs Accessibility) — how Capitalize / Lowercase work |
run <item-key> | Run one item: mode:clipboard | mode:emoji | mode:screenshots | mode:camera | mode:filesearch | mode:launcher, cmd:<id>, app:<path>. The built-in windows had a flag each until 2026-07-30 (--clipboard and friends); they’re items now, so one name works as a palette row, a hotkey target and a CLI argument. Needs the daemon |
--version | Print the version |
--request-accessibility / --check-accessibility | Manage the Accessibility (TCC) grant |
--request-bluetooth / --check-bluetooth | Manage the Bluetooth (TCC) grant — the bluetooth plugin calls this for you |
File paths
Section titled “File paths”| Path | What |
|---|---|
~/.config/pounce/config.json | Configuration |
~/.config/pounce/commands/ | Your commands (highest precedence) |
~/.config/pounce/cheatsheet.json | Optional cheatsheet content |
~/.local/share/pounce/frecency.json | Usage history for ranking |
~/.local/share/pounce/pounce.sock | Daemon control socket |
Environment variables
Section titled “Environment variables”Set by packagers (the rice), rarely by hand:
| Variable | Meaning |
|---|---|
POUNCE_BUILTIN_DIR | Directory of built-in commands |
POUNCE_EXTRA_COMMAND_DIRS | Colon-separated extra command dirs (Nix layers) |
POUNCE_COMMAND_PATH | Colon-separated ad-hoc command dirs |
Homebrew binaries
Section titled “Homebrew binaries”Installing via brew install pounce puts these on your PATH:
| Binary | What |
|---|---|
pounce | The app / daemon |
pounce-palette | The launcher wrapper (bind a hotkey to this) |
pounce-<command> | A wrapper per built-in command (e.g. pounce-clipboard) |
Inside nebelhaus, set the palette up via nebelhaus.pounce
instead — the module handles the daemon, hotkey, and permission survival for you.