CLI-first todo manager with local SQLite storage
Project description
mb-todo
CLI-first todo manager. Data stored locally in SQLite. All GUIs (Raycast extension, etc.) interact through CLI commands with --json flag — CLI is the single source of truth.
Data Model
Project
| Field | Type | Description |
|---|---|---|
| name | TEXT NOT NULL PRIMARY KEY | Project name |
Todo
| Field | Type | Nullable | Description |
|---|---|---|---|
| id | INTEGER PRIMARY KEY AUTOINCREMENT | NO | Short numeric ID for CLI ergonomics |
| title | TEXT NOT NULL | NO | Todo title |
| body | TEXT | YES | Extended description |
| closed | INTEGER NOT NULL DEFAULT 0 CHECK (closed IN (0, 1)) | NO | 0 = open, 1 = closed |
| priority | TEXT NOT NULL DEFAULT 'medium' CHECK (priority IN ('low','medium','high')) | NO | Priority level |
| project | TEXT REFERENCES projects(name) ON UPDATE CASCADE ON DELETE RESTRICT | YES | Project name (direct FK) |
| tags | TEXT NOT NULL DEFAULT '[]' | NO | JSON array of tag strings |
| created_at | INTEGER NOT NULL | NO | Unix timestamp |
| closed_at | INTEGER | YES | Unix timestamp, set on close |
| updated_at | INTEGER NOT NULL | NO | Unix timestamp, set to created_at on insert |
Indices
CREATE INDEX idx_todos_closed ON todos(closed);
CREATE INDEX idx_todos_project ON todos(project) WHERE project IS NOT NULL;
Design Decisions
- Integer IDs — CLI ergonomics (
mb-todo close 42) - Projects as name-only table — a project is just a label picked from a list; no metadata needed
- Text FK for project — eliminates JOINs, project name stored directly in todos
- RESTRICT on project delete — prevents orphaning; user must reassign todos first
- CASCADE on project rename — transparent propagation
- Tags as JSON array — simpler schema, queryable via
json_each(), no junction table needed - updated_at NOT NULL — set to
created_aton insert, refreshed on update; "never modified" =updated_at == created_at - closed_at nullable — set when closing, enables "done today/this week" queries
- Priority defaults to
medium - Timestamps as unix seconds
- SQLite STRICT mode
- No due_date — deferred; can add via migration later if needed
CLI
Global Flags
| Flag | Short | Description |
|---|---|---|
--json |
JSON output | |
--data-dir PATH |
Data directory (env: MB_TODO_DATA_DIR, default: ~/.local/mb-todo/) |
|
--version |
Print version and exit |
Todo Commands
mb-todo add <title>
Create a new todo.
| Option | Short | Type | Description |
|---|---|---|---|
--body |
TEXT | Extended description | |
--priority |
-P |
low|medium|high | Priority level (default: medium) |
--project |
-p |
TEXT | Assign to project (partial name ok, comma-separated for multiple — creates one todo per project) |
--tag |
-t |
TEXT (multiple) | Add tags (repeatable) |
mb-todo list
List todos. By default shows only open todos, sorted by updated_at (newest first).
| Option | Short | Type | Description |
|---|---|---|---|
--closed |
FLAG | Show only closed todos | |
--all |
-a |
FLAG | Show all todos (open + closed) |
--project |
-p |
TEXT | Filter by project (partial name ok) |
--priority |
-P |
low|medium|high | Filter by priority |
--tag |
-t |
TEXT | Filter by tag |
--sort |
-s |
created|priority|updated | Sort order (default: updated) |
--limit |
-n |
INT | Max number of results |
mb-todo show <id>
Show a single todo with all fields.
mb-todo edit <id>
Edit a todo. At least one option is required.
| Option | Short | Type | Description |
|---|---|---|---|
--title |
TEXT | New title | |
--body |
TEXT | New body | |
--priority |
-P |
low|medium|high | New priority |
--project |
-p |
TEXT | New project (partial name ok, empty to unset) |
--tag |
-t |
TEXT (multiple) | Replace all tags (repeatable) |
--add-tag |
TEXT (multiple) | Add tags (repeatable) | |
--remove-tag |
TEXT (multiple) | Remove tags (repeatable) |
Errors: NO_CHANGES if no options provided, TAG_CONFLICT if --tag used with --add-tag/--remove-tag.
mb-todo close <id>...
Close one or more todos. Sets closed = 1 and closed_at to current timestamp. Multiple IDs are processed best-effort.
mb-todo reopen <id>...
Reopen one or more closed todos. Sets closed = 0 and closed_at to null. Multiple IDs are processed best-effort.
mb-todo delete <id>...
Permanently delete one or more todos. Asks for confirmation before deleting (interactive mode only). Multiple IDs are processed best-effort.
| Option | Short | Type | Description |
|---|---|---|---|
--yes |
-y |
FLAG | Skip confirmation prompt |
Project Commands
Subgroup: mb-todo project <command>
mb-todo project list
List all projects.
mb-todo project add <name>
Create a new project. Error: PROJECT_EXISTS if name is taken.
mb-todo project rename <old> <new>
Rename a project. All todos referencing the old name are updated automatically (CASCADE). Error: PROJECT_EXISTS if new name is taken.
mb-todo project delete <name>
Delete a project. Requires exact project name. Error: PROJECT_HAS_TODOS if any todos reference the project (unless --with-todos).
| Option | Short | Type | Description |
|---|---|---|---|
--yes |
-y |
FLAG | Skip confirmation prompt |
--with-todos |
FLAG | Also delete all todos assigned to this project |
JSON Envelope
All --json output uses a consistent envelope (matches mb-pomodoro):
{"ok": true, "data": {...}}
{"ok": false, "error": "CODE", "message": "..."}
Error Codes
| Code | Description |
|---|---|
TODO_NOT_FOUND |
Todo ID does not exist |
ALREADY_CLOSED |
Todo is already closed |
ALREADY_OPEN |
Todo is already open |
PROJECT_NOT_FOUND |
Project name does not exist |
AMBIGUOUS_PROJECT |
Partial project name matches multiple projects |
PROJECT_EXISTS |
Project name already taken |
PROJECT_HAS_TODOS |
Cannot delete project with assigned todos |
NO_CHANGES |
edit called with no options |
TAG_CONFLICT |
--tag used together with --add-tag/--remove-tag |
VALIDATION_ERROR |
General input validation failure |
Configuration
Optional TOML config file at {data_dir}/config.toml. If absent, all defaults apply.
TUI Columns
Control which optional columns appear in the TUI table:
[tui]
columns = ["status", "priority", "project", "tags", "age"]
- Always shown:
id,title - Optional (all shown by default):
status,priority,project,tags,age - Set
columns = []to show only ID and Title - Column display order is fixed
TUI Filters
Filter state is persisted automatically when changed in the TUI:
[tui.filters]
project = "work"
status = "open"
- project — last selected project filter (omitted = all projects)
- status —
open(default),closed, orall
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mb_todo-0.0.9.tar.gz.
File metadata
- Download URL: mb_todo-0.0.9.tar.gz
- Upload date:
- Size: 22.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa24f56eb9cefc24cd7802fc58d45186ce8117f87938d57823acc09f968201f3
|
|
| MD5 |
58e1d8f21a1f3929f6db2385c71b66d9
|
|
| BLAKE2b-256 |
b58b5672a036084bbfb93ce88fb312cb214e97afcc839c7ba8f8504aeae4efd7
|
File details
Details for the file mb_todo-0.0.9-py3-none-any.whl.
File metadata
- Download URL: mb_todo-0.0.9-py3-none-any.whl
- Upload date:
- Size: 33.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dcd146e3c2c86c6f642c89423248403e2ce20531dbaec8a07855b4b2f6b38fe3
|
|
| MD5 |
ab8d97e85401beeb09c517db19490e81
|
|
| BLAKE2b-256 |
c95eabc3ac079cb2828d1f200b2f13de8992be824a3e51ce46411f276fb46f4b
|