A macOS menu bar app that provides quick access to zot from anywhere on your system.
Let zot sidekick whip up your emails:
Let zot sidekick give your code a glow-up:
Menu Bar Integration: Lives in your menu bar for quick access** Global Hotkey**: Long-press the Right Option key to toggle the panel from anywhere** Floating Panel**: Spotlight-style centered floating window that remembers its position and size** Chat Interface**: Clean, dark-themed chat interface with streaming responses** Image Support**: Drag and drop images for vision-based queries** Sessions**: Conversations auto-save and can be browsed, searched, reloaded, and deleted** Working Directory**: Set context for file operations** Inline Settings**: Configure provider, authentication (API key or subscription), and model directly in the panel
- macOS 26 or later
- A recent Xcode (the project targets the macOS 26 SDK)
- An Anthropic, OpenAI, etc. API key, or a supported subscription login
Note on subscription login.The OAuth client IDs used are the ones published in Anthropic's Claude Code CLI, OpenAI's Codex CLI, and the Kimi Code CLI device-code flow. Reusing them from a third-party tool may be against their terms of service and may be revoked at any time. Use it at your own risk; the API-key flow is the safe default.
The zot
binary is baked into the app bundle, downloaded from the official GitHub releases. No separate zot install is required, and the app never uses any zot on your system PATH.
On launch the app checks the
zot-sidekick releases
and compares the latest tag with its own version (AppUpdater.swift
). When a newer release exists, a small primary "Update" button appears in the panel top bar (after Settings and Sessions) that opens the latest release page.
The app manages its own copy of the zot binary in Application Support and
updates it entirely in Swift (ZotUpdater.swift
):
- On launch and on demand it checks the GitHub releases API for the latest version.
- When a newer version exists, Settings shows a primary "Update zot" button that downloads the correct darwin build for your architecture, extracts it, and atomically replaces the installed binary, then restarts the bridge.
- The bundled in-app copy only seeds the first install; downloaded updates are preferred and are not overwritten unless a newer build ships in the app.
No shell script is involved at runtime.
scripts/bundle-zot.sh
is a pre-release helper to refresh the binary that ships inside the app bundle. It is not part of the running app and does not affect the in-app update path.
- Open
zot sidekick.xcodeproj
in Xcode - Build and run (Cmd+R)
- Grant Accessibility (for the global hotkey and "Paste into") via the menu bar icon's right-click menu ("Open Accessibility Settings"); Screen Recording is requested lazily the first time you take a screenshot
- Open the panel and click the gear (Settings) in the top bar to configure authentication
Menu Bar Click: Click the icon in the menu bar to toggle the panel** Global Hotkey**: Long-press the Right Option key (about 0.4s) to toggle it from anywhere** Dismiss**: Press Escape or click the menu bar icon again
The panel remembers the position and size you drag it to across hide/show. It resets to the default centered position only when you quit and relaunch.
-
Type your question in the input field at the bottom
-
Press Enter or click the send button
-
Drag and drop images onto the window to include them in your query
-
Click "Copy" on any assistant response to copy it to clipboard
-
Click "Paste into …" to paste the response into your previously active application
-
Click the folder icon in the input bar to set a working directory
-
zot will use this directory as context for file operations
-
Conversations are saved automatically after each reply
-
Click "Sessions" in the panel top bar to browse, search, reload, or delete saved sessions
-
Sessions are stored as flat JSON files in
~/Library/Application Support/zot sidekick/sessions/
(no project folders) - Click "+ New" to start a fresh conversation
- Click the gear (Settings) in the panel top bar to open settings inline
- Configure:
- Provider (Anthropic, OpenAI, ChatGPT Subscription, Kimi, Google, DeepSeek, Ollama)
- Authentication: either a subscription login or an API key
- Default Model
- zot binary version, with a primary "Check for Updates" / "Update zot" button
Providers that support subscriptions (Anthropic Claude, ChatGPT, Kimi) offer a "Log in with subscription" button. This runs the same OAuth flow as the zot CLI:
- Anthropic and ChatGPT open a browser and capture the callback on a local loopback port (PKCE).
- Kimi uses the OAuth device flow (a code plus a browser page).
Tokens are written to the bundled binary's ZOT_HOME/auth.json
, so the embedded zot uses your subscription with no extra setup. API keys are stored the same way. Use "Sign Out" to remove stored credentials for a provider.
The model picker shows models from every provider you are logged into, grouped
by provider. The full live model list per provider is fetched via a one-shot
zot rpc
query (ZotModelFetcher
). Choosing a model from a different provider switches the active provider and restarts the bridge with its credentials.
AppDelegate: Owns the menu bar icon, the panel, and the global hotkey** HotkeyMonitor**: Long-press Right Option detection (CGEvent tap with an NSEvent fallback)** PanelController**: Controls the floating panel window and remembers its frame** PanelChatView**: SwiftUI chat UI, including the inline settings overlay (InlineSettingsView
) and the typing indicatorAppState: Observable state management with@Observable
, plus binary preparation and session/auth orchestrationZotBridge: Spawns and communicates with the bundledzot rpc
process via JSON-RPCSessionStore: Flat-file JSON persistence for sessions (no project folders)** ZotAuth / ZotOAuthLogin**: API keys and subscription OAuth, written toZOT_HOME/auth.json
ZotUpdater: GitHub release checks and in-Swift download/install of the bundled zot binary** AppUpdater**: Checks zot-sidekick releases and surfaces the in-app "Update" button** ZotModelFetcher**: One-shot RPC that fetches the full live model list per authenticated provider** SettingsWindow**: A standalone settings window (legacy fallback; the inline panel settings are the primary path)
The app uses:
- SwiftUI for the UI
- Observation framework for state management
- ScreenCaptureKit for screenshot functionality
- Process for spawning the bundled zot binary
- NWListener loopback servers for OAuth callbacks
- Icon Composer (
Icon.icon
) for the Liquid Glass app icon, with a classicAppIcon.appiconset
as the fallback for older macOS
The app icon is an Icon Composer bundle (Icon.icon
). Because the CI runner's
actool
can crash compiling .icon
, the catalog is precompiled on a Mac with
a recent Xcode into prebuilt-icon/Assets.car
(plus Icon.icns
) via
scripts/compile-icon.sh
, committed, and injected into the built app by the
workflow. Re-run that script and commit prebuilt-icon/
whenever the icon or
asset catalog changes. The classic AppIcon.appiconset
remains as a fallback
for pre-26 macOS.
.github/workflows/build-dmg.yml
builds the app on macos-latest
(pinned to the latest Xcode) on every push and on manual dispatch. It:
- auto-versions each build from the run number with rollover
(
`0.0.1 ... 0.0.99 -> 0.1.0 ... 0.99.99 -> 1.0.0 ...`
), - signs the app (including the embedded zot binary) with a Developer ID certificate and hardened runtime, then notarizes and staples the DMG,
- packages a
zot-sidekick-<version>.dmg
containing the app and anApplications
symlink for drag-and-drop install, - uploads the DMG as a build artifact, and publishes a GitHub Release
(tag
vX.Y.Z
, marked as latest) on every push tomain
.
Releases are notarized Developer ID builds, so they launch without Gatekeeper warnings. If the signing secrets are absent, the workflow falls back to an ad-hoc signature; such builds require a right-click "Open" on first launch and re-prompt for Accessibility on every launch.
This app bundles the zot binary and spawns it as a child process that reads
and writes files anywhere and runs developer tools. That model requires the
sandbox to be off (ENABLE_APP_SANDBOX = NO
) and is therefore distributed as a notarized, Developer ID-signed build outside the Mac App Store, the same as the zot CLI itself. The Mac App Store requires a sandbox that would prevent the embedded agent from doing its job, so it is not a target for this app.
For release: archive in Xcode, sign with your Developer ID (deep-signing the
embedded zot-bin
), notarize with notarytool
, then staple and ship the
.dmg
.
MIT