Guide

Change wisp itself, by asking: spells

In wisp you change the app itself by asking: “a floating button that opens all my PRs”, “a panel with my tool stats”, “a deploy block for agents”, “compact cards”. An agent writes the change as a spell, a small JSON file wisp layers over itself. You try it in the window, keep it, and share it as a file.

Source on GitHub

Free and open source · Claude Code, Codex, Cursor CLI, opencode, Antigravity CLI, Grok Build · macOS, Linux, Windows (beta) · Node 20+

Above: a working copy. Pick an ask and the little wisp window changes the way the app does; switch a spell off and it changes back. { } shows the spell file.

Cast one

  1. Start wisp.
  2. Say what to change. ⌘K → Cast a spell…, Settings → Spells, or type /spell in the prompt box: /spell a floating button that opens all my open PRs, with how many there are. Be as specific as you like about where it goes and what it shows.
  3. Look at the preview. The agent works in an empty folder of its own: it reads wisp’s spell reference, samples your real data, checks the spell with wisp until it passes, and hands it back as a card. The card shows its buttons as they will look, its panels drawn with your data, and a plain-words list of what it adds.
  4. Try it, then keep it. Try it applies the spell to this window only; Undo takes it back. Turn on keeps it. A follow-up on the card changes it (“make it a top bar button”, “only for api-server”), and “remove the … spell” drops it.
A Cast a spell report in wisp: the Task board spell with what it adds, a preview of its floating button and its panel drawn with real data, and Turn on, Try it, JSON and Copy buttons

Every spell you have is in Settings → Spells, with a switch, its reach, and export, copy and remove.

What a spell can change

Ask forWhat wisp does
A floating button that opens all my PRsA button in a corner with a live count, from a read-only gh search prs wisp runs; one click opens them all
My wisp tool statsA top bar button that opens a dialog of stats and charts from /usage/tools
A deploy block for agentsA new block type agents are told about and can end their reports with
Quick replies, a second opinionChips above the follow-up box, a Second opinion button that reruns the task on Codex in Plan mode
Compact cards, PR first, Open PR on hoverRestyles and reorders the card head, adds a hover button on cards that have a PR
A bigger, monospace prompt boxPlaceholder, rows and font of the prompt box; hides the parts you don’t use
A violet wispSets design tokens (--accent, --radius, …), with separate values for dark mode
A /explain commandA slash command for the follow-up box that sends a prompt with what you typed after it
Send failing tests back onceA hook: when a turn ends with failing tests, wisp replies to the agent, once per task
Codex always runs the linter firstA rule added to Codex agents’ prompts

What’s in a spell file

A spell is one JSON object. Only wispSpell, id and name are required; everything else is a list of things to add or change. This is the whole My PRs spell:

{
  "wispSpell": 1,
  "id": "my-prs",
  "name": "My PRs",
  "version": "1.0.0",
  "description": "Open every PR you have open on GitHub, from a floating button.",
  "data": {
    "prs": {
      "gh": ["search", "prs", "--author", "@me", "--state", "open", "--json", "url,title,repository"],
      "refresh": 120
    }
  },
  "buttons": [
    {
      "slot": "float",
      "icon": "github",
      "label": "My PRs",
      "badge": {
        "count": "prs"
      },
      "do": {
        "openAll": {
          "data": "prs",
          "field": "url"
        }
      }
    }
  ]
}

The other top-level fields:

FieldWhat it holds
descriptionOne sentence, shown in Settings → Spells and on its review
chipsSmall labels on a card head or under the prompt and follow-up boxes, from a template or a script
panelsDialogs, drawers and panels above the feed or inside a card, drawn by a script from the spell’s data
partsRestyle a named part of wisp, set its props (a monospace prompt box, compact cards) or reorder the card head
uihide named parts; tokens and dark recolour wisp
blocksNew report block types agents can use
promptRules added to agents’ prompts, for all tasks or some
settingsSettings that win over yours while the spell is on (shown as Set by … in Settings)
slash/commands for the follow-up box
commands⌘K items, optionally with a single-key shortcut
hooksSomething wisp does on its own when a turn finishes, fails, starts or waits
recipesSaved prompts with {params}
harnesses, whenWhich harnesses, projects, agents and modes its rules, blocks and hooks apply to
scriptJavaScript functions that turn data into blocks, strings or links, run in a sandbox

