Skip to content

Repository files navigation

Vee

Release Platform Swift License

A native, leak-free macOS menu-bar script runner — the modern successor to xbar and SwiftBar.

Every xbar plugin. None of the memory leaks. Run any script in your menu bar — and see what it touches before you install it.

Vee runs plugins — any executable, in any language — on a schedule and renders their standard output as menu-bar titles and dropdown menus. Your existing xbar/SwiftBar plugins run unchanged; Vee adds a native AppKit UI, a trust/transparency layer, a built-in plugin catalog, and typed SDKs (TypeScript, Python, Go).

Vee — native macOS menu-bar script runner

Screenshots

Vee's Discover window showing a category sidebar and a grid of plugin cards, each with a trust chip and an Install button The install trust sheet titled "What this plugin can do", listing plain-language capability rows with severity-colored icons and an un-sandboxed footer
Discover — browse the catalog with trust chips and install in one click. Trust at install — see a plugin's footprint in plain language before it runs.
The Plugin Manager showing plugin rows with trust badges and trailing on/off switches, plus a General settings section An auto-generated plugin settings form built from declared preferences, including a masked secret field
Plugin Manager — enable, disable, and review every plugin's trust badges. Plugin settings — typed forms from <xbar.var>; secrets masked in the Keychain.
A Vee plugin's native Liquid Glass dropdown showing a Macintosh HD row with an inline progress capsule gauge, an Open Disk Utility action, and a submenu Vee's menu-bar status items showing live disk usage and a clock, each with an SF Symbol
Native dropdown — a Liquid Glass menu with an inline progress= gauge on live data. In the menu bar — each plugin renders its own native status item.

The bottom row is a real capture of the shipped build running on macOS 26.

Why Vee

  • Native and leak-free — pure Swift 6.2 / SwiftUI + AppKit (NSStatusItem/NSMenu), Liquid Glass UI, zero third-party dependencies. No embedded WebView in the menu; subprocess output is drained incrementally and processes are timed out and killed, so long-running use doesn't leak memory.
  • Runs the existing ecosystem — the full xbar/SwiftBar plugin protocol (filename intervals, ---/-- menus, | params, <xbar.*>/<swiftbar.*> headers, SF Symbols, ANSI, Markdown, streaming, cron) works on day one.
  • Transparency, not a sandbox — plugins run un-sandboxed by design (the model requires it), but they can declare what they touch via <vee.*> tags, and Vee shows a plain-language trust summary before install plus trust badges in the Manager. Advisory, never enforced.
  • Configuration belongs to the plugin<xbar.var> declarations become an auto-generated settings form; secret fields are masked and stored in the macOS Keychain. The app never hardcodes service names or credentials.
  • Discover — a built-in browser over the shared matryer/xbar-plugins catalog, with trust chips and one-click install through the trust gate.
  • Optional typed SDK — write plugins in TypeScript with Menu/Section builders; Node runs the .ts directly, no build step.

Requirements

  • macOS 26 or later
  • Apple Silicon (arm64) — there is no Intel build

Install

Vee is distributed Developer-ID-signed and notarized, outside the Mac App Store (the App Store sandbox is incompatible with arbitrary plugin execution).

Vee ships as one binary that is both the menu-bar app and the vee CLI, so installing gets you both.

Homebrew (recommended):

brew install --cask navbytes/tap/vee

Puts Vee.app in /Applications and vee on your PATH. brew upgrade --cask vee picks up new releases.

Or one line, without Homebrew:

curl -fsSL https://vee.navbytes.io/install.sh | bash

Same result — app in /Applications, vee linked into ~/.local/bin (or /usr/local/bin). Re-run it to upgrade. Read it first if you would rather not pipe a script to a shell: scripts/install.sh.

The installer takes three options, so you are not stuck with its defaults. Pass them after bash -s --:

# Install per-user instead of system-wide
curl -fsSL https://vee.navbytes.io/install.sh | bash -s -- --app-dir ~/Applications

