A command bar for macOS

Press ⌥ Space
and cast a spell.

Cantrip opens your apps and runs your commands, and every command is a spell: a few lines of TypeScript, running on Bun, that you can write yourself.

curl -fsSL https://cantrip.just-be.dev/install.sh | sh
First release coming soon

macOS 15+ · Apple silicon

Spells are programs

Every command is an Effect program in a .ts file in your grimoire. Lists, details, searches, actions, toasts: describe the view, and the bar draws it natively.

Save to reload

Edit a spell and it's live the moment you save. Nothing to install: Cantrip links cantrip and effect into your grimoire and writes the tsconfig for you.

Daemons in the menu bar

Spells that stay running put a ring, a title and a menu in the menu bar. Their finalizers always run, and they hand state over across reloads.

Commands in your terminal

Give a spell an effect/cli Command and it's on your PATH, running inside Cantrip with the daemon's live state. Ctrl-C interrupts; exit codes come back.

Native, and quick

Rust, drawn on the GPU with Rune, with real AppKit menus and status items. Apps and spells in one fuzzy search, ⌘K for every action.

Two magical themes

Arcane's violet glass and stardust, or Sigil's ink and tarnished gold. The wand casts the bar open and sends a spark to every letter you type.

Writing a spell

A spell is a file.
Save it, cast it.

Your spells live in ~/.config/cantrip/grimoire/. Every exportedSpell.make(…) becomes a command in the bar. Each view gets its own scope, so fibers, resources and child processes are cleaned up when you close it.

  • Ui.list, Ui.detail, Ui.searchList — views are data
  • Action.copy, open, run, push
  • Logs and failures show up where you can see them
grimoire/uuid.ts
import * as Effect from "effect/Effect"
import { Spell, Ui } from "cantrip"

// No view: the bar hides at once, the spell runs, and a HUD flashes.
export const uuid = Spell.make({
  title: "Generate UUID",
  icon: "number",              // SF Symbol, emoji, or a file path
  mode: "no-view",
  run: Effect.gen(function*() {
    const id = crypto.randomUUID()
    yield* Ui.copy(id)
    yield* Ui.hud(`Copied ${id}`)
  })
})

Daemons

Some magic
keeps running.

A daemon puts an item in the menu bar and keeps going while the bar is closed. Two real ones ship in the grimoire: claude-usage, what's left of your plan limits as a ring, and insomnia, which keeps the Mac awake with the lid closed.

grimoire/battery.ts
export const battery = Daemon.summon({
  title: "Battery",
  icon: "battery.100",
  run: Effect.gen(function*() {
    while (true) {
      const level = yield* readBattery
      yield* MenuBar.render({
        icon: MenuBar.ring(level),
        title: `${Math.round(level * 100)}%`,
      })
      yield* Effect.sleep("1 minute")
    }
  })
})

Themes

Violet glass, or ink and gold.

Change Theme previews each one as you scroll. ↵ keeps it.

The bar in Arcane: violet glass
Arcane violet glass and starlight
The bar in Sigil: ink and tarnished gold, with italic serif glosses
Sigil ink and tarnished gold

Install

Summon it.

One line installs Cantrip to /Applications and opens it. Run it again to update; daemons hand their state over.

curl -fsSL https://cantrip.just-be.dev/install.sh | sh
First release coming soon

macOS 15+ · Apple silicon

Installing from the zip instead

Cantrip isn't notarized yet, so macOS quarantines a browser download. Unzip it, moveCantrip.app to Applications, then either right-click it and chooseOpen, or run:

xattr -dr com.apple.quarantine /Applications/Cantrip.app

Every release is listed at /latest.json with its SHA-256.