Text, labels, links and gh arguments can use {field} templates filled from the card’s task: {title}, {branch}, {project}, {agent}, {pr.number}, {pr.url} and more. In slash commands, {args} is what you typed after the command.

Where things can go

Spells never touch wisp’s page directly. They name a slot, and wisp puts the button, chip or panel there with its own markup, so wisp updates don’t break them.

SlotWhere
topbarThe top bar, next to Automations and Search: a labelled button with an optional count.
floatFloating buttons, stacked in a bottom corner of the window. Buttons with the same group fold into one that fans out.
paletteAn item in the ⌘K palette.
feed.topAbove the feed: a panel pinned there (panels only).
launcher.barIn the prompt box bar, before Send.
launcher.chipsChips under the prompt box (chips and buttons).
composer.barIn a card's follow-up box bar, before Send.
composer.chipsChips above a card's follow-up box (quick replies).
card.headA chip in a card's head, before the time.
card.hoverA small icon button on a card head, shown on hover.
card.menuAn item in a card's right-click menu.
card.topA panel at the top of an open card (panels only).
card.bottomA panel at the bottom of an open card, above the follow-up box (panels only).
sidebar.topSplit view: a button at the top of the task list, next to New task.
split.headSplit view: a button in the open task's header, next to its title (acts on that task).

Panels open as a dialog, a side drawer, pinned above the feed, or inside an open card. To restyle or hide something, a spell names a part (card.title, launcher.input, topbar.machines, …) and sets a short list of allowed CSS properties, props such as density or monospace, or the order of the card head.

The split view (tasks in a sidebar, the open one beside it) is a spell’s too: settings: { "view": "split" } switches to it, the sidebar part takes width, side (left, the default, or right) and details (full shows the model, machine and project on every row), split.main takes a readWidth, split.home a greeting over the prompt box, and the sidebar.top and split.head slots take buttons.

What buttons can do

Each action has a reach, and a spell’s reach is the furthest of its actions. You see it before you turn the spell on.

ActionReachDoes
openSafeOpen one http(s) link (a {field} template)
openAllSafeOpen every link in a data source (at most 20). Or { "fn": "name" }: a script function returning links, each of which must appear in the spell's data
panelSafeOpen one of this spell's panels
insertSafePut text into the box at the caret (prompt box or follow-up box)
copySafeCopy text
toastSafeShow a short message
setLaunchSafeSet the prompt box pickers
sendActs for youSend a follow-up to the card's task right away
replyActs for youSame as send, used by hooks
launchWithActs for youLaunch what is in the prompt box with these settings
launchActs for youStart a new task (cwd empty = the current project)
rerunActs for youRun the card's task again as a new task on another harness
recipeActs for youRun a recipe
shipActs for youOpen Ship it for the card's task
doneActs for youMark the card's task done
archiveActs for youArchive the card's task
notifyActs for youA desktop notification / toast (hooks: your phone, if linked)
webhookRuns commandsPOST the task summary as JSON to a URL (hooks)
execRuns commandsRun a shell command in the task's folder (asks you the first time)

Data and scripts

A spell can read two kinds of data, and wisp fetches both; spell code never touches the network.

To draw a panel, a spell carries a script: plain JavaScript functions that get the data and return wisp’s report blocks (stats, charts, checklists, callouts, tables of files, …). wisp checks what comes back and draws it with its own renderer. This is the panel behind Tool stats:

