Native desktop dialogs for Claude Code. When Claude needs your input — a permission prompt, or a question while it's waiting — Knowtify pops a dialog wherever you are, instead of making you switch back to the terminal.
Built and tested primarily on macOS (AppleScript). Linux is supported
on a best-effort basis via zenity/kdialog (dialogs), notify-send
(banners), and xdotool (focus, X11 only); if none are installed, Knowtify
quietly defers to Claude's in-terminal prompts.
Claude only. Cursor/Windsurf were evaluated and dropped: their hooks can't replace the editor's own approval UI, so an external dialog can't be the single prompt there. See the note at the bottom.
A small shared core (the "show a dialog over any app + collect the answer" layer) with a thin Claude adapter on top:
knowtify/ # ← this repo *is* the Claude Code plugin
├── .claude-plugin/
│ ├── plugin.json # plugin manifest (name, version, hooks)
│ └── marketplace.json # self-marketplace, so `/plugin marketplace add` works
├── hooks/
│ └── hooks.json # registers PermissionRequest + Stop with Claude Code
├── commands/ # plugin slash commands (/knowtify:style, /knowtify:notify-when)
│
├── core/ # shared, tool-agnostic primitives
│ ├── dialog.mjs # osascript Allow/Deny + text-input dialogs + banners
│ ├── focus.mjs # is the host app frontmost?
│ ├── config.mjs # user prefs (style, notifyWhen) — env › file › default
│ ├── logger.mjs # per-channel rolling logs
│ ├── paths.mjs # ~/.knowtify layout
│ └── io.mjs # stdin / safe JSON
│
├── claude/ # adapter: PermissionRequest + Stop hooks
│ ├── hooks/ # thin entry points (stdin → orchestrator → stdout)
│ ├── lib/ # pure transformers + orchestrators with injected deps
│ └── scripts/ # patch-settings.mjs (manual install) + set-style / set-notify-when
│
├── test/ # node:test suites (run orchestrators via fakes — no GUI)
├── install.sh · uninstall.sh # manual install path
Design principles
- Side effects are injected. Each
liborchestrator takes its dialog/focus/log/fs dependencies as parameters (defaulting to the real ones), so tests drive every decision path without opening a dialog. core/stays tool-agnostic, so a second adapter could be added later without touching it.
Knowtify is a self-contained Claude Code plugin. From inside Claude Code:
/plugin marketplace add Arjun20398/knowtify
/plugin install knowtify@knowtify
That's it — Claude registers the PermissionRequest + Stop hooks for you, no
shell script and no extra setup (GUI backends are detected at runtime). Update
later by re-running /plugin marketplace add Arjun20398/knowtify.
If you'd rather wire it into ~/.claude/settings.json directly:
git clone https://github.com/Arjun20398/knowtify ~/.knowtify
bash ~/.knowtify/install.shSyncs to ~/.knowtify and registers the PermissionRequest + Stop hooks in
~/.claude/settings.json. See claude/README.md for how
it works.
Almost everything works two ways — as a slash command inside Claude Code or
as claude plugin … from your shell. Use whichever you like.
Install
/plugin marketplace add Arjun20398/knowtify
/plugin install knowtify@knowtify
Update — get the latest version
claude plugin marketplace update knowtify # refresh the marketplace
claude plugin update knowtify@knowtify # then /reload-plugins to apply in-sessionManual install instead? Just re-run
bash ~/.knowtify/install.sh(it pulls the latest).
Enable / disable — turn it off without uninstalling
claude plugin disable knowtify@knowtify
claude plugin enable knowtify@knowtifyChange settings (details in Configuration below)
/knowtify:style notify # or: dialog — quiet banner vs a modal dialog
/knowtify:notify-when always # or: unfocused — alert always vs only when you're away
Run either with no argument to see the current value.
Uninstall
/plugin uninstall knowtify@knowtify
/plugin marketplace remove knowtify
Manual install? Run
bash ~/.knowtify/uninstall.shinstead.
Inspect
claude plugin list # what's installed + enabled
claude plugin details knowtify@knowtify # components it registersSubcommand names can vary slightly by Claude Code version — run
claude plugin --helpto see what's available on yours.
Knowtify defaults to blocking dialogs (act on it right there). If you'd
rather not be interrupted by a modal, switch to notify mode: instead of a
dialog, Knowtify fires a non-blocking banner ("Claude is waiting for your
input") and defers to Claude's own in-terminal prompt — for waiting questions,
permission requests, and AskUserQuestion alike.
Plugin users — toggle it from inside Claude Code (plugin commands are
namespaced, hence the knowtify: prefix):
/knowtify:style notify # or: dialog (run with no argument to see the current setting)
That writes the preference for you. Under the hood the setting lives in
~/.knowtify/config.json, which you can also edit by hand (handy for the manual
install):
{ "style": "notify" }"style" is "dialog" (default) or "notify". For a one-off override (e.g. a
single session) set the KNOWTIFY_STYLE env var, which takes precedence over
the file:
KNOWTIFY_STYLE=notify # or: dialogIn
notifymode a banner can't carry an Allow/Deny or a typed reply, so Knowtify never decides for you — it just nudges you and lets Claude's terminal prompt take over.
By default Knowtify stays quiet when Claude's window is already frontmost — no point popping a dialog you're staring at. But if you run several Claudes in separate terminal tabs of the same window (e.g. multiple projects in one IntelliJ window), that means a Claude finishing in a background tab gets suppressed too: tabs aren't separate OS windows, so there's no way to tell the focused tab from a background one.
Switch to always to be alerted regardless of focus:
/knowtify:notify-when always # or: unfocused (run with no argument to see the current setting)
{ "notifyWhen": "always" }KNOWTIFY_NOTIFY_WHEN=always # or: unfocused (one-off override, wins over the file)"notifyWhen" is "unfocused" (default) or "always". With always, focus
picks the channel for you: while you're focused on the window you get a
non-blocking banner (a modal would interrupt what you're doing), and when
the window is in the background your configured style applies — so the
dialog (with its clickable choices) still pops only when you're away.
To make several terminals tellable apart, each banner shows the first line of
your prompt as its body, with the project name as the subtitle — so when more
than one Claude is vying for attention you know at a glance which one is asking
(e.g. "Refactor the auth module" · sei-data-platform). Its chime plays at a
softer ~50% volume rather than the OS's full-volume notification sound.
npm test # node --test| OS | macOS 12 Monterey or later (primary); Linux with zenity/kdialog (best-effort) |
| Node.js | 18 or later |
| Claude Code | any recent version |
Claude Code's PermissionRequest hook is the authoritative approver — its
allow/deny response is what Claude acts on, so a native dialog can fully replace
the terminal prompt. Cursor and Windsurf hooks can only add a restriction
(deny); their allow does not suppress the editor's own approval UI, so an
external dialog can't be the single prompt. Rather than ship a confusing
double-prompt, Knowtify focuses on Claude.