Do-It.nvim is a modular task management framework for Neovim, providing a clean, distraction-free interface to manage your tasks and notes directly within your editor.
Do-It.nvim began as a way to track tasks and keep simple markdown notes per project. As a Principal Engineer with many disparate things to keep track of, I wanted a simple way to do that without leaving my editor. I've tried many task managers, but they all seemed too complex - I just needed to know what I needed to do, without bells and whistles.
This project is a fork of Dooing by atiladefreitas, expanded with a modular framework and additional plugins such ash project notes, and calendar..
- Modular Framework - Use only the components you need
- Task Management - Create, organize, and track to-dos
- Nested To-dos - Break a task into subtasks with
a; a subtree stays together in nvim, tmux, and the MCP server - Project Notes - Maintain project-specific documentation
- Tags & Filtering - Categorize tasks with #tags
- Due Dates - Set deadlines with calendar integration
- Priorities - Assign and sort by importance
- Time Estimation - Track estimated completion time
- Import/Export - Backup or share your tasks, or export pending items to markdown with
E - Lualine Integration - Show active tasks in your statusline
- Tmux Add-on - Optional tmux integration with shared data (see below)
- Session-Linked Lists - Each tmux session holds its own active list;
dailystays one popup away
return {
"bearded-giant/do-it.nvim",
config = function()
require("doit").setup()
end,
}- Open todos:
:Doitor<leader>td - Add a todo: Press
nin the todo window - Toggle status: Press
xon a todo - Open notes:
:DoItNotesor<leader>dn
- Neovim
>= 0.10.0 - Lazy.nvim as your plugin manager
return {
"bearded-giant/do-it.nvim",
config = function()
require("doit").setup({
modules = {
todos = { enabled = true },
notes = { enabled = true }
}
})
end,
}Use individual modules without the framework:
-- Just todos
return {
"bearded-giant/do-it.nvim",
config = function()
require("doit_todos").setup()
end,
}
-- Just notes
return {
"bearded-giant/do-it.nvim",
config = function()
require("doit_notes").setup()
end,
}
-- Calendar (v2.0) - macOS only, requires icalbuddy
return {
"bearded-giant/do-it.nvim",
config = function()
require("doit").setup({
modules = {
calendar = { enabled = true }
}
})
end,
}See Complete Keybindings Reference for all commands and keyboard shortcuts.
Framework Commands:
:DoItDashboard- Open main DoIt dashboard:DoItPlugins list- List installed modules:DoItPlugins info <module>- Show module details
Module Commands:
Todos:
:DoIt- Open main todo window:DoItList- Quick todo list (floating):DoItLists- Manage multiple todo lists
Notes:
:DoItNotes- Open notes window:DoItNotesNew- Create new project note:DoItNotesSearch- Search across notes
Calendar (macOS only):
:DoItCalendar- Toggle calendar window:DoItCalendarDay- Open in day view:DoItCalendar3Day- Open in 3-day view:DoItCalendarWeek- Open in week view
Basic Keys (in todo window):
<CR>- Add new todoa- Add a child todo under the current onec- Toggle status (pending β in-progress β done)d- Delete todo?- Show full helpL- List managerq- Close window
The keybindings documentation is auto-generated from a central source to ensure consistency. Run make update-help to regenerate after changes.
The todos module provides task management functionality: Full Documentation
- Create, edit, and organize to-dos
- Nested subtasks, rendered indented under their parent
- Tag-based filtering and organization
- Priority-based sorting
- Due dates with calendar integration
- Time estimation tracking
- Import/export capabilities
The notes module provides project-specific notes: Full Documentation | Work in Progress
- Project-specific notes based on Git repository
- Global notes mode for system-wide documentation
- Markdown syntax highlighting
- Floating window interface
- Automatic saving
The calendar module provides macOS calendar integration: Full Documentation | Module Docs
- icalbuddy Integration: View events from macOS Calendar app
- Multiple Views: Day, 3-day, and week views
- Smart Parsing: Handles 100% of icalbuddy event formats
- UTF-8 Support: Correctly displays special characters
- Auto-refresh: Updates when switching views
- All Calendar Sources: iCloud, Google, Exchange support
Note: Requires macOS with icalbuddy installed (brew install icalbuddy)
- User Guide: See
:help doitin Neovim - Framework Documentation:
:help doit-framework - Developer Documentation: docs/ directory
- API Reference:
:help doit-api
Do-It.nvim uses a nested configuration structure that separates core framework settings from module-specific options. This makes it easier to navigate and customize.
require("doit").setup({
-- Core framework settings
development_mode = false,
quick_keys = true,
timestamp = { enabled = true },
lualine = { enabled = true, max_length = 30 },
project = {
enabled = true,
detection = { use_git = true, fallback_to_cwd = true },
},
-- Module configurations
modules = {
todos = {
enabled = true,
ui = {
window = {
width = 55,
height = 20,
border = "rounded",
},
-- More UI settings...
},
formatting = {
pending = { icon = "β" },
in_progress = { icon = "β" },
done = { icon = "β" },
},
priorities = {
{ name = "critical", weight = 16 },
{ name = "urgent", weight = 8 },
{ name = "important", weight = 4 },
},
-- More todos settings...
},
notes = {
enabled = true,
ui = {
window = {
-- Absolute sizing
width = 80, -- columns
height = 30, -- lines
-- Or relative sizing
relative_width = 0.6, -- 60% of screen
relative_height = 0.6, -- 60% of screen
use_relative = true, -- toggle mode
position = "center", -- or top-left, bottom-right, etc.
},
},
storage = {
path = vim.fn.stdpath("data") .. "/doit/notes",
mode = "project", -- or "global"
},
-- More notes settings...
},
},
})- Core settings (top level): Framework-wide configurations like
development_mode,lualine, andprojectdetection - Module settings (
modules.todosandmodules.notes): Specific to each module, organized into logical groups likeui,storage,formatting, etc. - Backward compatibility: The plugin maintains support for the legacy flat configuration structure
For a complete list of all configuration options with detailed descriptions, see lua/doit/config.lua.
Also see :help doit-configuration in Neovim for interactive documentation.
Do-It.nvim provides several lualine components to display todo information in your statusline:
require("lualine").setup({
sections = {
lualine_c = {
-- Show current list and todo count
{ require("doit").lualine.current_list },
-- Show todo statistics (done/in-progress/pending)
{ require("doit").lualine.todo_stats },
-- Show active (in-progress) todo
{ require("doit").lualine.active_todo }
}
}
})Available components:
current_list- Shows current list name and todo count:π work (5)todo_stats- Shows todo statistics:β3 β1 β2(done/in-progress/pending)active_todo- Shows the current in-progress todo (if any)
Note: The tmux integration is a separate, optional add-on that shares data with the Neovim plugin. You can use doit.nvim without tmux, or use both together for a seamless experience across your terminal workflow.
The tmux add-on provides todo management directly from tmux, with status bar integration and an interactive fzf-based manager. Changes sync automatically between Neovim and tmux.
Add to your tmux.conf:
set -g @plugin 'bearded-giant/do-it.nvim'Then install with prefix + I.
With prefix key (prefix + d + ...):
| Key | Action |
|---|---|
t |
Quick todo popup |
i |
Interactive manager (fzf) |
x |
Toggle current todo done |
n |
New todo |
N |
Start next pending todo |
l |
Switch lists (links the current session) |
L |
List manager |
d |
Daily list popup (pinned to daily) |
Direct shortcuts (Alt+Shift):
| Key | Action |
|---|---|
Alt+Shift+T |
Quick todo popup |
Alt+Shift+I |
Interactive manager |
Alt+Shift+X |
Toggle todo done |
Alt+Shift+N |
New todo |
Alt+Shift+L |
Switch lists |
Alt+Shift+D |
Daily list popup |
In interactive manager:
| Key | Action |
|---|---|
Enter |
Open item/note in nvim (view, edit its note) |
s |
Start (in-progress) |
x |
Complete (done) |
X |
Revert to pending |
n / p |
New todo / paste new from clipboard |
e |
Edit todo text |
P |
Set priority |
N |
Edit note (description) |
g |
List notes modal |
K/J |
Reorder (a parent moves with its subtree) |
[/] |
Jump section down/up |
m |
Move todo to another list |
l / L |
Switch list / list manager |
t / c |
Filter by #tag / clear filter |
h |
Set or clear due date |
/ |
Search |
d / D |
Delete todo / clear all completed |
u |
Undo last delete |
E |
Export pending to markdown |
O |
Send to Obsidian daily note |
y / Y |
Copy todo text / list name |
i |
Copy item id (the MCP resolves it across lists) |
? |
Show help |
q / Esc |
Back to lists / quit |
In create/edit mode: type text, Enter twice to save, Esc to cancel.
# Change the prefix key (default: d)
set -g @doit-key "t"
# Disable Alt+Shift shortcuts
set -g @doit-alt-bindings "off"
# Use $EDITOR (nvim, vim) for create/edit instead of inline input
set -g @doit-use-editor "true"
# Interactive manager and list switcher/manager popup size (default: 80% x 80%)
# Accepts absolute cells ("120") or terminal-relative percentages ("80%")
set -g @doit-interactive-popup-w "80%"
set -g @doit-interactive-popup-h "80%"
# Where `E` writes markdown exports (default: the popup's working directory)
set -g @doit-export-dir "~/notes/exports"
# Per-project lists: a pane inside a git repo uses a list named after the
# repo directory, created on first use (default: off)
set -g @doit-project-lists "on"Each tmux session can hold its own active list. The link map lives in session.json (sessions: {"<tmux session>": "<list>"}), so links survive tmux restarts, and every surface resolves the active list through the same chain:
DOIT_ACTIVE_LISTenvironment override- The current tmux session's link
- Per-project derivation (
@doit-project-lists, opt-in) daily
An unlinked tmux session always lands on daily, never on whichever list was switched to last elsewhere. Outside tmux the session step is skipped and the global .active_list pointer takes the place of daily (falling back to daily when unset).
In the list switcher (prefix + d + l) and list manager (prefix + d + L), Enter links the selected list to the current session (and refreshes the global pointer), g sets the global pointer only, and u unlinks the current session (in the switcher these are ctrl-g and ctrl-u, so plain letters keep filtering as you type). Rows show which sessions link each list, with dead sessions dimmed, and daily stays pinned to the top with its pending count. A list holding open items past their due date is badged !N overdue (the preview repeats the count), and the nvim list manager badges the row !M name (N todos) in red. prefix + d + d (or Alt+Shift+D) opens the interactive manager pinned to daily from any session without touching any link.
If using bearded-giant-tmux theme, add todo to your status modules:
set -g @bearded_giant_status_modules_right "meetings todo"For other themes, use the status script directly:
set -g status-right "#(~/.tmux/plugins/do-it.nvim/tmux/scripts/todo-status.sh '#{session_name}')"Passing #{session_name} lets the status segment resolve the session's linked list; status commands run without a client context, so the script cannot discover the session on its own.
Do-it.nvim includes an MCP server that exposes your todo lists to Claude Code and other MCP-compatible tools. This lets you read, create, update, and search todos without leaving your AI coding session.
cd mcp && npm installAdd to your Claude Code settings.json under mcpServers:
{
"doit": {
"command": "node",
"args": ["/path/to/do-it.nvim/mcp/server.js"]
}
}If you configured a custom storage path in your do-it.nvim setup (via modules.todos.storage.save_path), set DOIT_DATA_DIR to match:
{
"doit": {
"command": "node",
"args": ["/path/to/do-it.nvim/mcp/server.js"],
"env": {
"DOIT_DATA_DIR": "/your/custom/data/path"
}
}
}Todo items:
| Tool | Description |
|---|---|
list_todos |
List items from a list, filtered by status, priority, #tag and/or due date |
get_todo |
Fetch one item by id from whichever list holds it, no list name needed |
search_todos |
Search every list for items matching a text pattern |
list_tags |
List the inline #tags on a list, with usage counts |
add_todo |
Create a todo, composing the text from type, deps, and an auto-assigned rank. Takes due and parent (nest under another item) |
update_todo |
Edit text, description, status, priority, due date, order, or parent. Needs an id |
start_todo |
Mark an item in progress. Fuzzy text query; only one item runs at a time |
complete_todo |
Mark an item done. Fuzzy text query; with no args it looks at in-progress items |
revert_todo |
Send an item back to pending |
delete_todo |
Delete an item, keeping it in _metadata.deleted_todos for undo |
clear_done |
Delete every completed item in a list, keeping the last 10 for undo |
dedupe_todos |
Remove items whose text matches after normalization. Dry run by default |
move_todo |
Move an item to another list |
Ids are global. Copy one from the tmux item view (i) or the nvim item view
(y), paste it at the model, and get_todo, start_todo, complete_todo,
revert_todo, update_todo, add_note, delete_todo and move_todo all
find the list that holds it.
Notes. Two different things share the word: add_note writes the description on a todo item, while the *_note tools manage standalone list-scoped notes.
| Tool | Description |
|---|---|
add_note |
Write a note onto a todo item. Appends by default; mode='replace' overwrites |
list_notes |
List the standalone notes on a list, returning titles and ids |
get_note |
Read one list note in full |
create_note |
Create a standalone list note, not attached to any todo |
update_note |
Update a list note's title and/or body. Replaces by default; mode='append' adds |
delete_note |
Delete a list note, keeping the last 10 for undo |
Lists:
| Tool | Description |
|---|---|
list_lists |
Show all lists, which one is active, and which tmux sessions link each |
switch_list |
Change the active list. Inside tmux it links the current session and updates the global pointer; scope narrows the write to session or global |
create_list |
Create a new empty list. Inside tmux it auto-links the new list when the session has none, and asks before relinking otherwise |
rename_list |
Rename a list. Switch away from it first |
delete_list |
Delete a list. Switch away from it first |
Items the MCP server creates get a scannable title instead of a bare sentence:
claude: [gate] 41. pre-store gate check (dep on #40)
You pass type (a short free-form tag like gate, decision, comms) and deps (the rank numbers of blocking items) as params β the server composes the final text. The rank number is assigned automatically from the highest one already in the list, so priority stays the bucket and the number tells you the order inside it. update_todo keeps an item's rank when you rewrite its text, and takes type / deps to retype or re-point it later. The claude: burn-down marker carries over too, so pass claude: false to drop it once an item is no longer model work. Composing is idempotent, so prefixes never stack.
Duplicate detection strips that whole wrapper before comparing, so an item you typed in Neovim as buy milk matches the one MCP stored as claude: [chore] 3. buy milk (dep on #1). Comparison ignores the claude: marker, the [type] tag, the rank prefix, the (dep on #N) suffix, case, and whitespace. A [[note link]] prefix is not a type tag and survives, and a number without a following space (3.buy milk, 1.5x throughput) is not a rank. When duplicates are collapsed the copy carrying real notes wins, and the rest go to the undo stack β <leader>D in Neovim confirms first, and dedupe_todos is a dry run unless you pass dry_run:false.
| Variable | Default | Description |
|---|---|---|
DOIT_DATA_DIR |
~/.local/share/nvim/doit |
Data directory path |
DOIT_ACTIVE_LIST |
(unset) | Override active list name. Wins over session links and per-project derivation |
The MCP server reads and writes the same JSON files used by the Neovim plugin and tmux add-on. Changes sync automatically.
See the developer documentation for:
- Development setup and debugging
- Framework architecture
- Module development guide
- Implementation notes
- Reorder To-dos
- Active To-do to Top
- Project Notes
- Modular Framework
- To-do Categories (with filtering)
- Cross-module Integration (todo-note linking)
- Module Registry (internal modules)
- Named (Multiple) To-do Lists
- Categories View Window
- External/Custom Module Loading
Do-It.nvim started as fork of Dooing by atiladefreitas. Special thanks to him for creating the original plugin.
The project notes feature was inspired by maple.nvim's project notes functionality.
The framework architecture was inspired by other modular Neovim plugins like mini.nvim and snack.nvim.