# Put the CLI somewhere else on your PATH
curl -fsSL https://vee.navbytes.io/install.sh | bash -s -- --bin-dir /opt/homebrew/bin

# Pin a specific release rather than the latest
curl -fsSL https://vee.navbytes.io/install.sh | bash -s -- --version v0.2.0
Option Environment Default
--app-dir DIR VEE_APP_DIR /Applications
--bin-dir DIR VEE_BIN_DIR first writable of ~/.local/bin, /usr/local/bin
--version TAG VEE_VERSION the latest release

Not VEE_APP_DIR=… curl … | bash. That sets the variable for curl, not for the bash reading the script, so it is silently ignored. Use a flag, or export the variable first.

Just the CLI, via mise:

mise use github:navbytes/vee

This is the CLI only — vee render, vee lint, vee dev, vee new. mise puts binaries on your PATH; it does not install the menu-bar app. Use it when you want the CLI pinned per-project, or on a machine that only needs the tooling.

Or download directly:

  1. Download the latest Vee.app from GitHub Releases.
  2. Drag it into /Applications.
  3. Launch it. On first launch, if Gatekeeper prompts, right-click Vee.appOpen and confirm.

Quick start — your first plugin

Plugins live in ~/Library/Application Support/Vee/plugins by default (Vee creates this folder on first launch; change it in the Plugin Manager → Choose Folder). A plugin's filename encodes its refresh interval: name.INTERVAL.ext.

Create the folder if you haven't launched Vee yet, then add hello.5s.sh:

mkdir -p ~/Library/Application\ Support/Vee/plugins
#!/bin/bash
echo "Hello 👋"
echo "---"
echo "It works!"
echo "Refresh | refresh=true"

Make it executable:

chmod +x ~/Library/Application\ Support/Vee/plugins/hello.5s.sh

The .5s runs it every 5 seconds. The line before --- is the menu-bar title; everything after is the dropdown. Full reference: Plugin authoring.

Migrating from SwiftBar / xbar

Point Vee at your existing plugins folder (Plugin Manager → Choose Folder) — that's the whole migration. The entire xbar/SwiftBar protocol is supported, and Vee injects the same environment variables (XBARDarkMode, SWIFTBAR_*, OS_*), so plugins run unchanged. Caveats: macOS 26+ and Apple Silicon only. See Migrating from SwiftBar/xbar.

Features

  • xbar/SwiftBar-compatible plugin format (titles, submenus, | params, metadata headers)
  • Filename refresh intervals (ms/s/m/h/d) and <swiftbar.schedule> cron
  • SF Symbols, ANSI color, inline Markdown, emoji shortcodes
  • Streaming plugins (~~~) with restart/backoff
  • Rich menu rows: progress= gauges, sparkline= trends, pie=/donut=/stackedbar= share charts, and live toggle=/slider= controls — drawn with AppKit and Swift Charts, no WebView
  • Detached plugin windows: Open in Window leaves a plugin's whole menu open on the desktop, live on its own refresh interval
  • Searchable filter panel (opt-in <vee.filter>): fuzzy-search a plugin's whole nested menu from a Spotlight-like popover, with an optional global hotkey (<vee.shortcut>)
  • Declared typed preferences → auto-generated forms; secrets in the Keychain
  • <vee.*> trust declarations → plain-language trust summary + badges
  • Discover: catalog browser with one-click, trust-gated install — over the public catalog or your own custom/enterprise store (a GitHub repo, GitHub Enterprise, static host, or air-gapped mirror)
  • Plugin Manager: enable/disable, per-plugin settings, reveal in Finder, choose folder, launch-at-login, refresh all
  • vee:// and swiftbar:// URL actions
  • Desktop widgets (Notification Center): a configurable per-plugin status tile with gauges/sparklines, a Vee Health roll-up, and a Control Center Refresh Vee control
  • Zero-dependency typed SDKs (TypeScript, Python, Go) with golden-fixture drift guards
  • vee dev save-driven authoring loop: watch a file, re-render on every save, optionally preview it as a real menu-bar item

