Minimal todo CLI for the terminal. Pure Python, zero dependencies.
Project description
tsk
A small task manager for the terminal.
$ tsk add water the plants @home +garden due:tomorrow every:weekly
added 1: water the plants
$ tsk add fix the login bug @work +urgent due:yesterday p:h
added 2: fix the login bug
$ tsk
2 H yesterday fix the login bug @work +urgent
1 tomorrow water the plants @home +garden every:weekly
No daemon, no config file, no dependencies. Your tasks live in one JSON file you can read.
Install
Needs Python 3.11 or newer.
uv tool install tsk-todo
The command is tsk. The package is tsk-todo, because
tsk on PyPI belongs to something else.
To try it without installing anything:
uvx --from tsk-todo tsk
pip install tsk-todo works too, and from a clone it is uv tool install .
The idea
You type what you mean and tsk works out which parts are metadata:
tsk add renew the passport @admin +boring due:eom p:h
@admin is the project, +boring is a tag, due:eom is the end of this month, p:h is high
priority. Everything left over is the task itself.
Only due:, p: and every: are read as attributes, so ordinary text stays ordinary:
$ tsk add email bob@example.com about the invoice
$ tsk add fix the -v flag
$ tsk add ticket: replace the boiler
None of those pick up a project, a tag or an attribute. If it is not a form tsk knows, it is
part of the description.
Commands
tsk list pending tasks
tsk add <text> [attrs] add a task
tsk done <id>... complete tasks
tsk rm <id>... delete tasks
tsk edit <id> <attrs> change a task
tsk note <id> <text> attach a note
tsk show <id> show one task in full
tsk list [filters] list tasks
tsk 3 on its own is shorthand for tsk show 3.
done and rm take several ids at once: tsk done 2 5 9.
Attributes
Use these when adding or editing.
@project set the project
+tag add a tag
-tag remove a tag, when editing
due:<when> set a due date
p:h|m|l set priority, high medium or low
every:<how> reschedule on completion
Priority also accepts the full word, so p:high and p:h are the same.
Editing changes only what you name. Everything else is left alone.
$ tsk edit 3 due:friday +urgent
updated 3: read the book
To clear something, give it no value:
$ tsk edit 3 due: p: every:
A bare @ clears the project. A leading - removes a tag.
Filters
tsk +garden tagged garden
tsk -work not tagged work
tsk @home in the home project
tsk overdue past due
tsk due:friday due on or before friday
tsk done completed tasks
tsk all everything
tsk plants search descriptions and notes
Filters combine, and list is optional:
$ tsk @home +garden overdue
$ tsk list @work due:eow
Bare words search. tsk list dentist finds anything with dentist in the description or in a note.
Dates
Anywhere a date is wanted:
today, tomorrow, yesterday
monday .. sunday, or mon .. sun
3d, 2w, 1m, 1y
eow, eom, eoy
2026-08-01
Weekday names mean the next one coming. On a Thursday, due:thursday is a week away, not today.
Month and year offsets land on a real date. 1m from the 31st of January is the 28th of February,
not the 3rd of March.
Due dates are plain calendar dates. due:friday means Friday wherever you happen to be, and it
does not drift when you cross a timezone.
Repeats
every:daily
every:weekdays
every:weekly
every:monthly
every:yearly
A repeating task needs a due date. When you complete it, the next one is created for you:
$ tsk done 1
done 1: water the plants
repeats as 5, due in 1 week
The next date is worked out from the old due date, then rolled forward until it is in the future. Finish a daily task five days late and the next one is due tomorrow, not last week. You never come back to a pile of overdue copies.
Notes do not carry over. The completed task keeps them.
Notes
$ tsk note 3 the ferns look worse than the rest
noted on 3: water the plants
Tasks with notes are marked with a * in the list. tsk show 3 prints them.
Ordering
Tasks sort by due date, then priority, then id. That is the whole rule. Nothing is scored or weighted behind your back, so the order is always one you can predict and explain.
Undated tasks sort last. They are not urgent, they are just there.
Your data
One JSON file, at ~/.local/share/tsk/tasks.json unless you say otherwise:
{
"version": 1,
"issued": 7,
"tasks": [
{
"id": 2,
"description": "fix the login bug",
"status": "pending",
"created": "2026-07-16T14:49:48+00:00",
"project": "work",
"tags": ["urgent"],
"due": "2026-07-15",
"priority": "high"
}
]
}
Set TSK_DATA to put it somewhere else, or XDG_DATA_HOME to move the lot. Keep it in a git repo
if you want history. Tags are written in a fixed order and absent fields are left out, so diffs
stay small.
Writes are atomic. The file is written beside itself and then moved into place, so an interrupted write cannot leave you with half a file. Existing permissions are preserved.
Ids are never reused. Once id 3 has been handed out, no later task gets it, even if you delete task 3 the same day. The id you read a minute ago is still the task you thought it was.
Colour
On when writing to a terminal, off when piped. Set NO_COLOR to turn it off entirely.
Overdue is red, due today is yellow, projects are cyan, tags are magenta.
What it does not do
There is no urgency score, no dependency graph, no user defined attributes, no hooks, no custom report definitions and no sync. Each of those either failed the question "would I miss this" or costs far more than it returns for a tool this size.
If you want a task on the calendar, due: is where it goes. If you want to find it later, tag it.
That is most of task management.
Development
uv sync
uv run pytest
uv run ruff check .
uv run ruff format .
uv run mypy
The suite runs in well under a second. It is worth keeping it that way.
Licence
MIT
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 tsk_todo-0.1.1.tar.gz.
File metadata
- Download URL: tsk_todo-0.1.1.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3ac4afc0e1695ea453d9ccec156bc0aa9b2a0538791d8b299ab058813a17776
|
|
| MD5 |
73f489a263701f86068b3f35e88c0a3a
|
|
| BLAKE2b-256 |
00372f5a88b25972676eaadd702615cdfccfe5525415ed28b121b4352d5e76ca
|
File details
Details for the file tsk_todo-0.1.1-py3-none-any.whl.
File metadata
- Download URL: tsk_todo-0.1.1-py3-none-any.whl
- Upload date:
- Size: 15.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98c77e3aa70d3c72410fdd7a5f76887a33cb73e6cb532b1fa4e90dc29f09e367
|
|
| MD5 |
2d966355651c511d70a724e52690ec93
|
|
| BLAKE2b-256 |
de7655dc02b8882e22920f533f36527f58ce1c2a74675c00fa4e0daada2c2b5f
|