A keyboard-first macOS menu bar app for working Checkvist lists fast. Quick navigation, priority and due workflows, focus timers, a kanban board, an honest daily log, and a command line that reaches the same data.
Priority lives in the menu bar and is built to be driven without the mouse. Checkvist owns your tasks; Priority adds the things Checkvist has no representation for — priority ranking, start dates, recurrence, focus sessions, daily habits, and a record of what actually happened each day.
It works offline. It works from the terminal. And it exposes the whole surface to an AI assistant over MCP.
- macOS 15.6+
- Repository: MaybeItsSoftware/priority
- Install · First run
- Keyboard flow · Command palette
- Views · The dock row
- Daily log · Obsidian daily notes
- Command line · MCP server · Plugins
- Build from source · Where your data lives
- Download the latest
.dmgfrom Releases. - Drag
Priority.appintoApplications. - Right-click it once and choose Open.
The build is signed with a development certificate rather than a Developer ID, so Gatekeeper will ask the first time. If it refuses outright:
xattr -cr /Applications/Priority.appOr build it yourself — see Build from source.
Open Preferences with Cmd+,:
| Step | What |
|---|---|
| 1 | Checkvist username and remote API key (from checkvist.com/auth/profile) |
| 2 | The checklist/list ID to work in |
| 3 | Global hotkey to toggle the popover |
| 4 | Quick-add hotkey, and whether it targets the list root or a specific parent |
| 5 | Day rollover hour — when your day starts, default 04:00 |
| 6 | Obsidian inbox folder (optional) |
| 7 | MCP integration (optional) |
| 8 | Launch at login |
Onboarding boxes guide the Checkvist, Obsidian and Google Calendar setup. Each one is dismissable, and the app stays usable offline-first without any of them.
| Key | Action |
|---|---|
j / ↓ |
Next task |
k / ↑ |
Previous task |
l / → |
Enter subtasks |
h / ← |
Exit to parent |
Ctrl+← / Ctrl+→ |
Cycle root view |
q |
All view |
w |
Due view |
e |
Tags view |
r |
Priority view |
t |
Kanban view |
y |
Matrix view |
Shift+T |
Daily view |
Esc |
Cancel input / close popover |
| Key | Action |
|---|---|
Space |
Complete |
Shift+Space |
Invalidate ("won't do") |
Enter |
Add sibling |
Shift+Enter / Tab |
Add child |
Shift+Tab |
Unindent |
Shift+A |
Quick-add at the configured location |
Cmd+↑ / Cmd+↓ |
Move task |
1–9 |
Scoped priority rank, within the parent |
Hyper+1–Hyper+9 |
Absolute priority rank (Ctrl+Cmd+Option+Shift) |
= |
Send to priority back |
- |
Clear scoped priority |
Hyper+- |
Clear absolute priority |
' |
Start a focus session on the selected task, from any view |
| Key | Action |
|---|---|
h / ← |
Previous column |
l / → |
Next column |
Cmd+← / Cmd+→ |
Move task between columns |
f |
Show this task in the All view, entering its subtasks if it has any |
| Key | Action |
|---|---|
j / k |
Move through the checklist |
Space |
Tick / un-tick |
Return |
Add a daily — stays open, so a whole routine can be typed in one go |
Cmd+↑ / Cmd+↓ |
Reorder |
Esc |
Close the add field |
| Key | Action |
|---|---|
o |
Open the selected task in Obsidian |
O |
Open in a new Obsidian window |
gc |
Add to Google Calendar |
Open with :, ; or Cmd+K. Most commands accept several spellings — unrepeat, no repeat, remove repeat and clear repeat all do the same thing.
| Family | Commands |
|---|---|
| Status | done, undone, invalidate, delete, undo |
| Due | due <value>, clear due |
| Start date | start <value>, edit start, clear start |
| Repeat | repeat <rule>, repeat daily, repeat every <n> <unit>, clear repeat |
| Tags | tag <name>, untag <name> |
| Priority | priority <1-9>, priority back, clear priority |
| Matrix | matrix <quadrant>, importance <value>, urgency <value> |
| Kanban | kanban left / right, kanban move left / right, kanban enter, kanban exit, kanban show in all, kanban focus mode |
| View | list <name>, tab <name>, cycle tab next / prev, cycle filter next / prev, toggle children, toggle subtree, toggle context, toggle hide future |
| Timer | focus mode, toggle timer, pause timer |
| Obsidian | sync obsidian, open obsidian new window, link / create / clear obsidian folder, choose obsidian inbox |
| Calendar | sync google calendar, open google calendar |
| MCP | mcp guide, mcp config, copy mcp config, refresh mcp path |
| App | preferences, search, quick add, refresh lists, upload offline tasks |
Due values understand natural language and times: due today 14:30, due tomorrow 9am, due next week, due 4pm fri, due next monday morning. The time words morning, noon, afternoon, evening, midnight, eod and cob all resolve to configurable named times.
| View | Key | What it shows |
|---|---|---|
| All | q |
The full tree |
| Due | w |
Due and overdue, soonest first |
| Tags | e |
Grouped by tag |
| Priority | r |
Your ranked queue |
| Kanban | t |
Configurable columns, filtered by tag or scoped to subtasks |
| Matrix | y |
Eisenhower quadrants by importance and urgency |
| Daily | Shift+T |
Dailies, the chart, and what you finished |
Kanban cards show the task text with tags stripped, a P1–P9 priority badge, the due date with overdue/today highlighting, inline tags, and a subtask count. Columns are configured in Preferences and reorder by drag.
A narrow strip along the bottom of the popover, in every view. Right to left:
| Button | Does |
|---|---|
| ⚙︎ Gear | Preferences |
| ↻ Refresh | Re-fetch from Checkvist, with a spinner while it runs |
| ↕ Resize | Reveal the drag strip |
| ▁▃▅ Graph | Show/hide the Daily chart — Daily view only |
Each root view remembers its own height. The Daily view stacks a checklist, a chart and a completions list where the All view is a single list, so one shared height would be wrong for one of them at all times. Drag the strip to set a height; double-click it to go back to sizing from the content.
Heights are clamped to 240–900pt on write and on read at launch, so a stored value can never put the strip out of reach. If one somehow does:
defaults delete uk.co.maybeitsadam.priority panelHeightOverridesByRootViewHiding the graph shortens the panel by exactly the chart's height, which turns the Daily view into a compact checklist on days you're only ticking things off.
The Daily view (Shift+T) answers "what did I get done, and how does today compare?"
Recurring things you intend to do — habits, not tasks — sitting at the top of the view as a checklist.
- They reset at every rollover and never go overdue. Miss one and it's a gap in the history: nothing to clear, nothing to reschedule. That's the whole reason they aren't Checkvist tasks with a
repeat dailyrule — a recurring task goes overdue and starts competing with real deadlines. - They're local. Stored in
~/Library/Application Support/Priority/dailies.json, so "brush teeth" never clutters your project lists or syncs to other Checkvist clients. Ticking one is instant and works offline. - Ticks land in the same log as task completions, so they count towards the chart and appear in the Obsidian note.
- Schedules and archiving live in
Preferences → Plugins → Daily Log— every day, or specific weekdays. Removing a daily archives it, so past days keep their record instead of showing an orphaned id.
- Recording is always on and always local. Completions, reopens, invalidations, finished focus sessions and the day's plan are appended to
~/Library/Application Support/Priority/daylog.jsonl— one JSON object per line, so it stays readable withtail, and a torn write costs one event rather than the file. - Checkvist owns current state, the log owns history, Obsidian owns the archive. Nothing syncs backwards, so there is no conflict resolution anywhere in this.
- The day's plan is derived, not authored. At the first popover open after your rollover hour, whatever is due, overdue or starting that day is snapshotted. That's what "N left" measures against — you never plan a day by hand.
- Deferring is not slipping. Pushing a due date forward is recorded distinctly from letting a task rot, so the view doesn't nag about a decision you made deliberately.
- The day starts at your rollover hour, not midnight (default 04:00), so a session finishing at 01:30 counts towards the day it belonged to.
- No backfill. History starts the day you first run this build. The chart is drawn from day one regardless — a flat run of days is a true statement about a history that has just started — with a "collecting since" line underneath until the window fills.
In Preferences → Plugins → Daily Log, point Priority at your dailies folder and set the note naming to match your vault (yyyy-MM-dd by default; a subfolder pattern like yyyy/MM nests them). The preview line shows exactly which note today's block would land in. Then switch on "Write days into Obsidian daily notes", which stays disabled until a folder is chosen.
Once a day closes, its block is spliced into that day's note:
<!-- priority:begin -->
## Log
**5 done** · **2/3 dailies** · **1h 40m focused** · 2 of 7 planned left
_Dailies:_
- [x] Read
- [x] Walk
- [ ] Stretch
- [x] Ship the DMG
- [x] Review the sync PR
_Unfinished:_
- [ ] Write release notes
<!-- priority:end -->Only the text between the markers is ever touched, and rewriting a day replaces its own block rather than stacking a second one. Creating missing notes is off by default, so the plugin can't beat a Templater or Daily Notes template to the file — turn it on only if nothing else builds your dailies.
priority is a Rust CLI covering the same ground: your lists, your dailies, your day log. It talks to the Checkvist API directly and reads Priority's local files off disk, so it works whether or not the app is running — and its writes take the same flock(2) the app does, so both can be open at once.
Run it with no arguments and it opens a terminal UI with the same tabs as the app, and the same keys to reach them:
All q │ Due w │ Tags e │ Priority r │ Kanban t │ Matrix y │ Daily d
┌ All ───────────────────────────────────────────────────────────────┐
│▎[ ] Ship v0.4 │
│ [ ] Draft the release notes #work │
│ [x] Tag the commit │
│ [ ] Buy milk #home │
└────────────────────────────────────────────────────────────────────┘
j/k move · l/h in-out · space done · a add · ? help · esc quit
Or drive it by subcommand:
./scripts/install_cli.sh # release build + a symlink onto your PATH
priority auth login
priority # the terminal UI
priority tasks
priority add Draft the release notes --due friday
priority search -q report --due-before 2026-09-01
priority daily add Read for twenty minutes --weekdays mon,wed,fri
priority log --days 7
priority --json dailies | jq '.dailies[] | select(.done | not)'Its credentials are its own, in ~/.config/priority/config.json at mode 0600 — separate from the app's login-keychain item, so neither depends on how the other was built or signed. The dailies, log and metadata commands need no credentials at all.
Every command is one of the MCP tools under a friendlier name, and the same binary serves them over MCP with priority --mcp-server.
Full guide: docs/cli.md
Priority exposes 19 MCP tools so an AI assistant can work with your lists directly — thirteen that reach the Checkvist API, and six for the local state Checkvist has no representation for (day log, dailies, priority ranks and recurrence).
Set it up from Preferences → Plugins → Native MCP Integration. It detects Claude Code, Claude Desktop, Cursor, Windsurf, VS Code and Zed, and adds Priority to the one you pick in a single click, preserving any servers already in that client's config.
There are three interchangeable implementations — the one embedded in the app, a dependency-free Python fallback script, and the Rust CLI. A client may be pointed at any of them, so scripts/mcp_parity_check.py drives all three against a fixture and a stub API and diffs their tool lists, their answers, the files they leave on disk, and the HTTP requests they make.
Full guide: docs/mcp-server.md
Every external integration is a plugin behind a protocol.
Built in: NativeCheckvistSyncPlugin, NativeObsidianIntegrationPlugin, NativeGoogleCalendarIntegrationPlugin, NativeMCPIntegrationPlugin, NativeDailyLogPlugin, OfflineTaskSyncPlugin.
To install your own, open Preferences → Plugins and click Install Plugin (folder, .zip, or .priority-plugin), or drop a plugin folder into ~/Library/Application Support/Priority/Plugins and hit Reload.
Built-in plugins are fully functional; user-installed plugins are manifest-driven (settings, metadata, lifecycle) and prepared for runtime capability wiring.
Authoring guide: docs/plugins.md
Requirements: macOS 15.6+, Xcode 17+, and Rust for the CLI.
git clone https://github.com/MaybeItsSoftware/priority.git
cd priority
# The app
xcodebuild -project 'Priority.xcodeproj' -scheme 'Priority' -configuration Debug -destination 'platform=macOS' build
# Headless logic (303 tests)
swift test
# The CLI (48 tests)
cargo test --manifest-path cli/Cargo.toml
# All three MCP servers agree
cargo build --release --manifest-path cli/Cargo.toml
python3 scripts/mcp_parity_check.py
# Build + launch Debug, or produce a release DMG
./scripts/run.sh
./scripts/build_dmg.sh <version>| Path | What |
|---|---|
Priority/ |
The macOS app. CoreLogic/ is pure, headless, UI-free logic. |
Priority/Plugins/ |
Integration plugins, one folder each, behind protocols |
cli/ |
The Rust CLI crate — shares no source with the Swift side |
scripts/ |
Build, install, the Python MCP fallback, and the parity check |
docs/ |
CLI · MCP · plugins · state ownership |
The same source tree is compiled by two build systems: the Xcode project builds the app, and Package.swift exposes PriorityCore, PriorityPlugins and PriorityAppLogic as SPM libraries so the headless logic can be tested without the app shell. Adding or moving a file often means updating Package.swift too — see CLAUDE.md.
| Path | What |
|---|---|
~/Library/Application Support/Priority/ |
Dailies, day log, task cache, installed plugins |
~/Library/Preferences/uk.co.maybeitsadam.priority.plist |
Settings, priority ranks, recurrence rules, start dates |
Login keychain, service uk.co.maybeitsadam.priority |
The app's Checkvist remote key |
~/.config/priority/config.json |
The CLI's own credentials, mode 0600 |
Nothing is sent anywhere except Checkvist, and Google Calendar or Obsidian if you enable them.
Upgrading from Bar Tasker? Everything is carried across automatically on first launch — preferences, dailies, the day log and your keychain item. The old locations are copied rather than moved, so they stay on disk until you delete them.
MIT