Personal time management for techies
Buzz through crazy busy days with as little stress as possible. Stay focused by seeing only one task at a time. Use this simple. fast, fun tool all day with the keyboard in a familiar terminal environment.
Quick installation (MacOS)
brew install python@3.11
python3.11 -m pip install pipx
pipx install busy
See detailed installation instructions for more.
Getting started
To get started, add some tasks to your default queue.
busy add 'Donate to the Busy project'
busy add 'Phone mom'
busy add 'Do the laundry'
busy add 'Take a shower'
Then, when you're ready to start your day, ask Busy what to do first:
busy
Returns:
Donate to the Busy project
That's it! Get to work.
See commands for details or read on to experiment more.
Handling tasks
When you've finished that task, mark it off to find the next task.
busy done
It will ask you to confirm that you're done, then mark the task as done and tell you what to do next.
Phone mom
If you want to see the whole queue, with sequence numbers, type:
busy list
Here's the list you will see. Note that the completed Tasks are gone:
1 Do the laundry
2 Take a shower
See planning to learn about time management or read on!
Planning
If you decide, in the moment, to wait until later today to perform a task, drop it to the bottom of the queue using the drop
command:
busy drop
If you see a task on the list that seems urgent, and you intend to perform it immediately, pop it to the top of the list:
busy pop 2
Let's say you realize that it's not an appropriate task for today, but you want to defer it to tomorrow:
busy defer
It will ask you to confirm "tomorrow" as the day for deferral. Agree with it for now. The Item will then be moved into the plan
State with tomorrow's date as the plan date. As usual, Busy will tell you what to do next after deferring that task.
At the start of a new day, tell Busy to add all the previously deferred Items to the current queue.
busy activate
Congratulations! You may now start enjoying the benefits of a more relaxed approach to your busy life.
Ready to get busy?
Learn the principles and data model
Installation
Briefly (MacOS)
brew install python@3.11
python3.11 -m pip install pipx
pipx install busy
Long version
You'll need a terminal emulator to access a command or shell prompt. Examples include:
- iTerm2 or Terminal on MacOS
- Gnome Terminal or XTerm on Linux
- CMD on Windows
- Terminator on all platforms
Busy requires Python 3.11 and pipx
. To check whether you already have the right version of Python on your system, start your terminal emulator and type:
python3 -V
If you don't have Python, or your version is out of date, install or upgrade it. In most cases, you'll want to do so using your system's package manager (such as Homebrew on MacOS or APT on Ubuntu). If you're not familiar with package managers, then download Python from the Python.org site directly and follow the instructions provided there. When done, use the version check above to confirm it's installed and the version is 3.11 or greater.
On MacOS, this might work:
brew install python@3.11
On Ubuntu, this might work:
apt-get install python@3.11
Install pipx
with pip
:
python3.11 install pipx
pipx install busy
To upgrade it later:
pipx upgrade busy
Ready to get busy?
Get all the commands
Background
Principles
Busy is built with the following usage principles in mind:
- Monotasking: We each focus better when we work on exactly one task at a time. So busy only shows you one task.
- Keyboard-driven: Productive people use the keyboard effectively, because muscle memory builds up over time, and it's faster to hit a key than to find an icon on a screen and move the pointer.
- Offline use: It's designed to run on your laptop or desktop computer, without needing an internet connection, so it works extremely fast under any conditions.
- Multi-platform Because Busy is a terminal-based application, it will run on MacOS, Linux, or Windows.
- Personal: Busy is not a collaboration platform or project management application. It's for managing your personal time, not assigning things to others.
- Importance over Urgency: Stop stressing out over last-minute tasks and impending deadlines! Busy makes it easy to capture future tasks and remember to do them early enough to reduce the pressure.
- Editable data: The data is stored in text files, which can easily be edited outside of Busy itself. (In fact, Busy started as a todo.txt type of approach and grew from there.)
Data model
Busy's core model is a collection of Items, which are typically tasks but can also be discussion topics, groceries to buy, or anything else you like. Items are organized into Queues, which are named sets of Items to do. You work on the top Item in a Queue, and when it's done, that Item gets marked as "done", to reveal the next one. There is a default Queue (called "tasks") but you can also create other Queues, for example a shopping list or discussion list.
Busy actually moves Items between States within each Queue. Each Queue contains a Collection (ordered list) of Items for each State. The States are:
todo
: Current Items for you to work on, discuss, or buy now.done
: Items that have been done, with the date completed.plan
: Items that have been deferred to a future date, with that date.
Ready to get busy?
Make sure you have an installation
Commands
List of commands
Output commands:
describe
provides the full BusyML descriptionsimple
includes less detail thandescribe
list
is likedescribe
but with sequence numbersview
outputs specific fields - for now only theval
fieldsqueues
to lists all the queuestags
to lists tags from a queue
Commands that make changes:
add
adds a new itemdone
marks an item as 'done' and gives the option of iterating - "done for today"defer
puts a task or set of items on the plan for a future datepop
moves a task or set of items to the top of a queue - "pop it to the top"drop
moves a task or set of items to the bottom of a queue - "drop it to the bottom"pick
moves one random item from the filtered set to the top of the queue - "pick one for me"delete
permanently removes a task or set of items from a queueedit
opens a text editor to edit items - the default is to edit only the top itemmanage
is the same asedit
, but defaults to edit the whole collectionactivate
finds everything planned for today and adds it to the list - "activate my day"
Common options
- filters to designate items to be acted upon, using sequence numbers or tags
- the
add
command requires a description of the new item instead of filters --queue
if not the defaulttasks
queue--help
to find out which options apply--yes
to skip confirmation of any command that requires it--state
to work on Items of a different state (todo
,plan
,done
)--when
applies to thedefer
command--defer
applies to thedone
command--fields
applies to theview
command
Default item designations
For the most part, commands that accept item designations default to only act on the top item in the queue. The exceptions are:
list
andmanage
default to handle the entire collectionpop
defaults to pop the last item in the collection to the topactivate
defaults to activateplan
items for today (more on that below)add
adds to the bottom of thetodo
queue
Alternate queues
Busy will manage any number of queues, which are entirely separate sets of items. For example, you might have a shopping
queue for items to buy at the store, and a movies
queue for films you'd like to watch. The default queue is called tasks
.
To designate an alternate queue, use the --queue
or -q
option. For example:
busy add --queue shopping -d "Skimmed Milk"
busy list -q movies
Ready to get busy?
Learn about Filtering and Selection
Filtering and Selection
Sequence numbers
Sequence numbers appear in the output from the list
command. Note that the numbering starts with 1, and is not an ID -- the number of a item will change when the collection is modified. So always reference the most recent output from the list
command.
Sequence numbers are used with filters. To designate more than one item, separate the sequence numbers with a space.
Another choice is ranges. A range of sequence numbers is separated by a hyphen, with no whitespace, and is inclusive. For example, 4-6
designates items 4, 5, and 6. A hyphen without a number after it includes all the items from that item to the end of the queue. A hyphen on its own indicates the last item in the queue.
Below are some examples of task designations by sequence number.
busy pop 5
pops item number 5busy drop 3-7
drops items 3 through 7 (4 items)busy list 3-
lists all the items from number 3 through the end of the listbusy delete 3 5 7 9
deletes only the items designatedbusy defer -
defers the last taskbusy edit -4
is an error! Usebusy edit 1-4
insteadbusy manage
allows you to edit the entire queue
Items will always be handled in the order they appear in the queue, regardless of the order the filter are provided. So for example, if a pop
command designates some items, they will be moved to the top of the queue in the order, relative to each other, they currently appear in the queue.
The sequence numbers in the list
command output are from the collection itself. So the list
command does not modify the sequence numbers, even when item designation is applied.
Tags
BusyML includes tags as words in item descriptions starting with #
.
Tags may be used as filter in addition to sequence numbers. For example, the following command will move all the items with the #errands
tag to the top of the queue.
busy pop errands
Numerical filters use OR logic with each other, and tag filters use OR logic with each other. But AND logic applies between the two types. For example, the command below deletes tasks in the range 1-10 that have either the admin or sales tag.
busy delete 1-10 admin sales
To use AND logic with tags in filters, combine them with a plus (+). For example, the command below lists tasks that are tagged both daily and admin.
busy list daily+admin
Named Filters
Certain data fields support named filters using a colon syntax in the command.
planmin:2024-09-28
for theplan
collections to specify the lowest plan dateplanmax:2024-09-28
for theplan
collections to specify the highest plan datedonemin:2024-09-28
for thedone
collections to specify the lowest done datedonemax:2024-09-28
for thedone
collections to specify the highest done dateval:i456
for any collection to specify a data value
Example:
busy list -s done donemin:2024-11-11 client awesomeco val:i456
Ready to get Busy?
Dig into Planning capabilities.
Planning
Doing things later
Busy supports several specific commands related to planning -- that is, scheduling tasks for the future. They are done
, defer
, and activate
. The task-specific commands handle items in the plan
state and, in some cases, the done
state.
The task commands accept filters. The done
and defer
commands reference the todo
collection; the activate
command references the plan
collection. The default for done
and defer
is the top item in the collection; the default for activate
is to activate only plans deferred to today or earlier.
Planning by date
Planning is by date, not time, and is relative to the current date according to the system clock.
In the done
command, the date can be specified using the --defer
option (or by inputting a value after issuing the command). If the option is omitted, then the date can be provided as input during confirmation.
The date may take any of the following forms:
- A specific date in
YYYY-MM-DD
format, such as2018-10-28
. Slashes are also acceptable, but the order is always year, then month, then day. - A specific date without the year in
MM-DD
format, such as7-4
, which will defer the item to that date in the future (even if it's in the next year). - A specific day of the month as a simple integer, such as
12
, which will defer the item to that day of the month, in either the current month or the next month. - An integer, a space, and the word
day
ordays
, such as4 days
, which will defer the item to that number of days from today. - An integer without a space and the letter
d
, such as4d
, which is a short form of4 days
. - The word
tomorrow
, which is also the default if no date is provided. - The word
today
, which is useful for activating tasks later in the day.
As an example, the following command will defer tasks 4, 5, and 6 from the todo
collection to the date 4 days from today, keeping them in the plan
collection until that date.
busy defer 4-6 -t "4 days"
Note that the plan
collection keep the task information from the todo
collection along with the date information (as an absolute date).
To pull tasks from the plan
collection and put them back into the todo
collection, use the activate
command. There are two ways to use the activate
command:
- With no filter, in which case Busy activates all the tasks scheduled for today or earlier, bringing the
todo
list up to date - With designated items from the
plan
collection; note that theactivate
command accepts item designation from theplan
queue itself so usebusy list -s plan
first to get the right list.
Finishing and repeating
The done
command removes the designated Task (or the top task if none is designated) from the todo
state and adds it to the done
state, with today's date to indicate when it was completed.
Optionally, a task can have a repeat value by adding a right angle bracket a relative date phrase (same as with the defer
command).
check email > 1 day
phone mom > sunday
balance the checkbook > 6
The done
command uses the repeat value as the default for its iteration.
Ready to get Busy?
Go deep with BusyML.
BusyML description markup
Every item in a Busy collection is created and stored with a description
that uses a specific form of markup we call "BusyML".
Loosely, an item's description contains one or more space-separated words, and those works that start with a marker indicate some special data field.
The idea is to concentrate useful information in a way that's easy to remember and type.
The markers are:
#
- tag%
- data value!
- timing-related data@
- url (only one allowed)>
- repeat timing, special because the marker itself can exist as its own word (i.e. have a space after) - only one allowed
All words without markers that appear before the repeat marker are considered the "base" description, and represent the human-readable action or topic of the item.
Example
`Check new Mac prices #shopping #weekend %i88 !e14 @https://apple.com/store > repeat on saturday'
- The base is "Check new Mac prices"
- Tags are
shopping
andweekend
- One data item
i88
(specifically might refer to a GitLab issue iid if using thebusy-gitlab
integration) - 14 minutes of elapsed time
- Related url is the Apple store
- When done, the task will repeat next Saturday
Tags
Items can have tags, which are space-separated hashtags in the item description. An item can have no tags, one tag, or more than one tag. For example the following item description has the tag "errands":
go to the supermarket #errands
The only punctuation that tags can contain is the hyphen ("-").
Data values
Data values are used by integrations to correlate to outside systems. They start with %
followed immediately by a 1-letter reference to the data value being tracked and (optionally) a value.
It's possible to filter by data vals.
busy list val:x45
Use the view
command to view data values in isolation.
busy view --fields val:x
Timing-related data
The time tracking subsystem inserts words starting with !
into tasks. Leave them alone.
URL
Useful place to bookmark a single URL related to the item
Repeat timing
Described on the planning page.
Ready to get Busy?
Find out how to edit data directly in Editing and Storage.
Editing and Storage
Text editor integration
The edit
and manage
commands launch the user's default text editor to directly edit a task, the whole queue, or part of a queue. Note that edit
and manage
are identical commands except for their default filter.
A text editor can be designated in a configuration file (.busy.yml
) otherwise Busy uses Emacs.
The edit
command with no filter will edit the top item in the list, and the manage
command with no filter will edit the entire list. But it's also possible to designate items to be edited with both commands using a filter. The commands do their best to replace the edited items in place in the list order. So if you edit
or manage
a tag whose items are recently popped (at the top of the collection), then the edited items will still appear at the top. Even if you add items, they will be inserted after the last item in the edited set, not at the end of the queue. But all the items brought up in the editor will be edited. So if you remove an item in the editor, it will be deleted and the others will be moved up to take its place.
The editing process includes the full descriptions in BusyML.
Data storage
Busy keeps the collections in plain text files, so if the tool doesn't do something you want, you may edit the files. The files are in a directory together, referred to as the "root". Each file is the named according to the following convention:
<queue>.<state>.psv
If a required file is missing, it will be created automatically. So typically, the root includes tasks.todo.psv
, tasks.plan.psv
, tasks.done.txt
, and any number of custom queue files.
Technically, Busy data files are pipe-delimited data files, though the todo
collections only have one field ("description") while the plan
and done
files have only two fields (date and description).
Busy is not a database (yet). There is no support for managing separate fields in the Busy tool itself.
The default location for the files is a directory at ~/.busy
, which will be generated as needed. Specify an alternative location using the --config
option referencing a YAML file with the following format:
busy:
storage:
directory: /some/other/directory
Note that Busy does not support concurrency or locking in any form. If two commands are executing at the same time, they might overwrite each other. Overwriting is especially risky with the edit
and manage
commands, which keeps the user's editor open until they close it.
The format is designed to be simple but not idiot-proof. Experimentation might result in unintended consequences.
Ready to get Busy?
Advance with Time tracking.
Time tracking
Busy keeps track of how long you spend on the current task, and records it in the list of completed tasks. It only works on the tasks
(default) queue and only works in minutes.
The elapsed time is stored in the task description using !e
and is visible with the busy describe
command.
For reporting, Busy can optionally apply a multiplier to the clock-minutes for e.g. invoicing (basic theory is that clock time at keyboard is only a part of overall value). The multiplier lives in the busy configuration.
At the bottom of busy list
, see the total value (time*multiplier) for the queried tasks.
Works great with the new donemin:
and donemax:
filters, for example:
busy list -s done donemin:2024-10-12 donemax:2024-10-18 myclient
The timing system records the "start" time (basically clock time as of the last busy command) in the queue, then updates elapsed time when the next command runs. So while it gives the impression of a running clock, it's really just subtracting.
Also note that tasks without text (i.e. purely tags) don't record time. Use them for gaps in your day. For example, I use a task called simply #pause
for breaks and such.
Ready to get Busy?
We recently added integrations.
Integrations
Status: alpha
For developers
Busy features a very simple plugin architecture for integrations. It's still a work in progress and might change with any release.
Integrations are designed for external systems that need to sync data (in either direction or both) with the Busy "database".
Integrations must be preloaded into the venv from which Busy is run, typically within the pipx
installation. In the future, we might provide a mechanism for loading them.
Two commands support integrations. Both tage the integration name as their first argument.
get
- to get a piece of datasync
- to "sync" data
When an integration-related command is run, Busy will attempt to import a module called busy_
plus the integration name. The module must be preloaded. General expectation is that the package name would start with busy-
plus the same integration name. For example, to sync with Asana, an integration might come in a package called busy-asana
containing a module called busy_asana
.
The interface for integrations is still under development and will be documented when it becomes more stable.
The general expectation is that the sync
method will perform some read and write activity in either of Busy or some external system, and raise an exception if the operation files. Additionally, it might return a value, for example if the integration is purely designed to generate output in a specific format.
The sync
method doesn't generate a status - that's up to the sync
command.
When developing an integration, include busy
itself in pyproject.toml
, ideally with a specific version or narrow range, to avoid the possibility of dependency conflicts.
Under current temporary guidelines, installing an integration might look something like this (example from MacsOS):
.local/pipx/venvs/busy/bin/python -m pip install busy-asana
Items support the following readable properties, all parsed from the description
- base (str)
- tags - (set)
- url - (str)
- repeat (str)
- elapsed - (int)
- data_value - The only one that's a method; takes the key as input
- description - Everything as entered, plus elapsed time as timing data
Note: Integrations first introduced in Busy 7.4.1 but considered unstable until at least 8.0.
Ready to get Busy?
Contribute
Request access to the source repo to contribute.
The code is intended to demonstrate some Python best practices:
- Object-oriented with classes and subclasses.
- Dynamic configuration using a unique approach we call "class families" - for example, the names of the commands are properties of the command classes, not in a big "if" statement.
- Extensive testing with high test coverage, guaranteed by CI.
- Leverage the standard library by requiring as few 3rd party PIP modules as possible.
To set everything up:
- Requires Python 3.11
- Clone the repo and CD into it
make init
poetry install
- To run it:
python -m busy
...
We use Visual Studio Code to build Busy, so there is a VS Code configuration file in the repository.
Then to run the test suite:
make test
Or to run test coverage:
make cover
And to check style:
make style
And to run all of the above, and prepare for CI:
make