{
  "wispSpell": 1,
  "id": "tool-stats",
  "name": "Tool stats",
  "version": "1.0.0",
  "data": {
    "tools": {
      "wisp": "/usage/tools?days=30"
    }
  },
  "buttons": [
    {
      "slot": "topbar",
      "icon": "stats",
      "label": "Tools",
      "do": {
        "panel": "tools"
      }
    }
  ],
  "panels": [
    {
      "id": "tools",
      "title": "Tool calls · last 30 days",
      "as": "dialog",
      "data": ["tools"],
      "render": "tools"
    }
  ],
  "script": "export function tools({ tools: t }) {\n  return [\n    { type: 'stats', items: [{ label: 'tool calls', value: t.total.calls }, { label: 'turns', value: t.total.turns }, { label: 'spend', value: Math.round(t.total.cost * 100) / 100, unit: '$' }] },\n    { type: 'chart', title: 'By kind', items: t.kinds.map((k) => ({ label: k.kind, value: k.calls })) },\n    { type: 'chart', title: 'By harness', items: t.agents.map((a) => ({ label: a.agent, value: a.calls })) },\n  ];\n}"
}

Blocks, prompt rules and hooks

Spells also change what agents do, for every harness or only some (harnesses), and for every task or only some projects, agents or modes (when).

{
  "wispSpell": 1,
  "id": "fix-failing-tests",
  "name": "Fix failing tests",
  "version": "1.0.0",
  "hooks": [
    {
      "on": "turn.finished",
      "if": {
        "testsFailed": true
      },
      "max": 1,
      "do": {
        "reply": "Some tests fail. Fix them, rerun the suite and report again."
      }
    }
  ]
}

Why it’s safe to keep one

Share it with your team

Settings → Spells exports a spell as a .spell.json file, or copies its JSON for a chat. A colleague drops the file on wisp, pastes it (or a link) into Settings → Spells, or runs:

wisp spell install ./my-prs.spell.json --on
wisp spell install https://example.com/my-prs.spell.json
wisp spell install gist:<id>

They see the same review first, and it stays off until they turn it on. Commit a team’s spells to a repo and everyone installs the same set.

From the terminal

CommandDoes
wisp spellList your spells: ● on, ○ off, and any that are broken
wisp spell cast "…"An agent writes a spell for it; keep it in the app. --wait blocks until it’s done; --agent, --model, --effort pick who writes it
wisp spell install <file|url>Check and add a spell (a file, an https link or gist:<id>). Off until you add --on
wisp spell on|off <id>Turn one on or off; wisp spell off --all turns every spell off
wisp spell rm <id>Remove it
wisp spell export <id>Print the spell file, to save or send
wisp spell cast "a /explain command that asks for a plain-words explanation" --wait
wisp spell export my-prs > my-prs.spell.json
wisp spell off --all

Over HTTP the same lives under /spells; see Integrations.

When something looks wrong

Questions

Do spells change wisp’s code?
No. A spell is a JSON file in ~/.wisp/spells that wisp reads at runtime. Your wisp install stays as it is, updates keep working, and turning a spell off removes everything it added.
Which agents can cast spells?
Any harness wisp runs: Claude Code, Codex, opencode, Cursor, Antigravity or Grok. The agent works in an empty folder of its own and uses wisp’s spell tools to read the reference and check its spell.
Can a spell I get from someone else run code on my computer?
Only if it says so: a spell whose buttons run commands is marked Runs commands and lists each command, and asks before the first run. Spell scripts run in a sandbox with no network or file access.
Can I write a spell by hand?
Yes. Write the JSON, then run wisp spell install ./my.spell.json: wisp checks it the same way it checks a cast spell and tells you, in plain words, what it does or what is wrong.
What if I ask for something spells can’t do?
The agent says so in its report and does the closest thing spells can. Spells can only use the slots, parts, actions and data sources in wisp’s reference, so they can’t reach further than that.