A Neovim plugin inspired by org-super-agenda for Emacs. Organize your org-mode agenda into smart, configurable groups β fast, keyboard-driven, and safe-by-design.
- One command:
:OrgSuperAgendaβ no buffers polluted - Fullscreen mode:
:OrgSuperAgenda fullscreenor:OrgSuperAgenda! - Centered float with configurable size & border
- Smart groups: Today, Tomorrow, Overdue, Upcoming, Important, Personal/Work, and anything you script
- Per-section sorting (with global fallback)
- Live filtering: exact (
of), fuzzy (oz), and advanced query (oq) - Bulk actions: mark multiple items (
m), then batch TODO state / reschedule / deadline - Quick TODO filters via perβstate keymaps (e.g.
ot,op,ow,od) - Inline editing for TODO, priority, scheduled & deadline
- Direct state setting via
sprefix (e.g.,st,sd) with colored menu fallback - Priority tools:
A,B,C,+,-,0 - Preview headline (
K), open (<CR>), refile (R) - Clock integration: clock in (
I), clock out (O), cancel (X), jump (gI) - Hide item (
x) / reset hidden (gX) with optional persistence - Undo last change (
u) within the agenda - Toggle duplicates across groups (
D) - Group folding:
<Tab>on headers, fold all (zM), unfold all (zR) - Custom views with pre-configured filters, groups, sort, and global keymaps (
Vpicker) - Switch view between
classicandcompact(ov) - Rightβaligned tags, customizable header format, and filename display
- Safety: refuses edits when a swapfile is present or buffer is modified elsewhere
- Sticky DONE: items marked DONE during the session remain visible until you close the window
:OrgSuperAgendaβ open the agenda (float):OrgSuperAgenda fullscreenor:OrgSuperAgenda!β open fullscreen:OrgSuperAgenda view <name>β open a specific custom view:OrgSuperAgenda viewsβ open the view picker
Convenient mappings:
vim.keymap.set('n', '<leader>oa', '<cmd>OrgSuperAgenda<cr>')
vim.keymap.set('n', '<leader>oA', '<cmd>OrgSuperAgenda!<cr>') -- fullscreenreturn {
'hamidi-dev/org-super-agenda.nvim',
dependencies = {
'nvim-orgmode/orgmode', -- required
{ 'lukas-reineke/headlines.nvim', config = true }, -- optional nicety
},
config = function()
require('org-super-agenda').setup({
-- Where to look for .org files
org_files = {},
org_directories = {}, -- recurse for *.org
exclude_files = {},
exclude_directories = {},
-- TODO states + their quick filter keymaps and highlighting
-- Optional: add `shortcut` field to override the default key (first letter)
todo_states = {
{ name='TODO', keymap='ot', color='#FF5555', strike_through=false, fields={'filename','todo','headline','priority','date','tags'} },
{ name='PROGRESS', keymap='op', color='#FFAA00', strike_through=false, fields={'filename','todo','headline','priority','date','tags'} },
{ name='WAITING', keymap='ow', color='#BD93F9', strike_through=false, fields={'filename','todo','headline','priority','date','tags'} },
{ name='DONE', keymap='od', color='#50FA7B', strike_through=true, fields={'filename','todo','headline','priority','date','tags'} },
},
-- Agenda keymaps (inline comments explain each)
keymaps = {
filter_reset = 'oa', -- reset all filters
toggle_other = 'oo', -- toggle catch-all "Other" section
filter = 'of', -- live filter (exact text)
filter_fuzzy = 'oz', -- live filter (fuzzy)
filter_query = 'oq', -- advanced query input
undo = 'u', -- undo last change
reschedule = 'cs', -- set/change SCHEDULED
set_deadline = 'cd', -- set/change DEADLINE
cycle_todo = 't', -- cycle TODO state
set_state = 's', -- set state directly (st, sd, etc.) or show menu
reload = 'r', -- refresh agenda
refile = 'R', -- refile via Telescope/org-telescope
hide_item = 'x', -- hide current item
preview = 'K', -- preview headline content
clock_in = 'I', -- clock in on current headline
clock_out = 'O', -- clock out active clock
clock_cancel = 'X', -- cancel active clock
clock_goto = 'gI', -- jump to active/recent clocked task
reset_hidden = 'gX', -- clear hidden list
fold_all = 'zM', -- collapse all groups
unfold_all = 'zR', -- expand all groups
toggle_duplicates = 'D', -- duplicate items may appear in multiple groups
cycle_view = 'ov', -- switch view (classic/compact)
bulk_mark = 'm', -- toggle mark on current item (β indicator)
bulk_unmark_all = 'M', -- clear all marks
bulk_reselect = 'gv', -- reselect last marks
bulk_action = 'B', -- run action on all marked items
open_view = 'V', -- open custom view picker
},
-- Window/appearance
window = {
width = 0.8,
height = 0.7,
border = 'rounded',
title = 'Org Super Agenda',
title_pos = 'center',
margin_left = 0,
margin_right = 0,
fullscreen_border = 'none', -- border style when using fullscreen
},
-- Group definitions (order matters; first match wins unless allow_duplicates=true)
groups = {
{ name = 'π
Today', matcher = function(i) return i.scheduled and i.scheduled:is_today() end, sort={ by='scheduled_time', order='asc' } },
{ name = 'ποΈ Tomorrow', matcher = function(i) return i.scheduled and i.scheduled:days_from_today() == 1 end, sort={ by='scheduled_time', order='asc' } },
{ name = 'β οΈ Deadlines', matcher = function(i) return i.deadline and i.todo_state ~= 'DONE' and not i:has_tag('personal') end, sort={ by='deadline', order='asc' } },
{ name = 'β Important', matcher = function(i) return i.priority == 'A' and (i.deadline or i.scheduled) end, sort={ by='date_nearest', order='asc' } },
{ name = 'β³ Overdue', matcher = function(i) return i.todo_state ~= 'DONE' and ((i.deadline and i.deadline:is_past()) or (i.scheduled and i.scheduled:is_past())) end, sort={ by='date_nearest', order='asc' } },
{ name = 'π Personal', matcher = function(i) return i:has_tag('personal') end },
{ name = 'πΌ Work', matcher = function(i) return i:has_tag('work') end },
{ name = 'π Upcoming', matcher = function(i)
local days = require('org-super-agenda.config').get().upcoming_days or 10
local d1 = i.deadline and i.deadline:days_from_today()
local d2 = i.scheduled and i.scheduled:days_from_today()
return (d1 and d1 >= 0 and d1 <= days) or (d2 and d2 >= 0 and d2 <= days)
end,
sort={ by='date_nearest', order='asc' }
},
},
-- Defaults & behavior
upcoming_days = 10,
hide_empty_groups = true, -- drop blank sections
keep_order = false, -- keep original org order (rarely useful)
allow_duplicates = false, -- if true, an item can live in multiple groups
group_format = '* %s', -- group header format
other_group_name = 'Other',
show_other_group = false, -- show catch-all section
show_tags = true, -- draw tags on the right
show_filename = true, -- include [filename]
heading_max_length = 70,
persist_hidden = false, -- keep hidden items across reopen
view_mode = 'classic', -- 'classic' | 'compact'
classic = { heading_order={'filename','todo','priority','headline'}, short_date_labels=false, inline_dates=true },
compact = { filename_min_width=10, label_min_width=12 },
-- Global fallback sort for groups that omit `sort`
group_sort = { by='date_nearest', order='asc' },
-- Popup mode: auto-detected when launched via the tmux script (ORG_SUPER_AGENDA_POPUP=1).
-- Override only if you use a different popup mechanism.
popup_mode = {
enabled = vim.env.ORG_SUPER_AGENDA_POPUP == '1',
hide_command = 'tmux detach-client',
},
debug = false,
-- Custom views: reusable named views with pre-configured filters
custom_views = {
-- work_week = {
-- name = "Work This Week",
-- keymap = "<leader>ow",
-- filter = "tag:work sched>=0 sched<7 -is:done",
-- },
},
})
end,
}Supported sort keys:
date_nearest(min of days-to-deadline/scheduled; missing β β)deadline,scheduled(days from today)deadline_time,scheduled_time(sorts by exact date+time when available)priority(A > B > C)todo(order of yourtodo_states, or customsort.todo_order)filename,headline
Tieβbreakers: priority desc, then filename asc, then headline asc.
Examples:
-- Global default
group_sort = { by = 'date_nearest', order = 'asc' }
-- Per group override (by date only)
{
name = 'β οΈ Deadlines',
matcher = function(i) return i.deadline and i.todo_state ~= 'DONE' end,
sort = { by = 'deadline', order = 'asc' }
}
-- Sort by exact time (includes hour:minute when present)
{
name = 'π
Today',
matcher = function(i) return i.scheduled and i.scheduled:is_today() end,
sort = { by = 'scheduled_time', order = 'asc' }
}
-- Sort by custom TODO order (per group)
{
name = 'π
Today',
matcher = function(i) return i.scheduled and i.scheduled:is_today() end,
sort = {
by = 'todo',
order = 'asc',
todo_order = { 'PROGRESS', 'NEXT', 'TODO', 'WAIT', 'CANX', 'DONE' },
}
}Note: Items with time-of-day (e.g., SCHEDULED: <2026-01-31 09:00>) are displayed with their times in classic view. Use scheduled_time or deadline_time sort keys to order by exact timestamp.
- Exact text:
ofβ type to filter; ESC/Enter to stop - Fuzzy text:
ozβ same but fuzzy - Reset:
oa - Perβstate:
ot/op/ow/od(customizable)
Tokens (space-separated):
tag:work|home # must have any of these tags
-file:archive # (negated) exclude if filename contains substring
file:notes # filename contains substring
todo:TODO|WAITING # limit to TODO states
prio>A # A is highest (A > B > C); also: prio:A
due<3 # deadline in < 3 days (relative)
sched>=0 # scheduled today or later
before:2025-12-31 # deadline before absolute date
after:2025-08-01 # scheduled after absolute date
is:overdue # scheduled/deadline in the past
is:done # DONE items only
has:todo # items with TODO state (excludes plain events)
-has:todo # items without TODO state (events only)
words here # free-text includes (AND)
-noise # free-text excludes
Examples:
# A-priority work due soon
prio:A tag:work due<5
# Upcoming personal, not waiting
tag:personal -WAITING sched>=0
# Only tasks (no events) with work tag
has:todo tag:work
# Only events (no TODO state) tagged personal
-has:todo tag:personal
Mark multiple items and apply an operation to all of them at once.
| Key | Action |
|---|---|
m |
Toggle mark on current item (shows β indicator) |
M |
Clear all marks |
gv |
Reselect last marks (like vim gv for visual) |
B |
Open bulk action menu for all marked items |
After pressing B, choose an action:
| Key | Bulk Action |
|---|---|
s |
Set TODO state (prompts for state) |
r |
Reschedule (datepicker once, applied to all) |
d |
Set deadline (datepicker once, applied to all) |
Example workflow:
mon several overdue tasksBβrβ pick new date β all rescheduled at once
All bulk operations are undoable individually via u.
Define reusable, named agenda views with pre-configured filters, groups, and sort. Switch between them instantly via keymaps, commands, or an in-agenda picker.
custom_views = {
work_week = {
name = "Work This Week",
keymap = "<leader>ow", -- global keymap to open this view
filter = "tag:work sched>=0 sched<7 -is:done", -- query string (same syntax as oq)
groups = { ... }, -- optional: override default groups
sort = { by = 'deadline', order = 'asc' }, -- optional: view-level sort fallback
title = "π’ Work Week", -- optional: custom window title
view_mode = "compact", -- optional: override layout
},
personal = {
name = "Personal",
keymap = "<leader>op",
filter = "tag:personal -is:done",
},
overdue = {
name = "Overdue",
filter = "is:overdue -is:done",
},
}All fields except the key itself are optional. A view with just a filter is perfectly valid.
- Global keymaps: defined via
keymapfield, available everywhere in normal mode - Commands:
:OrgSuperAgenda view work_weekβ open a specific view directly:OrgSuperAgenda viewsβ open the view picker
- In-agenda picker: press
Vinside the agenda to open the picker- Numbered selection (press
1,2, etc.) - Shows active view indicator (
β) - When a view is active, first option is "Default Agenda" to go back
- Numbered selection (press
- Window title updates to reflect the active view name
- Footer badge shows the active view name and its filter string
When a custom view is active:
- Per-group
sort(highest priority) - View-level
sort(from custom view config) - Global
group_sort(lowest priority fallback)
:OrgSuperAgenda supports tab completion for all view names:
:OrgSuperAgenda view <Tab>
- classic: headline prefix
[filename] TODO [#A] Title β¦with inline or separate date line - compact: fixed columns for filename and date label (
Sched. in 3 d.:), right-aligned tags
Switch with ov (or set view_mode in config).
Iclocks in the task under cursorOclocks out the currently active clockXcancels the currently active clockgIjumps to the active clock task- Active clock entries are marked with
β±in agenda rows - Active clock status is shown at the bottom of the agenda
Note: default reset_hidden key changed from X to gX because X is now used for clock cancel.
- Group headers include item counts:
* π Today (12 items) - Press
<Tab>on a group header to collapse/expand that group - Press
<Tab>on an item line to preview (same behavior asK) - Collapse all groups with
zM, expand all withzR
Set TODO states directly without cycling:
| Keys | Action |
|---|---|
st |
Set TODO |
sp |
Set PROGRESS |
sw |
Set WAITING |
sd |
Set DONE |
s0 |
Clear state |
s (wait) |
Show colored menu |
The prefix key (s) is configurable via keymaps.set_state. Shortcut keys are derived from the first letter of each state name. If you have conflicting state names (e.g., TODO and TRACKING), add an explicit shortcut field:
todo_states = {
{ name='TODO', shortcut='t', ... },
{ name='TRACKING', shortcut='r', ... }, -- explicit shortcut to avoid conflict
}When you press s and wait, a colored menu appears showing all available states in their configured colors.
- Hit
Ron an item to open a picker - Toggle between file and headline targets with
<C-Space> - Segment is moved and auto-reloaded in any open buffers
Run OrgSuperAgenda in a dedicated tmux session for instant access. Pressing q hides the popup (nvim keeps running in the background) instead of closing the buffer.
No config needed β popup mode is auto-detected when launched via the script below.
tmux script (~/.local/bin/tmux-org-agenda.sh or anywhere on your $PATH):
#!/usr/bin/env bash
SESSION="_org_agenda"
if ! tmux has-session -t "=$SESSION" 2>/dev/null; then
tmux new-session -d -s "$SESSION" -e "ORG_SUPER_AGENDA_POPUP=1" \
"nvim +'autocmd User VeryLazy ++once OrgSuperAgenda fullscreen'"
tmux set-option -t "$SESSION" status off
fi
tmux display-popup -w 100% -h 99% -E \
"tmux attach-session -t '=$SESSION'"tmux binding:
bind -n 'M-a' run-shell -b "$HOME/.local/bin/tmux-org-agenda.sh"That's it. The ORG_SUPER_AGENDA_POPUP=1 env var tells the plugin it's running in a popup, so q runs tmux detach-client to hide the popup cleanly.
Override if you use a different mechanism:
popup_mode = {
enabled = true, -- or use your own detection logic
hide_command = "my-hide-cmd", -- anything that dismisses your popup
}- Will refuse to edit if the buffer is modified in Neovim or a swapfile is detected (likely open elsewhere)
- Sticky DONE items remain visible in a dedicated section until the window is closed
- βOtherβ section never collects DONE items by design
- Hidden items respect
persist_hidden
PRs, issues, discussions welcome.
MIT License