Documentation

Writing plugins

A plugin is any executable that prints text to stdout — bash, Python, Ruby, a compiled binary, anything. For a new plugin, printing Vee's structured-JSON format ({"vee":1,…}) is the recommended way to start: typed booleans and numbers, no |-param quoting, and clean nesting for submenus. The xbar/SwiftBar text protocol is still fully supported — it's how every existing xbar/SwiftBar plugin keeps working unchanged — and is documented in full in the plugin authoring reference. See plugins/showcase/ for ready-to-run examples of both, including kitchen-sink.1m.sh, one file exercising every JSON field.

Download and run that one file to see everything — every JSON field in one plugin:

curl -o ~/Library/Application\ Support/Vee/plugins/kitchen-sink.1m.sh \
  https://raw.githubusercontent.com/navbytes/vee/main/plugins/showcase/kitchen-sink.1m.sh
chmod +x ~/Library/Application\ Support/Vee/plugins/kitchen-sink.1m.sh

For the edit loop, keep vee dev in a split terminal beside your editor — it re-runs the file and repaints the menu on every save:

vee dev ./cpu.10s.sh            # run it and re-render on save
vee dev --text menu.txt         # preview protocol text, executing nothing
vee dev --push ./cpu.10s.sh     # also show each save as a real status item
vee lint --format compact FILE  # diagnostics your editor can place on a line

Typed SDKs (TypeScript, Python, Go)

Prefer typed builders to hand-formatting text? Vee ships zero-dependency SDKs in three languages, each with Menu/Section builders and typed builders for the rich params (sparkline/toggle/slider/progress/chart) so quoting and escaping are handled for you:

A golden-fixture drift guard shared byte-for-byte across all three keeps every SDK and the Swift parser in lockstep.

Trust model

Vee runs plugins un-sandboxed with your privileges — a real sandbox would break the plugins people want to run, which is why apps in this category ship outside the App Store. Instead of pretending to isolate plugins, Vee makes them transparent: authors declare network domains, filesystem paths, secrets, and external binaries via <vee.*> tags, and Vee shows a plain-language summary before install and trust badges in the Manager. It's advisory, never enforced — read the source of anything you don't trust. Details: Trust model.

Build from source

swift build          # build the libraries + dev executable
swift test           # run the test suites
swift run vee        # run the menu-bar app for development

# Build the distributable app bundle:
xcodegen generate
xcodebuild -project Vee.xcodeproj -scheme Vee build

Repository layout:

Sources/
├─ VeeCore/         # primitives: RefreshInterval, PluginFilename, clock, errors
├─ VeePluginFormat/ # the xbar/SwiftBar output + header parser (pure)
├─ VeeRuntime/      # discovery, execution (leak-free), scheduling, streaming
├─ VeeMenu/         # ParsedOutput → NSMenu (colors, ANSI, SF Symbols, actions)
├─ VeeSearch/       # pure flatten + fuzzy-filter/rank of a plugin's menu tree
├─ VeePreferences/  # <xbar.var> stores + Keychain secrets
├─ VeeTrust/        # <vee.*> capability declarations → trust summaries
├─ VeeCatalog/      # xbar-plugins catalog client + installer
├─ VeeUI/           # SwiftUI settings + plugin-manager windows
├─ VeeApp/          # AppKit shell: status items, coordinators, app delegate
└─ vee/             # executable entry point
plugins/            # plugin SDKs (TypeScript/Python/Go), showcase plugins, shared fixtures

Contributing & license

Contributions are welcome — see CONTRIBUTING.md. Vee is open source under the MIT License.

About

A keyboard-first, plugin-extensible macOS launcher and menubar platform — a fast, local, beautiful successor to Hammerspoon.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages