Move Your Ass.
I program, watch videos and game all day, but I don't take care of my house
and sometimes not even of myself. mya is a CLI that fixes that: it hooks into
zsh and locks me out of my terminal while a task is overdue.
mya init zsh prints a hook that runs mya task check before every prompt
(precmd). As long as a task is overdue (exit code 3), the shell is replaced
by a restricted prompt:
overdue tasks
- vacuum
- do the dishes
terminal locked: only mya commands allowed (e.g. 'done bags'), Ctrl+D to quit
mya task>
At this prompt only task sub-commands are accepted, without the mya task
prefix. Mark the overdue tasks as done (done vacuum) to get your terminal
back.
You need uv. From the repository root:
uv tool install .In your .zshrc:
# mya: lock the terminal while a task is overdue
eval "$(mya init zsh)"$ mya task add "do the dishes" --every 1 --at 21:00
$ mya task done "do the dishes"
$ mya task list
NAME DUE AT REPEAT LAST DONE
do the dishes tomorrow 21:00 every 1d 2026-10-07Every command accepts -h for its full usage.
| Command | Description |
|---|---|
mya task add (--every N | --on DAYS) --at HH:MM [--no-freeze] NAME |
Create a task |
mya task edit [--every N | --on DAYS] [--at HH:MM] [--no-freeze] NAME |
Change the schedule of a task |
mya task remove NAME |
Delete a task |
mya task done NAME |
Mark a task as done |
mya task list |
List all tasks |
mya task today |
List the tasks due today |
mya task tomorrow |
List the tasks due tomorrow |
mya task check |
Exit with code 3 if a task is overdue (used by the shell hook) |
mya task freeze [--until YYYY-MM-DD] |
Suspend the lock |
mya task unfreeze |
Resume the lock |
| Command | Description |
|---|---|
mya timer new --for DURATION [--font FONT] [--frame FRAME] [--then-done NAME] |
Start a countdown |
| Command | Description |
|---|---|
mya init zsh |
Print the zsh hook |
mya --version |
Print the version |
--on schedules a task on specific days of the week, as a comma-separated
list: --on mon,thu. Available days: mon,tue,wed,thu,fri,sat,sun.
--every schedules a task every N days: --every 1, --every 15,
--every 30. N must be an integer.
The two flags are mutually exclusive. One of them is required in
mya task add; both are optional in mya task edit.
For vacations, or when you're simply not at home, mya task freeze stops mya
from locking the terminal. With --until YYYY-MM-DD (a date in the future) the
freeze ends by itself on that date. Without it, the freeze lasts until
mya task unfreeze.
A task created with --no-freeze ignores freezes: it still locks the terminal
while frozen. On mya task edit, --no-freeze is a toggle: it flips the
setting of the task, so passing it to a non-freezable task makes it freezable
again.
For tasks where I tell myself "I'll do this for 10 minutes":
mya timer new --for 25m --then-done "do the dishes"--then-done NAME marks the task as done once the timer is finished.
The look of the timer can be changed with --font and --frame:
- Fonts:
NUMBER,SEG,BLOCK,DOT - Frames:
SQUARE,ROUNDED,ASCII,SHADE
| Code | Where | When |
|---|---|---|
| 0 | everywhere | success |
| 1 | task add |
task already exists |
| 1 | task edit, task remove, task done |
task does not exist |
| 1 | task freeze |
already frozen, or --until is not in the future |
| 1 | task unfreeze |
not frozen |
| 1 | task today, task tomorrow |
no due task |
| 2 | mya without sub-command |
shows help |
| 2 | task edit |
--on and --every passed together |
| 2 | argparse (automatically) | wrong arguments, unknown option |
| 3 | task check |
at least one task is overdue |
| 4 | all commands | tasks.json or freeze.json unreadable or corrupted |
| 130 | timer |
interrupted by Ctrl-C |
All data lives in $XDG_DATA_HOME/mya/, or ~/.local/share/mya/ if
XDG_DATA_HOME is not set:
tasks.json: the tasksfreeze.json: the freeze state
A task is saved as follows:
{
"do the dishes": {
"name": "do the dishes",
"every": 1,
"on": null,
"at": "21:00",
"last_done": "2026-10-07",
"freezable": true
}
}Exactly one of every and on is set, the other is null.
- A task at
00:00never shows up intodayortomorrow: the window runs from00:01to23:59. The same goes for a task at23:59:30if--atis given with seconds. This is the current intended behavior and is covered by a test.
- A
mya financemodule to track my finances - A plugin to block YouTube and other websites
- Revisit the
today/tomorrowwindow (see above)
Commits follow Conventional Commits, which drive the version number:
| Commit | Effect on version |
|---|---|
feat: ... |
minor |
fix: ... |
patch |
docs: ..., chore: ..., refactor: ..., test: ... |
none |
feat!: ..., fix!: ... |
major (breaking change) |